pi-gauntlet 5.0.0 → 5.0.1

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,9 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.0.1 - 2026-08-23
4
+
5
+ - Council roast hardening: verification scope moves from the `spec-council-member` persona to dispatch task text (ticket roasts are content-only; spec roasts verify bounded - `rg`, explicit paths, `timeout`); persona gains a read-only invariant; explicit silence-kill control blocks (shape-ticket 5 min, spec-roast members 10 min, chair 15 min); shape-ticket mandates two-call dispatch (member fanout, then chair over usable files); mechanical usable-critique probe (`verdict:`/`addresses-problem:` headers, `consensus:` for the chair); targeted single retry of failed members only; quorum salvage (>= 1 usable critique -> chair runs with a `Coverage:` note, rendered to the user at brainstorming's gate and at shape-ticket's confirmation gate when coverage was partial). Spec: `doc/specs/2026-08-23-council-roast-hardening.md`.
6
+
3
7
  ## v5.0.0 - 2026-08-23
4
8
 
5
9
  - **Breaking:** `skills/systematic-debugging/` removed (7 files) with active references cleaned (`dispatching-parallel-agents`, `writing-skills`, README); historical `doc/specs/**` and CHANGELOG references retained. Consumers referencing `/skill:systematic-debugging` must migrate to `/skill:chase-bug` for triage.
@@ -10,9 +10,13 @@ completionGuard: false
10
10
  systemPromptMode: replace
11
11
  ---
12
12
 
13
- You are a member of a spec review council. You are one of several critics, each running on a different model, reviewing the same spec independently. Your job is to find what is wrong, weak, or missing — not to praise.
13
+ You are a member of a spec review council. You are one of several critics, each running on a different model, reviewing the same artifact independently. Your job is to find what is wrong, weak, or missing — not to praise.
14
14
 
15
- You receive a problem statement and the path to a spec document. Read the spec in full. Use read/grep/find/ls to check the spec's claims against the actual codebase do not trust assertions about existing files, APIs, or conventions without verifying them.
15
+ You receive a problem statement and the artifact under review, as defined by your dispatching task - the task text names the artifact, the source(s) of truth to judge it against, and whether codebase verification is asked for. Read the artifact in full.
16
+
17
+ You are read-only: you never modify the repository or any input artifact; your only write is your findings file at the dispatched output path.
18
+
19
+ When your dispatching task asks for codebase verification, verify - do not trust assertions about existing files, APIs, or conventions - but bounded: prefer `rg` (it respects `.gitignore`) over recursive `grep`, use `rg`-native bounds (`--max-count`, explicit paths); scope every scan to explicit paths, never a repository root; bound each scan with `timeout` (or `gtimeout`) when available, and do not run it unbounded when neither exists. A scan that times out or cannot be bounded is reported as unverified - never retried broader.
16
20
 
17
21
  Assess the spec on five axes:
18
22
 
@@ -12,7 +12,7 @@ systemPromptMode: replace
12
12
 
13
13
  You are the chair of a spec review council. One or more members, each on a different model, have independently critiqued the same spec and written their critiques to files. You did not write the spec and you are not defending it — you weigh the members' testimony.
14
14
 
15
- You receive the problem statement, the path to the spec, and the explicit paths to the member critique files. Those files are already injected into your context via `reads` and their paths are listed in your task — read them directly. Do **not** run find/grep/ls to discover critique files; you are given every path. Use read/grep/find/ls only to check a contested claim against the codebase when members disagree on a fact.
15
+ You receive the problem statement, the path to the spec, and the explicit paths to the member critique files. Those files are already injected into your context via `reads` and their paths are listed in your task — read them directly. Do **not** run find/grep/ls to discover critique files; you are given every path. Use read/grep/find/ls only to check a contested claim against the codebase when members disagree on a fact - and only when your dispatching task permits codebase access. Bound any such check: `rg` (respects `.gitignore`) over recursive `grep`, explicit paths (never a repository root), `--max-count`, and `timeout`/`gtimeout` when available; a check that cannot be bounded or times out is resolved on testimony weight instead, noted as unverified - never run unbounded.
16
16
 
