@nurix/etna 0.20.6 → 0.20.8

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 (55) hide show
  1. package/README.md +1 -1
  2. package/data/catalog/rules/testing.md +1 -0
  3. package/data/harness-index.json +1 -1
  4. package/data/harnesses/code/agents/lld-mode.md +3 -3
  5. package/data/harnesses/code/agents/maintenance.md +5 -4
  6. package/data/harnesses/code/codex/agents/lld-mode.toml +2 -2
  7. package/data/harnesses/code/codex/agents/maintenance.toml +3 -3
  8. package/data/harnesses/code/harness.json +2 -2
  9. package/data/harnesses/code/hooks/awareness.mjs +22 -3
  10. package/data/harnesses/code/hooks/lib/ownership.mjs +39 -4
  11. package/data/harnesses/code/rules/testing.md +1 -0
  12. package/data/harnesses/code/skills/brainstorm/README.md +1 -1
  13. package/data/harnesses/code/skills/brainstorm/references/doctrine.md +22 -0
  14. package/data/harnesses/code/skills/brainstorm/references/harvest.md +34 -0
  15. package/data/harnesses/code/skills/design/SKILL.md +1 -1
  16. package/data/harnesses/code/skills/design/components/index.md +1 -1
  17. package/data/harnesses/code/skills/documentation/SKILL.md +2 -2
  18. package/data/harnesses/code/skills/documentation/references/frontmatter.md +13 -6
  19. package/data/harnesses/code/skills/documentation/templates/plan.md +4 -1
  20. package/data/harnesses/code/skills/issue-bridge/SKILL.md +1 -1
  21. package/data/harnesses/code/skills/testing/SKILL.md +1 -1
  22. package/data/harnesses/delivery/agents/lld-mode.md +3 -3
  23. package/data/harnesses/delivery/agents/maintenance.md +5 -4
  24. package/data/harnesses/delivery/codex/agents/lld-mode.toml +2 -2
  25. package/data/harnesses/delivery/codex/agents/maintenance.toml +3 -3
  26. package/data/harnesses/delivery/harness.json +2 -2
  27. package/data/harnesses/delivery/hooks/generate-ownership-index.mjs +7 -4
  28. package/data/harnesses/delivery/skills/brainstorm/README.md +1 -1
  29. package/data/harnesses/delivery/skills/brainstorm/references/doctrine.md +22 -0
  30. package/data/harnesses/delivery/skills/brainstorm/references/harvest.md +34 -0
  31. package/data/harnesses/delivery/skills/documentation/SKILL.md +2 -2
  32. package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +13 -6
  33. package/data/harnesses/delivery/skills/documentation/templates/plan.md +4 -1
  34. package/data/harnesses/discover/harness.json +1 -1
  35. package/data/harnesses/discussion/CLAUDE.md +1 -1
  36. package/data/harnesses/discussion/harness.json +2 -2
  37. package/data/harnesses/etna/CLAUDE.md +16 -16
  38. package/data/harnesses/etna/agents/lld-mode.md +3 -3
  39. package/data/harnesses/etna/agents/maintenance.md +5 -4
  40. package/data/harnesses/etna/codex/agents/lld-mode.toml +2 -2
  41. package/data/harnesses/etna/codex/agents/maintenance.toml +3 -3
  42. package/data/harnesses/etna/harness.json +2 -2
  43. package/data/harnesses/etna/hooks/generate-ownership-index.mjs +18 -9
  44. package/data/harnesses/etna/rules/testing.md +1 -0
  45. package/data/harnesses/etna/skills/brainstorm/README.md +1 -1
  46. package/data/harnesses/etna/skills/brainstorm/references/doctrine.md +22 -0
  47. package/data/harnesses/etna/skills/brainstorm/references/harvest.md +34 -0
  48. package/data/harnesses/etna/skills/documentation/SKILL.md +2 -2
  49. package/data/harnesses/etna/skills/documentation/references/frontmatter.md +13 -6
  50. package/data/harnesses/etna/skills/documentation/templates/plan.md +4 -1
  51. package/data/harnesses/hr/harness.json +1 -1
  52. package/data/harnesses/kb-agi/harness.json +1 -1
  53. package/lib/codex/README.md +1 -1
  54. package/lib/codex/validate-codex-agents.mjs +1 -1
  55. package/package.json +2 -2
@@ -142,15 +142,18 @@ function buildIndex(projectDir) {
142
142
  const keywords = asList(field(block, "keywords"));
143
143
  indexed += 1;
144
144
  const doc = path.relative(projectDir, file).split(path.sep).join("/");
145
+ // METADATA ONCE PER DOC, NOT ONCE PER GLOB — kept in step with lib/ownership.mjs buildIndex(),
146
+ // which the parity suite pins. Re-emitting the keyword array beside every glob made the index
147
+ // grow with the dependency graph rather than the doc count.
145
148
  const kw = keywords.length ? `[${keywords.join(", ")}]` : "[]";
146
- for (const glob of owns) {
147
- lines.push(`${glob} → ${doc} · ${moduleLabel} · ${kw} · ${status}`);
148
- }
149
+ lines.push(
150
+ `${doc} · ${moduleLabel} · ${status}\n ${kw}\n` + owns.map((glob) => ` ${glob}`).join("\n")
151
+ );
149
152
  }
150
153
  if (lines.length === 0) return "";
151
154
  lines.sort();
152
155
  const header =
153
- "# Ownership index (generated) — code-path glob → owning feature doc · module · keywords · status\n" +
156
+ "# Ownership index (generated) — owning feature doc · module · status, then the code regions it governs.\n" +
154
157
  "# Match the file or topic you're about to touch, then read the owning doc before changing it.\n";
155
158
  // A doc dropped by a parse failure is indistinguishable from one that owns nothing,
156
159
  // so report the tally: seen vs indexed is the only signal a silent skip leaves.
@@ -6,7 +6,7 @@ correctly, the record stays readable at any pause, and the session ends with a h
6
6
  complete that the session folder is useless — it moves to history and the user deletes or
7
7
  commits it.
8
8
 
9
- > Spec (single source of truth): `docs/architecture/skills/brainstorm/` in the etna repo. The etna
9
+ > Spec (single source of truth): `docs/skills/brainstorm/` in the etna repo. The etna
10
10
  > copy of this skill (`skills/brainstorm/`) is canonical; the user-level path
11
11
  > `~/.claude/skills/brainstorm/` is a symlink to it.
12
12
 
