@nurix/etna 0.18.1 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/data/harness-index.json +2 -6
  2. package/data/harnesses/code/CLAUDE.md +1 -1
  3. package/data/harnesses/code/harness.json +3 -3
  4. package/data/harnesses/code/rules/nomenclature.md +11 -0
  5. package/data/harnesses/code/skills/brainstorm/harvest.md +1 -1
  6. package/data/harnesses/code/skills/documentation/SKILL.md +2 -0
  7. package/data/harnesses/code/skills/documentation/references/style-guide.md +23 -0
  8. package/data/harnesses/code/skills/documentation/references/templates.md +1 -1
  9. package/data/harnesses/discover/CLAUDE.md +25 -10
  10. package/data/harnesses/discover/harness.json +2 -2
  11. package/data/harnesses/discover/rules/cartography-coverage.md +94 -0
  12. package/data/harnesses/discover/rules/cartography-flows.md +99 -0
  13. package/data/harnesses/discover/rules/cartography-ia.md +88 -0
  14. package/data/harnesses/discover/rules/cartography.md +109 -0
  15. package/data/harnesses/discover/rules/evaluation.md +3 -1
  16. package/data/harnesses/discover/rules/ingestion.md +1 -1
  17. package/data/harnesses/discover/rules/self-correction.md +35 -23
  18. package/data/harnesses/hr/harness.json +1 -1
  19. package/data/harnesses/kb-agi/harness.json +1 -1
  20. package/package.json +1 -1
  21. package/data/harnesses/code-fable/.prettierignore +0 -12
  22. package/data/harnesses/code-fable/.prettierrc.json +0 -3
  23. package/data/harnesses/code-fable/.vscode/settings.json +0 -10
  24. package/data/harnesses/code-fable/CLAUDE.md +0 -124
  25. package/data/harnesses/code-fable/agents/brainstorm.md +0 -35
  26. package/data/harnesses/code-fable/agents/change-review.md +0 -251
  27. package/data/harnesses/code-fable/agents/harness-manager.md +0 -181
  28. package/data/harnesses/code-fable/agents/maintenance.md +0 -122
  29. package/data/harnesses/code-fable/agents/performance-review.md +0 -223
  30. package/data/harnesses/code-fable/agents/spec-mode.md +0 -56
  31. package/data/harnesses/code-fable/harness.json +0 -12
  32. package/data/harnesses/code-fable/hooks/hooks.json +0 -27
  33. package/data/harnesses/code-fable/hooks/lib/redact.mjs +0 -35
  34. package/data/harnesses/code-fable/hooks/prompt-recall.mjs +0 -155
  35. package/data/harnesses/code-fable/hooks/session-reflect.mjs +0 -285
  36. package/data/harnesses/code-fable/rules/general.md +0 -83
  37. package/data/harnesses/code-fable/rules/nomenclature.md +0 -30
  38. package/data/harnesses/code-fable/rules/typescript.md +0 -19
  39. package/data/harnesses/code-fable/skills/boundary/SKILL.md +0 -49
  40. package/data/harnesses/code-fable/skills/boundary/references/boundary-taxonomy.md +0 -796
  41. package/data/harnesses/code-fable/skills/brainstorm/README.md +0 -136
  42. package/data/harnesses/code-fable/skills/brainstorm/SKILL.md +0 -135
  43. package/data/harnesses/code-fable/skills/brainstorm/harvest.md +0 -26
  44. package/data/harnesses/code-fable/skills/brainstorm/templates/backlog.md +0 -39
  45. package/data/harnesses/code-fable/skills/brainstorm/templates/build.md +0 -37
  46. package/data/harnesses/code-fable/skills/brainstorm/templates/decisions.md +0 -29
  47. package/data/harnesses/code-fable/skills/brainstorm/templates/doctrine.md +0 -31
  48. package/data/harnesses/code-fable/skills/brainstorm/templates/domain-model.md +0 -25
  49. package/data/harnesses/code-fable/skills/brainstorm/templates/glossary.md +0 -20
  50. package/data/harnesses/code-fable/skills/brainstorm/templates/milestones.md +0 -43
  51. package/data/harnesses/code-fable/skills/brainstorm/templates/non-goals.md +0 -17
  52. package/data/harnesses/code-fable/skills/brainstorm/templates/plan-doc.md +0 -61
  53. package/data/harnesses/code-fable/skills/brainstorm/templates/questions.md +0 -36
  54. package/data/harnesses/code-fable/skills/brainstorm/templates/references.md +0 -28
  55. package/data/harnesses/code-fable/skills/brainstorm/templates/rejected.md +0 -29
  56. package/data/harnesses/code-fable/skills/brainstorm/templates/state.md +0 -1
  57. package/data/harnesses/code-fable/skills/brainstorm/templates/tech.md +0 -23
  58. package/data/harnesses/code-fable/skills/brainstorm/templates/thesis.md +0 -21
  59. package/data/harnesses/code-fable/skills/distiller/SKILL.md +0 -90
  60. package/data/harnesses/code-fable/skills/distiller/references/docs-mode.md +0 -299
  61. package/data/harnesses/code-fable/skills/distiller/references/method.md +0 -94
  62. package/data/harnesses/code-fable/skills/documentation/SKILL.md +0 -128
  63. package/data/harnesses/code-fable/skills/documentation/references/examples.md +0 -162
  64. package/data/harnesses/code-fable/skills/documentation/references/style-guide.md +0 -285
  65. package/data/harnesses/code-fable/skills/documentation/references/templates.md +0 -857
  66. package/data/harnesses/code-fable/skills/workflow-mapper/SKILL.md +0 -451
  67. package/data/harnesses/code-fable/skills/workflow-mapper/references/patterns.md +0 -214
