7a58a530b1
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
286 lines
16 KiB
Markdown
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.
|