@nurix/etna 0.20.28 → 0.20.30

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 (73) hide show
  1. package/data/harness-index.json +1 -1
  2. package/data/harnesses/code/CLAUDE.md +5 -5
  3. package/data/harnesses/code/agents/bookkeeper.md +2 -2
  4. package/data/harnesses/code/agents/change-review.md +3 -3
  5. package/data/harnesses/code/agents/lld-mode.md +3 -3
  6. package/data/harnesses/code/agents/maintenance.md +22 -19
  7. package/data/harnesses/code/agents/polygraph-finder.md +3 -3
  8. package/data/harnesses/code/agents/polygraph.md +4 -4
  9. package/data/harnesses/code/agents/retro.md +1 -1
  10. package/data/harnesses/code/codex/agents/bookkeeper.toml +7 -3
  11. package/data/harnesses/code/codex/agents/change-review.toml +3 -3
  12. package/data/harnesses/code/codex/agents/lld-mode.toml +3 -3
  13. package/data/harnesses/code/codex/agents/maintenance.toml +60 -37
  14. package/data/harnesses/code/codex/agents/polygraph-finder.toml +6 -2
  15. package/data/harnesses/code/codex/agents/polygraph.toml +4 -4
  16. package/data/harnesses/code/codex/agents/retro.toml +1 -1
  17. package/data/harnesses/code/docs/structure.md +15 -15
  18. package/data/harnesses/code/harness.json +2 -2
  19. package/data/harnesses/code/hooks/lib/ownership.mjs +12 -13
  20. package/data/harnesses/code/hooks/lib/sweep.mjs +1 -1
  21. package/data/harnesses/code/skills/documentation/SKILL.md +17 -27
  22. package/data/harnesses/code/skills/documentation/references/frontmatter.md +56 -62
  23. package/data/harnesses/code/skills/documentation/references/style-guide.md +2 -2
  24. package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +1 -1
  25. package/data/harnesses/code/skills/documentation/templates/Readme.md +1 -1
  26. package/data/harnesses/code/skills/documentation/templates/docs-readme.md +1 -1
  27. package/data/harnesses/code/skills/documentation/templates/doctrine.md +2 -2
  28. package/data/harnesses/code/skills/documentation/templates/feature.md +3 -4
  29. package/data/harnesses/code/skills/documentation/templates/package.md +2 -3
  30. package/data/harnesses/code/skills/documentation/templates/root-readme.md +1 -1
  31. package/data/harnesses/code/skills/documentation/templates/system.md +2 -4
  32. package/data/harnesses/code/skills/documentation/templates/ux-behavior.md +4 -4
  33. package/data/harnesses/delivery/agents/lld-mode.md +3 -3
  34. package/data/harnesses/delivery/agents/maintenance.md +22 -19
  35. package/data/harnesses/delivery/agents/retro.md +1 -1
  36. package/data/harnesses/delivery/codex/agents/lld-mode.toml +3 -3
  37. package/data/harnesses/delivery/codex/agents/maintenance.toml +60 -37
  38. package/data/harnesses/delivery/codex/agents/retro.toml +1 -1
  39. package/data/harnesses/delivery/harness.json +2 -2
  40. package/data/harnesses/delivery/skills/documentation/SKILL.md +17 -27
  41. package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +56 -62
  42. package/data/harnesses/delivery/skills/documentation/references/style-guide.md +2 -2
  43. package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +1 -1
  44. package/data/harnesses/delivery/skills/documentation/templates/Readme.md +1 -1
  45. package/data/harnesses/delivery/skills/documentation/templates/docs-readme.md +1 -1
  46. package/data/harnesses/delivery/skills/documentation/templates/doctrine.md +2 -2
  47. package/data/harnesses/delivery/skills/documentation/templates/feature.md +3 -4
  48. package/data/harnesses/delivery/skills/documentation/templates/package.md +2 -3
  49. package/data/harnesses/delivery/skills/documentation/templates/root-readme.md +1 -1
  50. package/data/harnesses/delivery/skills/documentation/templates/system.md +2 -4
  51. package/data/harnesses/delivery/skills/documentation/templates/ux-behavior.md +4 -4
  52. package/data/harnesses/etna/agents/change-review.md +3 -3
  53. package/data/harnesses/etna/agents/lld-mode.md +3 -3
  54. package/data/harnesses/etna/agents/maintenance.md +22 -19
  55. package/data/harnesses/etna/agents/retro.md +1 -1
  56. package/data/harnesses/etna/codex/agents/change-review.toml +3 -3
  57. package/data/harnesses/etna/codex/agents/lld-mode.toml +3 -3
  58. package/data/harnesses/etna/codex/agents/maintenance.toml +60 -37
  59. package/data/harnesses/etna/codex/agents/retro.toml +1 -1
  60. package/data/harnesses/etna/harness.json +2 -2
  61. package/data/harnesses/etna/skills/documentation/SKILL.md +17 -27
  62. package/data/harnesses/etna/skills/documentation/references/frontmatter.md +56 -62
  63. package/data/harnesses/etna/skills/documentation/references/style-guide.md +2 -2
  64. package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +1 -1
  65. package/data/harnesses/etna/skills/documentation/templates/Readme.md +1 -1
  66. package/data/harnesses/etna/skills/documentation/templates/docs-readme.md +1 -1
  67. package/data/harnesses/etna/skills/documentation/templates/doctrine.md +2 -2
  68. package/data/harnesses/etna/skills/documentation/templates/feature.md +3 -4
  69. package/data/harnesses/etna/skills/documentation/templates/package.md +2 -3
  70. package/data/harnesses/etna/skills/documentation/templates/root-readme.md +1 -1
  71. package/data/harnesses/etna/skills/documentation/templates/system.md +2 -4
  72. package/data/harnesses/etna/skills/documentation/templates/ux-behavior.md +4 -4
  73. package/package.json +2 -2
@@ -1,90 +1,84 @@
1
- # Frontmatter Derivation — the doc graph
1
+ # Frontmatter — documents in the knowledge graph
2
2
 
3
- Load when: creating a doc that declares an edge (a feature, package or system doc, a UX doc, a behavioral checklist), updating one whose scope moved, or running a baseline/reconcile pass over many docs.
3
+ Load when: creating a document that carries frontmatter, updating one whose subject moved, or running a baseline or reconcile pass over many documents.
4
4
 
5
- **A docs tree is a graph, and every edge in it is declared in frontmatter.** Two kinds of edge exist: **doc → code** (`owns:`), and **doc → doc** (`verifies:`, `applies:`). The SessionStart hook reads them all and injects the generated indexes; there is no hand-maintained map of either kind.
5
+ This reference states how a document joins the knowledge graph, and it is kept in step with the machine that reads it: the knowledge layer (`docs/architecture/systems/knowledge-layer.md` in a repository that carries it; § Documents and code). Where this reference and that design disagree, the design is the authority and this file is the defect.
6
6
 
7
- ## The one rule that keeps the graph honest
7
+ ## The model
8
8
 
9
- **An edge is declared once, at the consuming end. Every inverse is generated, never authored.**
9
+ A document names the product objects it designs. Every other edge between a document and the code is either authored on the code's side or computed, never typed into the document.
10
10
 
11
- A parent that hand-lists its children is a second thing to forget, and the moment the two directions can disagree nothing says which is right — you have doubled the staleness surface rather than halving it. So a checklist names the feature it verifies; the feature never lists its checklists. A feature names the architecture doc it obeys; the architecture doc never lists its consumers. Both inverses are derived on demand and injected, exactly as the ownership index already is.
11
+ ### Principles
12
12
 
