pi-gauntlet 7.7.1 → 7.7.2

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v7.7.2 - 2026-10-08
4
+
5
+ ### Changed
6
+
7
+ - `gatekeep-pr`: `fix` applies open blockers only; nits move behind a `fix nits` verb whose payloads are drafted at the pick, and the report counts open nits. Each fix helper gets a scoped test command resolved from the merge-base overrides or `AGENTS.md`, never the verification command. The pre-push review lists only the wave's own findings; the next round carries open closure lines, in-delta Critical/Moderate, and outside-delta Critical findings.
8
+
3
9
  ## v7.7.1 - 2026-10-07
4
10
 
5
11
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "7.7.1",
3
+ "version": "7.7.2",
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",
@@ -28,7 +28,7 @@ Verify (step 3) and Review (step 4) each run as a fresh helper with `cwd` the PR
28
28
  | 4 Review | `verification-brief.md` Section C | reviewer findings and the internal comment ledger | `review` |
29
29
  | 5 Integrate + report | `reference/post-selection-loop.md` `### Pre-menu refresh`, then `reference/findings.md`, then `reference/report.md` | AC outcomes, blockers, nits, drafted payloads, the rendered report | `report` |
30
30
  | 6 Menu | `reference/decision-menu.md` | the menu under the verdict line, exactly one `[recommended]` | `menu` |
31
- | 7 Loop | `reference/post-selection-loop.md` (a `fix` pick continues in `reference/fix-wave.md`) | the executed pick; re-entry until `stop` | (re-opens the re-entered stage) |
31
+ | 7 Loop | `reference/post-selection-loop.md` (a `fix` or `fix nits` pick continues in `reference/fix-wave.md`) | the executed pick; re-entry until `stop` | (re-opens the re-entered stage) |
32
32
 
33
33
  Re-entry: a fix wave's own push re-enters step 4 through the own-push sequence (`reference/fix-wave.md` `## Evidence after push`, then `reference/post-selection-loop.md` `### Re-render`); any other head move re-enters step 3; an unchanged head re-enters step 5 through `reference/post-selection-loop.md` `### Pre-menu refresh`. A loop re-entry never re-runs step 2b; the sync runs once per invocation.
34
34
 
