feat(pipeline): route to the lightest correct methodology; move execution loops onto the Workflow substrate

The selector forced every task through the heaviest methodology's
critical path: a behaviour-preserving, type-enumerable change paid the
same specify -> planner -> implement front-half as a novel feature,
because it was neither new behaviour (tdd) nor an observed bug (debug)
and so fell to specify by elimination. Two coupled defects — a selector
with no verification axis, and an all-or-nothing executor — kept the
existing lighter path unreachable and uneconomical. This fixes both.

Part A — verification-keyed selector (boss/SKILL.md):
- Replace the three-way "design line" with an ordered cascade that adds
  a verification/enumeration axis ahead of the settled-vs-fork question.
  Each lighter arm carries a positive trigger matched by signature, not
  reached by elimination.
- New `compiler-driven` arm: a type/signature edit at a definition site
  that propagates mechanically. Observe-then-bounce — make the edit,
  build, run the suite; clean build AND suite green unchanged commits;
  a hole bounces up (specify for a design choice, tdd for discovered
  test-specifiable new behaviour); a regression bounces to debug.
- The observed-bug RED-first gate is first in the cascade, so a
  mechanical-looking fix cannot bypass it.
- The straddle rule ("add an enum variant") is codified as a rule:
  mechanical/forwarding -> compiler-driven; encodes new behaviour ->
  tdd/spec; doubt routes up.
- The executor is the elevated inline carve-out plus a shipped workflow,
  not a heavy new skill ("the largest concrete win is small").

Part B — Workflow substrate (implement/workflows/):
- implement-loop.js: the per-task loop as a deterministic script. Each
  phase (implementer -> spec-compliance -> quality, + tester for E2E) is
  a separate top-level agent() call, so a single phase is independently
  invokable and inter-phase aggregation/re-loop is code. Retires the
  implement-orchestrator agent's inline-role-switch workaround (the four
  phase agents survive as the agent-types the script dispatches).
- compiler-driven-edit.js: the observe-then-bounce loop.
- install.sh / uninstall.sh symlink shipped workflows into
  ~/.claude/workflows/.
- specify and brainstorm stay prose + interactive (human-intent oracle);
  only the autonomous/mechanical loops moved. try-and-error is deferred.

Docs (pipeline taxonomy, design, agent-template, migration, README) and
all selector<->executor cross-references updated; the arm and its
executor are co-located so a future re-route through the full loop is a
visible regression.

Verified by an adversarial multi-agent pass: PASS on all six acceptance
criteria; two coherence concerns fixed. The shipped scripts are
syntax-validated but exercised only in a downstream target project (the
skills repo is not itself a pipeline target).

