@nurix/etna 0.14.0 → 0.15.2

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 (43) hide show
  1. package/README.md +28 -7
  2. package/bin/etna.js +58 -1
  3. package/data/harnesses/code/CLAUDE.md +11 -16
  4. package/data/harnesses/code/agents/brainstorm.md +13 -20
  5. package/data/harnesses/code/agents/change-review.md +5 -5
  6. package/data/harnesses/code/agents/harness-manager.md +49 -9
  7. package/data/harnesses/code/agents/maintenance.md +42 -5
  8. package/data/harnesses/code/agents/spec-mode.md +1 -1
  9. package/data/harnesses/code/harness.json +6 -0
  10. package/data/harnesses/code/hooks/hooks.json +41 -0
  11. package/data/harnesses/code/hooks/lib/redact.js +37 -0
  12. package/data/harnesses/code/hooks/post-commit.js +136 -0
  13. package/data/harnesses/code/hooks/post-tool-failure.js +136 -0
  14. package/data/harnesses/code/hooks/session-stop.js +207 -0
  15. package/data/harnesses/code/skills/boundary/SKILL.md +21 -3
  16. package/data/harnesses/code/skills/boundary/references/boundary-taxonomy.md +229 -161
  17. package/data/harnesses/code/skills/brainstorm/README.md +76 -227
  18. package/data/harnesses/code/skills/brainstorm/SKILL.md +78 -566
  19. package/data/harnesses/code/skills/brainstorm/harvest.md +26 -0
  20. package/data/harnesses/code/skills/brainstorm/templates/build.md +37 -0
  21. package/data/harnesses/code/skills/brainstorm/templates/decisions.md +12 -25
  22. package/data/harnesses/code/skills/brainstorm/templates/domain-model.md +25 -0
  23. package/data/harnesses/code/skills/brainstorm/templates/non-goals.md +2 -2
  24. package/data/harnesses/code/skills/brainstorm/templates/questions.md +18 -67
  25. package/data/harnesses/code/skills/brainstorm/templates/state.md +5 -5
  26. package/data/harnesses/code/skills/brainstorm/templates/tech.md +23 -0
  27. package/data/harnesses/code/skills/brainstorm/templates/thesis.md +21 -0
  28. package/data/harnesses/code/skills/distiller/SKILL.md +218 -0
  29. package/data/harnesses/code/skills/distiller/references/docs-mode.md +526 -0
  30. package/data/harnesses/code/skills/distiller/references/method.md +166 -0
  31. package/data/harnesses/code/skills/documentation/references/templates.md +78 -5
  32. package/data/harnesses/kb-agi/CLAUDE.md +1 -1
  33. package/data/harnesses/kb-agi/agents/kb-agi-init.md +2 -2
  34. package/data/harnesses/kb-agi/agents/kb-catch-stale.md +1 -1
  35. package/data/harnesses/kb-agi/agents/kb-find-gaps.md +2 -2
  36. package/data/harnesses/kb-agi/agents/kb-fix-links.md +1 -1
  37. package/data/harnesses/kb-agi/agents/kb-refresh-frontdoor.md +1 -1
  38. package/data/harnesses/kb-agi/agents/kb-refresh-summaries.md +1 -1
  39. package/data/harnesses/kb-agi/agents/kb-sync-metadata.md +1 -1
  40. package/data/harnesses/kb-agi/harness.json +6 -0
  41. package/package.json +1 -1
  42. package/data/harnesses/code/skills/brainstorm/greenfield.md +0 -99
  43. package/data/harnesses/code/skills/brainstorm/templates/inventory.md +0 -44
