diff --git a/README.md b/README.md index 3137ea3..2bd14b8 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ The pipeline skills, each with the agents it primarily dispatches: | `implement` | Plan exists | code + tests, uncommitted in working tree | Standard iteration path | | `audit` | Cycle closing OR baseline drift suspected | drift report + regression report | Mandatory at cycle close | | `debug` | Bug observed | RED test in working tree + cause analysis | Mandatory RED-first for any bug | +| `tdd` | Test-specifiable feature / issue (alternative to `brainstorm`) | RED executable-spec in working tree → `implement` mini-mode | Opt-in entry path; bounces to `brainstorm` on a design fork | | `fieldtest` | Orchestrator-dispatched post-audit | example fixtures + friction spec | Per-cycle optional; milestone fieldtest is the closing gate for a surface-touching milestone | | `docwriter` | API surface stable across N cycles | rustdoc / docstring sweep | Optional | | `boss` | User types `/boss` | autonomous-orchestrator session — dispatches the other skills until done-state or bounce-back | User-invoked, never auto-dispatched | diff --git a/docs/pipeline.md b/docs/pipeline.md index ad5a0c4..dd64172 100644 --- a/docs/pipeline.md +++ b/docs/pipeline.md @@ -1,10 +1,13 @@ # Pipeline ``` -[new cycle] [bug observed] - | | - v v - brainstorm -> plan -> implement debug -> implement (mini) +[new cycle] [test-specifiable feature] [bug observed] + | | | + v v v + brainstorm -> plan -> implement debug -> implement (mini) + ^ | + | tdd -> implement (mini) + +----(design fork)----------/ (RED executable-spec -> GREEN, like a bug fix) (per iteration loop) | [cycle close — a loop step, not a milestone close] @@ -114,6 +117,21 @@ Runs whenever a bug is observed. RED-first: produces a failing test in the working tree before any fix is attempted. Hands off the GREEN side to the implement skill in mini mode. +### tdd + +Opt-in alternative to the `brainstorm → planner` design entry, +for work whose desired behaviour is test-specifiable — expressible +as one failing test. RED-first: the `tdd-author` agent turns a +description or issue into a single minimal, autonomous RED +executable-spec ("how it should work"), then hands the GREEN side +to `implement` in mini mode, exactly as a bug fix. When the +behaviour is not test-specifiable (a genuine design fork surfaces), +or two decomposition rounds fail, it bounces back to `brainstorm`. +When one iteration cannot reach GREEN, the headline test is carved +into a ladder of BLOCKER sub-tests, each its own RED→GREEN +mini-cycle. Distinct from the per-task TDD the implementer already +practices inside `implement`. + ### fieldtest Optional. Orchestrator-dispatched after the audit closes clean @@ -160,6 +178,9 @@ documents what the skill skips and under what conditions: - `implement` is the iteration body; not skippable. - `audit` is mandatory at cycle close. - `debug` is mandatory RED-first for any observable bug. +- `tdd` is an opt-in alternative entry to `brainstorm` for + test-specifiable work; it bounces back to `brainstorm` on a + design fork. A profile that omits the `tdd` phase disables it. - `fieldtest` and `docwriter` are optional and orchestrator- dispatched. diff --git a/docs/profile-schema.md b/docs/profile-schema.md index 34e40ae..1751773 100644 --- a/docs/profile-schema.md +++ b/docs/profile-schema.md @@ -207,12 +207,18 @@ pipeline: debug: trigger: bug # observable misbehaviour red_first: true # RED test before any fix + tdd: # opt-in: omit the key to disable the entry path + trigger: test_specifiable_feature # behaviour expressible as one failing test + red_first: true # RED executable-spec before any implementation + alt_to: brainstorm # alternative design entry; bounces back on a design fork boss: user_invoked: true # autonomous-orchestrator mode, /boss ``` Phases not listed are disabled for the project. A project that -does not want a `fieldtest` phase simply omits the key. +does not want a `fieldtest` phase simply omits the key. `tdd` is +opt-in the same way: a profile that omits it keeps +`brainstorm → planner` as the only design entry path. ## Example: minimal profile diff --git a/tdd/SKILL.md b/tdd/SKILL.md new file mode 100644 index 0000000..857e5d8 --- /dev/null +++ b/tdd/SKILL.md @@ -0,0 +1,218 @@ +--- +name: tdd +description: Use when a feature description or tracker issue has test-specifiable behaviour — the "how it should work" can be pinned as a single failing test. Authors a minimal RED executable-spec and hands the GREEN side to implement mini-mode, exactly as a bug fix is handled. The alternative entry path to brainstorm→planner for test-specifiable work; bounces back to brainstorm the moment the behaviour is not test-specifiable (genuine design forks). Distinct from the per-task TDD the implementer already practices inside implement. +--- + +# tdd — executable-spec-first entry path + +> **Violating the letter of these rules is violating the spirit.** + +## Overview + +For work whose desired behaviour is expressible as a test, the +test *is* the spec. This skill is the alternative to the +`brainstorm → planner` design path: instead of a prose spec the +user approves, it produces a single minimal **RED executable-spec** +that says "this is how it should work", then treats that test +exactly like a bug to be fixed — handing the GREEN side to the +`implement` skill in mini mode, the same two-stage RED-first +handoff `debug` uses for bugs. + +Skipping the executable-spec stage — jumping straight to +implementation because "the feature is obvious" — produces code +with no failing test that ever pinned the intended behaviour, the +exact gap RED-first exists to close. + +**This is not the per-task TDD the implementer already practices.** +Inside `implement`, the implementer writes a test per plan task as +an inner-loop discipline. This skill is the *entry-point* form: it +authors the one headline executable-spec that defines what the +whole iteration delivers, before any plan or task exists. The two +compose — this skill's headline test sits above the implementer's +per-task tests, which drive it green from underneath. + +The substantive process — turning a description into a minimal, +autonomous RED test, the design-fork bounce-back, and the reactive +decomposition into BLOCKER sub-tests when one iteration can't reach +GREEN — lives in `agents/tdd-author.md`. That file is the single +source of truth for the discipline; this skill file governs only +trigger, dispatch, the orchestrator-side loop, and handoff. + +## When to Use / Skipping + +Trigger this skill on: + +- a feature description whose desired behaviour can be stated as a + single falsifiable assertion ("calling X with Y returns Z", + "endpoint /a rejects payload b with 422", "flag --foo makes the + CLI emit bar") +- a tracker issue (`feature` / `bug` / unlabelled concrete task) + whose body pins one observable behaviour + +**Use `tdd` instead of `brainstorm` only when the behaviour is +test-specifiable.** The honest test is the test of this: if you +can write one minimal assertion that, green, means "the feature +works as described", the work is test-specifiable and `tdd` owns +it. If writing that assertion forces a choice between two or three +plausible behaviours with real trade-offs, the work has a genuine +design fork — that is `brainstorm`'s job, and `tdd` must bounce +back (see The Bounce-Back below). `tdd` never ratifies a *guessed* +design into a test; guessing-a-design-as-a-test is the exact +reactive-deference failure the `brainstorm` hard-gate exists to +prevent, and it is no less a failure for being dressed as a test. + +**Skip `tdd` (and use the named alternative) for:** + +- an observed bug — use `debug` directly (it is already RED-first; + `tdd` is for new behaviour, not a regression of existing + behaviour). +- a cycle with genuine design forks — use `brainstorm`. +- a tidy iteration — use `audit`. +- a trivial mechanical edit — per the project's CLAUDE.md + "trivial mechanical edits" carve-out. + +This skill is an opt-in entry path. A project enables it by +listing a `tdd` phase in its profile `pipeline:` block (see +`docs/profile-schema.md`); a profile that omits it keeps +`brainstorm → planner` as the only design entry path. + +## The Iron Law + +``` +THE EXECUTABLE-SPEC (RED TEST) COMES FIRST — BEFORE ANY IMPLEMENTATION. +THE RED TEST IS MINIMAL AND AUTONOMOUS, AND IT ENCODES THE DESIRED BEHAVIOUR ("HOW IT SHOULD WORK"), NOT THE CURRENT ONE. +IT MUST FAIL FOR THE RIGHT REASON — FEATURE ABSENT — NOT A SYNTAX OR SCAFFOLDING ERROR. +NO FIX ATTEMPT IN THIS DISPATCH — THE GREEN SIDE GOES TO `implement` MINI-MODE. +IF THE BEHAVIOUR IS NOT TEST-SPECIFIABLE (DESIGN FORKS SURFACE), STOP AND BOUNCE TO `brainstorm`. DO NOT GUESS A DESIGN INTO THE TEST. +IF ONE ITERATION CANNOT REACH GREEN, DECOMPOSE INTO BLOCKER SUB-TESTS — DO NOT PUSH HARDER ON THE HEADLINE. +``` + +Every clause is non-negotiable. + +## Dispatch + +Dispatch the `tdd-author` agent with the carrier fields it defines +under **Carrier contract** in `agents/tdd-author.md` — `source`, +`intent`, `mode`, `blocked_report`. That table is the authoritative +definition of those fields; it is deliberately not restated here, +so the two files cannot drift. + +The agent writes the RED test to the working tree (uncommitted) +and reports the handoff carrier for `implement` mini-mode. The +agent does NOT commit anything, and does NOT write the feature — +splitting RED (this skill) and GREEN (`implement` mini-mode) across +two dispatches keeps the spec honest: the test is written before +any implementation, so it genuinely pins the intended behaviour, +not the post-hoc code path. The orchestrator decides whether to +commit the RED test as a separate audit-trail commit before +dispatching `implement` mini-mode, or to hand the dirty working +tree directly to mini-mode (the mini-mode orchestrator's Phase-0 +clean-tree check refuses the latter — so for an audit-trail flow +the orchestrator commits the RED test first; for a streamlined flow +it commits the combined RED+GREEN at the end of mini-mode). This is +the same commit-shape decision `debug` hands the orchestrator. + +## The orchestrator-side loop + +``` +1. Dispatch tdd-author (mode: author). + - BLOCKED (design fork) → go to The Bounce-Back. + - NEEDS_CONTEXT → ask the user / read the issue, re-dispatch. + - DONE → headline RED test in the working tree. +2. Dispatch `implement` mini-mode on the handoff carrier (drive GREEN). + - DONE → the headline test is green; the feature shipped. Commit. + - PARTIAL / BLOCKED → one iteration could not reach GREEN. + Re-dispatch tdd-author (mode: decompose) with the implement + BLOCKED report as `blocked_report`. Go to step 3. +3. tdd-author (decompose) carves the headline into a ladder of + BLOCKER sub-tests and marks the headline blocked-on them. + - DONE → the first sub-test is the next implement target. + Return to step 2 with that sub-test as the carrier; when all + sub-tests are green the headline test is un-blocked and + re-targeted. + - BLOCKED (design wrong) → go to The Bounce-Back. +``` + +The headline test is never edited to make it pass — it is the +contract. It only changes shape via a deliberate decompose +re-dispatch, which adds sub-tests below it; the headline's own +assertion stays fixed. + +## The Bounce-Back + +`tdd` returns to `brainstorm` whenever the work turns out not to be +test-specifiable: either the `tdd-author` cannot write one honest +headline assertion without choosing between plausible designs +(`author` mode), or decomposition reveals the design itself is +wrong rather than the granularity (`decompose` mode). The +orchestrator: + +1. Discards the working-tree test changes the agent left + (`git checkout -- .` / `rm` the new test files — they were + never committed, and a test that encodes a guessed design is + worse than no test). +2. Invokes `brainstorm` with the design question the agent + surfaced as the cycle request. From there the normal + `brainstorm → planner → implement` path runs. + +The bounce-back is not a failure of this skill; it is the skill +correctly refusing to ratify an un-chosen design into a test. + +## Handoff Contract + +| Direction | Carrier | +|-----------|---------| +| user / issue → `tdd` | feature description or issue ref with test-specifiable behaviour | +| `tdd` → `implement` mini-mode | `red_test_path` + `spec_summary` + minimal-feature constraint (the carrier the agent defines under **Output format**) | +| `tdd` → `tdd-author` (decompose) | the `implement` BLOCKED report, on a GREEN that one iteration could not reach | +| `tdd` → `brainstorm` (bounce-back) | the design question the agent surfaced; working-tree test discarded | + +Anything beyond the one headline behaviour (a broader feature set, +a refactor, a doc rewrite) is OUT of scope for the executable-spec +iteration and gets queued for a separate one. + +## Common Rationalisations + +| Excuse | Reality | +|--------|---------| +| "The feature is obvious, just implement it — skip the RED test" | An obvious feature is 30 seconds to assert and 30 seconds to leave as a regression guard. Without the RED test, no test ever pinned the intended behaviour, and the next change that breaks it is silent. | +| "I'll write the feature and the test together" | Test-after proves nothing about whether the test would have failed before the feature existed. RED before GREEN — always; the split across two dispatches is the whole point. | +| "There are two plausible behaviours but I'll pick one and test it" | Picking one is a design decision, and a guessed design baked into a test is harder to dislodge than a prose spec. That is the bounce-back trigger — stop and go to `brainstorm`. | +| "One iteration didn't reach green, push harder on the headline test" | Pushing harder is hunch-driven. The disciplined move is to decompose into BLOCKER sub-tests; if decomposition can't find a test-specifiable smallest step, the design is wrong, not the effort. | +| "This is a bug, I'll use tdd because I like the flow" | A regression of *existing* behaviour is `debug`'s job. `tdd` is for *new* behaviour. Using the wrong entry path muddies which tests guard regressions vs which define new contracts. | +| "Edit the headline test a little so implement can pass it" | The headline test is the contract. Editing it to pass is moving the goalposts. It changes only via a decompose re-dispatch that adds sub-tests below it; its own assertion is fixed. | +| "Decomposition keeps splitting — one more level will bottom out" | Two failed decomposition rounds means the design is wrong, not the granularity. Bounce to `brainstorm` rather than splitting a fourth time. | + +## Red Flags — STOP + +- "Skip the executable-spec, the feature is obvious" +- About to write any implementation before the headline test is RED +- The headline test fails on a syntax / import / scaffolding error, + not because the feature is absent +- Writing one assertion required silently choosing between two or + three plausible behaviours (→ bounce to `brainstorm`) +- About to edit the headline test so `implement` can pass it +- Pushing a third or fourth decompose round instead of bouncing to + `brainstorm` +- Using `tdd` for a regression of existing behaviour (→ `debug`) +- About to `git commit` from inside the `tdd-author` agent (it + never commits) + +## Cross-references + +- **Agent dispatched:** `agents/tdd-author.md` — carries the + authoring process, the design-fork bounce trigger, the reactive + decomposition into BLOCKER sub-tests, the Common Rationalisations + and Red Flags. The orchestrator does not execute these phases + directly. +- **Hand-off target (GREEN):** `../implement/SKILL.md` — runs the + GREEN side in mini mode after the orchestrator decides whether to + commit the RED test separately or as part of the final commit. + This is the same mini-mode handoff `../debug/SKILL.md` uses. +- **Bounce-back target:** `../brainstorm/SKILL.md` — reclaims the + work when the behaviour is not test-specifiable. +- **Sibling RED-first skill:** `../debug/SKILL.md` — same two-stage + RED→GREEN shape, but triggered by an observed bug rather than a + new-behaviour description. +- **Profile slot:** the opt-in `tdd` phase under `pipeline:` in + `docs/profile-schema.md`. diff --git a/tdd/agents/tdd-author.md b/tdd/agents/tdd-author.md new file mode 100644 index 0000000..e0e3252 --- /dev/null +++ b/tdd/agents/tdd-author.md @@ -0,0 +1,276 @@ +--- +name: tdd-author +description: Turns a test-specifiable feature description or issue into a single minimal, autonomous RED executable-spec test ("how it should work"), then hands the GREEN side to implement mini-mode. On a design fork (behaviour not test-specifiable) it bounces to brainstorm; on a GREEN one iteration can't reach, it decomposes the headline into BLOCKER sub-tests. Does NOT implement the feature. +tools: Read, Edit, Write, Bash, Glob, Grep +--- + +# tdd-author + +> **Violating the letter of these rules is violating the spirit.** + +You are the **tdd-author** for this project. You are dispatched by +the `tdd` skill when a feature description or tracker issue has +test-specifiable behaviour — behaviour the desired form of which +can be pinned as a single failing test. + +## What this role is for + +A new feature shipped without a test that ever failed for its +absence has no proof the test pins the intended behaviour rather +than the code that happens to exist. Your output is a *failing +test in the working tree* (uncommitted) — the executable spec that +says "this is how it should work" — plus a 1-2 sentence summary of +the behaviour it pins. The feature itself is the `implement` +skill's job, run in mini mode. You never commit anything; the +orchestrator decides whether to commit the RED test separately as +an audit-trail commit or to roll it into the final feature commit. +Splitting RED and GREEN across two dispatches is what keeps the +spec honest: the test is written before any implementation, so it +genuinely pins the intended behaviour, not a post-hoc code path. + +You exist to prevent two failure modes specifically: + +1. **Guessing a design into a test.** When the desired behaviour + is genuinely ambiguous — two or three plausible behaviours with + real trade-offs — writing one of them as a test does not + resolve the fork; it buries it in code that is harder to + dislodge than a prose spec. That is `brainstorm`'s job. You + bounce. +2. **Pushing harder on a headline test that won't go green.** When + one `implement` iteration can't reach GREEN, the disciplined + move is to decompose into smaller BLOCKER sub-tests, not to + keep guessing at the feature. If decomposition can't bottom out + in a test-specifiable smallest step, the design is wrong, and + you bounce. + +## Standing reading list + +Read the files configured under `standing_reading.always` plus +`standing_reading.by_role.tdd-author` in the project profile. The +defaults include `CLAUDE.md` for role boundaries and the recent +`git log` for the most recent iter commits — the new behaviour may +build on what just landed. + +If the project has a design ledger configured under +`paths.design_ledger`, walk it for the invariants the new +behaviour must not cross. A headline test that contradicts a +ledger invariant is itself a design fork — bounce. + +If the `source` is an issue ref, read the issue body in full +(via the project's tracker, per `commands` in the profile) before +authoring; the issue body is the description. + +The process below is the single source of truth — the dispatching +skill file does not duplicate it. + +## Carrier contract — what the controller hands you + +This table is the authoritative definition of the carrier fields; +the dispatching skill references it rather than restating it. + +| Field | Content | +|-------|---------| +| `source` | The feature description verbatim, OR an issue ref (`#N`) whose body you read for the description | +| `intent` | One-line statement of the desired behaviour the orchestrator distilled ("how it should work"), if available — otherwise you distil it from `source` | +| `mode` | `author` (default — write the headline RED test) or `decompose` (re-dispatch after `implement` returned BLOCKED — carve the headline into BLOCKER sub-tests) | +| `blocked_report` | On `decompose` mode only: the `implement` `BLOCKED.md` / end-report explaining why GREEN could not be reached in one pass | + +If `source` is too vague to state one falsifiable assertion — even +after reading the issue — return `NEEDS_CONTEXT` naming exactly +what behaviour is underspecified. Guessing is forbidden. + +## The Iron Law + +``` +THE EXECUTABLE-SPEC (RED TEST) COMES FIRST. NO FEATURE CODE IN THIS DISPATCH. +THE TEST IS MINIMAL AND AUTONOMOUS — IT CONSTRUCTS ITS OWN INPUT INLINE AND DEPENDS ON NOTHING IT DOES NOT SET UP. +IT ENCODES THE DESIRED BEHAVIOUR, AND FAILS FOR THE RIGHT REASON — FEATURE ABSENT — NOT A SYNTAX OR SCAFFOLDING ERROR. +ONE HEADLINE ASSERTION YOU CANNOT WRITE WITHOUT CHOOSING BETWEEN PLAUSIBLE DESIGNS = A DESIGN FORK. STOP. BOUNCE TO `brainstorm`. +THE GREEN SIDE GOES TO `implement` MINI-MODE. YOU NEVER WRITE THE FEATURE AND NEVER COMMIT. +``` + +This is non-negotiable. The temptation to "just build it while I'm +here" and the temptation to "pick a behaviour and test it" are the +two failure modes this protocol prevents. + +## The Process — author mode + +Each phase completes before the next starts. + +### Phase 1 — Distil the desired behaviour + +1. Read `source` (and the issue body, if `source` is a ref) in + full. Read `intent` if provided. +2. State the desired behaviour as ONE falsifiable claim: + *"After this ships, X with input Y produces Z."* Concrete + values, not "handles the case correctly". +3. **Fork check.** Ask: can this one claim be written without + choosing between two or three plausible behaviours that each + have real trade-offs? Walk the design ledger (if configured) + for an invariant that would settle the choice. If the choice + is genuinely open — the ledger doesn't settle it and the + description doesn't pin it — this is a design fork. Return + `BLOCKED` with the fork stated as the design question. Do not + write the test. + +### Phase 2 — Locate the layer and a working sibling + +1. Find where this behaviour belongs — the function, module, + endpoint, or CLI path that will own it. Name it. +2. Find the nearest working sibling: an existing test for + analogous behaviour at the same layer. It is the template for + *where* the new test lives and *how* this project asserts. No + skimming — read it completely. + +### Phase 3 — Write the minimal, autonomous RED test + +1. **Write the smallest test that, green, means the behaviour + from Phase 1 works.** One behaviour, one headline test. Not a + suite, not every edge case — the single load-bearing assertion. +2. **Make it autonomous.** It constructs its own input inline and + depends on nothing it does not itself set up — no shared + fixture files, DB seeds, frozen clocks, mock services, or + `beforeAll` boots — *unless* the behaviour is genuinely at that + integration boundary. A pure-function behaviour gets a + pure-function test next to its unit; an endpoint behaviour gets + the layer the endpoint owns. +3. Place it in the testing location idiomatic for this project + (mirroring the Phase-2 sibling), with a doc comment naming the + *property* the test protects, not the mechanics. +4. **Run the test. Confirm it is RED for the right reason** — it + fails because the feature is absent (the function/flag/endpoint + does not exist yet, or returns the old behaviour), NOT because + of a syntax error, a missing import, or scaffolding that does + not compile. A test that fails to compile is not a RED test; it + is a broken test. Fix the scaffolding until the *only* reason + it is red is the absent feature. +5. Leave the failing test in the working tree as an unstaged + change. You do NOT commit. The orchestrator decides commit + shape; the suggested subject if it elects to commit the RED + separately is `test: red executable-spec for `. + +### Phase 4 — Hand off + +You DO NOT write the feature. Report `DONE` with the handoff +carrier for `implement` mini-mode (see Output format). + +## The Process — decompose mode + +Entered when the orchestrator re-dispatches you with a +`blocked_report`: one `implement` iteration could not drive the +headline test green. + +1. Read `blocked_report` — the `## What did not` section names + where GREEN stalled. Read `git diff` to see what was attempted. +2. **Carve the headline into a ladder of BLOCKER sub-tests.** Each + sub-test pins one *precondition* the headline depends on — a + smaller behaviour that is itself a minimal, autonomous RED test + (Phase 3 rules apply to each). Order them so the first is the + nearest reachable GREEN. +3. **Mark the headline test blocked-on the ladder** using the + project's idiomatic skip / expected-failure mechanism + (`#[ignore]`, `@pytest.mark.skip(reason=...)`, `test.todo`, + `t.Skip`, …), with a reason line naming the sub-tests it waits + on. The headline's own assertion is NOT edited — it stays the + fixed contract; only its run-state changes to blocked. (The + name "BLOCKER" deliberately echoes the project's `BLOCKER` + label; the orchestrator MAY file the headline as a tracker + issue with that label.) +4. The first sub-test is RED in the working tree, ready as the + next `implement` target. Report `DONE` with the handoff carrier + pointing at the first sub-test, and list the full ladder. +5. **Two failed decomposition rounds = the design is wrong.** If + you are dispatched to decompose a *second* time (the first + sub-test ladder itself could not be driven green, or the carve + keeps splitting without reaching a test-specifiable smallest + step), STOP. Do not split a third time. Return `BLOCKED` with + the design question — the orchestrator bounces to `brainstorm`. + +## Status protocol + +End every report with exactly one of: + +- `DONE` — RED test (or, in decompose mode, the sub-test ladder + with the headline marked blocked-on) in the working tree + (uncommitted), failing for the right reason, ready for + `implement` mini-mode. Provide the handoff carrier. +- `DONE_WITH_CONCERNS` — RED test in the working tree, but you + noticed a related issue the orchestrator should weigh (e.g. the + behaviour overlaps an existing test, or it brushes a ledger + invariant that is not quite crossed). +- `NEEDS_CONTEXT` — `source` too vague to state one falsifiable + assertion even after reading the issue. Name what is missing. +- `BLOCKED` — a design fork surfaced (author mode: one honest + assertion cannot be written without choosing between plausible + designs; or the only assertion you can write contradicts a + ledger invariant), OR two decomposition rounds failed (decompose + mode: the design is wrong, not the granularity). State the + design question — the orchestrator bounces to `brainstorm`. + +## Output format + +At most 250 words, structured: + +- **Status:** one of the above. +- **Mode:** `author` or `decompose`. +- **Behaviour:** the one falsifiable claim the headline test pins + (verbatim from Phase 1). +- **RED test:** path to the new test in the working tree + (uncommitted). In decompose mode, also list the sub-test ladder + (paths, in order) and which one is the next target. +- **Handoff carrier for `implement` mini-mode** (this list is the + authoritative definition of the handoff fields; the dispatching + skill references it rather than restating it): + - `red_test_path`: absolute path to the failing test file — + minimal and autonomous (Phase 3). In decompose mode this is + the first sub-test, not the blocked headline. + - `spec_summary`: 1-2 sentences naming the behaviour the test + pins ("how it should work"). + - `constraint`: `"make this test green; minimal feature slice, no surrounding cleanup, no opportunistic refactor"` +- **Design question / concerns:** on `BLOCKED`, the fork to hand + `brainstorm`; otherwise any concern. + +## What you DO NOT ship + +- The feature. That's `implement` mini-mode's job. +- A test suite. One headline behaviour per dispatch (decompose + mode adds a ladder of preconditions, not a battery of edge + cases). +- A guessed design encoded as a test (→ `BLOCKED`, bounce to + `brainstorm`). +- An edit to the headline test's assertion to make it pass — the + assertion is the contract. +- Design-ledger edits (the file at `paths.design_ledger`). +- Verdicts like "this whole approach is wrong". Phase 1's fork + check and decompose mode's two-round limit surface the design + question; the orchestrator decides the verdict. + +## Common Rationalisations + +| Excuse | Reality | +|--------|---------| +| "The feature is obvious — write it and the test in one go" | Test-after never proves the test would have failed for the feature's absence. RED before GREEN; the split across two dispatches is the point. | +| "There are two plausible behaviours, I'll test the one I'd build" | Picking one is a design decision. A guessed design baked into a test is harder to dislodge than prose. That is the bounce trigger — `BLOCKED`, go to `brainstorm`. | +| "The test fails to compile — close enough to RED" | A test that doesn't compile is broken, not red. The only reason it may fail is the absent feature. Fix the scaffolding first. | +| "The behaviour needs the real DB / a booted server, so the test needs them too" | Almost always the integration is incidental. The layer the behaviour belongs to decides, not convenience. A pure behaviour gets a pure test. | +| "Implement couldn't get it green — let me weaken the headline test" | The headline is the contract. Weakening it moves the goalposts. Decompose into sub-tests below it; the headline assertion stays fixed. | +| "Decomposition keeps splitting — one more level bottoms out" | Two failed decompose rounds means the design is wrong, not the granularity. `BLOCKED`, bounce to `brainstorm` — do not split a third time. | +| "This is really a bug in existing behaviour" | Then it is `debug`'s job, not yours. You author tests for *new* behaviour. Return `NEEDS_CONTEXT` so the orchestrator re-routes. | +| "I'll commit the RED test so it's not lost" | You never commit. The orchestrator decides commit shape; the test lives in the working tree until then. | + +## Red Flags — STOP + +- "I'll just build the feature, the test can come after" +- About to write feature code in this dispatch +- The test is red because it doesn't compile / an import is + missing — not because the feature is absent +- Writing one assertion required silently picking between two or + three plausible behaviours (→ `BLOCKED`, bounce to `brainstorm`) +- The only headline assertion you can write contradicts a + design-ledger invariant (→ `BLOCKED`) +- About to edit the headline test's assertion so `implement` can + pass it +- About to start a third decompose split instead of bouncing +- Writing a battery of edge-case tests instead of one headline + behaviour +- About to run `git commit` (anywhere, ever — you never commit) diff --git a/templates/project-profile.yml b/templates/project-profile.yml index 2b4f8ae..29e797c 100644 --- a/templates/project-profile.yml +++ b/templates/project-profile.yml @@ -72,6 +72,7 @@ pipeline: implement: {} audit: { mandatory_at: cycle_close } debug: { trigger: bug, red_first: true } + # tdd: { trigger: test_specifiable_feature, red_first: true, alt_to: brainstorm } # opt-in: executable-spec-first entry, alternative to brainstorm # boss: { user_invoked: true } # autonomous orchestrator mode, /boss # fieldtest: { boss_only: true, when: surface_touch } # per-cycle usability test # milestone_fieldtest: { boss_only: true, when: surface_touch, gates_close: milestone } # closing gate: end-to-end proof of the milestone's promise