pi-gauntlet 4.12.2 → 4.13.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,14 @@
1
1
  # Changelog
2
2
 
3
+ ## v4.13.1 - 2026-08-20
4
+
5
+ - `gatekeep-pr`: dense output protocol - stable append-only finding IDs (`P#` blocking fixes, `L#` requirement/doc drift, `C#` verdict-neutral thread replies, `F#` owned follow-ups), bare-verb action vocabulary with selection grammar (`all`, ranges, exclusions), normative per-cell course table (one `[recommended]`, custom last, push and merge never bundled), fix-wave execution (file-disjoint batches, parallel edit-only implementers, one gate/re-review/push per wave), output done-check for external payloads with a `## comms style` overrides hook.
6
+
7
+ ## v4.13.0 - 2026-08-20
8
+
9
+ - Claude Code plugin marketplace (gh-11): new `.claude-plugin/marketplace.json` exposes exactly three tracker-facing skills (`shape-ticket`, `gatekeep-pr`, `check-delivery`) to Claude Code in place - plugin `gauntlet`, marketplace `pi-gauntlet`, consumer setting `"enabledPlugins": {"gauntlet@pi-gauntlet": true}`. Exclusivity rests on `source: "./"` + `strict: false` + specific subdir paths; `"agents": []` suppresses the default `agents/` scan so pi personas never register as CC agents. Zero skill-body edits; the directory is excluded from the npm tarball and never read by pi.
10
+ - `scripts/ci.mjs`: new marketplace guard - identity pins (`gauntlet`@`pi-gauntlet`), source/strict/agents assertions, scan-leak entry ban, allowlist path + frontmatter existence checks, bundle-local `.md` reference integrity for allowlisted skills, and a negative npm-pack assertion for `.claude-plugin/`.
11
+ - README: new "Use from Claude Code" section - consumer settings snippet, register/install/enable distinction, `CLAUDE.md` symlink advice, folder-trust gotcha, and an 11-step smoke-test cookbook.
3
12
  ## v4.12.2 - 2026-08-19
4
13
 
5
14
  - New `skills/shape-ticket/reference/split-axes.md`: the shared split test for tickets and specs - identity test (same precipitating failure / mooted outcome = one concern), user-observable outcome test, closed axis list with pass/fail examples, Never-axis list (fix location, service/repo/layer/team, "keep tickets small"), discovery-conversion exemption, fail-closed release-timing precondition gated on a documented `## Deployment` overrides section.
package/README.md CHANGED
@@ -69,7 +69,7 @@ Everything between gate 1 and gate 2 - task breakdown, implementation, both revi
69
69
 
70
70
  pi-gauntlet ships three kinds of pieces, layered on top of pi-cohort's dispatch:
71
71
 
72
- - **16 skills** - the workflow logic. Thirteen activate automatically when pi sees the matching kind of task, and each one gates the next: `brainstorming`, `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `systematic-debugging`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `writing-skills`. Three more are explicit-invocation-only (`disable-model-invocation: true`): `shape-ticket` creates or repairs one tracker issue per run against a Context/Problem/Idea/Acceptance-Criteria template, gated by an AC integrity check, a cheap council roast, and a single human-confirmed write - run it with `/skill:shape-ticket`. `gatekeep-pr` is consent-gated pre-merge verification of a PR against its issue - read-only gathering, running the project's verification command, a rubric-based review, then a deterministic authorship-aware menu; nothing mutates (fixes, pushes, reviews, merges) until you pick a row - run it with `/skill:gatekeep-pr <pr>`. `check-delivery` is a post-merge detective control: proves an issue actually shipped (default-branch landing, delivery target, per-AC evidence) before its tracker status advances; it never writes a terminal status - run it with `/skill:check-delivery <ref>`.
72
+ - **16 skills** - the workflow logic. Thirteen activate automatically when pi sees the matching kind of task, and each one gates the next: `brainstorming`, `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `systematic-debugging`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `writing-skills`. Three more are explicit-invocation-only (`disable-model-invocation: true`): `shape-ticket` creates or repairs one tracker issue per run against a Context/Problem/Idea/Acceptance-Criteria template, gated by an AC integrity check, a cheap council roast, and a single human-confirmed write - run it with `/skill:shape-ticket`. `gatekeep-pr` is consent-gated pre-merge verification of a PR against its issue - read-only gathering, running the project's verification command, a rubric-based review, then a deterministic authorship-aware menu with stable finding IDs (P#/L#/C#/F#) and numbered pre-composed courses (fixes execute as a single parallel-safe wave: one gate run, one re-review, one push); nothing mutates (fixes, pushes, reviews, merges) until you pick a row - run it with `/skill:gatekeep-pr <pr>`. `check-delivery` is a post-merge detective control: proves an issue actually shipped (default-branch landing, delivery target, per-AC evidence) before its tracker status advances; it never writes a terminal status - run it with `/skill:check-delivery <ref>`.
73
73
  - **7 subagent personas** - the specialized child agents the skills dispatch via pi-cohort: `implementer`, `code-reviewer`, `spec-reviewer`, `conformance-reviewer`, `spec-summarizer`, `spec-council-member`, `spec-council-synthesizer`. See [doc/personas.md](./doc/personas.md) for what each one does and why its permissions are scoped the way they are.
