pi-gauntlet 6.2.1 → 7.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.
@@ -1,226 +1,118 @@
1
- # gatekeep-pr: decision menu
2
-
3
- Read from SKILL.md `## Decide`. `## Decision` in the report has two parts: the
4
- action vocabulary, then the numbered courses.
5
-
6
- ## Actions
7
-
8
- ```markdown
9
- Actions (compose freely in the custom row):
10
- fix <P#s|all> apply blocking fixes in worktree, re-run gate, push (in-repo PRs only)
11
- push-docs push already-applied doc-drift edits (only when uncommitted
12
- reviewed doc edits exist
13
- in the worktree)
14
- merge-squash | merge-commit (preconditions per Verdict;
15
- never bundled with a push)
16
- merge-squash anyway | merge-commit anyway (custom row only: overrides a
17
- reviewer-withheld merge - the literal
18
- `anyway` accepts the named reason;
19
- GitHub-refused rows stay uncomposable)
20
- wait poll the reviewer run and comment set, then re-render
21
- request-changes | review-comment | approve (approve: never own PR)
22
- reply <C#s> post drafted thread replies
23
- tracker <act> tracker action (only when a tracker tool resolved)
24
- stop leave the PR as-is / report-only exit
25
- ```
1
+ # gatekeep-pr: step 6 - menu
2
+
3
+ Read from SKILL.md step 6. Input: the report's verdict and the PR state. Output: the menu appended under the verdict line. Nothing executes until a pick; the pick runs per `post-selection-loop.md`.
4
+
5
+ ## Verbs
6
+
7
+ `fix`, `push`, `review`, `approve`, `merge`, `merge anyway`, `reply`, `propose ticket change`, `show evidence`, `wait`, `stop`.
8
+
9
+ Rows are `<n>. <verb> - <consequence>`; every row, `stop` and `(not available: ...)` rows included, carries its consequence clause. `stop` is always last, before the compose hint. `show evidence` is always offered and never `[recommended]`. A row GitHub would refuse (branch protection, missing permission, `viewerPermission` too low) renders `(not available: <reason>)` and is never `[recommended]`. Exactly one row is `[recommended]`.
26
10
 
