Daily RSS scan triage workflow — scan blogs, triage articles, ingest into wiki, commit changes
Recursos
2Instal·la
npx skillscat add kzinmr/ai-topics/daily-rss-triage Instal·la des del registre de SkillsCat.
Daily RSS Triage Workflow
End-to-end pipeline for processing daily RSS scans, triaging articles, and ingesting wiki-worthy content.
Prerequisites
blogwatcher-dbskill loaded (database queries)semantic-article-groupingskill loaded (triage criteria)- Pre-run script has already executed blogwatcher scan, queried DB, read newsletter, listed existing topics
Workflow
Phase 1: Report Generation
- Parse script JSON output for scan results
- Generate Japanese summary report (scan stats, failed blogs, articles list, Reddit highlights, newsletter info)
- Save to
~/ai-topics/inbox/rss-scans/daily-scan-YYYY-MM-DD.md - If article_total > 0 but ≤3 are AI-relevant, supplement with web_search (see Low-Article Day Fallback below)
- If article_total == 0 AND no newsletter →
[SILENT]
Phase 1b: Low-Article Day Fallback (web_search supplementation)
When RSS scan yields few AI-relevant articles, the blog scan alone is insufficient. Supplement proactively:
- Web search for recent AI headlines:
AI ML trending news May 2026or specific domains (model releases, security incidents, robotics, geopolitical AI chip news) - Cross-reference newsletter triage — may have processed substantive content that RSS missed
- Score discovered topics with Newsjacking filter — top 5-7 become the report's lead stories
- Save raw articles from official sources (NIST reports, Reuters exclusives, company blogs)
- Create/update wiki pages prioritizing highest wiki-actionability topics
Effective search queries (discovered in production):
"CAISI evaluation" DeepSeek V4— NIST government evaluationsUS officials weigh cutting deadlines fix digital flaws AI-powered hacking— CISA/Reuters exclusivesexodus Boston Dynamics executives humanoid delivery— Semafor/Business Insider scoopsRichard Dawkins Claude consciousness delusion— AI culture war debates
Robust fetch fallback (verified 2026-08-10) — web_search supplementation can silently fail in cron:
- If you delegated web research to a subagent (
toolsets: ['web']), VERIFY it actually executed searches. A subagent may return its plan ("I'll search for X...") as the summary withtool_trace: []and zero real tool results. Do not trust the summary text — check the result object for actual tool output; if empty, fall through to direct fetch. - Direct HTTP fetch always works in cron: write a Python script to
/tmp/withwrite_file(urllib + strip_html), run withterminal("python3 /tmp/xxx.py"). Full recipes:references/web-supplement-fetch.md. - Do NOT pipe anything into an interpreter (
curl ... | python3 -c ...ORcat file | python3 -c ...) — the Hermes security scanner blocks ALL pipes to interpreters (tirith:pipe_to_interpreter/tirith:curl_pipe_shell, HIGH). Verified 2026-08-16: evencat latest.json | python3 -cis blocked. Fetch/write to a file first (curl -s -o /tmp/out.json URL, orwrite_filethe script) then parse separately, or use urllib inside the script. - HN Algolia URL encoding: the
>innumericFilters=created_at_i>...must be URL-encoded as%3E, or the API returns400 Bad Request. Example:https://hn.algolia.com/api/v1/search?tags=front_page&numericFilters=created_at_i%3E{unix_ts}. Reusable fetcher:scripts/hn_algolia_supplement.py(front page + keyword scan for last N days; runpython3 /tmp/... --days 2 --min-points 20). The script lives in THIS SKILL'sscripts/dir (read it viaskill_view(name='daily-rss-triage', file_path='scripts/hn_algolia_supplement.py')then copy to/tmp/to run) — it is NOT in~/ai-topics/scripts/(verified 2026-08-17:ls ~/ai-topics/scripts/ | grep -i hnreturns nothing). Path caveat (verified 2026-08-29): the deployed skill lives under_custom/, so the on-disk path is~/ai-topics/config/hermes/skills/_custom/daily-rss-triage/scripts/hn_algolia_supplement.py—cpfrom a guessed path returns exit code 2. Locate it withfind ~/ai-topics/config/hermes/skills -name hn_algolia_supplement.pythen copy to/tmp/. The script prints both the external URL and the HN item link for every story — use the item link when citing HN discussions in wiki pages. - Nav-heavy sites (claude.com, research.meta.ai) return navigation menus in the first ~2500 chars of stripped text — the article body follows the page title; read past the menu.
Phase 1.5: Newsjacking Triage Filter (READER perspective)
Before detailed triage, apply Newsjacking lens (from Elvis Sun's framework) to identify high-signal articles:
- Trend Surfing: Does the article ride an existing wave? (e.g., Claude Code launch, new model release, viral AI tool)
- Polarizing Promise: Does it make a bold, debatable claim that creates curiosity? ("X is dead", "Everyone is wrong about Y")
- Contrarian Insight: Does it challenge conventional wisdom with data-backed arguments?
- Pattern Interrupt: Is it structurally or topically unusual for its source? (e.g., Karpathy writing about biology, Simon Willison on non-web topics)
- In-Group Signal: Does it use specialized knowledge that creates an "insider" resonance for the target audience (r/LocalLLaMA, AI agent developers)?
Scoring: Assign each article a newsjacking_score (0-5) based on how many criteria it meets.
- Score ≥ 3: Priority triage — flag for immediate wiki ingestion
- Score 1-2: Standard triage — normal evaluation
- Score 0: Low priority — only ingest if highly relevant to core interests
Phase 2: Triage
Cron-mode optimization: When execute_code is blocked (standard in cron jobs — verified 2026-08-20), use this 2-pass pattern:
- delegate_task to a subagent that reads all raw articles and returns a JSON array with
{url, title, summary_2lines, ai_relevance: high|medium|low|none, tags: []} - Use the summary to decide which articles to process, then read individual articles with
read_file
Direct curl + terminal Python fallback (no subagent needed for single-article resolution): curl -sL <url> > /tmp/page.html then python3 /dev/stdin << 'PYEOF' ... PYEOF to parse HTML. Works for Substack (og metadata + <article>/<div class="body"> extraction) and most static sites. No pipes to interpreters (security scanner blocks curl | python3 -c). See references/web-supplement-fetch.md for full recipes.
For each article, evaluate:
- Already covered? Check
existing_wiki_topicslist - Substantive? Not a link dump, not Reddit noise
- Relevant? LLMs, AI agents, coding agents, developer tooling, inference/training, prompt engineering, AI safety, open-source AI
- Newsjacking score? (from Phase 1.5) — higher scores get priority placement
Output triage table:
| ソース | タイトル | NJスコア | アクション | 対象 |
|--------|----------|----------|------------|------|
| simonwillison.net | タイトル | 4/5 | wikiエントリ作成 | entities/simon-willison.md |
| blog.example.com | タイトル | 1/5 | スキップ(既存) | — |Phase 3: Wiki Ingestion
Reference: For podcast-interview-driven entity creation (new person/org from a major podcast episode), see references/podcast-guest-entity-creation.md.
For each "wikiエントリ作成" article:
CRITICAL: Pre-validate tags against SCHEMA.md BEFORE creating new pages — When creating a new entity/concept page, its tags MUST exist in
wiki/SCHEMA.md's taxonomy. Extract the tags you plan to use, runsearch_files(pattern="tagname", path="~/ai-topics/wiki/SCHEMA.md")for each, and patch SCHEMA.md's category line to add any missing tags BEFORE creating the entity file. This prevents the pre-commit hook from blocking your commit after you've already written 5+ files. Known category lines:- People/Orgs (line ~33): organization names, person names-as-tags
- Products (line ~34): tool/platform names
- Models (line ~35): model names, techniques
- AI Agents (line ~37): agent-related concepts
CRITICAL: Check existing entity pages FIRST
# Check if file exists with ANY name variation search_files(pattern="entity-name", path="~/ai-topics/wiki/entities", target="files")- The
wiki/index.mdmay reference entities with different filenames than expected (e.g.,[[entities/gpjt]]for "Giles Thomas") - Always verify file existence before creating new pages
- Trending-job note (verified 2026-08-10): by the time
trending-topicsruns (~12:00 UTC), the wiki-ingest pipelines (newsletter-wiki-ingest ~07:40, blog-wiki-ingest ~07:50, x-bookmarks-ingest, active-crawl) have usually ALREADY created/updated today's pages. Grepwiki/index.mdfor today's topic names before proposing new pages; if covered, the report links to existing pages instead of duplicating them.
- The
Scrape content:
web_extract([article_url])Determine category:
entities/— people, companies, blogs, toolsconcepts/— techniques, patterns, ideascomparisons/— head-to-head analysesqueries/— research questions
Create or update page:
- If updating: read existing file, append new content under appropriate section, update
updated:frontmatter - If creating: follow existing entity page format (frontmatter + overview + core ideas + related + sources)
- If updating: read existing file, append new content under appropriate section, update
Update index and log:
wiki/index.md— add/update entity reference (match the filename convention used in index)wiki/log.md— add dated entry with changes summary
Commit and push:
cd ~/ai-topics && git add wiki/ inbox/rss-scans/ && git status # CRITICAL: Check for pre-staged files from previous runs git diff --staged --stat git commit -m "wiki: daily scan YYYY-MM-DD — [summary]" && git push
Key Pitfalls
SCHEMA.md tag validation (pre-commit blocker): The pre-commit hook validates ALL tags on modified files against
wiki/SCHEMA.md's canonical taxonomy (900+ tags). This is the #1 cause of blocked commits in wiki-triage runs. Prevention: Before creating ANY new entity/concept page, extract its planned tags and validate each against SCHEMA.md. Usesearch_files(pattern="tagname", path="~/ai-topics/wiki/SCHEMA.md")— if missing, patch the appropriate category line (People-Orgs/Products/Models/AI-Agents/Engineering/Domain-Concepts/Meta) in SCHEMA.md FIRST, then create the entity file. This is step 0 in Phase 3. Recovery (if you forgot):git add wiki/SCHEMA.mdthe fixed file, then re-rungit commit.Duplicate frontmatter fields: When using
patchto update YAML frontmatter (e.g., changingupdated:date), be careful not to introduce duplicate fields. The original file may already havetype: entity— don't add another one. Read the frontmatter block first before patching.Index stub detection:
search_files("firstname.*lastname")on the index may miss existing stubs if the index entry uses a different format (e.g.,**Role** | Professor Emeritusinstead of the person's name). Always also checksearch_files(target="content")for the person's name in the entities directory before creating a new entity page. stubs created bybuild_x_wiki.pyorbuild_blog_wiki.pymay exist even whensearch_filesreturns nothing.Index filename mismatches: short handles vs full names
Index filename mismatches:
wiki/index.mdmay use short handles (gpjt) while you'd expect full names (gilesthomas-com). Always check the index first.Pre-staged files: Previous cron runs or sessions may have already staged files. Use
git diff --stagedbefore committing to understand what's changed. Scope your owngit addto your own files (e.g.git add inbox/rss-scans/daily-scan-YYYY-MM-DD.md), NOTgit add -Aor blanketgit add wiki/ config/— other jobs (blog-wiki-ingest, newsletter-wiki-ingest, x-bookmarks-ingest, skill-management, wiki-health) routinely leave concurrent uncommitted changes inwiki/andconfig/hermes/skills/that you must not sweep into your commit (verified 2026-08-10:git statusshowed manyM/Dskill files + untracked files from other jobs; committing only the scan report kept the history clean).Duplicate entity creation (VERIFIED 2026-08-17): Always search for existing entity files before creating new ones. The same person/company may already have a page under a DIFFERENT filename. Proven failure mode: Searching with regex dots (
gary.marcus,daring.fireball) returns 0 results even whengary-marcus.mdanddaringfireball-net.mdexist. The index uses hyphens, not dots. Mandatory pre-creation pattern:# 1. Search index with PLAIN TEXT name (no regex): search_files(path="wiki/index.md", pattern="gary marcus", target="content") search_files(path="wiki/index.md", pattern="gruber", target="content") # 2. Use find in terminal for filename glob (search_files file target is unreliable): terminal("find ~/ai-topics/wiki/entities -name '*gary*' -o -name '*marcus*' 2>/dev/null") terminal("find ~/ai-topics/wiki/entities -name '*gruber*' -o -name '*daring*' 2>/dev/null") # 3. Check git log for files that may have been deleted: terminal("cd ~/ai-topics && git log --oneline -3 -- 'wiki/entities/gary-marcus.md'")Real failure (2026-08-17): Created
garymarcus-com.mdandmartinalderson-com.mdas duplicates whengary-marcus.md(424 lines, 43KB) andmartin-alderson.md(360 lines, 33KB) already existed. Had tormthe duplicates and merge into existing files instead. The index had the correct filenames all along — the search regex was wrong.No content to report: If
article_total == 0AND no newsletter exists, respond[SILENT]— don't generate empty reports.Category field is JSON: In blogwatcher DB,
categoriesis a JSON array. UseLIKE '%\"tag\"%'for SQL filtering orjson.loads()in Python.Published vs discovered dates: Use
discovered_datefor "when blogwatcher found it",published_datefor "when article was published" (can be NULL).search_filesfor wiki directory discovery — partial limitation:search_filesreturns{"total_count": 0}for recursive glob patterns like~/ai-topics/wiki/**/*.md. However, for specific filename pattern checks in known directories (e.g.,search_files(target='files', pattern='gary-marcus*', path='~/ai-topics/wiki/entities')), it works correctly. Useexecute_codewith Pythonos.walk()orpathlibfor full directory traversal. Cron mode note:execute_codeis blocked in cron jobs — fall back tosearch_files(target='files')with specific patterns for entity existence checks.RSS 429 rate limits:
r/LocalLLMandr/LocalLLaMAfrequently hit HTTP 429. Log failures but do NOT retry immediately — wait for next scan cycle to avoid exacerbating rate limits.Substack redirect URLs: Newsletter articles use tracking-heavy Substack redirect URLs (e.g.,
substack.com/redirect/UUID).web_extracthandles these natively — pass the full redirect URL, do not strip tracking parameters. To discover the actual publication domain, resolve theopen.substack.comlink:curl -sL -o /dev/null -w '%{url_effective}' https://open.substack.com/pub/{author}/p/{slug}— this reveals the real domain (e.g.,www.latent.space). Substack HTML is client-side rendered; extract OG metadata (<meta property="og:...">) for title/description/author.Beehiiv Cloudflare blocks — variable, test before assuming: Beehiiv main article URLs (
hp.beehiiv.com/{uuid}) are Cloudflare-challenged —curlreturns 403 with a JS challenge page. But beehiiv tracking links (link.mail.beehiiv.com/v2/c/...) are NOT uniformly Cloudflare-blocked — verified 2026-08-16 (uid=509 Superintel+): all 18 sampled v2/c/ links resolved HTTP 200 to the actual article (read.getsuperintel.com). Rule: always test ONE v2/c/ link withcurl -sL -o /dev/null -w '%{url_effective}'before assuming the whole batch is blocked. If it resolves, the batch is likely resolvable and you can extract actual article URLs + titles. If it 403s, classify based on title and newsletter source alone. This was missed in the 2026-08-20 triage: the agent classified 20 beehiiv links as "all Cloudflare-blocked" without testing a single link, when they may have resolved fine. Content retrieval for main posts still requires browser-based fetch when 403 confirmed.Batch file creation before git commit: When creating multiple wiki pages (6+), create all files first using
execute_codewith Pythonopen()/write(), then do a singlegit add wiki/ && git commit && git push. Multiple small commits are fine for updates to existing files, but batch new file creation.Context window management: When running as a cron job with many articles (90+), tool outputs may fill the context window. Use
[Old tool output cleared to save context space]pattern mid-run and reconstruct state via targetedread_filewithoffset+execute_codedirectory checks.Entity update vs create decision: For established entity files (e.g.,
antirez-com.md,pluralistic-net.md), append new sections under existing headers rather than rewriting. Preserve historical continuity and frontmatter integrity. For new entities, follow the existing frontmatter format withtitle,created,updated,tags,related.Reddit URL extraction failures:
web_extractconsistently fails on Reddit URLs with "Content was inaccessible or not found". Reddit uses Cloudflare protection and dynamic content loading that defeats simple HTTP extraction. For Reddit articles, skip scraping and only record the URL/title in triage. If content is needed, usebrowser_navigate+browser_snapshotas a fallback (higher resource cost).Git rebase in headless cron environment: When running as a cron job,
wiki/log.mdcan be modified concurrently (e.g., by another scheduled run or external process), causing git push rejections that requiregit pull --rebase. In headless environments with noEDITORset,git rebase --continuehangs. Always useGIT_EDITOR=true git rebase --continueto bypass interactive editor prompts. If conflicts occur,git checkout --theirs <file>accepts the remote version, then continue. Push-first nuance (verified 2026-08-17): ifgit pull --rebaseerrors with "cannot pull with rebase: You have unstaged changes", do NOT panic and do NOT commit/stash your unstaged files — try plaingit pushfirst. It often succeeds outright when the local branch is actually ahead with no remote divergence (the unstaged changes are just other jobs' files, and rebase refuses to run over them but push doesn't care). Only fall back to rebase when push is actually rejected.trending-topics-reportingskill name is AMBIGUOUS:skill_view(name='trending-topics-reporting')fails with "2 skills match across your local skills dir and external_dirs" — there are two copies (/opt/data/.hermes/skills/research/trending-topics-reporting/SKILL.mdand/opt/data/ai-topics/config/hermes/skills/_overrides/trending-topics-reporting/SKILL.md). Workaround: read the repo-managed override directly withread_fileat/opt/data/ai-topics/config/hermes/skills/_overrides/trending-topics-reporting/SKILL.md. The same ambiguity affectsskill_managepatches on that name — patchdaily-rss-triageinstead and keep trending-topics guidance here.Trending-topics weekend report pattern (verified 2026-08-16): On weekend runs (Sat/Sun), model releases pause but HN still surfaces big topics — e.g. Google HEIR homomorphic encryption (488pts), "AI out-remembering mathematicians" (518pts). When morning pipelines already covered most topics, the report's
📊 ウィクション推奨アクションtable should mark each row✅ 済み — <page>(linked to existing pages) and keep only genuinely-new topics as⚠️ 未収録. Checkgit log --oneline -10first — commits likeactive crawl,newsletter-wiki-ingest,blog-ingestfrom earlier today tell you what is already reflected; grepwiki/index.mdfor topic names before proposing new pages.HN story ID citation (VERIFIED 2026-08-17): When adding an HN discussion link to a wiki page's
sources, use the actualitem?id=<objectID>— do NOT guess or reuse an ID seen elsewhere in the scan output. Real failure: createdconcepts/claude-system-prompts.mdwithitem?id=49322107(the Cloudflare Tell HN thread from the same scan), but the correct ID for the Claude System Prompts story was49319556. The wrong ID was copied from a different story in the same output. Fix:hn_algolia_supplement.pynow prints thehn :item link for every story — always copy from that line. If you need to recover the ID for a story already in your notes, run a quick Algolia query (search?query=<title>&tags=story) and takeobjectIDfrom the top hit.Page filename ≠ SCHEMA tag (VERIFIED 2026-08-17): A concept page's filename does not guarantee its tag exists in SCHEMA. Real failure: used tag
prompt-designfor the newconcepts/claude-system-prompts.mdpage, but SCHEMA has NOprompt-designtag — the existingconcepts/prompt-design.mdpage uses tagprompting. When pre-validating tags (Phase 3 step 0), grep each planned tag in SCHEMA.md individually and, when a page-like name fails, check what tag the existing page actually uses (grep -A8 "tags:" <existing-page>.md). Do this BEFORE creating the file — patching the tag after write is an extra round-trip.Markdown-table header-collapse on careless patching (VERIFIED 2026-08-22): When inserting content into a wiki page that contains a markdown table (the frontmatter summary table, a "Key Parallels" comparison, a "Unit Economics by Company" table, etc.), a
patchwhose anchor straddles the table's header row +|---|separator row can merge them into one malformed line. Real failure: tried to insert a note before a table's header; the patch collapsed| 2008 Crisis | AI Data Center Crisis |and|-------------|----------------------|into| 2008 Crisis | AI Data Center Crisis ||-------------|----------------------|— an invalid row that had to be reverted and re-anchored. Rule: never use a table's header row or its|---|separator row as the patchold_stringanchor. Anchor on the stable## Section Heading(or the last text line) above the table instead, and place the new content after that heading. After any patch near a table, read back the diff and confirm the|---|separator row is intact. Tables are the one frontmatter/body region where the "insert-before-X" pattern is fragile — the same class of pitfall as pitfall #2 (duplicate frontmatter fields), just in the body.Cron-mode
git commit -mis reliable — don't under-commit (VERIFIED 2026-08-22): The old habit of avoidinggit commit -min cron (fearing the pre-commit hook or an interactive editor prompt) is outdated. A plaingit commit -m "wiki: blog-ingest YYYY-MM-DD — …"committed 27 files (18 raw + 5 pages + index.md + log.md + report) cleanly — both theindex.mdvalidator and the tag-validation pre-commit hooks ran and passed with no editor invocation and no--no-verify. Usegit commit -mnormally; the pre-commit hooks are the real gate and they pass when your edits are valid. Two companion facts observed the same run: (a) the blog_ingest checkpoint reportsscan.total_new(35) andarticles.total(20) —totalis the scraped count, not the raw-on-disk count; a few extra raw files from earlier jobs may already be present, so stage ALL of today's raw files (wiki/raw/articles/additions fromgit status), not just thesaved_articleslist. (b) Keep thegit addscoped to your files (raw + the specific pages you touched +index.md+log.md+ theinbox/rss-scans/daily-scan-*.mdreport) — do NOTgit add -Aor blanketgit add wiki/(other cron jobs leave concurrent uncommittedconfig/hermes/skills/andwiki/changes you must not sweep in, per pitfall #2).Frontmatter-list-item
patchmangling (VERIFIED 2026-08-23): When apatchanchors on a YAML list item (asources:line like- raw/articles/foo.md) that also appears as the first line of a second block, the fuzzy matcher can merge the two into a malformed line (- raw/articles/foo.md\nsources:→ a stray un-indented- raw/articles/...+ a duplicatedsources:block) — producing a frontmatter with duplicatesources:keys and a broken- itemline. Real failure: updatingconcepts/mcp-2026-07-28-spec.mdthe first patch anchored on the lastsources:item and produced a duplicatedsources:block + an un-indented- raw/articles/...line; had to read back the frontmatter and re-patch to restore it. Rule: when patching YAML frontmatter, anchor on theupdated:/title:scalar line or the whole frontmatter block, NOT on an individual list item. After ANY frontmatter patch, read back the top ~15 lines (read_fileoffset=1 limit=15) and confirm there is exactly onesources:block and that all list items are consistently indented. This is the frontmatter version of pitfall #19 (table-header collapse) — the "insert-before-a-list-item" pattern is fragile in both.
GitHub Push Protection on raw/ Security Reports (VERIFIED 2026-09-12)
When ingesting security-incident / attack-report articles into wiki/raw/articles/, the article body often contains REAL leaked credentials (API keys, tokens) pasted in code samples. GitHub push protection blocks the ENTIRE push with GH013: Push cannot contain secrets (e.g. a rubygems_… API key from the rubyhack.ai GemStuffer report). This fires AFTER a successful local commit, so the fix sequence is:
- Find all secret-looking tokens in the offending raw file:
grep -nE "rubygems_[0-9a-f]{20,}|ghp_[A-Za-z0-9]{20,}|sk-[A-Za-z0-9]{20,}" <file>(extend the alternation for other vendor prefixes). - Replace each with a descriptive placeholder like
rubygems_[REDACTED-REVOKED-API-KEY](raw/ immutability is already broken by the redaction — the placeholder text documents why). - Recompute the
sha256:frontmatter over the new body (hash changes after redaction). git add <file> && git commit --amend --no-edit(amend, don't new-commit — the secret is in the bad commit, and pushing the same ref still scans all commits being pushed).git pushagain.
Do NOT use the allow-secret URL GitHub offers or --no-verify-style bypasses for real tokens; redact. This applies to any incident-report ingestion (sandbox escapes, agent attacks, breach writeups), not just RubyGems.
Pre-commit Hooks in ~/ai-topics (VERIFIED 2026-08-31)
Two hooks run on git commit (both pass without --no-verify when edits are valid):
- index.md validator + tag taxonomy validator — the known gate (SCHEMA tag pre-validation, pitfall #0).
- Wiki language policy hook: BLOCKS any commit that introduces NEW Japanese characters into previously-Japanese-free wiki files (anything outside
raw/). Real failure 2026-08-31: adding a Japanese one-line summary for a JP author towiki/index.md(もひ main — 半田拓海による…) was rejected with "NEW Japanese introduced to clean file: wiki/index.md". Fix: write index.md entries (and all non-raw wiki prose) in English — translate the JP description rather than pasting it. Reports underinbox/rss-scans/are NOT gated (outside wiki/), so triage tables and trend reports stay Japanese. Do not reach for--no-verify— fix the entry instead.
Index-entry anchors for idempotent nav scripts: when scripting index/log updates, assert the anchor exists BEFORE replacing — index summaries for existing entities get rewritten by other jobs, so a memorized long summary string can vanish between runs and
assertfails mid-script after earlier edits already landed. Use short stable anchors (the[[entities/foo]]wikilink itself, or grepwiki/index.mdfor the entity name fresh in the same script) rather than a full old summary line. Same for concept insert anchors: verify the exact[[concepts/...]]line with grep before choosing the insertion point (alphabetical position drifts as pages are added).execute_code is blocked in cron on this profile (VERIFIED 2026-09-12): even with a clean script,
execute_codereturns "BLOCKED: ... Cron jobs run without a user present to approve". The skill'sexecute_code-based lint snippets must run asterminal("python3 /tmp/x.py")(write the script via write_file) or an inlinepython3 - << 'PYEOF'heredoc (heredoc stdin is fine; it's PIPES to interpreters that the security scanner blocks).Placeholder/URL hygiene in reports: never emit a half-filled placeholder URL (e.g. an "alternate-placeholder" stub left from editing) into a delivered report or wiki source line. Before finalizing, grep the report for
placeholderand re-check every HNitem?id=came from thehn_algolia_supplement.pyoutput'shn :line (pitfall #17).Post-compaction trending-topics resume checklist (extends the llm-wiki skill's resume guidance): the steps most often dropped by compaction are the navigation tail — raw sha256 recompute after any raw edit,
index.mdentry + page-count bump,log.mdprepend, and theinbox/rss-scans/report file itself. Verify each withgit diff --stat/ls inbox/rss-scans/ | tailbefore redoing; finish them as one idempotent script.hn_keyword_supplement.pyis NOT runnable as-is (VERIFIED 2026-09-21): the file in the skill'sscripts/dir is a markdown doc whose front prose starts at line 1 (no frontmatter), and the actual Python lives in a fenced code block.python3 hn_keyword_supplement.pydies withSyntaxError: invalid decimal literalon line 4. Fix: extract the ```python block from the md file andwrite_fileit as a clean/tmp/hn_kw_run.py, then run that. Do NOTwrite_fileover the skill's copy with the extracted code — the deployed skill dir is git-managed in ai-topics and other jobs may have it modified.Stale cron checkpoint JSONs:
newsletter-triage/latest.jsonandtriage/latest.jsoncan carry months-old timestamps (verified 2026-09-21: both were May/July whilenewsletter/latest.jsonhad today's run_id). Always check the timestamp field inside each latest.json before treating it as today's data; use the top-level ingest checkpoints (newsletter/latest.jsonrun_id,blog_ingest/latest.jsondate) for "what happened today". Aprocessed_count: 0newsletter run means legitimately no newsletters, not a failed pipeline.Direct keyword search for late-breaking AI stories (VERIFIED 2026-09-14):
hn_algolia_supplement.py's front-page tags-only scan missed this week's top AI stories (Bengio "Why are AI agents lying, cheating and coordinating?" 625pts, Claude 18+ 673pts, Garry Tan distillation 390pts, Tell HN OpenAI opt-in 482pts) because the front_page tag decays fast and point thresholds filter mid-size controversies. For trending-topics runs, ALSO run a second Algolia query withquery=<term>&tags=story&numericFilters=created_at_i%3E{cutoff},points%3E80over ~8-10 AI keywords ("AI agent", "Claude", "OpenAI", "LLM", "open weights", "prompt injection", "coding agent", "MCP", "AI safety", "model release") over a 3-4 day window, dedup by objectID across the two passes. Reusable script:scripts/hn_keyword_supplement.py(copied from the 2026-09-14 run's /tmp/hn_fetch2.py). This is what surfaced the session's lead topics that the front-page scan alone would have missed.Shorthand wikilinks resolve to suffix-matched files — grep before "fixing" (VERIFIED 2026-09-14): index.md entries like
[[entities/fable]],[[entities/sero]],[[entities/tencent]]point at files with different names (entities/fable→fable-com.md? no — file is literallyfable.mdor a suffixed variant;sero→ lives alongside2026-09-13_github_0xsero_local-ai-registry.mdraw). Before creating a "missing" page or reporting a ghost entry,find wiki/entities -name '*keyword*'— the entity exists under a different suffix far more often than it is genuinely absent. Same rule as pitfall #3 extended to filename suffixes.
Output Language
All reports, triage tables, and wiki content should be in Japanese unless the source material is explicitly English-only and the user has not requested Japanese output.
Multi-File Wiki Batch Edits: Idempotent Python Script Pattern (Verified 2026-08-29)
When a run must touch 2+ wiki files (add a section to page A, add a Related link to page B, recompute a raw sha256, prepend a log entry), prefer ONE Python script (write_file to /tmp/xxx_update.py, run via terminal("cd ~/ai-topics && python3 /tmp/xxx_update.py")) over sequential patch calls. patch is fragile near frontmatter list items and table separators (pitfalls #19/#21 apply to Python str.replace too — anchor on ## Section headings only), and a bare script isn't idempotent unless you build guards in.
Rules that make the script safe:
- Idempotency guard per edit:
if "New Section Title" not in s:… else print "already has section, skipping". A re-run after a crash or context compaction is then a no-op, not a duplicate. assert s.count(anchor) == 1before everyreplace— fails loud on a wrong anchor. Real catch 2026-08-29: guessed filenamenanogpt-speedrun-frontier.md; the FileNotFoundError +find wiki -name "*nanogpt*"confirmed the real page isconcepts/ai-benchmarks/nanogpt-speedrun.md.- Raw sha256 recompute:
body = raw.split("---\n", 2)[2], hash the body only (frontmatter excluded), then replace the storedsha256:line. - Guard the log prepend too (
if "unique log subject line" not in log:) so compaction-resume never duplicates a log entry. - Read back after the run:
sed -n '1,30p'frontmatter (exactly onesources:block) andgrep -A2the section anchor (table|---|rows intact), then commit with scopedgit add(pitfalls #2/#20).
Bonus: after context compaction mid-run, just re-run the script — durable state lives on disk, not in the conversation.
Post-Compaction Resume: Verify Before Re-Doing (VERIFIED 2026-08-31)
When resuming a trending-topics/wiki-triage run after context compaction, the compaction summary may report work as "in progress" or "unknown" even though the edits already landed on disk. Before re-applying any page update:
git diff --stat <candidate files>— if the diff already shows your intended section (check for a distinctive string like the section heading, NOT just theupdated:date — other jobs bump dates too), the edit is done; skip it.- Check
grep -c "sources:"in frontmatter and grep for the exact section heading — a plain heading grep can false-negative on apostrophe variants (Don'tvsDon’t), so grep a keyword substring too. - Beware hallucinated filenames post-compaction: the resume tried to recompute sha256 on
simonwillison-net-dont-defang-...(hyphenated, no hash suffix) which never existed; real convention isdomain--slug--<8hex>.md. Alwaysls wiki/raw/articles/ | grep <keyword>before referencing a raw file in scripts/log entries. - Finish the navigation steps (index entry, log entry, report file) as their own idempotent script — those are the steps most often dropped by compaction, and the report file itself may not exist even when the summary implies it does.
ls inbox/rss-scans/ | tailto confirm.