gatekeep-pr ยท diff
git:20260918.8724376 to git:20260922.c3a7b2e
54 added, 437 removed. Audit A to A.
---
name: gatekeep-pr
description: Use when gating a pull request before it merges - whether you authored it or are reviewing someone else's work. Consent-gated - verification is read-only, every externally visible mutation (fix commits, pushes, reviews, merges) waits for explicit selection.
disable-model-invocation: true
argument-hint: "<pr> [issue-ref] (e.g. 123, or 123 gh-45)"
---
# gatekeep-pr
Verify, don't trust. A PR description is a claim, not proof: over-claimed coverage,
hallucinated references, and "tests pass" that were never rerun are the normal case,
- not the exception - especially on generated code. This skill gathers evidence, accepts green CI on the exact assessed head as
- verification evidence (running the project's own verification command only as
- the fallback), reviews the diff against a rubric, and
- presents a deterministic, authorship-aware menu. Authorship sets which row carries
+ not the exception - especially on generated code. Gather evidence, accept green CI on
+ the exact assessed head as verification evidence (run the project's own verification
+ command only as the fallback), review the diff against a rubric, and present a
+ deterministic, authorship-aware menu. Authorship sets which row carries
`[recommended]`; it never changes which rows are offered.
- **Consent gate.** The only actions this skill performs before you pick a menu row are:
+ **Consent gate.** The only actions performed before a menu row is picked are:
read-only gathering, provisioning the worktree, and applying uncommitted, worktree-local
doc-drift fixes discovered as a blocking finding. Every other action - code fixes,
- pushes, reviews, comments, merges - happens only on your explicit selection.
+ pushes, reviews, comments, merges - happens only on explicit selection.
**Residual risk.** Running the verification command executes PR code with the
- operator's ambient credentials. There is no sandbox. Only run this skill against PRs
+ operator's ambient credentials. There is no sandbox. Run this skill only against PRs
you are willing to execute.
## Arguments
- PR number or URL. If omitted: `gh pr view --json number,url` on the current branch;
no PR found there -> STOP and report.
- Optional issue reference. If omitted: infer from `closingIssuesReferences`, then
branch name, PR title, body, or commits. None found -> judge the PR against its
stated intent only; never invent acceptance criteria.
## Configuration resolution
- Applied per concern, first match wins, evaluated unconditionally - never delegated to
+ Apply per concern, first match wins, evaluated unconditionally - never delegated to
a wrapper skill:
- 1. **Repo root `REVIEW.md`** (rubric concerns only). Always wins over the shipped
+ 1. **Repo root `REVIEW.md`** (rubric concerns only). Wins over the shipped
baseline and reviewer-persona defaults on any conflict.
2. **Gauntlet overrides file** (3-location discovery, first found wins): the
`## PR gate` section (verification command, `timeout minutes`, `requires credentials`, issue
fetch, worktree wrapper, merge policy, `local verification`, `ci checks`). An existing `## verification-before-completion`
section is an accepted equivalent source for the verification command.
3. **Repo documentation** - an explicitly documented command or tool (e.g. `AGENTS.md`'s
canonical test entrypoint, a documented worktree wrapper, a documented tracker CLI,
or documented merge policy/branch rules). Reading documentation is not inference.
- Discovery-only: consumers are never told to add gatekeep-pr configuration here.
- 4. **Ask the user.** Never guessed from lockfiles, file heuristics, or vibes.
+ Discovery-only: never tell consumers to add gatekeep-pr configuration here.
+ 4. **Ask the user.** Never guess from lockfiles, file heuristics, or vibes.
The `## PR gate` overrides schema (all keys optional except the verification command,
which is required unless documented elsewhere):
```markdown
## PR gate
- verification command: <command> # required unless documented elsewhere
- timeout minutes: 15 # optional; default 15
- requires credentials: false # optional; true => skill reports "not run" as missing evidence
- local verification: always # optional; default (absent) = CI-first; "always" forces the local run even when exact-head CI is green
- ci checks: <comma-separated check names> # optional; narrows which checks count as evidence; absent = all checks on the assessed head
- worktree wrapper: <command> # optional
- issue fetch: <command with <ref> placeholder> # optional, replaces gh issue view
- merge policy: squash | merge-commit # optional
```
**Thin-wrapper contract.** A consumer wrapper skill is a pure proxy: trigger phrases
plus "follow `/skill:gatekeep-pr`" - zero configuration data. All customization lives
in the repo's `REVIEW.md` (rubric) and the gauntlet overrides file's `## PR gate`
section (everything else); anything a wrapper carries beyond trigger phrases is
misplaced and belongs in one of those two homes instead.
- All of the above is read from the **merge-base of the PR's base branch**, never from
+ Read the ladder from the **merge-base of the PR's base branch**, never from
the PR's head tree - a PR cannot weaken its own rubric or swap the command that will
gate it. Recipe: `MB=$(git merge-base origin/<baseRefName> <headRefOid>)`, then for
each ladder source `git show "$MB:<path>"` (e.g. `git show "$MB:REVIEW.md"`,
`git show "$MB:AGENTS.md"`). A plain cwd read (`cat REVIEW.md`, reading the file open
in the PR worktree) is invalid for any ladder source - it reads the PR's head, exactly
what this rule forbids - even when the assessment happens to run from inside the PR's
worktree. Exception: if the PR itself changes `REVIEW.md` or the overrides file, that
diff is review subject matter, surfaced as a finding - it is not applied to this run's
configuration.
## Progress tracking
Use `plan_tracker`, never `phase_tracker`. Init with the first four stages:
`gather`, `provision worktree`, `resolve evidence`, `claim-check`. While
`claim-check` is `in_progress`, `add` one task per material claim as the
Verifier enumerates them and record each verdict: a matched claim ->
`complete`, a contradicted claim -> `failed` (shown crossed, error color).
Once every claim is terminal and `claim-check` is closed, `add` `review` and
- `consent menu` and continue. Stages are never inited ahead of the claims:
- the tracker rejects a verdict recorded behind a still-pending stage. A
+ `consent menu` and continue. Never init stages ahead of the claims: the
+ tracker rejects a verdict recorded behind a still-pending stage. A
failed stage or claim stays `failed` while the skill stops at the menu -
never marked complete to move on. On a harness without the `plan_tracker`
tool: fall back to a plain checklist (or skip if none is available);
functionality is unchanged either way.
- ## Assessment
-
- Four phases, run in order, read-only through Phase 3:
-
- **Phase 1 - Gather.** Run verification-brief.md Section A in full: the fixed `gh`
- command set (`gh pr view`, `gh api user`, `gh pr diff`, both paginated comment
- endpoints, review threads, issue fetch, `git worktree list --porcelain` for
- discovery only), producing the normative gather digest.
-
- **Phase 2 - Provision worktree** (the orchestrator's mutation - a state machine):
-
- - A worktree already exists on the expected branch (`headRefName` for in-repo PRs,
- a fork-local `pr-<N>` branch for fork PRs), at any path -> reuse it unconditionally.
- In-repo PRs: `git fetch origin` + `git pull --ff-only` (the local branch tracks
- `origin/<headRefName>`). Fork PRs: the local `pr-<N>` branch has no upstream, so
- sync with `git fetch origin pull/<N>/head` + `git merge --ff-only FETCH_HEAD`
- instead. Either way, on divergence, dirt, or local-only commits -> STOP and surface.
- Never force, never create a duplicate.
- - The default path `.worktrees/pr-<N>` exists but holds a different branch -> STOP
- and surface; never repurpose.
- - Nothing exists -> create at `.worktrees/pr-<N>` (an overrides worktree wrapper may
- relocate it), following `using-git-worktrees` conventions (gitignore-first). In-repo
- PRs: `git fetch origin` + `git worktree add .worktrees/pr-<N> <headRefName>`. Fork
- PRs: `git fetch origin pull/<N>/head:pr-<N>` first, then add on that local branch.
- Verify post-checkout that HEAD == the digest's `headRefOid`.
-
- Record create-vs-reuse; it drives the non-merge teardown rule below.
-
- After provisioning, re-poll `mergeable` once (`gh pr view --json mergeable`) if Section
- A reported `UNKNOWN` - still `UNKNOWN` after this single re-poll is treated as not
- merge-ready and surfaced (see the merge preconditions below).
-
- **Phase 3 - Verify, then Review** (sequential, same worktree - deliberate: the
- verification command may write to the tree while the Reviewer reads it):
-
- - Run verification-brief.md Section B: resolve the verification evidence per its
- Evidence resolution table (green exact-head CI is the default evidence); run
- the resolved verification command only when the table selects a fallback or
- opt-out row, under its safety contract - self-contained and non-interactive (no prompts; run under a
- non-interactive environment), bounded by a timeout (default 15 minutes, `timeout
- minutes` override) via the first available mechanism: the harness's own bash
- timeout parameter, else the `timeout`/`gtimeout` CLI when installed, else a
- background-and-kill fallback - then material-claim checking against the PR body.
- After a local run, the orchestrator asserts tracked-only cleanliness (`git status --porcelain
- --untracked-files=no` empty, equivalently `git diff --quiet && git diff --cached
- --quiet`; HEAD unmoved) - untracked gate artifacts, including the Verifier's
- `log_path`, are expected and do not fail this check as long as `log_path` sits
- under a gitignored path inside the worktree. Any tracked change invalidates the
- run - re-provision and re-run once.
- - Run verification-brief.md Section C: review the source behind the diff against the
- merged rubric (shipped `review-baseline.md` overlaid by base-branch `REVIEW.md`),
- triage existing comments. The Reviewer emits its native output format only - AC
- coverage is not part of its contract.
-
- **Phase 4 - Integrate** (orchestrator):
-
- - **Provenance:** `worktree_root` matches the provisioned path, every `run_cwd` is
- inside it, `head_sha` matches the digest's `headRefOid`. On mismatch, re-fetch the
- PR head once and re-sync + re-run Phase 3 if it advanced; a second mismatch, or any
- path mismatch, is treated as missing evidence - not merge-ready. The claim stated in output names its source. Local path: precisely "reproduced
- locally under the project's documented verification command" - nothing
- stronger; never worded to imply a deployed, staging, or CI environment.
- CI path (`source: ci`): precisely
- `verified by CI: <check name(s)> succeeded on <sha> (run <url>)`
- - never phrased as local reproduction, never implying the local command ran;
- `<sha>` is the assessed `headRefOid`, `<url>` degrades to `unavailable` when
- absent. Provenance checks on `worktree_root`/`run_cwd` bind only to the local
- path.
- - **Telemetry record:** run `node <bin>/gauntlet-telemetry-salvage.mjs --worktree
- <provisioned path> --base origin/<baseRefName> --check` (`<bin>` = `<directory of
- this skill's SKILL.md>/../../bin`). Detect-only: it never mutates. `present` / `no
- telemetry run` / `never written` land in `## Evidence` as one line each. `stripped
- <path> in <sha>` mints a blocking `P#` (`source_ref`:
- `gauntlet-telemetry-salvage`) whose drafted fix is "run the salvage without
- `--check`" - the spec and its telemetry record are deliverables that ship in the
- squash. On a cell with no push row (fork overlay, report-only states) the same
- finding is a non-blocking follow-up instead: the record stays recoverable from the
- PR head ref after merge, and blocking would stop a ship the gate cannot repair.
- `unfinished <path>` (record still `in_progress` with no ship phase) also lands in `## Evidence` as one line and is non-blocking: pre-landing `in_progress` is normal, and the merge course's salvage run stamps it.
- - **Evidence:** On the CI path, list each satisfying check's name, conclusion, assessed SHA, and run URL - there is no command or raw_tail to paste. On the local path, paste each run's `command` and `raw_tail` verbatim, fenced - never
- paraphrased. Any authored summary is labeled as a summary and never substitutes for
- `raw_tail`.
- - **Severity translation:** Critical -> blocking, Moderate -> blocking, Minor ->
- non-blocking follow-up. A repo `REVIEW.md` severity mapping overrides this; any
- severity it names but does not map is fail-safe **blocking**, noted in the output.
- - **AC coverage:** the orchestrator computes `met` / `partial` / `missing` per
- acceptance criterion from the issue's ACs, the diff, and the Reviewer's findings -
- it is an integration product, not raw persona output. Only `met` is merge-ready;
- `partial` or `missing` is blocking. Skipped entirely when no issue is linked.
- - **Claims:** a failed local gate is a hard merge failure. A `contradicted` material
- claim is a blocking finding. An `unverifiable-pre-merge` claim used as merge proof
- (appears in the PR body's evidence/result/test-plan content) is blocking; stated as
- an explicit post-merge observation instead, it is a non-blocking follow-up.
- - **CI checks:** any blocking conclusion in the resolved check set (required or
- not - see the brief's Evidence resolution table) withholds merge from every
- pre-composed course until the user explicitly dispositions it, and mints a
- `P#`. Three dispositions: **flaky** (proceed via the custom row), **real** (it
- blocks until green), **CI-infrastructure-broken** (the checks themselves are
- untrustworthy: triggers the fallback local run, and merge stays withheld until
- that fallback produces green evidence). A **pending** required check (still
- running - the normal case, not a defect) is **wait-until-green, not
- dispositionable**: it mints no `P#`, is never dispositioned, and the withhold
- auto-lifts the moment it turns green - or, if it instead fails, converts into
- an undispositioned failing check with its own `P#` at that point. While
- pending, the report notes it under Evidence and every merge course simply does
- not render (a pending-only PR is not a blocking verdict - findings groups can
- all read "None" - the recommended course falls to `stop` or `review-comment`,
- never a merge course, until it resolves). The evidence decision is
- independent: a green check elsewhere in the resolved set still satisfies
- verification evidence while a pending required check withholds merge. The
- CI-sufficient path changes no consent surface: still read-only, no auto-merge,
- no posting, no menu change beyond the third disposition.
- - **Doc drift:** when the review finds committed doc drift as a **blocking** finding,
- the orchestrator applies the doc fixes itself, in the provisioned worktree (created
- or reused), as part of assessment - real edits, uncommitted, worktree-local. The
- result is presented in `## Findings`, the edits themselves under
- `## Drafted fixes / review`. Pushing them is a separate, later menu selection.
- Follow-ups alone never trigger doc fixes - only blocking drift does.
-
- ## Inline-first execution
-
- > This section is an optional optimization. Delete it and the rest of the skill still
- > works: the orchestrator can run every phase above itself, inline, with no subagent
- > system.
-
- The inline path is primary: the orchestrator runs the brief's sections itself, in
- order, self-contained. When pi-cohort is available, delegation is an optimization
- layered on top, never a hard dependency:
-
- - **Gatherer** -> `scout` builtin, as a prior sync run producing the gather digest.
- - **Verifier** -> `worker` builtin, dispatched with the report-only constraint
- prepended to its task ("report only - do not edit, fix, or commit anything").
- - **Reviewer** -> the existing `code-reviewer` agent, emitting its native output
- format (never overridden at call time).
+ ## Assess
- Verifier and Reviewer share the provisioned worktree via `cwd`, dispatched
- **sequentially** (Verify before Review, per Phase 3) - never `worktree: true`, which
- would provision a separate isolated worktree and break the shared-tree contract this
- skill depends on. A subagent that fails, or violates its section's output schema, is
- re-dispatched once demanding the schema; a second failure means that section runs
- inline instead.
+ Run four phases in order - gather, provision worktree, verify then review, integrate.
+ Read `reference/assessment.md` and `reference/findings.md` now.
## Verdict
Three states: **blocking findings** (failed gate, contradicted material claim, a
merge-proof unverifiable claim, `partial`/`missing` AC coverage, scope creep when an
issue is linked, committed doc drift, anything the merged rubric maps to blocking),
**follow-ups only** (never gate merge), or **clean**.
- **Merge preconditions** (all must hold): gate green with every blocking finding fixed,
- not deferred; verification evidence present per the brief's Evidence resolution table (a CI claim or a green local run - a table-sanctioned CI skip is evidence, not missing "not run" evidence; result: not run blocks only when the table required a fallback run that didn't happen); `mergeable == MERGEABLE` (`UNKNOWN` after the one post-provision
- re-poll withholds merge, same as `CONFLICTING`); no undispositioned failing check in
- the resolved set, no pending required check; evidence pasted with clean provenance; worktree clean and synced with the remote
- head (fixes pushed first); explicit selection with a head compare-and-swap that
- passes. A merge selection while any precondition fails is refused, naming the failing
- precondition, and the menu re-renders - never a dead end, never a silent merge. Merge
- always executes as `gh pr merge --match-head-commit <assessed-sha>`; push and merge
- are never bundled into one selection, with one scoped exception: the selected merge
- course first runs `node <bin>/gauntlet-telemetry-salvage.mjs --worktree <provisioned
- path> --base origin/<baseRefName>` (no `--check`). `present` -> merge as-is. `restored
- <path> from <sha>` -> push that single `telemetry: restore` commit as part of this
- course, re-fetch `headRefOid`, and pass the new SHA to `--match-head-commit`. A line ending `(marked shipped)` (`present` or `restored`) is handled the same way: push that single `telemetry:` commit, re-fetch `headRefOid`, pass the new SHA. `restore
- failed` -> merge proceeds, the reason is printed, and the follow-up names recovery
- from the PR head ref.
-
- **Consent menu** (deterministic - this table is the golden-scenario oracle):
+ **Merge preconditions** (all must hold):
- | Author | State | Offered rows (first = `[recommended]`) |
- |---|---|---|
- | you | clean / follow-ups only | merge (squash); merge (merge-commit); do not merge (leave it); post no-blockers comment |
- | 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 |
- | someone else | clean / follow-ups only | approve; merge (squash, offered-unrecommended); post no-blockers comment |
- | someone else | blocking | post request-changes review; apply fixes on their branch (courtesy option 2); reply to existing threads; post comment |
- | bot author | any | someone-else's rows for the same state, review actions recommended |
- | fork (any) | any | post review (request-changes / comment / approve per state) - push and merge rows absent |
- | any | draft PR | assessment rows only; merge and approve rows absent until ready-for-review |
- | any | merged / closed | report-only; no mutation rows |
+ - gate green with every blocking finding fixed, not deferred
+ - verification evidence present per the brief's Evidence resolution table (a CI claim
+ or a green local run - a table-sanctioned CI skip is evidence, not missing;
+ "not run" blocks only when the table required a fallback run that didn't happen)
+ - `mergeable == MERGEABLE` (`UNKNOWN` after the one post-provision re-poll withholds
+ merge, same as `CONFLICTING`)
+ - no undispositioned failing check in the resolved set, no pending required check
+ - evidence pasted with clean provenance
+ - worktree clean and synced with the remote head (fixes pushed first)
+ - explicit selection with a head compare-and-swap that passes
- The consent table above remains the single oracle for what may be offered; the
- Decision rendering section defines how its rows render as actions and numbered
- courses. Rows GitHub would refuse (branch protection, missing permissions,
- `viewerPermission` too low) are listed as unavailable with the reason. Approving
- your own PR is not offered. Nothing executes until explicit selection.
+ Define `<bin>` = `<directory of this skill's SKILL.md>/../../bin`. Merge execution
+ per `reference/post-selection-loop.md` `### Merge course`.
- ## Output
+ ## Report
The rendered report is terse by design: deciding factor, evidence lines, ID'd
findings, menu. No restating diffs, no narration, no recap prose.
```markdown
## Outcome
<one line + the deciding factor>
## Evidence
<CI path: satisfying check name(s)/conclusion/sha/url; local path: verbatim command + raw_tail per run; claims checked; resolved-set check dispositions>
## Findings (blocking)
Blocking findings (P#):
P1. **<source_ref>** - <defect>. Fix: <concrete change> | Action: <disposition/what unblocks>. [code | test | spec | security | performance | quality]
Requirement/doc drift (linked issue, committed doc drift, or spec conflict):
L1. <doc-drift | spec-conflict | outdated-AC | missing-behavior> -> <action>
## Comment-thread replies (existing discussion - verdict-neutral)
C1. <thread ref> -> <drafted reply> (already-addressed | reasonable | judgment-call)
## Non-blocking follow-ups
F1. **<source_ref>** - <action>. Owner: <pr-author | tracker | human>
## Decision
- <action vocabulary + numbered courses - see Decision rendering>
+ <action vocabulary + numbered courses>
## Drafted fixes / review
<payloads, each keyed by its finding ID>
```
- **ID rules:**
-
- - `<source_ref>` is a `file:line` where one exists, else the disputed thing (a
- quoted PR-body claim, a failing gate command, a required check name).
- - **Precedence:** Phase 4 and the merged rubric decide blocking vs. follow-up (the
- severity translation, AC coverage, claims, and required-check rules above); this
- section only chooses **which namespace** (`P#` / `L#` / `F#`) renders that
- decision. Category tags and the triage bar below never override an upstream
- blocking classification - a Phase-4 Moderate is always blocking (`P#` or `L#`
- per Total mapping), never demoted to `F#` by tag or by judgment call.
- - **Total mapping:** every blocking element of the Verdict maps to a `P#` or `L#` -
- a blocking verdict with "None" in both groups is a rendering bug. Concretely:
- failed gate -> `P#` `[test]` referencing the gate command; contradicted or
- merge-proof-unverifiable material claim -> `P#` `[spec]` referencing the claim;
- scope creep with a linked issue -> `L#` `spec-conflict`; committed doc drift ->
- `L#` `doc-drift`; `partial` AC coverage -> `L#` `outdated-AC`; `missing` AC
- coverage -> `L#` `missing-behavior`. `L#` covers exactly the drift the Verdict
- already blocks on (committed doc drift, AC coverage, spec conflict); it widens
- nothing. Code-level spec bugs (the diff contradicts the spec) are `P#` `[spec]`;
- requirement/doc mismatches (the spec or docs are stale relative to intent) are
- `L#`.
- - **Failing checks close by disposition, not by fix:** an undispositioned failing check in the resolved set
- is `P#` `[test]` referencing the check name; it is never a target
- of a worktree `fix`. The user's Phase-4 disposition annotates the same ID rather
- than closing it outright: dispositioned **flaky** -> annotate
- `(dispositioned: flaky)`; this annotation excepts the `P#` from the unfixed-blocker
- set - it no longer counts against "every `P#` blocks" or the merge precondition
- "every blocking finding fixed", and the merge path is Phase 4's explicit flaky
- disposition via the custom row. Dispositioned **real** -> annotate
- `(dispositioned: real)` and the `P#` keeps blocking until the check is green.
- Dispositioned **CI-infrastructure-broken** -> annotate
- `(dispositioned: ci-infrastructure-broken)`; the fallback local run executes,
- and the `P#` keeps blocking until that fallback is green.
- - **Severity is decided at triage, not by the category tag:** a finding lands in
- `P#` only when it must be fixed before merge (correctness, security, material
- performance trap, a convention the repo enforces); improvements that don't
- change merge correctness are `F#`, whatever their category. `[quality]` on a
- `P#` is a category, never a downgrade - every `P#` blocks. This triage bar governs
- findings the orchestrator originates itself; it never re-triages a classification
- Phase 4 already made (see Precedence above).
- - `C#` replies are verdict-neutral drafts: they never block and never gate merge;
- nothing posts until selected.
- - `F#` items carry an owner so follow-ups don't evaporate; when no tracker tool
- resolved, the report itself is their durable home.
- - **IDs are append-only for the run's lifetime:** minted at first assessment,
- never renumbered, never reused. A resolved finding keeps its ID annotated
- `(fixed in <sha>)`; later rounds continue each namespace's sequence.
- - Empty groups say "None".
-
- "Drafted fixes / review" holds, per finding ID, the concrete edit (for `fix`), the
- reviewed doc-drift edit (for `push-docs`, keyed to its `L#`), or the reply text
- (for `reply`) - each keyed to its finding ID, one selection mapping 1:1 to its
- payload. A posted review body is not itself a finding: it is composed at post time
- from the ID'd `P#`/`L#` findings being addressed - one summary sentence, then the
- numbered findings, ending on the fix or asked action - and occupies its own
- non-finding slot of this section.
-
- ## Decision rendering
-
- `## Decision` has two parts: the action vocabulary, then the numbered courses.
-
- **Action vocabulary** (bare verbs; availability constraints inline):
-
- ```markdown
- Actions (compose freely in the custom row):
- fix <P#s|all> apply blocking fixes in worktree, re-run gate, push (in-repo PRs only)
- push-docs push already-applied doc-drift edits (only when uncommitted
- reviewed doc edits exist
- in the worktree)
- merge-squash | merge-commit (preconditions per Verdict;
- never bundled with a push,
- except the telemetry: restore commit)
- request-changes | review-comment | approve (approve: never own PR)
- reply <C#s> post drafted thread replies
- tracker <act> tracker action (only when a tracker tool resolved)
- stop leave the PR as-is / report-only exit
- ```
-
- A `+ tracker <act>` suffix is available on any mutation course when a tracker tool
- resolved.
-
- **Selection grammar:** ID sets accept `all`, ranges (`P1-P4`), comma lists
- (`P1,P3`), and exclusions (`all but P2`).
-
- **Numbered courses** - a normative rendering of the consent table (never a second
- offer source): per author x state cell, exactly one `[recommended]` course first,
- the custom row always last. Courses are **atomic across pushes**: no course,
- pre-composed or custom, bundles a push-producing action (`fix`, `push-docs`) with
- `merge-*`; after a fix wave the menu re-renders with merge as row 1.
-
- | Author | State | Courses (first = `[recommended]`) |
- |---|---|---|
- | you | clean / follow-ups only | 1. merge-squash; 2. merge-commit; 3. stop; 4. review-comment (post no-blockers note) |
- | 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) is not rendered: push-docs becomes first when doc edits exist, else stop is first |
- | you | blocking, post-fix re-render (gate green, preconditions hold) | 1. merge-squash; 2. merge-commit; 3. stop; 4. review-comment |
- | someone else | clean / follow-ups only | 1. approve; 2. merge-squash (offered-unrecommended); 3. review-comment (no-blockers note) |
- | someone else | blocking | 1. request-changes; 2. fix all (courtesy, their branch - omitted when nothing is worktree-fixable); 3. reply <C#s> (omitted when the `C#` group is None); 4. review-comment |
- | bot author | any | someone-else's rows for the same state; review actions recommended |
- | any | draft | 1. request-changes / review-comment / reply <C#s> (omit the reply course when the `C#` group is None) / stop - `[recommended]` follows the same authorship rule as the non-draft cells, **except** on your own draft PR `request-changes` is never recommended (you cannot request changes on your own PR any more than you can approve it); the fallback recommendation there is `review-comment` when findings exist, else `stop`. Custom present but cannot compose `merge-*`/`approve`/`fix`/`push-docs` until ready-for-review |
- | 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 |
-
- **Fork overlay:** the consent-table fork row renders as an overlay on the authorship
- cells (push/merge/fix absent; approve also dropped when the viewer authored the PR) -
- it is not a distinct authorship cell. It overlays whichever authorship row above
- applies (you vs. someone else), removing `fix`, `push-docs`, and `merge-*` (never
- available on a fork). When you authored the fork PR, `approve` is
- also dropped (never offered on your own PR) - fork|you|clean renders
- `review-comment`/`stop` only; fork|you|blocking renders
- `request-changes`/`review-comment`/`stop` (the someone-else courtesy fix-on-their-
- branch course is also absent, since it is your own PR). A fork PR authored by someone
- else uses the someone-else cells above with `fix`/`push-docs`/`merge-*` removed.
-
- **CI-check gate on merge courses:** an undispositioned failing check in the resolved
- set, or a pending **required** check, withholds every pre-composed course containing `merge-*` (per the
- Verdict merge preconditions) - none render, whatever the author/state cell says. A
- pending check mints no `P#` and is wait-until-green, not dispositionable (see Phase
- 4); a failing one mints a `P#` and takes a disposition (flaky / real / CI-infrastructure-broken). A **flaky** disposition does
- not restore merge to a pre-composed course; merge proceeds only via the custom row
- naming the disposition explicitly. A **real** disposition, or an unresolved pending
- check, keeps every merge course withheld until the check is green; a **CI-infrastructure-broken** disposition keeps them withheld until the triggered fallback run is green - a pending-only
- render is not itself a blocking verdict (findings groups may all read "None"); the
- recommended course falls to `stop` or `review-comment` in the meantime. This never
- falls through to the clean cell's recommended `merge-squash` - a failing resolved-set
- check or a pending required check means the PR is not in the clean state to begin with.
-
- Rows a cell offers but GitHub would refuse (branch protection, missing permission)
- render listed-but-unavailable with the reason. Zero mutation courses is a legal
- render (merged/closed) - the menu still appears, carrying findings and `stop`.
-
- Example render (golden fixture 1 - own PR, blocking findings including committed doc
- drift, so uncommitted reviewed doc edits exist):
-
- ```markdown
- Pick one:
- 1. fix all (P1-P10) + push-docs [recommended]
- 2. push-docs (docs only, hold code fixes)
- 3. stop (leave as-is)
- 4. review-comment (post findings, act later)
- 5. Custom - compose: e.g. "fix P1-P8,P10 + push-docs" or "reply C1 + tracker comment"
- ```
-
- Golden fixture 2 - the post-fix re-render after course 1's gate re-run passes:
-
- ```markdown
- Pick one:
- 1. merge-squash [recommended]
- 2. merge-commit
- 3. stop (leave as-is)
- 4. review-comment
- 5. Custom
- ```
-
- ## Post-selection loop
-
- The menu is a state machine, not a one-shot report:
-
- 1. **Compare-and-swap before every external write:** re-fetch `headRefOid`, `state`,
- `mergeable`. Any change since assessment invalidates the current state - re-sync
- the worktree, re-run Phase 3, re-render the menu. **Exception:** a course's own
- push updates the assessed head to the pushed SHA as part of that course's
- execution - this self-inflicted head move does not invalidate the course; the
- next CAS check runs against the new head on the next external write.
- 2. Execute only the selected course. **Fix wave** (`fix <set>`): first filter the
- selected set to worktree-fixable `P#`s - drop any `P#` closed by disposition
- (an undispositioned failing-check `P#` is never a `fix` target; a **flaky**
- disposition already excepts it) - and route file-less `P#`s (a claim or a gate
- command as `source_ref`, no draft touching a file) to run inline/sequentially,
- never as part of a parallel file-batch.
+ Consent table and courses per `reference/decision-menu.md`.
- For the remaining worktree-fixable set, batch by the **union of files each
- finding's drafted edit in `## Drafted fixes / review` touches** (fallback to
- the `source_ref` file only when a finding has no draft) - findings whose
- drafts share a file share a batch.
+ ## Decide
- **Child contract (same for 1 batch or many):** children are `implementer`
- dispatches - `subagent({ agent: "implementer", context: "fresh", cwd: <PR
- worktree> })`, matching Inline-first execution's persona-naming style; this is
- the pi-cohort optimization path, inline is always valid per that section. The
- orchestrator owns commit, gate, and push - never a child. Every dispatched
- child gets `cwd` = the PR worktree, **`worktree: true` forbidden** (a separate
- isolated worktree breaks the shared-tree contract - see Inline-first
- execution); is **edit-only, no git commands, no verification runs**, and must
- never delete `.pi/gauntlet/telemetry/**` or anything under the configured
- telemetry dir (a fix that "cleans up" the run's telemetry record is a defect in
- the fix - the record is a deliverable); and its task is that batch's `P#` lines
- **plus the drafted edit already keyed to each
- ID** in `## Drafted fixes / review` - the child applies the consented payload,
- it does not re-solve the finding. Below the cutoff (<= 2 worktree-fixable findings), the orchestrator
- applies inline instead of dispatching - the no-cohort path stays available at
- any batch count per Inline-first execution. Above the cutoff, when more than
- one batch results, dispatch the batches' children **in parallel**, all under
- the same contract.
+ Render `## Decision` from the consent table for the PR's author and state. Read
+ `reference/decision-menu.md` now.
- Once every dispatched/inline batch returns, the orchestrator commits the golden
- course as one local commit set - the code fixes plus any already-applied
- reviewed doc edits selected alongside them (one commit, or one per batch
- sequentially; subjects name the fixes) - then re-resolves the evidence for the new head **once** (the brief's stale-head row: prior evidence is stale; the local command executes only on a fallback/opt-out resolution). Before the push, run
- `node <bin>/gauntlet-telemetry-salvage.mjs --worktree <provisioned path> --base
- origin/<baseRefName>` (no `--check`); a `restored` or `(marked shipped)` commit rides the wave's single
- push and the pushed SHA becomes the assessed head under the course's-own-push rule
- in step 1. Print its stdout in the re-rendered report's `## Evidence`. **On
- green**, push **once**; gate and push are per-wave invariants, never per-fix or
- per-batch. **On red**, do not push: leave the commit(s) local, re-render with
- the unresolved `P#`s still open, and warn that unpushed fix commits sit in the
- worktree exactly like unpushed doc edits (see Teardown). Doc fixes (`push-docs`
- alone) follow the same green-gate-then-push rule: stage + commit (subject names
- what is documented), re-run the gate, push only on green. Reviews, replies, and
- tracker actions -> `gh pr review` / `gh api` / the tracker tool, non-interactive,
- with the drafted payload for the selected IDs.
- 3. After any mutation that can change readiness (fix wave pushed, docs pushed, PR
- head moved), re-run the claim-check and Review on the synced worktree: claims
- are re-checked against the new head and findings are re-rendered, but
- the verification command itself is **not** re-executed here - step 2's evidence re-resolution
- already was the wave's one and only gate pass. Re-render the report:
- each selected `P#`/`L#` confirmed resolved is annotated `(fixed in <sha>)`
- under its original ID; unresolved ones stay open unchanged; new findings
- continue the sequence. Merge, if now available, renders as row 1.
- 4. Loop until the user selects merge or an explicit stop/no-action row.
+ ## Act
- **Teardown:** merge success -> tear down the worktree, whether it was reused or
- created (the sync precondition guarantees no local-only work is stranded, and the
- branch is gone remotely). A non-merge stop: offer teardown of a **created** worktree
- (never autonomous; warn if unpushed doc edits would be discarded); a **reused**
- worktree is left as found - if unpushed doc edits **or unpushed fix commits from a
- red-gate hold** remain in it, say so explicitly and let the user choose
- leave-or-discard.
+ The menu is a state machine that loops until merge or an explicit stop. Read
+ `reference/post-selection-loop.md` now.
## Output done-check
Before posting or committing any externally persisted payload - review bodies,
thread replies, commit subjects, tracker comments - re-read it against:
- ASCII only: `-`, `...`, straight quotes.
- No headings or template scaffolding on payloads under ~150 words - bullets and
prose carry short content.
- Review findings are file:line-specific where one exists, else keyed to the
finding's `source_ref`, and end on the fix or asked action, not a recap.
- Empty sections say "None"; never invent content to fill a section.
The check governs external payloads only - the skill's own rendered report keeps
its fixed headings regardless of length. Project rules extend this list via the
overrides file - see Project overrides.
## Red flags - STOP
- - Approving your own PR
- - Any mutation (fix, push, review, merge) without an explicit menu selection
- - Pasting paraphrased evidence instead of verbatim `raw_tail`
- - A provenance mismatch (worktree, `run_cwd`, or `head_sha`) noticed and ignored
- - Merging around an undispositioned blocking finding or failing-check `P#`
- - Reading configuration (rubric, verification command, or ladder sources) from the
- PR's head instead of the base branch's merge-base
- - Renumbering or reusing a finding ID between menu rounds
- - Presenting findings without IDs, a blocking verdict with no `P#`/`L#`, or a
- `## Decision` rendered without its action vocabulary
- - Treating `[quality]` or `[performance]` as a downgrade signal on a `P#` - only
- an explicit Phase-4 flaky disposition excepts a failing-check `P#` from the
- unfixed-blocker set, never a category tag
- - A course (pre-composed or custom) bundling a push-producing action with
- `merge-*`
- - A pre-composed course, or a custom row, composing an action the overlay or the
- cell lists as unavailable
- - Batching a file-less `P#` (a claim or a gate command as `source_ref`, no draft touching a file)
- into a parallel dispatch - a claim `P#` with a drafted file edit is
- worktree-fixable and may batch - dispatching parallel implementers over
- batches that share a file, or letting a fix-wave child run git commands or a
- verification pass in the shared worktree, or dispatching a fix-wave child with
- `worktree: true`
- - A second execution of the verification command, a second push, or pushing fix
- commits after a red gate, within one fix wave - re-running Verify/Review to
- re-confirm claims and annotate IDs (step 3) is not a second gate execution
- - Posting or committing an external payload without the output done-check
+ - Approving your own PR - owner: `reference/decision-menu.md` `## Consent table`
+ - Any mutation (fix, push, review, merge) without an explicit menu selection - owner: **Consent gate** (intro)
+ - Pasting paraphrased evidence instead of verbatim `raw_tail` - owner: `reference/assessment.md` `## Phase 4 - Integrate`
+ - A provenance mismatch (worktree, `run_cwd`, or `head_sha`) noticed and ignored - owner: `reference/assessment.md` `## Phase 4 - Integrate`
+ - Merging around an undispositioned blocking finding or failing-check `P#` - owner: `reference/findings.md` `## Dispositions`
+ - Reading configuration (rubric, verification command, or ladder sources) from the PR's head instead of the base branch's merge-base - owner: `## Configuration resolution`
+ - Renumbering or reusing a finding ID between menu rounds - owner: `reference/findings.md` `## IDs`
+ - Presenting findings without IDs, or a blocking verdict with no `P#`/`L#` - owner: `reference/findings.md` `## IDs`
+ - A `## Decision` rendered without its action vocabulary - owner: `reference/decision-menu.md` `## Actions`
+ - Treating `[quality]` or `[performance]` as a downgrade signal on a `P#` - only an explicit Phase-4 flaky disposition excepts a failing-check `P#` from the unfixed-blocker set, never a category tag - owner: `reference/findings.md` `## Triage`
+ - A course (pre-composed or custom) bundling a push-producing action with `merge-*` - owner: `reference/decision-menu.md` `## Courses`
+ - A pre-composed course, or a custom row, composing an action the overlay or the cell lists as unavailable - owner: `reference/decision-menu.md` `## Fork overlay`
+ - Batching a file-less `P#` (a claim or a gate command as `source_ref`, no draft touching a file) into a parallel dispatch - a claim `P#` with a drafted file edit is worktree-fixable and batches - dispatching parallel implementers over batches that share a file, or letting a fix-wave child run git commands or a verification pass in the shared worktree, or dispatching a fix-wave child with `worktree: true` - owner: `reference/post-selection-loop.md` `### Fix wave`
+ - A second execution of the verification command, a second push, or pushing fix commits after a red gate, within one fix wave - re-running Verify/Review to re-confirm claims and annotate IDs is not a second gate execution - owner: `reference/post-selection-loop.md` `### Fix wave`
+ - Posting or committing an external payload without the output done-check - owner: `## Output done-check`
## Project overrides
If a gauntlet overrides file exists - checked in order: `.pi/gauntlet-overrides.md`, `<repo root>/gauntlet-overrides.md`, `<repo root>/doc/gauntlet-overrides.md`; first found wins - read it. Any sections relevant to this skill - by name match, by topic (routing, verification, worktrees, etc.), or by workflow convention - override or extend the instructions above. Project-local `AGENTS.md` is already in context - check it for project-specific routing tables, service paths, and verification commands. A `## comms style` section in the overrides file extends the output done-check with project rules.