Files
Skills/implement/SKILL.md
T
Brummel 7a58a530b1 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
2026-06-17 12:27:51 +02:00

286 lines
16 KiB
Markdown

---
name: implement
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 on the Workflow substrate
> **Violating the letter of these rules is violating the spirit.**
## Overview
Plan execution runs as the **`implement-loop` Workflow** (shipped at
`workflows/implement-loop.js`, symlinked into `~/.claude/workflows/` by
`install.sh`). The orchestrator invokes it through the Workflow tool;
the script runs the entire per-task loop deterministically — for each
task, implementer → spec-compliance → quality, **each a separate
`agent()` call** dispatching the surviving phase agent-types
(`implementer`, `spec-reviewer`, `quality-reviewer`, and `tester` for
the E2E phase). The inter-phase re-loop, the aggregation, and the
DONE/PARTIAL/BLOCKED verdict are code in the script, not
natural-language reasoning between dispatches.
This replaces the former `implement-orchestrator` agent, which carried
the loop as inline role-switches inside one subagent context because
Claude Code forbids nested-subagent dispatch. A Workflow orchestrates
from the top level, so that workaround is retired: each phase is now a
real, **independently-invokable** agent call, and the routing keys on
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
Triggers:
- A plan exists under `docs/plans` (standard mode → `implement-loop`
with `mode: "standard"`).
- 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 plan-driven or RED-handoff code to ship.**
### The compiler-driven arm (light path)
A **behaviour-preserving type/signature edit at a definition site**
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 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.
PER-TASK PHASES ARE SEPARATE AGENT CALLS IN THE WORKFLOW SCRIPT — implementer, then spec-compliance, then quality. SPEC COMPLIANCE IS GATED BEFORE QUALITY.
TASKS RUN SEQUENTIALLY; A BLOCKED TASK STOPS THE LOOP — NO SKIP-AHEAD (TASK ORDERING DEPENDENCIES ARE UNKNOWN).
NEVER PUSH PAST `BLOCKED` BY HAND.
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 loop mechanics
The script runs, per task: implementer phase, then spec-compliance,
then quality — gated in that order. Each is a separate `agent()` call
with a structured-output schema, so the script branches on a real
verdict. The re-loop limit is **2 repair retries per failure-mode per
task; the 3rd unresolved attempt escalates to `BLOCKED`**. A
`non_compliant` / `changes_requested` verdict re-dispatches the
`implementer` with the review findings as the repair brief; `unclear`
task text → `spec-ambiguous` BLOCKED; a tooling failure → `infra`
BLOCKED. This logic lives as the loops in
`workflows/implement-loop.js` — the single source; it is deliberately
not restated as prose elsewhere, so the two cannot drift.
## The Process — orchestrator side
### 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:
```
Workflow({ name: "implement-loop", args: {
mode: "standard",
iter_id: "<iter_id>",
plan_path: "<path under docs/plans>",
task_range: [3, 8] // optional
}})
```
For a RED-first handoff from `debug` or `tdd` (mini mode):
```
Workflow({ name: "implement-loop", args: {
mode: "mini",
iter_id: "bugfix-<short-symptom>", // tdd: "feat-<short-behaviour>"
red_test_path: "<absolute path>",
cause_summary: "<1-2 sentences from debugger>", // tdd: the desired-behaviour line
constraint: "minimal fix, no surrounding cleanup" // tdd: "minimal feature, no surrounding scope"
}})
```
The carrier fields are defined authoritatively at the top of
`workflows/implement-loop.js`. The load-bearing `iter_id` rule is there
too: it names the scratch dir and the stats filename, NOT a branch
(there is no branch).
### Step 2 — Read the end-report
The workflow returns a small structured end-report (its return value):
`status`, `iter_id`, `started_from`, `tasks_total` / `tasks_completed`,
per-task summaries, concerns, E2E fixtures, the stats path, and — on
PARTIAL/BLOCKED — the blocked detail. Per-task chatter stayed inside
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)
The workflow returns with code edits and the stats file sitting in the
working tree as unstaged changes. Nothing is committed yet, and there
is no `BLOCKED.md` (DONE never writes one).
1. Inspect: `git status` and `git diff` — confirm the changes match
what the end-report claims. The end-report is the per-task summary;
use it as the basis for the commit body.
2. Decide commit shape — by default one cohesive commit for the whole
iter; split into a few logical commits only if the diff genuinely
covers multiple unrelated changes. Per-task commit splitting is NOT
a goal; the iter is the unit of consistency the orchestrator is
committing to.
3. Write the commit body. It carries everything a future reader needs
that the diff itself does not: the *why*, the alternatives
considered and rejected, the verification steps run, and any
concerns that remain. Detail-fill comes from the end-report.
4. Stage + commit the code edits and the stats file.
5. If the trigger is done-state and the user is away, run the project's
configured notification command per `../boss/SKILL.md` "Done-state
notifications".
### Step 4 — Orchestrator handling (on PARTIAL or BLOCKED)
The workflow returns with whatever work-in-progress it managed plus
`BLOCKED.md` at the repo root carrying the diagnostic. Nothing is
committed. The orchestrator decides what to do with the dirty working
tree:
1. Read `BLOCKED.md``## What did not` names the failure mode and the
blocked task's reason. Read `git diff` to see what was attempted.
2. Decide:
- **Repair:** keep the working-tree changes in place (or `git stash`
them if a clarifying read of clean main is needed first). Adjust
plan or extend context; **delete `BLOCKED.md`** (`rm BLOCKED.md`)
before re-running the workflow — its preflight clean-tree check
counts the file as dirt. Either stash everything and re-run on a
clean tree, or commit the known-good subset, then `rm BLOCKED.md`,
then re-run.
- **Discard:** `git checkout -- .` to drop unstaged file changes;
`git clean -fd` / `rm BLOCKED.md` plus anything else new. main HEAD
does NOT move.
- **Escalate:** ask the user via the configured notification
command. `BLOCKED.md` sits in the working tree until the
conversation resumes.
Under no circumstance does the orchestrator `git reset` or `git revert`
on main: there is nothing on main to undo (the workflow did not
commit), and the policy forbids history rewinding on main even if there
were.
## Handoff Contract
`implement` consumes:
| Source | Carrier |
|--------|---------|
| from `planner` | path to plan under `docs/plans` (+ optional `task_range`) → `implement-loop` standard mode |
| 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 code
edits and the stats file; on PARTIAL/BLOCKED, also `BLOCKED.md` at the
repo root. The orchestrator inspects, commits the code + stats (DONE)
or repairs/discards (PARTIAL/BLOCKED). The `compiler-driven-edit`
workflow produces an unstaged edit on `DONE`, or a `BOUNCE` verdict
naming `specify` / `debug`. No further hand-off — `audit` runs
independently at cycle close.
## Common Rationalisations
| Excuse | Reality |
|--------|---------|
| "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 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 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 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-run for the missing task or `git checkout -- .` and re-plan. |
| "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 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. |
| "The compiler-driven suite went red but the fix is one line — I'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. |
| "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
- Orchestrator dispatching `implementer` directly outside the workflow
(bypassing the gated loop).
- Orchestrator running `git reset` or `git revert` on main.
- The workflow (or any agent it spawns) running `git commit`.
- Two `implement-loop` runs overlapping on the same working tree.
- `BLOCKED.md` staged or committed by anyone.
- 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
- **Workflows dispatched:**
- `workflows/implement-loop.js` — the per-task loop (standard + mini).
Single source for the carrier contract, the re-loop limit, the
stats schema, the `BLOCKED.md` template, and the end-report shape.
- `workflows/compiler-driven-edit.js` — the observe-then-bounce light
path for a behaviour-preserving type/signature edit.
- **Phase agent-types the workflow dispatches** (they survive the
migration; the inline-role-switch orchestrator-agent does not):
- `agents/implementer.md` — implementer phase (TDD discipline)
- `agents/spec-reviewer.md` — spec-compliance phase
- `agents/quality-reviewer.md` — quality phase
- `agents/tester.md` — E2E coverage phase
- **Selector that routes here:** `../boss/SKILL.md` "Entry-path
reflection" — the `compiler-driven` arm names this executor, and this
file names that arm. They 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.
- **Why the substrate, not nested subagents.** Claude Code still
forbids a subagent from spawning subagents. A Workflow script
orchestrates from the top level, so the per-task phases run as its own
agent calls without nesting — see `../docs/design.md` § Plugin layer.
- **Input sources:** `../planner/SKILL.md` (plans), `../debug/SKILL.md`
and `../tdd/SKILL.md` (RED-test handoff for mini mode).
- **Output target:** orchestrator reads the end-report, inspects, and
commits; `../audit` runs at cycle close.