@@ -63,7 +63,7 @@ These bind every rendered report, menu, and external payload (review bodies, rep
63
63
  - Judging a ticket row the spec does not carry, or treating a spec `deferred: <ref>` row as a gap - owner: `reference/findings.md` `## AC outcomes`.
64
64
  - Reading the rubric, the verification command, or any ladder source from the PR's head instead of the base branch's merge-base - owner: `reference/assessment.md` `## Configuration`.
65
65
  - Raw command output, drafted payloads, or internal IDs in the rendered report - owner: `reference/report.md`.
66
- - An open-PR menu, other than the conflict menu, an own-merge or merge-queued menu, or a `--rebase` terminal stop (`stop` and `show evidence` only, `reference/sync.md`), without `fix` while a helper facility exists and the fix-round cap is neither `0` nor reached (a PR already merged or closed at step 1 offers `show evidence` and `stop` only); any menu without `show evidence`, with two `[recommended]`, or with `stop` not last - owner: `reference/decision-menu.md`.
66
+ - An open-PR menu, other than the conflict menu, an own-merge or merge-queued menu, or a `--rebase` terminal stop (`stop` and `show evidence` only, `reference/sync.md`), without `fix` while an open blocker exists, or without `fix nits` while a nit exists, when a helper facility exists and the fix-round cap is neither `0` nor reached (a PR already merged or closed at step 1 offers `show evidence` and `stop` only); any menu without `show evidence`, with two `[recommended]`, or with `stop` not last - owner: `reference/decision-menu.md`.
67
67
  - A local verification run while a binding check is pending, or a second push inside one round - owner: `reference/fix-wave.md` `## Evidence after push`.
68
68
  - The orchestrator resolving a merge conflict itself, or editing or committing a tracked file during a fix wave - owner: `reference/fix-wave.md` `## Conflicts` and `## Wave`.
69
69
  - A helper's duty run in the orchestrator's own context - owner: `## Harness notes`.
@@ -4,11 +4,12 @@ Read from SKILL.md step 6. Input: the report's verdict and the PR state. Output:
4
4
 
5
5
  ## Verbs
6
6
 
7
- `fix`, `push`, `review`, `approve`, `approve workflow run`, `update branch`, `merge`, `merge anyway`, `reply`, `post coverage to ticket`, `post coverage to PR`, `show evidence`, `wait`, `stop`.
7
+ `fix`, `fix nits`, `push`, `review`, `approve`, `approve workflow run`, `update branch`, `merge`, `merge anyway`, `reply`, `post coverage to ticket`, `post coverage to PR`, `show evidence`, `wait`, `stop`.
8
8
 
9
9
  Rows are `<n>. <verb> - <consequence>`; every row, `stop` included, carries its consequence clause. `stop` is always last, before the compose hint. `show evidence` is always offered and never `[recommended]`. A row the actor cannot execute - by `## Availability` below, or because the forge would refuse it (branch protection, a withhold) - is omitted and named in the compose line's `Not offered:` sentence; it is never `[recommended]`. Exactly one row is `[recommended]`.
10
10
 
11
- - `fix` - apply the drafted payloads for the named blockers (default: all open blockers; with no open blockers, the nit payloads) through fresh implementer helpers in the provisioned worktree, review the wave before any push, push when `push` is available (`fix-wave.md`); on a `rebased locally` head, lease-push once every carried blocker is resolved (`fix-wave.md` `## Evidence after push`); `fix nits` applies nit payloads only, `fix + nits` blockers and nits; with no drafted payload at all, the human names the change
11
+ - `fix` - apply the drafted payloads for the named blockers (open blockers by default) through fresh implementer helpers in the provisioned worktree, review the wave before any push, push when `push` is available (`fix-wave.md`); on a `rebased locally` head, lease-push once every carried blocker is resolved (`fix-wave.md` `## Evidence after push`); `fix + nits` composes this verb with `fix nits`
12
+ - `fix nits` - apply the nit payloads drafted at this pick (`findings.md` `## Drafted payloads`) through fresh implementer helpers in the provisioned worktree, review the wave before any push, push when `push` is available (`fix-wave.md`); `fix + nits` typed with zero open blockers runs as this verb
12
13
  - `push` - push local fix commits (renders only when unpushed commits exist)
13
14
  - `review` - post the blockers as a GitHub review: request-changes on someone else's PR, a comment on your own
14
15
  - `approve` - approve the PR (never your own)
@@ -23,7 +24,7 @@ Rows are `<n>. <verb> - <consequence>`; every row, `stop` included, carries its
23
24
  - `wait` - poll the reviewer run, every **binding** pending check in the resolved set (required or not), `mergeStateStatus` while it is `BLOCKED`, `UNKNOWN`, or `unreadable`, and the comment set, then re-render
24
25
  - `stop` - leave the PR as-is; on a `rebased locally` head run `git reset --hard <pre_head>` before `### Teardown`, discarding the rewrite and any unpushed fix commits, and assert `HEAD == <pre_head>` with a clean tree; a mismatch is the existing hard stop with the error, and the remote stays untouched
25
26
 
26
- Compose hint: `Type a number, or compose: "fix 2", "fix nits", "fix + nits", "fix 1 + reply". Not offered: push (needs push to <head_url>:<head_ref>), merge (needs WRITE on <owner>/<repo>).` The first sentence lists only compositions whose every verb is a rendered row: no `Nits` section, no `fix nits`; no `reply` row, no `fix 1 + reply`; no composable row, `Type a number.` alone. The second sentence names every omitted row as `<verb> (<why it is omitted>)` and is dropped when nothing is omitted. `merge anyway` renders as its own row when `## Availability` allows it, so the rendered-row rule does not apply to it. A composed line never bundles a push-producing verb (`fix`, `push`) with `merge`. When a failing check withholds `merge`, the hint line also offers `check <name> flaky | real | ci-broken`, which records the disposition on that check (`findings.md` `## Dispositions`): `flaky` lifts the blocker, `real` keeps it until green, `ci-broken` triggers the fallback local run (`ci-broken` records `ci-infrastructure-broken`).
27
+ Compose hint: `Type a number, or compose: "fix 2", "fix nits", "fix + nits", "fix 1 + reply". Not offered: push (needs push to <head_url>:<head_ref>), merge (needs WRITE on <owner>/<repo>).` The first sentence lists only compositions whose every verb is a rendered row: `fix nits` and `fix + nits` appear only when both the `fix` and `fix nits` rows render; no `reply` row, no `fix 1 + reply`; no composable row, `Type a number.` alone. The second sentence names every omitted row as `<verb> (<why it is omitted>)` and is dropped when nothing is omitted. `merge anyway` renders as its own row when `## Availability` allows it, so the rendered-row rule does not apply to it. A composed line never bundles a push-producing verb (`fix`, `fix nits`, `push`) with `merge`. When a failing check withholds `merge`, the hint line also offers `check <name> flaky | real | ci-broken`, which records the disposition on that check (`findings.md` `## Dispositions`): `flaky` lifts the blocker, `real` keeps it until green, `ci-broken` triggers the fallback local run (`ci-broken` records `ci-infrastructure-broken`).
27
28
 
28
29
  A composed line naming an omitted row is refused by name and the menu re-renders; the `merge anyway` row is the only override.
29
30
 
@@ -33,8 +34,9 @@ Rows derive from the digest's `permissions` block (`../verification-brief.md` Se
33
34
 
34
35
  | Row | Available when |
35
36
  |---|---|
36
- | `fix` | a helper facility exists (`../SKILL.md` `## Harness notes`) and the fix-round cap is not `0` and the round cap is not reached (`fix-wave.md` `## Wave`) |
37
- | `push` | `head_pushable` is true and unpushed wave commits exist and the last pre-push review had no Critical or Moderate finding |
37
+ | `fix` | an open blocker exists (a failing-check `P#` included; the wave reports a blocker with no file-level payload as not fixable, `post-selection-loop.md` `### Fix wave`), a helper facility exists (`../SKILL.md` `## Harness notes`), and the fix-round cap is neither `0` nor reached (`fix-wave.md` `## Wave`) |
38
+ | `fix nits` | a nit exists, a helper facility exists (`../SKILL.md` `## Harness notes`), and the fix-round cap is neither `0` nor reached (`fix-wave.md` `## Wave`); a `fix` with no open blocker, or a `fix nits` with no nit, is dropped from the row set and never named in `Not offered:` |
39
+ | `push` | `head_pushable` is true and unpushed wave commits exist and the last pre-push review's carried set (`fix-wave.md` `## Review before push`) held no Critical or Moderate |
38
40
  | `merge` | `viewer_permission` is `WRITE`, `MAINTAIN`, or `ADMIN`, and the merge preconditions hold (`post-selection-loop.md`) |
39
41
  | `merge anyway` | its own overlay's withhold is the only unmet merge prerequisite (`post-selection-loop.md` `### Merge preconditions`) and `viewer_permission` is `WRITE`, `MAINTAIN`, or `ADMIN`; otherwise omitted and named in `Not offered:` |
40
42
  | `approve workflow run` | `viewer_permission` is `WRITE`, `MAINTAIN`, or `ADMIN`, and a held run exists on the assessed head |
@@ -50,12 +52,12 @@ A `fixable` verdict with zero blockers (a withhold reason) uses the mergeable ro
50
52
 
51
53
  | Author | Verdict | Rows |
52
54
  |---|---|---|
53
- | you | mergeable | `merge`; `fix`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
54
- | you | fixable | `fix`; `review`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
55
- | someone else | mergeable | `approve`; `fix`; `merge`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
56
- | someone else | fixable | `review`; `fix`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
55
+ | you | mergeable | `merge`; `fix nits`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
56
+ | you | fixable | `fix`; `fix nits`; `review`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
57
+ | someone else | mergeable | `approve`; `fix nits`; `merge`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
58
+ | someone else | fixable | `review`; `fix`; `fix nits`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
57
59
 
58
- `push` joins any row set after `fix` when unpushed fix commits sit in the worktree.
60
+ `push` joins any row set after `fix` or `fix nits` when unpushed fix commits sit in the worktree.
59
61
 
60
62
  ## Withhold reason resolver
61
63
 
@@ -74,14 +76,14 @@ Overlays modify the consent row; they are never a second offer source, except `o
74
76
  | own merge | this run's `merge` pick ran and the post-merge read (`post-selection-loop.md` `### Merge course`) returned `state: MERGED` | the menu is `post coverage to ticket` (when `issue` is non-null and `scope.rows` is non-empty), `post coverage to PR`, `show evidence`, `stop`, with `stop` `[recommended]`; each post row renders once and leaves the menu after its post succeeds; a failed post keeps its row and prints the `gh` error; no compare-and-swap runs before a post |
75
77
  | merge queued | this run's `merge` pick ran and the post-merge read returned a state other than `MERGED` | the menu is `wait` (re-run the post-merge read, then re-render), `show evidence`, `stop`, with `wait` `[recommended]`; one `PR comments` line `The merge is queued, so coverage posts wait. (<state>)` |
76
78
  | merged or closed PR | `state` was not `OPEN` at step 1 | the report renders; the menu is `show evidence` and `stop` only |
77
- | draft PR | `isDraft` | `merge` and `approve` are omitted (`Not offered: merge (draft)`, plus `approve (draft)` when the consent row holds `approve`) and never carry `[recommended]`; `review` is recommended on someone else's fixable draft and `fix` on their mergeable draft; on your own, `fix` when a blocker exists, else `stop` |
79
+ | draft PR | `isDraft` | `merge` and `approve` are omitted (`Not offered: merge (draft)`, plus `approve (draft)` when the consent row holds `approve`) and never carry `[recommended]`; `review` is recommended on someone else's fixable draft, `fix nits` on their mergeable draft when a nit exists, else `stop`; on your own, `fix` when a blocker exists, else `stop` |
78
80
  | held run | an `action_required` conclusion on an Actions run of the assessed head (`../verification-brief.md` Evidence resolution, Held run) with no `local run: held run not approvable` evidence yet for that head | `approve workflow run` renders as row 1 when available (`## Availability`) and is `[recommended]` when `Blockers` is empty, else `[recommended]` stays on the consent row's first row; `merge` is omitted (`Not offered: merge (workflow run awaiting approval)`); once the held-run-not-approvable local run exists, this overlay no longer applies and `merge` follows the preconditions |
79
81
  | merge state | `merge_state_status` is `BLOCKED`, `UNKNOWN`, or `unreadable`, or the pre-menu refresh failed (`post-selection-loop.md` `### Pre-menu refresh`) - independent of any pending check, so a green-CI state-only block fires it | one `PR comments` line from `## Withhold reason resolver` (reason 1 or 2); `wait` as row 1; `[recommended]` goes to `approve` when the consent row renders it (someone else's PR, resolver reason 1, `reviewDecision` `REVIEW_REQUIRED`) - recommending a row attributes nothing to a rule - else to `wait` when `Blockers` is empty; `merge` omitted with the resolver's `Not offered` text; no `anyway` row |
80
82
  | verification evidence pending | the Evidence resolution table resolved Pending for the assessed head (`../verification-brief.md` Section B) | puts `wait` as row 1 and, when `Blockers` is empty, `[recommended]`; `merge` is omitted with `## Withhold reason resolver` reason 3's text; consent rows otherwise unchanged |
81
83
  | CI check | an undispositioned failing check in the resolved set, or a binding pending check (`findings.md` `## Dispositions`); a held run is not a binding pending check here (the `held run` overlay owns it) | on a failing check, the hint line offers `check <name> flaky \| real \| ci-broken`; `merge` is omitted (`Not offered: merge (<check name> failing)`); `flaky` restores `merge` on the next render; `real` and `ci-infrastructure-broken` keep it omitted until green; a binding pending check renders one `PR comments` line and omits `merge` with the text of `## Withhold reason resolver` reason 3, and puts `wait` as row 1 and, when `Blockers` is empty, `[recommended]`; under both this and the `merge state` overlay the higher one owns the reason and the single `PR comments` line; it mints no disposition hint (not dispositionable) |
82
84
  | pending reviewer | a `pending` ledger row, a queued or in-progress reviewer run on the assessed head, or a failed comment refetch (`post-selection-loop.md` `### Re-render`) | puts `wait` as row 1 and, when `Blockers` is empty, `[recommended]`; `merge` is omitted (`Not offered: merge (reviewer still running)` or `merge (comments not refreshed)`); `merge anyway` renders as its own row when `## Availability` allows it, overrides only this overlay, and does not bypass an unreviewed delta or a blocker; a `reviewer failed (<conclusion>)` row changes nothing |
83
- | comment source review incomplete | the `### Re-render` step-4 source review of a comment delta did not finish (`post-selection-loop.md`) | one `PR comments` line `The comment source review did not finish, so merge waits. (<reason>)`; `merge` is omitted (`Not offered: merge (comment source review incomplete)`) with no `anyway` row; `reply` and `review` are dropped (the delta is unreviewed); `fix`, `show evidence`, `stop` stay; `stop` is `[recommended]` |
84
- | head not pushable | `head_pushable` is false or `unreadable` (a fork head, a branch this actor cannot push, or an unreadable head) | `push` is omitted (`Not offered: push (needs push to <head_url>:<head_ref>)`); `fix` stays and applies to the local worktree branch (`assessment.md` provisioning), its consequence clause reading "open a PR from that branch, or hand the patch to the author"; `merge` follows `## Availability` |
85
+ | comment source review incomplete | the `### Re-render` step-4 source review of a comment delta did not finish (`post-selection-loop.md`) | one `PR comments` line `The comment source review did not finish, so merge waits. (<reason>)`; `merge` is omitted (`Not offered: merge (comment source review incomplete)`) with no `anyway` row; `reply` and `review` are dropped (the delta is unreviewed); `fix`, `fix nits`, `show evidence`, `stop` stay; `stop` is `[recommended]` |
86
+ | head not pushable | `head_pushable` is false or `unreadable` (a fork head, a branch this actor cannot push, or an unreadable head) | `push` is omitted (`Not offered: push (needs push to <head_url>:<head_ref>)`); `fix` and `fix nits` stay and apply to the local worktree branch (`assessment.md` provisioning), their consequence clauses reading "open a PR from that branch, or hand the patch to the author"; `merge` follows `## Availability` |
85
87
  | bot author | `author_is_bot` | the someone-else rows |
86
88
 
87
89
  ## Fixtures
@@ -99,20 +101,21 @@ PR comments:
99
101
 
100
102
  Nits:
101
103
  - The export pages results with its own loop instead of the shared paging helper, so a paging bug fixed once would need fixing twice. (src/api/export.ts:31, lib/page.ts)
104
+ 1 nit open - fix nits to take them
102
105
 
103
106
  Verdict: fixable - reviewer run in progress
104
107
 
105
108
  1. wait - poll the reviewer run, then re-render [recommended]
106
- 2. fix - apply the nit payload in the worktree, review the wave, push
109
+ 2. fix nits - apply the nit payload in the worktree, review the wave, push
107
110
  3. reply - post the drafted reply to maria
108
111
  4. show evidence - gate output, CI run
109
112
  5. stop - leave the PR as-is
110
- Type a number, or compose: "fix nits". Not offered: merge (reviewer still running).
113
+ Type a number. Not offered: merge (reviewer still running).
111
114
  ```
112
115
 
113
116
  Fixture 3 - a different PR: your own, every spec row covered, no nits, no drafted replies; the reviewer bot's run was the only withhold and `wait` just returned.
114
117
 
115
- (a) The run concluded `failure`: the `PR comments` line disappears, the reviewer check is inert, the verdict is `mergeable - CI green on the assessed head (test)`, and the menu is `merge` [recommended], `fix`, `show evidence`, `stop`.
118
+ (a) The run concluded `failure`: the `PR comments` line disappears, the reviewer check is inert, the verdict is `mergeable - CI green on the assessed head (test)`, and the menu is `merge` [recommended], `show evidence`, `stop`.
116
119
 
117
120
  (b) The run concluded `success` and the verdict comment raises a retry bug; source review confirms it and mints a blocker:
118
121
 
@@ -66,6 +66,6 @@ How each disposition changes the menu: `decision-menu.md` `## Overlays`, CI chec
66
66
 
67
67
  ## Drafted payloads
68
68
 
69
- Step 5 drafts, never applies. For every blocker and nit with a file-level fix - code and doc drift alike - draft the concrete edit as a payload keyed to the finding's internal ID. For every replyable `C#`, draft the reply. The worktree stays tracked-clean (`git status --porcelain --untracked-files=no` empty) at every menu render; a payload is applied only on a `fix` pick (the same path for code and docs) and dropped at teardown otherwise. A `P#` carried by a `rebased locally` sync record is drafted like any blocker, from the helper's test output and `git show <stopped commit>`; a `P#` with no draftable edit stays open and `fix` reports it as not fixable. The human sees the draft at the `fix` consent and decides whether code or test expectations change; the implementer never decides it.
69
+ Step 5 drafts, never applies. For every blocker with a file-level fix - code and doc drift alike - draft the concrete edit as a payload keyed to the finding's internal ID. Draft nit payloads on a `fix nits` or `fix + nits` pick, which is the consent: print them in the `show evidence` payload shape and dispatch the wave in the same pick with no second confirmation; before that pick `show evidence` prints only the payloads that exist. For every replyable `C#`, draft the reply. The worktree stays tracked-clean (`git status --porcelain --untracked-files=no` empty) at every menu render; a payload is applied only on a `fix`, `fix nits`, or `fix + nits` pick (the same path for code and docs) and dropped at teardown otherwise. A `P#` carried by a `rebased locally` sync record is drafted like any blocker, from the helper's test output and `git show <stopped commit>`; a `P#` with no draftable edit stays open and `fix` reports it as not fixable. The human sees the draft at the `fix` consent and decides whether code or test expectations change; the implementer never decides it.
70
70
 
71
71
  A posted review body is composed at post time from the blockers being addressed - one summary sentence, then the numbered items, ending on the fix or asked action.
@@ -1,6 +1,6 @@
1
1
  # gatekeep-pr: fix wave
2
2
 
3
- Read from `post-selection-loop.md` `### Fix wave` once the payloads for a `fix` pick are selected. Input: the selected payloads (`findings.md` `## Drafted payloads`), the pre-wave findings list, and the digest. Output: pushed commits with current evidence, or unpushed commits with the menu re-rendered. The orchestrator edits no tracked file and runs no git write other than `fetch`, `merge --ff-only`, `push`, and `reset --hard <pre_head>` after a refused lease or a remote head move on a `rebased locally` head (`post-selection-loop.md` `### Compare-and-swap`); every edit and every commit belongs to a fresh helper (`../SKILL.md` `## Harness notes`).
3
+ Read from `post-selection-loop.md` `### Fix wave` once the payloads for a `fix` or `fix nits` pick are selected. Input: the selected payloads (`findings.md` `## Drafted payloads`), the dispatched findings, and the digest. Output: pushed commits with current evidence, or unpushed commits with the menu re-rendered. The orchestrator edits no tracked file and runs no git write other than `fetch`, `merge --ff-only`, `push`, and `reset --hard <pre_head>` after a refused lease or a remote head move on a `rebased locally` head (`post-selection-loop.md` `### Compare-and-swap`); every edit and every commit belongs to a fresh helper (`../SKILL.md` `## Harness notes`).
4
4
 
5
5
  ## Conflicts
6
6
 
@@ -26,13 +26,13 @@ A worktree holding unpushed wave commits never shows `update branch`. The orches
26
26
 
27
27
  ## Wave
28
28
 
29
- `pre_wave_head` was recorded before the first `## Conflicts` check. A round is one pass over the open payloads plus one `## Review before push`. The round cap is `closureReview.maxFixRounds`, read with `gauntlet_setting({ key: "closureReview" })` when that tool exists, else 3; a cap of `0` omits `fix` from the menu, named as `fix (maxFixRounds is 0)` in the `Not offered:` sentence. The round count is per run; at the cap `fix` is omitted, named `fix (round cap reached)` in `Not offered:`.
29
+ `pre_wave_head` was recorded before the first `## Conflicts` check. A round is one pass over the open payloads plus one `## Review before push`. The round cap is `closureReview.maxFixRounds`, read with `gauntlet_setting({ key: "closureReview" })` when that tool exists, else 3; a cap of `0` omits `fix` and `fix nits` from the menu, named as `fix, fix nits (maxFixRounds is 0)` in the `Not offered:` sentence. The round count is per run; at the cap `fix` and `fix nits` are omitted, named `fix, fix nits (round cap reached)` in `Not offered:`.
30
30
 
31
31
  For each payload in review order, dispatch one fresh implementer helper with `cwd` the PR worktree. Its task holds:
32
32
 
33
33
  - the finding's evidence pack (`findings.md` `## Drafted payloads` shape) and the instruction to apply the consented payload - or, for a reviewer finding that has no drafted payload, the reviewer's finding text - never to re-solve the finding;
34
- - the scoped test commands: the test it writes, or the test file the payload touches, and for a `P#` carried by a `rebased locally` sync record the failing test file named in that record - never the resolved `verification command`;
35
- - the commit rule: commit on the PR branch only when the scoped test passes, one commit per payload, subject naming the fix; otherwise discard its edits (`git checkout -- <its files>`, remove files it created) and return `open - <why>`;
34
+ - `SCOPED_TEST_COMMANDS`, resolved once per wave from the merge-base copies of the overrides file, then `AGENTS.md` (`git show "$MB:<path>"`, the trust rule of `assessment.md` `## Configuration`): a scoped runner is a documented test command carrying a `<paths>` or `<files>` placeholder, or a command the doc labels scoped, in any section of either file (`## PR gate`, `## Project`, `## routing`, `## test-driven-development`, `## verification-before-completion` alike); keep its cwd and flags verbatim; with several runners take the one whose directory prefix matches the payload's test file; substitute the test files the payload touches or writes, and for a `P#` carried by a `rebased locally` sync record the failing test file named in that record; no runner or no test file -> `none`. Never the resolved `verification command`, whatever a consumer sentence says about fix waves - that command runs only where `../verification-brief.md` Section B selects a local-run row; `none` -> the helper reports `no scoped test` and the pre-push reviewer is its only gate;
35
+ - the commit rule: commit on the PR branch only when the scoped test passes, or when `SCOPED_TEST_COMMANDS` is `none`, one commit per payload, subject naming the fix; otherwise discard its edits (`git checkout -- <its files>`, remove files it created) and return `open - <why>`;
36
36
  - the report shape: the scoped test's failing run before the edit and passing run after it; a doc-only payload with no test reports `no scoped test` and its diff, and the reviewer below is its only gate.
37
37
 
38
38
  After each helper returns, read `git -C <worktree> status --porcelain` and `git -C <worktree> rev-parse HEAD`. A `done` return shows exactly one new commit and a clean tree; an `open` return shows no new commit and a clean tree. Any other state - staged, unstaged, or untracked residue, or a moved HEAD on `open` - stops the round: dispatch no further implementer, and re-render the menu naming the files and the payload. An `open` payload is never reported as fixed, whatever the reviewer reports. The orchestrator never commits or checks out files, and resets only to `<pre_head>` after a refused lease or a remote head move (`## Evidence after push`, `post-selection-loop.md` `### Compare-and-swap`).
@@ -41,9 +41,9 @@ After each helper returns, read `git -C <worktree> status --porcelain` and `git
41
41
 
42
42
  When HEAD equals `pre_wave_head` after a round (every payload returned `open`), skip the review and the push and re-render the menu with the `open` reasons.
43
43
 
44
- After the round's payloads, dispatch one fresh reviewer helper, `cwd` the PR worktree, read-only, over `<pushed_head>..HEAD`, where `pushed_head` is the digest's `headRefOid` (the assessed head), so commits a previous round left unpushed are always inside the reviewed range; `pre_wave_head` serves only the empty-round test. Its task carries the pre-wave findings list, numbered with this skill's finding ids, under `## Previous review report (re-review trigger)`, and the output of `git diff <pushed_head> HEAD` under `## Fix delta`, and asks for the reviewer's native report plus one closure line per pre-wave finding, accepted anywhere after the findings list: `<finding id>: resolved` or `<finding id>: open - <why>`. When the round holds a `merge base` commit, `## Fix delta` is `git diff <pushed_head> <merge-sha>^1`, then `git show --remerge-diff <merge-sha>`, then `git diff <merge-sha> HEAD`. Ignore a `TRAJECTORY:` line in the report.
44
+ After the round's payloads, dispatch one fresh reviewer helper, `cwd` the PR worktree, read-only, over `<pushed_head>..HEAD`, where `pushed_head` is the digest's `headRefOid` (the assessed head), so commits a previous round left unpushed are always inside the reviewed range; `pre_wave_head` serves only the empty-round test. Its task carries the findings whose payloads this wave dispatched, under their `P#`/`L#` ids, under `## Previous review report (re-review trigger)`, and the output of `git diff <pushed_head> HEAD` under `## Fix delta`, and asks for the reviewer's native report plus one closure line per listed finding, accepted anywhere after the findings list: `<finding id>: resolved` or `<finding id>: open - <why>`. When the round holds a `merge base` commit, `## Fix delta` is `git diff <pushed_head> <merge-sha>^1`, then `git show --remerge-diff <merge-sha>`, then `git diff <merge-sha> HEAD`. Ignore a `TRAJECTORY:` line in the report.
45
45
 
46
- Mark a finding resolved only from a `resolved` closure line; the orchestrator never marks one. Push only when the report has no Critical or Moderate finding. Otherwise re-dispatch implementers with the report verbatim for the `open` payloads and the new Critical or Moderate findings - one more round, up to the cap. At the cap, the commits stay unpushed on the PR branch; the menu re-renders with the reviewer's verdict line and its Critical and Moderate titles, and `push` is omitted, named as `push (reviewer blocked the wave)` in the `Not offered:` sentence.
46
+ Mark a finding resolved only from a `resolved` closure line; the orchestrator never marks one. The carried set is every `open` closure line of this wave's ids, every new Critical or Moderate finding inside `## Fix delta`, and every new Critical finding outside it; a new finding entering the set is minted a `P#` before dispatch and listed under it in the next round's reviewer task. An in-delta Minor translates to a nit (`findings.md` `## Namespaces`); a `[Minor] (outside fix delta)` finding, or an outside-delta finding the report tags Moderate, prints under `show evidence` only. Push only when the carried set holds no Critical or Moderate. Otherwise dispatch one fresh implementer, serially, per carried item - the payload for an `open` id, the reviewer's finding text for a new finding - one more round, up to the cap. At the cap, the commits stay unpushed on the PR branch; the menu re-renders with the reviewer's verdict line and the carried set's Critical and Moderate titles, and `push` is omitted, named as `push (reviewer blocked the wave)` in the `Not offered:` sentence.
47
47
 
48
48
  ## Evidence after push
49
49
 
@@ -24,7 +24,7 @@ Before every external write, re-fetch `headRefOid`, `state`, `mergeable`, and `m
24
24
 
25
25
  ### Fix wave
26
26
 
27
- For a `fix` pick, take the drafted payloads (`findings.md` `## Drafted payloads`) for the named blockers - all open blockers by default, the nit payloads when no blocker is open; `fix nits` takes nit payloads only, `fix + nits` both. A finding closed by disposition is skipped. A finding whose payload touches no file (a failing check, a claim with no drafted edit) is reported as not fixable by this verb and stays open.
27
+ For a `fix` pick, take the drafted payloads (`findings.md` `## Drafted payloads`) for the named blockers - all open blockers by default; `fix nits` takes nit payloads only, drafted at this pick; `fix + nits` both. A finding closed by disposition is skipped. A finding whose payload touches no file (a failing check, a claim with no drafted edit) is reported as not fixable by this verb and stays open.
28
28
 
29
29
  Apply per `fix-wave.md`: the local conflict check, one fresh implementer helper per payload with scoped tests only, the pre-push reviewer with its closure lines, the push, and the evidence poll. The orchestrator never edits a tracked file and never commits. When `head_pushable` is false, the wave's commits stay in the local `pr-<N>` worktree: report the branch name and re-enter step 5.
30
30
 
@@ -21,7 +21,7 @@ PR comments: (replyable review comments, a pending reviewer run, a b
21
21
  - The reviewer run is still in progress, so merge waits. (<run url>)
22
22
  - <the line from decision-menu.md `## Withhold reason resolver`>
23
23
 
24
- Nits:
24
+ Nits: (after the list, one line `<N> nits open - fix nits to take them` (`1 nit open` for one); rendered only while the `fix nits` row renders.)
25
25
  - <1-2 sentences>. (<file:line>)
26
26
 
27
27
  Blockers: (numbered; menu picks reference these numbers)
@@ -60,6 +60,7 @@ PR comments:
60
60
 
61
61
  Nits:
62
62
  - The export pages results with its own loop instead of the shared paging helper, so a paging bug fixed once would need fixing twice. (src/api/export.ts:31, lib/page.ts)
63
+ 1 nit open - fix nits to take them
63
64
 
64
65
  Blockers:
65
66
  1. The export endpoint ships with no test, so nothing proves it works and nothing
@@ -71,10 +72,11 @@ Blockers:
71
72
  Verdict: fixable - 2 blockers
72
73
 
73
74
  1. fix - apply both blockers in the worktree, review the wave, push [recommended]
74
- 2. review - post the blockers as a comment on your PR
75
- 3. reply - post the drafted reply to maria
76
- 4. show evidence - gate output, CI run, drafted edits
77
- 5. stop - leave the PR as-is
75
+ 2. fix nits - apply the nit payload in the worktree, review the wave, push
76
+ 3. review - post the blockers as a comment on your PR
77
+ 4. reply - post the drafted reply to maria
78
+ 5. show evidence - gate output, CI run, drafted edits
79
+ 6. stop - leave the PR as-is
78
80
  Type a number, or compose: "fix 2", "fix nits", "fix + nits", "fix 1 + reply".
79
81
  ```
80
82
 
@@ -86,8 +88,7 @@ Delivers: the reports page exports CSV on demand. ACs 1-5.
86
88
  Verdict: mergeable - CI green on the assessed head (test)
87
89
 
88
90
  1. merge - squash onto main [recommended]
89
- 2. fix - name the change to apply in the worktree, review the wave, push
90
- 3. show evidence - gate output, CI run
91
- 4. stop - leave the PR as-is
91
+ 2. show evidence - gate output, CI run
92
+ 3. stop - leave the PR as-is
92
93
  Type a number.
93
94
  ```