@@ -0,0 +1,26 @@
1
+ # Harvest adapter
2
+
3
+ Harvest is **optional and offered, never assumed**. By default the session folder is the product: a self-contained registry the next `/brainstorm start` resumes from. Run this adapter only when (a) the repo is v9-harnessed — `docs/architecture/` and `docs/modules/` exist — and (b) the user accepts the offer at `stop`.
4
+
5
+ ## Routing
6
+
7
+ The harvest is a structured diff between the session pillars and the live docs, routed by scope: module-local findings → `docs/modules/{m}/pillars/`, cross-module → `docs/architecture/`, IA/contract changes → `docs/boundaries/` (via the `boundary` skill), backlog → `docs/history/`.
8
+
9
+ | Session source | Destination |
10
+ |---|---|
11
+ | `thesis.md` narrative | `docs/history/plans/YYYY-MM-DD-{slug}.md` (Status: Draft, `documentation` skill §8 Plan template) |
12
+ | `domain-model.md` | `docs/architecture/domain-model.md`; if it changes the IA or a cross-cutting contract, **flag for the `boundary` skill (`create`)** — don't hand-write boundary files |
13
+ | Decisions spanning 2+ modules | one ADR per cohesive theme → `docs/architecture/adr/ADR-NNN-{slug}.md`, authored to the **`documentation` skill §10 ADR template** (frontmatter `status:` is the lifecycle source of truth — no body Status line; required sections Decision/Context/Consequences); rejected alternatives → the ADR's **Options Considered** table |
14
+ | Module-local decisions / doctrine / glossary / rejected | `docs/modules/{m}/pillars/{decisions,doctrine,glossary,rejected}.md` |
15
+ | Cross-module doctrine / glossary | `docs/architecture/domain-model.md` / `docs/architecture/glossary.md` |
16
+ | `tech.md` deviations | `docs/architecture/references.md` or the relevant ADR |
17
+ | `non-goals.md` / `references.md` | `docs/architecture/{non-goals,references}.md` |
18
+ | `backlog.md` | `docs/history/backlog.md` (P0–P3) |
19
+ | Still-open questions | `docs/architecture/open-questions.md`, each with a *Resolution lands in* pointer |
20
+ | `build.md` (brownfield/hybrid: migration strategy, gotchas, rollout) | the plan doc in `docs/history/plans/` — execution content is plan content, not architecture |
21
+
22
+ Append a changelog entry at `docs/history/changelog/YYYY-MM-DD-brainstorm-harvest-{slug}.md` listing what was written where.
23
+
24
+ ## After harvest
25
+
26
+ Offer to delete the session folder (the dispersed homes now carry the content; the next session primes from them by copying relevant docs into `basis/`). Default to **keep** if the user is unsure — a kept folder simply resumes.
@@ -0,0 +1,37 @@
1
+ # Build
2
+
3
+ This pillar holds **how the work gets executed** — decided during brainstorming, not deferred to plan mode. Migration constraints and execution gotchas discovered late invalidate designs; surfacing them here, while the design is still fluid, is the point.
4
+
5
+ At `start` the skill asks: *"Is this greenfield, or are we changing something that exists?"* The answer sets **Mode** below and opens or closes the execution sections. The sync agent treats this file as a contradiction source — a decision that implies migration work with no entry here raises a question.
6
+
7
+ ## Mode
8
+
9
+ `greenfield` *(default)* | `brownfield` | `hybrid`
10
+
11
+ - **greenfield** — nothing built yet: no production deploys, no users, no legacy code. The execution sections below are closed — mark them *Not applicable (greenfield)*. **Writing posture:** all pillars describe the system as it *will be*, in present tense, as though the chosen design was always the plan. Banned: "replaces" / "supersedes" / "previously" / "formerly" / "no longer" / "originally proposed" / `~~strikethrough~~` / "(resolved)" markers — the reader has no past, so don't reference one. Past tense lives only in `decisions.md`, `rejected.md`, `references.md`, and git history.
12
+ - **brownfield** — the idea changes a system that already exists. All sections below are live, and migration vocabulary — "replaces", "currently", "migrate from" — is legitimate and load-bearing **in this file**. Other pillars still describe the *target* state in present tense; the journey from here to there lives here.
13
+ - **hybrid** — a new surface attached to a live system (a new module in a shipping product). The sections below apply to the seams only: integration points, shared state, contract changes. The new surface itself follows greenfield posture.
14
+
15
+ ## Current state
16
+
17
+ *(brownfield/hybrid)* What exists today, as relevant to the change. Link code paths or docs rather than restating them.
18
+
19
+ ## Target state
20
+
21
+ One or two orienting sentences — cross-reference `thesis.md` rather than duplicating it.
22
+
23
+ ## Migration strategy
24
+
25
+ How we get from current to target: phases, order of operations, coexistence windows, rollback points. Harvest routes this section into the implementation plan.
26
+
27
+ ## Gotchas
28
+
29
+ Execution hazards known *now*, before plan mode — each one a bullet with why it bites:
30
+
31
+ - [e.g. data backfill must run before the new index exists, or reads 404 during the window]
32
+ - [e.g. auth cutover can't be phased — sessions are shared, it's all-or-nothing]
33
+ - [e.g. the event consumer is not idempotent; dual-write window will double-process]
34
+
35
+ ## Rollout & risks
36
+
37
+ Sequencing, feature flags, kill switches, and the failure modes each phase accepts.
@@ -1,42 +1,29 @@
1
1
  # Decisions
2
2
 
3
- This file is the **append-only ledger of resolved decisions**. Every decision the brainstorm produces lands here, with the reasoning that future-you will need to judge edge cases.
3
+ This file is the **append-only ledger of resolved decisions**, with the reasoning future-you needs to judge edge cases.
4
4
 