74
74
  - **3 runtime extensions** - the enforcement layer. `plan-tracker` and `phase-tracker` are tools skills call to track progress (with a TUI widget); `verify-before-ship` is a hook that warns if you push or open a PR without a passing test run since your last edit; a phase-tracker flow guard reminds on implement-phase commits missing spec/code review. See [doc/configuration.md](./doc/configuration.md) for the settings each one reads.
75
75
 
@@ -124,6 +124,109 @@ cd ~/path/to/your/repo && pi install -l ~/repos/pi-gauntlet
124
124
  cd ~/repos/pi-gauntlet && npm run link-agents # local-path installs skip npm install; run this once
125
125
  ```
126
126
 
127
+ ## Use from Claude Code
128
+
129
+ Three skills are exposed to Claude Code via the plugin marketplace at
130
+ `.claude-plugin/marketplace.json`: **shape-ticket**, **gatekeep-pr**, and
131
+ **check-delivery**. They are harness-portable by design - every pi-specific
132
+ mechanic they touch (`plan_tracker`, `gauntlet_setting`, `subagent()`) carries
133
+ an inline fallback, so they run on Claude Code's native facilities. This is the
134
+ supported set. Not exposed, in two classes: (a) genuinely pi-bound surface -
135
+ the full gated pipeline (brainstorming -> writing-plans ->
136
+ subagent-driven-development -> verify -> finish), the spec council, the
137
+ conformance gate, flow guards, verify-before-ship, and all `piGauntlet.*`
138
+ settings, which depend on pi extensions; (b) runtime-neutral skills
139
+ (e.g. `systematic-debugging`, `receiving-code-review`, `using-git-worktrees`) that
140
+ are simply out of scope for this channel, not incompatible - re-adding one is a
141
+ one-line allowlist append. For Claude-Code-native equivalents of the
142
+ methodology skills, see [obra/superpowers](https://github.com/obra/superpowers).
143
+
144
+ ### Setup
145
+
146
+ Add to your repo's `.claude/settings.json`:
147
+
148
+ ```json
149
+ {
150
+ "extraKnownMarketplaces": {
151
+ "pi-gauntlet": {
152
+ "source": { "source": "github", "repo": "jjuraszek/pi-gauntlet" }
153
+ }
154
+ },
155
+ "enabledPlugins": { "gauntlet@pi-gauntlet": true }
156
+ }
157
+ ```
158
+
159
+ Add `"ref": "vX.Y.Z"` to the source object to pin a tag; the default tracks the
160
+ default branch. Claude Code merges settings entries whole (no field-level
161
+ merge), so teams layering managed settings must carry the full objects.
162
+
163
+ Registration, installation, and enablement are distinct steps in Claude Code:
164
+ `extraKnownMarketplaces` registers the marketplace, `enabledPlugins` records
165
+ enablement intent. If a fresh machine shows the plugin as known but not
166
+ installed, run `/plugin install gauntlet@pi-gauntlet` once. Alternative path
167
+ without touching settings.json: `/plugin marketplace add jjuraszek/pi-gauntlet`,
168
+ then install.
169
+
170
+ Invocation: `/gauntlet:shape-ticket` (or bare `/shape-ticket` when unambiguous).
171
+
172
+ Project instructions: Claude Code reads `CLAUDE.md`, pi reads `AGENTS.md` - a
173
+ symlink keeps one source of truth: `ln -s AGENTS.md CLAUDE.md`. The gauntlet
174
+ overrides ladder (`.pi/gauntlet-overrides.md` -> `gauntlet-overrides.md` ->
175
+ `doc/gauntlet-overrides.md`) works unchanged on Claude Code - it is a plain
176
+ file read.
177
+
178
+ **Trust gotcha:** the marketplace auto-activates only after you trust *that
179
+ exact repo folder* in interactive Claude Code. Trusting a parent folder,
180
+ `claude -p`, or SDK runs in untrusted folders silently skip
181
+ `extraKnownMarketplaces` with no error.
182
+
183
+ ### Smoke test
184
+
185
+ 1. Create a scratch repo and add the marketplace config:
186
+
187
+ mkdir -p /tmp/cc-smoke/.claude && cd /tmp/cc-smoke && git init
188
+ cat > .claude/settings.json <<'EOF'
189
+ {
190
+ "extraKnownMarketplaces": {
191
+ "pi-gauntlet": {
192
+ "source": { "source": "github", "repo": "jjuraszek/pi-gauntlet" }
193
+ }
194
+ },
195
+ "enabledPlugins": { "gauntlet@pi-gauntlet": true }
196
+ }
197
+ EOF
198
+
199
+ 2. Start Claude Code interactively in that directory: `claude`
200
+ 3. When prompted, trust the folder (this exact folder - trust is what activates
201
+ the marketplace; there is no separate marketplace prompt).
202
+ 4. Run `/plugin` and confirm: marketplace `pi-gauntlet` is listed, plugin
203
+ `gauntlet` is enabled. If it shows as known but not installed, run
204
+ `/plugin install gauntlet@pi-gauntlet` and re-check.
205
+ 5. Confirm exactly three skills are registered under the plugin (via the
206
+ `/plugin` details view): shape-ticket, gatekeep-pr, check-delivery.
207
+ 6. Invoke `/gauntlet:shape-ticket` with a deliberately two-concern ask (e.g.
208
+ "shape a ticket: CSV import for operators, plus a partner-facing status
209
+ API") so the skill deterministically consults its
210
+ `reference/split-axes.md` before proposing a split. Expected: skill
211
+ activates, reads the reference file, reaches its tracker capability ladder
212
+ without erroring on missing pi tools. Stop at the first human gate; write
213
+ nothing to any tracker.
214
+ 7. Invoke `/gauntlet:gatekeep-pr` in the scratch repo (which has no PR).
215
+ Expected: the skill activates and stops at its configuration/verification
216
+ ladder reporting nothing to gate - no error about missing pi tools, no
217
+ mutation.
218
+ 8. Invoke `/gauntlet:check-delivery` with no deliverable reference. Expected:
219
+ the skill activates and asks for / reports a missing deliverable set - a
220
+ reported skip, not a pass, and no pi-tool error.
221
+ 9. Negative check: type `/gauntlet:brainstorming`. Expected: no such skill -
222
+ the allowlist excluded it.
223
+ 10. Optional validator pass: `claude plugin validate .` from a checkout of
224
+ pi-gauntlet (strict mode if available). Expected: no schema errors.
225
+ 11. Agent check: type `@gauntlet:` in the mention typeahead (or open the
226
+ plugin's details). Expected: the plugin registers no agents - `"agents": []`
227
+ in the marketplace entry suppresses the default `agents/` scan of the
228
+ repo's pi personas.
229
+
127
230
  ## Project-specific overrides
128
231
 
129
232
  The skills shipped here are generic on purpose - they describe *how* to TDD, brainstorm, debug, request review, etc., without naming your services, your CI command, or your worktree wrapper. When you need that level of detail, drop a file at `.pi/gauntlet-overrides.md` in your repo. The skills read it at runtime and merge sections that match the skill's name or topic:
@@ -259,7 +362,9 @@ customization lives in two places, never in the wrapper itself:
259
362
 
260
363
  An existing `## verification-before-completion` overrides section is an accepted