@@ -96,3 +96,25 @@ These bind every writer of the working surface — facilitator (ops), twin (ever
96
96
  5. **Directional steering is not feature truth.** A harvested principle informs many decisions
97
97
  and changes slowly; a feature fact belongs to its feature doc and changes at code speed. The
98
98
  harvest links between the two layers, never duplicates prose across them.
99
+
100
+ ## Field lessons — capture and converge
101
+
102
+ Measured on live sessions; each is the rule, distilled.
103
+
104
+ - **Separate user voice from facilitator re-derivation before ledgering** when both arrive in one
105
+ turn. Only the user's half is ledgerable; a re-derivation that quietly re-reads an existing
106
+ commitment into a new shape is caught as a fork with a named amendment to the affected `D#`,
107
+ never as an edit — a commitment recorded in the user's words is amended only by the user.
108
+ - **Stamp in UTC, always** — take the stamp from `date -u`, never from the runtime's injected
109
+ local date; the two disagree for part of every working day in some offsets, and a local-date
110
+ stamp runs the ledger a day ahead of capture.
111
+ - **Run the blindspot pass immediately after a converge burst, before the manifest gate.** When
112
+ several picks land in one sitting the strain migrates *between* them — collisions between
113
+ commitments taken within the same hour, found only by reading two together. The burst is the
114
+ cheapest moment to catch them and the last before they freeze.
115
+ - **When a pick answers three of a fork's four close-condition clauses, close it and file the
116
+ residue as a new fork in the same pass** — closing silently and leaving it open both lose
117
+ information; name the unanswered clause in the resolving entry and carry it under a new `F#`.
118
+ - **When a ledger entry's `Why` contains "already", verify it before the manifest.** A commitment
119
+ that declines to build something because it already exists rests on a fact, not a judgment, and
120
+ the stop gate is the last place that fact is checkable — after the folder moves it is not.
@@ -84,3 +84,37 @@ Then the offer, relayed by the facilitator: **delete the moved folder, or commit
84
84
  decides; the design assumes deletion — nothing anywhere may depend on the folder surviving.
85
85
  Committing is the explicit way to keep git-history forensics; an uncommitted folder deleted here
86
86
  is gone, and that residual is accepted by design.
87
+
88
+ ## Field lessons — apply
89
+
90
+ Measured on live harvests; each is the rule, distilled.
91
+
92
+ - **Run the move gate clean immediately before the move and treat that as the verdict** — the
93
+ gate's checks read `ops/`, which the move deletes, so a post-move re-run fails by construction.
94
+ Verify the post-move state by hand: `Status: harvested`, no `ops/`, both index rows,
95
+ `check-links` clean. And **distil the process record into `state.md` before the move** — the
96
+ sync log dies with `ops/`, and a ten-line dispatch summary is what separates a record that shows
97
+ its workings from one that asserts its commitments from nowhere.
98
+ - **Uncommitted harvest artifacts are deleted, not deprecated** — deprecate-in-place protects
99
+ readers holding a pointer, and a never-committed file has none; a tombstone for a
100
+ twenty-minute-old file is ceremony.
101
+ - **Audit the *other* decision records for impact at manifest time** — the manifest routes
102
+ through the docs the commitments name and will not think of the neighbours. The test is
103
+ commitment-level, not textual (grep finds nothing); read each AD's Decision and Open Questions
104
+ against the new commitments, and report the unimpacted verdicts with evidence too.
105
+ - **A manifest enumerates sites; the file you open contains more of them.** Fix every
106
+ same-doctrine site inside a file the manifest already opens — a self-contradicting document is
107
+ worse than a wider diff — but never wander into an unnamed file: that becomes a backlog row.
108
+ Say which you did, per row, in the application record.
109
+ - **Re-run the uselessness test when the destination *set* changes**, not only when content does —
110
+ a consolidation that moves referrers produces a document set no cold reader has graded. The
111
+ recurring find: a truth doc stating a decided-but-unbuilt mechanism in the present tense; the
112
+ fix is one clause naming the unbuilt prerequisite.
113
+ - **Retirement wording beats suppression.** doc-lint's heuristics block the honest retirement
114
+ sentence (`superseded by`, `no longer`); rephrase with no loss of meaning — "retired by
115
+ AD-00N", "is not the product's position", "lives in a package that is gone" — and reach for
116
+ `doc-lint-disable` only when the banned phrase is itself the claim.
117
+ - **Amending another session's draft: change the contradicting claim everywhere it is asserted,
118
+ and nothing else** — a claim stated in nine places and fixed in three leaves the doc worse; a
119
+ one-paragraph note saying what moved and what did not keeps the amendment legible to the doc's
120
+ author.
@@ -24,7 +24,7 @@ Document contracts and architecture, not implementation details. Show function s
24
24
 
25
25
  The journey — dates, migrations, progress, supersessions — lives only in records (`docs/history/`); when reality changes, a truth document simply becomes correct. The full strip-list and the record-side rules: [`references/style-guide.md`](references/style-guide.md) § The Two Natures — Tense & History.
26
26
 
27
- The mechanical subset of these rules is checked by [`scripts/doc-lint.mjs`](scripts/doc-lint.mjs) — run it over changed docs before finishing. Block-severity findings must be fixed; `<!-- doc-lint-disable: <id> -->` with a reason beside it is the escape hatch.
27
+ The mechanical subset of these rules is checked by [`scripts/doc-lint.mjs`](scripts/doc-lint.mjs) — run it over changed docs before finishing. Block-severity findings must be fixed; `<!-- doc-lint-disable: <id> -->` with a reason beside it is the escape hatch. Two invocation traps: the disable comment takes **bare rule ids only** — prose inside the comment silently breaks the parse, so put the reason in a second comment beside it — and the script grades truth-vs-record scope by matching `docs/(history|research|memory)/` against **the path string it is given**, so always hand it a repo-root-relative path; linting a record from inside its own folder grades it as a truth document.
28
28
 
29
29
  ---
30
30
 
@@ -85,7 +85,7 @@ The two guarantees are complementary and both break on mixing: a reader holding
85
85
  | Derive/update a feature doc's `owns:` | [`references/frontmatter.md`](references/frontmatter.md) |
86
86
  | Update existing doc | § Editing an existing doc (below) |
87
87
  | Instruction file (`CLAUDE.md`, a rule, an agent, skill prose) | [`references/instruction-style.md`](references/instruction-style.md) — not these templates; every-load prose stays lean, and the present-tense rule still applies |
88
- | Author or restructure a **skill** (fit, triggers, shape, anatomy, shipping) | The skill-authoring field guide — the `etna` repo (`nurixlabs/etna`), folder `docs/architecture/skills/playbook/`, chapters `00`–`10`. **No etna checkout available ⇒ ask the user for access to the etna repo, or proceed from first principles** with [`references/instruction-style.md`](references/instruction-style.md) governing the prose. This row is the only place the playbook is referenced |
88
+ | Author or restructure a **skill** (fit, triggers, shape, anatomy, shipping) | The skill-authoring field guide — the `etna` repo (`nurixlabs/etna`), folder `docs/playbooks/skills/`, chapters `00`–`10`. **No etna checkout available ⇒ ask the user for access to the etna repo, or proceed from first principles** with [`references/instruction-style.md`](references/instruction-style.md) governing the prose. This row is the only place the playbook is referenced |
89
89
  | **No template matches** | [`references/untemplated.md`](references/untemplated.md) — shape decisions + the co-authoring workflow |
90
90
 
91
91
  ---
@@ -24,13 +24,20 @@ A feature doc's ownership frontmatter is `owns:` (required), `module:` (optional
24
24
 
25
25
  **1. `owns:`** (the path scope)
26
26
 
27
- Read the doc's content. Extract every source file path it references (file listings, function locations, component paths). Group them into the narrowest glob patterns that cover all referenced files without overlapping another doc's `owns:` globs at equal specificity. A doc may own several globs.
27
+ `owns:` names the **regions whose truth this doc holds** — the directories where a change must be reconciled against this document before it ships. It is an authority claim a human makes, not an inventory of what the feature touches.
28
28
 
29
- | Doc references | Glob |
30
- | ------------------------------------ | ------------------------- |
31
- | Multiple files under one directory | `src/feature/**` |
32
- | Specific files in a shared directory | List each file explicitly |
33
- | A single entry-point file | The exact file path |
29
+ **A file the doc merely references is not a file the doc owns.** Reaching across a boundary into a shared module is a fact about the code, and it belongs to whoever owns that module's region. Transcribing those reach edges as `owns:` entries is how an index stops being a routing table and becomes a hand-maintained import graph: rows then track the dependency graph rather than the feature count, and the shared upstream file ends up filed under whichever downstream feature happened to name it first — a consumer recorded as the authority.
30
+
31
+ Prefer one region glob over many file globs. Ten paths say nothing about which is the entry point; one region says where the doc's writ runs.
32
+
33
+ | The doc governs | Glob |
34
+ | -------------------------------------------- | ------------------------------------ |
35
+ | A feature's own directory | `src/feature/**` |
36
+ | A feature split across two directories | One glob each |
37
+ | A single entry-point file, and nothing beside it | The exact file path |
38
+ | A shared file several features import | **Nothing — leave it unowned** |
39
+
40
+ **An unowned shared file is the system working, not a gap.** A shared module's authority is the contract it exposes, not any one consumer, so a library-references file or a common utility legitimately has no owning doc. Claiming it would make the index bigger and the routing less certain at once.
34
41
 
35
42
  Glob each pattern against the codebase. At `status: active`, drop any that match nothing; at `status: draft`, projected globs may match nothing yet — confirm them against real code when the doc flips to `active`.
36
43
 
@@ -71,7 +71,10 @@ Why this work is happening *now*. Link to the spec for *what* we're building; th
71
71
  <!-- Written by the `testing` skill (plan-gate mode) at the plan gate (after `boundary`). One row per
72
72
  promised behavior: layer · target file · oracle · fixtures · an empty Outcome cell the
73
73
  verification gate fills (satisfied / owes nothing / blocked → backlog). Amendable while the
74
- plan is open. Do not hand-author. -->
74
+ plan is open. Do not hand-author. NEVER put a pipe in a ledger cell, escaped or not — the
75
+ compliance hook splits rows on `|` at a fixed column index with no unescaping, so a
76
+ markdown-legal `\|` shifts every later cell and the wall blocks a row that is in fact
77
+ discharged. Write "`install` or `update`", not "`install \| update`". -->
75
78
 
76
79
  ## Open Questions
77
80
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "harness": "discover",
3
3
  "name": "discover",
4
- "version": "0.1.17",
4
+ "version": "0.1.18",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
@@ -18,7 +18,7 @@ Strictly separated — never mix them:
18
18
 
19
19
  **The operator layer**: `metadata/lessons/` — read `DIGEST.md` at session start alongside the routing files; it is **prior, not canon** (its reading-protocol block governs). Everything else under `lessons/` (append-only `log/`, `forecasts.md`) loads on demand only. The `lessons-maintenance` agent (instruction set: `metadata/lessons/distillation.md`) runs **only during user sessions** — spawn it in the background when the `metadata/ops/` cursor falls a session behind.
20
20
 
21
- The rules are the method — portable, abstract, generic examples; nothing depends on any local topic. Live exemplars are reached through `metadata/TOPICS.md`. Workspace-local method notes go to `metadata/method-changelog.md`; the harness itself is versioned by `.claude/harness.json` and documented in the etna forge (`docs/architecture/harness/discussion/`).
21
+ The rules are the method — portable, abstract, generic examples; nothing depends on any local topic. Live exemplars are reached through `metadata/TOPICS.md`. Workspace-local method notes go to `metadata/method-changelog.md`; the harness itself is versioned by `.claude/harness.json` and documented in the etna forge (`docs/harness/discussion/`).
22
22
 
23
23
  ## Delegation
24
24
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "harness": "discussion",
3
3
  "name": "discussion",
4
- "version": "0.6.0",
4
+ "version": "0.6.1",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
@@ -10,5 +10,5 @@
10
10
  "seeds": [],
11
11
  "tools": [],
12
12
  "docsRoot": "docs",
13
- "digest": "f1c6d3f61e3e"
13
+ "digest": "a3de25b70367"
14
14
  }