5
- Each entry has these load-bearing fields:
5
+ Load-bearing fields:
6
6
 
7
- - **Status** — `tentative | committed | rescinded`. New decisions land as `tentative` (no cascade dispatched, question stays open in `questions.md`). User explicitly says "commit" to lock in (cascade fires, question deletes). User explicitly says "rescind" to reverse (cascade reversals if previously committed, question restored if needed).
8
7
  - **Decision** — what was decided, in one or two sentences.
9
- - **Why** — the motivation: a past incident, a constraint, a deadline, a stakeholder ask. Without the *why*, decisions decay into rules no one understands.
10
- - **How to apply** — when and where this decision should shape future work. Without it, decisions get ignored at the boundaries where they matter most.
8
+ - **Why** — the motivation: a constraint, an incident, a deadline, a stakeholder ask. Without the *why*, decisions decay into rules no one understands.
9
+ - **How to apply** — when and where this decision shapes future work. Name the files, layers, or situations where it kicks in.
11
10
 
12
- This file is **append-only**. If a decision is reversed, mark it `STATUS: rescinded` with the reason — do not delete the entry. The ledger is a record of what was true (or proposed) when it was written. It is one of the four places inside the brainstorm folder where past tense is permitted (the others: `rejected.md`, `references.md`, git history).
11
+ There is no tentative/committed state machine. A decision is a decision the moment it's written; the next sync handles its ripples. **Reversing** a decision = append a new decision that names what it reverses, add a one-line `Reversed by D-M (YYYY-MM-DD)` note to the old entry, and let the sync raise questions for anything downstream.
13
12
 
