@nurix/etna 0.19.55 → 0.19.57

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.
Files changed (84) hide show
  1. package/data/harness-index.json +1 -1
  2. package/data/harnesses/apollo/CLAUDE.md +29 -30
  3. package/data/harnesses/apollo/agents/bookkeeper.md +1 -1
  4. package/data/harnesses/apollo/agents/change-review.md +1 -1
  5. package/data/harnesses/apollo/agents/lld-mode.md +1 -1
  6. package/data/harnesses/apollo/agents/maintenance.md +1 -1
  7. package/data/harnesses/apollo/agents/performance-review.md +1 -1
  8. package/data/harnesses/apollo/agents/retro.md +1 -1
  9. package/data/harnesses/apollo/harness.json +2 -2
  10. package/data/harnesses/apollo/hooks/approved-packages.md +42 -0
  11. package/data/harnesses/apollo/hooks/compliance.mjs +53 -6
  12. package/data/harnesses/apollo/rules/changelog.md +7 -1
  13. package/data/harnesses/apollo/rules/cloudflare.md +167 -0
  14. package/data/harnesses/apollo/rules/database.md +3 -127
  15. package/data/harnesses/apollo/rules/frontend.md +2 -4
  16. package/data/harnesses/apollo/rules/general.md +20 -42
  17. package/data/harnesses/apollo/rules/nomenclature.md +2 -6
  18. package/data/harnesses/apollo/rules/pnpm.md +2 -2
  19. package/data/harnesses/apollo/rules/testing.md +44 -3
  20. package/data/harnesses/apollo/skills/documentation/references/instruction-style.md +1 -0
  21. package/data/harnesses/apollo/skills/documentation/templates/plan.md +4 -4
  22. package/data/harnesses/apollo/skills/schema-design/SKILL.md +141 -0
  23. package/data/harnesses/apollo/skills/test/SKILL.md +104 -0
  24. package/data/harnesses/apollo/skills/testing/SKILL.md +100 -29
  25. package/data/harnesses/code/CLAUDE.md +29 -30
  26. package/data/harnesses/code/agents/bookkeeper.md +1 -1
  27. package/data/harnesses/code/agents/change-review.md +1 -1
  28. package/data/harnesses/code/agents/lld-mode.md +1 -1
  29. package/data/harnesses/code/agents/maintenance.md +1 -1
  30. package/data/harnesses/code/agents/performance-review.md +1 -1
  31. package/data/harnesses/code/agents/retro.md +1 -1
  32. package/data/harnesses/code/harness.json +2 -2
  33. package/data/harnesses/code/hooks/approved-packages.md +42 -0
  34. package/data/harnesses/code/hooks/compliance.mjs +53 -6
  35. package/data/harnesses/code/rules/changelog.md +7 -1
  36. package/data/harnesses/code/rules/cloudflare.md +167 -0
  37. package/data/harnesses/code/rules/database.md +3 -127
  38. package/data/harnesses/code/rules/frontend.md +2 -4
  39. package/data/harnesses/code/rules/general.md +20 -42
  40. package/data/harnesses/code/rules/nomenclature.md +2 -6
  41. package/data/harnesses/code/rules/pnpm.md +2 -2
  42. package/data/harnesses/code/rules/testing.md +44 -3
  43. package/data/harnesses/code/skills/documentation/references/instruction-style.md +1 -0
  44. package/data/harnesses/code/skills/documentation/templates/plan.md +4 -4
  45. package/data/harnesses/code/skills/schema-design/SKILL.md +141 -0
  46. package/data/harnesses/code/skills/test/SKILL.md +104 -0
  47. package/data/harnesses/code/skills/testing/SKILL.md +100 -29
  48. package/data/harnesses/delivery/CLAUDE.md +13 -1
  49. package/data/harnesses/delivery/agents/lld-mode.md +1 -1
  50. package/data/harnesses/delivery/agents/maintenance.md +1 -1
  51. package/data/harnesses/delivery/agents/retro.md +1 -1
  52. package/data/harnesses/delivery/harness.json +2 -2
  53. package/data/harnesses/delivery/rules/changelog.md +7 -1
  54. package/data/harnesses/delivery/rules/nomenclature.md +2 -6
  55. package/data/harnesses/delivery/skills/documentation/references/instruction-style.md +1 -0
  56. package/data/harnesses/delivery/skills/documentation/templates/plan.md +4 -4
  57. package/data/harnesses/discover/CLAUDE.md +14 -1
  58. package/data/harnesses/discover/harness.json +1 -1
  59. package/data/harnesses/discussion/CLAUDE.md +4 -2
  60. package/data/harnesses/discussion/harness.json +1 -1
  61. package/data/harnesses/etna/CLAUDE.md +14 -2
  62. package/data/harnesses/etna/agents/change-review.md +1 -1
  63. package/data/harnesses/etna/agents/lld-mode.md +1 -1
  64. package/data/harnesses/etna/agents/maintenance.md +1 -1
  65. package/data/harnesses/etna/agents/performance-review.md +1 -1
  66. package/data/harnesses/etna/agents/retro.md +1 -1
  67. package/data/harnesses/etna/harness.json +2 -2
  68. package/data/harnesses/etna/rules/changelog.md +7 -1
  69. package/data/harnesses/etna/rules/general.md +20 -42
  70. package/data/harnesses/etna/rules/nomenclature.md +2 -6
  71. package/data/harnesses/etna/rules/testing.md +44 -3
  72. package/data/harnesses/etna/skills/documentation/references/instruction-style.md +1 -0
  73. package/data/harnesses/etna/skills/documentation/templates/plan.md +4 -4
  74. package/data/harnesses/hr/CLAUDE.md +8 -3
  75. package/data/harnesses/hr/harness.json +1 -1
  76. package/data/harnesses/kb-agi/CLAUDE.md +8 -2
  77. package/data/harnesses/kb-agi/harness.json +1 -1
  78. package/package.json +2 -2
  79. package/data/harnesses/apollo/rules/express.md +0 -33
  80. package/data/harnesses/apollo/rules/verification.md +0 -67
  81. package/data/harnesses/apollo/skills/test-plan/SKILL.md +0 -85
  82. package/data/harnesses/code/rules/express.md +0 -33
  83. package/data/harnesses/code/rules/verification.md +0 -67
  84. package/data/harnesses/code/skills/test-plan/SKILL.md +0 -85
@@ -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.
@@ -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.