@@ -29,7 +29,8 @@ Invoke the `maintenance` agent whenever the docs look like they need it — not
29
29
 
30
30
  ## Getting Started
31
31
 
32
- - Read **[`docs/structure.md`](docs/structure.md)** — the documentation authority map (what belongs in `architecture/`, `features/`, `operations/`, `skills/`, `history/`, `memory/`). Don't restate it here.
32
+ - Read **[`docs/structure.md`](docs/structure.md)** — the documentation authority map. The top level mirrors the source roots the forge ships from (`skills/`, `harness/`, `rules/`, `packages/`), plus `playbooks/` for the craft guides and `architecture/` for truth spanning two or more subjects. Don't restate it here.
33
+ - Read **[`docs/writing-style.md`](docs/writing-style.md)** before writing anything — the single style authority for this repo.
33
34
  - Read **[`docs/memory/`](docs/memory/)** for agent memory files (one per agent).
34
35
  - Prefer `docs/` over `README.md` — `docs/` is canonical for agents.
35
36
  - The CLI and hooks are the only real code — detect the package manager from the lockfile (see `.claude/rules/general.md`) for anything under `packages/`.
@@ -61,7 +62,7 @@ Three peer modes plus a Completion step — enter wherever the user's thinking a
61
62
  | **LLD** (low-level design) | new component/feature work | `lld-mode` agent + `documentation` skill | Plan |
62
63
  | **Plan** | an LLD is approved | `documentation` skill → `docs/history/plans/YYYY-MM-DD-{slug}.md` | Completion |
63
64
 
64
- > Brainstorm entry needs a **basis** — a reference document or a one-shot brain dump; drip-sized asks (a one-line fix) are too small and get redirected. A **pillar** is a recorded strategic principle from a past brainstorm: in-progress sessions under `docs/research/`, harvested ones under `docs/history/brainstorms/`, harvested doctrine under `docs/architecture/`. The LLD is the feature doc itself — `docs/features/{feature}.md`, design intent first, extended in place at Completion.
65
+ > Brainstorm entry needs a **basis** — a reference document or a one-shot brain dump; drip-sized asks (a one-line fix) are too small and get redirected. A **pillar** is a recorded strategic principle from a past brainstorm: in-progress sessions under `docs/research/`, harvested ones under `docs/history/brainstorms/`, harvested doctrine under `docs/architecture/`. The LLD is the component's own doc — `docs/skills/{name}/`, `docs/harness/{name}/`, or `docs/packages/{name}.md` — design intent first, extended in place at Completion.
65
66
 
