pi-gauntlet 7.20.1 → 7.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +2 -2
- package/agents/code-reviewer.md +0 -2
- package/extensions/lib/plan-check.test.ts +9 -16
- package/extensions/lib/plan-check.ts +1 -32
- package/package.json +1 -1
- package/skills/dispatching-parallel-agents/SKILL.md +8 -2
- package/skills/gauntlet-resume/reference/brief-contract.md +1 -1
- package/skills/requesting-code-review/SKILL.md +5 -6
- package/skills/subagent-driven-development/SKILL.md +44 -48
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +5 -5
- package/skills/subagent-driven-development/stop-note.md +1 -1
- package/skills/verification-before-completion/reference/conformance-check.md +2 -2
- package/skills/writing-plans/SKILL.md +7 -17
- package/skills/writing-plans/reference/plan-contract.md +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v7.21.0 - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Removed sequential mode; every plan task runs through the wave loop.** Singleton waves use the same implementer/spec-review chain, integration, review, and accepted-wave commit as larger waves. Re-review fixes use a single tree-to-tree fix delta captured through a scratch index, so committed, staged, and unstaged repairs produce the same repair-only patch. Each accepted wave folds repair commits into one wave commit before tracker completion. (#71)
|
|
8
|
+
|
|
3
9
|
## v7.20.1 - 2026-10-19
|
|
4
10
|
|
|
5
11
|
### Changed
|
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ Run by a human against pi-cohort >= 7.1.0, before a pi-gauntlet release claims t
|
|
|
110
110
|
| Gate | A machine-enforced checkpoint between phases (e.g. within a brainstorming-entered flow, `complete verify` is blocked until conformance review has run). Not a suggestion. |
|
|
111
111
|
| Spec council | Multi-model critique of the spec before you see it (`roasting-the-spec`); members and chair return saved-file references, and a fresh applier inherits the main loop's model and thinking unless overridden, edits only the spec, and returns the audit. The main loop checks structure without ingesting member/chair findings, re-reads the spec, and hands it to the unchanged user gate to ratify or revert. Members flag unrelated extras or sufficient in-scope simplifications (`over-spec`); every member closes with a `lean:` line - "nothing to cut" is a first-class answer - and the chair tallies them (`lean: <k> of <n> members found nothing to cut`). Falls back to a single-model critique if no council is configured. |
|
|
112
112
|
| Conformance gate | The closing check: does the delivered code + docs match your *original prompt*, not the derived plan? Compatible executable recommendations auto-fix first; anything still open renders as a numbered decision brief - one item per decision, the problem in plain words first, lettered options each with a one-line consequence, and `Recommended: <letter> - <reason>` or `No recommendation - pick one`. Reply with one line per item (`1. B`), or bare `1` to take every recommendation (no-recommendation items are re-asked by number); a current `CONFORMS` / no-concerns handoff goes straight to branch options with no extra sign-off. |
|
|
113
|
-
| Wave | A batch of plan tasks
|
|
113
|
+
| Wave | A batch of N >= 1 plan tasks with disjoint files, run through the one wave loop; N > 1 dispatches concurrently |
|
|
114
114
|
| Overrides file | `.pi/gauntlet-overrides.md` - where you put project-specific detail the generic skills don't know (CI command, worktree wrapper, routing rules). |
|
|
115
115
|
|
|
116
116
|
## When to use / when NOT to use
|
|
@@ -122,7 +122,7 @@ Run by a human against pi-cohort >= 7.1.0, before a pi-gauntlet release claims t
|
|
|
122
122
|
## Requirements
|
|
123
123
|
|
|
124
124
|
- [pi-coding-agent](https://github.com/earendil-works/pi) ≥ 0.85.1 - tested minimum for sequential `phase_tracker` tool execution, which persists a tracker update before a later tool call in the same model message.
|
|
125
|
-
- [pi-cohort](https://github.com/jjuraszek/pi-cohort) >= 7.4.1 - required peer package, including binary-safe worktree patches. Skills that dispatch agents (`requesting-code-review`, `subagent-driven-development`, `dispatching-parallel-agents`, `writing-plans`, `forge-skill`, `shape-ticket`, `roasting-the-spec`) call `subagent({})`, which pi-cohort provides. pi-gauntlet does not vendor the dispatch tool; without pi-cohort those skills have nothing to call. Gauntlet execution dispatches explicitly use `async: false`; do not enable pi-cohort's incompatible `forceTopLevelAsync` setting. See pi-cohort's [dispatch configuration](https://github.com/jjuraszek/pi-cohort/blob/main/doc/configuration.md) for that setting's owner and semantics.
|
|
125
|
+
- [pi-cohort](https://github.com/jjuraszek/pi-cohort) >= 7.4.1 - required peer package, including binary-safe worktree patches. Skills that dispatch agents (`requesting-code-review`, `subagent-driven-development`, `dispatching-parallel-agents`, `writing-plans`, `forge-skill`, `shape-ticket`, `roasting-the-spec`) call `subagent({})`, which pi-cohort provides. pi-gauntlet does not vendor the dispatch tool; without pi-cohort those skills have nothing to call. Gauntlet execution dispatches explicitly use `async: false`; do not enable pi-cohort's incompatible `forceTopLevelAsync` setting. See pi-cohort's [dispatch configuration](https://github.com/jjuraszek/pi-cohort/blob/main/doc/configuration.md) for that setting's owner and semantics. A wave of N >= 1 tasks dispatches its implementers and first spec reviews as one chain call.
|
|
126
126
|
|
|
127
127
|
Both packages must be listed in your `.pi/settings.json#packages` array (pi adds them automatically when you `pi install`). pi-gauntlet and pi-cohort are versioned independently but release together whenever dispatch semantics change - pin compatible versions of both.
|
|
128
128
|
|
package/agents/code-reviewer.md
CHANGED
|
@@ -77,8 +77,6 @@ with one partition line over the `Fn` IDs assigned above; when a task requires
|
|
|
77
77
|
a trailing `TRAJECTORY:` verdict (re-review), that verdict comes after `Behaviour-change:` as the
|
|
78
78
|
true final line:
|
|
79
79
|
|
|
80
|
-
<!-- writing-plans' plan-time Parallel-safe: line is a deliberately different free-text form; do not unify -->
|
|
81
|
-
|
|
82
80
|
```
|
|
83
81
|
Parallel-safe: <group>[; <group>]*
|
|
84
82
|
<group> = <comma-separated finding-id list> " disjoint"
|
|
@@ -62,8 +62,6 @@ This task handles naming details.
|
|
|
62
62
|
|
|
63
63
|
## Wave 2 — Solo task
|
|
64
64
|
|
|
65
|
-
Solo: lone remaining task
|
|
66
|
-
|
|
67
65
|
### Task 3: Document banned token handling
|
|
68
66
|
|
|
69
67
|
**Spec:** doc/specs/fixture-spec.md § "Testing" L9
|
|
@@ -520,8 +518,6 @@ const ENTRYPOINT_PLAN = `# Fixture Plan
|
|
|
520
518
|
|
|
521
519
|
## Wave 1 — Solo
|
|
522
520
|
|
|
523
|
-
Solo: lone remaining task
|
|
524
|
-
|
|
525
521
|
### Task 1: Scoped test
|
|
526
522
|
|
|
527
523
|
**Spec:** doc/specs/fixture-spec.md § "Testing" L4
|
|
@@ -698,13 +694,14 @@ test("check 6 wave-file-disjointness: two tasks in the same wave declare the sam
|
|
|
698
694
|
);
|
|
699
695
|
});
|
|
700
696
|
|
|
701
|
-
test("
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
assert.
|
|
707
|
-
|
|
697
|
+
test("every-wave-singleton plan without Solo: lines passes; legacy Solo: lines are accepted silently", () => {
|
|
698
|
+
const plan = VALID_PLAN.replace(
|
|
699
|
+
"### Task 2: Implement naming",
|
|
700
|
+
"## Wave 2 - Naming\n\n### Task 2: Implement naming",
|
|
701
|
+
).replace("## Wave 2 — Solo task", "## Wave 3 - Documentation");
|
|
702
|
+
assert.deepEqual(checkPlan(plan, SPEC_TEXT, alwaysTruePort()), []);
|
|
703
|
+
const legacy = plan.replace("## Wave 2 - Naming", "## Wave 2 - Naming\n\nSolo: Task 2 depends on Wave 1");
|
|
704
|
+
assert.deepEqual(checkPlan(legacy, SPEC_TEXT, alwaysTruePort()), []);
|
|
708
705
|
});
|
|
709
706
|
|
|
710
707
|
test("check 8 header-entrypoint: verification entrypoint string reused outside the header", () => {
|
|
@@ -746,8 +743,6 @@ const EXEMPTION_PLAN = `# Exemption Plan
|
|
|
746
743
|
|
|
747
744
|
## Wave 1 — Solo task
|
|
748
745
|
|
|
749
|
-
Solo: only task in this wave
|
|
750
|
-
|
|
751
746
|
### Task 1: Document banned token handling
|
|
752
747
|
|
|
753
748
|
**Spec:** doc/specs/exemption-spec.md § "Notes" L4
|
|
@@ -799,9 +794,8 @@ test("check 2/5 exemption negative: without the task body carrying the literal,
|
|
|
799
794
|
);
|
|
800
795
|
});
|
|
801
796
|
|
|
802
|
-
test("aggregate: independent mutations across
|
|
797
|
+
test("aggregate: independent mutations across two checks are all reported together", () => {
|
|
803
798
|
let mutated = VALID_PLAN;
|
|
804
|
-
mutated = mutated.replace("Solo: lone remaining task\n\n", "");
|
|
805
799
|
mutated = mutated.replace(
|
|
806
800
|
"- via: `helperFn()`\n\nThis task implements helperFn() for parsing.",
|
|
807
801
|
"- via: `parserSeam()`\n\nThis task implements the helper for parsing.",
|
|
@@ -811,7 +805,6 @@ test("aggregate: independent mutations across three checks are all reported toge
|
|
|
811
805
|
"- Modify: extensions/lib/fixture-shared.ts",
|
|
812
806
|
);
|
|
813
807
|
const findings = checkPlan(mutated, SPEC_TEXT, alwaysTruePort());
|
|
814
|
-
assert.ok(findingsFor(findings, "solo-line").length > 0, "expected a solo-line finding");
|
|
815
808
|
assert.ok(findingsFor(findings, "quote-integrity").length > 0, "expected a quote-integrity finding");
|
|
816
809
|
assert.ok(findingsFor(findings, "wave-file-disjointness").length > 0, "expected a wave-file-disjointness finding");
|
|
817
810
|
});
|
|
@@ -67,8 +67,6 @@ interface Wave {
|
|
|
67
67
|
line: number;
|
|
68
68
|
text: string;
|
|
69
69
|
label: string;
|
|
70
|
-
soloLine: number | undefined;
|
|
71
|
-
soloReason: string | undefined;
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
interface CoverageRow {
|
|
@@ -188,18 +186,7 @@ function parsePlan(planText: string): ParsedPlan {
|
|
|
188
186
|
if (mask[i]) continue;
|
|
189
187
|
const m = waveRe.exec(lines[i]);
|
|
190
188
|
if (!m) continue;
|
|
191
|
-
|
|
192
|
-
let soloReason: string | undefined;
|
|
193
|
-
let j = i + 1;
|
|
194
|
-
while (j < lines.length && lines[j].trim() === "") j++;
|
|
195
|
-
if (j < lines.length) {
|
|
196
|
-
const sm = /^Solo: (.+)$/.exec(lines[j]);
|
|
197
|
-
if (sm) {
|
|
198
|
-
soloLine = j + 1;
|
|
199
|
-
soloReason = sm[1];
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
waves.push({ number: Number(m[1]), line: i + 1, text: lines[i], label: m[3], soloLine, soloReason });
|
|
189
|
+
waves.push({ number: Number(m[1]), line: i + 1, text: lines[i], label: m[3] });
|
|
203
190
|
}
|
|
204
191
|
|
|
205
192
|
const taskHeaderIdxs: number[] = [];
|
|
@@ -944,23 +931,6 @@ function globErrFinding(wave: Wave, pattern: string, err: Error): PlanCheckFindi
|
|
|
944
931
|
};
|
|
945
932
|
}
|
|
946
933
|
|
|
947
|
-
function checkSoloLine(parsed: ParsedPlan): PlanCheckFinding[] {
|
|
948
|
-
const findings: PlanCheckFinding[] = [];
|
|
949
|
-
for (const wave of parsed.waves) {
|
|
950
|
-
const count = parsed.tasks.filter((t) => t.waveNumber === wave.number).length;
|
|
951
|
-
if (count !== 1) continue;
|
|
952
|
-
if (wave.soloLine === undefined || !wave.soloReason || wave.soloReason.trim().length === 0) {
|
|
953
|
-
findings.push({
|
|
954
|
-
check: "solo-line",
|
|
955
|
-
line: wave.line,
|
|
956
|
-
text: wave.text,
|
|
957
|
-
reason: `single-task Wave ${wave.number} is missing a 'Solo: <reason>' line directly under its header`,
|
|
958
|
-
});
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
return findings;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
934
|
function checkHappyPathLine(parsed: ParsedPlan): PlanCheckFinding[] {
|
|
965
935
|
const { happyPathLine, happyPathText, happyPathCommand } = parsed.header;
|
|
966
936
|
if (happyPathLine === undefined || happyPathCommand !== undefined) return [];
|
|
@@ -1090,7 +1060,6 @@ export function checkPlan(planText: string, specText: string, fs: FsPort): PlanC
|
|
|
1090
1060
|
const requiredLiterals = computeRequiredLiteralsPerTask(parsed, specLines);
|
|
1091
1061
|
findings.push(...checkPlaceholderScan(parsed, requiredLiterals));
|
|
1092
1062
|
findings.push(...checkWaveFileDisjointness(parsed, fs));
|
|
1093
|
-
findings.push(...checkSoloLine(parsed));
|
|
1094
1063
|
findings.push(...checkHeaderEntrypoint(parsed));
|
|
1095
1064
|
findings.push(...checkHappyPathLine(parsed));
|
|
1096
1065
|
findings.push(...checkWaiverLiteral(parsed));
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@ When you have multiple **independent tasks** — unrelated test failures, or imp
|
|
|
13
13
|
|
|
14
14
|
**Core principle:** Dispatch one agent per independent problem domain (one bug, one subsystem, or one plan task). Let them work concurrently.
|
|
15
15
|
|
|
16
|
-
This skill is the **mechanic home** for parallel fan-out: fresh-context isolation, `worktree: true` filesystem isolation, and serial patch integration. `subagent-driven-development`'s
|
|
16
|
+
This skill is the **mechanic home** for parallel fan-out: fresh-context isolation, `worktree: true` filesystem isolation, and serial patch integration. `subagent-driven-development`'s wave loop builds its per-wave dispatch on this skill - debugging is the worked example below, but the mechanics are identical for implementation tasks.
|
|
17
17
|
|
|
18
18
|
Gauntlet flow callers dispatch foreground: explicitly set top-level `async: false`, including retries and prose-described dispatches. `forceTopLevelAsync` is incompatible; if an async handle is returned anyway, stop and report rather than polling or relaunching. See [pi-cohort dispatch configuration](https://github.com/jjuraszek/pi-cohort/blob/main/doc/configuration.md).
|
|
19
19
|
|
|
@@ -113,7 +113,13 @@ Fix rounds in review loops reuse the fan-out mechanics above, keyed off the revi
|
|
|
113
113
|
|
|
114
114
|
Reviewers certify fix concurrency with a `Parallel-safe:` line (see the reviewer's report contract). The fan-out trigger is a `disjoint` group naming **≥ 2 actionable finding IDs**: that group IS the parallel wave — dispatch **one `implementer` per actionable finding ID in the group (the probe's actionable set; the consuming loop places non-actionable findings)** (`context: "fresh"`, `worktree: true`, `cwd` = the current worktree; task = that finding's block **verbatim**, including its `touched-files` line as the ownership boundary). An actionable finding named in any `conflicts` pair runs sequentially after every finding it names has integrated (chained `conflicts` define a partial order; remaining serial findings run in the line's order). Actionable findings outside any ≥ 2-ID `disjoint` group run sequentially. Fan out per review line only — never merge or co-schedule groups from different `Parallel-safe:` lines; run those fan-outs serially.
|
|
115
115
|
|
|
116
|
-
**Precondition:**
|
|
116
|
+
**Precondition:** select the review source below before dispatching fixes.
|
|
117
|
+
|
|
118
|
+
| Review source | Fix dispatch |
|
|
119
|
+
|---|---|
|
|
120
|
+
| Clean committed HEAD containing the code under review (verify-phase whole-diff review or conformance loop) | Fan out from that HEAD when the certificate permits. |
|
|
121
|
+
| Unintegrated patch (per-patch spec review) | Branch each fix task from `WAVE_BASE_SHA` and carry the prior patch verbatim in its task text, per the consuming loop's spec-gap re-dispatch protocol. |
|
|
122
|
+
| Integrated wave before its accepted-wave commit | Fix in place; fan-out is unavailable. Keep the wave commit after its required review accepts, before `plan_tracker` `complete`; never commit early to enable fan-out. |
|
|
117
123
|
|
|
118
124
|
Grammar (identical across producers, modulo id prefix — `F` for code/spec reviewers, `G` for conformance-reviewer):
|
|
119
125
|
|
|
@@ -158,7 +158,7 @@ Classify every finding against the table by its `check` and `reason`, gathering
|
|
|
158
158
|
| `paths-exist`, `Modify: path "<p>" does not exist` | the owning task is `complete` or `skipped` in the task list, and `git -C <worktree> log --diff-filter=D --format=%h -1 -- <p>` is non-empty | drop that `Modify:` row |
|
|
159
159
|
| `paths-exist` or `tests-block`, a `Test:` path that does not exist or a `Tests:` segment not anchored | `git -C <worktree> log --diff-filter=R --format= --name-status -M -1 -- <old> <new>` shows the rename `<old>` -> `<new>` | substitute `<new>` for `<old>` in the `Test:` line and in the `Tests:` command; nothing else in the command changes |
|
|
160
160
|
|
|
161
|
-
Every other `check` (`input`, `placeholder-scan`, `header-entrypoint`, `header-happy-path`, `waiver-literal`, `wave-file-disjointness`, `
|
|
161
|
+
Every other `check` (`input`, `placeholder-scan`, `header-entrypoint`, `header-happy-path`, `waiver-literal`, `wave-file-disjointness`, `internal`) and every reason not listed (a missing `Files:` block, an invalid or empty glob, a missing or ambiguous heading, a `pending`/`in_progress` owner of a dead `Modify:` row, a command broadening) has no row. Task names, count, order, wave membership, `Solo:` lines, the owner column, and task text beyond an added literal or a substituted path never change.
|
|
162
162
|
|
|
163
163
|
Loop: write every classified edit, re-run `plan_check({ planPath })`, classify any new finding the same way; at most 3 rounds.
|
|
164
164
|
|
|
@@ -14,7 +14,7 @@ Dispatch a subagent with the code-reviewer prompt template to catch issues befor
|
|
|
14
14
|
## When to Request Review
|
|
15
15
|
|
|
16
16
|
**Mandatory:**
|
|
17
|
-
-
|
|
17
|
+
- Dispatch one initial code review per code-touching wave in subagent-driven development, over the integrated wave diff; doc-only waves are SR-only
|
|
18
18
|
- After completing major feature
|
|
19
19
|
- Before merge to main
|
|
20
20
|
|
|
@@ -25,7 +25,7 @@ Dispatch a subagent with the code-reviewer prompt template to catch issues befor
|
|
|
25
25
|
|
|
26
26
|
## How to Request
|
|
27
27
|
|
|
28
|
-
**1. Select the review source.** For an integrated uncommitted wave, use its recorded base as `BASE_SHA` and the literal `WORKTREE` as `HEAD_SHA`; stage new deliverables before dispatch, per `code-reviewer.md`. For
|
|
28
|
+
**1. Select the review source.** For an integrated uncommitted wave, use its recorded base as `BASE_SHA` and the literal `WORKTREE` as `HEAD_SHA`; stage new deliverables before dispatch, per `code-reviewer.md`. For the whole-diff review only, use the committed range:
|
|
29
29
|
```bash
|
|
30
30
|
BASE_SHA=$(git rev-parse HEAD~1) # or origin/main
|
|
31
31
|
HEAD_SHA=$(git rev-parse HEAD)
|
|
@@ -51,7 +51,7 @@ subagent({ agent: "code-reviewer", async: false, task: "... filled template ..."
|
|
|
51
51
|
- `{DESCRIPTION}` - Brief summary
|
|
52
52
|
- `{SCOPED_TEST_COMMANDS}` - the scoped verification commands the reviewer may run, or `none`
|
|
53
53
|
|
|
54
|
-
**Fix rounds.** A `FIX_FIRST` report triggers a fix round. A `SHIP` report dispatches nothing and ends the loop, whatever Minors it lists. `REJECT` is neither: stop and report (in SDD, per its `stop-note.md`); no fix round. When dispatched from an orchestrating skill, fixes go to `implementer` subagents (per the orchestrator's no-self-coding rule), fanned out per `dispatching-parallel-agents` "Fix fan-out" when the review's `Parallel-safe:` line certifies a `disjoint` group of ≥ 2 Critical/Moderate findings. Before fanning out, validate the review's `Parallel-safe:` line with the structural probe in `dispatching-parallel-agents` § Fix fan-out (exactly-one-line grammar check, one re-ask, then explicit sequential fallback). Sequential round (one task): the task carries the whole report; every Minor rides. Fan-out: the certificate partitions the Critical and Moderate findings; each Minor attaches once to the first task, in `Fn` order, whose ownership boundary - the union of its findings' `touched-files:` - already contains the Minor's `touched-files:`; a Minor no boundary contains is not dispatched this round, and a boundary never grows for a Minor. The implementer fixes its Minors or declines one with a stated reason. After integration and the project's test command, re-dispatch the reviewer once on the integrated delta in the foreground with top-level `async: false`; await its terminal result. A `FIX_FIRST` re-review buys one more fix round and one more foreground re-review; still `FIX_FIRST` -> escalate to the user. A `SHIP` report's Minors are not carried, with one exception: the whole-diff review's Minor finding lines go verbatim into the closure block's `whole-diff minors:` section (`verification-before-completion/reference/conformance-check.md` "Closure / conformance").
|
|
54
|
+
**Fix rounds.** A `FIX_FIRST` report triggers a fix round. A `SHIP` report dispatches nothing and ends the loop, whatever Minors it lists. `REJECT` is neither: stop and report (in SDD, per its `stop-note.md`); no fix round. When dispatched from an orchestrating skill, fixes go to `implementer` subagents (per the orchestrator's no-self-coding rule), fanned out per `dispatching-parallel-agents` "Fix fan-out" when the review's `Parallel-safe:` line certifies a `disjoint` group of ≥ 2 Critical/Moderate findings. Apply `dispatching-parallel-agents` "Fix fan-out" source precondition first: an integrated wave before its accepted-wave commit gets one in-place repair worker, not fan-out; keep the commit after acceptance. Before fanning out, validate the review's `Parallel-safe:` line with the structural probe in `dispatching-parallel-agents` § Fix fan-out (exactly-one-line grammar check, one re-ask, then explicit sequential fallback). Sequential round (one task): the task carries the whole report; every Minor rides. Fan-out: the certificate partitions the Critical and Moderate findings; each Minor attaches once to the first task, in `Fn` order, whose ownership boundary - the union of its findings' `touched-files:` - already contains the Minor's `touched-files:`; a Minor no boundary contains is not dispatched this round, and a boundary never grows for a Minor. The implementer fixes its Minors or declines one with a stated reason. After integration and the project's test command, re-dispatch the reviewer once on the integrated delta in the foreground with top-level `async: false`; await its terminal result. A `FIX_FIRST` re-review buys one more fix round and one more foreground re-review; still `FIX_FIRST` -> escalate to the user. A `SHIP` report's Minors are not carried, with one exception: the whole-diff review's Minor finding lines go verbatim into the closure block's `whole-diff minors:` section (`verification-before-completion/reference/conformance-check.md` "Closure / conformance").
|
|
55
55
|
|
|
56
56
|
Worked example: a `FIX_FIRST` report with 1 Moderate and 3 Minors -> one implementer task holding all four findings (one actionable ID, no fan-out). A `SHIP` report with 4 Minors -> no dispatch; the loop ends.
|
|
57
57
|
|
|
@@ -60,9 +60,8 @@ Take the recipient stance from `receiving-code-review`: verify each finding, ans
|
|
|
60
60
|
## Integration with Workflows
|
|
61
61
|
|
|
62
62
|
**Subagent-Driven Development:**
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
- Fix before moving to next task
|
|
63
|
+
- Dispatch one initial code review per code-touching wave over the integrated wave diff; doc-only waves are SR-only
|
|
64
|
+
- Fix and re-review before the accepted-wave commit and before moving to the next wave
|
|
66
65
|
|
|
67
66
|
**Ad-Hoc Development:**
|
|
68
67
|
- Review before merge
|
|
@@ -43,41 +43,19 @@ Periodic "should I continue?" prompts add latency without adding safety. The pla
|
|
|
43
43
|
- Approved plan or clear task scope
|
|
44
44
|
- `plan_tracker` list initialized at writing-plans handoff with the full wave-ordered task list. Consume and preserve it on continuation; only direct recovery with no tracker initializes the full plan list once, before dispatch, and never over an existing list.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Extract **`SCOPED_TEST_COMMANDS`** from the task's `**Tests:**` command bullets, backticks stripped, one per line; `- none:` -> `none`; a wave = the union of its tasks' commands. Treat `via:` bullets and `- Test:` paths as contract, not commands: carry them with the task text, never in `SCOPED_TEST_COMMANDS`.
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
**Dependent tasks:** include the previous task's implementation summary and relevant file paths in the next subagent's `task`. Track outputs so you can pass them forward.
|
|
51
|
-
|
|
52
|
-
## The Process
|
|
53
|
-
|
|
54
|
-
Before the first task, enter the implement phase: `phase_tracker({ action: "start", phase: "implement" })` — you hold this phase for the parent session while the per-task work is dispatched to subagents; `plan_tracker` auto-completes it once every task is done.
|
|
55
|
-
|
|
56
|
-
For each task in `plan_tracker`:
|
|
57
|
-
|
|
58
|
-
**`SCOPED_TEST_COMMANDS`** = the task's `**Tests:**` command bullets, backticks stripped, one per line; `- none:` -> `none`; a wave = the union of its tasks' commands. `via:` bullets and `- Test:` paths are contract, not commands: they ride with the task text, never in `SCOPED_TEST_COMMANDS`.
|
|
59
|
-
|
|
60
|
-
1. **Start, then dispatch implementer.** Mark the task's existing `plan_tracker` index `in_progress` before dispatch. Pass the full task text + scene-setting context + its `SCOPED_TEST_COMMANDS` (definition above) and its `**Tests:**` block verbatim. Don't make the subagent re-read the plan.
|
|
61
|
-
2. **Handle implementer status** (see below).
|
|
62
|
-
3. **Dispatch spec reviewer.** Pass the task text, the patch diff, the absolute spec path, and the task's `**Spec:**` anchors, plus the task's `**Tests:**` block and `- Test:` paths as contract (never as commands - SR never executes) — SR reads the anchored ranges from the spec file itself; never inline spec excerpts. The spec wins every dispute; the authority hierarchy and finding labels live in `./spec-reviewer-prompt.md`. Anchor-less tasks (no `**Spec:**` line): task text alone is the contract. Verify the change satisfies the anchored spec — nothing missing, nothing extra.
|
|
63
|
-
4. If spec reviewer finds gaps → re-dispatch implementer to fix → re-review. Loop until ✅, within [Fix-Loop Rounds](#fix-loop-rounds).
|
|
64
|
-
5. **Dispatch code-quality reviewer.** Only after spec is ✅. Skip for doc-only tasks (every file in the task's `Files:` block documentation-only) — SR-only, same exemption as doc-only waves. Pass the task's `SCOPED_TEST_COMMANDS`.
|
|
65
|
-
6. If the quality review is issue-bearing → re-dispatch the implementer per `/skill:requesting-code-review` Fix rounds → re-review. Loop until ✅, within [Fix-Loop Rounds](#fix-loop-rounds).
|
|
66
|
-
7. After its existing reviews accept the work (and its required commit point), mark that same task index `complete` in `plan_tracker`. A subagent exit or green tests alone are not acceptance.
|
|
67
|
-
|
|
68
|
-
The orchestrator is the spec's only writer during execution. Amendment trigger: implementer `BLOCKED` citing a spec defect, or a review finding showing the spec (not the code) is wrong -> pause the fix loop, `read` and execute brainstorming's [amendment path](../brainstorming/SKILL.md#amending-an-approved-spec) in place (it reviews first, stops only on escalation or redraw), resume. Code-vs-spec mismatch stays in the SR loop. An SR unable to read the spec at a cited anchor (missing file, unresolvable heading/range) returns a blocking finding — the contract is spec+task or stop, never a silent fallback to task-only review.
|
|
69
|
-
|
|
70
|
-
After all tasks: proceed to [After All Tasks](#after-all-tasks-complete) - it owns parent full verification, then whole-diff code review, then conformance.
|
|
48
|
+
Keep the orchestrator as the spec's only writer during execution. On implementer `BLOCKED` citing a spec defect, or a review finding showing the spec (not the code) is wrong, pause the fix loop, read and execute brainstorming's [amendment path](../brainstorming/SKILL.md#amending-an-approved-spec) in place (it reviews first, stops only on escalation or redraw), then resume. Keep code-vs-spec mismatch in the SR loop. Require an SR unable to read the spec at a cited anchor (missing file, unresolvable heading/range) to return a blocking finding - the contract is spec+task or stop, never a silent fallback to task-only review.
|
|
71
49
|
|
|
72
50
|
## Fix-Loop Rounds
|
|
73
51
|
|
|
74
|
-
|
|
52
|
+
Apply one rule to both review loops - spec-compliance and code-quality.
|
|
75
53
|
|
|
76
|
-
**Re-review dispatch rule:** every re-review task includes the complete prior review report verbatim under the marker `## Previous review report (re-review trigger)`, plus the trajectory block from the reviewer's prompt template. The marker's presence is what obligates the reviewer to emit the `TRAJECTORY:` line. You never select, summarize, or diff findings yourself - pattern-match the sentinel line only.
|
|
54
|
+
**Re-review dispatch rule:** every re-review task includes the complete prior review report verbatim under the marker `## Previous review report (re-review trigger)`, plus the trajectory block from the reviewer's prompt template. The marker's presence is what obligates the reviewer to emit the `TRAJECTORY:` line. You never select, summarize, or diff findings yourself - pattern-match the sentinel line only. Include the delta defined in [Fix delta](#fix-delta) in every SR/CR re-review.
|
|
77
55
|
|
|
78
|
-
**Fix fan-out.** When a fix round is fanned out per `/skill:requesting-code-review` Fix rounds, dispatch
|
|
56
|
+
**Fix fan-out.** When a fix round is fanned out per `/skill:requesting-code-review` Fix rounds, use the precondition and dispatch shape owned by `/skill:dispatching-parallel-agents` "Fix fan-out"; the fan-out counts as **one** fix against this budget, its scoped test gate is the consuming task/wave's `SCOPED_TEST_COMMANDS`, and one re-review of the integrated delta follows.
|
|
79
57
|
|
|
80
|
-
**Behaviour-change reroute.** When the triggering CR report carries `Behaviour-change: yes`, the fix round's re-review is SR first, then CR.
|
|
58
|
+
**Behaviour-change reroute.** When the triggering CR report carries `Behaviour-change: yes`, the fix round's re-review is SR first, then CR. Include `## Fix delta` on this SR-first behavior-change repair. Dispatch SR against the fix diff and the task's spec anchors as a first review (no `## Previous review report` marker, so no `TRAJECTORY:` line; SR carries no test commands but does carry the task's `**Tests:**` block and `- Test:` paths), but its ordinal continues the task's SR-loop count - a task whose SR loop ended at review 2 gets review 3 here, and an issue-bearing rerouted SR after review 4 escalates. Issues follow the normal sequence: fix, then SR re-review pasting this SR's report. CR round numbering is unchanged. `Behaviour-change: no` re-reviews with CR only. A missing or malformed `Behaviour-change:` line is re-asked once like `Parallel-safe:` (see `dispatching-parallel-agents` "Structural probe"); still missing -> route through SR, never default to `no`. Target the SR re-review at the task(s) whose files the fix touched; after integration, dispatch it against the current implementation worktree, not a pre-integration snapshot.
|
|
81
59
|
|
|
82
60
|
Every fix re-dispatch (implementer) and code-review re-review carries the consuming task/wave's `SCOPED_TEST_COMMANDS`; spec-reviewer re-reviews carry the `**Tests:**` block and `- Test:` paths, no commands - SR never executes.
|
|
83
61
|
|
|
@@ -90,7 +68,7 @@ Every fix re-dispatch (implementer) and code-review re-review carries the consum
|
|
|
90
68
|
|
|
91
69
|
Every dispatched fix is verified by a re-review before escalation or task progression - the loop only ever exits on a clean review or an escalation.
|
|
92
70
|
|
|
93
|
-
**Escalate** = one escalated round; stop only on failure. Call `gauntlet_setting({ key: "escalationLoop" })` (unavailable -> stop and report). `implModel` undefined -> stop note. Otherwise re-dispatch that fix - same payload and isolation knobs (`cwd`, `worktree`, `SCOPED_TEST_COMMANDS`, status protocol, prior patch, spec anchors) plus the prior report verbatim - overriding only `model: <implModel>`, `context: "fresh"`, `async: false`; one implementer, no fan-out. Run the normal fix-round review gate (SR then CR on `Behaviour-change: yes`, else the triggering reviewer with the re-review marker). All clean -> proceed;
|
|
71
|
+
**Escalate** = one escalated round; stop only on failure. Call `gauntlet_setting({ key: "escalationLoop" })` (unavailable -> stop and report). `implModel` undefined -> stop note. Otherwise re-dispatch that fix - same payload and isolation knobs (`cwd`, `worktree`, `SCOPED_TEST_COMMANDS`, status protocol, prior patch, spec anchors) plus the prior report verbatim - overriding only `model: <implModel>`, `context: "fresh"`, `async: false`; one implementer, no fan-out. Run the normal fix-round review gate (SR then CR on `Behaviour-change: yes`, else the triggering reviewer with the re-review marker). All clean -> proceed; the escalated patch supersedes the prior one at integrate. Any review with issues, a non-`DONE` status, or a dispatch error -> stop note per `stop-note.md`; no second dispatch. Once per loop; independent of the convergence exception. No `plan_tracker` write during escalation - the task stays `in_progress` until the human decides.
|
|
94
72
|
|
|
95
73
|
**Worked examples:**
|
|
96
74
|
|
|
@@ -98,13 +76,32 @@ Every dispatched fix is verified by a re-review before escalation or task progre
|
|
|
98
76
|
- Review-3 verdict `TRAJECTORY: CONVERGING (3 -> 1, max severity Moderate)` -> dispatch fix 3; if review 4 still finds issues, escalate.
|
|
99
77
|
- Review-3 verdict `TRAJECTORY: CONVERGING (3 -> 2, max severity Critical)` or `TRAJECTORY: DIVERGING` or no `TRAJECTORY:` line -> escalate.
|
|
100
78
|
|
|
79
|
+
## Fix delta
|
|
80
|
+
|
|
81
|
+
Include a `## Fix delta` block in every SR/CR re-review payload: `git diff --binary <tree-before> <tree-after>` between the tree recorded at the prior review's dispatch and the tree recorded at this re-review's dispatch. Record each tree with the shell block below; keep the printed SHA keyed by task for SR or wave for CR. Run it in the spec-review snapshot for pre-integration reviews, or in the branch checkout for integrated reviews (`FD_ROOT`). Tree objects share the repository's object store; use `git -C "$FD_ROOT" diff --binary <tree-before> <tree-after>` to produce the delta. Worker commits and staging state do not affect it; never substitute a SHA range or working-tree diff.
|
|
82
|
+
|
|
83
|
+
Substitute a shell-quoted checkout path for `{{CHECKOUT}}` and run the entire block. Stop review dispatch on a nonzero exit and report the shell error. The block reads the live index, writes only a scratch index and tree object, and preserves checkout status.
|
|
84
|
+
|
|
85
|
+
<!-- fix-delta-tree-shell -->
|
|
86
|
+
```bash
|
|
87
|
+
FD_ROOT={{CHECKOUT}}
|
|
88
|
+
FD_INDEX=$(mktemp) || exit 1
|
|
89
|
+
cp "$(git -C "$FD_ROOT" rev-parse --path-format=absolute --git-path index)" "$FD_INDEX" || { rm -f "$FD_INDEX"; exit 1; }
|
|
90
|
+
GIT_INDEX_FILE="$FD_INDEX" git -C "$FD_ROOT" add -A && GIT_INDEX_FILE="$FD_INDEX" git -C "$FD_ROOT" write-tree
|
|
91
|
+
FD_STATUS=$?
|
|
92
|
+
rm -f "$FD_INDEX"
|
|
93
|
+
exit "$FD_STATUS"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Omit the delta for whole-diff re-reviews. Otherwise omit it only when the prior tree is genuinely unavailable after a fresh-session resume; the reviewer then treats the task's whole diff as the delta. On an empty delta (including ignored-only repairs), dispatch no re-review: count a failed attempt under [When a Subagent Fails](#when-a-subagent-fails), resend the prior report with "your fix changed no tracked file", and stop after the existing two-attempt limit.
|
|
97
|
+
|
|
101
98
|
## Implementer Status
|
|
102
99
|
|
|
103
100
|
Every implementer dispatch returns one of:
|
|
104
101
|
|
|
105
102
|
| Status | Meaning | Orchestrator action |
|
|
106
103
|
|---|---|---|
|
|
107
|
-
| `DONE` | Implemented + tests pass + self-reviewed
|
|
104
|
+
| `DONE` | Implemented + tests pass + self-reviewed; work present in the worker's tree; the wave commit is step 7 | Proceed to spec review |
|
|
108
105
|
| `DONE_WITH_CONCERNS` | Implemented + tests pass, but flagged a deviation or risk | Read concerns. If acceptable, proceed. If not, dispatch fix subagent. |
|
|
109
106
|
| `NEEDS_CONTEXT` | Implementer needs information you have | Answer the questions, re-dispatch with answers in the task text |
|
|
110
107
|
| `BLOCKED` | Cannot proceed (missing dependency, spec contradicts code, environment broken) | Stop. Diagnose. Either fix orchestration-level issue and re-dispatch, or escalate to user. |
|
|
@@ -121,11 +118,11 @@ Implementer prompt templates must instruct subagents to return one of these stat
|
|
|
121
118
|
|
|
122
119
|
## Dispatch
|
|
123
120
|
|
|
124
|
-
Pass the implementation worktree as top-level `cwd
|
|
121
|
+
Pass the implementation worktree as top-level `cwd`. For per-patch spec reviews, use [Spec-review snapshots](#spec-review-snapshots) instead.
|
|
125
122
|
|
|
126
123
|
```ts
|
|
127
|
-
//
|
|
128
|
-
subagent({ agent: "implementer", async: false, cwd: "<abs worktree path>", task: "<task text + context + SCOPED_TEST_COMMANDS + status protocol>" })
|
|
124
|
+
// in-place repair worker
|
|
125
|
+
subagent({ agent: "implementer", async: false, cwd: "<abs worktree path>", task: "<task text + context + prior report verbatim + SCOPED_TEST_COMMANDS + status protocol>" })
|
|
129
126
|
|
|
130
127
|
// escalated fix round (Fix-Loop Rounds): same fix payload, model from gauntlet_setting({ key: "escalationLoop" }).implModel
|
|
131
128
|
subagent({ agent: "implementer", model: "<implModel>", context: "fresh", async: false, cwd: "<abs worktree path>", task: "<the just-dispatched fix payload + prior review report verbatim>" })
|
|
@@ -148,30 +145,29 @@ Prompt templates live alongside this SKILL.md:
|
|
|
148
145
|
- `./spec-reviewer-prompt.md`
|
|
149
146
|
- `./code-quality-reviewer-prompt.md`
|
|
150
147
|
|
|
151
|
-
##
|
|
148
|
+
## The Wave Loop
|
|
152
149
|
|
|
153
|
-
|
|
150
|
+
Run every plan task through this loop in a wave of N >= 1 tasks. Run waves in sequence; dispatch file- and runtime-resource-disjoint tasks concurrently in isolated worktrees, then integrate serially behind one test + review gate. For dependent tasks, include the previous task's implementation summary and relevant file paths in the next subagent's `task`; track outputs to pass them forward.
|
|
154
151
|
|
|
155
|
-
**Enter the implement phase first.** Before the first wave, call `phase_tracker({ action: "start", phase: "implement" })
|
|
152
|
+
**Enter the implement phase first.** Before the first wave, call `phase_tracker({ action: "start", phase: "implement" })`; `plan_tracker` auto-completes it once every task across all waves is done.
|
|
156
153
|
|
|
157
154
|
**Progress tracking (`plan_tracker`).** `plan_tracker` is a flat list with no native group concept, so waves are *encoded*, not modeled:
|
|
158
155
|
|
|
159
156
|
- **Consume, preserve, recover only if absent:** consume the wave-ordered list initialized at writing-plans handoff; indices are positional and stable, so never re-init on continuation or mid-run. Only direct recovery with no tracker initializes the full plan list once before dispatch.
|
|
160
|
-
- **Wave fan-out → `in_progress`:** unconditionally mark every task index in the wave `in_progress` before dispatch, in increasing index order (the tracker validates each call against the previous one and rejects a start while an earlier index is still `pending`).
|
|
157
|
+
- **Wave fan-out → `in_progress`:** unconditionally mark every task index in the wave `in_progress` before dispatch, in increasing index order (the tracker validates each call against the previous one and rejects a start while an earlier index is still `pending`). Expect one or more simultaneous entries.
|
|
161
158
|
- **Wave commit → `complete`:** after the wave's gate passes and it commits, unconditionally mark all those same indices `complete`. `complete` = durably committed, so a task in conflict fallback stays `in_progress` until its wave commits.
|
|
162
|
-
- **Lifecycle per task:** `pending → in_progress (wave fan-out) → complete (wave commit)`; `
|
|
163
|
-
- **Widget caveat (known, deliberately unfixed).** The persistent `plan_tracker` widget's icon strip (`○ → ✓`) and `(c/total)` count reflect every task, but its trailing *name* shows only the **first** `in_progress` task.
|
|
164
|
-
- **Sequential mode:** consume the same existing full list, one `in_progress` index at a time; wave prefixes are harmless.
|
|
159
|
+
- **Lifecycle per task:** `pending → in_progress (wave fan-out) → complete (wave commit)`; keep `in_progress` through exhausted integration or repair attempts pending the human's decision (see [When a Subagent Fails](#when-a-subagent-fails)); use `failed` only for a human-decided terminal failure; `skipped` when the plan drops it as not applicable (terminal, counted done). A rejected `update` names the earlier pending tasks and the legal fixes - record those tasks' true state, never `clear` or re-`init` to move on.
|
|
160
|
+
- **Widget caveat (known, deliberately unfixed).** The persistent `plan_tracker` widget's icon strip (`○ → ✓`) and `(c/total)` count reflect every task, but its trailing *name* shows only the **first** `in_progress` task. Use the icon strip and the `status` action for the full in-flight view; a richer multi-task widget is a separate extension change, out of scope (YAGNI).
|
|
165
161
|
|
|
166
162
|
**Per-wave loop:**
|
|
167
163
|
|
|
168
|
-
1. **Independence check.** Parse the wave's tasks' `Files:` blocks; assert pairwise-disjoint (mechanical, mirroring `wave-file-disjointness`: `Test`/`Test` on one path is not overlap, `Test` vs another task's `Create`/`Modify` is, `Modify`/`Modify` is). Runtime-resource disjointness (DB/schema, port, fixture, external service, shared temp path) is not machine-checkable here — trust the plan's wave grouping, which `writing-plans`' D5 contract guarantees. Either kind of overlap → the wave is mis-grouped;
|
|
164
|
+
1. **Independence check.** Parse the wave's tasks' `Files:` blocks; assert pairwise-disjoint (mechanical, mirroring `wave-file-disjointness`: `Test`/`Test` on one path is not overlap, `Test` vs another task's `Create`/`Modify` is, `Modify`/`Modify` is). Runtime-resource disjointness (DB/schema, port, fixture, external service, shared temp path) is not machine-checkable here — trust the plan's wave grouping, which `writing-plans`' D5 contract guarantees. Either kind of overlap → the wave is mis-grouped; split it into one-task waves and note it.
|
|
169
165
|
2. **Start, then dispatch.** Record `WAVE_BASE_SHA` with `git -C <worktree> rev-parse HEAD` before dispatch; keep it fixed through the wave's commit. Mark every wave index `in_progress` in increasing index order. Use the once-per-phase preflight below to choose exactly one initial chain (including one-task waves), or the fallback implementer fan-out.
|
|
170
166
|
3. **Read the result once.** Read statuses, verdicts, patches, and reports as specified below. All implementers `DONE` and all verdicts `COMPLIANT` -> step 4. Any other outcome -> [Chain stopped or failed](#chain-stopped-or-failed). The ticket's `FAIL` means a spec gap (`NEEDS_REWORK`), not a structured verdict or an SR `stopOn` condition.
|
|
171
|
-
4. **Integrate.** Run `git -C <worktree> apply --index <patch-path>` for each accepted task's patch sequentially. This stages additions as well as tracked changes for review.
|
|
172
|
-
5. **Test gate.** Run the wave's `SCOPED_TEST_COMMANDS` (union of its tasks' `Tests:` commands) on the integrated tree as `(cd "<worktree>" && <command>)` for each command — the full verification set is the verify phase's job, run once.
|
|
173
|
-
6. **Quality review.** CR binds to the wave: exactly one **initial** code-review dispatch per code-touching wave, over the integrated wave diff - never per task within a wave, never batched across waves. Subsequent dispatches within the wave are re-reviews triggered only by findings, per Fix-Loop Rounds. Pass the wave's `SCOPED_TEST_COMMANDS`, `BASE_SHA: <WAVE_BASE_SHA>`, and `HEAD_SHA: WORKTREE` per `code-quality-reviewer-prompt.md`; the review reads the staged and unstaged integrated diff, not a commit range. On findings, fix and re-review per `/skill:requesting-code-review` and [Fix-Loop Rounds](#fix-loop-rounds).
|
|
174
|
-
7. **Commit and complete the wave.** After
|
|
167
|
+
4. **Integrate.** Run `git -C <worktree> apply --index <patch-path>` for each accepted task's patch sequentially. This stages additions as well as tracked changes for review. On an apply failure, drop that task from the wave; finish steps 5-7 for the survivors, then re-dispatch the dropped task as a one-task wave from the new committed HEAD. With zero surviving patches, skip steps 5-7 and retry the failed task from the unchanged clean HEAD under [When a Subagent Fails](#when-a-subagent-fails)'s two-attempt limit; dispatch no empty review or commit. Keep its tracker index `in_progress` until its own wave commits.
|
|
168
|
+
5. **Test gate.** Run the wave's `SCOPED_TEST_COMMANDS` (union of its tasks' `Tests:` commands) on the integrated tree as `(cd "<worktree>" && <command>)` for each command — the full verification set is the verify phase's job, run once. On failure, dispatch one plain in-place `implementer` in the branch checkout with the failing output, the owning task's full text, and the wave's `SCOPED_TEST_COMMANDS`, under [When a Subagent Fails](#when-a-subagent-fails), then rerun the scoped gate. After the repaired tree passes, stage repair deliverables with `git -C <worktree> add -A` and dispatch spec review for every affected task against the integrated checkout before the initial CR; prior SR acceptance does not cover the repair. Pass each affected task's full text, absolute spec path, `Spec:` anchors, `Tests:` block, and `- Test:` paths; record its review tree per [Fix delta](#fix-delta). Resolve SR findings through [Fix-Loop Rounds](#fix-loop-rounds), with repairs in place and the scoped gate rerun. Never dispatch a nested wave from this dirty checkout: children cannot see integrated siblings from the old HEAD.
|
|
169
|
+
6. **Quality review.** CR binds to the wave: exactly one **initial** code-review dispatch per code-touching wave, over the integrated wave diff - never per task within a wave, never batched across waves. Subsequent dispatches within the wave are re-reviews triggered only by findings, per Fix-Loop Rounds. Pass the wave's `SCOPED_TEST_COMMANDS`, `BASE_SHA: <WAVE_BASE_SHA>`, and `HEAD_SHA: WORKTREE` per `code-quality-reviewer-prompt.md`; the review reads the staged and unstaged integrated diff, not a commit range. On findings, fix and re-review per `/skill:requesting-code-review` and [Fix-Loop Rounds](#fix-loop-rounds). Dispatch repairs as plain in-place workers in the branch checkout, never nested waves from the dirty checkout. Carry the report verbatim and no commit or staging instruction in the repair payload. Before every CR dispatch and every SR/CR re-review, run `git -C <worktree> add -A` so all deliverables appear in the whole diff; record the review tree per [Fix delta](#fix-delta). Preserve `WAVE_BASE_SHA` and `HEAD_SHA: WORKTREE` through every re-review and escalated in-place repair. Skip CR for doc-only waves (SR-only per the commit precondition below).
|
|
170
|
+
7. **Commit and complete the wave.** After CR accepts (SR alone for doc-only waves), fold worker commits and all remaining changes into one durable wave commit: `git -C <worktree> reset --soft "$WAVE_BASE_SHA" && git -C <worktree> add -A && git -C <worktree> commit -m <message>`. Then mark the wave's existing indices `complete`. Stop on an empty commit instead of completing tasks. Leave a clean tree so the next wave's children see the integrated work.
|
|
175
171
|
|
|
176
172
|
**Two-stage review is preserved:** spec review per task (pre-integration, dispatched `spec-reviewer` — not inline), quality review per wave (post-integration). A wave commit requires one spec-review verdict per accepted task, plus one code-review verdict on the integrated diff for waves that touch code. A doc-only wave (every task's `Files:` block documentation-only, per `writing-plans`' Wave Grouping) is SR-only — the CR gate does not apply.
|
|
177
173
|
|
|
@@ -272,7 +268,7 @@ Route the result already read in step 3; retain all successful sibling patches a
|
|
|
272
268
|
|
|
273
269
|
Parse each `DONE`/`BLOCKED`/etc. (see [Implementer Status](#implementer-status)) **first**. Then **dispatch a `spec-reviewer` per accepted patch** (`DONE`, or a `DONE_WITH_CONCERNS` you proceeded with) still missing a verdict in one parallel fan-out - `context: "fresh"`, each task's `cwd` = its [Spec-review snapshot](#spec-review-snapshots), **no `worktree` flag** (read-only) - each passed its full task text, the returned **patch diff**, the absolute spec path, and the task's `**Spec:**` anchors, plus its `**Tests:**` block and `- Test:` paths as contract. SR reads the anchored ranges itself (never inline excerpts; authority hierarchy in `./spec-reviewer-prompt.md`; anchor-less tasks are task-text-only). Review starts from the diff (its hunks carry `file:line`) and reads each touched file in full; test execution is never the reviewer's job (step 5 runs the wave's `SCOPED_TEST_COMMANDS`). Inline verdicts are fine at normal wave sizes; large waves use `output:` + `outputMode: "file-only"`; read the full report file itself.
|
|
274
270
|
|
|
275
|
-
**Re-dispatch by cause:** `BLOCKED`/`NEEDS_CONTEXT` per the [Implementer Status](#implementer-status) matrix; a **spec gap** re-dispatches only that implementer using a one-entry `tasks` array with invocation-level `worktree: true`, `context: "fresh"`, and implementation `cwd`, carrying the artifacts patch + full SR report verbatim.
|
|
271
|
+
**Re-dispatch by cause:** Before fixing a chain-run SR's `NEEDS_REWORK` or `OUT_OF_SCOPE_CHANGES`, reconstruct the original artifact patch's snapshot from `WAVE_BASE_SHA` and record its tree-before per [Fix delta](#fix-delta); the chain's own trees are already removed. Route `BLOCKED`/`NEEDS_CONTEXT` per the [Implementer Status](#implementer-status) matrix; a **spec gap** re-dispatches only that implementer using a one-entry `tasks` array with invocation-level `worktree: true`, `context: "fresh"`, and implementation `cwd`, carrying the artifacts patch + full SR report verbatim. Materialize the replacement patch in a new detached `WAVE_BASE_SHA` checkout with `git apply --index` per [Spec-review snapshots](#spec-review-snapshots); on setup or tree-capture failure, stop dispatch and retain the failed snapshot directory and shell output for diagnosis. After successful setup, record tree-after in that replacement snapshot before dispatching one plain re-review with `## Previous review report (re-review trigger)`, the trajectory block, and `## Fix delta`. The new patch supersedes the old at step 4. Loop until accepted + spec compliant, within [Fix-Loop Rounds](#fix-loop-rounds). Never dispatch a second chain in the same wave.
|
|
276
272
|
|
|
277
273
|
## Spec-review snapshots
|
|
278
274
|
|
|
@@ -292,7 +288,7 @@ git -C "$SR_TREE" apply --index "$SR_PATCH" || exit 1
|
|
|
292
288
|
printf 'snapshot: %s\n' "$SR_TREE"
|
|
293
289
|
```
|
|
294
290
|
|
|
295
|
-
Use the printed snapshot path as that spec-review task's `cwd` and whole-file read root. Keep the authoritative spec's absolute path in the implementation worktree. Pass the patch diff as before; the snapshot contains this task's changed and added files, with deleted files absent. Read deleted files from `WAVE_BASE_SHA` when the review needs their prior contents. Dispatch read-only, with no `worktree` flag.
|
|
291
|
+
Record its review tree per [Fix delta](#fix-delta) before every parent-dispatched SR, keyed by task. Use the printed snapshot path as that spec-review task's `cwd` and whole-file read root. Keep the authoritative spec's absolute path in the implementation worktree. Pass the patch diff as before; the snapshot contains this task's changed and added files, with deleted files absent. Read deleted files from `WAVE_BASE_SHA` when the review needs their prior contents. Dispatch read-only, with no `worktree` flag. On snapshot setup or tree-capture failure, or a missing snapshot path, stop dispatch and retain the failed snapshot directory and shell output for diagnosis; dispatch no review and perform no snapshot cleanup. Save each snapshot-backed review's terminal report to its `output:` path, read it, then remove that review's snapshot with `git -C <implementation-worktree> worktree remove --force <snapshot-path>`. Keep every snapshot used as a reviewer `cwd` until that terminal report is saved; recording its tree never authorizes early cleanup. Remove a reconstruction used only to record tree-before after capturing its tree, since no reviewer runs there. Recreate from the replacement patch for each pre-integration re-review. Post-integration SR reviews the implementation worktree directly.
|
|
296
292
|
|
|
297
293
|
## When a Subagent Fails
|
|
298
294
|
|
|
@@ -300,7 +296,7 @@ Use the printed snapshot path as that spec-review task's `cwd` and whole-file re
|
|
|
300
296
|
|
|
301
297
|
1. **Attempt 1:** dispatch a NEW fix subagent with the error output, the original task, and specific instructions about what went wrong.
|
|
302
298
|
2. **Attempt 2:** if attempt 1 also fails, dispatch one more with a simplified scope or different approach.
|
|
303
|
-
3. **After 2 failed attempts:** STOP. Report failure to the user.
|
|
299
|
+
3. **After 2 failed attempts:** STOP. Report failure to the user. Keep the task `in_progress`, not `failed` or `complete`, pending the human's decision; completion requires the wave commit.
|
|
304
300
|
|
|
305
301
|
**NEVER:**
|
|
306
302
|
|
|
@@ -10,11 +10,11 @@ Use this template when dispatching a code quality reviewer subagent.
|
|
|
10
10
|
Dispatch a subagent with the code-reviewer template:
|
|
11
11
|
Use the template at ../requesting-code-review/code-reviewer.md
|
|
12
12
|
|
|
13
|
-
DESCRIPTION: [
|
|
14
|
-
PLAN_OR_REQUIREMENTS:
|
|
15
|
-
BASE_SHA: [
|
|
16
|
-
HEAD_SHA: [
|
|
17
|
-
SCOPED_TEST_COMMANDS: [the
|
|
13
|
+
DESCRIPTION: [wave summary, from implementers' reports; whole-diff review: implementation summary]
|
|
14
|
+
PLAN_OR_REQUIREMENTS: [wave tasks from plan-file; whole-diff review: full plan/requirements]
|
|
15
|
+
BASE_SHA: [WAVE_BASE_SHA recorded before worker dispatch; whole-diff review: committed implementation base]
|
|
16
|
+
HEAD_SHA: [the literal WORKTREE for the integrated wave, including re-reviews; whole-diff review: committed HEAD]
|
|
17
|
+
SCOPED_TEST_COMMANDS: [the union of the wave's tasks' Tests: commands; `none` for the whole-diff verify-phase review]
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
**In addition to standard code quality concerns, the reviewer should check:**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Stop note (subagent-driven-development companion)
|
|
2
2
|
|
|
3
|
-
Emitted when no escalation model resolves, the escalated dispatch errors, or its one escalated round fails (see `SKILL.md` "Fix-Loop Rounds"); an unavailable `gauntlet_setting` tool is a configuration error - stop and report, no stop note. Inline in the reply; the turn ends; phase stays `implement`, the task stays `in_progress`; no further tasks start.
|
|
3
|
+
Emitted when no escalation model resolves, the escalated dispatch errors, or its one escalated round fails (see `SKILL.md` "Fix-Loop Rounds"); an unavailable `gauntlet_setting` tool is a configuration error - stop and report, no stop note. Inline in the reply; the turn ends; phase stays `implement`, the task stays `in_progress`; no further tasks start. Emit one note per stalled task, after the current batch returns.
|
|
4
4
|
|
|
5
5
|
## Template
|
|
6
6
|
|
|
@@ -124,9 +124,9 @@ carried gap's `recommended`. Re-run steps 1-2 above over the **full current
|
|
|
124
124
|
open-gap set** each time the reviewer returns a report — never reuse a stale
|
|
125
125
|
partition from an earlier round.
|
|
126
126
|
|
|
127
|
-
### Fix loop
|
|
127
|
+
### Fix loop - SDD wave-loop mirror, per round
|
|
128
128
|
|
|
129
|
-
Mirrors `subagent-driven-development`
|
|
129
|
+
Mirrors `subagent-driven-development`'s wave loop and reuses its
|
|
130
130
|
`plan_tracker` progress surface. Runs entirely inside the gate — it invokes
|
|
131
131
|
**no** phase-transition `phase_tracker` calls (`start`/`complete`/`skip`/`reset`
|
|
132
132
|
- `phase_tracker({ phase: "implement" })` errors while verify is `in_progress`;
|
|
@@ -75,7 +75,7 @@ The main loop never writes the plan. One fresh `plan-writer` child does the reco
|
|
|
75
75
|
- `OPEN QUESTIONS` + any result -> present the plan's `## Open Questions` section to the user and wait (the Open-Questions halt; no `plan_tracker` init before the answers). After the answers, re-dispatch per step 3 with the answers as the `Findings:` block, then step 5.
|
|
76
76
|
- `PLAN WRITTEN` + `FAIL` -> re-dispatch per step 3 with the verbatim `FAIL (...)` output as the `Findings:` block, then step 5. Once per verbatim FAIL output: the same FAIL a second time goes to the user as an open question (the FAIL lines, verbatim), as an exhausted finding does.
|
|
77
77
|
- No child reply (the step 3 entry branch on a restored plan) -> read the result as if the reply were `OPEN QUESTIONS` when the plan carries an `## Open Questions` section, else `PLAN WRITTEN`, and take that reply's branch above; a `FAIL` re-dispatch overwrites the restored plan with a repaired one.
|
|
78
|
-
7. Clear the substep (`phase_tracker({ action: "substep", phase: "plan", substep: null })`). Read the plan once, then `## Execution Handoff` as written below: `phase_tracker({ action: "complete", phase: "plan" })`, `plan_tracker({ action: "init", ... })` from the `W<k>: <title>` list,
|
|
78
|
+
7. Clear the substep (`phase_tracker({ action: "substep", phase: "plan", substep: null })`). Read the plan once, then `## Execution Handoff` as written below: `phase_tracker({ action: "complete", phase: "plan" })`, `plan_tracker({ action: "init", ... })` from the `W<k>: <title>` list, `/skill:subagent-driven-development`.
|
|
79
79
|
|
|
80
80
|
The parent holds no run id and never calls `resume` or `status`; every dispatch here is `async: false`. Re-entering this section without a restored plan overwrites whatever the plan path holds - a committed prior plan (recoverable from git history) or an uncommitted one (destroyed); re-planning is a deliberate overwrite. An amendment of the approved spec after the plan exists re-enters at step 3 with an `Amend:` block (brainstorming's `reference/amendment-surface.md` section 5 owns that call).
|
|
81
81
|
|
|
@@ -108,35 +108,31 @@ If you can't list the files, the spec isn't ready: amend or redraw per brainstor
|
|
|
108
108
|
|
|
109
109
|
## Wave Grouping
|
|
110
110
|
|
|
111
|
-
Group tasks into **waves** so the executor can parallelize independent work (see `subagent-driven-development`
|
|
111
|
+
Group tasks into **waves** so the executor can parallelize independent work (see `subagent-driven-development` wave loop). A wave is a maximal set of tasks that (a) have no ordering dependency on each other, (b) own **pairwise-disjoint files**, and (c) contend on **no shared mutable runtime resource** (same DB/schema, port, fixture file, external service, shared temp path).
|
|
112
112
|
|
|
113
|
-
- Group independent tasks into the same wave by default.
|
|
114
|
-
-
|
|
113
|
+
- Group independent tasks into the same wave by default.
|
|
114
|
+
- Group a pure dependency chain into one task per wave.
|
|
115
115
|
- Each wave after the first states its dependency on prior waves.
|
|
116
116
|
|
|
117
117
|
**File-ownership contract.** See [reference/plan-contract.md § Files](reference/plan-contract.md).
|
|
118
118
|
|
|
119
119
|
**Test contract.** Every task that creates or modifies code declares a `Test:` path and an executable `**Tests:**` command anchored to it (grammar: [reference/plan-contract.md § Tests](reference/plan-contract.md)); `- none: <category>` only when no tests apply. Anchoring is presence, not coverage. When the anchored spec names the thing under test, the task carries `- via:` naming it; a fixture path the spec names goes under `Create:`. Prefer the existing test file for the behavior as `Test:`; a new test file is declared as both `Create:` and `Test:`.
|
|
120
120
|
|
|
121
|
-
**Runtime-resource disjointness.** File-disjoint is necessary but not sufficient: two tasks with disjoint files that both mutate the same DB, bind the same port, or share a fixture are **not** parallel-safe and must land in different waves.
|
|
121
|
+
**Runtime-resource disjointness.** File-disjoint is necessary but not sufficient: two tasks with disjoint files that both mutate the same DB, bind the same port, or share a fixture are **not** parallel-safe and must land in different waves. Use this grouping as the parallel-safety guarantee for every multi-task wave. No new mandatory per-task syntax; when a shared runtime resource is the reason two file-disjoint tasks sit in different waves, record it in an inline note on the later wave.
|
|
122
122
|
|
|
123
123
|
**Doc tasks.** Doc updates are real plan tasks, not an afterthought. Task-local docs (a doc that only describes the file(s) a task already touches) ride with that task. Cross-cutting or index docs (README, `AGENTS.md`, topic guides, taxonomy indexes) sequence into a dedicated trailing doc-only wave — last wave by convention, file-disjoint from every code task so the pairwise-disjoint wave contract holds.
|
|
124
124
|
|
|
125
|
-
**Doc-wave collision rule.** Amend-over-create (see `brainstorming/reference/documentation-impact.md`) concentrates edits into hub docs like README and `AGENTS.md`.
|
|
125
|
+
**Doc-wave collision rule.** Amend-over-create (see `brainstorming/reference/documentation-impact.md`) concentrates edits into hub docs like README and `AGENTS.md`. Run a multi-task doc wave's tasks concurrently with pairwise-disjoint files; merge doc tasks that land on the *same* file into one task rather than splitting. A single-task trailing doc wave is the expected shape, not a smell.
|
|
126
126
|
|
|
127
127
|
```markdown
|
|
128
128
|
## Wave 1 — Foundations
|
|
129
129
|
|
|
130
|
-
Parallel-safe: Tasks 1–3 own disjoint files (see each task's Files block).
|
|
131
|
-
|
|
132
130
|
### Task 1: ...
|
|
133
131
|
### Task 2: ...
|
|
134
132
|
### Task 3: ...
|
|
135
133
|
|
|
136
134
|
## Wave 2 — Wire-up
|
|
137
135
|
|
|
138
|
-
Solo: Task 4 depends on Wave 1 Task 1's API (named-blocker justification).
|
|
139
|
-
|
|
140
136
|
Depends on Wave 1: Task 4 consumes the API introduced by Task 1.
|
|
141
137
|
|
|
142
138
|
### Task 4: ...
|
|
@@ -240,7 +236,7 @@ Each task uses `- [ ]` checkbox steps so execution tools (and humans) can track
|
|
|
240
236
|
|
|
241
237
|
Every code task carries this step (red -> green -> fmt/lint -> commit). Doc-only tasks omit it unless the project formats Markdown.
|
|
242
238
|
|
|
243
|
-
|
|
239
|
+
Use bare `git` in task commit steps: the orchestrator commits each wave after code review accepts; a task's own commit step runs inside its nested worktree and is captured as its patch.
|
|
244
240
|
|
|
245
241
|
**Anchor rules.** See [reference/plan-contract.md § Spec anchors](reference/plan-contract.md). A task with no anchorable requirement omits the `**Spec:**` line and carries a mechanical-task row in `## Spec coverage` — silence is never valid.
|
|
246
242
|
|
|
@@ -277,7 +273,6 @@ The plan-writer runs these before replying: the deterministic checker, then the
|
|
|
277
273
|
- **Type / API consistency.** Function signatures and field names that appear in multiple tasks must match exactly. The plan is its own contract — internal contradictions surface as bugs during execution.
|
|
278
274
|
- **Test contract.** Every code task's `Tests:` commands are anchored to its `Test:` path(s); `none:` only where no tests apply; a spec-named seam appears as `via:`, a spec-named fixture path as `Create:`. Prefer the existing test file for the behavior as `Test:`; a new test file is declared as both `Create:` and `Test:`.
|
|
279
275
|
- **Runtime-resource disjointness.** For every multi-task wave, confirm no two tasks contend on a shared mutable runtime resource (DB/schema, port, fixture, external service, shared temp path) — `Files:` overlap is checked mechanically, resource contention is not. Contention = mis-grouped wave; split or re-order before handoff.
|
|
280
|
-
- **Solo-reason validity.** Every single-task wave's `Solo:` line (presence is checked mechanically) must name its specific blocker — the blocking task/wave, the contended resource, or `lone remaining task`. Category-only justifications are under-justified; merge or justify before handoff.
|
|
281
276
|
- **Waiver authorization.** `waived: <reason>` is only for requirements the spec marks out of scope **and** that exclude work from the change. A requirement whose text carries an inline code span (`` `literal` ``) names concrete behaviour and is never waivable - it maps to a task or `Verification`. A waiver on an in-scope normative requirement is a Self-Review failure — there is no human plan-review gate to catch it downstream.
|
|
282
277
|
- **Verification-ownership authorization.** `Verification` on a requirement no header command exercises is a Self-Review failure.
|
|
283
278
|
- **Documentation-impact mapping.** Each Documentation impact entry maps to a plan task (or explicit "none").
|
|
@@ -304,11 +299,6 @@ phase_tracker({ action: "complete", phase: "plan" })
|
|
|
304
299
|
plan_tracker({ action: "init", tasks: ["W1: <title>", "W1: <title>", "W2: <title>"] })
|
|
305
300
|
```
|
|
306
301
|
|
|
307
|
-
Then auto-select the execution mode and proceed — no pause, no picker. The mode is a pure function of the plan's wave structure:
|
|
308
|
-
|
|
309
|
-
- **If any wave contains ≥2 tasks → Parallel-Wave Mode.** The strengthened Wave Grouping contract (files + runtime-resource disjoint) guarantees every multi-task wave is parallel-safe.
|
|
310
|
-
- **Otherwise (pure dependency chain, one task per wave) → Sequential Mode.**
|
|
311
|
-
|
|
312
302
|
Auto-invoke `/skill:subagent-driven-development` in this session. Do not wait for confirmation — the spec gate already happened, and the plan is a mechanical derivative. The only pauses from here are in-flight STOPs (`BLOCKED` / `NEEDS_CONTEXT`) and the end gate, both owned by the executor.
|
|
313
303
|
|
|
314
304
|
## Red Flags — STOP
|
|
@@ -29,10 +29,6 @@ Absence is never valid. A `- [ ]` step or any non-bullet line ends the block. `v
|
|
|
29
29
|
|
|
30
30
|
Commands are written repo-relative and run in the worktree via `(cd "<abs worktree path>" && <command>)` - the checker never runs them; the executor does. Each command is split into segments on `&&`, `||`, `;`, `|`; every segment must contain, as a whitespace-delimited token, a `Test:` path of the same task (the path alone, or followed by `::`, `#`, or `:` and a filter). A `Test:` value containing `*`, `?`, `[` or ending in `/` never anchors; any other argument token with those shapes is a broadening selector and fails. `cd `, `sh -c`, `bash -c`, `eval `, `$(` are unsupported. Each `Test:` path must exist or be a `Create:` path of some task. A segment equal to a header `**Verification:**` or `**Happy path:**` segment is a full-suite command and fails. Runners with no file-addressable form are out of scope (`go test ./pkg -run X`, `mvn -Dtest=`).
|
|
31
31
|
|
|
32
|
-
## Solo line (`solo-line`)
|
|
33
|
-
|
|
34
|
-
A wave with one task carries, directly under its `## Wave N — <label>` header, the line `Solo: <reason>`. The reason names the blocking task/wave, the contended runtime resource, or `lone remaining task`. Presence is checked here; validity is `writing-plans` Self-Review.
|
|
35
|
-
|
|
36
32
|
## Header-only entrypoint (`header-entrypoint`)
|
|
37
33
|
|
|
38
34
|
The `**Verification:**` line is the **only** place the full verification entrypoint may appear — never in any task or wave step. The verify phase reads it from the plan instead of re-deriving it; execution runs scoped commands only.
|