27
- A `+ tracker <act>` suffix is available on any mutation course when a tracker tool
28
- resolved.
11
+ - `fix` - apply the drafted payloads for the named blockers (default: all open blockers; with no open blockers, the nit payloads) in the provisioned worktree, run the verification pass once at the resulting SHA, push when `push` is available; `fix nits` applies nit payloads only, `fix + nits` blockers and nits; with no drafted payload at all, the human names the change
12
+ - `push` - push local fix commits (renders only when unpushed commits exist)
13
+ - `review` - post the blockers as a GitHub review: request-changes on someone else's PR, a comment on your own
14
+ - `approve` - approve the PR (never your own)
15
+ - `merge` - `gh pr merge --match-head-commit <assessed-sha>` with the resolved `merge policy` (default squash); preconditions per `post-selection-loop.md` `### Merge preconditions`
16
+ - `merge anyway - accept AC<n> as impossible` - merge while an `impossible` row withholds it; lifts exactly that AC's withhold
17
+ - `merge anyway` - merge while the pending-reviewer overlay withholds it; prints what it overrode
18
+ - `reply` - post the drafted replies under `PR comments` (`reply <n>` for one)
19
+ - `propose ticket change` - show the drafted AC text verbatim; post it as a tracker comment on an explicit confirmation reply, via the resolved tracker verb (`gh issue comment`, the overrides' tracker mapping, or `/skill:linear`); re-fetch the ticket first and re-render instead of posting when the AC row text drifted; no write path -> copy-paste block
20
+ - `show evidence` - print the evidence record, claim and AC outcomes, dispositions, drafted payloads and replies (`report.md` `## What stays out`)
21
+ - `wait` - poll the reviewer run, the pending required checks, and the comment set, then re-render
22
+ - `stop` - leave the PR as-is
29
23
 
30
- Selection grammar: ID sets accept `all`, ranges (`P1-P4`), comma lists
31
- (`P1,P3`), and exclusions (`all but P2`).
24
+ Compose hint: `Type a number, or compose: "fix 2", "fix nits", "fix + nits", "fix 1 + reply".` The hint 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. 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`).
25
+
26
+ A composed line naming a `(not available: ...)` row is refused by name and the menu re-renders; the two `merge anyway` forms are the only overrides.
32
27
 
33
28
  ## Consent table
34
29
 
35
- Deterministic - this table is the golden-scenario oracle.
30
+ Authorship moves only `[recommended]`, never which rows are offered. The first row is `[recommended]`; a `(not available: ...)` row is skipped, so the first available row carries it; when every other row is `show evidence`, `stop` is `[recommended]`.
31
+
32
+ A `fixable` verdict with zero blockers (a withhold reason) uses the mergeable rows; `merge` renders `(not available: <withhold reason>)` - the overlay's reason when one applies, else the verdict's. On such a withhold, `[recommended]` goes to `wait` under the pending-reviewer or pending-required-check overlay, else to `propose ticket change` while an `impossible` row is `drafted`, else to `stop`.
36
33
 
37
- | Author | State | Offered rows (first = `[recommended]`) |
34
+ | Author | Verdict | Rows |
38
35
  |---|---|---|
39
- | you | clean / follow-ups only | merge (squash); merge (merge-commit); do not merge (leave it); post no-blockers comment |
40
- | you | blocking | apply code fixes (named finding subset): skill edits in worktree, commits, re-runs gate, pushes - then merge re-offered; push applied doc fixes; do not act; post review-comment of findings |
41
- | someone else | clean / follow-ups only | approve; merge (squash, offered-unrecommended); post no-blockers comment |
42
- | someone else | blocking | post request-changes review; apply fixes on their branch (courtesy option 2); reply to existing threads; post comment |
43
- | bot author | any | someone-else's rows for the same state, review actions recommended |
44
- | fork (any) | any | post review (request-changes / comment / approve per state) - push and merge rows absent |
45
- | any | draft PR | assessment rows only; merge and approve rows absent until ready-for-review |
46
- | any | merged / closed | report-only; no mutation rows |
47
-
48
- This table is the single oracle for what is offered; `## Courses` renders its
49
- rows as actions and numbered courses. Rows GitHub would refuse (branch protection,
50
- missing permissions, `viewerPermission` too low) render listed-but-unavailable with
51
- the reason. Approving your own PR is never offered. Nothing executes until explicit
52
- selection. The fork, pending-reviewer, and CI-check overlays below modify the cell's rows; they are never a second offer source.
53
-
54
- ## Courses
55
-
56
- A normative rendering of the consent table (never a second offer source): per
57
- author x state cell, exactly one `[recommended]` course renders first, the custom
58
- row renders last. Courses are atomic across pushes: no course, pre-composed or
59
- custom, bundles a push-producing action (`fix`, `push-docs`) with `merge-*`; after
60
- a fix wave the menu re-renders with merge as row 1 unless withheld (`reviewer still running` / `comments not refreshed`).
61
-
62
- | Author | State | Courses (first = `[recommended]`) |
36
+ | you | mergeable | `merge`; `fix`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
37
+ | you | fixable | `fix`; `review`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
38
+ | someone else | mergeable | `approve`; `fix`; `merge`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
39
+ | someone else | fixable | `review`; `fix`; `reply` (when a drafted reply exists); `show evidence`; `stop` |
40
+
41
+ `propose ticket change` joins any row set after the `fix` rows whenever an `impossible` row is `drafted`. `push` joins any row set after `fix` when unpushed fix commits sit in the worktree.
42
+
43
+ ## Overlays
44
+
45
+ Overlays modify the consent row; they are never a second offer source. When two apply, the one higher in this table wins the `merge` reason and `[recommended]`.
46
+
47
+ | Overlay | Trigger | Effect |
63
48
  |---|---|---|
64
- | you | clean / follow-ups only | 1. merge-squash; 2. merge-commit; 3. stop; 4. review-comment (post no-blockers note) |
65
- | 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 failing-check-only or L#-only), course 1 (fix) does not render: push-docs becomes first when doc edits exist, else stop is first |
66
- | you | blocking, post-fix re-render (gate green, preconditions hold) | 1. merge-squash; 2. merge-commit; 3. stop; 4. review-comment |
67
- | someone else | clean / follow-ups only | 1. approve; 2. merge-squash (offered-unrecommended); 3. review-comment (no-blockers note) |
68
- | someone else | blocking | 1. request-changes; 2. fix all (courtesy, their branch - omitted when nothing is worktree-fixable); 3. reply <C#s> (omitted when no replyable `C#` exists - `findings.md` `## IDs`; `reply all` and ranges skip non-replyable rows); 4. review-comment |
69
- | bot author | any | someone-else's rows for the same state; review actions recommended |
70
- | any | draft | 1. request-changes / review-comment / reply <C#s> (omit the reply course when no replyable `C#` exists) / 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 |
71
- | 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 |
72
-
73
- ## Fork overlay
74
-
75
- The consent-table fork row renders as an overlay on the authorship cells
76
- (push/merge/fix absent; approve also dropped when the viewer authored the PR) - it
77
- is not a distinct authorship cell. It overlays the applicable authorship cell (you
78
- or someone else), removing `fix`, `push-docs`, and `merge-*` (never available on a
79
- fork). When you authored the fork PR, `approve` is also dropped (never offered on
80
- your own PR) - fork|you|clean renders `review-comment`/`stop` only; fork|you|blocking
81
- renders `request-changes`/`review-comment`/`stop` (the someone-else courtesy
82
- fix-on-their-branch course is also absent, since it is your own PR). A fork PR
83
- authored by someone else uses the someone-else cells with `fix`/`push-docs`/
84
- `merge-*` removed.
85
-
86
- ## Pending-reviewer overlay
87
-
88
- While any `C#` row is `pending`, a reviewer run on the assessed head is
89
- queued/in progress, or the last comment refetch failed
90
- (`post-selection-loop.md` `### Re-render`), every pre-composed
91
- `merge-squash` / `merge-commit` course renders listed-but-unavailable with
92
- the reason: `reviewer still running` or `comments not refreshed`. This is a
93
- menu-level gate modelled on the `flaky` disposition's custom-row path, never
94
- a `## Verdict` precondition: `### Merge course` does not refuse the override.
95
- Only the custom row's `merge-squash anyway` / `merge-commit anyway` executes
96
- merge in that state, under the normal Merge course rules. Apply the comment-delta consent and incomplete-review rules in `post-selection-loop.md` `### Compare-and-swap` and `### Re-render`; `anyway` does not bypass an unreviewed delta or a blocking finding.
97
-
98
- `wait` is `[recommended]` in cells whose recommended course would otherwise
99
- be `merge-*` or `approve` (clean / follow-ups only, and the post-fix
100
- re-render); in blocking cells the existing first course (`fix`,
101
- `request-changes`) stays recommended and `wait` renders as row 2. Draft and
102
- merged/closed cells do not offer `wait`. A `reviewer failed (<conclusion>)`
103
- row changes nothing: the cell renders as it would without it.
104
-
105
- The overlay applies on the initial assessment too: a PR gated while the
106
- reviewer is mid-run withholds pre-composed merge from the first menu.
107
-
108
- Precedence: when the CI-check gate below also applies, its rendering wins -
109
- merge rows are absent and the CI-check gate line names both reasons.
110
-
111
- ## CI-check gate
112
-
113
- An undispositioned failing check in the resolved set, or a pending required check,
114
- withholds every pre-composed course containing `merge-*` (merge preconditions per
115
- SKILL.md `## Verdict`) - none render, whatever the author/state cell says.
116
- Disposition and pending-check definitions per `findings.md` `## Dispositions`.
117
-
118
- | Disposition | Merge courses |
119
- |---|---|
120
- | Flaky | Not restored to a pre-composed course; merge proceeds only via the custom row naming the disposition explicitly |
121
- | Real, or an unresolved pending check | Withheld until the check is green |
122
- | CI-infrastructure-broken | Withheld until the triggered fallback run is green |
123
-
124
- A pending-only render is not itself a blocking verdict (findings groups can all
125
- read "None"); the recommended course falls to `stop` or `review-comment` in the
126
- meantime. This never falls through to the clean cell's recommended `merge-squash` -
127
- a failing resolved-set check or a pending required check means the PR is not in the
128
- clean state to begin with.
49
+ | merged or closed PR | `state` is not `OPEN` | the report renders; the menu is `show evidence` and `stop` only |
50
+ | draft PR | `isDraft` | `merge` and `approve` render `(not available: draft)` 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` |
51
+ | CI check | an undispositioned failing check in the resolved set, or a pending required check (`findings.md` `## Dispositions`) | on a failing check, the hint line offers `check <name> flaky \| real \| ci-broken`; `merge` renders `(not available: <check name> failing)`; `flaky` restores `merge` on the next render; `real` and `ci-infrastructure-broken` keep it withheld until green; a pending required check renders one `PR comments` line (`A required check is still pending, so merge waits. (<check name>)`), puts `wait` as row 1 and, when `Blockers` is empty, `[recommended]`, and renders `merge` `(not available: required check pending)`; it mints no disposition hint (not dispositionable) |
52
+ | 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` renders `(not available: reviewer still running)` or `(not available: comments not refreshed)`; a composed `anyway` (`merge anyway`) overrides only this overlay and does not bypass an unreviewed delta or a blocker; a `reviewer failed (<conclusion>)` row changes nothing |
53
+ | 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` renders `(not available: comment source review incomplete)` with no `anyway` override; `reply` and `review` are dropped (the delta is unreviewed); `fix`, `show evidence`, `stop` stay; `stop` is `[recommended]` |
54
+ | impossible AC | an `impossible` row in `drafted` or `proposed` while every other merge prerequisite holds | `merge` renders `(not available: ticket change pending on AC<n>)`; `merge anyway - accept AC<n> as impossible` renders and lifts exactly that withhold - blockers, an unreviewed delta, pending checks, and GitHub-refused states stay in force |
55
+ | fork PR | `isCrossRepository` | `push` and `merge` render `(not available: fork)`; `fix` stays and applies to the local `pr-<N>` worktree, its consequence clause reading "open a PR from `pr-<N>`, or hand the patch to the author"; `approve` is dropped on your own fork PR |
56
+ | bot author | `author_is_bot` | the someone-else rows |
129
57
 
130
58
  ## Fixtures
131
59
 
132
- Refused rows render per `## Consent table`. Zero mutation courses is a legal
133
- render (merged/closed) - the menu still appears, carrying findings and `stop`.
60
+ Fixture 1 - own PR, fixable, one nit, one drafted ticket change, one drafted reply: the menu in `report.md` `## Worked example`.
134
61
 
135
- Example render (golden fixture 1 - own PR, blocking findings including committed
136
- doc drift, so uncommitted reviewed doc edits exist):
62
+ Fixture 2 - the re-render after fixture 1's `fix` pushed; the reviewer bot is mid-run on the new head:
137
63
 
138
- ```markdown
139
- Pick one:
140
- 1. fix all (P1-P10) + push-docs [recommended]
141
- 2. push-docs (docs only, hold code fixes)
142
- 3. stop (leave as-is)
143
- 4. review-comment (post findings, act later)
144
- 5. Custom - compose: e.g. "fix P1-P8,P10 + push-docs" or "reply C1 + tracker comment"
145
64
  ```
146
-
147
- Golden fixture 2 - the post-fix re-render after course 1's gate re-run passes,
148
- with the sticky reviewer bot mid-run and one independently edited comment:
149
-
150
- ```markdown
151
- ## Comment-thread replies
152
- C1. <thread ref> -> superseded by C3
153
- C2. <thread ref> -> superseded by C4
154
- C3. <thread ref> -> pending https://github.com/<owner>/<repo>/actions/runs/<run-id>
155
- C4. <thread ref> -> <drafted reply> (reasonable)
156
-
157
- Pick one:
158
- 1. wait [recommended]
159
- 2. merge-squash (unavailable: reviewer still running)
160
- 3. merge-commit (unavailable: reviewer still running)
161
- 4. stop (leave as-is)
162
- 5. review-comment
163
- 6. Custom - compose: e.g. "merge-squash anyway" or "reply C4"
65
+ Delivers: the reports page exports CSV on demand. The PR covers AC1-2 of gh-45; AC3
66
+ (export completes under 5s on production data) is checked after merge, not here.
67
+
68
+ Ticket changes:
69
+ - AC4 asks for the customer's credit score in the export, and the vendor API this
70
+ service reads returns no such field.
71
+ Proposed wording: "Exports include the customer's risk tier." (drafted; vendor doc api.example.com/v2/customers)
72
+
73
+ PR comments:
74
+ - maria's comment on the missing CSV header is already addressed, so the drafted reply points her at the fix. (3f2a1c0)
75
+ - The reviewer run is still in progress, so merge waits. (https://github.com/<owner>/<repo>/actions/runs/<run-id>)
76
+
77
+ Nits:
78
+ - 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)
79
+
80
+ Verdict: fixable - reviewer run in progress
81
+
82
+ 1. wait - poll the reviewer run, then re-render [recommended]
83
+ 2. merge - squash onto main (not available: reviewer still running)
84
+ 3. fix - apply the nit payload in the worktree, re-run the gate, push
85
+ 4. propose ticket change - show the AC4 edit for approval before it posts
86
+ 5. reply - post the drafted reply to maria
87
+ 6. show evidence - gate output, CI run
88
+ 7. stop - leave the PR as-is
89
+ Type a number, or compose: "merge anyway", "fix nits".
164
90
  ```
165
91
 
166
- Golden fixture 3 - the same PR after `wait` completes. (a) The run concluded
167
- `failure` and the bot rewrote its comment to the error header:
168
-
169
- ```markdown
170
- ## Evidence
171
- reviewer check <name> failed - inert (reviewer failure never withholds)
172
-
173
- ## Comment-thread replies
174
- C1. <thread ref> -> superseded by C3
175
- C2. <thread ref> -> superseded by C4
176
- C3. <thread ref> -> superseded by C5
177
- C4. <thread ref> -> <drafted reply> (reasonable)
178
- C5. <thread ref> -> reviewer failed (error) https://github.com/<owner>/<repo>/actions/runs/<run-id>
179
-
180
- Pick one:
181
- 1. merge-squash [recommended]
182
- 2. merge-commit
183
- 3. stop (leave as-is)
184
- 4. review-comment
185
- 5. Custom
186
- ```
92
+ Fixture 3 - a different PR: your own, ticket fully covered, no nits, no drafted replies; the reviewer bot's run was the only withhold and `wait` just returned.
93
+
94
+ (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`.
95
+
96
+ (b) The run concluded `success` and the verdict comment raises a retry bug; source review confirms it and mints a blocker:
187
97
 
188
- (b) The run concluded `success`; the reviewer check moved from pending to
189
- `success` in the refreshed rollup and the comment carries the verdict. Reconcile C5 against source at the assessed head. For a confirmed retry bug missed by the previous source review, mint source-backed P11 and withhold merge; C5's triage label remains verdict-neutral:
190
-
191
- ```markdown
192
- ## Findings (blocking)
193
- Blocking findings (P#):
194
- P11. **<source_ref>** - Retry attempts never increment. Fix: increment attempts on failure and throw after the retry limit. | Action: fix P11. [code]
195
- ## Comment-thread replies
196
- C1. <thread ref> -> superseded by C3
197
- C2. <thread ref> -> superseded by C4
198
- C3. <thread ref> -> superseded by C5
199
- C4. <thread ref> -> <drafted reply> (reasonable)
200
- C5. <thread ref> -> <drafted reply> (reasonable)
201
- Pick one:
202
- 1. fix P11 [recommended]
203
- 2. stop
204
- 3. review-comment
205
- 4. Custom
206
98
  ```
99
+ PR comments:
100
+ - The reviewer bot's retry concern holds up against the source, so the drafted reply confirms it. (src/retry.ts)
101
+
102
+ Blockers:
103
+ 1. Retries never count up, so a failing call retries forever instead of giving up
104
+ after the limit. (src/retry.ts:41)
207
105
 
208
- When source review instead disproves C5's concern, mint no `P#` and retain the clean menu:
209
-
210
- ```markdown
211
- ## Comment-thread replies
212
- C1. <thread ref> -> superseded by C3
213
- C2. <thread ref> -> superseded by C4
214
- C3. <thread ref> -> superseded by C5
215
- C4. <thread ref> -> <drafted reply> (reasonable)
216
- C5. <thread ref> -> <drafted reply> (judgment-call)
217
-
218
- Pick one:
219
- 1. merge-squash [recommended]
220
- 2. merge-commit
221
- 3. stop (leave as-is)
222
- 4. review-comment
223
- 5. Custom
106
+ Verdict: fixable - 1 blocker
107
+
108
+ 1. fix - apply the blocker in the worktree, re-run the gate, push [recommended]
109
+ 2. review - post the blocker as a comment on your PR
110
+ 3. reply - post the drafted reply
111
+ 4. show evidence - gate output, CI run, drafted edit
112
+ 5. stop - leave the PR as-is
113
+ Type a number, or compose: "fix 1 + reply".
224
114
  ```
225
115
 
226
- Golden fixture 4 - last premerge refetch finds a new human comment after merge consent. Reconcile it against source at the assessed head, abort that merge even when the concern is false, and show the refreshed menu for a new selection. A same-head identical-body timestamp edit mints the next `C#` but causes no repeat source review or test run. A bot placeholder or error header keeps its existing state and does not enter source review.
116
+ When source review disproves the comment, no blocker is minted; the mergeable menu renders with `reply` for the drafted reply.
117
+
118
+ Fixture 4 - the pre-merge refetch finds a new human comment after a `merge` pick: the merge aborts even when the concern is false, the comment is reviewed against source, and the menu re-renders for a fresh pick. A same-head identical-body timestamp edit mints a ledger row but triggers no source review or test run.
@@ -1,120 +1,72 @@
1
- # gatekeep-pr: findings
2
-
3
- Read from SKILL.md `## Assess`. Phase 4 mints the IDs the report and menu carry.
4
-
5
- ## IDs
6
-
7
- `<source_ref>` is a `file:line` where one exists, else the disputed thing (a
8
- quoted PR-body claim, a failing gate command, a required check name).
9
-
10
- Precedence: Phase 4 and the merged rubric decide blocking vs. follow-up (the
11
- severity translation, AC coverage, claims, and required-check rules). This
12
- section only chooses which namespace (`P#` / `L#` / `F#`) renders that
13
- decision. Category tags and the triage bar never override an upstream
14
- blocking classification - a Phase-4 Moderate is always blocking (`P#` or `L#`
15
- per Total mapping), never demoted to `F#` by tag or by judgment call.
16
-
17
- Total mapping: every blocking element of the Verdict maps to a `P#` or `L#` -
18
- a blocking verdict with "None" in both groups is a rendering bug. Map: failed
19
- gate -> `P#` `[test]` referencing the gate command; contradicted or
20
- merge-proof-unverifiable material claim -> `P#` `[spec]` referencing the
21
- claim; scope creep with a linked issue -> `L#` `spec-conflict`; committed doc
22
- drift -> `L#` `doc-drift`; `partial` AC coverage -> `L#` `outdated-AC`;
23
- `missing` AC coverage -> `L#` `missing-behavior`. `L#` covers exactly the
24
- drift the Verdict already blocks on (committed doc drift, AC coverage, spec
25
- conflict); it widens nothing.
26
-
27
- `P#` vs `L#` boundary: code-level spec bugs (the diff contradicts the spec)
28
- are `P#` `[spec]`; requirement/doc mismatches (the spec or docs are stale
29
- relative to intent) are `L#`.
30
-
31
- Labelled `C#` rows are verdict-neutral drafts: they never gate merge, and
32
- nothing posts until selected. The `pending` state, a queued/in-progress
33
- reviewer run, and a failed comment refetch withhold pre-composed `merge-*`
34
- courses at the menu level (`decision-menu.md` `## Pending-reviewer overlay`);
35
- they are not `## Verdict` preconditions.
36
-
37
- **`C#` ledger.** Each `C#` row carries its comment `id` and the `updated_at`
38
- it was minted against; the post-push diff (`post-selection-loop.md`
39
- `### Re-render`) runs against this ledger, never against the report text.
40
- States rendered under a `C#`: a triage label (`already-addressed` /
41
- `reasonable` / `judgment-call`) with a drafted reply; `superseded by C<new>`;
42
- `withdrawn`; `pending`; `reviewer failed (<conclusion>)`. Superseded and
43
- withdrawn rows keep rendering for the rest of the run, so a sticky bot's
44
- chain reads `C1` (old verdict) `superseded by C3`, `C3 pending`, then
45
- `C3 superseded by C5`, `C5 <label> -> <reply>`. The last four states carry no
46
- reply and are not replyable: `reply all` and ranges skip them silently; an
47
- explicitly named non-replyable `C#` is refused with its state named and the
48
- menu re-renders; reply courses are omitted when no replyable `C#` exists.
49
-
50
- `F#` items carry an owner (pr-author | tracker | human) so follow-ups don't
51
- evaporate; when no tracker tool resolved, the report itself is their durable
52
- home.
53
-
54
- IDs are append-only for the run's lifetime: minted at first assessment, never
55
- renumbered, never reused. A resolved finding keeps its ID annotated
56
- `(fixed in <sha>)`; later rounds continue each namespace's sequence.
57
-
58
- Empty groups say "None".
59
-
60
- ## Triage
61
-
62
- A finding lands in `P#` only when it must be fixed before merge (correctness,
63
- security, material performance trap, a convention the repo enforces);
64
- improvements that don't change merge correctness are `F#`, whatever their
65
- category. `[quality]` and `[performance]` on a `P#` are categories, never a
66
- downgrade - every `P#` blocks. This triage bar governs findings the
67
- orchestrator originates itself; it never re-triages a classification Phase 4
68
- already made (see Precedence in `## IDs`).
1
+ # gatekeep-pr: step 5 - integrate
2
+
3
+ Read from SKILL.md step 5, before `report.md`. Inputs: the digest, the step-2 AC rows, the step-3 evidence and claim dispositions, the step-4 reviewer output and comment ledger. Output: AC outcomes, blockers, nits, drafted payloads - the report's content, not its rendering.
4
+
5
+ ## Provenance
6
+
7
+ Local evidence: `worktree_root` matches the provisioned path, every `run_cwd` sits inside it, `head_sha` matches the digest's `headRefOid`. On mismatch, re-fetch the PR head once and re-sync + re-run steps 3-4 when it advanced; a second mismatch, or any path mismatch, is missing evidence. The evidence clause names its source exactly: local -> `verification command passed locally`; CI -> `CI green on the assessed head (<check name>)`, never worded as a local run.
8
+
9
+ ## AC outcomes
10
+
11
+ Inputs: the ticket AC rows (step 2), the source behind the diff at the assessed `headRefOid`, tests in the diff and existing tests the diff reaches, docs in the diff and docs the diff makes stale, CI per the brief's Evidence resolution table. PR body, PR comments, and a spec inside the PR are leads to verify, never evidence.
12
+
13
+ Split each AC row once into a **mechanism half** (code + test + doc that make the behavior possible) and, when the row names an observation that needs an environment, dataset, deployed target, or external system that repository tests cannot reach, an **observation half**. Behavior a unit test can assert ("retries three times then fails", "rejects a nil name") is mechanism, never an observation half. A `venue:` disposition in the PR's spec is a lead to compare against the ticket's own AC text, never proof and never an exemption. Judge the mechanism half only; the observation half is checked after merge, not here, and blocks nothing.
14
+
15
+ | Outcome | Condition | Renders as | Blocks |
16
+ |---|---|---|---|
17
+ | `covered` | evidence matches what the AC promises: executable behavior needs the code path plus a real test that exercises it; a documentation-only AC is judged against the promised doc text and demands no test; in both cases docs that describe the behavior agree with it | counted in `Delivers` | no |
18
+ | `gap` | any part of the mechanism absent in this PR and the row not explicitly split (see Whole or part) | one `Blockers` item | yes - a mechanism `gap` is a blocker |
19
+ | `not judged here` | the observation half; the mechanism half of the same row is still judged `covered`/`gap` | one `Delivers` clause ("<row>'s observable half is checked after merge, not here"); nothing is written, listed, or handed to check-delivery | no |
20
+ | `impossible` | a `gap` whose fix is on the ticket - all four conditions below hold | one `Ticket changes` item with a drafted replacement AC text, in state `drafted` or `proposed` | withholds `merge` until the ticket body changes or the human picks `merge anyway - accept AC<n> as impossible` |
21
+
22
+ `impossible` requires all of: (1) no change to this repository can satisfy the mechanism half; (2) the constraint is cited - a vendor/platform doc URL, a dependency's released API at `file:line` or in its changelog, a repo policy at `file:line`, or a second AC in the same ticket whose quoted text contradicts this one; (3) the cited source was read this run; (4) the reason is none of: cost, effort, "needs another PR", "needs a deploy first" (that is `not judged here`), a `deviates:`/`deferred:` disposition in the PR's spec, "the AC is ambiguous" (an ambiguous row stays judged as written). Anything failing a condition is `gap`. Passes: the AC asks the export to include the customer's credit score and the vendor API the repository reads returns no such field (vendor doc URL cited). Fails: the AC asks for the user's local timezone and the browser sends no timezone header - a request parameter is a repository-side mechanism, so this is `gap`.
23
+
24
+ **Impossible-AC lifecycle.** Three states in order: `drafted` (the proposal exists in the run), `proposed` (`propose ticket change` posted it as a tracker comment; a comment is not an edit), `resolved` (a human edited the ticket body). Every step-5 re-entry re-fetches the ticket and re-extracts the AC rows; a changed row is re-judged on the current head, so a human edit to the ticket body lifts the withhold without a PR head change. `Ticket changes` keeps one item per unresolved row, labeled `drafted` or `proposed`, and drops it only after the re-extracted row no longer meets the four conditions.
25
+
26
+ **No ticket** (none linked, or fetch failed): no AC rows; `Delivers` states the PR's intent as read from its title and body; `not judged here`, `impossible`, `Ticket changes`, and scope creep do not apply.
27
+
28
+ **Whole or part.** With a ticket linked, `Delivers` names coverage: *whole* when every row is `covered` or is `not judged here` with its mechanism half `covered`; *part, acceptable* when every uncovered row is either an observation half or **explicitly split** - the ticket body or a human-authored ticket comment, read from the tracker this run, names another tracker ref or a PR in another repository for that row; a cross-repo PR named there waives exactly the rows whose mechanism lives in that repository. Only the tracker waives an obligation: a spec `deferred: <where>`, a later PR in the same repository, a PR named only in this PR's body, or a `proposed` (not yet `resolved`) ticket change is a lead to check the tracker, never a waiver. Any other uncovered row is `gap`: "a later PR will add X" in the PR body, an unchecked box with no tracker split, a `deferred:` the ticket does not confirm.
29
+
30
+ **Claims.** The Verifier's three dispositions stand. `contradicted` is a blocker. `unverifiable-pre-merge` is not evidence and renders nothing: a PR whose only proof of a new path is "verified on stg" is blocked by the untested-path rubric row, not by a claim rule.
31
+
32
+ ## Namespaces
33
+
34
+ Every finding is one of two:
35
+
36
+ | Namespace | Contents | Gates merge |
37
+ |---|---|---|
38
+ | blocker | code defects, security, untested new path, `contradicted` claim, AC `gap`, scope creep against a linked ticket (diff content traceable to no AC and no stated intent), doc drift beyond wording (a doc now describes behavior the code does not have, or omits an operation or parameter the code adds), failed gate or undispositioned failing check | yes |
39
+ | nit | wording-only doc drift (typo, label, phrasing with the same meaning), style, reuse of an existing helper, naming | no; take-or-leave at the menu; untracked after the run |
40
+
41
+ Behavior the ticket promises is a blocker or an explicit tracker split - never deferred to a PR nobody opened.
42
+
43
+ **One defect, one blocker.** A missing mechanism surfaces through several rules at once (reviewer finding, AC `gap`, doc drift, `contradicted` claim); render it as one blocker that names the defect and lists every locator, and keep each contributing rule's ID in the ledger.
44
+
45
+ **Severity translation.** Reviewer Critical and Moderate -> blocker; Minor -> nit. A repo `REVIEW.md` mapping overrides this; a severity it names but does not map is fail-safe blocker, noted in `show evidence`.
46
+
47
+ **Triage bar** for findings the orchestrator originates itself: blocker only when it must be fixed before merge (correctness, security, a material performance trap, a convention the repo enforces); everything else is a nit, whatever its category. The bar never re-triages a classification the severity translation already made.
48
+
49
+ **Internal IDs.** `P#` (blocker or nit on code, a claim, a check), `L#` (AC `gap`, doc drift, scope creep), and the comment ledger's `C#` survive as keys for the post-push re-render diff and the fix wave; the human never sees them. IDs are append-only for the run: minted once, never renumbered, never reused. A fixed blocker leaves the rendered list on the next render; its ID stays in the ledger.
50
+
51
+ **`C#` ledger.** Each ledger row carries its comment `id` and the `updated_at` it was minted against; the post-push diff (`post-selection-loop.md` `### Re-render`) runs against this ledger, never against the report text. Row states: a triage label (`already-addressed` / `reasonable` / `judgment-call`) with a drafted reply; `superseded by C<new>`; `withdrawn`; `pending`; `reviewer failed (<conclusion>)`. The last four carry no reply and are not replyable; `reply` skips them.
69
52
 
70
53
  ## Dispositions
71
54
 
72
- Any blocking conclusion in the resolved check set (required or not - per
73
- `../verification-brief.md` Section B, Evidence resolution table) withholds
74
- merge from every pre-composed course until the user explicitly dispositions
75
- it, and mints a `P#` - except the reviewer check. claude-code-action's sticky
76
- mode runs on `pull_request` events, so its job is also a check run: a failing
77
- check whose run id (from its `url` / `detailsUrl`) matches a `reviewer failed`
78
- `C#` row, or whose `workflowName` equals the recorded reviewer `workflowName`
79
- while that run is `reviewer failed`, is inert when it is the only failing
80
- check mapping to that run id (two or more failing checks on one run id: none
81
- inert, each stays a `P#`, fail-safe) - no `P#`, no withhold, one `## Evidence`
82
- line `reviewer check <name> failed - inert (reviewer failure never withholds)`.
83
- With no sibling `success` left, evidence resolves to the Fallback row, not
84
- Failed CI. Reviewer failure never withholds merge; GitHub-enforced
85
- restrictions still apply.
86
-
87
- An undispositioned failing check in the resolved set is `P#` `[test]`
88
- referencing the check name; it is never a target of a worktree `fix`. Close
89
- failing checks by disposition, not by fix: the user's Phase-4 disposition
90
- annotates the same ID rather than closing it outright.
91
-
92
- | Disposition | Annotation on the `P#` | Counts against unfixed-blocker set | Merge course | Fallback local run |
93
- |---|---|---|---|---|
94
- | undispositioned failing check | none yet | yes | withheld from every pre-composed course | none |
95
- | flaky | `(dispositioned: flaky)` | excepted - no longer counts against "every `P#` blocks" or "every blocking finding fixed" | only the custom row naming the disposition explicitly; no pre-composed course restores | none |
96
- | real | `(dispositioned: real)` | still counts - `P#` keeps blocking | withheld until the check is green | none |
97
- | CI-infrastructure-broken | `(dispositioned: ci-infrastructure-broken)` | still counts - `P#` keeps blocking; the checks themselves are untrustworthy | withheld until the fallback run is green | triggers the fallback local run, and merge stays withheld until that fallback produces green evidence |
98
- | pending required check | mints no `P#`, is never dispositioned | not applicable - not dispositionable | withheld; auto-lifts the moment it turns green, or converts to an undispositioned failing check with its own `P#` on failure | none |
99
- | reviewer check failed (matches a `reviewer failed` `C#`) | mints no `P#`, is never dispositioned | not applicable - inert | not withheld; GitHub-enforced restrictions still apply | none |
100
-
101
- A pending required check is wait-until-green, not dispositionable. While
102
- pending, the report notes it under Evidence.
103
-
104
- The evidence decision is independent of the merge decision: a green check
105
- elsewhere in the resolved set still satisfies verification evidence while a
106
- pending required check withholds merge. The CI-sufficient path changes no
107
- consent surface: still read-only, no auto-merge, no posting, no menu change
108
- beyond the third disposition.
109
-
110
- ## Payloads
111
-
112
- "Drafted fixes / review" holds, per finding ID, the concrete edit (for
113
- `fix`), the reviewed doc-drift edit (for `push-docs`, keyed to its `L#`), or
114
- the reply text (for `reply`) - each keyed to its finding ID, one selection
115
- mapping 1:1 to its payload.
116
-
117
- A posted review body is not itself a finding: compose it at post time from
118
- the ID'd `P#`/`L#` findings being addressed - one summary sentence, then the
119
- numbered findings, ending on the fix or asked action - and give it its own
120
- non-finding slot of this section.
55
+ Any blocking conclusion in the resolved check set (required or not - `../verification-brief.md` Section B, Evidence resolution table) withholds `merge` until the user dispositions it, and mints a `P#` - except the reviewer check, whose exception the brief's Section B defines (one `show evidence` line `reviewer check <name> failed - inert`). An undispositioned failing check is never a target of `fix`; close it by disposition. A pending required check mints nothing and is not dispositionable: merge waits until it turns green, or it converts to a failing check with its own `P#`.
56
+
57
+ | Disposition | Annotation on the `P#` |
58
+ |---|---|
59
+ | undispositioned failing check | none yet |
60
+ | flaky | `(dispositioned: flaky)` |
61
+ | real | `(dispositioned: real)` |
62
+ | CI-infrastructure-broken | `(dispositioned: ci-infrastructure-broken)` |
63
+
64
+ Only `flaky` lifts the blocker; an undispositioned check, `real`, and `ci-infrastructure-broken` keep it until the check - or, for `ci-infrastructure-broken`, the fallback local run (brief Section B, Failed CI row) - is green.
65
+
66
+ How each disposition changes the menu: `decision-menu.md` `## Overlays`, CI check row.
67
+
68
+ ## Drafted payloads
69
+
70
+ 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 `impossible` row, draft the replacement AC text. 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.
71
+
72
+ 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.