feat(implement-loop): retire the finalize dispatch; the end-report carries the artifacts
The finalize agent only templated the script's already-computed
aggregate into stats.json / BLOCKED.md — ~41k real tokens per run
(197 dispatches / 8.0M in the 206-run corpus) for pure formatting.
The templates now live in the script (single source): the end-report
carries artifacts.stats_json and artifacts.blocked_md (null on DONE
and clean no-ops), and the orchestrator writes them byte-identical,
filling exactly two runtime slots it owns anyway — {{DATE}} (date +%F)
and {{FILES_TOUCHED}} (git status --porcelain, its Step-3/4 inspection
duty). File paths, consumers (tdd decompose reads BLOCKED.md,
postmortem parses it), and content shape are unchanged; the
agent-authored 'suggested next step' line is dropped — blocked_detail
carries the diagnosis.
refs #29
This commit is contained in:
+34
-20
@@ -1,6 +1,6 @@
|
||||
---
|
||||
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.
|
||||
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
|
||||
@@ -30,11 +30,17 @@ 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
|
||||
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), inspects the unstaged tree,
|
||||
and decides commit shape.
|
||||
end-report (the workflow's return value), writes the artifacts,
|
||||
inspects the unstaged tree, and decides commit shape.
|
||||
|
||||
## When to Use / Skipping
|
||||
|
||||
@@ -99,13 +105,13 @@ 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.
|
||||
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 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.)
|
||||
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).
|
||||
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).
|
||||
@@ -227,20 +233,28 @@ worktree branch).
|
||||
|
||||
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
|
||||
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. An **infra early-exit** (malformed or missing carrier, dirty-tree
|
||||
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 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).
|
||||
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;
|
||||
@@ -268,16 +282,16 @@ is no `BLOCKED.md` (DONE never writes one).
|
||||
|
||||
### 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:
|
||||
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 `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
|
||||
`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`
|
||||
|
||||
Reference in New Issue
Block a user