a1d6409163
Introduce the worktree convention the corpus previously lacked: any substantive work stream targeting main — in every mode, autonomous (/boss) and interactive alike — runs in a git worktree on its own branch (issue-N-<slug>), and the branch reaches main only through a user-ratified merge performed by the orchestrator (fast-forward preferred, push covered by the ratification, no pull request). Background sessions were already harness-isolated; this extends the same isolation to the sessions the harness does not guard. The convention depends on worktree.baseRef=head (the default 'fresh' bases branches on origin's routinely-stale tip under the no-autonomous-push discipline), now pinned in conventions.md. boss/SKILL.md gains the run-level mechanics (§ The run worktree): one worktree per run named after the reference issue, entered before the first dispatch; the merge is a user-reserved run-close act; the done-state notify names the branch as the review handle; closes #N fires at merge-and-push time. Iron Law, rollback-sandbox framing, user-reserved-act enumerations, rationalisations, and red flags updated to match. Corollary reconciliations after adversarial review: implement's 'there is no branch' narrowed to 'the loop creates no branch of its own'; specify's auto-signed-commits-on-main heading reworded to ratified-history framing; the sandbox bullets in conventions.md and the CLAUDE.md fragment note the anchor is the run branch's base. The skills meta-repo itself stays exempt (its CLAUDE.md: install symlinks resolve to the primary clone) and direct-on-main.
384 lines
23 KiB
Markdown
384 lines
23 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: 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, TESTS, AND THE STATS FILE LIVE IN THE WORKING TREE AS UNSTAGED CHANGES UNTIL THE ORCHESTRATOR COMMITS THEM.
|
|
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 SCRIPT WRITES `BLOCKED.md` AT THE REPO ROOT — UNCOMMITTED BY CONVENTION. ON `DONE`, NO SEPARATE FILE. (EXCEPTIONS THAT WRITE NO `BLOCKED.md`: 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).
|
|
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 `tree-check` agent runs
|
|
`git status --porcelain | wc -l` after the per-task loop but **before**
|
|
E2E and finalize. A `DONE` outcome with a zero count is a no-op iteration
|
|
→ `BLOCKED`. `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/finalize keeps fixtures and the
|
|
stats/`BLOCKED.md` artefacts from inflating it; and a non-returning agent
|
|
is treated as `BLOCKED` (infra), never waved through. A clean-tree no-op
|
|
writes no `BLOCKED.md` (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.
|
|
|
|
**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 <sha>:<path>`). 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 -- <path>` cannot discard
|
|
them). 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: "<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
|
|
(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 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. 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 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.
|
|
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 plus
|
|
`BLOCKED.md` at the repo root carrying the diagnostic. 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 `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.
|
|
- **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.
|