create-afe-project 0.0.0-stage → 1.0.0
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/CHANGELOG.md +351 -0
- package/LICENSE +21 -0
- package/README.md +77 -2
- package/bin/create-afe-project.js +123 -0
- package/package.json +43 -4
- package/src/answers.js +147 -0
- package/src/config.js +152 -0
- package/src/project.js +217 -0
- package/src/render.js +51 -0
- package/template/.claude/harness.conf +47 -0
- package/template/.claude/hooks/post-edit-check.sh +38 -0
- package/template/.claude/hooks/protect-docs.sh +315 -0
- package/template/.claude/hooks/stop-check-decisions.sh +25 -0
- package/template/.claude/hooks/stop-check-docs.sh +29 -0
- package/template/.claude/hooks/stop-check-template.sh +23 -0
- package/template/.claude/hooks/stop-run-project-hooks.sh +27 -0
- package/template/.claude/path-classes +61 -0
- package/template/.claude/settings.project.json +5 -0
- package/template/.claude/settings.template.json +76 -0
- package/template/.claude/skills/afe-init/SKILL.md +104 -0
- package/template/.claude/skills/create-pr/SKILL.md.jinja +107 -0
- package/template/.claude/skills/update-template/SKILL.md +40 -0
- package/template/.claude/skills/{% if code_review %}review-code{% endif %}/SKILL.md.jinja +46 -0
- package/template/.claude/skills/{% if docs_review %}docs-review{% endif %}/SKILL.md +56 -0
- package/template/.claude/skills/{% if dual_review %}dual-review{% endif %}/SKILL.md +58 -0
- package/template/.github/workflows/decisions.yml.jinja +47 -0
- package/template/.github/workflows/docs.yml.jinja +40 -0
- package/template/.github/workflows/{% if code_review %}code-review.yml{% endif %}.jinja +51 -0
- package/template/.github/workflows/{% if docs_review %}docs-review.yml{% endif %}.jinja +49 -0
- package/template/.github/workflows/{% if fail_without_change %}fail-without-change.yml{% endif %}.jinja +40 -0
- package/template/.github/workflows/{% if secret_scan %}secret-scan.yml{% endif %}.jinja +47 -0
- package/template/.markdownlint-cli2.jsonc +27 -0
- package/template/.template/owned.txt.jinja +63 -0
- package/template/AGENTS.md.jinja +91 -0
- package/template/CLAUDE.md +3 -0
- package/template/README.md.jinja +41 -0
- package/template/_gitignore +14 -0
- package/template/docs/README.md.jinja +44 -0
- package/template/docs/contributing.md.jinja +206 -0
- package/template/docs/decisions/adr-template.md +38 -0
- package/template/docs/documentation-rules.md.jinja +200 -0
- package/template/docs/testing-rules.md +80 -0
- package/template/scripts/check-decisions.sh +382 -0
- package/template/scripts/check-docs.sh +20 -0
- package/template/scripts/check-links.mjs +142 -0
- package/template/scripts/check-project.sh +27 -0
- package/template/scripts/check-template.sh +88 -0
- package/template/scripts/check.sh +21 -0
- package/template/scripts/env.sh +29 -0
- package/template/scripts/gen-decisions-index.sh +57 -0
- package/template/scripts/gen-settings.sh +14 -0
- package/template/scripts/lib/classes.sh +89 -0
- package/template/scripts/lib/git-base.sh +23 -0
- package/template/scripts/lib/harness-conf.sh +48 -0
- package/template/scripts/lib/json.mjs +160 -0
- package/template/scripts/pr-metrics.sh +80 -0
- package/template/scripts/protect-branch.sh +53 -0
- package/template/scripts/{% if acceptance_check %}check-acceptance.sh{% endif %} +47 -0
- package/template/scripts/{% if fail_without_change %}fail-without-change.sh{% endif %} +88 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Documentation rules
|
|
2
|
+
|
|
3
|
+
Reference for creating, changing, and retiring documentation. `AGENTS.md` holds the short rules
|
|
4
|
+
and takes precedence; this file comes from the template, so report a disagreement to the
|
|
5
|
+
template (section 8). It has at most 200 lines (checked): an addition pays with a removal.
|
|
6
|
+
|
|
7
|
+
## 1. Principles
|
|
8
|
+
|
|
9
|
+
- Docs as code: documentation lives here, in Markdown, is reviewed like code, and is checked
|
|
10
|
+
automatically (section 7).
|
|
11
|
+
- Progressive disclosure: entry files (`AGENTS.md`, `docs/README.md`) stay short and link out.
|
|
12
|
+
- One owner per fact: `docs/README.md` maps kinds of facts to owner documents; others link, never
|
|
13
|
+
copy. Generated artefacts come from a script, are checked in CI, and are never hand-edited.
|
|
14
|
+
- Content before structure: a section exists only with real content; future ones are listed in
|
|
15
|
+
the map, not created as placeholders. Diátaxis is a lens for content type, not a folder tree.
|
|
16
|
+
|
|
17
|
+
## 2. Document kinds and formats
|
|
18
|
+
|
|
19
|
+
| Kind | Location | Format |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| Agent rules | `AGENTS.md` | at most 90 template lines plus a project block of at most 10 lines (checked); numbered sections; `CLAUDE.md` imports it with `@AGENTS.md` |
|
|
22
|
+
| Documentation map | `docs/README.md` | tables of existing and planned sections |
|
|
23
|
+
| Testing rules | `docs/testing-rules.md` | at most 80 lines (checked); numbered sections: what a test is for, how it proves behaviour, tests and the change, gates; the project's test levels, tools, and edge cases live in its own documents |
|
|
24
|
+
| Contribution rules | `docs/contributing.md` | at most 200 lines (checked); numbered sections: branches, commits, pull requests and when a change is done, template updates, review |
|
|
25
|
+
| Decision (ADR) | `docs/decisions/NNNN-title-with-dashes.md` | only for a decision that meets section 5; short MADR 4.0 from `adr-template.md`, about one page, details linked to their owner document; front matter `status`, `date`, `decision-makers`, `scope`, `summary`, optional `supersedes` |
|
|
26
|
+
| Decision index | `docs/decisions/README.md` | generated by `scripts/gen-decisions-index.sh` from the front matter of the records; never hand-edited |
|
|
27
|
+
| Plan | `docs/plans/<slug>.md` | front matter `status`, `owner`, `created`, `review-by`; sections Goal, Steps, Open questions |
|
|
28
|
+
| Requirement | `docs/requirements/` | one `REQ-NNN` per statement; RFC 2119 keywords; fields `status`, `source`, `verification`; its acceptance criteria `AC-NN` as Given, When, Then, with the check that establishes each |
|
|
29
|
+
| Invariants register | `docs/invariants.md` | one `INV-NN` per invariant that must hold everywhere (`testing-rules.md`, section 4); fields `statement`, `source` (the requirement or owner decision), `enforced-by` (the test or project check that fails when it breaks, or `none`, which is an open question) |
|
|
30
|
+
| Architecture | `docs/architecture/` | the system map: components, domain ownership, public interfaces and the dependency rules a project check enforces; explanation and reference; front matter `status`, `date`, `verified-against` (commit, or `none` while no code exists) |
|
|
31
|
+
| Contract | `contracts/` | machine-readable, for example OpenAPI or JSON Schema; the file is the contract; prose links to it |
|
|
32
|
+
| Operations | `docs/operations/` | how-to; every procedure names the command or file it relies on |
|
|
33
|
+
| Conventions and recipes | `docs/conventions/`, `docs/recipes/` | one area or task per file; a convention names the check that enforces it, or `none`; a recipe lists its steps and commands, and skills link to it |
|
|
34
|
+
|
|
35
|
+
This is the fixed set of document kinds the template defines; another project document, such as
|
|
36
|
+
a changelog, exists only when the owner approves it and the map lists it. Front matter is YAML
|
|
37
|
+
between `---` lines at the top of the file.
|
|
38
|
+
|
|
39
|
+
## 3. Status vocabulary
|
|
40
|
+
|
|
41
|
+
| Document | Allowed `status` values and transitions | Transition set by |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| ADR | `proposed` to `accepted` or `rejected`; `accepted` to `deprecated` or `superseded by ADR-NNNN` | owner; agents create `proposed` |
|
|
44
|
+
| Requirement | `draft` to `approved` to `implemented` or `retired` | owner; `implemented` needs a linked test |
|
|
45
|
+
| Plan | `active`, then the file is deleted when done or abandoned | the change that completes or abandons it |
|
|
46
|
+
| Architecture, operations | `draft` to `current`; `current` with a stale `verified-against` is suspect | the reviewer who verified it against code |
|
|
47
|
+
| Owner decision in an owner document | a statement carries its document's status until the owner decides it; then it ends with, or its section opens with, `Decided by the owner, YYYY-MM-DD.` | owner; an agent adds the marker only on the owner's explicit instruction and lists it in the pull request (`contributing.md`, section 3); the owner's merge confirms it |
|
|
48
|
+
|
|
49
|
+
Wording rules for behaviour:
|
|
50
|
+
|
|
51
|
+
- "X does Y" only for implemented behaviour, merged code with a test; link the test or module
|
|
52
|
+
when the claim matters. Requirements use RFC 2119 keywords and never claim to be implemented.
|
|
53
|
+
Proposals say "proposed", accepted decisions "accepted" or "decided", unknown things "not
|
|
54
|
+
decided" or "unknown", never guessed.
|
|
55
|
+
- A statement about a check (a lint rule, a test check, a hook, a workflow) names the forms it
|
|
56
|
+
catches and what is left to the review; an absolute claim about a check needs a test for each
|
|
57
|
+
form it covers.
|
|
58
|
+
|
|
59
|
+
## 4. Currency
|
|
60
|
+
|
|
61
|
+
A document is current when its content agrees with its owner source (code, contract, decision).
|
|
62
|
+
`date` records the last content change. `verified-against: <commit>` records the last review
|
|
63
|
+
against code and is required for architecture and operations documents once code exists.
|
|
64
|
+
Touching a file or bumping a date is not verification.
|
|
65
|
+
|
|
66
|
+
## 5. Lifecycle
|
|
67
|
+
|
|
68
|
+
- Create a document when its first real content exists. A new section or plan needs the owner's
|
|
69
|
+
approval, and the map moves the section from planned to existing in the same change. An agent
|
|
70
|
+
may create an ADR with `status: proposed`; the owner's acceptance is the approval.
|
|
71
|
+
- A new ADR only for a decision that is costly to reverse once code or data depend on it and had
|
|
72
|
+
a real alternative: language, runtime, or framework; storage and persistent data formats;
|
|
73
|
+
public protocols and contract formats; the security, isolation, or authentication model; an
|
|
74
|
+
external service or infrastructure. Copy `adr-template.md`, take the highest number plus one,
|
|
75
|
+
set `status: proposed`, record one decision in about one page, link the owner documents, and
|
|
76
|
+
regenerate the index: `scripts/gen-decisions-index.sh > docs/decisions/README.md`.
|
|
77
|
+
- Other owner decisions are edits of the owner document with the marker of section 3; a decision
|
|
78
|
+
about a template-owned file goes to the template. Process rules live in the rule documents and
|
|
79
|
+
their checks; implementation choices stay in the pull request and the commit messages.
|
|
80
|
+
- A plan only for work that spans sessions or needs owner follow-up; the change that completes or
|
|
81
|
+
abandons it deletes the file. A plan past `review-by` is updated or deleted in the next change
|
|
82
|
+
to `docs/`, and agents flag it.
|
|
83
|
+
- Update the owner document in the same change that makes it wrong; refactoring that leaves
|
|
84
|
+
behaviour, contracts, and operations unchanged needs none. Update links and the map.
|
|
85
|
+
- An ADR on `main` changes only its `status` line along section 3. A `proposed` ADR that needs
|
|
86
|
+
other wording is `rejected` and replaced; an ADR is never deleted or renamed. `superseded by
|
|
87
|
+
ADR-NNNN` names an ADR the same change adds, which lists the records it replaces in
|
|
88
|
+
`supersedes`. Requirements are retired with a reason and a link to their replacement.
|
|
89
|
+
- There is no archive directory: a retired document stays with its retired status. Temporary
|
|
90
|
+
material (research notes, logs, transcripts, agent reports) never enters `docs/`; a person
|
|
91
|
+
extracts durable facts into the owner document.
|
|
92
|
+
|
|
93
|
+
## 6. Alignment of documentation, contracts, and code
|
|
94
|
+
|
|
95
|
+
These rules apply once code exists; they are fixed now so the first code follows them.
|
|
96
|
+
|
|
97
|
+
- Contracts are machine-readable files in `contracts/`. The file is the source of truth for the
|
|
98
|
+
interface. The implementation conforms to it, and prose documentation links to it. Contract
|
|
99
|
+
linting and breaking-change detection are added to CI with the first contract, as decided in
|
|
100
|
+
an ADR together with the contract format.
|
|
101
|
+
- Implemented behaviour is proven by tests that follow `docs/testing-rules.md`. Documentation
|
|
102
|
+
that describes behaviour links to the test or module. A described behaviour without a test is
|
|
103
|
+
`proposed`, not `implemented`.
|
|
104
|
+
- CI runs the documentation checks, the contract checks, and the tests on every pull request.
|
|
105
|
+
- Architecture documents carry `verified-against`; a review against code updates it.
|
|
106
|
+
- With the first code: the project adds its code checks to `scripts/checks.d/` (section 9), and
|
|
107
|
+
`docs/README.md` moves "implemented behaviour" to existing.
|
|
108
|
+
|
|
109
|
+
## 7. Automated checks and semantic review
|
|
110
|
+
|
|
111
|
+
| Concern | Automated (`scripts/check.sh`, CI) | Semantic review, per change |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| Markdown syntax and structure | markdownlint-cli2 | none |
|
|
114
|
+
| Broken internal links and anchors | `scripts/check-links.mjs`, offline, with anchors | none |
|
|
115
|
+
| Secrets in a change | {% if secret_scan %}gitleaks in the `secret-scan` workflow; {% endif %}the protect hook asks when an edit contains a known token format | none |
|
|
116
|
+
| External links | not checked automatically | author verifies when adding; spot check on review |
|
|
117
|
+
| Status correctness | none | status matches the evidence (section 3) |
|
|
118
|
+
| Invented facts | none | every new fact has a source or is marked unknown |
|
|
119
|
+
| Duplication | none | new content links instead of copying |
|
|
120
|
+
| Currency | none | `verified-against` and the map are updated |
|
|
121
|
+
| Documentation updated together with code | {% if docs_review %}`docs-review` workflow on pull requests, one pass, a sensor{% else %}none{% endif %} | reviewer confirms the owner document changed |
|
|
122
|
+
| ADRs on `main` change only their status line (section 5); new ADRs carry `scope` and `summary`; the decision index matches the records | `scripts/check-decisions.sh`: Stop hook and `decisions` workflow | none |
|
|
123
|
+
| A new ADR meets the criteria of section 5 | none | {% if docs_review %}`docs-review` reports it; {% endif %}the owner decides |
|
|
124
|
+
| Template-owned files unchanged; one project block in `AGENTS.md`; the line budgets of the rule documents; `.claude/settings.json` generated, and every hook command it names exists (sections 2 and 9) | `scripts/check-template.sh`: Stop hook and `docs` workflow | none |
|
|
125
|
+
| Each invariant names its check (section 2) | none | the review checks `docs/invariants.md` against the checks it names |
|
|
126
|
+
| Project checks (section 9) | `scripts/check-project.sh` runs `scripts/checks.d/*.sh`: `scripts/check.sh` and `docs` workflow; a Stop hook only if the project adds one | as the project defines |
|
|
127
|
+
| Code and tests meet the review rubric and the testing rules (`contributing.md`, section 6; `testing-rules.md`) | the project's test checks in `scripts/checks.d/`{% if fail_without_change %}; `scripts/fail-without-change.sh`{% endif %}{% if code_review %}; `code-review` workflow, a sensor{% endif %} | reviewer applies the rubric, by the class of the paths |
|
|
128
|
+
| Merge readiness (`contributing.md`, section 3) | `scripts/check-decisions.sh --ready`, in the forms that section names{% if acceptance_check %}, with `scripts/check-acceptance.sh`{% endif %}; `decisions` workflow; signals, does not block | whether the tests prove their identifiers; no `blocker` open; the owner's merge confirms the listed markers |
|
|
129
|
+
| Measurement of the sensors (`contributing.md`, section 3) | `scripts/pr-metrics.sh` aggregates the Metrics of merged pull requests | the owner removes a rule or check that never fires |
|
|
130
|
+
| Finishing with failing checks | Stop hook in Claude Code | none |
|
|
131
|
+
| Class approval and irreversible actions (section 8) | protect hook: owner prompt in Claude Code, denied headless; deny rules of `.claude/settings.json` | explicit, separate, owner-approved |
|
|
132
|
+
|
|
133
|
+
Semantic review checklist for any documentation change:
|
|
134
|
+
|
|
135
|
+
1. Each new statement has a status, and the status matches the evidence; a statement about a
|
|
136
|
+
check names the forms it catches and what is left to the review, and an absolute claim about
|
|
137
|
+
a check has a test for each form it covers (section 3).
|
|
138
|
+
2. No requirement, default, or decision was invented; gaps are listed as open questions.
|
|
139
|
+
3. No content duplicates another document; links point to the owner.
|
|
140
|
+
4. The map (`docs/README.md`) and all links are updated; `scripts/check.sh` passes.
|
|
141
|
+
5. ADRs that exist on `main` (apart from their status line), template-owned files, the
|
|
142
|
+
documentation map, and the structure are unchanged unless the change is explicitly about them.
|
|
143
|
+
6. A new ADR meets the criteria of section 5 and links details instead of listing them; every
|
|
144
|
+
new `Decided by the owner` marker is listed in the pull request with the owner's instruction,
|
|
145
|
+
quoted with its date (`contributing.md`, section 3).
|
|
146
|
+
|
|
147
|
+
## 8. Path classes, access, and changing these rules
|
|
148
|
+
|
|
149
|
+
Governance follows risk and reversibility: every path a change touches takes the highest class
|
|
150
|
+
that applies, from the template's built-in rules and the project's `.claude/path-classes`
|
|
151
|
+
(section 9); `scripts/check-decisions.sh --classes` prints them and the review they need.
|
|
152
|
+
|
|
153
|
+
| Class | Paths (defaults; the project adds its own) | Before the edit | Review before the pull request (`contributing.md`, section 6) |
|
|
154
|
+
| --- | --- | --- | --- |
|
|
155
|
+
| low | Markdown outside the rules, the decision index, a template update | nothing | none |
|
|
156
|
+
| normal | code, tests, scripts: every path no other class takes | nothing | one reviewer |
|
|
157
|
+
| gate | workflows, project checks and Stop hooks, `.claude/harness.conf`, dependency manifests and lock files, linter, test, coverage, and mutation configuration | nothing; listed under "Gate files", reviewed by the owner after the merge | one reviewer |
|
|
158
|
+
| high | security, secrets, access and authentication, data migrations, money and billing, public contracts (`contracts/`) | nothing | {% if dual_review %}Santa loop{% else %}one reviewer; the Santa loop comes with the review module{% endif %} |
|
|
159
|
+
| approval | `AGENTS.md`, the template-owned files, the generated manifest, answers file, and `.claude/settings.json`, the map, `.claude/path-classes`, `.claude/settings.project.json`, `.mcp.json`, credentials, ADRs on `main` or not `proposed` | the protect hook asks the owner; a headless run is denied | by the class of the content |
|
|
160
|
+
|
|
161
|
+
The protect hook (`.claude/hooks/protect-docs.sh`) asks on an Edit, Write, or MultiEdit call and
|
|
162
|
+
on a shell command of a form its comments name (a redirect, `cp`, `mv`, `tee`, `rm`, `sed -i`,
|
|
163
|
+
and the like), and on the irreversible commands it names: `git push` that forces, deletes,
|
|
164
|
+
mirrors, prunes, or pushes tags (a release); `gh pr merge`; `gh release create` and `delete`;
|
|
165
|
+
`gh repo delete`. A side effect of a tool, a path or command named only inside a script or
|
|
166
|
+
behind a variable, an alias, or a directory a script changes is not caught and is left to the
|
|
167
|
+
review of the diff.
|
|
168
|
+
|
|
169
|
+
Access control comes before prompts: an agent gets no credentials or tools for irreversible or
|
|
170
|
+
production actions outside its task. `.claude/settings.json` denies force pushes and deletions
|
|
171
|
+
of remote branches in the forms its rules spell out, and reading `.env`, `.env.*`, `*.pem`, and
|
|
172
|
+
`*.key`; in `.claude/settings.project.json` the project adds deny rules, for example for the
|
|
173
|
+
destructive tools of an MCP server (`mcp__<server>__<tool>`), and takes a template entry out
|
|
174
|
+
with `"remove"`. For scripted, owner-driven changes the owner starts Claude Code
|
|
175
|
+
with `DOCS_GUARD=off`, which also lifts the Stop hooks for decisions and template integrity.
|
|
176
|
+
Other agents are bound by `AGENTS.md` and the review only.
|
|
177
|
+
|
|
178
|
+
Template-owned files (section 9), including this file and `AGENTS.md` outside its project block,
|
|
179
|
+
change only in the template and arrive through an update (`contributing.md`, section 5). A
|
|
180
|
+
change to the map or the structure is a dedicated change approved by the owner.
|
|
181
|
+
|
|
182
|
+
## 9. Ownership of files
|
|
183
|
+
|
|
184
|
+
Every file belongs to exactly one class:
|
|
185
|
+
|
|
186
|
+
| Class | Files | Changed by |
|
|
187
|
+
| --- | --- | --- |
|
|
188
|
+
| Template-owned | the files listed with their hashes in `.template/owned.sha256` (rules, ADR template, checks, hooks, skills, workflows, linter configuration), and `AGENTS.md` outside its project block | a release of the template, applied with `npx create-afe-project@latest update` (`contributing.md`, section 5); never edited in this repository |
|
|
189
|
+
| Seeded | `README.md`, `CLAUDE.md`, `.gitignore`, `docs/README.md`, `.claude/path-classes`, `.claude/harness.conf`, `.claude/settings.project.json`, and the project block of `AGENTS.md`: created once by the template | the project |
|
|
190
|
+
| Generated | `docs/decisions/README.md`, `.template/owned.sha256`, `.afe-answers.json`, `.claude/settings.json` (by `scripts/gen-settings.sh` from `.claude/settings.template.json` and `.claude/settings.project.json`) | their generator only |
|
|
191
|
+
| Project | everything else, including the documentation sections of section 2 and the extension points below | the project |
|
|
192
|
+
|
|
193
|
+
The project extends the template only at its extension points: checks in `scripts/checks.d/*.sh`,
|
|
194
|
+
run in name order by `scripts/check.sh` and the `docs` workflow, which provision any tool they
|
|
195
|
+
need beyond Git, bash, and Node.js; Stop hooks in `.claude/hooks/project/stop/*.sh`, which
|
|
196
|
+
receive the Stop hook input and block the agent by exiting with 2; its own workflows in
|
|
197
|
+
`.github/workflows/`; its path classes in `.claude/path-classes`; its harness settings in
|
|
198
|
+
`.claude/harness.conf` (the Ralph cap, the identifier pattern, test globs, test commands); and
|
|
199
|
+
its Claude Code settings in `.claude/settings.project.json`. Checks and Stop hooks keep their
|
|
200
|
+
executable bit. Personal settings go into `.claude/settings.local.json`, never committed.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Testing rules
|
|
2
|
+
|
|
3
|
+
Rules that every test in this repository follows, whatever its language or tools. `AGENTS.md`
|
|
4
|
+
holds the short rules and takes precedence if the two ever disagree. This file comes from the
|
|
5
|
+
project template: report such a disagreement to the template instead of editing it. The project
|
|
6
|
+
adds the levels of its verification ladder (static checks, unit, integration, user journeys),
|
|
7
|
+
tools, commands, thresholds, and the edge cases of its domain in documents the map names, and
|
|
8
|
+
its test checks in `scripts/checks.d/` (`documentation-rules.md`, section 9).
|
|
9
|
+
|
|
10
|
+
## 1. What a test is for
|
|
11
|
+
|
|
12
|
+
- A test protects a behaviour that a requirement, an acceptance criterion, a contract, or a fixed
|
|
13
|
+
defect names. It carries that identifier (`REQ-NNN`, `AC-NN`) in its name or a tag when one
|
|
14
|
+
applies, so that an `implemented` status and the "Acceptance and test plan" of a pull request can
|
|
15
|
+
point at it (`documentation-rules.md`, section 3; `contributing.md`, section 3). The project's
|
|
16
|
+
checks in `scripts/checks.d/` map each identifier to the tests that carry it.
|
|
17
|
+
- Test count and coverage are not goals. Coverage shows which lines ran, not what a test checked:
|
|
18
|
+
it is a floor the project sets, never evidence of quality. Where the project runs mutation
|
|
19
|
+
testing, the mutation score is the measure.
|
|
20
|
+
- What the type checker, schema validation, or the contract check already proves (a shape, a
|
|
21
|
+
required field, a closed set of values) gets no test of its own.
|
|
22
|
+
- One behaviour has one test, or one parametrised test over its table of inputs. A test that
|
|
23
|
+
catches nothing the other tests miss is removed, and removing it is a valid change.
|
|
24
|
+
|
|
25
|
+
## 2. How a test proves behaviour
|
|
26
|
+
|
|
27
|
+
- A test asserts an observable outcome: the returned value or result, the resulting state, or the
|
|
28
|
+
emitted events, records, and output. An assertion that a mock was called, that a value is
|
|
29
|
+
defined or truthy, or that nothing threw is not enough on its own.
|
|
30
|
+
- Fakes stand in for the boundaries of the unit under test (external services, storage, the
|
|
31
|
+
clock, randomness), never for code inside it.
|
|
32
|
+
- Expected values are literals or fixtures. A test never computes its expectation with the logic
|
|
33
|
+
it tests, and a snapshot is never the only assertion of a rule.
|
|
34
|
+
- Failure paths are tested as deliberately as success: every distinct outcome or error a unit can
|
|
35
|
+
return has a test that produces it, and every invalid example of a contract is rejected where
|
|
36
|
+
the input is validated.
|
|
37
|
+
- The edge cases that apply are covered: boundaries, empty and missing values, unknown against
|
|
38
|
+
zero, duplicates and retries, concurrent use, time zones and period boundaries, and the largest
|
|
39
|
+
input the requirement allows. The project lists the edge cases of its domain.
|
|
40
|
+
- Tests are deterministic: no sleeps, no real clock, no real network or external service, and no
|
|
41
|
+
dependence on the order in which tests run.
|
|
42
|
+
|
|
43
|
+
## 3. Tests and the change they cover
|
|
44
|
+
|
|
45
|
+
- Tests come first: they are written before the implementation and run once to see them fail for
|
|
46
|
+
the expected reason. A test that passes before the implementation exists tests nothing.
|
|
47
|
+
- The acceptance tests of a requirement's criteria are written or reviewed by someone other than
|
|
48
|
+
the author of the implementation, before it exists; the author changes them only with the
|
|
49
|
+
owner's agreement.
|
|
50
|
+
- The agent derives the test plan from the identifiers the change serves: their cases, failure
|
|
51
|
+
paths, and edge cases (section 2). It is a report under "Acceptance and test plan"
|
|
52
|
+
(`contributing.md`, section 3), never approved per pull request and not maintained afterwards.
|
|
53
|
+
Each new or changed test fails on the base and passes with the change.
|
|
54
|
+
- A defect is fixed only after a regression test that reproduces it fails.
|
|
55
|
+
- A refactoring keeps the tests as they are. A change of behaviour first changes the requirement
|
|
56
|
+
or the acceptance criterion, then the test that carries the same identifier; the pull request
|
|
57
|
+
names both.
|
|
58
|
+
|
|
59
|
+
## 4. Gates
|
|
60
|
+
|
|
61
|
+
- No test is skipped, focused, or deleted, no assertion is weakened, no code is excluded from
|
|
62
|
+
coverage, and no threshold is lowered to make a check pass. Such a need is an open question for
|
|
63
|
+
the owner.
|
|
64
|
+
- The quality measures the project records (coverage, mutation score) never fall below those of
|
|
65
|
+
the base branch.
|
|
66
|
+
- The configuration of the test runner, coverage, mutation testing, and linters is class gate in
|
|
67
|
+
`.claude/path-classes` (`documentation-rules.md`, section 8): it changes without a prompt, the
|
|
68
|
+
pull request lists it under "Gate files", which the readiness check enforces, and the owner
|
|
69
|
+
reviews it after the merge.
|
|
70
|
+
- An invariant that must hold everywhere (for example, a secret never appears in output) is an
|
|
71
|
+
executable check, a test or a project check, and the register `docs/invariants.md` names it
|
|
72
|
+
with that check (`documentation-rules.md`, section 2); its invariants are criteria of the
|
|
73
|
+
review of class high (`contributing.md`, section 6).
|
|
74
|
+
- Threat model of development gates: a gate (a lint rule, a test check, a coverage or mutation
|
|
75
|
+
setting, an agent hook) catches honest mistakes and shortcuts; it is not a security boundary.
|
|
76
|
+
It decides which bypass is a `blocker` (`contributing.md`, section 6): a diff that itself
|
|
77
|
+
loosens or evades a gate (a test skipped, an assertion weakened, a threshold lowered, a test
|
|
78
|
+
behind an aliased import), and any bypass of a product invariant (security, secrets, policy
|
|
79
|
+
enforcement, limits), whatever it takes. A gap that only deliberate evasion exploits, such as
|
|
80
|
+
a computed property or tampering with a gate's own state, is a `suggestion`.
|