13
- The consuming end is also the end that **knows**. A checklist author knows what they are verifying; an architecture doc has no idea who will obey it next year.
13
+ 1. **Objects as the document's edge:** a document's frontmatter carries `objects:`, the product objects it designs, by their register names. That is its one edge into the graph and its discovery terms.
14
+ 2. **The header pointer as the code's edge:** a source file's path header names the document that holds its fuller account (the root instruction file's § File Structure). The pointer is authored on the code's side, a move carries it, and a new file without one is a lint.
15
+ 3. **Computed ownership:** the object closure (document → objects → tables → their write, read and refusal sites → files) reaches files nobody listed. Where the header pointer and the closure disagree, the graph prints both. Which document speaks for a path is a query, answered per path on demand.
16
+ 4. **Nothing recomputable stored:** a list of files, a list of consumers, or a list of governing documents typed into frontmatter is a copy of something the graph computes. It is stale the day the code moves, and it fails the stability test (`style-guide.md` § Shape — the model first, the detail after).
17
+ 5. **One declaration, at the consuming end:** the edges a person must author (`objects:`, `verifies:`) are declared once, by the document that knows. Every inverse is generated, never authored.
14
18
 
15
- ## When to run
19
+ ## Keys by genre
16
20
 
17
- - **Baseline / reconcile** — for every owning doc whose code paths aren't yet declared in its `owns:` frontmatter (the `maintenance` agent (baseline mode) calls this)
18
- - **New owning doc** — when creating one via the [Feature](../templates/feature.md), [Package](../templates/package.md) or [System](../templates/system.md) template, project its `owns:` globs
19
- - **Doc update** — when the documented scope changes, update the doc's `owns:` globs
21
+ | Genre | Keys |
22
+ | --- | --- |
23
+ | Feature | `type`, `status`, `objects`, `module` (optional) · transitional `owns` |
24
+ | System | `type`, `status`, `objects` · transitional `owns` |
25
+ | Package | `type`, `status`, `objects` · transitional `owns` |
26
+ | Doctrine | `type`, `status`, `objects` |
27
+ | UX | `type`, `status`, `objects` · transitional `owns` when it speaks for a surface's components |
28
+ | Behavioral checklist | `type: checklist`, `verifies` |
29
+ | Acceptance criteria | `type`, `status` |
30
+ | Every other genre | `type`, plus its template's own keys |
20
31
 
21
- ## Which genres own code
32
+ Tooling keys sit beside these on any genre: `rev:` (a generator's generation stamp), `slug:` (the identity key on generated documents, minted once, and in the repository the filename), and `last-reviewed:` (stamped on a substantive review, the `maintenance` agent's staleness signal). Sessions preserve them on edits and never narrate or inject them. The `maintenance` agent stamps `type:` where it is missing.
22
33
 
23
- **Three do, and no others: `feature`, `package`, `system`.** They layer by glob specificity over the same tree, which is what lets a repo be documented at three altitudes without subtracting anything by hand:
34
+ ## Keys
24
35
 
25
- | Genre | Claims | Wins because |
26
- | --- | --- | --- |
27
- | `package` (`docs/architecture/packages/`) | `packages/{name}/**` — the whole tree, one glob | Nothing; it is the **residual** owner and loses every narrower claim, keeping what no one else takes |
28
- | `system` (`docs/architecture/systems/`) | the subtrees and exact files one machine is made of | Its globs are narrower than the package glob — specificity |
29
- | `feature` (`docs/features/`) | one capability's or one surface's globs | Same — specificity over the package glob |
30
-
31
- Every other genre owns no code and carries no `owns:`. That includes the cross-cutting doctrine in `docs/architecture/` (and `docs/architecture/common/`), which describes patterns rather than regions — a surface declares it is bound by one through `applies:`, and the doctrine never claims the surface back. Checklists, acceptance docs, ADs, operations procedures, research and brainstorm records own nothing.
32
-
33
- ## How to derive the frontmatter
34
-
35
- An owning doc's frontmatter is `objects:` (required — the product objects it designs, by their graph names; its discovery terms and its traced edge into the knowledge graph), `owns:` (required until the graph computes ownership from traces), `module:` (optional — feature docs only; a package is already a bounded unit and a system spans contexts by definition), and `status:` (required). An optional `last-reviewed:` date (stamped whenever the doc is substantively reviewed or updated) gives the `maintenance` agent a staleness signal to rank its audits by — it is tooling metadata, never narrated in the body. `type:` (required — the genre, enum = the template names) and `rev:` (generator-written generation stamp) are the same class: external tooling reads them, sessions **preserve them on edits** and never narrate or inject them. The `maintenance` agent stamps `type:` on docs that predate the key. `slug:` completes the set on generated docs: the identity key, minted once at creation and never re-derived from a display name — in the repo the filename is the slug, and the `maintenance` agent re-aligns the two when they diverge.
36
-
37
- **1. `owns:`** (the path scope)
36
+ ### `objects:`
38
37
 
39
- `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.
38
+ - **Values:** the product objects the document designs, by the names the object register uses (`docs/dna/objects/<group>.yaml`, the product-dna skill's). Where a repository has no register yet, the names its product model and its documents already use.
39
+ - **Meaning:** designs, not mentions. A feature that reads a session designs no session; the document that decides what a session is does.
40
+ - **Discovery:** there is no `keywords:` key. An object's name and its register aliases are its discovery terms.
41
+ - **Doctrine:** a doctrine document names the cross-cutting objects it governs (principal, credential, session). The surfaces bound by it are the documents that design or touch the same objects, which the closure computes.
40
42
 
41
- **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.
43
+ ### `status:`
42
44
 
43
- 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.
45
+ - **Values:** `active`, `draft` or `deprecated`. It steers a reader toward live documents.
44
46
 
45
- | The doc governs | Glob |
46
- | -------------------------------------------- | ------------------------------------ |
47
- | A feature's own directory | `src/feature/**` |
48
- | A feature split across two directories | One glob each |
49
- | A single entry-point file, and nothing beside it | The exact file path |
50
- | A whole workspace package, residually | `packages/{name}/**` — one glob, on the package doc |
51
- | A machine spanning packages | One glob or exact path per subtree it is made of, on the system doc |
52
- | A shared file several features import | **Nothing — leave it unowned** |
47
+ ### `module:`
53
48
 
54
- **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.
49
+ - **Values:** one bounded-context label on a feature document, or omitted for a cross-cutting feature. A label, not a folder. Package and system documents omit it.
55
50
 
56
- 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`.
51
+ ### `verifies:`
57
52
 
58
- **2. `module:`** (feature docs only)
53
+ - **Values:** on a behavioral checklist, the repo-relative path of the feature or UX document whose promises it proves. One is the norm.
54
+ - **Authorship:** a checklist knows what it proves, and nothing in the code says so. A checklist with no `verifies:` is an orphan: nothing implicates it when its subject changes.
59
55
 
60
- The single bounded-context label this feature belongs to, or omit it for a cross-cutting feature that belongs to no single context. A module is a label, not a folder — features sharing a `module:` value are what ties that module to code (a filter over the generated ownership index). Package and system docs omit it entirely.
56
+ ### `owns:` — transitional
61
57
 
62
- **3. `objects:` & `status:`**
58
+ - **Reason for the key:** the harness's ownership scan (`.claude/hooks/lib/ownership.mjs`) keys on `owns:` until it switches to the header pointers and the object closure, which is the knowledge layer's recorded gap. Until then the key keeps the session's ownership index and the doc-debt detectors working.
59
+ - **Form:** region globs only, one per region the document speaks for: `packages/{name}/**` on a package document, the machine's subtrees on a system document, a feature's own directory on a feature document. Never a list of individual files, and never a shared file several features import.
60
+ - **Growth:** none. A new document carries the region glob its genre needs and nothing more; an edit never adds file-level entries.
61
+ - **Retirement:** removed from every document in the same change that switches the scan, and this section with it.
63
62
 
64
- `objects:` names the product objects the document designs — the discovery terms topic→doc lookup rides, and the edge the knowledge graph renders each object's documents from. There is no `keywords:` key: an object's name is its discovery term. `status:` is `active`, `draft`, or `deprecated` — it steers the model toward live docs and away from draft/deprecated ones.
63
+ ## Retired keys
65
64
 
66
- ## The doc → doc edges
67
-
68
- A prose link is not an edge. It states that one doc mentions another; it never states **why**, so nothing can be derived from it — not who consumes a doctrine, not what to re-read before an edit, not what falls stale when a surface moves. A tree can carry hundreds of prose links and still have no graph. These two keys carry the relationships that matter; everything else stays prose.
69
-
70
- **`verifies:`** — on a behavioral checklist (`docs/tests/{feature}.md`), the feature or UX doc(s) whose promises it proves. One value is the norm; several when a checklist spans a capability that two docs decide. A checklist carrying no `verifies:` is an orphan by construction: nothing implicates it when its subject changes, so it rots silently and the rot is undetectable.
71
-
72
- **`applies:`** — on a feature or UX doc, the architecture docs it obeys. Not "docs I read" and not "docs I mention" — **obligation**: a doctrine this surface is bound by, such that a change to the doctrine implicates this doc. The narrow reading is the load-bearing one; a key that collects everything consulted degrades into a bibliography and stops meaning anything. A feature bound by nothing cross-cutting omits the key.
73
-
74
- Cross-cutting *contracts* remain diagnosed by the `boundary` skill from source, never persisted — `applies:` records which doctrine a surface answers to, not which code it touches.
65
+ | Key | Retired because | What replaces it |
66
+ | --- | --- | --- |
67
+ | `applies:` | A list of governing documents is a hand-kept copy of a relationship the graph computes through the objects both documents name, and it drifts the way any copy does | The objects the doctrine and the surface share, computed; for people, the document's Scope section names its governing doctrine and what each settles |
68
+ | `keywords:` | An object's name and aliases are its discovery terms | `objects:` |
69
+ | A file-level `owns:` list | The header pointers and the object closure compute it | Region globs only, until the scan switches (above) |
75
70
 
76
- ## What is not frontmatter, deliberately
71
+ An edit to a document that carries a retired key removes the key in the same edit.
77
72
 
78
- **`Pinned:` stays a section-level line inside a checklist**, naming the suite tests that guard that section. It is already typed and already resolved mechanically by the `maintenance` agent. Hoisting it into frontmatter would flatten per-section detail into a doc-level list and duplicate what the sections already say — the exact double-declaration the one rule above forbids.
73
+ ## Not frontmatter, deliberately
79
74
 
80
- That edge has a property the others lack, and it is worth naming: **a `Pinned:` reference terminates.** A test file exists or it does not, so the edge is falsifiable by a resolver sweep with no judgement. An `owns:` glob is only falsifiable at the dangling end — a glob that resolves says nothing about whether the doc still describes what the code does. Terminal edges get resolver sweeps; non-terminal edges need a human or an agent to read both ends. Do not build a checker that pretends otherwise.
75
+ - **`Pinned:`** stays a section-level line inside a checklist, naming the suite tests that guard that section. It is typed and resolved mechanically; hoisting it would flatten per-section detail into a document-level list.
76
+ - **Terminal and non-terminal edges:** a `Pinned:` reference terminates (a test file exists or it does not), so a resolver sweep checks it with no judgment. An `objects:` value is checked against the register by the graph's index-time lints. Whether a document still describes what its objects do needs a person or an agent to read both ends; no checker pretends otherwise.
81
77
 
82
78
  ## Rules
83
79
 
84
- - One canonical doc per path. A doc's `owns:` globs must not overlap another doc's at equal specificity — the narrower glob wins; equal-specificity overlap is an ownership conflict to resolve. Overlap at **unequal** specificity is the layering working as designed: a package doc's `packages/{name}/**` is meant to be outranked, and what survives every narrower claim is exactly what that doc describes.
85
- - Use glob patterns for directories, explicit paths for individual files. At `active`, never list paths that don't exist in the codebase; a `draft`'s projected paths are confirmed at `active`.
86
- - When a doc covers a deep feature with sub-specs (e.g., `editor/`), each sub-spec owns its own globs — don't duplicate a path across siblings.
87
- - For baseline/reconcile with many docs, use parallel sub-agents to read source and propose `owns:` globs.
88
- - **Never author an inverse.** No doc lists its verifiers, its consumers, or its dependents — those are generated. A hand-written "consumed by" table is a defect to delete, not a convention to follow.
89
- - **Every edge value is a repo-relative doc path** (`docs/architecture/common/security.md`), never a bare filename and never a display title — the resolver is a path lookup, and a title changes without the file moving.
90
- - **A dangling edge is a block, not a warning.** `verifies:` or `applies:` naming a doc that does not exist is a typo or a deletion; either way the graph is lying. Resolve at authoring time.
80
+ - **Inverses never authored:** no document lists its verifiers, its consumers, its governing doctrine's other surfaces, or the files that point at it. A hand-written "consumed by" table is a defect to delete.
81
+ - **Edge values:** `verifies:` values are repo-relative document paths, never a bare filename or a display title.
82
+ - **Dangling edges block:** a `verifies:` naming a document that does not exist, or an `objects:` value no register or product model knows, is a typo or a deletion. Resolve it at authoring time.
83
+ - **Index-time lints the graph prints:** a header pointing at a document that does not exist, a document no file points at, an object no document designs. They never fail a refresh; a session that sees one resolves it or files it.
84
+ - **Baseline and reconcile:** the `maintenance` agent stamps `type:` and `objects:`, reduces any file-level `owns:` list to its region globs, removes retired keys, and reports source files whose header names no document. Many documents ⇒ parallel sub-agents, one document each.
@@ -34,8 +34,8 @@ Every genre takes the same shape (principle 2) and the same labels (principle 5)
34
34
  | Operations procedure, migration runbook | Truth, record | The exact commands, in order, with their expected output | Nothing. The detail is the point |
35
35
  | Record (plan, changelog entry, journal, memory) | Record | Its template's fields, as of the moment it describes | Nothing. Its prose follows § Voice and § Labels; the dates and the journey are its own |
36
36
 
37
- - **Owning documents:** feature, package and system are the three genres that carry `owns:`. They take principles 5 and 6 in full, their sections are fixed (§ Owning documents — the body and the footer), and `doc-lint` reads them.
38
- - **Doctrine:** the same shape and labels as an owning document, with no `owns:` and no mechanism. Its model layer may argue in paragraphs; below the model it is bullets like every other explaining document.
37
+ - **Owning documents:** feature, package and system are the three genres that speak for code: source files name them in their headers, and the ownership scan resolves paths to them (`frontmatter.md`). They take principles 5 and 6 in full, their sections are fixed (§ Owning documents — the body and the footer), and `doc-lint` reads them.
38
+ - **Doctrine:** the same shape and labels as an owning document, speaking for no code and describing no mechanism. Its model layer may argue in paragraphs; below the model it is bullets like every other explaining document.
39
39
  - **Reference documents and procedures:** the one place a signature, a shape, a variable table, a command list or a file path is the content. They keep the detail the owning genres hand to the graph (§ Reference documents).
40
40
  - **Indexes:** a folder's `Readme.md` describes directories, never files; the root README follows `templates/root-readme.md`. Both take § Labels.
41
41
 
@@ -159,7 +159,7 @@ function lintFile(path, findings) {
159
159
  }
160
160
  if (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/features\/[^/]+\.md$/.test(path)) {
161
161
  const fm = text.startsWith('---') ? text.slice(0, text.indexOf('\n---', 3)) : '';
162
- for (const key of ['owns:', 'status:', 'type:'])
162
+ for (const key of ['objects:', 'status:', 'type:'])
163
163
  if (!fm.includes(key)) findings.push({ path, n: 1, severity: 'block', id: 'feature-frontmatter', message: `feature doc missing \`${key}\` frontmatter` });
164
164
  }
165
165
 
@@ -38,4 +38,4 @@ One file per template. Every template is either a **truth document** (states cur
38
38
 
39
39
  **Baseline scaffolds** — files that must exist after every baseline (`docs/structure.md` is not among them: it is harness-owned, shipped whole by the installer and overwritten on every refresh, and has no template); the `maintenance` agent (baseline mode) creates any that are missing: [docs-readme.md](docs-readme.md), [changelog-index.md](changelog-index.md), [backlog.md](backlog.md), [memory-main.md](memory-main.md), [setup.md](setup.md) (when the repo has a lockfile and a runnable root script; never overwriting an existing first-run procedure), and [root-readme.md](root-readme.md) (never overwriting an existing root README).
40
40
 
41
- **Ownership.** Three genres carry code — **feature**, **package**, **system** — and each is the canonical reference for the code paths in its **`owns:` frontmatter**, the only ownership key. To find what a doc owns, read its `owns:` globs; to find the doc for a path, match the path against the injected ownership index (the union of every owning doc's globs), narrower glob wins. That is what layers them: a package doc claims `packages/{name}/**` and is the tree's **residual** owner, while a system doc claims the narrower subtrees of one machine and a feature doc claims one surface — both win those paths by specificity, and the package doc keeps everything neither claims. Two docs may never claim a path at **equal** specificity. Every other genre here owns no code, including the cross-cutting doctrine in `docs/architecture/common/`. See [`../references/frontmatter.md`](../references/frontmatter.md) for turning code into `owns:` globs.
41
+ **Ownership.** Three genres speak for code — **feature**, **package**, **system**. A document names the objects it designs in `objects:`; a source file names the document that speaks for it in its path header; which document speaks for a path is computed from those headers and the object closure, and answered per path by the ownership index (`references/frontmatter.md`). Until the harness's ownership scan switches to that computation, it reads a transitional `owns:` region glob on each of the three genres: `packages/{name}/**` on a package document (the residual owner), a machine's subtrees on a system document, a feature's own directory on a feature document; the narrower glob wins.
@@ -21,7 +21,7 @@ The design layer — [architecture/](architecture/): {what the folder holds —
21
21
 
22
22
  ## Features
23
23
 
24
- [features/](features/) — one living reference per feature (how it works today), flat, named `{feature}.md`; each carries `owns:` for the code it speaks for. Find a feature by its name or by the ownership index, never by a row here.
24
+ [features/](features/) — one living reference per feature (how it works today), flat, named `{feature}.md`; each names the objects it designs in `objects:`, and the source files it speaks for name it in their headers. Find a feature by its name or by the ownership index, never by a row here.
25
25
 
26
26
  ## Tests
27
27
 
@@ -1,6 +1,6 @@
1
1
  # Doctrine Documentation (docs/architecture/common/{concern}.md)
2
2
 
3
- Truth document. Use for **one concern the platform handles everywhere**: authentication, authorization, storage, the API protocol, credentials, telemetry. One file per concern, named for the concern. A doctrine document owns no code and carries no `owns:`. A surface declares that it is bound by one through its own `applies:`.
3
+ Truth document. Use for **one concern the platform handles everywhere**: authentication, authorization, storage, the API protocol, credentials, telemetry. One file per concern, named for the concern. A doctrine document speaks for no code: no source header names it, and it carries no `owns:`. The surfaces bound by it are the documents that design or touch the objects it governs, computed through those objects, and each names it in its Scope.
4
4
 
5
5
  **A doctrine document states the design and nothing beneath it** (`references/style-guide.md` § Shape — the model first, the detail after). It is the most stable document in the tree, because it says why the platform is shaped the way it is, and that changes far less often than the code. It explains no mechanism: no function, file, column, cookie attribute, header value or per-route error code, since each of those fails the stability test. The code graph answers what the code does. This document answers why, and which principles any new code in the concern must keep.
6
6
 
@@ -77,5 +77,5 @@ Code cannot reveal what it does not yet do, so decided-but-unbuilt intent is one
77
77
  | --- | --- |
78
78
  | A tour of the code that implements the concern | The code graph |
79
79
  | The catalog of routes, error codes or variables | An API reference or environment registry |
80
- | The one feature that happens to use the concern most | That feature's own document, which `applies:` this one |
80
+ | The one feature that happens to use the concern most | That feature's own document, which names this doctrine in its Scope |
81
81
  | The story of how the concern got this way | A decision record (`decisions/`) for the why, and the changelog for the when |
@@ -8,7 +8,7 @@ Truth document. Use for every feature in `docs/features/`: something a person po
8
8
  - **Each bullet is a bold label, then one or two third-person sentences** saying what happens: "**Organization user:** user lands in their existing organization", never "you land…".
9
9
  - **Flows carry the journey; Decisions carry the reasons; Promises run deepest.**
10
10
 
11
- Every computable fact (columns, vocabularies, foreign keys, writers, routes, guards, files) is the code graph's (`docs/architecture/systems/knowledge-layer.md`), and a hand-typed copy is the defect this genre removes. Code is named only where a person types or sees it (a page, a command, a flag, an environment variable) or at a cross-cutting seam the graph would not lead a reader to.
11
+ The frontmatter names the objects the feature designs; which files it speaks for is computed from the source headers and the object closure, never listed (`references/frontmatter.md`). Every computable fact (columns, vocabularies, foreign keys, writers, routes, guards, files) is the code graph's (`docs/architecture/systems/knowledge-layer.md`), and a hand-typed copy is the defect this genre removes. Code is named only where a person types or sees it (a page, a command, a flag, an environment variable) or at a cross-cutting seam the graph would not lead a reader to.
12
12
 
13
13
  **A feature document runs deepest toward its end.** Scope and Flows are the model and the journey. **Promises** goes as deep as the behaviour does: every state the surface can be in, every edge case, what a refusal tells the person, and the exact wording wherever the wording itself is promised. That depth is earned because a person or a test checks it and the code cannot state it as intent. It still stops before mechanism. A promise reads "a removed membership shows a panel, not a redirect", never which route answers it.
14
14
 
@@ -32,9 +32,8 @@ Nothing else. `doc-lint` blocks the retired mechanism sections (`mechanism-secti
32
32
  type: feature # required tooling key — preserve, never narrate
33
33
  status: active # required — active | draft | deprecated
34
34
  objects: [device, credential grant] # required — the product objects this document designs, by their graph names
35
- owns: ["packages/studio/src/server/routes/devices.ts"] # required until the graph computes ownership — the code this document speaks for; narrower glob wins
36
35
  module: identity # optional — one bounded-context label
37
- applies: [docs/architecture/common/security.md] # optional — doctrine this surface is BOUND by
36
+ owns: ["packages/studio/src/server/devices/**"] # transitional — one region glob, read only by the ownership scan until it switches to header pointers (references/frontmatter.md)
38
37
  ---
39
38
  ```
40
39
 
@@ -90,6 +89,6 @@ No tables, routes or symbol rosters — those are the graph's.
90
89
  | When | What is written |
91
90
  | --- | --- |
92
91
  | `status: draft` (design) | goal, Scope, Decisions, Promises (with gaps named), For the agent; the LLD research goes to the plan |
93
- | `status: active` | Flows written from the shipped surface; `owns:` confirmed against real code |
92
+ | `status: active` | Flows written from the shipped surface; `objects:` confirmed against the register, and each owned source file's header names this document |
94
93
 
95
94
  A feature whose code is gone is deleted, or `status: deprecated` with its footer pointing at what replaced it.
@@ -6,7 +6,7 @@ Truth document. Use for: **one workspace package** — one unit of distribution:
6
6
 
7
7
  **A package document's deepest layer is its public contract** (`references/style-guide.md` § Shape — the model first, the detail after): the commands and flags a person types, the exports another package imports, what is installed and where. For a CLI the flags *are* the promise, so they belong in Usage. It stops before internal layout, build steps and which file holds what.
8
8
 
9
- **A package document is the residual owner of its own tree.** Its `owns:` is the whole package glob — the broadest claim on those paths, so it loses to every narrower system or feature claim. The one enumerable fact it declares is the ownership table: which subtrees other documents claim.
9
+ **A package document is the residual owner of its own tree.** Every file in the package whose header names no narrower document is its to speak for, so it loses to every system or feature document a header names. Until the ownership scan switches to header pointers, its transitional `owns:` is the whole package glob. The one enumerable fact it declares is the ownership table: which subtrees other documents speak for.
10
10
 
11
11
  **Sharp, bulleted, labelled with noun phrases** (`references/style-guide.md` § Labels). Every section after the goal line is bullets under a noun-phrase heading, each bullet a bold noun-phrase label followed by one or two sentences.
12
12
 
@@ -29,8 +29,7 @@ Nothing else. The feature template's lint rules apply. Forty to a hundred and tw
29
29
  type: package
30
30
  status: active
31
31
  objects: [nustack cli, device credential] # required — the objects this package designs or ships
32
- owns: ["packages/{name}/**"] # required until the graph computes ownership — the residual claim
33
- applies: [docs/architecture/common/api.md] # optional
32
+ owns: ["packages/{name}/**"] # transitional — the residual region glob, read only by the ownership scan until it switches to header pointers
34
33
  ---
35
34
  ```
36
35
 
@@ -37,7 +37,7 @@ Sections come in this order; 1 to 3 are the outward-facing first screen, 4 onwar
37
37
  - **Relative links into the repo only.** Never an absolute repo URL. Never an external site as the home of a canonical doc.
38
38
  - **Link folders and mandated scaffolds, never rosters.** The allowed targets are folders, `docs/README.md`, `docs/structure.md`, `docs/operations/setup.md`, `docs/history/CHANGELOG.md`, `docs/history/backlog.md`, `CLAUDE.md` and the owning docs in the Map. A procedure is named only where a Destinations row needs it. Every heading and table header is a noun phrase (`../references/style-guide.md` § Labels). Never list individual feature docs: the ownership index finds them.
39
39
  - **No tree.** The Map stops at top-level units and never lists files. Mark paths that look primary but are not, for example "`legacy/`: reference only, never edited".
40
- - **The Map is a coarse view of the ownership index, not a rival to it.** The Owning doc column names the doc that the index's `owns:` globs resolve to. When the two disagree the index wins, and the Map row is fixed in the same change.
40
+ - **The Map is a coarse view of the ownership index, not a rival to it.** The Owning doc column names the doc the index resolves for that path. When the two disagree the index wins, and the Map row is fixed in the same change.
41
41
  - **No version numbers and no dates.** Name the file that pins a version (`.nvmrc`, `packageManager`, `rust-toolchain.toml`). Write in the present tense, with no "coming soon" and no "as of".
42
42
  - **State rules, don't argue them.** One line each. The reasoning lives in the linked doc, and agent process rules stay in `CLAUDE.md`.
43
43
  - **Commands come from the repo.** Detect the package manager from the lockfile (`pnpm-lock.yaml` gives pnpm, `yarn.lock` gives yarn, otherwise npm). Use only scripts that exist, each with a trailing comment saying what it does.
@@ -6,7 +6,7 @@ Truth document. Use for: **one machine that spans packages**, which a person can
6
6
 
7
7
  **A system document's deepest layer is the machine's behaviour across packages** (`references/style-guide.md` § Shape — the model first, the detail after): the order it runs in, the sequence and state diagrams at role level, the invariant that holds at each place where parts meet, and what re-drives a stall. That layer is earned because no single file shows a cross-package flow. It stops before per-function steps, tables, columns and routes.
8
8
 
9
- **A system document's globs win by specificity.** Its `owns:` names the subtrees and exact files the machine is made of, narrower than the package glob. Two documents never claim a path at equal specificity; every glob matches a file on disk at `status: active`.
9
+ **A system document speaks for the files the machine is made of.** Their headers name it, which outranks the package document's residual claim. Until the ownership scan switches to header pointers, its transitional `owns:` names the machine's subtrees as region globs, narrower than the package glob; every glob matches a file on disk at `status: active`.
10
10
 
11
11
  **Sharp, bulleted, labelled with noun phrases** (`references/style-guide.md` § Labels). The opening paragraph defines the machine; everything after it is bullets under noun-phrase headings, each bullet a bold noun-phrase label followed by one or two sentences.
12
12
 
@@ -28,10 +28,8 @@ Nothing else. The lint rules of the feature template apply. Sixty to a hundred a
28
28
  type: system # required tooling key
29
29
  status: active # required — draft = the machine as a whole does not run
30
30
  objects: [run, run ledger, settlement] # required — the objects this machine owns or moves
31
- owns: # required until the graph computes ownership — narrower than the package glob
31
+ owns: # transitional — region globs, one per subtree; read only by the ownership scan until it switches to header pointers (references/frontmatter.md)
32
32
  - packages/{pkg}/src/{machine}/**
33
- applies: # optional — decisions and doctrine this machine is BOUND by
34
- - docs/architecture/decisions/AD-0NN-{slug}.md
35
33
  ---
36
34
  ```
37
35
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Truth document.** States what a person sees and can do on a surface, and what the surface promises never to do. Present tense, like every truth document.
4
4
 
5
- **`type: ux`**, plus `applies:` — the architecture docs this surface is bound by (the state vocabulary, the routing doctrine, the information architecture it answers to). Same key and same narrow reading as a feature doc's: obligation, not a reading list.
5
+ **`type: ux`**, plus `status:` and `objects:` — the objects the surface presents (`references/frontmatter.md`). The doctrine it answers to (the state vocabulary, the routing doctrine, the information architecture) is named in its Purpose for people and computed through shared objects for tooling.
6
6
 
7
7
  **Why this genre exists.** A feature doc (LLD) states what a feature is, how its data flows, and which contracts it honours — all of which stay plausibly true through a rewrite of the surface. Nothing else in the repo states what a person *observes*. So a change to the wire lands, the interface absorbs it, and the absorption reads as maintenance: no test fails, no lint fires, no document is contradicted. This document is the thing a silent change contradicts.
8
8
 
@@ -12,12 +12,12 @@
12
12
 
13
13
  | The behavior is… | Where it lives |
14
14
  | --- | --- |
15
- | One surface's — a panel, an editor, a dialog | `docs/features/{surface}-ux.md`, with `owns:` over that surface's component paths |
15
+ | One surface's — a panel, an editor, a dialog | `docs/features/{surface}-ux.md`; its component files' headers name it |
16
16
  | Cross-cutting — routing, information architecture, the state vocabulary every surface obeys, presentation of streams | `docs/architecture/` — owns no code, like every architecture doc |
17
17
 
18
- **Ownership: take the component globs only where they are unclaimed.** A surface's components are frequently already owned — by the page's LLD, by the feature behind one panel — and claiming them again is an ownership conflict, not protection. So: unclaimed globs go in `owns:`; otherwise the doc declares `owns: []` and is named in each owning LLD's **coordinated edits** row, phrased as *different axes, not a conflict* — the LLD owns the code, this document owns what a person observes.
18
+ **Ownership: a component file names one document in its header.** A surface's components are frequently already spoken for — by the page's feature document, by the feature behind one panel — and a file's header names exactly one document. Where the header already names the feature document, this document is named in that feature document's **coordinated edits** row instead, phrased as *different promises over the same files*, and the object closure links the two through the objects both name. Where no document speaks for the component yet, its header names this one.
19
19
 
20
- That matters because a doc nothing implicates is a doc that rots, and a surface rewritten under an unimplicated document is the failure this genre exists to prevent. The coordinated-edit row is the weaker of the two bindings; prefer real ownership wherever the globs are free.
20
+ That matters because a doc nothing implicates is a doc that rots, and a surface rewritten under an unimplicated document is the failure this genre exists to prevent. The coordinated-edit row is the weaker of the two bindings; prefer the header pointer wherever no document speaks for the component yet.
21
21
 
22
22
  ---
23
23
 
@@ -33,7 +33,7 @@ Parse these from the user's prompt; use the default when unstated.
33
33
 
34
34
  1. If `./docs/memory/change-review.md` exists, read it for historical velocity snapshots, prior risk-zone flags, known concentration risks, and the project's **Module Taxonomy** / **Feature Taxonomy**.
35
35
  2. If a CSV from a prior run already exists at the project root (`change-review-YYYY-MM-DD.csv`), prefer it as input and **skip Phase 1** — re-classify only when the requested window changed.
36
- 3. Build the ownership map from feature `owns:` frontmatter — consult the injected ownership index (`glob → doc · module · objects · status`), or glob `docs/features/**/*.md` and read each doc's `owns:` globs — to map churn to owning feature docs and their `module:` labels.
36
+ 3. Build the ownership map — consult the injected ownership index (`glob → doc · module · objects · status`), or read each changed file's path header for the document it names, falling back to each feature doc's transitional `owns:` globs — to map churn to owning feature docs and their `module:` labels.
37
37
 
38
38
  ---
39
39
 
@@ -198,7 +198,7 @@ For each module with high churn (top quartile in the 1m window), check how contr
198
198
 
199
199
  - Does its code own cross-cutting contract surfaces (schema/data access, wire handlers, auth/authz, event dispatch, env/config readers)? Skim the module's files to judge — contract-heavy churn is where silent breakage lives.
200
200
  - Is the module's owning feature doc up to date? (cross-reference with the maintenance agent's last drift check if available)
201
- - Do the module's code paths appear under any feature doc's `owns:` globs?
201
+ - Do the module's source files name a document in their headers, or fall under a feature doc's transitional `owns:` glob?
202
202
 
203
203
  Flag as a risk zone: high churn × contract-heavy × low doc coverage.
204
204
 
@@ -221,7 +221,7 @@ Per module, list which authors made the most commits in the 3-month window. Surf
221
221
  - **Risk zones**: top 5 high-churn modules that own contract-heavy surfaces, with the specific contracts at risk named
222
222
  - **Stabilization signals**: modules where review overhead can decrease
223
223
  - **Concentration risks**: bus-factor and review-coverage flags
224
- - **Recommendations**: prioritized list — typically "run the `boundary` skill on changes touching X", "add `owns:` coverage for Y to a feature doc", "spread reviews of Z across more authors"
224
+ - **Recommendations**: prioritized list — typically "run the `boundary` skill on changes touching X", "add header pointers naming a feature doc to the files under Y", "spread reviews of Z across more authors"
225
225
 
226
226
  ### Update tracking
227
227
 
@@ -21,7 +21,7 @@ Map the documentation landscape — what exists, what is adjacent, how detailed
21
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
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 · objects · 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.
24
+ 5. Consult the **injected ownership index** (the generated `glob → doc · module · objects · status` map — computed from source headers and the transitional `owns:` region globs) to build the ownership map. It is the source of truth for what code paths are already spoken for; a source file's path header names its document directly. 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
 
@@ -30,7 +30,7 @@ Map the documentation landscape — what exists, what is adjacent, how detailed
30
30
  3. **Affected modules** — Identify what files, routes, components, and data flows are relevant
31
31
  4. **Current state** — Understand how the system works today in the area being specified
32
32
  5. **Dependencies** — Map what this feature touches and what depends on it
33
- 6. **Ownership conflicts** — Compare the new feature's intended code paths against existing feature docs' `owns:` globs. Report overlaps (two docs claiming the same paths at equal specificity).
33
+ 6. **Ownership conflicts** — Compare the new feature's intended code paths against the documents those paths already reach — the documents their source headers name, and existing transitional `owns:` region globs. Report overlaps: paths another document already speaks for, or two region globs at equal specificity.
34
34
  7. **Invalidation** — a different question from conflict, and the one that gets missed. A conflict is two docs claiming one glob; an invalidation is **one existing doc whose stated behavior this feature makes false** — it may own entirely different paths and still be wrong once this ships. For every doc whose territory this feature touches or whose described flow it changes, name the specific claim that stops being true.
35
35
 
36
36
  ## What You Return
@@ -42,7 +42,7 @@ Structure your findings as:
42
42
  - **Affected modules**: Files, routes, components that will be involved
43
43
  - **Current behavior**: How the relevant part of the system works today
44
44
  - **Conventions**: Naming, structure, and patterns to follow based on existing code
45
- - **Ownership conflicts**: Existing feature docs whose `owns:` globs overlap with the proposed feature. Include the conflicting paths and the owning doc name.
45
+ - **Ownership conflicts**: Existing documents that already speak for the proposed feature's paths (by source header or transitional region glob). Include the conflicting paths and the owning doc name.
46
46
  - **Invalidated claims**: One row per existing doc this feature makes wrong — doc path, the claim, why it stops being true. This list seeds the plan's **Doc Obligations** ledger, so it is the research finding most likely to survive into an artifact; "none" is a real answer and says the feature is genuinely additive.
47
47
  - **Risks or concerns**: Anything the LLD should address or call out
48
48
 
@@ -44,29 +44,32 @@ 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 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:`.
47
+ Ownership is **computed, never typed**, per the `documentation` skill's `references/frontmatter.md`: a document names the objects it designs (`objects:`), and each source file's path header names the document that speaks for it. Until the harness's ownership scan switches to those edges, it reads a **transitional `owns:` region glob** on each owning document — the same source the SessionStart ownership index derives from today. There is no `docs/routing.md` and no precomputed file to regenerate; you rebuild the map from the source headers and the transitional globs.
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 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
- 3. Build a map: glob → owning doc + module. Narrower glob wins on overlap. **This IS the source of truth.**
52
+ 2. Read each source file's path header for the `docs/…` path it names — the header pointer, the authored edge on the code's side.
53
+ 3. Glob the docs tree for markdown carrying frontmatter, ignoring `*.bkp.*` files (installer backups awaiting the fold) and anything under `docs/history/` (frozen records quote retired globs as provenance). For each, parse `objects:`, `module:` and any transitional `owns:` region globs.
54
+ 4. Build the map: path → the document its header names; where a header names none, the narrowest transitional glob that matches. Where the two disagree, report both — never pick one silently.
54
55
 
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
+ **Architecture-first fallback.** If no source header names a document and 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 computed map becomes primary the moment any header or feature doc names ownership — this is a fallback, not a replacement.
56
57
 
57
58
  ### Step 2: Validate ownership
58
59
 
59
- For each `owns:` glob across the feature docs:
60
+ Across the header pointers and the transitional globs:
60
61
 
61
- - Verify the glob matches existing files. Report globs matching nothing, deleted paths, and orphaned docs.
62
- - Flag **equal-specificity overlaps** — two feature docs whose `owns:` globs claim the same paths at the same specificity (an ambiguous owner). `node .claude/hooks/check-ownership.mjs` reports these mechanically alongside dead globs (`overlaps()` in `lib/ownership.mjs`); a glob at strictly higher specificity carving out of a broader `**` is the designed layering and is never flagged. Surface a real overlap as a question — narrowing someone's glob is an author's call, not remediation.
62
+ - **Header pointers:** a header naming a document that does not exist is remediation — correct the path. A source file whose header names no document, inside a region an owning document speaks for, is reported as a header to add, never invented.
63
+ - **Transitional globs:** verify each matches existing files; report globs matching nothing, deleted paths, and orphaned docs. A file-level `owns:` list is reduced to its region globs (remediation, not a question); the files it dropped are reported where their headers name no document.
64
+ - **Retired keys:** `applies:` and `keywords:` are removed wherever found (remediation); a doctrine the removed `applies:` named stays named in the document's Scope section.
65
+ - Flag **equal-specificity overlaps** — two feature docs whose transitional `owns:` globs claim the same paths at the same specificity (an ambiguous owner). `node .claude/hooks/check-ownership.mjs` reports these mechanically alongside dead globs (`overlaps()` in `lib/ownership.mjs`); a glob at strictly higher specificity carving out of a broader `**` is the designed layering and is never flagged. Surface a real overlap as a question — narrowing someone's glob is an author's call, not remediation.
63
66
  - **Stamp `type:` where missing.** Every doc carries a `type:` genre key (enum = the `documentation` skill's template names); docs predating the key are stamped mechanically — the genre is readable from placement and shape (a `docs/features/` doc is `type: feature`, an AD is `type: architecture-decision`). Stamping is remediation, not a question; an ambiguous genre is backlogged instead.
64
67
 
65
- **Then validate the doc→doc edges — `owns:` is only half the graph.** `node .claude/hooks/check-ownership.mjs` and the session-injected **doc edge index** carry both directions; read the edge index and check three things:
68
+ **Then validate the doc→doc edge.** `node .claude/hooks/check-ownership.mjs` and the session-injected **doc edge index** carry `verifies:` and its generated inverse; read the edge index and check two things:
66
69
 
67
- - **Dangling edges.** A `verifies:` or `applies:` naming a doc that does not exist is remediation, not a question — fix the path, or drop the edge when its target was deleted deliberately. The generated index marks these `← TARGET MISSING`.
70
+ - **Dangling edges.** A `verifies:` naming a doc that does not exist is remediation, not a question — fix the path, or drop the edge when its target was deleted deliberately. The generated index marks these `← TARGET MISSING`.
68
71
  - **Checklists with no `verifies:`.** Every `docs/tests/` file names the doc whose promises it proves. A checklist without one is unreachable — nothing implicates it when its subject moves — so it is the single highest-value backfill in the tree, and it is mechanical wherever a same-named feature doc exists (`docs/tests/auth.md` → `docs/features/auth.md`). Where the name does not match, ask rather than guess.
69
- - **Architecture docs nothing declares.** An architecture doc named by no `applies:` is either genuinely unbound or a doctrine everyone obeys and nobody declares. Both are worth reporting; neither is auto-fixable, because `applies:` states an obligation only the consuming doc's author can assert. Report the list; backlog the judgement.
72
+ - **Doctrine with no `objects:`.** A `docs/architecture/common/` document that names no objects is reachable by nothing: the closure that links a doctrine to the surfaces it governs runs through the objects both name. Report the list; naming a doctrine's objects is its author's call.
70
73
 
71
74
  **Never author an inverse to fix a gap.** A doc listing its own verifiers or consumers is a defect — the inverse is generated. If the index looks wrong, the frontmatter at the consuming end is what is wrong.
72
75
 
@@ -85,15 +88,15 @@ Also check the **env contract** when the repo declares a canonical `.env.example
85
88
 
86
89
  Run `git log --since="2 weeks ago" --name-only --pretty=format:"%h %s"` to find changed source files. For each changed file:
87
90
 
88
- - Match against the `owns:` map (narrower glob wins) → identify the owning doc.
89
- - If no glob matches → flag as **unowned**.
91
+ - Match against the map from Step 1 (the header pointer first, then the narrowest transitional glob) → identify the owning doc.
92
+ - If neither names a document → flag as **unowned**.
90
93
  - If an owner exists → compare last-modified dates (`git log -1 --format="%ci" -- <path>`), flag docs where code changed more recently (14-day threshold).
91
94
 
92
95
  **Audit priority.** Rank the docs to inspect by their `last-reviewed:` frontmatter (optional key): never-stamped first, then oldest-first. A doc reviewed last week rarely needs a re-read; one never stamped is the likeliest to have drifted.
93
96
 
94
97
  ### Step 4: Check architecture & backlog
95
98
 
96
- **Unowned paths.** Compare the repo's actual top-level source directories (derive them — the top-level dirs that contain code, or the roots named across the `owns:` globs; do not assume `src/`) against the `owns:` map. Report directories no `owns:` glob covers.
99
+ **Unowned paths.** Compare the repo's actual top-level source directories (derive them — the top-level dirs that contain code, or the roots named across the headers and the transitional globs; do not assume `src/`) against the map. Report directories whose files no header and no transitional glob reaches.
97
100
 
98
101
  **Architecture drift.** Grep `docs/architecture/` for file-path references. Verify referenced paths still exist. Remove or update stale references.
99
102
 
@@ -134,7 +137,7 @@ docs/
134
137
  ├── architecture/ # Cross-module design + steering layer
135
138
  │ │ # domain-model, glossary, non-goals, references, open-questions
136
139
  │ └── decisions/ # Architecture Decisions (ADs — one per subject, updated in place; journey in each AD's Decision Changelog)
137
- ├── features/ # Feature specs — the only first-class doc unit (flat; ownership in each doc's `owns:` frontmatter)
140
+ ├── features/ # Feature specs — the only first-class doc unit (flat; ownership computed from each doc's `objects:` and the source headers)
138
141
  │ └── {group}/ # browse-only subfolder for grouping; NEVER a module; may carry a Readme.md
139
142
  ├── operations/ # Setup, development, deployment, CI/CD
140
143
  │ └── bookkeeping.md # the shared doc-sync work queue (hook-appended, bookkeeper-drained, walled at commit) — author-created, never scaffolded; treat rows as live work, never as drift
@@ -156,7 +159,7 @@ docs/
156
159
 
157
160
  **✱ marks the scaffolded files — the only paths a baseline creates, and the only shape signals.** `docs/structure.md` is not one of them: the installer owns it and rewrites it on every install or refresh, so a missing or hand-edited `structure.md` means the harness needs refreshing (`npx @nurix/etna`), never a scaffold from a template — and its body is never converged, only its `harness-version` field stamped. Every **folder** in this tree is created on first use and is simply absent until it holds a file; git cannot track an empty directory, so a missing folder is never evidence of drift and is never scaffolded to satisfy the shape. Its role is already in `docs/structure.md`; a repo's own notes about legacy folders go in `docs/README.md` § Notes for this repository.
158
161
 
159
- There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillars/`**, **no `docs/boundaries/`**. Code ownership lives in feature `owns:` frontmatter; a module is a `module:` label, not a folder; boundaries are diagnosed from source; brainstorm records live in `docs/research/` (active) and `docs/history/brainstorms/` (harvested).
162
+ There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillars/`**, **no `docs/boundaries/`**. Code ownership is computed from `objects:` and source headers, with a transitional `owns:` region glob; a module is a `module:` label, not a folder; boundaries are diagnosed from source; brainstorm records live in `docs/research/` (active) and `docs/history/brainstorms/` (harvested).
160
163
 
161
164
  > Brainstorm records are **author-created, never scaffolded**; legacy active folders carry the older `-brainstorming/` suffix — treat both spellings as canonical. **Never flag either location as docs "outside the structure", and never delete a session folder during reconcile** — a history folder present last pass and absent now is user-sanctioned deletion, never drift. Keep each folder's `Readme.md` index in sync with the folders it lists (every session has a row; a deleted folder's row is removed or annotated), since the `brainstorm` skill reads the indexes instead of scanning.
162
165
 
@@ -176,7 +179,7 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
176
179
  | `*.bkp.*` files at the repo root or in config (`CLAUDE.bkp.md`, `.prettierrc.bkp.json`, …) | **never merge these.** Top-level config is harness-owned by design; list each one to the user as their call, and leave the file in place |
177
180
  | no `harness-version` in `structure.md` | stamp it (Step B4) |
178
181
  | `docs/tests/` checklists carrying **no frontmatter** (the shape before the graph — the genre was exempt) | **backfill `type: checklist` + `verifies:`.** Mechanical where a same-named feature or UX doc exists; where the name does not match, list the checklist to the user with your best candidate and let them confirm. This is the row that upgrades a repo from a tree of files to a graph — without it every checklist in an older repo stays unreachable, and nothing detects that |
179
- | feature / UX docs with no `applies:`, in a tree whose `docs/architecture/` is non-empty | **report, never invent.** `applies:` is an obligation the doc's author asserts, not a similarity a scan can infer; a guessed edge is worse than a missing one because it reads as declared. List the candidates (architecture docs the feature's prose already links) and backlog the confirmation |
182
+ | docs carrying a retired `applies:` or `keywords:` key, or a file-level `owns:` list | **remove the retired key; reduce `owns:` to its region globs.** Mechanical: a doctrine the removed `applies:` named stays named in the document's Scope; the files a reduced `owns:` dropped are reported where their headers name no document (`documentation` skill, `references/frontmatter.md`) |
180
183
 
181
184
  **The support window: three past minor versions.** The fold table covers shapes at most three minors behind the installed harness — which today means the rows above, because every older dissolved construct (`docs/routing.md`, `docs/modules/`, pillars, the Refinement Ledger, the ADR genre, legacy brainstorm folders, …) predates the window. An older shape is **reported, never improvised**: name what the probe found, point the user at etna's dated migration runbooks (`docs/history/migrations/` in the forge), and recommend a fresh baseline over an archaeological fold — a repo more than three minors behind re-baselines rather than migrates.
182
185
 
@@ -215,8 +218,8 @@ Write `harness-version: <installed version>` (the semver from `.claude/harness.j
215
218
 
216
219
  Modules are **labels, declared, never inferred from physical layout**. Derive the repo's candidate bounded contexts directly from the code and docs: distinct wire surfaces (who owns routes/handlers), schema/data-access ownership, entry points and process units, plus any contexts `docs/architecture/` already names.
217
220
 
218
- - Propose the derived contexts as candidate `module:` labels. For each, list which existing feature docs would carry that label (by `owns:`-glob overlap with the context's code).
219
- - **Propose — don't force.** Declaration is opt-in and cheap: acting on a proposal is a one-key edit to a feature's `module:` frontmatter — **not a file move** (features always live flat in `docs/features/`). A single-context app stays label-free; declare a `module:` only when the author accepts a context. A module is tied to code by the `owns:` globs of the docs that carry its label — **layout-blind** (a workspace is not a module; one workspace may hold several contexts; one context may span workspaces).
221
+ - Propose the derived contexts as candidate `module:` labels. For each, list which existing feature docs would carry that label (by which of the context's files name them in their headers, or fall under their transitional globs).
222
+ - **Propose — don't force.** Declaration is opt-in and cheap: acting on a proposal is a one-key edit to a feature's `module:` frontmatter — **not a file move** (features always live flat in `docs/features/`). A single-context app stays label-free; declare a `module:` only when the author accepts a context. A module is tied to code through the documents that carry its label — the files their headers and transitional globs reach — **layout-blind** (a workspace is not a module; one workspace may hold several contexts; one context may span workspaces).
220
223
 
221
224
  ### Step B6 — Compact the agent memory
222
225
 
@@ -235,7 +238,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
235
238
  - **Enforce the memory cap on every run** (§ Strategy memory) — you are its only writer, so no one else can do it.
236
239
  - **A clean report is valid** — do not invent issues.
237
240
  - **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.
238
- - **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: `objects:` · `owns:` · `module:` · `status:` · `last-reviewed:` · `type: feature` · `derivation: "extracted (baseline YYYY-MM-DD, nustack graph + source read)"`), plus the architecture, tests, and operations layers. Use the `nustack-graph` 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
+ - **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: `objects:` · a transitional `owns:` region glob · `module:` · `status:` · `last-reviewed:` · `type: feature` · `derivation: "extracted (baseline YYYY-MM-DD, nustack graph + source read)"`), plus the architecture, tests, and operations layers. Use the `nustack-graph` 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.
239
242
  - **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.
240
243
  - **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: nustack 0.9.0`): use it — localize every question through `nustack graph` before reading source, and never run an index step first: the family re-indexes a stale store before it answers, so the only freshness decision left is the deliberate `--no-refresh`. **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: nustack 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.
241
244
  - **One sentinel.** `docs/structure.md` `harness-version` is the only baseline/version marker.