@nurix/etna 0.20.9 → 0.20.11
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/catalog/rules/cloudflare.md +8 -0
- package/data/catalog/rules/database.md +10 -0
- package/data/catalog/rules/frontend.md +10 -0
- package/data/catalog/rules/pnpm.md +9 -0
- package/data/catalog/rules/react-native.md +10 -0
- package/data/catalog/rules/rust-desktop.md +8 -0
- package/data/catalog/rules/temporal.md +9 -0
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/CLAUDE.md +6 -4
- package/data/harnesses/code/agents/polygraph-applier.md +24 -0
- package/data/harnesses/code/agents/polygraph-finder.md +31 -0
- package/data/harnesses/code/agents/polygraph-verifier.md +20 -0
- package/data/harnesses/code/codex/agents/polygraph-applier.toml +21 -0
- package/data/harnesses/code/codex/agents/polygraph-finder.toml +28 -0
- package/data/harnesses/code/codex/agents/polygraph-verifier.toml +17 -0
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/hooks/awareness.mjs +23 -6
- package/data/harnesses/code/hooks/lib/ownership.mjs +51 -19
- package/data/harnesses/code/hooks/lib/plans.mjs +11 -1
- package/data/harnesses/code/rules/cloudflare.md +8 -0
- package/data/harnesses/code/rules/database.md +10 -0
- package/data/harnesses/code/rules/frontend.md +10 -0
- package/data/harnesses/code/rules/pnpm.md +9 -0
- package/data/harnesses/code/skills/documentation/SKILL.md +10 -3
- package/data/harnesses/code/skills/documentation/references/frontmatter.md +21 -7
- package/data/harnesses/code/skills/documentation/references/instruction-style.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/Readme.md +3 -1
- package/data/harnesses/code/skills/documentation/templates/package.md +131 -0
- package/data/harnesses/code/skills/documentation/templates/plan.md +2 -2
- package/data/harnesses/code/skills/documentation/templates/structure.md +9 -3
- package/data/harnesses/code/skills/documentation/templates/system.md +143 -0
- package/data/harnesses/code/skills/polygraph/SKILL.md +71 -0
- package/data/harnesses/code/skills/polygraph/references/dispatch.md +80 -0
- package/data/harnesses/code/skills/polygraph/references/rows.md +57 -0
- package/data/harnesses/code/skills/polygraph/scripts/audit.mjs +113 -0
- package/data/harnesses/delivery/harness.json +2 -2
- package/data/harnesses/delivery/skills/documentation/SKILL.md +10 -3
- package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +21 -7
- package/data/harnesses/delivery/skills/documentation/references/instruction-style.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/Readme.md +3 -1
- package/data/harnesses/delivery/skills/documentation/templates/package.md +131 -0
- package/data/harnesses/delivery/skills/documentation/templates/plan.md +2 -2
- package/data/harnesses/delivery/skills/documentation/templates/structure.md +9 -3
- package/data/harnesses/delivery/skills/documentation/templates/system.md +143 -0
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/skills/documentation/SKILL.md +10 -3
- package/data/harnesses/etna/skills/documentation/references/frontmatter.md +21 -7
- package/data/harnesses/etna/skills/documentation/references/instruction-style.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/Readme.md +3 -1
- package/data/harnesses/etna/skills/documentation/templates/package.md +131 -0
- package/data/harnesses/etna/skills/documentation/templates/plan.md +2 -2
- package/data/harnesses/etna/skills/documentation/templates/structure.md +9 -3
- package/data/harnesses/etna/skills/documentation/templates/system.md +143 -0
- package/package.json +2 -2
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# System Documentation (docs/architecture/systems/{system}.md)
|
|
2
|
+
|
|
3
|
+
Truth document. Use for: **one machine that spans packages** — exactly how it works, end to end, in one place. One file per machine, named for the machine (`docs/architecture/systems/update-engine.md`). It states the machine's model and its whole path: what enters it, every stage it passes through, what it writes, what re-drives it when it stalls, and what a reader sees at the far end.
|
|
4
|
+
|
|
5
|
+
**A system document's globs win by specificity.** Its `owns:` names the subtrees and exact files the machine is made of — `packages/studio/src/server/execution/**`, `packages/ui/src/shared/harnessVocabulary.ts` — and those are narrower than the package document's `packages/studio/**`, so the system takes them and the package keeps the residue. That is the whole mechanism: **the package document is the residual owner and the system document is the specific one**, and nothing needs to be subtracted by hand. A machine confined to one package still gets a system document if the machine is the thing worth stating; it is the *narrower glob* that makes it a system, not the package count.
|
|
6
|
+
|
|
7
|
+
**Two rules bind the globs.** Neither genre may claim a path another document claims at **equal specificity** — equal-specificity overlap is an ownership conflict to resolve before either document ships. And every glob **matches at least one file on disk** at `status: active`; projected globs are permitted only at `draft` and are confirmed when the document flips. Because a system's `owns:` list is long and hand-assembled, re-glob it whenever the machine's code moves — a stale entry here silently un-owns real code back to the package document, which does not describe it.
|
|
8
|
+
|
|
9
|
+
**How this differs from its neighbours — and the mistake this template exists to prevent.** A **feature** document covers one capability or one page a person names and owns that surface's globs. A **package** document covers one unit of distribution and owns what is left of its tree. A **system** document covers a *machine*: something with an entry, stages, state and an outcome, which a person does not open and cannot point at. **The failure mode is a system document written as a giant feature document** — a tour of screens and what each button does, at ten times a feature doc's length. The test is mechanical: if the subject can be named by an address a person navigates to, it is a feature; if it can be named only as a verb over the product's own nouns — the walk, the egress, the ingest, the settlement — it is a system. A system document says what a surface *renders from* and links to the feature document for what the surface looks like; it never describes the surface.
|
|
10
|
+
|
|
11
|
+
**Required vs optional.** Required in every system document: the **frontmatter**, the **opening statement** (what the machine is, then what this document states end to end), the **boundary paragraph** (what this document does not restate, and which document holds each part), the **machine diagram**, at least one **stage section**, **Files**, and **Related**. Everything else is optional. Do not add a section because it seems owed — a stage the machine does not have is a section that will be filled with something untrue.
|
|
12
|
+
|
|
13
|
+
System documents are long by nature, because the point is that the whole machine is in one place — roughly 300 to 1000 lines. Length is earned by stages, never by restating a neighbour: every paragraph that could have been a link to the document that owns it is the genre's characteristic waste.
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
---
|
|
17
|
+
owns: # required — the machine's subtrees and exact files; narrower than the package glob
|
|
18
|
+
- packages/{pkg}/src/{machine}/**
|
|
19
|
+
- packages/{pkg}/src/routes/{machine}.ts
|
|
20
|
+
- packages/{other}/src/shared/{machine}Vocabulary.ts
|
|
21
|
+
keywords: [machine name, its nouns, its verbs] # required — discovery terms: the machine's own vocabulary, the table and route names
|
|
22
|
+
status: active # required — active | draft | deprecated
|
|
23
|
+
applies: # optional — the decisions and doctrine this machine is BOUND by (obligation, not a bibliography)
|
|
24
|
+
- docs/architecture/decisions/AD-0NN-{slug}.md
|
|
25
|
+
- docs/architecture/common/{doctrine}.md
|
|
26
|
+
type: system # required tooling key — genre stamp; preserve on edits, never narrate
|
|
27
|
+
---
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
> A system document carries no `module:` — a machine that spans packages belongs to no single
|
|
31
|
+
> bounded context, and labelling it with one asserts a boundary it crosses by definition.
|
|
32
|
+
> `last-reviewed:` is optional tooling metadata: preserve it, never narrate it.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
# {The machine}
|
|
36
|
+
|
|
37
|
+
{One paragraph that defines the machine in its own first sentence — bold the term being defined —
|
|
38
|
+
then says what this document states, in the order the body states it. The enumeration is the
|
|
39
|
+
contract: a reader who wants one stage finds it by reading this sentence. Where the machine's
|
|
40
|
+
purpose is easier stated as an outcome, lead with `**Goal:**` and one sentence instead.}
|
|
41
|
+
|
|
42
|
+
{One paragraph: the boundary. What this document does **not** restate, and which document holds
|
|
43
|
+
each part — the neighbouring systems, the package documents whose residue this sits inside, the
|
|
44
|
+
feature documents that render from it, the common-plane doctrine it obeys, the decision records
|
|
45
|
+
behind its shape. Name each with a link and the subject it holds. This paragraph is what keeps the
|
|
46
|
+
document from growing into its neighbours.}
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
{The machine diagram. One fenced block, drawn as the thing flows — entry at the top or left,
|
|
50
|
+
each stage a box or a labelled arrow, the durable writes named, the re-drive path shown returning.
|
|
51
|
+
Label the arrows with what crosses them, not with verbs. This is the picture a reader holds while
|
|
52
|
+
reading the rest, so it carries the machine's real names — routes, tables, classes, files — and
|
|
53
|
+
not generic boxes.}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
{Optional, and worth its line whenever the body names paths: "Every path below is relative to
|
|
57
|
+
`packages/{pkg}/src/{dir}/` unless it names another package."}
|
|
58
|
+
|
|
59
|
+
{Optional: a reading order for a long document — "§ X is the data; § Y is the code that reads it;
|
|
60
|
+
§ Z is what re-drives it" — where the section list is not self-evidently a sequence.}
|
|
61
|
+
|
|
62
|
+
## The shape
|
|
63
|
+
|
|
64
|
+
{Optional. Where the machine has parts a reader must hold at once before any stage makes sense,
|
|
65
|
+
name them in one table before the stages begin.}
|
|
66
|
+
|
|
67
|
+
| Part | Where | What it holds |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| {Entry} | `routes/{x}.ts` | {thin, tenancy first} |
|
|
70
|
+
| {Ledger} | `{machine}/ledger.ts` | {the durable row} |
|
|
71
|
+
|
|
72
|
+
## {Stage sections — one per stage, in the order the machine runs them}
|
|
73
|
+
|
|
74
|
+
{The body, and the only genre where the section order is decided for you: the machine's own order.
|
|
75
|
+
Entry, then each stage, then what is written durably, then what re-drives a stalled one, then what
|
|
76
|
+
reads the result. Name each section for its stage in the product's own vocabulary. Within a
|
|
77
|
+
section, state the mechanism and its reason: what the code does, what it refuses, and why it
|
|
78
|
+
refuses that rather than something else. Tables for rosters (routes, columns, frames, states,
|
|
79
|
+
bindings); prose for a rule and the defect it prevents. Never paste an implementation — a
|
|
80
|
+
signature, a state list, a wire shape, a table's columns.}
|
|
81
|
+
|
|
82
|
+
## Tests
|
|
83
|
+
|
|
84
|
+
{Optional. What proves this machine, and what each suite pins — a `Suite | Pins` table. Include it
|
|
85
|
+
where the machine has a suite of its own; omit the heading where the coverage lives in the package
|
|
86
|
+
documents' suites.}
|
|
87
|
+
|
|
88
|
+
## Files
|
|
89
|
+
|
|
90
|
+
| Path | Role |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `packages/{pkg}/src/{machine}/{file}.ts` | {what it holds, by name — the exports a reader is looking for} |
|
|
93
|
+
|
|
94
|
+
{One row per file this document owns — the roster is the `owns:` list, made readable. Close with
|
|
95
|
+
the inverse: "Read here, owned elsewhere:" naming the paths the body describes but another document
|
|
96
|
+
claims, each with the document that claims it. That closing line is what stops a system document
|
|
97
|
+
from quietly annexing the code it reads.}
|
|
98
|
+
|
|
99
|
+
## Related
|
|
100
|
+
|
|
101
|
+
- [AD-0NN — {the ruling}](../decisions/AD-0NN-{slug}.md) — {the decision behind this shape, the options weighed}
|
|
102
|
+
- [`{neighbour}.md`]({neighbour}.md) — {the machine on the other side of this seam}
|
|
103
|
+
- [`{feature}.md`](../../features/{feature}.md) — {what a person sees of this machine}
|
|
104
|
+
- [`{doctrine}.md`](../common/{doctrine}.md) — {the cross-cutting contract this machine answers to}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### The boundary paragraph is the genre's load-bearing section
|
|
108
|
+
|
|
109
|
+
Systems overlap: they share tables, call each other, and render into the same surfaces. Without a
|
|
110
|
+
paragraph that names the seams, two system documents describe the same stage from two angles and
|
|
111
|
+
drift apart on the first change — and a reader has no way to tell which is authoritative. So the
|
|
112
|
+
boundary paragraph is not a courtesy list of related reading; it is the statement of what this
|
|
113
|
+
document is *not*, written specifically enough that a later editor can tell where to put a fact.
|
|
114
|
+
Write it as a list of subjects with their owners — "the coordinator's tables, alarm and drive claim
|
|
115
|
+
are `harness.md`; a run's ledger row and settlement are `generation-runs.md`" — never as bare
|
|
116
|
+
links.
|
|
117
|
+
|
|
118
|
+
### Marking what is decided and unbuilt
|
|
119
|
+
|
|
120
|
+
Where the machine has a stage that is decided and not built — a predicate that selects but never
|
|
121
|
+
acts, a column that is written and never read, a door with no caller — wrap the prose in a marker
|
|
122
|
+
so the gap is machine-findable and its evidence travels with it:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
<!-- UNBUILT:START — {what is decided, where it is decided (an AD § or an owner ruling), and the
|
|
126
|
+
source evidence that it is not built: the function with no caller, the route that is not mounted,
|
|
127
|
+
the value written and never read} -->
|
|
128
|
+
{the prose describing the unbuilt stage, and what runs in its place}
|
|
129
|
+
<!-- UNBUILT:END -->
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**State what runs instead.** A machine with a hole in it still works somehow, and the workaround is
|
|
133
|
+
the fact a reader needs. **A design decided *against* carries no marker** — a marker means the work
|
|
134
|
+
is owed and absent, so using one for a rejected design turns the register of debt into a register
|
|
135
|
+
of opinions.
|
|
136
|
+
|
|
137
|
+
### `status: draft` on a system
|
|
138
|
+
|
|
139
|
+
A system document is `draft` when the **machine as a whole** does not run — the code is on disk and
|
|
140
|
+
nothing reaches it. Say so in the first section, in plain words, with the evidence: which halves
|
|
141
|
+
exist, which do not, and what a reader who invokes it actually observes. A scoped gap in a working
|
|
142
|
+
machine is not a draft: the document stays `active` and takes an UNBUILT marker at the stage that
|
|
143
|
+
has the hole.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "etna",
|
|
3
3
|
"name": "etna",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.19",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
"seeds": [],
|
|
11
11
|
"tools": [],
|
|
12
12
|
"docsRoot": "docs",
|
|
13
|
-
"digest": "
|
|
13
|
+
"digest": "4cf950471dd8"
|
|
14
14
|
}
|
|
@@ -60,6 +60,8 @@ The two guarantees are complementary and both break on mixing: a reader holding
|
|
|
60
60
|
| Baseline / reconcile docs | `maintenance` agent (baseline mode) — it calls this skill per template |
|
|
61
61
|
| Document feature/module (LLD) | [`templates/feature.md`](templates/feature.md) |
|
|
62
62
|
| Document data flow / state / Redux | [`templates/feature.md`](templates/feature.md) — they are sections of the feature doc |
|
|
63
|
+
| Document a workspace package (`docs/architecture/packages/{package}.md`) | [`templates/package.md`](templates/package.md) — the **residual** owner of `packages/{name}/**`: its manifest, build, entry, layout and every behavior no system or feature doc claims at higher specificity |
|
|
64
|
+
| Document one system spanning packages (`docs/architecture/systems/{system}.md`) | [`templates/system.md`](templates/system.md) — one machine end to end; its `owns:` names the machine's subtrees, narrower than the package glob, so it wins by specificity. Not a screen — a system doc written as a giant feature doc is the mistake it prevents |
|
|
63
65
|
| Create/update changelog entry | [`templates/changelog-entry.md`](templates/changelog-entry.md) (record — one per unit of work; carries the refinement fields, frozen inside its commit, distilled by the `retro` agent) |
|
|
64
66
|
| Create/update plan | [`templates/plan.md`](templates/plan.md) (record) |
|
|
65
67
|
| Record/revise architecture decision | [`templates/architecture-decision.md`](templates/architecture-decision.md) — one AD per subject; revisions update it in place |
|
|
@@ -82,7 +84,7 @@ The two guarantees are complementary and both break on mixing: a reader holding
|
|
|
82
84
|
| Add to protected files | [`templates/protected-files.md`](templates/protected-files.md) |
|
|
83
85
|
| Create/update root README | [`templates/root-readme.md`](templates/root-readme.md) |
|
|
84
86
|
| Scaffold a standard doc (docs index, `structure.md`, CHANGELOG, backlog, memory) | [`templates/Readme.md`](templates/Readme.md) — the index names each scaffold file |
|
|
85
|
-
| Derive/update
|
|
87
|
+
| Derive/update an owning doc's `owns:` (feature, package, system) | [`references/frontmatter.md`](references/frontmatter.md) |
|
|
86
88
|
| Update existing doc | § Editing an existing doc (below) |
|
|
87
89
|
| Instruction file (`CLAUDE.md`, a rule, an agent, skill prose) | [`references/instruction-style.md`](references/instruction-style.md) — not these templates; every-load prose stays lean, and the present-tense rule still applies |
|
|
88
90
|
| Author or restructure a **skill** (fit, triggers, shape, anatomy, shipping) | The skill-authoring field guide — the `etna` repo (`nurixlabs/etna`), folder `docs/playbooks/skills/`, chapters `00`–`10`. **No etna checkout available ⇒ ask the user for access to the etna repo, or proceed from first principles** with [`references/instruction-style.md`](references/instruction-style.md) governing the prose. This row is the only place the playbook is referenced |
|
|
@@ -105,6 +107,8 @@ The two guarantees are complementary and both break on mixing: a reader holding
|
|
|
105
107
|
| Document | Nature | Name | Lifecycle |
|
|
106
108
|
| --- | --- | --- | --- |
|
|
107
109
|
| Feature (LLD) | Truth | `kebab-case.md` | Living; `status:` `draft` → `active` → `deprecated` |
|
|
110
|
+
| Package | Truth | `{package-folder}.md` | Living; `status:` as the feature doc |
|
|
111
|
+
| System | Truth | `{machine}.md` | Living; `status:` as the feature doc |
|
|
108
112
|
| Architecture Decision | Truth | `AD-NNN-kebab-case.md` | Living; `Proposed` → `Accepted` → `Deprecated`; revisions rewrite the body + append to its Decision Changelog |
|
|
109
113
|
| Steering docs (overview, domain model, glossary, non-goals, security, testing strategy) | Truth | fixed names | Living |
|
|
110
114
|
| API reference / environment registry | Truth | fixed names | Living |
|
|
@@ -125,8 +129,11 @@ doc, plus the genre's own keys beside it; a document without its frontmatter fai
|
|
|
125
129
|
contract and is not done. **There is no frontmatter-less genre.** A feature or UX doc carries the
|
|
126
130
|
full set — `owns:` (required), `module:` (optional), `keywords:` (required), `status:` (required),
|
|
127
131
|
`applies:` (the architecture docs it obeys; omitted when none), optional `last-reviewed:`. A
|
|
128
|
-
|
|
129
|
-
|
|
132
|
+
**package** doc (`docs/architecture/packages/`) and a **system** doc (`docs/architecture/systems/`)
|
|
133
|
+
carry the same set minus `module:` — a package is already a bounded unit and a machine spanning
|
|
134
|
+
packages belongs to no single context. A behavioral checklist (`docs/tests/`) carries
|
|
135
|
+
`type: checklist` and `verifies:` — the doc whose promises it proves. An acceptance-criteria doc
|
|
136
|
+
(`docs/acceptance/`) carries `type:` and `status:`.
|
|
130
137
|
|
|
131
138
|
**The frontmatter is where the docs tree's edges live**, and a doc that declares none is a node
|
|
132
139
|
nothing can reach: no change implicates it, so it rots without the rot being detectable. Deriving
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Frontmatter Derivation — the doc graph
|
|
2
2
|
|
|
3
|
-
Load when: creating a doc that declares an edge (a feature doc, a UX doc, a behavioral checklist), updating one whose scope moved, or running a baseline/reconcile pass over many docs.
|
|
3
|
+
Load when: creating a doc that declares an edge (a feature, package or system doc, a UX doc, a behavioral checklist), updating one whose scope moved, or running a baseline/reconcile pass over many docs.
|
|
4
4
|
|
|
5
5
|
**A docs tree is a graph, and every edge in it is declared in frontmatter.** Two kinds of edge exist: **doc → code** (`owns:`), and **doc → doc** (`verifies:`, `applies:`). The SessionStart hook reads them all and injects the generated indexes; there is no hand-maintained map of either kind.
|
|
6
6
|
|
|
@@ -14,13 +14,25 @@ The consuming end is also the end that **knows**. A checklist author knows what
|
|
|
14
14
|
|
|
15
15
|
## When to run
|
|
16
16
|
|
|
17
|
-
- **Baseline / reconcile** — for every
|
|
18
|
-
- **New
|
|
17
|
+
- **Baseline / reconcile** — for every owning doc whose code paths aren't yet declared in its `owns:` frontmatter (the `maintenance` agent (baseline mode) calls this)
|
|
18
|
+
- **New owning doc** — when creating one via the [Feature](../templates/feature.md), [Package](../templates/package.md) or [System](../templates/system.md) template, project its `owns:` globs
|
|
19
19
|
- **Doc update** — when the documented scope changes, update the doc's `owns:` globs
|
|
20
20
|
|
|
21
|
+
## Which genres own code
|
|
22
|
+
|
|
23
|
+
**Three do, and no others: `feature`, `package`, `system`.** They layer by glob specificity over the same tree, which is what lets a repo be documented at three altitudes without subtracting anything by hand:
|
|
24
|
+
|
|
25
|
+
| Genre | Claims | Wins because |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| `package` (`docs/architecture/packages/`) | `packages/{name}/**` — the whole tree, one glob | Nothing; it is the **residual** owner and loses every narrower claim, keeping what no one else takes |
|
|
28
|
+
| `system` (`docs/architecture/systems/`) | the subtrees and exact files one machine is made of | Its globs are narrower than the package glob — specificity |
|
|
29
|
+
| `feature` (`docs/features/`) | one capability's or one surface's globs | Same — specificity over the package glob |
|
|
30
|
+
|
|
31
|
+
Every other genre owns no code and carries no `owns:`. That includes the cross-cutting doctrine in `docs/architecture/` (and `docs/architecture/common/`), which describes patterns rather than regions — a surface declares it is bound by one through `applies:`, and the doctrine never claims the surface back. Checklists, acceptance docs, ADs, operations procedures, research and brainstorm records own nothing.
|
|
32
|
+
|
|
21
33
|
## How to derive the frontmatter
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
An owning doc's ownership frontmatter is `owns:` (required), `module:` (optional — feature docs only; a package is already a bounded unit and a system spans contexts by definition), `keywords:` (required), and `status:` (required). An optional `last-reviewed:` date (stamped whenever the doc is substantively reviewed or updated) gives the `maintenance` agent a staleness signal to rank its audits by — it is tooling metadata, never narrated in the body. `type:` (required — the genre, enum = the template names) and `rev:` (generator-written generation stamp) are the same class: external tooling reads them, sessions **preserve them on edits** and never narrate or inject them. The `maintenance` agent stamps `type:` on docs that predate the key. `slug:` completes the set on generated docs: the identity key, minted once at creation and never re-derived from a display name — in the repo the filename is the slug, and the `maintenance` agent re-aligns the two when they diverge.
|
|
24
36
|
|
|
25
37
|
**1. `owns:`** (the path scope)
|
|
26
38
|
|
|
@@ -35,15 +47,17 @@ Prefer one region glob over many file globs. Ten paths say nothing about which i
|
|
|
35
47
|
| A feature's own directory | `src/feature/**` |
|
|
36
48
|
| A feature split across two directories | One glob each |
|
|
37
49
|
| A single entry-point file, and nothing beside it | The exact file path |
|
|
50
|
+
| A whole workspace package, residually | `packages/{name}/**` — one glob, on the package doc |
|
|
51
|
+
| A machine spanning packages | One glob or exact path per subtree it is made of, on the system doc |
|
|
38
52
|
| A shared file several features import | **Nothing — leave it unowned** |
|
|
39
53
|
|
|
40
54
|
**An unowned shared file is the system working, not a gap.** A shared module's authority is the contract it exposes, not any one consumer, so a library-references file or a common utility legitimately has no owning doc. Claiming it would make the index bigger and the routing less certain at once.
|
|
41
55
|
|
|
42
56
|
Glob each pattern against the codebase. At `status: active`, drop any that match nothing; at `status: draft`, projected globs may match nothing yet — confirm them against real code when the doc flips to `active`.
|
|
43
57
|
|
|
44
|
-
**2. `module:`**
|
|
58
|
+
**2. `module:`** (feature docs only)
|
|
45
59
|
|
|
46
|
-
The single bounded-context label this feature belongs to, or omit it for a cross-cutting feature that belongs to no single context. A module is a label, not a folder — features sharing a `module:` value are what ties that module to code (a filter over the generated ownership index).
|
|
60
|
+
The single bounded-context label this feature belongs to, or omit it for a cross-cutting feature that belongs to no single context. A module is a label, not a folder — features sharing a `module:` value are what ties that module to code (a filter over the generated ownership index). Package and system docs omit it entirely.
|
|
47
61
|
|
|
48
62
|
**3. `keywords:` & `status:`**
|
|
49
63
|
|
|
@@ -67,7 +81,7 @@ That edge has a property the others lack, and it is worth naming: **a `Pinned:`
|
|
|
67
81
|
|
|
68
82
|
## Rules
|
|
69
83
|
|
|
70
|
-
- One canonical doc per path. A doc's `owns:` globs must not overlap another doc's at equal specificity — the narrower glob wins; equal-specificity overlap is an ownership conflict to resolve.
|
|
84
|
+
- One canonical doc per path. A doc's `owns:` globs must not overlap another doc's at equal specificity — the narrower glob wins; equal-specificity overlap is an ownership conflict to resolve. Overlap at **unequal** specificity is the layering working as designed: a package doc's `packages/{name}/**` is meant to be outranked, and what survives every narrower claim is exactly what that doc describes.
|
|
71
85
|
- Use glob patterns for directories, explicit paths for individual files. At `active`, never list paths that don't exist in the codebase; a `draft`'s projected paths are confirmed at `active`.
|
|
72
86
|
- When a doc covers a deep feature with sub-specs (e.g., `editor/`), each sub-spec owns its own globs — don't duplicate a path across siblings.
|
|
73
87
|
- For baseline/reconcile with many docs, use parallel sub-agents to read source and propose `owns:` globs.
|
|
@@ -4,7 +4,7 @@ The shared style for every file an agent obeys — a `CLAUDE.md`, a rule, an age
|
|
|
4
4
|
|
|
5
5
|
## The test for every line
|
|
6
6
|
|
|
7
|
-
An instruction file is paid for whenever it loads, scaled by its load class: `CLAUDE.md` and rules load on **every turn of every session** — the most expensive prose in a repo per word; an agent definition loads per dispatch, a skill per invocation. The bar is not "is this true?" but **"must this be in front of the agent whenever this file loads?"** Measure weight in tokens, not lines; trim biggest-first on every edit.
|
|
7
|
+
An instruction file is paid for whenever it loads, scaled by its load class: `CLAUDE.md` and unscoped rules load on **every turn of every session** — the most expensive prose in a repo per word; a `paths:`-scoped rule loads on those same terms from its first matching file read on; an agent definition loads per dispatch, a skill per invocation. The bar is not "is this true?" but **"must this be in front of the agent whenever this file loads?"** Measure weight in tokens, not lines; trim biggest-first on every edit.
|
|
8
8
|
|
|
9
9
|
**Rewriting, the test is armor vs. narration.** A reason clause either blocks a move an agent would otherwise make — the failure is counter-intuitive, the wrong fix looks correct, or the rule reads as arbitrary tidiness and gets "improved away" — or it explains the self-evident. Ask of each: *does the forbidden move look correct to an agent mid-task?* Keep the first kind, cut the second. **A constraint may lose its explanation; it may never lose its existence** — compression is fewer words for the same constraints, so a passage that cannot shrink without dropping one stays long.
|
|
10
10
|
|
|
@@ -5,6 +5,8 @@ One file per template. Every template is either a **truth document** (states cur
|
|
|
5
5
|
| Template | File | Nature |
|
|
6
6
|
| --- | --- | --- |
|
|
7
7
|
| Feature (LLD) | [feature.md](feature.md) | Truth |
|
|
8
|
+
| Package (one workspace package) | [package.md](package.md) | Truth |
|
|
9
|
+
| System (one machine spanning packages) | [system.md](system.md) | Truth |
|
|
8
10
|
| Architecture Decision (AD) | [architecture-decision.md](architecture-decision.md) | Truth (Decision Changelog tail sanctioned) |
|
|
9
11
|
| System overview | [system-overview.md](system-overview.md) | Truth |
|
|
10
12
|
| Domain model | [domain-model.md](domain-model.md) | Truth |
|
|
@@ -35,4 +37,4 @@ One file per template. Every template is either a **truth document** (states cur
|
|
|
35
37
|
|
|
36
38
|
**Baseline scaffolds** — files that must exist after every baseline; the `maintenance` agent (baseline mode) creates any that are missing: [docs-readme.md](docs-readme.md), [structure.md](structure.md), [changelog-index.md](changelog-index.md), [backlog.md](backlog.md), [memory-main.md](memory-main.md), and [root-readme.md](root-readme.md) (never overwriting an existing root README).
|
|
37
39
|
|
|
38
|
-
**Ownership.**
|
|
40
|
+
**Ownership.** Three genres carry code — **feature**, **package**, **system** — and each is the canonical reference for the code paths in its **`owns:` frontmatter**, the only ownership key. To find what a doc owns, read its `owns:` globs; to find the doc for a path, match the path against the injected ownership index (the union of every owning doc's globs), narrower glob wins. That is what layers them: a package doc claims `packages/{name}/**` and is the tree's **residual** owner, while a system doc claims the narrower subtrees of one machine and a feature doc claims one surface — both win those paths by specificity, and the package doc keeps everything neither claims. Two docs may never claim a path at **equal** specificity. Every other genre here owns no code, including the cross-cutting doctrine in `docs/architecture/common/`. See [`../references/frontmatter.md`](../references/frontmatter.md) for turning code into `owns:` globs.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Package Documentation (docs/architecture/packages/{package}.md)
|
|
2
|
+
|
|
3
|
+
Truth document. Use for: **one workspace package — what it is and how it behaves**. One file per package, named for the package's folder (`packages/cli/` → `docs/architecture/packages/cli.md`). It states the package's own machinery: its manifest and published identity, its build and entry point, its source layout, and every behavior of its tree that no system or feature document claims.
|
|
4
|
+
|
|
5
|
+
**A package document is the residual owner of its own tree.** Its `owns:` is the whole package glob — `["packages/{name}/**"]` — and that glob is the *broadest* claim anything makes on those paths, so it loses to every narrower one. A system document naming `packages/{name}/src/server/runs/**` wins those files by specificity; a feature document naming `packages/{name}/src/surfaces/apps/**` wins those. What is left after every narrower claim is what this document describes, and it describes that exhaustively. **The residual claim is a promise of completeness, not of scope**: a package document with nothing left after the systems and features have claimed their subtrees is still owed — the manifest, the build, the entry, the layout, the test wiring and the deploy shape are always residual.
|
|
6
|
+
|
|
7
|
+
**Two rules bind the glob.** Neither genre may claim a path another document claims at **equal specificity** — equal-specificity overlap is an ownership conflict to resolve before either document ships, not a duplication to live with. And every glob **matches at least one file on disk** at `status: active`; a projected glob is permitted only at `draft`, and is confirmed when the document flips.
|
|
8
|
+
|
|
9
|
+
**How this differs from its neighbours.** A **feature** document covers one capability or one page a person names, and owns that surface's globs. A **system** document covers one machine that spans packages, and owns the subtrees that machine is made of. A **package** document covers one unit of distribution — a manifest, a build, a binary or a bundle — and owns what is left of it. The unit is the package boundary, so if the subject you are writing about does not stop at a `package.json`, it is not this genre. Route it: one screen → feature; one machine crossing two packages → [system](system.md).
|
|
10
|
+
|
|
11
|
+
**Required vs optional.** Required in every package document: the **frontmatter**, the **opening** (title, identity line, what the package is), the **ownership boundary** (what this document does not describe and which document does), at least one **behavior section**, **Files**, and **Related**. Everything else is optional — include a section only when the package has something real to put in it. A package with no test wiring of its own gets no verification section; a package that nothing else in the repo consumes says so in one line rather than growing a Consumers section. A short accurate document beats a full skeleton with empty headings.
|
|
12
|
+
|
|
13
|
+
Package documents run long because they are exhaustive over their residue — roughly 150 to 600 lines is the working range, and the top of that range is a package holding an application, not a document that has lost discipline.
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
---
|
|
17
|
+
owns: ["packages/{name}/**"] # required — the whole package tree, one glob; the residual claim
|
|
18
|
+
keywords: [package-name, binary, build, entry] # required — discovery terms: the npm name, the binary, the surfaces
|
|
19
|
+
status: active # required — active | draft | deprecated
|
|
20
|
+
applies: [docs/architecture/common/api.md] # optional — cross-cutting doctrine this package is BOUND by (obligation, not a bibliography); omit when none
|
|
21
|
+
type: package # required tooling key — genre stamp; preserve on edits, never narrate
|
|
22
|
+
---
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> A package document carries no `module:` — a package is already a bounded unit, and a second label
|
|
26
|
+
> for the same boundary is a thing to keep in sync for nothing. `last-reviewed:` is optional and,
|
|
27
|
+
> like `type:`, is tooling metadata: preserve it, never narrate it.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
# {package-path} — {the one-line role}
|
|
31
|
+
|
|
32
|
+
**Package:** `@scope/name` · **Ships as:** {a public npm package with one binary `x` / a private
|
|
33
|
+
source-first workspace package / a Worker on {domain}} · **Source:** `packages/{name}`
|
|
34
|
+
|
|
35
|
+
{One paragraph: what this package is and the job it does in the product. Name what it holds that
|
|
36
|
+
nothing else does, and what it deliberately does not hold. Written for someone who has not opened
|
|
37
|
+
the folder.}
|
|
38
|
+
|
|
39
|
+
{One paragraph: the ownership boundary. This document is the residual owner of `packages/{name}/**`;
|
|
40
|
+
these subtrees are claimed at higher specificity by these documents and are pointed at, not
|
|
41
|
+
restated. Where the routing is more than a sentence, make it the table below instead.}
|
|
42
|
+
|
|
43
|
+
{Optional, and worth its line whenever the body names paths: "Paths below are relative to
|
|
44
|
+
`packages/{name}/` unless they name another package."}
|
|
45
|
+
|
|
46
|
+
## What is built and what is not
|
|
47
|
+
|
|
48
|
+
{Optional — required in substance whenever some of the tree does not run. State plainly which
|
|
49
|
+
halves exist and which do not, with the source evidence for each claim, and say what a reader who
|
|
50
|
+
runs the unbuilt half will actually observe. A package whose subject as a whole does not run is
|
|
51
|
+
`status: draft` and says so here; a scoped gap in a working package stays `active` and takes an
|
|
52
|
+
UNBUILT marker at the section that describes the gap.}
|
|
53
|
+
|
|
54
|
+
## Ownership — packages/{name}
|
|
55
|
+
|
|
56
|
+
{Optional — use the table form when more than two documents claim subtrees of this package.}
|
|
57
|
+
|
|
58
|
+
**The package glob is the residual claim.** A system or feature document's `owns:` is more specific
|
|
59
|
+
and wins by specificity, so this document describes only what none of them claims, and points to
|
|
60
|
+
each for the rest.
|
|
61
|
+
|
|
62
|
+
| Path | What it is |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `src/index.ts` | {the residue: the entry, the dispatch, the layout} |
|
|
65
|
+
|
|
66
|
+
| Subtree | Stated and owned by |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `src/{machine}/**` | [{system}](../systems/{system}.md) |
|
|
69
|
+
| `src/surfaces/{x}/**` | [{feature}](../../features/{feature}.md) |
|
|
70
|
+
|
|
71
|
+
## {Behavior sections — named for this package's own machinery}
|
|
72
|
+
|
|
73
|
+
{The body. There is no fixed section list, and inventing one would be a lie: what a CLI owes
|
|
74
|
+
(commands, the store, the on-ramp) and what a UI package owes (the host port, the frame, the route
|
|
75
|
+
zones, the component pool) share no headings. Name each section for the thing it describes and
|
|
76
|
+
order them the way a reader meets them — identity and build first, then the surface, then what it
|
|
77
|
+
writes, then how it is verified. Prefer a table wherever the content is a roster (commands, routes,
|
|
78
|
+
exports, bindings, environment variables); prefer prose wherever the content is a rule and its
|
|
79
|
+
reason. State mechanisms and contracts, never pasted implementations.}
|
|
80
|
+
|
|
81
|
+
## Tests
|
|
82
|
+
|
|
83
|
+
{Optional. What proves this package, and what each suite pins — a `Suite | Pins` table, or the
|
|
84
|
+
commands a reader runs. A package whose verification is a build check names the check instead
|
|
85
|
+
(`## Checks`, `## Build and test`); a package with no suite of its own says so in one line in the
|
|
86
|
+
build section and carries no heading here.}
|
|
87
|
+
|
|
88
|
+
## Files
|
|
89
|
+
|
|
90
|
+
| Path | Role |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `packages/{name}/package.json` | {the manifest: the bin, the exports, the scripts} |
|
|
93
|
+
| `packages/{name}/src/index.ts` | {the entry} |
|
|
94
|
+
|
|
95
|
+
{One row or bullet per file this document owns, with what it holds — the roster a reader uses to go
|
|
96
|
+
from the document to the code. Close with the inverse where it helps: "Read here, owned elsewhere:"
|
|
97
|
+
naming the paths the body describes but another document claims.}
|
|
98
|
+
|
|
99
|
+
## Related
|
|
100
|
+
|
|
101
|
+
- [`{system}.md`](../systems/{system}.md) — {why a reader of this document would open that one}
|
|
102
|
+
- [`{other-package}.md`]({other-package}.md) — {the seam between the two}
|
|
103
|
+
- [`{doctrine}.md`](../common/{doctrine}.md) — {the cross-cutting contract this package answers to}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### The identity line
|
|
107
|
+
|
|
108
|
+
The bolded `·`-separated run under the title is what a reader checks first, and it answers three
|
|
109
|
+
questions no body section should have to: **what is this called** on the registry or in the
|
|
110
|
+
workspace, **how does it ship** (a public tarball, a private source-first package, a deployed
|
|
111
|
+
Worker, a signed installer), and **where is its source**. A package that ships two artifacts names
|
|
112
|
+
both. Add a `**Crate:**` or `**Deploys as:**` term where the package has one; drop any term that
|
|
113
|
+
would restate the title.
|
|
114
|
+
|
|
115
|
+
### Marking what is decided and unbuilt
|
|
116
|
+
|
|
117
|
+
Where the package holds code for a design that is decided and not built — or a surface whose other
|
|
118
|
+
half does not exist — wrap the prose in a marker so the gap is machine-findable and its evidence
|
|
119
|
+
travels with it:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
<!-- UNBUILT:START — {what is decided, where it is decided (an AD or an owner ruling), and the
|
|
123
|
+
source evidence that it is not built: the file that does not exist, the route that is not mounted,
|
|
124
|
+
the binding that is not declared} -->
|
|
125
|
+
{the prose describing the unbuilt half}
|
|
126
|
+
<!-- UNBUILT:END -->
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**A design that was decided *against* carries no marker.** A marker means the work is owed and
|
|
130
|
+
absent; using one for a rejected design turns the register of debt into a register of opinions.
|
|
131
|
+
Say in prose that it is not built and not owed, and name the decision that settled it.
|
|
@@ -8,13 +8,13 @@ The `boundary` skill parses the **Proposed Changes** sections to check boundary
|
|
|
8
8
|
|
|
9
9
|
**Doc Obligations is the one ledger the plan's author writes.** Deriving tests from behavior is judgment, which is why `testing` owns that section and it is marked never-hand-author; resolving Proposed Changes' file paths to the feature docs that own them is a **lookup** against the ownership index already injected at session start. Different work, different rule — fill it yourself, while the touch list is fresh.
|
|
10
10
|
|
|
11
|
-
The **`Gates:`** line carries
|
|
11
|
+
The **`Gates:`** line carries two machine-read segments. The compliance hook greps **`Boundary:`** — implementation edits inside the plan's scope are blocked while it reads `Boundary: pending`; the `boundary` skill rewrites it to `Boundary: passed YYYY-MM-DD` or `Boundary: blocked YYYY-MM-DD`. The other two plan gates are enforced by **section presence**: a plan with no `## Test Obligations` or no `## Doc Obligations` section blocks the same edits — the sections are the artifacts, and the gate derives from them, not from a claim about them. **`Polygraph:`** is the completion gate's trace: it reads `Polygraph: pending` from the first draft until the `polygraph` skill (gate mode) certifies the plan's documents — every Doc Obligations row discharged **updated** plus every `Spec:` doc — and rewrites it to `Polygraph: passed YYYY-MM-DD (N docs · N claims · N corrected · N registered)`. A plan that touched no document writes `Polygraph: owes nothing — {reason}` by hand; nothing else on this segment is ever hand-written. A Complete plan still reading `pending` raises a `needs-session:` queue row and fails CI. Never hand-edit the line to skip a gate.
|
|
12
12
|
|
|
13
13
|
```
|
|
14
14
|
# {Feature Name}
|
|
15
15
|
|
|
16
16
|
**Status:** Draft | In Progress | Complete | Abandoned
|
|
17
|
-
**Gates:** Boundary: pending
|
|
17
|
+
**Gates:** Boundary: pending · Polygraph: pending
|
|
18
18
|
**Spec:** [docs/features/{feature}.md](../../features/{feature}.md) <!-- one line per feature doc this plan implements against; a plan spanning three features names three -->
|
|
19
19
|
**Started:** YYYY-MM-DD
|
|
20
20
|
**Completed:** YYYY-MM-DD <!-- fill in the SAME edit that flips Status to Complete — along with every ledger Outcome cell: the flip freezes the file instantly, so anything left for a second edit is unreachable (the frozen-record gate rightly blocks it) -->
|
|
@@ -18,8 +18,10 @@ Each topic has one canonical location. Read from here, not from plans or changel
|
|
|
18
18
|
|
|
19
19
|
| Topic | Authority | Key Files |
|
|
20
20
|
| ----- | --------- | --------- |
|
|
21
|
-
| Code-path ownership (path → doc + module) |
|
|
21
|
+
| Code-path ownership (path → doc + module) | `owns:` frontmatter on features, packages and systems | the generated ownership index |
|
|
22
22
|
| System overview | `docs/architecture/` | system-design.md |
|
|
23
|
+
| One workspace package | `docs/architecture/packages/` | {package-folder}.md |
|
|
24
|
+
| One machine spanning packages | `docs/architecture/systems/` | {machine}.md |
|
|
23
25
|
| Steering (domain model, glossary, non-goals) | `docs/architecture/` | domain-model.md, glossary.md, non-goals.md |
|
|
24
26
|
| Architecture decisions (ADs) | `docs/architecture/decisions/` | AD-NNN-*.md |
|
|
25
27
|
| API surface | `docs/architecture/` | api.md |
|
|
@@ -45,8 +47,12 @@ Every doc is a **truth document** (states current absolutes, topic-named, update
|
|
|
45
47
|
|
|
46
48
|
## Folder Rules
|
|
47
49
|
|
|
48
|
-
- `docs/features/`: Living reference for implemented features. Each feature doc **carries** the `owns:` / `module:` / `keywords:` / `status:` frontmatter — `owns:` declares the code-path globs it owns (the only ownership key), `module:` is an optional bounded-context label, `keywords:` are discovery terms, `status:` is `active`/`draft`/`deprecated`. The SessionStart hook unions every
|
|
49
|
-
- `docs/architecture/`: The truth layer for system-wide shape: overview, domain model, glossary, non-goals, API surface, security, and `decisions/` (ADs — one per subject, updated in place, journey in each AD's Decision Changelog). Update when cross-cutting reality changes.
|
|
50
|
+
- `docs/features/`: Living reference for implemented features. Each feature doc **carries** the `owns:` / `module:` / `keywords:` / `status:` frontmatter — `owns:` declares the code-path globs it owns (the only ownership key), `module:` is an optional bounded-context label, `keywords:` are discovery terms, `status:` is `active`/`draft`/`deprecated`. The SessionStart hook unions every owning doc's `owns:` globs into the injected ownership index.
|
|
51
|
+
- `docs/architecture/`: The truth layer for system-wide shape: overview, domain model, glossary, non-goals, API surface, security, and `decisions/` (ADs — one per subject, updated in place, journey in each AD's Decision Changelog). Update when cross-cutting reality changes. The cross-cutting doctrine directly under `architecture/` (and under `architecture/common/` where a repo separates it) **owns no code** — it describes patterns every surface obeys, and `applies:` is how a surface declares it is bound.
|
|
52
|
+
- `docs/architecture/packages/`: One doc per workspace package (`{package-folder}.md`), present in a workspace repo. Each is the **residual owner** of its tree — `owns: ["packages/{name}/**"]` — describing the manifest, build, entry, layout and every behavior no system or feature doc claims at higher specificity. Template: the `documentation` skill's `templates/package.md`.
|
|
53
|
+
- `docs/architecture/systems/`: One doc per machine that spans packages (`{machine}.md`) — the walk, the egress, the ingest. Its `owns:` names the machine's own subtrees and files, narrower than the package glob, so it wins those paths by specificity and the package doc keeps the rest. Not a surface doc: what a person sees stays in `docs/features/`. Template: the `documentation` skill's `templates/system.md`.
|
|
54
|
+
|
|
55
|
+
**Three genres carry `owns:`** — features, packages, systems — and no others. Their globs layer by specificity (package `**` residual, system and feature narrower), and two docs may never claim a path at **equal** specificity: that is an ownership conflict to resolve, not a duplication to live with. Every glob matches at least one file on disk at `status: active`.
|
|
50
56
|
- `docs/operations/`: How-to procedures for setup, development, and deployment, plus the environment registry. Update when workflows change. One-shot migrations do NOT live here — they are records (`docs/history/migrations/`).
|
|
51
57
|
- `docs/tests/`: Testing strategy and test docs. Present when the repo has a test posture worth documenting.
|
|
52
58
|
- `docs/history/plans/`: Records. Must have `Status:` field. Never modify after completion.
|