@@ -1,36 +0,0 @@
1
- # Open Questions
2
-
3
- This file is the **single convergence currency** of the session. Every unresolved design call lives here and only here — whether the user raised it, the sync agent found it, or a probe surfaced it. Plan docs and pillars never host their own questions; they link back here.
4
-
5
- The session is **coherent** exactly when this file is empty after a fresh sync. That makes hygiene load-bearing:
6
-
7
- - Every entry must be **closeable** — the `Closes when.` field says what resolution looks like. Vague musings are not questions; distill them until they're closeable or leave them in the conversation.
8
- - Only **design-calls** belong here — items whose resolution changes the idea. Editorial bookkeeping is the sync agent's job, applied directly and noted in its report.
9
- - A resolved question is **deleted** (its decision lives in `decisions.md`). A parked question is deleted too, with an explicit entry in `backlog.md` or `non-goals.md`. No strikethrough, no "(resolved)" markers.
10
-
11
- Each question is **self-contained**: full text in the user's framing, readable without opening another doc. Group under `## Category` headers when the list grows past ~10.
12
-
13
- IDs are `QN`, stable while open, never reused after deletion.
14
-
15
- ---
16
-
17
- ## Q1 — [Title in the user's words]
18
-
19
- **Question.** [Full prose — paraphrase what the user said, or state the contradiction/gap the sync found. Repeat context; a future reader should need nothing else.]
20
-
21
- **Raised by.** user | sync | probe
22
-
23
- **Closes when.** [What resolution looks like — a pick between options, a confirmed fact, an accepted risk.]
24
-
25
- **Options.** _(optional — only when named by the user or entering converge)_
26
-
27
- - A. [one line]
28
- - B. [one line]
29
-
30
- **Lean.** _(optional — the user's lean, never the skill's)_
31
-
32
- **Claude's take.** _(opt-in only — write only when the user asks for a recommendation on this question; delete if it goes stale)_
33
-
34
- **Resolution lands in.** _(optional)_ [pillar or doc + section]
35
-
36
- **Blocks.** _(optional)_ [milestone or other Q-numbers]
@@ -1,28 +0,0 @@
1
- # References
2
-
3
- This file collects **external prior art** — repos, docs, papers, talks — with one-line pointers to what we use each for. Reference implementations beat invented solutions: if someone has already solved the problem you're about to design, the right move is to link it here and use it, not re-derive.
4
-
5
- References are not bookmarks. Every entry should answer the question "what specific pattern, idea, or implementation do we lift from this?" A reference without that grounding decays into noise. If you can't write the one-line "what we use it for", you probably don't need the entry.
6
-
7
- References to **source materials** copied into `./plan/sources/` during the session also live here — point at the local copy, not the original location. This keeps the brainstorm self-contained.
8
-
9
- When a referenced thing changes (renames, moves, deprecation), **update or remove the entry**. A stale reference that no longer points at what the description claims is worse than no reference — it teaches the reader to distrust the file.
10
-
11
- ---
12
-
13
- ## Internal repos / paths
14
-
15
- - `~/path/to/repo` — [what pattern we borrow]
16
- - `~/path/to/another` — [what pattern we borrow]
17
-
18
- ## Source materials (this session)
19
-
20
- - `./plan/sources/[file]` — [original location, why it's relevant]
21
-
22
- ## External docs
23
-
24
- - `https://example.com/foo` — [what we use it for]
25
-
26
- ## Papers / talks
27
-
28
- - [Title] — [link or citation] — [the specific idea we're applying]
@@ -1,29 +0,0 @@
1
- # Rejected alternatives
2
-
3
- This file captures **paths that were considered and rejected**, with the reason for rejection. Its purpose is to prevent re-litigation: when someone proposes the same alternative six weeks later, this file answers "we already looked at that; here's why we didn't go that way."
4
-
5
- Each entry needs three load-bearing parts: **Considered** (what was proposed, in concrete terms), **Rejected because** (the specific reason — "didn't fit" is useless to future-you; name the constraint, the cost, the risk), and **Re-evaluate if** (what would need to change for the rejected path to become viable again). The re-evaluate field stops the rejection from looking permanent when it isn't — most rejections are scope-conditional, not absolute.
6
-
7
- This is one of the four places inside the brainstorm folder where past tense and "we considered X" framing is permitted (the others: `decisions.md`, `references.md`, git history). The whole point of this file is to capture the path-not-taken so future architects don't have to re-discover it.
8
-
9
- If circumstances change and a rejected path becomes attractive again, **do not silently revive it**. Add a new question to `questions.md` that references the rejection here, and resolve it explicitly. The new decision can override the rejection — but it should override, not pretend the rejection never happened.
10
-
11
- ---
12
-
13
- ## R1 — [Rejected option]
14
-
15
- **Considered.** [What was proposed.]
16
-
17
- **Rejected because.** [The reason. Be specific.]
18
-
19
- **Re-evaluate if.** [What would change to make this viable.]
20
-
21
- **Came up during.** [Q-number or D-number for context.]
22
-
23
- ---
24
-
25
- ## R2 — [Rejected option]
26
-
27
- (same format)
28
-
29
- ---
@@ -1 +0,0 @@
1
- STATE: open MODE: dump COMPLEXITY: medium SESSION STARTED: YYYY-MM-DD SESSION CLOSED: CURRENT TOPIC: (one line — updated on every topic shift) LAST SYNC: never SYNC IN FLIGHT: no
@@ -1,23 +0,0 @@
1
- # Tech
2
-
3
- This file holds the **technology choices** for the idea. It is seeded at bootstrap from the harness's opinionated stack (e.g. `.claude/rules/general.md` recommended-packages table) when one exists — those rows arrive as defaults, not decisions.
4
-
5
- Its job afterward is to be a **lens**: when a conversation move conflicts with a row here, the sync (or the skill, in the moment) surfaces it — _"you just proposed local component stores; the stack says Redux Toolkit — deviating or steering back?"_ A confirmed deviation gets recorded below with its why; an unconfirmed conflict becomes an open question.
6
-
7
- ## Stack
8
-
9
- | Layer | Choice | Source |
10
- | ------------ | ------------------ | --------------------------- |
11
- | [e.g. UI] | [react ^19] | harness default |
12
- | [e.g. state] | [@reduxjs/toolkit] | harness default |
13
- | [layer] | [choice] | decided this session — D[n] |
14
-
15
- ## Deviations
16
-
17
- Each deviation from a harness default carries a why:
18
-
19
- ### [Choice] instead of [default]
20
-
21
- **Why.** [Constraint, requirement, or incident that justifies the deviation.]
22
-
23
- **Scope.** [Whole project, or only a named module/surface.]
@@ -1,21 +0,0 @@
1
- # Thesis
2
-
3
- This file is the **running narrative of what we are designing** — the single place a cold reader (or the next session) goes to understand the idea as it currently stands. It is rewritten freely as the idea evolves; it always describes the _current_ shape, never the journey (writing posture per `build.md`'s mode).
4
-
5
- Keep it prose-first. If a section outgrows this file at high complexity, it may move to a numbered deep-dive doc (`01-foo.md`) at the session root with a one-line pointer left here.
6
-
7
- ## What this is
8
-
9
- [2–5 paragraphs. What the thing is, who it's for, what it does. Distilled from the basis at `start` and kept current by the loop.]
10
-
11
- ## Aspect map
12
-
13
- The thinking dimensions for _this_ idea — proposed by the skill from the basis, edited by the user at `start`. The sync agent audits coverage against this list: an aspect with no content anywhere becomes an open question.
14
-
15
- - [ ] [aspect — e.g. users & jobs]
16
- - [ ] [aspect — e.g. domain model]
17
- - [ ] [aspect — e.g. architecture]
18
- - [ ] [aspect — e.g. tech]
19
- - [ ] [aspect — e.g. risks]
20
-
21
- Check an aspect only when it has real content in the pillars, not when it has merely been mentioned.
@@ -1,90 +0,0 @@
1
- ---
2
- name: distiller
3
- description: The one place captured session lessons are distilled into colder, higher-signal knowledge. One skill, two modes — `facts` (memory inbox → atomic provenance-linked fact-cards in `docs/memory/facts/`) and `docs` (fact-cards → proposed, human-approved doc deltas). Both modes read one shared algorithm spec, `references/method.md` (surprisal filter, EDU fact-cards, reconciliation, bi-temporal supersession — all stolen, all cited), so they can never drift apart. Consumes the inbox entries it digests (the inbox is a queue, not an audit log — git history and commit/session provenance are the audit trail); NEVER auto-runs (invoked by the user, or by accepting the maintenance agent's threshold proposal). Triggers on "distill the inbox", "distill facts", "run the distiller", "distill into fact-cards", "facts from the inbox", "distill docs", "draft doc deltas", "distiller docs", "/distiller".
4
- ---
5
-
6
- # Distiller Skill
7
-
8
- The one place a repo's **captured session lessons** are distilled into colder, higher-signal knowledge. The memory inbox (`docs/memory/inbox/{actor}.md`) is a per-actor queue of lessons the `session-reflect` hook extracted at session end; the distiller reconciles the _surprising_ part of it into atomic, self-contained, provenance-linked **fact-cards** (`docs/memory/facts/`) and consumes the entries it digests. Volume falls, signal rises — one hop down the distillation gradient.
9
-
10
- The methods this skill runs are **stolen, not invented** — surprisal-based retention (Nemori), non-lossy EDU fact-cards (EMem), per-fact ADD/UPDATE/ SUPERSEDE/NOOP reconciliation (Mem0), and bi-temporal supersession (Zep/ Graphiti). They live in exactly one place:
11
-
12
- > **Read [`references/method.md`](references/method.md) first, in every mode.** It carries the four mechanisms verbatim-grade (with their arXiv ids — provenance matters here of all places), the fact-card format, and the inbox-consumption protocol. This SKILL.md only orchestrates those mechanisms over the inbox.
13
-
14
- The reason the two modes are one skill: **both read one algorithm root.** Two components would mean two copies of the surprisal/reconciliation/supersession model that drift apart. One skill, one spec, zero drift.
15
-
16
- ---
17
-
18
- ## Picking a mode
19
-
20
- | You want to… | Mode | Ships |
21
- | --- | --- | --- |
22
- | Distill new inbox entries into fact-cards | **`facts`** | now |
23
- | Distill fact-cards into proposed doc deltas | **`docs`** | now — procedure in [`references/docs-mode.md`](references/docs-mode.md) |
24
-
25
- Both modes read [`references/method.md`](references/method.md); each mode's procedure that sequences it lives in its own section below.
26
-
27
- ## Execution model — pure skill, never autonomous
28
-
29
- Runs **in the main session** — no standing sub-agents, no daemon, no cron, no watch. The harness **proposes**, the user **disposes**: this skill runs only when the user asks, when the **Completion flow** invokes it (the kit `CLAUDE.md` Completion step distils the just-finished plan's trail — `facts` if the inbox holds entries, then `docs` which still drafts for approval), or when the user accepts the `maintenance` agent's threshold proposal (≥5 undigested inbox entries). It never schedules itself, never runs on a hook, never gates anything.
30
-
31
- ---
32
-
33
- ## Mode: `facts` (inbox → fact-cards)
34
-
35
- Distill the inbox's undigested entries into fact-cards. The algorithm is in [`references/method.md`](references/method.md) — follow it; this section is the procedure that sequences it.
36
-
37
- 1. **Collect the inbox.** Read every entry block (`## <timestamp> · session <id> · <repo>@<branch>`) across `docs/memory/inbox/*.md`. The inbox is a consume-on-digest queue — what is present is exactly what is undigested, so there is no separate high-water mark. An immediate second run finds an empty inbox and is a clean NOOP.
38
-
39
- **Thin or empty inbox ⇒ not a NOOP — distill the decision record.** A project whose sessions produced no reflected lessons is **not** a project with nothing to distill. Architecture decisions and reversals are high-surprisal by nature — they exist precisely because they were _not_ predictable. So when the inbox is thin/empty, also ingest, as surprisal sources for steps 2–5:
40
- - `docs/architecture/adr/**` — each accepted ADR is a surprising decision (its `Decision` + **Options Considered** — the rejected-alternatives section in the `documentation` §10 ADR template — carry the prediction error).
41
- - `docs/memory/brainstorm.md` — owner pronouncements, reversals, and discovered constraints recorded outside the inbox. Provenance points at the ADR file / brainstorm section, not a session id. These sources are **never consumed** — reconciliation (step 4) is what keeps a re-run over them idempotent.
42
-
43
- 2. **Surprisal filter → survivors** (method §1). Present the LLM with (a) a summary of the _current_ fact-set, (b) the undigested entries. Keep only what is **surprising** relative to (a). Predictable ⇒ dropped — no importance scores, no templates, prediction error is the only retention signal. Inbox entries arrive semi-distilled (the reflection hook already dropped routine work), so the filter's job here is narrower: drop what the existing fact-set already predicts. (First run, empty fact-set: keep decisions, reversals, discovered constraints, and failures with a cause worth remembering.)
44
-
45
- 3. **Decompose survivors → EDU candidates** (method §2). Each survivor becomes **one** atomic, self-contained statement — entity names normalized, minimal context baked into the sentence — with `provenance` pointing at the session ids and commits its inbox entry names. Decomposition, **not** summarization: one survivor may yield several cards; no information is destroyed; everything stays auditable back to its session and commits.
46
-
47
- 4. **Reconcile each candidate against existing cards** (method §3). Query the existing facts for semantic neighbors, then decide **ADD / UPDATE / SUPERSEDE / NOOP** for each. Gate on the **facts collection holding cards**, not on the binary and not on the collection merely being registered — a present qmd whose `{repo}-facts` collection is _registered but empty_ (the common first run: harness-manager's baseline registers + embeds `{repo}-facts` over a freshly-scaffolded `docs/memory/facts/` that has zero cards) returns nothing useful, exactly like an unregistered collection.
48
- - **Cards present** — `command -v qmd` succeeds **and** `docs/memory/facts/` holds at least one card (`docs/memory/facts/*.md` is non-empty): query scoped to the collection — `qmd query --format json -c {repo}-facts "<the candidate statement>"`. `{repo}` = the repo's directory name. Scope with `-c` always. (If the query returns nothing on a non-empty card set, the collection is registered-but- unbuilt — treat that as the fallback trigger below, not as "no neighbors.")
49
- - **No cards to query** (qmd absent, **or** `docs/memory/facts/*.md` empty — the common first-run case where the facts dir was scaffolded but never distilled into): fall back to filename/grep similarity over `docs/memory/facts/*.md` (slug overlap + entity-name grep), and **note in the report** that reconciliation ran without semantic retrieval. Step 7 builds/refreshes the collection at the end of the run, so the _next_ run — with cards now present — reconciles semantically.
50
-
51
- 5. **Write the results** (method §4 — bi-temporal, never delete).
52
- - **ADD** → write a new card `docs/memory/facts/{kebab-slug}.md` in the format.
53
- - **UPDATE** → refine the existing card's statement in place; extend its `provenance`.
54
- - **SUPERSEDE** → on the **old** card set `invalid_from:` (today) and `superseded_by:` (the new fact-id); write the **new** card with `valid_from:` today. The old file stays in place — history stays queryable, nothing vanishes.
55
- - **Self-contained reversal** (one input states X-then-reversed-to-Y, no old card exists yet) → emit **two** cards in one pass: a born-invalid historical card for X (`invalid_from` + `superseded_by` → Y) and a live card for Y. See method.md §4 "Self-contained reversal."
56
- - **NOOP** → the fact is already known; write nothing.
57
-
58
- 6. **Consume the digested entries LAST.** Only after every card is written, delete exactly the entry blocks this run digested from `docs/memory/inbox/*.md` (keep each file's header; never touch undigested blocks). Last, so a crash mid-run leaves the entries in place and the re-run re-digests them — reconciliation NOOPs the already-written cards. This consumption is the only bookkeeping; there is no `.digest-state.md`.
59
-
60
- 7. **If qmd present, refresh the lens** (skip silently if `command -v qmd` finds nothing):
61
- - `qmd collection add docs/memory/facts --name {repo}-facts` — register the collection. Re-adding an existing name exits non-zero with "already exists"; **treat that as success.** Never register `docs/memory` as a whole — that would sweep the raw inbox queue into the index.
62
- - `qmd context add qmd://{repo}-facts/ "Distilled recent decisions/observations, provenance-linked"` — upserts, safe to re-run.
63
- - `qmd update` — **re-scan the collection for the cards you just wrote.** In qmd 2.5.3 `collection add` on an already-registered name does NOT re-index new files; `qmd update` is what picks them up. Skipping this leaves the new cards invisible to retrieval (to the `prompt-recall` hook, and to distill②).
64
- - `qmd embed -c {repo}-facts` — regenerate vectors for the new/changed cards. Always scope with `-c`; a bare `qmd embed` re-embeds every repo on the machine. The `prompt-recall` hook's vector fallback only sees embedded cards, so skipping this hides new cards from prompt-time recall.
65
-
66
- 8. **Report** (in chat). Cards added / updated / superseded / NOOP'd, the entries consumed per actor, and — if reconciliation fell back to filename/grep (qmd absent, **or** `{repo}-facts` registered but holding no cards yet — the first-run-on-a-scaffolded-facts-dir case) — the one advisory line saying so. Fire this advisory whenever semantic retrieval was effectively unavailable, even if the collection appeared in `qmd collection list`: a registered-but-empty collection is not semantic retrieval. A run that finds nothing surprising is **valid**: say "no surprising entries in the inbox," don't pad.
67
-
68
- ---
69
-
70
- ## Mode: `docs` (fact-cards → proposed doc deltas)
71
-
72
- Distill fact-cards into proposed edits to `docs/` (the cold, human-owned SSoT) — the next hop down the gradient from `facts`. The full procedure (input selection, delta drafting, digestion marking, the pillar escape valve, trigger wiring) is in [`references/docs-mode.md`](references/docs-mode.md) — follow it; reuses `method.md`'s surprisal / EDU / reconciliation / bi-temporal vocabulary unchanged. It NEVER writes a doc on its own — it **drafts and presents**; the human disposes.
73
-
74
- ---
75
-
76
- ## Rules
77
-
78
- - **One algorithm root.** Every mode reads `references/method.md`. Extend the spec there — never inline a divergent copy of the four methods in a mode.
79
- - **Consume, never rewrite.** `docs/memory/inbox/` is a queue owned by the `session-reflect` capture hook. The distiller deletes exactly the entry blocks it digested this run — no edits, no reordering, no touching undigested blocks. Beyond that consumption it writes only under `docs/memory/facts/`.
80
- - **Idempotent via consumption.** What is in the inbox is what is undigested. Consume last; a second run finds an empty inbox and is a NOOP.
81
- - **Cards are atomic and provenance-linked.** One self-contained statement per card; every card resolves back to the session ids and commits it came from. Decompose — never lossily summarize.
82
- - **Supersede ≠ delete.** Bi-temporal only: set `invalid_from:` + `superseded_by:` on the old card and leave the file in place. Nothing is ever removed from `docs/memory/facts/`.
83
- - **Findings are questions, not verdicts.** When a candidate appears to contradict an existing card, phrase it as a question (is X now superseded by Y?) — never assert staleness or falsity.
84
- - **Never auto-runs.** Invoked by the user, by the Completion flow (kit `CLAUDE.md`), or by accepting the maintenance proposal. No cron, no daemon, no hook self-trigger — a workflow step the agent runs is not a self-trigger.
85
- - **qmd is strictly optional.** Guard every qmd call on `command -v qmd`; absent ⇒ silent skip. Reconciliation additionally needs the `{repo}-facts` collection to hold **cards** — not merely to be registered. "Unbuilt" here means **no cards** (`docs/memory/facts/*.md` empty), not "unregistered": harness-manager's baseline registers + embeds `{repo}-facts` over an empty scaffolded facts dir, so on a qmd-equipped, freshly-baselined repo the collection _appears in_ `qmd collection list` yet indexes zero cards. Binary present but collection cardless (first run) falls back to filename/grep too, same as qmd being absent.
86
-
87
- ## Output locations
88
-
89
- - `facts` reads `docs/memory/inbox/`, writes fact-cards to `docs/memory/facts/{kebab-slug}.md`, and deletes the consumed entry blocks from the inbox; the report goes to chat.
90
- - `docs` presents doc deltas in chat and, **only on approval**, writes the accepted prose into the target doc under `docs/` (+ a new `docs/history/changelog/` entry if drafted) and appends `digested_into:` to each consumed card in `docs/memory/facts/`. See [`references/docs-mode.md`](references/docs-mode.md).
@@ -1,299 +0,0 @@
1
- # distiller · mode `docs` — facts → documentation deltas
2
-
3
- The shared knowledge root for this mode is still [`references/method.md`](method.md) — **read it first, in this mode too.** `docs` mode reuses its surprisal, EDU-decomposition, reconciliation (ADD/UPDATE/SUPERSEDE/NOOP), and bi-temporal vocabulary unchanged. This file is the _procedure_ that sequences those mechanisms one hop further down the gradient: from `docs/memory/facts/` (warm, distilled cards) into proposed edits to `docs/` (the cold, human-owned SSoT). It NEVER writes a doc on its own — it **drafts and presents**; the human disposes (P7).
4
-
5
- > `facts` mode = inbox → cards. `docs` mode = cards → _presented_ doc deltas. One component, two modes, one algorithm root. distill② is the greenfield hop no surveyed tool has built (prior art stops at index/retrieval); the discipline below is what keeps it from corrupting documentation.
6
-
7
- ---
8
-
9
- ## The audit-trail invariant (the spine)
10
-
11
- Every rule here serves one invariant, because the load-bearing moment in this mode is the **human-approval moment**, and that moment is only trustworthy if the reviewer can mechanically check every claim:
12
-
13
- > **Two-way traceability.** Every drafted delta _line_ names the fact-card(s) it decomposes from (forward: delta → card). Every consumed card names the doc(s) it was digested into (backward: card → delta). Every card already names the sessions/commits it came from (`provenance`, from `facts` mode). The chain **doc-line → card → session/commit** must resolve end to end with no orphan link. A delta line with no card, or a card marked digested into a doc that does not contain its content, is a **defect**: the run fails its acceptance check, the line is not presented.
14
-
15
- This mode introduces **no prose that is not a decomposition of a card** — decomposition-with- citation, exactly as `facts` mode decomposes inbox entries into cards. If the agent cannot cite a card for a sentence, that sentence is not written.
16
-
17
- ---
18
-
19
- ## Design lens: the six ways facts→docs corrupts documentation
20
-
21
- Designed **failure-modes-outward.** Every step below names the hole it closes; if a step seems heavy, trace it to the failure it prevents.
22
-
23
- | # | Failure mode | What it looks like | The protection that closes it |
24
- | --- | --- | --- | --- |
25
- | **F1** | **Invention** | The draft asserts something no card says — interpolated, generalized, or hallucinated prose. | Decomposition-never-invention: every line carries `[from: fact-…]`; a line with no card behind it is illegal and dropped (Step 3, Acceptance A2). |
26
- | **F2** | **Stale-card application** | A superseded/invalidated card is written into a doc as if current. | Bi-temporal gate: only `invalid_from: null` cards are eligible; superseded cards are skipped and named in the report (Step 2). |
27
- | **F3** | **Double digestion** | The same card is written into the same doc twice across runs. | `digested_into:` on the card is the idempotency mark — a (card, doc) pair already recorded is not re-drafted (Resolution 3, Step 2). |
28
- | **F4** | **Conflict steamrolling** | A card contradicts existing prose and the draft silently overwrites it. | Card-vs-prose conflicts become **D7 questions**, never edits (Resolution 2, Step 4). |
29
- | **F5** | **Pillar-drift absorption** | A card contradicting a doctrine/decision headline is quietly written into a doc, laundering a belief change. | Pillar escape valve: such a card emits a _propose-Brainstorming-Mode_ item, never a doc edit (Resolution 4, Step 2.4). |
30
- | **F6** | **Partial-approval limbo** | Human approves 3 of 5 lines; the card is marked fully digested (losing 2) or fully un-digested (re-proposing 3). | Partial digestion: `digested_into:` records only the (card → doc) pairs _actually written_; declined lines → backlog (D10); the card recirculates only its un-applied content (Resolution 3, Step 6). |
31
-
32
- Two structural failures sit above these: **frozen-record corruption** (editing a pillar, a `Complete`/`Abandoned` plan, or changelog history — forbidden outright) and **axis merge** (folding distill② into maintenance — refused: reconcile owns _structure_, maintenance owns _docs-vs-code fidelity_, distill② owns the _facts→docs distillation_ hop; three distinct axes, P19).
33
-
34
- ---
35
-
36
- ## Execution model
37
-
38
- Runs **in the main session.** No standing sub-agent, no daemon, no cron, no hook self-trigger (P6). The harness proposes; the user disposes. Invoked one of three ways, all human-gated:
39
-
40
- 1. The user asks (`distiller docs`, "distill the facts into docs", "draft pending doc deltas").
41
- 2. The **Completion flow** points at it (one router line in the kit `CLAUDE.md` — Resolution 5a).
42
- 3. The user accepts the **maintenance threshold proposal** (≥N undigested cards map to one doc — Resolution 5b).
43
-
44
- In every path the output is **a presented draft, never a written doc.** Nothing mutates a doc without an explicit human "yes" on that specific draft.
45
-
46
- ### Invocation shapes
47
-
48
- ```
49
- distiller docs # Completion default: resolve target from the active plan/spec
50
- distiller docs --feature <slug> # just-completed feature → resolve to its owning doc
51
- distiller docs --doc <path> # threshold path / direct: digest cards touching one named doc
52
- ```
53
-
54
- `docs` mode runs against **one target doc at a time** — the unit of a delta is a single doc. The Completion caller uses the bare/`--feature` form (it knows what just shipped); the threshold caller (maintenance) uses `--doc` (it knows which doc the cards piled up against). A card-first run may surface several single-doc deltas; each is presented and approved independently.
55
-
56
- ---
57
-
58
- ## Procedure (executable, numbered)
59
-
60
- > Each step names the failure mode(s) it closes. Ordering is load-bearing: the pillar check precedes drafting (a belief-changing card never reaches the draft); the digestion mark is written **last** (a crash re-presents rather than silently swallows — the mirror of `facts` mode's "consume the digested entries last").
61
-
62
- ### 0. Read the roots
63
-
64
- Read [`method.md`](method.md) (surprisal / EDU / reconciliation / bi-temporal vocabulary) and this file. Confirm the target doc exists, or note that none does (Edge Case A).
65
-
66
- ### 1. Resolve the target doc, read the three roots in order
67
-
68
- 1. `method.md` (algorithm vocabulary).
69
- 2. The **target doc** — full current prose + its `owns`/`touches` frontmatter. This is the SSoT you are proposing to revise; you read it before you draft so conflicts surface as questions, not silent overwrites.
70
- - `--doc <path>` → that doc.
71
- - `--feature <slug>` / bare → the spec that just flipped to `Active` (Completion names it), or the feature doc whose `owns` glob covers the plan's Proposed-Changes paths. Glob `docs/{features,modules/*/features}/**/*.md`, match `owns` against changed paths. Architecture/operations docs have no `owns` — for those, fall to entity/semantic match.
72
- - No doc resolves → Edge Case A.
73
- 3. The **pillar headlines** — the doctrine pillar (`## Pn —` headlines) and the decisions pillar (`## Dn —` headlines) plus each one's one-line Principle/Decision statement. Read **every** decision headline except those marked superseded: a decision carrying a `Reversed by D-M` marker (or a legacy `Status: tentative`) is NOT a binding pillar headline and is skipped; absence of any status marker means the decision is live (the new append-only model has no `Status: committed` field — a live decision is simply one nothing has reversed). Headlines only, never the bodies — this is the conservative contradiction-check substrate (Resolution 4).
74
-
75
- ### 2. INPUT SELECTION — gather the cards "touching" the target (Resolution 1) — _closes F2, F3, F5_
76
-
77
- 1. **Structural retriever (always runs, qmd-independent).** Read every card in `docs/memory/facts/*.md`. A card _structurally touches_ the target when its `provenance` paths or `entities` intersect the target's `owns`/`touches` globs — match defined precisely in Resolution 1.
78
- 2. **Semantic retriever (qmd, optional).** If `command -v qmd` succeeds, for the target's identity (title + each `owns` glob stem + any `anchors:` query) run `qmd query --format json -c {repo}-facts "<the doc's subject>"`. `{repo}` = repo dir name; scope with `-c` always; repeat `-c` per relevant `{repo}-*` facts collection (the set `qmd collection list` names). qmd absent ⇒ skip silently, note in the report that selection ran structural-only (changes recall, not correctness).
79
- 3. **Bi-temporal + digestion gate (F2, F3).** From the union, drop:
80
- - any card with `invalid_from` ≠ `null` (superseded — list as "skipped: superseded");
81
- - any card whose `digested_into:` already lists the target doc path (already consumed here);
82
- - any card whose `superseded_by:` chain leads to a live card already in the set (the live card wins; keep the superseded one out of drafting, keep its trail — Edge Case D). What remains is the **working card-set**.
83
- 4. **Partition the working card-set (F5):**
84
- - **pillar-conflicting** — cards in explicit headline-level conflict with a pillar/decision (Resolution 4). These NEVER become a doc edit; they route to the escape valve **before** drafting, so a belief-changing fact can never be laundered into prose.
85
- - **doc-bound** — everything else (most cards). A card can be both: handle both arms; do not let one suppress the other.
86
-
87
- ### 3. DELTA DRAFTING — classify, then decompose doc-bound cards (Resolution 2) — _closes F1, F4_
88
-
89
- First **surprisal-gate** each doc-bound card against the _doc_ (method §1, retargeted): a card whose content the doc already states is predictable ⇒ **NOOP** — draft nothing, but still mark it digested on accept (Step 5; it is absorbed, so it must not re-surface forever). This is the only retention signal; no importance scores.
90
-
91
- For each surviving card, classify against existing prose — the card-vs-doc decision (method §3, retargeted), exactly one of:
92
-
93
- - **EXTEND** — adds behavior/rationale the doc lacks, no conflict → draft an additive line.
94
- - **REFINE** — sharpens an existing doc claim that is incomplete, not wrong → draft an in-place refinement (additive; never deletes prose).
95
- - **CONFLICT** — card and prose disagree on a matter of fact → **do not resolve.** Emit a D7 question; draft the line only _conditionally_, gated on the answer (F4).
96
- - **ESCALATE** — contradicts a pillar headline → already routed to the valve in Step 2.4 (Resolution 4); never drafted here.
97
- - **NOOP** — doc already states it → mark digested, draft nothing.
98
-
99
- For each EXTEND/REFINE card, decide which **allowed section** it informs (Resolution 2 fixes the allow-list) and draft the smallest faithful prose change. Every drafted line carries an inline provenance marker `[from: fact-<slug>]` for each contributing card. These markers are **scaffolding for the review moment, not final doc text** — stripped on accept (Step 5.1), so the human-owned prose stays clean. The durable forward-link survives as the card's `digested_into:` back-pointer.
100
-
101
- The output of this step is a **single presented delta document** (in chat, not written to disk):
102
-
103
- ```
104
- TARGET: docs/features/memory-capture.md
105
-
106
- ── PROPOSED DELTA ────────────────────────────────────────
107
- § Current Behavior (EXTEND)
108
- + The qmd retrieval lens is organized into per-subtree collections, with the
109
- memory inbox excluded from indexing by construction. [from: fact-qmd-optional-retrieval-lens]
110
-
111
- § Changelog entry (new — docs/history/changelog/2026-06-04-...md)
112
- ### Modified
113
- - capture-hook merge is now idempotent across re-install [from: fact-etna-hook-merge-idempotent]
114
-
115
- ── QUESTIONS (D7 phrasing — answer before accept) ────────
116
- Q1. docs/features/memory-capture.md §Current Behavior says the index spans all of
117
- docs/memory; fact-qmd-optional-retrieval-lens says the memory inbox is excluded.
118
- Confirm the doc is stale before I revise it — I will not overwrite it silently.
119
-
120
- ── PILLAR-CONFLICT ESCAPE (NOT a doc edit) ───────────────
121
- (see Resolution 4 — emitted as a propose-Brainstorming-Mode item, if any)
122
-
123
- ── DIGESTION PLAN (applied only on accept) ───────────────
124
- On accept I will set `digested_into:` on: fact-qmd-..., fact-etna-hook-..., and the
125
- NOOP'd cards already stated by the doc: fact-...
126
- ──────────────────────────────────────────────────────────
127
- ```
128
-
129
- ### 4. PRESENT and await disposition — never auto-write (locked default #4) — _the gate itself_
130
-
131
- Present the delta document in chat and **stop.** The agent does not edit the target doc, write the changelog, or touch any card frontmatter at this point. The human disposes:
132
-
133
- - **Accept (whole or per-line)** → Step 5.
134
- - **Accept with edits** → the human's edits are authored prose; apply them as doc text, but digestion-marking (Step 5) still records the contributing cards as evidence consumed (the human edited the _abstraction_; the _evidence_ is unchanged). Lines the human deletes entirely → their sole-source cards are **not** marked digested (nothing landed for them).
135
- - **Decline** → Step 6 (backlog at par, D10).
136
- - **Blocked-on-question (CONFLICT)** → leave undigested; the question re-surfaces next run if unanswered. Not backlogged unless the user explicitly declines to engage it.
137
-
138
- ### 5. ON ACCEPT — write doc, write changelog, mark digestion LAST (Resolution 3) — _closes F3, F6_
139
-
140
- Apply in this order so a crash leaves the audit trail consistent (doc content exists before any card claims to have landed in it):
141
-
142
- 1. **Write the accepted prose into the target doc**, stripping the `[from: ...]` markers from the final text (review scaffolding; the doc's prose is the human-owned abstraction). Prefer handing approved lines to the **`documentation` skill**, which owns the feature/changelog templates — this mode carries no copy of them.
143
- 2. **Write the changelog entry** if the delta included one (via the `documentation` skill's Changelog template, `docs/history/changelog/`). Append-only; never edit an existing entry.
144
- 3. **Mark digestion LAST:** on each card that contributed an _accepted_ line (and each NOOP'd card the doc already stated), append the target doc path (and changelog path, if it contributed there) to `digested_into:`. Marking last means a crash mid-write re-presents the delta rather than falsely claiming digestion.
145
-
146
- Cards STAY — `digested_into:` is the only mutation; bi-temporal rules from `method.md` still apply (a card is never deleted; supersession is `facts` mode's surface). The card is now both evidence-for-the-doc and a queryable audit record of _where its content went_.
147
-
148
- ### 6. ON DECLINE — backlog at par, deduped (D10) — _closes F6_
149
-
150
- Append the declined delta to `docs/history/backlog.md` at the finding's own P0–P3 severity (same scale as code work), deduped:
151
-
152
- ```
153
- - **[P2]** distiller-docs proposed updating docs/features/memory-capture.md §Current Behavior from fact-qmd-optional-retrieval-lens (declined) — discovered by distiller docs
154
- ```
155
-
156
- Dedupe by (target doc § section, contributing card-ids): if an identical pending item exists, leave it. Never a segregated section, never auto-lowest-tier (D10). Never block Completion. The declined cards stay undigested, so they re-surface next pass.
157
-
158
- ### 7. Report (in chat)
159
-
160
- Target doc; working-set cards; lines drafted and their cards; cards classified EXTEND / REFINE / NOOP / CONFLICT-question / ESCALATE / skipped-as-superseded; questions raised; pillar-conflict items verbatim; on accept — doc + changelog written and cards marked digested; on decline — the backlog line. If qmd was absent, the one advisory line that selection ran structural-only. A run where no undigested card touches the target is **valid**: say "no undigested cards touch {target}," do not pad — and **touch nothing**.
161
-
162
- ---
163
-
164
- ## The five resolutions
165
-
166
- ### Resolution 1 — INPUT SELECTION: what "touching" means, precisely
167
-
168
- A card **touches** the target doc iff it satisfies **either** retriever, and is not removed by the Step-2.3 gates. "Touching" is a **union of retrievers, intersected with eligibility** — broad capture, conservative gating.
169
-
170
- **(a) Structural intersection (exact, always evaluated).** True when ANY of:
171
-
172
- 1. **Provenance match (STRONG signal).** A `commit <sha>` entry in the card's `provenance` resolves to changed files (`git show --name-only <sha>` when the commit is local); a changed file under the target's `owns`/`touches` globs is a match. (`session <id>` entries carry no paths and do not participate; an ADR/brainstorm path matches the globs directly.) This is strong because the card derives from a commit that edited code the doc owns.
173
- 2. **Entity match (SUPPORTING signal).** A normalized name in the card's `entities` equals (case-insensitively, after kebab/camel normalization) a module/feature stem in an `owns` glob, the doc title, or a symbol the doc explicitly names. **Bare prose mentions do NOT count** — only declared ownership, so "touching" can't be triggered by a doc that merely discusses an entity it doesn't own.
174
-
175
- **(b) Semantic neighborhood (qmd, optional, recall-only).** A card returned by `qmd query -c {repo}-facts "<doc subject>"` within qmd's default result window. Semantic hits with **no** structural intersection are still candidates but flagged `[semantic-only]` in the delta — the link is associative, not provenance-grounded, so the reviewer treats it as lower-trust and may reject on link-strength alone.
176
-
177
- The candidate set is the **union** of (a) and (b); correctness rests on (a) (exact, deterministic, qmd-independent), (b) only raises recall. This is why the mode runs fully without qmd, and why the maintenance threshold (Resolution 5b) counts on (a) alone.
178
-
179
- A card may touch **several** docs (its entities span modules) — expected, handled by partial digestion (Resolution 3): the card is processed once per target doc, and `digested_into:` accumulates each doc it actually fed.
180
-
181
- ### Resolution 2 — DELTA DRAFTING RULES: what a fact may update, with provenance
182
-
183
- **Allow-list of writable targets** (a card may inform ONLY these):
184
-
185
- | Target | What a card may do | Why bounded here |
186
- | --- | --- | --- |
187
- | **`## Current Behavior`** (feature doc) | Add/modify a sentence describing how the system works _today_, when a card reports a now-true behavior the section omits or contradicts. The primary target. | The "how it works now" section cards are evidence for. |
188
- | **`## Background`** — why-built metadata (feature doc) | Add a sentence of _why_ a decision was made, when a card records a decision/constraint/reversal Background omits. | Cards carry the "why" the loop captured; Background is its home. |
189
- | **`## Architecture` / `## Key Functions`** (feature doc) | Additive correction only when a card names a concrete, code-anchored fact (a new function, a moved file). Prefer Current Behavior unless the card is structural. | Code-anchored structure, not prose. |
190
- | **A new changelog entry** in `docs/history/changelog/` (via `documentation` skill's Changelog template, `### Added`/`### Modified`/`### Removed`) | Draft a changelog line for a code change a card witnesses, when a plan just completed. | Append-only history; a _new_ file, never an edit. |
191
-
192
- **Never writable by distill②** (hard stops): `## Goal`, `## Goals & Non-Goals`, `## Approach`, `## Open Questions`, `## State`, frontmatter `owns`/`touches`/`anchors` (maintenance/spec axis — P19), any **pillar** file, any **plan** (frozen), any **existing** changelog entry (frozen history), any **boundary** file. A card implying one of these needs a change becomes a **question** (below) or, for pillars, an **escape item** (Resolution 4).
193
-
194
- **Per-line provenance (the invariant, mechanized).** Every drafted line carries `[from: fact-<slug>]` for each contributing card. A line with no such tag is a **defect** — not presented (Acceptance A2). On accept the tag is stripped from the final doc prose (it was review scaffolding); the durable forward-link survives as `digested_into:` (Resolution 3) — so traceability is preserved without polluting human-owned prose.
195
-
196
- **Decomposition, never invention.** As in `facts` mode: one card may yield several doc lines; the agent rephrases the card's atomic statement into doc-fit prose but **adds no claim the card does not support.** When two cards are needed for one line, both ids attach. If doc-fit prose would require a fact in no card, that fact is _missing_ — raise it as a gap in the report, do not invent it.
197
-
198
- **Conflict → question (D7 phrasing), never silent resolution.** When a card contradicts an existing sentence, the agent does NOT overwrite. It emits a D7 question — _"docs/x.md §S says A; fact-<slug> (from <provenance>) says B. Confirm the doc is stale before I revise it."_ — and drafts the conflicting line only as a _conditional_ proposal gated on the answer. Docs stay SSoT: the card is evidence for revision, the human decides.
199
-
200
- ### Resolution 3 — DIGESTION MARKING: `digested_into:` semantics
201
-
202
- On accept, each contributing (and each NOOP'd) card gains a `digested_into:` frontmatter list:
203
-
204
- ```yaml
205
- ---
206
- id: fact-qmd-optional-retrieval-lens
207
- valid_from: 2026-06-04
208
- invalid_from: null
209
- superseded_by: null
210
- digested_into: # ← added by docs mode, on accept, never on draft
211
- - docs/features/memory-capture.md
212
- - docs/history/changelog/2026-06-04-qmd-lens.md
213
- entities: [qmd, retrieval-lens, per-subtree-collections, inbox]
214
- provenance: [session e3c4a623, commit d262e18]
215
- ---
216
- ```
217
-
218
- A **flat list of doc paths** — the brief's shape. Track _which doc_ a card landed in, not which sentence; whole-card-vs-target is the granularity, which keeps selection (Step 2.3) a simple list-membership test.
219
-
220
- Semantics:
221
-
222
- - **Absent / empty** ⇒ undigested ⇒ eligible for selection and counted by the maintenance threshold. The default state of a fresh `facts`-mode card.
223
- - **A list of doc paths** ⇒ the card's content has been decomposed into each listed doc — the backward audit link completing the two-way trace.
224
- - **Partial digestion (F6), keyed on whether THE CURRENT TARGET is listed.** A card may contribute to doc X yet have residual facts not placed elsewhere. Mark it `digested_into: [docs/X.md]` — digested into X, but **still selectable** for other targets, because selection drops a card only when the list already contains _the current target_. A card stays in circulation until every doc it touches has consumed it. No separate flag. Lines the human declined are not represented in the list (their cards recirculate); only _applied_ content is recorded.
225
- - **One card, several docs** ⇒ the list grows **append-only across runs.** Each entry is independently meaningful; order is run-order. **Never rewrite or remove an entry** — a doc later reverted does not un-digest the card (the card _was_ digested there at that transaction time; the doc's own git history records the revert). This mirrors `method.md`'s bi-temporal stance: the trail only grows.
226
- - **Supersession interaction (F2 + F4 seam).** If a digested card is later superseded by a `facts`-mode run, the _old_ card keeps its `digested_into:` (it documents what the doc once said and why) AND gains `invalid_from:`/`superseded_by:` from the normal bi-temporal path. The _new_ card is undigested → on the next `docs` run its content surfaces the now-stale doc prose as a **CONFLICT question** (Resolution 2), never an auto-rewrite. The trail shows: doc prose ← old card (now invalid) ← superseded_by → new card (pending). No link is ever broken.
227
-
228
- `digested_into:` is the **only** field `docs` mode writes on a card. It never edits the card's body, `provenance`, `entities`, or bi-temporal fields — those are `facts` mode's surface.
229
-
230
- ### Resolution 4 — PILLAR ESCAPE VALVE: "contradicts a pillar," conservatively
231
-
232
- **Definition (deliberately narrow).** A card _contradicts a pillar_ iff its atomic statement is in **explicit, headline-level conflict** with a doctrine principle (`## Pn —` headline) or a live decision (`## Dn —` headline) — i.e. the card asserts X and the headline (or its one-line Principle/Decision statement) asserts not-X about the _same_ subject. A decision counts as a binding headline unless it has been superseded: a `Reversed by D-M` marker (or a legacy `Status: tentative`) drops it from the substrate, and the reversing decision is the live headline in its place. The operative test: **could you point to the exact pillar headline the card negates?** If not, it is not a pillar contradiction.
233
-
234
- - ✅ Escapes: a card stating "the harness auto-runs the distiller on a cron" vs P6's "build no enforcement machinery / no cron" — direct negation of a headline claim. A card recording a reversal that negates a live decision headline — e.g. "modules auto-create at a flat→modules threshold" vs a live D-headline "the user sets module granularity."
235
- - ❌ Does NOT escape: a card that merely _touches a topic a pillar discusses_; adds nuance, scope, or an exception not contemplated by a pillar (normal doc evolution); is "in tension with the spirit"; or conflicts only with a pillar's **body** but not its headline (that is a normal doc CONFLICT question — D7 — or nothing). **Vibes do not escape.**
236
-
237
- If uncertain whether a conflict is headline-level, **do not escape** — treat it as a doc CONFLICT question or drop it. Rationale (the decision rule under uncertainty): a false-negative — a real conflict slips through as a question — is **recoverable** by the human in the review moment; a false-positive — a spurious brainstorm proposal — costs the operator a mode-entry decision and **erodes the valve's signal.** When unsure, the cheaper miss is the question.
238
-
239
- **Emission (P7 mode-entry instance — propose, never enter).** A pillar-conflicting card produces a **propose-Brainstorming-Mode item** in the PILLAR-CONFLICT block, never a doc edit and never an auto-entry into Brainstorming Mode:
240
-
241
- ```
242
- ── PILLAR-CONFLICT ESCAPE (NOT a doc edit) ───────────────
243
- fact-<slug> (from <provenance>) appears to contradict doctrine headline
244
- "P6 — the harness builds no enforcement machinery":
245
- card says: <the card's atomic statement>
246
- P6 headline: <the headline claim>
247
- This is a belief-level conflict, not a doc revision. Propose entering Brainstorming
248
- Mode to reconcile it? (I will not edit the pillar, and I have NOT digested this card
249
- into any doc.) [from: fact-<slug>, contradicts: P6]
250
- ```
251
-
252
- The conflicting card is **NOT marked digested** (it was not absorbed — it escaped) and **NOT backlogged** as a doc-delta (it awaits the brainstorm disposition). It stays undigested so the conflict re-surfaces until brainstorm resolves it (the recurrence _is_ the dedupe). Distillation never quietly rewrites belief — the pillars are the human-owned headwater that _steers_; distill② may not silently change what they steer. (If a brainstorm later lands a pillar change plus a downstream doc edit, that doc edit re-enters this mode normally.)
253
-
254
- ### Resolution 5 — TRIGGER WIRING (P10: at most one line each)
255
-
256
- **(a) Completion-flow pointer — ONE router-grade line in the kit `CLAUDE.md` Completion step.** Added to the "On **Completion**" flow in `harness/code/CLAUDE.md` §Workflows. Terse, points, does not re-teach (the procedure lives in this file). Verbatim:
257
-
258
- > On Completion, after flipping the spec to `Active`, consult `distiller docs` for the just-completed feature's doc — it drafts any pending fact-card deltas for your approval (presented, never auto-written).
259
-
260
- **(b) Maintenance threshold — ONE line in `agents/maintenance.md` Step 7**, sibling to the existing inbox-distillation threshold (the `facts` analogue: ≥5 undigested _inbox entries_ proposes a `facts` run). Verbatim:
261
-
262
- > **Docs-distillation threshold.** Per target doc, count undigested fact-cards (`invalid_from: null`, the doc's path not in their `digested_into:`) that **touch** it by Resolution 1's structural intersection (exact, qmd-independent). If **≥10** map to **one** doc, add one line to the report **proposing** a `distiller docs --doc <path>` run on that doc — propose only, never auto-run, no cron. Declined/ignored ⇒ the count keeps growing and the proposal reappears next pass; that recurrence is the dedupe, so never backlog the proposal itself.
263
-
264
- `N = 10` default (vs the inbox threshold's 5 — inbox entries are rare and rich; cards accrue per doc). "Touch" uses the **structural** retriever only (deterministic, no qmd dependency in the maintenance loop). Per-doc count, not repo-wide: the proposal names the one doc, so the run is already scoped. This lands as a proposal emitter only — maintenance does not run the distiller (P19: distinct axis).
265
-
266
- ---
267
-
268
- ## Edge cases
269
-
270
- **A. No target doc exists yet for a completed feature.** distill② does **not** create the feature doc — that is the Completion step's job (flip spec to `Active`) via the `documentation` skill. If the invocation finds no doc, the mode reports _"no target doc yet for {feature}; create it via the spec flow first, then re-invoke `distiller docs`"_ and stops. It never scaffolds a doc from cards alone — a doc born purely from distilled facts would have no human-authored abstraction (Background/Goal/Approach) and would violate docs-as-SSoT + decomposition-never-invention. Cards stay undigested; they'll be drafted once the doc exists. If the gap persists, backlog at par: `- **[P2]** Feature {slug} has {N} undigested cards but no owning doc — discovered by distiller docs`.
271
-
272
- **B. A card touching zero docs.** Not selected for any target — it stays undigested, queryable, counted toward no threshold. Normal and valid: not every fact belongs in a doc (a transient observation, a fact about an unowned path). **Never** force-fit into the nearest doc (that is invention). If maintenance reports many undigested cards touching _no_ doc, that is an **unowned-code** signal for maintenance's existing unowned-path step, not a distill② action. Report it as a count (_"{N} cards touch no current doc"_); do not absorb it. No doc is touched.
273
-
274
- **C. A card touching a FROZEN record (a plan, an existing changelog entry, a pillar).** Never edited (hard stop). A card whose only relevant target is frozen:
275
-
276
- - if it _contradicts_ a pillar headline → escape valve (Resolution 4);
277
- - if it reports a code change worth recording → draft a **new** changelog entry (append-only is allowed; editing an existing one is not);
278
- - otherwise → it touches a writable doc section instead, or touches zero docs (Edge Case B). The frozen file is never the write target.
279
-
280
- **D. Conflicting cards (two live cards disagree about the target's area).** distill② does not adjudicate which is true — that is `facts`-mode reconciliation's job (ADD/UPDATE/SUPERSEDE/NOOP, `method.md` §3). If two _live_ (non-superseded) cards make contradictory claims about the same doc sentence, surface **both** as a single D7 question — _"fact-A says X, fact-B says Y about §S; these disagree — which holds? I will not draft a revision until the cards reconcile"_ — and draft **neither** line. Suggest the user run `distiller facts` to reconcile first. If one card is already `superseded_by` the other, the live card wins and the superseded one is excluded from drafting (Step 2.3) but its trail is preserved. distill② never resolves a fact-vs-fact conflict by picking one — that crosses into fabricating belief.
281
-
282
- **E. A card hand-edited into the doc (accept-with-edits), then re-surfaces.** Because `digested_into:` was set on accept (Step 5.3) listing the target, selection (Step 2.3) drops the card on the next run — it is digested into that doc regardless of whether the landed prose is the agent's draft or the human's edit (the human edited the _abstraction_; the _evidence_ is recorded either way). The card does not re-surface for that doc. Only if the human _deleted_ a line entirely (so nothing landed for its sole-source card) does that card stay undigested and re- surface — which is correct: its content never reached the doc.
283
-
284
- **F. Disambiguation — prose-conflict vs pillar-conflict.** If a card collides with _both_ a doc sentence and a pillar headline, the **pillar escape wins** (the heavier, more conservative path): emit the propose-Brainstorming-Mode item and do not draft the doc line, because the doc prose may itself be downstream of the belief now in question. Resolve the belief first, then let any resulting doc edit re-enter normally.
285
-
286
- ---
287
-
288
- ## Acceptance checks
289
-
290
- 1. **Presented, never auto-written.** A run produces a chat-presented draft and waits; no doc file changes until the human approves (locked default #4).
291
- 2. **Per-line provenance.** Every drafted prose line / changelog bullet carries a `[from: ...]` tag naming ≥1 source card; a line with no card behind it does not exist in the draft (F1).
292
- 3. **Digestion recorded + cards survive.** On approval, every consumed (incl. NOOP'd) card gains the target path in `digested_into:` and the file remains in `docs/memory/facts/` (F3); marking happens last, after the doc write.
293
- 4. **Pillar-contradicting card → brainstorm proposal, not a doc edit.** A planted card negating a doctrine/decision headline produces a propose-Brainstorming-Mode item and zero doc/pillar edits; it is neither digested nor backlogged (Resolution 4, F5).
294
- 5. **Conflict surfaces as a question.** A card disagreeing with doc prose yields a D7-phrased QUESTION, never a silent rewrite (Resolution 2, F4).
295
- 6. **Declined delta → backlog at par, deduped.** Declining produces one P-tiered backlog line interleaved with code items; a second decline adds nothing (D10).
296
- 7. **Docs without relevant cards untouched.** A doc no card touches is never opened or modified; a run with an empty working set reports "no undigested cards touch {target}" and changes nothing.
297
- 8. **Idempotent via the digestion mark.** An immediate re-run on the same doc finds its touching cards already `digested_into:` that doc and is a clean NOOP — the digestion mark is to `docs` mode what inbox consumption is to `facts` mode (F3).
298
- 9. **Trigger budget.** Exactly one line added to `CLAUDE.md` Completion; exactly one line added to maintenance Step 7. No procedure inlined into either (P10).
299
- 10. **Not autonomous.** No cron/daemon/hook; invoked by the Completion step, by accepting maintenance's threshold proposal, or by direct user request only (P6).