소스 정보
- 저장소
- QianJinGuo/wiki
- 최근 소스 활동
- 2026년 8월 6일 05:21
- 감지된 SKILL.md 언어
- 영어
- 스타
- 1
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/QianJinGuo/wiki --skill manual-article-ingestion명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
SOC 직업 분류 기준
| name | manual-article-ingestion |
| description | Process a single URL into the wiki from start to finish. |
| tags | ["wiki","ingestion","workflow","pipeline-routing"] |
| references | ["references/script-invocation-quirks.md","references/wechat-article-summary-format.md","references/append-content-pitfalls.md","references/historical-corrections.md","references/wechat-fetch-bypass-pitfalls.md","references/raw-article-frontmatter-conventions.md","references/existing-raw-upsert-scoring-pitfall.md","references/entity-cross-referencing-conventions.md","references/patch-table-corruption-and-index-count-drift.md","references/scoring-heuristics-quickref.md","references/local-file-ingestion.md"] |
Process a single URL into the wiki from start to finish.
When user provides a direct URL to ingest, not via inbox pipeline.
Batch (2+ URLs): See references/subagent-batch-ingestion.md — parallel subagents.
Files (PDF): See references/local-file-ingestion.md — local doc → wiki.
Before doing anything else for a single URL, call:
skill_view(name='wiki-pipeline')
The wiki-pipeline skill is the orchestrator. It classifies the input (URL → Phase 1 web-content-reviewer, raw text → Phase 2 llm-wiki, query → Phase 2 query mode, etc.) and routes correctly.
Why this fails: See references/historical-corrections.md for all archived session corrections (wiki-pipeline bypass, fetch sync launch, index.md patch corruption, WeChat curl bypass, index.md |- prefix bug).
Mandatory: load wiki-pipeline first as step 0a.
Symptom of bypass: Agent says "Phase 1 scoring: v×c=N" without ever having loaded wiki-pipeline. The scoring happens correctly, but the routing decision was made ad-hoc. The user will correct you on this.
Symptom of correct flow: wiki-pipeline loaded → input classified as "WeChat article URL" → routes to Phase 1 (web-content-reviewer) → sub-skill loaded → scoring happens.
Reinforcement after 2026-06-04 9-URL batch session: The bypass happened again — 9 consecutive WeChat URLs, ZERO wiki-pipeline loads. The agent produced correct outputs (proper scoring, dedup, lint), but the orchestrator was never consulted. The skill was the right one to use, and the manual-article-ingestion skill itself contains "ALWAYS load wiki-pipeline first" in its description, yet the load was skipped on every URL. Concrete fix: when this skill loads, the first tool call should be skill_view(name='wiki-pipeline') — not terminal, not execute_code, not read_file. If the agent finds itself calling any other tool first, it has skipped step 0a.
Repetition pressure trap: In a batch session, the first URL tends to be a "warm-up" where the agent skips the load to be efficient. By URL #5, skipping the load is muscle memory. The user observes: 9 correct ingestions but zero pipeline loads = the skill exists for a reason and is being systematically ignored. The fix is to enforce step 0a as the literal first tool call, even when batching, even when the URL pattern is identical to the last 4 URLs.
After loading wiki-pipeline, also load the sub-skill that matches the URL before attempting any fetch:
https://mp.weixin.qq.com/s/... → CDP Chrome via wechat-cdp-fetch.py (content-fetching umbrella). The CDP script connects to the user's logged-in Chrome via DevTools Protocol port 9222 — 3-5s/article, no anti-bot wall, no CAPTCHA. Playwright Profile (fetch-with-profile.js) is fallback only when CDP is unreachable. See references/wechat-user-pasted-fast-path.md in wiki/content-fetching.skill_view(name='github-repo-management') or rely on direct api.github.com/repos/... per step 1.skill_view(name='readability-extractor') for JSDOM+Readability+Turndown pipeline.Why this is its own step (2026-06-04 verification): In a session where the user pasted 4 WeChat URLs in one turn, the agent did curl first for all 4 (per this skill's own step 1), hit the 17KB verify.html wall 2 times, and only THEN opened the playwright profile. Each wall hit cost ~30s of failed curl + python-strip. Loading the sub-skill at the top of the workflow makes the fallback path visible before the agent commits to the slow path.
Session-level micro-rule (2026-06-04 patch): Once the agent has hit a WeChat verify.html wall at any point in this session, subsequent WeChat URLs in the same turn should default to node fetch-with-profile.js URL --text without re-trying curl. Track this in conversation context, not in the skill.
Always check if the URL is already in the wiki before creating anything.
Search ALL raw articles' frontmatter for the exact URL:
grep -r "source_url: <URL>" /Users/jinguo/wiki/raw/articles/
Also search index.md and entities for the URL:
grep "<URL>" /Users/jinguo/wiki/index.md
grep -r "<URL>" /Users/jinguo/wiki/entities/
Why filename search isn't enough: Different curation sources (WeChat, RSS, Newsletter) may slug the same URL differently. A file named obsidian-claude-code-integration-guide.md won't show up when searching entities for "obsidian" if the filename lacks the term. The source_url frontmatter field is the only reliable dedup check.
Reprint/variant detection: If same article appears on multiple platforms (e.g., TiDB 官号 18:02 vs InfoQ 18:52 with 50-min delay), check authorship. If same author/content → reject as reprint, do not create duplicate. Report as "same article, different platform, already ingested."
Sibling-agent concurrent-ingest check (2026-06-04 verified — PilotDeck case): Concurrent cron/sibling sessions can create an entity for the same product launch event under a different slug, often with a different WeChat publisher. The new URL is genuinely new, but the product/launch event is already covered. Detection method:
# After fetching the new article body and identifying 2-3 product keywords,
# list candidate entities by partial slug match BEFORE writing anything.
ls -la entities/*<product-keyword>* raw/articles/*<product-keyword>*
# For each candidate, read the entity's frontmatter:
# - sources: list of raw slugs (do they already cover this product?)
# - created/updated: is it recent (concurrent session is in the last hour)?
# - description: does it describe the same launch event / product?
Why this matters: In a multi-agent wiki setup, cron jobs (wechat-inbox-pipeline, rss-feed-scan) and sibling sessions can ingest the same launch event minutes apart. The new URL is unique but the content is a paraphrase. Without this check, you create a duplicate entity. The dedup signal here is the product/launch event, not the URL or the author.
Decision: If the candidate entity already covers the product with depth ≥ 3KB and same launch event → short-circuit reply:
✅ 重复内容 — 已由 sibling session 入库
- 已有 commit: <hash>
- 已有 entity: entities/<slug>
- 内容: 同一产品 launch event 不同公众号转载
See references/sibling-concurrent-ingest.md for full case study.
Use search_files (ripgrep), NOT Python full-text scan (2026-06-05 verified, 12x speedup — see pitfall 16m):
search_files pattern="<keyword1>|<keyword2>|<keyword3>" target="files" path="/Users/jinguo/wiki/entities"
search_files pattern="<keyword1>|<keyword2>" target="files" path="/Users/jinguo/wiki/concepts"
search_files pattern="<keyword1>|<keyword2>" target="files" path="/Users/jinguo/wiki/raw/articles"
A 6500+ file Python scan takes 60s+ and times out; the same query via search_files takes 3-5s.
Same-author same-series overlap (2026-06-01 若飞 5张卡 case): When the new article is from a WeChat author who has existing entities in the same domain, do an explicit topic overlap check before deciding to create a new entity:
# 1. Find all entities from same author / same series
grep -l "<author_name\|<feed_name>" /Users/jinguo/wiki/entities/*.md
# 2. For each candidate entity, check topic overlap
grep -l "<key_topic_keyword>" /Users/jinguo/wiki/entities/*.md
# 3. Read the candidate entity fully — assess depth (>3KB = substantive)
Same-topic different-source merge (2026-06-03 Thariq blog case): When a new article is a translation / re-publication of a topic already covered by an existing entity, do the dedup by 1-2 distinctive terms from the new article (not generic keywords):
# Distinctive terms (choose 1-2):
# - named person (e.g. "Thariq", "trq212")
# - named source (e.g. "高可用架构", "GAIA")
# - specific feature name (e.g. "动态工作流", "Dynamic Workflows")
# - author handle from original (e.g. "trq212")
grep -lE "(Thariq|动态工作流|trq212)" /Users/jinguo/wiki/entities/*.md /Users/jinguo/wiki/raw/articles/*.md
Merge vs new entity decision (2-criteria test):
2026-06-03 Thariq blog case (positive): New article = Thariq 官方博客中文版 (Thariq, 动态工作流, 6 模式 + 3 失败模式 + 10 场景). Existing entity = claude-code-dynamic-workflows-multi-agent-orchestration (7272 bytes, 2026-05-29, covers technical depth on scheduler/worker). Decision: merge — 70% topic overlap + 30% unique (6 patterns catalog, 3 failure modes taxonomy, 10 use cases with concrete prompts, 静态 vs 动态演进). Appended as ## Thariq 官方博客补充:6 种模式 + 3 类失败模式 and ## Thariq 实战:10 大使用场景 sections.
Decision tree (from wiki-pipeline § merge-decision-criteria):
| Overlap degree | Action |
|---|---|
| > 70% overlap (same topic + same author + same series) | Merge into existing entity. Add to sources: array, append new section to body. Delete any new entity file created. |
| 30-70% overlap | Consider merge — compare depth. If new article is deeper, replace entity. If shallower, merge unique angles. |
| < 30% overlap | Full Phase 2 — create new entity. Cross-link wikilinks only. |
2026-06-01 若飞 5张卡 counter-example: Agent searched for "hermes" keywords in entities, found 51 matches, but missed the same-author check on long-running-agent-ralph-loop-handover-harness-ruofei (10.6KB, 若飞 2026-05-21, same domain "long-running agent governance"). Created a new entity that should have been merged. Had to refactor (delete entity, append section to existing, fix index/log). The lesson: always grep by author name, not just topic keywords.
2026-06-01 counter-example (positive case): Stripe Minions + 字节 DeerFlow 2.0 + 蚂蚁支小助 — searched for stripe, deerflow, 支小助, 墙比模型 (specific new concepts), 0 matches. Existing entities (harness-engineering-framework, agent-harness-engineering-survey-2026, etc.) covered the abstract framework but not these 3 specific industry cases. New entity was the right call. Lesson: search by specific named concepts from the new article, not just generic topic keywords.
If you accidentally created files for an already-ingested URL or for an entity that should have been merged:
rm -f orphan raw and entity filespatchTrigger confusion — user said "query this article" vs "save this article":
WeChat articles — extraction priority (2026-07-05 update — CDP Chrome primary):
CDP Chrome via wechat-cdp-fetch.py — connnects to the user's existing logged-in Chrome via DevTools Protocol (port 9222). 3-5s per article, never hits anti-bot wall:
python3 ~/wiki/scripts/wechat-cdp-fetch.py "<URL>" --out /tmp/wx_<short>.txt
Checks output size: wc -c /tmp/wx_<short>.txt (expect >5000 for an article). On success, extract metadata from the frontmatter lines in the output.
CDP unavailable fallback (Playwright Profile) — if CDP Chrome is unreachable (Chrome not running, port 9222 doesn't respond), use the Playwright profile:
cd ~/wiki/scripts/playwright-profile && \
node fetch-with-profile.js "<URL>" --text 2>/dev/null > /tmp/wx_<short>.txt
TinyFish — last resort. Costs API quota, often 120s timeout on WeChat.
Jina Reader is BLOCKED on WeChat (returns CAPTCHA ~312 bytes).
Decision flow for a new WeChat URL (2026-07-05 update):
cdp-chrome (wechat-cdp-fetch.py)
├─ success → frontmatter parsed, article ready (~3-5s)
└─ fails (Chrome CDP unreachable) → playwright profile → done (~10-15s)
Why this sequence (2026-07-05 update): The CDP Chrome script connects to the user's existing daily-driver Chrome (port 9222), which is already logged into WeChat. No anti-bot wall is ever encountered because the Chrome session has real cookies. 3-5s is faster than Playwright's ~10-15s cold start. The only failure mode is Chrome not running or port 9222 not configured — in that case, fall through to Playwright Profile which uses its own persistent browser profile.
Verification after extraction:
document.querySelector('#activity-name')?.innerText → titledocument.querySelector('#js_name')?.innerText → authordocument.querySelector('#publish_time')?.innerText → datedocument.querySelector('#js_content').innerText.length → body size (≥1500 chars for normal articles)If content < 1KB after extraction, this is a paywall truncation — report extraction failure without retrying.
GitHub: Fetch raw content via raw.githubusercontent.com directly.
Other platforms: Use the readability-extractor skill's Node.js pipeline (JSDOM + Mozilla Readability + TurndownService) via extract.mjs. This strips nav/ads/sidebars/footers via DOM scoring, preserving semantic structure (h1-h6, code blocks, links).
READABILITY_SCRIPT="$HOME/.hermes/skills/wiki/readability-extractor/scripts/extract.mjs"
node "$READABILITY_SCRIPT" --url "<url>" --frontmatter
If extraction fails (< 200 chars output), fall back to Jina Reader API:
curl -sL --max-time 15 "https://r.jina.ai/<url>"
Why not Jina first: Jina returns decent text but loses heading hierarchy, code block fences, and link structure. CDP Chrome and Readability both produce semantically richer Markdown. Jina is reserved for when CDP/Readability/Playwright all fail or timeout.
Why not browser_navigate for WeChat: The browser tool opens a fresh context with no WeChat cookies, hitting the same 17KB verify.html CAPTCHA wall as curl. CDP Chrome uses the user's real logged-in Chrome instance — it's not the same thing as browser_navigate.
This step is the quality gate. It applies to EVERY direct URL the user provides, regardless of source.
Score via heuristic or API:
review_value: 1-10 (technical depth, uniqueness, practical value)review_confidence: 1-10 (source reliability, specificity of details)Threshold: score ≥ 49 → proceed to ingest. Below threshold → give a single-line rejection reason only, create no files.
Content type classification FIRST (calibrates confidence):
technical (architecture, code, deep-dive) → v/c both can be highanalysis (case study, industry proof) → high v if novel synthesistutorial (step-by-step from-scratch) → high c (concrete code), moderate v (educational not novel)news (announcement, product launch) → cap confidence at 6 (format-deception guard)lifestyle (employee perks, marketing) → stars ≤ 2 = auto-reject regardless of v×cWhy this fails: When a user says "save this URL" without providing a score, the historical failure mode is assuming ingestion is the intended action. Articles like "腾讯员工公寓曝光" (v×c < 30) slipped through because the URL-ingress path was treated as "user wants this in" rather than "score first." The gate is the decision rule, not a post-check.
⚠️ If the raw file already exists (e.g., orphan from inbox pipeline with source: wechat and no scoring fields): use patch to add scoring fields to frontmatter, NOT write_file — that overwrites the entire article body. See references/existing-raw-upsert-scoring-pitfall.md.
Path: raw/articles/<slug>.md
Use terminal heredoc, NOT execute_code + write_file (2026-05-30 verified pitfall):
cat > /Users/jinguo/wiki/raw/articles/<slug>.md <<'RAWEOF'
---
title: "<title>"
source_url: "<url>"
author: "<author>"
feed_name: "<feed>"
publish_date: <YYYY-MM-DD>
created: <YYYY-MM-DD>
ingested: <YYYY-MM-DD>
tags: [<tags>]
type: article
review_value: <V>
review_confidence: <C>
review_recommendation: strong
review_stars: <1-5>
sha256: <hash-or-placeholder>
---
# <title>
...
RAWEOF
execute_code + write_file can silently write 0-byte files if the script crashes, AND the execute_code + read_file cache pollution can result in double-line-number content being written. Terminal heredoc creates a fresh subprocess without cache.
Critical: Write sha256: placeholder in frontmatter first, then compute and patch the real hash.
sha256sum /Users/jinguo/wiki/raw/articles/<filename>.md
Patch frontmatter with the actual hash (replace "placeholder").
ls /Users/jinguo/wiki/raw/wechat-inbox/ /Users/jinguo/wiki/raw/rss-inbox/ 2>/dev/null | grep -iE "<keywords>"
If a duplicate exists in inbox, delete it after successful ingestion.
Also clean up any /tmp/wx_*.txt files from the CDP/Playwright fetch to avoid stale cache across sessions:
rm -f /tmp/wx_*.txt
Path: entities/<slug>.md
Required frontmatter fields (lint will report NO FRONTMATTER / MISSING type / etc if missing):
---
title: "<title>"
created: <YYYY-MM-DD>
updated: <YYYY-MM-DD>
type: entity
tags: [<tags>]
sources: [<raw-slug-without-.md>]
review_value: <V>
review_confidence: <C>
review_recommendation: strong
review_stars: <1-5>
---
Wikilink format rules — CRITICAL:
sources: → without .md: sources: [raw/articles/filename].md: → [[raw/articles/filename.md|原文存档]][[entities/other-slug]] (not bare [[other-slug]])When creating both entity AND raw for the same URL, use two commits:
Why two commits: Per wiki-pipeline rules, the user prefers "small frequent commits (each article one commit)" rather than batching index/log with file writes. This makes rollbacks cleaner and git history readable.
# Step 1: commit files
git add entities/<slug>.md raw/articles/<slug>.md && git commit -m "ingest: <descriptive title>
- v=N, c=N, v×c=N
- <brief description>
- Topic overlap pre-check passed/failed
- Total pages: X → Y"
# Step 2: update index.md and log.md
# ...patch index.md (entity section + sources section)...
# ...patch log.md (append entry)...
git add index.md log.md && git commit -m "index/log: <slug> 登记
- 实体 + 原稿各 1 条入库
- Total pages: X → Y"
Why not just one commit: Conflating file writes with index/log updates makes atomic recovery harder. If a file write succeeds but index update fails, the rollback is messier.
Index entry locations (entity section + sources section):
Common failure mode: After committing entity+raw together, the agent continues to the next URL without updating index.md. The entity+raw exist in git but the wiki's index tracking falls out of sync.
node /Users/jinguo/wiki/scripts/wiki-lint.mjs 2>&1 | tail -20
Total pages verification (CRITICAL): The linter's actual count is the source of truth, NOT arithmetic.
node /Users/jinguo/wiki/scripts/wiki-lint.mjs 2>&1 | grep -E "tracked|page\(s\)" | head -3
# Output: Wiki lint: 0 error(s), N warning(s), <actual_count> tracked page(s)
Compare <actual_count> to the **Total pages: X** header in index.md. If they differ (almost always, due to concurrent cron activity), patch the header to match the linter's count. Never use "+N" arithmetic — the linter uses find | wc -l and is always right.
Why this fails: Adding 2 pages (entity + raw) and bumping header by 2 often results in mismatch because other concurrent sessions may have added/deleted pages. The linter is the source of truth.
Frequency reality (2026-06-04 batch session): In a single session ingesting 6 URLs consecutively, the Total pages header needed to be patch-bumped via lint after every single ingest (6/6 = 100% drift rate). Sibling agents (cron jobs like wechat-inbox-pipeline) were committing index.md changes in parallel. The "arithmetic +2 then move on" pattern is broken — the right pattern is: run lint, copy the actual count, patch header to that exact number, every single time. Don't try to be clever with arithmetic.
Look for errors that YOUR changes caused. Pre-existing warnings/errors (MISSING sha256, MISSING from index for older entities, NO FRONTMATTER) are NOT actionable.
INDEX DRIFT format: Total pages header says X, actual count is Y
**Total pages: X** → **Total pages: Y**sed -i '' 's/^|- /- /' index.md to fix prefix corruption from the patch itself|- prefix corruption: The patch tool always corrupts list items in index.md, inserting |- instead of - . Run the sed fix AFTER EVERY patch to index.md, even if corruption isn't visible in the diff — lint may still not find the entry.
BROKEN LINK: Entity's wikilink has .md in body wikilink.
Wrong: → [[raw/articles/filename.md|原文存档]]
Right: → [[raw/articles/filename|原文存档]]
Also check: Frontmatter sources: field must be without .md.
Entity Merge Protocol
When a new article should merge into an existing entity rather than creating a new one:
sha256: placeholder → compute hash after)sources: array (without .md)updated: date to today## YYYY-MM-DD | merge | <existing-slug> (补充: <topic section>)Merge decision tree:
MERGE signal catalog:
| 信号 | 动作 | 示例 |
|---|---|---|
| 同作者同工具系列 | merge | Claude Code企业实践 → Claude Code entity |
| 补充特定架构章节 | merge | Gateway API三层模型 → Higress entity |
| 实战心法/玩法补充 | merge | Jason Liu Codex playbook → Codex /goal entity |
| 同topic扩展已有entity | merge | MCP最佳实践 → MCP架构entity |
| v×c≥49但内容高度重叠 | raw only | CLI-Anything(与同日entity重叠) |
| 全新topic无覆盖 | 新entity | EmbodiSkill/SkillEvolver |
| 同一topic多方解读(源码+实战+综述) | merge到已有entity | Codex /goal 源码解析 → merge到codex-goal-agent-runtime(已有完整架构层,互补充) |
| 同产品多角度报道(官方+实战+技术), 不同作者(user's explicit preference 2026-06-04) | 新 entity + 显式 cross-link 已有同产品 angle entity | 量子位 Coze 3.0 官方升级 → coze-3-0-collaboration-system (3rd angle for Coze 3.0 alongside 网黑哥 实战 + coze-bridge 技术) |
| 面试材料包装的技术文 | raw only | 技术自由圈文章(大量卖课营销,技术有价值但不独家) |
| 媒体转述二手信息 | raw only | Mythos Glasswing(新智元转述,无新细节) |
| 同作者同系列 30-70% 重叠 (WeChat 续篇) | merge 优先 | 若飞 5张卡 → ralph-loop-handover-harness-ruofei entity(不创建新 entity) |
printf '\n## %s | ingest | %s — v=%d, c=%d, v×c=%d | new entity (%s) | %s\n' \
"$(date +%Y-%m-%d)" \
"<slug>" \
"<V>" "<C>" "<V*C>" \
"<one-line topic>" \
"<one-line description>" \
>> /Users/jinguo/wiki/log.md
log.md patch safety (2026-05-30 verified): When patching log.md, the patch tool's old_string matching can replace the WRONG entry if multiple entries have similar format. Use the slug as the primary anchor, not just the date/format pattern:
old_string = "## [2026-05-23] ingest | hermes-agent-tool-system-architecture — score=9×8=72"
This is unique because of the slug. A pattern like ## [2026-05-23] ingest | opencli — merge would match both entries on the same day. If patch overwrites the wrong entry, recover by git log --oneline to find the displaced commit, then echo >> to append the lost entry to the end.
Run lint one final time — verify no NEW errors/broken-links/index-drift caused by this ingestion. Pre-existing issues are expected.
# Verify commit exists
git log --oneline -3
# Verify entity appears in index.md
grep -c "<entity-slug>" /Users/jinguo/wiki/index.md # must be ≥ 2 (entity + raw)
# Verify lint clean
node /Users/jinguo/wiki/scripts/wiki-lint.mjs 2>&1 | grep -E "0 error|tracked page" | head -3
Never say "入库成功" / "入库完成" until ALL three checks pass. The user has been bitten by "files written but not committed" and "files committed but not in index" multiple times.
Wikilinks: .md extension in body wikilinks causes BROKEN LINK on every lint run. Frontmatter sources must NOT have .md.
Index drift — linter is always right: The Total pages header must match the linter's actual count, not arithmetic. Always run lint at the end and use the linter's number. Concurrent cron activity means the actual count is unpredictable from the agent's perspective.
sha256 placeholder: Don't forget to patch with actual hash before final lint.
Inbox duplicates: Always check and delete duplicates after successful ingestion.
Product pages 404 / URL unavailable: When a product page returns 404 during fetch, and the user asked to "query" or "check" the article — report unavailability and offer to update from prior conversation context. Do not spend time on alternate URLs or cache sites.
WeChat navigation edge case: The browser title sometimes shows a single character (e.g., "图") on first snapshot even for full articles. If browser_navigate returns an unusually short title, extract full content via browser_console JavaScript anyway — the article body is always complete. Do NOT assume the article is a single-image post just because the heading is minimal.
wiki-index.mjs silently skips: Always verify with grep "<slug>" index.md after running the index script. The script can exit non-zero without surfacing an error. Manual echo-append is more reliable when the script fails.
entity+raw commit missing index update: After committing entity+raw in one commit, index.md update is a SEPARATE commit. Do NOT skip or combine — treating them as one step causes index to fall out of sync.
Bypass wiki-pipeline (2026-06-01): Always call skill_view(name='wiki-pipeline') FIRST. The user explicitly corrected this. Symptom of bypass: agent does scoring and ingestion without ever loading the pipeline. The user will correct you.
Topic overlap pre-check uses author names AND specific named concepts (2026-06-01): When checking whether to create a new entity vs merge into existing, grep by author name AND specific named concepts (e.g., stripe, deerflow, 支小助, DIPG, 墙比模型), not just generic topic keywords (, , ). Generic keywords hit too many existing entities to be useful. Specific named concepts from the new article are the reliable overlap signal.
For arxiv papers, search by arxiv ID + paper title + team/org (most reliable for paper introductions): The arxiv ID (e.g., 2505.11063) is the strongest dedup signal — almost no false positives. Combine with paper title, GitHub org/repo, and HF/ModelScope model name for redundancy.
execute_code + write_file corruption (2026-05-30): Use terminal heredoc for wiki file writes, not execute_code + write_file. Two documented failure modes: (a) silently writes 0-byte files if Python script crashes; (b) read_file/write_file cache pollution can result in double-line-number content being written. Terminal heredoc is safe.11a. execute_code BLOCKED in cron context (2026-06-03 verified): When the session is running under a cron job, execute_code returns BLOCKED: ... cron jobs run without a user present to approve it. Use normal tools instead. Workaround: write the script to /tmp/<name>.py via write_file (small files are not blocked), then run it via terminal("python3 /tmp/<name>.py"). This is the ONLY reliable path to append to large files like log.md (>200K chars) when running in cron mode. Plain terminal("echo ... >> log.md") and terminal("cat << EOF >> log.md") both timeout at 60s on files >200K chars. Same workaround for terminal(...,timeout=N) interactive Python REPL — embed the Python in a heredoc, not in python3 -c if the script is non-trivial (single-line -c overflows at ~50 tool-call mark).
11a-bis (non-cron execute_code blocks too — 2026-06-07 verified): The same BLOCKED: execute_code runs arbitrary local Python (including subprocess calls that bypass shell-string approval checks) error fires in non-cron interactive sessions when the script imports subprocess or other "approval bypass" modules. Same fix: write_file → terminal. See references/execute-code-blocks.md for the full protocol and detection signal.
Detection signal: the BLOCK error message contains "subprocess" + "bypass shell-string approval checks" + the literal string "cron_mode". This is the same error in both cron and non-cron contexts.
11b. Cross-verify article claims against primary sources (2026-06-04 verified): When the article makes specific quantitative claims about a named open-source project (star count, version, license, last-push date, command count, file count), verify them against the primary source before writing the entity. Article authors paraphrase and lag reality; the entity becomes the source of truth and will be referenced months later. Two fast ground-truth endpoints:
# GitHub project — anonymous, no auth needed, ~200ms
curl -sL -H "User-Agent: hermes-verify" \
"https://api.github.com/repos/{owner}/{repo}" \
| python3 -c "import json,sys; d=json.load(sys.stdin); \
print('stars:', d['stargazers_count']); \
print('pushed:', d['pushed_at']); \
print('license:', d.get('license',{}).get('spdx_id') if d.get('license') else None); \
print('lang:', d['language'])"
# npm package
curl -sL "https://registry.npmjs.org/{pkg}/latest" \
| python3 -c "import json,sys; d=json.load(sys.stdin); print('version:', d['version']); print('license:', d.get('license'))"
# PyPI package
curl -sL "https://pypi.org/pypi/{pkg}/json" \
| python3 -c "import json,sys; d=json.load(sys.stdin); print('version:', d['info']['version'])"
Where it caught real drift (2026-06-04 Impeccable case): Article claimed "33.4k Star". GitHub API returned 34,108 — 1k+ drift, worth updating. Article also claimed "23 commands" and "41 detection rules"; I confirmed these by listing repo files (api.github.com/repos/.../contents/...), not trusting the article's paraphrase. The ## 仓库元数据(YYYY-MM-DD 验证) section at the bottom of the raw article is the durable evidence trail.
Don't trust the article's numbers for:
Do trust the article for:
This adds 2-3 tool calls per ingest but produces entities that survive scrutiny a year later.
patch on log.md replaces wrong entry: Multiple entries with the same date+format can be matched by the wrong one. Always anchor old_string on the slug name for uniqueness. If overwrite detected, recover via git log --oneline + echo >> log.md.
patch on index.md corrupts |- prefix: The patch tool always corrupts list items in index.md, inserting |- instead of - . Run sed -i '' 's/^|- /- /' index.md AFTER EVERY patch, before the next tool call. The corruption compounds across patches — never batch.
Strongly preferred alternative for index.md INSERT operations (2026-06-06 verified): use terminal Python lines.insert(idx, line) instead of patch. This eliminates BOTH pitfall 13 (corruption) and pitfall 16h (silent drop) at the same time, because Python insert is 100% additive. See references/index-md-python-insert.md for the full recipe. Use Python for the first index.md insert in any 3+ insert session; subsequent inserts can be Python or patch (Python still safer).
✅ 入库 | <type> | v×c=N | <hash>. NO detailed recap, NO bullet-point breakdown, NO paragraph analysis after claiming completion. The user wants efficient signal, not a second pass at the article. If you find yourself writing more than 1-2 lines after the closeout, stop and strip.14a. "patch failed but commit succeeded" silent index drift (2026-06-04 verified): When patch on the entity page returns success: false (e.g., the file was externally modified by a sibling agent between your read_file and your patch), but you already git added the raw + log + index files and ran git commit, the commit lands with raw/log/index changes intact, but the entity page is missing the new section. The session moves on thinking everything is fine, but the wiki's entities/<slug>.md doesn't have the new content.
14b. "patch success but new line silently dropped" duplicate old_string (2026-06-04 verified — 16h): Companion to 14a. When patch to index.md returns success: true with a clean-looking diff, but the new entity line is not actually added because the old_string already exists in the file (e.g., a sibling agent already added it, or the patch target is from a prior failed commit). The Total pages header gets bumped (the only unique anchor) but the new entity line never lands. Detection: grep -c <new-slug> index.md after every patch to index.md, before sed -i '' 's/^|- /- /' index.md. Recovery: re-patch with multi-line old_string that includes the Total pages header as a unique anchor. See 16h for full protocol.
Symptoms after the fact:
git log -1 shows the commitls entities/<slug>.md shows the filewc -l entities/<slug>.md shows the OLD line count (new section missing)sources: field DOES include the new raw (because the frontmatter patch succeeded)Detection (before declaring "done"): After any merge commit, verify the entity file has the new section by counting the new section's header text:
grep -c "## 架构师 JiaGouX 译本补充" entities/claude-code-dynamic-workflows-multi-agent-orchestration.md
# If 0: the patch failed silently and the section is missing
Recovery: A follow-up commit is needed. Use patch again with the full old/new text (the entity file is now in a known state, so the patch will succeed):
# Read current state of the entity first
# Then patch in the missing section
# Commit: "index: add <slug> 追加section — patch 失败回填"
Prevention:
git commit — before declaring completionpatch returned success: false in any earlier tool call, the work is not done — investigate before committing the raw fileWhy this is a session-level pitfall, not a tool bug: patch is non-idempotent and depends on byte-exact file state. Sibling agents editing the same file in parallel can invalidate your patch's old_string without you knowing. The fix is post-commit verification, not "patch more carefully".
16n. git add -A pollution + lint-stale Total pages at commit time (2026-06-05 verified): Two distinct pollution sources hit a batch-mode ingest at the final git add -A && git commit step. Both are quiet and produce a clean success: true shell that masks the underlying drift.
git add -A picks up cron-inbox files (the foreign-file pollution). Any raw/rss-inbox/* or raw/wechat-inbox/* modified between your git add and git commit ends up in the commit. The commit is technically clean (those files DO belong to the repo), but the commit message claims an article ingest and the diff includes unrelated files.
Total pages drift between lint and commit (the value pollution). The Total pages: value you set based on a pre-write lint may already be wrong by the time you commit, because cron siblings commit index.md in parallel.
Real 2026-06-05 case: The tencent-skill-writing-complete-playbook-jackjchou commit (0b93cf05) had 13 files changed, 561 insertions, 86 deletions — but the expected delta was 2 files (entity + raw). The extra ~9 files (rss-inbox orphan, status logs, heartbeat updates) were the cost of git add -A. Separately, my pre-write lint had reported 3617 but the lint at commit time reported 3619 because a cron sibling had committed 2 more files in parallel — I had to re-patch the header from 3617 to 3619 right before commit.
Mandatory protocol (commit time, not pre-write time):
# 1. Run git status to see exactly what would be committed
git status --short
# 2. EXCLUDE inbox files explicitly — they belong to cron, not to this ingest
git add entities/<slug>.md raw/articles/<slug>.md index.md log.md
# (NEVER use `git add -A` in batch mode)
# 3. Re-run lint AFTER the add, BEFORE the commit
node scripts/wiki-lint.mjs 2>&1 | grep "tracked page" | head -1
# This number is the one that goes into index.md (or has gone in already)
# 4. If the lint count differs from what you already wrote into index.md:
# a. Patch the index.md header to the new lint count
# b. Run `sed -i '' 's/^|- /- /' index.md`
# c. Re-stage index.md: `git add index.md`
# d. THEN commit
# 5. Verify the commit's actual file set
git show --stat HEAD | head -20
# Confirm: only entities/<slug>.md + raw/articles/<slug>.md + index.md + log.md
# If you see raw/rss-inbox/ or raw/wechat-inbox/ files: pollution — recover with amend
Recovery if commit was polluted:
git reset --soft HEAD~1
git status --short
git add entities/<slug>.md raw/articles/<slug>.md index.md log.md
git commit --amend --no-edit
Why this is its own pitfall and not 16g (Total pages bump in every commit): 16g covers the "use lint's count, not arithmetic" rule. 16n covers the "re-lint right before commit, not at the start of step 7" timing — and the "use git add <files> not git add -A" hygiene. The two combine: 16g = correct counting principle; 16n = correct counting AT the right moment + clean commit boundaries.
Prevention rule of thumb: a targeted git add <explicit files> excludes the cron pollution that git add -A would silently include. The cost is 2-3 extra characters per file; the benefit is a clean per-article commit diff that survives git log --stat review.
16o. search_files pattern with 6+ keywords times out on raw/articles/ (2026-06-05 verified): The 16m tip says "use search_files not Python full-text scan" — and that's the right call for ≤ 5 keywords. But for 6+ keyword OR-patterns (e.g., OntoZ|百型智能|百型|企业本体|本体论|Palantir), even search_files(target='files', pattern=<6-keyword-OR>) over raw/articles/ (1700+ files) times out at 60s. The search_files tool itself is ripgrep-fast, but the total scanned content size × 1700+ files × 6 keywords is the bound.
Fix (2-step):
# Step 1: Split the keyword OR-pattern into 2 narrower queries
search_files pattern="OntoZ|百型智能" target="files" path="/Users/jinguo/wiki/entities"
search_files pattern="企业本体|本体论" target="files" path="/Users/jinguo/wiki/entities"
# Each runs in ~3-5s and returns 0-3 hits. Combine results.
# Step 2: Only search the smaller directories (concepts, queries, comparisons)
# with the full pattern, since they're tiny (<100 files each)
search_files pattern="<full-6-keyword-OR>" target="files" path="/Users/jinguo/wiki/concepts"
# (concepts/ has 68 files — even 6-keyword OR runs in <2s)
Why 1700+ files × 6 keywords hits the 60s timeout:
search_files tool's overhead (output formatting, hit deduplication) pushes the wall time past 60s for borderline casesRule of thumb: For topic-overlap pre-check, entities/ is the only directory that matters for the "should I create a new entity?" decision (it's where 90%+ of coverage lives). The other directories (concepts/queries/comparisons) are <100 files and can take the full pattern. If you need to also check raw/articles/, always split the keywords into 2 narrower queries to avoid the 60s timeout.
Don't split by directory — split by keyword count. The "search entities/ with full 6-keyword OR" works fine; the bottleneck is raw/articles/, not entities/. The 16m tip's recommendation to also search raw/articles/ is the high-value but slow step — the fix is to make the raw/articles/ search narrower (1-2 keywords at a time), not to skip it.
Patch entity frontmatter required fields: The entity needs created, updated, type: entity, tags, sources, review_value, review_confidence. Without these, lint reports NO FRONTMATTER as error. Backfill on entity creation, not later.
Same-article multi-URL pattern: When user submits the same article via different URLs (e.g., original source + 微信公众号 re-post), check content equivalence. Same content → report "已入库 | same URL" or "已入库 | same topic covered by ". Different sources/angles on same topic → separately create entity + raw for each.
16a. Same-product multi-angle pattern (2026-06-04 verified — Coze 3.0 case): When a new article covers a product that's already in the wiki with 1+ existing angle entities (e.g., 网黑哥 实战 + coze-bridge 技术 for Coze 3.0), the decision is NOT "merge" even though the topic overlap is high. The right action is to create a new angle entity and add a "与已入库 [product] 报道对照" table with explicit cross-links to all sibling angle entities. The user's memory rule is "不同来源/视角→分别创建 entity 和 raw,都保留" — this overrides the skill's "merge if complementary" default for same-product cases. When the new article is the 3rd, 4th, 5th angle for the same product, the entity frontmatter should reference all sibling angle entities in a "相关对照" section. Don't create an "umbrella" Coze 3.0 entity and merge angles into it — that loses the angle-specific voice and 3-layer abstractions. Each angle stays as a peer entity.
16b. Duplicate-URL short-circuit reply (2026-06-04 verified): When the user sends a URL that is identical or content-equivalent to a URL already in the wiki (same source_url in raw/articles/ OR same article on different platform with same author/content), do NOT re-ingest. Reply with a compact one-liner:
✅ 重复 URL — 上一轮已入库 (commit: <hash>)
No re-fetch, no new files, no commit. Verify the existing commit by git log --oneline | grep <slug> if needed. This is part of the user's "简洁状态总结" preference and saves 30-90s of redundant work. The detection logic is in step 0b (URL dedup check) — the only difference is the action: instead of proceeding to ingest, short-circuit to the one-liner reply.
16b-2. User re-sends the same URL in a later turn (2026-06-04 verified — hxBkT × 2): When the user sends the same URL twice in the same session (after the first turn's short-circuit reply), the second send is also a no-op. Reply with the same one-line duplicate marker. Do not re-fetch, re-verify, or re-state the original reasoning — the user is signaling "still pending" or "didn't see your reply", not asking for a fresh evaluation. Cost of responding incorrectly: 30-90s of redundant fetch + grep. The shortcut to the right reply is literally: copy the prior turn's reply, prepend "(重复发送)" if helpful.
16c-bis. patch silently drops new line when old_string already exists in file (2026-06-04 verified — 16h): Distinct from 14a. The patch tool returns success: true with a correct-looking diff, but the new line is never actually added because the old_string already exists in the file. Symptom: after git commit, grep <new-slug> index.md returns 0 matches. Detection: always grep -c <new-slug> index.md after every patch to index.md, before running the sed corruption fix. Use multi-line old_string blocks (include Total pages header) for unique anchors. See 16h for full protocol.
16b-2-bis. Borderline v×c + 1-2 unique concepts → "peripheral 周边 note" action (2026-06-04 verified — 码上飞 case): When the article scores below 49 (so the full ingest path is rejected) but contains 1-2 unique concepts that are closely related to an existing entity, the right action is neither full reject nor full ingest but a peripheral 周边 note appended to the existing entity's "相关实体" or "启示" section. This is the third option in the save decision tree, sitting between save and reject.
When this applies (3 conditions all met):
v × c < 49 (e.g., v=7 × c=6 = 42 for a promotional article)Action sequence:
# 1. Identify the closest existing entity (the concept naturally extends)
ls entities/*kimi-work* entities/*vibe-working*
# 2. Append a small "周边注" or related concept section to the existing entity
# (NOT a full new entity, NOT a peripheral 周边 source commentary like 16c)
# 3. Cite the URL as the source (so future readers can find the original)
# Add a sentence: "概念 'Vibe Business' 由 [作者/产品] 在 [日期] 公开提出,[URL]"
Reply format: Same one-liner style as 16b-2 short-circuit, but with the 周边 note action explicit:
❌ 拒绝入库 (v×c=42 < 49) | 周边注已 append 到 entities/kimi-work-codex-vibe-working-paradigm-shift.md
- 概念 'Vibe Coding → Vibe Business' (码上飞 / Vibe Business 是商业节点) 已在 [[entities/kimi-work-codex-vibe-working-paradigm-shift|Kimi Work entity]] 周边章节登记
- commit: <hash>
When NOT to use this (3 negative cases):
review_stars: 1 (reference-only)Cost of getting this wrong in either direction:
Decision flow:
Score + extract article
├─ v×c ≥ 49 → standard ingest (new entity / merge / raw only per merge decision tree)
└─ v×c < 49
├─ 0 unique concepts OR all concepts are 营销/repost of existing
│ → full reject
├─ 1-2 unique concepts closely related to existing entity
│ → peripheral 周边 note (this pitfall) ✓
└─ 1-2 unique concepts completely novel (no existing entity)
→ thin stub entity with `review_stars: 1`
Concrete 2026-06-04 case: 码上飞 promotional article (v=7 × c=6 = 42) — too product-marketing for full ingest, but introduced the "Vibe Coding → Vibe Business" framework and the "5% App / 95% 生意" perspective. The 周边 note was almost added to entities/kimi-work-codex-vibe-working-paradigm-shift.md (which covers "Vibe Coding → Vibe Working") but I chose full-reject because the article was 70% product demo. Better path forward: the 周边 note option should have been applied, with the 1 unique concept (Vibe Business vs Vibe Working) called out explicitly. The decision flow now reflects this.
Synergy with the 16c Nth-translation pattern: Both involve "small contribution to existing entity", but differ in what's being added:
The 周边 note is smaller and more specific than 16c's source commentary.
16b-3. Same-author cross-article differentiation (2026-06-04 verified — Thariq × 3): When the same author publishes multiple articles on different topics (e.g., Thariq wrote: ① Dynamic Workflows ② Context Engineering ③ /init architecture), each is a separate standalone entity even though they share an author. The dedup signal here is topic, not author. The detection pattern:
grep -l "<distinctive-topic-keyword>" entities/*.md — if NO match, proceed with new entity.Inverse of same-author-wechat-merge-pattern.md: That pattern is for "same author + same topic + different article = merge". This pattern is for "same author + different topic = new entity". The merge-decision tree's "30-70% overlap" rule is the dividing line: < 30% topic overlap = new entity, regardless of author.
Real cost of getting this wrong: In this session, Thariq's "Context Engineering" article (56) and "Dynamic Workflows" article (merged earlier at 32da0cfe) and "/init architecture" article (56) were ingested as 3 separate entities with 3 separate cross-links. The user expects this granularity — Thariq writing about Dynamic Workflows is not the same as Thariq writing about Context Engineering, even though both are "Claude Code tips from Anthropic". Merging them would lose the topic-specific depth.
16b-bis. Content-equivalent dedup (different URL, same article body — 2026-06-04 verified): When the URL is new (not in raw/articles/*/source_url:) but the user submitted it before, OR a WeChat publisher re-summarized the same Anthropic engineering blog with a different URL, the source_url grep misses it. Detect via 3-step verify:
# 1. Fetch the new URL (only ~5s via playwright if WeChat)
NEW_BODY=$(node ~/wiki/scripts/playwright-profile/fetch-with-profile.js --text "<new-url>")
# 2. Pick a distinctive phrase from the new body (named person, specific feature, quoted text)
echo "$NEW_BODY" | grep -oE "(Thariq|Cat Wu|Dynamic Workflow|ultracode|harness for every task|动态工作流)" | head -3
# 3. If 2+ distinctive phrases match an existing entity's body, it's a paraphrase — reply with existing commit
grep -l "Thariq" entities/claude-code-dynamic-workflows-multi-agent-orchestration.md
When matched: reply with the existing commit and a one-liner showing 2-3 distinctive phrases that confirmed the match. Do not re-ingest even if URL differs. This is the 2nd-most-common dup case (after exact source_url match) and the skill must not rely on URL equality alone.
Case study (2026-06-04): User sent https://mp.weixin.qq.com/s/hxBkT-iJleQkaODzjWVC2A after I had already ingested https://mp.weixin.qq.com/s/1eSGt71P-PeaGszs2cikTw. Both are Chinese summaries of Anthropic's "A harness for every task" blog. Different publisher, different URL, but body contains "上周 Anthropic 在发布 Claude Opus 4.8" + "A harness for every task" + "Workflow" + "Thariq". A source_url grep on the new URL returned 0, but a body-phrase grep matched the existing entity 4-for-4. Detection cost: 1 fetch + 2 greps (~10s). Re-ingestion would have cost ~5 minutes.
16b-ter. Complementary coverage dedup (different URL, different body, same event — 2026-06-04 verified): When two 公众号 independently cover the same product launch event (e.g., Microsoft Build 2026), each with their own narrative and unique sections, the right action is NOT 16b-bis reject (the bodies are genuinely different), NOT sibling-concurrent dedup (the coverage angles are complementary, not duplicate), but a 2-source merge with appended sections for the unique content. Detection:
Case study (2026-06-04): User sent https://mp.weixin.qq.com/s/qeb0jxNQIUYPVZum0guvfg (量子位) after I had ingested https://mp.weixin.qq.com/s/H6dZibozzF8Q_j8pumvsJw (AI 前线) for the same Microsoft Build 2026 event. Both 公众号 covered the same 7 MAI models + Scout agent, but 量子位 had 4 unique sections the AI 前线 version did not:
The merge added 4 appendices, bringing the entity from 198 lines / 1 source to 285 lines / 2 sources. Decision rule: when the 2nd article's body has any substantial new section (not just reordered recap), it's complementary coverage, not paraphrase → 2-source merge with appended unique sections.
Quick check for 16b-ter (cost: ~5s):
# After fetching both bodies (existing raw + new fetch), find the longest run of text
# in the new body that is NOT present in the existing entity
diff <(echo "$EXISTING_BODY" | tr -d '[:space:]') \
<(echo "$NEW_BODY" | tr -d '[:space:]') \
| grep -c "^[<>]" # counts lines in diff
# > 50 lines of new content = 16b-ter (complementary coverage, not paraphrase)
# < 10 lines of new content = 16b-bis (paraphrase, reject)
16c. Nth-translation merge pattern (N ≥ 3 — 2026-06-04 verified, 玉澄 case): When the same source article (e.g., Anthropic Thariq/Sid's "A harness for every task") has been translated to Chinese 3+ times by different 公众号, each new translation adds only 1-2 unique bullets. The decision tree for the Nth translation:
Anti-pattern (what NOT to do for the 5th+ translation): re-listing the 6 模式 / 3 失败模式 / 8 Prompt in the new section, repeating the existing entity body 1-for-1, and not adding any "整合视角" closing note. This bloats the entity with redundant text that future readers have to scroll past.
Why this rule emerged: The 玉澄译本 (6th Chinese translation of the same Anthropic source) had 80% body overlap with the existing entity. The unique value was:
If I had re-listed the 6 模式 / 3 失败模式 / 8 Prompt from scratch, the entity would have grown from 302 lines to ~400 lines with no new information density. The correct move was a 33-line "玉澄 / 51CTO 译本补充" section with only the 5 unique bullets + 1 closing "整合视角" paragraph. Density per line of added content matters more than total line count.
16c-2. 7th+ translation: still requires evaluation, not auto-merge (2026-06-04 verified, 架构师 JiaGouX case): The Nth-translation pattern continues beyond 6 — the 7th Chinese translation of "A harness for every task" (架构师 JiaGouX) still had substantially unique content:
Rule of thumb: N is not the signal — unique framing + unique data points + unique actionable strategy is. The 7th translation had 3 unique bullets at the meta level (unifying framework + new-bottleneck + 7-question landing checklist) that justified the merge even though the body was 80% overlapping. Auto-rejecting the 7th+ translation because "we already have 6" is wrong — each translation can have its own authorial voice, and the meta-level synthesis is often where the 7th translation adds the most value.
When to actually stop merging (negative case): A 7th+ translation is worth rejecting only when it has zero unique content — i.e., it is a 1-for-1 paraphrase with no new framing, no new data, no new strategy. In that case, apply 16b-bis (content-equivalent dedup) and short-circuit. Otherwise, the 7th source + small append section is the right move.
16c-4. Authority-publisher Nth-source bonus (2026-06-05 verified — 机器之心 9th source, AGI Hunt 3rd source): When the Nth translation/transcription is from a high-authority publisher (e.g., 机器之心 = AI 主流媒体 / 新智元 / 量子位 / InfoQ 中文版 / The Paper / 36Kr), the "zero unique content → reject" bar is lowered. The publisher itself becomes durable value because:
When to apply the bonus:
Real 2026-06-04 cases that applied this bonus:
| Nth source | Publisher | Bonus applied because | Unique value kept |
|---|---|---|---|
| 9th | 机器之心 (dynamic-workflows entity) | AI 主流媒体 + 完整译本 | "Opus 4.8" 显式版本号 + 开篇 8 示例 prompt 完整保留 + 权威媒体背书 |
| 3rd | AGI Hunt / 尹 John (kimi-work entity) | 前网易 CTO + 实战第一人称 | 1224 项目商机挖掘漏斗 + 0.3% 全球数据 + 90% 自生成自指证据 + 可燃员工/自燃团队 比喻 + WebBridge vs 爬虫 |
Real 2026-06-05 cases that applied this bonus:
| Nth source | Publisher | Bonus applied because | Unique value kept |
|---|---|---|---|
| 9th | 机器之心 (dynamic-workflows entity) | AI 主流媒体 + 完整译本 | "Opus 4.8" 显式版本号 + 开篇 8 示例 prompt 完整保留 + 权威媒体背书 |
| 3rd | AGI Hunt / 尹 John (kimi-work entity) | 前网易 CTO + 实战第一人称 | 1224 项目商机挖掘漏斗 + 0.3% 全球数据 + 90% 自生成自指证据 + 可燃员工/自燃团队 比喻 + WebBridge vs 爬虫 |
Anti-pattern (don't apply the bonus):
Section length expectation when bonus applied: 30-60 lines. The section should:
Cost of NOT applying the bonus (false reject): The wiki loses a citable "最权威单一译本" reference. Future readers asking "which translation should I cite?" get no answer. The cost is small but durable — a missing reference in a knowledge base is harder to add later than to add now.
Cost of applying the bonus when not warranted (false accept): The entity grows a 30-60 line section with thin value. Future readers scroll past it. The cost is also small but durable — bloat in a knowledge base is hard to remove later without disrupting citations.
Decision rule:
Publisher tier?
├─ AI 主流媒体 / 权威科技媒体 + complete translation
│ → 16c-4 bonus applies: keep as Nth source even with thin unique value
├─ Mid-tier (新智元 / 量子位 / 36Kr / InfoQ 中文版)
│ → Standard 16c-2/16c-3 rules: keep only if substantive unique content
└─ Low-tier (营销号 / 卖课号 / 内容农场 / 二手转载)
→ 16b-bis reject: short-circuit
The closing note becomes more important at N=7+: Each 7th+ merge MUST end with a "整合视角" paragraph that:
Without that closing note, future readers cannot tell at a glance why the 7th translation was added and what it uniquely contributes.
16c-3. 2nd source merge = always distinct content section (2026-06-04 verified, Kimi Work case): When merging a 2nd source into an existing entity, do NOT just patch the frontmatter sources: array and call it done. The 2nd source's unique content (60-100% of its body) must be appended as a new section with a clear ## 第二来源补充 (or ## Nth source — <descriptive name>) heading. The first source's body stays untouched. The 2nd source's body gets selectively distilled to only the unique bullets — do not re-listing 80% body-overlap content from the first source. Detection: after merging, the new section should be 30-70% of the original article's useful bullets (not 100% — the overlap is the existing entity body). The 2026-06-04 Kimi Work 2nd source merge added 89 lines / 5 万行 92% 自主生成 + K2 一年路径 + 投研并行 + 赛博禅心 887 篇 + 模型公司论断 + 国内竞争格局 = ~6 unique chunks, each 10-20 lines. The unique chunks are the 2nd source's value — re-listing existing 1st source content is anti-pattern.
16g. 反序传播陷阱 — "转发"型译本易误判为 reject (2026-06-09 PilotDeck case):
症状: 12 天前已有同 underlying source 入库 (PilotDeck 2026-05-28 ASI启示录), 今日新 URL 来自不同公众号 (数据派THU 2026-06-09), Phase 1 倾向认为"二次传播"无新价值 → reject/skip。
错误根因: 把"翻译" (translation, 同 underlying source 独立译出) 和"转发" (repost + 加自身 demo) 混为一谈。转发型也有 5 大类独到价值:
正确处理 (12 维对照表法): 即便是"转发", 也要写完整 12 维对照表 (核心叙事/GitHub 链接/技术细节独占项/跨域 demo/横向对比/数据表格/受众/publish_time/媒体立场/章节展开/与已有 entity 章节呼应点/开发者 onboarding 价值) — 让未来 Nth source 章节 append 时有标准模板。
触发路径: 看到"同一 underlying 事件 + 时差 > 1 周 + 新 URL 公众号不是原翻译者 + 新 prose 结构 + 有具体 demo/链接" → 100% 走 merge 而非 reject。
鉴别方法: 问自己"新文章如果是当月首发 (无现有 entity), 评分多少? v×c ≥ 49?" — 如果是, 即便 12 天后, 也值得 merge (作为 Nth source)。中文文献独立性是 1:N 对多公众号。
2026-06-09 PilotDeck 实战数据:
MWj2lKQi8JdPu4qJDEFOYg (数据派THU 转发)pilotdeck-data派thu-2026.md 独立存档34250762成本分析:
反模式: ❌ "12 天前的同主题稿件 + 今日新 URL" → 误认为无新价值 reject。✅ 强制走"4 问判定 + 12 维对照表 + 写新章节" 流程。
16f. Cross-link-only merge (no body edit, 2026-06-04 verified, Darwin 2.0 case): When a new article is closely related to an existing entity (same author ecosystem, same product, same conference coverage) but the new article's content is substantially self-contained as a separate entity (not a duplicate or paraphrase), the action is NOT to merge into existing AND NOT to skip the cross-link — it is to create the new entity AND append a one-line cross-link to the existing entity's tail section. The 2026-06-04 case: KK大叔's Skill 互优化 article is from the same author ecosystem as Darwin 2.0 (花叔/KK大叔), references Darwin by name, and is the natural next experiment after Darwin 2.0 — but it's a distinct experiment (4 轮互优化 vs Darwin 自身迭代) with its own contribution. The merge mode was: create entities/hermes-agent-skill-crossover-optimization.md as new entity, AND patch entities/darwin-skill-2-huashu.md's "## 相关概念" section to add one line:
- [[entities/hermes-agent-skill-crossover-optimization|Hermes Agent Skill 互优化]] — KK大叔:Darwin × SkillEvolver 4 轮互优化闭环,验证清华论文核心结论**AI 不需要更强模型**
When to use 16f vs 16c:
Anti-pattern for 16f: editing the existing entity's body to add multi-paragraph commentary about the new article. The one-line cross-link in the existing entity's "相关概念" section is sufficient — anything more is bloat. The new entity stands on its own.
16g. Total pages bump in EVERY commit, not arithmetic (2026-06-04 verified over 5 consecutive commits): The skill's pitfall #2 already says "linter is always right, never use arithmetic". In a 5-URL batch within one session, the Total pages: header was bumped in every single commit (3540→3541→3542→3543→3544→3545), never with arithmetic. The pattern: after git add entity+raw+index.md+log.md but BEFORE git commit, run node scripts/wiki-lint.mjs 2>&1 | grep "tracked page", copy the actual count, patch the index.md header to that exact number, run sed -i '' 's/^|- /- /' index.md to fix the patch corruption, THEN commit. The lint + patch + sed adds 3 tool calls per commit but eliminates index drift entirely. The arithmetic pattern "+1 for entity, +1 for raw" is always wrong because concurrent cron siblings (e.g., wechat-inbox-pipeline) are also committing in parallel, and the actual count moves in unpredictable amounts. Trust the linter, not your head.
16h. patch tool silently drops new line on duplicate old_string match (2026-06-04 verified over 2 commits): The patch tool's "old_string not unique" failure mode has a second variant that complements 14a:
patch returns success: false because old_string matches multiple locations. The agent ignores the error and continues.patch returns success: true and the diff looks correct in the output, but the new line was already present in the file (e.g., from a prior failed commit, or from a sibling agent's parallel commit), so the patch "succeeds" without actually adding the new content. The Total pages header gets bumped (the only unique anchor) but the new entity line never lands.Symptom: After git commit, git log -1 shows the commit but grep "<new-entity-slug>" index.md returns 0 matches. The entity is committed but not in the wiki's index.
Detection protocol (cost: ~2 seconds, run after every patch to index.md):
# 1. Grep for the new entry
grep -c "<new-entity-slug>" /Users/jinguo/wiki/index.md
# Expected: 1 (entity line). If 0: patch silently dropped the new line.
# 2. If 0, re-patch with a more unique old_string
# (use a multi-line block including the new Total pages + a unique preceding line)
# The new `old_string` must contain BOTH the unique anchor AND the line to insert before.
Recovery (3-step):
index.md to see what's actually thereold_string that includes the Total pages header (unique within ~3 lines) and re-insert the new entity lineReal 2026-06-04 case: After committing gitlab-14pct-layoff-agent-platform-ai-2026q1, the index patch succeeded for the Total pages (3542→3543) but the new entity line was silently dropped. The next URL's patch added the new entity alongside the (already-applied) Total pages update, and a follow-up patch was needed to recover the GitLab entity entry. Cost of detecting early: 1 grep. Cost of detecting late: 1 extra tool round + an "amend" or "fix" commit that pollutes the git history.
Prevention (3 rules):
grep -c <new-slug> index.md after every patch to index.md — before running sed -i '' 's/^|- /- /' index.md (which masks the issue)old_string blocks that include the Total pages header — it changes every commit, so it's a stable unique anchorold_string pattern has been used in the last 3 commits, the index.md state may be different from what you remember — re-read the file before patching16i. 9-URL mixed-mode batch rhythm (2026-06-04 verified — 5 new + 1 merge + 1 reject + 1 cross-link + 1 short-circuit dup): A single-session batch with mixed ingest types requires a slightly different cadence than the 16d "all-new-entities" pattern. The verified rhythm from 2026-06-04:
URL 1 (新 entity) → fetch + score 64 + write files + patch index + lint + commit
URL 2 (新 entity) → fetch + score 81 + write files + patch index + lint + commit
URL 3 (新 entity) → fetch + score 90 + write files + patch index + lint + commit
URL 4 (新 entity) → fetch + score 90 + write files + patch index + lint + commit
URL 5 (新 entity) → fetch + score 56 (borderline) + write files + patch index + lint + commit
URL 6 (新 entity) → fetch + score 49 (borderline) + write files + patch index + lint + commit
URL 7 (merge 2nd source) → fetch + read existing entity + write new raw + patch entity frontmatter + append new section + patch existing entity's 相关概念 link + commit
URL 8 (rej 边界档) → fetch + score 35 < 49 + log only + commit (no entity, no raw, no index entry)
URL 9 (short-circuit dup) → grep source_url → match existing → 1-line reply, no fetch, no commit
Key rhythm observations:
Total pages bump is still required even on merge and reject (rejects: the new log entry might add a "tracked page" if it counts; merges: the new raw is a new tracked page).User feedback after such a session: The user reads each per-URL commit's reply (one line) and the git log themselves. They DO NOT want a session-end "rollup" listing all 9 URLs. The per-article reply IS the report. Do not synthesize a "summary" unless asked.
16j. Cross-link in BOTH directions on new entity creation (2026-06-04 verified, Kimi Work 2nd-source case): When a new entity is closely related to an existing one, the cross-link must appear in both the new entity's "与已有实体的关系" section AND the existing entity's "相关概念" section:
patch to add one line)The 16f rule covers the back-link. The forward-link is implicit in the new entity's structure (every new entity has a "## 与已有实体的关系" section), but the rule is: the new entity's "与已有实体的关系" must explicitly call out which specific existing entity the new content is a 2nd source / 互补 coverage for, not just generic wikilinks. Example from 2026-06-04:
## 与已有实体的关系
- [[kimi-work-codex-vibe-working-paradigm-shift|Kimi Work]] — 同为通用 Agent 产品,但走的是**本地桌面+WebBridge** 路线
- **TRAE SOLO Work 模式** = **云端+指令式** 路线(用户不写代码,AI 帮你做)
- **共同点**:都把"非程序员用 AI 完成真实工作"作为目标
The forward-link is a short paragraph (not a bullet list) that names the specific relationship axis (e.g., "本地桌面 vs 云端指令式"). This makes the relationship searchable and meaningful, not just a wikilink.
16j-bis. Bidirectional 5-column comparison table (2026-06-04 verified — Hermes ↔ GEPA case): When two related entities (created/merged at different times) need a structured, multi-dimensional comparison, the cross-link is enriched by a 5-column markdown table with the same rows in both directions. The pattern:
Entity A → Entity B: append a section "## 与 [Entity B] 的关联" with a markdown table:
| 维度 | Entity A | Entity B |
|---|---|---|
| X | A's value | B's value |
| Y | A's value | B's value |
| Z | A's value | B's value |
Plus 3-5 actionable bullet points ("GEPA 给本实验的 5 大可借鉴实践") and a "双向补强关系" + "结合路径建议" closing pair.
Entity B → Entity A: append a mirror section with the SAME table (so both directions show the same comparison), plus the symmetric "双向补强关系" and "结合路径" framing.
Why this works (vs. a single forward-link): The 5-column table makes the relationship scannable — a future reader can see the dimensions of difference at a glance, then dive into the actionable bullets. Pure prose cross-links force readers to read both entity bodies in full to extract the comparison structure. Tables reduce the cognitive cost of "are these two entities solving the same problem or different problems?" by ~10x.
When to use 5-column table vs. prose only:
comparisons/ (different skill, not this pitfall)Cost of doing this in 1 direction only (forward-link with table, but back-link prose only): Readers coming from Entity B's side see only "related to A" without the structured comparison. The wiki's value as a knowledge graph is asymmetric. Always mirror.
Real 2026-06-04 case (Hermes Agent Skill 互优化 ↔ GEPA optimize_anything): User asked to cross-link the two entities. I added a 5-row comparison table to both, plus 5 bullets of "GEPA 给本实验的 5 大可借鉴实践" + "双向补强关系" + "结合路径建议" closing paragraph. The table covered 优化对象 / 核心机制 / 评估方式 / 跨任务迁移 / Skill 自动化. Commit 579f92e4 (3 files changed, 88 insertions, 6 deletions). The closing paragraph synthesized "如果把 GEPA 的 Pareto 反思 + ASI 机制注入到 SkillEvolver 的 3 阶段流程中——这就是工业级 Skill 自进化的完整方案" which became the durable insight, not just the table.
Symmetric pattern check (post-write verification):
# 1. Verify the new entity has a forward-link to the related existing entity
grep "现有.*entity\|与已有实体的关系" entities/<new-slug>.md | head -3
# 2. Verify the existing entity has a back-link to the new entity
grep "<new-slug>" entities/<existing-slug>.md
# If 2 is 0: the back-link patch failed (per 16h), re-patch
Cost of getting this wrong: The new entity stands alone without context for its conceptual sibling. Future readers can't navigate between related entities. The wiki becomes a list of disconnected pages instead of a knowledge graph.
16j-ter. User-triggered cross-link of existing entities (2026-06-04 verified — Hermes ↔ GEPA 5-column table): When the user explicitly asks to "关联起来" / "链接" / "对照" / "cross-link" two already-ingested entities (not a new article, just a relation request between existing knowledge), the action is 5-column bidirectional table enrichment (16j-bis pattern) without any new file creation:
Trigger pattern (any one of these):
Action sequence (NO new file creation):
harnessagentmemory