14
- When a question in `questions.md` is resolved, the question is **NOT** immediately deleted — it is marked tentatively-answered (referenced by the new decision's `Resolves Q-N` field). The question is deleted only when the decision transitions to `committed`. This gives the user space to reverse a tentative call without restoring text by hand.
13
+ This ledger is one of the places where past tense is permitted (with `rejected.md`, `references.md`, `build.md`'s execution sections, and git history).
15
14
 
16
15
  ---
17
16
 
18
17
  ## D1 — [Title]
19
18
 
20
- **Status.** tentative
19
+ **Decision.** [What was decided.]
21
20
 
22
- **Decision.** [What was decided, in one or two sentences.]
21
+ **Why.** [The motivation future-you needs.]
23
22
 
24
- **Why.** [The motivation — past incident, constraint, deadline, stakeholder ask. The reason future-you needs to judge edge cases.]
23
+ **How to apply.** [Where this rule kicks in — be specific.]
25
24
 
26
- **How to apply.** [When and where this decision shapes future work. Be specific — name the files, layers, or situations where this rule kicks in.]
25
+ **Resolves.** Q1 (deleted from `questions.md`).
27
26
 
28
- **Resolves.** Q1 (deleted from `questions.md` on commit).
27
+ **Alternatives considered.** *(optional — reference `rejected.md` if substantial)*
29
28
 
30
- **Alternatives considered.** [Optional. Reference `rejected.md` entries if substantial. Skip if obvious.]
31
-
32
- **Committed on.** [YYYY-MM-DD — filled when STATUS transitions to committed. Omit until then.]
33
-
34
- **Rescinded on.** [YYYY-MM-DD with one-line reason — filled when STATUS transitions to rescinded. Omit until then.]
35
-
36
- ---
37
-
38
- ## D2 — [Title]
39
-
40
- (same format)
41
-
42
- ---
29
+ **Reverses.** *(only when this decision reverses an earlier one — name it and say what changed)*
@@ -0,0 +1,25 @@
1
+ # Domain model
2
+
3
+ This file holds the **information architecture** of the idea: entities, relationships, cardinalities, and who owns what state. This is where facts like "an account has many leads" land the moment they're said — they are neither principles (`doctrine.md`) nor terms (`glossary.md`), and they must not be lost to the transcript.
4
+
5
+ The loop's *abstract* step feeds this file: when the user implies structure without naming it, propose the entry here.
6
+
7
+ ## Entities
8
+
9
+ ### [Entity]
10
+
11
+ [One paragraph: what it is, what state it owns, where it lives (client / server / external system).]
12
+
13
+ ## Relationships
14
+
15
+ | From | To | Cardinality | Notes |
16
+ |---|---|---|---|
17
+ | Account | Lead | 1 : N | [e.g. leads cannot transfer between accounts — see D3] |
18
+
19
+ Cardinality changes are design changes — when one shifts, expect the sync to raise questions against routing, storage, and milestones.
20
+
21
+ ## Invariants
22
+
23
+ Rules the model must always satisfy, stated as present-tense facts:
24
+
25
+ - [e.g. Every lead belongs to exactly one account at any point in time.]
@@ -1,12 +1,12 @@
1
1
  # Non-goals
2
2
 
3
- This file is the **explicit list of things we are not building** in the current scope, with the reason for each. It is the mirror of `inventory.md`: where inventory says "this is in scope", non-goals say "this looked plausible but is deliberately out".
3
+ This file is the **explicit list of things we are not building** in the current scope, with the reason for each. It is the mirror of the scope described in `thesis.md`: where the thesis says "this is what we're building", non-goals say "this looked plausible but is deliberately out". Distinguish it from `backlog.md` (things we *will* build, just later) — a non-goal may never be built.
4
4
 
5
5
  Non-goals carry real weight. Every entry here is a constraint that future decisions should respect — a scope-creep guard. The reason field is load-bearing: without it, a non-goal looks like an arbitrary "no" that future-you will second-guess at the first complication. With the reason, future-you knows whether the non-goal still holds when circumstances change.
6
6
 
7
7
  Each non-goal should include a **Re-evaluate when** clause where applicable. Non-goals are not permanent vows; they are scope decisions tied to current constraints. When the constraint changes (a customer signs in a new region, a competitor ships the feature, infrastructure costs drop), the non-goal may move into scope. The re-evaluate clause names the trigger so the team doesn't carry a stale non-goal forever.
8
8
 
9
- At milestone boundaries, scan this file. If a non-goal's re-evaluate trigger has fired, move the item to `inventory.md` with status `build` and delete the non-goal entry here — do not strikethrough, do not leave a "(promoted)" marker.
9
+ At milestone boundaries, scan this file. If a non-goal's re-evaluate trigger has fired, fold the item into scope (`thesis.md`, or `backlog.md` if it's planned-but-later) and delete the non-goal entry here — do not strikethrough, do not leave a "(promoted)" marker.
10
10
 
11
11
  ---
12
12
 
@@ -1,84 +1,35 @@
1
1
  # Open Questions
2
2
 
3
- This file is the **live ledger of unresolved concerns and pending decisions**. Every open design question or worry lives here, and only here — plan docs never host their own questions, they link back to this file with the relevant Q-numbers.
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
4
 
5
- Each question is **self-contained**: full text, optional options, optional lean, and where the resolution lands. You should be able to read any question and understand it without opening another doc. Duplicating one paragraph of context is much cheaper than the cognitive cost of context-switching.
5
+ The session is **coherent** exactly when this file is empty after a fresh sync. That makes hygiene load-bearing:
6
6
 
7
- **In Explore mode**, questions land here in the user's framing — possibly just a concern stated in prose, without Options or Lean. That is fine. Captured concerns are valid questions even when their shape is fuzzy. Resist the urge to immediately structure them into A/B/C/D choices; that happens when the user invites decide-mode for a specific question.
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.
8
10
 
9
- When a question transitions through a decision flow:
10
- 1. User signals "let's resolve Q-N" → enter decide mode for this Q.
11
- 2. Decision lands in `decisions.md` with `STATUS: tentative`. **The question stays here**, in a tentatively-answered state (cross-reference the D-N).
12
- 3. User says "commit D-N" → the question is **deleted** from this file. No strikethrough, no "(resolved)" marker.
13
- 4. User says "rescind D-N" before commit → the question stays, the decision is marked rescinded.
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.
14
12
 
15
- When you add a new question, fill in fields the user has actually given you. Leave others blank. The dependency graph (`Blocks.` field) is computed only when the user moves into decide mode and needs sequencing.
13
+ IDs are `QN`, stable while open, never reused after deletion.
16
14
 
17
15
  ---
18
16
 
19
- ## Conventions
17
+ ## Q1 — [Title in the user's words]
20
18
 
21
- **Group questions by domain** when the list grows past ~10 entries. Use `## Category` headers (e.g. `## Data & Schema`, `## Runtime`, `## Connectors`). Categories are project-specific.
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.]
22
20
 
23
- **Question ID** is `QN` where N is the next free integer. IDs are stable for as long as the question is open. On commit-delete, the ID disappears with the question — do not reuse it.
21
+ **Raised by.** user | sync | probe
24
22
 
25
- **Question body fields** (per entry):
23
+ **Closes when.** [What resolution looks like — a pick between options, a confirmed fact, an accepted risk.]
26
24
 
