WHAT
Purpose and context
- Purpose: Periodic wiki health check — orphans, missing links, contradictions, stale (wiki→wiki AND code→wiki drift), index gaps; starts with a deterministic no-op gate (wiki-sync, 0 token).
- Trigger (when to invoke): After every 10 ingests, or when wiki stale/inconsistent, or session_start reports
[wiki-sync] code đã đổi N commit. - Non-goals: do not take sides when two pages contradict (flag for humans); do not guess an upgrade trigger for marker
shortcut:; do not move drafts on your own (that is/tidy, has a human approval gate); do not fix what isn't broken when the wiki is already current.
Mental model
wiki-sync --check (0 current · 3 drift · 2 chưa neo) → [hồ sơ cờ ⚑: events → scratch-log → provenance-log] → [docs-impact-plan + phân loại drift 4 nhánh] → quét nội-wiki (orphan · link · contradiction · stale · index · empty · Origin) → pulse báo-cáo-không-chặn (shortcut · unknown · skill-health · usage · tidy · claim-receipts · decision-liveness) → log → chốt neo.
Input and output contract
| Field | Required? | Meaning | |
|---|---|---|---|
| In | llmwiki/wiki/ + wiki-sync anchor |
yes | anchor missing → exit 2, complete fully then set the anchor |
| In | harness/scripts/* (or the global version ~/.claude/harness/harness/scripts/) |
yes | downstream has no harness/ in the repo, use the global version |
| In | fdk/tools/skill-usage.py |
no | framework repo only; downstream skips that step |
| Out | in-place fixes: missing links, index gaps; stale pages fixed surgically | based on the result | trace back to a specific code change |
| Out | list of flags (contradiction, orphan, empty, missing Origin, pulse) | yes | waiting for human decision |
| Out | line ## YYYY-MM-DD — lint in llmwiki/wiki/log.md + new anchor |
yes | even when no-op |
| Out | draft output report | yes (unless 0 artifacts) | Delivery section |
Rules and capabilities
- RULE-01 (MUST): Fix automatically: missing links (step 2), index gaps (step 5).
- RULE-02 (MUST): Flag, not resolve: contradictions, orphans, empty pages — need human decision.
- RULE-03 (MUST): Surgical update (distill openwiki 060726): fixing a stale page = replace exactly the wrong sentence, do NOT rewrite the page that is still correct; prefer fixing 1 sentence over adding 1 paragraph.
- RULE-04 (MUST): Soft diff budget: <5 code files changed → fix at most 1–2 wiki pages; if you find you need to fix >3 pages → stop and ask yourself why before editing broadly.
- RULE-05 (MUST): No formatting-only edits: do not reformat tables, do not normalize blank lines/wording when the surrounding content is not wrong — noisy diffs are debt for the reviewer.
- RULE-06 (MUST): Canonical home: one main owning page per concept; other pages only mention it briefly +
[[wikilink]], do not duplicate the explanation. - RULE-07 (MUST): Valid no-op: "wiki is already current, change nothing" is a successful lint result — log it and stop, don't change things just to have something to do.
- Capabilities: read wiki + local metrics; run deterministic 0-token scripts; write wiki pages (surgical), index, log, sync anchors.
Failure boundaries
- Exit 0 and the goal is "does the wiki match the code?" → stop early, succeeded (valid no-op).
- A page suspected stale but not traceable to any code change → DO NOT touch.
- Need to edit >3 pages for <5 changed code files → stop and self-question before broad edits (clarify).
- Contradiction → flag for a human, do not pick a winner yourself.
- Why dossier empty → write "thin dossier, need to open the diff" instead of staying silent.
HOW
Main workflow
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W01 | deterministic | neo wiki-sync | Step 0: wiki-sync.py --check |
exit 0 / 3 / 2 | 0 + goal is code-match → stop; 3 → B01; 2 → B02 |
| W02 | deterministic | code file "⇐" | Step 0b + 8f: flag dossiers ⚑ (events, scratch-log, provenance-log) | phiên · ts · why |
why empty → write "thin dossier" |
| W03 | deterministic | wiki | Steps 1–7: orphans, missing links, contradictions, stale, index, empty, Origin | fix 2 and 5, flag the rest | — |
| W04 | deterministic | repo | Steps 8–8f: non-blocking report pulse | flag list | script/path not present → skip that step |
| W05 | effect | results | Step 9: append log | log line | — |
| W06 | effect | wiki reviewed | Step 10: wiki-sync.py --mark-synced |
new anchor | content unchanged → do not write |
| W07 | effect | results | Output report draft (Delivery section) | draft + index + log | 0 artifacts → skip |
Step-by-step details (source of truth for W01–W07):
- Code-drift gate (0 token) —
RUN: python3 harness/scripts/wiki-sync.py --check(if downstream has no harness/ in the repo, use the global version:python3 ~/.claude/harness/harness/scripts/wiki-sync.py --check --root .).
0b. Dossier for each flag ⚑ (0 token — 2-step secondary memory JOIN, wired 2026-07-18): for EACH code file that step 0 reports "⇐" (the page is suspected stale):
- Step 1 — WHICH session touched it (thick store, file-level):
RUN: grep "<tên-file>" harness/metrics/events.jsonl | tail -3→ takesession+ts. - Step 2 — WHY (the why of that session):
RUN: grep "<session-id-8-ký-tự>" harness/metrics/scratch-log.jsonl | tail -2. Attachphiên · ts · whyto the flag when reporting to the user — a flag-with-dossier decides in a few seconds (reading the why is enough to know whether the change invalidates the claim), a bare flag must dig through the diff. Empty why/auto-cast-from-commit → note "thin dossier, need to open the diff" instead of staying silent. (Real limitation: scratch-log has 1 why/session; events are thick but only have the path — the richness of the why depends on whether the session was written by hand.) - Exit 0 (
current): code unchanged since the anchor → if the purpose of this lint pass is "does the wiki match the code", then stop here, answer "wiki is current" — a no-op is a good result, don't invent work. If you still want to scan intra-wiki health (orphan/index/origin), continue to step 1. - Exit 3 (
drift): the suspected-stale pages have been flaggedcode-driftinstale.jsonwith the code files that caused it. Build a docs-impact-plan BEFORE editing: each page you intend to edit must trace back to a specific code change (code đổi → trang → sửa gì → vì sao); a page that can't be traced to any change must NOT be touched.- Classify the drift before editing (4-branch rubric, borrowing the temporal-invalidation idea from RedPlanetHQ/core, github.com/RedPlanetHQ/core
prompts/statements.ts): how do the old claim and the new reality relate?- contradiction — the two claims cannot both be true (e.g. "API uses key X" when the key has been renamed) → the old claim is WRONG, overwrite as usual.
- superseding — the old claim WAS true, now no longer in effect because of an architecture change (e.g. engine swap/refactor) → don't silently overwrite: add 2 fields to the page's frontmatter —
invalid_at: YYYY-MM-DD+invalidated_by: <commit-sha hoặc file gây invalidate>— then update the content. The page still keeps a short line saying until when the old claim was true (don't erase history, just stamp it). - progression — both are true, different stages (e.g. "designing X" → "shipped X") → keep both, do NOT invalidate, just append the new claim.
- equivalence — same meaning different wording (due to ingesting twice) → treat as a duplicate, merge into 1 sentence, not real drift.
- Field
invalid_at/invalidated_byis optional, R9 (okf_frontmatter) doesn't block unknown keys — safe to add without editing the validator.
- Classify the drift before editing (4-branch rubric, borrowing the temporal-invalidation idea from RedPlanetHQ/core, github.com/RedPlanetHQ/core
- Exit 2 (no anchor yet / anchor no longer in effect): complete the full lint then set the anchor at step 10.
-
Orphans —
RUN: grep -rL "wiki/" --include="*.md" llmwiki/wiki/concepts/ llmwiki/wiki/entities/→ files not referenced anywhere. Flag each. -
Missing links — scan pages for entity/concept names that exist as wiki files but not written as
[[wikilinks]]. Fix in place. -
Contradictions — compare claims about the same entity across ≤2 pages at a time. Flag pairs (file:line vs file:line). Do NOT pick a winner — flag for human review.
-
Stale claims —
RUN: grep -rl "raw/" --include="*.md" llmwiki/wiki/→ pages referencing raw/. Flag each. -
Index gaps —
RUN: comm -23 <(find llmwiki/wiki -name "*.md" | sort) <(grep -o "llmwiki/wiki/[^)]*" llmwiki/wiki/index.md | sort)→ files missing from index. Add rows. -
Empty pages —
RUN: for f in llmwiki/wiki/**/*.md; do [ $(wc -l < "$f") -lt 5 ] && echo "$f"; done→ flag for deletion or content. -
Missing Origin —
RUN: grep -rL "## Origin" llmwiki/wiki/concepts/ llmwiki/wiki/entities/→ flag each as incomplete. -
Markers missing trigger —
RUN: grep -rn "shortcut:" --include="*.py" --include="*.js" --include="*.ts" --include="*.sh" . | grep -v ",";→ each intentional markershortcut:must have the format<trần hiện tại>, <trigger nâng cấp>(comma separating the two clauses). Lines that slip through this grep = markers missing an upgrade trigger (debt with no deadline → easily becomes "never"). Flag each line for the writer to add a trigger; do NOT guess the trigger yourself.
8c. Unknown debt (fill-first, awaiting verify — deterministic, 0 tokens) — RUN: python3 harness/scripts/unknown-ledger.py --list → print the number of open unknowns + the oldest one + which SPEC has the most debt. These are the defaults the model fills in "to keep things running, figure it out later" ((default, find-out-later)). REPORT, DO NOT BLOCK — fill-first intentionally does not block; blocking here betrays the mechanism. Purpose: debt surfaces so it doesn't sink, the reviewer decides when to pay it (see [[150726-unknown-ledger]]).
8b. Skill-health (skill shape, deterministic — 0 tokens) — RUN: python3 harness/scripts/skill-health.py (repo has skills/) → print total model-invoked context load + which skills have flags: desc (description too long) · sprawl (SKILL.md too long without progressive disclosure) · negation (high ratio of prohibitive sentences — should switch to affirmative sentences) · weak-criterion (block ### Task missing a checkable condition at the end) · near-duplicate description pairs. Original criteria in [[skill-craft]]. Report, DO NOT BLOCK — quality, not safety. Flag for the writer to consider, don't fix in bulk yourself.
8b. Skill-usage pulse (0 tokens, framework repos only) — RUN: python3 fdk/tools/skill-usage.py --weekly --no-html → weekly frequency table + dead skills (idle ≥4 weeks). This is the feed for the gauge "which skills are worth keeping" (wired 2026-07-18, grower loop): dropping/dead usage is EVIDENCE for the keep-or-cut decision, not gut feeling. Downstream without fdk/tools → skip this step.
8c. Docs-sprawl pulse (0 tokens) — RUN: python3 harness/scripts/tidy.py check → exit 3 when root-tier drafts > 10 → tell the user, suggest running /tidy (apply moves into archive/ + reindex — has a human review gate, the tool does not move things itself on lint). Draft/html is an ephemeral RENDER; letting it swell buries the essence under junk — the lifecycle must be swept on a rhythm (wired 2026-07-18).
8d. Claim-receipts on ACTIVE drafts (0 tokens) — for each KEEP/TREO draft (don't scan things about to be archived): RUN: python3 harness/scripts/claim-receipts.py --check <draft> → extract every CITED text file/path, verify it still resolves on disk. Unresolved = deterministic evidence content-outdated (points to something dead/renamed) — flag with a list of dead refs, human decides fix-or-archive. Advisory (adapter claim-receipts.config.yaml verified:false) — non-blocking, inspect only (wired 2026-07-18, 7th unwired case).
8e. Decision-anchoring liveness (0 token, deterministic) — RUN: python3 harness/scripts/decision-liveness.py check (if downstream lacks harness/ in the repo, use the global version: python3 ~/.claude/harness/harness/scripts/decision-liveness.py check; if the repo lacks harness/mechanisms.yaml → skip this step). Print every entry with anchor_symbol/live_probe with label LIVE/STALE/ORPHAN/UNAVAILABLE — the reviewer distinguishes ORPHAN (needs anchor fix or decision deletion via status: retired, NOT physical deletion) from STALE (needs re-reading, may still be correct) by label alone, without re-reading all content (SC-005). REPORT, DO NOT BLOCK — matches the non-blocking pattern of draft-age/unknown-ledger (step 8c).
8f. Provenance-log lookup (0 token, deterministic) — for each file/decision that step 0b is examining for "why it changed": RUN: grep "<tên-file-hoặc-id>" harness/metrics/provenance-log.jsonl | tail -5 (downstream uses the global version similarly to step 0). Each matching line carries writer_id/topic/ts_utc (explicit UTC)/git_sha — the SECOND "why" source beside events.jsonl/scratch-log.jsonl in step 0b, an important difference: git-tracked, travels along when cloned to another machine (the other 2 sources are either gitignored-local or not automatically comprehensive — see [[log-model]]). Use as a supplementary CLUE, not a replacement for step 0b. REPORT, DO NOT BLOCK.
-
Append to
llmwiki/wiki/log.md:## YYYY-MM-DD — lintwith issues found/fixed vs flagged. -
Finalize anchor —
RUN: python3 harness/scripts/wiki-sync.py --mark-synced(or the global version as in step 0). Write only when wiki content actually changes (content-hash); auto-clear the flagcode-driftalready reviewed. The feedback loop must close: no anchor finalized = next check reports false drift.
Branches
| ID | Kind | Guard | Behavior | Skip / failure | Rejoin |
|---|---|---|---|---|---|
| B01 | conditional_required | step 0 exit 3 (drift) |
docs-impact-plan + classification contradiction / superseding / progression / equivalence before editing | page not traceable → do not touch | W03 |
| B02 | conditional_required | step 0 exit 2 (no anchor yet / anchor no longer valid) | complete lint fully then finalize anchor at step 10 | — | W03 |
| B03 | user_optional | step 0 exit 0 but still want to scan in-wiki health | proceed to step 1 | don't want to → stop "wiki is current" | W03 |
| B04 | capability_optional | downstream has no harness/ or fdk/tools in the repo |
use the global version ~/.claude/harness/...; no fdk/tools or harness/mechanisms.yaml → skip that step |
— | W04 |
Validation and stopping
The wiki-sync exit code decides the entry point (deterministic); pulses 8–8f only report. After lint, lock the anchor so the next check does not report false drift. Stopping early is valid when exit is 0.
Examples
- Positive: session_start reports
[wiki-sync] code đã đổi 3 commit→ step 0 exits 3, flag 1 page ⇐harness/scripts/tidy.py→ why record shows draft threshold change → classify as superseding → addinvalid_at+invalidated_by, edit 1 sentence → log +--mark-synced. - Boundary/failure: step 0 exits 0 and the user only asks "does the wiki match the code" → stop, answer "wiki is current", write log, do not edit any page.
Delivery — Output Report
After all main skill tasks complete, write a propose draft to the wiki.
Steps
1. Build the filename:
- Format:
DDMMYY-<ten>.md DDMMYY= today (e.g.,020626for 2 June 2026)<ten>= 2–4 kebab-case words summarising what was done (e.g.,landing-page-coteccons,brand-kit-fintech,ingest-auth-spec)
2. Write llmwiki/wiki/sources/draft/DDMMYY-<ten>.md:
---
type: draft
title: "DDMMYY-<ten>"
status: proposed
tags: [<skill-name>, output-report]
timestamp: YYYY-MM-DD
---
# DDMMYY-<ten>
**Type:** draft
**Status:** proposed
**Tags:** <skill-name>, output-report
**Proposed:** YYYY-MM-DD
## What
<One sentence — what this skill invocation produced or decided>
## Output
<Key artefacts, files created/modified, or decisions made>
## Files
| File | Action |
|------|--------|
| `path/to/file` | created / modified |
## Notes
- Invoked via: `/<skill-name>` skill
## Origin
- **Draft:** `wiki/sources/draft/DDMMYY-<ten>.md`
- **Commit:** _(filled by verify-before-commit)_
- **Date promoted:** _(filled by verify-before-commit)_
3. Update wiki index & log:
llmwiki/wiki/index.md— append one row:| [DDMMYY-<ten>](sources/draft/DDMMYY-<ten>.md) | draft | YYYY-MM-DD |llmwiki/wiki/log.md— append:## YYYY-MM-DD — <skill-name> — <ten>
Skip only when the skill produces zero artefacts and zero decisions (e.g., a pure display mode like
/caveman-stats).