@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.
- package/README.md +28 -7
- package/bin/etna.js +58 -1
- package/data/harnesses/code/CLAUDE.md +11 -16
- package/data/harnesses/code/agents/brainstorm.md +13 -20
- package/data/harnesses/code/agents/change-review.md +5 -5
- package/data/harnesses/code/agents/harness-manager.md +49 -9
- package/data/harnesses/code/agents/maintenance.md +42 -5
- package/data/harnesses/code/agents/spec-mode.md +1 -1
- package/data/harnesses/code/harness.json +6 -0
- package/data/harnesses/code/hooks/hooks.json +41 -0
- package/data/harnesses/code/hooks/lib/redact.js +37 -0
- package/data/harnesses/code/hooks/post-commit.js +136 -0
- package/data/harnesses/code/hooks/post-tool-failure.js +136 -0
- package/data/harnesses/code/hooks/session-stop.js +207 -0
- package/data/harnesses/code/skills/boundary/SKILL.md +21 -3
- package/data/harnesses/code/skills/boundary/references/boundary-taxonomy.md +229 -161
- package/data/harnesses/code/skills/brainstorm/README.md +76 -227
- package/data/harnesses/code/skills/brainstorm/SKILL.md +78 -566
- package/data/harnesses/code/skills/brainstorm/harvest.md +26 -0
- package/data/harnesses/code/skills/brainstorm/templates/build.md +37 -0
- package/data/harnesses/code/skills/brainstorm/templates/decisions.md +12 -25
- package/data/harnesses/code/skills/brainstorm/templates/domain-model.md +25 -0
- package/data/harnesses/code/skills/brainstorm/templates/non-goals.md +2 -2
- package/data/harnesses/code/skills/brainstorm/templates/questions.md +18 -67
- package/data/harnesses/code/skills/brainstorm/templates/state.md +5 -5
- package/data/harnesses/code/skills/brainstorm/templates/tech.md +23 -0
- package/data/harnesses/code/skills/brainstorm/templates/thesis.md +21 -0
- package/data/harnesses/code/skills/distiller/SKILL.md +218 -0
- package/data/harnesses/code/skills/distiller/references/docs-mode.md +526 -0
- package/data/harnesses/code/skills/distiller/references/method.md +166 -0
- package/data/harnesses/code/skills/documentation/references/templates.md +78 -5
- package/data/harnesses/kb-agi/CLAUDE.md +1 -1
- package/data/harnesses/kb-agi/agents/kb-agi-init.md +2 -2
- package/data/harnesses/kb-agi/agents/kb-catch-stale.md +1 -1
- package/data/harnesses/kb-agi/agents/kb-find-gaps.md +2 -2
- package/data/harnesses/kb-agi/agents/kb-fix-links.md +1 -1
- package/data/harnesses/kb-agi/agents/kb-refresh-frontdoor.md +1 -1
- package/data/harnesses/kb-agi/agents/kb-refresh-summaries.md +1 -1
- package/data/harnesses/kb-agi/agents/kb-sync-metadata.md +1 -1
- package/data/harnesses/kb-agi/harness.json +6 -0
- package/package.json +1 -1
- package/data/harnesses/code/skills/brainstorm/greenfield.md +0 -99
- 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
|
|
3
|
+
This file is the **append-only ledger of resolved decisions**, with the reasoning future-you needs to judge edge cases.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
|
10
|
-
- **How to apply** — when and where this decision
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
19
|
+
**Decision.** [What was decided.]
|
|
21
20
|
|
|
22
|
-
**
|
|
21
|
+
**Why.** [The motivation future-you needs.]
|
|
23
22
|
|
|
24
|
-
**
|
|
23
|
+
**How to apply.** [Where this rule kicks in — be specific.]
|
|
25
24
|
|
|
26
|
-
**
|
|
25
|
+
**Resolves.** Q1 (deleted from `questions.md`).
|
|
27
26
|
|
|
28
|
-
**
|
|
27
|
+
**Alternatives considered.** *(optional — reference `rejected.md` if substantial)*
|
|
29
28
|
|
|
30
|
-
**
|
|
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 `
|
|
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,
|
|
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 **
|
|
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
|
-
|
|
5
|
+
The session is **coherent** exactly when this file is empty after a fresh sync. That makes hygiene load-bearing:
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13
|
+
IDs are `QN`, stable while open, never reused after deletion.
|
|
16
14
|
|
|
17
15
|
---
|
|
18
16
|
|
|
19
|
-
##
|
|
17
|
+
## Q1 — [Title in the user's words]
|
|
20
18
|
|
|
21
|
-
**
|
|
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
|
-
**
|
|
21
|
+
**Raised by.** user | sync | probe
|
|
24
22
|
|
|
25
|
-
**
|
|
23
|
+
**Closes when.** [What resolution looks like — a pick between options, a confirmed fact, an accepted risk.]
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
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
|
-
**
|
|
29
|
+
**Lean.** *(optional — the user's lean, never the skill's)*
|
|
36
30
|
|
|
37
|
-
|
|
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
|
-
|
|
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:
|
|
2
|
+
MODE: dump
|
|
3
|
+
COMPLEXITY: medium
|
|
3
4
|
SESSION STARTED: YYYY-MM-DD
|
|
4
5
|
SESSION CLOSED:
|
|
5
|
-
CURRENT TOPIC: (one
|
|
6
|
-
LAST
|
|
7
|
-
|
|
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).
|