pi-gauntlet 4.8.2 → 4.9.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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## v4.9.0 - 2026-08-14
4
+
5
+ Review fix rounds parallelize when the reviewer certifies disjoint findings.
6
+
7
+ - `plan_tracker`: new `add` action - appends tasks as `pending`, preserving existing statuses; fix sub-waves extend the tracker instead of re-initializing it (conformance fix rounds no longer wipe the implement phase's completed task list).
8
+ - `dispatching-parallel-agents`: new "Fix fan-out" section - reviewer-certified `disjoint` finding groups fix in one parallel wave (one implementer per finding, verbatim finding blocks, serial integration, one re-review); silent sequential degradation when no certificate is present.
9
+ - Reviewer contracts (`spec-reviewer-prompt`, `code-reviewer` template + persona, `conformance-reviewer`): global finding IDs (`F<n>`/`G<n>`), per-finding `touched-files`/`touched-resources`, and a shared `Parallel-safe:` partition grammar (drift-guarded copies across templates and personas).
10
+ - `subagent-driven-development`, `requesting-code-review`, `verification-before-completion/conformance-check`: fix loops reference the shared fan-out rule; `requesting-code-review` gains a minimal fix loop (2 rounds, then escalate); severity vocabulary unified on Critical/Moderate/Minor.
11
+
3
12
  ## v4.8.2 - 2026-08-14
4
13
 
5
14
  Tracker-neutral skill wording - Linear is an example, not the canonical vocabulary.
@@ -32,12 +32,15 @@ Verdict: SHIP | FIX_FIRST | REJECT
32
32
  Confidence: low | medium | high (based on how much you could verify locally)
33
33
 
34
34
  Findings:
35
- - [Critical] path/to/file.ts:42 — one-sentence problem
35
+ - [Critical] F1: path/to/file.ts:42 — one-sentence problem
36
36
  Fix: one or two sentences.
37
- - [Moderate] ...
38
- - [Minor] [shrink] path/to/file.ts:30 — manual loop builds dict; `dict(zip(keys, values))`, 1 line.
37
+ touched-files: path/to/file.ts
38
+ touched-resources: none
39
+ - [Moderate] F2: ...
40
+ - [Minor] F3: [shrink] path/to/file.ts:30 — manual loop builds dict; `dict(zip(keys, values))`, 1 line.
39
41
 
40
42
  Complexity: net -<N> lines (omit if nothing to cut)
43
+ Parallel-safe: F1,F3 disjoint; F2 conflicts F1 (both touch auth.ts)
41
44
  ```
42
45
 
43
46
  Severity:
@@ -46,4 +49,27 @@ Severity:
46
49
  - **Moderate** — should fix; open for discussion (significant but not strictly blocking).
47
50
  - **Minor** — nit, style, preference, suggestion.
48
51
 
52
+ Label every finding with a globally unique `F1..Fn` ID (no restart per severity),
53
+ and a `touched-files:`/`touched-resources:` pair (files/resources a fix would
54
+ touch, or the literal `none`). On any issue-bearing review end the findings
55
+ with one partition line over the `Fn` IDs assigned above; when a task requires
56
+ a trailing `TRAJECTORY:` verdict (re-review), that verdict follows it as the
57
+ true final line:
58
+
59
+ <!-- grammar identical to skills/requesting-code-review/code-reviewer.md — change them together or not at all; writing-plans' plan-time Parallel-safe: line is a deliberately different free-text form, do NOT unify -->
60
+
61
+ ```
62
+ Parallel-safe: <group>[; <group>]*
63
+ <group> = <comma-separated finding-id list> " disjoint"
64
+ | <finding-id> " conflicts " <finding-id> " (" <reason> ")"
65
+ ```
66
+
67
+ Example: `Parallel-safe: F1,F3 disjoint; F2 conflicts F1 (both touch auth.ts)`
68
+
69
+ IDs inside a `disjoint` list are mutually parallel-safe (their fixes can run
70
+ concurrently). Any file OR runtime-resource overlap between two findings' fixes
71
+ forces `conflicts`. Runtime-resource disjointness is estimated over: DB/schema,
72
+ port, fixture, external service, shared temp path. When you cannot confidently
73
+ certify a pair disjoint, mark them `conflicts` (conservative default = serial).
74
+
49
75
  If you ran verification commands, quote them and their output verbatim under a `Verification:` section. If you did not, say so.
@@ -99,6 +99,8 @@ Empty values use the literal tokens `absent` / `none` / `unknown` — never a bl
99
99
  After the gap blocks, emit one `Parallel-safe:` line so the orchestrator does not
100
100
  re-derive fix concurrency:
101
101
 
102
+ <!-- grammar identical to skills/subagent-driven-development/spec-reviewer-prompt.md and skills/requesting-code-review/code-reviewer.md (modulo G vs F id prefix) — change them together or not at all; writing-plans' plan-time Parallel-safe: line is a deliberately different free-text form, do NOT unify -->
103
+
102
104
  ```
103
105
  Parallel-safe: <group>[; <group>]*
104
106
  <group> = <comma-separated gap-id list> " disjoint"
@@ -112,9 +114,7 @@ Parallel-safe: G1,G3 disjoint; G2 conflicts G1 (both touch auth.ts); G4 conflict
112
114
  ```
113
115
 
114
116
  Any **file OR runtime-resource** overlap forces the conflicting gaps into separate
115
- serial waves — identical to planned-execution wave grouping. Runtime-resource
116
- disjointness is not machine-checkable; estimate it as `writing-plans`' Runtime-resource
117
- disjointness rule does. When you cannot confidently certify a pair disjoint, mark them
117
+ serial waves — identical to planned-execution wave grouping. Runtime-resource disjointness is not machine-checkable; estimate it over: DB/schema, port, fixture, external service, shared temp path. When you cannot confidently certify a pair disjoint, mark them
118
118
  `conflicts` (conservative default = serial).
119
119
 
120
120
  ### `recommended` selection policy
@@ -25,20 +25,58 @@ You are a spec compliance reviewer. Your job is to verify that an implementation
25
25
  ```
26
26
  Per-requirement status:
27
27
  - [MET] REQ-1: short requirement text — evidence: file.ts:42
28
- - [PARTIAL] REQ-2: ... — evidence: file.ts:80; missing: ...
29
- - [MISSING] REQ-3: ... — searched: <where>
28
+ - [PARTIAL] F1: REQ-2: ... — evidence: file.ts:80; missing: ...
29
+ touched-files: file.ts
30
+ touched-resources: none
31
+ - [MISSING] F2: REQ-3: ... — searched: <where>
32
+ touched-files: file.ts, other.ts
33
+ touched-resources: none
30
34
  - [OUT_OF_SCOPE] REQ-4: ... — flagged as non-goal in spec
31
35
 
32
36
  Scope creep (not in spec, but present):
33
- - file.ts:120 — short description
37
+ - F3: widget.ts:120 — short description
38
+ touched-files: widget.ts
39
+ touched-resources: none
34
40
 
35
41
  Missing from implementation:
36
- - REQ-3 — short description
42
+ - F2: REQ-3 — short description
37
43
 
38
44
  Verdict: COMPLIANT | NEEDS_REWORK | OUT_OF_SCOPE_CHANGES
39
45
  Confidence: low | medium | high
46
+
47
+ Parallel-safe: F1,F3 disjoint; F2 conflicts F1 (both touch file.ts)
40
48
  ```
41
49
 
50
+ ## Finding IDs and fix-concurrency certification
51
+
52
+ Label every finding (each `PARTIAL`/`MISSING` requirement, each scope-creep
53
+ item) with a globally unique ID `F1..Fn`, numbered across the whole report
54
+ (no restart per section). Each finding carries:
55
+
56
+ - `touched-files:` — files a fix would edit (not just the evidence location), comma-separated, or the literal `none`
57
+ - `touched-resources:` — shared runtime resources a fix or its verification touches (DB/schema, port, fixture, external service, shared temp path), or the literal `none`
58
+
59
+ On any issue-bearing review (any `PARTIAL`, `MISSING`, or scope-creep finding),
60
+ end the findings with one partition line over the `Fn` IDs assigned above; when
61
+ a task requires a trailing `TRAJECTORY:` verdict (re-review), that verdict
62
+ follows it as the true final line:
63
+
64
+ <!-- grammar identical to skills/subagent-driven-development/spec-reviewer-prompt.md — change them together or not at all; writing-plans' plan-time Parallel-safe: line is a deliberately different free-text form, do NOT unify -->
65
+
66
+ ```
67
+ Parallel-safe: <group>[; <group>]*
68
+ <group> = <comma-separated finding-id list> " disjoint"
69
+ | <finding-id> " conflicts " <finding-id> " (" <reason> ")"
70
+ ```
71
+
72
+ Example: `Parallel-safe: F1,F3 disjoint; F2 conflicts F1 (both touch auth.ts)`
73
+
74
+ IDs inside a `disjoint` list are mutually parallel-safe (their fixes can run
75
+ concurrently). Any file OR runtime-resource overlap between two findings' fixes
76
+ forces `conflicts`. Runtime-resource disjointness is estimated over: DB/schema,
77
+ port, fixture, external service, shared temp path. When you cannot confidently
78
+ certify a pair disjoint, mark them `conflicts` (conservative default = serial).
79
+
42
80
  ## Rules
43
81
 
44
82
  - You are **read-only**. Never edit files.
@@ -0,0 +1,66 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+ import registerPlanTracker from "./plan-tracker.ts";
4
+
5
+ type ToolResult = {
6
+ content: { type: string; text: string }[];
7
+ details: { action: string; tasks: { name: string; status: string }[]; error?: string };
8
+ };
9
+
10
+ function harness(branch: unknown[] = []) {
11
+ const tools: { name: string; execute: (...args: any[]) => unknown }[] = [];
12
+ const pi = {
13
+ on(_event: string, _handler: unknown) {},
14
+ registerTool(tool: { name: string; execute: (...args: any[]) => unknown }) {
15
+ tools.push(tool);
16
+ },
17
+ };
18
+ registerPlanTracker(pi as any);
19
+ const ctx = { hasUI: false, sessionManager: { getBranch: () => branch } };
20
+ const call = async (params: Record<string, unknown>): Promise<ToolResult> =>
21
+ (await tools[0].execute("id", params, undefined, undefined, ctx)) as ToolResult;
22
+ return { call };
23
+ }
24
+
25
+ test("add appends pending tasks and preserves existing statuses", async () => {
26
+ const { call } = harness();
27
+ await call({ action: "init", tasks: ["a", "b", "c"] });
28
+ await call({ action: "update", index: 0, status: "complete" });
29
+ const res = await call({ action: "add", tasks: ["d", "e"] });
30
+ assert.equal(res.details.error, undefined);
31
+ assert.equal(res.details.action, "add");
32
+ assert.deepEqual(
33
+ res.details.tasks.map((t) => [t.name, t.status]),
34
+ [["a", "complete"], ["b", "pending"], ["c", "pending"], ["d", "pending"], ["e", "pending"]],
35
+ );
36
+ });
37
+
38
+ test("add with no active plan creates one", async () => {
39
+ const { call } = harness();
40
+ const res = await call({ action: "add", tasks: ["g1"] });
41
+ assert.equal(res.details.error, undefined);
42
+ assert.deepEqual(res.details.tasks, [{ name: "g1", status: "pending" }]);
43
+ });
44
+
45
+ test("add result carries the FULL merged list (reconstruction invariant)", async () => {
46
+ const { call } = harness();
47
+ await call({ action: "init", tasks: ["a"] });
48
+ await call({ action: "update", index: 0, status: "in_progress" });
49
+ const res = await call({ action: "add", tasks: ["b"] });
50
+ // reconstructState rebuilds wholesale from the latest details.tasks:
51
+ // the add result alone must reproduce the whole plan.
52
+ assert.deepEqual(res.details.tasks, [
53
+ { name: "a", status: "in_progress" },
54
+ { name: "b", status: "pending" },
55
+ ]);
56
+ });
57
+
58
+ test("add with empty/missing tasks errors and preserves state", async () => {
59
+ const { call } = harness();
60
+ await call({ action: "init", tasks: ["a"] });
61
+ const res = await call({ action: "add", tasks: [] });
62
+ assert.equal(res.details.error, "tasks required");
63
+ assert.deepEqual(res.details.tasks, [{ name: "a", status: "pending" }]);
64
+ const res2 = await call({ action: "add" });
65
+ assert.equal(res2.details.error, "tasks required");
66
+ });
@@ -19,18 +19,18 @@ interface Task {
19
19
  }
20
20
 
21
21
  interface PlanTrackerDetails {
22
- action: "init" | "update" | "status" | "clear";
22
+ action: "init" | "add" | "update" | "status" | "clear";
23
23
  tasks: Task[];
24
24
  error?: string;
25
25
  }
26
26
 
27
27
  const PlanTrackerParams = Type.Object({
28
- action: StringEnum(["init", "update", "status", "clear"] as const, {
28
+ action: StringEnum(["init", "add", "update", "status", "clear"] as const, {
29
29
  description: "Action to perform",
30
30
  }),
31
31
  tasks: Type.Optional(
32
32
  Type.Array(Type.String(), {
33
- description: "Task names (for init)",
33
+ description: "Task names (for init and add)",
34
34
  }),
35
35
  ),
36
36
  index: Type.Optional(
@@ -129,7 +129,7 @@ export default function (pi: ExtensionAPI) {
129
129
  name: "plan_tracker",
130
130
  label: "Plan Tracker",
131
131
  description:
132
- "Track progress while EXECUTING an implementation plan (the implement phase) or a verify-phase conformance fix wave. Actions: init (set task list), update (change task status), status (show current state), clear (remove plan). Do NOT use for brainstorming, research, or planning checklists: those phases are open-ended and a bounded task list misrepresents them as a fixed N-step process.",
132
+ "Track progress while EXECUTING an implementation plan (the implement phase) or a verify-phase conformance fix wave. Actions: init (set task list), add (append tasks as pending; existing statuses preserved), update (change task status), status (show current state), clear (remove plan). Do NOT use for brainstorming, research, or planning checklists: those phases are open-ended and a bounded task list misrepresents them as a fixed N-step process.",
133
133
  parameters: PlanTrackerParams,
134
134
 
135
135
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
@@ -158,6 +158,30 @@ export default function (pi: ExtensionAPI) {
158
158
  };
159
159
  }
160
160
 
161
+ case "add": {
162
+ if (!params.tasks || params.tasks.length === 0) {
163
+ return {
164
+ content: [{ type: "text", text: "Error: tasks array required for add" }],
165
+ details: {
166
+ action: "add",
167
+ tasks: tasks.map((t) => ({ ...t })),
168
+ error: "tasks required",
169
+ } as PlanTrackerDetails,
170
+ };
171
+ }
172
+ tasks.push(...params.tasks.map((name) => ({ name, status: "pending" as TaskStatus })));
173
+ updateWidget(ctx);
174
+ return {
175
+ content: [
176
+ {
177
+ type: "text",
178
+ text: `Added ${params.tasks.length} tasks (${tasks.length} total).\n${formatStatus(tasks)}`,
179
+ },
180
+ ],
181
+ details: { action: "add", tasks: tasks.map((t) => ({ ...t })) } as PlanTrackerDetails,
182
+ };
183
+ }
184
+
161
185
  case "update": {
162
186
  if (params.index === undefined || !params.status) {
163
187
  return {
@@ -248,7 +272,7 @@ export default function (pi: ExtensionAPI) {
248
272
  text += ` ${theme.fg("accent", `[${args.index}]`)}`;
249
273
  if (args.status) text += ` → ${theme.fg("dim", args.status)}`;
250
274
  }
251
- if (args.action === "init" && args.tasks) {
275
+ if ((args.action === "init" || args.action === "add") && args.tasks) {
252
276
  text += ` ${theme.fg("dim", `(${args.tasks.length} tasks)`)}`;
253
277
  }
254
278
  return new Text(text, 0, 0);
@@ -273,6 +297,12 @@ export default function (pi: ExtensionAPI) {
273
297
  0,
274
298
  0,
275
299
  );
300
+ case "add":
301
+ return new Text(
302
+ theme.fg("success", "✓ ") + theme.fg("muted", `Added tasks (${taskList.length} total)`),
303
+ 0,
304
+ 0,
305
+ );
276
306
  case "update": {
277
307
  const complete = taskList.filter((t) => t.status === "complete").length;
278
308
  return new Text(
@@ -30,6 +30,8 @@ const sources = {
30
30
  Boolean: schema,
31
31
  Union: schema,
32
32
  Null: schema,
33
+ Array: schema,
34
+ Integer: schema,
33
35
  };
34
36
  `,
35
37
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "4.8.2",
3
+ "version": "4.9.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",
@@ -104,6 +104,20 @@ When agents return:
104
104
 
105
105
  **If some agents failed:** Integrate successful agents first (commit their work). Then retry the failed agent with fresh context that includes the integrated changes.
106
106
 
107
+ ## Fix fan-out
108
+
109
+ Fix rounds in review loops reuse the fan-out mechanics above, keyed off the reviewer's partition certificate — the orchestrator never partitions findings itself.
110
+
111
+ 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 finding IDs**: that group IS the parallel wave — dispatch **one `implementer` per finding ID in the group** (`context: "fresh"`, `worktree: true`, `cwd` = the current worktree; task = that finding's block **verbatim**, including its `touched-files` line as the ownership boundary). A 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). 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.
112
+
113
+ **Precondition:** a clean committed HEAD containing the code under review. When the reviewed change is an unintegrated patch (a wave-mode per-patch spec review), each fix task branches from the wave's base HEAD and carries the prior patch verbatim in its task text — the consuming loop's existing re-dispatch protocol. When the tree is dirty (e.g. post-integration, before the wave commit), the fan-out is unavailable: fix sequentially in place.
114
+
115
+ **Degradation:** missing, malformed, or ID-less `Parallel-safe:` line, or no `disjoint` group with ≥ 2 IDs → fully sequential fixes. Degradation is silent — it costs parallelism, never correctness.
116
+
117
+ **After the fix wave:** integrate patches serially per "Review and Integrate" above (mis-partition is self-healing: integrate the successes, re-run the conflicting finding sequentially on integrated HEAD); run the consuming loop's scoped test gate on the integrated tree; then one re-review of the integrated fix delta, per the consuming loop's own rules. The fan-out counts as one fix round against the consuming loop's budget — it grants no extra rounds.
118
+
119
+ **Progress:** `plan_tracker({ action: "add" })` one task per fixed finding, named mechanically — `"<prefix>fix F<n>: <finding's first line verbatim>"`, where `<prefix>` is `"W<k>-"` inside an execution wave and empty elsewhere. Fix tasks always extend the tracker, never re-init. Mark `in_progress` at dispatch, `complete` at integration.
120
+
107
121
  ## Agent Prompt Structure
108
122
 
109
123
  Good agent prompts are:
@@ -52,10 +52,12 @@ subagent({ agent: "code-reviewer", task: "... filled template ..." })
52
52
 
53
53
  **3. Act on feedback:**
54
54
  - Fix Critical issues immediately
55
- - Fix Important issues before proceeding
55
+ - Fix Moderate issues before proceeding
56
56
  - Note Minor issues for later
57
57
  - Push back if reviewer is wrong (with reasoning)
58
58
 
59
+ **Fix rounds.** Critical and Moderate findings trigger a 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 findings. After integration and the project's test command, re-dispatch the reviewer once on the integrated delta. If Critical or Moderate findings remain, run one more fix round and one more re-review; still failing → escalate to the user. Minor findings never trigger the fan-out.
60
+
59
61
  ## Example
60
62
 
61
63
  ```
@@ -76,7 +78,7 @@ HEAD_SHA=$(git rev-parse HEAD)
76
78
  [Subagent returns]:
77
79
  Strengths: Clean architecture, real tests
78
80
  Issues:
79
- Important: Missing progress indicators
81
+ Moderate: Missing progress indicators
80
82
  Minor: Magic number (100) for reporting interval
81
83
  Assessment: Ready to proceed
82
84
 
@@ -100,7 +102,7 @@ You: [Fix progress indicators]
100
102
  **Never:**
101
103
  - Skip review because "it's simple"
102
104
  - Ignore Critical issues
103
- - Proceed with unfixed Important issues
105
+ - Proceed with unfixed Moderate issues
104
106
  - Argue with valid technical feedback
105
107
 
106
108
  **If reviewer wrong:**
@@ -21,7 +21,7 @@ You are reviewing code changes for production readiness.
21
21
 
22
22
  Before writing the report:
23
23
 
24
- - **Not everything is Critical.** Reserve Critical for bugs, data loss, security, broken functionality. A missing helper method is Important. A naming preference is Minor.
24
+ - **Not everything is Critical.** Reserve Critical for bugs, data loss, security, broken functionality. A missing helper method is Moderate. A naming preference is Minor.
25
25
  - **Lead with strengths.** Accurate praise earns the implementer's trust on the critique that follows. Generic praise ("good code") undermines it.
26
26
  - **If you wouldn't block a PR over it, it's not Critical.** Be honest with yourself about severity before assigning it.
27
27
  - **Plan deviations get their own treatment.** If the implementation diverged from the spec/plan — added scope, removed scope, changed an interface — call it out under a dedicated "Plan Deviations" heading, not buried in Critical or Minor.
@@ -90,17 +90,20 @@ git diff {BASE_SHA}..{HEAD_SHA}
90
90
  #### Critical (Must Fix)
91
91
  [Bugs, security issues, data loss risks, broken functionality]
92
92
 
93
- #### Important (Should Fix)
93
+ #### Moderate (Should Fix)
94
94
  [Architecture problems, missing features, poor error handling, test gaps]
95
95
 
96
96
  #### Minor (Nice to Have)
97
97
  [Code style, optimization opportunities, documentation improvements]
98
98
 
99
99
  **For each issue:**
100
+ - `Fn` label - globally unique, numbered across the whole report (no restart per severity section)
100
101
  - File:line reference
101
102
  - What's wrong
102
103
  - Why it matters
103
104
  - How to fix (if not obvious)
105
+ - `touched-files:` - files a fix would edit (not just the evidence location), comma-separated, or the literal `none`
106
+ - `touched-resources:` - shared runtime resources a fix or its verification touches (DB/schema, port, fixture, external service, shared temp path), or the literal `none`
104
107
 
105
108
  ### Recommendations
106
109
  [Improvements for code quality, architecture, or process]
@@ -111,6 +114,27 @@ git diff {BASE_SHA}..{HEAD_SHA}
111
114
 
112
115
  **Reasoning:** [Technical assessment in 1-2 sentences]
113
116
 
117
+ ### Fix-concurrency certification
118
+
119
+ On any issue-bearing review, end the report with one partition line over the
120
+ `Fn` IDs assigned above:
121
+
122
+ <!-- grammar identical to agents/conformance-reviewer.md (modulo G vs F id prefix) — change them together or not at all; writing-plans' plan-time Parallel-safe: line is a deliberately different free-text form, do NOT unify -->
123
+
124
+ ```
125
+ Parallel-safe: <group>[; <group>]*
126
+ <group> = <comma-separated finding-id list> " disjoint"
127
+ | <finding-id> " conflicts " <finding-id> " (" <reason> ")"
128
+ ```
129
+
130
+ Example: `Parallel-safe: F1,F3 disjoint; F2 conflicts F1 (both touch auth.ts)`
131
+
132
+ IDs inside a `disjoint` list are mutually parallel-safe (their fixes can run
133
+ concurrently). Any file OR runtime-resource overlap between two findings' fixes
134
+ forces `conflicts`. Runtime-resource disjointness is estimated over: DB/schema,
135
+ port, fixture, external service, shared temp path. When you cannot confidently
136
+ certify a pair disjoint, mark them `conflicts` (conservative default = serial).
137
+
114
138
  ## Critical Rules
115
139
 
116
140
  **DO:**
@@ -137,22 +161,28 @@ git diff {BASE_SHA}..{HEAD_SHA}
137
161
 
138
162
  ### Issues
139
163
 
140
- #### Important
141
- 1. **Missing help text in CLI wrapper**
164
+ #### Moderate
165
+ F1. **Missing help text in CLI wrapper**
142
166
  - File: index-conversations:1-31
143
167
  - Issue: No --help flag, users won't discover --concurrency
144
168
  - Fix: Add --help case with usage examples
169
+ - touched-files: index-conversations.ts
170
+ - touched-resources: none
145
171
 
146
- 2. **Date validation missing**
172
+ F2. **Date validation missing**
147
173
  - File: search.ts:25-27
148
174
  - Issue: Invalid dates silently return no results
149
175
  - Fix: Validate ISO format, throw error with example
176
+ - touched-files: search.ts
177
+ - touched-resources: none
150
178
 
151
179
  #### Minor
152
- 1. **Progress indicators**
180
+ F3. **Progress indicators**
153
181
  - File: indexer.ts:130
154
182
  - Issue: No "X of Y" counter for long operations
155
183
  - Impact: Users don't know how long to wait
184
+ - touched-files: indexer.ts
185
+ - touched-resources: none
156
186
 
157
187
  ### Recommendations
158
188
  - Add progress reporting for user experience
@@ -162,5 +192,7 @@ git diff {BASE_SHA}..{HEAD_SHA}
162
192
 
163
193
  **Ready to merge: With fixes**
164
194
 
165
- **Reasoning:** Core implementation is solid with good architecture and tests. Important issues (help text, date validation) are easily fixed and don't affect core functionality.
195
+ **Reasoning:** Core implementation is solid with good architecture and tests. Moderate issues (help text, date validation) are easily fixed and don't affect core functionality.
196
+
197
+ Parallel-safe: F1,F2,F3 disjoint
166
198
  ```
@@ -69,6 +69,8 @@ One rule governs both review loops - spec-compliance and code-quality - in seque
69
69
 
70
70
  **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.
71
71
 
72
+ **Fix fan-out.** When the triggering review's `Parallel-safe:` line certifies a `disjoint` group of ≥ 2 findings, dispatch that fix round 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 plan-declared commands, and one re-review of the integrated delta follows.
73
+
72
74
  **The sequence.** Each review that finds issues is a decision point: read the `TRAJECTORY:` line before dispatching anything (review 1 has no line - on issues, dispatch fix 1). Any clean review ends the loop.
73
75
 
74
76
  1. **Review 1** (first review - no sentinel). Issues -> dispatch fix 1.
@@ -215,7 +217,7 @@ For the fan-out + worktree + patch-integration + conflict mechanics, see `dispat
215
217
  0. Call `phase_tracker({ action: "start", phase: "verify" })`. (The `implement` phase was started at execution start and auto-completes from `plan_tracker` once all tasks are done; this flow runs its own verify gate instead of `/skill:verification-before-completion`, so it must mark verify itself.)
216
218
  1. **Run the whole-diff code review.** Dispatch `/skill:requesting-code-review` against the worktree's full diff vs `main` (already covered in [The Process](#the-process) step "After all tasks"). Address Critical and Moderate findings before handoff. (Consumers wanting an in-flow project-specific audit re-add it as an explicit step in `.pi/gauntlet-overrides.md`, or run `/self-audit` manually.)
217
219
  2. **Run the full verification set — once.** Read the plan header's `**Verification:**` line and run it: tests + style + format (a single bundling entrypoint, or the listed individual commands). Green output is the fresh evidence verify requires; this is the only full run before conformance — task and wave gates ran scoped commands only. After conformance fix rounds land, re-run the set before re-dispatching the gate.
218
- 3. **Close the loop — conformance check.** The review in step 1 is plan-vs-code (single-step); it inherits any requirement the plan already dropped. Before marking verify complete, dispatch a fresh-context **`conformance-reviewer`** — its **own** dispatch, never fused into the step-1 review — to confront the deliverable (code **and** docs) against the *origin* — the spec **and** the original prompt — per `verification-before-completion/reference/conformance-check.md`. Pass the spec path, the verbatim original prompt, and the full diff. Follow that reference for the partition rule, concern decomposition, and fix-loop mechanics; do not reimplement them here. The fix loop may drive `plan_tracker` to surface fix-wave progress (task name = `Gn: <short desc>`, lifecycle `pending in_progress complete` per gap); it never calls `phase_tracker`. Call `phase_tracker({ action: "complete", phase: "verify" })` only when the reference says the handoff is durably complete: either a current `CONFORMS` result, or a current `## Closure / conformance` inventory whose carried-open concerns all come from valid deferred gaps, including `recommended: fix` gaps carried open because a declared precondition made the fix loop unavailable (`maxFixRounds: 0`, or no eligible named-branch worktree). A started positive-cap fix loop that blocks, fails, or exhausts its rounds with an open `fix` gap is escalation, not completion; on escalation, do not complete verify, stop and report.
220
+ 3. **Close the loop — conformance check.** The review in step 1 is plan-vs-code (single-step); it inherits any requirement the plan already dropped. Before marking verify complete, dispatch a fresh-context **`conformance-reviewer`** — its **own** dispatch, never fused into the step-1 review — to confront the deliverable (code **and** docs) against the *origin* — the spec **and** the original prompt — per `verification-before-completion/reference/conformance-check.md`. Pass the spec path, the verbatim original prompt, and the full diff. Follow that reference for the partition rule, concern decomposition, and fix-loop mechanics; do not reimplement them here. The fix loop may drive `plan_tracker` to surface fix-wave progress (task naming and lifecycle per conformance-check.md's fix loop / the Fix fan-out Progress rule); it never calls `phase_tracker`. Call `phase_tracker({ action: "complete", phase: "verify" })` only when the reference says the handoff is durably complete: either a current `CONFORMS` result, or a current `## Closure / conformance` inventory whose carried-open concerns all come from valid deferred gaps, including `recommended: fix` gaps carried open because a declared precondition made the fix loop unavailable (`maxFixRounds: 0`, or no eligible named-branch worktree). A started positive-cap fix loop that blocks, fails, or exhausts its rounds with an open `fix` gap is escalation, not completion; on escalation, do not complete verify, stop and report.
219
221
  4. Summarize what was implemented (tasks completed, files changed, test counts, code-review verdict). Emit the `## Closure / conformance` block exactly as defined in `verification-before-completion/reference/conformance-check.md`: it must open with the two-line sentinel (`status: CONFORMS (0 open)` or `status: GAPS (N open)`, then `audited-base: <full HEAD SHA>`), then carry the exact durable concern schema by reference with no renamed or reformatted fields. `finishing-a-development-branch` Step 3.5 consumes that block verbatim.
220
222
  5. **Proceed to finishing — no confirmation prompt.** Once verify is complete per step 3's criterion, invoke `/skill:finishing-a-development-branch` immediately. Its Step 4 menu (squash / PR / keep / discard) is the human gate; a separate "ready to finish?" prompt only stacks a second stop in front of it. Carried-open concerns are resolved there per concern via the `## Closure / conformance` block from step 4. Manual testing is a follow-up after the finishing choice (on `<base-branch>` after a squash-merge, or on the PR branch), never a reason to hold this gate.
221
223
 
@@ -235,6 +237,7 @@ For the fan-out + worktree + patch-integration + conflict mechanics, see `dispat
235
237
  - Skipping the `Implementer Status` parse — treating every response as DONE
236
238
  - Starting on main without explicit user consent
237
239
  - Dispatching `code-reviewer` before every one of the wave's spec-review verdicts has landed (including fusing SR+CR into one parallel call)
240
+ - Dispatching fixes sequentially on a clean HEAD despite a ≥ 2-ID `disjoint` group in the review's `Parallel-safe:` line
238
241
  - About to run the full verification entrypoint during the implement phase — task and wave gates run scoped, plan-declared commands only; the full set belongs to verify
239
242
 
240
243
  ## Integration
@@ -24,6 +24,8 @@ Dispatch a subagent with the code-reviewer template:
24
24
 
25
25
  **Code reviewer returns:** Strengths, Issues (Critical/Moderate/Minor), Assessment
26
26
 
27
+ Emit finding IDs and the `Parallel-safe:` line per that contract.
28
+
27
29
  ## Re-review: trajectory verdict
28
30
 
29
31
  Include the following in the reviewer's task text on every re-review, after
@@ -31,7 +33,8 @@ the prior review report pasted verbatim under a
31
33
  `## Previous review report (re-review trigger)` heading:
32
34
 
33
35
  If your task contains a "Previous review report (re-review trigger)" section
34
- and you found issues, end your report with exactly one line:
36
+ and you found issues, append exactly one more line after `Parallel-safe:` — this
37
+ line, not `Parallel-safe:`, is the true final line of the report:
35
38
 
36
39
  TRAJECTORY: CONVERGING (<n_prev> -> <n_now>, max severity <X>)
37
40
  TRAJECTORY: DIVERGING
@@ -62,10 +62,38 @@ Dispatch a subagent with this prompt:
62
62
 
63
63
  **Verify by reading code, not by trusting report.**
64
64
 
65
+ ### Finding IDs and fix-concurrency certification
66
+
67
+ Label every finding with a globally unique ID `F1..Fn`, numbered across the whole
68
+ report (no restart per severity section). Each finding carries:
69
+
70
+ - `touched-files:` — files a fix would edit (not just the evidence location), comma-separated, or the literal `none`
71
+ - `touched-resources:` — shared runtime resources a fix or its verification touches (DB/schema, port, fixture, external service, shared temp path), or the literal `none`
72
+
73
+ On any issue-bearing review, end the findings with one partition line (this is the
74
+ final line of the report unless a re-review trajectory verdict is also required — see below):
75
+
76
+ <!-- grammar identical to agents/conformance-reviewer.md (modulo G vs F id prefix) — change them together or not at all; writing-plans' plan-time Parallel-safe: line is a deliberately different free-text form, do NOT unify -->
77
+
78
+ ```
79
+ Parallel-safe: <group>[; <group>]*
80
+ <group> = <comma-separated finding-id list> " disjoint"
81
+ | <finding-id> " conflicts " <finding-id> " (" <reason> ")"
82
+ ```
83
+
84
+ Example: `Parallel-safe: F1,F3 disjoint; F2 conflicts F1 (both touch auth.ts)`
85
+
86
+ IDs inside a `disjoint` list are mutually parallel-safe (their fixes can run
87
+ concurrently). Any file OR runtime-resource overlap between two findings' fixes
88
+ forces `conflicts`. Runtime-resource disjointness is estimated over: DB/schema,
89
+ port, fixture, external service, shared temp path. When you cannot confidently
90
+ certify a pair disjoint, mark them `conflicts` (conservative default = serial).
91
+
65
92
  ## Re-review: trajectory verdict
66
93
 
67
94
  If your task contains a "Previous review report (re-review trigger)" section
68
- and you found issues, end your report with exactly one line:
95
+ and you found issues, append exactly one more line after `Parallel-safe:` — this
96
+ line, not `Parallel-safe:`, is the true final line of the report:
69
97
 
70
98
  TRAJECTORY: CONVERGING (<n_prev> -> <n_now>)
71
99
  TRAJECTORY: DIVERGING
@@ -142,19 +142,21 @@ prerequisites hold.
142
142
 
143
143
  Per round:
144
144
 
145
- 1. **`plan_tracker` init** with the round's gaps as tasks. Wave-prefix tasks
146
- when the reviewer's `Parallel-safe:` line marks a `conflicts` pair (file OR
147
- `touched-resources` overlap) that pair runs in separate serial waves;
148
- `disjoint` gaps share one wave. Lifecycle per gap: `pending` →
149
- `in_progress` → `complete`. This re-init **replaces** the implement phase's
150
- completed task list in the singleton widget — state-safe, since
151
- `phase-tracker.ts` `applyPlanActivity` only auto-completes `implement`
152
- while it is `in_progress`; the widget now shows fix-wave progress during
145
+ 1. **`plan_tracker` add** append the round's gaps as tasks (`Gn: <gap origin
146
+ clause verbatim, truncated>`; carry the gap's requirement text mechanically,
147
+ no orchestrator-authored summaries); never `init`, which would wipe the
148
+ implement phase's completed task list. Lifecycle per gap: `pending` →
149
+ `in_progress` → `complete`. The widget now shows fix-wave progress during
153
150
  verify.
154
- 2. **Per gap** (task `in_progress`): dispatch `implementer` (fresh context,
155
- `worktree: true`, `cwd` = the conformance worktree, `touched-files` from the
156
- gap block as an explicit ownership boundary) dispatch `spec-reviewer` on
157
- the gap-block reference contract below task `complete`.
151
+ 2. **Fix dispatch** per `dispatching-parallel-agents` "Fix fan-out": a `disjoint`
152
+ group of 2 gaps (per the report's `Parallel-safe:` line) fixes in one parallel
153
+ dispatch one `implementer` per gap (fresh context, `worktree: true`, `cwd` =
154
+ the conformance worktree, task = the gap block verbatim with `touched-files` as
155
+ the ownership boundary); `conflicts` pairs serialize. Gaps outside any ≥ 2-ID
156
+ `disjoint` group run sequentially as before. Then dispatch `spec-reviewer` per
157
+ gap on the gap-block reference contract below. Task lifecycle: mark `in_progress` at
158
+ dispatch; `complete` is deferred until the gap's patch is successfully
159
+ integrated in step 3 below.
158
160
  3. **Integrate** serially via `git apply` onto the worktree HEAD, one gap's
159
161
  patch at a time. Failure handling is inherited verbatim from
160
162
  `dispatching-parallel-agents` "Review and Integrate": textual conflict →