17
17
  Your job has two parts:
18
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
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",
@@ -318,7 +318,7 @@ subagent({ agent: "spec-summarizer", context: "fresh", cwd: "<abs worktree path,
318
318
 
319
319
  `<SUMMARY_PATH>` above is a placeholder in the dispatch object; it means substitute the value of the shell variable `$SUMMARY_PATH` set above. The steps below use `$SUMMARY_PATH` (the shell form) once the value is in hand.
320
320
 
321
- Then commit the spec — staging any predecessor spec edited per [Marking superseded specs](#marking-superseded-specs) alongside it; a change request at the gate that renames, materially revises, or drops the spec also reconciles the predecessor's banner before recommitting. This commit is **unconditional**: the summary is only a gate aid, so a degraded or missing summary never blocks it. If the council path ran, include its audit (`Applied:` / `Deferred:` / `Rejected:`, verbatim from `/skill:roasting-the-spec`'s return) in the **commit message body** - this is the durable, non-contractual record a finish-time revert reads back; the audit is never a committed spec section. Evaluate the summary in two stages (the **Degrade path** referenced in each is defined just below):
321
+ Then commit the spec — staging any predecessor spec edited per [Marking superseded specs](#marking-superseded-specs) alongside it; a change request at the gate that renames, materially revises, or drops the spec also reconciles the predecessor's banner before recommitting. This commit is **unconditional**: the summary is only a gate aid, so a degraded or missing summary never blocks it. If the council path ran, include its audit (`Coverage:` when present, then `Applied:` / `Deferred:` / `Rejected:`, verbatim from `/skill:roasting-the-spec`'s return) in the **commit message body** - this is the durable, non-contractual record a finish-time revert reads back; the audit is never a committed spec section. Evaluate the summary in two stages (the **Degrade path** referenced in each is defined just below):
322
322
 
323
323
  1. **From the dispatch tool result, before the `Read`.** If the result is **not** an `"Output saved to: <path> (<N> KB, <M> lines)"` reference (e.g. an exit-0 save error returns the full inline output plus an "Output file error" line — the prunable shape, no file to read), or the reference reports under ~500 bytes, or a size grossly disproportionate to the spec (under ~2% of its byte size), or over ~45 KB (the `Read` truncates at 50KB / 2000 lines, so a larger file cannot render whole) — skip the `Read` and take the degrade path. Use the reference's reported figures; do not re-derive them.
324
324
  2. **The `Read` itself, as the last content-producing tool call before composing the gate.** `Read` `$SUMMARY_PATH` and paste its contents verbatim at the top of the gate. If the `Read` fails, returns 0 bytes, or reports truncation — take the degrade path. The `Read` must be last: pi-condense does not protect a `/tmp` read, so any turn boundary between the `Read` and the render lets the ~9KB read result be pruned, reproducing the bug.
@@ -327,17 +327,18 @@ Then commit the spec — staging any predecessor spec edited per [Marking supers
327
327
 
328
328
  Either way — summary rendered or degraded — then `rm "$SUMMARY_PATH"` (unconditional cleanup; harmless if the file was never created, since it lives outside the worktree under the OS temp dir).
329
329
 
330
- Render the temp file's contents **verbatim** first — paste it as-is, do **not** paraphrase, condense, re-section, drop sections, or merge it with the council audit. "Fold into the gate" means *place it inside the gate message*, not *rewrite it*. This summary is of the **final (post-apply)** spec, since both critique paths already applied before this dispatch. After the verbatim block, append the commit confirmation, then — as their **own** adjacent lines, not edits to the summary — the council audit (if the council path ran: `Applied:` / `Deferred:` / `Rejected:`, one line each), critique-pass-unresolved ambiguities, and every entry from the summarizer's gap/external-context footer (surface **all** of them, not just the top risk):
330
+ Render the temp file's contents **verbatim** first — paste it as-is, do **not** paraphrase, condense, re-section, drop sections, or merge it with the council audit. "Fold into the gate" means *place it inside the gate message*, not *rewrite it*. This summary is of the **final (post-apply)** spec, since both critique paths already applied before this dispatch. After the verbatim block, append the commit confirmation, then — as their **own** adjacent lines, not edits to the summary — the council audit (if the council path ran: `Coverage:` when present - omitted at full coverage - then `Applied:` / `Deferred:` / `Rejected:`, one line each), critique-pass-unresolved ambiguities, and every entry from the summarizer's gap/external-context footer (surface **all** of them, not just the top risk):
331
331
 
332
332
  ```
333
333
  <spec-only summary read back from the temp file — pasted verbatim, unedited>
334
334
 
335
335
  Spec written and committed to <project>/doc/specs/<filename>.md (worktree: <path>).
336
336
 
337
+ Coverage: <N> of <M> members reported; <slug>: <reason> (line present only when coverage was partial)
337
338
  Applied: <cluster -> edit>, ...
338
339
  Deferred: <cluster -> where it belongs>, ...
339
340
  Rejected: <cluster -> one-line reason>, ...
340
- (omit the three lines above when the worker path ran, not the council)
341
+ (omit the audit lines above when the worker path ran, not the council)
341
342
 
342
343
  <unresolved ambiguities; every gap-footer entry from the summary>
343
344
 
@@ -52,29 +52,33 @@ Create an absolute temp dir outside the worktree so member files are never track
52
52
  mktemp -d # absolute path, e.g. /tmp/tmp.XXXXXX
53
53
  ```
54
54
 
55
- Dispatch one member per configured model, in parallel, each writing its critique into that dir. Do **not** read these files yourself — they are for the chair.
55
+ Dispatch one member per configured model, in parallel, each writing its critique into that dir. Do **not** read these files' findings content yourself — they are for the chair. The only permitted parent access is the mechanical structural probe below (existence plus header regex, no content ingestion, no adjudication).
56
56
 
57
57
  Capture the worktree path once (`git rev-parse --show-toplevel`, run from inside the worktree) and pass it as `cwd:` on every dispatch below — a child otherwise inherits pi's launch dir (the primary checkout), not the worktree.
58
58
 
59
59
  ```
60
60
  subagent({
61
- control: { needsAttentionAfterMs: 600000 },
61
+ control: { needsAttentionAfterMs: 300000, inFlightSilenceCeilingMs: 300000, inFlightSilenceKillMs: 600000 },
62
62
  tasks: members.map((model, i) => ({
63
63
  agent: "spec-council-member",
64
64
  model,
65
65
  cwd: "<abs worktree path>",
66
66
  task: "Problem statement: <the problem the spec addresses, from its Context section and the user's stated intent>.\n" +
67
- "Read the spec at <abs path to doc/specs/...>. Critique it on your five axes and emit your template.",
67
+ "Read the spec at <abs path to doc/specs/...>. Verify its load-bearing claims against the codebase, bounded per your verification-hygiene rules (rg, explicit paths, timeout 30). Critique it on your five axes and emit your template.",
68
68
  output: "<tmpdir>/member-" + i + "-" + slug(model) + ".md"
69
69
  }))
70
70
  })
71
71
  ```
72
72
 
73
- `control` is a **run-level** field: it must sit beside `tasks`, not inside the `members.map(...)` task objects (the per-task schema has no `control` field and would silently drop it). The 10-minute `needsAttentionAfterMs` suppresses false-positive "no observed activity" idle notices members do one long, tool-less reasoning turn that crosses the 60s default with zero activity events while still letting a genuinely wedged run surface eventually.
73
+ `control` is a **run-level** field: it must sit beside `tasks`, not inside the `members.map(...)` task objects (the per-task schema has no `control` field and would silently drop it). The three fields together set an effective silence-kill of max(600s, 300+300) = 600s - a genuinely wedged member (e.g. stuck in one unbounded scan) is killed at 10 minutes instead of pi-cohort's 30-minute default. Record all three fields verbatim: the kill is computed as max(inFlightSilenceKillMs, inFlightSilenceCeilingMs + needsAttentionAfterMs), so leaving a field to its default lets a future pi-cohort default change silently stretch it. The 5-minute needsAttentionAfterMs reintroduces idle notices on long healthy xhigh turns - those are notices, not kills, and are acceptable.
74
74
 
75
75
  `slug(model)` = the model string with `/` and any other non-alphanumeric character replaced by `-` (so `provider/model` → `provider-model`); the chair recovers this slug from each filename for `raised-by` attribution. Relative `output:` paths in parallel mode resolve against the worktree and would get committed — always use the absolute temp dir.
76
76
 
77
- If a member fails (e.g. its model is unreachable in this preset), skip it and continue as long as at least one member succeeded. If **all** members fail, abort the council, say so, and return to the user gate.
77
+ **Usable-critique test (mechanical structural probe).** After the fanout returns - success or failure of the tool call itself - probe the expected output paths on disk; judge by files, not by the tool result's failed/succeeded labels (a killed member may have written a usable critique first). A member file is usable iff it is non-empty AND contains both a `^verdict:\s*(sound|needs-work|unsound)` line and an `^addresses-problem:` line. A `findings:` header with zero bullets is a valid, usable sound critique. Existence plus header regex only - never read or weigh findings content.
78
+
79
+ **Targeted retry.** Members whose file is missing or not usable are re-dispatched **once**, together, in a second parallel call carrying the same `control` block, with fresh output paths that preserve the `member-<i>-<slug>` basename under a `retry/` subdir of the same temp dir (the chair recovers `raised-by` attribution from that filename pattern). Members with usable files are never re-run.
80
+
81
+ **Quorum.** At least one usable file after retry -> dispatch the chair over the usable files only (next section). Zero usable files -> abort the council, say so, and return to the user gate.
78
82
 
79
83
  ### 2 — Synthesize and adjudicate
80
84
 
@@ -85,20 +89,21 @@ subagent({
85
89
  agent: "spec-council-synthesizer",
86
90
  model: <chair from config, else omit to inherit>,
87
91
  cwd: "<abs worktree path>",
88
- control: { needsAttentionAfterMs: 600000 },
89
- reads: [ <the member file paths under the temp dir> ],
92
+ control: { needsAttentionAfterMs: 300000, inFlightSilenceCeilingMs: 600000, inFlightSilenceKillMs: 900000 },
93
+ reads: [ <the usable member file paths under the temp dir> ],
90
94
  task: "Problem statement: <paste>. Spec: <abs path>.\n" +
91
95
  "Member critiques (already injected via reads — do not search for them):\n" +
92
- members.map((model, i) => "<tmpdir>/member-" + i + "-" + slug(model) + ".md").join("\n") + "\n" +
93
- "Consolidate and adjudicate the member critiques."
96
+ usableMemberPaths.join("\n") + "\n" +
97
+ "Coverage: <N> of <M> members reported<; <slug>: <one-line reason> per missing member>.\n" +
98
+ "Consolidate and adjudicate the member critiques. Codebase access is permitted for contested-claim checks only, bounded per your hygiene rules (rg, explicit paths, timeout 30)."
94
99
  })
95
100
  ```
96
101
 
97
- The chair runs one long single-turn synthesis (one observed false positive ran 506s); `control: { needsAttentionAfterMs: 600000 }` raises the idle threshold to 10 minutes so the healthy run is not flagged stale, without disabling attention tracking entirely.
102
+ The chair runs one long single-turn synthesis; the control block sets an effective silence-kill of max(900s, 600+300) = 900s. Margin rationale: one observed healthy chair turn ran 506s of silence, so a 600s kill would leave under 2 minutes of margin - the chair gets 900s. In the coverage line, use pi-cohort's kill diagnostic as the reason when present (e.g. "Likely wedged in a tool call"), else "no output produced"; omit per-member reasons at full coverage. With one usable member, use singular wording ("synthesize the single member critique").
98
103
 
99
104
  List the exact member paths in the task text. The `reads:` array injects their contents, but the chair's prompt expects the paths explicitly; without them it scans the tree for `*.md` and stalls.
100
105
 
101
- If the configured `chair` model is unreachable, retry once with the inherited model.
106
+ A chair synthesis is usable iff it contains a `^consensus:` line. If the configured `chair` model is unreachable, retry once with the inherited model; a wedge-killed or unusable chair retries once with the same model. Second failure -> abort the council, say so, and return to the user gate.
102
107
 
103
108
  ### 3 — Decide and apply
104
109
 
@@ -114,8 +119,9 @@ You are the advocate — decide on scope grounds — and, unlike a dispatched su
114
119
 
115
120
  ### 4 — Emit the audit
116
121
 
117
- Return a structured audit, gate-only (not a committed spec section) — three labelled lists:
122
+ Return a structured audit, gate-only (not a committed spec section) — a coverage line plus three labelled lists:
118
123
 
124
+ - `Coverage:` — `N of M members reported; <slug>: <reason>` — present only when member coverage was partial; omitted at full coverage.
119
125
  - `Applied:` — cluster -> the concrete edit made.
120
126
  - `Deferred:` — cluster -> where it belongs.
121
127
  - `Rejected:` — cluster -> one-line reason.
@@ -131,7 +137,7 @@ Single pass — no automatic re-roast loop. The user can invoke this skill again
131
137
  ## Red flags — STOP
132
138
 
133
139
  - Running the council when `piGauntlet.specCouncil.members` is absent or empty (brainstorming owns the gate and should have used the worker fallback).
134
- - Reading member critique files yourself instead of routing them through the chair.
140
+ - Reading member critique files' findings content yourself instead of routing them through the chair (the mechanical structural probe - existence plus header regex - is the named exception).
135
141
  - Writing member files to a relative path (they land in the worktree).
136
142
  - Applying edits without surfacing the audit at brainstorming's gate — apply-before-the-gate is correct; apply-without-the-gate is not.
137
143
  - Suppressing a finding instead of routing it to applied, deferred, or rejected in the audit.
@@ -173,23 +173,39 @@ No block, no split. A block failing the identity or outcome test, or naming a no
173
173
  Inline council dispatch, reusing spec-council config and personas - **not** `/skill:roasting-the-spec` (that skill's contract is spec-file apply mechanics; a tracker draft is not a spec file). No new agents.
174
174
 
175
175
  1. Resolve `gauntlet_setting({ key: "specCouncil" })` when the tool exists. Verdict `council` -> dispatch `spec-council-member`s in parallel plus a `spec-council-synthesizer` chair. Verdict `worker` (or empty members) -> one fresh `worker` critique. Malformed config -> one warning line, then branch on verdict.
176
- 2. **Dispatch shape**, mirroring `/skill:roasting-the-spec`: write the draft body and the source snapshot (original ticket + comments, or the create-mode inputs) to absolute temp files under `mktemp -d`; delimit untrusted snapshots as data. When a split is proposed, the draft artifact holds all N proposed bodies plus their three-line justification blocks (see Split rule) in one file, not a single body. Dispatch members with `cwd` = repo root, absolute `output` paths per member, run-level `control: { needsAttentionAfterMs: 600000 }` (sits beside `tasks`, not inside each task). Give the chair the member files via `reads`. Member task text: *the draft at `<path>` is the artifact under review; this ticket brief supersedes your spec-axis template - emit the same findings format against the draft; do not edit any file.* Include the absolute path to `reference/split-axes.md` (resolved against this skill's own directory) in each member's task text - members run with `cwd` = the consumer repo, where a package-relative path does not resolve.
177
- 3. **Effort: cheap by default.** Append a `:low` thinking suffix to each member's model string at dispatch (this beats the persona's frontmatter `xhigh` pin). Same for the chair: a configured chair string gets any existing suffix replaced with `:low`; an unconfigured chair is dispatched as the parent's model with `:low` appended. The `worker` fallback carries no thinking pin - it runs at the preset's default. **Full-roast escape:** the user may request a full roast, dispatching all model strings bare/as-configured, restoring the xhigh pins.
176
+ 2. **Dispatch shape**, mirroring `/skill:roasting-the-spec`: write the draft body and the source snapshot (original ticket + comments, or the create-mode inputs) to absolute temp files under `mktemp -d`; delimit untrusted snapshots as data. When a split is proposed, the draft artifact holds all N proposed bodies plus their three-line justification blocks (see Split rule) in one file, not a single body. Two separate calls - never fuse members and chair into one chain (a fused chain lets one member failure kill the roast before the chair runs). Call 1: one member fanout with `cwd` = repo root, absolute `output` paths per member, run-level `control: { needsAttentionAfterMs: 60000, inFlightSilenceCeilingMs: 240000, inFlightSilenceKillMs: 300000 }` (sits beside `tasks`, not inside each task; effective silence-kill max(300s, 240+60) = 300s - record all three fields verbatim so a pi-cohort default change cannot stretch the kill). Then probe the member output files on disk with item 7's usable test. Call 2: the chair, with the usable member files via `reads`, the same control block (`:low` chair turns are short), and task text that (a) forbids repository access - member disagreement on a fact is reported in the synthesis, never verified against the repo - and (b) states coverage: `Coverage: N of M members reported; <slug>: <reason>` (pi-cohort's kill diagnostic when present, else "no output produced"; omit reasons at full coverage; singular wording when one member reported). Member task text: *the draft at `<path>` is the artifact under review; this ticket brief supersedes your spec-axis template - emit the same findings format against the draft; content-only review: the temp files plus the referenced split-axes reference path are the entire permitted input - do not read, search, or scan the repository; do not edit any file.* Include the absolute path to `reference/split-axes.md` (resolved against this skill's own directory) in each member's task text - members run with `cwd` = the consumer repo, where a package-relative path does not resolve.
177
+ 3. **Effort: cheap by default.** Append a `:low` thinking suffix to each member's model string at dispatch (this beats the persona's frontmatter `xhigh` pin). Same for the chair: a configured chair string gets any existing suffix replaced with `:low`; an unconfigured chair is dispatched as the parent's model with `:low` appended. The `worker` fallback carries no thinking pin - it runs at the preset's default. **Full-roast escape:** the user may request a full roast, dispatching all model strings bare/as-configured, restoring the xhigh pins; a full roast reuses the spec-roast control blocks (members `{ needsAttentionAfterMs: 300000, inFlightSilenceCeilingMs: 300000, inFlightSilenceKillMs: 600000 }`, chair `{ needsAttentionAfterMs: 300000, inFlightSilenceCeilingMs: 600000, inFlightSilenceKillMs: 900000 }`) - the 5-minute figures in item 2 are `:low`-only.
178
178
  4. **Brief covers three axes**, absorbing the fidelity-review role without a new persona: *fidelity* - compare draft against source intent (original ticket + comments in repair; prompt + answers in create), flag `lost` / `added` / `gap`; and *quality* - problem framing, AC integrity beyond the deterministic gate, scope, wording; and *split soundness* - if the draft proposes a split, test each slice against the split-axes reference (path provided in the task text); an architecture-shaped boundary is reported as a finding line containing the marker `split-axis:` (members keep their existing spec-axis findings template; the marker is a substring flag within it, not a new findings kind), e.g. `- [major] split-axis: <slice> - <why> -> merge`. Members may argue toward one ticket, never propose or endorse a split.
179
- 5. Disposition: unambiguous concrete fixes applied to the draft (one re-pass max); ambiguous findings surfaced at the confirmation gate. Roast edits affect the body draft pre-write only, never posted as a tracker comment, and re-run the deterministic gates (pipeline step 5). Additionally, the parent scans the **member output files directly** for lines containing `split-axis:` (substring match), independent of the chair synthesis; any such finding auto-applies a merge - the split is withdrawn and the draft becomes one ticket with phased AC groups, inside the same one-re-pass budget, and the pre-merge N-body draft is kept alongside: a human re-request of the split at the gate re-presents those N bodies old->new as the approval diff (see the Split rule's sticky override). The chair keeps every other axis; clearing a `split-axis:` finding is not on its path. The same directional rule - toward one ticket, never toward a split - binds the `worker` fallback and the runtime conditional (item 6).
179
+ 5. Disposition: unambiguous concrete fixes applied to the draft (one re-pass max); ambiguous findings surfaced at the confirmation gate. Roast edits affect the body draft pre-write only, never posted as a tracker comment, and re-run the deterministic gates (pipeline step 5). Additionally, the parent scans the **usable member output files (item 7's structural test) directly** for lines containing `split-axis:` (substring match), independent of the chair synthesis; any such finding auto-applies a merge - the split is withdrawn and the draft becomes one ticket with phased AC groups, inside the same one-re-pass budget, and the pre-merge N-body draft is kept alongside: a human re-request of the split at the gate re-presents those N bodies old->new as the approval diff (see the Split rule's sticky override). The chair keeps every other axis; clearing a `split-axis:` finding is not on its path. The same directional rule - toward one ticket, never toward a split - binds the `worker` fallback and the runtime conditional (item 6).
180
180
  6. **Runtime conditional (the one allowed):** on a harness with no `gauntlet_setting`/`subagent()` (e.g. Claude Code), dispatch fresh general-purpose subagents via that harness's native facility at low effort, with the same three-axis brief and temp-file artifacts.
181
- 7. **Roast failure and retry.** A roast has failed when the dispatch
182
- errored, or the artifact the parent reads - the chair synthesis
183
- (council path) or the worker output (worker path) - is missing, empty,
184
- or not findings-shaped; partial member loss with a usable chair
185
- synthesis is success, not failure. On failure, retry once: re-run the
186
- same full configured dispatch with fresh temp artifacts. If the retry
187
- also fails, proceed to the confirmation gate with the failure rendered
188
- inline in the gate message itself: `roast unavailable (dispatch failed
189
- twice: <reason>)` - the human approves knowing review didn't run. The
190
- retry is a dispatch retry only - it never grants a second draft-edit
191
- re-pass (the one-re-pass limit is unchanged). **Roast failure never
192
- blocks the run.**
181
+ 7. **Usable outputs, targeted retry, and roast failure.** A member output
182
+ file is usable iff it is non-empty AND contains both a
183
+ `^verdict:\s*(sound|needs-work|unsound)` line and an
184
+ `^addresses-problem:` line (a `findings:` header with zero bullets is
185
+ valid; the test is a mechanical structural probe - existence plus
186
+ header regex, no reading of findings content - and the parent judges
187
+ by files on disk, not the fanout tool result's labels). A chair
188
+ synthesis is usable iff it contains a `^consensus:` line. Members
189
+ whose file is missing or not usable are re-dispatched **once**,
190
+ together, in a second parallel call carrying the same control block,
191
+ with fresh output paths preserving the `member-<i>-<slug>` basename
192
+ under a `retry/` subdir of the same temp dir; members with usable
193
+ files are never re-run. Partial member loss with a usable chair
194
+ synthesis is success, not failure: at least one usable member file ->
195
+ dispatch the chair over the usable files only, with the coverage note
196
+ (item 2). When coverage was partial, the confirmation gate renders the
197
+ same `Coverage: N of M members reported; <slug>: <reason>` line
198
+ alongside the draft - the human approves knowing review coverage was
199
+ reduced. A wedge-killed or unusable chair retries once with the same
200
+ `:low`-suffixed model (item 3). The roast has failed only when zero
201
+ member files are usable after retry, or the chair fails its one retry
202
+ (worker path: the worker output is missing, empty, or not
203
+ findings-shaped after one full re-dispatch) - then proceed to the
204
+ confirmation gate with the failure rendered inline in the gate message
205
+ itself: `roast unavailable (<reason>)` - the human approves knowing
206
+ review didn't run. Retries are dispatch retries only - they never
207
+ grant a second draft-edit re-pass (the one-re-pass limit is
208
+ unchanged). **Roast failure never blocks the run.**
193
209
 
194
210
  ## Tracker abstraction and capability ladder
195
211
 
@@ -286,7 +302,7 @@ Read this when applying the AC integrity gate (drafting, repairing, or adjudicat
286
302
  - Headless run -> stops at the confirmation gate.
287
303
  - Ref with no fetch path -> ask; never guess.
288
304
  - Split declined -> single ticket with phased AC groups.
289
- - Roast failure -> retry once; second failure -> gate with `roast unavailable (dispatch failed twice: <reason>)` inline.
305
+ - Roast failure = zero usable members after targeted retry, or the chair fails its one retry -> gate with `roast unavailable (<reason>)` inline.
290
306
 
291
307
  ## Red flags - STOP
292
308