66
67
  **On Plan Completion:**
67
68
 
@@ -79,14 +80,14 @@ These three mechanisms keep the repo self-updating: **ownership** routes an edit
79
80
 
80
81
  ### Ownership — route before editing
81
82
 
82
- Which doc is canonical for which paths is the **feature doc's own frontmatter**. A feature doc (flat in `docs/features/`) carries `owns:` (path globs — the only ownership key, pointing at `skills/**`, `harness/**`, `packages/cli/**`, …), an optional `module:` label, `keywords:`, and `status:`. Path → doc ownership is the union of every feature doc's `owns:` globs, surfaced as the generated ownership index at session start. A module is tied to its components by the `owns:` globs of the docs carrying its `module:` label — not a folder, not a separate map. Architecture docs describe cross-cutting patterns and own nothing; operations docs describe procedures.
83
+ **Ownership is declared, not positional.** Any document under `docs/` carrying `owns:` frontmatter claims those code paths — path globs pointing at `skills/**`, `harness/**`, `packages/**`, … — plus an optional `module:` label, `keywords:`, and `status:`. The SessionStart hook scans the whole tree and builds the injected index from the union, so a component's own record claims its path directly and a doc's location is a browsing decision, never an ownership one. Where two docs overlap, the **narrower glob wins**. A module is tied to its components by the `owns:` globs of the docs carrying its `module:` label — not a folder, not a separate map. Indexes, chapters, and explainers carry no `owns:`, so a subject claims its path exactly once.
83
84
 
84
85
  Before modifying a component in an unfamiliar area:
85
86
 