261
364
  equivalent source for the verification command only; all other PR-gate keys
262
- still live under `## PR gate`.
365
+ still live under `## PR gate`. A `## comms style` section in the same overrides
366
+ file extends gatekeep-pr's output done-check (rules applied to review bodies,
367
+ replies, tracker comments, and commit subjects before posting).
263
368
 
264
369
  Anything a wrapper skill contains beyond trigger phrases is misplaced - move it to
265
370
  `REVIEW.md` or the overrides file instead.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "4.12.2",
3
+ "version": "4.13.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",
@@ -165,10 +165,18 @@ verification command may write to the tree while the Reviewer reads it):
165
165
  claim is a blocking finding. An `unverifiable-pre-merge` claim used as merge proof
166
166
  (appears in the PR body's evidence/result/test-plan content) is blocking; stated as
167
167
  an explicit post-merge observation instead, it is a non-blocking follow-up.
168
- - **Required CI checks:** a failing or pending **required** status
169
- check withholds merge from every pre-composed course until the user explicitly
170
- dispositions it - flaky (proceed via the custom row) or real (it blocks). Non-required
171
- checks are informational, listed in Evidence only.
168
+ - **Required CI checks:** a failing **required** status check withholds merge from
169
+ every pre-composed course until the user explicitly dispositions it - flaky
170
+ (proceed via the custom row) or real (it blocks); it mints a `P#`. A **pending**
171
+ required check (still running - the normal case, not a defect) is **wait-until-
172
+ green, not dispositionable**: it mints no `P#`, is never flaky/real-dispositioned,
173
+ and the withhold auto-lifts the moment it turns green - or, if it instead fails,
174
+ converts into an undispositioned failing check with its own `P#` at that point.
175
+ While pending, the report notes it under Evidence and every merge course simply
176
+ does not render (a pending-only PR is not a blocking verdict - findings groups can
177
+ all read "None" - the recommended course falls to `stop` or `review-comment`,
178
+ never a merge course, until it resolves). Non-required checks are informational,
179
+ listed in Evidence only.
172
180
  - **Doc drift:** when the review finds committed doc drift as a **blocking** finding,
173
181
  the orchestrator applies the doc fixes itself, in the provisioned worktree (created
174
182
  or reused), as part of assessment - real edits, uncommitted, worktree-local. The
@@ -208,8 +216,8 @@ issue is linked, committed doc drift, anything the merged rubric maps to blockin
208
216
 
209
217
  **Merge preconditions** (all must hold): gate green with every blocking finding fixed,
210
218
  not deferred; `mergeable == MERGEABLE` (`UNKNOWN` after the one post-provision
211
- re-poll withholds merge, same as `CONFLICTING`); no undispositioned failing required
212
- check; evidence pasted with clean provenance; worktree clean and synced with the remote
219
+ re-poll withholds merge, same as `CONFLICTING`); no undispositioned failing or
220
+ pending required check; evidence pasted with clean provenance; worktree clean and synced with the remote
213
221
  head (fixes pushed first); explicit selection with a head compare-and-swap that
