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

16 KiB

name, description
name description
implement 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.