pi-gauntlet 7.17.0 → 7.18.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 +16 -0
- package/README.md +1 -1
- package/extensions/telemetry.test.ts +61 -1
- package/extensions/telemetry.ts +4 -4
- package/package.json +4 -1
- package/skills/brainstorming/SKILL.md +27 -4
- package/skills/requesting-code-review/SKILL.md +2 -2
- package/skills/requesting-code-review/code-reviewer.md +9 -2
- package/skills/subagent-driven-development/SKILL.md +32 -12
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v7.18.0 - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Brainstorming questions list one option per line.** Each `A)`/`B)` option and the `Recommendation:` line sit on their own physical line instead of one paragraph; wording and the framing rules are unchanged. (#70)
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Telemetry counts the agents of a failed dispatch.** A `subagent` call that ended in error (a failed chain step, a sibling that exited non-zero) still records its retained per-persona results; spec-round and conformance bookkeeping stay on the success path. `package.json` now declares pi-cohort >= 7.3.1 as a peer dependency, matching the README requirement. (#70)
|
|
12
|
+
|
|
13
|
+
## v7.17.1 - 2026-10-18
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Wave reviews inspect the implemented files.** Pre-integration spec reviews read a temporary checkout with the task's patch applied, including new and deleted files. Integrated code reviews compare staged and unstaged deliverables against the recorded wave base instead of an unchanged commit range; in-place repairs preserve that base and supply only their fix delta. Real Git fixtures cover both review sources and malformed patches. Paired with pi-cohort's binary-safe patch capture fix.
|
|
18
|
+
|
|
3
19
|
## v7.17.0 - 2026-10-18
|
|
4
20
|
|
|
5
21
|
### Changed
|
package/README.md
CHANGED
|
@@ -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)
|
|
125
|
+
- [pi-cohort](https://github.com/jjuraszek/pi-cohort) >= 7.3.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.
|
|
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
|
|
|
@@ -588,7 +588,7 @@
|
|
|
588
588
|
assert.deepEqual(h.readRecord().derived.council, EXPECTED_COUNCIL);
|
|
589
589
|
await h.phaseResult("complete", P({ brainstorm: "complete" }));
|
|
590
590
|
assert.deepEqual(h.readRecord().derived.council, EXPECTED_COUNCIL);
|
|
591
|
-
assert.
|
|
591
|
+
assert.deepEqual(h.readRecord().derived.personas["spec-council-member"], { dispatches: 3, models: ["p/alpha:xhigh", "p/beta:high"], tokens: { input: 3, output: 0, cache_read: 0, cache_write: 0, cost: 0 } });
|
|
592
592
|
});
|
|
593
593
|
test("council defaults missing chair model and survives unrelated result before audit", async () => {
|
|
594
594
|
const h = await boundInBrainstorm();
|
|
@@ -727,6 +727,66 @@
|
|
|
727
727
|
assert.deepEqual(rec.derived.phases.plan?.tokens, { input: 152, output: 15, cache_read: 0, cache_write: 0, cost: 1.5 });
|
|
728
728
|
});
|
|
729
729
|
|
|
730
|
+
for (const [name, agents] of [
|
|
731
|
+
["chain", ["implementer", "spec-reviewer", "spec-reviewer"]],
|
|
732
|
+
["plain tasks", ["implementer", "implementer"]],
|
|
733
|
+
["empty results", []],
|
|
734
|
+
["missing results", undefined],
|
|
735
|
+
] as const) {
|
|
736
|
+
test(`failed subagent ${name}: count terminal retained results once, not progress`, async () => {
|
|
737
|
+
const h = harness();
|
|
738
|
+
await h.phaseResult("start", P({ brainstorm: "in_progress" }));
|
|
739
|
+
await h.writeSpec("doc/specs/a.md");
|
|
740
|
+
await h.phaseResult("start", P({ brainstorm: "complete", plan: "complete", implement: "in_progress" }));
|
|
741
|
+
await h.emit("tool_result", { toolName: "plan_tracker", toolCallId: "pt", input: {}, content: [], isError: false, details: { action: "init", tasks: [{ name: "repair", status: "in_progress" }] } });
|
|
742
|
+
const before = h.readRecord();
|
|
743
|
+
const results = agents?.map((agent, i) => ({ agent, exitCode: i === agents.length - 1 ? 1 : 0, model: `p/m${i}`, usage: usage(10, 2, 0.5) }));
|
|
744
|
+
const terminal = { ...subagentResult(results ?? []), isError: true, details: results === undefined ? {} : { results } };
|
|
745
|
+
for (const partial of [results?.slice(0, 1), results]) {
|
|
746
|
+
await h.emit("tool_execution_update", { type: "tool_execution_update", toolName: "subagent", toolCallId: "sa", args: {}, partialResult: { content: [], details: { results: partial ?? [] } } });
|
|
747
|
+
}
|
|
748
|
+
assert.deepEqual(h.readRecord(), before);
|
|
749
|
+
await h.emit("tool_result", terminal);
|
|
750
|
+
const rec = h.readRecord();
|
|
751
|
+
const dispatches = rec.events.filter((e) => e.kind === "dispatch");
|
|
752
|
+
assert.equal(dispatches.length, agents?.length ?? 0);
|
|
753
|
+
assert.deepEqual(dispatches.map((e) => ({ agent: e.agent, model: e.model, exit: e.exit, phase: e.phase })), results?.map((r) => ({ agent: r.agent, model: r.model, exit: r.exitCode || undefined, phase: "implement" })) ?? []);
|
|
754
|
+
for (const agent of new Set(agents)) {
|
|
755
|
+
const retained = results!.filter((r) => r.agent === agent);
|
|
756
|
+
assert.deepEqual(rec.derived.personas[agent], { dispatches: retained.length, models: retained.map((r) => r.model), tokens: { input: 10 * retained.length, output: 2 * retained.length, cache_read: 0, cache_write: 0, cost: 0.5 * retained.length } });
|
|
757
|
+
}
|
|
758
|
+
if (name === "chain") assert.deepEqual(rec.derived.reviews?.["spec-reviewer"], { dispatches: 2, nonzero_exit: 1 });
|
|
759
|
+
if (results?.length) assert.deepEqual(rec.derived.phases.implement?.tokens, { input: 10 * results.length, output: 2 * results.length, cache_read: 0, cache_write: 0, cost: 0.5 * results.length });
|
|
760
|
+
else assert.deepEqual(rec, before);
|
|
761
|
+
assert.deepEqual(rec.events.filter((e) => e.kind === "phase" || e.kind === "gate"), before.events.filter((e) => e.kind === "phase" || e.kind === "gate"));
|
|
762
|
+
assert.deepEqual(rec.derived.plan, before.derived.plan);
|
|
763
|
+
assert.deepEqual(rec.derived.gates, before.derived.gates);
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
test("failed spec and conformance dispatches count usage and failures without advancing successful gates", async () => {
|
|
768
|
+
const h = await boundInPlan();
|
|
769
|
+
await h.phaseResult("start", P({ brainstorm: "complete", plan: "complete", implement: "complete", verify: "in_progress" }));
|
|
770
|
+
const results = ["spec-summarizer", "conformance-reviewer"].map((agent) => ({ agent, exitCode: 1, model: "p/r", usage: usage(3) }));
|
|
771
|
+
const before = h.readRecord();
|
|
772
|
+
await h.emit("tool_result", { ...subagentResult(results, "Conformance verdict: GAPS\nG1:\n verdict: MISSING"), isError: true });
|
|
773
|
+
let rec = h.readRecord();
|
|
774
|
+
assert.deepEqual(rec.events.filter((e) => e.kind === "dispatch").map((e) => ({ agent: e.agent, exit: e.exit, phase: e.phase })), results.map((r) => ({ agent: r.agent, exit: 1, phase: "verify" })));
|
|
775
|
+
for (const { agent } of results) assert.deepEqual(rec.derived.personas[agent], { dispatches: 1, models: ["p/r"], tokens: { input: 3, output: 0, cache_read: 0, cache_write: 0, cost: 0 } });
|
|
776
|
+
assert.equal(rec.derived.gates.spec_rounds, before.derived.gates.spec_rounds);
|
|
777
|
+
assert.equal(rec.derived.conformance_loops, before.derived.conformance_loops);
|
|
778
|
+
assert.equal(rec.derived.conformance_open_gaps, before.derived.conformance_open_gaps);
|
|
779
|
+
assert.deepEqual(rec.derived.reviews?.["conformance-reviewer"], { dispatches: 1, nonzero_exit: 1 });
|
|
780
|
+
assert.deepEqual(rec.derived.phases.verify?.tokens, { input: 6, output: 0, cache_read: 0, cache_write: 0, cost: 0 });
|
|
781
|
+
assert.deepEqual(rec.events.filter((e) => e.kind === "phase" || e.kind === "gate"), before.events.filter((e) => e.kind === "phase" || e.kind === "gate"));
|
|
782
|
+
await h.emit("tool_result", subagentResult(results.map((r) => ({ ...r, exitCode: 0 })), "Conformance verdict: GAPS\nG1:\n verdict: MISSING"));
|
|
783
|
+
rec = h.readRecord();
|
|
784
|
+
assert.equal(rec.derived.gates.spec_rounds, before.derived.gates.spec_rounds + 1);
|
|
785
|
+
assert.equal(rec.derived.conformance_loops, 1);
|
|
786
|
+
assert.equal(rec.derived.conformance_open_gaps, 1);
|
|
787
|
+
assert.deepEqual(rec.derived.reviews?.["conformance-reviewer"], { dispatches: 2, nonzero_exit: 1 });
|
|
788
|
+
});
|
|
789
|
+
|
|
730
790
|
test("conformance-reviewer during verify counts loops and retains the latest open gap count", async () => {
|
|
731
791
|
const h = await boundInPlan();
|
|
732
792
|
await h.phaseResult("complete", P({ brainstorm: "complete", plan: "complete" }));
|
package/extensions/telemetry.ts
CHANGED
|
@@ -548,7 +548,7 @@
|
|
|
548
548
|
}
|
|
549
549
|
if (event.toolName === "subagent") {
|
|
550
550
|
if (phaseNow() === "brainstorm") onCouncilResults(event.details, event.content);
|
|
551
|
-
|
|
551
|
+
await onSubagentResult(event.details, event.content, event.isError);
|
|
552
552
|
return undefined;
|
|
553
553
|
}
|
|
554
554
|
if (event.toolName === "plan_tracker" && !event.isError) {
|
|
@@ -670,7 +670,7 @@
|
|
|
670
670
|
flush();
|
|
671
671
|
};
|
|
672
672
|
|
|
673
|
-
const onSubagentResult = async (details: unknown, content: unknown) => {
|
|
673
|
+
const onSubagentResult = async (details: unknown, content: unknown, isError: boolean) => {
|
|
674
674
|
const results = (details as { results?: unknown[] } | undefined)?.results;
|
|
675
675
|
if (!Array.isArray(results) || results.length === 0) return;
|
|
676
676
|
const acc = live();
|
|
@@ -687,7 +687,7 @@
|
|
|
687
687
|
persona.dispatches += 1;
|
|
688
688
|
if (model && !persona.models.includes(model)) persona.models.push(model);
|
|
689
689
|
if (tokens) persona.tokens = addTokens(persona.tokens, tokens);
|
|
690
|
-
if (agent === "spec-summarizer") acc.gates.spec_rounds += 1;
|
|
690
|
+
if (!isError && agent === "spec-summarizer") acc.gates.spec_rounds += 1;
|
|
691
691
|
if (REVIEWER_AGENTS.has(agent)) {
|
|
692
692
|
const review = (acc.reviews[agent] ??= { dispatches: 0, nonzero_exit: 0 });
|
|
693
693
|
review.dispatches += 1;
|
|
@@ -699,7 +699,7 @@
|
|
|
699
699
|
review.findings.major += findings.major;
|
|
700
700
|
review.findings.minor += findings.minor;
|
|
701
701
|
}
|
|
702
|
-
if (agent === "conformance-reviewer") {
|
|
702
|
+
if (!isError && agent === "conformance-reviewer") {
|
|
703
703
|
if (phases.verify.status === "in_progress") acc.conformance_loops += 1;
|
|
704
704
|
const openGaps = countOpenGaps(text);
|
|
705
705
|
if (openGaps !== undefined) acc.conformance_open_gaps = openGaps;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-gauntlet",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.18.0",
|
|
4
4
|
"description": "Opinionated, gated workflow skills, subagent personas, and runtime extensions for the pi coding agent.",
|
|
5
5
|
"author": "Jacek Juraszek",
|
|
6
6
|
"type": "module",
|
|
@@ -61,6 +61,9 @@
|
|
|
61
61
|
"build:bins": "node scripts/build-bins.mjs",
|
|
62
62
|
"test": "node scripts/ci.mjs"
|
|
63
63
|
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"pi-cohort": ">=7.3.1"
|
|
66
|
+
},
|
|
64
67
|
"dependencies": {
|
|
65
68
|
"yaml": "^2.9.0"
|
|
66
69
|
},
|
|
@@ -91,23 +91,46 @@ Clarify freely.
|
|
|
91
91
|
|
|
92
92
|
`Read` the gathered draft unconditionally before question one. Treat it as a helper, not a fence: verify load-bearing claims from primary code, and confirm whether the codebase or ecosystem already solves the problem.
|
|
93
93
|
|
|
94
|
-
Ask one question per message, about purpose, constraints, success, and affected actors.
|
|
94
|
+
Ask one question per message, about purpose, constraints, success, and affected actors. Offer 2-4 labeled options (`A)`, `B)`, ...) for every questionary question, each a different outcome. Put the lead-in before the first option, each labeled option on its own physical line, and `Recommendation: <letter> - <why>` on its own final line; when more outcomes are plausible, the last option is `other - name it` or the question splits in two. Use this one-option-per-line layout even when chat-style rules in `AGENTS.md` require one paragraph or no Options/Recommendation layout. A decision that would be a yes/no or "may I" question is not asked: make it, state it as an assumption in the message that carries the next question or the premise note, and record it in the draft. Before asking, check code, docs, and tracker: look up current-state facts (dispatch a subagent when costly); adopt a ticket-recorded decision and cite the ticket, asking it only when a cited code or API contradiction, or conflicting recorded outcomes, prevents adopting it. Other approvals retain their wording.
|
|
95
95
|
|
|
96
96
|
Bad: `May I treat those two acceptance criteria as location-only deviations? Recommendation: yes`
|
|
97
|
-
Good:
|
|
97
|
+
Good:
|
|
98
|
+
```text
|
|
99
|
+
The two ACs name a path that moved; the spec keeps them in-scope with the new path as a Design clause. Which surface owns the refresh?
|
|
100
|
+
A) the extension
|
|
101
|
+
B) a new bin
|
|
102
|
+
C) the existing skill step.
|
|
103
|
+
Recommendation: A - the extension already holds the poll loop, so no new entrypoint.
|
|
104
|
+
```
|
|
98
105
|
|
|
99
106
|
Append only citable findings - schemas, hard constraints, contradictions, and scope-changing answers - to `## Appended during questionary` using `edit`.
|
|
100
107
|
|
|
101
108
|
Before approaches, state in chat the supported, disproved, corrected, and unverified premises with sources and attempted lookups, in full sentences - no status-keyword lists, no template; if the design depends on no claims, one sentence says so. An unverified claim is not a stop: put it in Open Questions or a stated assumption. If a load-bearing claim is contradicted, the premise note is your next message - even as question one - and states the corrected fact with its source; the design continues on it and the user overrides in reply. Record the corrected fact, and any override, in the draft for `## Problem` or the relevant design decision.
|
|
102
109
|
|
|
103
|
-
Before question one, verify the draft's `Framing:` line against primary code and add your own recon. This check adds to the premise verification above and never replaces it: trace every consumer, caller, and data path the ask's constraints name before writing the holds-statement or the question, and name those traces in the holds-statement, not only the files read. A framing concern exists when you can state a cited case that the ask is misdiagnosed, wrong-sized, already solved, a band-aid where the root cause is reachable, or cuts across a sound pattern - including a repo convention that is itself the debt - and can name the alternative it implies; an objection without an alternative is a lead to investigate, not a concern. With a concern, question one is the framing question in the questionary format:
|
|
110
|
+
Before question one, verify the draft's `Framing:` line against primary code and add your own recon. This check adds to the premise verification above and never replaces it: trace every consumer, caller, and data path the ask's constraints name before writing the holds-statement or the question, and name those traces in the holds-statement, not only the files read. A framing concern exists when you can state a cited case that the ask is misdiagnosed, wrong-sized, already solved, a band-aid where the root cause is reachable, or cuts across a sound pattern - including a repo convention that is itself the debt - and can name the alternative it implies; an objection without an alternative is a lead to investigate, not a concern. With a concern, question one is the framing question in the questionary format:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
A) as framed
|
|
114
|
+
B) pivot: <named alternative> - <cited reason>
|
|
115
|
+
C) smaller or none: <what ships, or what happens if nothing ships>
|
|
116
|
+
Recommendation: <letter> - <why>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
For a band-aid concern, put the root-cause alternative in B. Without a concern, open the first message with one sentence naming what was checked - `The framing holds: checked <x>, <y>` - and continue to the first ordinary question; a scout objection that yielded no alternative is named among the things checked.
|
|
104
120
|
|
|
105
121
|
After any premise correction in the same message, ask the framing question or give the holds-statement. Ask the framing question at most once per brainstorm, including after pushback; the user's reply is final, and later doubts on the same point go to Open Questions or a `Pattern:` line. Recommend; never pivot without the reply. A ticket-recorded decision on the exact point follows the ticket rule above; an adopted decision records `Framing: kept (ticket <id>)`. A pivot never rewrites AC rows: rows it no longer satisfies take `deviates:` dispositions, and a pivot that changes the topic re-mints the slug.
|
|
106
122
|
|
|
107
123
|
Record the outcome in the draft under `## Appended during questionary` as `Framing: kept` or `Framing: pivoted to <x> - <why>`; spec-writing carries that line into `## Problem`. On redraw entry (checklist step 4) the count restarts: run this evaluation once against the new draft. On redraw the draft is the approved spec: its `## Problem` `Framing:` line is context, not scout evidence, and the new outcome is recorded directly in `## Problem`. When the scout failed there is no `Framing:` line; the holds-statement or the question still comes from your own recon.
|
|
108
124
|
|
|
109
125
|
Bad: `The ask adds another settings path - should we? Recommendation: no`
|
|
110
|
-
Good:
|
|
126
|
+
Good:
|
|
127
|
+
```text
|
|
128
|
+
The ask adds a second settings path beside the resolver that already owns every setting (settings/resolver.ts:11).
|
|
129
|
+
A) as framed
|
|
130
|
+
B) pivot: a key under the existing resolver - one read path, no new boundary
|
|
131
|
+
C) smaller or none: document the default and ship no setting.
|
|
132
|
+
Recommendation: B - the owning schema already exists.
|
|
133
|
+
```
|
|
111
134
|
|
|
112
135
|
### 4. Explore approaches
|
|
113
136
|
|
|
@@ -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.
|
|
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 committed changes, get git SHAs:
|
|
29
29
|
```bash
|
|
30
30
|
BASE_SHA=$(git rev-parse HEAD~1) # or origin/main
|
|
31
31
|
HEAD_SHA=$(git rev-parse HEAD)
|
|
@@ -47,7 +47,7 @@ subagent({ agent: "code-reviewer", async: false, task: "... filled template ..."
|
|
|
47
47
|
- `{WHAT_WAS_IMPLEMENTED}` - What you just built
|
|
48
48
|
- `{PLAN_OR_REQUIREMENTS}` - What it should do
|
|
49
49
|
- `{BASE_SHA}` - Starting commit
|
|
50
|
-
- `{HEAD_SHA}` - Ending commit
|
|
50
|
+
- `{HEAD_SHA}` - Ending commit, or `WORKTREE` for an integrated uncommitted wave
|
|
51
51
|
- `{DESCRIPTION}` - Brief summary
|
|
52
52
|
- `{SCOPED_TEST_COMMANDS}` - the scoped verification commands the reviewer may run, or `none`
|
|
53
53
|
|
|
@@ -40,9 +40,16 @@ Before writing the report:
|
|
|
40
40
|
**Base:** {BASE_SHA}
|
|
41
41
|
**Head:** {HEAD_SHA}
|
|
42
42
|
|
|
43
|
+
Use a commit SHA for committed changes, or the literal `WORKTREE` for an integrated uncommitted wave. For `WORKTREE`, the caller stages every new deliverable before dispatch; read the current checkout, including staged and unstaged changes, against Base. Use the same source on re-review.
|
|
44
|
+
|
|
43
45
|
```bash
|
|
44
|
-
|
|
45
|
-
git diff {BASE_SHA}
|
|
46
|
+
if [ "{HEAD_SHA}" = WORKTREE ]; then
|
|
47
|
+
git diff --stat {BASE_SHA} -- .
|
|
48
|
+
git diff --binary {BASE_SHA} -- .
|
|
49
|
+
else
|
|
50
|
+
git diff --stat {BASE_SHA}..{HEAD_SHA}
|
|
51
|
+
git diff {BASE_SHA}..{HEAD_SHA}
|
|
52
|
+
fi
|
|
46
53
|
```
|
|
47
54
|
|
|
48
55
|
## Review Checklist
|
|
@@ -39,7 +39,7 @@ Periodic "should I continue?" prompts add latency without adding safety. The pla
|
|
|
39
39
|
|
|
40
40
|
## Prerequisites
|
|
41
41
|
|
|
42
|
-
- The worktree path is known - from the `using-git-worktrees` Step 4 report in this session or the handoff brief's `worktree:` row - and is passed as the top-level dispatch `cwd
|
|
42
|
+
- The worktree path is known - from the `using-git-worktrees` Step 4 report in this session or the handoff brief's `worktree:` row - and is passed as the top-level dispatch `cwd`, except per-patch spec reviews use [Spec-review snapshots](#spec-review-snapshots). If no worktree path exists, stop and invoke `/skill:using-git-worktrees` first - implementation never lands directly on `main`. The process cwd is the primary checkout; nothing here changes it.
|
|
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
|
|
|
@@ -73,11 +73,11 @@ After all tasks: proceed to [After All Tasks](#after-all-tasks-complete) - it ow
|
|
|
73
73
|
|
|
74
74
|
One rule governs both review loops - spec-compliance and code-quality - in sequential and parallel-wave modes alike.
|
|
75
75
|
|
|
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. Code re-reviews also carry a `## Fix delta` block holding the fix round just integrated: sequential mode, the output of `git diff <HEAD the prior review was dispatched against> HEAD`; wave mode (HEAD unmoved until step 7), the applied fix patch(es) verbatim, never a SHA range. Whole-diff re-reviews never carry the block. Omit it otherwise only when the input is unavailable (a fresh-session resume); the reviewer then treats the task's whole diff as the delta.
|
|
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. Code re-reviews also carry a `## Fix delta` block holding the fix round just integrated: sequential mode, the output of `git diff <HEAD the prior review was dispatched against> HEAD`; wave mode (HEAD unmoved until step 7), the applied fix patch(es) verbatim, or the in-place fix delta captured in wave step 6, never a SHA range. Whole-diff re-reviews never carry the block. Omit it otherwise only when the input is unavailable (a fresh-session resume); the reviewer then treats the task's whole diff as the delta.
|
|
77
77
|
|
|
78
78
|
**Fix fan-out.** When a fix round is fanned out per `/skill:requesting-code-review` Fix rounds, dispatch per `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
79
|
|
|
80
|
-
**Behaviour-change reroute.** When the triggering CR report carries `Behaviour-change: yes`, the fix round's re-review is SR first, then CR. The SR dispatch reviews the fix diff against 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`. In wave mode the SR re-review targets the task(s) whose files the fix touched.
|
|
80
|
+
**Behaviour-change reroute.** When the triggering CR report carries `Behaviour-change: yes`, the fix round's re-review is SR first, then CR. The SR dispatch reviews the fix diff against 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`. In wave mode the SR re-review targets the task(s) whose files the fix touched; after integration, dispatch it against the current implementation worktree, not a pre-integration snapshot.
|
|
81
81
|
|
|
82
82
|
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
83
|
|
|
@@ -104,7 +104,7 @@ Every implementer dispatch returns one of:
|
|
|
104
104
|
|
|
105
105
|
| Status | Meaning | Orchestrator action |
|
|
106
106
|
|---|---|---|
|
|
107
|
-
| `DONE` | Implemented + tests pass + self-reviewed + committed | Proceed to spec review |
|
|
107
|
+
| `DONE` | Implemented + tests pass + self-reviewed + committed (in-place wave repairs leave the commit to wave step 7) | Proceed to spec review |
|
|
108
108
|
| `DONE_WITH_CONCERNS` | Implemented + tests pass, but flagged a deviation or risk | Read concerns. If acceptable, proceed. If not, dispatch fix subagent. |
|
|
109
109
|
| `NEEDS_CONTEXT` | Implementer needs information you have | Answer the questions, re-dispatch with answers in the task text |
|
|
110
110
|
| `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,7 +121,7 @@ Implementer prompt templates must instruct subagents to return one of these stat
|
|
|
121
121
|
|
|
122
122
|
## Dispatch
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
Pass the implementation worktree as top-level `cwd` - sequential mode included. For per-patch spec reviews, use [Spec-review snapshots](#spec-review-snapshots) instead.
|
|
125
125
|
|
|
126
126
|
```ts
|
|
127
127
|
// implementer
|
|
@@ -130,8 +130,8 @@ subagent({ agent: "implementer", async: false, cwd: "<abs worktree path>", task:
|
|
|
130
130
|
// escalated fix round (Fix-Loop Rounds): same fix payload, model from gauntlet_setting({ key: "escalationLoop" }).implModel
|
|
131
131
|
subagent({ agent: "implementer", model: "<implModel>", context: "fresh", async: false, cwd: "<abs worktree path>", task: "<the just-dispatched fix payload + prior review report verbatim>" })
|
|
132
132
|
|
|
133
|
-
// spec compliance
|
|
134
|
-
subagent({ agent: "spec-reviewer", async: false, cwd: "<abs
|
|
133
|
+
// spec compliance: per-patch reviews use the materialized snapshot cwd
|
|
134
|
+
subagent({ agent: "spec-reviewer", async: false, cwd: "<abs review checkout path>", task: "<task text + patch diff + absolute spec path + task's Spec: anchors + Tests: block + Test: paths>" })
|
|
135
135
|
|
|
136
136
|
// code quality
|
|
137
137
|
subagent({ agent: "code-reviewer", async: false, cwd: "<abs worktree path>", task: "<diff range + SCOPED_TEST_COMMANDS (task commands; wave: union; whole-diff: none) + ask: production-ready?>" })
|
|
@@ -166,11 +166,11 @@ Auto-selected at handoff by `writing-plans` (any wave with ≥2 tasks) when the
|
|
|
166
166
|
**Per-wave loop:**
|
|
167
167
|
|
|
168
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; run those tasks as sequential single-task waves and note it.
|
|
169
|
-
2. **Start, then fan out.** Mark every wave index `in_progress` before one parallel foreground dispatch (shape below): `implementer` per task, `context: "fresh"`, `worktree: true`. Each returns a status + a patch.
|
|
170
|
-
3. **Status + spec review per task.** 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) in one parallel fan-out
|
|
171
|
-
4. **Integrate.** `git -C <worktree> apply
|
|
169
|
+
2. **Start, then fan out.** 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` before one parallel foreground dispatch (shape below): `implementer` per task, `context: "fresh"`, `worktree: true`. Each returns a status + a patch.
|
|
170
|
+
3. **Status + spec review per task.** 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) 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 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, and test execution is never the reviewer's job - in either mode (persona rule; the wave test gate in step 5 runs the wave's `SCOPED_TEST_COMMANDS`). Inline verdicts are fine at normal wave sizes; large waves use `output:` + `outputMode: "file-only"` to keep verdicts out of your context. **Re-dispatch by cause:** `BLOCKED`/`NEEDS_CONTEXT` per the [Implementer Status](#implementer-status) matrix; a **spec gap** re-dispatches the implementer (fresh, `worktree: true`) carrying the prior patch + the reviewer's findings, the new patch superseding the old at step 4. Loop until accepted + spec ✅, within [Fix-Loop Rounds](#fix-loop-rounds), same as sequential.
|
|
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. Apply fails = textual conflict → drop that task, finish the rest, re-run the dropped task sequentially on the updated HEAD.
|
|
172
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. Failure = semantic conflict or bug → re-run the offending task sequentially, else fix per [When a Subagent Fails](#when-a-subagent-fails).
|
|
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
|
|
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). For in-place repairs, stage the reviewed deliverables before dispatch and override the implementer commit step: leave the fix uncommitted and unstaged. After it returns, run `git -C <worktree> add -N -- <new-fix-paths>` for new deliverables, capture `git -C <worktree> diff --binary` as `## Fix delta`, then stage the fix's deliverable paths before SR/CR re-review. 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).
|
|
174
174
|
7. **Commit and complete the wave.** After the gate passes, commit with `git -C <worktree> add ... && git -C <worktree> commit ...`, then mark all of its existing indices `complete`. Leaves a clean tree; the next wave's children branch from this commit and so see the integrated work.
|
|
175
175
|
|
|
176
176
|
**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.
|
|
@@ -179,7 +179,7 @@ Auto-selected at handoff by `writing-plans` (any wave with ≥2 tasks) when the
|
|
|
179
179
|
|
|
180
180
|
**Caveat:** each task must be independently runnable and verifiable in a fresh worktree — no reliance on uncommitted local state. `pi-cohort` symlinks `node_modules`; repos needing other per-worktree setup must account for it.
|
|
181
181
|
|
|
182
|
-
Pass the worktree's absolute path as
|
|
182
|
+
Pass the implementation worktree's absolute path as top-level `cwd`; override each per-patch SR task's `cwd` with its snapshot path, without `worktree: true`. Rule and rationale for editing dispatches: `dispatching-parallel-agents` "pi-cohort Integration".
|
|
183
183
|
|
|
184
184
|
```bash
|
|
185
185
|
REPORT_DIR=$(mktemp -d)
|
|
@@ -202,6 +202,26 @@ subagent({
|
|
|
202
202
|
|
|
203
203
|
For the fan-out + worktree + patch-integration + conflict mechanics, see `dispatching-parallel-agents`.
|
|
204
204
|
|
|
205
|
+
## Spec-review snapshots
|
|
206
|
+
|
|
207
|
+
Materialize each accepted patch before its pre-integration spec review, including replacement patches on re-review. Keep the implementation worktree unchanged; pi-cohort removes its worker checkouts before returning patches.
|
|
208
|
+
|
|
209
|
+
Substitute shell-quoted literals for the implementation worktree, the recorded `WAVE_BASE_SHA`, and the absolute patch path. Run this block once per patch:
|
|
210
|
+
|
|
211
|
+
<!-- spec-review-snapshot-shell -->
|
|
212
|
+
```bash
|
|
213
|
+
SR_ROOT={{WORKTREE}}
|
|
214
|
+
SR_BASE={{BASE_SHA}}
|
|
215
|
+
SR_PATCH={{PATCH_PATH}}
|
|
216
|
+
SR_DIR=$(mktemp -d) || exit 1
|
|
217
|
+
SR_TREE="$SR_DIR/tree"
|
|
218
|
+
git -C "$SR_ROOT" worktree add --detach "$SR_TREE" "$SR_BASE" || exit 1
|
|
219
|
+
git -C "$SR_TREE" apply --index "$SR_PATCH" || exit 1
|
|
220
|
+
printf 'snapshot: %s\n' "$SR_TREE"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
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. A failed command or missing snapshot path stops dispatch; retain the failed snapshot for diagnosis. After a terminal review report is saved, remove its snapshot with `git -C <implementation-worktree> worktree remove --force <snapshot-path>`. Recreate from the replacement patch for each pre-integration re-review. Post-integration SR reviews the implementation worktree directly.
|
|
224
|
+
|
|
205
225
|
## When a Subagent Fails
|
|
206
226
|
|
|
207
227
|
**You are the orchestrator. You do NOT write code.**
|
|
@@ -12,8 +12,8 @@ Dispatch a subagent with the code-reviewer template:
|
|
|
12
12
|
|
|
13
13
|
DESCRIPTION: [task summary, from implementer's report]
|
|
14
14
|
PLAN_OR_REQUIREMENTS: Task N from [plan-file]
|
|
15
|
-
BASE_SHA: [commit before task]
|
|
16
|
-
HEAD_SHA: [current commit]
|
|
15
|
+
BASE_SHA: [commit before task; wave: WAVE_BASE_SHA recorded before worker dispatch]
|
|
16
|
+
HEAD_SHA: [current commit; integrated uncommitted wave: the literal WORKTREE, including re-reviews]
|
|
17
17
|
SCOPED_TEST_COMMANDS: [the consuming task's Tests: commands; wave reviews: the union of the wave's tasks' Tests: commands; `none` for the whole-diff verify-phase review]
|
|
18
18
|
```
|
|
19
19
|
|