214
222
  passes. A merge selection while any precondition fails is refused, naming the failing
215
223
  precondition, and the menu re-renders - never a dead end, never a silent merge. Merge
@@ -229,14 +237,17 @@ are never bundled into one selection.
229
237
  | any | draft PR | assessment rows only; merge and approve rows absent until ready-for-review |
230
238
  | any | merged / closed | report-only; no mutation rows |
231
239
 
232
- Plus always: a final **custom row** composing the full action vocabulary (apply code
233
- fixes / push doc fixes / post review / reply to thread / merge / tracker comment when
234
- a tracker tool resolved). Rows GitHub would refuse (branch protection, missing
235
- permissions, `viewerPermission` too low) are listed as unavailable with the reason.
236
- Approving your own PR is not offered. Nothing executes until explicit selection.
240
+ The consent table above remains the single oracle for what may be offered; the
241
+ Decision rendering section defines how its rows render as actions and numbered
242
+ courses. Rows GitHub would refuse (branch protection, missing permissions,
243
+ `viewerPermission` too low) are listed as unavailable with the reason. Approving
244
+ your own PR is not offered. Nothing executes until explicit selection.
237
245
 
238
246
  ## Output
239
247
 
248
+ The rendered report is terse by design: deciding factor, evidence lines, ID'd
249
+ findings, menu. No restating diffs, no narration, no recap prose.
250
+
240
251
  ```markdown
241
252
  ## Outcome
242
253
  <one line + the deciding factor>
@@ -245,21 +256,171 @@ Approving your own PR is not offered. Nothing executes until explicit selection.
245
256
  <verbatim command + raw_tail per run; claims checked; CI rollup with required-check disposition>
246
257
 
247
258
  ## Findings (blocking)
248
- <file:line, defect, fix>
259
+ Blocking findings (P#):
260
+ P1. **<source_ref>** - <defect>. Fix: <concrete change> | Action: <disposition/what unblocks>. [code | test | spec | security | performance | quality]
261
+ Requirement/doc drift (linked issue, committed doc drift, or spec conflict):
262
+ L1. <doc-drift | spec-conflict | outdated-AC | missing-behavior> -> <action>
263
+
264
+ ## Comment-thread replies (existing discussion - verdict-neutral)
265
+ C1. <thread ref> -> <drafted reply> (already-addressed | reasonable | judgment-call)
249
266
 
250
267
  ## Non-blocking follow-ups
251
- <list, or "None">
268
+ F1. **<source_ref>** - <action>. Owner: <pr-author | tracker | human>
252
269
 
253
270
  ## Decision
254
- <the menu>
271
+ <action vocabulary + numbered courses - see Decision rendering>
255
272
 
256
273
  ## Drafted fixes / review
257
- <the exact payload to be applied or posted>
274
+ <payloads, each keyed by its finding ID>
275
+ ```
276
+
277
+ **ID rules:**
278
+
279
+ - `<source_ref>` is a `file:line` where one exists, else the disputed thing (a
280
+ quoted PR-body claim, a failing gate command, a required check name).
281
+ - **Precedence:** Phase 4 and the merged rubric decide blocking vs. follow-up (the
282
+ severity translation, AC coverage, claims, and required-check rules above); this
283
+ section only chooses **which namespace** (`P#` / `L#` / `F#`) renders that
284
+ decision. Category tags and the triage bar below never override an upstream
285
+ blocking classification - a Phase-4 Moderate is always blocking (`P#` or `L#`
286
+ per Total mapping), never demoted to `F#` by tag or by judgment call.
287
+ - **Total mapping:** every blocking element of the Verdict maps to a `P#` or `L#` -
288
+ a blocking verdict with "None" in both groups is a rendering bug. Concretely:
289
+ failed gate -> `P#` `[test]` referencing the gate command; contradicted or
290
+ merge-proof-unverifiable material claim -> `P#` `[spec]` referencing the claim;
291
+ scope creep with a linked issue -> `L#` `spec-conflict`; committed doc drift ->
292
+ `L#` `doc-drift`; `partial` AC coverage -> `L#` `outdated-AC`; `missing` AC
293
+ coverage -> `L#` `missing-behavior`. `L#` covers exactly the drift the Verdict
294
+ already blocks on (committed doc drift, AC coverage, spec conflict); it widens
295
+ nothing. Code-level spec bugs (the diff contradicts the spec) are `P#` `[spec]`;
296
+ requirement/doc mismatches (the spec or docs are stale relative to intent) are
297
+ `L#`.
298
+ - **Required checks close by disposition, not by fix:** an undispositioned failing
299
+ required check is `P#` `[test]` referencing the check name; it is never a target
300
+ of a worktree `fix`. The user's Phase-4 disposition annotates the same ID rather
301
+ than closing it outright: dispositioned **flaky** -> annotate
302
+ `(dispositioned: flaky)`; this annotation excepts the `P#` from the unfixed-blocker
303
+ set - it no longer counts against "every `P#` blocks" or the merge precondition
304
+ "every blocking finding fixed", and the merge path is Phase 4's explicit flaky
305
+ disposition via the custom row. Dispositioned **real** -> annotate
306
+ `(dispositioned: real)` and the `P#` keeps blocking until the check is green.
307
+ - **Severity is decided at triage, not by the category tag:** a finding lands in
308
+ `P#` only when it must be fixed before merge (correctness, security, material
309
+ performance trap, a convention the repo enforces); improvements that don't
310
+ change merge correctness are `F#`, whatever their category. `[quality]` on a
311
+ `P#` is a category, never a downgrade - every `P#` blocks. This triage bar governs
312
+ findings the orchestrator originates itself; it never re-triages a classification
313
+ Phase 4 already made (see Precedence above).
314
+ - `C#` replies are verdict-neutral drafts: they never block and never gate merge;
315
+ nothing posts until selected.
316
+ - `F#` items carry an owner so follow-ups don't evaporate; when no tracker tool
317
+ resolved, the report itself is their durable home.
318
+ - **IDs are append-only for the run's lifetime:** minted at first assessment,
319
+ never renumbered, never reused. A resolved finding keeps its ID annotated
320
+ `(fixed in <sha>)`; later rounds continue each namespace's sequence.
321
+ - Empty groups say "None".
322
+
323
+ "Drafted fixes / review" holds, per finding ID, the concrete edit (for `fix`), the
324
+ reviewed doc-drift edit (for `push-docs`, keyed to its `L#`), or the reply text
325
+ (for `reply`) - each keyed to its finding ID, one selection mapping 1:1 to its
326
+ payload. A posted review body is not itself a finding: it is composed at post time
327
+ from the ID'd `P#`/`L#` findings being addressed - one summary sentence, then the
328
+ numbered findings, ending on the fix or asked action - and occupies its own
329
+ non-finding slot of this section.
330
+
331
+ ## Decision rendering
332
+
333
+ `## Decision` has two parts: the action vocabulary, then the numbered courses.
334
+
335
+ **Action vocabulary** (bare verbs; availability constraints inline):
336
+
337
+ ```markdown
338
+ Actions (compose freely in the custom row):
339
+ fix <P#s|all> apply blocking fixes in worktree, re-run gate, push (in-repo PRs only)
340
+ push-docs push already-applied doc-drift edits (only when uncommitted
341
+ reviewed doc edits exist
342
+ in the worktree)
343
+ merge-squash | merge-commit (preconditions per Verdict;
344
+ never bundled with a push)
345
+ request-changes | review-comment | approve (approve: never own PR)
346
+ reply <C#s> post drafted thread replies
347
+ tracker <act> tracker action (only when a tracker tool resolved)
348
+ stop leave the PR as-is / report-only exit
258
349
  ```
