@nurix/etna 0.20.24 → 0.20.26
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 +1 -1
- package/data/harnesses/code/agents/change-review.md +1 -1
- package/data/harnesses/code/agents/lld-mode.md +1 -1
- package/data/harnesses/code/agents/maintenance.md +7 -9
- package/data/harnesses/code/docs/structure.md +133 -71
- package/data/harnesses/code/harness.json +4 -4
- package/data/harnesses/code/hooks/awareness.mjs +0 -10
- package/data/harnesses/code/hooks/hooks.json +4 -4
- package/data/harnesses/code/hooks/lib/ownership.mjs +4 -2
- package/data/harnesses/code/hooks/lib/sweep.mjs +4 -8
- package/data/harnesses/code/skills/design/components/index.md +1 -1
- package/data/harnesses/code/skills/documentation/SKILL.md +20 -9
- package/data/harnesses/code/skills/documentation/references/frontmatter.md +3 -3
- package/data/harnesses/code/skills/documentation/references/style-guide.md +70 -27
- package/data/harnesses/code/skills/documentation/references/untemplated.md +2 -1
- package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +87 -1
- package/data/harnesses/code/skills/documentation/templates/Readme.md +4 -3
- package/data/harnesses/code/skills/documentation/templates/bookkeeping.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/docs-readme.md +15 -36
- package/data/harnesses/code/skills/documentation/templates/doctrine.md +82 -0
- package/data/harnesses/code/skills/documentation/templates/feature.md +31 -123
- package/data/harnesses/code/skills/documentation/templates/handover.md +174 -0
- package/data/harnesses/code/skills/documentation/templates/operations-procedure.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/package.md +28 -94
- package/data/harnesses/code/skills/documentation/templates/root-readme.md +103 -65
- package/data/harnesses/code/skills/documentation/templates/setup.md +138 -0
- package/data/harnesses/code/skills/documentation/templates/system.md +25 -109
- package/data/harnesses/delivery/agents/lld-mode.md +1 -1
- package/data/harnesses/delivery/agents/maintenance.md +7 -9
- package/data/harnesses/delivery/harness.json +2 -2
- package/data/harnesses/delivery/hooks/hooks.json +1 -1
- package/data/harnesses/delivery/skills/documentation/SKILL.md +20 -9
- package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +3 -3
- package/data/harnesses/delivery/skills/documentation/references/style-guide.md +70 -27
- package/data/harnesses/delivery/skills/documentation/references/untemplated.md +2 -1
- package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +87 -1
- package/data/harnesses/delivery/skills/documentation/templates/Readme.md +4 -3
- package/data/harnesses/delivery/skills/documentation/templates/bookkeeping.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/docs-readme.md +15 -36
- package/data/harnesses/delivery/skills/documentation/templates/doctrine.md +82 -0
- package/data/harnesses/delivery/skills/documentation/templates/feature.md +31 -123
- package/data/harnesses/delivery/skills/documentation/templates/handover.md +174 -0
- package/data/harnesses/delivery/skills/documentation/templates/operations-procedure.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/package.md +28 -94
- package/data/harnesses/delivery/skills/documentation/templates/root-readme.md +103 -65
- package/data/harnesses/delivery/skills/documentation/templates/setup.md +138 -0
- package/data/harnesses/delivery/skills/documentation/templates/system.md +25 -109
- package/data/harnesses/discussion/harness.json +2 -2
- package/data/harnesses/discussion/hooks/hooks.json +1 -1
- package/data/harnesses/etna/agents/change-review.md +1 -1
- package/data/harnesses/etna/agents/lld-mode.md +1 -1
- package/data/harnesses/etna/agents/maintenance.md +7 -9
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/hooks/hooks.json +1 -1
- package/data/harnesses/etna/skills/documentation/SKILL.md +20 -9
- package/data/harnesses/etna/skills/documentation/references/frontmatter.md +3 -3
- package/data/harnesses/etna/skills/documentation/references/style-guide.md +70 -27
- package/data/harnesses/etna/skills/documentation/references/untemplated.md +2 -1
- package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +87 -1
- package/data/harnesses/etna/skills/documentation/templates/Readme.md +4 -3
- package/data/harnesses/etna/skills/documentation/templates/bookkeeping.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/docs-readme.md +15 -36
- package/data/harnesses/etna/skills/documentation/templates/doctrine.md +82 -0
- package/data/harnesses/etna/skills/documentation/templates/feature.md +31 -123
- package/data/harnesses/etna/skills/documentation/templates/handover.md +174 -0
- package/data/harnesses/etna/skills/documentation/templates/operations-procedure.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/package.md +28 -94
- package/data/harnesses/etna/skills/documentation/templates/root-readme.md +103 -65
- package/data/harnesses/etna/skills/documentation/templates/setup.md +138 -0
- package/data/harnesses/etna/skills/documentation/templates/system.md +25 -109
- package/lib/install/materialize.mjs +18 -14
- package/lib/install/plan.mjs +4 -11
- package/package.json +3 -2
- package/data/harnesses/code/skills/documentation/templates/structure.md +0 -71
- package/data/harnesses/delivery/skills/documentation/templates/structure.md +0 -71
- package/data/harnesses/etna/skills/documentation/templates/structure.md +0 -71
|
@@ -1,104 +1,142 @@
|
|
|
1
|
-
# Project Root README
|
|
1
|
+
# Project Root README (/README.md)
|
|
2
2
|
|
|
3
|
-
Truth document. Use for: the
|
|
3
|
+
Truth document. Use for: the repository root `/README.md`. It opens outward — what the product does, for whom, and where it runs, in a user's terms — then turns inward for the team and coding agents: what state the repo is in, how it works, what its parts are and what must not break, then routes every other question into `docs/`. The outward part comes first because it is what a stranger reads; the inward part is longer because it is what the team uses. **It hosts nothing it can link to.** It is distinct from `docs/README.md`, which describes the folders of the docs tree, and from `CLAUDE.md` / `AGENTS.md`, the standing rules an agent loads. It points at both and restates neither.
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### What it carries
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Sections come in this order; 1 to 3 are the outward-facing first screen, 4 onward the inward page. Skip any section the repo cannot fill truthfully. Never pad it, and never leave a stub.
|
|
8
8
|
|
|
9
|
-
1. **
|
|
10
|
-
2. **What
|
|
11
|
-
3. **
|
|
12
|
-
4. **
|
|
13
|
-
5. **
|
|
14
|
-
6. **How
|
|
15
|
-
7. **What
|
|
9
|
+
1. **Identity.** The H1 plus two or three sentences in a user's terms: what the product is, where it runs, and what using it is like.
|
|
10
|
+
2. **What it does.** Two to four paragraphs, outward-facing: the core loop as a person meets it, what surrounds that loop, how the product follows the person across its surfaces, and who it is for, closing with what it deliberately is not. Plain words, no feature roster, no superlatives.
|
|
11
|
+
3. **Where it runs.** One paragraph: the addresses, the distributed artifacts and where they are published, where identity comes from, and whether a self-hosted edition exists.
|
|
12
|
+
4. **The turn.** A rule and one paragraph saying the rest is for the team and agents, with the scope line: what lives in this repo and what lives elsewhere.
|
|
13
|
+
5. **State.** Optional: a one-line blockquote, present only while something repo-wide is true that a newcomer must know (frozen, mid-migration, mid-breaking-change on `dev`). It links the plan or runbook that owns the change. Say here which units publish and where, if any do.
|
|
14
|
+
6. **How it works.** Three to six short paragraphs, or numbered stages, along the main request or data path: who arrives, what is built, where state lives, how the result leaves. Name a technology only where it explains a stage. End with a link to the system overview.
|
|
15
|
+
7. **What is in the repository.** A sentence on how the tree is organised, then one table row per top-level unit (package, app, service), giving its path, what it is in one line, and its owning doc; the top folders that are not packages get a sentence each. This replaces the tree.
|
|
16
|
+
8. **Rules that bite.** Optional. At most five one-line invariants, public/internal boundaries, never-edit paths (generated, protected) or non-goals, each linked to the doc that argues it.
|
|
17
|
+
9. **Where to go.** An intent-to-destination table. Its last row is the one for agents.
|
|
18
|
+
10. **Everyday commands.** Optional. At most four commands run on every change, ending at the gate a push must pass. First-run commands are never here.
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
### What it leaves out
|
|
21
|
+
|
|
22
|
+
| Left out | Lives in |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| Getting started, install, prerequisites, env files, first run, troubleshooting | `docs/operations/setup.md` |
|
|
25
|
+
| Stack: technologies, what each is for, where its version is pinned | `docs/operations/setup.md` § Stack. Why it was chosen goes in `docs/architecture/` |
|
|
26
|
+
| Directory or file tree | `docs/structure.md`, the only tree |
|
|
27
|
+
| Daily loop, watch modes, per-package traps | `docs/operations/local-development.md` |
|
|
28
|
+
| Contributing procedure, branch model, commit rules | `CLAUDE.md` and `docs/operations/` |
|
|
29
|
+
| Feature list, news, "what's new", roadmap, changelog | `docs/features/`, `docs/history/` |
|
|
30
|
+
| Badges, logos, images, screenshots, social links, sponsors, hand-written TOC, license text, superlatives | Nowhere. The outward sections are plain prose too. Agents cannot read them and the team does not need them. License is one line or absent |
|
|
18
31
|
|
|
19
32
|
### Writing rules
|
|
20
33
|
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
34
|
+
- **Budget: between 60 and 140 non-blank lines, the outward part under a third of it.** Prose is welcome — How it works and Rules that bite read better as paragraphs than as rows — but past the ceiling a section is hosting content that belongs in a doc. Move it and leave the link.
|
|
35
|
+
- **Every section is either a fact the reader acts on or a link.** If a section needs a paragraph, it needs a doc.
|
|
36
|
+
- **One H1, then H2 sections, in plain Markdown.** Sentence-case headings, no emoji, no HTML layout, and no `#` comment lines inside code blocks (they parse as H1s).
|
|
37
|
+
- **Relative links into the repo only.** Never an absolute repo URL. Never an external site as the home of a canonical doc.
|
|
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 Where-to-go row needs it. Never list individual feature docs: the ownership index finds them.
|
|
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 that the index's `owns:` globs resolve to. When the two disagree the index wins, and the Map row is fixed in the same change.
|
|
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
|
+
- **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
|
+
- **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.
|
|
29
44
|
|
|
30
45
|
### Template
|
|
31
46
|
|
|
32
47
|
```markdown
|
|
33
|
-
# {
|
|
48
|
+
# {Project name}
|
|
34
49
|
|
|
35
|
-
{
|
|
50
|
+
{Two or three sentences in a user's terms: what the product is, where it runs, what using it is like.}
|
|
36
51
|
|
|
37
|
-
## What
|
|
52
|
+
## What it does
|
|
38
53
|
|
|
39
|
-
|
|
40
|
-
- **{Family 2}** — {what's in it.}
|
|
41
|
-
- **{Family 3}** — {what's in it.}
|
|
54
|
+
{The core loop as a person meets it: what they bring, what the product does, what they get back and what record it leaves.}
|
|
42
55
|
|
|
43
|
-
|
|
56
|
+
{What surrounds the loop: the capabilities a team needs to run it, named plainly.}
|
|
44
57
|
|
|
45
|
-
|
|
58
|
+
{How it follows the person across surfaces: CLI, desktop, integrations, agents' doors.}
|
|
46
59
|
|
|
47
|
-
|
|
48
|
-
- {Build tool} {version}
|
|
49
|
-
- {Other load-bearing deps}
|
|
60
|
+
{Who it is for, and what it deliberately is not.}
|
|
50
61
|
|
|
51
|
-
##
|
|
62
|
+
## Where it runs
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
{pm} install
|
|
55
|
-
{pm} run dev # what it does (port, URL)
|
|
56
|
-
{pm} run build # what it does
|
|
57
|
-
{pm} run preview # what it does (omit if not applicable)
|
|
58
|
-
\`\`\`
|
|
64
|
+
{Addresses; distributed artifacts and where they publish; where identity comes from; whether a self-hosted edition exists.}
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
---
|
|
61
67
|
|
|
62
|
-
|
|
68
|
+
The rest of this page is for the team and for coding agents working in this repository. {What lives in this repo and what lives elsewhere.} {One sentence: the sections below route every other question into docs/.}
|
|
63
69
|
|
|
64
|
-
{
|
|
70
|
+
> **State:** {one line: what is in flux right now; which units publish where} → [{plan or runbook}](docs/history/{plans|migrations}/{file}.md) ← optional; delete when the flux ends
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
| -------------------- | ---------------------- |
|
|
68
|
-
| `{route or command}` | {description} |
|
|
72
|
+
## How it works
|
|
69
73
|
|
|
70
|
-
|
|
74
|
+
1. **{Stage}**: {what happens, naming the mechanism only where it explains the step}.
|
|
75
|
+
2. **{Stage}**: {…}.
|
|
76
|
+
3. **{Stage}**: {where state lives}.
|
|
77
|
+
4. **{Stage}**: {the outbound effect: what is written, deployed or returned}.
|
|
71
78
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
└── {folder}/ # {purpose}
|
|
78
|
-
\`\`\`
|
|
79
|
+
{Optional, one sentence naming the seam everything passes through, e.g. "Every client reaches data through `{path}`; nothing else talks to the database."} The full account: [{system overview}](docs/architecture/{overview}.md).
|
|
80
|
+
|
|
81
|
+
## What is in the repository
|
|
82
|
+
|
|
83
|
+
{One or two sentences: the workspace shape and where each unit's design document lives.}
|
|
79
84
|
|
|
80
|
-
|
|
85
|
+
| Path | Role | Owning doc |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| [`{packages/unit}/`]({packages/unit}/) | {one line: what it is and who consumes it} | [{doc}](docs/architecture/packages/{unit}.md) |
|
|
88
|
+
| [`{packages/unit}/`]({packages/unit}/) | {one line} | [{doc}]({path}) |
|
|
89
|
+
| [`{top-folder}/`]({top-folder}/) | {one line; "generated" or "reference only" where true} | — |
|
|
81
90
|
|
|
82
|
-
|
|
83
|
-
2. {Re-export from the public surface.}
|
|
84
|
-
3. {Wire into the playground / example app / test harness so reviewers can see it render.}
|
|
85
|
-
4. {Document any divergence from upstream in `docs/{...}`.}
|
|
91
|
+
{One sentence each for the top folders that are not packages — `registry/`, `scripts/`, `docs/` — with their owning doc.} Where every doc lives: [docs/structure.md](docs/structure.md).
|
|
86
92
|
|
|
87
|
-
##
|
|
93
|
+
## Rules that bite
|
|
88
94
|
|
|
89
|
-
{
|
|
95
|
+
{One sentence saying these are stated here and argued in the documents they point at.}
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
- **{Invariant}**: {the rule, one line}. → [{source}]({path})
|
|
98
|
+
- **{Boundary}**: `{path}` is the only {public | published} surface; everything else is internal.
|
|
99
|
+
- **{Never-edit paths}**: `{path}` is {generated by `{command}` | protected}; edit {the source} instead.
|
|
100
|
+
- **Not here:** {a non-goal stated as a refusal}.
|
|
92
101
|
|
|
93
|
-
|
|
102
|
+
## Where to go
|
|
94
103
|
|
|
95
|
-
|
|
104
|
+
| I want to… | Go to |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| Set up a machine and run it the first time | [docs/operations/setup.md](docs/operations/setup.md) |
|
|
107
|
+
| Work on it day to day | [docs/operations/{local-development}.md](docs/operations/{local-development}.md) |
|
|
108
|
+
| Understand the design | [docs/architecture/](docs/architecture/) |
|
|
109
|
+
| Change a feature | [docs/features/](docs/features/), found through the ownership index |
|
|
110
|
+
| Change a contract ({db schema · wire envelope · auth · events · env}) | [{contract doc}]({path}), boundary-checked before the change |
|
|
111
|
+
| Ship or release | [docs/operations/{deployment}.md](docs/operations/{deployment}.md) |
|
|
112
|
+
| Place a new doc | [docs/structure.md](docs/structure.md) · [docs/README.md](docs/README.md) |
|
|
113
|
+
| See what changed and what is open | [CHANGELOG](docs/history/CHANGELOG.md) · [backlog](docs/history/backlog.md) |
|
|
114
|
+
| Work here as a coding agent | [CLAUDE.md](CLAUDE.md) ([AGENTS.md](AGENTS.md) for other runtimes): load before any edit |
|
|
96
115
|
|
|
97
|
-
|
|
116
|
+
## Everyday commands
|
|
117
|
+
|
|
118
|
+
\`\`\`bash
|
|
119
|
+
{pm} {typecheck} # {what it checks}
|
|
120
|
+
{pm} {test} # {the unit suite, from a cold shell}
|
|
121
|
+
{pm} {gate} # {what a change must pass before it is pushed}
|
|
122
|
+
\`\`\`
|
|
123
|
+
|
|
124
|
+
`{pm} --filter <unit> run` lists a unit's own scripts.
|
|
125
|
+
|
|
126
|
+
{License, one line, e.g. "UNLICENSED, internal use only."} ← optional
|
|
98
127
|
```
|
|
99
128
|
|
|
100
129
|
### Baseline scaffolding behavior
|
|
101
130
|
|
|
102
|
-
The baseline
|
|
131
|
+
The baseline **does** scaffold `/README.md` when it is missing, and it fills each section from the repo:
|
|
132
|
+
|
|
133
|
+
- **Identity, What it does, Where it runs:** from the product charter and vision documents under `docs/architecture/common/` (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
|
+
- **How it works:** from the system overview.
|
|
135
|
+
- **Map:** from the workspace globs and the ownership index.
|
|
136
|
+
- **Rules:** from `docs/operations/protected-files.md` and the architecture doctrine.
|
|
137
|
+
- **Commands:** from the lockfile and the root scripts.
|
|
138
|
+
- **Where to go:** only the rows whose targets exist.
|
|
139
|
+
|
|
140
|
+
It drops every section it cannot fill concretely.
|
|
103
141
|
|
|
104
|
-
**
|
|
142
|
+
**It never overwrites an existing root README.** If one is present, even sparse or outdated, the baseline leaves it untouched and raises a `note:` row proposing the reshape. Reshaping is a separate, explicit request. When it runs, each removed section moves in the same change: getting-started, install and env content and the Stack go to `docs/operations/setup.md`, a drawn tree becomes the Map (the tree itself stays in `structure.md`), and a contributing checklist goes to `docs/operations/`. Nothing is deleted without a home.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Setup (docs/operations/setup.md)
|
|
2
|
+
|
|
3
|
+
Truth document, operations genre (`type: operations`). Use for: **the one-time path from a fresh machine to a first verified run.** It covers what the repo is built on, prerequisites, install, env files, local services, the first commands, proof that it works, and the failures a fresh clone hits. There is **one per repo**, always named `setup.md`, and it is a mandated scaffold. The root README's only setup content is a Where-to-go row linking it.
|
|
4
|
+
|
|
5
|
+
It is distinct from `local-development.md`, the daily loop: watch modes, resetting state, per-package traps, the inner test cycle. Setup runs once per machine. Local development runs every session. Setup ends by handing off to it, and neither copies the other. Deploying is `deployment.md`.
|
|
6
|
+
|
|
7
|
+
Required: **Goal**, **Stack**, **Prerequisites**, **Install**, **First run**, **Verify**. Optional, included only with real content: the audience fork, **Before cloning**, **Environment files**, **Local services**, **Reset**, **Common failures**.
|
|
8
|
+
|
|
9
|
+
### Writing rules
|
|
10
|
+
|
|
11
|
+
- **Never write a version a file already pins.** Each row names the pinning file (`.nvmrc`, `package.json` `engines` / `packageManager`, `rust-toolchain.toml`, `go.mod`) and the command that checks it. A typed number is a second source that drifts on the next bump.
|
|
12
|
+
- **Stack is one row per layer.** Give the technology, what it is for in this repo, and where its version is pinned. Why it was chosen belongs to `docs/architecture/`, linked once. Don't repeat the dependency list: the manifests are the roster.
|
|
13
|
+
- **One path, from the lockfile.** `pnpm-lock.yaml` gives pnpm, `yarn.lock` gives yarn, otherwise npm. Say how it is activated (`corepack enable`). Never write "or npm / yarn". Give a per-OS variant only where the commands really differ.
|
|
14
|
+
- **Traps before commands.** Anything a fresh clone hits before the first command goes in Before cloning, not halfway down the steps: LFS, submodules, a required stub file, an access grant, a credential that has to be requested.
|
|
15
|
+
- **Copy-paste runnable, one action per step, each with its expected outcome.** Run non-interactively where the tool allows it, so an agent can execute the step unchanged. "It should work now" is never a step.
|
|
16
|
+
- **Script first, then document.** If a bootstrap script or task exists, it is the step, and the doc carries only the judgment around it. A setup sequence run often with no script gets a script.
|
|
17
|
+
- **Env files as a table, never a variable dump.** One row per file: where it is copied from, what must be filled, where the values come from, whether it is required to boot. The variable contract is the example file and the code's env type, linked and never copied. Secret values never appear.
|
|
18
|
+
- **Destructive commands state their blast radius where they appear:** what is deleted or truncated, whether it prompts, and the flag that skips the prompt.
|
|
19
|
+
- **Verification is observable:** a health probe and its expected status, a URL and what it shows, a seeded login, a doctor command and its output, or the test command passing from a cold shell.
|
|
20
|
+
- **Common failures are keyed by the exact error text,** followed by the cause and the fix, where the fix is a command or a file edit. Append a failure only once it has been root-caused, never as speculation.
|
|
21
|
+
- **Per-package divergence is a subsection of this file.** A unit whose setup differs gets a `### {unit}` block under First run, or a row linking its own procedure. It never gets a second setup doc.
|
|
22
|
+
|
|
23
|
+
### Template
|
|
24
|
+
|
|
25
|
+
```markdown
|
|
26
|
+
---
|
|
27
|
+
type: operations
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Setup
|
|
31
|
+
|
|
32
|
+
**Goal:** take a fresh machine from clone to a verified first run of {what runs: e.g. "the product Worker on :{port} and the web shell on :{port}, signed in as the dev fixture"}. Done once per machine; the daily loop is [`local-development.md`]({local-development}.md).
|
|
33
|
+
|
|
34
|
+
{Optional audience fork: "Only running {the CLI / one package}? Stop after Install and see [`{procedure}`]({path})."}
|
|
35
|
+
|
|
36
|
+
## Stack
|
|
37
|
+
|
|
38
|
+
| Layer | Technology | For | Version pinned by |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| {Runtime} | {Node} | {what runs on it} | [`.nvmrc`](../../.nvmrc) |
|
|
41
|
+
| {Server} | {framework · data layer} | {what it serves} | [`{package.json}`](../../{path}/package.json) |
|
|
42
|
+
| {Client} | {framework · build tool · styling} | {which surfaces} | [`{package.json}`](../../{path}/package.json) |
|
|
43
|
+
| {Tests} | {runners} | {unit · e2e} | {manifest} |
|
|
44
|
+
|
|
45
|
+
Why these were chosen: [{system overview}](../architecture/{overview}.md).
|
|
46
|
+
|
|
47
|
+
## Prerequisites
|
|
48
|
+
|
|
49
|
+
| Tool | Version from | Check |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| {Node} | [`.nvmrc`](../../.nvmrc) | `node -v` |
|
|
52
|
+
| {pnpm} | `packageManager` in [`package.json`](../../package.json), via `corepack enable` | `pnpm -v` |
|
|
53
|
+
| {Docker / Rust / wrangler / …} | {pinning file, or "any current"} | `{command}` |
|
|
54
|
+
|
|
55
|
+
- **Access:** {repo, secrets store, cloud account, registry token, and who grants each}.
|
|
56
|
+
|
|
57
|
+
## Before cloning ← delete if none
|
|
58
|
+
|
|
59
|
+
- **{Trap}:** {what a fresh clone hits and the one command that clears it, e.g. `git lfs install`, `git submodule update --init`}.
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
\`\`\`bash
|
|
64
|
+
git clone {url} && cd {repo}
|
|
65
|
+
corepack enable
|
|
66
|
+
{pm} install # expect: {what a clean install ends with}
|
|
67
|
+
\`\`\`
|
|
68
|
+
|
|
69
|
+
## Environment files ← delete if none
|
|
70
|
+
|
|
71
|
+
| File | Copy from | Fill | Values come from | Required to boot |
|
|
72
|
+
| --- | --- | --- | --- | --- |
|
|
73
|
+
| `{path/.dev.vars}` | `{path/.dev.vars.example}` | {keys, or "every blank"} | {vault, the owner, a generated local value} | {yes / no} |
|
|
74
|
+
|
|
75
|
+
The contract is [`{example file or typed Env}`](../../{path}). Read it before concluding a value is missing. {Which of these files are gitignored.}
|
|
76
|
+
|
|
77
|
+
## Local services ← delete if none
|
|
78
|
+
|
|
79
|
+
| Service | Start | Port | Ready when |
|
|
80
|
+
| --- | --- | --- | --- |
|
|
81
|
+
| {local DB / emulator} | `{command}` | {port} | `{probe}` returns {expected} |
|
|
82
|
+
|
|
83
|
+
## First run
|
|
84
|
+
|
|
85
|
+
1. Prepare local state.
|
|
86
|
+
|
|
87
|
+
\`\`\`bash
|
|
88
|
+
{pm} --filter {unit} {migrate-and-seed} # expect: {output}
|
|
89
|
+
\`\`\`
|
|
90
|
+
|
|
91
|
+
2. Start the {backend}.
|
|
92
|
+
|
|
93
|
+
\`\`\`bash
|
|
94
|
+
{pm} --filter {unit} {dev:server} # expect: listening on :{port}
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
3. Start the {frontend / CLI}.
|
|
98
|
+
|
|
99
|
+
\`\`\`bash
|
|
100
|
+
{pm} --filter {unit} {dev} # expect: {URL}
|
|
101
|
+
\`\`\`
|
|
102
|
+
|
|
103
|
+
4. Open `{http://localhost:port}` and {sign in as `{seeded user}` / reach `{first screen}`}.
|
|
104
|
+
|
|
105
|
+
## Verify
|
|
106
|
+
|
|
107
|
+
- `{curl -fsS http://localhost:{port}/{health}}` returns `200` {and `{body}`}.
|
|
108
|
+
- `{doctor or status command}` reports {expected}. ← keep whichever exists
|
|
109
|
+
- `{pm} {test}` passes from this cold shell ({expected tail}).
|
|
110
|
+
|
|
111
|
+
## Reset ← delete if none
|
|
112
|
+
|
|
113
|
+
| Command | Deletes | Prompts? |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| `{pm} {reset-script}` | {local state at `{path}`} | {yes; `{FORCE=1}` skips / no} |
|
|
116
|
+
|
|
117
|
+
## Common failures
|
|
118
|
+
|
|
119
|
+
- **`{exact error text}`.** {Cause.} Fix: `{command or file edit}`.
|
|
120
|
+
|
|
121
|
+
## Next
|
|
122
|
+
|
|
123
|
+
The daily loop and its per-package traps: [`local-development.md`]({local-development}.md). Deploying: [`deployment.md`](deployment.md). Standing rules: [`CLAUDE.md`](../../CLAUDE.md).
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Baseline scaffolding behavior
|
|
127
|
+
|
|
128
|
+
The baseline scaffolds `docs/operations/setup.md` when it is missing and the repo has a lockfile and at least one runnable root script. It fills each section from the repo:
|
|
129
|
+
|
|
130
|
+
- **Stack:** from the workspace manifests, one row per layer.
|
|
131
|
+
- **Prerequisites:** from the pinning files.
|
|
132
|
+
- **Install and First run:** from the root README's getting-started block (if one exists) and the root scripts.
|
|
133
|
+
- **Environment files:** from every `*.example` / `.dev.vars.example` in the workspace.
|
|
134
|
+
- **Verify:** from a health route, a doctor command or the test script.
|
|
135
|
+
|
|
136
|
+
It leaves Common failures empty until a failure is root-caused, and it drops every section it cannot fill concretely.
|
|
137
|
+
|
|
138
|
+
**It never overwrites an existing `setup.md`.** If the repo already has a first-run procedure under another name, the baseline proposes the rename with a `note:` row and does not create a second file.
|
|
@@ -1,143 +1,59 @@
|
|
|
1
1
|
# System Documentation (docs/architecture/systems/{system}.md)
|
|
2
2
|
|
|
3
|
-
Truth document. Use for: **one machine that spans packages
|
|
3
|
+
Truth document. Use for: **one machine that spans packages**, which a person cannot point at — the walk, the egress, the ingest, the settlement, a gate, a sweep, a door an agent calls. One file per machine, named for the machine. A thing a person navigates to is a feature document, even when it owns tables; a unit of distribution is a package document.
|
|
4
4
|
|
|
5
|
-
**A
|
|
5
|
+
**A document is read by people, and its footer is for the agent** (`references/style-guide.md` § Documents are read by people). A system document explains a machine to a person: what it is for, why it is split where it is split, how it behaves from the outside, what it guarantees at the far end, what re-drives it when it stalls. Its stages, tables, routes, frames, bindings and files are the code graph's (`docs/architecture/systems/knowledge-layer.md`), not a hand-typed tour. The body names code only at a cross-cutting seam another machine or feature would not find from its own files.
|
|
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
|
-
**
|
|
9
|
+
**A system document's globs win by specificity.** Its `owns:` names the subtrees and exact files the machine is made of, narrower than the package glob. Two documents never claim a path at equal specificity; every glob matches a file on disk at `status: active`.
|
|
10
10
|
|
|
11
|
-
**Required
|
|
12
|
-
|
|
13
|
-
System documents are long by nature, because the point is that the whole machine is in one place — roughly 300 to 1000 lines. Length is earned by stages, never by restating a neighbour: every paragraph that could have been a link to the document that owns it is the genre's characteristic waste.
|
|
11
|
+
**Required:** the frontmatter, the opening paragraph (the machine defined in its first sentence, what enters, what it leaves behind), the **boundary paragraph** (what this document does not decide, and which document does — subjects with owners, never bare links), **Why it exists**, **How it works**, **What it promises**, **For the agent**. **What was decided** once any choice was made the code cannot explain. **Known gaps** only when there is one. Nothing else — the lint rules of the feature template apply. Sixty to a hundred and sixty lines.
|
|
14
12
|
|
|
15
13
|
```yaml
|
|
16
14
|
---
|
|
17
|
-
|
|
15
|
+
type: system # required tooling key
|
|
16
|
+
status: active # required — draft = the machine as a whole does not run
|
|
17
|
+
objects: [run, run ledger, settlement] # required — the objects this machine owns or moves
|
|
18
|
+
owns: # required until the graph computes ownership — narrower than the package glob
|
|
18
19
|
- packages/{pkg}/src/{machine}/**
|
|
19
|
-
|
|
20
|
-
- packages/{other}/src/shared/{machine}Vocabulary.ts
|
|
21
|
-
keywords: [machine name, its nouns, its verbs] # required — discovery terms: the machine's own vocabulary, the table and route names
|
|
22
|
-
status: active # required — active | draft | deprecated
|
|
23
|
-
applies: # optional — the decisions and doctrine this machine is BOUND by (obligation, not a bibliography)
|
|
20
|
+
applies: # optional — decisions and doctrine this machine is BOUND by
|
|
24
21
|
- docs/architecture/decisions/AD-0NN-{slug}.md
|
|
25
|
-
- docs/architecture/common/{doctrine}.md
|
|
26
|
-
type: system # required tooling key — genre stamp; preserve on edits, never narrate
|
|
27
22
|
---
|
|
28
23
|
```
|
|
29
24
|
|
|
30
|
-
> A system document carries no `module:` — a machine that spans packages belongs to no single
|
|
31
|
-
> bounded context, and labelling it with one asserts a boundary it crosses by definition.
|
|
32
|
-
> `last-reviewed:` is optional tooling metadata: preserve it, never narrate it.
|
|
33
|
-
|
|
34
25
|
```
|
|
35
26
|
# {The machine}
|
|
36
27
|
|
|
37
|
-
{
|
|
38
|
-
then says what this document states, in the order the body states it. The enumeration is the
|
|
39
|
-
contract: a reader who wants one stage finds it by reading this sentence. Where the machine's
|
|
40
|
-
purpose is easier stated as an outcome, lead with `**Goal:**` and one sentence instead.}
|
|
41
|
-
|
|
42
|
-
{One paragraph: the boundary. What this document does **not** restate, and which document holds
|
|
43
|
-
each part — the neighbouring systems, the package documents whose residue this sits inside, the
|
|
44
|
-
feature documents that render from it, the common-plane doctrine it obeys, the decision records
|
|
45
|
-
behind its shape. Name each with a link and the subject it holds. This paragraph is what keeps the
|
|
46
|
-
document from growing into its neighbours.}
|
|
47
|
-
|
|
48
|
-
```text
|
|
49
|
-
{The machine diagram. One fenced block, drawn as the thing flows — entry at the top or left,
|
|
50
|
-
each stage a box or a labelled arrow, the durable writes named, the re-drive path shown returning.
|
|
51
|
-
Label the arrows with what crosses them, not with verbs. This is the picture a reader holds while
|
|
52
|
-
reading the rest, so it carries the machine's real names — routes, tables, classes, files — and
|
|
53
|
-
not generic boxes.}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
{Optional, and worth its line whenever the body names paths: "Every path below is relative to
|
|
57
|
-
`packages/{pkg}/src/{dir}/` unless it names another package."}
|
|
28
|
+
{The machine defined in its first sentence — bold the term — then what it takes in, what it leaves behind, and what a reader sees at the far end. Plain words.}
|
|
58
29
|
|
|
59
|
-
{
|
|
60
|
-
§ Z is what re-drives it" — where the section list is not self-evidently a sequence.}
|
|
30
|
+
{The boundary paragraph: what this document does not decide and which document does.}
|
|
61
31
|
|
|
62
|
-
##
|
|
32
|
+
## Why it exists
|
|
63
33
|
|
|
64
|
-
{
|
|
65
|
-
name them in one table before the stages begin.}
|
|
34
|
+
{Why this is one machine and not two, what it deliberately is not, the constraint that shaped it.}
|
|
66
35
|
|
|
67
|
-
|
|
68
|
-
| --- | --- | --- |
|
|
69
|
-
| {Entry} | `routes/{x}.ts` | {thin, tenancy first} |
|
|
70
|
-
| {Ledger} | `{machine}/ledger.ts` | {the durable row} |
|
|
36
|
+
## How it works
|
|
71
37
|
|
|
72
|
-
|
|
38
|
+
{The machine's behaviour from the outside, in the order it runs, in plain words: what starts it, what it does at each step as a person would describe it, what it writes that outlives it, what re-drives it, what it refuses. Terms of art introduced on first use. Code named only at a seam.}
|
|
73
39
|
|
|
74
|
-
|
|
75
|
-
Entry, then each stage, then what is written durably, then what re-drives a stalled one, then what
|
|
76
|
-
reads the result. Name each section for its stage in the product's own vocabulary. Within a
|
|
77
|
-
section, state the mechanism and its reason: what the code does, what it refuses, and why it
|
|
78
|
-
refuses that rather than something else. Tables for rosters (routes, columns, frames, states,
|
|
79
|
-
bindings); prose for a rule and the defect it prevents. Never paste an implementation — a
|
|
80
|
-
signature, a state list, a wire shape, a table's columns.}
|
|
40
|
+
## What was decided
|
|
81
41
|
|
|
82
|
-
|
|
42
|
+
{Themed paragraphs; alternatives named only where a reader would reach for them.}
|
|
83
43
|
|
|
84
|
-
|
|
85
|
-
where the machine has a suite of its own; omit the heading where the coverage lives in the package
|
|
86
|
-
documents' suites.}
|
|
44
|
+
## What it promises
|
|
87
45
|
|
|
88
|
-
|
|
46
|
+
{Checkable guarantees and refusals, grouped; what holds at the far end; what a stalled run gets.}
|
|
89
47
|
|
|
90
|
-
|
|
91
|
-
| --- | --- |
|
|
92
|
-
| `packages/{pkg}/src/{machine}/{file}.ts` | {what it holds, by name — the exports a reader is looking for} |
|
|
48
|
+
## Known gaps
|
|
93
49
|
|
|
94
|
-
{One
|
|
95
|
-
the inverse: "Read here, owned elsewhere:" naming the paths the body describes but another document
|
|
96
|
-
claims, each with the document that claims it. That closing line is what stops a system document
|
|
97
|
-
from quietly annexing the code it reads.}
|
|
50
|
+
{One paragraph, only when needed.}
|
|
98
51
|
|
|
99
|
-
##
|
|
52
|
+
## For the agent
|
|
100
53
|
|
|
101
|
-
|
|
102
|
-
- [`{neighbour}.md`]({neighbour}.md) — {the machine on the other side of this seam}
|
|
103
|
-
- [`{feature}.md`](../../features/{feature}.md) — {what a person sees of this machine}
|
|
104
|
-
- [`{doctrine}.md`](../common/{doctrine}.md) — {the cross-cutting contract this machine answers to}
|
|
54
|
+
Objects: {…}. Code roots {…} — directories, one per package the machine spans. Seams {…} by name. Checklist `tests/{system}.md`. See also {neighbour} ({why}). No tables, routes or symbol rosters — those are the graph's.
|
|
105
55
|
```
|
|
106
56
|
|
|
107
|
-
### The boundary paragraph is the genre's load-bearing section
|
|
108
|
-
|
|
109
|
-
Systems overlap: they share tables, call each other, and render into the same surfaces. Without a
|
|
110
|
-
paragraph that names the seams, two system documents describe the same stage from two angles and
|
|
111
|
-
drift apart on the first change — and a reader has no way to tell which is authoritative. So the
|
|
112
|
-
boundary paragraph is not a courtesy list of related reading; it is the statement of what this
|
|
113
|
-
document is *not*, written specifically enough that a later editor can tell where to put a fact.
|
|
114
|
-
Write it as a list of subjects with their owners — "the coordinator's tables, alarm and drive claim
|
|
115
|
-
are `harness.md`; a run's ledger row and settlement are `generation-runs.md`" — never as bare
|
|
116
|
-
links.
|
|
117
|
-
|
|
118
57
|
### Marking what is decided and unbuilt
|
|
119
58
|
|
|
120
|
-
Where
|
|
121
|
-
acts, a column that is written and never read, a door with no caller — wrap the prose in a marker
|
|
122
|
-
so the gap is machine-findable and its evidence travels with it:
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
<!-- UNBUILT:START — {what is decided, where it is decided (an AD § or an owner ruling), and the
|
|
126
|
-
source evidence that it is not built: the function with no caller, the route that is not mounted,
|
|
127
|
-
the value written and never read} -->
|
|
128
|
-
{the prose describing the unbuilt stage, and what runs in its place}
|
|
129
|
-
<!-- UNBUILT:END -->
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**State what runs instead.** A machine with a hole in it still works somehow, and the workaround is
|
|
133
|
-
the fact a reader needs. **A design decided *against* carries no marker** — a marker means the work
|
|
134
|
-
is owed and absent, so using one for a rejected design turns the register of debt into a register
|
|
135
|
-
of opinions.
|
|
136
|
-
|
|
137
|
-
### `status: draft` on a system
|
|
138
|
-
|
|
139
|
-
A system document is `draft` when the **machine as a whole** does not run — the code is on disk and
|
|
140
|
-
nothing reaches it. Say so in the first section, in plain words, with the evidence: which halves
|
|
141
|
-
exist, which do not, and what a reader who invokes it actually observes. A scoped gap in a working
|
|
142
|
-
machine is not a draft: the document stays `active` and takes an UNBUILT marker at the stage that
|
|
143
|
-
has the hole.
|
|
59
|
+
Where a stage is decided and not built, wrap its prose in `<!-- UNBUILT:START — {what, where decided, the evidence it is not built} -->` … `<!-- UNBUILT:END -->` and say in What it promises what runs instead. A design decided *against* carries no marker.
|
|
@@ -3,20 +3,12 @@
|
|
|
3
3
|
// harness's files land — a bundle says `skills/design/SKILL.md`, a project says
|
|
4
4
|
// `.claude/skills/design/SKILL.md`, and this module is the only place that mapping exists.
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// They diverged once — the push sent the BUNDLE layout, so a downloaded project had its
|
|
10
|
-
// instruction tree at the archive root, no AGENTS.md, and no wired hooks, while the manifest
|
|
11
|
-
// claimed otherwise. One function is what stops that recurring.
|
|
12
|
-
//
|
|
13
|
-
// ZERO DEPENDENCIES, BY NECESSITY. The push runs in CI without an `npm install` (that workflow
|
|
14
|
-
// reaches `npm publish` → `prepack` and nothing else), so this module and everything it imports
|
|
15
|
-
// may use only what Node ships. That rules out importing `bin/etna.js`, which pulls in
|
|
16
|
-
// @clack/prompts for its UI — hence the extraction rather than a re-export.
|
|
6
|
+
// ONE MAPPING, COMPUTED ONCE. `bin/etna.js` calls this against the user's cwd; the placement
|
|
7
|
+
// itself comes from `planHarnessFiles`, so the installed tree and the compiler's array cannot
|
|
8
|
+
// describe different trees — the suite runs both and compares.
|
|
17
9
|
//
|
|
18
10
|
// PURE FILE OPS, NO UI. Every human-facing string is RETURNED (installed/notices/warnings) and
|
|
19
|
-
// printed by the caller,
|
|
11
|
+
// printed by the caller, which is what leaves presentation to the CLI alone.
|
|
20
12
|
|
|
21
13
|
import {
|
|
22
14
|
existsSync,
|
|
@@ -227,10 +219,19 @@ function groupHasCommand(group, command) {
|
|
|
227
219
|
return (group.hooks || []).some((h) => h.command === command);
|
|
228
220
|
}
|
|
229
221
|
|
|
222
|
+
// The harness script a hook command runs, by its `.claude/hooks/<name>` path — the identity
|
|
223
|
+
// that survives a change to the command's shape. Null for a command that runs no such script.
|
|
224
|
+
function hookScript(command) {
|
|
225
|
+
const found = /\.claude\/hooks\/([\w.-]+)/.exec(command || '');
|
|
226
|
+
return found ? found[1] : null;
|
|
227
|
+
}
|
|
228
|
+
|
|
230
229
|
// Deep-merge the bundle's hooks.json object into <project>/.claude/settings.json under
|
|
231
230
|
// "hooks", preserving every existing key. Idempotent: for each event, an incoming entry
|
|
232
231
|
// merges into the matcher-group of the same matcher, skipping any hook whose command
|
|
233
|
-
// already exists there; a
|
|
232
|
+
// already exists there; a hook already registered for the same script under an older command
|
|
233
|
+
// shape is replaced in place, never doubled; a new matcher creates a new group. Returns the
|
|
234
|
+
// count of hooks added or replaced.
|
|
234
235
|
function mergeHooks(targetDir, bundledHooksJson) {
|
|
235
236
|
if (!existsSync(bundledHooksJson)) return 0;
|
|
236
237
|
const bundled = readJson(bundledHooksJson);
|
|
@@ -261,7 +262,10 @@ function mergeHooks(targetDir, bundledHooksJson) {
|
|
|
261
262
|
match.hooks = match.hooks || [];
|
|
262
263
|
for (const hook of group.hooks || []) {
|
|
263
264
|
if (groupHasCommand(match, hook.command)) continue;
|
|
264
|
-
|
|
265
|
+
const script = hookScript(hook.command);
|
|
266
|
+
const staleIndex = script ? match.hooks.findIndex((h) => hookScript(h.command) === script) : -1;
|
|
267
|
+
if (staleIndex === -1) match.hooks.push(hook);
|
|
268
|
+
else match.hooks[staleIndex] = hook;
|
|
265
269
|
added += 1;
|
|
266
270
|
}
|
|
267
271
|
}
|