27
- - **Question.** Full prose, may be multi-paragraph. Repeat context if needed. Use the user's words when capturing in Explore mode.
28
- - **Options.** *(optional)* Lettered list (A / B / C ...) with one-line description per option. Add only when the user has named alternatives, or when entering decide mode for this question.
29
- - **Lean.** *(optional)* The user's current preference, one line. Drop the field entirely if no lean exists yet — don't write "none yet". This field reflects the user's lean, not the skill's.
30
- - **Claude's take.** *(optional, OPT-IN ONLY)* The skill's recommendation. **Default: do not write this field.** Emit only when the user explicitly asks "what would you recommend?" / "what's your take?" / similar on this specific question. Phrase as a clear pick + one-line reason. If the field becomes stale (options change, scope shifts), delete it.
31
- - **Tentatively answered by.** *(only when a tentative D-N exists)* Cross-reference to `decisions.md`. Marks the question as in-flight rather than fully open.
32
- - **Resolution lands in.** *(optional)* Target doc + section (e.g. `04-runtime.md §3.2`). Tells the resolver where to absorb the answer.
33
- - **Blocks.** *(optional)* Milestone (`M0`) or other Q-numbers (`Q5, Q7`) that depend on this. Empty = "nothing yet".
25
+ **Options.** *(optional — only when named by the user or entering converge)*
26
+ - A. [one line]
27
+ - B. [one line]
34
28
 
35
- **Extension sections** beyond plain questions are allowed:
29
+ **Lean.** *(optional — the user's lean, never the skill's)*
36
30
 
37
- - `## Upstream feature requests` — items that aren't questions we own; they're features we need from upstream services. Use IDs like `U-foo-1`.
38
- - `## Doc completeness follow-ups` — work items that aren't decisions but should not be forgotten. Use IDs like `F1`, `F2`.
31
+ **Claude's take.** *(opt-in only — write only when the user asks for a recommendation on this question; delete if it goes stale)*
39
32
 
40
- Resolved upstream-feature items and completed follow-ups get **deleted**, same rule as questions.
33
+ **Resolution lands in.** *(optional)* [pillar or doc + section]
41
34
 