86
- 1. Consult the **injected ownership index** — the `glob → doc · module · keywords · status` map. Match the path or topic to the owning feature doc and read it first. (No index in context ⇒ the repo isn't reconciled — see Maintenance.)
87
+ 1. Consult the **injected ownership index** — the `glob → doc · module · keywords · status` map. Match the path or topic to the owning doc and read it first. (No index in context ⇒ the repo isn't reconciled — see Maintenance.)
87
88
  2. If a `docs/memory/` file exists for the area, read it for domain knowledge and failure modes.
88
89
 
89
- The `lld-mode` agent checks for ownership conflicts during LLD research — feature docs whose `owns:` globs overlap at equal specificity. Resolve them in the LLD before proceeding.
90
+ The `lld-mode` agent checks for ownership conflicts during LLD research — docs whose `owns:` globs overlap at equal specificity. Resolve them in the LLD before proceeding.
90
91
 
91
92
  ### Boundaries — check while changing
92
93
 
@@ -101,10 +102,11 @@ Invoke the `boundary` skill after a plan is approved and before its first implem
101
102
 
102
103
  ### Documentation — close the loop
103
104
 
104
- - **Every markdown document goes through the `documentation` skill.** Before creating or substantively editing any `.md` document — a doc, a README, an index, a record — invoke the `documentation` skill and apply its templates and `references/style-guide.md` (truth documents present-tense; history only in records). Instruction files — `CLAUDE.md`, rules, agents, skills — follow the skill's [`references/instruction-style.md`](skills/documentation/references/instruction-style.md) instead.
105
+ - **Everything written in this repo follows [`docs/writing-style.md`](docs/writing-style.md) — no exceptions.** It is the single style authority for both genres: instruction files (`CLAUDE.md`, rules, agents, skill prose) and documents. Read it before writing prose here, not after. Its load-bearing rule is the Two Natures — truth documents state the present and narrate no history; records are dated and never edited to match the present.
106
+ - **Every markdown document also goes through the `documentation` skill** for its *genre* — invoke it before creating or substantively editing any `.md` file and apply the matching template. The skill supplies structure; `writing-style.md` supplies the prose.
105
107
  - **[override]** Keep `docs/` updated when a change alters documented behaviour — creating and updating docs is part of the work.
106
108
  - After plan completion **[override]**: write the `docs/history/changelog/` entry as a **draft** immediately, while the context is fresh — no confirmation needed — and append its `docs/history/CHANGELOG.md` row; the commit fills what the draft left open and freezes it. Non-plan work needs no separate prompt: every non-exempt commit carries its own entry (the `changelog` rule).
107
- - When moving or renaming a component: update the `owns:` globs of the feature doc that owns it so the ownership index stays accurate. A new doc that owns code declares its paths in its own `owns:` frontmatter.
109
+ - When moving or renaming a component: update the `owns:` globs of the doc that owns it so the ownership index stays accurate. A new doc that owns code declares its paths in its own `owns:` frontmatter.
108
110
  - `docs/structure.md` is the authority on what belongs where — read it before adding or moving a doc; one canonical source per topic, link don't copy. Only `CLAUDE.md`, `AGENTS.md`, and `README.md` live at the repo root.
109
111
 
110
112
  > Agents and skills live under `.claude/agents/` and `.claude/skills/`. Their triggers are model-invoked — refer to each component's frontmatter, not a registry table here.
@@ -120,7 +122,7 @@ A commit marks the user's behavioral verification of the work, and the working s
120
122
  - **A commit is the smallest unit an entry may describe**; above that the mapping is judgment. Work exceeding a draft's scope gets a *second* entry rather than stretching the first.
121
123
  - **Distillation is the `retro` agent** — run on demand (before a brainstorm or LLD, before a new project, after a refinement arc), it converts accumulated entries into a typed punch list (doc content, doc structure, skill evolution, pins, harness rules), appends actionable items to the backlog, and proposes only — application runs through the normal documentation loop.
122
124
 
123
- **One-shot migrations are a different genre.** Work that runs **once** against a live system — a harness rollout across consumer repos, a catalog-wide cutover, a data backfill — is a **migration runbook** at `docs/history/migrations/YYYY-MM-DD-{slug}.md` (template: the `documentation` skill's `templates/migration-runbook.md`), authored **before** execution and outcome-stamped after. A changelog entry says what changed in this repo; a runbook says what was executed against the world and how to verify or undo it. A repeatable procedure is neither — that is `docs/operations/`. When a runbook defers targets, it records **why** and the **trigger to re-evaluate them**, so the next run re-checks rather than inheriting the skip.
125
+ **One-shot migrations are a different genre.** Work that runs **once** against a live system — a harness rollout across consumer repos, a catalog-wide cutover, a data backfill — is a **migration runbook** at `docs/history/migrations/YYYY-MM-DD-{slug}.md` (template: the `documentation` skill's `templates/migration-runbook.md`), authored **before** execution and outcome-stamped after. A changelog entry says what changed in this repo; a runbook says what was executed against the world and how to verify or undo it. A repeatable procedure is neither — that belongs with the component it operates. When a runbook defers targets, it records **why** and the **trigger to re-evaluate them**, so the next run re-checks rather than inheriting the skip.
124
126
 
125
127
  ---
126
128
 
@@ -164,12 +166,10 @@ The single policy for sub-agent dispatch — when to fan out, at what tier, and
164
166
 
165
167
  ❌ prohibitions hold unless the user explicitly asks otherwise; ✅ and ⚠️ items are standing policy:
166
168
 
167
- - ❌ **Tests** — etna is a markdown-authoring repo plus a small CLI with no test suite; never generate, update, or modify test files unless the user explicitly asks. (The `code` harness ships a gated-verification posture for the projects it governs — that is a shipped rule, not etna's own.)
169
+ - ❌ **Tests** — etna authors markdown; the only suite is the CLI's, under `packages/cli/`. Never generate, update, or modify test files unless the user explicitly asks. (The `code` harness ships a gated-verification posture for the projects it governs — that is a shipped rule, not etna's own.)
168
170
  - ❌ **Dependencies** — never upgrade frameworks, libraries, or dependencies.
169
- - ❌ **Compatibility** — never maintain backward compatibility unless specified.
170
171
  - ❌ **Branches & Worktrees [override]** — never self-create git branches or worktrees; commit straight to the checked-out branch. This replaces any built-in "branch first on the default branch" default. Exception: when the user asks for a pull request, create the branch the PR requires before committing its changes.
171
- - ❌ **Protected Files** — before editing, if `docs/operations/protected-files.md` exists, read it and never modify any path it lists; never modify `AGENTS.md` or that file itself.
172
- - ✅ **Browser Automation** — Claude in Chrome / CDP browser tools: use freely when useful, no need to ask first.
172
+ - ❌ **Protected Files** — never modify `AGENTS.md`.
173
173
  - ✅ **Delegation** — when to fan out, tiers, and trust are § Delegation.
174
174
  - ⚠️ **Plans** — created directly in `docs/history/plans/` (see Workflows). Once `Status: Complete` or `Abandoned`, frozen — never edited. If scope changes after completion, write a new dated plan linking back.
175
175
  - ⚠️ **Tool Issues** — if any CLI tool, shell alias, or MCP tool behaves unexpectedly (syntax errors, silent failures, wrong defaults), report the exact tool, command, and error to the user so they can fix the underlying library.
@@ -178,15 +178,15 @@ The single policy for sub-agent dispatch — when to fan out, at what tier, and
178
178
 
179
179
  ## Authoring Conventions
180
180
 
181
- Most artifacts are markdown — skills, agents, rules, docs. Follow the `kb` / `nomenclature` rules; markdown has no file-path-comment rule. Keep the `.claude/rules/*` reference docs frontmatter-free.
181
+ Almost everything here is markdown — skills, agents, rules, harness policies, docs. **The prose style for all of it is [`docs/writing-style.md`](docs/writing-style.md)**; the guides below add what is specific to each artifact class. Follow the `kb` / `nomenclature` rules; markdown has no file-path-comment rule. Keep the `.claude/rules/*` reference docs frontmatter-free.
182
182
 
183
183
  **Each artifact class has an authoring guide — read the one you are editing, before you edit it.** The forge is the only repo that authors these, so the guides live here and ship nowhere:
184
184
 
185
185
  | Editing | Guide |
186
186
  | --- | --- |
187
- | A **rule** (`rules/*.md`) | [`docs/architecture/rules.md`](docs/architecture/rules.md) — the catalog, the conventions, and the whole-file audit |
188
- | A **skill** (`skills/*/`) | [`docs/architecture/skills/playbook/`](docs/architecture/skills/playbook/Readme.md) — fit and scope through shipping and maintenance |
189
- | An **agent** (`agents/*.md`) | [`docs/architecture/harness/authoring-agents.md`](docs/architecture/harness/authoring-agents.md) |
187
+ | A **rule** (`rules/*.md`) | [`docs/rules/Readme.md`](docs/rules/Readme.md) — the catalog, the conventions, and the whole-file audit |
188
+ | A **skill** (`skills/*/`) | [`docs/playbooks/skills/`](docs/playbooks/skills/Readme.md) — fit and scope through shipping and maintenance |
189
+ | An **agent** (`agents/*.md`) | [`docs/playbooks/agents.md`](docs/playbooks/agents.md) |
190
190
 
191
191
  Authoring or updating an **agent** keeps both runtimes in sync: the `.md` is the single source, and its Codex `.codex.toml` twin is derived from it by the guide above, then checked with `node packages/cli/lib/codex/validate-codex-agents.mjs`.
192
192
 
@@ -18,10 +18,10 @@ You do NOT write the LLD — you return structured research that the main agent
18
18
  Map the documentation landscape — what exists, what is adjacent, how detailed the LLD should be, and what code paths are already owned:
19
19
 
20
20
  1. Read `./docs/structure.md` to understand the documentation structure
21
- 2. If `./docs/features/README.md` exists, read it to see existing feature LLDs and where the new one fits; otherwise glob the feature docs directly
21
+ 2. Locate the repo's feature-doc layer from `structure.md` — the standard shape is `./docs/features/`, but a repo may document components under their own subject folders. Read that layer's index if it has one; otherwise glob for docs carrying `owns:` frontmatter
22
22
  3. If `./docs/memory/lld-mode.md` exists (or the legacy `./docs/memory/spec-mode.md`), read it for feature doc inventory, ownership conflict zones, and lessons from previous research
23
- 4. Read 1-2 existing feature LLDs in `./docs/features/` to calibrate format and depth
24
- 5. Consult the **injected ownership index** (the generated `glob → doc · module · keywords · status` map — the union of every feature doc's `owns:` frontmatter) to build the ownership map. It is the source of truth for what code paths are already owned. If the index isn't present in context, glob `docs/features/**/*.md` and read each doc's `owns:` frontmatter directly.
23
+ 4. Read 1-2 existing feature LLDs from that layer to calibrate format and depth
24
+ 5. Consult the **injected ownership index** (the generated `glob → doc · module · keywords · status` map — the union of every feature doc's `owns:` frontmatter) to build the ownership map. It is the source of truth for what code paths are already owned. If the index isn't present in context, glob the docs tree (excluding `docs/history/`) and read the `owns:` frontmatter of every doc that declares it.
25
25
 
26
26
  ## What You Research
27
27
 
@@ -44,15 +44,15 @@ Decide the mode once, up front, from the version marker — then run.
44
44
 
45
45
  ## Part A — Coherence audit (always runs)
46
46
 
47
- The ownership source of truth is the **union of every feature doc's `owns:` frontmatter** — the same source the SessionStart ownership index derives from. There is no `docs/routing.md` and no precomputed file to regenerate; you rebuild the map by globbing `docs/features/`.
47
+ The ownership source of truth is the **union of every doc's `owns:` frontmatter** — the same source the SessionStart ownership index derives from. There is no `docs/routing.md` and no precomputed file to regenerate; you rebuild the map by globbing the docs tree for docs that declare `owns:`.
48
48
 
49
49
  ### Step 1: Build the ownership map
50
50
 
51
51
  1. Read `docs/structure.md` for the authority map.
52
- 2. Glob `docs/features/**/*.md`, ignoring `*.bkp.*` files (installer backups awaiting the fold); for each, parse the frontmatter `owns:` globs and `module:` label.
52
+ 2. Glob the docs tree for markdown carrying `owns:` frontmatter, ignoring `*.bkp.*` files (installer backups awaiting the fold) and anything under `docs/history/` (frozen records quote retired globs as provenance). **Ownership is declared, not positional** — the standard shape files these docs under `docs/features/`, but a repo may declare ownership on any doc, and the index reads them all. For each, parse the `owns:` globs and `module:` label.
53
53
  3. Build a map: glob → owning doc + module. Narrower glob wins on overlap. **This IS the source of truth.**
54
54
 
55
- **Architecture-first fallback.** If no feature doc carries `owns:` yet (a fresh-baseline repo can carry thousands of LOC and zero feature docs), drive this pass's ownership/drift surface off `docs/architecture/**` file-references instead — they become the **primary** ownership map for Steps 2–5. The `owns:` map becomes primary the moment any feature doc declares ownership — this is a fallback, not a replacement.
55
+ **Architecture-first fallback.** If no doc carries `owns:` yet (a fresh-baseline repo can carry thousands of LOC and zero feature docs), drive this pass's ownership/drift surface off `docs/architecture/**` file-references instead — they become the **primary** ownership map for Steps 2–5. The `owns:` map becomes primary the moment any feature doc declares ownership — this is a fallback, not a replacement.
56
56
 
57
57
  ### Step 2: Validate ownership
58
58
 
@@ -109,7 +109,7 @@ For each stale doc: update the doc directly when the drift is clear (renamed fun
109
109
 
110
110
  For each feature doc you verified accurate or updated this pass, stamp `last-reviewed:` in its frontmatter with today's date (add the key if absent). Never write review dates into the body — it's frontmatter metadata only.
111
111
 
112
- For unowned code paths, append: `- **[P2]** No feature doc owns \`{dir}/\` — discovered by maintenance` (`{dir}` = the actual unowned source directory).
112
+ For an unowned code **region**, append: `- **[P2]** No feature doc owns \`{dir}/\` — discovered by maintenance` (`{dir}` = the actual unowned source directory). The target is **every region has an owner**, never every file: an individual shared file that several features import is expected to be unowned, because its authority is the contract it exposes rather than any one consumer. Report a directory that no doc's region covers; never mint a row for a shared file, and never propose closing a gap by adding a file glob to whichever doc happens to import it.
113
113
 
114
114
  **Declined realignment.** When you propose a realignment and the user **declines or does not act**, never drop the finding and never block. Append it to `docs/history/backlog.md` as `- **[P0–P3]** <finding> — discovered by maintenance`, priority = the finding's own severity **on the same P0–P3 scale as code items** — never a segregated "compliance" section, never auto-relegated to the lowest tier. Record once: dedupe against existing entries; if already present, leave it.
115
115
 
@@ -238,6 +238,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
238
238
  - **A clean report is valid** — do not invent issues.
239
239
  - **Declarative, idempotent.** Re-running baseline mode on a current repo reshapes nothing — a second pass is just the audit plus the module proposal. Converge to the current shape; never replay a version ladder; never reconstruct a past shape.
240
240
  - **Brownfield extraction (operator-ruled 2026-08-19).** When baseline mode meets a repo with a real source tree and few or no feature docs, the module *proposal* is not enough — run the extraction pass: produce feature docs for the recognized modules (docs/features/, full frontmatter: `owns:` · `module:` · `keywords:` · `status:` · `last-reviewed:` · `type: feature` · `derivation: "extracted (baseline YYYY-MM-DD, codegraph + source read)"`), plus the architecture, tests, and operations layers. Use the `codegraph` skill where installed — localize through the index before reading; the docs record intent, the index answers facts. **Every extracted feature doc carries a `## History & Ownership` section** — top contributors with recency, churn posture, and bus-factor notes, dated "as of YYYY-MM-DD" (operator ruling: this section is mandatory in extraction output even though doc-lint flags dated narrative in truth docs; the findings are accepted for now, and a future harness version may retire the section in favor of live index pointers). `status:` is evidence-graded: `deprecated` only when history shows the area dead, never by guess. Scale by fan-out (one extraction lane per module, a completeness critic over the result) rather than one long cold read.
241
+ - **Diagrams ride the baseline (operator-ruled 2026-08-24).** Whenever baseline mode produces or converges the architecture layer, draw two ASCII diagrams from the real source, each in its own fenced code block: (1) an **architecture diagram** in the system-design/architecture doc — the actual processes, entry points, and wire surfaces as boxes, the calls/queues/files between them as labelled arrows; (2) a **DB ER diagram** wherever the repo declares a relational schema (DDL, migrations, an ORM schema) — one box per table listing its key columns, FK edges drawn to the table they reference, cardinality on the edge; it lands in the domain-model doc, or `docs/architecture/data-model.md` when none exists. Box-drawing characters (`┌─┐│└┘├┤`), width ≤ 100 columns. Derived only from what the code declares — a diagram never invents a component the source does not show, and a repo with no relational schema gets **no** ER diagram rather than an imagined one. Diagrams are truth-document sections: later baselines and audits converge them in place like any other section.
241
242
  - **Tool tiers.** The SessionStart context names each manifest-declared tool with its tier; calibrate the run to it, never stall on it. **Present-fresh** (`Tool: codegraph 0.2.0`): use it — check `codegraph projects` for staleness, re-run `codegraph index` if HEAD has moved, then localize every question through the index before reading source. **Present-stale** (below the catalog minimum): still use it, but note the version gap and the upgrade command in the run report — a stale contract may lack answer shapes the skill assumes. **Absent** (`Tool: codegraph MISSING`): fall back to the cold method (Glob/Grep/read), state in the run report that the tool was absent and give the install command — degraded output is acceptable, silent degradation is not.
242
243
  - **One sentinel.** `docs/structure.md` `harness-version` is the only baseline/version marker.
243
244
  - **Modules are labels, layout-blind.** Seed from the code's bounded contexts; never from `pnpm-workspace.yaml`/workspace walls. Propose, don't force; acting is a `module:` tag, never a folder.
@@ -14,9 +14,9 @@ You do NOT write the LLD — you return structured research that the main agent
14
14
  Map the documentation landscape — what exists, what is adjacent, how detailed the LLD should be, and what code paths are already owned:
15
15
 
16
16
  1. Read `./docs/structure.md` to understand the documentation structure
17
- 2. If `./docs/features/README.md` exists, read it to see existing feature LLDs and where the new one fits; otherwise glob the feature docs directly
17
+ 2. Locate the repo's feature-doc layer from `structure.md` — the standard shape is `./docs/features/`, but a repo may document components under their own subject folders. Read that layer's index if it has one; otherwise glob for docs carrying `owns:` frontmatter
18
18
  3. If `./docs/memory/lld-mode.md` exists (or the legacy `./docs/memory/spec-mode.md`), read it for feature doc inventory, ownership conflict zones, and lessons from previous research
19
- 4. Read 1-2 existing feature LLDs in `./docs/features/` to calibrate format and depth
19
+ 4. Read 1-2 existing feature LLDs from that layer to calibrate format and depth
20
20
  5. Consult the **injected ownership index** (the generated `glob → doc · module · keywords · status` map — the union of every feature doc's `owns:` frontmatter) to build the ownership map. It is the source of truth for what code paths are already owned. If the index isn't present in context, glob `docs/features/**/*.md` and read each doc's `owns:` frontmatter directly.
21
21
 
22
22
  ## What You Research
@@ -42,15 +42,15 @@ Decide the mode once, up front, from the version marker — then run.
42
42
 
43
43
  ## Part A — Coherence audit (always runs)
44
44
 
45
- The ownership source of truth is the **union of every feature doc's `owns:` frontmatter** — the same source the SessionStart ownership index derives from. There is no `docs/routing.md` and no precomputed file to regenerate; you rebuild the map by globbing `docs/features/`.
45
+ The ownership source of truth is the **union of every doc's `owns:` frontmatter** — the same source the SessionStart ownership index derives from. There is no `docs/routing.md` and no precomputed file to regenerate; you rebuild the map by globbing `docs/features/`.
46
46
 
47
47
  ### Step 1: Build the ownership map
48
48
 
49
49
  1. Read `docs/structure.md` for the authority map.
50
- 2. Glob `docs/features/**/*.md`, ignoring `*.bkp.*` files (installer backups awaiting the fold); for each, parse the frontmatter `owns:` globs and `module:` label.
50
+ 2. Glob the docs tree for markdown carrying `owns:` frontmatter, ignoring `*.bkp.*` files and anything under `docs/history/`. **Ownership is declared, not positional** — the standard shape files these under `docs/features/`, but a repo may declare ownership on any doc. For each, parse the `owns:` globs and `module:` label.
51
51
  3. Build a map: glob → owning doc + module. Narrower glob wins on overlap. **This IS the source of truth.**
52
52
 
53
- **Architecture-first fallback.** If no feature doc carries `owns:` yet (a fresh-baseline repo can carry thousands of LOC and zero feature docs), drive this pass's ownership/drift surface off `docs/architecture/**` file-references instead — they become the **primary** ownership map for Steps 2–5. The `owns:` map becomes primary the moment any feature doc declares ownership — this is a fallback, not a replacement.
53
+ **Architecture-first fallback.** If no doc carries `owns:` yet (a fresh-baseline repo can carry thousands of LOC and zero feature docs), drive this pass's ownership/drift surface off `docs/architecture/**` file-references instead — they become the **primary** ownership map for Steps 2–5. The `owns:` map becomes primary the moment any feature doc declares ownership — this is a fallback, not a replacement.
54
54
 
55
55
  ### Step 2: Validate ownership
56
56
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "harness": "etna",
3
3
  "name": "etna",
4
- "version": "1.5.12",
4
+ "version": "1.5.16",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
@@ -10,5 +10,5 @@
10
10
  "seeds": [],
11
11
  "tools": [],
12
12
  "docsRoot": "docs",
13
- "digest": "b9aaab03fa33"
13
+ "digest": "f836486a6ca4"
14
14
  }
@@ -1,4 +1,4 @@
1
- // harness/etna/hooks/generate-ownership-index.mjs — SessionStart hook: scan docs/features/**/*.md,
1
+ // harness/etna/hooks/generate-ownership-index.mjs — SessionStart hook: scan docs/**/*.md,
2
2
  // extract the focal frontmatter (owns / module / keywords / status), and inject a compact
3
3
  // code-path → owning-doc index via additionalContext. Fires on startup/resume/clear/compact so the
4
4
  // map survives /clear and /compact and is regenerated fresh every time. Writes no file.
@@ -115,10 +115,16 @@ function asList(v) {
115
115
 
116
116
  // Build the ownership index text from every feature doc's frontmatter.
117
117
  function buildIndex(projectDir) {
118
- const featuresDir = path.join(projectDir, "docs", "features");
119
- // *.bkp.* files are install-time backups awaiting the maintenance fold — a backed-up feature
118
+ // Ownership is DECLARED, not positional: any doc anywhere under docs/ that carries owns:
119
+ // claims those paths, so a component's record claims its own path instead of a stub doing it
120
+ // by proxy. docs/history/ is skipped — frozen plans and records quote old globs as provenance,
121
+ // and indexing them would resurrect retired ownership.
122
+ const docsDir = path.join(projectDir, "docs");
123
+ // *.bkp.* files are install-time backups awaiting the maintenance fold — a backed-up
120
124
  // doc double-owning every path it names is the drift this filter prevents (parity: lib/ownership.mjs).
121
- const files = listMarkdown(featuresDir).filter((f) => !f.includes(".bkp."));
125
+ const files = listMarkdown(docsDir)
126
+ .filter((f) => !f.includes(".bkp."))
127
+ .filter((f) => !f.split(path.sep).includes("history"));
122
128
  const lines = [];
123
129
  let deprecated = 0;
124
130
  let indexed = 0;
@@ -142,15 +148,18 @@ function buildIndex(projectDir) {
142
148
  const keywords = asList(field(block, "keywords"));
143
149
  indexed += 1;
144
150
  const doc = path.relative(projectDir, file).split(path.sep).join("/");
151
+ // METADATA ONCE PER DOC, NOT ONCE PER GLOB — kept in step with lib/ownership.mjs buildIndex(),
152
+ // which the parity suite pins. Re-emitting the keyword array beside every glob made the index
153
+ // grow with the dependency graph rather than the doc count.
145
154
  const kw = keywords.length ? `[${keywords.join(", ")}]` : "[]";
146
- for (const glob of owns) {
147
- lines.push(`${glob} → ${doc} · ${moduleLabel} · ${kw} · ${status}`);
148
- }
155
+ lines.push(
156
+ `${doc} · ${moduleLabel} · ${status}\n ${kw}\n` + owns.map((glob) => ` ${glob}`).join("\n")
157
+ );
149
158
  }
150
159
  if (lines.length === 0) return "";
151
160
  lines.sort();
152
161
  const header =
153
- "# Ownership index (generated) — code-path glob → owning feature doc · module · keywords · status\n" +
162
+ "# Ownership index (generated) — owning feature doc · module · status, then the code regions it governs.\n" +
154
163
  "# Match the file or topic you're about to touch, then read the owning doc before changing it.\n";
155
164
  // A doc dropped by a parse failure is indistinguishable from one that owns nothing,
156
165
  // so report the tally: seen vs indexed is the only signal a silent skip leaves.
@@ -159,7 +168,7 @@ function buildIndex(projectDir) {
159
168
  if (deprecated > 0) notes.push(`${deprecated} deprecated`);
160
169
  if (skipped > 0) notes.push(`${skipped} with no owns:`);
161
170
  const footer = notes.length
162
- ? `\n# (${indexed}/${files.length} feature doc(s) indexed - ${notes.join(", ")})`
171
+ ? `\n# (${indexed}/${files.length} doc(s) claim ownership - ${notes.join(", ")})`
163
172
  : "";
164
173
  return header + lines.join("\n") + footer;
165
174
  }
@@ -37,6 +37,7 @@ Rules for the plan regime between the plan gate and the verification gate:
37
37
  - **On a rendered surface, browser driving LEADS the gate — it is not its final rung.** The order inverted deliberately: the defects that reach users through an interface are geometric and compositional, and a simulated DOM computes no layout, so a suite that is entirely green says nothing about whether anything is where it should be. **Reach the browser Chrome-CDP-first and Playwright second** — CDP drives a real browser interactively and needs no script written in advance, which is what discovery requires; Playwright is the fallback where CDP is unavailable and the tool of record for whatever must run unattended afterwards. Cheap oracles run in the same pass and are what the drive's findings get pinned into; they are no longer a gate to pass before looking. A surface with nothing rendered — a CLI, a service, a library — drives its own real binary or wire instead, and this clause does not apply.
38
38
  - **A judged drive discovers; a scripted assertion guards.** Only the first finds a defect nobody has seen — a script asserting a control's value passes over a control drawn on top of its own label. Scripts are written after a judged drive names the class of defect, never generated ahead of one, and **a red script is never regenerated** — regeneration re-derives the assertion from the surface as it currently is, which turns a defect into the asserted truth with everything green.
39
39
  - **Report what happened, including failures.** A gate that reports green while a suite is red is worse than no gate; state the failing test and its output.
40
+ - **`cmd > log; echo "EXIT=$?"` reports the echo's status, never the command's** — a SIGTERM'd suite surfaces as "exit code 0", which is the worst direction for a gate to be wrong in. Read the real status from the log, or drop the trailing echo. Adjacent trap: a runner that never emits a summary is a dying **worker**, not a slow test — `--testTimeout` bounds neither, so bound the run and read the log.
40
41
  - **Abandonment does not erase debt.** Before `Status: Abandoned` freezes a plan whose code was committed, convert its undischarged rows into backlog items; uncommitted code owes nothing.
41
42
 
42
43
  ## Frameworks by stack
@@ -6,7 +6,7 @@ correctly, the record stays readable at any pause, and the session ends with a h
6
6
  complete that the session folder is useless — it moves to history and the user deletes or
7
7
  commits it.
8
8
 
9
- > Spec (single source of truth): `docs/architecture/skills/brainstorm/` in the etna repo. The etna
9
+ > Spec (single source of truth): `docs/skills/brainstorm/` in the etna repo. The etna
10
10
  > copy of this skill (`skills/brainstorm/`) is canonical; the user-level path
11
11
  > `~/.claude/skills/brainstorm/` is a symlink to it.
12
12
 
@@ -96,3 +96,25 @@ These bind every writer of the working surface — facilitator (ops), twin (ever
96
96
  5. **Directional steering is not feature truth.** A harvested principle informs many decisions
97
97
  and changes slowly; a feature fact belongs to its feature doc and changes at code speed. The
98
98
  harvest links between the two layers, never duplicates prose across them.
99
+
100
+ ## Field lessons — capture and converge
101
+
102
+ Measured on live sessions; each is the rule, distilled.
103
+
104
+ - **Separate user voice from facilitator re-derivation before ledgering** when both arrive in one
105
+ turn. Only the user's half is ledgerable; a re-derivation that quietly re-reads an existing
106
+ commitment into a new shape is caught as a fork with a named amendment to the affected `D#`,
107
+ never as an edit — a commitment recorded in the user's words is amended only by the user.
108
+ - **Stamp in UTC, always** — take the stamp from `date -u`, never from the runtime's injected
109
+ local date; the two disagree for part of every working day in some offsets, and a local-date
110
+ stamp runs the ledger a day ahead of capture.
111
+ - **Run the blindspot pass immediately after a converge burst, before the manifest gate.** When
112
+ several picks land in one sitting the strain migrates *between* them — collisions between
113
+ commitments taken within the same hour, found only by reading two together. The burst is the
114
+ cheapest moment to catch them and the last before they freeze.
115
+ - **When a pick answers three of a fork's four close-condition clauses, close it and file the
116
+ residue as a new fork in the same pass** — closing silently and leaving it open both lose
117
+ information; name the unanswered clause in the resolving entry and carry it under a new `F#`.
118
+ - **When a ledger entry's `Why` contains "already", verify it before the manifest.** A commitment
119
+ that declines to build something because it already exists rests on a fact, not a judgment, and
120
+ the stop gate is the last place that fact is checkable — after the folder moves it is not.
@@ -84,3 +84,37 @@ Then the offer, relayed by the facilitator: **delete the moved folder, or commit
84
84
  decides; the design assumes deletion — nothing anywhere may depend on the folder surviving.
85
85
  Committing is the explicit way to keep git-history forensics; an uncommitted folder deleted here
86
86
  is gone, and that residual is accepted by design.
87
+
88
+ ## Field lessons — apply
89
+
90
+ Measured on live harvests; each is the rule, distilled.
91
+
92
+ - **Run the move gate clean immediately before the move and treat that as the verdict** — the
93
+ gate's checks read `ops/`, which the move deletes, so a post-move re-run fails by construction.
94
+ Verify the post-move state by hand: `Status: harvested`, no `ops/`, both index rows,
95
+ `check-links` clean. And **distil the process record into `state.md` before the move** — the
96
+ sync log dies with `ops/`, and a ten-line dispatch summary is what separates a record that shows
97
+ its workings from one that asserts its commitments from nowhere.
98
+ - **Uncommitted harvest artifacts are deleted, not deprecated** — deprecate-in-place protects
99
+ readers holding a pointer, and a never-committed file has none; a tombstone for a
100
+ twenty-minute-old file is ceremony.
101
+ - **Audit the *other* decision records for impact at manifest time** — the manifest routes
102
+ through the docs the commitments name and will not think of the neighbours. The test is
103
+ commitment-level, not textual (grep finds nothing); read each AD's Decision and Open Questions
104
+ against the new commitments, and report the unimpacted verdicts with evidence too.
105
+ - **A manifest enumerates sites; the file you open contains more of them.** Fix every
106
+ same-doctrine site inside a file the manifest already opens — a self-contradicting document is
107
+ worse than a wider diff — but never wander into an unnamed file: that becomes a backlog row.
108
+ Say which you did, per row, in the application record.
109
+ - **Re-run the uselessness test when the destination *set* changes**, not only when content does —
110
+ a consolidation that moves referrers produces a document set no cold reader has graded. The
111
+ recurring find: a truth doc stating a decided-but-unbuilt mechanism in the present tense; the
112
+ fix is one clause naming the unbuilt prerequisite.
113
+ - **Retirement wording beats suppression.** doc-lint's heuristics block the honest retirement
114
+ sentence (`superseded by`, `no longer`); rephrase with no loss of meaning — "retired by
115
+ AD-00N", "is not the product's position", "lives in a package that is gone" — and reach for
116
+ `doc-lint-disable` only when the banned phrase is itself the claim.
117
+ - **Amending another session's draft: change the contradicting claim everywhere it is asserted,
118
+ and nothing else** — a claim stated in nine places and fixed in three leaves the doc worse; a
119
+ one-paragraph note saying what moved and what did not keeps the amendment legible to the doc's
120
+ author.