@nurix/etna 0.20.25 → 0.20.27

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 (75) hide show
  1. package/data/harness-index.json +1 -1
  2. package/data/harnesses/code/CLAUDE.md +4 -4
  3. package/data/harnesses/code/agents/bookkeeper.md +1 -1
  4. package/data/harnesses/code/agents/change-review.md +1 -1
  5. package/data/harnesses/code/agents/lld-mode.md +1 -1
  6. package/data/harnesses/code/agents/maintenance.md +7 -9
  7. package/data/harnesses/code/docs/structure.md +133 -71
  8. package/data/harnesses/code/harness.json +4 -4
  9. package/data/harnesses/code/hooks/awareness.mjs +0 -10
  10. package/data/harnesses/code/hooks/lib/ownership.mjs +4 -2
  11. package/data/harnesses/code/hooks/lib/sweep.mjs +4 -8
  12. package/data/harnesses/code/skills/documentation/SKILL.md +22 -11
  13. package/data/harnesses/code/skills/documentation/references/examples.md +29 -25
  14. package/data/harnesses/code/skills/documentation/references/frontmatter.md +3 -3
  15. package/data/harnesses/code/skills/documentation/references/style-guide.md +211 -292
  16. package/data/harnesses/code/skills/documentation/references/untemplated.md +4 -2
  17. package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +104 -2
  18. package/data/harnesses/code/skills/documentation/templates/Readme.md +4 -3
  19. package/data/harnesses/code/skills/documentation/templates/bookkeeping.md +1 -1
  20. package/data/harnesses/code/skills/documentation/templates/docs-readme.md +15 -36
  21. package/data/harnesses/code/skills/documentation/templates/doctrine.md +81 -0
  22. package/data/harnesses/code/skills/documentation/templates/feature.md +63 -119
  23. package/data/harnesses/code/skills/documentation/templates/handover.md +174 -0
  24. package/data/harnesses/code/skills/documentation/templates/operations-procedure.md +1 -1
  25. package/data/harnesses/code/skills/documentation/templates/package.md +49 -92
  26. package/data/harnesses/code/skills/documentation/templates/root-readme.md +103 -65
  27. package/data/harnesses/code/skills/documentation/templates/setup.md +138 -0
  28. package/data/harnesses/code/skills/documentation/templates/system.md +45 -107
  29. package/data/harnesses/delivery/agents/lld-mode.md +1 -1
  30. package/data/harnesses/delivery/agents/maintenance.md +7 -9
  31. package/data/harnesses/delivery/harness.json +2 -2
  32. package/data/harnesses/delivery/skills/documentation/SKILL.md +22 -11
  33. package/data/harnesses/delivery/skills/documentation/references/examples.md +29 -25
  34. package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +3 -3
  35. package/data/harnesses/delivery/skills/documentation/references/style-guide.md +211 -292
  36. package/data/harnesses/delivery/skills/documentation/references/untemplated.md +4 -2
  37. package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +104 -2
  38. package/data/harnesses/delivery/skills/documentation/templates/Readme.md +4 -3
  39. package/data/harnesses/delivery/skills/documentation/templates/bookkeeping.md +1 -1
  40. package/data/harnesses/delivery/skills/documentation/templates/docs-readme.md +15 -36
  41. package/data/harnesses/delivery/skills/documentation/templates/doctrine.md +81 -0
  42. package/data/harnesses/delivery/skills/documentation/templates/feature.md +63 -119
  43. package/data/harnesses/delivery/skills/documentation/templates/handover.md +174 -0
  44. package/data/harnesses/delivery/skills/documentation/templates/operations-procedure.md +1 -1
  45. package/data/harnesses/delivery/skills/documentation/templates/package.md +49 -92
  46. package/data/harnesses/delivery/skills/documentation/templates/root-readme.md +103 -65
  47. package/data/harnesses/delivery/skills/documentation/templates/setup.md +138 -0
  48. package/data/harnesses/delivery/skills/documentation/templates/system.md +45 -107
  49. package/data/harnesses/etna/agents/change-review.md +1 -1
  50. package/data/harnesses/etna/agents/lld-mode.md +1 -1
  51. package/data/harnesses/etna/agents/maintenance.md +7 -9
  52. package/data/harnesses/etna/harness.json +2 -2
  53. package/data/harnesses/etna/skills/documentation/SKILL.md +22 -11
  54. package/data/harnesses/etna/skills/documentation/references/examples.md +29 -25
  55. package/data/harnesses/etna/skills/documentation/references/frontmatter.md +3 -3
  56. package/data/harnesses/etna/skills/documentation/references/style-guide.md +211 -292
  57. package/data/harnesses/etna/skills/documentation/references/untemplated.md +4 -2
  58. package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +104 -2
  59. package/data/harnesses/etna/skills/documentation/templates/Readme.md +4 -3
  60. package/data/harnesses/etna/skills/documentation/templates/bookkeeping.md +1 -1
  61. package/data/harnesses/etna/skills/documentation/templates/docs-readme.md +15 -36
  62. package/data/harnesses/etna/skills/documentation/templates/doctrine.md +81 -0
  63. package/data/harnesses/etna/skills/documentation/templates/feature.md +63 -119
  64. package/data/harnesses/etna/skills/documentation/templates/handover.md +174 -0
  65. package/data/harnesses/etna/skills/documentation/templates/operations-procedure.md +1 -1
  66. package/data/harnesses/etna/skills/documentation/templates/package.md +49 -92
  67. package/data/harnesses/etna/skills/documentation/templates/root-readme.md +103 -65
  68. package/data/harnesses/etna/skills/documentation/templates/setup.md +138 -0
  69. package/data/harnesses/etna/skills/documentation/templates/system.md +45 -107
  70. package/lib/install/materialize.mjs +4 -12
  71. package/lib/install/plan.mjs +4 -11
  72. package/package.json +3 -2
  73. package/data/harnesses/code/skills/documentation/templates/structure.md +0 -71
  74. package/data/harnesses/delivery/skills/documentation/templates/structure.md +0 -71
  75. package/data/harnesses/etna/skills/documentation/templates/structure.md +0 -71
