iter disc.1: boss-only commits + main-as-quarantine (no branches in implement)

Two project-wide rules are now explicit across every skill:

1. Only the Boss commits. No skill agent (implementer,
   brainstormer, planner, debugger, fieldtester, docwriter,
   architect, bencher) runs `git commit`. Agents write their
   artefacts to the working tree as unstaged changes; the Boss
   inspects, decides commit shape, and commits.
2. main HEAD is sacrosanct. No actor runs `git reset` or
   `git revert` on main. Bad work stays in the working tree
   where it is still discardable via `git checkout -- <paths>`.

Implement loses the `iter/<iter_id>` branch mechanic entirely;
Phase 0 of the orchestrator-agent now does a clean-tree check
and refuses to start on a dirty tree. Per-task agent commits
are removed everywhere; reviewers operate against
`git diff HEAD` instead of `pre_task_sha..head_sha`.

Motivation: 2026-05-11 iter 23.4 stranded prep2/prep3 commits on
an iter-branch that never integrated to main, then a corrected
spec falsely claimed those commits had shipped. Branch-per-iter
+ manual-Boss-merge + iter-stacking made the strand structurally
possible. See docs/journals/2026-05-11-iter-disc.1.md for the
full per-task notes and motivation.
This commit is contained in:
2026-05-11 22:44:43 +02:00
parent 841d65d88c
commit 51da9fab53
18 changed files with 552 additions and 248 deletions
+71 -54
View File
@@ -1,6 +1,6 @@
---
name: implement
description: Use when an implementation plan exists in docs/plans/ and is ready to execute, OR when a debug RED-test is handed off for a bugfix. Dispatches the ailang-implement-orchestrator agent, which runs the entire per-task loop (implementer phase → spec-compliance check → quality check, as sequential role-switches in its own context) on an isolated branch `iter/<iter_id>`, writes a per-iter journal + stats file, and returns a compressed end-report. The Boss reads the end-report, optionally rewrites the journal Summary section, appends one line to docs/journals/INDEX.md, and fast-forwards main to the iter branch.
description: Use when an implementation plan exists in docs/plans/ and is ready to execute, OR when a debug RED-test is handed off for a bugfix. Dispatches the ailang-implement-orchestrator agent, which runs the entire per-task loop (implementer phase → spec-compliance check → quality check, as sequential role-switches in its own context) directly in the working tree without creating commits, writes a per-iter journal + stats file (also uncommitted), and returns a compressed end-report. The Boss reads the end-report, inspects the working tree, decides commit shape, and performs all commits.
---
# implement — plan execution via a dedicated orchestrator-agent
@@ -14,9 +14,10 @@ ONE subagent (`ailang-implement-orchestrator`), which runs the entire
per-task loop in its own context: implementer phase → spec-compliance
check → quality check, per task, as sequential role-switches inside
the orchestrator-agent itself (Claude Code does not permit nested
subagent dispatch — see Cross-references). All commits land on a
dedicated branch `iter/<iter_id>`. The Boss sees one ≤500-token
end-report and a per-iter journal file on the branch.
subagent dispatch — see Cross-references). All work lives in the
working tree: code edits, the per-iter journal file, and the stats
file. The orchestrator does NOT commit. The Boss sees one ≤500-token
end-report and an unstaged working tree, then decides commit shape.
This skill body is intentionally short. The procedural details of
the per-task loop live in
@@ -41,7 +42,8 @@ is shed.
## The Iron Law
```
ONE BRANCH PER ITER — `iter/<iter_id>`, created from local main.
THE IMPLEMENTER NEVER COMMITS. CODE EDITS, THE PER-ITER JOURNAL, AND THE STATS FILE ALL LIVE IN THE WORKING TREE AS UNSTAGED CHANGES UNTIL THE BOSS COMMITS THEM.
MAIN HEAD IS SACROSANCT — NO RESET, NO REVERT, BY ANY ACTOR. MAIN MOVES FORWARD ONLY VIA BOSS COMMITS.
PER-TASK PHASES RUN SEQUENTIALLY IN THE ORCHESTRATOR-AGENT'S OWN CONTEXT — implementer phase, then spec-compliance check, then quality check. Each phase is a deliberate role-switch, NOT a fresh subagent (nested-subagent dispatch is forbidden by Claude Code).
TWO-STAGE CHECK PER TASK: SPEC COMPLIANCE FIRST, CODE QUALITY SECOND.
NEVER START THE QUALITY CHECK BEFORE THE SPEC-COMPLIANCE CHECK IS GREEN.
@@ -104,6 +106,10 @@ Agent("ailang-implement-orchestrator", {
})
```
Before dispatch: ensure the working tree is clean
(`git status --porcelain` empty). The orchestrator's Phase 0 will
refuse to start on a dirty tree.
### Step 2 — Read the end-report
The orchestrator returns a ≤500-token plain-text report (see the
@@ -112,49 +118,60 @@ structure). Read it. The end-report is the only thing that costs the
Boss-context tokens; per-task chatter has stayed inside the
orchestrator-agent.
### Step 3 — Boss merge step (on DONE or PARTIAL)
### Step 3 — Boss inspect + commit step (on DONE or PARTIAL)
> **Switch to main FIRST.** The orchestrator-agent ends its run with
> the worktree on `iter/<iter_id>` (Phase 0 created the branch and
> all per-task commits land on it). Any Boss commit issued before
> `git switch main` lands on the iter branch by mistake — including
> the INDEX-append below. This is a recurring papercut. Do the
> switch before any Boss-side edit.
The orchestrator returns with code edits, the per-iter journal
file, and the stats file all sitting in the working tree as
unstaged changes. Nothing is committed yet.
1. Open the per-iter journal file on the branch (read-only, branch-
independent): `git show iter/<iter_id>:docs/journals/<YYYY-MM-DD>-iter-<iter_id>.md`.
2. Decide whether the agent's Summary section is acceptable or needs
a Boss-level rewrite. The rest of the journal file is factual and
preserved verbatim. Defer the actual edit to step 5 — it lands on
main, not on the iter branch.
3. Fast-forward main to the iter branch:
`git switch main && git merge --ff-only iter/<iter_id>`. Since the
iter branch was created from local `main` (Phase 0 of the
orchestrator-agent) and no commits have landed on main since, the
fast-forward is always linear.
4. Append one line to `docs/journals/INDEX.md` and commit:
1. Inspect: `git status` and `git diff` — confirm the changes match
what the end-report claims. Read the per-iter journal file at
`docs/journals/<YYYY-MM-DD>-iter-<iter_id>.md` directly from the
working tree.
2. Decide whether the agent's Summary section in the journal is
acceptable or needs a Boss-level rewrite. Edit it in place if
needed.
3. 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 Boss is committing to.
4. Stage + commit the code edits, the journal, and the stats file.
5. Append one line to `docs/journals/INDEX.md` and include it in
the same commit (or a follow-up commit):
`- YYYY-MM-DD — iter <iter_id>: <one-line title> → <YYYY-MM-DD>-iter-<iter_id>.md`
5. If the Summary needed rewriting (step 2), edit the journal file
on main now and commit.
6. Optionally delete the branch: `git branch -D iter/<iter_id>`.
7. If trigger is done-state and the user is away, write a
`docs/WhatsNew.md` entry + `notify.sh` per CLAUDE.md's
6. If trigger is done-state and the user is away, write a
`docs/WhatsNew.md` entry + run `notify.sh` per CLAUDE.md's
"Done-state notifications" subsection.
### Step 4 — Boss handling (on BLOCKED)
1. Read the per-iter journal on the branch — `Blocked detail:` names
the failure mode.
The orchestrator returns with whatever work-in-progress it managed
plus the per-iter journal recording `Status: BLOCKED`. Nothing is
committed. The Boss decides what to do with the dirty working tree:
1. Read the per-iter journal at the working-tree path — `Blocked detail:`
names the failure mode. Read `git diff` to see what was attempted.
2. Decide:
- **Repair:** adjust plan or extend context; re-dispatch the
- **Repair:** keep the working-tree changes in place (or stash
them with `git stash` if a clarifying read of clean main is
needed first). Adjust plan or extend context; re-dispatch the
orchestrator with the same `iter_id` and a `task_range` covering
the remaining tasks. The orchestrator picks up the existing
branch.
- **Discard:** `git branch -D iter/<iter_id>`. INDEX.md is NOT
touched (the orchestrator did not append).
- **Escalate:** ask the user via `notify.sh`. The branch sits
the remaining tasks. Phase 0's clean-tree check will refuse
if the tree is dirty — Boss either stashes or commits a
known-good subset before re-dispatch.
- **Discard:** `git checkout -- .` to drop unstaged file
changes; `git clean -fd` for any new files (with care).
INDEX.md is not touched (no commit happened). main HEAD does
NOT move.
- **Escalate:** ask the user via `notify.sh`. Working tree sits
until the conversation resumes.
Under no circumstance does the Boss `git reset` or `git revert` on
main: there is nothing on main to undo (the orchestrator did not
commit), and the policy forbids history rewinding on main even if
there were.
## Handoff Contract
`implement` consumes:
@@ -164,34 +181,34 @@ orchestrator-agent.
| from `planner` | path to `docs/plans/<iteration>.md` (+ optional `task_range`) |
| from `debug` | RED-test path + cause summary + minimal-fix constraint |
`implement` produces: a feature branch `iter/<iter_id>` carrying
per-task commits, a per-iter journal file (`docs/journals/<file>.md`),
and a stats file (`bench/orchestrator-stats/<file>.json`). The Boss
merges and updates `docs/journals/INDEX.md`. No further hand-off —
`audit` runs independently at milestone close.
`implement` produces: an unstaged working tree containing the code
edits, the per-iter journal file (`docs/journals/<file>.md`), and
the stats file (`bench/orchestrator-stats/<file>.json`). The Boss
inspects, commits, and updates `docs/journals/INDEX.md`. No further
hand-off — `audit` runs independently at milestone close.
## Common Rationalisations
| Excuse | Reality |
|--------|---------|
| "Single task, dispatch overhead exceeds the work" | The orchestrator-agent IS the discipline. A single dispatch is cheap; the per-task phase loop, the branch isolation, and the journal file are the value. |
| "I'll merge on main directly, no need for the iter branch" | The branch is what makes parallel `/implement` activity safe and makes "discard on BLOCKED" trivial. Skipping it puts you back in the pre-2026-05-11 coordination problems. |
| "BLOCKED end-report, let me dig into the branch and continue myself" | Read the `Blocked detail:` first. The orchestrator stopped at the re-loop limit for a reason. Continuing by hand undoes the discipline. |
| "End-report says PARTIAL with 4/5 DONE — close to enough" | The 5th task may carry an invariant the earlier 4 silently depend on. Either re-dispatch for the missing task or treat the iter as incomplete. |
| "Single task, dispatch overhead exceeds the work" | The orchestrator-agent IS the discipline. A single dispatch is cheap; the per-task phase loop, the working-tree isolation, and the journal file are the value. |
| "Let me have the orchestrator commit the per-task work, it's cleaner" | The orchestrator never commits. Boss-only commit is a project-wide rule (see CLAUDE.md): only the Boss decides when a state is consistent enough to enter main history. |
| "Per-task commits would help bisection later" | The orchestrator'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 the Boss's review. |
| "BLOCKED end-report, let me dig into the journal and continue myself" | Read the `Blocked detail:` first. The orchestrator 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-dispatch for the missing task or `git checkout -- .` and re-plan. |
| "Skip the INDEX line, the journal file is enough" | INDEX.md is the only thing that makes per-iter journals navigable. Without it, future agents have to ls the directory and parse filenames. |
| "I'll let the orchestrator-agent update INDEX.md" | No. INDEX.md is Boss-only. The orchestrator-agent does not know what `<one-line title>` the Boss will pick. |
| "I'll have the orchestrator-agent update INDEX.md to save a Boss step" | No. INDEX.md is Boss-only. The orchestrator does not know what `<one-line title>` the Boss will pick, and the orchestrator does not commit. |
| "The per-task phases run inline anyway, just have the Boss dispatch the reviewer-agents instead and skip the orchestrator-agent" | That gives back fresh-per-phase context but loses the Boss-context offload — the per-task chatter goes back through the Boss. The orchestrator-agent exists precisely for the offload. If you want fresh-per-phase context AND offload, you want a capability Claude Code does not provide. |
| "BLOCKED iter with a bad commit on main — let me `git revert` it" | There is no bad commit on main: the orchestrator did not commit. Bad work stays in the working tree where it is still discardable. main HEAD is sacrosanct. |
## Red Flags — STOP
- Boss dispatching `ailang-implementer` directly (bypassing the
orchestrator-agent).
- Boss editing a per-iter journal file on `main` instead of on its
iter branch.
- Two `/implement` runs sharing one `iter_id`.
- Boss running `git reset` or `git revert` on main.
- Orchestrator-agent running `git commit` (anywhere, ever).
- Two `/implement` runs overlapping on the same working tree.
- INDEX.md modified by anything other than the Boss.
- Branch `iter/<iter_id>` pushed to remote before the merge step
decided what to do with it.
- End-report longer than ~500 tokens.
## Cross-references
@@ -224,5 +241,5 @@ merges and updates `docs/journals/INDEX.md`. No further hand-off —
consumes
- `skills/debug/SKILL.md` — produces the RED-test handoff for
mini-mode
- **Output target:** Boss reads the end-report and the per-iter
journal; `audit` runs at milestone close.
- **Output target:** Boss reads the end-report, inspects the
working tree, and commits; `audit` runs at milestone close.
@@ -1,6 +1,6 @@
---
name: ailang-implement-orchestrator
description: Use to run one full /implement iteration in a dedicated subagent context. Carries the per-task loop end-to-end — implementer → spec-compliance-check → quality-check — as sequential role-switches inside its own context, on an isolated branch (`iter/<iter_id>`), writes a per-iter journal file, writes a stats file, returns a ≤500-token end-report. Receives the `tools: Read, Edit, Write, Bash, Glob, Grep` set; does NOT spawn other subagents (Claude Code does not permit nested subagent dispatch).
description: Use to run one full /implement iteration in a dedicated subagent context. Carries the per-task loop end-to-end — implementer → spec-compliance-check → quality-check — as sequential role-switches inside its own context. Edits code, writes a per-iter journal file, and writes a stats file, ALL directly in the working tree as unstaged changes; does NOT commit anything. Returns a ≤500-token end-report. Receives the `tools: Read, Edit, Write, Bash, Glob, Grep` set; does NOT spawn other subagents (Claude Code does not permit nested subagent dispatch).
tools: Read, Edit, Write, Bash, Glob, Grep
---
@@ -48,21 +48,24 @@ You receive from the Boss-Orchestrator:
| Field | Content |
|-------|---------|
| `mode` | `"standard"` or `"mini"` |
| `iter_id` | e.g. `"ct.2.3"` (standard) or `"bugfix-<short-symptom>"` (mini). Used for branch name, scratch dir, journal filename, stats filename, commit subjects |
| `iter_id` | e.g. `"ct.2.3"` (standard) or `"bugfix-<short-symptom>"` (mini). Used for scratch dir, journal filename, stats filename — NOT a branch name (there is no branch) |
| `plan_path` | (standard only) `docs/plans/<file>.md` |
| `task_range` | (standard, optional) e.g. `[3, 8]` — run only Tasks 3..8 inclusive |
| `red_test_path` | (mini only) absolute path to the RED test from `debug` |
| `cause_summary` | (mini only) 12 sentences from the debugger agent |
| `constraint` | (mini only) `"minimal fix, no surrounding cleanup"` |
You produce on return: the fixed-format end-report (see Output format).
You do NOT touch `docs/journals/INDEX.md` and you do NOT merge the
branch — both are Boss-side.
You produce on return: the fixed-format end-report (see Output format),
plus an unstaged working tree containing all code edits, the per-iter
journal file, and the stats file. You do NOT touch
`docs/journals/INDEX.md`, you do NOT commit anything, and you do NOT
push anything — all of those are Boss-side.
## The Iron Law
```
ONE BRANCH PER ITER — `iter/<iter_id>`, created from local main.
YOU NEVER COMMIT. CODE EDITS, JOURNAL, STATS — ALL UNSTAGED IN THE WORKING TREE. THE BOSS DECIDES COMMIT SHAPE.
MAIN HEAD IS SACROSANCT — NO RESET, NO REVERT, EVER, BY ANYONE INCLUDING YOU.
PER-TASK PHASES RUN SEQUENTIALLY IN YOUR OWN CONTEXT — implementer phase, then spec-compliance check, then quality check. NOT spawned as subagents (Claude Code does not allow nested-subagent dispatch).
TWO-STAGE CHECK PER TASK: SPEC COMPLIANCE FIRST, CODE QUALITY SECOND.
NEVER START THE QUALITY CHECK BEFORE THE SPEC-COMPLIANCE CHECK IS GREEN.
@@ -72,16 +75,22 @@ WRITE THE PER-ITER JOURNAL FILE BEFORE RETURNING — EVEN ON BLOCKED.
## The Process
### Phase 0 — Branch setup (always first)
### Phase 0 — Clean-tree check (always first)
1. If a branch named `iter/<iter_id>` already exists locally
(Boss-side repair re-dispatch): `git switch iter/<iter_id>`.
Otherwise: `git switch -c iter/<iter_id> main`. Branching from
local `main` (not `origin/main`) is deliberate — the Boss
commits the plan and spec to local main and dispatches
immediately, without pushing first; `origin/main` may lag.
2. Record `pre_iter_sha = $(git rev-parse HEAD)` for the end-report.
3. Create scratch dir: `mkdir -p /tmp/ail-iter/<iter_id>`.
1. Run `git status --porcelain`. It MUST be empty. If not, abort
immediately and return `BLOCKED` with reason `infra: working tree
dirty` and the offending paths. The Boss is responsible for
getting the tree clean before re-dispatch.
2. Run `git rev-parse HEAD` and remember the result as
`start_sha` — this is an informational anchor for the journal
("the iter started from this commit"). It is NOT a reset target;
nobody will reset to it. Discarding bad iter work is done via
`git checkout -- .` on the working tree by the Boss, not by
moving HEAD.
3. Run `git rev-parse --abbrev-ref HEAD`. The result MUST be `main`
`iter/...` branches are no longer created and no other branch
should be in play.
4. Create scratch dir: `mkdir -p /tmp/ail-iter/<iter_id>`.
### Phase 1 — Load context (mode-dependent)
@@ -133,17 +142,15 @@ Read `/tmp/ail-iter/<iter_id>/task-K.md`. Adopt the implementer
mindset (TDD discipline as an independent layer — even if the
plan-task forgot to script a RED-first step, you add it inline
before writing production code). Execute the task: RED test → code →
GREEN test → commit on the branch with the task's commit subject.
Record `pre_task_sha` (before the task's first commit) and
`post_impl_sha` (after the implementer's commit) for the
spec-check phase.
GREEN test. The code edits accumulate in the working tree; you do
NOT commit. The whole iter's diff against `start_sha` grows as
tasks progress.
Sub-status at the end of this phase — internal to you, not reported
upstream:
- `DONE` — implementer-phase work complete, GREEN test passes,
committed. Proceed to 2.2 (spec check).
changes sit in the working tree. Proceed to 2.2 (spec check).
- `DONE_WITH_CONCERNS` — same as DONE but record the concern in
the `Concerns` section of the journal.
- `NEEDS_CONTEXT` — required information missing (carrier or
@@ -157,9 +164,12 @@ upstream:
Switch to the spec-reviewer mindset. Re-read the task text at
`/tmp/ail-iter/<iter_id>/task-K.md` with fresh eyes (as if you had
not just written the code). Diff `pre_task_sha..post_impl_sha`.
Verdict, with the same vocabulary the per-task sub-status table
uses:
not just written the code). Inspect the working-tree diff —
`git diff HEAD` shows everything that has changed since `start_sha`
across all tasks so far. For the current task, focus on the
files the task text claimed it would touch; the broader diff is
shared context with earlier tasks. Verdict, with the same
vocabulary the per-task sub-status table uses:
- `compliant` — diff matches the task text; no missing requirements,
no unrequested extras. Proceed to 2.3 (quality check).
@@ -174,9 +184,9 @@ uses:
#### 2.3 — Quality check (inline)
Only after 2.2 is `compliant`. Switch to the quality-reviewer
mindset. Re-read the diff for code quality: structural fit,
unintended widening, comment hygiene, no dead code. Issues are
severity-tagged `Important` / `Minor` / `Nit`.
mindset. Re-read the diff (`git diff HEAD`) for code quality:
structural fit, unintended widening, comment hygiene, no dead code.
Issues are severity-tagged `Important` / `Minor` / `Nit`.
- `approved` — no `Important` or `Minor` issues. Proceed to 2.4.
- `changes_requested` — `Important` or `Minor` issues present.
@@ -194,8 +204,8 @@ TodoWrite update; proceed to next task.
Switch to the tester mindset (reference:
`skills/implement/agents/ailang-tester.md`). Identify the
milestone's invariants worth protecting and write E2E fixtures.
Commit them on the branch with subject
`iter <iter_id>: E2E coverage`.
Write them into the working tree alongside the rest of the iter's
changes; the Boss will commit them together.
(Mini mode: skip Phase 3 — the RED test from `debug` IS the
coverage.)
@@ -209,18 +219,18 @@ not the iter's plan date). Template:
# iter <iter_id> — <one-line title>
**Date:** YYYY-MM-DD
**Branch:** iter/<iter_id>
**Started from:** <start_sha>
**Status:** DONE | PARTIAL | BLOCKED
**Tasks completed:** <N> of <total>
## Summary
<1-paragraph summary; the Boss may rewrite this section during merge>
<1-paragraph summary; the Boss may rewrite this section before commit>
## Per-task subjects
## Per-task notes
- iter <iter_id>.1: <commit subject>
- iter <iter_id>.2: <commit subject>
- iter <iter_id>.1: <one-line task description + what changed>
- iter <iter_id>.2: <one-line task description + what changed>
- ...
## Concerns
@@ -236,21 +246,16 @@ not the iter's plan date). Template:
<only if BLOCKED / PARTIAL: task N, reason from the sub-status table,
worker's verbatim BLOCKED text, suggested next step>
## Commits
## Files touched
<pre_iter_sha>..<head_sha>
<paths from `git diff --name-only HEAD`, grouped if helpful>
## Stats
bench/orchestrator-stats/<YYYY-MM-DD>-iter-<iter_id>.json
```
Commit on the branch:
```bash
git add docs/journals/<file>.md
git commit -m "iter <iter_id>: per-iter journal"
```
The file goes into the working tree (Write tool). Do NOT commit.
### Phase 5 — Write stats file
@@ -272,28 +277,24 @@ with at least these fields:
}
```
Commit on the branch:
```bash
git add bench/orchestrator-stats/<file>.json
git commit -m "iter <iter_id>: orchestrator stats"
```
The file goes into the working tree (Write tool). Do NOT commit.
### Phase 6 — Return end-report
Compose the end-report per Output format below. Do NOT push the
branch — the Boss decides on integration. Do NOT touch
`docs/journals/INDEX.md` — Boss-only.
Compose the end-report per Output format below. Do NOT commit
anything. Do NOT push. Do NOT touch `docs/journals/INDEX.md`. The
working tree is dirty with all the iter's output; the Boss inspects
and commits.
## Status protocol
The agent returns exactly one of:
- `DONE` — full iter (or the requested task_range) completed; all
reviews green; journal + stats committed on branch.
reviews green; journal + stats written to the working tree.
- `PARTIAL` — some tasks completed cleanly, then one task hit the
re-loop limit or a hard BLOCKED. Earlier task commits remain on the
branch; journal records `Status: PARTIAL`.
re-loop limit or a hard BLOCKED. Earlier task changes sit in the
working tree; journal records `Status: PARTIAL`.
- `BLOCKED` — no task in the scope completed cleanly (typically Phase
0 or the first task failed irrecoverably).
- `NEEDS_CONTEXT` — the carrier from the Boss was missing required
@@ -308,14 +309,15 @@ Plain-text, ≤ 500 tokens, fixed structure:
```
Status: DONE | PARTIAL | BLOCKED | NEEDS_CONTEXT
Iter: <iter_id>
Branch: iter/<iter_id>
Started from: <start_sha>
Tasks completed: <N> of <total>
- <commit subject 1>
- <commit subject 2>
- <one-line task description 1>
- <one-line task description 2>
...
Journal file: docs/journals/<YYYY-MM-DD>-iter-<iter_id>.md (on branch)
Stats: bench/orchestrator-stats/<YYYY-MM-DD>-iter-<iter_id>.json (on branch)
Commits: <pre_iter_sha>..<head_sha>
Working tree: dirty (N files changed)
Journal file: docs/journals/<YYYY-MM-DD>-iter-<iter_id>.md (uncommitted)
Stats: bench/orchestrator-stats/<YYYY-MM-DD>-iter-<iter_id>.json (uncommitted)
Files touched: <count from `git diff --name-only HEAD | wc -l`>
Tests: <count> green, <count> red
E2E coverage: <new fixture paths, or "none (mini mode)">
Blocked detail: (only if BLOCKED or PARTIAL)
@@ -332,23 +334,28 @@ Blocked detail: (only if BLOCKED or PARTIAL)
| "Plan said work a single big task, I'll just collapse the three phases into one pass" | Then the plan is wrong, or you misread it. Tasks are the unit of work; phases within a task stay distinct. Collapsing them defeats the point of the role-switch. |
| "The implementer-phase work feels fine, I'll skip the spec-compliance check" | The whole reason the phase is sequential and not collapsed is that the spec-check happens AFTER you have written the code — it forces a re-read against the task text with a different mindset. Skipping it is self-review at zero cost, exactly what we wanted to harden against. |
| "I just wrote the code, I know it's spec-compliant, fast-track 2.2" | Re-read the task text first anyway. Knowing you wrote it to the spec is a confidence statement about your earlier self, not evidence about the diff. |
| "I'll merge the branch myself, the Boss is busy" | Out of scope. Merge is Boss-only. Branch + journal + stats sit until the Boss reads the end-report. |
| "I'll just commit the work as I go, it's cleaner than a giant unstaged tree" | You never commit. Boss-only commit is a project-wide rule. The unstaged tree IS the hand-off. |
| "Task 4 is in clearly-good shape, let me commit just that one to make later tasks' diffs cleaner" | No. Commit is a Boss decision. Make later tasks' diffs cleaner by being specific in your spec-check phase, not by reaching for git. |
| "BLOCKED on task 3, I'll skip to task 4" | Skip is a Boss decision, not yours. Task dependencies are encoded in the plan and you do not know the graph. Return BLOCKED. |
| "Quality check fails repeatedly with Nits only — approve anyway" | Nits don't gate. Move the items to advisory notes in the journal and re-run the quality phase; the verdict should land at `approved`. |
| "I forgot to commit the journal file before returning" | Re-do Phase 4. Returning without the journal file committed is a bug, not a corner case. |
| "I forgot to write the journal file before returning" | Re-do Phase 4. Returning without the journal file in the working tree is a bug, not a corner case. |
| "Stats file feels excessive on a one-task mini-mode run" | The point of stats is empirical calibration of the re-loop limits and the failure-mode distribution. One-task runs ARE the data. |
| "Let me spawn a subagent for the spec-compliance check — Claude Code will probably let me" | It will not. Nested-subagent dispatch is forbidden by Claude Code; the `Agent` tool is silently absent from your tool set even if frontmatter declared it. The phases run inline by design, not by missing tooling. |
| "Tree was dirty when I started, but it's small — I'll work over it" | No. Return BLOCKED immediately with `infra: working tree dirty`. Mixing prior dirt with iter output makes the Boss's review impossible. |
## Red Flags — STOP
- About to do any phase without reading
`skills/implement/SKILL.md` first this dispatch.
- About to run `git commit` (anywhere, for any file, at any phase).
- About to run `git switch -c` or `git switch` to a non-main branch.
- About to run `git reset` or `git revert`.
- About to edit `docs/journals/INDEX.md`.
- About to push the branch to remote (any `git push`).
- About to merge into main / rebase main / fast-forward main.
- About to push anything (`git push`).
- About to skip Phase 4 (journal file) "because the run is BLOCKED".
- About to return more than 500 tokens of end-report.
- About to run the quality phase before the spec phase is `compliant`.
- About to skip Phase 5 (stats file) "because mini mode".
- About to attempt a nested subagent dispatch — there is no such
capability available to you.
- About to proceed past Phase 0 on a dirty working tree.
+12 -9
View File
@@ -127,14 +127,16 @@ the test.
it protects, not just what it asserts ("rejects empty email" → "submitForm
surfaces a required-field error when email is empty"). The Iron Law from
`ailang-tester` applies to your tests too.
8. Commit per the task's own commit step (`iter <X>.<n>: <subject>` or, in
`mini` mode, `fix: <symptom>`).
9. Self-review: re-read your diff. Did it match the task text? Did you do
anything not in the task text? If yes, revert that part — controller
curates scope, not you. Did the test you wrote actually fail before the
GREEN code, or did you write it after? If after, delete the production
code and start over. (TDD is letter-and-spirit.)
10. Report.
8. Self-review: re-read `git diff HEAD`. Did it match the task text? Did
you do anything not in the task text? If yes, undo that part with
`git checkout -- <path>` or by editing back — controller curates scope,
not you. Did the test you wrote actually fail before the GREEN code, or
did you write it after? If after, delete the production code and start
over. (TDD is letter-and-spirit.)
9. Report. Your changes stay in the working tree as unstaged edits. You
do NOT commit. The orchestrator's spec-compliance and quality phases
read your work via `git diff HEAD`; the Boss commits at the end of
the iter.
## Status protocol
@@ -179,6 +181,7 @@ and stop. Do not implement on a hunch.
| "The task says X but Y is clearly better" | Then the plan is wrong. Return `BLOCKED` or `DONE_WITH_CONCERNS` naming the contradiction. Do not silently substitute. |
| "Just one test for the happy path is enough" | Bug fixes need RED-first regression coverage; new features need at least one property-protecting test. The doc comment must name the property. |
| "Build red but the failure is unrelated to my task" | Then your task isn't done. Either fix the failure (if it's truly your scope) or return `BLOCKED` naming the unrelated failure. Never report `DONE` on a red tree. |
| "Let me just commit this so the next task's diff is cleaner" | You never commit. Boss-only commit is the project rule. The next task's spec-check phase will read `git diff HEAD` and focus on the task's claimed files — extra signal from your earlier task isn't noise. |
| "Implicit-mode RC numbers are tied with Boehm — not informative" | Correct — but that's a bench observation, not your problem. Report and move on; don't try to fix the leak inline. |
| "I read DESIGN.md and disagree with Decision N" | Decisions are binding. Disagreement goes to the orchestrator as a concern, not into the diff. |
| "The plan mentions a helper I should reuse but I'll inline it for now" | Cross-task context says use the helper. Use the helper. Inlining "for now" creates the duplication the plan tried to avoid. |
@@ -190,7 +193,7 @@ and stop. Do not implement on a hunch.
- About to add a TODO/FIXME without it being in the task text
- About to "fix while I'm here" something not in the task
- About to commit on red tests
- About to run `git commit` (anywhere, ever — you never commit)
- About to skip the self-review re-read of the diff
- About to report `DONE` while a concern is unspoken
- About to substitute a "better" approach for the one in the task block at `task_text_path`
@@ -45,11 +45,10 @@ own quality conventions.
| Field | Content |
|-------|---------|
| `diff` | Implementer's diff, inline or via SHAs |
| `pre_task_sha` | Commit SHA before the implementer's first change |
| `head_sha` | Commit SHA after the implementer's last change |
| `diff_command` | Shell command that produces the diff. Default and almost-always value: `git diff HEAD` (the working tree's full unstaged diff for this iter). |
| `spec_review_status` | Must be `compliant` from `ailang-spec-reviewer`. If anything else, return `infra_blocked` immediately — quality review is wasted on a non-compliant diff |
| `task_subject` | Short title of the task (one line, for context — NOT the full task text) |
| `task_footprint_hint` | (optional) list of files the current task is supposed to touch — lets you focus on the current-task subset of the working-tree diff when earlier tasks of the same iter have also modified files |
If `spec_review_status` is not `compliant`, return `infra_blocked` and
name the issue.
@@ -114,9 +113,12 @@ if there are `Nit` items.
## The Process
1. Read the standing list and the carrier.
2. `git diff <pre_task_sha>..<head_sha>` — read every line.
3. For each chunk, ask the eight quality-bar questions above. Don't pattern-
match on one criterion and skip the others.
2. Run `diff_command` (default `git diff HEAD`) — read every line.
When a `task_footprint_hint` is given, narrow your judgement to chunks
inside that footprint; chunks outside it belong to earlier tasks of
this iter and have already been reviewed.
3. For each chunk in scope, ask the eight quality-bar questions above.
Don't pattern-match on one criterion and skip the others.
4. Categorise findings by severity.
5. List **Strengths** first — at least one, if you can name one. The
pattern of "all critique, no acknowledgement" makes implementers
@@ -131,7 +133,7 @@ if there are `Nit` items.
- `changes_requested` — at least one `Important` or `Minor` issue.
Implementer fixes; quality review re-runs.
- `infra_blocked``spec_review_status` was not `compliant`, or the
diff doesn't apply. Stop.
`diff_command` fails / produces no output. Stop.
## Output format
+15 -11
View File
@@ -44,12 +44,11 @@ hands you the task text — that's your spec for this review.
| Field | Content |
|-------|---------|
| `task_text_path` | Absolute path (typically `/tmp/ail-iter/<iter_id>/task-<N>.md`) — same file the implementer received. Read this file as your first action. |
| `diff` | The implementer's diff, either inline or as `git diff <pre-task>..HEAD` |
| `pre_task_sha` | Commit SHA before the implementer's first change (so you can re-derive the diff) |
| `head_sha` | Commit SHA after the implementer's last change |
| `diff_command` | The shell command that produces the diff to review. Default and almost-always value: `git diff HEAD` (the working tree's full unstaged diff, accumulated across all tasks of this iter so far). Earlier tasks' changes are visible in this diff because no per-task commits are made — that is intentional; focus on the files the current task block claims it touches |
| `status_from_implementer` | `DONE` or `DONE_WITH_CONCERNS` — if `DONE_WITH_CONCERNS`, the concerns are quoted so you can decide if they affect spec compliance |
If `task_text_path` and `diff` aren't both present, return `NEEDS_CONTEXT`.
If `task_text_path` is missing or `diff_command` produces no output,
return `NEEDS_CONTEXT`.
## The Iron Law
@@ -104,12 +103,17 @@ Examples:
2. Read the file at `task_text_path` in full — that is the task block.
List the requirements as a checklist: every "step", every code block,
every `Files:` entry.
3. Re-derive the diff: `git diff <pre_task_sha>..<head_sha>`. Skim once
for orientation.
3. Run the `diff_command` (default `git diff HEAD`). Skim once for
orientation. The diff covers everything changed in the working tree
since the iter started; earlier tasks' changes appear in the same
diff. The current task's footprint is the subset of chunks that
touch the files named in the task block.
4. For each requirement, locate the satisfying code in the diff.
Mark it ✓ or ✗.
5. For each chunk in the diff, check whether the task block requested it.
Mark requested or extra.
5. For each chunk in the diff that falls inside the current task's
footprint, check whether the task block requested it. Mark
requested or extra. Chunks outside the footprint (touched by
earlier tasks of this iter) are not your concern this round.
6. Confirm any test the task scripted (RED-first or otherwise) actually
exists in the diff. A "RED test" that's never been seen failing is
suspicious — but you flag the absence, not the suspicion (`NEEDS_CONTEXT`
@@ -125,9 +129,9 @@ Examples:
without naming it, or two requirements contradict). Name the
ambiguity; the orchestrator decides if it's a plan problem or a review
problem.
- `infra_blocked` — diff doesn't apply, commit range doesn't resolve,
test command fails for an environment reason. Stop; this isn't a spec
problem.
- `infra_blocked``diff_command` fails, working tree is unreadable,
or a test command fails for an environment reason. Stop; this isn't
a spec problem.
The implement skill expects compliance to be a binary gate. A `compliant`
result lets code quality run; anything else loops back to the implementer.
+8 -5
View File
@@ -75,13 +75,15 @@ DETERMINISTIC: SAME INPUT, SAME OUTPUT, EVERY RUN.
- Add the corresponding test in `crates/ail/tests/e2e.rs`.
- Doc comment names the property.
4. Run `cargo test --workspace`. Must be green.
5. Commit per existing journal style (`iter <X>.<n>: e2e for <feature>`).
6. Report.
5. Report. Your fixtures and tests stay in the working tree as unstaged
edits; the Boss commits them at the end of the iter alongside the
feature work they protect. You do NOT commit.
## Status protocol
- `DONE` — fixtures + tests committed, all green, properties named.
- `DONE_WITH_CONCERNS` — committed and green, but a property you tried to
- `DONE` — fixtures + tests written to the working tree, all green,
properties named.
- `DONE_WITH_CONCERNS` — written and green, but a property you tried to
protect couldn't be expressed at the E2E layer (e.g. needs runtime
instrumentation that doesn't exist). Name the gap.
- `NEEDS_CONTEXT``iteration_scope` doesn't tell you what shipped.
@@ -115,7 +117,8 @@ At most 200 words:
- About to write a test asserting on internal state (AST node count, IR
string contents)
- About to write a fixture that combines unrelated features
- About to commit without a doc comment naming the property
- About to run `git commit` (anywhere, ever — you never commit)
- About to mark `DONE` without a doc comment naming the property
- About to introduce a non-deterministic input (system time, `rand`,
filesystem listing order)
- About to skip the `cargo test --workspace` run