closes #7
This commit is contained in:
2026-06-17 12:27:51 +02:00
parent 8e72aa5c36
commit 7a58a530b1
21 changed files with 991 additions and 720 deletions
+28 -2
View File
@@ -22,7 +22,7 @@ The pipeline skills, each with the agents it primarily dispatches:
| `brainstorm` | New cycle with an open design | ratified design handed to `specify` (writes no spec itself) | Optional discovery front-end | | `brainstorm` | New cycle with an open design | ratified design handed to `specify` (writes no spec itself) | Optional discovery front-end |
| `specify` | Design settled in sources, or handed over by `brainstorm` | spec under `docs/specs` | Hard-gate before plan | | `specify` | Design settled in sources, or handed over by `brainstorm` | spec under `docs/specs` | Hard-gate before plan |
| `planner` | New iteration within an open cycle | plan under `docs/plans` | Hard-gate before implement | | `planner` | New iteration within an open cycle | plan under `docs/plans` | Hard-gate before implement |
| `implement` | Plan exists | code + tests, uncommitted in working tree | Standard iteration path | | `implement` | Plan exists, OR a debug/tdd RED handoff | code + tests, uncommitted in working tree (via the `implement-loop` Workflow) | Standard iteration path |
| `audit` | Cycle closing OR baseline drift suspected | drift report + regression report | Mandatory at cycle close | | `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 | | `debug` | Bug observed | RED test in working tree + cause analysis | Mandatory RED-first for any bug |
| `tdd` | Test-specifiable feature / issue (third entry path, alongside `brainstorm` and `specify`) | RED executable-spec in working tree → `implement` mini-mode | Standard entry path; bounces to `brainstorm` on a design fork | | `tdd` | Test-specifiable feature / issue (third entry path, alongside `brainstorm` and `specify`) | RED executable-spec in working tree → `implement` mini-mode | Standard entry path; bounces to `brainstorm` on a design fork |
@@ -30,6 +30,30 @@ The pipeline skills, each with the agents it primarily dispatches:
| `docwriter` | API surface stable across N cycles | rustdoc / docstring sweep | Optional | | `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; can optionally sign specs in the user's place (opt-in, see below) | User-invoked, never auto-dispatched | | `boss` | User types `/boss` | autonomous-orchestrator session — dispatches the other skills until done-state or bounce-back; can optionally sign specs in the user's place (opt-in, see below) | User-invoked, never auto-dispatched |
**Entry is a selector cascade, not a single door.** New feature work is
routed on a verification / enumeration axis, walked top to bottom, first
match wins: an observed bug → `debug` (RED-first, always first); a design
fork → `brainstorm`; a **behaviour-preserving type/signature edit at a
definition site** → the `compiler-driven` arm; new behaviour pinnable as
one assertion → `tdd`; everything else → spec-driven (`specify`
`planner`). The `compiler-driven` arm is **observe-then-bounce**: make the
edit, build, run the suite — clean build AND suite green *unchanged*
commits; a hole bounces up (`specify`, or `tdd` for test-specifiable new
behaviour), a regression bounces to `debug`. It is not a standalone skill
but an arm of `implement` plus a shipped Workflow.
**The autonomous execution loops run on the Workflow substrate.** The
per-task implement loop and the compiler-driven loop are shipped as
Workflow scripts under `implement/workflows/` (`implement-loop.js`,
`compiler-driven-edit.js`), symlinked into `~/.claude/workflows/` by
`install.sh`. A Workflow orchestrates from the top level, so each per-task
phase (implementer → spec-compliance → quality) is a real,
independently-invokable `agent()` call — which retired the old
`implement-orchestrator` agent's inline-role-switch workaround. The four
phase agents survive as the agent-types the scripts dispatch. The
interactive paths (`specify`, `brainstorm`) stay prose skills — their
oracle is human intent and needs a human in the loop.
Three further **utility skills** are invoked on demand rather than as Three further **utility skills** are invoked on demand rather than as
pipeline phases: `issue` (file or update a tracker item), `glossary` pipeline phases: `issue` (file or update a tracker item), `glossary`
(build or maintain the project glossary — see `docs/glossary-convention.md`), (build or maintain the project glossary — see `docs/glossary-convention.md`),
@@ -77,7 +101,9 @@ This repo (the **plugin**) carries everything that is universal:
- Working-tree-as-quarantine and only-orchestrator-commits - Working-tree-as-quarantine and only-orchestrator-commits
- main HEAD sacrosanct - main HEAD sacrosanct
- No nested subagent dispatch (Claude Code platform constraint; - No nested subagent dispatch (Claude Code platform constraint;
Opus 4.8 Workflows fan out at the top level but do not lift it) Workflows orchestrate at the top level but do not lift it — the
execution loops run as Workflow scripts so their phase agents are
top-level calls, not nested ones)
- No orphan agents - No orphan agents
The constants that used to be configurable but were the same in The constants that used to be configurable but were the same in
+113 -51
View File
@@ -100,6 +100,8 @@ brainstorm → specify → planner → implement → audit → fieldtest
+ +
tdd → implement (mini) (test-specifiable feature) tdd → implement (mini) (test-specifiable feature)
+ +
compiler-driven (behaviour-preserving type/signature edit; observe-then-bounce)
+
docwriter (post-stability) docwriter (post-stability)
``` ```
@@ -110,63 +112,111 @@ new feature work with no spec or plan yet, run the **Entry-path
reflection** below before dispatching anything. Read each skill's reflection** below before dispatching anything. Read each skill's
`SKILL.md` trigger section if unsure. `SKILL.md` trigger section if unsure.
**Entry-path reflection (feature work).** There are **three** entry **Entry-path reflection (feature work).** Entry paths form an **ordered
paths for new feature work, and the choice is made by reflection every cascade**, and the choice is made by reflection every time, not by
time, not by habit. Before habit. The discriminator is no longer a single "design line": it adds a
dispatching, decide which fits the item in hand and record the one-line **verification / enumeration axis** *ahead of* the settled-vs-fork
verdict ("test-specifiable → `tdd`" / "design settled → `specify`" / question, so each lighter path carries a **positive** trigger matched by
"design fork → `brainstorm`") in the loop. The discriminator is the signature rather than being reached by elimination. Walk the cascade top
**design line**: to bottom and take the **first** arm that matches; record the one-line
verdict ("observed bug → `debug`" / "design fork → `brainstorm`" /
"mechanical type-edit → `compiler-driven`" / "test-specifiable →
`tdd`" / "settled prose design → `specify`") in the loop.
- The item's desired behaviour can be pinned by one honest minimal ```
assertion — "calling X with Y returns Z" — then it is route(task):
**test-specifiable** and `tdd` owns it. Dispatch `tdd` autonomously, observed bug -> debug # RED-first gate, FIRST — never bypassed, even for a one-line fix
the same way a bug issue is dispatched to `debug`. forces a genuine design choice -> brainstorm # the only place doubt resolves "up"
- The sources resolve every load-bearing design decision — an a type/signature edit at a definition
exhaustive issue body, a long in-context discussion — then the design site, propagating mechanically -> compiler-driven # observe-then-bounce; behaviour-preserving
is **settled** and `specify` owns it. Dispatch `specify` new behaviour as one honest assertion -> tdd
autonomously: it is bounded (no interview), produces the spec through else -> spec-driven # the deliberate heavy fallback (specify -> planner)
all the gates, and — unless the project enables spec auto-sign (see ```
below) — pauses at its own user-review gate (a problem-state notify,
not a pre-dispatch checkpoint). - **Observed bug → `debug`.** A regression of existing behaviour is
- Writing the spec would force a choice between two or three plausible RED-first, always, and this check is **first in the cascade** so a bug
designs with real trade-offs — a genuine design fork — then discovery whose fix happens to be a one-line type edit cannot slip into the
must resolve it first, and `brainstorm` owns it. Starting a fresh compiler-driven arm and bypass the RED-first gate. Dispatch
autonomously, as a bug issue already is.
- **Forces a genuine design choice → `brainstorm`.** Writing the work
down would force a choice between two or three plausible designs with
real trade-offs. Discovery must resolve it first. Starting a fresh
`brainstorm` cycle is itself a bounce-back per §"Direction freedom" `brainstorm` cycle is itself a bounce-back per §"Direction freedom"
trigger 4: green-light the cycle (and the session shape) with the trigger 4 green-light the cycle (and the session shape) with the
user first; only then does `brainstorm` get dispatched. user first.
- **A type/signature edit at a definition site, propagating mechanically
`compiler-driven`.** The positive trigger is *syntactic and
observable*: a carrier/newtype narrowing, a constructor or field
rename, a signature change at a definition that the type checker then
enumerates across N call sites — **behaviour preserved**. The arm is
**observe-then-bounce** (its executor is `../implement` "The
compiler-driven arm" + the `compiler-driven-edit` workflow): make the
edit, build, run the suite; **clean build AND suite green unchanged →
commit; a hole that forces a design decision → bounce to `specify` (or
`tdd` if the hole turns out to be test-specifiable new behaviour, per
the straddle rule); the suite not green-unchanged → bounce to `debug`,
RED-first.** Entering on
the observable trigger and letting a real run settle the verdict is
what makes this sound — a mis-routed behaviour change fails the
post-condition and bounces instead of shipping green-but-wrong.
Dispatch autonomously; its bounces are reactive, not a pre-dispatch
checkpoint.
- **New behaviour as one honest assertion → `tdd`.** The desired
behaviour can be pinned by one minimal falsifiable assertion — "calling
X with Y returns Z". Dispatch autonomously.
- **Else → spec-driven (`specify``planner`).** The deliberate heavy
fallback for prose-design work the sources already settle (an
exhaustive issue body, a long in-context discussion). `specify` is
bounded (no interview); dispatch it autonomously and it pauses at its
own user-review gate (a problem-state notify, not a pre-dispatch
checkpoint) unless the project enables spec auto-sign.
The asymmetry in autonomy — `tdd` and `specify` dispatch proceed, a **The straddle rule (a rule, not an example).** "Add an enum variant"
fresh `brainstorm` cycle bounces back — is **not** a ranking of the straddles the compiler-driven and the tdd/spec arms. Decide by what the
skills. It is a consequence of context budget: `tdd` and `specify` are edit *encodes*, not by its surface shape: an arm that is **mechanical /
both bounded (no open Q&A), whereas a fresh `brainstorm` is high-context forwarding** — the type checker enumerates the sites and behaviour is
discovery the orchestrator cannot compact on its own (see trigger 4). preserved (an exhaustiveness-only variant, a pass-through case) — is
`specify` dispatched in `/boss` pauses at its own Step-6 user-review **compiler-driven**; an arm that **encodes new behaviour** — a variant
gate to take sign-off — that is a final-sign-off notify, not a whose handling is a new code path with new observable output — is
**tdd** (one assertion) or **spec-driven** (a real design). When unsure
which side a change falls on, that doubt is itself the signal it carries
behaviour: route up to tdd/spec, not down to compiler-driven.
The asymmetry in autonomy — `debug`, `compiler-driven`, `tdd`, and
`specify` all dispatch autonomously, while a fresh `brainstorm` cycle
bounces back — is **not** a ranking of the paths. It is a consequence of
context budget: the four are bounded (no open Q&A; `compiler-driven` and
`debug` self-settle on a run), whereas a fresh `brainstorm` is
high-context discovery the orchestrator cannot compact on its own (see
trigger 4). `specify` dispatched in `/boss` pauses at its own Step-6
user-review gate to take sign-off — a final-sign-off notify, not a
pre-dispatch checkpoint. The one exception is spec auto-sign: when a pre-dispatch checkpoint. The one exception is spec auto-sign: when a
project enables it in its CLAUDE.md project facts, a project enables it in its CLAUDE.md project facts, a spec that clears all
spec that clears all objective gates AND a unanimous adversarial objective gates AND a unanimous adversarial `spec-skeptic` panel is
`spec-skeptic` panel is signed by the orchestrator without pausing, signed by the orchestrator without pausing, and the run continues to
and the run continues to `planner` — see §"Spec auto-sign" below. The `planner` — see §"Spec auto-sign" below. The gate is built so the
gate is built so that the orchestrator's own confidence is never what orchestrator's own confidence is never what signs; absent that, the
signs; absent that, the human signature stays mandatory. Do not human signature stays mandatory. Do not let the asymmetry harden into a
let the asymmetry harden into a reflex reflex of routing borderline items to `brainstorm` "to be safe" — that
of routing borderline items to `brainstorm` "to be safe" — that is the is the exact bias this reflection exists to break; walk the cascade
exact bias this reflection exists to break; apply the design-line test
honestly each time. The one principled tilt is two-sided and lives honestly each time. The one principled tilt is two-sided and lives
*inside* the test: genuine doubt about whether the sources (or one *inside* the test: genuine doubt about whether the sources (or one
assertion) can pin the design *is itself* the design-fork signal, so it assertion) can pin the design *is itself* the design-fork signal, so it
resolves to `brainstorm` — not because `brainstorm` is preferred, but resolves to `brainstorm` — not because `brainstorm` is preferred, but
because unresolved ambiguity is a fork by definition. A `specify` or because unresolved ambiguity is a fork by definition. The lighter arms
`tdd` bounce-back also fires reactively: if either later reports the also bounce reactively: a `specify` or `tdd` that later reports the
design was not settled / not test-specifiable after all, that escalation design was not settled / not test-specifiable, or a `compiler-driven`
routes to `brainstorm` and — being a new cycle needing a fresh run that hits a hole, escalates — `specify` / `tdd` to `brainstorm` (a
discovery — is a bounce-back to the user per the same trigger 4. fresh cycle, a trigger-4 bounce-back), `compiler-driven` to `specify` or
`tdd` (a hole — a design choice vs. test-specifiable new behaviour, per
the straddle rule) or `debug` (the regression).
All three entry paths are always available — none is profile-gated. All entry paths are always available — none is profile-gated. The
The reflection is therefore always three-way: `tdd` for cascade is the order of decision: the lighter arms (`debug`,
test-specifiable behaviour, `specify` for a settled design, `compiler-driven`, `tdd`) carry positive triggers and dispatch
`brainstorm` for an open one. autonomously, `specify` dispatches autonomously and pauses at its
sign-off gate, and a fresh `brainstorm` is the one path that bounces
back to the user before it starts.
If the working tree is mid-flight (uncommitted changes left over If the working tree is mid-flight (uncommitted changes left over
from a previous session): inspect first, then resume the right from a previous session): inspect first, then resume the right
@@ -409,9 +459,12 @@ and defined there. The boss-side contract is just this:
| "Notify can name the iteration code — user will figure out what it means" | No. The notify text is for the user-as-reader who did not watch the orchestrator work. Iteration codes, crate names, function identifiers, type names — all internal, all banned. | | "Notify can name the iteration code — user will figure out what it means" | No. The notify text is for the user-as-reader who did not watch the orchestrator work. Iteration codes, crate names, function identifiers, type names — all internal, all banned. |
| "The previous cycle closed cleanly and the next backlog item is obvious — just dispatch `brainstorm` and keep going" | No. Starting a new cycle is itself a bounce-back. A fresh `brainstorm` is high-context work, and the orchestrator cannot compact its own context — only the user can decide whether the next cycle wants a fresh session or a continuation of this one. The "obviousness" of the candidate is irrelevant; the *cross-cycle hop* is the checkpoint, not the topic choice. | | "The previous cycle closed cleanly and the next backlog item is obvious — just dispatch `brainstorm` and keep going" | No. Starting a new cycle is itself a bounce-back. A fresh `brainstorm` is high-context work, and the orchestrator cannot compact its own context — only the user can decide whether the next cycle wants a fresh session or a continuation of this one. The "obviousness" of the candidate is irrelevant; the *cross-cycle hop* is the checkpoint, not the topic choice. |
| "It's the same broad area as the cycle I just closed, that's not really a new cycle" | If there is no spec file for it yet and it would route through `brainstorm` to get one, it IS a new cycle for this rule's purposes. The rule keys on "needs a fresh spec", not on subjective continuity. | | "It's the same broad area as the cycle I just closed, that's not really a new cycle" | If there is no spec file for it yet and it would route through `brainstorm` to get one, it IS a new cycle for this rule's purposes. The rule keys on "needs a fresh spec", not on subjective continuity. |
| "Feature work, so route it to `brainstorm` — that's the safe default" | `brainstorm`, `specify`, and `tdd` are co-equal entry paths; none is the default. Routing a settled design to `brainstorm` re-litigates decided choices; routing a test-specifiable item there wastes the test path. Run the Entry-path reflection and pick by the design line. The only tilt toward `brainstorm` is a genuinely unresolved fork — and that is a fork, not a default. | | "Feature work, so route it to `brainstorm` — that's the safe default" | The entry paths are an ordered cascade, none the default. Routing a settled design to `brainstorm` re-litigates decided choices; routing a test-specifiable item there wastes the test path; routing a behaviour-preserving type-edit there is the heavy-path gravity well this whole reflection exists to break. Walk the cascade and take the first matching positive trigger. The only tilt toward `brainstorm` is a genuinely unresolved fork — and that is a fork, not a default. |
| "It's just a mechanical rename across N files — run the full implement loop on it" | That is the `compiler-driven` arm's job, not the heavy loop. Route it there; the type checker enumerates the sites, and the done-signal (clean build AND suite green unchanged) is the gate. Funnelling a behaviour-preserving edit through the full per-task loop is the disproportionality the cascade was added to fix. |
| "The compiler-driven edit built clean — that's the done-signal, commit" | Clean build is only half. The done-signal is clean build **AND suite green unchanged**. A behaviour change can build clean; the unchanged-green suite is what catches it. No suite run, no commit — and if the suite isn't green-unchanged, bounce to `debug`. |
| "It's a bug but the fix is a one-line type edit — route it `compiler-driven`" | No. An observed bug is first in the cascade → `debug`, RED-first, regardless of how mechanical the fix looks. The bug-gate sits before the type-edit arm precisely so a regression cannot launder itself around RED-first. |
| "The issue is exhaustive but it's a new cycle, so bounce to the user before `specify`" | `specify` direct-entry is bounded and autonomously dispatchable — it is NOT the high-context `brainstorm` cycle that trigger 4 reserves for the user. Dispatch it; it will pause at its own user-review gate for sign-off. The pre-dispatch bounce is for an *open* design that needs discovery, not for a settled one that needs only production. | | "The issue is exhaustive but it's a new cycle, so bounce to the user before `specify`" | `specify` direct-entry is bounded and autonomously dispatchable — it is NOT the high-context `brainstorm` cycle that trigger 4 reserves for the user. Dispatch it; it will pause at its own user-review gate for sign-off. The pre-dispatch bounce is for an *open* design that needs discovery, not for a settled one that needs only production. |
| "`tdd` is the secondary skill, `brainstorm` is the real entry" | All three entry paths are always available and co-equal. The choice among them is decided by fit per item, reflected on each time — not by treating `brainstorm` as primary and `tdd` as the exception. | | "`tdd` is the secondary skill, `brainstorm` is the real entry" | All entry paths are always available. The choice among them is the cascade, walked per item — not a habit of treating `brainstorm` as primary and the lighter arms as exceptions. The lighter arms exist to be reached; reaching past them by elimination is the failure the verification axis was added to prevent. |
| "Auto-sign is on and this spec is clearly good — I'll sign it and skip the panel" | The panel IS how a spec gets signed under auto-sign; there is no signing on judgement. Your sense that it is clearly good is the precise signal the gate is built not to trust. Run the objective gates, dispatch the five jurors, require unanimity. | | "Auto-sign is on and this spec is clearly good — I'll sign it and skip the panel" | The panel IS how a spec gets signed under auto-sign; there is no signing on judgement. Your sense that it is clearly good is the precise signal the gate is built not to trust. Run the objective gates, dispatch the five jurors, require unanimity. |
| "Four jurors said SOUND, one blocked on something I think is wrong — I'll sign" | Unanimous-or-nothing. You never sign over a `BLOCK`. If it is an editorial lens, repair it and re-run the whole panel (≤ 2 rounds); if it is a design lens or the budget is spent, it routes to the human sign-off it would have had anyway. Signing because *you* think the juror is wrong is overruling the panel — confidence by the back door. | | "Four jurors said SOUND, one blocked on something I think is wrong — I'll sign" | Unanimous-or-nothing. You never sign over a `BLOCK`. If it is an editorial lens, repair it and re-run the whole panel (≤ 2 rounds); if it is a design lens or the budget is spent, it routes to the human sign-off it would have had anyway. Signing because *you* think the juror is wrong is overruling the panel — confidence by the back door. |
| "Auto-sign let me continue, so I don't need to notify — it's just progress" | The auto-sign notify is mandatory and carries a decision (the user's veto over a signature made without them). It is the one sanctioned mid-flow notify precisely because it is not progress — it is the audit trail for a delegated gate. | | "Auto-sign let me continue, so I don't need to notify — it's just progress" | The auto-sign notify is mandatory and carries a decision (the user's veto over a signature made without them). It is the one sanctioned mid-flow notify precisely because it is not progress — it is the audit trail for a delegated gate. |
@@ -424,7 +477,10 @@ and defined there. The boss-side contract is just this:
- About to send a notify that names a crate, an iteration code, or an agent. - About to send a notify that names a crate, an iteration code, or an agent.
- About to bounce back at every iteration boundary "just to be safe" — that's reactive deference, not direction freedom. - About to bounce back at every iteration boundary "just to be safe" — that's reactive deference, not direction freedom.
- About to dispatch `brainstorm` on a backlog issue that does not yet have a spec file, without first bouncing back to the user. New cycles never start autonomously. - About to dispatch `brainstorm` on a backlog issue that does not yet have a spec file, without first bouncing back to the user. New cycles never start autonomously.
- About to route feature work to `brainstorm` without running the Entry-path reflection — defaulting to it because it "feels safer" than `specify` or `tdd`, rather than applying the design-line test. The three are co-equal; the choice is reflected on each time. Routing a *settled* design to `brainstorm` (re-litigating decided choices) is as much a failure as skipping discovery on an open one. - About to route feature work to `brainstorm` without walking the Entry-path cascade — defaulting to it because it "feels safer" than the lighter arms, rather than taking the first matching positive trigger. Routing a *settled* design, a *test-specifiable* item, or a *behaviour-preserving type-edit* to `brainstorm` is as much a failure as skipping discovery on an open one.
- About to route an observed bug to the `compiler-driven` arm because "the fix is mechanical" — a regression is `debug`, RED-first; the bug-gate is first in the cascade for exactly this reason.
- About to route a behaviour-encoding change to `compiler-driven` because it "looks mechanical" — apply the straddle rule: if the change encodes new behaviour it is `tdd` / `spec-driven`, and doubt means it carries behaviour.
- About to accept a `compiler-driven` edit on a clean build without confirming the suite is green *unchanged* — the conjunction is the done-signal; a green-but-wrong change builds clean.
- About to sign a spec under auto-sign on confidence — without all objective gates green and a unanimous `spec-skeptic` panel; signing over any `BLOCK`; self-correcting a *design*-lens (`scope-fork` / `grounding`) `BLOCK` instead of escalating; or looping past the 2-round budget. The gate exists so the orchestrator's confidence never signs. - About to sign a spec under auto-sign on confidence — without all objective gates green and a unanimous `spec-skeptic` panel; signing over any `BLOCK`; self-correcting a *design*-lens (`scope-fork` / `grounding`) `BLOCK` instead of escalating; or looping past the 2-round budget. The gate exists so the orchestrator's confidence never signs.
- About to run the auto-sign path at all when the project has not enabled spec auto-sign, or to skip the mandatory auto-sign notify after signing. - About to run the auto-sign path at all when the project has not enabled spec auto-sign, or to skip the mandatory auto-sign notify after signing.
@@ -453,3 +509,9 @@ and defined there. The boss-side contract is just this:
`../fieldtest`, `../debug`, `../tdd` (test-specifiable feature, `../fieldtest`, `../debug`, `../tdd` (test-specifiable feature,
always available; autonomously dispatchable like `../debug`), always available; autonomously dispatchable like `../debug`),
`../docwriter`. `../docwriter`.
- **Compiler-driven arm:** the lighter `compiler-driven` cascade arm and
its observe-then-bounce executor are owned by `../implement` ("The
compiler-driven arm") and shipped as the `compiler-driven-edit`
workflow. The selector arm here and that executor are co-located on
purpose: a future edit re-routing the light path back through the full
loop changes both cross-references and is a visible regression.
+3 -3
View File
@@ -95,9 +95,9 @@ separate one.
Common Rationalisations table, and the Red Flags list. The Common Rationalisations table, and the Red Flags list. The
orchestrator does not execute these phases directly. orchestrator does not execute these phases directly.
- **Hand-off target:** `../implement/SKILL.md` — runs the - **Hand-off target:** `../implement/SKILL.md` — runs the
GREEN side after the orchestrator has decided whether to GREEN side as the `implement-loop` workflow in `mode: "mini"`, after
commit the RED test separately or as part of the final fix the orchestrator has decided whether to commit the RED test separately
commit. or as part of the final fix commit.
- **Sibling RED-first skill:** `../tdd/SKILL.md` — same - **Sibling RED-first skill:** `../tdd/SKILL.md` — same
two-stage RED→GREEN handoff to `implement` mini-mode, but two-stage RED→GREEN handoff to `implement` mini-mode, but
triggered by a new test-specifiable behaviour rather than an triggered by a new test-specifiable behaviour rather than an
+8 -5
View File
@@ -76,8 +76,7 @@ plugin's agent path is enough disambiguator).
Examples: `architect`, `bencher`, `debugger`, `implementer`, Examples: `architect`, `bencher`, `debugger`, `implementer`,
`tester`, `fieldtester`, `docwriter`, `grounding-check`, `tester`, `fieldtester`, `docwriter`, `grounding-check`,
`spec-skeptic`, `plan-recon`, `spec-reviewer`, `quality-reviewer`, `spec-skeptic`, `plan-recon`, `spec-reviewer`, `quality-reviewer`.
`implement-orchestrator`.
### `description` ### `description`
@@ -98,9 +97,13 @@ Common tool sets:
grounding-check, spec-skeptic, plan-recon): `Read, Glob, Grep, Bash` grounding-check, spec-skeptic, plan-recon): `Read, Glob, Grep, Bash`
- Implementation (implementer, tester, debugger, docwriter, - Implementation (implementer, tester, debugger, docwriter,
fieldtester, bencher): `Read, Edit, Write, Bash, Glob, Grep` fieldtester, bencher): `Read, Edit, Write, Bash, Glob, Grep`
- Orchestrator (implement-orchestrator): `Read, Edit, Write,
Bash, Glob, Grep` — same as implementer; the orchestration The autonomous execution loops are **Workflow scripts**, not agents
happens via sequential role-switches within its own context (`implement/workflows/`): they orchestrate the implementation phase
agents from the top level via `agent()` calls. The agents they
dispatch still carry no `Agent` tool — the no-nested-dispatch rule is
unchanged; the workflow simply does the orchestration the retired
`implement-orchestrator` agent used to fake with inline role-switches.
## Sections in detail ## Sections in detail
+23 -9
View File
@@ -29,8 +29,13 @@ genuinely-varying parts moved into each project's `CLAUDE.md`.
The plugin owns: The plugin owns:
- **Pipeline form** — the directed graph of phases: - **Pipeline form** — the directed graph of phases:
`design → plan → execute → review → close`, with the `design → plan → execute → review → close`, entered through a
bug-driven side path `debug → execute (mini)`. **selector cascade** of entry paths walked on a verification /
enumeration axis (observed bug → `debug`; design fork →
`brainstorm`; behaviour-preserving type/signature edit →
`compiler-driven`; test-specifiable → `tdd`; else → spec-driven).
The taxonomy of paths and their oracles lives in
`pipeline.md` § The methodology taxonomy.
- **Hard-gates** — spec before plan, plan before implement, - **Hard-gates** — spec before plan, plan before implement,
audit before cycle-close. Skipping rules are codified per audit before cycle-close. Skipping rules are codified per
skill. skill.
@@ -50,13 +55,22 @@ The plugin owns:
the working tree, never commit. Only the orchestrator commits. the working tree, never commit. Only the orchestrator commits.
- **main HEAD sacrosanct** — no reset, no revert, by any actor. - **main HEAD sacrosanct** — no reset, no revert, by any actor.
main moves forward only via orchestrator commits. main moves forward only via orchestrator commits.
- **No nested subagent dispatch** — a hard Claude Code - **No nested subagent dispatch** — a hard Claude Code platform
platform constraint; the implement-orchestrator runs phases constraint: an agent cannot spawn further agents. Workflows do not
as sequential role-switches inside its own context. Opus 4.8 lift it; a workflow script orchestrates from the **top level**, and
added Workflows as an orthogonal top-level fan-out mechanism the agents it spawns still cannot spawn further agents. This is
but did not lift this constraint — a workflow script exactly why the autonomous execution loops moved onto the Workflow
orchestrates from the top level, yet the agents it spawns substrate: the `implement-loop` and `compiler-driven-edit` scripts
still cannot spawn further agents. (`implement/workflows/`) run the per-phase agents as their own
top-level `agent()` calls, so a single phase is independently
invokable — which retired the former `implement-orchestrator`
agent's inline-role-switch workaround (it had to absorb the three
phases into one context precisely because it could not nest). The
four phase agents survive as the agent-types the scripts dispatch;
what was retired is the dispatch/aggregation prose. The interactive
paths (`specify`, `brainstorm`) stay prose skills — their oracle is
human intent and needs a human in the loop, which a
run-to-completion workflow cannot host.
- **No orphan agents** — every agent lives under the skill - **No orphan agents** — every agent lives under the skill
that dispatches it. that dispatches it.
- **Output budget discipline** — agents have word budgets on - **Output budget discipline** — agents have word budgets on
+10
View File
@@ -30,6 +30,16 @@ it into `~/.claude/skills/<name>`; if the skill has an
discovery still finds everything while the source tree keeps discovery still finds everything while the source tree keeps
the structural binding. the structural binding.
A skill that ships executable **Workflow scripts** keeps them in a
`workflows/` subdirectory beside its `SKILL.md` (e.g.
`implement/workflows/implement-loop.js`). `install.sh` symlinks each
`*.js` there into the flat `~/.claude/workflows/` directory — where the
Workflow tool resolves named workflows from — keeping the same
structural binding (the script lives with its dispatching skill) as
agents do. `uninstall.sh` removes those symlinks too. A workflow script
is the deterministic form of an autonomous execution loop; it dispatches
the skill's agent-types via top-level `agent()` calls (no nesting).
## Migration checklist per skill ## Migration checklist per skill
1. Strip project-specific paths (`docs/specs`, `docs/plans`, 1. Strip project-specific paths (`docs/specs`, `docs/plans`,
+74 -17
View File
@@ -1,14 +1,15 @@
# Pipeline # Pipeline
``` ```
[new cycle] [test-specifiable feature] [bug observed] ENTRY PATHS — the selector cascade, walked top to bottom (see boss/SKILL.md)
| | | bug observed ........... debug ........................ implement (mini) # RED-first; FIRST in the cascade
v v v design fork ............ brainstorm -> specify -> plan -> implement
brainstorm -> specify -> plan -> implement debug -> implement (mini) type/signature edit .... compiler-driven -> commit (clean build + suite green unchanged)
^ ^ | -> bounce: specify | tdd (a hole) | debug (suite red)
| | tdd -> implement (mini) test-specifiable ....... tdd -> implement (mini) # RED executable-spec -> GREEN
| specify -> plan (design settled in sources) settled prose design ... specify -> plan -> implement
+----(design fork)----------/ (specify/tdd bounce here; RED executable-spec -> GREEN)
(specify / tdd bounce to brainstorm on a design fork)
(per iteration loop) (per iteration loop)
| |
[cycle close — a loop step, not a milestone close] [cycle close — a loop step, not a milestone close]
@@ -33,6 +34,30 @@
next cycle next cycle
``` ```
## The methodology taxonomy
Development paths differ on two irreducible axes — the **first artefact
written**, and the **correctness oracle**. (Error-recovery is not a
third axis: whole-attempt discard is the plugin's universal containment,
not a per-path signature.) The top-level cut — ratified prose intent
before code (spec-driven) vs. machine / test / run oracles — is the hard
boundary; the paths cannot be centralised in one skill, only share the
implement executor as a primitive and the cycle-close `audit` tail.
| Path | First artefact | Oracle | Bounce-out when |
|------|----------------|--------|-----------------|
| **spec-driven** | ratified prose spec | human intent, judged at the document | (the deliberate heavy sink) |
| **tdd / debug** | a RED test | the assertion goes green | not test-specifiable → `brainstorm` |
| **compiler-driven** | a type / signature edit | clean build + suite green unchanged | a hole needs a decision → `specify` (or `tdd` if it is test-specifiable new behaviour); suite red → `debug` |
| **try-and-error** | throwaway code | an observed run | attempt budget exhausted |
`try-and-error` is **deferred** — listed for completeness, not yet a
live path. It is an exploration front-end terminating in a tdd-style
handoff, and its attempt tail is structurally unbounded (it can cost
more wall-clock than the spec-driven baseline it is meant to beat, and
its attempt-budget is a magic number the selector cannot derive). Out of
scope until it is given a bounded form.
## Cycle vs. milestone ## Cycle vs. milestone
These are two distinct axes, and conflating them is a bug. These are two distinct axes, and conflating them is a bug.
@@ -130,12 +155,35 @@ the plan-recon agent for read-only file-structure mapping.
### implement ### implement
Dispatches the implement-orchestrator agent, which runs the Runs the `implement-loop` Workflow (`implement/workflows/implement-loop.js`),
entire per-task loop (implementer phase → spec-compliance check a deterministic script that executes the per-task loop —
→ quality check) as sequential role-switches inside its own implementer → spec-compliance → quality, each a separate
context. Writes code, tests, and stats files directly in the `agent()` call — and aggregates the verdicts in code. A Workflow
working tree as unstaged changes. On `PARTIAL` or `BLOCKED`, orchestrates from the top level, so the per-task phases are real,
also writes `BLOCKED.md` at the repo root. independently-invokable agent calls; this retired the former
`implement-orchestrator` agent's inline-role-switch workaround
(the four phase agents survive as the agent-types the script
dispatches). Writes code, tests, and stats files directly in the
working tree as unstaged changes; never commits. On `PARTIAL` or
`BLOCKED`, also writes `BLOCKED.md` at the repo root.
### compiler-driven
The lighter executor for a **behaviour-preserving type/signature
edit at a definition site** — the cascade arm between `debug` and
`tdd`. Runs the `compiler-driven-edit` Workflow
(`implement/workflows/compiler-driven-edit.js`): make the edit,
propagate it mechanically across the sites the build enumerates,
then let a real build + suite run settle the verdict —
**observe-then-bounce**. Clean build AND suite green *unchanged*
the edit is committed by the orchestrator; a hole that forces a
design decision → bounce to `specify` (or `tdd` if it turns out to be
test-specifiable new behaviour, per the straddle rule); the suite not
green-unchanged (a regression — the edit was not behaviour-preserving)
→ bounce to `debug`, RED-first. A truly trivial edit may run inline
without the
workflow, under the same done-signal. Never skips the
review-and-commit discipline; the orchestrator inspects and commits.
### audit ### audit
@@ -209,14 +257,23 @@ documents what the skill skips and under what conditions:
before it: skipped when the design is already settled in the sources before it: skipped when the design is already settled in the sources
(the work enters through `specify` directly), run when a load-bearing (the work enters through `specify` directly), run when a load-bearing
decision is still open. decision is still open.
- `planner` is never skipped at iteration start, except for - `planner` is never skipped at iteration start, except for the
the bug-driven `debug → implement (mini)` side path. side paths that carry no prose plan: the bug-driven
`debug → implement (mini)` path and the `compiler-driven` path
(whose "plan" is the type-checker's enumeration of edit sites).
- `implement` is the iteration body; not skippable. - `implement` is the iteration body; not skippable.
- `audit` is mandatory at cycle close. - `audit` is mandatory at cycle close.
- `debug` is mandatory RED-first for any observable bug. - `debug` is mandatory RED-first for any observable bug — first in
the selector cascade, so a mechanical-looking fix cannot bypass it.
- `tdd` is a standard alternative entry to `brainstorm` for - `tdd` is a standard alternative entry to `brainstorm` for
test-specifiable work; it bounces back to `brainstorm` on a test-specifiable work; it bounces back to `brainstorm` on a
design fork. Always available — not opt-in. design fork. Always available — not opt-in.
- `compiler-driven` is the cascade arm for a behaviour-preserving
type/signature edit: it skips `specify` and `planner`, and its
done-signal (clean build + suite green unchanged) is the gate. It
bounces on a hole to `specify` (a design choice) or `tdd` (discovered
test-specifiable new behaviour), and to `debug` on a regression.
Always available — not opt-in.
- `fieldtest` and `docwriter` are optional and orchestrator- - `fieldtest` and `docwriter` are optional and orchestrator-
dispatched. dispatched.
+205 -174
View File
@@ -1,186 +1,216 @@
--- ---
name: implement name: implement
description: Use when an implementation plan exists under docs/plans and is ready to execute, OR when a debug RED-test is handed off for a bugfix. Dispatches the implement-orchestrator agent, which runs the entire per-task loop (implementer phase → spec-compliance check → quality check, as sequential role-switches in its own context) directly in the working tree without creating commits, writes a stats file (and on BLOCKED/PARTIAL also `BLOCKED.md`), and returns a compressed end-report. The orchestrator reads the end-report, inspects the working tree, decides commit shape, and performs all commits. description: Use when an implementation plan exists under docs/plans and is ready to execute, OR when a debug/tdd RED-test is handed off for the GREEN side. Runs the `implement-loop` Workflow — a deterministic script that executes the per-task loop (implementer → spec-compliance → quality, each a separate agent call) and aggregates in code, writing edits, tests, and a stats file to the working tree without committing (and `BLOCKED.md` on PARTIAL/BLOCKED). The orchestrator reads the end-report, inspects the working tree, decides commit shape, and performs all commits. Also documents the `compiler-driven` light-edit arm.
--- ---
# implement — plan execution via a dedicated orchestrator-agent # implement — plan execution on the Workflow substrate
> **Violating the letter of these rules is violating the spirit.** > **Violating the letter of these rules is violating the spirit.**
## Overview ## Overview
Plan execution is fully delegated. The orchestrator dispatches Plan execution runs as the **`implement-loop` Workflow** (shipped at
ONE subagent (`implement-orchestrator`), which runs the entire `workflows/implement-loop.js`, symlinked into `~/.claude/workflows/` by
per-task loop in its own context: implementer phase → spec- `install.sh`). The orchestrator invokes it through the Workflow tool;
compliance check → quality check, per task, as sequential the script runs the entire per-task loop deterministically — for each
role-switches inside the orchestrator-agent itself (Claude task, implementer → spec-compliance → quality, **each a separate
Code does not permit nested subagent dispatch — see `agent()` call** dispatching the surviving phase agent-types
Cross-references). All work lives in the working tree: code (`implementer`, `spec-reviewer`, `quality-reviewer`, and `tester` for
edits and the stats file (and `BLOCKED.md` if the outcome is the E2E phase). The inter-phase re-loop, the aggregation, and the
PARTIAL/BLOCKED). The orchestrator-agent does NOT commit. DONE/PARTIAL/BLOCKED verdict are code in the script, not
The orchestrator sees one ≤500-token end-report and an natural-language reasoning between dispatches.
unstaged working tree, then decides commit shape — the
end-report carries the per-task summary the orchestrator
uses to write the commit body.
This skill body is intentionally short. The procedural This replaces the former `implement-orchestrator` agent, which carried
details of the per-task loop live in the loop as inline role-switches inside one subagent context because
`agents/implement-orchestrator.md`. The **canonical Claude Code forbids nested-subagent dispatch. A Workflow orchestrates
discipline** (Iron Law, sub-status table, common from the top level, so that workaround is retired: each phase is now a
rationalisations) lives in this file and is read by the real, **independently-invokable** agent call, and the routing keys on
orchestrator-agent every dispatch. the structured verdict each agent reports (which encodes the project's
real build/test outcome). The four phase agents survive unchanged as the
agent-types the script dispatches; only the dispatch/aggregation prose
the orchestrator-agent carried is gone.
All work lives in the working tree: code edits, the stats file, and —
on PARTIAL/BLOCKED — `BLOCKED.md` at the repo root. The script **never
commits** and never moves main HEAD. The orchestrator reads one
end-report (the workflow's return value), inspects the unstaged tree,
and decides commit shape.
## When to Use / Skipping ## When to Use / Skipping
Triggers: Triggers:
- A plan exists under `docs/plans` (standard mode). - A plan exists under `docs/plans` (standard mode`implement-loop`
- A `debug` skill (RED test + cause) or a `tdd` skill (RED with `mode: "standard"`).
executable-spec) has handed off for the GREEN side (mini mode). - A `debug` (RED test + cause) or `tdd` (RED executable-spec) handoff
for the GREEN side (mini mode → `implement-loop` with `mode: "mini"`).
**Never skipped** when there is code to ship. Trivial **Never skipped when there is plan-driven or RED-handoff code to ship.**
mechanical edits (one-line typo fix, schema rename across N
files) MAY be handled inline by the orchestrator without ### The compiler-driven arm (light path)
dispatch, per the project's CLAUDE.md "trivial mechanical
edits" carve-out — but no review-and-commit discipline is A **behaviour-preserving type/signature edit at a definition site**
shed. one the type checker enumerates across N sites, e.g. a carrier/newtype
narrowing or a constructor rename propagated mechanically — does NOT go
through the full per-task loop. It is the `compiler-driven` selector
arm (see `../boss/SKILL.md` "Entry-path reflection"), routed here by a
**positive** trigger matched on the edit's signature, not reached by
elimination.
Its executor is **observe-then-bounce**, shipped as
`workflows/compiler-driven-edit.js`:
1. Make the edit; propagate it mechanically across the sites the build
flags. Introduce no new behaviour; add or weaken no test.
2. **Run the project's real build + full suite.** This run — not an
ex-ante guess that the change is "behaviour-preserving" — is the
oracle.
3. **Done-signal: clean build AND suite green unchanged → the edits sit
unstaged for the orchestrator to commit.** That conjunction is the
whole gate; it keeps the light path light without letting a
green-but-wrong change through.
4. **Bounce** otherwise — the change was not purely behaviour-preserving
after all, so route up per the straddle rule: a site that forces a
design decision → `specify`; a site that turns out to **encode new
behaviour** pinnable as one assertion → `tdd`, RED-first; the suite
not green-unchanged (a regression surfaced) → `debug`, RED-first. The
fallible ex-ante guess becomes a cheap ex-post detection.
A truly trivial mechanical edit (a one-line typo, a rename across a
handful of files) the orchestrator MAY still do inline without running
the workflow — but the **same done-signal binds**: clean build + suite
green unchanged, or it bounces. No review-and-commit discipline is shed;
the orchestrator still inspects and commits.
**Hard gate — observed bugs never enter this arm.** An observed bug is
`debug`'s job (RED-first), even when the fix is a one-line type edit.
The selector places the observed-bug check *before* the type-edit arm
for exactly this reason; "the fix is mechanical" must not reroute a
regression around the RED-first gate.
## The Iron Law ## The Iron Law
``` ```
THE IMPLEMENTER NEVER COMMITS. CODE EDITS AND THE STATS FILE LIVE IN THE WORKING TREE AS UNSTAGED CHANGES UNTIL THE ORCHESTRATOR COMMITS THEM. THE LOOP NEVER COMMITS. CODE EDITS, TESTS, AND THE STATS FILE LIVE IN THE WORKING TREE AS UNSTAGED CHANGES UNTIL THE ORCHESTRATOR COMMITS THEM.
MAIN HEAD IS SACROSANCT — NO RESET, NO REVERT, BY ANY ACTOR. MAIN MOVES FORWARD ONLY VIA ORCHESTRATOR COMMITS. MAIN HEAD IS SACROSANCT — NO RESET, NO REVERT, BY ANY ACTOR. MAIN MOVES FORWARD ONLY VIA ORCHESTRATOR COMMITS.
PER-TASK PHASES RUN SEQUENTIALLY IN THE ORCHESTRATOR-AGENT'S OWN CONTEXT — implementer phase, then spec-compliance check, then quality check. Each phase is a deliberate role-switch, NOT a fresh subagent (nested-subagent dispatch is forbidden by Claude Code). PER-TASK PHASES ARE SEPARATE AGENT CALLS IN THE WORKFLOW SCRIPT — implementer, then spec-compliance, then quality. SPEC COMPLIANCE IS GATED BEFORE QUALITY.
TWO-STAGE CHECK PER TASK: SPEC COMPLIANCE FIRST, CODE QUALITY SECOND. TASKS RUN SEQUENTIALLY; A BLOCKED TASK STOPS THE LOOP — NO SKIP-AHEAD (TASK ORDERING DEPENDENCIES ARE UNKNOWN).
NEVER START THE QUALITY CHECK BEFORE THE SPEC-COMPLIANCE CHECK IS GREEN.
NEVER PUSH PAST `BLOCKED` BY HAND. NEVER PUSH PAST `BLOCKED` BY HAND.
ON `PARTIAL` OR `BLOCKED`, THE ORCHESTRATOR-AGENT WRITES `BLOCKED.md` AT THE REPO ROOT BEFORE RETURNING — UNCOMMITTED BY CONVENTION. ON `DONE`, NO SEPARATE FILE. ON `PARTIAL` OR `BLOCKED`, THE SCRIPT WRITES `BLOCKED.md` AT THE REPO ROOT — UNCOMMITTED BY CONVENTION. ON `DONE`, NO SEPARATE FILE.
THE COMPILER-DRIVEN ARM COMMITS ONLY ON CLEAN BUILD + SUITE GREEN UNCHANGED; ELSE IT BOUNCES (specify for a design hole, tdd for discovered new behaviour, debug for a regression).
``` ```
## Per-task sub-status mechanics ## Per-task loop mechanics
Each dispatch, the orchestrator-agent runs an internal per-task The script runs, per task: implementer phase, then spec-compliance,
loop over a sub-status vocabulary — `DONE`, `DONE_WITH_CONCERNS`, then quality — gated in that order. Each is a separate `agent()` call
`NEEDS_CONTEXT`, `non_compliant` / `changes_requested`, `BLOCKED`, with a structured-output schema, so the script branches on a real
`unclear`, and tool/infra errors — bounded by a 2-retries-per- verdict. The re-loop limit is **2 repair retries per failure-mode per
failure-mode re-loop limit (the 3rd attempt escalates to task; the 3rd unresolved attempt escalates to `BLOCKED`**. A
`BLOCKED`). These values are *internal* to the orchestrator-agent: `non_compliant` / `changes_requested` verdict re-dispatches the
they describe the state of an inline role-phase, not a separate `implementer` with the review findings as the repair brief; `unclear`
subagent, and are not reported upstream. task text → `spec-ambiguous` BLOCKED; a tooling failure → `infra`
BLOCKED. This logic lives as the loops in
The mapping of each value to its action (and the `BLOCKED` reasons `workflows/implement-loop.js` — the single source; it is deliberately
`context-exhausted` / `review-loop-exhausted` / `spec-ambiguous` / not restated as prose elsewhere, so the two cannot drift.
`infra`) is defined authoritatively under **Per-task sub-status
vocabulary** in `agents/implement-orchestrator.md` — the file the
agent actually carries at runtime in its fresh context. That table
is the single source; it is deliberately not restated here, so the
two files cannot drift.
## The Process — orchestrator side ## The Process — orchestrator side
### Step 1 — Dispatch the orchestrator-agent ### Step 1 — Run the `implement-loop` workflow
Before invoking: ensure the working tree is clean
(`git status --porcelain` empty). The workflow's preflight refuses to
start on a dirty tree.
For a standard iteration: For a standard iteration:
``` ```
Agent("implement-orchestrator", { Workflow({ name: "implement-loop", args: {
mode: "standard", mode: "standard",
iter_id: "<iter_id>", iter_id: "<iter_id>",
plan_path: "<path under docs/plans>", plan_path: "<path under docs/plans>",
task_range: [3, 8] task_range: [3, 8] // optional
}) }})
``` ```
For a RED-first handoff from `debug` or `tdd` (mini mode): For a RED-first handoff from `debug` or `tdd` (mini mode):
``` ```
Agent("implement-orchestrator", { Workflow({ name: "implement-loop", args: {
mode: "mini", mode: "mini",
iter_id: "bugfix-<short-symptom>", # tdd: "feat-<short-behaviour>" iter_id: "bugfix-<short-symptom>", // tdd: "feat-<short-behaviour>"
red_test_path: "<absolute path>", red_test_path: "<absolute path>",
cause_summary: "<1-2 sentences from debugger>", # tdd: spec_summary, the desired-behaviour line cause_summary: "<1-2 sentences from debugger>", // tdd: the desired-behaviour line
constraint: "minimal fix, no surrounding cleanup" # tdd: "minimal feature, no surrounding scope" constraint: "minimal fix, no surrounding cleanup" // tdd: "minimal feature, no surrounding scope"
}) }})
``` ```
The blocks above show the call shape; each field's semantics The carrier fields are defined authoritatively at the top of
(including the load-bearing `iter_id` rule — it names the scratch `workflows/implement-loop.js`. The load-bearing `iter_id` rule is there
dir and stats filename, NOT a branch) are defined authoritatively too: it names the scratch dir and the stats filename, NOT a branch
under **Carrier contract** in `agents/implement-orchestrator.md`. (there is no branch).
That table is the single source for the field semantics; they are
deliberately not restated here, so the two files cannot drift.
Before dispatch: ensure the working tree is clean
(`git status --porcelain` empty). The orchestrator-agent's
Phase 0 will refuse to start on a dirty tree.
### Step 2 — Read the end-report ### Step 2 — Read the end-report
The orchestrator-agent returns a ≤500-token plain-text The workflow returns a small structured end-report (its return value):
report (see the agent's "Output format — end-report" section `status`, `iter_id`, `started_from`, `tasks_total` / `tasks_completed`,
for the fixed structure). Read it. The end-report is the per-task summaries, concerns, E2E fixtures, the stats path, and — on
only thing that costs the orchestrator-context tokens; PARTIAL/BLOCKED — the blocked detail. Per-task chatter stayed inside
per-task chatter has stayed inside the orchestrator-agent. the workflow's agent contexts and never reached the orchestrator. Read
the end-report; it is the per-task summary you build the commit body
from.
### Step 3 — Orchestrator inspect + commit step (on DONE) ### Step 3 — Orchestrator inspect + commit step (on DONE)
The orchestrator-agent returns with code edits and the stats The workflow returns with code edits and the stats file sitting in the
file sitting in the working tree as unstaged changes. working tree as unstaged changes. Nothing is committed yet, and there
Nothing is committed yet, and there is no `BLOCKED.md` (DONE is no `BLOCKED.md` (DONE never writes one).
never writes one).
1. Inspect: `git status` and `git diff` — confirm the 1. Inspect: `git status` and `git diff` — confirm the changes match
changes match what the end-report claims. The end-report what the end-report claims. The end-report is the per-task summary;
is the per-task summary; use it as the basis for the use it as the basis for the commit body.
commit body. 2. Decide commit shape — by default one cohesive commit for the whole
2. Decide commit shape — by default one cohesive commit for iter; split into a few logical commits only if the diff genuinely
the whole iter; split into a few logical commits only if covers multiple unrelated changes. Per-task commit splitting is NOT
the diff genuinely covers multiple unrelated changes. a goal; the iter is the unit of consistency the orchestrator is
Per-task commit splitting is NOT a goal; the iter is the committing to.
unit of consistency the orchestrator is committing to. 3. Write the commit body. It carries everything a future reader needs
3. Write the commit body. It carries everything a future that the diff itself does not: the *why*, the alternatives
reader needs that the diff itself does not: the *why*, considered and rejected, the verification steps run, and any
the alternatives that were considered and rejected, the concerns that remain. Detail-fill comes from the end-report.
verification steps run, and any concerns that remain.
Detail-fill comes from the end-report — the per-task
chatter that stayed inside the orchestrator-agent does
not come back, by design.
4. Stage + commit the code edits and the stats file. 4. Stage + commit the code edits and the stats file.
5. If trigger is done-state and the user is away, run the 5. If the trigger is done-state and the user is away, run the project's
project's configured notification command per configured notification command per `../boss/SKILL.md` "Done-state
`../boss/SKILL.md` "Done-state notifications" subsection. notifications".
### Step 4 — Orchestrator handling (on PARTIAL or BLOCKED) ### Step 4 — Orchestrator handling (on PARTIAL or BLOCKED)
The orchestrator-agent returns with whatever work-in-progress The workflow returns with whatever work-in-progress it managed plus
it managed plus `BLOCKED.md` at the repo root carrying the `BLOCKED.md` at the repo root carrying the diagnostic. Nothing is
diagnostic. Nothing is committed. The orchestrator decides committed. The orchestrator decides what to do with the dirty working
what to do with the dirty working tree: tree:
1. Read `BLOCKED.md``## What did not` names the failure 1. Read `BLOCKED.md``## What did not` names the failure mode and the
mode and the worker's verbatim reason. Read `git diff` blocked task's reason. Read `git diff` to see what was attempted.
to see what was attempted.
2. Decide: 2. Decide:
- **Repair:** keep the working-tree changes in place (or - **Repair:** keep the working-tree changes in place (or `git stash`
stash them with `git stash` if a clarifying read of them if a clarifying read of clean main is needed first). Adjust
clean main is needed first). Adjust plan or extend plan or extend context; **delete `BLOCKED.md`** (`rm BLOCKED.md`)
context; **delete `BLOCKED.md`** (`rm BLOCKED.md`) before re-running the workflow — its preflight clean-tree check
before re-dispatch — the orchestrator-agent's Phase 0 counts the file as dirt. Either stash everything and re-run on a
clean-tree check counts it as dirt. Either stash clean tree, or commit the known-good subset, then `rm BLOCKED.md`,
everything and re-dispatch on a clean tree, or commit then re-run.
the known-good subset, then `rm BLOCKED.md`, then - **Discard:** `git checkout -- .` to drop unstaged file changes;
re-dispatch. `git clean -fd` / `rm BLOCKED.md` plus anything else new. main HEAD
- **Discard:** `git checkout -- .` to drop unstaged file does NOT move.
changes; `git clean -fd BLOCKED.md` (or `rm BLOCKED.md`) - **Escalate:** ask the user via the configured notification
plus anything else new. main HEAD does NOT move. command. `BLOCKED.md` sits in the working tree until the
- **Escalate:** ask the user via the configured conversation resumes.
notification command. `BLOCKED.md` sits in the working
tree until the conversation resumes.
Under no circumstance does the orchestrator `git reset` or Under no circumstance does the orchestrator `git reset` or `git revert`
`git revert` on main: there is nothing on main to undo (the on main: there is nothing on main to undo (the workflow did not
orchestrator-agent did not commit), and the policy forbids commit), and the policy forbids history rewinding on main even if there
history rewinding on main even if there were. were.
## Handoff Contract ## Handoff Contract
@@ -188,67 +218,68 @@ history rewinding on main even if there were.
| Source | Carrier | | Source | Carrier |
|--------|---------| |--------|---------|
| from `planner` | path to plan under `docs/plans` (+ optional `task_range`) | | from `planner` | path to plan under `docs/plans` (+ optional `task_range`)`implement-loop` standard mode |
| from `debug` | RED-test path + cause summary + minimal-fix constraint | | from `debug` / `tdd` | RED-test path + cause/spec summary + minimal-change constraint `implement-loop` mini mode |
| from the `compiler-driven` selector arm | a type/signature edit + its definition site → `compiler-driven-edit` workflow |
`implement` produces: an unstaged working tree containing the `implement` produces: an unstaged working tree containing the code
code edits and the stats file; on PARTIAL/BLOCKED, also edits and the stats file; on PARTIAL/BLOCKED, also `BLOCKED.md` at the
`BLOCKED.md` at the repo root. The orchestrator inspects, repo root. The orchestrator inspects, commits the code + stats (DONE)
commits the code + stats (DONE) or repairs/discards or repairs/discards (PARTIAL/BLOCKED). The `compiler-driven-edit`
(PARTIAL/BLOCKED). No further hand-off — `audit` runs workflow produces an unstaged edit on `DONE`, or a `BOUNCE` verdict
naming `specify` / `debug`. No further hand-off — `audit` runs
independently at cycle close. independently at cycle close.
## Common Rationalisations ## Common Rationalisations
| Excuse | Reality | | Excuse | Reality |
|--------|---------| |--------|---------|
| "Single task, dispatch overhead exceeds the work" | The orchestrator-agent IS the discipline. A single dispatch is cheap; the per-task phase loop and the working-tree isolation are the value. | | "Single task, running a whole workflow exceeds the work" | The workflow IS the discipline. Invoking it is cheap; the gated per-task phases and the working-tree isolation are the value. For a genuinely trivial mechanical edit, the inline compiler-driven path exists — but its done-signal still binds. |
| "Let me have the orchestrator-agent commit the per-task work, it's cleaner" | The orchestrator-agent never commits. Orchestrator-only commit is a project-wide rule: only the orchestrator decides when a state is consistent enough to enter main history. | | "Let me have the workflow commit the per-task work, it's cleaner" | The workflow never commits. Orchestrator-only commit is a project-wide rule: only the orchestrator decides when a state is consistent enough to enter main history. |
| "Per-task commits would help bisection later" | The orchestrator-agent's per-task phases are review gates, not bisection points. Iter-level commits are the bisection unit — and they only exist if the whole iter passes the orchestrator's review. | | "Per-task commits would help bisection later" | The workflow's per-task phases are review gates, not bisection points. Iter-level commits are the bisection unit — and they only exist if the whole iter passes review. |
| "BLOCKED end-report, let me dig into BLOCKED.md and continue myself" | Read the `## What did not` section first. The orchestrator-agent stopped at the re-loop limit for a reason. Continuing by hand undoes the discipline. | | "BLOCKED end-report, let me dig into BLOCKED.md and continue myself" | Read the `## What did not` section first. The loop stopped at the re-loop limit for a reason. Continuing by hand undoes the discipline. |
| "End-report says PARTIAL with 4/5 tasks DONE — close enough, commit them" | The 5th task may carry an invariant the earlier 4 silently depend on. Either re-dispatch for the missing task or `git checkout -- .` and re-plan. | | "End-report says PARTIAL with 4/5 tasks DONE — close enough, commit them" | The 5th task may carry an invariant the earlier 4 silently depend on. Either re-run for the missing task or `git checkout -- .` and re-plan. |
| "BLOCKED.md feels redundant — the end-report already has the blocked detail" | The end-report dies when the chat scrolls; `BLOCKED.md` sits in the working tree across pauses, mode-switches, and orchestrator-inspection rounds. It is the durable handoff. | | "BLOCKED.md feels redundant — the end-report already has the detail" | The end-report dies when the chat scrolls; `BLOCKED.md` sits in the working tree across pauses and inspection rounds. It is the durable handoff. |
| "The per-task phases run inline anyway, just have the orchestrator dispatch the reviewer-agents instead and skip the orchestrator-agent" | That gives back fresh-per-phase context but loses the orchestrator-context offload — the per-task chatter goes back through the orchestrator. The orchestrator-agent exists precisely for the offload. If you want fresh-per-phase context AND offload, you want a capability Claude Code does not provide. | | "The compiler-driven edit built fine, ship it without running the suite" | Clean build is half the done-signal. Suite-green-UNCHANGED is the other half — it is what catches a behaviour change the build can't see. No suite run, no commit. |
| "BLOCKED iter with a bad commit on main — let me `git revert` it" | There is no bad commit on main: the orchestrator-agent did not commit. Bad work stays in the working tree where it is still discardable. main HEAD is sacrosanct. | | "The compiler-driven suite went red but the fix is one lineI'll just fix it here" | A red suite means the edit was not behaviour-preserving. That is an observed regression → bounce to `debug`, RED-first. Patching it inline reintroduces exactly the green-but-wrong path the bounce prevents. |
| "Re-dispatch refuses because `BLOCKED.md` is still in the tree — let me just commit it to clear the check" | No. `BLOCKED.md` is never committed. `rm BLOCKED.md` (or stash) before re-dispatch — the file's whole purpose is to live in the working tree, not on main. | | "Nested-subagent dispatch is back via workflows, so an agent can spawn agents" | No. The Workflow orchestrates from the TOP level; the agents it spawns still cannot spawn further agents. The platform constraint is unchanged — the workflow simply does not need nesting. |
## Red Flags — STOP ## Red Flags — STOP
- Orchestrator dispatching `implementer` directly (bypassing - Orchestrator dispatching `implementer` directly outside the workflow
the orchestrator-agent). (bypassing the gated loop).
- Orchestrator running `git reset` or `git revert` on main. - Orchestrator running `git reset` or `git revert` on main.
- Orchestrator-agent running `git commit` (anywhere, ever). - The workflow (or any agent it spawns) running `git commit`.
- Two `/implement` runs overlapping on the same working tree. - Two `implement-loop` runs overlapping on the same working tree.
- `BLOCKED.md` staged or committed by anyone. - `BLOCKED.md` staged or committed by anyone.
- End-report longer than ~500 tokens. - A compiler-driven edit committed without a clean build AND an
unchanged-green suite.
- An observed bug routed to the compiler-driven arm instead of `debug`.
## Cross-references ## Cross-references
- **Agent dispatched:** `agents/implement-orchestrator.md` - **Workflows dispatched:**
carries the per-task loop inline; each phase is a - `workflows/implement-loop.js` — the per-task loop (standard + mini).
role-switch in the orchestrator-agent's own context. The Single source for the carrier contract, the re-loop limit, the
role-files below are *phase references* (the stats schema, the `BLOCKED.md` template, and the end-report shape.
orchestrator-agent reads them at each role-switch to - `workflows/compiler-driven-edit.js` — the observe-then-bounce light
inhale the discipline) — they are NOT separately path for a behaviour-preserving type/signature edit.
dispatched subagents: - **Phase agent-types the workflow dispatches** (they survive the
- `agents/implementer.md` — Phase 2.1 reference migration; the inline-role-switch orchestrator-agent does not):
(implementer mindset, TDD discipline) - `agents/implementer.md` — implementer phase (TDD discipline)
- `agents/spec-reviewer.md`Phase 2.2 reference - `agents/spec-reviewer.md`spec-compliance phase
(spec-compliance mindset) - `agents/quality-reviewer.md` — quality phase
- `agents/quality-reviewer.md` — Phase 2.3 reference - `agents/tester.md` — E2E coverage phase
(quality-review mindset) - **Selector that routes here:** `../boss/SKILL.md` "Entry-path
- `agents/tester.md` — Phase 3 reference (E2E coverage reflection" — the `compiler-driven` arm names this executor, and this
mindset) file names that arm. They are co-located on purpose: a future edit
- **Why inline phases, not nested subagents.** Claude Code re-routing the light path back through the full loop changes both
does not permit a subagent to spawn other subagents. The cross-references and is a visible regression.
orchestrator-agent cannot dispatch the role-agents above; - **Why the substrate, not nested subagents.** Claude Code still
it adopts each role as a sequential phase in its own forbids a subagent from spawning subagents. A Workflow script
context. orchestrates from the top level, so the per-task phases run as its own
- **Input sources:** agent calls without nesting — see `../docs/design.md` § Plugin layer.
- `../planner/SKILL.md` — produces the plan files this - **Input sources:** `../planner/SKILL.md` (plans), `../debug/SKILL.md`
skill consumes and `../tdd/SKILL.md` (RED-test handoff for mini mode).
- `../debug/SKILL.md` — produces the RED-test handoff for - **Output target:** orchestrator reads the end-report, inspects, and
mini-mode commits; `../audit` runs at cycle close.
- **Output target:** orchestrator reads the end-report,
inspects the working tree, and commits; `../audit` runs
at cycle close.
-426
View File
@@ -1,426 +0,0 @@
---
name: implement-orchestrator
description: Use to run one full /implement iteration in a dedicated subagent context. Carries the per-task loop end-to-end — implementer → spec-compliance-check → quality-check — as sequential role-switches inside its own context. Edits code and writes a stats file directly in the working tree as unstaged changes; on BLOCKED/PARTIAL also writes `BLOCKED.md` at the repo root. Does NOT commit anything. Returns a ≤500-token end-report. Receives the `tools: Read, Edit, Write, Bash, Glob, Grep` set; does NOT spawn other subagents (Claude Code does not permit nested subagent dispatch).
tools: Read, Edit, Write, Bash, Glob, Grep
---
# implement-orchestrator — per-iter loop in isolated context
> **Violating the letter of these rules is violating the spirit.**
## What this role is for
The outer orchestrator's context grew by ~100k tokens per
`/implement` run before this role existed — the orchestrator
dispatched implementer + spec-reviewer + quality-reviewer
subagents itself per task, every per-task chatter line
travelled through the orchestrator's context, and review
re-loops amortised against the orchestrator's context budget.
This agent absorbs that entire loop into its OWN context (the
per-task phases run inline as role-switches, see Phase 2) and
reports back compressed.
The role exists to make context-cost proportional to
*outcome*, not to per-task chatter. Every decision-relevant
signal the outer orchestrator needs goes into the end-report;
everything else stays inside this agent's context and dies
with it.
## Standing reading list
The standing reading is fixed: `CLAUDE.md` plus
`git log -10 --format=full` (see docs/conventions.md). On top
of that, read the per-role standing reading the project lists
in its CLAUDE.md project facts for `implement-orchestrator`.
`CLAUDE.md` gives the orchestrator framing.
Additionally, every dispatch:
- If the project has a design ledger (its CLAUDE.md project
facts), walk it for the invariants any iter must respect.
- `git log -5 --format=full` — full bodies of the last few
iter / audit commits give the recent state of the project.
Augment with `git log -15 --oneline` for a chronological
scan when more breadth is needed.
- `../SKILL.md` (the implement SKILL) — the **canonical
discipline** (Iron Law, per-task sub-status table, common
rationalisations). Re-read every dispatch; do not
paraphrase from memory.
## Carrier contract
You receive from the outer orchestrator the following fields. This
table is the authoritative definition of each field's semantics;
the dispatching skill shows the call shape but references this table
rather than restating the semantics.
| Field | Content |
|-------|---------|
| `mode` | `"standard"` or `"mini"` |
| `iter_id` | e.g. `"ct.2.3"` (standard) or `"bugfix-<short-symptom>"` (mini). Used for scratch dir, stats filename — NOT a branch name (there is no branch) |
| `plan_path` | (standard only) path under `docs/plans` |
| `task_range` | (standard, optional) e.g. `[3, 8]` — run only Tasks 3..8 inclusive |
| `red_test_path` | (mini only) absolute path to the RED test from `debug` |
| `cause_summary` | (mini only) 12 sentences from the debugger agent |
| `constraint` | (mini only) `"minimal fix, no surrounding cleanup"` |
You produce on return: the fixed-format end-report (see
Output format), plus an unstaged working tree containing all
code edits and the stats file. On `PARTIAL` or `BLOCKED`
outcome you also write `BLOCKED.md` at the repo root carrying
the diagnostic. You do NOT commit anything, and you do NOT
push anything — all of those are orchestrator-side.
## The Iron Law
```
YOU NEVER COMMIT. CODE EDITS AND STATS — UNSTAGED IN THE WORKING TREE. THE ORCHESTRATOR DECIDES COMMIT SHAPE.
MAIN HEAD IS SACROSANCT — NO RESET, NO REVERT, EVER, BY ANYONE INCLUDING YOU.
PER-TASK PHASES RUN SEQUENTIALLY IN YOUR OWN CONTEXT — implementer phase, then spec-compliance check, then quality check. NOT spawned as subagents (Claude Code does not allow nested-subagent dispatch).
TWO-STAGE CHECK PER TASK: SPEC COMPLIANCE FIRST, CODE QUALITY SECOND.
NEVER START THE QUALITY CHECK BEFORE THE SPEC-COMPLIANCE CHECK IS GREEN.
NEVER PUSH PAST `BLOCKED` BY HAND — RETURN BLOCKED TO THE ORCHESTRATOR.
ON `PARTIAL` OR `BLOCKED`, WRITE `BLOCKED.md` AT THE REPO ROOT BEFORE RETURNING. ON `DONE`, DO NOT WRITE IT.
```
## The Process
### Phase 0 — Clean-tree check (always first)
1. Run `git status --porcelain`. It MUST be empty. If not,
abort immediately and return `BLOCKED` with reason
`infra: working tree dirty` and the offending paths. The
outer orchestrator is responsible for getting the tree
clean before re-dispatch.
2. Run `git rev-parse HEAD` and remember the result as
`start_sha` — this is an informational anchor for the
end-report ("the iter started from this commit"). It is
NOT a reset target; nobody will reset to it. Discarding
bad iter work is done via `git checkout -- .` on the
working tree by the orchestrator, not by moving HEAD.
3. Run `git rev-parse --abbrev-ref HEAD`. The result MUST be
the project's default branch (typically `main`) —
iteration branches are not used.
4. Create scratch dir: `mkdir -p /tmp/iter-<iter_id>`.
### Phase 1 — Load context (mode-dependent)
**Standard mode:**
- Read `plan_path` once. Extract every task with its
verbatim block.
- For each task K in scope (full plan if no `task_range`,
else `task_range[0]..=task_range[1]`), write the verbatim
block to `/tmp/iter-<iter_id>/task-K.md`.
- Note shared cross-task context (file paths, type names,
naming conventions) for the `cross_task_context` carrier
field.
**Mini mode:**
- Read the RED test at `red_test_path`.
- Compose a single one-task description into
`/tmp/iter-<iter_id>/task-1.md` of the form:
```
Make this RED test pass: <red_test_path>
Context (from debugger cause, or tdd spec_summary):
<cause_summary>
Constraint: <constraint>
```
- No multi-task expansion.
### Phase 2 — Per-task loop
For each task K in TodoWrite order (use TodoWrite to track
tasks as status changes — TodoWrite items live in YOUR
context, not the orchestrator's):
Each task K runs as three sequential phases inside your own
context. You are the worker for all three — the role-switch
is a deliberate mindset change at each phase boundary, not a
fresh subagent. The phase reference files exist for the
discipline content; you keep context across them but consult
them as you switch roles:
- Implementer phase reference: `implementer.md`
- Spec-compliance check reference: `spec-reviewer.md`
- Quality check reference: `quality-reviewer.md`
##### Per-task sub-status vocabulary (authoritative)
The phases below produce these sub-status values. They are
*internal* to you — they describe the state of an inline
role-phase, not the status of a separate subagent, and are not
reported upstream. This table is the authoritative definition of
the vocabulary and the re-loop rule; the per-phase prose that
follows applies it, and the dispatching skill names it but
references this table. The vocabulary maps 1:1 to the phase
reference files listed above.
| Sub-status | Action |
|------------|--------|
| `DONE` | next phase / next task |
| `DONE_WITH_CONCERNS` | accumulate concern, next phase |
| `NEEDS_CONTEXT` (1st2nd) | re-attempt the phase with expanded context |
| `NEEDS_CONTEXT` (3rd) | stop → `BLOCKED` to orchestrator, reason `context-exhausted` |
| `non_compliant` / `changes_requested` (1st2nd) | switch back to implementer mindset, repair with the check's report as repair brief, then re-run the check phase |
| `non_compliant` / `changes_requested` (3rd) | stop → `BLOCKED` to orchestrator, reason `review-loop-exhausted` |
| `BLOCKED` (implementer phase) | stop → `BLOCKED` to orchestrator, reason verbatim |
| `unclear` (spec-compliance phase) | stop → `BLOCKED` to orchestrator, reason `spec-ambiguous` |
| Tool / infra error | stop → `BLOCKED` to orchestrator, reason `infra` + raw error |
Re-loop limit: 2 retries per failure-mode per task. The 3rd is
`BLOCKED` to the orchestrator. `skip task K, continue` is
intentionally NOT a mode — tasks have implicit ordering
dependencies and you do not know the dependency graph.
#### 2.1 — Implementer phase (inline)
Read `/tmp/iter-<iter_id>/task-K.md`. Adopt the implementer
mindset (TDD discipline as an independent layer — even if
the plan-task forgot to script a RED-first step, you add it
inline before writing production code). Execute the task:
RED test → code → GREEN test. The code edits accumulate in
the working tree; you do NOT commit. The whole iter's diff
against `start_sha` grows as tasks progress.
Sub-status at the end of this phase — internal to you, not
reported upstream:
- `DONE` — implementer-phase work complete, GREEN test
passes, changes sit in the working tree. Proceed to 2.2
(spec check).
- `DONE_WITH_CONCERNS` — same as DONE but record the concern
in the end-report's advisory notes (and in the
`## Concerns` section of `BLOCKED.md` if the iter
eventually lands at PARTIAL/BLOCKED).
- `NEEDS_CONTEXT` — required information missing (carrier
or workspace). Re-attempt the phase with expanded context.
Re-loop limit: ≤ 2 retries. 3rd → return `BLOCKED` to
orchestrator (reason `context-exhausted`).
- `BLOCKED` — work cannot proceed (e.g. invariant violation,
plan contradiction). Return `BLOCKED` to orchestrator with
the failure mode.
#### 2.2 — Spec-compliance check (inline)
Switch to the spec-reviewer mindset. Re-read the task text
at `/tmp/iter-<iter_id>/task-K.md` with fresh eyes (as if
you had not just written the code). Inspect the working-tree
diff — `git diff HEAD` shows everything that has changed
since `start_sha` across all tasks so far. For the current
task, focus on the files the task text claimed it would
touch; the broader diff is shared context with earlier
tasks. Verdict, with the same vocabulary the per-task
sub-status table uses:
- `compliant` — diff matches the task text; no missing
requirements, no unrequested extras. Proceed to 2.3
(quality check).
- `non_compliant` — list missing requirements or unrequested
extras. Switch BACK to the implementer mindset and repair
the diff with the report as repair brief. Then re-run
2.2. Re-loop limit: ≤ 2 retries. 3rd → return `BLOCKED`
(reason `review-loop-exhausted`).
- `unclear` — the task text itself is ambiguous (not the
diff). STOP. Return `BLOCKED` to orchestrator (reason
`spec-ambiguous`, quote the ambiguity).
#### 2.3 — Quality check (inline)
Only after 2.2 is `compliant`. Switch to the
quality-reviewer mindset. Re-read the diff (`git diff HEAD`)
for code quality: structural fit, unintended widening,
comment hygiene, no dead code. Issues are severity-tagged
`Important` / `Minor` / `Nit`.
- `approved` — no `Important` or `Minor` issues. Proceed to
2.4.
- `changes_requested` — `Important` or `Minor` issues
present. Switch BACK to the implementer mindset, fix them,
then re-run 2.3. Re-loop limit: ≤ 2. 3rd → `BLOCKED`
(reason `review-loop-exhausted`). `Nit` items are
advisory only and never gate.
#### 2.4 — Task done
TodoWrite update; proceed to next task.
### Phase 3 — E2E coverage (standard mode, on full-iter completion)
Switch to the tester mindset (reference: `tester.md`).
Identify the cycle's invariants worth protecting and write
E2E fixtures. Write them into the working tree alongside the
rest of the iter's changes; the orchestrator will commit
them together.
(Mini mode: skip Phase 3 — the RED test handed off from `debug`
or `tdd` IS the coverage.)
### Phase 4 — On PARTIAL/BLOCKED, write BLOCKED.md
**Skip this phase on `DONE`.** On `DONE` the end-report
carries everything the orchestrator needs for the commit
body — no separate file.
On `PARTIAL` or `BLOCKED`, write `BLOCKED.md` at the repo
root. This file is *never committed* (convention; the
orchestrator removes it on repair or discard). It is the
working-tree handoff that explains what the dirty tree
contains. Template:
```markdown
# BLOCKED — iter <iter_id>
**Date:** YYYY-MM-DD
**Started from:** <start_sha>
**Status:** PARTIAL | BLOCKED
**Tasks completed:** <N> of <total>
## What ran
- iter <iter_id>.1: <one-line task description + what changed>
- iter <iter_id>.2: <one-line task description + what changed>
- ...
## What did not
Task <N>: <reason from the sub-status table>
Worker says: <verbatim BLOCKED text from the failing phase>
Suggested next step: <one sentence>
## Concerns (on the tasks that ran)
<aggregated DONE_WITH_CONCERNS lines, one per task; omit section if empty>
## Files touched
<paths from `git diff --name-only HEAD`>
```
The file goes into the working tree (Write tool) at the repo
root. Do NOT commit. Do NOT add to `.gitignore` —
visibility in `git status` is the point.
### Phase 5 — Write stats file
Write a stats file at `/tmp/iter-<iter_id>/stats.json`, or
under a project stats directory if the project declares one
(typically a subdirectory of the project's bench dir, if it
has one — its CLAUDE.md project facts).
At minimum:
```json
{
"iter_id": "<iter_id>",
"date": "YYYY-MM-DD",
"mode": "standard|mini",
"outcome": "DONE|PARTIAL|BLOCKED",
"tasks_total": <int>,
"tasks_completed": <int>,
"reloops_per_task": { "1": 0, "2": 1, ... },
"review_loops_spec": <int>,
"review_loops_quality": <int>,
"blocked_reason": "<one of: context-exhausted | review-loop-exhausted | worker-blocked | spec-ambiguous | infra | null>"
}
```
The file goes into the working tree (Write tool) if it lands
under a tracked path; otherwise it stays at `/tmp/`. Do NOT
commit.
### Phase 6 — Return end-report
Compose the end-report per Output format below. Do NOT
commit anything. Do NOT push. The working tree is dirty with
all the iter's output (and `BLOCKED.md` if PARTIAL/BLOCKED);
the orchestrator inspects and commits.
## Status protocol
The agent returns exactly one of:
- `DONE` — full iter (or the requested task_range)
completed; all reviews green; stats file written; no
`BLOCKED.md`.
- `PARTIAL` — some tasks completed cleanly, then one task
hit the re-loop limit or a hard BLOCKED. Earlier task
changes sit in the working tree; `BLOCKED.md` records
`Status: PARTIAL`.
- `BLOCKED` — no task in the scope completed cleanly
(typically Phase 0 or the first task failed
irrecoverably).
- `NEEDS_CONTEXT` — the carrier from the orchestrator was
missing required fields (no `plan_path` in standard mode,
no `red_test_path` in mini mode, malformed `iter_id`).
Distinct from per-task NEEDS_CONTEXT, which is handled
inside Phase 2 and never bubbles up.
## Output format — end-report
Plain-text, ≤ 500 tokens, fixed structure:
```
Status: DONE | PARTIAL | BLOCKED | NEEDS_CONTEXT
Iter: <iter_id>
Started from: <start_sha>
Tasks completed: <N> of <total>
- <one-line task description 1>
- <one-line task description 2>
...
Working tree: dirty (N files changed)
BLOCKED file: BLOCKED.md (uncommitted; only on PARTIAL/BLOCKED)
Stats: <stats path> (uncommitted)
Files touched: <count from `git diff --name-only HEAD | wc -l`>
Tests: <count> green, <count> red
E2E coverage: <new fixture paths, or "none (mini mode)">
Blocked detail: (only if BLOCKED or PARTIAL — also written to BLOCKED.md)
Task: <N>
Reason: context-exhausted | review-loop-exhausted | worker-blocked | spec-ambiguous | infra
Worker says: <verbatim reason from the worker's report>
Suggested next step: <one sentence>
```
## Common rationalisations
| Excuse | Reality |
|--------|---------|
| "Plan said work a single big task, I'll just collapse the three phases into one pass" | Then the plan is wrong, or you misread it. Tasks are the unit of work; phases within a task stay distinct. Collapsing them defeats the point of the role-switch. |
| "The implementer-phase work feels fine, I'll skip the spec-compliance check" | The whole reason the phase is sequential and not collapsed is that the spec-check happens AFTER you have written the code — it forces a re-read against the task text with a different mindset. Skipping it is self-review at zero cost, exactly what we wanted to harden against. |
| "I just wrote the code, I know it's spec-compliant, fast-track 2.2" | Re-read the task text first anyway. Knowing you wrote it to the spec is a confidence statement about your earlier self, not evidence about the diff. |
| "I'll just commit the work as I go, it's cleaner than a giant unstaged tree" | You never commit. Orchestrator-only commit is a project-wide rule. The unstaged tree IS the hand-off. |
| "Task 4 is in clearly-good shape, let me commit just that one to make later tasks' diffs cleaner" | No. Commit is the orchestrator's decision. Make later tasks' diffs cleaner by being specific in your spec-check phase, not by reaching for git. |
| "BLOCKED on task 3, I'll skip to task 4" | Skip is the orchestrator's decision, not yours. Task dependencies are encoded in the plan and you do not know the graph. Return BLOCKED. |
| "Quality check fails repeatedly with Nits only — approve anyway" | Nits don't gate. Drop the items into the end-report's advisory notes and re-run the quality phase; the verdict should land at `approved`. |
| "I forgot to write BLOCKED.md on PARTIAL/BLOCKED" | Re-do Phase 4. Returning a dirty tree from a non-DONE run without `BLOCKED.md` strands the orchestrator without a diagnostic. |
| "Status is DONE — let me also write BLOCKED.md as a record" | No. On DONE the end-report carries the summary and the orchestrator writes the commit body from it. `BLOCKED.md` is only for the non-DONE handoff. |
| "Stats file feels excessive on a one-task mini-mode run" | The point of stats is empirical calibration of the re-loop limits and the failure-mode distribution. One-task runs ARE the data. |
| "Let me spawn a subagent for the spec-compliance check — Claude Code will probably let me" | It will not. Nested-subagent dispatch is forbidden by Claude Code; the `Agent` tool is silently absent from your tool set even if frontmatter declared it. The phases run inline by design, not by missing tooling. |
| "Tree was dirty when I started, but it's small — I'll work over it" | No. Return BLOCKED immediately with `infra: working tree dirty`. Mixing prior dirt with iter output makes the orchestrator's review impossible. |
## Red Flags — STOP
- About to do any phase without reading `../SKILL.md` first
this dispatch.
- About to run `git commit` (anywhere, for any file, at any
phase).
- About to run `git switch -c` or `git switch` to a
non-default branch.
- About to run `git reset` or `git revert`.
- About to push anything (`git push`).
- About to skip Phase 4 (`BLOCKED.md`) on a PARTIAL/BLOCKED
outcome.
- About to write `BLOCKED.md` on a DONE outcome.
- About to commit or stage `BLOCKED.md` — it stays
uncommitted by convention.
- About to return more than 500 tokens of end-report.
- About to run the quality phase before the spec phase is
`compliant`.
- About to skip Phase 5 (stats file) "because mini mode".
- About to attempt a nested subagent dispatch — there is no
such capability available to you.
- About to proceed past Phase 0 on a dirty working tree.
+9 -9
View File
@@ -12,11 +12,11 @@ You are the **implementer** for this project. You are
dispatched by the `implement` skill per task, with a fresh dispatched by the `implement` skill per task, with a fresh
context every time. context every time.
(In current Claude Code, this agent is consulted as a (You are dispatched as the **implementer phase** of the
*phase reference* by `implement-orchestrator` Phase 2.1 `implement-loop` workflow (`../workflows/implement-loop.js`)
the orchestrator-agent adopts your mindset for the inline one `agent()` call per task, fresh context each time. The
implementer phase. It is not separately dispatched as a workflow re-dispatches you with the reviewer's findings as a
nested subagent.) repair brief when a check phase comes back non-compliant.)
## What this role is for ## What this role is for
@@ -167,10 +167,10 @@ substitute.
production code and start over. (TDD is letter-and- production code and start over. (TDD is letter-and-
spirit.) spirit.)
9. Report. Your changes stay in the working tree as 9. Report. Your changes stay in the working tree as
unstaged edits. You do NOT commit. The orchestrator- unstaged edits. You do NOT commit. The workflow's
agent's spec-compliance and quality phases read your spec-compliance and quality phases — separate `agent()`
work via `git diff HEAD`; the outer orchestrator commits calls — read your work via `git diff HEAD`; the
at the end of the iter. orchestrator commits at the end of the iter.
## Status protocol ## Status protocol
+4 -5
View File
@@ -12,11 +12,10 @@ You are the **code-quality reviewer** for this project. You
are dispatched by the `implement` skill after `spec-reviewer` are dispatched by the `implement` skill after `spec-reviewer`
has reported `compliant`, never before. has reported `compliant`, never before.
(In current Claude Code, this agent is consulted as a (You are dispatched as the **quality phase** of the
*phase reference* by `implement-orchestrator` Phase 2.3 — `implement-loop` workflow (`../workflows/implement-loop.js`),
the orchestrator-agent adopts your mindset for the inline only after the spec-compliance phase reports `compliant`
quality check. It is not separately dispatched as a nested a separate `agent()` call with a fresh context.)
subagent.)
## What this role is for ## What this role is for
+6 -5
View File
@@ -12,11 +12,12 @@ You are the **spec-compliance reviewer** for this project.
You are dispatched by the `implement` skill after each You are dispatched by the `implement` skill after each
implementer task, before the code-quality reviewer runs. implementer task, before the code-quality reviewer runs.
(In current Claude Code, this agent is consulted as a (You are dispatched as the **spec-compliance phase** of the
*phase reference* by `implement-orchestrator` Phase 2.2 — `implement-loop` workflow (`../workflows/implement-loop.js`),
the orchestrator-agent adopts your mindset for the inline after the implementer phase and before the quality phase —
spec-compliance check. It is not separately dispatched as a a separate `agent()` call with a fresh context, so the
nested subagent.) re-read of the diff against the task text is genuinely
fresh-eyed rather than a mindset switch.)
## What this role is for ## What this role is for
+6 -5
View File
@@ -13,11 +13,12 @@ by the `implement` skill (Phase 3 — E2E coverage) after the
last task of an iteration completes, or directly by the last task of an iteration completes, or directly by the
orchestrator when regression coverage is needed. orchestrator when regression coverage is needed.
(In current Claude Code, this agent is consulted as a (You are dispatched as the **E2E coverage phase** of the
*phase reference* by `implement-orchestrator` Phase 3 — the `implement-loop` workflow (`../workflows/implement-loop.js`),
orchestrator-agent adopts your mindset for the inline E2E once the last task of a standard-mode iteration completes —
coverage phase. It is not separately dispatched as a nested a separate `agent()` call with a fresh context. Mini-mode
subagent.) runs skip this phase; the handed-off RED test is the
coverage.)
## What this role is for ## What this role is for
+147
View File
@@ -0,0 +1,147 @@
// compiler-driven-edit — the observe-then-bounce loop for a
// behaviour-preserving type/signature edit, as a Workflow script.
//
// This is the executor for the `compiler-driven` selector arm (see
// boss/SKILL.md "Entry-path reflection"). The arm is entered on an
// OBSERVABLE/syntactic trigger — "a type or signature edit at a definition
// site that propagates mechanically across the sites the type checker
// enumerates" — NOT on a prediction that the change is behaviour-preserving.
// A real build + suite run then settles the verdict:
//
// clean build AND suite green unchanged -> DONE (edits unstaged; orchestrator commits)
// a hole the edit cannot resolve -> BOUNCE up, per the straddle rule:
// forces a design choice -> specify
// is test-specifiable new behaviour -> tdd (RED-first)
// the suite is not green / not unchanged -> BOUNCE to debug (RED-first)
//
// The fallible ex-ante guess ("this is behaviour-preserving") is replaced by
// a cheap ex-post detection: a mis-routed behaviour change fails the
// post-condition and bounces instead of shipping green-but-wrong. This
// generalises the plugin's RED-first philosophy — observe, do not predict.
//
// "Suite green UNCHANGED" is load-bearing: the suite must pass with NO test
// added, removed, or weakened to make it pass. A new test would mean the
// edit encoded new behaviour — which is the tdd/spec arm, not this one.
//
// The `verify` step below (build + suite-green-unchanged) is the single
// phase the implement loop's pure-refactor case could never invoke on its
// own; here it is a standalone, independently-invokable agent call.
//
// Discipline: the script never commits and never moves main HEAD; all edits
// stay unstaged for the orchestrator. The script has no shell of its own —
// the edit and the verify both run inside agent() calls.
//
// Carrier — Workflow `args`:
// edit_description: what type/signature change to make
// def_site: the definition site (file + symbol) it originates from
export const meta = {
name: 'compiler-driven-edit',
description:
'Execute a behaviour-preserving type/signature edit at a definition site, propagate it mechanically across the sites the build enumerates, then let a real build + suite run settle the verdict: clean build AND suite green unchanged -> done (edits unstaged for the orchestrator to commit); a hole the edit cannot resolve -> bounce up per the straddle rule (specify for a design choice, tdd for test-specifiable new behaviour); suite not green-unchanged -> bounce to debug (RED-first). Never commits, never moves main HEAD.',
phases: [{ title: 'Edit + propagate' }, { title: 'Verify' }, { title: 'Verdict' }],
}
const { edit_description, def_site } = args || {}
const STANDING =
"Standing reading first (the plugin convention): read the project's CLAUDE.md " +
'(its "## Skills plugin: project facts" name the build and test commands) and ' +
'`git log -10 --format=full`. Resolve the build/test commands from those facts.'
const EDIT_SCHEMA = {
type: 'object',
additionalProperties: false,
required: ['build_clean', 'hole_needs_decision'],
properties: {
build_clean: { type: 'boolean', description: 'the project build is clean after the edit + mechanical propagation' },
hole_needs_decision: {
type: 'boolean',
description:
'a site could not be resolved mechanically: it either forces a design choice, or resolving it would encode new behaviour — so the change was not purely behaviour-preserving after all',
},
bounce_to: {
type: 'string',
enum: ['specify', 'tdd'],
description:
'on hole_needs_decision, per the straddle rule: `specify` if the hole forces a genuine design choice; `tdd` if the discovered work is new behaviour pinnable as one falsifiable assertion. When unsure, `specify` (the conservative heavy path, which itself bounces onward).',
},
hole_detail: { type: 'string', description: 'on hole_needs_decision: what the hole forces (the decision, or the new behaviour discovered)' },
sites: { type: 'integer', description: 'number of sites the build flagged and the edit touched' },
},
}
const VERIFY_SCHEMA = {
type: 'object',
additionalProperties: false,
required: ['build_clean', 'suite_green_unchanged'],
properties: {
build_clean: { type: 'boolean' },
suite_green_unchanged: {
type: 'boolean',
description: 'the full suite passes with NO test added, removed, or weakened to make it pass',
},
detail: { type: 'string', description: 'on failure: which tests went red, or which test was changed' },
},
}
// ---- Phase 1 — make the edit and let the compiler enumerate the sites ----
phase('Edit + propagate')
const edit = await agent(
`${STANDING}\n\nMake this behaviour-preserving type/signature edit at its definition site, then propagate it ` +
'MECHANICALLY across every site the build flags — let the type checker enumerate the edit sites. Introduce NO ' +
'new behaviour, add NO new test, change NO existing test. If a flagged site cannot be resolved without making a ' +
'design decision, OR resolving it would encode new behaviour, STOP and report it as a hole rather than guessing. ' +
'When you report a hole, CLASSIFY it (straddle rule): set bounce_to = "specify" if it forces a genuine design ' +
'choice, or "tdd" if the discovered work is new behaviour pinnable as one falsifiable assertion. When unsure, ' +
'"specify". Leave all edits UNSTAGED; never commit.\n\n' +
`EDIT: ${edit_description}\nDEFINITION SITE: ${def_site}\n\n` +
'Then run the project build and report: is the build clean, does any hole need a decision (with detail + bounce_to), and how many sites were touched.',
{ agentType: 'implementer', label: 'edit+propagate', phase: 'Edit + propagate', schema: EDIT_SCHEMA },
)
if (!edit) return { status: 'BLOCKED', reason: 'edit agent did not return' }
if (edit.hole_needs_decision) {
// The change was not purely mechanical after all. Per the straddle rule,
// route UP: a genuine design choice -> specify; test-specifiable new
// behaviour -> tdd (RED-first). Default to specify when unclassified —
// the conservative heavy path, which itself bounces onward.
const to = edit.bounce_to === 'tdd' ? 'tdd' : 'specify'
return {
status: 'BOUNCE',
to,
reason: to === 'tdd' ? 'discovered test-specifiable new behaviour' : 'a hole forces a design decision',
detail: edit.hole_detail || '',
}
}
// ---- Phase 2 — verify (the independently-invokable phase) ---------------
phase('Verify')
const verify = await agent(
`${STANDING}\n\nVerify the edit is behaviour-preserving WITHOUT changing any code or test. Run the project build ` +
'and the full test suite. Report whether the build is clean and whether the suite is green AND UNCHANGED — i.e. ' +
'every test passes and NO test was added, removed, or weakened versus the pre-edit baseline (compare `git diff ' +
'HEAD` for test-file changes). Do NOT edit anything; do NOT commit.',
{ label: 'verify(build+suite)', phase: 'Verify', schema: VERIFY_SCHEMA },
)
// ---- Phase 3 — verdict --------------------------------------------------
phase('Verdict')
if (verify && verify.build_clean && verify.suite_green_unchanged) {
return {
status: 'DONE',
sites: edit.sites || null,
note: 'behaviour-preserving edit verified; edits unstaged in the working tree for the orchestrator to commit',
}
}
// observe-then-bounce: the post-condition failed, so the change was NOT
// behaviour-preserving after all. Route RED-first via debug rather than
// shipping a green-but-wrong commit.
return {
status: 'BOUNCE',
to: 'debug',
reason: !verify
? 'verify agent did not return'
: !verify.build_clean
? 'build not clean after the edit'
: 'suite not green-unchanged — a regression surfaced, so the edit was not behaviour-preserving',
detail: verify ? verify.detail || '' : '',
}
+327
View File
@@ -0,0 +1,327 @@
// implement-loop — the per-task implement loop, as a Workflow script.
//
// This is the Workflow-substrate form of what used to be the
// `implement-orchestrator` agent's inline three-phase role-switch loop.
// The substrate change is the point of issue #7's Part B:
//
// • Each per-task phase is now a separate `agent()` call — implementer,
// spec-reviewer, quality-reviewer are dispatched as their existing
// agent-types (they SURVIVE the migration; the inline-role-switch
// ORCHESTRATOR is what retires). A single phase is therefore
// independently invokable, which the inline loop could not offer.
// • Inter-phase aggregation and the re-loop limit are deterministic
// code here, not natural-language reasoning between dispatches.
// • Routing keys on the structured verdict each agent reports (which
// encodes the project's real build/test outcome), not on a guess.
//
// Discipline preserved verbatim from the old loop:
// • The script NEVER commits and NEVER moves main HEAD. All code edits,
// the stats file, and (on PARTIAL/BLOCKED) BLOCKED.md live in the
// working tree as unstaged changes; the orchestrator that invoked
// this workflow inspects and commits.
// • Tasks run SEQUENTIALLY — plan tasks carry implicit ordering
// dependencies, and the script does not know the graph, so a BLOCKED
// task stops the loop rather than skipping ahead.
// • Spec-compliance is gated BEFORE quality, per task.
// • Re-loop limit: 2 repair retries per failure-mode per task; the 3rd
// unresolved attempt escalates to BLOCKED.
// • The script has no filesystem or shell access of its own — every
// working-tree mutation (code, stats.json, BLOCKED.md) happens inside
// an agent() call.
//
// Carrier — passed as the Workflow `args` object by the orchestrator:
// mode: 'standard' | 'mini'
// iter_id: e.g. 'ct.2.3' (standard) or 'bugfix-<symptom>' / 'feat-<behaviour>' (mini)
// plan_path: (standard) path under docs/plans
// task_range: (standard, optional) [from, to] inclusive, 1-based
// red_test_path: (mini) absolute path to the RED test from debug / tdd
// cause_summary: (mini) 12 sentences (debugger cause, or tdd spec_summary)
// constraint: (mini) e.g. 'minimal fix, no surrounding cleanup'
export const meta = {
name: 'implement-loop',
description:
'Execute a plan iteration (standard) or a debug/tdd RED->GREEN handoff (mini) task-by-task: implementer -> spec-compliance -> quality, with deterministic re-loop and aggregation. Writes code, tests, stats.json, and (on PARTIAL/BLOCKED) BLOCKED.md to the working tree as unstaged changes; never commits, never moves main HEAD.',
phases: [
{ title: 'Preflight' },
{ title: 'Per-task loop' },
{ title: 'E2E coverage' },
{ title: 'Report' },
],
}
const {
mode = 'standard',
iter_id,
plan_path,
task_range = null,
red_test_path,
cause_summary,
constraint = 'minimal change, no surrounding cleanup',
} = args || {}
const STANDING =
"Standing reading first (the plugin convention): read the project's CLAUDE.md " +
'(its "## Skills plugin: project facts" name the build and test commands and any ' +
'design ledger) and `git log -10 --format=full`. Resolve the build/test commands ' +
'from those facts — do not assume a toolchain.'
// JSON Schemas — force structured verdicts so the script can branch on real outcomes.
const PREFLIGHT_SCHEMA = {
type: 'object',
additionalProperties: false,
required: ['clean', 'head_sha', 'branch'],
properties: {
clean: { type: 'boolean', description: '`git status --porcelain` produced no output' },
head_sha: { type: 'string' },
branch: { type: 'string' },
dirty_paths: { type: 'array', items: { type: 'string' } },
},
}
const TASKS_SCHEMA = {
type: 'object',
additionalProperties: false,
required: ['tasks'],
properties: {
tasks: {
type: 'array',
items: {
type: 'object',
additionalProperties: false,
required: ['id', 'title', 'text'],
properties: {
id: { type: 'integer' },
title: { type: 'string' },
text: { type: 'string', description: 'verbatim task block from the plan' },
},
},
},
},
}
const IMPL_SCHEMA = {
type: 'object',
additionalProperties: false,
required: ['status', 'summary'],
properties: {
status: { type: 'string', enum: ['DONE', 'DONE_WITH_CONCERNS', 'NEEDS_CONTEXT', 'BLOCKED'] },
summary: { type: 'string', description: 'one line: what changed (paths + functions)' },
concerns: { type: 'array', items: { type: 'string' } },
reason: { type: 'string', description: 'on BLOCKED/NEEDS_CONTEXT: the blocker' },
},
}
const SPEC_SCHEMA = {
type: 'object',
additionalProperties: false,
required: ['status'],
properties: {
status: { type: 'string', enum: ['compliant', 'non_compliant', 'unclear', 'infra_blocked'] },
findings: { type: 'array', items: { type: 'string' }, description: 'missing requirements + unrequested extras' },
ambiguity: { type: 'string' },
},
}
const QUAL_SCHEMA = {
type: 'object',
additionalProperties: false,
required: ['status'],
properties: {
status: { type: 'string', enum: ['approved', 'changes_requested', 'infra_blocked'] },
issues: { type: 'array', items: { type: 'string' }, description: 'Important + Minor only (Nits never gate)' },
},
}
const E2E_SCHEMA = {
type: 'object',
additionalProperties: false,
required: ['status'],
properties: {
status: { type: 'string', enum: ['DONE', 'DONE_WITH_CONCERNS', 'NEEDS_CONTEXT', 'BLOCKED'] },
fixtures: { type: 'array', items: { type: 'string' } },
note: { type: 'string' },
},
}
const FINALIZE_SCHEMA = {
type: 'object',
additionalProperties: false,
required: ['stats_path', 'wrote_blocked_md'],
properties: {
stats_path: { type: 'string' },
wrote_blocked_md: { type: 'boolean' },
files_touched: { type: 'integer' },
},
}
// ---- Phase 0 — clean-tree preflight ------------------------------------
phase('Preflight')
const pre = await agent(
`${STANDING}\n\nYou are the clean-tree preflight for an implement iteration. ` +
'Run `git status --porcelain`, `git rev-parse HEAD`, and `git rev-parse --abbrev-ref HEAD`. ' +
'Report whether the tree is clean (no porcelain output), the HEAD sha, the branch, and any dirty paths. ' +
'Do NOT modify anything, do NOT commit. The HEAD sha is an informational anchor only — never a reset target.',
{ label: 'preflight', phase: 'Preflight', schema: PREFLIGHT_SCHEMA },
)
if (!pre) return { status: 'BLOCKED', iter_id, reason: 'infra: preflight agent did not return' }
if (!pre.clean) {
return {
status: 'BLOCKED',
iter_id,
reason: 'infra: working tree dirty — the orchestrator must clean it before re-invoking',
dirty_paths: pre.dirty_paths || [],
}
}
const startSha = pre.head_sha
// ---- Phase 1 — assemble the task list ----------------------------------
let tasks
if (mode === 'mini') {
tasks = [
{
id: 1,
title: 'drive the RED test to GREEN',
text:
`Make this RED test pass: ${red_test_path}\n\n` +
`Context (debugger cause, or tdd spec_summary): ${cause_summary}\n\n` +
`Constraint: ${constraint}`,
},
]
} else {
const extracted = await agent(
`${STANDING}\n\nRead the plan at ${plan_path}. Extract ` +
(task_range ? `tasks ${task_range[0]}..${task_range[1]} (inclusive, 1-based)` : 'every task') +
', each as its VERBATIM block with the task id and a one-line title. Do not summarise or reorder. Do not edit anything.',
{ label: 'plan-extract', phase: 'Per-task loop', schema: TASKS_SCHEMA },
)
if (!extracted || !extracted.tasks || extracted.tasks.length === 0) {
return { status: 'NEEDS_CONTEXT', iter_id, reason: `no tasks extracted from ${plan_path}` }
}
tasks = extracted.tasks
}
// ---- Phase 2 — per-task loop (sequential) ------------------------------
phase('Per-task loop')
async function runTask(task) {
// 2.1 — implementer phase
const impl = await agent(
`${STANDING}\n\nImplement this task exactly, RED-first if it adds behaviour (TDD is an independent ` +
'inner-loop discipline — add the failing test inline even if the task text forgot to script it). ' +
'Build and test must be green before you report DONE. Leave all edits UNSTAGED; never commit.\n\n' +
`mode: ${mode}\niter_id: ${iter_id}\n\nTASK ${task.id}${task.title}\n${task.text}`,
{ agentType: 'implementer', label: `impl:${task.id}`, phase: 'Per-task loop', schema: IMPL_SCHEMA },
)
if (!impl) return { id: task.id, title: task.title, outcome: 'BLOCKED', reason: 'implementer agent did not return' }
if (impl.status === 'BLOCKED') return { id: task.id, title: task.title, outcome: 'BLOCKED', reason: impl.reason || 'worker-blocked' }
if (impl.status === 'NEEDS_CONTEXT') return { id: task.id, title: task.title, outcome: 'BLOCKED', reason: `context-exhausted: ${impl.reason || ''}` }
const concerns = impl.concerns || []
// 2.2 — spec-compliance check (gated before quality), with repair re-loop
for (let round = 0; ; round++) {
const spec = await agent(
`${STANDING}\n\nSpec-compliance review of task ${task.id}. Compare \`git diff HEAD\` against the task ` +
'text ONLY — list missing requirements and unrequested extras. No quality opinions, no fix proposals. ' +
`Focus on the files this task claims to touch.\n\nTASK ${task.id}${task.title}\n${task.text}`,
{ agentType: 'spec-reviewer', label: `spec:${task.id}`, phase: 'Per-task loop', schema: SPEC_SCHEMA },
)
if (spec && spec.status === 'compliant') break
if (spec && spec.status === 'unclear') return { id: task.id, title: task.title, outcome: 'BLOCKED', reason: 'spec-ambiguous', detail: spec.ambiguity }
if (round >= 2) return { id: task.id, title: task.title, outcome: 'BLOCKED', reason: 'review-loop-exhausted (spec-compliance)' }
// repair: re-dispatch the implementer with the review findings as the repair brief
await agent(
`${STANDING}\n\nRepair task ${task.id} to satisfy the spec-compliance review. Address EACH finding; ` +
'do not widen scope. Keep build + tests green; leave edits unstaged.\n\n' +
`TASK ${task.id}${task.title}\n${task.text}\n\nFINDINGS:\n- ${(spec?.findings || ['(none reported)']).join('\n- ')}`,
{ agentType: 'implementer', label: `impl-fix-spec:${task.id}`, phase: 'Per-task loop', schema: IMPL_SCHEMA },
)
}
// 2.3 — quality check, with repair re-loop
for (let round = 0; ; round++) {
const qual = await agent(
`${STANDING}\n\nQuality review of task ${task.id} (spec-compliance is already green — do not re-check it). ` +
'Read every chunk of `git diff HEAD` in this task\'s footprint. Report only Important and Minor issues; ' +
'Nits never gate.',
{ agentType: 'quality-reviewer', label: `qual:${task.id}`, phase: 'Per-task loop', schema: QUAL_SCHEMA },
)
if (qual && qual.status === 'approved') break
if (round >= 2) return { id: task.id, title: task.title, outcome: 'BLOCKED', reason: 'review-loop-exhausted (quality)' }
await agent(
`${STANDING}\n\nFix the quality issues on task ${task.id}. Address EACH Important/Minor issue; do not widen ` +
'scope. Keep build + tests green; leave edits unstaged.\n\nISSUES:\n- ' + (qual?.issues || ['(none reported)']).join('\n- '),
{ agentType: 'implementer', label: `impl-fix-qual:${task.id}`, phase: 'Per-task loop', schema: IMPL_SCHEMA },
)
}
return { id: task.id, title: task.title, outcome: 'DONE', concerns }
}
const results = []
for (const task of tasks) {
const r = await runTask(task)
results.push(r)
if (r.outcome === 'BLOCKED') break // do not skip ahead — task ordering dependencies are unknown
}
const completed = results.filter((r) => r.outcome === 'DONE')
const blocked = results.find((r) => r.outcome === 'BLOCKED')
let outcome
if (!blocked) outcome = 'DONE'
else if (completed.length > 0) outcome = 'PARTIAL'
else outcome = 'BLOCKED'
// ---- Phase 3 — E2E coverage (standard mode, only on a full clean run) --
let e2e = null
if (mode === 'standard' && outcome === 'DONE') {
phase('E2E coverage')
e2e = await agent(
`${STANDING}\n\nThe iteration completed. Identify 13 invariants worth protecting and write the smallest ` +
'E2E fixtures for them in the project\'s canonical fixture form; each test\'s doc comment names the property ' +
'it protects. Run the project\'s test command — all green. Leave edits unstaged; never commit.\n\n' +
`iter_id: ${iter_id}\nshipped: ${completed.map((c) => `#${c.id} ${c.title}`).join('; ')}`,
{ agentType: 'tester', label: 'e2e', phase: 'E2E coverage', schema: E2E_SCHEMA },
)
}
// ---- Phase 4/5 — finalize: stats.json always, BLOCKED.md on non-DONE ----
phase('Report')
const aggregate = {
iter_id,
mode,
started_from: startSha,
outcome,
tasks_total: tasks.length,
tasks_completed: completed.length,
task_summaries: results.map((r) => ({ id: r.id, title: r.title, outcome: r.outcome })),
concerns: results.flatMap((r) => r.concerns || []),
blocked_detail: blocked ? { task: blocked.id, reason: blocked.reason, detail: blocked.detail || null } : null,
}
const fin = await agent(
`${STANDING}\n\nFinalize an implement iteration. Using the aggregate below:\n` +
'1. Write a stats file at `/tmp/iter-' + iter_id + '/stats.json` (or under the project stats dir if its facts ' +
'declare one) containing: iter_id, date (from `date +%F`), mode, outcome, tasks_total, tasks_completed, and ' +
'blocked_reason (or null).\n' +
(outcome === 'DONE'
? '2. Do NOT write BLOCKED.md — this run is DONE.\n'
: '2. Write `BLOCKED.md` at the repo ROOT (uncommitted by convention; never staged). Sections: a `# BLOCKED — ' +
'iter ' + iter_id + '` header with Date/Started-from/Status/Tasks-completed, `## What ran` (one line per DONE ' +
'task), `## What did not` (the blocked task, its reason, and a one-line suggested next step), `## Concerns`, ' +
'and `## Files touched` (from `git diff --name-only HEAD`).\n') +
'Report the stats path, whether you wrote BLOCKED.md, and the count from `git diff --name-only HEAD | wc -l`. ' +
'Do NOT commit anything.\n\nAGGREGATE:\n' + JSON.stringify(aggregate, null, 2),
{ label: 'finalize', phase: 'Report', schema: FINALIZE_SCHEMA },
)
// ---- End-report — the orchestrator reads this, inspects the tree, commits.
return {
status: outcome,
iter_id,
mode,
started_from: startSha,
tasks_total: tasks.length,
tasks_completed: completed.length,
task_summaries: aggregate.task_summaries,
concerns: aggregate.concerns,
e2e_fixtures: e2e ? e2e.fixtures || [] : [],
stats_path: fin ? fin.stats_path : null,
blocked_md: fin && fin.wrote_blocked_md ? 'BLOCKED.md (uncommitted)' : null,
files_touched: fin ? fin.files_touched : null,
blocked_detail: aggregate.blocked_detail,
}
+12 -1
View File
@@ -5,6 +5,10 @@
# a skill. For each such <name>, creates symlinks: # a skill. For each such <name>, creates symlinks:
# ~/.claude/skills/<name> -> <repo>/<name> # ~/.claude/skills/<name> -> <repo>/<name>
# ~/.claude/agents/<name> -> <repo>/<name>/agents (if that dir exists) # ~/.claude/agents/<name> -> <repo>/<name>/agents (if that dir exists)
# and, for each executable Workflow script the skill ships:
# ~/.claude/workflows/<file>.js -> <repo>/<name>/workflows/<file>.js
# (the flat .claude/workflows/ dir is where the Workflow tool resolves
# named workflows from — e.g. implement/workflows/implement-loop.js).
# #
# Idempotent — existing symlinks pointing to this repo are left # Idempotent — existing symlinks pointing to this repo are left
# alone; existing entries that point elsewhere are reported and # alone; existing entries that point elsewhere are reported and
@@ -16,8 +20,9 @@ REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLAUDE_SKILLS="$HOME/.claude/skills" CLAUDE_SKILLS="$HOME/.claude/skills"
CLAUDE_AGENTS="$HOME/.claude/agents" CLAUDE_AGENTS="$HOME/.claude/agents"
CLAUDE_WORKFLOWS="$HOME/.claude/workflows"
mkdir -p "$CLAUDE_SKILLS" "$CLAUDE_AGENTS" mkdir -p "$CLAUDE_SKILLS" "$CLAUDE_AGENTS" "$CLAUDE_WORKFLOWS"
link_one() { link_one() {
local src="$1" local src="$1"
@@ -48,6 +53,12 @@ for d in "$REPO_DIR"/*/; do
if [ -d "$d/agents" ]; then if [ -d "$d/agents" ]; then
link_one "${d%/}/agents" "$CLAUDE_AGENTS/$name" link_one "${d%/}/agents" "$CLAUDE_AGENTS/$name"
fi fi
if [ -d "$d/workflows" ]; then
for wf in "$d"workflows/*.js; do
[ -f "$wf" ] || continue
link_one "$wf" "$CLAUDE_WORKFLOWS/$(basename "$wf")"
done
fi
done done
echo echo
+1 -1
View File
@@ -48,7 +48,7 @@ def status_of(final_text):
os.unlink(path) os.unlink(path)
# A realistic implement-orchestrator end-report that SHIPPED (Status: DONE) # A realistic plain-text iteration end-report that SHIPPED (Status: DONE)
# but whose body carries the template's BLOCKED-bearing lines. This is the # but whose body carries the template's BLOCKED-bearing lines. This is the
# exact shape that issue #3 reports as misclassified. # exact shape that issue #3 reports as misclassified.
SHIPPED_DONE_REPORT = """\ SHIPPED_DONE_REPORT = """\
+1 -1
View File
@@ -434,7 +434,7 @@ CLAUDE.md project facts):
**Re-loop limit: ≤ 2 self-correction rounds. The 3rd unresolved **Re-loop limit: ≤ 2 self-correction rounds. The 3rd unresolved
editorial `BLOCK` escalates** — same shape as the per-task re-loop editorial `BLOCK` escalates** — same shape as the per-task re-loop
limit in `implement-orchestrator`. limit in the implement loop (`../implement/workflows/implement-loop.js`).
Three properties keep this loop honest, not a way to grind the panel Three properties keep this loop honest, not a way to grind the panel
into submission: into submission:
+4 -3
View File
@@ -206,9 +206,10 @@ iteration and gets queued for a separate one.
and Red Flags. The orchestrator does not execute these phases and Red Flags. The orchestrator does not execute these phases
directly. directly.
- **Hand-off target (GREEN):** `../implement/SKILL.md` — runs the - **Hand-off target (GREEN):** `../implement/SKILL.md` — runs the
GREEN side in mini mode after the orchestrator decides whether to GREEN side as the `implement-loop` workflow in `mode: "mini"`, after
commit the RED test separately or as part of the final commit. the orchestrator decides whether to commit the RED test separately or
This is the same mini-mode handoff `../debug/SKILL.md` uses. 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 - **Bounce-back target:** `../brainstorm/SKILL.md` — reclaims the
work when the behaviour is not test-specifiable. work when the behaviour is not test-specifiable.
- **Sibling fast path:** `../specify/SKILL.md` — the other bounded - **Sibling fast path:** `../specify/SKILL.md` — the other bounded
+9 -3
View File
@@ -29,9 +29,15 @@ discipline the agents are designed to enforce.
a fixed design, tester for E2E coverage, debugger for a fixed design, tester for E2E coverage, debugger for
diagnostics, architect for read-only drift review. diagnostics, architect for read-only drift review.
- **Trivial mechanical edits** (one-line fixes, doc typos, - **Trivial mechanical edits** (one-line fixes, doc typos,
rename across N files) — fine to do directly. Anything that rename across N files) — fine to do directly. This is the
requires reading large surface area or making judgement inline form of the `compiler-driven` cascade arm, and the
calls should go to an agent. **same done-signal binds**: a clean build AND the suite green
*unchanged*, or it bounces (a hole → `specify`, or `tdd` if it
is test-specifiable new behaviour; a regression → `debug`,
RED-first). An observed bug never enters here — it
is `debug`'s job. Anything that requires reading large surface
area or making judgement calls should go to an agent (or the
`compiler-driven-edit` / `implement-loop` workflow).
- **Verify the work** — agent reports describe intent, not - **Verify the work** — agent reports describe intent, not
outcome. After every agent run I check the diff and the test outcome. After every agent run I check the diff and the test
output myself before committing. output myself before committing.
+1
View File
@@ -28,5 +28,6 @@ unlink_pointing_here() {
unlink_pointing_here "$HOME/.claude/skills" unlink_pointing_here "$HOME/.claude/skills"
unlink_pointing_here "$HOME/.claude/agents" unlink_pointing_here "$HOME/.claude/agents"
unlink_pointing_here "$HOME/.claude/workflows"
echo "Uninstall complete." echo "Uninstall complete."