@nurix/etna 0.20.45 → 0.20.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/agents/maintenance.md +5 -1
- package/data/harnesses/code/codex/agents/maintenance.toml +5 -1
- package/data/harnesses/code/docs/structure.md +13 -10
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/hooks/compliance.mjs +20 -0
- package/data/harnesses/code/hooks/lib/walls.mjs +2 -1
- package/data/harnesses/code/skills/documentation/SKILL.md +12 -2
- package/data/harnesses/code/skills/documentation/references/document-sets.md +1 -1
- package/data/harnesses/code/skills/documentation/references/style-guide.md +2 -1
- package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +3 -3
- package/data/harnesses/code/skills/documentation/templates/Readme.md +3 -2
- package/data/harnesses/code/skills/documentation/templates/doctrine.md +4 -1
- package/data/harnesses/code/skills/documentation/templates/playbook.md +63 -0
- package/data/harnesses/code/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/ux-behavior.md +1 -1
- package/data/harnesses/delivery/agents/maintenance.md +5 -1
- package/data/harnesses/delivery/codex/agents/maintenance.toml +5 -1
- package/data/harnesses/delivery/harness.json +2 -2
- package/data/harnesses/delivery/skills/documentation/SKILL.md +12 -2
- package/data/harnesses/delivery/skills/documentation/references/document-sets.md +1 -1
- package/data/harnesses/delivery/skills/documentation/references/style-guide.md +2 -1
- package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +3 -3
- package/data/harnesses/delivery/skills/documentation/templates/Readme.md +3 -2
- package/data/harnesses/delivery/skills/documentation/templates/doctrine.md +4 -1
- package/data/harnesses/delivery/skills/documentation/templates/playbook.md +63 -0
- package/data/harnesses/delivery/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/ux-behavior.md +1 -1
- package/data/harnesses/etna/agents/maintenance.md +5 -1
- package/data/harnesses/etna/codex/agents/maintenance.toml +5 -1
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/skills/documentation/SKILL.md +12 -2
- package/data/harnesses/etna/skills/documentation/references/document-sets.md +1 -1
- package/data/harnesses/etna/skills/documentation/references/style-guide.md +2 -1
- package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +3 -3
- package/data/harnesses/etna/skills/documentation/templates/Readme.md +3 -2
- package/data/harnesses/etna/skills/documentation/templates/doctrine.md +4 -1
- package/data/harnesses/etna/skills/documentation/templates/playbook.md +63 -0
- package/data/harnesses/etna/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/ux-behavior.md +1 -1
- package/package.json +2 -2
|
@@ -20,7 +20,7 @@ This skill writes one document at a time: the genre registry says what kind of d
|
|
|
20
20
|
## Writing one document
|
|
21
21
|
|
|
22
22
|
1. **Find the genre.** Match the request against § Routing, then read that genre's registry row: nature, `type:`, location, required keys, required sections. No row fits ⇒ `references/untemplated.md`.
|
|
23
|
-
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
23
|
+
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. A new doctrine or playbook document also waits for the user's approval (§ Doctrine and playbooks). If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
24
24
|
3. **Earn it, and route a set.** A new truth document that states rules (doctrine, a system, an architecture decision) first lists the decisions only it would own, each checked against the documents that exist; under about six, its rules go to their homes and the document is not written. More than one sibling document, or any doctrine document, follows `references/document-sets.md` from here: a term table and a rule ledger before prose, rendered from the ledger, one cold read.
|
|
25
25
|
4. **Place and name it** at the registry's location. `docs/structure.md` is the placement authority for anything the registry leaves open.
|
|
26
26
|
5. **Write it from the template, then run its genre procedure.** A template's procedure section binds that genre only: doctrine's is the reason check (`templates/doctrine.md` § Reason check). Keep the template's section names exactly; omit optional sections with nothing real to say. Write the body from the outside in (style guide § Shape), every heading and bold label a noun phrase, every sentence after a label third person, one rule per sentence in the term engineers use (style guide § Sentences, § Vocabulary). In a truth document, every sentence is present tense (§ The three natures). Every line owns a decision: before stating a rule, search the neighbours for it, and cite the one found rather than restating it (style guide § Net-net).
|
|
@@ -41,6 +41,15 @@ Some genres carry steps beyond writing from the template. Each procedure lives i
|
|
|
41
41
|
|
|
42
42
|
A genre with no row here has no procedure beyond its template.
|
|
43
43
|
|
|
44
|
+
## Doctrine and playbooks
|
|
45
|
+
|
|
46
|
+
Two genres are standards rather than accounts of one repository: a doctrine document settles one concern every product handles, and a playbook settles one interface or workflow shape every product builds. Both are read by the next product as much as by this one, so four rules bind them beyond their templates:
|
|
47
|
+
|
|
48
|
+
- **General form:** each reads correctly copied verbatim into another product's repository. It names nothing only its own repository has: the product, its domain entities, its vendors and stack, its people, its own documents by path. An example uses an object every product has, and the repository's own values stay in its feature documents, which name the standard in their Scope.
|
|
49
|
+
- **Extend first:** before a new document is proposed, the existing set is read (`grep -m1 '^description:' docs/architecture/{doctrine,playbooks}/*.md`), and content whose subject one of them already covers lands there as a section, a principle or a row.
|
|
50
|
+
- **New only on a clear distinction:** a new document is written only for a concern or a shape none of the existing ones owns, with decisions of its own (the template's earn-it test).
|
|
51
|
+
- **Never without approval:** an agent never creates a doctrine or playbook document on its own initiative, a baseline and a harvest included. It names the proposed document, the existing documents it was checked against and the distinction between them, then waits for the user's yes in chat. An unattended session files the request as a stop. An edit to an existing document follows the normal loop.
|
|
52
|
+
|
|
44
53
|
## The three natures
|
|
45
54
|
|
|
46
55
|
Every document is exactly one of three things, and the registry names which for every genre. Decide before writing a word: every other rule follows from the split.
|
|
@@ -104,7 +113,8 @@ A document names the objects it designs in `objects:`, and a source file names t
|
|
|
104
113
|
| Document a feature (LLD) | [`templates/feature.md`](templates/feature.md) |
|
|
105
114
|
| Document a feature's data flow or state | The feature doc's Flows (behaviour) and Promises (guarantees); a persisted shape belongs in the API reference (style guide § State at persistence points); component and transient state are not documented |
|
|
106
115
|
| Document a workspace package | [`templates/package.md`](templates/package.md), the residual owner of `packages/{name}/**` |
|
|
107
|
-
| Document one concern the platform handles everywhere (authentication, storage, the API protocol) | [`templates/doctrine.md`](templates/doctrine.md): design and principles, no mechanism, owns no code; written through [`references/document-sets.md`](references/document-sets.md) |
|
|
116
|
+
| Document one concern the platform handles everywhere (authentication, storage, the API protocol) | [`templates/doctrine.md`](templates/doctrine.md): design and principles in general form, no mechanism, no project link, owns no code; written through [`references/document-sets.md`](references/document-sets.md) |
|
|
117
|
+
| Document one interface or workflow shape every product builds (the shell, a settings area, analytics pages, a record's lifecycle) | [`templates/playbook.md`](templates/playbook.md): the shape and its rules in general form, owns no code; created only with the user's approval (§ Doctrine and playbooks) |
|
|
108
118
|
| Write or rework several sibling documents at once (a doctrine folder, a family of features) | [`references/document-sets.md`](references/document-sets.md): usefulness test, term table, rule ledger, render, one cold read |
|
|
109
119
|
| Document one system spanning packages | [`templates/system.md`](templates/system.md): one machine end to end, never a giant feature doc |
|
|
110
120
|
| Record or revise an architecture decision | [`templates/architecture-decision.md`](templates/architecture-decision.md): one AD per subject, revised in place |
|
|
@@ -87,7 +87,7 @@ A finding names the file and line, quotes the text, names its class, and propose
|
|
|
87
87
|
| No decision of its own | A document under about six owned decisions; a section, table or bullet that states nothing the principles do not |
|
|
88
88
|
| Missing reason | A rule that meets a trigger of the style guide's § Sentences (a tempting alternative, a cost, an arbitrary value, an invisible failure) and carries no reason; in doctrine, a reason the reason check judged non-obvious and the document lacks |
|
|
89
89
|
| Filler | A sentence carrying no fact, rule, boundary or reason: a scene-setting opener, a generic truth used as framing, a metaphor or slogan standing in for a rule, a question |
|
|
90
|
-
| Wrong layer | Mechanism, a product's specifics or build state in doctrine; history in a truth document |
|
|
90
|
+
| Wrong layer | Mechanism, a product's specifics or build state in doctrine, or a project link: the repository's own domain example, vendor, stack or document by path; history in a truth document |
|
|
91
91
|
| Fused reason | A rule and its reason joined into one sentence, so a reader cannot tell which half binds |
|
|
92
92
|
| Two readings | A rule a reader could act on in two incompatible ways |
|
|
93
93
|
|
|
@@ -28,7 +28,8 @@ Every genre takes the same shape (principle 2) and the same labels (principle 5)
|
|
|
28
28
|
|
|
29
29
|
| Genre | Nature | Deepest layer | Excluded |
|
|
30
30
|
| --- | --- | --- | --- |
|
|
31
|
-
| Doctrine (`docs/architecture/
|
|
31
|
+
| Doctrine (`docs/architecture/doctrine/`) | Truth | The decisions behind each part and the reasons for them | Any mechanism. A doctrine document describes no code |
|
|
32
|
+
| Playbook (`docs/architecture/playbooks/`) | Truth | What a person sees and does in the shape, part by part: its arrangement, each part's behaviour, its states and promises | Any one product's values: its labels, routes, limits and timings, which are its feature documents'. Any mechanism |
|
|
32
33
|
| System (`docs/architecture/systems/`) | Truth | The machine's behaviour across packages: the order it runs in, sequence and state diagrams at role level, the invariant at each place where parts meet, what re-drives a stall. No single file shows a cross-package flow, so this layer earns its place | Per-function steps, tables and columns, routes |
|
|
33
34
|
| Package (`docs/architecture/packages/`) | Truth | The public contract a consumer touches: commands and their flags, exports, what is installed and where. For a CLI the flags *are* the promise | Internal layout, the build's steps, which file holds what |
|
|
34
35
|
| Feature (`docs/features/`) | Truth | The behaviour a person meets, in full: each state the surface can be in, each edge case, what a rejection tells them, the wording where the wording is promised. This genre runs deepest toward its end, in Promises, because a promise is checked by a person or a test and the code cannot state it as intent | Columns, routes, guards, functions, files |
|
|
@@ -255,7 +255,7 @@ function lintFile(path, findings) {
|
|
|
255
255
|
if (/^[a-z]+:\/\//.test(target) || SCAFFOLD.test(target)) continue;
|
|
256
256
|
// The repo-root README routes by intent (templates/root-readme.md): its Map names owning package and system
|
|
257
257
|
// docs, its Destinations rows name procedures and rules. Individual feature docs stay off it — the ownership index finds them.
|
|
258
|
-
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|
|
|
258
|
+
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|doctrine|playbooks)\/|operations\/)[^/]+\.md$|(^|\/)\.claude\/rules\/[^/]+\.md$/.test(target)) continue;
|
|
259
259
|
if (isRootReadme && /(^|\/)docs\/features\/[^/]+\.md$/.test(target)) { findings.push({ path, n, severity: 'warn', id: 'root-readme-feature-link', message: `root README links an individual feature doc (\`${target}\`) — point at docs/features/ and let the ownership index find it`, excerpt: line.trim().slice(0, 90) }); continue; }
|
|
260
260
|
findings.push({ path, n, severity: 'block', id: 'index-lists-files', message: `index links an individual document (\`${target}\`) — name the folder and how its files are named; \`ls\` is the roster`, excerpt: line.trim().slice(0, 90) });
|
|
261
261
|
}
|
|
@@ -382,7 +382,7 @@ function lintFile(path, findings) {
|
|
|
382
382
|
// A heading or a bold bullet label names; it never says. A label with a finite verb drags in a tense,
|
|
383
383
|
// a person and a voice ("What was decided", "You already belong to…"), so a label opening with a
|
|
384
384
|
// wh-word or a personal pronoun, or ending in a full stop, is a clause (style-guide § Labels).
|
|
385
|
-
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/
|
|
385
|
+
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/(doctrine|playbooks)\/[^/]+\.md$/.test(path));
|
|
386
386
|
if (isLabelledDoc && !disabled.has('clause-label')) {
|
|
387
387
|
const CLAUSE = /^(how|what|why|where|when|who|which|whose|you|your|we|our|i|it)\b|\b(you|your)\b|\.$/i;
|
|
388
388
|
for (const { n, line } of lines) {
|
|
@@ -413,7 +413,7 @@ function lintFile(path, findings) {
|
|
|
413
413
|
|
|
414
414
|
// A doctrine folder is read as a set: its documents cite each other's principles by number and must not restate each
|
|
415
415
|
// other (templates/doctrine.md, references/document-sets.md), so these checks read the siblings beside the linted file.
|
|
416
|
-
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/
|
|
416
|
+
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/doctrine\/[^/]+\.md$/;
|
|
417
417
|
// A doctrine document closes on Terms and Related documents; an older one on "For the agent". The body ends at the first.
|
|
418
418
|
const DOCTRINE_CLOSING = /^##\s+(For the agent|Terms|Related documents)\s*$/;
|
|
419
419
|
// The words a doctrine document replaces with the term engineers use (references/style-guide.md § Vocabulary).
|
|
@@ -17,7 +17,8 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
17
17
|
| Feature | [feature.md](feature.md) | truth | `feature` | `docs/features/{feature}.md` | `type` `description` `status` `objects` | Scope · Promises · For the agent |
|
|
18
18
|
| Package | [package.md](package.md) | truth | `package` | `docs/architecture/packages/{package}.md` | `type` `description` `status` `objects` | Scope · Ownership · Usage · Promises · For the agent |
|
|
19
19
|
| System | [system.md](system.md) | truth | `system` | `docs/architecture/systems/{system}.md` | `type` `description` `status` `objects` | Scope · Flow · Promises · For the agent |
|
|
20
|
-
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/
|
|
20
|
+
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/doctrine/{concern}.md` | `type` `description` `status` `objects` | Principles · Terms · Related documents |
|
|
21
|
+
| Playbook | [playbook.md](playbook.md) | truth | `playbook` | `docs/architecture/playbooks/{subject}.md` | `type` `description` `status` `objects` | Purpose · Related documents |
|
|
21
22
|
| Architecture decision | [architecture-decision.md](architecture-decision.md) | truth | `architecture-decision` | `docs/architecture/decisions/AD-NNN-{subject}.md` | `type` `title` `description` `status` `last-updated` | Decision · Context · Consequences |
|
|
22
23
|
| UX behavior | [ux-behavior.md](ux-behavior.md) | truth | `ux` | `docs/features/{surface}-ux.md` | `type` `description` `status` `objects` | Purpose · Anatomy · States · Affordances · Promises · Transitions |
|
|
23
24
|
| System overview | [system-overview.md](system-overview.md) | truth | `system-overview` | `docs/architecture/system-design.md` | `type` `description` | — |
|
|
@@ -25,7 +26,7 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
25
26
|
| Glossary | [glossary.md](glossary.md) | truth | `glossary` | `docs/architecture/glossary.md` | `type` `description` | — |
|
|
26
27
|
| Non-goals | [non-goals.md](non-goals.md) | truth | `non-goals` | `docs/architecture/non-goals.md` | `type` `description` | — |
|
|
27
28
|
| Security overview | [security-overview.md](security-overview.md) | truth | `security-overview` | `docs/architecture/security.md` | `type` `description` | — |
|
|
28
|
-
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/
|
|
29
|
+
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/doctrine/testing.md` | `type` `description` | — |
|
|
29
30
|
| API reference | [api-reference.md](api-reference.md) | truth | `api-reference` | `docs/architecture/api.md` | `type` `description` | — |
|
|
30
31
|
| Environment registry | [environment-registry.md](environment-registry.md) | truth | `environment-registry` | `docs/operations/environment.md` | `type` `description` | — |
|
|
31
32
|
| Behavioral checklist | [behavioral-checklist.md](behavioral-checklist.md) | truth | `checklist` | `docs/tests/{feature}.md` | `type` `description` `verifies` | — |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Doctrine documentation (docs/architecture/
|
|
1
|
+
# Doctrine documentation (docs/architecture/doctrine/{concern}.md)
|
|
2
2
|
|
|
3
3
|
Truth document. Use for **one concern the platform handles everywhere**: authentication, authorization, storage, the API protocol, credentials, telemetry. One file per concern, named for the concern. A doctrine document speaks for no code: no source header names it, and it carries no `owns:`. The surfaces bound by it are the documents that design or touch the objects it governs, computed through those objects, and each names it in its Scope.
|
|
4
4
|
|
|
@@ -6,6 +6,8 @@ Truth document. Use for **one concern the platform handles everywhere**: authent
|
|
|
6
6
|
|
|
7
7
|
**A doctrine document is a specification, not a description.** It states how every product handles the concern, so a product whose code differs has a gap to plan and build, and the document never records which product has built what: no `UNBUILT` marker, no "today the product…", no product's name, setting value or code root. When one product's posture against the doctrine matters, it is that product's own security overview, plan or backlog.
|
|
8
8
|
|
|
9
|
+
**A doctrine document is written in general form, never linked to the project that holds it.** It reads correctly when copied verbatim into any other product's repository. So it names nothing only that repository has: the product, its domain entities (a hiring product's candidates), its vendors and stack (a hosting platform, a database engine), its people, or its own documents by path (a feature document, a plan, a decision record). An example uses an object every product has (a member, an invoice, an uploaded file); where a domain object is unavoidable, it comes from a kind of product other than the repository's own. Its links reach only sibling doctrine documents and published standards. The project's own facts belong to its feature documents, system overview and decision records, which name this document in their Scope.
|
|
10
|
+
|
|
9
11
|
**Earn the document before writing it.** List the decisions only this document would own, each checked against the documents that exist: a decision another document already states is a citation, not an owned decision. Fewer than about six owned decisions means the concern is a section of a neighbour, not a document (`references/document-sets.md` § Usefulness test).
|
|
10
12
|
|
|
11
13
|
**Required:**
|
|
@@ -98,4 +100,5 @@ Principles are cited by number, sections by heading, always in parentheses: "(pr
|
|
|
98
100
|
| The one feature that happens to use the concern most | That feature's own document, which names this document in its Scope |
|
|
99
101
|
| The story of how the concern got this way | The subject's architecture decision (`decisions/`), whose Decision Changelog tail carries how it moved, and the changelog for the when |
|
|
100
102
|
| A record of which product has built which part | That product's plan, backlog, or security overview |
|
|
103
|
+
| A description of the repository it sits in: its product, domain, stack or documents | That product's feature documents, system overview and decision records |
|
|
101
104
|
| A restatement of a sibling's rule for completeness | A citation of the sibling's principle, with the one clause of context the sentence needs |
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Playbook documentation (docs/architecture/playbooks/{subject}.md)
|
|
2
|
+
|
|
3
|
+
Truth document. Use for **one interface or workflow shape every product builds**: the shell, a settings area, analytics pages, a record's lifecycle, the interaction patterns, automated agents. One file per shape, named for it. A playbook speaks for no code: no source header names it, and it carries no `owns:`. The product's own feature documents hold its instance of the shape, and each names the playbook in its Scope.
|
|
4
|
+
|
|
5
|
+
**A playbook states the shape, never one product's instance of it.** It says what the parts are, how they are arranged, how each behaves and what the shape promises, so a new instance is new content poured into a known shape rather than a new layout. A product's own values (its labels, routes, limits, timings, the wording of its copy) belong to its feature document. A playbook explains no mechanism, for the reasons a doctrine document explains none (`templates/doctrine.md`).
|
|
6
|
+
|
|
7
|
+
**Doctrine or playbook.** A doctrine document settles the decisions of one concern every product handles, such as how authorization works. A playbook settles what a person sees and does in one recurring shape, such as how a settings page is laid out. A playbook cites the doctrine its shape rests on, and never restates it.
|
|
8
|
+
|
|
9
|
+
**A playbook is written in general form, never linked to the project that holds it.** It reads correctly when copied verbatim into any other product's repository, under the same rule a doctrine document keeps (`templates/doctrine.md`). Where the shape needs a word for the product's own record, the Purpose introduces a generic one once ("the subject: the record a report is read per, such as a job in a hiring product") and the body uses it throughout. Its links reach only sibling playbooks, doctrine documents and published standards.
|
|
10
|
+
|
|
11
|
+
**Earn the document, and ask before writing it.** List the rules only this playbook would own, each checked against the playbooks and doctrine that exist. Fewer than about six owned rules means the shape is a section of a neighbour, not a document. A new playbook is created only with the user's approval (SKILL.md § Doctrine and playbooks).
|
|
12
|
+
|
|
13
|
+
**Required:**
|
|
14
|
+
|
|
15
|
+
- the frontmatter
|
|
16
|
+
- **Purpose:** what the shape is for, what it is not for, and the generic words it introduces
|
|
17
|
+
- **Related documents**
|
|
18
|
+
|
|
19
|
+
**Optional, each only with something real to say:**
|
|
20
|
+
|
|
21
|
+
- **Anatomy:** an ASCII diagram of the arrangement with each region labelled, then one bullet per region
|
|
22
|
+
- **One section per part** that carries rules of its own, named for the part ("Roster", "Readiness", "Side peek")
|
|
23
|
+
- **States:** one table — State · Entered when · The person sees · Can do
|
|
24
|
+
- **Affordances:** what a person can do, one bullet per verb
|
|
25
|
+
- **Principles:** numbered, each a noun-phrase label and its rule
|
|
26
|
+
- **Promises:** what the shape guarantees, grouped under `###` headings where there are several kinds
|
|
27
|
+
- **Transitions:** what moves the shape between its states, and who moves it
|
|
28
|
+
|
|
29
|
+
Sections run Purpose first and Related documents last; the rest follow the order above. Each is named for its concept, never for its layer. Expect one hundred to two hundred lines.
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
---
|
|
33
|
+
type: playbook # required tooling key
|
|
34
|
+
description: "{at most 300 characters: the shape this playbook settles, and that a product's own instance is its feature doc}" # required
|
|
35
|
+
status: active # required — active | draft
|
|
36
|
+
objects: [rail, command palette, account menu] # required — the parts this playbook shapes, by their graph names
|
|
37
|
+
---
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
# {Shape} playbook
|
|
42
|
+
|
|
43
|
+
## Purpose
|
|
44
|
+
|
|
45
|
+
{What the shape is for and what it is not for, in two or three sentences. The generic words it introduces, each in one bullet.}
|
|
46
|
+
|
|
47
|
+
## Anatomy
|
|
48
|
+
|
|
49
|
+
{An ASCII diagram, then one bullet per labelled region.}
|
|
50
|
+
|
|
51
|
+
## {Part}
|
|
52
|
+
|
|
53
|
+
- **{Rule label}:** {the rule, in general form}
|
|
54
|
+
|
|
55
|
+
## Principles
|
|
56
|
+
|
|
57
|
+
1. **{Label}:** {the rule}. {Its reason, as a separate sentence.}
|
|
58
|
+
|
|
59
|
+
## Related documents
|
|
60
|
+
|
|
61
|
+
- [{Sibling playbook}]({file}.md): {what it settles beside this one}
|
|
62
|
+
- [{Doctrine}](../doctrine/{concern}.md): {the decision this shape rests on}
|
|
63
|
+
```
|
|
@@ -130,7 +130,7 @@ The rest of this page is for the team and for coding agents working in this repo
|
|
|
130
130
|
|
|
131
131
|
The baseline **does** scaffold `/README.md` when it is missing, and it fills each section from the repo:
|
|
132
132
|
|
|
133
|
-
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/
|
|
133
|
+
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/doctrine/` (what the product is, the core loop, who it is for), the package manifests (what publishes where), and the deploy config (addresses). Written in a user's terms; where the charter is missing, the baseline writes Identity only and raises a `note:` row for the rest.
|
|
134
134
|
- **The main path:** from the system overview.
|
|
135
135
|
- **Map:** from the workspace globs and the ownership index.
|
|
136
136
|
- **Rules:** from `docs/operations/protected-files.md` and the architecture doctrine.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Testing strategy (docs/architecture/
|
|
1
|
+
# Testing strategy (docs/architecture/doctrine/testing.md)
|
|
2
2
|
|
|
3
3
|
Truth document. Use for: the repo's test posture — what is tested at which level, how to run it, and what is deliberately untested. The framework choices come from the harness testing rule; this doc records how *this repo* applies them.
|
|
4
4
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UX behavior (docs/features/{surface}-ux.md)
|
|
2
2
|
|
|
3
|
-
**Truth document.** States what a person sees and can do on a surface, and what the surface promises never to do. Present tense, like every truth document.
|
|
3
|
+
**Truth document.** States what a person sees and can do on a surface, and what the surface promises never to do. Present tense, like every truth document. It is one product's surface; a shape every product's surfaces share, such as a settings area or an analytics page, is a playbook (`templates/playbook.md`), which the UX document names in its Purpose and fills with its own values.
|
|
4
4
|
|
|
5
5
|
**`type: ux`**, plus `status:` and `objects:` — the objects the surface presents (`references/frontmatter.md`). The doctrine it answers to (the state vocabulary, the routing doctrine, the information architecture) is named in its Purpose for people and computed through shared objects for tooling.
|
|
6
6
|
|
|
@@ -70,7 +70,7 @@ Across the header pointers and the transitional globs:
|
|
|
70
70
|
|
|
71
71
|
- **Dangling edges.** A `verifies:` naming a doc that does not exist is remediation, not a question — fix the path, or drop the edge when its target was deleted deliberately. The generated index marks these `← TARGET MISSING`.
|
|
72
72
|
- **Checklists with no `verifies:`.** Every `docs/tests/` file names the doc whose promises it proves. A checklist without one is unreachable — nothing implicates it when its subject moves — so it is the single highest-value backfill in the tree, and it is mechanical wherever a same-named feature doc exists (`docs/tests/auth.md` → `docs/features/auth.md`). Where the name does not match, ask rather than guess.
|
|
73
|
-
- **Doctrine with no `objects:`.** A `docs/architecture/
|
|
73
|
+
- **Doctrine with no `objects:`.** A `docs/architecture/doctrine/` or `docs/architecture/playbooks/` document that names no objects is reachable by nothing: the closure that links a doctrine to the surfaces it governs runs through the objects both name. Report the list; naming a doctrine's objects is its author's call.
|
|
74
74
|
|
|
75
75
|
**Never author an inverse to fix a gap.** A doc listing its own verifiers or consumers is a defect — the inverse is generated. If the index looks wrong, the frontmatter at the consuming end is what is wrong.
|
|
76
76
|
|
|
@@ -137,6 +137,8 @@ docs/
|
|
|
137
137
|
├── structure.md # Harness-owned authority map — the installer writes it on every refresh; you stamp only `harness-version`
|
|
138
138
|
├── architecture/ # Cross-module design + steering layer
|
|
139
139
|
│ │ # domain-model, glossary, non-goals, references, open-questions
|
|
140
|
+
│ ├── doctrine/ # Doctrine — one `{concern}.md` per concern every product handles; owns no code
|
|
141
|
+
│ ├── playbooks/ # Playbooks — one `{subject}.md` per shape every product builds; owns no code
|
|
140
142
|
│ └── decisions/ # Architecture Decisions (ADs — one per subject, updated in place; journey in each AD's Decision Changelog)
|
|
141
143
|
├── features/ # Feature specs — flat, no subfolders; ownership computed from each doc's `objects:` and the source headers
|
|
142
144
|
├── operations/ # Setup, development, deployment, CI/CD
|
|
@@ -180,6 +182,7 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
|
|
|
180
182
|
| no `harness-version` in `structure.md` | stamp it (Step B4) |
|
|
181
183
|
| `docs/tests/` checklists carrying **no frontmatter** (the shape before the graph — the genre was exempt) | **backfill `type: checklist` + `verifies:`.** Mechanical where a same-named feature or UX doc exists; where the name does not match, list the checklist to the user with your best candidate and let them confirm. This is the row that upgrades a repo from a tree of files to a graph — without it every checklist in an older repo stays unreachable, and nothing detects that |
|
|
182
184
|
| docs carrying a retired `applies:` or `keywords:` key, or a file-level `owns:` list | **remove the retired key; reduce `owns:` to its region globs.** Mechanical: a doctrine the removed `applies:` named stays named in the document's Scope; the files a reduced `owns:` dropped are reported where their headers name no document (`documentation` skill, `references/frontmatter.md`) |
|
|
185
|
+
| a `docs/architecture/common/` folder (the doctrine folder's name before `doctrine/`) | **move it to `docs/architecture/doctrine/` with `git mv`, then repoint every reference.** Every markdown link, `objects:`-adjacent path, `Bound by` and Related-documents entry, and every source-file path header that points into `common/` is rewritten to `doctrine/`; frozen records under `docs/history/` keep the old path as provenance. Verify with a search: no reference into `docs/architecture/common/` remains outside `docs/history/` |
|
|
183
186
|
|
|
184
187
|
**The support window: three past minor versions.** The fold table covers shapes at most three minors behind the installed harness — which today means the rows above, because every older dissolved construct (`docs/routing.md`, `docs/modules/`, pillars, the Refinement Ledger, the ADR genre, legacy brainstorm folders, …) predates the window. An older shape is **reported, never improvised**: name what the probe found, point the user at etna's dated migration runbooks (`docs/history/migrations/` in the forge), and recommend a fresh baseline over an archaeological fold — a repo more than three minors behind re-baselines rather than migrates.
|
|
185
188
|
|
|
@@ -231,6 +234,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
|
|
|
231
234
|
## Rules
|
|
232
235
|
|
|
233
236
|
- **Never delete feature docs** — only update or flag for review.
|
|
237
|
+
- **Never create a doctrine or playbook document.** A baseline folds, moves and converges the ones that exist; content that looks like a new standard goes to the backlog as a proposal for the user (the `documentation` skill § Doctrine and playbooks).
|
|
234
238
|
- **Never modify `docs/history/plans/`, `docs/history/changelog/`, or `docs/history/migrations/`** (frozen records — changelog entries are written only at commit time and distilled only by the `retro` agent; a migration runbook freezes at its Outcome stamp).
|
|
235
239
|
- **Preserve existing doc structure and formatting** when updating.
|
|
236
240
|
- **When in doubt, create a backlog item** rather than making a wrong update.
|
|
@@ -68,7 +68,7 @@ Across the header pointers and the transitional globs:
|
|
|
68
68
|
|
|
69
69
|
- **Dangling edges.** A `verifies:` naming a doc that does not exist is remediation, not a question — fix the path, or drop the edge when its target was deleted deliberately. The generated index marks these `← TARGET MISSING`.
|
|
70
70
|
- **Checklists with no `verifies:`.** Every `docs/tests/` file names the doc whose promises it proves. A checklist without one is unreachable — nothing implicates it when its subject moves — so it is the single highest-value backfill in the tree, and it is mechanical wherever a same-named feature doc exists (`docs/tests/auth.md` → `docs/features/auth.md`). Where the name does not match, ask rather than guess.
|
|
71
|
-
- **Doctrine with no `objects:`.** A `docs/architecture/
|
|
71
|
+
- **Doctrine with no `objects:`.** A `docs/architecture/doctrine/` or `docs/architecture/playbooks/` document that names no objects is reachable by nothing: the closure that links a doctrine to the surfaces it governs runs through the objects both name. Report the list; naming a doctrine's objects is its author's call.
|
|
72
72
|
|
|
73
73
|
**Never author an inverse to fix a gap.** A doc listing its own verifiers or consumers is a defect — the inverse is generated. If the index looks wrong, the frontmatter at the consuming end is what is wrong.
|
|
74
74
|
|
|
@@ -135,6 +135,8 @@ docs/
|
|
|
135
135
|
├── structure.md # Harness-owned authority map — the installer writes it on every refresh; you stamp only `harness-version`
|
|
136
136
|
├── architecture/ # Cross-module design + steering layer
|
|
137
137
|
│ │ # domain-model, glossary, non-goals, references, open-questions
|
|
138
|
+
│ ├── doctrine/ # Doctrine — one `{concern}.md` per concern every product handles; owns no code
|
|
139
|
+
│ ├── playbooks/ # Playbooks — one `{subject}.md` per shape every product builds; owns no code
|
|
138
140
|
│ └── decisions/ # Architecture Decisions (ADs — one per subject, updated in place; journey in each AD's Decision Changelog)
|
|
139
141
|
├── features/ # Feature specs — flat, no subfolders; ownership computed from each doc's `objects:` and the source headers
|
|
140
142
|
├── operations/ # Setup, development, deployment, CI/CD
|
|
@@ -178,6 +180,7 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
|
|
|
178
180
|
| no `harness-version` in `structure.md` | stamp it (Step B4) |
|
|
179
181
|
| `docs/tests/` checklists carrying **no frontmatter** (the shape before the graph — the genre was exempt) | **backfill `type: checklist` + `verifies:`.** Mechanical where a same-named feature or UX doc exists; where the name does not match, list the checklist to the user with your best candidate and let them confirm. This is the row that upgrades a repo from a tree of files to a graph — without it every checklist in an older repo stays unreachable, and nothing detects that |
|
|
180
182
|
| docs carrying a retired `applies:` or `keywords:` key, or a file-level `owns:` list | **remove the retired key; reduce `owns:` to its region globs.** Mechanical: a doctrine the removed `applies:` named stays named in the document's Scope; the files a reduced `owns:` dropped are reported where their headers name no document (`documentation` skill, `references/frontmatter.md`) |
|
|
183
|
+
| a `docs/architecture/common/` folder (the doctrine folder's name before `doctrine/`) | **move it to `docs/architecture/doctrine/` with `git mv`, then repoint every reference.** Every markdown link, `objects:`-adjacent path, `Bound by` and Related-documents entry, and every source-file path header that points into `common/` is rewritten to `doctrine/`; frozen records under `docs/history/` keep the old path as provenance. Verify with a search: no reference into `docs/architecture/common/` remains outside `docs/history/` |
|
|
181
184
|
|
|
182
185
|
**The support window: three past minor versions.** The fold table covers shapes at most three minors behind the installed harness — which today means the rows above, because every older dissolved construct (`docs/routing.md`, `docs/modules/`, pillars, the Refinement Ledger, the ADR genre, legacy brainstorm folders, …) predates the window. An older shape is **reported, never improvised**: name what the probe found, point the user at etna's dated migration runbooks (`docs/history/migrations/` in the forge), and recommend a fresh baseline over an archaeological fold — a repo more than three minors behind re-baselines rather than migrates.
|
|
183
186
|
|
|
@@ -229,6 +232,7 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
|
|
|
229
232
|
## Rules
|
|
230
233
|
|
|
231
234
|
- **Never delete feature docs** — only update or flag for review.
|
|
235
|
+
- **Never create a doctrine or playbook document.** A baseline folds, moves and converges the ones that exist; content that looks like a new standard goes to the backlog as a proposal for the user (the `documentation` skill § Doctrine and playbooks).
|
|
232
236
|
- **Never modify `docs/history/plans/`, `docs/history/changelog/`, or `docs/history/migrations/`** (frozen records — changelog entries are written only at commit time and distilled only by the `retro` agent; a migration runbook freezes at its Outcome stamp).
|
|
233
237
|
- **Preserve existing doc structure and formatting** when updating.
|
|
234
238
|
- **When in doubt, create a backlog item** rather than making a wrong update.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "etna",
|
|
3
3
|
"name": "etna",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.42",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
"origin": "https://cdn.nustack.io",
|
|
15
15
|
"track": "latest"
|
|
16
16
|
},
|
|
17
|
-
"digest": "
|
|
17
|
+
"digest": "9a42d4d6b62e"
|
|
18
18
|
}
|
|
@@ -20,7 +20,7 @@ This skill writes one document at a time: the genre registry says what kind of d
|
|
|
20
20
|
## Writing one document
|
|
21
21
|
|
|
22
22
|
1. **Find the genre.** Match the request against § Routing, then read that genre's registry row: nature, `type:`, location, required keys, required sections. No row fits ⇒ `references/untemplated.md`.
|
|
23
|
-
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
23
|
+
2. **Find the existing document.** If a document on the topic already exists, extend it; a second document on one topic is drift. A new doctrine or playbook document also waits for the user's approval (§ Doctrine and playbooks). If the file is a frozen record (a plan at `Complete` or `Abandoned`, a committed changelog entry, an executed runbook), never edit it: write a new dated record that links back.
|
|
24
24
|
3. **Earn it, and route a set.** A new truth document that states rules (doctrine, a system, an architecture decision) first lists the decisions only it would own, each checked against the documents that exist; under about six, its rules go to their homes and the document is not written. More than one sibling document, or any doctrine document, follows `references/document-sets.md` from here: a term table and a rule ledger before prose, rendered from the ledger, one cold read.
|
|
25
25
|
4. **Place and name it** at the registry's location. `docs/structure.md` is the placement authority for anything the registry leaves open.
|
|
26
26
|
5. **Write it from the template, then run its genre procedure.** A template's procedure section binds that genre only: doctrine's is the reason check (`templates/doctrine.md` § Reason check). Keep the template's section names exactly; omit optional sections with nothing real to say. Write the body from the outside in (style guide § Shape), every heading and bold label a noun phrase, every sentence after a label third person, one rule per sentence in the term engineers use (style guide § Sentences, § Vocabulary). In a truth document, every sentence is present tense (§ The three natures). Every line owns a decision: before stating a rule, search the neighbours for it, and cite the one found rather than restating it (style guide § Net-net).
|
|
@@ -41,6 +41,15 @@ Some genres carry steps beyond writing from the template. Each procedure lives i
|
|
|
41
41
|
|
|
42
42
|
A genre with no row here has no procedure beyond its template.
|
|
43
43
|
|
|
44
|
+
## Doctrine and playbooks
|
|
45
|
+
|
|
46
|
+
Two genres are standards rather than accounts of one repository: a doctrine document settles one concern every product handles, and a playbook settles one interface or workflow shape every product builds. Both are read by the next product as much as by this one, so four rules bind them beyond their templates:
|
|
47
|
+
|
|
48
|
+
- **General form:** each reads correctly copied verbatim into another product's repository. It names nothing only its own repository has: the product, its domain entities, its vendors and stack, its people, its own documents by path. An example uses an object every product has, and the repository's own values stay in its feature documents, which name the standard in their Scope.
|
|
49
|
+
- **Extend first:** before a new document is proposed, the existing set is read (`grep -m1 '^description:' docs/architecture/{doctrine,playbooks}/*.md`), and content whose subject one of them already covers lands there as a section, a principle or a row.
|
|
50
|
+
- **New only on a clear distinction:** a new document is written only for a concern or a shape none of the existing ones owns, with decisions of its own (the template's earn-it test).
|
|
51
|
+
- **Never without approval:** an agent never creates a doctrine or playbook document on its own initiative, a baseline and a harvest included. It names the proposed document, the existing documents it was checked against and the distinction between them, then waits for the user's yes in chat. An unattended session files the request as a stop. An edit to an existing document follows the normal loop.
|
|
52
|
+
|
|
44
53
|
## The three natures
|
|
45
54
|
|
|
46
55
|
Every document is exactly one of three things, and the registry names which for every genre. Decide before writing a word: every other rule follows from the split.
|
|
@@ -104,7 +113,8 @@ A document names the objects it designs in `objects:`, and a source file names t
|
|
|
104
113
|
| Document a feature (LLD) | [`templates/feature.md`](templates/feature.md) |
|
|
105
114
|
| Document a feature's data flow or state | The feature doc's Flows (behaviour) and Promises (guarantees); a persisted shape belongs in the API reference (style guide § State at persistence points); component and transient state are not documented |
|
|
106
115
|
| Document a workspace package | [`templates/package.md`](templates/package.md), the residual owner of `packages/{name}/**` |
|
|
107
|
-
| Document one concern the platform handles everywhere (authentication, storage, the API protocol) | [`templates/doctrine.md`](templates/doctrine.md): design and principles, no mechanism, owns no code; written through [`references/document-sets.md`](references/document-sets.md) |
|
|
116
|
+
| Document one concern the platform handles everywhere (authentication, storage, the API protocol) | [`templates/doctrine.md`](templates/doctrine.md): design and principles in general form, no mechanism, no project link, owns no code; written through [`references/document-sets.md`](references/document-sets.md) |
|
|
117
|
+
| Document one interface or workflow shape every product builds (the shell, a settings area, analytics pages, a record's lifecycle) | [`templates/playbook.md`](templates/playbook.md): the shape and its rules in general form, owns no code; created only with the user's approval (§ Doctrine and playbooks) |
|
|
108
118
|
| Write or rework several sibling documents at once (a doctrine folder, a family of features) | [`references/document-sets.md`](references/document-sets.md): usefulness test, term table, rule ledger, render, one cold read |
|
|
109
119
|
| Document one system spanning packages | [`templates/system.md`](templates/system.md): one machine end to end, never a giant feature doc |
|
|
110
120
|
| Record or revise an architecture decision | [`templates/architecture-decision.md`](templates/architecture-decision.md): one AD per subject, revised in place |
|
|
@@ -87,7 +87,7 @@ A finding names the file and line, quotes the text, names its class, and propose
|
|
|
87
87
|
| No decision of its own | A document under about six owned decisions; a section, table or bullet that states nothing the principles do not |
|
|
88
88
|
| Missing reason | A rule that meets a trigger of the style guide's § Sentences (a tempting alternative, a cost, an arbitrary value, an invisible failure) and carries no reason; in doctrine, a reason the reason check judged non-obvious and the document lacks |
|
|
89
89
|
| Filler | A sentence carrying no fact, rule, boundary or reason: a scene-setting opener, a generic truth used as framing, a metaphor or slogan standing in for a rule, a question |
|
|
90
|
-
| Wrong layer | Mechanism, a product's specifics or build state in doctrine; history in a truth document |
|
|
90
|
+
| Wrong layer | Mechanism, a product's specifics or build state in doctrine, or a project link: the repository's own domain example, vendor, stack or document by path; history in a truth document |
|
|
91
91
|
| Fused reason | A rule and its reason joined into one sentence, so a reader cannot tell which half binds |
|
|
92
92
|
| Two readings | A rule a reader could act on in two incompatible ways |
|
|
93
93
|
|
|
@@ -28,7 +28,8 @@ Every genre takes the same shape (principle 2) and the same labels (principle 5)
|
|
|
28
28
|
|
|
29
29
|
| Genre | Nature | Deepest layer | Excluded |
|
|
30
30
|
| --- | --- | --- | --- |
|
|
31
|
-
| Doctrine (`docs/architecture/
|
|
31
|
+
| Doctrine (`docs/architecture/doctrine/`) | Truth | The decisions behind each part and the reasons for them | Any mechanism. A doctrine document describes no code |
|
|
32
|
+
| Playbook (`docs/architecture/playbooks/`) | Truth | What a person sees and does in the shape, part by part: its arrangement, each part's behaviour, its states and promises | Any one product's values: its labels, routes, limits and timings, which are its feature documents'. Any mechanism |
|
|
32
33
|
| System (`docs/architecture/systems/`) | Truth | The machine's behaviour across packages: the order it runs in, sequence and state diagrams at role level, the invariant at each place where parts meet, what re-drives a stall. No single file shows a cross-package flow, so this layer earns its place | Per-function steps, tables and columns, routes |
|
|
33
34
|
| Package (`docs/architecture/packages/`) | Truth | The public contract a consumer touches: commands and their flags, exports, what is installed and where. For a CLI the flags *are* the promise | Internal layout, the build's steps, which file holds what |
|
|
34
35
|
| Feature (`docs/features/`) | Truth | The behaviour a person meets, in full: each state the surface can be in, each edge case, what a rejection tells them, the wording where the wording is promised. This genre runs deepest toward its end, in Promises, because a promise is checked by a person or a test and the code cannot state it as intent | Columns, routes, guards, functions, files |
|
|
@@ -255,7 +255,7 @@ function lintFile(path, findings) {
|
|
|
255
255
|
if (/^[a-z]+:\/\//.test(target) || SCAFFOLD.test(target)) continue;
|
|
256
256
|
// The repo-root README routes by intent (templates/root-readme.md): its Map names owning package and system
|
|
257
257
|
// docs, its Destinations rows name procedures and rules. Individual feature docs stay off it — the ownership index finds them.
|
|
258
|
-
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|
|
|
258
|
+
if (isRootReadme && /(^|\/)docs\/(architecture\/(packages|systems|doctrine|playbooks)\/|operations\/)[^/]+\.md$|(^|\/)\.claude\/rules\/[^/]+\.md$/.test(target)) continue;
|
|
259
259
|
if (isRootReadme && /(^|\/)docs\/features\/[^/]+\.md$/.test(target)) { findings.push({ path, n, severity: 'warn', id: 'root-readme-feature-link', message: `root README links an individual feature doc (\`${target}\`) — point at docs/features/ and let the ownership index find it`, excerpt: line.trim().slice(0, 90) }); continue; }
|
|
260
260
|
findings.push({ path, n, severity: 'block', id: 'index-lists-files', message: `index links an individual document (\`${target}\`) — name the folder and how its files are named; \`ls\` is the roster`, excerpt: line.trim().slice(0, 90) });
|
|
261
261
|
}
|
|
@@ -382,7 +382,7 @@ function lintFile(path, findings) {
|
|
|
382
382
|
// A heading or a bold bullet label names; it never says. A label with a finite verb drags in a tense,
|
|
383
383
|
// a person and a voice ("What was decided", "You already belong to…"), so a label opening with a
|
|
384
384
|
// wh-word or a personal pronoun, or ending in a full stop, is a clause (style-guide § Labels).
|
|
385
|
-
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/
|
|
385
|
+
const isLabelledDoc = isOwningDoc || (scope === 'truth' && !isFolderIndex && /(^|\/)docs\/architecture\/(doctrine|playbooks)\/[^/]+\.md$/.test(path));
|
|
386
386
|
if (isLabelledDoc && !disabled.has('clause-label')) {
|
|
387
387
|
const CLAUSE = /^(how|what|why|where|when|who|which|whose|you|your|we|our|i|it)\b|\b(you|your)\b|\.$/i;
|
|
388
388
|
for (const { n, line } of lines) {
|
|
@@ -413,7 +413,7 @@ function lintFile(path, findings) {
|
|
|
413
413
|
|
|
414
414
|
// A doctrine folder is read as a set: its documents cite each other's principles by number and must not restate each
|
|
415
415
|
// other (templates/doctrine.md, references/document-sets.md), so these checks read the siblings beside the linted file.
|
|
416
|
-
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/
|
|
416
|
+
const DOCTRINE_LOCATION = /(^|\/)docs\/architecture\/doctrine\/[^/]+\.md$/;
|
|
417
417
|
// A doctrine document closes on Terms and Related documents; an older one on "For the agent". The body ends at the first.
|
|
418
418
|
const DOCTRINE_CLOSING = /^##\s+(For the agent|Terms|Related documents)\s*$/;
|
|
419
419
|
// The words a doctrine document replaces with the term engineers use (references/style-guide.md § Vocabulary).
|
|
@@ -17,7 +17,8 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
17
17
|
| Feature | [feature.md](feature.md) | truth | `feature` | `docs/features/{feature}.md` | `type` `description` `status` `objects` | Scope · Promises · For the agent |
|
|
18
18
|
| Package | [package.md](package.md) | truth | `package` | `docs/architecture/packages/{package}.md` | `type` `description` `status` `objects` | Scope · Ownership · Usage · Promises · For the agent |
|
|
19
19
|
| System | [system.md](system.md) | truth | `system` | `docs/architecture/systems/{system}.md` | `type` `description` `status` `objects` | Scope · Flow · Promises · For the agent |
|
|
20
|
-
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/
|
|
20
|
+
| Doctrine | [doctrine.md](doctrine.md) | truth | `doctrine` | `docs/architecture/doctrine/{concern}.md` | `type` `description` `status` `objects` | Principles · Terms · Related documents |
|
|
21
|
+
| Playbook | [playbook.md](playbook.md) | truth | `playbook` | `docs/architecture/playbooks/{subject}.md` | `type` `description` `status` `objects` | Purpose · Related documents |
|
|
21
22
|
| Architecture decision | [architecture-decision.md](architecture-decision.md) | truth | `architecture-decision` | `docs/architecture/decisions/AD-NNN-{subject}.md` | `type` `title` `description` `status` `last-updated` | Decision · Context · Consequences |
|
|
22
23
|
| UX behavior | [ux-behavior.md](ux-behavior.md) | truth | `ux` | `docs/features/{surface}-ux.md` | `type` `description` `status` `objects` | Purpose · Anatomy · States · Affordances · Promises · Transitions |
|
|
23
24
|
| System overview | [system-overview.md](system-overview.md) | truth | `system-overview` | `docs/architecture/system-design.md` | `type` `description` | — |
|
|
@@ -25,7 +26,7 @@ Every fact about a genre lives in one row of the table below: its template, its
|
|
|
25
26
|
| Glossary | [glossary.md](glossary.md) | truth | `glossary` | `docs/architecture/glossary.md` | `type` `description` | — |
|
|
26
27
|
| Non-goals | [non-goals.md](non-goals.md) | truth | `non-goals` | `docs/architecture/non-goals.md` | `type` `description` | — |
|
|
27
28
|
| Security overview | [security-overview.md](security-overview.md) | truth | `security-overview` | `docs/architecture/security.md` | `type` `description` | — |
|
|
28
|
-
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/
|
|
29
|
+
| Testing strategy | [testing-strategy.md](testing-strategy.md) | truth | `testing-strategy` | `docs/architecture/doctrine/testing.md` | `type` `description` | — |
|
|
29
30
|
| API reference | [api-reference.md](api-reference.md) | truth | `api-reference` | `docs/architecture/api.md` | `type` `description` | — |
|
|
30
31
|
| Environment registry | [environment-registry.md](environment-registry.md) | truth | `environment-registry` | `docs/operations/environment.md` | `type` `description` | — |
|
|
31
32
|
| Behavioral checklist | [behavioral-checklist.md](behavioral-checklist.md) | truth | `checklist` | `docs/tests/{feature}.md` | `type` `description` `verifies` | — |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Doctrine documentation (docs/architecture/
|
|
1
|
+
# Doctrine documentation (docs/architecture/doctrine/{concern}.md)
|
|
2
2
|
|
|
3
3
|
Truth document. Use for **one concern the platform handles everywhere**: authentication, authorization, storage, the API protocol, credentials, telemetry. One file per concern, named for the concern. A doctrine document speaks for no code: no source header names it, and it carries no `owns:`. The surfaces bound by it are the documents that design or touch the objects it governs, computed through those objects, and each names it in its Scope.
|
|
4
4
|
|
|
@@ -6,6 +6,8 @@ Truth document. Use for **one concern the platform handles everywhere**: authent
|
|
|
6
6
|
|
|
7
7
|
**A doctrine document is a specification, not a description.** It states how every product handles the concern, so a product whose code differs has a gap to plan and build, and the document never records which product has built what: no `UNBUILT` marker, no "today the product…", no product's name, setting value or code root. When one product's posture against the doctrine matters, it is that product's own security overview, plan or backlog.
|
|
8
8
|
|
|
9
|
+
**A doctrine document is written in general form, never linked to the project that holds it.** It reads correctly when copied verbatim into any other product's repository. So it names nothing only that repository has: the product, its domain entities (a hiring product's candidates), its vendors and stack (a hosting platform, a database engine), its people, or its own documents by path (a feature document, a plan, a decision record). An example uses an object every product has (a member, an invoice, an uploaded file); where a domain object is unavoidable, it comes from a kind of product other than the repository's own. Its links reach only sibling doctrine documents and published standards. The project's own facts belong to its feature documents, system overview and decision records, which name this document in their Scope.
|
|
10
|
+
|
|
9
11
|
**Earn the document before writing it.** List the decisions only this document would own, each checked against the documents that exist: a decision another document already states is a citation, not an owned decision. Fewer than about six owned decisions means the concern is a section of a neighbour, not a document (`references/document-sets.md` § Usefulness test).
|
|
10
12
|
|
|
11
13
|
**Required:**
|
|
@@ -98,4 +100,5 @@ Principles are cited by number, sections by heading, always in parentheses: "(pr
|
|
|
98
100
|
| The one feature that happens to use the concern most | That feature's own document, which names this document in its Scope |
|
|
99
101
|
| The story of how the concern got this way | The subject's architecture decision (`decisions/`), whose Decision Changelog tail carries how it moved, and the changelog for the when |
|
|
100
102
|
| A record of which product has built which part | That product's plan, backlog, or security overview |
|
|
103
|
+
| A description of the repository it sits in: its product, domain, stack or documents | That product's feature documents, system overview and decision records |
|
|
101
104
|
| A restatement of a sibling's rule for completeness | A citation of the sibling's principle, with the one clause of context the sentence needs |
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Playbook documentation (docs/architecture/playbooks/{subject}.md)
|
|
2
|
+
|
|
3
|
+
Truth document. Use for **one interface or workflow shape every product builds**: the shell, a settings area, analytics pages, a record's lifecycle, the interaction patterns, automated agents. One file per shape, named for it. A playbook speaks for no code: no source header names it, and it carries no `owns:`. The product's own feature documents hold its instance of the shape, and each names the playbook in its Scope.
|
|
4
|
+
|
|
5
|
+
**A playbook states the shape, never one product's instance of it.** It says what the parts are, how they are arranged, how each behaves and what the shape promises, so a new instance is new content poured into a known shape rather than a new layout. A product's own values (its labels, routes, limits, timings, the wording of its copy) belong to its feature document. A playbook explains no mechanism, for the reasons a doctrine document explains none (`templates/doctrine.md`).
|
|
6
|
+
|
|
7
|
+
**Doctrine or playbook.** A doctrine document settles the decisions of one concern every product handles, such as how authorization works. A playbook settles what a person sees and does in one recurring shape, such as how a settings page is laid out. A playbook cites the doctrine its shape rests on, and never restates it.
|
|
8
|
+
|
|
9
|
+
**A playbook is written in general form, never linked to the project that holds it.** It reads correctly when copied verbatim into any other product's repository, under the same rule a doctrine document keeps (`templates/doctrine.md`). Where the shape needs a word for the product's own record, the Purpose introduces a generic one once ("the subject: the record a report is read per, such as a job in a hiring product") and the body uses it throughout. Its links reach only sibling playbooks, doctrine documents and published standards.
|
|
10
|
+
|
|
11
|
+
**Earn the document, and ask before writing it.** List the rules only this playbook would own, each checked against the playbooks and doctrine that exist. Fewer than about six owned rules means the shape is a section of a neighbour, not a document. A new playbook is created only with the user's approval (SKILL.md § Doctrine and playbooks).
|
|
12
|
+
|
|
13
|
+
**Required:**
|
|
14
|
+
|
|
15
|
+
- the frontmatter
|
|
16
|
+
- **Purpose:** what the shape is for, what it is not for, and the generic words it introduces
|
|
17
|
+
- **Related documents**
|
|
18
|
+
|
|
19
|
+
**Optional, each only with something real to say:**
|
|
20
|
+
|
|
21
|
+
- **Anatomy:** an ASCII diagram of the arrangement with each region labelled, then one bullet per region
|
|
22
|
+
- **One section per part** that carries rules of its own, named for the part ("Roster", "Readiness", "Side peek")
|
|
23
|
+
- **States:** one table — State · Entered when · The person sees · Can do
|
|
24
|
+
- **Affordances:** what a person can do, one bullet per verb
|
|
25
|
+
- **Principles:** numbered, each a noun-phrase label and its rule
|
|
26
|
+
- **Promises:** what the shape guarantees, grouped under `###` headings where there are several kinds
|
|
27
|
+
- **Transitions:** what moves the shape between its states, and who moves it
|
|
28
|
+
|
|
29
|
+
Sections run Purpose first and Related documents last; the rest follow the order above. Each is named for its concept, never for its layer. Expect one hundred to two hundred lines.
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
---
|
|
33
|
+
type: playbook # required tooling key
|
|
34
|
+
description: "{at most 300 characters: the shape this playbook settles, and that a product's own instance is its feature doc}" # required
|
|
35
|
+
status: active # required — active | draft
|
|
36
|
+
objects: [rail, command palette, account menu] # required — the parts this playbook shapes, by their graph names
|
|
37
|
+
---
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
# {Shape} playbook
|
|
42
|
+
|
|
43
|
+
## Purpose
|
|
44
|
+
|
|
45
|
+
{What the shape is for and what it is not for, in two or three sentences. The generic words it introduces, each in one bullet.}
|
|
46
|
+
|
|
47
|
+
## Anatomy
|
|
48
|
+
|
|
49
|
+
{An ASCII diagram, then one bullet per labelled region.}
|
|
50
|
+
|
|
51
|
+
## {Part}
|
|
52
|
+
|
|
53
|
+
- **{Rule label}:** {the rule, in general form}
|
|
54
|
+
|
|
55
|
+
## Principles
|
|
56
|
+
|
|
57
|
+
1. **{Label}:** {the rule}. {Its reason, as a separate sentence.}
|
|
58
|
+
|
|
59
|
+
## Related documents
|
|
60
|
+
|
|
61
|
+
- [{Sibling playbook}]({file}.md): {what it settles beside this one}
|
|
62
|
+
- [{Doctrine}](../doctrine/{concern}.md): {the decision this shape rests on}
|
|
63
|
+
```
|
|
@@ -130,7 +130,7 @@ The rest of this page is for the team and for coding agents working in this repo
|
|
|
130
130
|
|
|
131
131
|
The baseline **does** scaffold `/README.md` when it is missing, and it fills each section from the repo:
|
|
132
132
|
|
|
133
|
-
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/
|
|
133
|
+
- **Identity, Capabilities, Deployment:** from the product charter and vision documents under `docs/architecture/doctrine/` (what the product is, the core loop, who it is for), the package manifests (what publishes where), and the deploy config (addresses). Written in a user's terms; where the charter is missing, the baseline writes Identity only and raises a `note:` row for the rest.
|
|
134
134
|
- **The main path:** from the system overview.
|
|
135
135
|
- **Map:** from the workspace globs and the ownership index.
|
|
136
136
|
- **Rules:** from `docs/operations/protected-files.md` and the architecture doctrine.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Testing strategy (docs/architecture/
|
|
1
|
+
# Testing strategy (docs/architecture/doctrine/testing.md)
|
|
2
2
|
|
|
3
3
|
Truth document. Use for: the repo's test posture — what is tested at which level, how to run it, and what is deliberately untested. The framework choices come from the harness testing rule; this doc records how *this repo* applies them.
|
|
4
4
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UX behavior (docs/features/{surface}-ux.md)
|
|
2
2
|
|
|
3
|
-
**Truth document.** States what a person sees and can do on a surface, and what the surface promises never to do. Present tense, like every truth document.
|
|
3
|
+
**Truth document.** States what a person sees and can do on a surface, and what the surface promises never to do. Present tense, like every truth document. It is one product's surface; a shape every product's surfaces share, such as a settings area or an analytics page, is a playbook (`templates/playbook.md`), which the UX document names in its Purpose and fills with its own values.
|
|
4
4
|
|
|
5
5
|
**`type: ux`**, plus `status:` and `objects:` — the objects the surface presents (`references/frontmatter.md`). The doctrine it answers to (the state vocabulary, the routing doctrine, the information architecture) is named in its Purpose for people and computed through shared objects for tooling.
|
|
6
6
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nurix/etna",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.47",
|
|
4
4
|
"description": "Bootstrap a project with a Nurix harness — writes the committed contract (CLAUDE.md, AGENTS.md, .claude/harness.json, settings.json, the mount hook) and mounts the harness itself, gitignored, from a bundled payload; sessions keep it current from the CDN.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"agent",
|
|
47
47
|
"plugin"
|
|
48
48
|
],
|
|
49
|
-
"etnaFingerprint": "
|
|
49
|
+
"etnaFingerprint": "7097a106c017"
|
|
50
50
|
}
|