@nurix/etna 0.19.55 → 0.19.56
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/apollo/CLAUDE.md +29 -30
- package/data/harnesses/apollo/harness.json +1 -1
- package/data/harnesses/apollo/hooks/approved-packages.md +42 -0
- package/data/harnesses/apollo/hooks/compliance.mjs +11 -6
- package/data/harnesses/apollo/rules/changelog.md +7 -1
- package/data/harnesses/apollo/rules/cloudflare.md +167 -0
- package/data/harnesses/apollo/rules/database.md +1 -125
- package/data/harnesses/apollo/rules/frontend.md +2 -4
- package/data/harnesses/apollo/rules/general.md +20 -42
- package/data/harnesses/apollo/rules/nomenclature.md +2 -6
- package/data/harnesses/apollo/rules/pnpm.md +2 -2
- package/data/harnesses/apollo/rules/testing.md +44 -3
- package/data/harnesses/apollo/skills/documentation/references/instruction-style.md +1 -0
- package/data/harnesses/apollo/skills/documentation/templates/plan.md +4 -4
- package/data/harnesses/apollo/skills/schema-design/SKILL.md +171 -0
- package/data/harnesses/apollo/skills/test/SKILL.md +104 -0
- package/data/harnesses/apollo/skills/testing/SKILL.md +100 -29
- package/data/harnesses/code/CLAUDE.md +29 -30
- package/data/harnesses/code/harness.json +1 -1
- package/data/harnesses/code/hooks/approved-packages.md +42 -0
- package/data/harnesses/code/hooks/compliance.mjs +11 -6
- package/data/harnesses/code/rules/changelog.md +7 -1
- package/data/harnesses/code/rules/cloudflare.md +167 -0
- package/data/harnesses/code/rules/database.md +1 -125
- package/data/harnesses/code/rules/frontend.md +2 -4
- package/data/harnesses/code/rules/general.md +20 -42
- package/data/harnesses/code/rules/nomenclature.md +2 -6
- package/data/harnesses/code/rules/pnpm.md +2 -2
- package/data/harnesses/code/rules/testing.md +44 -3
- package/data/harnesses/code/skills/documentation/references/instruction-style.md +1 -0
- package/data/harnesses/code/skills/documentation/templates/plan.md +4 -4
- package/data/harnesses/code/skills/schema-design/SKILL.md +171 -0
- package/data/harnesses/code/skills/test/SKILL.md +104 -0
- package/data/harnesses/code/skills/testing/SKILL.md +100 -29
- package/data/harnesses/delivery/CLAUDE.md +13 -1
- package/data/harnesses/delivery/harness.json +1 -1
- package/data/harnesses/delivery/rules/changelog.md +7 -1
- package/data/harnesses/delivery/rules/nomenclature.md +2 -6
- package/data/harnesses/delivery/skills/documentation/references/instruction-style.md +1 -0
- package/data/harnesses/delivery/skills/documentation/templates/plan.md +4 -4
- package/data/harnesses/discover/CLAUDE.md +14 -1
- package/data/harnesses/discover/harness.json +1 -1
- package/data/harnesses/discussion/CLAUDE.md +4 -2
- package/data/harnesses/discussion/harness.json +1 -1
- package/data/harnesses/etna/CLAUDE.md +14 -2
- package/data/harnesses/etna/harness.json +1 -1
- package/data/harnesses/etna/rules/changelog.md +7 -1
- package/data/harnesses/etna/rules/general.md +20 -42
- package/data/harnesses/etna/rules/nomenclature.md +2 -6
- package/data/harnesses/etna/rules/testing.md +44 -3
- package/data/harnesses/etna/skills/documentation/references/instruction-style.md +1 -0
- package/data/harnesses/etna/skills/documentation/templates/plan.md +4 -4
- package/data/harnesses/hr/CLAUDE.md +8 -3
- package/data/harnesses/hr/harness.json +1 -1
- package/data/harnesses/kb-agi/CLAUDE.md +8 -2
- package/data/harnesses/kb-agi/harness.json +1 -1
- package/package.json +2 -2
- package/data/harnesses/apollo/rules/express.md +0 -33
- package/data/harnesses/apollo/rules/verification.md +0 -67
- package/data/harnesses/apollo/skills/test-plan/SKILL.md +0 -85
- package/data/harnesses/code/rules/express.md +0 -33
- package/data/harnesses/code/rules/verification.md +0 -67
- package/data/harnesses/code/skills/test-plan/SKILL.md +0 -85
|
@@ -4,7 +4,20 @@ This is the **policy** half of the `discover` harness. The plugin ships the capa
|
|
|
4
4
|
|
|
5
5
|
A run is five modes, in order: **Discovery → Ingestion → Evaluation → Cartography → Self-correction.** The main session drives all five — Ingestion and Evaluation fan out parallel sub-agents (dispatch each via the Agent/Task tool with `model: opus`); Cartography re-enters the live product (the browser singleton) to walk + screenshot it and may fan out doc-drafting sub-agents; a sub-agent cannot spawn further sub-agents.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Delegation
|
|
10
|
+
|
|
11
|
+
The policy for sub-agent dispatch across all five modes — when to fan out, at what tier, and how far to trust the result.
|
|
12
|
+
|
|
13
|
+
- **Delegation keeps bulk tool output out of the main context.** Wide reads, sweeps, broad research, and audits default to a sub-agent: a sub-agent's raw output dies with it, while the same output read inline is re-read and re-billed on every later turn. The hand-off is self-contained (the sub-agent sees none of the conversation), so it must still cost less than the work; independent delegations run in parallel.
|
|
14
|
+
- **Small or judgment work stays inline.** A task finishable in a handful of tool calls is faster done than briefed. Judgment is never delegated by reflex — a second opinion is for a high-stakes call or an explicit request.
|
|
15
|
+
- **A delegated result is a report, not verifiable ground truth.**
|
|
16
|
+
- **Tiers**: mechanical fan-out runs on the cheapest capable tier; judgment-heavy delegation runs on the standard tier; the strongest tier only on explicit user request.
|
|
17
|
+
- **An explicit user directive about tier, cost, or delegation overrides this section.**
|
|
18
|
+
- **Ingestion is the sanctioned mechanical case** — each of the eleven dimensions is large and independent, so its collectors are fanned out deliberately as the default, never by reflex.
|
|
19
|
+
|
|
20
|
+
No dispatch mechanism ⇒ inline.
|
|
8
21
|
|
|
9
22
|
---
|
|
10
23
|
|
|
@@ -20,6 +20,8 @@ Strictly separated — never mix them:
|
|
|
20
20
|
|
|
21
21
|
The rules are the method — portable, abstract, generic examples; nothing depends on any local topic. Live exemplars are reached through `metadata/TOPICS.md`. Workspace-local method notes go to `metadata/method-changelog.md`; the harness itself is versioned by `.claude/harness.json` and documented in the etna forge (`docs/architecture/harness/discussion/`).
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Delegation
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
- **Delegation keeps bulk tool output out of the main session's context** — a sub-agent's raw output dies with it, while output read inline is re-read and re-billed on every later turn. Wide reads, sweeps, broad research, and audits default to a sub-agent, run in parallel when independent; no dispatch mechanism, work stays inline.
|
|
26
|
+
- **Small or judgment work stays inline** — a task finishable in a handful of tool calls is faster done than briefed, and judgment is never delegated by reflex; a delegated result is a report, not verifiable ground truth, so anything load-bearing is verified directly.
|
|
27
|
+
- **Tiers**: mechanical fan-out runs on the cheapest capable tier, judgment-heavy delegation on the session's standard tier, the strongest tier only on explicit user request — an explicit user directive about tier, cost, or delegation overrides this section.
|
|
@@ -148,6 +148,18 @@ When a plan's implementation surfaces work that is important but outside the pla
|
|
|
148
148
|
|
|
149
149
|
---
|
|
150
150
|
|
|
151
|
+
## Delegation
|
|
152
|
+
|
|
153
|
+
The single policy for sub-agent dispatch — when to fan out, at what tier, and how far to trust the result. Component-internal dispatch mechanics (the foreman loop, the brainstorm twin, batch classification) stay in their components and defer here for anything this section covers. No dispatch mechanism ⇒ inline.
|
|
154
|
+
|
|
155
|
+
- **Delegate to keep bulk out of the main context.** Wide file reads, sweeps, broad research, audits, fixture prep — work whose raw tool output the main session doesn't need — defaults to a sub-agent: output a sub-agent reads dies with it, while output read inline is re-read and re-billed on every later turn. The hand-off is self-contained (the sub-agent sees none of the conversation), so it must still cost less than the work; run independent delegations in parallel.
|
|
156
|
+
- **Small or judgment work stays inline.** A task finishable in a handful of tool calls is faster done than briefed. **Never delegate judgment by reflex** — a second opinion is for a high-stakes call or an explicit request.
|
|
157
|
+
- **A delegated result is a report, not verifiable ground truth** — verify load-bearing results directly.
|
|
158
|
+
- **Tiers**: mechanical fan-out → the cheapest capable tier; judgment-heavy delegation → the session's standard tier; the strongest tier **only** on explicit user request. The session itself defaults to the standard tier, entering the strongest only by explicit user switch. Model names live in Runtime Notes.
|
|
159
|
+
- **An explicit user directive about tier, cost, or delegation overrides this section.**
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
151
163
|
## Guardrails & Defaults
|
|
152
164
|
|
|
153
165
|
❌ prohibitions hold unless the user explicitly asks otherwise; ✅ and ⚠️ items are standing policy:
|
|
@@ -158,7 +170,7 @@ When a plan's implementation surfaces work that is important but outside the pla
|
|
|
158
170
|
- ❌ **Branches & Worktrees [override]** — never self-create git branches or worktrees; commit straight to the checked-out branch. This replaces any built-in "branch first on the default branch" default. Exception: when the user asks for a pull request, create the branch the PR requires before committing its changes.
|
|
159
171
|
- ❌ **Protected Files** — before editing, if `docs/operations/protected-files.md` exists, read it and never modify any path it lists; never modify `AGENTS.md` or that file itself.
|
|
160
172
|
- ✅ **Browser Automation** — Claude in Chrome / CDP browser tools: use freely when useful, no need to ask first.
|
|
161
|
-
- ✅ **
|
|
173
|
+
- ✅ **Delegation** — when to fan out, tiers, and trust are § Delegation.
|
|
162
174
|
- ⚠️ **Plans** — created directly in `docs/history/plans/` (see Workflows). Once `Status: Complete` or `Abandoned`, frozen — never edited. If scope changes after completion, write a new dated plan linking back.
|
|
163
175
|
- ⚠️ **Tool Issues** — if any CLI tool, shell alias, or MCP tool behaves unexpectedly (syntax errors, silent failures, wrong defaults), report the exact tool, command, and error to the user so they can fix the underlying library.
|
|
164
176
|
|
|
@@ -179,4 +191,4 @@ For real code (the CLI under `packages/`, hook scripts):
|
|
|
179
191
|
|
|
180
192
|
## Runtime Notes
|
|
181
193
|
|
|
182
|
-
etna runs in Claude Code. Sub-agent dispatch is the Agent tool; sub-agents can run skills and spawn their own sub-agents (probe-verified; re-verify on runtime updates), but cannot hold the browser connection (browser work is Claude in Chrome, main session only). The user-level memory store is `~/.claude/` (auto-memory under `~/.claude/projects/...`). The ownership index and skill/agent descriptions are injected at session start; skills are invoked as slash commands (e.g. `/brainstorm start`). Dispatch tiers: mechanical fan-out runs on the cheapest capable tier;
|
|
194
|
+
etna runs in Claude Code. Sub-agent dispatch is the Agent tool; sub-agents can run skills and spawn their own sub-agents (probe-verified; re-verify on runtime updates), but cannot hold the browser connection (browser work is Claude in Chrome, main session only). The user-level memory store is `~/.claude/` (auto-memory under `~/.claude/projects/...`). The ownership index and skill/agent descriptions are injected at session start; skills are invoked as slash commands (e.g. `/brainstorm start`). Dispatch tiers (§ Delegation): the standard tier is the session default; mechanical fan-out runs on the cheapest capable tier; the strongest tier only on explicit user switch or request — pick by capability at dispatch, since model names go stale.
|
|
@@ -8,7 +8,9 @@ Every unit of work leaves one record — `docs/history/changelog/YYYY-MM-DD-{thr
|
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| A plan reaches `Status: Complete` | A draft entry for that plan, linked to it — written now, while the session still holds the context |
|
|
10
10
|
| The user asks for a changelog | The entry for the work in hand. No commit required |
|
|
11
|
-
| A commit is made | Every non-exempt commit is covered by an entry — the `commit` skill writes or completes it |
|
|
11
|
+
| A commit is made | Every non-exempt commit is covered by an entry — the `commit` skill writes or completes it (no `commit` skill in the runtime ⇒ write the entry from the `documentation` skill's `templates/changelog-entry.md` and stage it with the commit) |
|
|
12
|
+
|
|
13
|
+
An entry is owed only by work that changes promised behavior or takes a design decision; mechanical and cosmetic commits — a typo, a copy tweak, a config value, a log line — take only their `docs/history/CHANGELOG.md` row, no entry file. The failure fields (`Ask:`, `Skills:`, `Pin:`) are required only when non-none.
|
|
12
14
|
|
|
13
15
|
## Lifecycle
|
|
14
16
|
|
|
@@ -17,3 +19,7 @@ Draft (created before its commit exists) → complete (missing fields filled fro
|
|
|
17
19
|
## Granularity
|
|
18
20
|
|
|
19
21
|
A commit is the smallest unit an entry may describe; above that the mapping is judgment — four commits may be four entries or one, whichever reads better as history. An entry spanning several commits rides in the **last** of them, naming the predecessors in `Commits:`. When work exceeds what a draft describes, write a second entry rather than stretching the draft — the first stays an honest account of the plan.
|
|
22
|
+
|
|
23
|
+
## Migrations are a different genre
|
|
24
|
+
|
|
25
|
+
**One-shot migrations are a different genre.** Work that runs **once** against a live system — a data backfill, a schema cutover, a dependency or harness upgrade rolled across repos — is a **migration runbook** at `docs/history/migrations/YYYY-MM-DD-{slug}.md` (template: the `documentation` skill's `templates/migration-runbook.md`), authored **before** execution and outcome-stamped after. A changelog entry says what changed in the repo; a runbook says what was executed against the world and how to verify or undo it. A repeatable procedure is neither — that is `docs/operations/`. When a runbook defers targets, it records **why** and the **trigger to re-evaluate them**, so the next run re-checks rather than inheriting the skip.
|
|
@@ -1,54 +1,32 @@
|
|
|
1
1
|
# General Standards
|
|
2
2
|
|
|
3
|
-
Runtime versions, the
|
|
3
|
+
Runtime versions, package policy, the API response envelope, and GitHub Actions pins — the workspace baseline.
|
|
4
4
|
|
|
5
5
|
## Runtime Versions
|
|
6
6
|
|
|
7
|
-
- **Node.js:**
|
|
7
|
+
- **Node.js:** the version pinned in `.nvmrc` / `package.json` `engines` — **never** a hardcoded number, **never** the runner's default. Workflows use `node-version-file: .nvmrc`.
|
|
8
8
|
- **Package manager:** detect from the lockfile — **never** hardcode one.
|
|
9
9
|
- **PostgreSQL:** 17 · **Redis:** 7.x (latest)
|
|
10
10
|
|
|
11
11
|
## Packages
|
|
12
12
|
|
|
13
|
-
**Only**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
| `sonner` | ^2.0.7 | Toast notifications | `react-toastify` |
|
|
31
|
-
| `react-window` | ^2.2.3 | List virtualization | `react-virtualized` |
|
|
32
|
-
| `nanoid` | ^6.0.0 | ID generation | `uuid` |
|
|
33
|
-
| `express` | ^5.2.1 | HTTP server | `fastify`, `koa` |
|
|
34
|
-
| `drizzle-orm` | ^0.45.2 | Database ORM | `prisma`, `sequelize`, `typeorm` |
|
|
35
|
-
| `drizzle-kit` | ^0.31.10 | Schema tooling | — |
|
|
36
|
-
| `zod` | ^4.2.1 | Schema validation | `yup`, `joi` |
|
|
37
|
-
| `jose` | ^6.1.3 | JWT handling | `jsonwebtoken` |
|
|
38
|
-
| `microdiff` | ^1.5.0 | JSON object diffing | `deep-diff`, `just-diff` |
|
|
39
|
-
| `react18-json-view` | ^0.2.9 | JSON tree view | `react-json-view` |
|
|
40
|
-
| `@tiptap/react` | ^3.11.1 | Rich text editor | `slate`, `quill`, `draft-js` |
|
|
41
|
-
| `shiki` | ^4.3.1 | Syntax highlighting | `prism`, `highlight.js` |
|
|
42
|
-
| `lucide-react` | ^1.26.0 | Icons | `react-icons`, `heroicons` |
|
|
43
|
-
| `@pdfme/generator` | ^6.1.12 | PDF generation | `jspdf`, `pdfkit` |
|
|
44
|
-
| `cmdk` | ^1.1.1 | Command palette | `kbar`, `ninja-keys` |
|
|
45
|
-
| `@monaco-editor/react` | ^4.7.0 | Code editor | `codemirror`, `ace` |
|
|
46
|
-
| `localforage` | ^1.10.0 | Client-side storage | `idb`, `dexie` |
|
|
47
|
-
| `react-markdown` | ^10.1.0 | Markdown rendering | `marked`, `markdown-it` |
|
|
48
|
-
| `remark-gfm` | ^4.0.1 | GFM (tables, tasklists) | `remark-github`, custom |
|
|
49
|
-
| `fast-deep-equal` | ^3.1.3 | Deep equality checks | `lodash.isEqual` |
|
|
50
|
-
| `@ngneat/falso` | latest | Fake data generation | `@faker-js/faker`, `chance` |
|
|
51
|
-
| `pino` | ^10.1.0 | Logging | `winston` |
|
|
13
|
+
**Only packages in the approved table** — `.claude/hooks/approved-packages.md`, enforced by the PreToolUse install gate, which names the file on a block. The table changes only by explicit user decision. Test packages install at `latest`; fake data comes from `@ngneat/falso` — **never** `@faker-js/faker` / `chance`.
|
|
14
|
+
|
|
15
|
+
## API Response Envelope
|
|
16
|
+
|
|
17
|
+
Success and error share **one** envelope — a cross-service contract clients branch on, so a per-service "improvement" (`success: true`, a boolean `status`, a bare payload) breaks every consumer:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"status": 1,
|
|
22
|
+
"message": "Message sent successfully",
|
|
23
|
+
"data": { "delivered": [{ "user@example.com": 2 }] }
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Error: `{ "status": 0, "message": "Error description", "data": null }`
|
|
28
|
+
|
|
29
|
+
HTTP codes, default list: `200`, `201` (created), `400`, `401` (not authenticated), `403` (not authorized), `404`, `500` — plus `204`/`304` (null-body responses), `409` (conflicting write), `429` (rate limit), `503` (unhealthy dependency) where the semantics call for them. Middleware- and protocol-emitted codes are exempt from the envelope.
|
|
52
30
|
|
|
53
31
|
## GitHub Actions
|
|
54
32
|
|
|
@@ -59,4 +37,4 @@ Pin the major, and check it against the action's releases — a major behind sti
|
|
|
59
37
|
| `actions/checkout` | v7 | Repo checkout |
|
|
60
38
|
| `actions/setup-node` | v7 | Node toolchain + npm cache |
|
|
61
39
|
|
|
62
|
-
Set `node-version`
|
|
40
|
+
Set `node-version` via `node-version-file: .nvmrc`, **never** a hardcoded number and **never** the runner's default.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Nomenclature
|
|
2
2
|
|
|
3
|
-
Naming and casing for code — identifiers, files, folders. The rationale throughout is consistency: one concept, one name, one casing shape, in every repo that loads this rule.
|
|
3
|
+
Naming and casing for code — identifiers, files, folders. The rationale throughout is consistency: one concept, one name, one casing shape, in every repo that loads this rule.
|
|
4
4
|
|
|
5
5
|
Names state intent in full words — clarity over brevity; no abbreviations unless universally understood (`id`, `url` ✅ · `cfg`, `btn`, `idx` ❌).
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ Names state intent in full words — clarity over brevity; no abbreviations unle
|
|
|
12
12
|
| `PascalCase` | classes, type definitions, components; the file exporting one | `PaymentProcessor`, `AppsListPage.tsx` |
|
|
13
13
|
| `UPPER_SNAKE_CASE` | constants, environment variables | `MAX_RETRY_COUNT` |
|
|
14
14
|
| `kebab-case` | URLs, CSS class names, HTML IDs, multi-word folders | `user-profile`, `workspace-provisioning/` |
|
|
15
|
-
| `snake_case` | database tables, columns, indexes
|
|
15
|
+
| `snake_case` | database tables, columns, indexes | `created_at` |
|
|
16
16
|
|
|
17
17
|
**A file exporting one component carries that component's name exactly**, so an import names the file it came from; a single-word folder is plain lowercase (`lib`, `surfaces`). ❌ Never `snake_case` a file or folder — that casing belongs to the database.
|
|
18
18
|
|
|
@@ -28,7 +28,3 @@ Names state intent in full words — clarity over brevity; no abbreviations unle
|
|
|
28
28
|
| Event handler | `handle` or `on` prefix | `handleSubmit`, `onPageLoad` |
|
|
29
29
|
| Interface | `I` prefix acceptable; type aliases stay plain | `IConfigOptions`, `AuthResponse` |
|
|
30
30
|
| Async function | verb phrase; `Async` suffix acceptable | `loadConfigAsync` |
|
|
31
|
-
|
|
32
|
-
## Database
|
|
33
|
-
|
|
34
|
-
Table, column, index, and key naming for relational schemas — the `id` / `slug` / `label` vocabulary, junction `_map` tables, and Drizzle mirrors — lives in [`database.md`](database.md), which routes general code naming back here.
|
|
@@ -1,9 +1,44 @@
|
|
|
1
|
-
# Testing
|
|
1
|
+
# Testing & Verification
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**When** work is proven and **which technology** proves it — the standing constraints that bind on every turn. The full lifecycle procedure — deriving a plan's Test Obligations, placing tests in strata, executing the verification gate — is the `testing` skill, invoked at those moments; this rule never restates it.
|
|
4
|
+
|
|
5
|
+
## Two regimes
|
|
6
|
+
|
|
7
|
+
**Deferral is a property of plans, not of code.** A plan carries a Test Obligations ledger and a gate that discharges it; nothing else does, so nothing else may defer.
|
|
8
|
+
|
|
9
|
+
| Regime | During the work | Proven |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| **Plan-driven build** | No new test files. Code only — the loop stays fast | At the verification gate, before `Status: Complete` |
|
|
12
|
+
| **Bug fix** — a defect in shipped behavior | The regression test ships with the fix | Immediately — it fails before the change, passes after |
|
|
13
|
+
| **Ad-hoc / non-plan change** | The test ships with the change **when it alters promised behavior** — layout, copy, and config tweaks owe none | Immediately, same discipline as a bug fix |
|
|
14
|
+
|
|
15
|
+
- **A bug fix cannot defer** — "fails before, passes after" is provable afterward only by reverting to the pre-fix ref, a cost nobody pays; capture the property at fix time or lose it.
|
|
16
|
+
- **Ad-hoc work cannot defer — there is no ledger to defer into.** A non-plan change that alters no promised behavior — a pure refactor, a rename — owes no test.
|
|
17
|
+
|
|
18
|
+
## During the loop
|
|
19
|
+
|
|
20
|
+
Rules for the plan regime between the plan gate and the verification gate:
|
|
21
|
+
|
|
22
|
+
- **No new test files** — obligations wait in the plan's ledger (written at the plan gate by the `testing` skill).
|
|
23
|
+
- **Existing tests broken by the plan's intended behavior change are updated to the new contract before any mid-plan commit** — the shared branch never carries a red suite, and an update is never a weakening (§ Discipline).
|
|
24
|
+
- **A defect in the plan's own in-flux code is the loop iterating** — no test, no ledger row. **A pre-existing bug in shipped behavior is the bug-fix regime even mid-plan**: fix it with its regression test now — deferring it to the gate loses "fails before" — or send it to the backlog via Out-of-Scope Discovery. Never fix silently.
|
|
25
|
+
- **The ledger is amendable while the plan is open** — the gate discharges whatever it holds at gate time.
|
|
26
|
+
- **A mid-plan commit is a checkpoint, not a verification claim.**
|
|
27
|
+
|
|
28
|
+
## The gate
|
|
29
|
+
|
|
30
|
+
**A discrete, user-triggered step.** When implementation is complete, report it and offer the gate — never run it unbidden, and never flip `Status: Complete` without it. An explicit user acceptance of finished plan work — "ship it", "commit", "looks good" — counts as the trigger; open the gate without asking again. A plan with outstanding obligations stays open, whatever the code does. The procedure — checklist, drive-then-pin ladder, row discharge, what earns a durable test — is the `testing` skill (gate mode).
|
|
31
|
+
|
|
32
|
+
- **Precondition: the ledger exists.** A plan with no Test Obligations section is blocked at the gate — run the `testing` skill (plan-gate mode) on it first; an empty ledger passes vacuously and the gap surfaces only after the file is frozen.
|
|
33
|
+
- **The verdict is never delegated** (the root policy file § Delegation), and where browser work is main-session-only (its § Runtime Notes), the driving is too. Fan-out may prepare fixtures or draft test bodies; the driving and the verdict stay inline.
|
|
34
|
+
- **The gate needs no stronger tier than the session default** — a standard-tier session suffices whatever tier built the plan; the plan and its ledger carry all the state the gate needs, so gating in a fresh session loses nothing.
|
|
35
|
+
- **Report what happened, including failures.** A gate that reports green while a suite is red is worse than no gate; state the failing test and its output.
|
|
36
|
+
- **Abandonment does not erase debt.** Before `Status: Abandoned` freezes a plan whose code was committed, convert its undischarged rows into backlog items; uncommitted code owes nothing.
|
|
4
37
|
|
|
5
38
|
## Frameworks by stack
|
|
6
39
|
|
|
40
|
+
Framework choice is **never** ad hoc and never the user's problem mid-task: detect the stack, use its row; if the repo already has a runner, that runner wins — **never** introduce a second framework beside it.
|
|
41
|
+
|
|
7
42
|
| Stack | Unit / integration | Component | E2E | HTTP mocking | Don't use |
|
|
8
43
|
| --- | --- | --- | --- | --- | --- |
|
|
9
44
|
| Node + Vite / React (web) | `vitest` | `@testing-library/react` (+ `@testing-library/user-event`, `happy-dom`) | `@playwright/test` | `msw` | `jest`, `mocha`, `jasmine`, `cypress`, `enzyme` |
|
|
@@ -12,7 +47,7 @@ Test technology and placement — framework per stack, suite layout, mocking pol
|
|
|
12
47
|
| Cloudflare Workers | `vitest` + `@cloudflare/vitest-pool-workers` | — | `@playwright/test` against `wrangler dev` | `msw` | `jest` |
|
|
13
48
|
| Rust | `cargo test` (built-in) | — | — | — | — |
|
|
14
49
|
|
|
15
|
-
Test packages install at `latest` —
|
|
50
|
+
Test packages install at `latest` — outside the approved-packages pin set. Fake data comes from `@ngneat/falso` — **never** `@faker-js/faker` / `chance`.
|
|
16
51
|
|
|
17
52
|
## Conventions
|
|
18
53
|
|
|
@@ -21,3 +56,9 @@ Test packages install at `latest` — they sit outside [`general.md`](general.md
|
|
|
21
56
|
- **Unit and E2E trees stay separate.** Vitest/Jest suites in `tests/`; Playwright/Maestro suites in `e2e/` with their own config — **never** mixed into the unit runner's glob.
|
|
22
57
|
- **No test doubles for what you own.** Prefer real instances — in-memory SQLite for drizzle, the actual Express app via `supertest` — over mocks; mock **only** true externals (network via `msw`, time via the runner's fake timers).
|
|
23
58
|
- **Determinism.** No real network, no real time, no shared mutable state between tests; every test **must** pass alone and in any order.
|
|
59
|
+
|
|
60
|
+
## Discipline
|
|
61
|
+
|
|
62
|
+
- **Never weaken, skip, or delete a test to mask an unexplained failure.** When a plan or LLD intentionally changes behavior, updating the affected tests to the new contract is part of that change.
|
|
63
|
+
- **A behavior that is compiler-guarded owes no test** — if it cannot ship green-but-broken, a test adds cost and no safety.
|
|
64
|
+
- **Throwaway checks are not tests.** Temporary scripts written to drive a change live in scratch space and are deleted — never committed into the test tree.
|
|
@@ -24,6 +24,7 @@ Policy files (`CLAUDE.md`, rules) are declarative — assertion bullets and tabl
|
|
|
24
24
|
## Routing
|
|
25
25
|
|
|
26
26
|
- **Link, don't copy — one canonical source per topic.** `CLAUDE.md` is a **router, not a manual**: per workflow, when to enter, what to dispatch, where the output lands — mechanics live in the agent or skill file, which loads only when it runs.
|
|
27
|
+
- **A rule never references a sibling rule.** Rules co-load, so pointing at one from another routes a reader to text already in front of them — noise that also drifts the moment either file moves. State what the rule owns, self-contained; the only outward links a rule carries are to skills and agents, whose bodies do *not* co-load. Routing downward from `CLAUDE.md` to a rule is fine — that is the router doing its job.
|
|
27
28
|
- **Cut generic engineering advice.** Agents already know how to code; keep only what is true of *this* stack and not derivable by looking at it.
|
|
28
29
|
- **Keep load-bearing rules even when project-specific.** Genericizing is for reference material, not guardrails — deleting a hard rule removes a constraint the agent will otherwise violate.
|
|
29
30
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Plan (docs/history/plans/YYYY-MM-DD-feature-name.md)
|
|
2
2
|
|
|
3
|
-
Record. Use for: every implementation plan. Plans are created here directly with `Status: Draft`; after approval the plan gates (`boundary`, then `
|
|
3
|
+
Record. Use for: every implementation plan. Plans are created here directly with `Status: Draft`; after approval the plan gates (`boundary`, then `testing` plan-gate mode) run against the still-`Draft` file, the status flips to `Status: In Progress` at the first implementation step, and the file freezes at `Status: Complete` or `Status: Abandoned`. Once frozen, never edit — write a new dated plan if scope changes. The plan records the journey; the truth it implemented lives on in the feature doc and any AD it touched.
|
|
4
4
|
|
|
5
|
-
The `boundary` skill parses the **Proposed Changes** sections to check boundary impact before execution, and the `
|
|
5
|
+
The `boundary` skill parses the **Proposed Changes** sections to check boundary impact before execution, and the `testing` skill (plan-gate mode) writes a **Test Obligations** section into the plan at the same gate. Keep that structure stable — section headings and bullet shape are load-bearing for the parsers, and the Test Obligations table is the ledger the verification gate discharges before the plan may reach `Status: Complete`.
|
|
6
6
|
|
|
7
|
-
The **`Gates:`** line is machine-read: the compliance hook greps exactly the forms below, and implementation edits are blocked while a segment reads `pending`. Each gate skill rewrites only its own segment when it runs — `boundary` to `Boundary: passed YYYY-MM-DD` or `Boundary: blocked YYYY-MM-DD`; `
|
|
7
|
+
The **`Gates:`** line is machine-read: the compliance hook greps exactly the forms below, and implementation edits are blocked while a segment reads `pending`. Each gate skill rewrites only its own segment when it runs — `boundary` to `Boundary: passed YYYY-MM-DD` or `Boundary: blocked YYYY-MM-DD`; `testing` (plan-gate mode) to `Test-plan: written YYYY-MM-DD (N rows)` — the segment label stays `Test-plan`, greppable. Never hand-edit the line to skip a gate.
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
# {Feature Name}
|
|
@@ -52,7 +52,7 @@ Why this work is happening *now*. Link to the spec for *what* we're building; th
|
|
|
52
52
|
|
|
53
53
|
## Test Obligations
|
|
54
54
|
|
|
55
|
-
<!-- Written by the `
|
|
55
|
+
<!-- Written by the `testing` skill (plan-gate mode) at the plan gate (after `boundary`). One row per
|
|
56
56
|
promised behavior: layer · target file · oracle · fixtures. Amendable while the
|
|
57
57
|
plan is open; discharged at the verification gate. Do not hand-author. -->
|
|
58
58
|
|
|
@@ -63,6 +63,11 @@ After the rollup, **always produce a one-page candidate brief** (verdict + level
|
|
|
63
63
|
|
|
64
64
|
Every output lands under `out_dir` and is plain markdown/YAML — reviewable, quotable, and reversible.
|
|
65
65
|
|
|
66
|
-
##
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
## Delegation
|
|
67
|
+
|
|
68
|
+
- **Delegate to keep bulk tool output out of the main context.** A sub-agent's raw output dies with it; the same output read inline is re-read and re-billed on every later turn.
|
|
69
|
+
- **Batch code-project scoring is the canonical fan-out here** — the `code-eval-batcher` rule dispatches the `evaluator` skill across projects in parallel, one project per sub-agent. That per-project scoring is exactly the independent, repetitive work this section exists to push out of the main context; nothing here narrows it.
|
|
70
|
+
- **Session-steering scoring stays inline.** Reading a full transcript for evidence and banding it against `rubric.md` is judgment work, not mechanical fan-out — never delegate a scoring pass by reflex; a second opinion is for a high-stakes call or an explicit request.
|
|
71
|
+
- **A delegated result is a report, not verifiable ground truth** — a returned scorecard or rollup still needs its own validation (`validate_scorecard.py`) before it's trusted.
|
|
72
|
+
- **Tiers**: mechanical fan-out (batch per-project dispatch) runs on the cheapest capable tier; judgment-heavy delegation runs on the standard tier; the strongest tier fires only on explicit user request.
|
|
73
|
+
- **An explicit user directive about tier, cost, or delegation overrides this section.** No dispatch mechanism ⇒ inline.
|
|
@@ -60,6 +60,12 @@ The user is adding knowledge. Drafting and placement happen here; the human deci
|
|
|
60
60
|
|
|
61
61
|
Everything is git-checked-in: any write is reviewable and reversible. When anything here seems to conflict with `meta/contract.md`, the contract wins.
|
|
62
62
|
|
|
63
|
-
##
|
|
63
|
+
## Delegation
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
Governs sub-agent dispatch — when to fan out, at what tier, how far to trust the result. No dispatch mechanism ⇒ inline.
|
|
66
|
+
|
|
67
|
+
- **Delegate bulk vault work.** Whole-vault greps, multi-note traversals, corpus sweeps, and audits (`kb-audit`, `kb-init`) are self-contained hand-offs that cost less than doing them in the main session — a sub-agent's raw output dies with it, while the same output read inline is re-read and re-billed on every later turn. Independent sweeps run in parallel.
|
|
68
|
+
- **Small or judgment work stays inline** — a handful of tool calls, a single note's placement call (extend / supersede / background). Judgment is never delegated by reflex: a second opinion is for a high-stakes call or an explicit request.
|
|
69
|
+
- **A delegated result is a report, not verifiable ground truth** — a citation or claim drawn from a sub-agent's findings still resolves to the actual note before it reaches the user.
|
|
70
|
+
- **Tiers**: mechanical fan-out (sweeps, grep passes, batch classification) runs on the cheapest capable tier; judgment-heavy delegation (audit synthesis, proposal drafting) runs on the standard tier; the strongest tier only on explicit user request.
|
|
71
|
+
- **An explicit user directive about tier, cost, or delegation overrides this section.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nurix/etna",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.56",
|
|
4
4
|
"description": "Bootstrap a project with a Nurix harness — copies the harness's CLAUDE.md, skills, agents, rules, and hooks directly into the project from a bundled payload (no marketplace fetch, no network).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"agent",
|
|
43
43
|
"plugin"
|
|
44
44
|
],
|
|
45
|
-
"etnaFingerprint": "
|
|
45
|
+
"etnaFingerprint": "e7493c40d358"
|
|
46
46
|
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Express & API Format
|
|
2
|
-
|
|
3
|
-
Express 5.x server setup and the JSON response envelope every Nurix HTTP service returns — backend only. Package versions live in [`general.md`](general.md); web-frontend conventions in [`frontend.md`](frontend.md).
|
|
4
|
-
|
|
5
|
-
## Express 5.x
|
|
6
|
-
|
|
7
|
-
Canonical middleware stack:
|
|
8
|
-
|
|
9
|
-
```javascript
|
|
10
|
-
app.use(cors({ origin: process.env.FRONTEND_URL }));
|
|
11
|
-
app.use(compression());
|
|
12
|
-
app.use(express.json({ limit: '10mb' }));
|
|
13
|
-
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
- **Middleware order is fixed**: body parsers → custom middleware → routes → error handlers (centralized, **always** last) — no ad-hoc per-route error responses.
|
|
17
|
-
- Auth is middleware, not inline route checks. Routes are modular via Express Router.
|
|
18
|
-
|
|
19
|
-
## API Response Format
|
|
20
|
-
|
|
21
|
-
Success and error share **one** envelope — a cross-service contract clients branch on, so a per-service "improvement" (`success: true`, a boolean `status`, a bare payload) breaks every consumer:
|
|
22
|
-
|
|
23
|
-
```json
|
|
24
|
-
{
|
|
25
|
-
"status": 1,
|
|
26
|
-
"message": "Message sent successfully",
|
|
27
|
-
"data": { "delivered": [{ "user@example.com": 2 }] }
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Error: `{ "status": 0, "message": "Error description", "data": null }`
|
|
32
|
-
|
|
33
|
-
HTTP codes, **closed list**: `200`, `201` (created), `400`, `401` (not authenticated), `403` (not authorized), `404`, `500`.
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# Verification
|
|
2
|
-
|
|
3
|
-
**When** work is proven and **by what mechanism** — the gate, the two regimes, and the ladder of ways to drive a change. Framework choice, suite layout, and mocking policy are [`testing.md`](testing.md); the plan-gate obligation manifest is the `test-plan` skill. This rule owns the moment, not the technology.
|
|
4
|
-
|
|
5
|
-
## Two regimes
|
|
6
|
-
|
|
7
|
-
**Deferral is a property of plans, not of code.** A plan carries a Test Obligations ledger and a gate that discharges it; nothing else does, so nothing else may defer.
|
|
8
|
-
|
|
9
|
-
| Regime | During the work | Proven |
|
|
10
|
-
| --- | --- | --- |
|
|
11
|
-
| **Plan-driven build** | No new test files. Code only — the loop stays fast | At the verification gate, before `Status: Complete` |
|
|
12
|
-
| **Bug fix** — a defect in shipped behavior | The regression test ships with the fix | Immediately — it fails before the change, passes after |
|
|
13
|
-
| **Ad-hoc / non-plan change** | The test ships with the change | Immediately, same discipline as a bug fix |
|
|
14
|
-
|
|
15
|
-
- **A bug fix cannot defer, because "fails before, passes after" is provable afterward only by reverting to the pre-fix ref** — a cost nobody pays, so the property is captured at fix time or lost.
|
|
16
|
-
- **Ad-hoc work cannot defer, because there is no ledger to defer into.** An obligation recorded nowhere is an obligation forgotten. A non-plan change that alters no promised behavior — a pure refactor, a rename — owes no test: the implementer applies § What earns a durable test directly.
|
|
17
|
-
|
|
18
|
-
## During the loop
|
|
19
|
-
|
|
20
|
-
Rules for the plan regime between the plan gate and the verification gate:
|
|
21
|
-
|
|
22
|
-
- **No new test files** — obligations wait in the ledger.
|
|
23
|
-
- **Existing tests broken by the plan's intended behavior change are updated to the new contract before any mid-plan commit** — the shared branch never carries a red suite, and an update is never a weakening (§ What earns a durable test).
|
|
24
|
-
- **A defect in the plan's own in-flux code is the loop iterating** — no test, no ledger row; the behavior it belongs to is already in the ledger. **A pre-existing bug in shipped behavior is the bug-fix regime even mid-plan**: fix it with its regression test now — deferring it to the gate loses "fails before", the property the regime exists to capture — or, when not fixing it now, send it to the backlog via Out-of-Scope Discovery. Never fix silently.
|
|
25
|
-
- **The ledger is amendable while the plan is open** — a boundary finding or a newly promised behavior appends a row; the gate discharges whatever the ledger holds at gate time.
|
|
26
|
-
- **A mid-plan commit is a checkpoint, not a verification claim.** It may ship deliberately unverified code; the gate, not the commit, is the verification event for plan work.
|
|
27
|
-
|
|
28
|
-
## The gate
|
|
29
|
-
|
|
30
|
-
**The gate is a discrete, user-triggered step.** When implementation is complete, report it and offer the gate — never run it unbidden, and never flip `Status: Complete` without it. A plan with outstanding obligations stays open, whatever the code does.
|
|
31
|
-
|
|
32
|
-
- **Precondition: the ledger exists.** A plan with no Test Obligations section is blocked at the gate — run the `test-plan` skill on it first. Without this check an empty ledger passes vacuously and the gap surfaces only after the file is frozen.
|
|
33
|
-
- **Every row is discharged into exactly one outcome**: **satisfied** (the test exists and is green, or the driven check passed), **owes nothing** (with the reason — compiler-guarded, covered elsewhere), or **blocked** (the missing infrastructure named, the row converted to a backlog item so the debt outlives the frozen plan). A row left unexamined blocks the gate.
|
|
34
|
-
- **The gate's verdict is never delegated.** A sub-agent's summary is not verifiable ground truth, and where browser work is main-session-only (the root `CLAUDE.md` § Runtime Notes), the driving is too. Fan-out may prepare fixtures or draft test bodies; the driving and the verdict stay inline.
|
|
35
|
-
- **Abandonment does not erase debt.** Before `Status: Abandoned` freezes a plan whose code was committed, convert its undischarged rows into backlog items; uncommitted code owes nothing.
|
|
36
|
-
- **Report what happened, including failures.** A gate that reports green while a suite is red is worse than no gate; state the failing test and its output.
|
|
37
|
-
|
|
38
|
-
## The mechanism ladder
|
|
39
|
-
|
|
40
|
-
**Drive the real thing first, then pin what must not regress.** Ordering matters: driving finds the defects that matter to a user, and shows which behaviors are worth the permanent cost of a test.
|
|
41
|
-
|
|
42
|
-
| Rung | Mechanism | Proves |
|
|
43
|
-
| --- | --- | --- |
|
|
44
|
-
| 0 | **Write the checklist** — `docs/tests/{feature}.md`, from the ledger and the LLD's promises | There is a stated behavior to drive against, and to trace a test back to |
|
|
45
|
-
| 1 | **Drive the real surface** — browser automation for web UI, the actual CLI binary, HTTP against the running worker | The change works in the product, against real wiring |
|
|
46
|
-
| 2 | **Pin the durable behaviors** as suite tests per [`testing.md`](testing.md) | The behavior survives the next change |
|
|
47
|
-
| 3 | **Run the affected suite**, plus the project's e2e smoke gate where one is wired | Nothing else broke |
|
|
48
|
-
|
|
49
|
-
- **Rung 0 is what makes 1 and 2 repeatable.** The checklist is the script rung 1 executes and the menu rung 2 selects from — one `**Assert**` per observable, and the section records which test now guards it (or that none does, and why). Derived after the driving it is a report; derived before, it is the only artifact that survives the session and still says what the feature promises. Its shape is the `documentation` skill's `templates/behavioral-checklist.md`.
|
|
50
|
-
- **A failing checklist assertion is never softened to make a run green.** The checklist states the contract; a run that fails records the defect and sends it to the backlog.
|
|
51
|
-
- **Browser automation is the default for a web surface, and needs no permission to use** — a rendered page driven through real interaction is the only oracle that catches wiring, styling, and state defects together.
|
|
52
|
-
- **Where the runtime offers no browser automation**, rung 1's verdict belongs to the user: emit a browser-verification checklist (URLs, actions, expected outcomes), the user runs it and reports, the result lands in the changelog entry — and `Status: Complete` waits for it.
|
|
53
|
-
- **A driven check is evidence, not an artifact.** It proves the change works today; it leaves nothing behind. That is why rung 2 exists.
|
|
54
|
-
|
|
55
|
-
## What earns a durable test
|
|
56
|
-
|
|
57
|
-
**A test is a permanent maintenance cost, so it is bought deliberately** — the ledger names behaviors, and the gate decides which of them are worth pinning.
|
|
58
|
-
|
|
59
|
-
| Earns a suite test | Verified by driving alone |
|
|
60
|
-
| --- | --- |
|
|
61
|
-
| A promise an LLD makes about the feature's contract | Layout, spacing, copy — restated by a test, not protected by one |
|
|
62
|
-
| A bug that occurred once — its regression test | A one-off migration or backfill's output |
|
|
63
|
-
| An error path, race, or idempotency guarantee | Behavior a type or the compiler already makes unshippable |
|
|
64
|
-
|
|
65
|
-
- **A behavior that is compiler-guarded owes no test** — if it cannot ship green-but-broken, a test adds cost and no safety.
|
|
66
|
-
- **Never weaken, skip, or delete a test to mask an unexplained failure.** When a plan or LLD intentionally changes behavior, updating the affected tests to the new contract is part of that change.
|
|
67
|
-
- **Throwaway checks are not tests.** Temporary scripts written to drive a change live in scratch space and are deleted — never committed into the test tree.
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: test-plan
|
|
3
|
-
description: "Use when deriving the tests a plan owes. Test-obligation diagnostic: runs at the plan gate after boundary, reads the plan's Proposed Changes + owning LLD, and writes a Test Obligations section (a row per behavior: layer, target file, oracle, fixtures). Writes no test files. Triggers on 'test plan'."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Test-Obligation Diagnostic
|
|
7
|
-
|
|
8
|
-
You derive the tests an implementation plan owes, and pin them into the plan as an explicit, reviewable manifest — at the same gate `boundary` runs, ordered second. You never write a test file; you write the **ledger the verification gate discharges** (`.claude/rules/verification.md`), so the gate proves a checklist instead of a vibe, and so a later doc-health pass can prove the tests match the promises.
|
|
9
|
-
|
|
10
|
-
**The litmus is "which promise, at which altitude, proven by what?"** A plan promises behavior (an LLD's `owns:` surface does X); each promise earns exactly one test at the cheapest layer that can actually prove it. Spend your reading deciding the *layer* and the *oracle*, not on prose.
|
|
11
|
-
|
|
12
|
-
## The three-moment split
|
|
13
|
-
|
|
14
|
-
Testing spans three moments in the plan lifecycle. This skill owns **only the first** — it is why "write the tests at plan time" is wrong: at the gate the code does not exist yet, so the only coherent artifact is the manifest.
|
|
15
|
-
|
|
16
|
-
| Moment | Artifact | Owner |
|
|
17
|
-
| --- | --- | --- |
|
|
18
|
-
| **Plan gate** (here, after boundary) | Test Obligations section written into the plan | this skill |
|
|
19
|
-
| **Verification gate** (before `Status: Complete`) | the obligated tests written and green, plus the driven checks against the real surface | the implementer at the gate (`.claude/rules/verification.md`) |
|
|
20
|
-
| **Verification gate** | behavioral checklist at `docs/tests/{feature}.md`, derived from this ledger **before** the driving, then the tests it seeds | the gate + the `documentation` skill |
|
|
21
|
-
| **Completion** | the gate's results recorded in the changelog; each checklist section carrying its `Pinned:` trace | Completion steps |
|
|
22
|
-
|
|
23
|
-
Writing tests *before* the code they cover either fabricates implementation coupling (labels/routes/copy the plan hasn't fixed) or leaves the suite red on the shared branch for the plan's whole duration. Writing them *during* the build loop taxes every iteration for behavior still in flux. So the plan gate emits obligations, the build loop stays free of test files, and the manifest is discharged in one pass at the verification gate.
|
|
24
|
-
|
|
25
|
-
## Contract sources
|
|
26
|
-
|
|
27
|
-
- **The plan's Proposed Changes** — files created/modified, entities/state, API changes, cross-module contracts. Each is a behavior that may owe a test.
|
|
28
|
-
- **The owning LLD** (`docs/features/{feature}.md`, matched via the ownership index) — the canonical statement of *what the feature does*. Every obligation names the LLD promise it proves; the LLD is never restated, only referenced. This one-directional chain (LLD → manifest → tests + Completion checklist) is what stops the three artifacts drifting into three contradictory truths.
|
|
29
|
-
- **`.claude/rules/testing.md`** — the frameworks and canonical placement per stack. The manifest's target paths and runners come from here, never invented. The repo's existing runner always wins.
|
|
30
|
-
- **`boundary`'s output** — a HIGH/CRITICAL finding it surfaced is itself a behavior owed a regression test (the failure mode, asserted). Fold boundary's amendments first, then read the amended plan.
|
|
31
|
-
|
|
32
|
-
## Layer assignment
|
|
33
|
-
|
|
34
|
-
Assign each behavior the **cheapest layer that can prove it**, then name the oracle (the concrete thing that shows pass/fail):
|
|
35
|
-
|
|
36
|
-
- **unit** — a pure function / reducer / validator. Oracle: return value, thrown error.
|
|
37
|
-
- **component** — a UI unit's behavior in isolation (a widget, form, panel). Oracle: rendered role/text, emitted event. Runner: the stack's component runner per `testing.md`.
|
|
38
|
-
- **integration** — a route/handler against a real in-memory datastore via the app instance (not a mock of what you own). Oracle: response envelope shape, status code, the row actually written. **This is the default for server behavior** — most obligations land here, and this layer is the one the `test` script / CI runs.
|
|
39
|
-
- **e2e** — see the altitude gate below.
|
|
40
|
-
|
|
41
|
-
Error paths, races, and idempotency are obligations too, and they belong at **integration** with injected faults (network mock, the runner's fake timers, a forced constraint) — a process driving a live product cannot reliably *produce* them, so they are never an e2e obligation.
|
|
42
|
-
|
|
43
|
-
### The e2e altitude gate
|
|
44
|
-
|
|
45
|
-
An e2e obligation (a scenario in the project's e2e tree per `testing.md`) is earned **only** when the behavior genuinely spans processes/services that no single-process test can exercise together. A single-process behavior, however important, is an integration obligation, never e2e.
|
|
46
|
-
|
|
47
|
-
- Density is **one scenario per cross-process journey, not one per feature** — the project's existing scenarios are the reference density. Mark each e2e obligation **"extend scenario `<name>`"** in preference to **"new scenario"**; a new scenario needs a genuinely new journey.
|
|
48
|
-
- **Refuse to emit any e2e obligation while the project's e2e smoke gate is not wired.** A generated spec fed to a suite nothing runs is a rot accelerator. The gate is a capped, tagged smoke subset run in CI (not the full e2e suite — an oversized always-on e2e suite is what gets evicted from CI, taking all enforcement with it). If no such CI gate exists, emit the e2e obligation as a **blocked** row naming the missing gate, not as work to do.
|
|
49
|
-
|
|
50
|
-
## Run
|
|
51
|
-
|
|
52
|
-
**Input:** a plan path (given, or the newest in `docs/history/plans/`), already boundary-checked with amendments folded. For a non-plan diff in test-worthy code, the hunks stand in for Proposed Changes — but the derived obligations are paid **immediately**, not deferred: only a plan has a ledger and a gate, so for a diff your output is the checklist the implementer satisfies now.
|
|
53
|
-
|
|
54
|
-
1. **Build the behavior list.** From the plan's Proposed Changes + the owning LLD's promised behavior, list the behaviors this plan introduces or changes. Fold in each HIGH/CRITICAL boundary finding as a regression obligation. Note behaviors a type or the compiler already guards — those owe **no** test (the boundary litmus, inverted: if it can't ship green-but-broken, it needs no test).
|
|
55
|
-
|
|
56
|
-
2. **Assign a layer + oracle** to each behavior (rules above). Prefer the cheapest layer that can actually prove the promise.
|
|
57
|
-
|
|
58
|
-
3. **Map to a target file** per `testing.md` — the stack's unit/integration path for most, the e2e scenario tree for a program scenario. Name fixtures/factories (existing helper, or new) and the fault-injection needed (network mock, fake timers, forced constraint). Use the repo-standard fake-data source.
|
|
59
|
-
|
|
60
|
-
4. **Apply the e2e altitude gate** to any cross-process obligation; downgrade everything else to integration.
|
|
61
|
-
|
|
62
|
-
5. **Emit the Test Obligations section into the plan**, plus a one-line chat summary. Write it as a subsection of the plan (an executor dispatched with only the plan file sees nothing else — an obligation that lives only in chat is lost at dispatch):
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
## Test Obligations
|
|
66
|
-
|
|
67
|
-
| # | Behavior (LLD promise) | Layer | Target file | Oracle | Fixtures / faults |
|
|
68
|
-
| - | ---------------------- | ----- | ----------- | ------ | ----------------- |
|
|
69
|
-
|
|
70
|
-
Coverage: {N} behaviors · {N} integration · {N} component · {N} unit · {N} e2e ({N} extend, {N} new)
|
|
71
|
-
Completion checklist: docs/tests/{feature}.md (derived at Completion, not now)
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
A behavior with no obligation is stated too, with the reason (compiler-guarded, covered by an existing test) — an empty cell reads as "not yet analyzed"; an explicit "none — compiler-guarded" reads as "checked."
|
|
75
|
-
|
|
76
|
-
6. **Record the write in the plan's `Gates:` line.** Rewrite only your own segment, to exactly `Test-plan: written YYYY-MM-DD (N rows)` where N is the ledger's row count. The compliance hook greps this exact form. A plan with no `Gates:` line predates the format; add the line rather than skipping the write-back.
|
|
77
|
-
|
|
78
|
-
## Rules
|
|
79
|
-
|
|
80
|
-
- **Manifest only.** You write the Test Obligations section into the plan and a chat summary. You write **no** test files and **no** `docs/tests/` checklist — those are verification-gate-time and Completion-time, owned by others.
|
|
81
|
-
- **Every obligation names its LLD promise.** No promise, no obligation source — trace it or drop it. This is the anti-drift pin.
|
|
82
|
-
- **Cheapest proving layer wins.** Do not obligate an e2e scenario for what an integration test proves, or an integration test for what a unit test proves.
|
|
83
|
-
- **e2e only above the cross-process bar, and only while the smoke gate runs.** Otherwise the obligation is a blocked row naming the missing gate.
|
|
84
|
-
- **Runs after boundary; skipped on BLOCK.** A blocked plan is re-planned first; there is nothing stable to obligate tests against.
|
|
85
|
-
- **A short manifest is valid.** A plan that owes three integration tests owes three rows — do not pad. The coverage line is the evidence it was analyzed, not skipped.
|