pi-gauntlet 7.4.0 → 7.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/skills/gatekeep-pr/SKILL.md +6 -4
- package/skills/gatekeep-pr/reference/assessment.md +1 -1
- package/skills/gatekeep-pr/reference/decision-menu.md +13 -4
- package/skills/gatekeep-pr/reference/findings.md +1 -1
- package/skills/gatekeep-pr/reference/fix-wave.md +2 -2
- package/skills/gatekeep-pr/reference/post-selection-loop.md +10 -5
- package/skills/gatekeep-pr/reference/report.md +4 -4
- package/skills/gatekeep-pr/reference/sync.md +1 -1
- package/skills/gatekeep-pr/verification-brief.md +9 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v7.5.0 - 2026-10-06
|
|
4
|
+
|
|
5
|
+
- `gatekeep-pr` classifies every pending check as binding or not from GitHub's viewer-relative `mergeStateStatus`, refreshed before every menu (`### Pre-menu refresh`): a pending commit status or review requirement that does not bind the gating actor no longer withholds `merge` or recommends `wait`; a `BLOCKED`, `UNKNOWN`, or unreadable merge state, and a check whose Actions run is still live, still withhold. No new settings or overrides key. New `eval/gatekeep-pr-merge-state/` target with six digest fixtures and a recorded baseline/candidate run.
|
|
6
|
+
|
|
3
7
|
## v7.4.0 - 2026-10-06
|
|
4
8
|
|
|
5
9
|
### Added
|
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
|
-
- **20 skills** - the workflow logic. Twelve activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `forge-skill`, `linear` (reads/searches/comments on/manages Linear tickets via the `linearis` CLI; owns all linearis mechanics and the `## Issue tracker` overrides schema; tracker-facing skills route to it). Eight more require explicit entry (`disable-model-invocation: true`): `brainstorming` starts the gated workflow on explicit consent - use `/skill:brainstorming` for reliable discovery, or opt in through reachable explicit prose or a human-selected handoff; after spec approval, downstream planning and implementation still chain automatically. `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's acceptance criteria - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; the project's verification command runs only as fallback), a rubric-based review, a compact bottom-up report (what the PR delivers, ticket changes, PR comments, nits, blockers, then a `mergeable` / `fixable` verdict) and a verb menu whose rows derive from the actor's permissions on the PR; nothing mutates (fixes, pushes, reviews, merges, tracker comments) until you pick a row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification - run it with `/skill:gatekeep-pr <pr> [--rebase [base]]`. `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>`. `chase-bug` is human-only bug triage: read-only root-cause discovery to an evidenced verdict menu (real bug -> ticket/brainstorm/hotfix/respond; five negative verdicts), then a gated response to the reporter for addressable origins (GitHub issue / tracker ticket) and a rendered verdict summary otherwise - it never fixes during triage; the hotfix row hands off to `skills/chase-bug/hotfix.md` after the menu - run it with `/skill:chase-bug`. `gauntlet-performance` reads the committed run telemetry (current repo by default, `--dir <path>` adds others) through the parse-only `gauntlet-performance` CLI and answers with one example-led recommendation, 3-5 cornerstone numbers, a council assessment when available, and a menu of at most three actions (render a report file, open the recommendation as a ticket via `shape-ticket`, drill into one run); it writes nothing unless you pick render - run it with `/skill:gauntlet-performance [--dir <path>]... [--since <version>]`. `gauntlet-handoff` ends a session whose flow a fresh session will continue: it invokes pi-cohort's `handoff` skill (`--out <path>` or `--key <name>`, default key = the run worktree's branch with `/` flattened to `-`, default file `<tmpdir>/pi-handoff/<key>.md`), then appends the gauntlet process-state section (phase/plan tracker status) per the shared contract `skills/gauntlet-resume/reference/brief-contract.md` - run it with `/skill:gauntlet-handoff [--out <path> | --key <name>]`. `gauntlet-resume` is the only way back into an interrupted flow from a fresh session: it takes a `gauntlet-handoff` brief (a file path, pasted text, or - with no arguments - a pick from the briefs under `<tmpdir>/pi-handoff/`), a bare worktree that already holds a spec, or a spec tracked on main under a configured spec dir (a spec seed: it creates or reuses the worktree through `using-git-worktrees` and pins the spec), restores phase/plan tracker state through the legal arming sequence (`start brainstorm`, `skip` with `resume:` reasons, `plan_check` before implement-or-later), and never infers approval from artifacts - run it with `/skill:gauntlet-resume [<brief-file> | <spec>.md | <worktree-name-or-path>]`.
|
|
72
|
+
- **20 skills** - the workflow logic. Twelve activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `forge-skill`, `linear` (reads/searches/comments on/manages Linear tickets via the `linearis` CLI; owns all linearis mechanics and the `## Issue tracker` overrides schema; tracker-facing skills route to it). Eight more require explicit entry (`disable-model-invocation: true`): `brainstorming` starts the gated workflow on explicit consent - use `/skill:brainstorming` for reliable discovery, or opt in through reachable explicit prose or a human-selected handoff; after spec approval, downstream planning and implementation still chain automatically. `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's acceptance criteria - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; a pending commit status that GitHub does not report as blocking the gating actor (`mergeStateStatus` not `BLOCKED`) is not waited for - external-CI commit statuses included - while a running Actions check, an unknown merge state, and every other merge precondition still withhold; the project's verification command runs only as fallback), a rubric-based review, a compact bottom-up report (what the PR delivers, ticket changes, PR comments, nits, blockers, then a `mergeable` / `fixable` verdict) and a verb menu whose rows derive from the actor's permissions on the PR; nothing mutates (fixes, pushes, reviews, merges, tracker comments) until you pick a row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification - run it with `/skill:gatekeep-pr <pr> [--rebase [base]]`. `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>`. `chase-bug` is human-only bug triage: read-only root-cause discovery to an evidenced verdict menu (real bug -> ticket/brainstorm/hotfix/respond; five negative verdicts), then a gated response to the reporter for addressable origins (GitHub issue / tracker ticket) and a rendered verdict summary otherwise - it never fixes during triage; the hotfix row hands off to `skills/chase-bug/hotfix.md` after the menu - run it with `/skill:chase-bug`. `gauntlet-performance` reads the committed run telemetry (current repo by default, `--dir <path>` adds others) through the parse-only `gauntlet-performance` CLI and answers with one example-led recommendation, 3-5 cornerstone numbers, a council assessment when available, and a menu of at most three actions (render a report file, open the recommendation as a ticket via `shape-ticket`, drill into one run); it writes nothing unless you pick render - run it with `/skill:gauntlet-performance [--dir <path>]... [--since <version>]`. `gauntlet-handoff` ends a session whose flow a fresh session will continue: it invokes pi-cohort's `handoff` skill (`--out <path>` or `--key <name>`, default key = the run worktree's branch with `/` flattened to `-`, default file `<tmpdir>/pi-handoff/<key>.md`), then appends the gauntlet process-state section (phase/plan tracker status) per the shared contract `skills/gauntlet-resume/reference/brief-contract.md` - run it with `/skill:gauntlet-handoff [--out <path> | --key <name>]`. `gauntlet-resume` is the only way back into an interrupted flow from a fresh session: it takes a `gauntlet-handoff` brief (a file path, pasted text, or - with no arguments - a pick from the briefs under `<tmpdir>/pi-handoff/`), a bare worktree that already holds a spec, or a spec tracked on main under a configured spec dir (a spec seed: it creates or reuses the worktree through `using-git-worktrees` and pins the spec), restores phase/plan tracker state through the legal arming sequence (`start brainstorm`, `skip` with `resume:` reasons, `plan_check` before implement-or-later), and never infers approval from artifacts - run it with `/skill:gauntlet-resume [<brief-file> | <spec>.md | <worktree-name-or-path>]`.
|
|
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
|
- **4 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. In a brainstorming-entered flow, phase-tracker rejects `implement` or `verify` completion while tracker tasks remain pending or in progress; see [its configuration reference](./doc/configuration.md#phase-tracker). phase-tracker also registers `plan_check`, which verifies a plan against its spec and against the grammar in [skills/writing-plans/reference/plan-contract.md](./skills/writing-plans/reference/plan-contract.md), including that each task's `Tests:` commands are selective and never the full suite; a pass stamps the plan for implementation. `telemetry` records one YAML record per gauntlet run (phase timing, models, personas, gate/fix rounds, diff at ship) that ships in the squash beside the spec - it is written only inside a brainstorming-entered flow, kept out of history until `finishing-a-development-branch` seals it once with `gauntlet-telemetry-seal` (a `telemetry:` commit under git; the working-copy snapshot in a plain jj workspace) - and blocks a brainstorm `write` into an already-shipped spec; see [its configuration reference](./doc/configuration.md#telemetry). See [doc/configuration.md](./doc/configuration.md) for the settings each one reads.
|
|
75
75
|
|
package/package.json
CHANGED
|
@@ -26,11 +26,11 @@ Verify (step 3) and Review (step 4) each run as a fresh helper with `cwd` the PR
|
|
|
26
26
|
| 2b Sync (only with `--rebase`) | `reference/sync.md` | the PR worktree on the rebased, pushed head with a refreshed digest, or a `sync` record and the unsynced head | `sync` |
|
|
27
27
|
| 3 Verify | `verification-brief.md` Section B | evidence record (CI-first) and one disposition per material claim | `verify` |
|
|
28
28
|
| 4 Review | `verification-brief.md` Section C | reviewer findings and the internal comment ledger | `review` |
|
|
29
|
-
| 5 Integrate + report | `reference/findings.md`, then `reference/report.md` | AC outcomes, blockers, nits, drafted payloads, the rendered report | `report` |
|
|
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
31
|
| 7 Loop | `reference/post-selection-loop.md` (a `fix` pick continues in `reference/fix-wave.md`) | the executed pick; re-entry until `merge` or `stop` | (re-opens the re-entered stage) |
|
|
32
32
|
|
|
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. A loop re-entry never re-runs step 2b; the sync runs once per invocation.
|
|
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
|
|
|
35
35
|
## Progress tracking
|
|
36
36
|
|
|
@@ -65,11 +65,13 @@ These bind every rendered report, menu, and external payload (review bodies, rep
|
|
|
65
65
|
- 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`.
|
|
66
66
|
- Raw command output, drafted payloads, or internal IDs in the rendered report - owner: `reference/report.md`.
|
|
67
67
|
- An open-PR menu, other than the conflict 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 merged or closed PR offers `show evidence` and `stop` only); any menu without `show evidence`, with two `[recommended]`, or with `stop` not last - owner: `reference/decision-menu.md`.
|
|
68
|
-
- A local verification run while
|
|
68
|
+
- 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`.
|
|
69
69
|
- 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`.
|
|
70
70
|
- A helper's duty run in the orchestrator's own context - owner: `## Harness notes`.
|
|
71
71
|
- Approving your own PR - owner: `reference/decision-menu.md` `## Consent table`.
|
|
72
|
-
- Merging around an undispositioned failing check or a pending
|
|
72
|
+
- Merging around an undispositioned failing check or a binding pending check, or a `BLOCKED`/`UNKNOWN`/`unreadable` merge state - owner: `reference/findings.md` `## Dispositions`.
|
|
73
|
+
- A menu rendered without the pre-menu refresh - owner: `reference/post-selection-loop.md` `### Pre-menu refresh`.
|
|
74
|
+
- Reading `mergeStateStatus` from anything but a `gh pr view` invocation, attributing a `BLOCKED` to a named rule, or treating a non-`BLOCKED` value as a merge verdict - owner: `verification-brief.md` Section A.
|
|
73
75
|
- A claim verdict recorded behind a pending tracker stage - owner: `## Progress tracking`.
|
|
74
76
|
|
|
75
77
|
## Project overrides
|
|
@@ -14,7 +14,7 @@ Record create-vs-reuse; it drives teardown (`post-selection-loop.md` `### Teardo
|
|
|
14
14
|
|
|
15
15
|
Then set the digest's `permissions.head_pushable` (`../verification-brief.md` Section A): `git -C <worktree> push --dry-run --no-verify --no-follow-tags <head_url> HEAD:refs/heads/<head_ref>` exit 0 is `true`, a non-zero exit is `false`, when `head_url` is `unreadable`, `head_pushable` is `unreadable` and the probe does not run. The probe records observed authorization; a real push the server rejects (branch protection) is reported as that push's failure.
|
|
16
16
|
|
|
17
|
-
When the digest reported `mergeable: UNKNOWN`, re-poll once (`gh pr view --json mergeable`). Still `UNKNOWN` is not merge-ready
|
|
17
|
+
When the digest reported `mergeable: UNKNOWN` or `mergeStateStatus: UNKNOWN`, re-poll once (`gh pr view --json mergeable,mergeStateStatus`) and write both values back to the digest (`mergeStateStatus` also to `permissions.merge_state_status`). Still `UNKNOWN` is not merge-ready: the loop's merge preconditions treat `mergeable: UNKNOWN` like `CONFLICTING`, and `merge_state_status: UNKNOWN` makes every pending check binding (`../verification-brief.md` Section B, binding classification) and withholds `merge` as `merge state unknown`.
|
|
18
18
|
|
|
19
19
|
## Configuration
|
|
20
20
|
|
|
@@ -20,7 +20,7 @@ Rows are `<n>. <verb> - <consequence>`; every row, `stop` included, carries its
|
|
|
20
20
|
- `reply` - post the drafted replies under `PR comments` (`reply <n>` for one)
|
|
21
21
|
- `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
|
|
22
22
|
- `show evidence` - print the evidence record, claim and AC outcomes, dispositions, drafted payloads and replies (`report.md` `## What stays out`)
|
|
23
|
-
- `wait` - poll the reviewer run, every pending check in the resolved set (required or not), and the comment set, then re-render
|
|
23
|
+
- `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
24
|
- `stop` - leave the PR as-is
|
|
25
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` and `merge anyway - accept AC<n> as impossible` render as their own rows when `## Availability` allows them, so the rendered-row rule does not apply to them. 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`).
|
|
@@ -45,7 +45,7 @@ Rows derive from the digest's `permissions` block (`../verification-brief.md` Se
|
|
|
45
45
|
|
|
46
46
|
Authorship moves only `[recommended]`, never which rows are offered. The first rendered row is `[recommended]`; when every other row is `show evidence`, `stop` is `[recommended]`.
|
|
47
47
|
|
|
48
|
-
A `fixable` verdict with zero blockers (a withhold reason) uses the mergeable rows with `merge` omitted; the reason stays in the verdict line (`report.md`) and in the `Not offered:` sentence. On such a withhold, `[recommended]` goes to `approve workflow run` under the held-run overlay, else to `wait` under the verification-evidence-pending, pending-reviewer, or pending-
|
|
48
|
+
A `fixable` verdict with zero blockers (a withhold reason) uses the mergeable rows with `merge` omitted; the reason stays in the verdict line (`report.md`) and in the `Not offered:` sentence. On such a withhold, `[recommended]` goes to `approve workflow run` under the held-run overlay, else to `approve` or `wait` under the merge state overlay, else to `wait` under the verification-evidence-pending, pending-reviewer, or binding-pending-check overlay, else to `propose ticket change` while an `impossible` row is `drafted`, else to `stop`.
|
|
49
49
|
|
|
50
50
|
| Author | Verdict | Rows |
|
|
51
51
|
|---|---|---|
|
|
@@ -56,6 +56,14 @@ A `fixable` verdict with zero blockers (a withhold reason) uses the mergeable ro
|
|
|
56
56
|
|
|
57
57
|
`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.
|
|
58
58
|
|
|
59
|
+
## Withhold reason resolver
|
|
60
|
+
|
|
61
|
+
The single source for the merge-withhold reason, the `PR comments` line, and the `Not offered: merge (...)` text, in this precedence; overlays above `merge state` (merged or closed PR, draft PR, held run) keep their own reasons, and under two firing overlays the higher one owns the reason and the single `PR comments` line - the first matching reason renders, and `report.md`'s `fixable - <withhold reason>` carries the same word:
|
|
62
|
+
|
|
63
|
+
1. `merge_state_status` is `BLOCKED` -> line `GitHub reports this actor's merge as blocked, so merge waits. (merge_state_status BLOCKED; reviewDecision <value>)`, `Not offered: merge (blocked by GitHub)`, reason `blocked by GitHub`. `reviewDecision` rides as a locator only - never as a claim about which rule blocks, and no rendered sentence says another developer's approval is required.
|
|
64
|
+
2. `merge_state_status` is `UNKNOWN` or `unreadable`, or the pre-menu refresh failed -> line `GitHub has not reported this actor's merge state, so merge waits. (merge_state_status <value>)` (on a failed refresh the locator is `merge state not refreshed (<reason>)`), `Not offered: merge (merge state unknown)`, reason `merge state unknown`.
|
|
65
|
+
3. a binding pending check (`../verification-brief.md` Section B, binding classification) -> line `A check that binds this actor is still pending, so merge waits. (<check name>)`, `Not offered: merge (<check name> pending)`, reason `binding check pending`.
|
|
66
|
+
|
|
59
67
|
## Overlays
|
|
60
68
|
|
|
61
69
|
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]`.
|
|
@@ -65,8 +73,9 @@ Overlays modify the consent row; they are never a second offer source. When two
|
|
|
65
73
|
| merged or closed PR | `state` is not `OPEN` | the report renders; the menu is `show evidence` and `stop` only |
|
|
66
74
|
| 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` |
|
|
67
75
|
| 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 |
|
|
68
|
-
|
|
|
69
|
-
|
|
|
76
|
+
| 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 |
|
|
77
|
+
| 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 |
|
|
78
|
+
| 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) |
|
|
70
79
|
| 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 |
|
|
71
80
|
| 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]` |
|
|
72
81
|
| impossible AC | an `impossible` row in `drafted` or `proposed` while every other merge prerequisite holds | `merge` is omitted (`Not offered: merge (ticket change pending on AC<n>)`); `merge anyway - accept AC<n> as impossible` renders as its own row when `## Availability` allows it and lifts exactly that withhold - blockers, an unreviewed delta, pending checks, and forge-refused states stay in force |
|
|
@@ -52,7 +52,7 @@ Behavior the ticket promises is a blocker or an explicit tracker split - never d
|
|
|
52
52
|
|
|
53
53
|
## Dispositions
|
|
54
54
|
|
|
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
|
|
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 **binding** pending check (`../verification-brief.md` Section B, binding classification) mints nothing and is not dispositionable: merge waits until it turns green, or `merge_state_status` leaves `BLOCKED`, or it converts to a failing check with its own `P#`. A not-binding pending check withholds nothing, never renders under `PR comments`, and prints under `show evidence` only, as `<name> pending - not binding this viewer (merge_state_status <value>)`; the `required` field stays gathered and no longer withholds on its own.
|
|
56
56
|
|
|
57
57
|
| Disposition | Annotation on the `P#` |
|
|
58
58
|
|---|---|
|
|
@@ -49,8 +49,8 @@ Mark a finding resolved only from a `resolved` closure line; the orchestrator ne
|
|
|
49
49
|
|
|
50
50
|
Run `## Conflicts` once more, then push to the recorded destination: `git -C <worktree> push --no-follow-tags <head_url> HEAD:refs/heads/<head_ref>`. Report a rejected push verbatim and re-render the menu with the commits unpushed.
|
|
51
51
|
|
|
52
|
-
Poll the pushed head only, every 30 seconds for up to `timeout minutes` (default 15): `gh pr view <N> --json headRefOid,statusCheckRollup` and `gh run list -R <base-owner>/<base-repo> -c <head> --json databaseId,status,conclusion`. A moved `headRefOid` stops the poll and takes the head-move rule (re-enter step 3, `post-selection-loop.md` intro). Normalize first: a `CheckRun` is terminal at `status: COMPLETED` with its `conclusion`; a `StatusContext` maps `state` `SUCCESS` to success, `FAILURE` and `ERROR` to blocking, `PENDING` and `EXPECTED` to pending; apply the `ci checks` filter and the reviewer-check exception (`../verification-brief.md` Section B) before the table
|
|
52
|
+
Poll the pushed head only, every 30 seconds for up to `timeout minutes` (default 15): `gh pr view <N> --json headRefOid,statusCheckRollup,mergeStateStatus` and `gh run list -R <base-owner>/<base-repo> -c <head> --json databaseId,status,conclusion`. A moved `headRefOid` stops the poll and takes the head-move rule (re-enter step 3, `post-selection-loop.md` intro). Normalize first: a `CheckRun` is terminal at `status: COMPLETED` with its `conclusion`; a `StatusContext` maps `state` `SUCCESS` to success, `FAILURE` and `ERROR` to blocking, `PENDING` and `EXPECTED` to pending; apply the `ci checks` filter and the reviewer-check exception (`../verification-brief.md` Section B) before the table, then the binding classification of every pending check on the pushed head's `mergeStateStatus`.
|
|
53
53
|
|
|
54
54
|
Resolve the normalized set through `../verification-brief.md` `## Section B - Verifier` (Evidence resolution) - the same table the first pass uses. When the re-resolve moves the evidence from `source: pending` to CI-sufficient (an approved held run went green, or a `wait` ended), dispatch a fresh Section B helper in claim-check-only mode (`../verification-brief.md` Section B) before rendering the menu. `approve workflow run` executes `gh api --method POST repos/<base-owner>/<base-repo>/actions/runs/<id>/approve`, then re-polls. The evidence block records the outcome and, when a local-run row fires, dispatch a fresh Section B helper; the evidence block records which trigger fired: `local run: no conclusive check`, `local run: held run not approvable`, or `local run: local verification: always`.
|
|
55
55
|
|
|
56
|
-
Then run the own-push sequence of `post-selection-loop.md` `### Re-render`: the comment refetch and reconciliation, a fresh reviewer helper for any eligible comment delta, and the menu rendered from the closure lines for the exact pushed SHA. No claim re-check and no whole-wave review runs after an own push.
|
|
56
|
+
Then run the own-push sequence of `post-selection-loop.md` `### Re-render`: the comment refetch and reconciliation, a fresh reviewer helper for any eligible comment delta, and the menu rendered from the closure lines for the exact pushed SHA. No claim re-check and no whole-wave review runs after an own push. The sequence runs the pre-menu refresh as `post-selection-loop.md` `### Re-render` step 1 (`### Pre-menu refresh`) - an approval dismissed by the push flips a bound viewer to `BLOCKED`, and the menu says so.
|
|
@@ -12,14 +12,15 @@ All must hold before `merge` executes; a `merge` pick while one fails is refused
|
|
|
12
12
|
- evidence provenance clean (`findings.md` `## Provenance`): `worktree_root`, every `run_cwd`, and `head_sha` match on the local path
|
|
13
13
|
- `mergeable == MERGEABLE` (`UNKNOWN` after step 2's single re-poll withholds like `CONFLICTING`)
|
|
14
14
|
- `merge_state_status` is not `BEHIND`; while it is, the menu renders `update branch` (`fix-wave.md` `## Conflicts` row 1, same precondition) beside the consent rows
|
|
15
|
-
- no undispositioned failing check in the resolved set, no pending
|
|
15
|
+
- no undispositioned failing check in the resolved set, no **binding** pending check (`../verification-brief.md` Section B, binding classification)
|
|
16
|
+
- `merge_state_status` is not `BLOCKED`, `UNKNOWN`, or `unreadable`, and the pre-menu refresh succeeded (`### Pre-menu refresh`); a non-`BLOCKED` value is never a verdict - it only leaves this row satisfied, and a `gh pr merge` refusal is reported verbatim
|
|
16
17
|
- no retained, unreviewed comment delta (`### Re-render` step 4)
|
|
17
18
|
- worktree tracked-clean and synced with the remote head (fixes pushed first)
|
|
18
19
|
- the compare-and-swap below passes
|
|
19
20
|
|
|
20
21
|
### Compare-and-swap
|
|
21
22
|
|
|
22
|
-
Before every external write, re-fetch `headRefOid`, `state`, and `
|
|
23
|
+
Before every external write, re-fetch `headRefOid`, `state`, `mergeable`, and `mergeStateStatus`. A head or state change since assessment invalidates the current state - re-sync the worktree, re-enter step 3, and re-render the menu; a `mergeStateStatus` change alone (head unchanged) invalidates a `merge` or `merge anyway` pick and re-enters step 5 through `### Pre-menu refresh`; `reply`, `review`, `approve`, and `propose ticket change` proceed, and the next menu renders from the refreshed value. Exception: a pick's own push updates the assessed head to the pushed SHA as part of that pick's execution - this self-inflicted head move does not invalidate the pick; the next compare-and-swap runs against the new head on the next external write. Before a merge executes (plain or `anyway`), run the full comment refetch and reconciliation (`### Re-render` steps 1-5), not just placeholder detection. If the head changed, re-enter step 3 instead. A new or changed-body delta since the consent render aborts the selected merge, plain or `anyway`: show the reconciled report and request a fresh pick even when no blocker resulted. A newly `pending` row, a queued/in-progress reviewer run, or a failed refetch (`comments not refreshed (<reason>)`) refuses a plain merge and re-renders; `anyway` overrides only those existing pending/refetch-failure overlays and prints what it overrode, never a new blocker or unreviewed delta.
|
|
23
24
|
|
|
24
25
|
### Fix wave
|
|
25
26
|
|
|
@@ -37,7 +38,7 @@ Apply per `fix-wave.md`: the local conflict check, one fresh implementer helper
|
|
|
37
38
|
|
|
38
39
|
After every push or any mutation that can change readiness (fix wave pushed, PR head moved), re-sync the worktree. On an own push, after `fix-wave.md` `## Evidence after push`, run steps 1-5 below and render the menu from the pre-push reviewer's closure lines for the exact pushed SHA, where a blocker whose closure line reads `resolved` leaves the rendered list while its internal ID stays in the ledger and new findings continue each sequence; no claim re-check and no whole-wave review runs after an own push. On every other head move, re-enter step 3, then run steps 1-5 below - the last read before the menu renders:
|
|
39
40
|
|
|
40
|
-
1.
|
|
41
|
+
1. Run the pre-menu refresh (`### Pre-menu refresh`), then re-run the Section A comment fetches (`../verification-brief.md`: both `--paginate` calls, plus the GraphQL `reviewThreads` query when the initial gather used it), one `gh run view` per placeholder row, and the reviewer-run `gh run list` when a reviewer workflow is known. Read-only.
|
|
41
42
|
2. Diff the fresh set against the `C#` ledger (`findings.md` `## Namespaces`) by `id` and `updated_at`:
|
|
42
43
|
- same `id`, same `updated_at` -> unchanged: keep the existing `C#`.
|
|
43
44
|
- same `id`, different `updated_at` -> edited: mint a new `C#`; the old one is `superseded by C<new>`. An `updated_at` change with an identical body (reaction, revert) still counts as edited.
|
|
@@ -50,11 +51,15 @@ After every push or any mutation that can change readiness (fix wave pushed, PR
|
|
|
50
51
|
|
|
51
52
|
**Refetch failure** (`gh` non-zero, network, pagination incomplete): re-render with the ledger's prior states, one `PR comments` line `Comments could not be refreshed, so merge waits. (<reason>)`, and the pending-reviewer overlay withholding `merge`; `wait` is recommended; a composed `merge anyway` remains available when `decision-menu.md` `## Availability` allows it.
|
|
52
53
|
|
|
54
|
+
### Pre-menu refresh
|
|
55
|
+
|
|
56
|
+
The first read of step 5, before the report and the menu render from one state - the first pass included (a long local run or review can stale the gather), every `### Re-render` entry, the post-`wait` re-fetch, `update branch`, a `merge base` round, and a fix-wave push (`fix-wave.md` `## Evidence after push`). Run `gh pr view <N> --json headRefOid,state,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup` and `gh run list -R <owner>/<repo> -c <headRefOid> --json databaseId,status,conclusion,workflowName,url`. Head moved -> the head-move rule (re-enter step 3). `state` or `mergeable` changed (head unchanged) -> route through `### Compare-and-swap` re-assessment. Head unchanged with `state` and `mergeable` unchanged -> replace `permissions.merge_state_status`, `pr.reviewDecision`, `status_checks[]`, and `actions_runs[]` in the digest together, re-normalize and re-classify every pending check (`../verification-brief.md` Section B, binding classification), and re-resolve the Evidence resolution table on the fresh set, so a check whose run completed since the last menu is never paired with its cached run status; when that re-resolve moves `source: pending` to CI-sufficient, dispatch the Section B helper in claim-check-only mode (`../verification-brief.md` Section B) before rendering. A `gh` failure keeps the prior values, classifies every pending check as binding (`../verification-brief.md` Section B, binding classification), and renders the `merge state` overlay (`decision-menu.md` `## Overlays`) through `## Withhold reason resolver` reason 2 (locator `merge state not refreshed (<reason>)`). The refresh is read-only and never mints `C#` rows - comment reconciliation stays in `### Re-render`.
|
|
57
|
+
|
|
53
58
|
### Wait course
|
|
54
59
|
|
|
55
|
-
For a `wait` pick, run a sequence of short bounded calls - never one long bash call. Each iteration: `gh run view -R <repo> <run-id> --json status,conclusion` for every tracked run (placeholder-linked and head-listed), and `gh pr view <N> -R <repo> --json statusCheckRollup` while any check in the resolved set is
|
|
60
|
+
For a `wait` pick, run a sequence of short bounded calls - never one long bash call. Each iteration: `gh run view -R <repo> <run-id> --json status,conclusion` for every tracked run (placeholder-linked and head-listed), and `gh pr view <N> -R <repo> --json statusCheckRollup,mergeStateStatus` while any pending check in the resolved set is binding when classified against the polled rollup and the polled `mergeStateStatus`, or the polled `mergeStateStatus` is `BLOCKED`, `UNKNOWN`, or `unreadable`; when any row has no parsable URL, or its run is completed while the prefix persists, one comment refetch as well; then sleep 30 s. Use each polling refetch to classify binding checks and compare the loop's stop condition against the polled values; leave the digest and `C#` ledger untouched until the completion/timeout reconciliation below - this preserves persisted digest state, not the loop's comparison values. Check the deadline between iterations: the resolved `timeout minutes` (default 15, the same knob as the local verification run). Stop when every tracked run is completed and no pending check is binding against the polled values and the polled `mergeStateStatus` is none of `BLOCKED`, `UNKNOWN`, `unreadable` and no row is in the "completed `success`, prefix persists" state, or the deadline passes.
|
|
56
61
|
|
|
57
|
-
Then re-
|
|
62
|
+
Then run `### Pre-menu refresh` (it re-fetches `statusCheckRollup`, `headRefOid`, `state`, `mergeable`, `mergeStateStatus`). If the head advanced or state/mergeability changed, route through compare-and-swap re-assessment before reusing evidence or reviewing comments; otherwise re-resolve the brief's Evidence table on the fresh rollup; dispatch a fresh Section B helper for a local run only when the re-resolved table selects a local-run row (Fallback, Held run not approvable, or Opt-out - `../verification-brief.md` Evidence resolution) and no evidence exists yet for this head - otherwise the existing evidence stands. On completion or timeout, run steps 2-5 of `### Re-render` plus step 1's comment and reviewer-run reads - the pre-menu refresh already ran - and re-render; do not re-run claim-check or whole-diff review solely because the head did not move. On timeout: rows in the completed-`success`/prefix-persists state become `reviewer failed (stale placeholder)` (brief Section C placeholder table); every other `pending` row stays `pending`, merge stays withheld, `wait` renders as row 1 again.
|
|
58
63
|
|
|
59
64
|
### Output done-check
|
|
60
65
|
|
|
@@ -4,7 +4,7 @@ Read from SKILL.md step 5, after `findings.md`. Input: the integration output. O
|
|
|
4
4
|
|
|
5
5
|
## Order
|
|
6
6
|
|
|
7
|
-
Render bottom-up for a terminal: least important first, verdict and menu last. Empty sections are omitted - never "None". A failed comment refresh, an incomplete comment source review (`post-selection-loop.md` `### Re-render`), or a pending
|
|
7
|
+
Render bottom-up for a terminal: least important first, verdict and menu last. Empty sections are omitted - never "None". A failed comment refresh, an incomplete comment source review (`post-selection-loop.md` `### Re-render`), a blocked or unknown merge state, or a binding pending check renders its one line under `PR comments` as well.
|
|
8
8
|
|
|
9
9
|
Section order is fixed:
|
|
10
10
|
|
|
@@ -18,10 +18,10 @@ Delivers: <one sentence: what the PR does. With a ticket, a second sentence buil
|
|
|
18
18
|
Ticket changes: (one item per unresolved `impossible` row)
|
|
19
19
|
- AC<n> asks for <X>, and <cited constraint>. Proposed wording: "<new AC text>". (drafted | proposed <comment url>; <source>)
|
|
20
20
|
|
|
21
|
-
PR comments: (replyable review comments, a pending reviewer run or
|
|
21
|
+
PR comments: (replyable review comments, a pending reviewer run, a blocked or unknown merge state, a binding pending check, a failed refresh, or an incomplete source review - see the paragraph above)
|
|
22
22
|
- <reviewer>'s comment on <topic> <verdict clause>, so the drafted reply <what it says>. (<locator>)
|
|
23
23
|
- The reviewer run is still in progress, so merge waits. (<run url>)
|
|
24
|
-
-
|
|
24
|
+
- <the line from decision-menu.md `## Withhold reason resolver`>
|
|
25
25
|
|
|
26
26
|
Nits:
|
|
27
27
|
- <1-2 sentences>. (<file:line>)
|
|
@@ -40,7 +40,7 @@ The `Sync:` line renders only when `--rebase` was passed, as one whole sentence
|
|
|
40
40
|
|
|
41
41
|
## Verdict
|
|
42
42
|
|
|
43
|
-
Two states: `mergeable` or `fixable`. `mergeable` carries the evidence clause from `findings.md` `## Provenance`: `CI green on the assessed head (<check name>)` or `verification command passed locally`. `fixable - <N> blockers` when `Blockers` is non-empty; `fixable - <withhold reason>` when it is empty but a merge prerequisite is unmet: `ticket change pending on AC<n>` (for example `ticket change pending on AC4`), `reviewer run in progress`, `
|
|
43
|
+
Two states: `mergeable` or `fixable`. `mergeable` carries the evidence clause from `findings.md` `## Provenance`: `CI green on the assessed head (<check name>)` or `verification command passed locally`. `fixable - <N> blockers` when `Blockers` is non-empty; `fixable - <withhold reason>` when it is empty but a merge prerequisite is unmet: `ticket change pending on AC<n>` (for example `ticket change pending on AC4`), `reviewer run in progress`, `binding check pending`, `blocked by GitHub`, `merge state unknown`, `failed gate` (listed for spec parity; a red gate mints a blocker per `findings.md` `## Namespaces`, so `Blockers` is non-empty), `comments not refreshed`, `comment source review incomplete`, `verification not run` (the command needed credentials or no command resolved, with nothing else blocking), `mergeable unknown`, `behind base`, `merge conflict` (for `mergeable == CONFLICTING`).
|
|
44
44
|
|
|
45
45
|
`<N> blockers` reads `1 blocker` for one.
|
|
46
46
|
|
|
@@ -50,7 +50,7 @@ A refusal whose message names the lease (`stale info`, the remote head moved sin
|
|
|
50
50
|
|
|
51
51
|
## Re-gather
|
|
52
52
|
|
|
53
|
-
Re-run every step-1 forge read for the PR (`../verification-brief.md` Section A fixed command set): `headRefOid`, `mergeable`, `mergeStateStatus`, `reviewDecision`, status checks and `actions_runs`, commits, changed files and diff, comments and reviews; the step-1 ticket reads are not repeated. Unreadable fields fail closed as in step 1. Re-resolve the configuration ladder (`assessment.md` `## Configuration`) at `git merge-base origin/<baseRefName> HEAD` - `baseRefName` stays the trust source even when `<base>` names another branch. Run no CI poll here: step 3's first pass is the single poll window (every 30 s for up to `timeout minutes`; a check still pending at the limit renders the `
|
|
53
|
+
Re-run every step-1 forge read for the PR (`../verification-brief.md` Section A fixed command set): `headRefOid`, `mergeable`, `mergeStateStatus`, `reviewDecision`, status checks and `actions_runs`, commits, changed files and diff, comments and reviews; the step-1 ticket reads are not repeated. Unreadable fields fail closed as in step 1. Re-resolve the configuration ladder (`assessment.md` `## Configuration`) at `git merge-base origin/<baseRefName> HEAD` - `baseRefName` stays the trust source even when `<base>` names another branch. Run no CI poll here: step 3's first pass is the single poll window (every 30 s for up to `timeout minutes`; a binding check still pending at the limit renders the `verification evidence pending` overlay (`../verification-brief.md` Section B, binding classification), never green, and a head with no check, or only not-binding pending checks, takes the `Fallback` row) and step 3 resolves the pushed head as any assessed head - the sync push is not an own push for `### Re-render` and the `Fix wave` row of Section B does not apply; a head that moves during that poll takes the existing head-move rule (`post-selection-loop.md` `### Re-render`), with step 2's divergence stop as the backstop. Red CI on the pushed head triggers no automatic fix: step 3 records the failing checks and the menu offers `fix` - `fix [recommended]` only when `decision-menu.md` and `../verification-brief.md` Section B make it `[recommended]`. Set the digest's `sync` field (`../verification-brief.md` Section A), close the `sync` stage `complete`, and enter step 3 with the refreshed digest.
|
|
54
54
|
|
|
55
55
|
## Failure path
|
|
56
56
|
|
|
@@ -63,8 +63,8 @@ provisioning, so it never re-polls; step 2 re-polls once after provisioning (`re
|
|
|
63
63
|
- pr: { number, title, body, author, author_is_bot, state, isDraft, headRefName, baseRefName,
|
|
64
64
|
isCrossRepository, mergeable, mergeStateStatus, headRefOid, headRepository, maintainerCanModify, files, additions, deletions, reviewDecision }
|
|
65
65
|
- viewer: { login, is_author }
|
|
66
|
-
- permissions: { viewer_permission, is_cross_repository, maintainer_can_modify, merge_state_status, can_update_branch, head_url, head_ref, head_pushable } # head_pushable is set in step 2 after provisioning (reference/assessment.md); any field that cannot be read is the literal `unreadable`; menu availability derives from this block (reference/decision-menu.md ## Availability)
|
|
67
|
-
- status_checks: [ { name, status, conclusion, required, url, workflowName } ] # evidence semantics: Section B Evidence resolution; workflowName from the CheckRun rollup entry (absent on StatusContext)
|
|
66
|
+
- permissions: { viewer_permission, is_cross_repository, maintainer_can_modify, merge_state_status, can_update_branch, head_url, head_ref, head_pushable } # head_pushable is set in step 2 after provisioning (reference/assessment.md); merge_state_status is `BLOCKED`, `BEHIND`, `UNKNOWN`, `unreadable` (null), or any other GitHub value (`CLEAN`, `UNSTABLE`, `HAS_HOOKS`, `DIRTY`) and is written only from a `gh pr view` invocation that names `mergeStateStatus` (this call, the step-2 re-poll, the step-2b re-gather (`reference/sync.md`), the pre-menu refresh, compare-and-swap, the wait and fix-wave polls), never from an ad hoc GraphQL query or `gh pr list`, and never from a reply carrying `errors`; any field that cannot be read is the literal `unreadable`; menu availability derives from this block (reference/decision-menu.md ## Availability)
|
|
67
|
+
- status_checks: [ { name, kind, status, conclusion, required, url, workflowName } ] # kind: check_run | status_context | unreadable, from the rollup entry's __typename; evidence semantics: Section B Evidence resolution; workflowName from the CheckRun rollup entry (absent on StatusContext)
|
|
68
68
|
- actions_runs: [ { databaseId, status, conclusion, workflowName, url } ] # from the fixed gh run list call on the assessed head
|
|
69
69
|
- comments: { inline[ { id, updated_at, user_type, body, ... } ], top_level[ { id, updated_at, user_type, body, ... } ], review_threads[]? } # retain REST body for source-review deltas; C# identity diffs on id/updated_at, Section C gates placeholder detection on user_type
|
|
70
70
|
- issue_ref: <ref> | null
|
|
@@ -99,6 +99,8 @@ a null `headRepository` (deleted fork) gives `unreadable`;
|
|
|
99
99
|
`viewerPermission` (null when authenticated as an App -> `unreadable`);
|
|
100
100
|
`can_update_branch` is `viewerCanUpdateBranch`.
|
|
101
101
|
|
|
102
|
+
That `mergeStateStatus` reflects the viewer's own standing is an observation (an exempt viewer reads `UNSTABLE` on a review-required PR), not a documented guarantee; GitHub documents `BLOCKED` only as "the merge is blocked" with no per-rule attribution, so the gate never names which rule blocks. A non-`BLOCKED` value never approves a merge, it only leaves the `merge` row available to the preconditions (`reference/post-selection-loop.md`).
|
|
103
|
+
|
|
102
104
|
## Section B - Verifier
|
|
103
105
|
|
|
104
106
|
Resolves the verification evidence per the table below - green exact-head CI is
|
|
@@ -119,6 +121,7 @@ command runs. Inputs come from Section A's fixed `gh pr view` and `gh run list`
|
|
|
119
121
|
completed check with a missing/unreadable conclusion cannot satisfy
|
|
120
122
|
(fail-safe). A StatusContext's `state` maps `SUCCESS` to success,
|
|
121
123
|
`FAILURE`/`ERROR` to blocking, `PENDING`/`EXPECTED` to pending.
|
|
124
|
+
- **Binding classification**: every pending check in the resolved set is classified once, here, as `binding` or `not binding`. `binding` when `merge_state_status` is `BLOCKED`, `UNKNOWN` (after the step-2 re-poll), or `unreadable`, or the pre-menu refresh failed (`reference/post-selection-loop.md` `### Pre-menu refresh` - the cached value is stale, so every pending check binds until a refresh succeeds); or when `kind` is `unreadable`; or when `kind` is `check_run` and its `actions_runs[]` entry (matched by the run id parsed from its `detailsUrl`, as the Held run row does) has `status` other than `completed` - `queued`, `in_progress`, `waiting`, `requested`, `pending`, or unreadable - or has no matching entry (an external CheckRun app, or an unparsable `detailsUrl`: no completed-run evidence exists, so it fails closed). `not binding` otherwise: a pending `status_context`, or a pending `check_run` whose matched run is `completed`, while `merge_state_status` is any other value. Every consumer below reads "pending" as "pending and binding"; a not-binding pending check withholds nothing and prints only under `show evidence` as `<name> pending - not binding this viewer (merge_state_status <value>)`. Trade-off, stated: a pending commit status posted by an external CI (Buildkite, Jenkins, CircleCI) is a `status_context` with no run linkage, so for a viewer it does not bind it is not waited for; a consumer whose external CI matters makes it a required check, which binds through `BLOCKED`.
|
|
122
125
|
- **Reviewer-check exception**: claude-code-action's sticky mode runs on
|
|
123
126
|
`pull_request` events, so its job is also a check run on the head. A
|
|
124
127
|
resolved-set check whose run id (from its `url` / `detailsUrl`) equals a
|
|
@@ -142,14 +145,14 @@ On the first pass the orchestrator re-runs Section A's fixed `gh pr view` and `g
|
|
|
142
145
|
| Opt-out | `local verification: always` in `## PR gate` | Run local command unconditionally; a Failed-CI block below still applies independently | Local; `local run: local verification: always` |
|
|
143
146
|
| Failed CI | Any blocking conclusion in the resolved set | Blocks: mints a `P#` (any resolved-set failure, required or not) and names the check in the evidence block; the menu re-renders with `fix` recommended only when a drafted payload exists for that failure, else per the overlays. A green local run never overrides it. Only the human `ci-infrastructure-broken` disposition - a pick, never a trigger - runs the fallback local run; merge stays withheld until that run is green | The disposition; plus the fallback run's result when the disposition triggered one |
|
|
144
147
|
| Held run | an `action_required` rollup CheckRun whose run matches an `actions_runs` entry, or an `actions_runs` entry with `action_required` and no rollup entry (a rollup CheckRun matches the `actions_runs` entry whose `databaseId` equals the run id parsed from the CheckRun's `detailsUrl`) | Pending: render `approve workflow run` when available - `viewer_permission` in `WRITE`/`MAINTAIN`/`ADMIN`, the rule `reference/decision-menu.md` `## Availability` owns; when the row is not available, or it was picked and the API returned 403 or 404, run the local command at once; the fresh Section B helper receives the resolved row `held run not approvable` in its task; an approved run that completes is re-resolved per `reference/fix-wave.md` `## Evidence after push`, which dispatches the claim-check-only helper | `local run: held run not approvable` when it fired, else n/a (waiting) |
|
|
145
|
-
| Pending | >=1 pending check, no blocking conclusion, and the poll window (first pass or after a push) elapsed | the `verification evidence pending` overlay renders (`reference/decision-menu.md` `## Overlays`); `wait` polls another `timeout minutes`; never a local run, never an evidence-less merge; when a later re-resolve moves this head to CI-sufficient, the orchestrator dispatches a fresh Section B helper in claim-check-only mode (no local run) so every material claim gets a disposition | n/a (waiting) |
|
|
146
|
-
| CI-sufficient | >=1 `success`, zero blocking, zero pending | Skip local run | CI claim: check name(s), conclusion, assessed SHA, run URL |
|
|
147
|
-
| Fallback | No conclusive check: no checks on the assessed head, or only inert / fail-safe conclusions, after `timeout minutes` | Run local command (protocol below) | Local command + `log_path`; `local run: no conclusive check` |
|
|
148
|
+
| Pending | >=1 **binding** pending check, no blocking conclusion, and the poll window (first pass or after a push) elapsed | the `verification evidence pending` overlay renders (`reference/decision-menu.md` `## Overlays`); `wait` polls another `timeout minutes`; never a local run, never an evidence-less merge; when a later re-resolve moves this head to CI-sufficient, the orchestrator dispatches a fresh Section B helper in claim-check-only mode (no local run) so every material claim gets a disposition | n/a (waiting) |
|
|
149
|
+
| CI-sufficient | >=1 `success`, zero blocking, zero binding pending | Skip local run | CI claim: check name(s), conclusion, assessed SHA, run URL |
|
|
150
|
+
| Fallback | No conclusive check: no checks on the assessed head, or only inert / fail-safe conclusions or only not-binding pending checks, after `timeout minutes` | Run local command (protocol below) | Local command + `log_path`; `local run: no conclusive check` |
|
|
148
151
|
| Fix wave | A fix-wave push by this gate (not the step-2b sync push) | Poll the pushed head per `reference/fix-wave.md` `## Evidence after push`, then resolve this table on the normalized set | The resolved row's evidence for the pushed SHA |
|
|
149
152
|
| Stale head | Head advances since evidence was resolved (another actor's push); fires across runs - a single gather is same-head by construction | All prior evidence (CI or local) is stale; re-resolve this table for the new head before merge is offered | Fresh evidence for the new head |
|
|
150
153
|
|
|
151
154
|
CI-sufficient predicate, stated once (the rows implement exactly this): **>=1
|
|
152
|
-
completed `success`, zero blocking conclusions, zero pending checks, no
|
|
155
|
+
completed `success`, zero blocking conclusions, zero **binding** pending checks, no
|
|
153
156
|
opt-out.** The evidence decision ("run local?") and the merge decision ("can
|
|
154
157
|
this merge?") are separate consumers of the same `status_checks` data. A local
|
|
155
158
|
run fires on exactly three triggers, each recorded in the evidence block:
|