@nurix/etna 0.20.46 → 0.20.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/agents/maintenance.md +5 -1
- package/data/harnesses/code/codex/agents/maintenance.toml +5 -1
- package/data/harnesses/code/docs/structure.md +13 -10
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/hooks/compliance.mjs +20 -0
- package/data/harnesses/code/hooks/lib/walls.mjs +2 -1
- package/data/harnesses/code/skills/documentation/SKILL.md +11 -1
- package/data/harnesses/code/skills/documentation/references/style-guide.md +2 -1
- package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +3 -3
- package/data/harnesses/code/skills/documentation/templates/Readme.md +3 -2
- package/data/harnesses/code/skills/documentation/templates/doctrine.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/playbook.md +63 -0
- package/data/harnesses/code/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/ux-behavior.md +1 -1
- package/data/harnesses/delivery/agents/maintenance.md +5 -1
- package/data/harnesses/delivery/codex/agents/maintenance.toml +5 -1
- package/data/harnesses/delivery/harness.json +2 -2
- package/data/harnesses/delivery/skills/documentation/SKILL.md +11 -1
- package/data/harnesses/delivery/skills/documentation/references/style-guide.md +2 -1
- package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +3 -3
- package/data/harnesses/delivery/skills/documentation/templates/Readme.md +3 -2
- package/data/harnesses/delivery/skills/documentation/templates/doctrine.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/playbook.md +63 -0
- package/data/harnesses/delivery/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/ux-behavior.md +1 -1
- package/data/harnesses/etna/agents/maintenance.md +5 -1
- package/data/harnesses/etna/codex/agents/maintenance.toml +5 -1
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/skills/documentation/SKILL.md +11 -1
- package/data/harnesses/etna/skills/documentation/references/style-guide.md +2 -1
- package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +3 -3
- package/data/harnesses/etna/skills/documentation/templates/Readme.md +3 -2
- package/data/harnesses/etna/skills/documentation/templates/doctrine.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/playbook.md +63 -0
- package/data/harnesses/etna/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/ux-behavior.md +1 -1
- package/package.json +2 -2
package/data/harness-index.json
CHANGED
|
@@ -70,7 +70,7 @@ Across the header pointers and the transitional globs:
|
|
|
70
70
|
|
|
71
71
|
- **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`.
|
|
72
72
|
- **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.
|
|
73
|
-
- **Doctrine with no `objects:`.** A `docs/architecture/
|
|
73
|
+
- **Doctrine with no `objects:`.** A `docs/architecture/doctrine/` or `docs/architecture/playbooks/` 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.
|
|
74
74
|
|
|
75
75
|
**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.
|
|
76
76
|
|
|
@@ -137,6 +137,8 @@ docs/
|
|
|
137
137
|
├── structure.md # Harness-owned authority map — the installer writes it on every refresh; you stamp only `harness-version`
|
|
138
138
|
├── architecture/ # Cross-module design + steering layer
|
|
139
139
|
│ │ # domain-model, glossary, non-goals, references, open-questions
|
|
140
|
+
│ ├── doctrine/ # Doctrine — one `{concern}.md` per concern every product handles; owns no code
|
|
141
|
+
│ ├── playbooks/ # Playbooks — one `{subject}.md` per shape every product builds; owns no code
|
|
140
142
|
│ └── decisions/ # Architecture Decisions (ADs — one per subject, updated in place; journey in each AD's Decision Changelog)
|
|
141
143
|
├── features/ # Feature specs — flat, no subfolders; ownership computed from each doc's `objects:` and the source headers
|
|
142
144
|
├── operations/ # Setup, development, deployment, CI/CD
|
|
@@ -180,6 +182,7 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
|
|
|
180
182
|
| no `harness-version` in `structure.md` | stamp it (Step B4) |
|
|
181
183
|
| `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 |
|
|
182
184
|
| 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`) |
|
|
185
|
+
| a `docs/architecture/common/` folder (the doctrine folder's name before `doctrine/`) | **move it to `docs/architecture/doctrine/` with `git mv`, then repoint every reference.** Every markdown link, `objects:`-adjacent path, `Bound by` and Related-documents entry, and every source-file path header that points into `common/` is rewritten to `doctrine/`; frozen records under `docs/history/` keep the old path as provenance. Verify with a search: no reference into `docs/architecture/common/` remains outside `docs/history/` |
|
|
183
186
|
|
|
184
187
|
**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.
|
|
185
188
|
|
|
@@ -231,6 +234,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
|
|
|
231
234
|
## Rules
|
|
232
235
|
|
|
233
236
|
- **Never delete feature docs** — only update or flag for review.
|
|
237
|
+
- **Never create a doctrine or playbook document.** A baseline folds, moves and converges the ones that exist; content that looks like a new standard goes to the backlog as a proposal for the user (the `documentation` skill § Doctrine and playbooks).
|
|
234
238
|
- **Never modify `docs/history/plans/`, `docs/history/changelog/`, or `docs/history/migrations/`** (frozen records — changelog entries are written only at commit time and distilled only by the `retro` agent; a migration runbook freezes at its Outcome stamp).
|
|
235
239
|
- **Preserve existing doc structure and formatting** when updating.
|
|
236
240
|
- **When in doubt, create a backlog item** rather than making a wrong update.
|
|
@@ -68,7 +68,7 @@ Across the header pointers and the transitional globs:
|
|
|
68
68
|
|
|
69
69
|
- **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`.
|
|
70
70
|
- **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.
|
|
71
|
-
- **Doctrine with no `objects:`.** A `docs/architecture/
|
|
71
|
+
- **Doctrine with no `objects:`.** A `docs/architecture/doctrine/` or `docs/architecture/playbooks/` 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.
|
|
72
72
|
|
|
73
73
|
**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.
|
|
74
74
|
|
|
@@ -135,6 +135,8 @@ docs/
|
|
|
135
135
|
├── structure.md # Harness-owned authority map — the installer writes it on every refresh; you stamp only `harness-version`
|
|
136
136
|
├── architecture/ # Cross-module design + steering layer
|
|
137
137
|
│ │ # domain-model, glossary, non-goals, references, open-questions
|
|
138
|
+
│ ├── doctrine/ # Doctrine — one `{concern}.md` per concern every product handles; owns no code
|
|
139
|
+
│ ├── playbooks/ # Playbooks — one `{subject}.md` per shape every product builds; owns no code
|
|
138
140
|
│ └── decisions/ # Architecture Decisions (ADs — one per subject, updated in place; journey in each AD's Decision Changelog)
|
|
139
141
|
├── features/ # Feature specs — flat, no subfolders; ownership computed from each doc's `objects:` and the source headers
|
|
140
142
|
├── operations/ # Setup, development, deployment, CI/CD
|
|
@@ -178,6 +180,7 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
|
|
|
178
180
|
| no `harness-version` in `structure.md` | stamp it (Step B4) |
|
|
179
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 |
|
|
180
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`) |
|
|
183
|
+
| a `docs/architecture/common/` folder (the doctrine folder's name before `doctrine/`) | **move it to `docs/architecture/doctrine/` with `git mv`, then repoint every reference.** Every markdown link, `objects:`-adjacent path, `Bound by` and Related-documents entry, and every source-file path header that points into `common/` is rewritten to `doctrine/`; frozen records under `docs/history/` keep the old path as provenance. Verify with a search: no reference into `docs/architecture/common/` remains outside `docs/history/` |
|
|
181
184
|
|
|
182
185
|
**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.
|
|
183
186
|
|
|
@@ -229,6 +232,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
|
|
|
229
232
|
## Rules
|
|
230
233
|
|
|
231
234
|
- **Never delete feature docs** — only update or flag for review.
|
|
235
|
+
- **Never create a doctrine or playbook document.** A baseline folds, moves and converges the ones that exist; content that looks like a new standard goes to the backlog as a proposal for the user (the `documentation` skill § Doctrine and playbooks).
|
|
232
236
|
- **Never modify `docs/history/plans/`, `docs/history/changelog/`, or `docs/history/migrations/`** (frozen records — changelog entries are written only at commit time and distilled only by the `retro` agent; a migration runbook freezes at its Outcome stamp).
|
|
233
237
|
- **Preserve existing doc structure and formatting** when updating.
|
|
234
238
|
- **When in doubt, create a backlog item** rather than making a wrong update.
|
|
@@ -15,13 +15,15 @@ docs/
|
|
|
15
15
|
├── README.md # Documentation root — the product in a paragraph, what each folder holds, and the repo's
|
|
16
16
|
│ # own notes. Describes directories, never files or a tree — `ls` is the roster
|
|
17
17
|
├── structure.md # This file — harness-owned authority map; `harness-version` stamped after baseline
|
|
18
|
-
├── architecture/ # The design layer, in
|
|
18
|
+
├── architecture/ # The design layer, in five folders; Readme.md is its summary and names folders only
|
|
19
19
|
│ ├── packages/ # One `{package}.md` per project package: what it is, how it behaves. Speaks
|
|
20
20
|
│ │ # RESIDUALLY for `packages/<pkg>/**` — every file no narrower document speaks for
|
|
21
21
|
│ ├── systems/ # One `{machine}.md` per machine spanning packages. Speaks for its subtrees,
|
|
22
22
|
│ │ # more specific than a package's claim
|
|
23
|
-
│ ├──
|
|
24
|
-
│ │ # the same way everywhere. Owns no code
|
|
23
|
+
│ ├── doctrine/ # The enterprise plane — one `{concern}.md` per concern the platform handles
|
|
24
|
+
│ │ # the same way everywhere, in general form. Owns no code
|
|
25
|
+
│ ├── playbooks/ # One `{subject}.md` per interface or workflow shape every product builds (the shell,
|
|
26
|
+
│ │ # a settings area, a record's lifecycle), in general form. Owns no code
|
|
25
27
|
│ └── decisions/ # ADs — `AD-NNN-{slug}.md`, one per subject, updated in place; the journey lives in
|
|
26
28
|
│ # each file's Decision Changelog tail. Each AD's `description:` and `status:` say
|
|
27
29
|
│ # what it decides and whether it stands; Readme.md describes the folder, never a row per AD
|
|
@@ -62,13 +64,13 @@ docs/
|
|
|
62
64
|
|
|
63
65
|
- **Every document has one of three natures.** A **truth** document states what is true now and is rewritten in place, present tense, with no history in its body. A **record** states what happened, is dated, and is frozen at its terminal status. A **queue** holds work not done yet: rows are appended and deleted when resolved, and nothing else is edited — the backlog, the journal, the bookkeeping queue and `memory/`. Which genre has which nature is the genre registry's; the folders below follow it, with `history/` holding records and two queues.
|
|
64
66
|
- **No index keeps a row per file.** A folder's `Readme.md` describes what the folder holds and how its files are named; each file's `description:` is its own account, and listing the folder is the roster. Nothing appends to an index when a sibling file is added — not the changelog guide, not a decisions, research, brainstorms or release-notes folder.
|
|
65
|
-
- **`docs/architecture/`** is the design layer in
|
|
66
|
-
**`
|
|
67
|
-
**What earns which genre.** A project package earns a `packages/` document — one, always. A machine whose parts sit in several packages, which a reader asks about as one thing, earns a `systems/` document. A **feature** document exists for something a person points at — an address, a command family, a named control — and is cut by the objects it designs, never by the code's routers, tables, plans or packages; a machine nobody navigates to is a `systems/` document even when it owns tables, and a fragment whose central object is declared in a sibling merges into it. A concern handled the same way everywhere is `
|
|
67
|
+
- **`docs/architecture/`** is the design layer in five folders: **`packages/`** (one per project package — what it is and how it behaves), **`systems/`** (one per machine spanning packages), **`doctrine/`** (the enterprise plane), **`playbooks/`** (the shapes every product builds) and **`decisions/`** (the ADs, one per subject, updated in place, each journey confined to that file's Decision Changelog tail).
|
|
68
|
+
**`doctrine/`, `playbooks/` and `decisions/` own no code; `packages/` and `systems/` do.** Ownership is computed, not positional and not typed: a document names the objects it designs in `objects:`, each source file's path header names the document that speaks for it, and the object closure links the two; the more specific claim wins — a feature's beats a system's, a system's beats a package's. Until the ownership scan switches to those edges it reads a transitional `owns:` region glob on each of the three owning genres (the `documentation` skill's `references/frontmatter.md`). **A package document is the residual owner of its tree**, so a package's exhaustive behavior spec is that document, not a feature doc.
|
|
69
|
+
**What earns which genre.** A project package earns a `packages/` document — one, always. A machine whose parts sit in several packages, which a reader asks about as one thing, earns a `systems/` document. A **feature** document exists for something a person points at — an address, a command family, a named control — and is cut by the objects it designs, never by the code's routers, tables, plans or packages; a machine nobody navigates to is a `systems/` document even when it owns tables, and a fragment whose central object is declared in a sibling merges into it. A concern handled the same way everywhere is `doctrine/` and owns nothing; a shape every product's interface or workflow repeats is `playbooks/` and owns nothing. Both are written in general form, and a new one is created only with the user's approval (the `documentation` skill § Doctrine and playbooks) — including the governing-decisions register (fixed product decisions `F-###`, baseline defaults `B-###`, external boundaries, out-of-scope), which is authored doctrine the `boundary` skill never persists to.
|
|
68
70
|
- **`history/research/` is not legacy — it is the installed deep-research genre**, one `{topic}/` folder per research pass in the three-file shape. A record is frozen and cited, owns no code, and is never edited to stay current; a new question gets a new topic folder. Entries that predate the three-file convention — a single file or a single-report folder — are kept as authored.
|
|
69
71
|
- **`docs/features/` is flat — every feature authority is a file, and no subfolder exists.** A subject too large for one file is two files, not a folder: `objects:` and the source headers already state authority, and a nested doc is one the ownership scan and the index rows reach last.
|
|
70
72
|
- **`docs/research/`** holds records, not authorities: its standing-research folders and loose notes ground plans and decisions. The `brainstorm` skill finds a session by its folder name.
|
|
71
|
-
- **`docs/tests/`** holds behavioral test checklists — one `{feature}.md` per surface, written as `- [ ] step` → `- [ ] **Assert**` (how to verify a feature's promised behavior). A checklist is written or updated **at the verification gate, before the driving** — it is the script the real surface is driven against and the menu the pinned suite tests are drawn from, so each behavior section closes with a `> Pinned:` line naming the test that guards it, `driven only` with the reason, or `none yet` with the test it owes. **No section carries a date** — the *event* of driving (its date, environment, evidence, a `FAIL` and the backlog item it raised) belongs to the changelog entry of the unit that drove it. A section a drive cannot reach says `not reachable — {what blocks it}` on its trace line until the blocker is gone. **The gate that drives a checklist maintains it** — retiring dead sections and repairing traces is part of the run, because a checklist owns no code and no ownership glob implicates it. The cross-system testing strategy is doctrine in `architecture/
|
|
73
|
+
- **`docs/tests/`** holds behavioral test checklists — one `{feature}.md` per surface, written as `- [ ] step` → `- [ ] **Assert**` (how to verify a feature's promised behavior). A checklist is written or updated **at the verification gate, before the driving** — it is the script the real surface is driven against and the menu the pinned suite tests are drawn from, so each behavior section closes with a `> Pinned:` line naming the test that guards it, `driven only` with the reason, or `none yet` with the test it owes. **No section carries a date** — the *event* of driving (its date, environment, evidence, a `FAIL` and the backlog item it raised) belongs to the changelog entry of the unit that drove it. A section a drive cannot reach says `not reachable — {what blocks it}` on its trace line until the blocker is gone. **The gate that drives a checklist maintains it** — retiring dead sections and repairing traces is part of the run, because a checklist owns no code and no ownership glob implicates it. The cross-system testing strategy is doctrine in `architecture/doctrine/`. A checklist carries `type: checklist` plus `verifies:`, the doc(s) whose promises it proves; a target is legitimately a feature, a package or a system document, and several values are correct where one checklist proves promises two docs decide.
|
|
72
74
|
**`verifies:` is the load-bearing key, and the filename convention is not a substitute for it.** A same-named file is a coincidence no resolver can trust — it breaks the moment a checklist spans two docs or a feature is renamed. The inverse — which checklists cover a feature — is **generated, never listed in the feature doc**.
|
|
73
75
|
- **`docs/operations/bookkeeping.md`** is the shared doc-sync work queue — hooks append rows, the `bookkeeper` agent drains them, and the commit wall blocks while rows remain. It is **author-created, never scaffolded**, and its rows are **live work, never drift**: a drained row is deleted, so a fully drained file holds no rows at all. Read a waiver's referent before deleting it; deleting the record while the condition survives re-appends the finding as an `auto:` row that fails the wall the waiver passed.
|
|
74
76
|
- **`type:` is a required frontmatter key** on every doc that carries frontmatter — the genre stamp, its values the genre registry's `type:` column (`feature`, `architecture-decision`, `project-memory`, `ux`, `acceptance-criteria`, …). It is tooling metadata like `last-reviewed:` and `status:`: external tooling reads it, sessions **preserve it on edits** and never narrate it in the body. The `maintenance` agent stamps it on docs that lack it. The owning genres (`type: feature`, `type: package`, `type: system`) carry the transitional `owns:` region glob the ownership scan reads until it switches to source headers; `type: doctrine` carries `objects:` and owns no code; the bookkeeping queue carries no block. Docs written by a skill that ships a block (`issue-bridge`'s `type: issue` / `type: triage`) carry it.
|
|
@@ -103,11 +105,12 @@ The `type:` column is the value the document carries; its required keys and sect
|
|
|
103
105
|
| You are adding… | It goes in… | `type:` |
|
|
104
106
|
| --- | --- | --- |
|
|
105
107
|
| A feature document — something a person points at (an address, a command family, a named control), cut by the objects it designs | `features/{feature}.md` (flat) | `type: feature` |
|
|
106
|
-
| What a person sees and can do on a surface (UX behavior) | `features/{surface}-ux.md`, named in the headers of the component files no other document speaks for;
|
|
108
|
+
| What a person sees and can do on a surface (UX behavior) | `features/{surface}-ux.md`, named in the headers of the component files no other document speaks for; a shape every product repeats is a playbook | `type: ux` |
|
|
107
109
|
| A package's behavior document | `architecture/packages/{package}.md`, one per package | `type: package` |
|
|
108
110
|
| A system's behavior document | `architecture/systems/{system}.md`, one per machine | `type: system` |
|
|
109
|
-
| Cross-cutting design (doctrine) | `architecture/
|
|
110
|
-
|
|
|
111
|
+
| Cross-cutting design (doctrine) — with the user's approval, after extending an existing one is ruled out | `architecture/doctrine/{concern}.md` | `type: doctrine` |
|
|
112
|
+
| An interface or workflow shape every product builds (playbook) — with the user's approval, after extending an existing one is ruled out | `architecture/playbooks/{subject}.md` | `type: playbook` |
|
|
113
|
+
| The testing strategy | `architecture/doctrine/testing.md` | `type: testing-strategy` |
|
|
111
114
|
| An Architecture Decision (AD) | `architecture/decisions/AD-NNN-{subject}.md` — one file per subject, updated in place; the journey lives in its Decision Changelog | `type: architecture-decision` |
|
|
112
115
|
| The system at a glance | `architecture/system-design.md` | `type: system-overview` |
|
|
113
116
|
| The domain model | `architecture/domain-model.md` | `type: domain-model` |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "code",
|
|
3
3
|
"name": "code",
|
|
4
|
-
"version": "3.20.
|
|
4
|
+
"version": "3.20.17",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"origin": "https://cdn.nustack.io",
|
|
30
30
|
"track": "latest"
|
|
31
31
|
},
|
|
32
|
-
"digest": "
|
|
32
|
+
"digest": "273216478e99"
|
|
33
33
|
}
|
|
@@ -96,6 +96,13 @@ function protectedPaths(projectDir) {
|
|
|
96
96
|
return protectedCache;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
// Names the genre of a file the write would create in a standards folder, or null.
|
|
100
|
+
function newStandardDoc(projectDir, rel) {
|
|
101
|
+
const match = new RegExp(`^${docsRoot(projectDir)}/architecture/(doctrine|playbooks)/[^/]+\\.md$`).exec(rel);
|
|
102
|
+
if (!match || /\/Readme\.md$/i.test(rel) || fs.existsSync(path.join(projectDir, rel))) return null;
|
|
103
|
+
return match[1] === "doctrine" ? "doctrine" : "playbook";
|
|
104
|
+
}
|
|
105
|
+
|
|
99
106
|
function fileGate(projectDir, rel, sessionId) {
|
|
100
107
|
const frozen = wallOn(projectDir, "frozen-record") ? frozenStatusAtHead(projectDir, rel) : { frozen: false };
|
|
101
108
|
if (frozen.frozen) {
|
|
@@ -112,6 +119,19 @@ function fileGate(projectDir, rel, sessionId) {
|
|
|
112
119
|
return deny(projectDir, sessionId, "protected-file", rel, `BLOCKED — protected file. \`${rel}\` is on the protected list.`);
|
|
113
120
|
}
|
|
114
121
|
|
|
122
|
+
// A doctrine or playbook document is a standard every product reads, so a new one waits for the
|
|
123
|
+
// user (the documentation skill § Doctrine and playbooks); an edit to an existing one passes.
|
|
124
|
+
const standard = newStandardDoc(projectDir, rel);
|
|
125
|
+
if (standard && wallOn(projectDir, "new-standard-doc")) {
|
|
126
|
+
const asked = askOnce(
|
|
127
|
+
projectDir,
|
|
128
|
+
sessionId,
|
|
129
|
+
"new-standard-doc:" + rel,
|
|
130
|
+
`\`${rel}\` would be a new ${standard} document. A ${standard} is created only with the user's approval, after extending an existing one is ruled out: name the existing documents it was checked against and the distinction (the documentation skill § Doctrine and playbooks). Create it?`
|
|
131
|
+
);
|
|
132
|
+
if (asked) return asked;
|
|
133
|
+
}
|
|
134
|
+
|
|
115
135
|
// Before the claim ask and outside the plan gate's `inDocs` skip — the graph's own files live
|
|
116
136
|
// under .claude/, which that branch deliberately excludes.
|
|
117
137
|
const graphWall = graphGate(projectDir, rel);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// the two sets agree, so a rename fails there rather than in a generation box at 3am.
|
|
7
7
|
//
|
|
8
8
|
// The first ten are PreToolUse gate ids as recorded in the session log; the last four name Stop
|
|
9
|
-
// sub-walls that had no id at all until `mode.walls` needed to talk about them.
|
|
9
|
+
// sub-walls that had no id at all until `mode.walls` needed to talk about them; the last is an ask.
|
|
10
10
|
export const WALLS = [
|
|
11
11
|
"frozen-record",
|
|
12
12
|
"protected-file",
|
|
@@ -22,6 +22,7 @@ export const WALLS = [
|
|
|
22
22
|
"open-denials",
|
|
23
23
|
"ledger",
|
|
24
24
|
"changelog-entry",
|
|
25
|
+
"new-standard-doc",
|
|
25
26
|
];
|
|
26
27
|
|
|
27
28
|
// The build graph's own denials are deliberately absent: they are not skippable, because they
|
|
@@ -20,7 +20,7 @@ This skill writes one document at a time: the genre registry says what kind of d
|
|
|
20
20
|
## Writing one document
|
|
21
21
|
|
|
22
22
|
1. **Find the genre.** Match the request against § Routing, then read that genre's registry row: nature, `type:`, location, required keys, required sections. No row fits ⇒ `references/untemplated.md`.
|
|
23
|
-
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
23
|
+
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. A new doctrine or playbook document also waits for the user's approval (§ Doctrine and playbooks). If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
24
24
|
3. **Earn it, and route a set.** A new truth document that states rules (doctrine, a system, an architecture decision) first lists the decisions only it would own, each checked against the documents that exist; under about six, its rules go to their homes and the document is not written. More than one sibling document, or any doctrine document, follows `references/document-sets.md` from here: a term table and a rule ledger before prose, rendered from the ledger, one cold read.
|
|
25
25
|
4. **Place and name it** at the registry's location. `docs/structure.md` is the placement authority for anything the registry leaves open.
|
|
26
26
|
5. **Write it from the template, then run its genre procedure.** A template's procedure section binds that genre only: doctrine's is the reason check (`templates/doctrine.md` § Reason check). Keep the template's section names exactly; omit optional sections with nothing real to say. Write the body from the outside in (style guide § Shape), every heading and bold label a noun phrase, every sentence after a label third person, one rule per sentence in the term engineers use (style guide § Sentences, § Vocabulary). In a truth document, every sentence is present tense (§ The three natures). Every line owns a decision: before stating a rule, search the neighbours for it, and cite the one found rather than restating it (style guide § Net-net).
|
|
@@ -41,6 +41,15 @@ Some genres carry steps beyond writing from the template. Each procedure lives i
|
|
|
41
41
|
|
|
42
42
|
A genre with no row here has no procedure beyond its template.
|
|
43
43
|
|
|
44
|
+
## Doctrine and playbooks
|
|
45
|
+
|
|
46
|
+
Two genres are standards rather than accounts of one repository: a doctrine document settles one concern every product handles, and a playbook settles one interface or workflow shape every product builds. Both are read by the next product as much as by this one, so four rules bind them beyond their templates:
|
|
47
|
+
|
|
48
|
+
- **General form:** each reads correctly copied verbatim into another product's repository. It names nothing only its own repository has: the product, its domain entities, its vendors and stack, its people, its own documents by path. An example uses an object every product has, and the repository's own values stay in its feature documents, which name the standard in their Scope.
|
|
49
|
+
- **Extend first:** before a new document is proposed, the existing set is read (`grep -m1 '^description:' docs/architecture/{doctrine,playbooks}/*.md`), and content whose subject one of them already covers lands there as a section, a principle or a row.
|
|
50
|
+
- **New only on a clear distinction:** a new document is written only for a concern or a shape none of the existing ones owns, with decisions of its own (the template's earn-it test).
|
|
51
|
+
- **Never without approval:** an agent never creates a doctrine or playbook document on its own initiative, a baseline and a harvest included. It names the proposed document, the existing documents it was checked against and the distinction between them, then waits for the user's yes in chat. An unattended session files the request as a stop. An edit to an existing document follows the normal loop.
|
|
52
|
+
|
|
44
53
|
## The three natures
|
|
45
54
|
|
|
46
55
|
Every document is exactly one of three things, and the registry names which for every genre. Decide before writing a word: every other rule follows from the split.
|
|
@@ -105,6 +114,7 @@ A document names the objects it designs in `objects:`, and a source file names t
|
|
|
105
114
|
| Document a feature's data flow or state | The feature doc's Flows (behaviour) and Promises (guarantees); a persisted shape belongs in the API reference (style guide § State at persistence points); component and transient state are not documented |
|
|
106
115
|
| Document a workspace package | [`templates/package.md`](templates/package.md), the residual owner of `packages/{name}/**` |
|
|
107
116
|
| Document one concern the platform handles everywhere (authentication, storage, the API protocol) | [`templates/doctrine.md`](templates/doctrine.md): design and principles in general form, no mechanism, no project link, owns no code; written through [`references/document-sets.md`](references/document-sets.md) |
|
|
117
|
+
| Document one interface or workflow shape every product builds (the shell, a settings area, analytics pages, a record's lifecycle) | [`templates/playbook.md`](templates/playbook.md): the shape and its rules in general form, owns no code; created only with the user's approval (§ Doctrine and playbooks) |
|
|
108
118
|
| Write or rework several sibling documents at once (a doctrine folder, a family of features) | [`references/document-sets.md`](references/document-sets.md): usefulness test, term table, rule ledger, render, one cold read |
|
|
109
119
|
| Document one system spanning packages | [`templates/system.md`](templates/system.md): one machine end to end, never a giant feature doc |
|
|
110
120
|
| Record or revise an architecture decision | [`templates/architecture-decision.md`](templates/architecture-decision.md): one AD per subject, revised in place |
|
|
@@ -28,7 +28,8 @@ Every genre takes the same shape (principle 2) and the same labels (principle 5)
|
|
|
28
28
|
|
|
29
29
|
| Genre | Nature | Deepest layer | Excluded |
|
|
30
30
|
| --- | --- | --- | --- |
|
|
31
|
-
| Doctrine (`docs/architecture/
|
|
31
|
+
| Doctrine (`docs/architecture/doctrine/`) | Truth | The decisions behind each part and the reasons for them | Any mechanism. A doctrine document describes no code |
|
|
32
|
+
| Playbook (`docs/architecture/playbooks/`) | Truth | What a person sees and does in the shape, part by part: its arrangement, each part's behaviour, its states and promises | Any one product's values: its labels, routes, limits and timings, which are its feature documents'. Any mechanism |
|
|
32
33
|
| System (`docs/architecture/systems/`) | Truth | The machine's behaviour across packages: the order it runs in, sequence and state diagrams at role level, the invariant at each place where parts meet, what re-drives a stall. No single file shows a cross-package flow, so this layer earns its place | Per-function steps, tables and columns, routes |
|
|
33
34
|
| Package (`docs/architecture/packages/`) | Truth | The public contract a consumer touches: commands and their flags, exports, what is installed and where. For a CLI the flags *are* the promise | Internal layout, the build's steps, which file holds what |
|
|
34
35
|
| Feature (`docs/features/`) | Truth | The behaviour a person meets, in full: each state the surface can be in, each edge case, what a rejection tells them, the wording where the wording is promised. This genre runs deepest toward its end, in Promises, because a promise is checked by a person or a test and the code cannot state it as intent | Columns, routes, guards, functions, files |
|
|
@@ -255,7 +255,7 @@ function lintFile(path, findings) {
|
|
|
255
255
|
if (/^[a-z]+:\/\//.test(target) || SCAFFOLD.test(target)) continue;
|
|
256
256
|
// The repo-root README routes by intent (templates/root-readme.md): its Map names owning package and system
|
|
257
257
|
// docs, its Destinations rows name procedures and rules. Individual feature docs stay off it — the ownership index finds them.
|
|
258
|
-
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|
|
|
258
|
+
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|doctrine|playbooks)\/|operations\/)[^/]+\.md$|(^|\/)\.claude\/rules\/[^/]+\.md$/.test(target)) continue;
|
|
259
259
|
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; }
|
|
260
260
|
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) });
|
|
261
261
|
}
|
|
@@ -382,7 +382,7 @@ function lintFile(path, findings) {
|
|
|
382
382
|
// A heading or a bold bullet label names; it never says. A label with a finite verb drags in a tense,
|
|
383
383
|
// a person and a voice ("What was decided", "You already belong to…"), so a label opening with a
|
|
384
384
|
// wh-word or a personal pronoun, or ending in a full stop, is a clause (style-guide § Labels).
|
|
385
|
-
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/
|
|
385
|
+
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/(doctrine|playbooks)\/[^/]+\.md$/.test(path));
|
|
386
386
|
if (isLabelledDoc && !disabled.has('clause-label')) {
|
|
387
387
|
const CLAUSE = /^(how|what|why|where|when|who|which|whose|you|your|we|our|i|it)\b|\b(you|your)\b|\.$/i;
|
|
388
388
|
for (const { n, line } of lines) {
|
|
@@ -413,7 +413,7 @@ function lintFile(path, findings) {
|
|
|
413
413
|
|
|
414
414
|
// A doctrine folder is read as a set: its documents cite each other's principles by number and must not restate each
|
|
415
415
|
// other (templates/doctrine.md, references/document-sets.md), so these checks read the siblings beside the linted file.
|
|
416
|
-
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/
|
|
416
|
+
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/doctrine\/[^/]+\.md$/;
|
|
417
417
|
// A doctrine document closes on Terms and Related documents; an older one on "For the agent". The body ends at the first.
|
|
418
418
|
const DOCTRINE_CLOSING = /^##\s+(For the agent|Terms|Related documents)\s*$/;
|
|
419
419
|
// The words a doctrine document replaces with the term engineers use (references/style-guide.md § Vocabulary).
|
|
@@ -17,7 +17,8 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
17
17
|
| Feature | [feature.md](feature.md) | truth | `feature` | `docs/features/{feature}.md` | `type` `description` `status` `objects` | Scope · Promises · For the agent |
|
|
18
18
|
| Package | [package.md](package.md) | truth | `package` | `docs/architecture/packages/{package}.md` | `type` `description` `status` `objects` | Scope · Ownership · Usage · Promises · For the agent |
|
|
19
19
|
| System | [system.md](system.md) | truth | `system` | `docs/architecture/systems/{system}.md` | `type` `description` `status` `objects` | Scope · Flow · Promises · For the agent |
|
|
20
|
-
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/
|
|
20
|
+
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/doctrine/{concern}.md` | `type` `description` `status` `objects` | Principles · Terms · Related documents |
|
|
21
|
+
| Playbook | [playbook.md](playbook.md) | truth | `playbook` | `docs/architecture/playbooks/{subject}.md` | `type` `description` `status` `objects` | Purpose · Related documents |
|
|
21
22
|
| Architecture decision | [architecture-decision.md](architecture-decision.md) | truth | `architecture-decision` | `docs/architecture/decisions/AD-NNN-{subject}.md` | `type` `title` `description` `status` `last-updated` | Decision · Context · Consequences |
|
|
22
23
|
| UX behavior | [ux-behavior.md](ux-behavior.md) | truth | `ux` | `docs/features/{surface}-ux.md` | `type` `description` `status` `objects` | Purpose · Anatomy · States · Affordances · Promises · Transitions |
|
|
23
24
|
| System overview | [system-overview.md](system-overview.md) | truth | `system-overview` | `docs/architecture/system-design.md` | `type` `description` | — |
|
|
@@ -25,7 +26,7 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
25
26
|
| Glossary | [glossary.md](glossary.md) | truth | `glossary` | `docs/architecture/glossary.md` | `type` `description` | — |
|
|
26
27
|
| Non-goals | [non-goals.md](non-goals.md) | truth | `non-goals` | `docs/architecture/non-goals.md` | `type` `description` | — |
|
|
27
28
|
| Security overview | [security-overview.md](security-overview.md) | truth | `security-overview` | `docs/architecture/security.md` | `type` `description` | — |
|
|
28
|
-
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/
|
|
29
|
+
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/doctrine/testing.md` | `type` `description` | — |
|
|
29
30
|
| API reference | [api-reference.md](api-reference.md) | truth | `api-reference` | `docs/architecture/api.md` | `type` `description` | — |
|
|
30
31
|
| Environment registry | [environment-registry.md](environment-registry.md) | truth | `environment-registry` | `docs/operations/environment.md` | `type` `description` | — |
|
|
31
32
|
| Behavioral checklist | [behavioral-checklist.md](behavioral-checklist.md) | truth | `checklist` | `docs/tests/{feature}.md` | `type` `description` `verifies` | — |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Doctrine documentation (docs/architecture/
|
|
1
|
+
# Doctrine documentation (docs/architecture/doctrine/{concern}.md)
|
|
2
2
|
|
|
3
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
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Playbook documentation (docs/architecture/playbooks/{subject}.md)
|
|
2
|
+
|
|
3
|
+
Truth document. Use for **one interface or workflow shape every product builds**: the shell, a settings area, analytics pages, a record's lifecycle, the interaction patterns, automated agents. One file per shape, named for it. A playbook speaks for no code: no source header names it, and it carries no `owns:`. The product's own feature documents hold its instance of the shape, and each names the playbook in its Scope.
|
|
4
|
+
|
|
5
|
+
**A playbook states the shape, never one product's instance of it.** It says what the parts are, how they are arranged, how each behaves and what the shape promises, so a new instance is new content poured into a known shape rather than a new layout. A product's own values (its labels, routes, limits, timings, the wording of its copy) belong to its feature document. A playbook explains no mechanism, for the reasons a doctrine document explains none (`templates/doctrine.md`).
|
|
6
|
+
|
|
7
|
+
**Doctrine or playbook.** A doctrine document settles the decisions of one concern every product handles, such as how authorization works. A playbook settles what a person sees and does in one recurring shape, such as how a settings page is laid out. A playbook cites the doctrine its shape rests on, and never restates it.
|
|
8
|
+
|
|
9
|
+
**A playbook is written in general form, never linked to the project that holds it.** It reads correctly when copied verbatim into any other product's repository, under the same rule a doctrine document keeps (`templates/doctrine.md`). Where the shape needs a word for the product's own record, the Purpose introduces a generic one once ("the subject: the record a report is read per, such as a job in a hiring product") and the body uses it throughout. Its links reach only sibling playbooks, doctrine documents and published standards.
|
|
10
|
+
|
|
11
|
+
**Earn the document, and ask before writing it.** List the rules only this playbook would own, each checked against the playbooks and doctrine that exist. Fewer than about six owned rules means the shape is a section of a neighbour, not a document. A new playbook is created only with the user's approval (SKILL.md § Doctrine and playbooks).
|
|
12
|
+
|
|
13
|
+
**Required:**
|
|
14
|
+
|
|
15
|
+
- the frontmatter
|
|
16
|
+
- **Purpose:** what the shape is for, what it is not for, and the generic words it introduces
|
|
17
|
+
- **Related documents**
|
|
18
|
+
|
|
19
|
+
**Optional, each only with something real to say:**
|
|
20
|
+
|
|
21
|
+
- **Anatomy:** an ASCII diagram of the arrangement with each region labelled, then one bullet per region
|
|
22
|
+
- **One section per part** that carries rules of its own, named for the part ("Roster", "Readiness", "Side peek")
|
|
23
|
+
- **States:** one table — State · Entered when · The person sees · Can do
|
|
24
|
+
- **Affordances:** what a person can do, one bullet per verb
|
|
25
|
+
- **Principles:** numbered, each a noun-phrase label and its rule
|
|
26
|
+
- **Promises:** what the shape guarantees, grouped under `###` headings where there are several kinds
|
|
27
|
+
- **Transitions:** what moves the shape between its states, and who moves it
|
|
28
|
+
|
|
29
|
+
Sections run Purpose first and Related documents last; the rest follow the order above. Each is named for its concept, never for its layer. Expect one hundred to two hundred lines.
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
---
|
|
33
|
+
type: playbook # required tooling key
|
|
34
|
+
description: "{at most 300 characters: the shape this playbook settles, and that a product's own instance is its feature doc}" # required
|
|
35
|
+
status: active # required — active | draft
|
|
36
|
+
objects: [rail, command palette, account menu] # required — the parts this playbook shapes, by their graph names
|
|
37
|
+
---
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
# {Shape} playbook
|
|
42
|
+
|
|
43
|
+
## Purpose
|
|
44
|
+
|
|
45
|
+
{What the shape is for and what it is not for, in two or three sentences. The generic words it introduces, each in one bullet.}
|
|
46
|
+
|
|
47
|
+
## Anatomy
|
|
48
|
+
|
|
49
|
+
{An ASCII diagram, then one bullet per labelled region.}
|
|
50
|
+
|
|
51
|
+
## {Part}
|
|
52
|
+
|
|
53
|
+
- **{Rule label}:** {the rule, in general form}
|
|
54
|
+
|
|
55
|
+
## Principles
|
|
56
|
+
|
|
57
|
+
1. **{Label}:** {the rule}. {Its reason, as a separate sentence.}
|
|
58
|
+
|
|
59
|
+
## Related documents
|
|
60
|
+
|
|
61
|
+
- [{Sibling playbook}]({file}.md): {what it settles beside this one}
|
|
62
|
+
- [{Doctrine}](../doctrine/{concern}.md): {the decision this shape rests on}
|
|
63
|
+
```
|
|
@@ -130,7 +130,7 @@ The rest of this page is for the team and for coding agents working in this repo
|
|
|
130
130
|
|
|
131
131
|
The baseline **does** scaffold `/README.md` when it is missing, and it fills each section from the repo:
|
|
132
132
|
|
|
133
|
-
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/
|
|
133
|
+
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/doctrine/` (what the product is, the core loop, who it is for), the package manifests (what publishes where), and the deploy config (addresses). Written in a user's terms; where the charter is missing, the baseline writes Identity only and raises a `note:` row for the rest.
|
|
134
134
|
- **The main path:** from the system overview.
|
|
135
135
|
- **Map:** from the workspace globs and the ownership index.
|
|
136
136
|
- **Rules:** from `docs/operations/protected-files.md` and the architecture doctrine.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Testing strategy (docs/architecture/
|
|
1
|
+
# Testing strategy (docs/architecture/doctrine/testing.md)
|
|
2
2
|
|
|
3
3
|
Truth document. Use for: the repo's test posture — what is tested at which level, how to run it, and what is deliberately untested. The framework choices come from the harness testing rule; this doc records how *this repo* applies them.
|
|
4
4
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UX behavior (docs/features/{surface}-ux.md)
|
|
2
2
|
|
|
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.
|
|
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. It is one product's surface; a shape every product's surfaces share, such as a settings area or an analytics page, is a playbook (`templates/playbook.md`), which the UX document names in its Purpose and fills with its own values.
|
|
4
4
|
|
|
5
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
|
|
|
@@ -70,7 +70,7 @@ Across the header pointers and the transitional globs:
|
|
|
70
70
|
|
|
71
71
|
- **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`.
|
|
72
72
|
- **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.
|
|
73
|
-
- **Doctrine with no `objects:`.** A `docs/architecture/
|
|
73
|
+
- **Doctrine with no `objects:`.** A `docs/architecture/doctrine/` or `docs/architecture/playbooks/` 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.
|
|
74
74
|
|
|
75
75
|
**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.
|
|
76
76
|
|
|
@@ -137,6 +137,8 @@ docs/
|
|
|
137
137
|
├── structure.md # Harness-owned authority map — the installer writes it on every refresh; you stamp only `harness-version`
|
|
138
138
|
├── architecture/ # Cross-module design + steering layer
|
|
139
139
|
│ │ # domain-model, glossary, non-goals, references, open-questions
|
|
140
|
+
│ ├── doctrine/ # Doctrine — one `{concern}.md` per concern every product handles; owns no code
|
|
141
|
+
│ ├── playbooks/ # Playbooks — one `{subject}.md` per shape every product builds; owns no code
|
|
140
142
|
│ └── decisions/ # Architecture Decisions (ADs — one per subject, updated in place; journey in each AD's Decision Changelog)
|
|
141
143
|
├── features/ # Feature specs — flat, no subfolders; ownership computed from each doc's `objects:` and the source headers
|
|
142
144
|
├── operations/ # Setup, development, deployment, CI/CD
|
|
@@ -180,6 +182,7 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
|
|
|
180
182
|
| no `harness-version` in `structure.md` | stamp it (Step B4) |
|
|
181
183
|
| `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 |
|
|
182
184
|
| 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`) |
|
|
185
|
+
| a `docs/architecture/common/` folder (the doctrine folder's name before `doctrine/`) | **move it to `docs/architecture/doctrine/` with `git mv`, then repoint every reference.** Every markdown link, `objects:`-adjacent path, `Bound by` and Related-documents entry, and every source-file path header that points into `common/` is rewritten to `doctrine/`; frozen records under `docs/history/` keep the old path as provenance. Verify with a search: no reference into `docs/architecture/common/` remains outside `docs/history/` |
|
|
183
186
|
|
|
184
187
|
**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.
|
|
185
188
|
|
|
@@ -231,6 +234,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
|
|
|
231
234
|
## Rules
|
|
232
235
|
|
|
233
236
|
- **Never delete feature docs** — only update or flag for review.
|
|
237
|
+
- **Never create a doctrine or playbook document.** A baseline folds, moves and converges the ones that exist; content that looks like a new standard goes to the backlog as a proposal for the user (the `documentation` skill § Doctrine and playbooks).
|
|
234
238
|
- **Never modify `docs/history/plans/`, `docs/history/changelog/`, or `docs/history/migrations/`** (frozen records — changelog entries are written only at commit time and distilled only by the `retro` agent; a migration runbook freezes at its Outcome stamp).
|
|
235
239
|
- **Preserve existing doc structure and formatting** when updating.
|
|
236
240
|
- **When in doubt, create a backlog item** rather than making a wrong update.
|
|
@@ -68,7 +68,7 @@ Across the header pointers and the transitional globs:
|
|
|
68
68
|
|
|
69
69
|
- **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`.
|
|
70
70
|
- **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.
|
|
71
|
-
- **Doctrine with no `objects:`.** A `docs/architecture/
|
|
71
|
+
- **Doctrine with no `objects:`.** A `docs/architecture/doctrine/` or `docs/architecture/playbooks/` 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.
|
|
72
72
|
|
|
73
73
|
**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.
|
|
74
74
|
|
|
@@ -135,6 +135,8 @@ docs/
|
|
|
135
135
|
├── structure.md # Harness-owned authority map — the installer writes it on every refresh; you stamp only `harness-version`
|
|
136
136
|
├── architecture/ # Cross-module design + steering layer
|
|
137
137
|
│ │ # domain-model, glossary, non-goals, references, open-questions
|
|
138
|
+
│ ├── doctrine/ # Doctrine — one `{concern}.md` per concern every product handles; owns no code
|
|
139
|
+
│ ├── playbooks/ # Playbooks — one `{subject}.md` per shape every product builds; owns no code
|
|
138
140
|
│ └── decisions/ # Architecture Decisions (ADs — one per subject, updated in place; journey in each AD's Decision Changelog)
|
|
139
141
|
├── features/ # Feature specs — flat, no subfolders; ownership computed from each doc's `objects:` and the source headers
|
|
140
142
|
├── operations/ # Setup, development, deployment, CI/CD
|
|
@@ -178,6 +180,7 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
|
|
|
178
180
|
| no `harness-version` in `structure.md` | stamp it (Step B4) |
|
|
179
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 |
|
|
180
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`) |
|
|
183
|
+
| a `docs/architecture/common/` folder (the doctrine folder's name before `doctrine/`) | **move it to `docs/architecture/doctrine/` with `git mv`, then repoint every reference.** Every markdown link, `objects:`-adjacent path, `Bound by` and Related-documents entry, and every source-file path header that points into `common/` is rewritten to `doctrine/`; frozen records under `docs/history/` keep the old path as provenance. Verify with a search: no reference into `docs/architecture/common/` remains outside `docs/history/` |
|
|
181
184
|
|
|
182
185
|
**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.
|
|
183
186
|
|
|
@@ -229,6 +232,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
|
|
|
229
232
|
## Rules
|
|
230
233
|
|
|
231
234
|
- **Never delete feature docs** — only update or flag for review.
|
|
235
|
+
- **Never create a doctrine or playbook document.** A baseline folds, moves and converges the ones that exist; content that looks like a new standard goes to the backlog as a proposal for the user (the `documentation` skill § Doctrine and playbooks).
|
|
232
236
|
- **Never modify `docs/history/plans/`, `docs/history/changelog/`, or `docs/history/migrations/`** (frozen records — changelog entries are written only at commit time and distilled only by the `retro` agent; a migration runbook freezes at its Outcome stamp).
|
|
233
237
|
- **Preserve existing doc structure and formatting** when updating.
|
|
234
238
|
- **When in doubt, create a backlog item** rather than making a wrong update.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "delivery",
|
|
3
3
|
"name": "delivery",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.52",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
"origin": "https://cdn.nustack.io",
|
|
15
15
|
"track": "latest"
|
|
16
16
|
},
|
|
17
|
-
"digest": "
|
|
17
|
+
"digest": "7a383f0f8900"
|
|
18
18
|
}
|
|
@@ -20,7 +20,7 @@ This skill writes one document at a time: the genre registry says what kind of d
|
|
|
20
20
|
## Writing one document
|
|
21
21
|
|
|
22
22
|
1. **Find the genre.** Match the request against § Routing, then read that genre's registry row: nature, `type:`, location, required keys, required sections. No row fits ⇒ `references/untemplated.md`.
|
|
23
|
-
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
23
|
+
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. A new doctrine or playbook document also waits for the user's approval (§ Doctrine and playbooks). If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
24
24
|
3. **Earn it, and route a set.** A new truth document that states rules (doctrine, a system, an architecture decision) first lists the decisions only it would own, each checked against the documents that exist; under about six, its rules go to their homes and the document is not written. More than one sibling document, or any doctrine document, follows `references/document-sets.md` from here: a term table and a rule ledger before prose, rendered from the ledger, one cold read.
|
|
25
25
|
4. **Place and name it** at the registry's location. `docs/structure.md` is the placement authority for anything the registry leaves open.
|
|
26
26
|
5. **Write it from the template, then run its genre procedure.** A template's procedure section binds that genre only: doctrine's is the reason check (`templates/doctrine.md` § Reason check). Keep the template's section names exactly; omit optional sections with nothing real to say. Write the body from the outside in (style guide § Shape), every heading and bold label a noun phrase, every sentence after a label third person, one rule per sentence in the term engineers use (style guide § Sentences, § Vocabulary). In a truth document, every sentence is present tense (§ The three natures). Every line owns a decision: before stating a rule, search the neighbours for it, and cite the one found rather than restating it (style guide § Net-net).
|
|
@@ -41,6 +41,15 @@ Some genres carry steps beyond writing from the template. Each procedure lives i
|
|
|
41
41
|
|
|
42
42
|
A genre with no row here has no procedure beyond its template.
|
|
43
43
|
|
|
44
|
+
## Doctrine and playbooks
|
|
45
|
+
|
|
46
|
+
Two genres are standards rather than accounts of one repository: a doctrine document settles one concern every product handles, and a playbook settles one interface or workflow shape every product builds. Both are read by the next product as much as by this one, so four rules bind them beyond their templates:
|
|
47
|
+
|
|
48
|
+
- **General form:** each reads correctly copied verbatim into another product's repository. It names nothing only its own repository has: the product, its domain entities, its vendors and stack, its people, its own documents by path. An example uses an object every product has, and the repository's own values stay in its feature documents, which name the standard in their Scope.
|
|
49
|
+
- **Extend first:** before a new document is proposed, the existing set is read (`grep -m1 '^description:' docs/architecture/{doctrine,playbooks}/*.md`), and content whose subject one of them already covers lands there as a section, a principle or a row.
|
|
50
|
+
- **New only on a clear distinction:** a new document is written only for a concern or a shape none of the existing ones owns, with decisions of its own (the template's earn-it test).
|
|
51
|
+
- **Never without approval:** an agent never creates a doctrine or playbook document on its own initiative, a baseline and a harvest included. It names the proposed document, the existing documents it was checked against and the distinction between them, then waits for the user's yes in chat. An unattended session files the request as a stop. An edit to an existing document follows the normal loop.
|
|
52
|
+
|
|
44
53
|
## The three natures
|
|
45
54
|
|
|
46
55
|
Every document is exactly one of three things, and the registry names which for every genre. Decide before writing a word: every other rule follows from the split.
|
|
@@ -105,6 +114,7 @@ A document names the objects it designs in `objects:`, and a source file names t
|
|
|
105
114
|
| Document a feature's data flow or state | The feature doc's Flows (behaviour) and Promises (guarantees); a persisted shape belongs in the API reference (style guide § State at persistence points); component and transient state are not documented |
|
|
106
115
|
| Document a workspace package | [`templates/package.md`](templates/package.md), the residual owner of `packages/{name}/**` |
|
|
107
116
|
| Document one concern the platform handles everywhere (authentication, storage, the API protocol) | [`templates/doctrine.md`](templates/doctrine.md): design and principles in general form, no mechanism, no project link, owns no code; written through [`references/document-sets.md`](references/document-sets.md) |
|
|
117
|
+
| Document one interface or workflow shape every product builds (the shell, a settings area, analytics pages, a record's lifecycle) | [`templates/playbook.md`](templates/playbook.md): the shape and its rules in general form, owns no code; created only with the user's approval (§ Doctrine and playbooks) |
|
|
108
118
|
| Write or rework several sibling documents at once (a doctrine folder, a family of features) | [`references/document-sets.md`](references/document-sets.md): usefulness test, term table, rule ledger, render, one cold read |
|
|
109
119
|
| Document one system spanning packages | [`templates/system.md`](templates/system.md): one machine end to end, never a giant feature doc |
|
|
110
120
|
| Record or revise an architecture decision | [`templates/architecture-decision.md`](templates/architecture-decision.md): one AD per subject, revised in place |
|
|
@@ -28,7 +28,8 @@ Every genre takes the same shape (principle 2) and the same labels (principle 5)
|
|
|
28
28
|
|
|
29
29
|
| Genre | Nature | Deepest layer | Excluded |
|
|
30
30
|
| --- | --- | --- | --- |
|
|
31
|
-
| Doctrine (`docs/architecture/
|
|
31
|
+
| Doctrine (`docs/architecture/doctrine/`) | Truth | The decisions behind each part and the reasons for them | Any mechanism. A doctrine document describes no code |
|
|
32
|
+
| Playbook (`docs/architecture/playbooks/`) | Truth | What a person sees and does in the shape, part by part: its arrangement, each part's behaviour, its states and promises | Any one product's values: its labels, routes, limits and timings, which are its feature documents'. Any mechanism |
|
|
32
33
|
| System (`docs/architecture/systems/`) | Truth | The machine's behaviour across packages: the order it runs in, sequence and state diagrams at role level, the invariant at each place where parts meet, what re-drives a stall. No single file shows a cross-package flow, so this layer earns its place | Per-function steps, tables and columns, routes |
|
|
33
34
|
| Package (`docs/architecture/packages/`) | Truth | The public contract a consumer touches: commands and their flags, exports, what is installed and where. For a CLI the flags *are* the promise | Internal layout, the build's steps, which file holds what |
|
|
34
35
|
| Feature (`docs/features/`) | Truth | The behaviour a person meets, in full: each state the surface can be in, each edge case, what a rejection tells them, the wording where the wording is promised. This genre runs deepest toward its end, in Promises, because a promise is checked by a person or a test and the code cannot state it as intent | Columns, routes, guards, functions, files |
|
|
@@ -255,7 +255,7 @@ function lintFile(path, findings) {
|
|
|
255
255
|
if (/^[a-z]+:\/\//.test(target) || SCAFFOLD.test(target)) continue;
|
|
256
256
|
// The repo-root README routes by intent (templates/root-readme.md): its Map names owning package and system
|
|
257
257
|
// docs, its Destinations rows name procedures and rules. Individual feature docs stay off it — the ownership index finds them.
|
|
258
|
-
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|
|
|
258
|
+
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|doctrine|playbooks)\/|operations\/)[^/]+\.md$|(^|\/)\.claude\/rules\/[^/]+\.md$/.test(target)) continue;
|
|
259
259
|
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; }
|
|
260
260
|
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) });
|
|
261
261
|
}
|
|
@@ -382,7 +382,7 @@ function lintFile(path, findings) {
|
|
|
382
382
|
// A heading or a bold bullet label names; it never says. A label with a finite verb drags in a tense,
|
|
383
383
|
// a person and a voice ("What was decided", "You already belong to…"), so a label opening with a
|
|
384
384
|
// wh-word or a personal pronoun, or ending in a full stop, is a clause (style-guide § Labels).
|
|
385
|
-
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/
|
|
385
|
+
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/(doctrine|playbooks)\/[^/]+\.md$/.test(path));
|
|
386
386
|
if (isLabelledDoc && !disabled.has('clause-label')) {
|
|
387
387
|
const CLAUSE = /^(how|what|why|where|when|who|which|whose|you|your|we|our|i|it)\b|\b(you|your)\b|\.$/i;
|
|
388
388
|
for (const { n, line } of lines) {
|
|
@@ -413,7 +413,7 @@ function lintFile(path, findings) {
|
|
|
413
413
|
|
|
414
414
|
// A doctrine folder is read as a set: its documents cite each other's principles by number and must not restate each
|
|
415
415
|
// other (templates/doctrine.md, references/document-sets.md), so these checks read the siblings beside the linted file.
|
|
416
|
-
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/
|
|
416
|
+
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/doctrine\/[^/]+\.md$/;
|
|
417
417
|
// A doctrine document closes on Terms and Related documents; an older one on "For the agent". The body ends at the first.
|
|
418
418
|
const DOCTRINE_CLOSING = /^##\s+(For the agent|Terms|Related documents)\s*$/;
|
|
419
419
|
// The words a doctrine document replaces with the term engineers use (references/style-guide.md § Vocabulary).
|
|
@@ -17,7 +17,8 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
17
17
|
| Feature | [feature.md](feature.md) | truth | `feature` | `docs/features/{feature}.md` | `type` `description` `status` `objects` | Scope · Promises · For the agent |
|
|
18
18
|
| Package | [package.md](package.md) | truth | `package` | `docs/architecture/packages/{package}.md` | `type` `description` `status` `objects` | Scope · Ownership · Usage · Promises · For the agent |
|
|
19
19
|
| System | [system.md](system.md) | truth | `system` | `docs/architecture/systems/{system}.md` | `type` `description` `status` `objects` | Scope · Flow · Promises · For the agent |
|
|
20
|
-
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/
|
|
20
|
+
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/doctrine/{concern}.md` | `type` `description` `status` `objects` | Principles · Terms · Related documents |
|
|
21
|
+
| Playbook | [playbook.md](playbook.md) | truth | `playbook` | `docs/architecture/playbooks/{subject}.md` | `type` `description` `status` `objects` | Purpose · Related documents |
|
|
21
22
|
| Architecture decision | [architecture-decision.md](architecture-decision.md) | truth | `architecture-decision` | `docs/architecture/decisions/AD-NNN-{subject}.md` | `type` `title` `description` `status` `last-updated` | Decision · Context · Consequences |
|
|
22
23
|
| UX behavior | [ux-behavior.md](ux-behavior.md) | truth | `ux` | `docs/features/{surface}-ux.md` | `type` `description` `status` `objects` | Purpose · Anatomy · States · Affordances · Promises · Transitions |
|
|
23
24
|
| System overview | [system-overview.md](system-overview.md) | truth | `system-overview` | `docs/architecture/system-design.md` | `type` `description` | — |
|
|
@@ -25,7 +26,7 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
25
26
|
| Glossary | [glossary.md](glossary.md) | truth | `glossary` | `docs/architecture/glossary.md` | `type` `description` | — |
|
|
26
27
|
| Non-goals | [non-goals.md](non-goals.md) | truth | `non-goals` | `docs/architecture/non-goals.md` | `type` `description` | — |
|
|
27
28
|
| Security overview | [security-overview.md](security-overview.md) | truth | `security-overview` | `docs/architecture/security.md` | `type` `description` | — |
|
|
28
|
-
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/
|
|
29
|
+
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/doctrine/testing.md` | `type` `description` | — |
|
|
29
30
|
| API reference | [api-reference.md](api-reference.md) | truth | `api-reference` | `docs/architecture/api.md` | `type` `description` | — |
|
|
30
31
|
| Environment registry | [environment-registry.md](environment-registry.md) | truth | `environment-registry` | `docs/operations/environment.md` | `type` `description` | — |
|
|
31
32
|
| Behavioral checklist | [behavioral-checklist.md](behavioral-checklist.md) | truth | `checklist` | `docs/tests/{feature}.md` | `type` `description` `verifies` | — |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Doctrine documentation (docs/architecture/
|
|
1
|
+
# Doctrine documentation (docs/architecture/doctrine/{concern}.md)
|
|
2
2
|
|
|
3
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
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Playbook documentation (docs/architecture/playbooks/{subject}.md)
|
|
2
|
+
|
|
3
|
+
Truth document. Use for **one interface or workflow shape every product builds**: the shell, a settings area, analytics pages, a record's lifecycle, the interaction patterns, automated agents. One file per shape, named for it. A playbook speaks for no code: no source header names it, and it carries no `owns:`. The product's own feature documents hold its instance of the shape, and each names the playbook in its Scope.
|
|
4
|
+
|
|
5
|
+
**A playbook states the shape, never one product's instance of it.** It says what the parts are, how they are arranged, how each behaves and what the shape promises, so a new instance is new content poured into a known shape rather than a new layout. A product's own values (its labels, routes, limits, timings, the wording of its copy) belong to its feature document. A playbook explains no mechanism, for the reasons a doctrine document explains none (`templates/doctrine.md`).
|
|
6
|
+
|
|
7
|
+
**Doctrine or playbook.** A doctrine document settles the decisions of one concern every product handles, such as how authorization works. A playbook settles what a person sees and does in one recurring shape, such as how a settings page is laid out. A playbook cites the doctrine its shape rests on, and never restates it.
|
|
8
|
+
|
|
9
|
+
**A playbook is written in general form, never linked to the project that holds it.** It reads correctly when copied verbatim into any other product's repository, under the same rule a doctrine document keeps (`templates/doctrine.md`). Where the shape needs a word for the product's own record, the Purpose introduces a generic one once ("the subject: the record a report is read per, such as a job in a hiring product") and the body uses it throughout. Its links reach only sibling playbooks, doctrine documents and published standards.
|
|
10
|
+
|
|
11
|
+
**Earn the document, and ask before writing it.** List the rules only this playbook would own, each checked against the playbooks and doctrine that exist. Fewer than about six owned rules means the shape is a section of a neighbour, not a document. A new playbook is created only with the user's approval (SKILL.md § Doctrine and playbooks).
|
|
12
|
+
|
|
13
|
+
**Required:**
|
|
14
|
+
|
|
15
|
+
- the frontmatter
|
|
16
|
+
- **Purpose:** what the shape is for, what it is not for, and the generic words it introduces
|
|
17
|
+
- **Related documents**
|
|
18
|
+
|
|
19
|
+
**Optional, each only with something real to say:**
|
|
20
|
+
|
|
21
|
+
- **Anatomy:** an ASCII diagram of the arrangement with each region labelled, then one bullet per region
|
|
22
|
+
- **One section per part** that carries rules of its own, named for the part ("Roster", "Readiness", "Side peek")
|
|
23
|
+
- **States:** one table — State · Entered when · The person sees · Can do
|
|
24
|
+
- **Affordances:** what a person can do, one bullet per verb
|
|
25
|
+
- **Principles:** numbered, each a noun-phrase label and its rule
|
|
26
|
+
- **Promises:** what the shape guarantees, grouped under `###` headings where there are several kinds
|
|
27
|
+
- **Transitions:** what moves the shape between its states, and who moves it
|
|
28
|
+
|
|
29
|
+
Sections run Purpose first and Related documents last; the rest follow the order above. Each is named for its concept, never for its layer. Expect one hundred to two hundred lines.
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
---
|
|
33
|
+
type: playbook # required tooling key
|
|
34
|
+
description: "{at most 300 characters: the shape this playbook settles, and that a product's own instance is its feature doc}" # required
|
|
35
|
+
status: active # required — active | draft
|
|
36
|
+
objects: [rail, command palette, account menu] # required — the parts this playbook shapes, by their graph names
|
|
37
|
+
---
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
# {Shape} playbook
|
|
42
|
+
|
|
43
|
+
## Purpose
|
|
44
|
+
|
|
45
|
+
{What the shape is for and what it is not for, in two or three sentences. The generic words it introduces, each in one bullet.}
|
|
46
|
+
|
|
47
|
+
## Anatomy
|
|
48
|
+
|
|
49
|
+
{An ASCII diagram, then one bullet per labelled region.}
|
|
50
|
+
|
|
51
|
+
## {Part}
|
|
52
|
+
|
|
53
|
+
- **{Rule label}:** {the rule, in general form}
|
|
54
|
+
|
|
55
|
+
## Principles
|
|
56
|
+
|
|
57
|
+
1. **{Label}:** {the rule}. {Its reason, as a separate sentence.}
|
|
58
|
+
|
|
59
|
+
## Related documents
|
|
60
|
+
|
|
61
|
+
- [{Sibling playbook}]({file}.md): {what it settles beside this one}
|
|
62
|
+
- [{Doctrine}](../doctrine/{concern}.md): {the decision this shape rests on}
|
|
63
|
+
```
|
|
@@ -130,7 +130,7 @@ The rest of this page is for the team and for coding agents working in this repo
|
|
|
130
130
|
|
|
131
131
|
The baseline **does** scaffold `/README.md` when it is missing, and it fills each section from the repo:
|
|
132
132
|
|
|
133
|
-
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/
|
|
133
|
+
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/doctrine/` (what the product is, the core loop, who it is for), the package manifests (what publishes where), and the deploy config (addresses). Written in a user's terms; where the charter is missing, the baseline writes Identity only and raises a `note:` row for the rest.
|
|
134
134
|
- **The main path:** from the system overview.
|
|
135
135
|
- **Map:** from the workspace globs and the ownership index.
|
|
136
136
|
- **Rules:** from `docs/operations/protected-files.md` and the architecture doctrine.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Testing strategy (docs/architecture/
|
|
1
|
+
# Testing strategy (docs/architecture/doctrine/testing.md)
|
|
2
2
|
|
|
3
3
|
Truth document. Use for: the repo's test posture — what is tested at which level, how to run it, and what is deliberately untested. The framework choices come from the harness testing rule; this doc records how *this repo* applies them.
|
|
4
4
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UX behavior (docs/features/{surface}-ux.md)
|
|
2
2
|
|
|
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.
|
|
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. It is one product's surface; a shape every product's surfaces share, such as a settings area or an analytics page, is a playbook (`templates/playbook.md`), which the UX document names in its Purpose and fills with its own values.
|
|
4
4
|
|
|
5
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
|
|
|
@@ -70,7 +70,7 @@ Across the header pointers and the transitional globs:
|
|
|
70
70
|
|
|
71
71
|
- **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`.
|
|
72
72
|
- **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.
|
|
73
|
-
- **Doctrine with no `objects:`.** A `docs/architecture/
|
|
73
|
+
- **Doctrine with no `objects:`.** A `docs/architecture/doctrine/` or `docs/architecture/playbooks/` 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.
|
|
74
74
|
|
|
75
75
|
**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.
|
|
76
76
|
|
|
@@ -137,6 +137,8 @@ docs/
|
|
|
137
137
|
├── structure.md # Harness-owned authority map — the installer writes it on every refresh; you stamp only `harness-version`
|
|
138
138
|
├── architecture/ # Cross-module design + steering layer
|
|
139
139
|
│ │ # domain-model, glossary, non-goals, references, open-questions
|
|
140
|
+
│ ├── doctrine/ # Doctrine — one `{concern}.md` per concern every product handles; owns no code
|
|
141
|
+
│ ├── playbooks/ # Playbooks — one `{subject}.md` per shape every product builds; owns no code
|
|
140
142
|
│ └── decisions/ # Architecture Decisions (ADs — one per subject, updated in place; journey in each AD's Decision Changelog)
|
|
141
143
|
├── features/ # Feature specs — flat, no subfolders; ownership computed from each doc's `objects:` and the source headers
|
|
142
144
|
├── operations/ # Setup, development, deployment, CI/CD
|
|
@@ -180,6 +182,7 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
|
|
|
180
182
|
| no `harness-version` in `structure.md` | stamp it (Step B4) |
|
|
181
183
|
| `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 |
|
|
182
184
|
| 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`) |
|
|
185
|
+
| a `docs/architecture/common/` folder (the doctrine folder's name before `doctrine/`) | **move it to `docs/architecture/doctrine/` with `git mv`, then repoint every reference.** Every markdown link, `objects:`-adjacent path, `Bound by` and Related-documents entry, and every source-file path header that points into `common/` is rewritten to `doctrine/`; frozen records under `docs/history/` keep the old path as provenance. Verify with a search: no reference into `docs/architecture/common/` remains outside `docs/history/` |
|
|
183
186
|
|
|
184
187
|
**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.
|
|
185
188
|
|
|
@@ -231,6 +234,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
|
|
|
231
234
|
## Rules
|
|
232
235
|
|
|
233
236
|
- **Never delete feature docs** — only update or flag for review.
|
|
237
|
+
- **Never create a doctrine or playbook document.** A baseline folds, moves and converges the ones that exist; content that looks like a new standard goes to the backlog as a proposal for the user (the `documentation` skill § Doctrine and playbooks).
|
|
234
238
|
- **Never modify `docs/history/plans/`, `docs/history/changelog/`, or `docs/history/migrations/`** (frozen records — changelog entries are written only at commit time and distilled only by the `retro` agent; a migration runbook freezes at its Outcome stamp).
|
|
235
239
|
- **Preserve existing doc structure and formatting** when updating.
|
|
236
240
|
- **When in doubt, create a backlog item** rather than making a wrong update.
|
|
@@ -68,7 +68,7 @@ Across the header pointers and the transitional globs:
|
|
|
68
68
|
|
|
69
69
|
- **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`.
|
|
70
70
|
- **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.
|
|
71
|
-
- **Doctrine with no `objects:`.** A `docs/architecture/
|
|
71
|
+
- **Doctrine with no `objects:`.** A `docs/architecture/doctrine/` or `docs/architecture/playbooks/` 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.
|
|
72
72
|
|
|
73
73
|
**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.
|
|
74
74
|
|
|
@@ -135,6 +135,8 @@ docs/
|
|
|
135
135
|
├── structure.md # Harness-owned authority map — the installer writes it on every refresh; you stamp only `harness-version`
|
|
136
136
|
├── architecture/ # Cross-module design + steering layer
|
|
137
137
|
│ │ # domain-model, glossary, non-goals, references, open-questions
|
|
138
|
+
│ ├── doctrine/ # Doctrine — one `{concern}.md` per concern every product handles; owns no code
|
|
139
|
+
│ ├── playbooks/ # Playbooks — one `{subject}.md` per shape every product builds; owns no code
|
|
138
140
|
│ └── decisions/ # Architecture Decisions (ADs — one per subject, updated in place; journey in each AD's Decision Changelog)
|
|
139
141
|
├── features/ # Feature specs — flat, no subfolders; ownership computed from each doc's `objects:` and the source headers
|
|
140
142
|
├── operations/ # Setup, development, deployment, CI/CD
|
|
@@ -178,6 +180,7 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
|
|
|
178
180
|
| no `harness-version` in `structure.md` | stamp it (Step B4) |
|
|
179
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 |
|
|
180
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`) |
|
|
183
|
+
| a `docs/architecture/common/` folder (the doctrine folder's name before `doctrine/`) | **move it to `docs/architecture/doctrine/` with `git mv`, then repoint every reference.** Every markdown link, `objects:`-adjacent path, `Bound by` and Related-documents entry, and every source-file path header that points into `common/` is rewritten to `doctrine/`; frozen records under `docs/history/` keep the old path as provenance. Verify with a search: no reference into `docs/architecture/common/` remains outside `docs/history/` |
|
|
181
184
|
|
|
182
185
|
**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.
|
|
183
186
|
|
|
@@ -229,6 +232,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
|
|
|
229
232
|
## Rules
|
|
230
233
|
|
|
231
234
|
- **Never delete feature docs** — only update or flag for review.
|
|
235
|
+
- **Never create a doctrine or playbook document.** A baseline folds, moves and converges the ones that exist; content that looks like a new standard goes to the backlog as a proposal for the user (the `documentation` skill § Doctrine and playbooks).
|
|
232
236
|
- **Never modify `docs/history/plans/`, `docs/history/changelog/`, or `docs/history/migrations/`** (frozen records — changelog entries are written only at commit time and distilled only by the `retro` agent; a migration runbook freezes at its Outcome stamp).
|
|
233
237
|
- **Preserve existing doc structure and formatting** when updating.
|
|
234
238
|
- **When in doubt, create a backlog item** rather than making a wrong update.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "etna",
|
|
3
3
|
"name": "etna",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.42",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
"origin": "https://cdn.nustack.io",
|
|
15
15
|
"track": "latest"
|
|
16
16
|
},
|
|
17
|
-
"digest": "
|
|
17
|
+
"digest": "9a42d4d6b62e"
|
|
18
18
|
}
|
|
@@ -20,7 +20,7 @@ This skill writes one document at a time: the genre registry says what kind of d
|
|
|
20
20
|
## Writing one document
|
|
21
21
|
|
|
22
22
|
1. **Find the genre.** Match the request against § Routing, then read that genre's registry row: nature, `type:`, location, required keys, required sections. No row fits ⇒ `references/untemplated.md`.
|
|
23
|
-
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
23
|
+
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. A new doctrine or playbook document also waits for the user's approval (§ Doctrine and playbooks). If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
24
24
|
3. **Earn it, and route a set.** A new truth document that states rules (doctrine, a system, an architecture decision) first lists the decisions only it would own, each checked against the documents that exist; under about six, its rules go to their homes and the document is not written. More than one sibling document, or any doctrine document, follows `references/document-sets.md` from here: a term table and a rule ledger before prose, rendered from the ledger, one cold read.
|
|
25
25
|
4. **Place and name it** at the registry's location. `docs/structure.md` is the placement authority for anything the registry leaves open.
|
|
26
26
|
5. **Write it from the template, then run its genre procedure.** A template's procedure section binds that genre only: doctrine's is the reason check (`templates/doctrine.md` § Reason check). Keep the template's section names exactly; omit optional sections with nothing real to say. Write the body from the outside in (style guide § Shape), every heading and bold label a noun phrase, every sentence after a label third person, one rule per sentence in the term engineers use (style guide § Sentences, § Vocabulary). In a truth document, every sentence is present tense (§ The three natures). Every line owns a decision: before stating a rule, search the neighbours for it, and cite the one found rather than restating it (style guide § Net-net).
|
|
@@ -41,6 +41,15 @@ Some genres carry steps beyond writing from the template. Each procedure lives i
|
|
|
41
41
|
|
|
42
42
|
A genre with no row here has no procedure beyond its template.
|
|
43
43
|
|
|
44
|
+
## Doctrine and playbooks
|
|
45
|
+
|
|
46
|
+
Two genres are standards rather than accounts of one repository: a doctrine document settles one concern every product handles, and a playbook settles one interface or workflow shape every product builds. Both are read by the next product as much as by this one, so four rules bind them beyond their templates:
|
|
47
|
+
|
|
48
|
+
- **General form:** each reads correctly copied verbatim into another product's repository. It names nothing only its own repository has: the product, its domain entities, its vendors and stack, its people, its own documents by path. An example uses an object every product has, and the repository's own values stay in its feature documents, which name the standard in their Scope.
|
|
49
|
+
- **Extend first:** before a new document is proposed, the existing set is read (`grep -m1 '^description:' docs/architecture/{doctrine,playbooks}/*.md`), and content whose subject one of them already covers lands there as a section, a principle or a row.
|
|
50
|
+
- **New only on a clear distinction:** a new document is written only for a concern or a shape none of the existing ones owns, with decisions of its own (the template's earn-it test).
|
|
51
|
+
- **Never without approval:** an agent never creates a doctrine or playbook document on its own initiative, a baseline and a harvest included. It names the proposed document, the existing documents it was checked against and the distinction between them, then waits for the user's yes in chat. An unattended session files the request as a stop. An edit to an existing document follows the normal loop.
|
|
52
|
+
|
|
44
53
|
## The three natures
|
|
45
54
|
|
|
46
55
|
Every document is exactly one of three things, and the registry names which for every genre. Decide before writing a word: every other rule follows from the split.
|
|
@@ -105,6 +114,7 @@ A document names the objects it designs in `objects:`, and a source file names t
|
|
|
105
114
|
| Document a feature's data flow or state | The feature doc's Flows (behaviour) and Promises (guarantees); a persisted shape belongs in the API reference (style guide § State at persistence points); component and transient state are not documented |
|
|
106
115
|
| Document a workspace package | [`templates/package.md`](templates/package.md), the residual owner of `packages/{name}/**` |
|
|
107
116
|
| Document one concern the platform handles everywhere (authentication, storage, the API protocol) | [`templates/doctrine.md`](templates/doctrine.md): design and principles in general form, no mechanism, no project link, owns no code; written through [`references/document-sets.md`](references/document-sets.md) |
|
|
117
|
+
| Document one interface or workflow shape every product builds (the shell, a settings area, analytics pages, a record's lifecycle) | [`templates/playbook.md`](templates/playbook.md): the shape and its rules in general form, owns no code; created only with the user's approval (§ Doctrine and playbooks) |
|
|
108
118
|
| Write or rework several sibling documents at once (a doctrine folder, a family of features) | [`references/document-sets.md`](references/document-sets.md): usefulness test, term table, rule ledger, render, one cold read |
|
|
109
119
|
| Document one system spanning packages | [`templates/system.md`](templates/system.md): one machine end to end, never a giant feature doc |
|
|
110
120
|
| Record or revise an architecture decision | [`templates/architecture-decision.md`](templates/architecture-decision.md): one AD per subject, revised in place |
|
|
@@ -28,7 +28,8 @@ Every genre takes the same shape (principle 2) and the same labels (principle 5)
|
|
|
28
28
|
|
|
29
29
|
| Genre | Nature | Deepest layer | Excluded |
|
|
30
30
|
| --- | --- | --- | --- |
|
|
31
|
-
| Doctrine (`docs/architecture/
|
|
31
|
+
| Doctrine (`docs/architecture/doctrine/`) | Truth | The decisions behind each part and the reasons for them | Any mechanism. A doctrine document describes no code |
|
|
32
|
+
| Playbook (`docs/architecture/playbooks/`) | Truth | What a person sees and does in the shape, part by part: its arrangement, each part's behaviour, its states and promises | Any one product's values: its labels, routes, limits and timings, which are its feature documents'. Any mechanism |
|
|
32
33
|
| System (`docs/architecture/systems/`) | Truth | The machine's behaviour across packages: the order it runs in, sequence and state diagrams at role level, the invariant at each place where parts meet, what re-drives a stall. No single file shows a cross-package flow, so this layer earns its place | Per-function steps, tables and columns, routes |
|
|
33
34
|
| Package (`docs/architecture/packages/`) | Truth | The public contract a consumer touches: commands and their flags, exports, what is installed and where. For a CLI the flags *are* the promise | Internal layout, the build's steps, which file holds what |
|
|
34
35
|
| Feature (`docs/features/`) | Truth | The behaviour a person meets, in full: each state the surface can be in, each edge case, what a rejection tells them, the wording where the wording is promised. This genre runs deepest toward its end, in Promises, because a promise is checked by a person or a test and the code cannot state it as intent | Columns, routes, guards, functions, files |
|
|
@@ -255,7 +255,7 @@ function lintFile(path, findings) {
|
|
|
255
255
|
if (/^[a-z]+:\/\//.test(target) || SCAFFOLD.test(target)) continue;
|
|
256
256
|
// The repo-root README routes by intent (templates/root-readme.md): its Map names owning package and system
|
|
257
257
|
// docs, its Destinations rows name procedures and rules. Individual feature docs stay off it — the ownership index finds them.
|
|
258
|
-
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|
|
|
258
|
+
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|doctrine|playbooks)\/|operations\/)[^/]+\.md$|(^|\/)\.claude\/rules\/[^/]+\.md$/.test(target)) continue;
|
|
259
259
|
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; }
|
|
260
260
|
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) });
|
|
261
261
|
}
|
|
@@ -382,7 +382,7 @@ function lintFile(path, findings) {
|
|
|
382
382
|
// A heading or a bold bullet label names; it never says. A label with a finite verb drags in a tense,
|
|
383
383
|
// a person and a voice ("What was decided", "You already belong to…"), so a label opening with a
|
|
384
384
|
// wh-word or a personal pronoun, or ending in a full stop, is a clause (style-guide § Labels).
|
|
385
|
-
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/
|
|
385
|
+
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/(doctrine|playbooks)\/[^/]+\.md$/.test(path));
|
|
386
386
|
if (isLabelledDoc && !disabled.has('clause-label')) {
|
|
387
387
|
const CLAUSE = /^(how|what|why|where|when|who|which|whose|you|your|we|our|i|it)\b|\b(you|your)\b|\.$/i;
|
|
388
388
|
for (const { n, line } of lines) {
|
|
@@ -413,7 +413,7 @@ function lintFile(path, findings) {
|
|
|
413
413
|
|
|
414
414
|
// A doctrine folder is read as a set: its documents cite each other's principles by number and must not restate each
|
|
415
415
|
// other (templates/doctrine.md, references/document-sets.md), so these checks read the siblings beside the linted file.
|
|
416
|
-
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/
|
|
416
|
+
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/doctrine\/[^/]+\.md$/;
|
|
417
417
|
// A doctrine document closes on Terms and Related documents; an older one on "For the agent". The body ends at the first.
|
|
418
418
|
const DOCTRINE_CLOSING = /^##\s+(For the agent|Terms|Related documents)\s*$/;
|
|
419
419
|
// The words a doctrine document replaces with the term engineers use (references/style-guide.md § Vocabulary).
|
|
@@ -17,7 +17,8 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
17
17
|
| Feature | [feature.md](feature.md) | truth | `feature` | `docs/features/{feature}.md` | `type` `description` `status` `objects` | Scope · Promises · For the agent |
|
|
18
18
|
| Package | [package.md](package.md) | truth | `package` | `docs/architecture/packages/{package}.md` | `type` `description` `status` `objects` | Scope · Ownership · Usage · Promises · For the agent |
|
|
19
19
|
| System | [system.md](system.md) | truth | `system` | `docs/architecture/systems/{system}.md` | `type` `description` `status` `objects` | Scope · Flow · Promises · For the agent |
|
|
20
|
-
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/
|
|
20
|
+
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/doctrine/{concern}.md` | `type` `description` `status` `objects` | Principles · Terms · Related documents |
|
|
21
|
+
| Playbook | [playbook.md](playbook.md) | truth | `playbook` | `docs/architecture/playbooks/{subject}.md` | `type` `description` `status` `objects` | Purpose · Related documents |
|
|
21
22
|
| Architecture decision | [architecture-decision.md](architecture-decision.md) | truth | `architecture-decision` | `docs/architecture/decisions/AD-NNN-{subject}.md` | `type` `title` `description` `status` `last-updated` | Decision · Context · Consequences |
|
|
22
23
|
| UX behavior | [ux-behavior.md](ux-behavior.md) | truth | `ux` | `docs/features/{surface}-ux.md` | `type` `description` `status` `objects` | Purpose · Anatomy · States · Affordances · Promises · Transitions |
|
|
23
24
|
| System overview | [system-overview.md](system-overview.md) | truth | `system-overview` | `docs/architecture/system-design.md` | `type` `description` | — |
|
|
@@ -25,7 +26,7 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
25
26
|
| Glossary | [glossary.md](glossary.md) | truth | `glossary` | `docs/architecture/glossary.md` | `type` `description` | — |
|
|
26
27
|
| Non-goals | [non-goals.md](non-goals.md) | truth | `non-goals` | `docs/architecture/non-goals.md` | `type` `description` | — |
|
|
27
28
|
| Security overview | [security-overview.md](security-overview.md) | truth | `security-overview` | `docs/architecture/security.md` | `type` `description` | — |
|
|
28
|
-
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/
|
|
29
|
+
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/doctrine/testing.md` | `type` `description` | — |
|
|
29
30
|
| API reference | [api-reference.md](api-reference.md) | truth | `api-reference` | `docs/architecture/api.md` | `type` `description` | — |
|
|
30
31
|
| Environment registry | [environment-registry.md](environment-registry.md) | truth | `environment-registry` | `docs/operations/environment.md` | `type` `description` | — |
|
|
31
32
|
| Behavioral checklist | [behavioral-checklist.md](behavioral-checklist.md) | truth | `checklist` | `docs/tests/{feature}.md` | `type` `description` `verifies` | — |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Doctrine documentation (docs/architecture/
|
|
1
|
+
# Doctrine documentation (docs/architecture/doctrine/{concern}.md)
|
|
2
2
|
|
|
3
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
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Playbook documentation (docs/architecture/playbooks/{subject}.md)
|
|
2
|
+
|
|
3
|
+
Truth document. Use for **one interface or workflow shape every product builds**: the shell, a settings area, analytics pages, a record's lifecycle, the interaction patterns, automated agents. One file per shape, named for it. A playbook speaks for no code: no source header names it, and it carries no `owns:`. The product's own feature documents hold its instance of the shape, and each names the playbook in its Scope.
|
|
4
|
+
|
|
5
|
+
**A playbook states the shape, never one product's instance of it.** It says what the parts are, how they are arranged, how each behaves and what the shape promises, so a new instance is new content poured into a known shape rather than a new layout. A product's own values (its labels, routes, limits, timings, the wording of its copy) belong to its feature document. A playbook explains no mechanism, for the reasons a doctrine document explains none (`templates/doctrine.md`).
|
|
6
|
+
|
|
7
|
+
**Doctrine or playbook.** A doctrine document settles the decisions of one concern every product handles, such as how authorization works. A playbook settles what a person sees and does in one recurring shape, such as how a settings page is laid out. A playbook cites the doctrine its shape rests on, and never restates it.
|
|
8
|
+
|
|
9
|
+
**A playbook is written in general form, never linked to the project that holds it.** It reads correctly when copied verbatim into any other product's repository, under the same rule a doctrine document keeps (`templates/doctrine.md`). Where the shape needs a word for the product's own record, the Purpose introduces a generic one once ("the subject: the record a report is read per, such as a job in a hiring product") and the body uses it throughout. Its links reach only sibling playbooks, doctrine documents and published standards.
|
|
10
|
+
|
|
11
|
+
**Earn the document, and ask before writing it.** List the rules only this playbook would own, each checked against the playbooks and doctrine that exist. Fewer than about six owned rules means the shape is a section of a neighbour, not a document. A new playbook is created only with the user's approval (SKILL.md § Doctrine and playbooks).
|
|
12
|
+
|
|
13
|
+
**Required:**
|
|
14
|
+
|
|
15
|
+
- the frontmatter
|
|
16
|
+
- **Purpose:** what the shape is for, what it is not for, and the generic words it introduces
|
|
17
|
+
- **Related documents**
|
|
18
|
+
|
|
19
|
+
**Optional, each only with something real to say:**
|
|
20
|
+
|
|
21
|
+
- **Anatomy:** an ASCII diagram of the arrangement with each region labelled, then one bullet per region
|
|
22
|
+
- **One section per part** that carries rules of its own, named for the part ("Roster", "Readiness", "Side peek")
|
|
23
|
+
- **States:** one table — State · Entered when · The person sees · Can do
|
|
24
|
+
- **Affordances:** what a person can do, one bullet per verb
|
|
25
|
+
- **Principles:** numbered, each a noun-phrase label and its rule
|
|
26
|
+
- **Promises:** what the shape guarantees, grouped under `###` headings where there are several kinds
|
|
27
|
+
- **Transitions:** what moves the shape between its states, and who moves it
|
|
28
|
+
|
|
29
|
+
Sections run Purpose first and Related documents last; the rest follow the order above. Each is named for its concept, never for its layer. Expect one hundred to two hundred lines.
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
---
|
|
33
|
+
type: playbook # required tooling key
|
|
34
|
+
description: "{at most 300 characters: the shape this playbook settles, and that a product's own instance is its feature doc}" # required
|
|
35
|
+
status: active # required — active | draft
|
|
36
|
+
objects: [rail, command palette, account menu] # required — the parts this playbook shapes, by their graph names
|
|
37
|
+
---
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
# {Shape} playbook
|
|
42
|
+
|
|
43
|
+
## Purpose
|
|
44
|
+
|
|
45
|
+
{What the shape is for and what it is not for, in two or three sentences. The generic words it introduces, each in one bullet.}
|
|
46
|
+
|
|
47
|
+
## Anatomy
|
|
48
|
+
|
|
49
|
+
{An ASCII diagram, then one bullet per labelled region.}
|
|
50
|
+
|
|
51
|
+
## {Part}
|
|
52
|
+
|
|
53
|
+
- **{Rule label}:** {the rule, in general form}
|
|
54
|
+
|
|
55
|
+
## Principles
|
|
56
|
+
|
|
57
|
+
1. **{Label}:** {the rule}. {Its reason, as a separate sentence.}
|
|
58
|
+
|
|
59
|
+
## Related documents
|
|
60
|
+
|
|
61
|
+
- [{Sibling playbook}]({file}.md): {what it settles beside this one}
|
|
62
|
+
- [{Doctrine}](../doctrine/{concern}.md): {the decision this shape rests on}
|
|
63
|
+
```
|
|
@@ -130,7 +130,7 @@ The rest of this page is for the team and for coding agents working in this repo
|
|
|
130
130
|
|
|
131
131
|
The baseline **does** scaffold `/README.md` when it is missing, and it fills each section from the repo:
|
|
132
132
|
|
|
133
|
-
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/
|
|
133
|
+
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/doctrine/` (what the product is, the core loop, who it is for), the package manifests (what publishes where), and the deploy config (addresses). Written in a user's terms; where the charter is missing, the baseline writes Identity only and raises a `note:` row for the rest.
|
|
134
134
|
- **The main path:** from the system overview.
|
|
135
135
|
- **Map:** from the workspace globs and the ownership index.
|
|
136
136
|
- **Rules:** from `docs/operations/protected-files.md` and the architecture doctrine.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Testing strategy (docs/architecture/
|
|
1
|
+
# Testing strategy (docs/architecture/doctrine/testing.md)
|
|
2
2
|
|
|
3
3
|
Truth document. Use for: the repo's test posture — what is tested at which level, how to run it, and what is deliberately untested. The framework choices come from the harness testing rule; this doc records how *this repo* applies them.
|
|
4
4
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UX behavior (docs/features/{surface}-ux.md)
|
|
2
2
|
|
|
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.
|
|
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. It is one product's surface; a shape every product's surfaces share, such as a settings area or an analytics page, is a playbook (`templates/playbook.md`), which the UX document names in its Purpose and fills with its own values.
|
|
4
4
|
|
|
5
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
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nurix/etna",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.47",
|
|
4
4
|
"description": "Bootstrap a project with a Nurix harness — writes the committed contract (CLAUDE.md, AGENTS.md, .claude/harness.json, settings.json, the mount hook) and mounts the harness itself, gitignored, from a bundled payload; sessions keep it current from the CDN.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"agent",
|
|
47
47
|
"plugin"
|
|
48
48
|
],
|
|
49
|
-
"etnaFingerprint": "
|
|
49
|
+
"etnaFingerprint": "7097a106c017"
|
|
50
50
|
}
|