259
350
 
260
- Empty lists say "None". For code fixes, "Drafted fixes / review" holds the concrete
261
- edit per finding; for reviews, the full body - one summary sentence, then numbered
262
- file:line findings, ending on the fix.
351
+ A `+ tracker <act>` suffix is available on any mutation course when a tracker tool
352
+ resolved.
353
+
354
+ **Selection grammar:** ID sets accept `all`, ranges (`P1-P4`), comma lists
355
+ (`P1,P3`), and exclusions (`all but P2`).
356
+
357
+ **Numbered courses** - a normative rendering of the consent table (never a second
358
+ offer source): per author x state cell, exactly one `[recommended]` course first,
359
+ the custom row always last. Courses are **atomic across pushes**: no course,
360
+ pre-composed or custom, bundles a push-producing action (`fix`, `push-docs`) with
361
+ `merge-*`; after a fix wave the menu re-renders with merge as row 1.
362
+
363
+ | Author | State | Courses (first = `[recommended]`) |
364
+ |---|---|---|
365
+ | you | clean / follow-ups only | 1. merge-squash; 2. merge-commit; 3. stop; 4. review-comment (post no-blockers note) |
366
+ | you | blocking | 1. fix (worktree-fixable P#s only - `all` covers only those) [+ push-docs when uncommitted doc edits exist]; 2. push-docs (alone, when doc edits exist); 3. stop; 4. review-comment (post findings). When no P# is worktree-fixable (blocking is required-check-only or L#-only), course 1 (fix) is not rendered: push-docs becomes first when doc edits exist, else stop is first |
367
+ | you | blocking, post-fix re-render (gate green, preconditions hold) | 1. merge-squash; 2. merge-commit; 3. stop; 4. review-comment |
368
+ | someone else | clean / follow-ups only | 1. approve; 2. merge-squash (offered-unrecommended); 3. review-comment (no-blockers note) |
369
+ | someone else | blocking | 1. request-changes; 2. fix all (courtesy, their branch - omitted when nothing is worktree-fixable); 3. reply <C#s> (omitted when the `C#` group is None); 4. review-comment |
370
+ | bot author | any | someone-else's rows for the same state; review actions recommended |
371
+ | any | draft | 1. request-changes / review-comment / reply <C#s> (omit the reply course when the `C#` group is None) / stop - `[recommended]` follows the same authorship rule as the non-draft cells, **except** on your own draft PR `request-changes` is never recommended (you cannot request changes on your own PR any more than you can approve it); the fallback recommendation there is `review-comment` when findings exist, else `stop`. Custom present but cannot compose `merge-*`/`approve`/`fix`/`push-docs` until ready-for-review |
372
+ | any | merged / closed | 1. stop; report-only, no other mutation courses at all; Custom present but cannot compose `merge-*`/`approve`/`fix`/`push-docs`/`request-changes`/`review-comment`/`reply`/`tracker` - nothing remains actionable |
373
+
374
+ **Fork overlay:** the consent-table fork row renders as an overlay on the authorship
375
+ cells (push/merge/fix absent; approve also dropped when the viewer authored the PR) -
376
+ it is not a distinct authorship cell. It overlays whichever authorship row above
377
+ applies (you vs. someone else), removing `fix`, `push-docs`, and `merge-*` (never
378
+ available on a fork). When you authored the fork PR, `approve` is
379
+ also dropped (never offered on your own PR) - fork|you|clean renders
380
+ `review-comment`/`stop` only; fork|you|blocking renders
381
+ `request-changes`/`review-comment`/`stop` (the someone-else courtesy fix-on-their-
382
+ branch course is also absent, since it is your own PR). A fork PR authored by someone
383
+ else uses the someone-else cells above with `fix`/`push-docs`/`merge-*` removed.
384
+
385
+ **Required-check gate on merge courses:** an undispositioned failing **or pending**
386
+ required check withholds every pre-composed course containing `merge-*` (per the
387
+ Verdict merge preconditions) - none render, whatever the author/state cell says. A
388
+ pending check mints no `P#` and is wait-until-green, not dispositionable (see Phase
389
+ 4); a failing one mints a `P#` and takes a disposition. A **flaky** disposition does
390
+ not restore merge to a pre-composed course; merge proceeds only via the custom row
391
+ naming the disposition explicitly. A **real** disposition, or an unresolved pending
392
+ check, keeps every merge course withheld until the check is green - a pending-only
393
+ render is not itself a blocking verdict (findings groups may all read "None"); the
394
+ recommended course falls to `stop` or `review-comment` in the meantime. This never
395
+ falls through to the clean cell's recommended `merge-squash` - a required-check
396
+ failure or pend means the PR is not in the clean state to begin with.
397
+
398
+ Rows a cell offers but GitHub would refuse (branch protection, missing permission)
399
+ render listed-but-unavailable with the reason. Zero mutation courses is a legal
400
+ render (merged/closed) - the menu still appears, carrying findings and `stop`.
401
+
402
+ Example render (golden fixture 1 - own PR, blocking findings including committed doc
403
+ drift, so uncommitted reviewed doc edits exist):
404
+
405
+ ```markdown
406
+ Pick one:
407
+ 1. fix all (P1-P10) + push-docs [recommended]
408
+ 2. push-docs (docs only, hold code fixes)
409
+ 3. stop (leave as-is)
410
+ 4. review-comment (post findings, act later)
411
+ 5. Custom - compose: e.g. "fix P1-P8,P10 + push-docs" or "reply C1 + tracker comment"
412
+ ```
413
+
414
+ Golden fixture 2 - the post-fix re-render after course 1's gate re-run passes:
415
+
416
+ ```markdown
417
+ Pick one:
418
+ 1. merge-squash [recommended]
419
+ 2. merge-commit
420
+ 3. stop (leave as-is)
421
+ 4. review-comment
422
+ 5. Custom
423
+ ```
263
424
 
264
425
  ## Post-selection loop
265
426
 
@@ -267,22 +428,83 @@ The menu is a state machine, not a one-shot report:
267
428
 
268
429
  1. **Compare-and-swap before every external write:** re-fetch `headRefOid`, `state`,
269
430
  `mergeable`. Any change since assessment invalidates the current state - re-sync
270
- the worktree, re-run Phase 3, re-render the menu.
271
- 2. Execute only the selected row: code fixes -> commit on the PR branch (subject
272
- names the fix), re-run the gate, push. Doc fixes -> stage + commit (subject names
273
- what is documented), re-run the gate, push. Reviews and comments -> `gh pr review`
274
- / `gh api`, non-interactive, with the drafted body.
275
- 3. After any mutation that can change readiness (fix pushed, docs pushed, PR head
276
- moved), re-run Verify + Review on the synced worktree and re-render `## Outcome`,
277
- `## Evidence`, `## Findings`, and the menu.
431
+ the worktree, re-run Phase 3, re-render the menu. **Exception:** a course's own
432
+ push updates the assessed head to the pushed SHA as part of that course's
433
+ execution - this self-inflicted head move does not invalidate the course; the
434
+ next CAS check runs against the new head on the next external write.
435
+ 2. Execute only the selected course. **Fix wave** (`fix <set>`): first filter the
436
+ selected set to worktree-fixable `P#`s - drop any `P#` closed by disposition
437
+ (an undispositioned required-check failure is never a `fix` target; a **flaky**
438
+ disposition already excepts it) - and route file-less `P#`s (a claim or a gate
439
+ command as `source_ref`, no draft touching a file) to run inline/sequentially,
440
+ never as part of a parallel file-batch.
441
+
442
+ For the remaining worktree-fixable set, batch by the **union of files each
443
+ finding's drafted edit in `## Drafted fixes / review` touches** (fallback to
444
+ the `source_ref` file only when a finding has no draft) - findings whose
445
+ drafts share a file share a batch.
446
+
447
+ **Child contract (same for 1 batch or many):** children are `implementer`
448
+ dispatches - `subagent({ agent: "implementer", context: "fresh", cwd: <PR
449
+ worktree> })`, matching Inline-first execution's persona-naming style; this is
450
+ the pi-cohort optimization path, inline is always valid per that section. The
451
+ orchestrator owns commit, gate, and push - never a child. Every dispatched
452
+ child gets `cwd` = the PR worktree, **`worktree: true` forbidden** (a separate
453
+ isolated worktree breaks the shared-tree contract - see Inline-first
454
+ execution); is **edit-only, no git commands, no verification runs**; and its
455
+ task is that batch's `P#` lines **plus the drafted edit already keyed to each
456
+ ID** in `## Drafted fixes / review` - the child applies the consented payload,
457
+ it does not re-solve the finding. Below the cutoff (<= 2 worktree-fixable findings), the orchestrator
458
+ applies inline instead of dispatching - the no-cohort path stays available at
459
+ any batch count per Inline-first execution. Above the cutoff, when more than
460
+ one batch results, dispatch the batches' children **in parallel**, all under
461
+ the same contract.
462
+
463
+ Once every dispatched/inline batch returns, the orchestrator commits the golden
464
+ course as one local commit set - the code fixes plus any already-applied
465
+ reviewed doc edits selected alongside them (one commit, or one per batch
466
+ sequentially; subjects name the fixes) - then re-runs the gate **once**. **On
467
+ green**, push **once**; gate and push are per-wave invariants, never per-fix or
468
+ per-batch. **On red**, do not push: leave the commit(s) local, re-render with
469
+ the unresolved `P#`s still open, and warn that unpushed fix commits sit in the
470
+ worktree exactly like unpushed doc edits (see Teardown). Doc fixes (`push-docs`
471
+ alone) follow the same green-gate-then-push rule: stage + commit (subject names
472
+ what is documented), re-run the gate, push only on green. Reviews, replies, and
473
+ tracker actions -> `gh pr review` / `gh api` / the tracker tool, non-interactive,
474
+ with the drafted payload for the selected IDs.
475
+ 3. After any mutation that can change readiness (fix wave pushed, docs pushed, PR
476
+ head moved), re-run the claim-check and Review on the synced worktree: claims
477
+ are re-checked against the new head and findings are re-rendered, but the
478
+ verification command itself is **not** re-executed here - step 2's gate run
479
+ already was the wave's one and only execution of it. Re-render the report:
480
+ each selected `P#`/`L#` confirmed resolved is annotated `(fixed in <sha>)`
481
+ under its original ID; unresolved ones stay open unchanged; new findings
482
+ continue the sequence. Merge, if now available, renders as row 1.
278
483
  4. Loop until the user selects merge or an explicit stop/no-action row.