42
- ---
43
-
44
- ## Q1 — [Title]
45
-
46
- **Question.** [Full text. Repeat context if needed — a future reader should be able to read this without leaving the doc. In Explore mode, capture in the user's words.]
47
-
48
- **Options.** *(optional — only when named by the user or entering decide mode)*
49
- - A. [Option A — what it means in one line]
50
- - B. [Option B]
51
- - C. [Option C]
52
-
53
- **Lean.** *(optional, user's lean)*
54
-
55
- **Tentatively answered by.** *(only if a tentative D-N exists)*
56
-
57
- **Resolution lands in.** *(optional)* `XX-foo.md` §N
58
-
59
- **Blocks.** *(optional)* [Milestone (e.g., M0) or other Q-numbers.]
60
-
61
- ---
62
-
63
- ## Q2 — [Title]
64
-
65
- (same format)
66
-
67
- ---
68
-
69
- ## Upstream feature requests *(optional section)*
70
-
71
- ### U-foo-1 — [Title]
72
-
73
- [What we want from the upstream service.]
74
-
75
- **Why we want it.** [Reason — usually a current workaround that's costing us something.]
76
-
77
- [Current status: filed, tracked, ETA, etc.]
78
-
79
- ---
80
-
81
- ## Doc completeness follow-ups *(optional section)*
82
-
83
- - **F1** — [Doc name + one-line scope]. [What triggers writing it.] [Which milestone it must precede.]
84
- - **F2** — [Doc name + one-line scope]. [What triggers writing it.] [Which milestone it must precede.]
35
+ **Blocks.** *(optional)* [milestone or other Q-numbers]
@@ -1,8 +1,8 @@
1
1
  STATE: open
2
- MODE: explore
2
+ MODE: dump
3
+ COMPLEXITY: medium
3
4
  SESSION STARTED: YYYY-MM-DD
4
5
  SESSION CLOSED:
5
- CURRENT TOPIC: (one-line summary — updated on every detected topic shift)
6
- LAST UPDATED: YYYY-MM-DD
7
- CASCADE LOCKS: none
8
- CASCADE QUEUE: none
6
+ CURRENT TOPIC: (one line — updated on every topic shift)
7
+ LAST SYNC: never
8
+ SYNC IN FLIGHT: no
@@ -0,0 +1,23 @@
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.]
@@ -0,0 +1,21 @@
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.
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: distiller
3
+ description: The one place the raw event ledger is distilled into colder, higher-signal knowledge. One skill, two modes — `facts` (ledger → 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. NEVER edits the ledger (append-only audit substrate); NEVER auto-runs (invoked by the user, or by accepting the maintenance agent's threshold proposal). Triggers on "distill the ledger", "distill facts", "run the distiller", "distill into fact-cards", "facts from the ledger", "distill docs", "draft doc deltas", "distiller docs", "/distiller".
4
+ plugins: [code]
5
+ ---
6
+
7
+ # Distiller Skill
8
+
9
+ The one place a repo's **raw event ledger** is distilled into colder,
10
+ higher-signal knowledge. The ledger (`docs/memory/ledger/`) is a high-volume,
11
+ append-only stream of what happened; the distiller turns the *surprising* part of
12
+ it into atomic, self-contained, provenance-linked **fact-cards**
13
+ (`docs/memory/facts/`). Volume falls, signal rises — one hop down the
14
+ distillation gradient.
15
+
16
+ The methods this skill runs are **stolen, not invented** — surprisal-based
17
+ retention (Nemori), non-lossy EDU fact-cards (EMem), per-fact ADD/UPDATE/
18
+ SUPERSEDE/NOOP reconciliation (Mem0), and bi-temporal supersession (Zep/
19
+ Graphiti). They live in exactly one place:
20
+
21
+ > **Read [`references/method.md`](references/method.md) first, in every mode.**
22
+ > It carries the four mechanisms verbatim-grade (with their arXiv ids — provenance
23
+ > matters here of all places), the fact-card format, and the high-water-mark
24
+ > protocol. This SKILL.md only orchestrates those mechanisms over the ledger.
25
+
26
+ The reason the two modes are one skill: **both read one algorithm root.** Two
27
+ components would mean two copies of the surprisal/reconciliation/supersession
28
+ model that drift apart. One skill, one spec, zero drift.
29
+
30
+ ---
31
+
32
+ ## Picking a mode
33
+
34
+ | You want to… | Mode | Ships |
35
+ |---|---|---|
36
+ | Distill new ledger entries into fact-cards | **`facts`** | now |
37
+ | Distill fact-cards into proposed doc deltas | **`docs`** | now — procedure in [`references/docs-mode.md`](references/docs-mode.md) |
38
+
39
+ Both modes read [`references/method.md`](references/method.md); each mode's
40
+ procedure that sequences it lives in its own section below.
41
+
42
+ ## Execution model — pure skill, never autonomous
43
+
44
+ Runs **in the main session** — no standing sub-agents, no daemon, no cron, no
45
+ watch. The harness **proposes**, the user **disposes**: this skill runs only when
46
+ the user asks, when the **Completion flow** invokes it (the kit `CLAUDE.md`
47
+ Completion step distils the just-finished plan's trail — `facts` if the ledger
48
+ advanced past the high-water mark, then `docs` which still drafts for approval),
49
+ or when the user accepts the `maintenance` agent's threshold proposal (≥20
50
+ undigested ledger entries). It never schedules itself, never runs on a hook,
51
+ never gates anything.
52
+
53
+ ---
54
+
55
+ ## Mode: `facts` (ledger → fact-cards)
56
+
57
+ Distill ledger entries newer than the high-water mark into fact-cards. The
58
+ algorithm is in [`references/method.md`](references/method.md) — follow it; this
59
+ section is the procedure that sequences it.
60
+
61
+ 1. **Read the high-water mark.** Read `docs/memory/facts/.digest-state.md`
62
+ (the frontmatter records the last-digested file + entry timestamp **per
63
+ actor**). Absent ⇒ start of history — digest everything. Then collect ledger
64
+ entries from `docs/memory/ledger/YYYY-MM-DD--{actor}.md` that are **newer**
65
+ than the per-actor mark. This is what makes the run idempotent — an immediate
66
+ second run finds nothing new and is a clean NOOP.
67
+
68
+ **Thin or empty ledger ⇒ not a NOOP — distill the decision record.** A
69
+ commit-less project, or one whose ledger holds only mechanical entries the
70
+ surprisal filter would drop (a transient exit-1, a bare echo), is **not** a
71
+ project with nothing to distill. Architecture decisions and reversals are
72
+ high-surprisal by nature — they exist precisely because they were *not*
73
+ predictable. So when the ledger is thin/empty, also ingest, as surprisal
74
+ sources for steps 2–5:
75
+ - `docs/architecture/adr/**` — each accepted ADR is a surprising decision
76
+ (its `Decision` + **Options Considered** — the rejected-alternatives section
77
+ in the `documentation` §10 ADR template — carry the prediction error).
78
+ - `docs/memory/brainstorm.md` — owner pronouncements, reversals, and
79
+ discovered constraints recorded outside the ledger.
80
+ Provenance points at the ADR file / brainstorm section, not a ledger
81
+ `#HH:MM`. These sources sit outside the high-water mark (which tracks ledger
82
+ entries only) — reconciliation (step 4) is what keeps a re-run idempotent.
83
+
84
+ 2. **Surprisal filter → survivors** (method §1). Present the LLM with (a) a
85
+ summary of the *current* fact-set, (b) the new undigested entries. Keep only
86
+ what is **surprising** relative to (a). Predictable ⇒ dropped — no importance
87
+ scores, no templates, prediction error is the only retention signal. (First
88
+ run, empty fact-set: "predictable" = mechanical routine — a bare commit echo,
89
+ a transient exit-1 with no lesson; keep decisions, reversals, discovered
90
+ constraints, and failures with a cause worth remembering.)
91
+
92
+ 3. **Decompose survivors → EDU candidates** (method §2). Each survivor becomes
93
+ **one** atomic, self-contained statement — entity names normalized, minimal
94
+ context baked into the sentence — with `provenance` pointing back at the
95
+ ledger entries/commits it derives from. Decomposition, **not** summarization:
96
+ one survivor may yield several cards; no information is destroyed; everything
97
+ stays auditable back to raw.
98
+
99
+ 4. **Reconcile each candidate against existing cards** (method §3). Query the
100
+ existing facts for semantic neighbors, then decide
101
+ **ADD / UPDATE / SUPERSEDE / NOOP** for each. Gate on the **facts collection
102
+ holding cards**, not on the binary and not on the collection merely being
103
+ registered — a present qmd whose `{repo}-facts` collection is *registered but
104
+ empty* (the common first run: harness-manager's baseline registers + embeds
105
+ `{repo}-facts` over a freshly-scaffolded `docs/memory/facts/` that has zero
106
+ cards) returns nothing useful, exactly like an unregistered collection.
107
+ - **Cards present** — `command -v qmd` succeeds **and** `docs/memory/facts/`
108
+ holds at least one card (`docs/memory/facts/*.md` is non-empty): query
109
+ scoped to the collection —
110
+ `qmd query --format json -c {repo}-facts "<the candidate statement>"`.
111
+ `{repo}` = the repo's directory name. Scope with `-c` always. (If the query
112
+ returns nothing on a non-empty card set, the collection is registered-but-
113
+ unbuilt — treat that as the fallback trigger below, not as "no neighbors.")
114
+ - **No cards to query** (qmd absent, **or** `docs/memory/facts/*.md` empty —
115
+ the common first-run case where the facts dir was scaffolded but never
116
+ distilled into): fall back to filename/grep similarity over
117
+ `docs/memory/facts/*.md` (slug overlap + entity-name grep), and **note in
118
+ the report** that reconciliation ran without semantic retrieval. Step 7
119
+ builds/refreshes the collection at the end of the run, so the *next* run —
120
+ with cards now present — reconciles semantically.
121
+
122
+ 5. **Write the results** (method §4 — bi-temporal, never delete).
123
+ - **ADD** → write a new card `docs/memory/facts/{kebab-slug}.md` in the format.
124
+ - **UPDATE** → refine the existing card's statement in place; extend its
125
+ `provenance`.
126
+ - **SUPERSEDE** → on the **old** card set `invalid_from:` (today) and
127
+ `superseded_by:` (the new fact-id); write the **new** card with `valid_from:`
128
+ today. The old file stays in place — history stays queryable, nothing
129
+ vanishes.
130
+ - **Self-contained reversal** (one input states X-then-reversed-to-Y, no old
131
+ card exists yet) → emit **two** cards in one pass: a born-invalid historical
132
+ card for X (`invalid_from` + `superseded_by` → Y) and a live card for Y.
133
+ See method.md §4 "Self-contained reversal."
134
+ - **NOOP** → the fact is already known; write nothing.
135
+
136
+ 6. **Update the high-water mark LAST.** Only after every card is written, update
137
+ `docs/memory/facts/.digest-state.md` to the newest entry timestamp processed
138
+ per actor. Last, so a crash mid-run re-digests rather than silently skips.
139
+
140
+ 7. **If qmd present, refresh the lens** (skip silently if `command -v qmd`
141
+ finds nothing):
142
+ - `qmd collection add docs/memory/facts --name {repo}-facts` — register the
143
+ collection. Re-adding an existing name exits non-zero with "already exists";
144
+ **treat that as success.** Never register `docs/memory` as a whole — that
145
+ would sweep the raw ledger into the index.
146
+ - `qmd context add qmd://{repo}-facts/ "Distilled recent decisions/observations, provenance-linked"`
147
+ — upserts, safe to re-run.
148
+ - `qmd update` — **re-scan the collection for the cards you just wrote.** In
149
+ qmd 2.5.3 `collection add` on an already-registered name does NOT re-index
150
+ new files; `qmd update` is what picks them up. Skipping this leaves the new
151
+ cards invisible to retrieval (and to distill②).
152
+ - `qmd embed -c {repo}-facts` — regenerate vectors for the new/changed cards.
153
+ Always scope with `-c`; a bare `qmd embed` re-embeds every repo on the
154
+ machine.
155
+
156
+ 8. **Report** (in chat). Cards added / updated / superseded / NOOP'd, the
157
+ high-water mark advance per actor, and — if reconciliation fell back to
158
+ filename/grep (qmd absent, **or** `{repo}-facts` registered but holding no
159
+ cards yet — the first-run-on-a-scaffolded-facts-dir case) — the one advisory
160
+ line saying so. Fire this advisory whenever semantic retrieval was effectively
161
+ unavailable, even if the collection appeared in `qmd collection list`: a
162
+ registered-but-empty collection is not semantic retrieval. A run that finds
163
+ nothing surprising is **valid**: say
164
+ "no surprising entries since the mark," don't pad.
165
+
166
+ ---
167
+
168
+ ## Mode: `docs` (fact-cards → proposed doc deltas)
169
+
170
+ Distill fact-cards into proposed edits to `docs/` (the cold, human-owned SSoT) —
171
+ the next hop down the gradient from `facts`. The full procedure (input selection,
172
+ delta drafting, digestion marking, the pillar escape valve, trigger wiring) is in
173
+ [`references/docs-mode.md`](references/docs-mode.md) — follow it; reuses
174
+ `method.md`'s surprisal / EDU / reconciliation / bi-temporal vocabulary unchanged.
175
+ It NEVER writes a doc on its own — it **drafts and presents**; the human disposes.
176
+
177
+ ---
178
+
179
+ ## Rules
180
+
181
+ - **One algorithm root.** Every mode reads `references/method.md`. Extend the
182
+ spec there — never inline a divergent copy of the four methods in a mode.
183
+ - **Never edit the ledger.** `docs/memory/ledger/` is the append-only audit
184
+ substrate. The distiller is **read-only** on it — no deletes, no rewrites, no
185
+ reordering, not even archiving. It writes only under `docs/memory/facts/`.
186
+ - **Idempotent via the mark.** The high-water mark is the only thing standing
187
+ between this skill and reprocessing. Read it first, advance it last; a second
188
+ run with no new entries is a NOOP.
189
+ - **Cards are atomic and provenance-linked.** One self-contained statement per
190
+ card; every card resolves back to the ledger entries/commits it came from.
191
+ Decompose — never lossily summarize.
192
+ - **Supersede ≠ delete.** Bi-temporal only: set `invalid_from:` +
193
+ `superseded_by:` on the old card and leave the file in place. Nothing is ever
194
+ removed from `docs/memory/facts/`.
195
+ - **Findings are questions, not verdicts.** When a candidate appears to
196
+ contradict an existing card, phrase it as a question (is X now superseded by
197
+ Y?) — never assert staleness or falsity.
198
+ - **Never auto-runs.** Invoked by the user, by the Completion flow (kit
199
+ `CLAUDE.md`), or by accepting the maintenance proposal. No cron, no daemon, no
200
+ hook self-trigger — a workflow step the agent runs is not a self-trigger.
201
+ - **qmd is strictly optional.** Guard every qmd call on `command -v qmd`; absent
202
+ ⇒ silent skip. Reconciliation additionally needs the `{repo}-facts` collection
203
+ to hold **cards** — not merely to be registered. "Unbuilt" here means **no
204
+ cards** (`docs/memory/facts/*.md` empty), not "unregistered": harness-manager's
205
+ baseline registers + embeds `{repo}-facts` over an empty scaffolded facts dir,
206
+ so on a qmd-equipped, freshly-baselined repo the collection *appears in*
207
+ `qmd collection list` yet indexes zero cards. Binary present but collection
208
+ cardless (first run) falls back to filename/grep too, same as qmd being absent.
209
+
210
+ ## Output locations
211
+
212
+ - `facts` writes fact-cards to `docs/memory/facts/{kebab-slug}.md` and the
213
+ high-water mark to `docs/memory/facts/.digest-state.md`; the report goes to
214
+ chat. It reads `docs/memory/ledger/` but never writes there.
215
+ - `docs` presents doc deltas in chat and, **only on approval**, writes the accepted
216
+ prose into the target doc under `docs/` (+ a new `docs/history/changelog/` entry if
217
+ drafted) and appends `digested_into:` to each consumed card in `docs/memory/facts/`.
218
+ See [`references/docs-mode.md`](references/docs-mode.md).