How It Works: Staying in Sync
Knowledge that requires manual maintenance rots. This page covers the two machines that keep the fabric current without anyone remembering to: hooks (per-project, event-driven) and CI (per-push, assertion-driven).
The hook loop: drift → capture → compile
A project's docs change through normal development — a commit renames a feature, updates a README, deletes a doc that had claims pointing at it. The opt-in post-commit hook turns that drift into fabric updates without a session:
Four design choices make this safe to leave on:
- Drift-gated: capture recomputes sha256 for every tracked source file; unchanged files cost zero tokens. Only exit code 2 ("drift") proceeds.
- Self-skipping: commits touching only fabric-owned paths (
evidence/,registry/) never re-trigger — the hook can't create a capture/ingest loop feeding itself. - Detached: the hook's work runs in a background process;
git commitreturns immediately. Output lands in~/.cache/wiki-fabric-hook.log. - LLM-optional: with plain
wf hook install, drift is captured and recorded without claims (0 tokens) — the agent ingests interactively on its next session, when you're watching. Withwf hook install --extract-claims, drift also compiles immediately (1 call per changed file).
WIKI_SKIP_HOOK=1 git commit ... skips once; wf hook status shows state.
The hook cycle also runs (graphify-bridge.py --import → --enrich → --diff) after code commits when the integration is active, and (4) writes the pending-decision manifest (registry/pending-gate.md via wf gate --write-manifest) so any harness's session-start can read pending human decisions without running commands. The harness fabric itself also self-captures in CI — an opt-in workflow (fabric-refresh.yml, gated on the WIKI_FABRIC_REFRESH=1 repo variable) runs the same sha256-gated capture + lint on every push to main.
Other capture channels
The hook handles doc drift; two more channels feed the fabric, both sha256-anti-looped:
- Git history (
wf capture <slug> --git, and the post-merge hook which runs it automatically — merge is when the why exists): PR narratives, issue threads, and commits land askind: pr-recordevidence. PRs/issues capture the why for shipped work. Setup and scenarios live in Core Workflows §2. - Agent chats (
wf capture chat <slug>): claude/opencode/codex/gemini session stores, with thread frontmatter (session id, files touched) feeding the thread index. Chats capture the why for everything else: debugging dead ends, rejected approaches, environment quirks.
Then wf mine chats <slug> [--llm] distills transcripts into durable takeaways — patterns (e.g. "red-check every regression test by confirming it fails against the pre-fix code"), anti-patterns, workflows, constraints — with explicit transient filtering (CI states, PR counts, "as of today" snapshots are excluded as low-value). Output lands in evidence/insights/<project>/ for human review before anything enters the promotion pipeline.
CI: the fabric checks its own homework
The harness repo's CI runs five deterministic gates on every push — each one a claim about the system, each one verified:
| Gate | What it proves |
|---|---|
| Unit tests (860+) | script logic: parsing, routing, discovery, merge — plus the mechanical guards (layout single-truth, shim↔dispatch verb parity, exit contracts, statement-dump convention) |
| Smoke test | the CLI works end-to-end in a throwaway fabric |
| Behavior eval | the context manifest actually delivers the right knowledge (banned approaches named, correct alternatives present) — the P4 proof, zero-LLM |
| Stability eval | deterministic operations are byte-deterministic (20 context runs identical; catalog identical across rebuilds) |
| Fabric lint + OKF floor + okflint | the bundle conforms to its own contracts and the external validator agrees |
Two properties make this meaningful: every gate is 0-token (deterministic code, no model in the loop), and the behavior eval is the homepage's proof in CI form — "the manifest changed the agent's instructions" is an assertion, not a sentence.
The freshness contract, end to end
Putting the layers together — what guarantees a teammate's fabric reflects reality:
- A commit changes docs → the hook captures and compiles the drift (minutes, automatically).
wf bootstrapinstalls this hook automatically (that's what makes it a requirement, not a chore) — layer 1 of the guarantee depends on it; without it, drift waits for a manual capture. - A dependency updates its docs →
wf capturerecomputes hashes;SOURCE-DRIFTlint errors name exactly which claims are affected - A refactor renames code → graphify's AST diff flags code-referencing claims as stale
- Knowledge ages →
review_after/stale_aftergates drop it from manifests before it misleads - Structure drifts (new project, moved dir) →
wf vault --checkfails loudly;wf vaultself-heals - Upstream moves while every machine is dormant → the scheduled freshness cycle (
wf freshness, daily on the corpus CI whenWIKI_FABRIC_FRESHNESS=1): capture-git pulls new upstream PRs/issues (sha256-gated) + mechanically re-verifies claims — refreshed evidence lands in the team corpus and reaches every machine onsync pull(see the freshness cycle).
None of these require remembering to run something. The hooks run on commits; the lint runs in CI and on every command that could be affected; wf status surfaces whatever needs attention.
The scheduled upstream-freshness cycle
Hooks capture local commit drift on active machines. A team whose every machine is dormant for a week can still carry silently stale claims — so a cadence that depends on nobody's work habits can't be load-bearing. The freshness cycle closes that hole:
wf freshness [--dry-run] [projects...](0 tokens): per connected project —capture-git --since-state(new upstream PRs/issues → evidence/raw/, sha256-gated) thenreview --auto-reverify+ the newest-wins contradiction sweep (#175 — mechanical quote+hash re-verification). Exit 1 = drift captured; the human gate (wf gate) lists anything that needs a decision.- Scheduled on the corpus CI:
wf sync init/sync setupscaffold.github/workflows/freshness.yml(daily, opt-in per repo variableWIKI_FABRIC_FRESHNESS=1). The run commits refreshed evidence and pushes; teammates pick it up on the nextwf sync pull. - Evidence-plane only: the cycle never rewrites claims — stale/contested states are the representation; re-extraction stays opt-in per repo routing tier.
The scheduled chat-mining cycle
The twin hole on the intake side: chats flow in (capture hooks, capture chat), but distillation to candidates waited for someone to remember wf mine chats <slug> --propose — the promotion inbox starved on exactly the input the system captures hardest. The mining cadence closes it:
wf mine chats <slug> [--propose]heuristic mode (0 tokens): distill each session's durable takeaways + stage pattern/anti-pattern candidates — with the judged near-miss refinement (#173/G-J-gated) merging paraphrase takeaways into existing candidates.- Scheduled on the corpus CI:
sync init/setupscaffold.github/workflows/mining.yml(weekly, Monday, opt-in per repo variableWIKI_FABRIC_MINING=1). Heuristic ONLY — CI stays LLM-free;--llmdistillation stays interactive. The run commits staged candidates as inbox drift (the same plane-classified ritual as freshness). - Auto-promote (scoped, opt-in, #190): candidates land un-applied in
patterns/_inbox/; the mining pipeline itself never promotes. On a fabric that opts in (tuning.promotion.auto_apply: true), the human runswf promote-patterns --auto(never the CI): judged-confident candidates (calibrated judge, p ≥promotion.auto_threshold) apply, near-band escalates, everything recorded + reversible.wf gate/promote-patterns --listsurface them aftersync pull.