@@ -22,14 +22,16 @@ When a draft wants to be both — the current state *and* the story of getting t
22
22
  ## 3. Shape it
23
23
 
24
24
  - **Open with the goal.** The first line under the H1 states what the document is for and who reads it — a reader (or agent) landing here cold decides in one sentence whether to keep reading.
25
+ - **Headings are noun phrases** — no verb, no tense, no person (`references/style-guide.md` § Labels).
25
26
  - **Headings are retrieval keys.** Sections get pulled out of context by search and agents; qualify generic headings with the topic (`## Merge rules — data sync`, not `## Rules`) so each stands alone.
26
27
  - **Tables for enumerable facts, prose for judgment.** One row per item; anything needing a *because* lives in prose around the table, never inside a cell.
27
28
  - **Every section earns its place.** No empty stubs — omit what has nothing real to say; write an explicit "None" only where the absence itself is information.
28
- - **Cite sources and link neighbours.** If the document mentions a concept another document owns, link it; if it draws on code, name the files.
29
+ - **Build from the outside in.** The question and its boundary, then the model, then the parts, then the detail — and nothing that fails the stability test (`references/style-guide.md` § Shape — the model first, the detail after). An explanation of a concern the whole platform handles is not untemplated: it is `templates/doctrine.md`.
30
+ - **Cite sources and link neighbours.** If the document mentions a concept another document owns, link it; if it draws on code, name the directory it lives in — the code graph finds the files, and a file list goes stale.
29
31
 
30
32
  ## 4. Write it in the house style
31
33
 
32
- `references/style-guide.md` carries the rules: present tense with no history narration, progress markers, or stale-reference tripwires (§ The Two Natures); imperative/active voice; code examples under 15 lines showing contracts, not implementations; ASCII diagrams in docs only; inline-code every identifier, filename, command, and config key.
34
+ `references/style-guide.md` carries the rules: present tense with no history narration, progress markers, or stale-reference tripwires (§ The Two Natures); third-person, present-tense, active sentences after noun-phrase labels (§ Voice, § Labels); diagrams in documents only, beside the prose and never in its place (§ Diagrams); signatures, state shapes and code examples under 15 lines in reference documents only (§ Reference documents).
33
35
 
34
36
  ## 5. With a human in the loop — the co-authoring workflow
35
37
 
@@ -17,7 +17,7 @@ const RULES = [
17
17
  { id: 'status-glyph', severity: 'warn', scope: 'truth', exempt: /(^|\/)docs\/(tests|operations)\//, pattern: /(^|\s)(✅|⏳|- \[[ x]\])/u, message: 'status glyph / checklist in a truth doc — tracking belongs in a record' },
18
18
  // Truth docs are supposed to cite records, and record filenames are dated — so a date inside a link
19
19
  // target or a code span is a path, not narration. Strip those before matching.
20
- { id: 'date-in-truth-doc', severity: 'block', scope: 'truth', strip: /\[[^\]]*\]\([^)]*\)|`[^`]*`|^(last-reviewed|last-updated|rev|harness-version):.*$/gm, pattern: /\b20\d{2}-\d{2}-\d{2}\b/, message: 'date in a truth doc — cite the record by name and link; the closed exceptions are the frontmatter tooling keys, an AD Decision Changelog tail and structure.md\'s Health Log tail' },
20
+ { id: 'date-in-truth-doc', severity: 'block', scope: 'truth', strip: /\[[^\]]*\]\([^)]*\)|`[^`]*`|^(last-reviewed|last-updated|rev|harness-version):.*$/gm, pattern: /\b20\d{2}-\d{2}-\d{2}\b/, message: 'date in a truth doc — cite the record by name and link; the closed exceptions are the frontmatter tooling keys, an AD Decision Changelog tail and a dated path inside a link or code span' },
21
21
  ];
22
22
 
23
23
  // Sections inside a truth doc where dated history is sanctioned (the two bridges).
