--- 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 and tests to the working tree without committing. The orchestrator reads the end-report, writes the stats file (and `BLOCKED.md` on PARTIAL/BLOCKED) from the contents the end-report carries, 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 code edits live in the working tree. The stats file and — on PARTIAL/BLOCKED — `BLOCKED.md` are **built by the script and carried in the end-report** (`artifacts.stats_json` / `artifacts.blocked_md`): the script has no filesystem access of its own, and dispatching an agent just to template the script's own aggregate cost ~41k real tokens per run (issue #29). The orchestrator writes them byte-identical, filling the two runtime slots it owns anyway — `{{DATE}}` (`date +%F`) and `{{FILES_TOUCHED}}` (`git status --porcelain`). The script **never commits** and never moves main HEAD. The orchestrator reads one end-report (the workflow's return value), writes the artifacts, 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: an edit actually landed AND 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. The "edit actually landed" conjunct is load-bearing: `clean build AND suite green` is *vacuously* true on an empty tree, so a no-op edit would otherwise read as DONE. A clean working tree after the edit phase is a no-op, not a success → distinct `BLOCKED` (no-op-edit), never DONE. Positive evidence (tree differs from HEAD) is required, checked both at the edit phase and independently at verify. (A malformed or missing carrier is likewise a distinct `BLOCKED` (bad-carrier) before any agent runs — never a bounce.) 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 AND TESTS LIVE IN THE WORKING TREE AS UNSTAGED CHANGES UNTIL THE ORCHESTRATOR COMMITS THEM; THE STATS FILE AND BLOCKED.md ARE WRITTEN BY THE ORCHESTRATOR FROM THE CONTENTS THE END-REPORT CARRIES. WITHIN THIS LOOP, MAIN HEAD IS SACROSANCT — NO RESET, NO REVERT; THE LOOP ONLY EVER WRITES UNSTAGED CHANGES. MAIN MOVES FORWARD ONLY VIA ORCHESTRATOR COMMITS. (THE `/boss` ROLLBACK SANDBOX WINDS BACK ALREADY-COMMITTED AUTONOMOUS WORK — A BOSS-LEVEL RECOVERY, NOT A STEP IN THIS LOOP.) NO IN-LOOP AGENT EVER DISCARDS WORKING-TREE STATE VIA `git checkout` / `git restore` — WHOLE-FILE OR `--patch`: NOTHING COMMITS BETWEEN TASKS, SO A SHARED FILE CARRIES EARLIER TASKS' UNCOMMITTED DONE WORK, AND A CHECKOUT SILENTLY DESTROYS IT (issue #23). RECOVERY IS FORWARD — EDIT BACK, OR REPORT `BLOCKED`. (THE ORCHESTRATOR'S OWN END-OF-ITERATION `git checkout -- .` DISPOSITION HAPPENS OUTSIDE THE LOOP.) 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 END-REPORT CARRIES THE `BLOCKED.md` CONTENT (`artifacts.blocked_md`); THE ORCHESTRATOR'S FIRST ACTION ON READING SUCH A REPORT IS WRITING IT VERBATIM TO `BLOCKED.md` AT THE REPO ROOT — UNCOMMITTED BY CONVENTION. ON `DONE`, `artifacts.blocked_md` IS NULL. (EXCEPTIONS WHERE IT IS ALSO NULL: A NO-OP ITERATION — ITS TREE IS ALREADY CLEAN — AND ANY PRE-LOOP EARLY EXIT (MALFORMED CARRIER, DIRTY-TREE PREFLIGHT, PLAN-EXTRACT GUARD): NOTHING RAN AND NOTHING WAS WRITTEN, SO THE STATUS + `infra:`-PREFIXED REASON RIDE THE END-REPORT AND THERE IS NOTHING TO CLEAN UP OR EXPLAIN.) `DONE` REQUIRES POSITIVE EVIDENCE THAT AN EDIT LANDED — A NO-OP ITERATION (`git status --porcelain` EMPTY, untracked files INCLUDED) IS NEVER `DONE`, IT IS `BLOCKED` (the issue #11 vacuous-green shape, guarded on the main path). THE VERDICT IS THE GIT TREE COUNT, NEVER A SELF-REPORT. IN MINI MODE THE RED->GREEN IS OBSERVED, NOT ASSERTED: AN INDEPENDENT VERIFY RE-RUNS THE HANDED-OFF RED TEST AND THE SUITE; A STILL-RED TEST OR A REGRESSION IS `BLOCKED` (route back to debug, RED-first). IN STANDARD MODE THE END-VERIFY RE-RUNS THE FULL SUITE INDEPENDENTLY: SUITE RED IS `BLOCKED` (route back to debug, RED-first) — GREEN IS OBSERVED, NEVER TAKEN FROM PER-TASK SELF-REPORTS. 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`** — except that a quality round whose findings are all `Minor`, arriving after at least one repair dispatch, surfaces them as concerns and closes the task instead of gating (cosmetic residue must not discard a green task). 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. **Positive-evidence precondition on `DONE`** (issue #12 — the issue #11 vacuous-green shape on the main path). `build green AND suite green` is not enough: it is satisfiable by doing nothing. So `DONE` also requires positive evidence that an edit landed. The verdict is taken from **git ground truth, never a self-report** — a self-report must never be able to *fail* a run that actually did the work: - **Iteration gate (standard mode)** — a dedicated end-`verify` agent runs after the per-task loop but **before** E2E, with two independently-gating legs: `git status --porcelain | wc -l` (a `DONE` outcome with a zero count is a no-op iteration → `BLOCKED`) and a full **independent suite run** (suite red → `BLOCKED`, route back to `debug` RED-first — standard mode previously had no end-of-iteration suite gate while mini and compiler-driven both re-run the suite; the asymmetry is closed). `git status --porcelain` (not `git diff HEAD`) is used so a brand-new **untracked** file — the implementer leaves edits unstaged — still counts; running before E2E keeps fixtures from inflating it; and a non-returning agent is treated as `BLOCKED` (infra), never waved through. A clean-tree no-op carries no `BLOCKED.md` content (nothing to clean up — see Step 4). - **Mini mode gate** — an independent `mini-verify` agent re-runs the handed-off RED test and the suite and checks the tree is dirty (`git status --porcelain`); a still-red test, a regression, or a clean tree is `BLOCKED`. The GREEN is observed, not taken from the implementer's self-report. - **Per-task `applied_changes`** (self-report, OR-ed across the initial dispatch and every repair) does NOT gate the outcome — a self-report must never be able to *fail* a run that actually did the work (e.g. the initial implementer no-ops but a repair writes the files). It feeds only a neutral concern when a task wrote nothing (it may be genuinely already-satisfied — the orchestrator weighs it). The E2E phase's `status` is also read now (not just its fixtures): a non-`DONE` E2E status or a zero-fixture run surfaces as a concern. **Quality-tier selection** (issue #30). The quality phase runs opus/xhigh by default, tiered to sonnet/high for a small diff: ≤25 changed lines AND no contract-referenced path, both measured by the spec-reviewer (which reads `git diff HEAD` itself — never the implementer's self-report) via `diff_magnitude` / `touches_contract`. Unmeasured or contract-touching diffs stay opus. The independent end-verify suite gate is the deterministic backstop; the chosen tier is logged per task in the end-report (`qual_tier`) so the split stays auditable. **Cross-task discard guard** (issue #23). In a multi-task run nothing commits between tasks, so one task's file-level `git checkout`/`git restore` can silently destroy an earlier task's DONE work in a shared file — invisible to the reviewers (scoped to the current task's footprint) and to the tree-count gate (the surviving changes keep the count nonzero). The loop therefore records a snapshot boundary after every task (`git stash create` — a dangling commit; HEAD, index, and working tree untouched) and hard-`BLOCK`s when a path that was HEAD-modified at one boundary is HEAD-identical at the next: the end-report names the lost paths, the boundary, and the recovery snapshot sha the discarded content can be read from (`git show :`). Coarse by design, in both directions: a checkout followed by re-edits of the same file, or a `--patch`-level restore of a single hunk, escapes the comparison (the path never leaves the diff) — the snapshot still keeps any loss diagnosable and recoverable; conversely a task that legitimately edits a shared file back to byte-identical HEAD content trips the guard, so a tripped verdict means *adjudicate against the snapshot*, not proven malice. Renames do not trip it (the boundary diff runs `--no-renames`, so a renamed file's old path stays listed as a deletion). Untracked files sit outside the threat model (`git checkout -- ` cannot discard them). Boundaries are HEAD-aware (issue #32): each snapshot records the HEAD its diff ran against, and a boundary pair straddling a HEAD move — the sanctioned commit-the-subset-then-resume repair — resets the guard with a concern instead of comparing, so a replayed pre-commit boundary never counts the committed subset as lost paths; in a single live run the same concern flags an agent that moved HEAD mid-run. Cost: one extra sonnet/medium snapshot call per task of a multi-task run; single-task and mini runs skip the guard. The detection details live in `workflows/implement-loop.js`, the single source. ## 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: "", plan_path: "", plan_sha256: "", // recompute at EVERY (re-)invocation (issue #32) task_range: [3, 8], // optional repo_root: "" // strongly recommended; effectively required in worktree sessions (issue #25) }}) ``` For a RED-first handoff from `debug` or `tdd` (mini mode): ``` Workflow({ name: "implement-loop", args: { mode: "mini", iter_id: "bugfix-", // tdd: "feat-" red_test_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" repo_root: "" // strongly recommended; effectively required in worktree sessions (issue #25) }}) ``` `plan_sha256` content-addresses the plan (issue #32): a resume replays agents cached per (prompt, opts), and with only the plan *path* in the extraction prompts an edit-and-resume would replay the stale pre-edit task text — the repair silently no-ops. Compute the hash fresh (`sha256sum `) at every invocation, including a `resumeFromRunId` re-invocation after a plan repair; never reuse the old value. `repo_root` is stamped into every stage prompt as a `git -C` anchor: in worktree sessions a dispatched agent's cwd can resolve to the primary checkout and review the wrong tree (false `infra_blocked` quality reviews, a false no-op `BLOCKED` from the end-verify — issue #25). Without the field the first dispatched stage probes `git rev-parse --show-toplevel` as a fallback, which is subject to the same race — pass the field. 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 (the loop creates no branch of its own; it runs in whatever checkout the session sits in — under the worktree convention, the run's worktree 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 `artifacts` block (`stats_json`, `stats_path_hint`, `blocked_md`), 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. **First action, before anything else:** materialise the artifacts — write `artifacts.stats_json` to the stats path (fill `{{DATE}}` with `date +%F`), and when `artifacts.blocked_md` is non-null write it verbatim to `BLOCKED.md` at the repo root (fill `{{DATE}}` and `{{FILES_TOUCHED}}` from `git status --porcelain`). The contents are the script's single-sourced templates — do not rephrase, restructure, or embellish them. An **infra early-exit** (malformed or missing carrier, dirty-tree preflight, a plan-extract guard) returns a minimal report instead: `status: BLOCKED` plus an `infra:`-prefixed `reason`, no `blocked_detail` and none of the per-task fields — nothing ran. ### Step 3 — Orchestrator inspect + commit step (on DONE) The workflow returns with code edits sitting in the working tree as unstaged changes; the stats file was just written by you from `artifacts.stats_json` (Step 2). Nothing is committed yet, and there is no `BLOCKED.md` (`artifacts.blocked_md` is null on DONE). 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. A concern phrased as a *held* or *unresolved quality finding* (the per-task quality gate kept a plan-prescribed element against a reviewer finding, or a repair was a no-op) is the gate handing you the judgement it could not make in code: verify that finding by hand against the diff before committing — do not auto-sign past it, even under `/boss`. Keep it if it is a principled plan-hold; if it is an unaddressed defect, send the iter back rather than commit. 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; `BLOCKED.md` at the repo root carries the diagnostic — written by you in Step 2 from `artifacts.blocked_md`. Nothing is committed. The orchestrator decides what to do with the dirty working tree: 0. **No-op `BLOCKED` special case** — if the end-report's status is `BLOCKED` with `artifacts.blocked_md` null and a no-op `blocked_detail` (the reason names a no-op iteration or a clean-tree no-op fix; `files_touched` is 0 in standard mode, null in mini), there is **no `BLOCKED.md`** and the tree is already clean. The diagnostic is in the end-report's `blocked_detail`, not a file. Nothing to clean up or stash; re-plan or re-run from the same clean tree. The same no-file shape occurs for an **infra early-exit** (`reason` prefixed `infra:` — malformed/missing carrier, dirty preflight tree, a plan-extract guard): no `BLOCKED.md`, no `blocked_detail`, and the tree exactly as the loop found it. Fix the invocation (or clean the tree) and re-invoke. 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. The re-run is either a fresh invocation with `task_range` covering the remaining tasks, or a resume (`resumeFromRunId` on the same script) — for a resume, recompute `plan_sha256` from the edited plan so the extraction prompts change: the repaired task runs live, and a completed task replays from cache when its re-extracted text is byte-identical (the usual case; worst case it re-runs redundantly, never stale — issue #32). The discard guard tolerates the interim subset commit: boundaries are HEAD-aware, so the moved baseline resets the guard with a concern instead of a false lost-paths `BLOCKED`. - **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. Within the `implement` loop the orchestrator does not `git reset` or `git revert` on main: there is nothing on main to undo — the workflow never commits, it only writes unstaged working-tree changes. (The one place autonomous work is ever wound back is the `/boss` rollback sandbox, which hard-resets *already-committed, unpushed* autonomous commits above the session anchor on a dead-ended run — a boss-level recovery, not a step in this loop. See `../boss/SKILL.md` § Direction freedom.) ## 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.