@nurix/etna 0.20.28 → 0.20.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/CLAUDE.md +5 -5
- package/data/harnesses/code/agents/bookkeeper.md +2 -2
- package/data/harnesses/code/agents/change-review.md +3 -3
- package/data/harnesses/code/agents/lld-mode.md +3 -3
- package/data/harnesses/code/agents/maintenance.md +22 -19
- package/data/harnesses/code/agents/polygraph-finder.md +3 -3
- package/data/harnesses/code/agents/polygraph.md +4 -4
- package/data/harnesses/code/agents/retro.md +1 -1
- package/data/harnesses/code/codex/agents/bookkeeper.toml +7 -3
- package/data/harnesses/code/codex/agents/change-review.toml +3 -3
- package/data/harnesses/code/codex/agents/lld-mode.toml +3 -3
- package/data/harnesses/code/codex/agents/maintenance.toml +60 -37
- package/data/harnesses/code/codex/agents/polygraph-finder.toml +6 -2
- package/data/harnesses/code/codex/agents/polygraph.toml +4 -4
- package/data/harnesses/code/codex/agents/retro.toml +1 -1
- package/data/harnesses/code/docs/structure.md +15 -15
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/hooks/lib/ownership.mjs +12 -13
- package/data/harnesses/code/hooks/lib/sweep.mjs +1 -1
- package/data/harnesses/code/skills/documentation/SKILL.md +17 -27
- package/data/harnesses/code/skills/documentation/references/frontmatter.md +56 -62
- package/data/harnesses/code/skills/documentation/references/style-guide.md +2 -2
- package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +1 -1
- package/data/harnesses/code/skills/documentation/templates/Readme.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/docs-readme.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/doctrine.md +2 -2
- package/data/harnesses/code/skills/documentation/templates/feature.md +3 -4
- package/data/harnesses/code/skills/documentation/templates/package.md +2 -3
- package/data/harnesses/code/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/system.md +2 -4
- package/data/harnesses/code/skills/documentation/templates/ux-behavior.md +4 -4
- package/data/harnesses/delivery/agents/lld-mode.md +3 -3
- package/data/harnesses/delivery/agents/maintenance.md +22 -19
- package/data/harnesses/delivery/agents/retro.md +1 -1
- package/data/harnesses/delivery/codex/agents/lld-mode.toml +3 -3
- package/data/harnesses/delivery/codex/agents/maintenance.toml +60 -37
- package/data/harnesses/delivery/codex/agents/retro.toml +1 -1
- package/data/harnesses/delivery/harness.json +2 -2
- package/data/harnesses/delivery/skills/documentation/SKILL.md +17 -27
- package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +56 -62
- package/data/harnesses/delivery/skills/documentation/references/style-guide.md +2 -2
- package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/Readme.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/docs-readme.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/doctrine.md +2 -2
- package/data/harnesses/delivery/skills/documentation/templates/feature.md +3 -4
- package/data/harnesses/delivery/skills/documentation/templates/package.md +2 -3
- package/data/harnesses/delivery/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/system.md +2 -4
- package/data/harnesses/delivery/skills/documentation/templates/ux-behavior.md +4 -4
- package/data/harnesses/etna/agents/change-review.md +3 -3
- package/data/harnesses/etna/agents/lld-mode.md +3 -3
- package/data/harnesses/etna/agents/maintenance.md +22 -19
- package/data/harnesses/etna/agents/retro.md +1 -1
- package/data/harnesses/etna/codex/agents/change-review.toml +3 -3
- package/data/harnesses/etna/codex/agents/lld-mode.toml +3 -3
- package/data/harnesses/etna/codex/agents/maintenance.toml +60 -37
- package/data/harnesses/etna/codex/agents/retro.toml +1 -1
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/skills/documentation/SKILL.md +17 -27
- package/data/harnesses/etna/skills/documentation/references/frontmatter.md +56 -62
- package/data/harnesses/etna/skills/documentation/references/style-guide.md +2 -2
- package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +1 -1
- package/data/harnesses/etna/skills/documentation/templates/Readme.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/docs-readme.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/doctrine.md +2 -2
- package/data/harnesses/etna/skills/documentation/templates/feature.md +3 -4
- package/data/harnesses/etna/skills/documentation/templates/package.md +2 -3
- package/data/harnesses/etna/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/system.md +2 -4
- package/data/harnesses/etna/skills/documentation/templates/ux-behavior.md +4 -4
- package/package.json +2 -2
|
@@ -13,7 +13,7 @@ Document contracts and architecture, not implementation details. Signatures and
|
|
|
13
13
|
|
|
14
14
|
## Where a file's documentation lives
|
|
15
15
|
|
|
16
|
-
**In `docs/`, never in the file's comments** — and this skill is the half of that rule that makes it safe. A source file carries a repo-relative path header of two or three present-tense sentences and one third-person sentence above each non-obvious function; the shape is the harness's `CLAUDE.md` § File Structure, which is canonical and is not restated here. What lands **here** is everything that shape displaces: design rationale, the alternatives weighed, the measurement behind a constant, the reason an obvious-looking change is wrong.
|
|
16
|
+
**In `docs/`, never in the file's comments** — and this skill is the half of that rule that makes it safe. A source file carries a repo-relative path header of two or three present-tense sentences and one third-person sentence above each non-obvious function; the shape is the harness's `CLAUDE.md` § File Structure, which is canonical and is not restated here. That header's `docs/…` path is also the file's edge into the knowledge graph: the header pointer names the document that speaks for the file, which is how ownership is computed ([`references/frontmatter.md`](references/frontmatter.md)). What lands **here** is everything that shape displaces: design rationale, the alternatives weighed, the measurement behind a constant, the reason an obvious-looking change is wrong.
|
|
17
17
|
|
|
18
18
|
**Cutting a comment is a move, not a deletion.** A claim taken out of a source file is owed one of two things before the cut is done, and the choice is per claim, not per file:
|
|
19
19
|
|
|
@@ -92,8 +92,8 @@ The two guarantees are complementary and both break on mixing: a reader holding
|
|
|
92
92
|
| Document feature/module (LLD) | [`templates/feature.md`](templates/feature.md) |
|
|
93
93
|
| Document data flow / state / Redux | [`templates/feature.md`](templates/feature.md) — they are sections of the feature doc |
|
|
94
94
|
| 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 |
|
|
95
|
-
| Document one concern the platform handles everywhere (`docs/architecture/common/{concern}.md`) — authentication, storage, the API protocol | [`templates/doctrine.md`](templates/doctrine.md) — the design and its principles, no mechanism; owns no code, and surfaces
|
|
96
|
-
| Document one system spanning packages (`docs/architecture/systems/{system}.md`) | [`templates/system.md`](templates/system.md) — one machine end to end
|
|
95
|
+
| Document one concern the platform handles everywhere (`docs/architecture/common/{concern}.md`) — authentication, storage, the API protocol | [`templates/doctrine.md`](templates/doctrine.md) — the design and its principles, no mechanism; owns no code, and the surfaces bound by it are the ones that design or touch the objects it governs |
|
|
96
|
+
| Document one system spanning packages (`docs/architecture/systems/{system}.md`) | [`templates/system.md`](templates/system.md) — one machine end to end, the objects it owns or moves. Not a screen — a system doc written as a giant feature doc is the mistake it prevents |
|
|
97
97
|
| 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) |
|
|
98
98
|
| Create/update plan | [`templates/plan.md`](templates/plan.md) (record) |
|
|
99
99
|
| Record/revise architecture decision | [`templates/architecture-decision.md`](templates/architecture-decision.md) — one AD per subject; revisions update it in place |
|
|
@@ -118,7 +118,7 @@ The two guarantees are complementary and both break on mixing: a reader holding
|
|
|
118
118
|
| Add to protected files | [`templates/protected-files.md`](templates/protected-files.md) |
|
|
119
119
|
| Create/update root README (outward opening in a user's terms, then orientation and routing for the team and agents — no install, stack, tree or chrome) | [`templates/root-readme.md`](templates/root-readme.md) |
|
|
120
120
|
| Scaffold a standard doc (docs index, CHANGELOG, backlog, memory) | [`templates/Readme.md`](templates/Readme.md) — the index names each scaffold file; `structure.md` is the installer's, never scaffolded |
|
|
121
|
-
| Derive
|
|
121
|
+
| Derive or update a document's frontmatter (`objects:`, the header pointers, the transitional `owns:`) | [`references/frontmatter.md`](references/frontmatter.md) |
|
|
122
122
|
| Update existing doc | § Editing an existing doc (below) |
|
|
123
123
|
| 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 |
|
|
124
124
|
| 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`–`13`. **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 |
|
|
@@ -132,7 +132,7 @@ The two guarantees are complementary and both break on mixing: a reader holding
|
|
|
132
132
|
|
|
133
133
|
1. **Determine the nature first.** A frozen record (a `Status: Complete`/`Abandoned` plan, a changelog entry, an executed runbook) is never edited — write a new dated record that links back.
|
|
134
134
|
2. **Preserve structure, not violations.** Keep the doc's existing section shape, but every section touched sheds history narration, progress markers, and stale tripwires on the way through (§ Present tense, always).
|
|
135
|
-
3. **Stamp and re-derive.** A substantive feature-doc update bumps `last-reviewed:`; if the
|
|
135
|
+
3. **Stamp and re-derive.** A substantive feature-doc update bumps `last-reviewed:`; if the objects it designs changed, update `objects:`, and remove any retired key on the way through ([`references/frontmatter.md`](references/frontmatter.md)).
|
|
136
136
|
|
|
137
137
|
---
|
|
138
138
|
|
|
@@ -157,27 +157,17 @@ The two guarantees are complementary and both break on mixing: a reader holding
|
|
|
157
157
|
|
|
158
158
|
---
|
|
159
159
|
|
|
160
|
-
## Frontmatter
|
|
161
|
-
|
|
162
|
-
**Frontmatter is compulsory on every document this skill authors
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
without claiming it. A behavioral checklist (`docs/tests/`) carries
|
|
172
|
-
`type: checklist` and `verifies:` — the doc whose promises it proves. An acceptance-criteria doc
|
|
173
|
-
(`docs/acceptance/`) carries `type:` and `status:`.
|
|
174
|
-
|
|
175
|
-
**The frontmatter is where the docs tree's edges live**, and a doc that declares none is a node
|
|
176
|
-
nothing can reach: no change implicates it, so it rots without the rot being detectable. Deriving
|
|
177
|
-
every edge — `owns:` from code, `verifies:` and `applies:` from the docs they answer to — is
|
|
178
|
-
[`references/frontmatter.md`](references/frontmatter.md), and it carries the rule the whole graph
|
|
179
|
-
rests on: **declare once at the consuming end, generate every inverse.** Every doc create or
|
|
180
|
-
update, and the `maintenance` agent's baseline pass, route there.
|
|
160
|
+
## Frontmatter
|
|
161
|
+
|
|
162
|
+
**Frontmatter is compulsory on every document this skill authors**: `type:` (the genre) on every document, plus its genre's keys. There is no frontmatter-less genre. The keys follow the knowledge layer (`docs/architecture/systems/knowledge-layer.md` § Documents and code), and this skill stays in step with it:
|
|
163
|
+
|
|
164
|
+
- **`objects:` is a document's edge into the graph:** the product objects it designs, by their register names. Feature, system, package, doctrine and UX documents carry it, with `type:` and `status:`; a feature may add `module:`.
|
|
165
|
+
- **The code's edge is the source file's header pointer:** authored on the code's side, naming the document that holds the file's fuller account. Which document speaks for a path is computed from the pointers and the object closure, never typed into the document.
|
|
166
|
+
- **`owns:` is transitional:** region globs only, kept solely because the harness's ownership scan still reads it, and removed everywhere in the change that switches the scan. Never a list of files.
|
|
167
|
+
- **`applies:` is retired:** a document's governing doctrine is computed through the objects both name, and its Scope section names it for people.
|
|
168
|
+
- **`verifies:` stays on a behavioral checklist:** the document whose promises it proves.
|
|
169
|
+
|
|
170
|
+
Keys by genre, each key's rules, the retired keys, and the one rule the graph rests on (declare once, at the consuming end; generate every inverse) are [`references/frontmatter.md`](references/frontmatter.md). Every document create or update, and the `maintenance` agent's baseline pass, route there.
|
|
181
171
|
|
|
182
172
|
---
|
|
183
173
|
|
|
@@ -188,7 +178,7 @@ update, and the `maintenance` agent's baseline pass, route there.
|
|
|
188
178
|
| `templates/` | One file per template, indexed by [`Readme.md`](templates/Readme.md) — feature (LLD), changelog, plan, AD, steering docs, API reference, environment registry, operations procedure, migration runbook, protected files, READMEs, and the baseline scaffolds |
|
|
189
179
|
| `references/style-guide.md` | The writing-style authority: the eight principles, depth per genre, the outside-in shape and stability test, noun-phrase labels and third-person voice, the owning document's body and footer, the two-natures tense rules, diagrams, headings, reference-document detail |
|
|
190
180
|
| `references/instruction-style.md` | The style for instruction files (`CLAUDE.md`, rules, agents, skill prose) — the every-line test, armor vs. narration, voice, negative space |
|
|
191
|
-
| `references/frontmatter.md` |
|
|
181
|
+
| `references/frontmatter.md` | Frontmatter by genre, in step with the knowledge layer: `objects:`, the header pointer, the transitional `owns:`, the retired `applies:`, `verifies:` |
|
|
192
182
|
| `references/untemplated.md` | How to write a document no template covers (nature, placement, shape) + the co-authoring workflow |
|
|
193
183
|
| `references/examples.md` | Good vs bad documentation examples |
|
|
194
184
|
| `scripts/doc-lint.mjs` | Mechanical checks over written docs (tense narration, dates, stubs, redirects) — run over changed docs before finishing |
|
|
@@ -1,90 +1,84 @@
|
|
|
1
|
-
# Frontmatter
|
|
1
|
+
# Frontmatter — documents in the knowledge graph
|
|
2
2
|
|
|
3
|
-
Load when: creating a
|
|
3
|
+
Load when: creating a document that carries frontmatter, updating one whose subject moved, or running a baseline or reconcile pass over many documents.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This reference states how a document joins the knowledge graph, and it is kept in step with the machine that reads it: the knowledge layer (`docs/architecture/systems/knowledge-layer.md` in a repository that carries it; § Documents and code). Where this reference and that design disagree, the design is the authority and this file is the defect.
|
|
6
6
|
|
|
7
|
-
## The
|
|
7
|
+
## The model
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
A document names the product objects it designs. Every other edge between a document and the code is either authored on the code's side or computed, never typed into the document.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
### Principles
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
1. **Objects as the document's edge:** a document's frontmatter carries `objects:`, the product objects it designs, by their register names. That is its one edge into the graph and its discovery terms.
|
|
14
|
+
2. **The header pointer as the code's edge:** a source file's path header names the document that holds its fuller account (the root instruction file's § File Structure). The pointer is authored on the code's side, a move carries it, and a new file without one is a lint.
|
|
15
|
+
3. **Computed ownership:** the object closure (document → objects → tables → their write, read and refusal sites → files) reaches files nobody listed. Where the header pointer and the closure disagree, the graph prints both. Which document speaks for a path is a query, answered per path on demand.
|
|
16
|
+
4. **Nothing recomputable stored:** a list of files, a list of consumers, or a list of governing documents typed into frontmatter is a copy of something the graph computes. It is stale the day the code moves, and it fails the stability test (`style-guide.md` § Shape — the model first, the detail after).
|
|
17
|
+
5. **One declaration, at the consuming end:** the edges a person must author (`objects:`, `verifies:`) are declared once, by the document that knows. Every inverse is generated, never authored.
|
|
14
18
|
|
|
15
|
-
##
|
|
19
|
+
## Keys by genre
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
| Genre | Keys |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| Feature | `type`, `status`, `objects`, `module` (optional) · transitional `owns` |
|
|
24
|
+
| System | `type`, `status`, `objects` · transitional `owns` |
|
|
25
|
+
| Package | `type`, `status`, `objects` · transitional `owns` |
|
|
26
|
+
| Doctrine | `type`, `status`, `objects` |
|
|
27
|
+
| UX | `type`, `status`, `objects` · transitional `owns` when it speaks for a surface's components |
|
|
28
|
+
| Behavioral checklist | `type: checklist`, `verifies` |
|
|
29
|
+
| Acceptance criteria | `type`, `status` |
|
|
30
|
+
| Every other genre | `type`, plus its template's own keys |
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
Tooling keys sit beside these on any genre: `rev:` (a generator's generation stamp), `slug:` (the identity key on generated documents, minted once, and in the repository the filename), and `last-reviewed:` (stamped on a substantive review, the `maintenance` agent's staleness signal). Sessions preserve them on edits and never narrate or inject them. The `maintenance` agent stamps `type:` where it is missing.
|
|
22
33
|
|
|
23
|
-
|
|
34
|
+
## Keys
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
| --- | --- | --- |
|
|
27
|
-
| `package` (`docs/architecture/packages/`) | `packages/{name}/**` — the whole tree, one glob | Nothing; it is the **residual** owner and loses every narrower claim, keeping what no one else takes |
|
|
28
|
-
| `system` (`docs/architecture/systems/`) | the subtrees and exact files one machine is made of | Its globs are narrower than the package glob — specificity |
|
|
29
|
-
| `feature` (`docs/features/`) | one capability's or one surface's globs | Same — specificity over the package glob |
|
|
30
|
-
|
|
31
|
-
Every other genre owns no code and carries no `owns:`. That includes the cross-cutting doctrine in `docs/architecture/` (and `docs/architecture/common/`), which describes patterns rather than regions — a surface declares it is bound by one through `applies:`, and the doctrine never claims the surface back. Checklists, acceptance docs, ADs, operations procedures, research and brainstorm records own nothing.
|
|
32
|
-
|
|
33
|
-
## How to derive the frontmatter
|
|
34
|
-
|
|
35
|
-
An owning doc's frontmatter is `objects:` (required — the product objects it designs, by their graph names; its discovery terms and its traced edge into the knowledge graph), `owns:` (required until the graph computes ownership from traces), `module:` (optional — feature docs only; a package is already a bounded unit and a system spans contexts by definition), and `status:` (required). An optional `last-reviewed:` date (stamped whenever the doc is substantively reviewed or updated) gives the `maintenance` agent a staleness signal to rank its audits by — it is tooling metadata, never narrated in the body. `type:` (required — the genre, enum = the template names) and `rev:` (generator-written generation stamp) are the same class: external tooling reads them, sessions **preserve them on edits** and never narrate or inject them. The `maintenance` agent stamps `type:` on docs that predate the key. `slug:` completes the set on generated docs: the identity key, minted once at creation and never re-derived from a display name — in the repo the filename is the slug, and the `maintenance` agent re-aligns the two when they diverge.
|
|
36
|
-
|
|
37
|
-
**1. `owns:`** (the path scope)
|
|
36
|
+
### `objects:`
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
- **Values:** the product objects the document designs, by the names the object register uses (`docs/dna/objects/<group>.yaml`, the product-dna skill's). Where a repository has no register yet, the names its product model and its documents already use.
|
|
39
|
+
- **Meaning:** designs, not mentions. A feature that reads a session designs no session; the document that decides what a session is does.
|
|
40
|
+
- **Discovery:** there is no `keywords:` key. An object's name and its register aliases are its discovery terms.
|
|
41
|
+
- **Doctrine:** a doctrine document names the cross-cutting objects it governs (principal, credential, session). The surfaces bound by it are the documents that design or touch the same objects, which the closure computes.
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
### `status:`
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
- **Values:** `active`, `draft` or `deprecated`. It steers a reader toward live documents.
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
| -------------------------------------------- | ------------------------------------ |
|
|
47
|
-
| A feature's own directory | `src/feature/**` |
|
|
48
|
-
| A feature split across two directories | One glob each |
|
|
49
|
-
| A single entry-point file, and nothing beside it | The exact file path |
|
|
50
|
-
| A whole workspace package, residually | `packages/{name}/**` — one glob, on the package doc |
|
|
51
|
-
| A machine spanning packages | One glob or exact path per subtree it is made of, on the system doc |
|
|
52
|
-
| A shared file several features import | **Nothing — leave it unowned** |
|
|
47
|
+
### `module:`
|
|
53
48
|
|
|
54
|
-
**
|
|
49
|
+
- **Values:** one bounded-context label on a feature document, or omitted for a cross-cutting feature. A label, not a folder. Package and system documents omit it.
|
|
55
50
|
|
|
56
|
-
|
|
51
|
+
### `verifies:`
|
|
57
52
|
|
|
58
|
-
**
|
|
53
|
+
- **Values:** on a behavioral checklist, the repo-relative path of the feature or UX document whose promises it proves. One is the norm.
|
|
54
|
+
- **Authorship:** a checklist knows what it proves, and nothing in the code says so. A checklist with no `verifies:` is an orphan: nothing implicates it when its subject changes.
|
|
59
55
|
|
|
60
|
-
|
|
56
|
+
### `owns:` — transitional
|
|
61
57
|
|
|
62
|
-
**
|
|
58
|
+
- **Reason for the key:** the harness's ownership scan (`.claude/hooks/lib/ownership.mjs`) keys on `owns:` until it switches to the header pointers and the object closure, which is the knowledge layer's recorded gap. Until then the key keeps the session's ownership index and the doc-debt detectors working.
|
|
59
|
+
- **Form:** region globs only, one per region the document speaks for: `packages/{name}/**` on a package document, the machine's subtrees on a system document, a feature's own directory on a feature document. Never a list of individual files, and never a shared file several features import.
|
|
60
|
+
- **Growth:** none. A new document carries the region glob its genre needs and nothing more; an edit never adds file-level entries.
|
|
61
|
+
- **Retirement:** removed from every document in the same change that switches the scan, and this section with it.
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
## Retired keys
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
**`applies:`** — on a feature or UX doc, the architecture docs it obeys. Not "docs I read" and not "docs I mention" — **obligation**: a doctrine this surface is bound by, such that a change to the doctrine implicates this doc. The narrow reading is the load-bearing one; a key that collects everything consulted degrades into a bibliography and stops meaning anything. A feature bound by nothing cross-cutting omits the key.
|
|
73
|
-
|
|
74
|
-
Cross-cutting *contracts* remain diagnosed by the `boundary` skill from source, never persisted — `applies:` records which doctrine a surface answers to, not which code it touches.
|
|
65
|
+
| Key | Retired because | What replaces it |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `applies:` | A list of governing documents is a hand-kept copy of a relationship the graph computes through the objects both documents name, and it drifts the way any copy does | The objects the doctrine and the surface share, computed; for people, the document's Scope section names its governing doctrine and what each settles |
|
|
68
|
+
| `keywords:` | An object's name and aliases are its discovery terms | `objects:` |
|
|
69
|
+
| A file-level `owns:` list | The header pointers and the object closure compute it | Region globs only, until the scan switches (above) |
|
|
75
70
|
|
|
76
|
-
|
|
71
|
+
An edit to a document that carries a retired key removes the key in the same edit.
|
|
77
72
|
|
|
78
|
-
|
|
73
|
+
## Not frontmatter, deliberately
|
|
79
74
|
|
|
80
|
-
|
|
75
|
+
- **`Pinned:`** stays a section-level line inside a checklist, naming the suite tests that guard that section. It is typed and resolved mechanically; hoisting it would flatten per-section detail into a document-level list.
|
|
76
|
+
- **Terminal and non-terminal edges:** a `Pinned:` reference terminates (a test file exists or it does not), so a resolver sweep checks it with no judgment. An `objects:` value is checked against the register by the graph's index-time lints. Whether a document still describes what its objects do needs a person or an agent to read both ends; no checker pretends otherwise.
|
|
81
77
|
|
|
82
78
|
## Rules
|
|
83
79
|
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
- **
|
|
89
|
-
- **Every edge value is a repo-relative doc path** (`docs/architecture/common/security.md`), never a bare filename and never a display title — the resolver is a path lookup, and a title changes without the file moving.
|
|
90
|
-
- **A dangling edge is a block, not a warning.** `verifies:` or `applies:` naming a doc that does not exist is a typo or a deletion; either way the graph is lying. Resolve at authoring time.
|
|
80
|
+
- **Inverses never authored:** no document lists its verifiers, its consumers, its governing doctrine's other surfaces, or the files that point at it. A hand-written "consumed by" table is a defect to delete.
|
|
81
|
+
- **Edge values:** `verifies:` values are repo-relative document paths, never a bare filename or a display title.
|
|
82
|
+
- **Dangling edges block:** a `verifies:` naming a document that does not exist, or an `objects:` value no register or product model knows, is a typo or a deletion. Resolve it at authoring time.
|
|
83
|
+
- **Index-time lints the graph prints:** a header pointing at a document that does not exist, a document no file points at, an object no document designs. They never fail a refresh; a session that sees one resolves it or files it.
|
|
84
|
+
- **Baseline and reconcile:** the `maintenance` agent stamps `type:` and `objects:`, reduces any file-level `owns:` list to its region globs, removes retired keys, and reports source files whose header names no document. Many documents ⇒ parallel sub-agents, one document each.
|
|
@@ -34,8 +34,8 @@ Every genre takes the same shape (principle 2) and the same labels (principle 5)
|
|
|
34
34
|
| Operations procedure, migration runbook | Truth, record | The exact commands, in order, with their expected output | Nothing. The detail is the point |
|
|
35
35
|
| Record (plan, changelog entry, journal, memory) | Record | Its template's fields, as of the moment it describes | Nothing. Its prose follows § Voice and § Labels; the dates and the journey are its own |
|
|
36
36
|
|
|
37
|
-
- **Owning documents:** feature, package and system are the three genres that
|
|
38
|
-
- **Doctrine:** the same shape and labels as an owning document,
|
|
37
|
+
- **Owning documents:** feature, package and system are the three genres that speak for code: source files name them in their headers, and the ownership scan resolves paths to them (`frontmatter.md`). They take principles 5 and 6 in full, their sections are fixed (§ Owning documents — the body and the footer), and `doc-lint` reads them.
|
|
38
|
+
- **Doctrine:** the same shape and labels as an owning document, speaking for no code and describing no mechanism. Its model layer may argue in paragraphs; below the model it is bullets like every other explaining document.
|
|
39
39
|
- **Reference documents and procedures:** the one place a signature, a shape, a variable table, a command list or a file path is the content. They keep the detail the owning genres hand to the graph (§ Reference documents).
|
|
40
40
|
- **Indexes:** a folder's `Readme.md` describes directories, never files; the root README follows `templates/root-readme.md`. Both take § Labels.
|
|
41
41
|
|
|
@@ -159,7 +159,7 @@ function lintFile(path, findings) {
|
|
|
159
159
|
}
|
|
160
160
|
if (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/features\/[^/]+\.md$/.test(path)) {
|
|
161
161
|
const fm = text.startsWith('---') ? text.slice(0, text.indexOf('\n---', 3)) : '';
|
|
162
|
-
for (const key of ['
|
|
162
|
+
for (const key of ['objects:', 'status:', 'type:'])
|
|
163
163
|
if (!fm.includes(key)) findings.push({ path, n: 1, severity: 'block', id: 'feature-frontmatter', message: `feature doc missing \`${key}\` frontmatter` });
|
|
164
164
|
}
|
|
165
165
|
|
|
@@ -38,4 +38,4 @@ One file per template. Every template is either a **truth document** (states cur
|
|
|
38
38
|
|
|
39
39
|
**Baseline scaffolds** — files that must exist after every baseline (`docs/structure.md` is not among them: it is harness-owned, shipped whole by the installer and overwritten on every refresh, and has no template); the `maintenance` agent (baseline mode) creates any that are missing: [docs-readme.md](docs-readme.md), [changelog-index.md](changelog-index.md), [backlog.md](backlog.md), [memory-main.md](memory-main.md), [setup.md](setup.md) (when the repo has a lockfile and a runnable root script; never overwriting an existing first-run procedure), and [root-readme.md](root-readme.md) (never overwriting an existing root README).
|
|
40
40
|
|
|
41
|
-
**Ownership.** Three genres
|
|
41
|
+
**Ownership.** Three genres speak for code — **feature**, **package**, **system**. A document names the objects it designs in `objects:`; a source file names the document that speaks for it in its path header; which document speaks for a path is computed from those headers and the object closure, and answered per path by the ownership index (`references/frontmatter.md`). Until the harness's ownership scan switches to that computation, it reads a transitional `owns:` region glob on each of the three genres: `packages/{name}/**` on a package document (the residual owner), a machine's subtrees on a system document, a feature's own directory on a feature document; the narrower glob wins.
|
|
@@ -21,7 +21,7 @@ The design layer — [architecture/](architecture/): {what the folder holds —
|
|
|
21
21
|
|
|
22
22
|
## Features
|
|
23
23
|
|
|
24
|
-
[features/](features/) — one living reference per feature (how it works today), flat, named `{feature}.md`; each
|
|
24
|
+
[features/](features/) — one living reference per feature (how it works today), flat, named `{feature}.md`; each names the objects it designs in `objects:`, and the source files it speaks for name it in their headers. Find a feature by its name or by the ownership index, never by a row here.
|
|
25
25
|
|
|
26
26
|
## Tests
|
|
27
27
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Doctrine Documentation (docs/architecture/common/{concern}.md)
|
|
2
2
|
|
|
3
|
-
Truth document. Use for **one concern the platform handles everywhere**: authentication, authorization, storage, the API protocol, credentials, telemetry. One file per concern, named for the concern. A doctrine document
|
|
3
|
+
Truth document. Use for **one concern the platform handles everywhere**: authentication, authorization, storage, the API protocol, credentials, telemetry. One file per concern, named for the concern. A doctrine document speaks for no code: no source header names it, and it carries no `owns:`. The surfaces bound by it are the documents that design or touch the objects it governs, computed through those objects, and each names it in its Scope.
|
|
4
4
|
|
|
5
5
|
**A doctrine document states the design and nothing beneath it** (`references/style-guide.md` § Shape — the model first, the detail after). It is the most stable document in the tree, because it says why the platform is shaped the way it is, and that changes far less often than the code. It explains no mechanism: no function, file, column, cookie attribute, header value or per-route error code, since each of those fails the stability test. The code graph answers what the code does. This document answers why, and which principles any new code in the concern must keep.
|
|
6
6
|
|
|
@@ -77,5 +77,5 @@ Code cannot reveal what it does not yet do, so decided-but-unbuilt intent is one
|
|
|
77
77
|
| --- | --- |
|
|
78
78
|
| A tour of the code that implements the concern | The code graph |
|
|
79
79
|
| The catalog of routes, error codes or variables | An API reference or environment registry |
|
|
80
|
-
| The one feature that happens to use the concern most | That feature's own document, which
|
|
80
|
+
| The one feature that happens to use the concern most | That feature's own document, which names this doctrine in its Scope |
|
|
81
81
|
| The story of how the concern got this way | A decision record (`decisions/`) for the why, and the changelog for the when |
|
|
@@ -8,7 +8,7 @@ Truth document. Use for every feature in `docs/features/`: something a person po
|
|
|
8
8
|
- **Each bullet is a bold label, then one or two third-person sentences** saying what happens: "**Organization user:** user lands in their existing organization", never "you land…".
|
|
9
9
|
- **Flows carry the journey; Decisions carry the reasons; Promises run deepest.**
|
|
10
10
|
|
|
11
|
-
Every computable fact (columns, vocabularies, foreign keys, writers, routes, guards, files) is the code graph's (`docs/architecture/systems/knowledge-layer.md`), and a hand-typed copy is the defect this genre removes. Code is named only where a person types or sees it (a page, a command, a flag, an environment variable) or at a cross-cutting seam the graph would not lead a reader to.
|
|
11
|
+
The frontmatter names the objects the feature designs; which files it speaks for is computed from the source headers and the object closure, never listed (`references/frontmatter.md`). Every computable fact (columns, vocabularies, foreign keys, writers, routes, guards, files) is the code graph's (`docs/architecture/systems/knowledge-layer.md`), and a hand-typed copy is the defect this genre removes. Code is named only where a person types or sees it (a page, a command, a flag, an environment variable) or at a cross-cutting seam the graph would not lead a reader to.
|
|
12
12
|
|
|
13
13
|
**A feature document runs deepest toward its end.** Scope and Flows are the model and the journey. **Promises** goes as deep as the behaviour does: every state the surface can be in, every edge case, what a refusal tells the person, and the exact wording wherever the wording itself is promised. That depth is earned because a person or a test checks it and the code cannot state it as intent. It still stops before mechanism. A promise reads "a removed membership shows a panel, not a redirect", never which route answers it.
|
|
14
14
|
|
|
@@ -32,9 +32,8 @@ Nothing else. `doc-lint` blocks the retired mechanism sections (`mechanism-secti
|
|
|
32
32
|
type: feature # required tooling key — preserve, never narrate
|
|
33
33
|
status: active # required — active | draft | deprecated
|
|
34
34
|
objects: [device, credential grant] # required — the product objects this document designs, by their graph names
|
|
35
|
-
owns: ["packages/studio/src/server/routes/devices.ts"] # required until the graph computes ownership — the code this document speaks for; narrower glob wins
|
|
36
35
|
module: identity # optional — one bounded-context label
|
|
37
|
-
|
|
36
|
+
owns: ["packages/studio/src/server/devices/**"] # transitional — one region glob, read only by the ownership scan until it switches to header pointers (references/frontmatter.md)
|
|
38
37
|
---
|
|
39
38
|
```
|
|
40
39
|
|
|
@@ -90,6 +89,6 @@ No tables, routes or symbol rosters — those are the graph's.
|
|
|
90
89
|
| When | What is written |
|
|
91
90
|
| --- | --- |
|
|
92
91
|
| `status: draft` (design) | goal, Scope, Decisions, Promises (with gaps named), For the agent; the LLD research goes to the plan |
|
|
93
|
-
| `status: active` | Flows written from the shipped surface; `
|
|
92
|
+
| `status: active` | Flows written from the shipped surface; `objects:` confirmed against the register, and each owned source file's header names this document |
|
|
94
93
|
|
|
95
94
|
A feature whose code is gone is deleted, or `status: deprecated` with its footer pointing at what replaced it.
|
|
@@ -6,7 +6,7 @@ Truth document. Use for: **one workspace package** — one unit of distribution:
|
|
|
6
6
|
|
|
7
7
|
**A package document's deepest layer is its public contract** (`references/style-guide.md` § Shape — the model first, the detail after): the commands and flags a person types, the exports another package imports, what is installed and where. For a CLI the flags *are* the promise, so they belong in Usage. It stops before internal layout, build steps and which file holds what.
|
|
8
8
|
|
|
9
|
-
**A package document is the residual owner of its own tree.**
|
|
9
|
+
**A package document is the residual owner of its own tree.** Every file in the package whose header names no narrower document is its to speak for, so it loses to every system or feature document a header names. Until the ownership scan switches to header pointers, its transitional `owns:` is the whole package glob. The one enumerable fact it declares is the ownership table: which subtrees other documents speak for.
|
|
10
10
|
|
|
11
11
|
**Sharp, bulleted, labelled with noun phrases** (`references/style-guide.md` § Labels). Every section after the goal line is bullets under a noun-phrase heading, each bullet a bold noun-phrase label followed by one or two sentences.
|
|
12
12
|
|
|
@@ -29,8 +29,7 @@ Nothing else. The feature template's lint rules apply. Forty to a hundred and tw
|
|
|
29
29
|
type: package
|
|
30
30
|
status: active
|
|
31
31
|
objects: [nustack cli, device credential] # required — the objects this package designs or ships
|
|
32
|
-
owns: ["packages/{name}/**"] #
|
|
33
|
-
applies: [docs/architecture/common/api.md] # optional
|
|
32
|
+
owns: ["packages/{name}/**"] # transitional — the residual region glob, read only by the ownership scan until it switches to header pointers
|
|
34
33
|
---
|
|
35
34
|
```
|
|
36
35
|
|
|
@@ -37,7 +37,7 @@ Sections come in this order; 1 to 3 are the outward-facing first screen, 4 onwar
|
|
|
37
37
|
- **Relative links into the repo only.** Never an absolute repo URL. Never an external site as the home of a canonical doc.
|
|
38
38
|
- **Link folders and mandated scaffolds, never rosters.** The allowed targets are folders, `docs/README.md`, `docs/structure.md`, `docs/operations/setup.md`, `docs/history/CHANGELOG.md`, `docs/history/backlog.md`, `CLAUDE.md` and the owning docs in the Map. A procedure is named only where a Destinations row needs it. Every heading and table header is a noun phrase (`../references/style-guide.md` § Labels). Never list individual feature docs: the ownership index finds them.
|
|
39
39
|
- **No tree.** The Map stops at top-level units and never lists files. Mark paths that look primary but are not, for example "`legacy/`: reference only, never edited".
|
|
40
|
-
- **The Map is a coarse view of the ownership index, not a rival to it.** The Owning doc column names the doc
|
|
40
|
+
- **The Map is a coarse view of the ownership index, not a rival to it.** The Owning doc column names the doc the index resolves for that path. When the two disagree the index wins, and the Map row is fixed in the same change.
|
|
41
41
|
- **No version numbers and no dates.** Name the file that pins a version (`.nvmrc`, `packageManager`, `rust-toolchain.toml`). Write in the present tense, with no "coming soon" and no "as of".
|
|
42
42
|
- **State rules, don't argue them.** One line each. The reasoning lives in the linked doc, and agent process rules stay in `CLAUDE.md`.
|
|
43
43
|
- **Commands come from the repo.** Detect the package manager from the lockfile (`pnpm-lock.yaml` gives pnpm, `yarn.lock` gives yarn, otherwise npm). Use only scripts that exist, each with a trailing comment saying what it does.
|
|
@@ -6,7 +6,7 @@ Truth document. Use for: **one machine that spans packages**, which a person can
|
|
|
6
6
|
|
|
7
7
|
**A system document's deepest layer is the machine's behaviour across packages** (`references/style-guide.md` § Shape — the model first, the detail after): the order it runs in, the sequence and state diagrams at role level, the invariant that holds at each place where parts meet, and what re-drives a stall. That layer is earned because no single file shows a cross-package flow. It stops before per-function steps, tables, columns and routes.
|
|
8
8
|
|
|
9
|
-
**A system document
|
|
9
|
+
**A system document speaks for the files the machine is made of.** Their headers name it, which outranks the package document's residual claim. Until the ownership scan switches to header pointers, its transitional `owns:` names the machine's subtrees as region globs, narrower than the package glob; every glob matches a file on disk at `status: active`.
|
|
10
10
|
|
|
11
11
|
**Sharp, bulleted, labelled with noun phrases** (`references/style-guide.md` § Labels). The opening paragraph defines the machine; everything after it is bullets under noun-phrase headings, each bullet a bold noun-phrase label followed by one or two sentences.
|
|
12
12
|
|
|
@@ -28,10 +28,8 @@ Nothing else. The lint rules of the feature template apply. Sixty to a hundred a
|
|
|
28
28
|
type: system # required tooling key
|
|
29
29
|
status: active # required — draft = the machine as a whole does not run
|
|
30
30
|
objects: [run, run ledger, settlement] # required — the objects this machine owns or moves
|
|
31
|
-
owns: #
|
|
31
|
+
owns: # transitional — region globs, one per subtree; read only by the ownership scan until it switches to header pointers (references/frontmatter.md)
|
|
32
32
|
- packages/{pkg}/src/{machine}/**
|
|
33
|
-
applies: # optional — decisions and doctrine this machine is BOUND by
|
|
34
|
-
- docs/architecture/decisions/AD-0NN-{slug}.md
|
|
35
33
|
---
|
|
36
34
|
```
|
|
37
35
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Truth document.** States what a person sees and can do on a surface, and what the surface promises never to do. Present tense, like every truth document.
|
|
4
4
|
|
|
5
|
-
**`type: ux`**, plus `
|
|
5
|
+
**`type: ux`**, plus `status:` and `objects:` — the objects the surface presents (`references/frontmatter.md`). The doctrine it answers to (the state vocabulary, the routing doctrine, the information architecture) is named in its Purpose for people and computed through shared objects for tooling.
|
|
6
6
|
|
|
7
7
|
**Why this genre exists.** A feature doc (LLD) states what a feature is, how its data flows, and which contracts it honours — all of which stay plausibly true through a rewrite of the surface. Nothing else in the repo states what a person *observes*. So a change to the wire lands, the interface absorbs it, and the absorption reads as maintenance: no test fails, no lint fires, no document is contradicted. This document is the thing a silent change contradicts.
|
|
8
8
|
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
|
|
13
13
|
| The behavior is… | Where it lives |
|
|
14
14
|
| --- | --- |
|
|
15
|
-
| One surface's — a panel, an editor, a dialog | `docs/features/{surface}-ux.md
|
|
15
|
+
| One surface's — a panel, an editor, a dialog | `docs/features/{surface}-ux.md`; its component files' headers name it |
|
|
16
16
|
| Cross-cutting — routing, information architecture, the state vocabulary every surface obeys, presentation of streams | `docs/architecture/` — owns no code, like every architecture doc |
|
|
17
17
|
|
|
18
|
-
**Ownership:
|
|
18
|
+
**Ownership: a component file names one document in its header.** A surface's components are frequently already spoken for — by the page's feature document, by the feature behind one panel — and a file's header names exactly one document. Where the header already names the feature document, this document is named in that feature document's **coordinated edits** row instead, phrased as *different promises over the same files*, and the object closure links the two through the objects both name. Where no document speaks for the component yet, its header names this one.
|
|
19
19
|
|
|
20
|
-
That matters because a doc nothing implicates is a doc that rots, and a surface rewritten under an unimplicated document is the failure this genre exists to prevent. The coordinated-edit row is the weaker of the two bindings; prefer
|
|
20
|
+
That matters because a doc nothing implicates is a doc that rots, and a surface rewritten under an unimplicated document is the failure this genre exists to prevent. The coordinated-edit row is the weaker of the two bindings; prefer the header pointer wherever no document speaks for the component yet.
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -21,7 +21,7 @@ Map the documentation landscape — what exists, what is adjacent, how detailed
|
|
|
21
21
|
2. Locate the repo's feature-doc layer from `structure.md` — the standard shape is `./docs/features/`, but a repo may document components under their own subject folders. Read that layer's index if it has one; otherwise glob for docs carrying `owns:` frontmatter
|
|
22
22
|
3. If `./docs/memory/lld-mode.md` exists (or the legacy `./docs/memory/spec-mode.md`), read it for feature doc inventory, ownership conflict zones, and lessons from previous research
|
|
23
23
|
4. Read 1-2 existing feature LLDs from that layer to calibrate format and depth
|
|
24
|
-
5. Consult the **injected ownership index** (the generated `glob → doc · module · objects · status` map —
|
|
24
|
+
5. Consult the **injected ownership index** (the generated `glob → doc · module · objects · status` map — computed from source headers and the transitional `owns:` region globs) to build the ownership map. It is the source of truth for what code paths are already spoken for; a source file's path header names its document directly. If the index isn't present in context, glob the docs tree (excluding `docs/history/`) and read the `owns:` frontmatter of every doc that declares it.
|
|
25
25
|
|
|
26
26
|
## What You Research
|
|
27
27
|
|
|
@@ -30,7 +30,7 @@ Map the documentation landscape — what exists, what is adjacent, how detailed
|
|
|
30
30
|
3. **Affected modules** — Identify what files, routes, components, and data flows are relevant
|
|
31
31
|
4. **Current state** — Understand how the system works today in the area being specified
|
|
32
32
|
5. **Dependencies** — Map what this feature touches and what depends on it
|
|
33
|
-
6. **Ownership conflicts** — Compare the new feature's intended code paths against existing
|
|
33
|
+
6. **Ownership conflicts** — Compare the new feature's intended code paths against the documents those paths already reach — the documents their source headers name, and existing transitional `owns:` region globs. Report overlaps: paths another document already speaks for, or two region globs at equal specificity.
|
|
34
34
|
7. **Invalidation** — a different question from conflict, and the one that gets missed. A conflict is two docs claiming one glob; an invalidation is **one existing doc whose stated behavior this feature makes false** — it may own entirely different paths and still be wrong once this ships. For every doc whose territory this feature touches or whose described flow it changes, name the specific claim that stops being true.
|
|
35
35
|
|
|
36
36
|
## What You Return
|
|
@@ -42,7 +42,7 @@ Structure your findings as:
|
|
|
42
42
|
- **Affected modules**: Files, routes, components that will be involved
|
|
43
43
|
- **Current behavior**: How the relevant part of the system works today
|
|
44
44
|
- **Conventions**: Naming, structure, and patterns to follow based on existing code
|
|
45
|
-
- **Ownership conflicts**: Existing
|
|
45
|
+
- **Ownership conflicts**: Existing documents that already speak for the proposed feature's paths (by source header or transitional region glob). Include the conflicting paths and the owning doc name.
|
|
46
46
|
- **Invalidated claims**: One row per existing doc this feature makes wrong — doc path, the claim, why it stops being true. This list seeds the plan's **Doc Obligations** ledger, so it is the research finding most likely to survive into an artifact; "none" is a real answer and says the feature is genuinely additive.
|
|
47
47
|
- **Risks or concerns**: Anything the LLD should address or call out
|
|
48
48
|
|