@@ -119,11 +119,113 @@ function lintFile(path, findings) {
119
119
 
120
120
  // A folder index (Readme.md/README.md) is a browse page, not a feature doc — it owns no code.
121
121
  const isFolderIndex = /(^|\/)(readme)\.md$/i.test(path);
122
+
123
+ // An index names folders, never files (SKILL.md § The Two Natures). A link from a folder index or
124
+ // the authority map to an individual document is a row that must be edited on every add, rename
125
+ // and retirement; the only files an index may link are the mandated scaffolds and a sibling
126
+ // folder's own Readme.
127
+ const isAuthorityMap = /(^|\/)docs\/structure\.md$/.test(path);
128
+ const isRootReadme = /^(\.\/)?README\.md$/i.test(path.replace(process.cwd() + '/', ''));
129
+ if (scope === 'truth' && (isFolderIndex || isAuthorityMap) && !disabled.has('index-lists-files')) {
130
+ const SCAFFOLD = /(^|\/)(readme|structure|changelog|backlog|main|health-log|journal|claude|agents)\.md$|(^|\/)operations\/setup\.md$/i;
131
+ for (const { n, line } of lines) {
132
+ if (line.includes('doc-lint-disable: index-lists-files')) continue;
133
+ for (const m of line.matchAll(/\]\(([^)\s#]+\.md)(#[^)]*)?\)/g)) {
134
+ const target = m[1];
135
+ if (/^[a-z]+:\/\//.test(target) || SCAFFOLD.test(target)) continue;
136
+ // The repo-root README routes by intent (templates/root-readme.md): its Map names owning package and system
137
+ // docs, its Destinations rows name procedures and rules. Individual feature docs stay off it — the ownership index finds them.
138
+ if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|common)\/|operations\/)[^/]+\.md$|(^|\/)\.claude\/rules\/[^/]+\.md$/.test(target)) continue;
139
+ if (isRootReadme && /(^|\/)docs\/features\/[^/]+\.md$/.test(target)) { findings.push({ path, n, severity: 'warn', id: 'root-readme-feature-link', message: `root README links an individual feature doc (\`${target}\`) — point at docs/features/ and let the ownership index find it`, excerpt: line.trim().slice(0, 90) }); continue; }
140
+ findings.push({ path, n, severity: 'block', id: 'index-lists-files', message: `index links an individual document (\`${target}\`) — name the folder and how its files are named; \`ls\` is the roster`, excerpt: line.trim().slice(0, 90) });
141
+ }
142
+ }
143
+ }
144
+ // A folder index describes directories; a drawn tree is a file roster in another costume
145
+ // (SKILL.md § Indexes describe directories, never files). structure.md is the authority map and keeps its tree.
146
+ if (scope === 'truth' && isFolderIndex && (isRootReadme || /(^|\/)docs\//.test(path)) && !disabled.has('index-file-tree')) {
147
+ // Raw lines, not proseLines: a tree is drawn inside a code fence, which proseLines drops. The repo-root README routes by a Map table; its tree is docs/structure.md's too (templates/root-readme.md).
148
+ const rawLines = text.split('\n').map((line, i) => ({ n: i + 1, line }));
149
+ const tree = rawLines.find(({ line }) => /^\s*[│├└]/.test(line));
150
+ if (tree) findings.push({ path, n: tree.n, severity: 'block', id: 'index-file-tree', message: 'a tree drawn in a folder index — describe each directory in a sentence; the tree is docs/structure.md\'s', excerpt: tree.line.trim().slice(0, 90) });
151
+ }
152
+ // The repo-root README is an orientation page with a budget (templates/root-readme.md): up to about 140 non-blank lines, plain Markdown, no chrome.
153
+ if (scope === 'truth' && isRootReadme) {
154
+ const raw = text.split('\n');
155
+ const nonBlank = raw.filter((l) => l.trim()).length;
156
+ if (!disabled.has('root-readme-budget') && nonBlank > 140) findings.push({ path, n: 1, severity: 'warn', id: 'root-readme-budget', message: `${nonBlank} non-blank lines — the root README orients and routes; a section that needs a paragraph needs a doc (templates/root-readme.md)` });
157
+ const chrome = raw.findIndex((l) => /shields\.io|<img\b|<div\b|<p align|^!\[/.test(l));
158
+ if (!disabled.has('root-readme-chrome') && chrome !== -1) findings.push({ path, n: chrome + 1, severity: 'block', id: 'root-readme-chrome', message: 'badge, image or HTML layout in the root README — agents cannot read it and the team does not need it', excerpt: raw[chrome].trim().slice(0, 90) });
159
+ }
122
160
  if (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/features\/[^/]+\.md$/.test(path)) {
123
161
  const fm = text.startsWith('---') ? text.slice(0, text.indexOf('\n---', 3)) : '';
124
- for (const key of ['owns:', 'keywords:', 'status:', 'type:'])
162
+ for (const key of ['owns:', 'status:', 'type:'])
125
163
  if (!fm.includes(key)) findings.push({ path, n: 1, severity: 'block', id: 'feature-frontmatter', message: `feature doc missing \`${key}\` frontmatter` });
126
164
  }
165
+
166
+ // An owning document (feature, package, system) is intent and promises; the sections that narrate
167
+ // mechanism — a file roster, a walk through the code, a schema or route table — are the graph's
168
+ // to compute and are retired from the genre (SKILL.md § A document is intent and promises).
169
+ const isOwningDoc = scope === 'truth' && !isFolderIndex && /(^|\/)docs\/(features|architecture\/(packages|systems))\/[^/]+\.md$/.test(path);
170
+ if (isOwningDoc && !disabled.has('mechanism-section')) {
171
+ const RETIRED = /^#{2,4}\s+(Files|Current Behavior|Key Functions|Data|API|Refusals?|Architecture|State|Redux|Affected Modules|Conventions to Follow|Ownership Conflicts|Tests|The shape|Layout|Build and test|Commands|Routes|Schema|Wire)\b/i;
172
+ for (const { n, line } of lines) {
173
+ if (!RETIRED.test(line) || line.includes('doc-lint-disable: mechanism-section')) continue;
174
+ findings.push({ path, n, severity: 'block', id: 'mechanism-section', message: 'mechanism section in an owning document — columns, routes, guards, functions and files are the graph\'s to compute; keep the decision or the promise, point by name, cut the rest', excerpt: line.trim().slice(0, 90) });
175
+ }
176
+ const fm = text.startsWith('---') ? text.slice(0, text.indexOf('\n---', 3)) : '';
177
+ if (!/^objects:/m.test(fm)) findings.push({ path, n: 1, severity: 'block', id: 'objects-missing', message: 'owning document names no `objects:` — the product objects it designs are its edge into the graph and its discovery terms' });
178
+ }
179
+
180
+ // The body of an owning document is scanned by people; its "For the agent" footer holds the pointers
181
+ // (style-guide § Owning documents — the body and the footer). Body identifiers warn, never block: a seam reference
182
+ // is allowed in the body, and the judgment of which identifier is a seam is the author's.
183
+ if (isOwningDoc) {
184
+ const footerAt = lines.findIndex(({ line }) => /^##\s+For the agent\b/.test(line));
185
+ const body = footerAt === -1 ? lines : lines.slice(0, footerAt);
186
+ let section = null; // proseLines already dropped the frontmatter and the fences
187
+ const sections = [];
188
+ for (const { n, line } of body) {
189
+ if (/^##\s+/.test(line)) {
190
+ section = { n, title: line.replace(/^##\s+/, '').trim(), ids: 0 };
191
+ sections.push(section);
192
+ continue;
193
+ }
194
+ if (!section) continue;
195
+ for (const m of line.matchAll(/`([^`]+)`/g)) {
196
+ const t = m[1];
197
+ // What a person types or sees is the feature's surface, not code: a page path, a flag, an environment variable, a command.
198
+ if (/^\/[a-z]|^--?[a-z]|^[A-Z][A-Z0-9_]+(=\S*)?$|^[a-z][\w-]* [a-z-]/.test(t)) continue;
199
+ if (/[\/]|\.(ts|tsx|mjs|js|md|json|sql)$|_|\(|^[a-z]+[A-Z]|^[A-Z][a-z]+[A-Z]|^(GET|POST|PUT|PATCH|DELETE) /.test(t)) section.ids++;
200
+ }
201
+ }
202
+ for (const s of sections) {
203
+ if (/^(Ownership|Gaps|Known gaps)$/.test(s.title)) continue;
204
+ if (!disabled.has('body-identifier') && s.ids > 3) findings.push({ path, n: s.n, severity: 'warn', id: 'body-identifier', message: `${s.ids} code identifiers in a body section — the body names code only where a person types or sees it, or at a cross-cutting seam (a middleware, gate, hook, store); the rest is plain names, with pointers in "For the agent"`, excerpt: s.title });
205
+ }
206
+ }
207
+
208
+ // A heading or a bold bullet label names; it never says. A label with a finite verb drags in a tense,
209
+ // a person and a voice ("What was decided", "You already belong to…"), so a label opening with a
210
+ // wh-word or a personal pronoun, or ending in a full stop, is a clause (style-guide § Labels).
211
+ const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/common\/[^/]+\.md$/.test(path));
212
+ if (isLabelledDoc && !disabled.has('clause-label')) {
213
+ const CLAUSE = /^(how|what|why|where|when|who|which|whose|you|your|we|our|i|it)\b|\b(you|your)\b|\.$/i;
214
+ for (const { n, line } of lines) {
215
+ const heading = /^#{2,4}\s+(.+?)\s*$/.exec(line);
216
+ const bold = /^\s*[-*]\s+\*\*(.+?)\*\*/.exec(line);
217
+ const label = heading ? heading[1] : bold ? bold[1].replace(/:$/, '').trim() : null;
218
+ if (!label || /^For the agent$/i.test(label)) continue;
219
+ if (CLAUSE.test(label.replace(/`[^`]*`/g, 'X'))) findings.push({ path, n, severity: 'warn', id: 'clause-label', message: 'a heading or bold label written as a clause — a label is a noun phrase with no verb, tense or person ("Decisions", not "What was decided"; "Organization member:", not "You already belong to an organization:")', excerpt: label.slice(0, 90) });
220
+ }
221
+ // The sentences after a label describe the product in the third person; quoted interface copy keeps its own "you".
222
+ if (!disabled.has('second-person')) {
223
+ for (const { n, line } of lines) {
224
+ const bare = line.replace(/`[^`]*`/g, '').replace(/\*\*[^*]+\*\*/g, 'X').replace(/\*"[^"]*"\*|\*[^*]+\*|"[^"]*"/g, '');
225
+ if (/\b(you|your|yours|yourself)\b/i.test(bare)) findings.push({ path, n, severity: 'warn', id: 'second-person', message: 'second person in a truth document — the sentence after a label is third person ("user lands…", never "you land…"); quoted interface copy is exempt', excerpt: line.trim().slice(0, 90) });
226
+ }
227
+ }
228
+ }
127
229
  }
128
230
 
129
231
  // A changed plan whose committed version is already frozen must not be edited.
@@ -6,6 +6,7 @@ One file per template. Every template is either a **truth document** (states cur
6
6
  | --- | --- | --- |
7
7
  | Feature (LLD) | [feature.md](feature.md) | Truth |
8
8
  | Package (one workspace package) | [package.md](package.md) | Truth |
9
+ | Doctrine (one concern the platform handles everywhere) | [doctrine.md](doctrine.md) | Truth |
9
10
  | System (one machine spanning packages) | [system.md](system.md) | Truth |
10
11
  | Architecture Decision (AD) | [architecture-decision.md](architecture-decision.md) | Truth (Decision Changelog tail sanctioned) |
11
12
  | System overview | [system-overview.md](system-overview.md) | Truth |
@@ -18,13 +19,13 @@ One file per template. Every template is either a **truth document** (states cur
18
19
  | Testing strategy | [testing-strategy.md](testing-strategy.md) | Truth |
19
20
  | Behavioral checklist | [behavioral-checklist.md](behavioral-checklist.md) | Truth |
20
21
  | UX behavior | [ux-behavior.md](ux-behavior.md) | Truth |
22
+ | Setup (first run, one per repo) | [setup.md](setup.md) | Truth |
21
23
  | Operations procedure | [operations-procedure.md](operations-procedure.md) | Truth |
22
24
  | Implementation map | [implementation-map.md](implementation-map.md) | Truth |
23
25
  | Bookkeeping ledger | [bookkeeping.md](bookkeeping.md) | Truth (work queue) |
24
26
  | Protected files | [protected-files.md](protected-files.md) | Truth |
25
- | Project root README | [root-readme.md](root-readme.md) | Truth |
27
+ | Project root README (outward opening, then orientation and routing) | [root-readme.md](root-readme.md) | Truth |
26
28
  | docs/README.md (doc index) | [docs-readme.md](docs-readme.md) | Truth |
27
- | docs/structure.md (authority map) | [structure.md](structure.md) | Truth (Health Log tail sanctioned) |
28
29
  | Plan | [plan.md](plan.md) | Record |
29
30
  | Changelog entry | [changelog-entry.md](changelog-entry.md) | Record |
30
31
  | Migration runbook | [migration-runbook.md](migration-runbook.md) | Record |
@@ -35,6 +36,6 @@ One file per template. Every template is either a **truth document** (states cur
35
36
 
36
37
  **The `type:` enum lives here.** A doc's `type:` frontmatter takes its value from this table — one genre per template (`feature`, `architecture-decision`, `non-goals`, …). External generators consume this list rather than defining their own; a type this table does not name defaults to `docs/architecture/` placement and is never added to the protected-files list.
37
38
 
38
- **Baseline scaffolds** — files that must exist after every baseline; the `maintenance` agent (baseline mode) creates any that are missing: [docs-readme.md](docs-readme.md), [structure.md](structure.md), [changelog-index.md](changelog-index.md), [backlog.md](backlog.md), [memory-main.md](memory-main.md), and [root-readme.md](root-readme.md) (never overwriting an existing root README).
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).
39
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.
@@ -21,4 +21,4 @@ The shared work queue keeping documentation and code in sync — a queue, never
21
21
 
22
22
  **A waiver covers a finding only when it names the finding's kind and its subject in backticks** — the matcher (`waiverCovers`) tests the backticked subject exactly, so a waiver written without them silences nothing and the wall stands. A `waived:` row's `expires=` date is enforced: CI honors the waiver until the date, then fails. `dead-glob` waivers double as the dead-glob allowlist; `ownership-overlap` waivers cover the CI ownership check the same way.
23
23
 
24
- Finding kinds for `auto:`: `dead-glob` · `doc-implicated` · `doc-lint` · `header-missing` · `index-row-missing` · `package-rejected` · `frozen-record-edited` · `source-lint`. The CI ownership check additionally accepts `waived:` rows of kind `ownership-overlap`.
24
+ Finding kinds for `auto:`: `dead-glob` · `doc-implicated` · `doc-lint` · `header-missing` · `package-rejected` · `frozen-record-edited` · `source-lint`. The CI ownership check additionally accepts `waived:` rows of kind `ownership-overlap`.
@@ -2,67 +2,46 @@
2
2
 
3
3
  Truth document. Use for: the entry point for all documentation in a repo. Created during baseline audit.
4
4
 
5
+ **This file describes directories, never files.** Each section names one folder under `docs/` and says what the folder holds, who reads it and how its files are named — a description of the directory, not of its contents. No per-file rows, no file tree, no `├──` block: the tree is `structure.md`'s, as the authority on placement, and `ls` is the roster. A row per file forces an index edit on every file added, renamed or retired, and the index's own history then records roster churn instead of what the index said — a repo carried 103 commits on `docs/README.md` before this rule, most of them row edits. Each section below names a folder and says what the folder holds and how its files are named; the only files an index may name are the mandated scaffolds (`structure.md`, `operations/setup.md`, `history/CHANGELOG.md`, `history/backlog.md`) and the root `CLAUDE.md`. The repo-root `README.md` routes the team and agents in by intent; this file describes the folders of the docs tree — each links the other once and neither restates the other. `doc-lint` blocks a folder index that links an individual document (`index-lists-files`) or draws a tree (`index-file-tree`).
6
+
5
7
  **Link only what exists.** Every row below is a placeholder — drop any whose target the repo does not have. Most `history/` and `architecture/` folders are created on first use, so linking them unconditionally ships a docs index with dead links on day one. An index is a browse page: a link that resolves to nothing is worse than an absent row.
6
8
 
7
9
  ```
8
10
  # {Project Name} Documentation
9
11
 
10
- Technical documentation for {project description}.
12
+ Technical documentation for {project description}. {Two or three sentences: what the product is, its planes or packages, who the docs are for.}
11
13
 
12
14
  > For documentation organization, authority map, and folder rules, see **[structure.md](structure.md)**.
13
15
 
14
16
  ---
15
17
 
16
- ## Quick Links
17
-
18
- | Document | Description |
19
- | --------------------------------- | ------------------------------------ |
20
- | [Structure](structure.md) | Documentation entry point and rules |
21
- | [Setup](operations/setup.md) | Installation and configuration guide |
22
-
23
- ---
24
-
25
18
  ## Architecture
26
19
 
27
- System design, steering docs, and standing decisions — the truth layer.
28
-
29
- - [system-design.md](architecture/system-design.md) - System overview: subsystems and key flows
30
- - [domain-model.md](architecture/domain-model.md) - Entities, relationships, state ownership
31
- - [glossary.md](architecture/glossary.md) - Project vocabulary
32
- - [non-goals.md](architecture/non-goals.md) - What this system deliberately does not do
33
- - [decisions/](architecture/decisions/Readme.md) - Architecture Decisions (ADs)
34
- {list other architecture docs — api.md, security.md — when present}
35
-
36
- ---
20
+ The design layer — [architecture/](architecture/): {what the folder holds — the system overview, steering docs, cross-cutting doctrine, the per-package and per-system documents, and `decisions/` (one Architecture Decision per subject, `AD-NNN-{slug}.md`, status in each file's frontmatter)}.
37
21
 
38
22
  ## Features
39
23
 
40
- Living reference docs for implemented features (how things currently work).
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.
41
25
 
42
- - [feature-name.md](features/feature-name.md) - Feature description
43
- {list all feature docs}
26
+ ## Tests
44
27
 
45
- ---
28
+ [tests/](tests/) — one behavioral checklist per documented surface, `{feature}.md`, `verifies:` naming the document whose promises it proves.
46
29
 
47
30
  ## Operations
48
31
 
49
- How to run, develop, and deploy.
32
+ [operations/](operations/) — how to set up, run, develop, deploy and recover. Start at [`setup.md`](operations/setup.md) on a new machine; every other procedure is named for its task.
33
+
34
+ ## History
50
35
 
51
- - [setup.md](operations/setup.md) - Installation and configuration guide
52
- {list all operations docs — environment.md when present}
36
+ [history/](history/) — the records: `plans/` (one dated plan per unit of work, frozen at completion), `changelog/` (one entry per unit of work, indexed by [CHANGELOG.md](history/CHANGELOG.md)), `migrations/` (one-shot runbooks), `brainstorms/`, `research/`, and [backlog.md](history/backlog.md).
53
37
 
54
- ---
38
+ ## Research and memory
55
39
 
56
- ## History
40
+ [research/](research/) — in-progress brainstorm sessions and standing research. [memory/](memory/) — project memory, one file per agent.
57
41
 
58
- The records — everything dated, frozen, or append-only.
42
+ ## Notes for this repository
59
43
 
60
- - [CHANGELOG.md](history/CHANGELOG.md) - Master changelog index
61
- - [changelog/](history/changelog/) - Individual changelog entries
62
- - [plans/](history/plans/) - Implementation plans (frozen at completion)
63
- - [migrations/](history/migrations/) - One-shot migration runbooks (frozen once executed)
64
- - [research/](history/research/) - Deep-research artifacts (one folder per topic)
65
- - [backlog.md](history/backlog.md) - Tracked work items and technical debt
44
+ {The one place project-specific placement facts live, because `structure.md` is the harness's and is overwritten on every refresh: legacy folders kept as frozen provenance, a retired genre whose residue is still on disk, a local exception to a folder rule. One bullet each; delete the section when there are none.}
66
45
 
67
46
  ---
68
47
 
@@ -0,0 +1,81 @@
1
+ # Doctrine Documentation (docs/architecture/common/{concern}.md)
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:`.
4
+
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
+
7
+ **Required:**
8
+
9
+ - the frontmatter
10
+ - the opening (the question, the boundary, and the authority sentence)
11
+ - **The model**, ending in numbered principles, each with its reason
12
+ - **The parts**: one table or diagram placing every part in the model, then their relations
13
+ - **one section per part**
14
+ - **For the agent**
15
+
16
+ **Optional:**
17
+
18
+ - an `UNBUILT` block for decided-but-unbuilt intent
19
+
20
+ Nothing else: the neighbouring documents live in **For the agent**, the one closing link surface (`references/style-guide.md` § Self-contained documents and the link surface). Expect one hundred and fifty to three hundred lines; past that, mechanism has usually crept in.
21
+
22
+ ```yaml
23
+ ---
24
+ type: doctrine # required tooling key
25
+ status: active # required — active | draft
26
+ objects: [principal, credential, device grant] # recommended — the concepts this doctrine governs, by their graph names
27
+ ---
28
+ ```
29
+
30
+ ```
31
+ # {Concern}
32
+
33
+ {The question this concern answers for every request, row or run, in one sentence, bolded — then what it does not answer and which document does. One paragraph.}
34
+
35
+ {The authority sentence: this document states the design, not the code; for what does what, read the code roots in the footer or ask the code graph; where this document and the code disagree on *what*, the code is right; on *why* or a principle, the disagreement is a defect to raise.}
36
+
37
+ ## The model
38
+
39
+ ### {The ownership boundary, as a noun phrase — e.g. "Identity: checked, not owned"}
40
+
41
+ {The one framing fact a reader needs before anything else. Plain words, no addresses.}
42
+
43
+ ### {The participants, as a noun phrase — e.g. "Callers"}
44
+
45
+ {Who or what participates: one bullet each, a bold noun-phrase label in plain words, its term of art in parentheses, then one line.}
46
+
47
+ ### {The organizing axis, as a noun phrase — e.g. "Proof checking"}
48
+
49
+ {The one or two dimensions every part is placed on. A small table is right here.}
50
+
51
+ ### Principles
52
+
53
+ {Numbered invariants that hold across every part, each a bold noun-phrase label, then the rule and its reason in a sentence or two. Later sections cite them by number.}
54
+
55
+ ## {The parts, as a noun phrase — e.g. "Credential families"}
56
+
57
+ {One table: each part placed on the model's axes. Then a "Relations" subsection: what they share, where they meet, what keeps them apart, and the order in which they are tried — each a bullet with a bold noun-phrase label and its reason. A role-level diagram (mermaid) where the relationships are a flow.}
58
+
59
+ ## {The parts in depth, as a noun phrase — e.g. "Families in depth"}
60
+
61
+ ### {One part, named for who meets it — e.g. "Browser person", "Trusted system"}
62
+
63
+ {One line on why the part exists, then the decisions it rests on as bullets — a bold noun-phrase label, then the reason — citing principles by number. A sequence diagram at role level where the part is a flow. Detail that a person or test can check is welcome; detail that only restates the code is not.}
64
+
65
+ ## For the agent
66
+
67
+ {Pointers, not an inventory: the objects with one line each; the code roots as directories; any cross-cutting seam by name; the neighbouring doctrine with a purpose each.}
68
+ ```
69
+
70
+ ### Marking what is decided and unbuilt
71
+
72
+ Code cannot reveal what it does not yet do, so decided-but-unbuilt intent is one of the few places a doctrine document records something the code does not. Wrap it in `<!-- UNBUILT:START — {what, and the evidence it is not built} -->` … `<!-- UNBUILT:END -->`, in plain words. A design decided *against* carries no marker.
73
+
74
+ ### What a doctrine document is not
75
+
76
+ | It is not | That is |
77
+ | --- | --- |
78
+ | A tour of the code that implements the concern | The code graph |
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 |
81
+ | The story of how the concern got this way | A decision record (`decisions/`) for the why, and the changelog for the when |
@@ -1,151 +1,95 @@
1
- # Feature Documentation — the LLD (docs/features/{feature}.md)
1
+ # Feature Documentation (docs/features/{feature}.md)
2
2
 
3
- Truth document. Use for: every feature in `docs/features/`. The feature doc is the feature's **LLD (low-level design)**: the same file persists from design (Status: Draft, written in LLD Mode) through implementation to live reference (Status: Active). Sections fill in as the feature matures — no template swap. The body always describes the feature as it currently is; the journey lives in the linked plan and changelog entries.
3
+ Truth document. Use for every feature in `docs/features/`: something a person points at, such as an address, a command family or a named control. One file per feature, cut by the objects it designs, never by the code's routers, tables, plans or packages. The file persists from design (`status: draft`) to live reference (`status: active`).
4
4
 
5
- **Required vs optional.** Required in every feature doc: the frontmatter, **Goal**, and **Background** — plus **Files** once implementation exists (a Draft for unbuilt code has no real paths to list) and **Current Behavior** once research or implementation exists (at Draft it's the pre-feature baseline; at Active it's how the feature works today). Every other section is optional: include it only when the feature has something real to put in it (no Redux → no Redux section; no risks worth naming → no Risks section). A **minimal form** is sanctioned for small features: frontmatter + Goal + a short how-it-works section + Files. A two-screen doc that is accurate beats a full-template doc with empty sections.
5
+ **A feature document is sharp, bulleted and actionable** (`references/style-guide.md` § Labels; § Shape — the model first, the detail after). A reader scans it rather than reads it:
6
6
 
7
- The four research-derived sections (**Affected Modules**, **Conventions to Follow**, **Current Behavior**, **Ownership Conflicts**) map 1:1 to the `lld-mode` agent's return shape. The orchestrator drops its findings into those slots without restructuring.
7
+ - **Headings and bullet labels are noun phrases.** For example `Flows`, `Organization member`, `Dead grant` — never a clause like "How it works" or "You already belong to an organization".
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
+ - **Flows carry the journey; Decisions carry the reasons; Promises run deepest.**
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.
12
+
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
+
15
+ **Sections:**
16
+
17
+ | Section | When |
18
+ | --- | --- |
19
+ | Frontmatter | required |
20
+ | Goal line | required |
21
+ | `Scope` | required |
22
+ | `Flows` | required |
23
+ | `Decisions` | once any choice was made that the code cannot explain |
24
+ | `Promises` | required |
25
+ | `Gaps` | only when there is one |
26
+ | `For the agent` | required |
27
+
28
+ Nothing else. `doc-lint` blocks the retired mechanism sections (`mechanism-section`), and warns on code identifiers in the body (`body-identifier`), on a heading or bullet label written as a clause (`clause-label`), and on "you" outside quoted copy (`second-person`).
8
29
 
9
30
  ```yaml
10
31
  ---
11
- owns: ["src/feature/**", "src/other/file.ts"] # required — code-path globs this doc owns (the only ownership key)
12
- module: billing # optional — single bounded-context label; omit if cross-cutting
13
- keywords: [invoice, pdf] # required — discovery terms
32
+ type: feature # required tooling key — preserve, never narrate
14
33
  status: active # required — active | draft | deprecated
15
- applies: [docs/architecture/common/security.md] # optional — architecture docs this surface is BOUND by (obligation, not a bibliography); omit when none
16
- last-reviewed: YYYY-MM-DD # optional tooling key — stamped on substantive review/update; the maintenance agent ranks audit priority by it (metadata, never narrated in the body)
17
- type: feature # required tooling key — genre stamp (generator-written or authored; enum = the template names), read by tooling (UI grouping, protected-file mapping); preserve on edits, never narrate
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
+ module: identity # optional — one bounded-context label
37
+ applies: [docs/architecture/common/security.md] # optional — doctrine this surface is BOUND by
18
38
  ---
19
39
  ```
20
40
 
21
- > Declare the code paths it owns in its `owns:` frontmatter — projected at Draft, confirmed against real code at Active.
22
-
23
41
  ```
24
- # {Feature Name}
25
-
26
- <!-- Lifecycle lives in the frontmatter `status:` key (active | draft | deprecated) — the single source of truth. Don't restate it in the body. -->
27
- **Plan:** [history/plans/YYYY-MM-DD-feature-name.md](../history/plans/YYYY-MM-DD-feature-name.md) <!-- added once a plan exists -->
28
-
29
- ## Goal
30
-
31
- One sentence. What this feature accomplishes, in user-facing terms.
32
-
33
- ## Goals & Non-Goals
34
-
35
- **In scope**
36
- - Bullet 1
37
- - Bullet 2
38
-
39
- **Out of scope**
40
- - Bullet 1
41
-
42
- ## Background
43
-
44
- Why this feature exists. The user need, the business trigger, the constraint that pushed it above the line. One paragraph.
45
-
46
- (For Product-level work, also include: problem statement, key hypotheses, open questions.)
47
-
48
- ## Approach
42
+ # {Feature name}
49
43
 
50
- High-level shape of the solution. ASCII diagram if it helps. Implementation specifics belong in the plan.
44
+ {One sentence: what this lets a person do.}
51
45
 
52
- ## Affected Modules
46
+ ## Scope
53
47
 
54
- (From lld-mode research) Files, routes, components, slices this feature involves. Group by module.
48
+ - **Covers:** {the surfaces, commands and journeys this document owns}
49
+ - **Bound by:** {the doctrine documents it applies, each with what it settles}
50
+ - **Not here:** {the neighbouring features a reader might expect here, each linked}
55
51
 
56
- | Module | Path | Role in this feature |
57
- |--------|------|----------------------|
52
+ ## Flows
58
53
 
59
- ## Conventions to Follow
54
+ ### {Entry point or situation, as a noun phrase — e.g. "Browser", "Terminal", "Landing", "Sign-out"}
60
55
 
61
- (From lld-mode research) Naming patterns, structural patterns, doc-fit heuristics confirmed for the area being touched. Cite the source file(s) where the convention is established.
56
+ - **{Noun-phrase label — e.g. "Signed-out visitor", "Claimed domain", "`nustack login`"}:** {what the user does or meets, and what happens. One or two sentences, third person, present tense — "user lands…", never "you land…".}
57
+ - {a sub-case, only where the parent genuinely branches}
62
58
 
63
- ## Current Behavior
59
+ ## Decisions
64
60
 
65
- (From lld-mode research) How the relevant part of the system works *today*, before this feature lands. Without this baseline the design intent is hard to evaluate.
61
+ - **{The decision as a noun phrase — e.g. "Single entry, no sign-up", "Unsigned organization cookie"}:** {why, in one or two sentences, and the alternative a reader would reach for if it matters.}
66
62
 
67
- ## Ownership Conflicts
63
+ ## Promises
68
64
 
69
- (From lld-mode research) Existing feature docs whose `owns:` globs overlap with the projected scope. Resolve before the plan starts — leave nothing unresolved.
65
+ ### {Theme as a noun phrase — e.g. "Sign-in failures", "Gate outcomes", "Terminal grant states"}
70
66
 
71
- | Conflicting doc | Overlapping path(s) | Resolution |
72
- |-----------------|---------------------|------------|
67
+ {A table where the cases are parallel states — | Situation | What the person sees | — carrying the exact wording where the wording is promised. Bullets otherwise:}
73
68
 
74
- ## Architecture
69
+ - **{Noun-phrase label}:** {the guarantee or refusal, checkable by a person or a test.}
75
70
 
76
- [ASCII diagram. Fill once implementation lands. See style-guide.md for box-drawing patterns.]
71
+ ## Gaps
77
72
 
78
- ## Key Functions
73
+ - **{Noun-phrase label}:** {what the code does not keep, and what closing it would take. The backlog row carries the rest.}
79
74
 
80
- ### `functionName(param1, param2)`
81
- **Goal:** What it accomplishes
82
- **Input:**
83
- - `param1` (type) - Description
84
- - `param2` (type) - Description
85
- **Output:** Return type and shape
86
- **Side Effects:** DB writes, API calls, state updates
75
+ ## For the agent
87
76
 
88
- ### `hookName(config)`
89
- **Goal:** What it provides
90
- **Returns:** `{ data, loading, error }`
91
- **Triggers:** On mount, on change, manual
77
+ - **Objects:** {object}: {one plain line}; {object}: {…}
78
+ - **Pages:** {`/…`}
79
+ - **Commands:** {`nustack …`}
80
+ - **Code roots:** {`packages/…/src/…/`}, directories where the graph should start
81
+ - **Seams:** {`name`}, only where the roots would not lead to them
82
+ - **Checklist:** `tests/{feature}.md`
83
+ - **See also:** {neighbour}: {why}
92
84
 
93
- ## State
94
-
95
- **Frontend (at persistence):**
96
- {
97
- "data": [],
98
- "changes": { "added": [], "updated": [], "removed": [] }
99
- // "cache": {} // UNUSED: dead key; kept so old persisted state rehydrates
100
- }
101
-
102
- **Storage:** localStorage `app_state`
103
-
104
- **Backend:**
105
- export const resources = pgTable('resources', {
106
- id: text('id').primaryKey(),
107
- data: jsonb('data'),
108
- // oldField: text('old_field'), // UNUSED: dead column; nothing reads it
109
- });
110
-
111
- **API Shape:**
112
- { "status": 1, "data": {...} }
113
-
114
- ## Redux (if applicable)
115
-
116
- **Slice:** `sliceName`
117
-
118
- **Actions:**
119
- - `setData(payload)` - Replace all data
120
- - `upsertItem(item)` - Add or update single item
121
-
122
- **Selectors:**
123
- - `selectAll(state)` → `Array<Item>`
124
- - `selectById(state, id)` → `Item | undefined`
125
-
126
- ## Files
127
-
128
- - `src/path/to/main.js` - Core logic
129
- - `src/path/to/hooks.js` - React hooks
130
-
131
- ## Open Questions
132
-
133
- - Question — who decides, by when
134
- - (Resolved questions: fold the resolution into Background.)
135
-
136
- ## Risks
137
-
138
- - Risk — likelihood / impact / mitigation
85
+ No tables, routes or symbol rosters — those are the graph's.
139
86
  ```
140
87
 
141
- ### Section evolution
142
-
143
- Sections fill in across the feature's lifecycle. No template swap — they accumulate in place.
88
+ ### Lifecycle
144
89
 
145
- | When | Sections written | Source |
146
- | --- | --- | --- |
147
- | Status: Draft (design) | Goal, Goals & Non-Goals, Background, Approach, Open Questions, Risks | User interview |
148
- | Status: Draft (research) | Affected Modules, Conventions to Follow, Current Behavior, Ownership Conflicts | lld-mode agent |
149
- | Status: Draft → Active (implementation lands) | Architecture, Key Functions, State, Redux, Files; `owns:` globs confirmed against real code | Main agent (from actual code) |
90
+ | When | What is written |
91
+ | --- | --- |
92
+ | `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 |
150
94
 
151
- Resolved Open Questions can be summarized into Background; unresolved ones stay visible.
95
+ A feature whose code is gone is deleted, or `status: deprecated` with its footer pointing at what replaced it.