279
484
 
280
485
  **Teardown:** merge success -> tear down the worktree, whether it was reused or
281
486
  created (the sync precondition guarantees no local-only work is stranded, and the
282
487
  branch is gone remotely). A non-merge stop: offer teardown of a **created** worktree
283
488
  (never autonomous; warn if unpushed doc edits would be discarded); a **reused**
284
- worktree is left as found - if unpushed doc edits remain in it, say so explicitly and
285
- let the user choose leave-or-discard.
489
+ worktree is left as found - if unpushed doc edits **or unpushed fix commits from a
490
+ red-gate hold** remain in it, say so explicitly and let the user choose
491
+ leave-or-discard.
492
+
493
+ ## Output done-check
494
+
495
+ Before posting or committing any externally persisted payload - review bodies,
496
+ thread replies, commit subjects, tracker comments - re-read it against:
497
+
498
+ - ASCII only: `-`, `...`, straight quotes.
499
+ - No headings or template scaffolding on payloads under ~150 words - bullets and
500
+ prose carry short content.
501
+ - Review findings are file:line-specific where one exists, else keyed to the
502
+ finding's `source_ref`, and end on the fix or asked action, not a recap.
503
+ - Empty sections say "None"; never invent content to fill a section.
504
+
505
+ The check governs external payloads only - the skill's own rendered report keeps
506
+ its fixed headings regardless of length. Project rules extend this list via the
507
+ overrides file - see Project overrides.
286
508
 
287
509
  ## Red flags - STOP
288
510
 
@@ -293,7 +515,27 @@ let the user choose leave-or-discard.
293
515
  - Merging around an undispositioned blocking finding or required-check failure
294
516
  - Reading configuration (rubric, verification command, or ladder sources) from the
295
517
  PR's head instead of the base branch's merge-base
518
+ - Renumbering or reusing a finding ID between menu rounds
519
+ - Presenting findings without IDs, a blocking verdict with no `P#`/`L#`, or a
520
+ `## Decision` rendered without its action vocabulary
521
+ - Treating `[quality]` or `[performance]` as a downgrade signal on a `P#` - only
522
+ an explicit Phase-4 flaky disposition excepts a required-check `P#` from the
523
+ unfixed-blocker set, never a category tag
524
+ - A course (pre-composed or custom) bundling a push-producing action with
525
+ `merge-*`
526
+ - A pre-composed course, or a custom row, composing an action the overlay or the
527
+ cell lists as unavailable
528
+ - Batching a file-less `P#` (a claim or a gate command as `source_ref`, no draft touching a file)
529
+ into a parallel dispatch - a claim `P#` with a drafted file edit is
530
+ worktree-fixable and may batch - dispatching parallel implementers over
531
+ batches that share a file, or letting a fix-wave child run git commands or a
532
+ verification pass in the shared worktree, or dispatching a fix-wave child with
533
+ `worktree: true`
534
+ - A second execution of the verification command, a second push, or pushing fix
535
+ commits after a red gate, within one fix wave - re-running Verify/Review to
536
+ re-confirm claims and annotate IDs (step 3) is not a second gate execution
537
+ - Posting or committing an external payload without the output done-check
296
538
 
297
539
  ## Project overrides
298
540
 
299
- If a gauntlet overrides file exists - checked in order: `.pi/gauntlet-overrides.md`, `<repo root>/gauntlet-overrides.md`, `<repo root>/doc/gauntlet-overrides.md`; first found wins - read it. Any sections relevant to this skill - by name match, by topic (routing, verification, worktrees, etc.), or by workflow convention - override or extend the instructions above. Project-local `AGENTS.md` is already in context - check it for project-specific routing tables, service paths, and verification commands.
541
+ If a gauntlet overrides file exists - checked in order: `.pi/gauntlet-overrides.md`, `<repo root>/gauntlet-overrides.md`, `<repo root>/doc/gauntlet-overrides.md`; first found wins - read it. Any sections relevant to this skill - by name match, by topic (routing, verification, worktrees, etc.), or by workflow convention - override or extend the instructions above. Project-local `AGENTS.md` is already in context - check it for project-specific routing tables, service paths, and verification commands. A `## comms style` section in the overrides file extends the output done-check with project rules.