pi-gauntlet 8.4.7 → 8.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## v8.4.9 - 2026-11-11
4
+
5
+ - Gatekeep-pr uses action, final and post menus with the judged recommendation on row 1. Its plain-language report describes the change and scope, explains standing and problems, then recommends a choice; detailed evidence stays behind `show evidence`.
6
+ - Gatekeep-pr preserves the freshness read for replies on approved, open PRs; a moved head skips the reply and retains its body. Set cleanup sharing checks unresolved `<issue>`, `<pr>` and `<merge-sha>` tokens, so an issue-only command remains PR-specific.
7
+ - Gatekeep-pr set mode retires each merged PR after one post-menu pick and collects its unrun steps in the parent's closing message. A shared cleanup item is recommended only on the last merged PR and runs at most once, even when selected earlier.
8
+ - Gatekeep-pr offers merger actions named in the PR or spec as post steps only when merge-base documentation supplies one command. Missing, ambiguous or incomplete commands stay open with their source sentences; PR command text is never executed as authority.
9
+ - Gatekeep-pr asks only once for an invalid post selection. A repeated invalid pick leaves the saved menu waiting without another question or any action.
10
+ - Gatekeep-pr collects terminal follow-ups in one post menu. The recommended combination names its member rows; replies, coverage, cleanup and individual overrides remain separately selectable, and the run ends after the picked steps finish.
11
+ - pi-cohort peer floor raised to 7.7.4.
12
+
13
+ ## v8.4.8 - 2026-11-11
14
+
15
+ - Brainstorming opens with Today, The change, and What I checked in everyday words, followed by one question. Sources stay in the draft; the opening span is capped at 220 words except corrected or unchecked claims and intact quoted ticket rows.
16
+
3
17
  ## v8.4.7 - 2026-11-11
4
18
 
5
19
  ### Fixed
package/README.md CHANGED
@@ -87,17 +87,23 @@ One finish choice spans all legs. PR, draft PR and squash deliver companions in
87
87
 
88
88
  ## Architecture
89
89
 
90
- A `gatekeep-pr` run after your newest submitted, non-dismissed review uses that review's commit as its baseline when it is an ancestor of the assessed head. The reviewer checks your posted items and only the intervening code delta; the report shows `Prior review: <k> of <n> items addressed`. Contract checks still judge the whole head. See the [review task](skills/gatekeep-pr/reference/assessment.md#step-4-review-task).
90
+ A `gatekeep-pr` run after your newest submitted, non-dismissed review uses that review's commit as its baseline when it is an ancestor of the assessed head. The reviewer checks your posted items and only the intervening code delta; the report explains how many items were addressed in plain prose. Contract checks still judge the whole head. See the [review task](skills/gatekeep-pr/reference/assessment.md#step-4-review-task).
91
91
 
92
- `gatekeep-pr` selects one course per run: `assess` posts open blockers as one review (`request-changes` on someone else's PR, `comment` on your own), posts nothing with zero blockers, and performs no repair or sync; `deliver` repairs blockers, syncs, pushes, watches CI, and re-reviews until readiness, an obstacle, or a cap exit. `deliver with nits` stores `deliver-nits` and includes current and later nits. A ready first report skips the course pick and renders the final menu directly. Delivery rebases your own non-bot branch with `--force-with-lease` unless its history contains merge commits; someone else's, bot-authored, and merge-containing heads merge base instead. Final `merge` and `approve` remain separate picks; approval is offered only on someone else's non-draft PR with zero blockers. See the [course loop](skills/gatekeep-pr/reference/post-selection-loop.md) and [menu templates](skills/gatekeep-pr/reference/menus.md). In pi, `/skill:gatekeep-pr 12 15 18 [--rebase [base]]` gates a set of PR numbers or same-repository URLs, with one fresh `pr-gatekeeper` child and one worktree per PR. The parent shows a summary and unchanged per-PR menus in input order, queues every pick until the last menu, then executes the queued work in parallel. The set must fit pi-cohort's `parallel.maxTasks` (default `8`); it is never chunked. With `--rebase`, the parent asks once per set for eligible other-author or unreadable-author heads, excluding no-op heads and heads with merge commits. Multi-PR mode rejects positional issue references and is pi-only. An explicit no-argument invocation on either harness discovers your open PRs in origin, authored by you or directly awaiting your review (not team requests), excludes drafts and the `APPROVED` and `CHANGES_REQUESTED` states, and sorts by additions plus deletions, then PR number, ascending. One match gates directly on the single-PR path; multiple matches enter pi's batch path, while Claude Code stops with the sorted `discovered, smallest first:` list. Every report shows the refreshed viewer `Role:` under `Delivers`, not in the batch summary row. See the [parent-child contract](skills/gatekeep-pr/reference/batch.md).
92
+ `gatekeep-pr` selects one course per run: `assess` posts open blockers as one review (`request-changes` on someone else's PR, `comment` on your own), posts nothing with zero blockers, and performs no repair or sync; `deliver` repairs blockers, syncs, pushes, watches CI, and re-reviews until readiness, an obstacle, or a cap exit. `deliver with nits` stores `deliver-nits` and includes current and later nits. A ready first report skips the course pick and renders the final menu directly. Delivery rebases your own non-bot branch with `--force-with-lease` unless its history contains merge commits; someone else's, bot-authored, and merge-containing heads merge base instead. Final `merge` and `approve` remain separate picks; approval is offered only on someone else's non-draft PR with zero blockers. See the [course loop](skills/gatekeep-pr/reference/post-selection-loop.md) and [three menus](skills/gatekeep-pr/reference/menus.md). In pi, `/skill:gatekeep-pr 12 15 18 [--rebase [base]]` gates a set of PR numbers or same-repository URLs, with one fresh `pr-gatekeeper` child and one worktree per PR. The parent shows a summary and unchanged per-PR menus in input order and queues every pick until the last menu. It runs terminal consequences in roster order from the primary checkout and dispatches nonterminal child work in parallel. The set must fit pi-cohort's `parallel.maxTasks` (default `8`); it is never chunked. With `--rebase`, the parent asks once per set for eligible other-author or unreadable-author heads, excluding no-op heads and heads with merge commits. Multi-PR mode rejects positional issue references and is pi-only. An explicit no-argument invocation on either harness discovers your open PRs in origin, authored by you or directly awaiting your review (not team requests), excludes drafts and the `APPROVED` and `CHANGES_REQUESTED` states, and sorts by additions plus deletions, then PR number, ascending. One match gates directly on the single-PR path; multiple matches enter pi's batch path, while Claude Code stops with the sorted `discovered, smallest first:` list. Every report explains the refreshed viewer's role and the other requested reviewers in plain prose, not in the batch summary row. See the [parent-child contract](skills/gatekeep-pr/reference/batch.md).
93
+
94
+ Gatekeep uses three menus: action selects a course or continues after an obstacle, final offers merge, approval and remaining work, and post collects terminal follow-ups. Row 1 is the judged recommendation, not a stable action number. Contract proposals recommend preserve contract, and an unchanged obstacle no available action can change recommends stop. Otherwise, your own PR defaults to deliver; an author decision favors assess on someone else's PR, a ready repair or pending-only check favors deliver, and readiness favors merge regardless of author. An unchanged assess result recommends stop. The report has three unlabeled parts: a two-sentence change/scope opening, standing and problems with concrete examples, and a separate recommendation immediately above the choices. Detailed row mappings, counters, raw checks and IDs stay under `show evidence`.
95
+
96
+ After merge, one post menu offers drafted replies, ticket and PR coverage, guarded checkout cleanup, PR-named steps and individual cleanup overrides. Row 1 combines runnable steps except duplicate PR coverage when ticket coverage is included; each step also has its own row. A comma-separated reply selects a set, expands combinations and collapses duplicates. The gate runs the set in step order, keeps nested skills' gates intact and ends without another menu, listing every unpicked, skipped or failed step with its command or payload path. Approved and closed PRs use the same menu with eligible steps; an approved/open reply retains its pre-write freshness read. In a set, each PR retires after its pick and shared overrides run at most once.
93
97
 
94
98
  The course, target/scope baseline, repair and sync usage, and poll deadline live in one OS temporary continuation record, whose path appears in the tracker or checklist and `show evidence`. Gatekeep initializes independent repair-round and sync budgets to `2 x closureReview.maxFixRounds` (default `5`, no-tool fallback `2 x 5`). Only source-changing repair waves spend rounds; conflict-only syncs spend the separate sync budget, and description-only work, watches, and re-reviews spend neither. Obstacle `deliver` extends each cap by `2 x` the resolved value without resetting usage; a timeout continuation instead opens one more watch window without extending caps. Only the gate's own push automatically renews the CI window (`timeout minutes`, default `15`). The verify-phase runtime guard still uses `1 x` the resolved value and can block a gatekeep dispatch earlier.
95
99
 
100
+ PR-body or head-spec instructions assigning the merger an after-merge action become post steps. Their commands come only from the merge-base overrides file, `AGENTS.md`, or documents it links directly: exactly one distinct documented command makes the step runnable and part of row 1. PR-supplied command text never supplies execution authority. Missing or ambiguous commands and unresolved tokens leave the action open in the menu and closing message with its source sentence. See [PR-named steps](skills/gatekeep-pr/reference/findings.md#pr-named-steps).
101
+
96
102
  Non-final `stop` removes an eligible tracked-clean, non-primary worktree, created or reused, with no unpushed commits and a matching configured home; otherwise it keeps the tree and reports why. It never deletes the branch, pulls base, or runs cleanup overrides. See the [stop lifecycle](skills/gatekeep-pr/reference/cleanup.md#non-final-stop). Missing or invalid state stops course work without resetting budgets. Terminal exit deletes the record; OS cleanup can remove it earlier, and a new session never discovers or resumes authority automatically. This is a skill protocol, not hard runtime enforcement.
97
103
 
98
104
  pi-gauntlet ships three kinds of pieces, layered on top of pi-cohort's dispatch:
99
105
 
100
- - **21 skills** - the workflow logic. Sixteen activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `shape-ticket` (creates one tracker issue or repairs a comma/whitespace list of refs against a Context/Problem/Idea/Acceptance-Criteria template, with a grounded repo audit, an AC integrity check, a cheap council roast, and sequential per-ticket exact-diff write confirmations; model-invocable so other skills and commands can route "file a ticket" to it, the gate still needs your yes), `gatekeep-pr` (model-invocable on an explicit ask that names the PR and the verb; the gates still need your selection - is consent-gated pre-merge verification of a PR against the scope contract at its head - the approved spec's acceptance-criteria dispositions and Design when the PR carries one, else the PR's stated intent; the ticket is cross-checked, never judged here (`check-delivery` owns it) - 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 and an unknown merge state still withhold; a viewer GitHub reports as a ruleset bypass actor merges a `BLOCKED` PR on the ordinary `merge` row with CI evidence still binding and all other merge preconditions unchanged; false or unreadable bypass eligibility keeps `BLOCKED` withheld, and live non-admin bypass behavior remains unverified; the project's verification command runs only as fallback), a rubric-based review, a compact bottom-up report (what the PR delivers, including rows deferred or deviating per spec, PR comments, nits, blockers, then a `mergeable` / `fixable` verdict) and course and terminal menus whose rows derive from the actor's permissions on the PR; nothing mutates (repairs, pushes, reviews, merges, tracker comments) until you pick a course or terminal row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification, or holds the rebased head locally when a conflict stop's scoped tests fail, so a selected delivery course resolves it before the lease push; after your own merge two opt-in rows post the `AC coverage` block to the ticket or the PR; own-merge, approved, and closed-PR menus offer a recommended `cleanup` row that tears down the gate's worktree and branch and runs the overrides file's `## cleanup` steps - run it with `/skill:gatekeep-pr [<pr>] [--rebase [base]]` (no argument: your open PRs)), `check-delivery` (model-invocable on an explicit ask that names the ticket and the verb; the gates still need your selection - 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>` or, in pi, `/skill:check-delivery #12 #15` for fresh parallel verification and one gate and write per ticket in turn), `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `loop-implementation` (the plan-free implement driver that iterates a fresh `loop-implementer` over the spec's acceptance-criteria ledger when approval picks the loop engine), `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). Five 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. `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>]`.
106
+ - **21 skills** - the workflow logic. Sixteen activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `shape-ticket` (creates one tracker issue or repairs a comma/whitespace list of refs against a Context/Problem/Idea/Acceptance-Criteria template, with a grounded repo audit, an AC integrity check, a cheap council roast, and sequential per-ticket exact-diff write confirmations; model-invocable so other skills and commands can route "file a ticket" to it, the gate still needs your yes), `gatekeep-pr` (model-invocable on an explicit ask that names the PR and the verb; the gates still need your selection - is consent-gated pre-merge verification of a PR against the scope contract at its head - the approved spec's acceptance-criteria dispositions and Design when the PR carries one, else the PR's stated intent; the ticket is cross-checked, never judged here (`check-delivery` owns it) - 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 and an unknown merge state still withhold; a viewer GitHub reports as a ruleset bypass actor merges a `BLOCKED` PR on the ordinary `merge` row with CI evidence still binding and all other merge preconditions unchanged; false or unreadable bypass eligibility keeps `BLOCKED` withheld, and live non-admin bypass behavior remains unverified; the project's verification command runs only as fallback), a rubric-based review, a three-part plain-language report (change and scope, standing and problems, then recommendation) and action, final and post menus whose rows derive from the actor's permissions on the PR; nothing mutates (repairs, pushes, reviews, merges, tracker comments) until you pick a course or terminal row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification, or holds the rebased head locally when a conflict stop's scoped tests fail, so a selected delivery course resolves it before the lease push; after your own merge one post menu offers drafted replies, ticket and PR copies of `AC coverage`, guarded checkout cleanup and separate `## cleanup` items; row 1 combines the runnable steps except duplicate PR coverage when ticket coverage is included, each step keeps its own row, and the run ends after the selected set finishes; approved and closed-PR states use the same post menu with their eligible steps - run it with `/skill:gatekeep-pr [<pr>] [--rebase [base]]` (no argument: your open PRs)), `check-delivery` (model-invocable on an explicit ask that names the ticket and the verb; the gates still need your selection - 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>` or, in pi, `/skill:check-delivery #12 #15` for fresh parallel verification and one gate and write per ticket in turn), `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `loop-implementation` (the plan-free implement driver that iterates a fresh `loop-implementer` over the spec's acceptance-criteria ledger when approval picks the loop engine), `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). Five 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. `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>]`.
101
107
  - **11 subagent personas** - the specialized child agents the skills dispatch via pi-cohort: `implementer`, `loop-implementer`, `wave-reviewer`, `conformance-reviewer`, `spec-summarizer`, `plan-writer`, `spec-council-member`, `spec-council-synthesizer`, `spec-council-applier`, `pr-gatekeeper`, `delivery-checker`. See [doc/personas.md](./doc/personas.md) for what each one does and why its permissions are scoped the way they are.
102
108
  - **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 (expected when a loop gate set does not match its patterns; the scoped `Verification:` line answers it, not a full local run); a phase-tracker flow guard reminds on implement-phase commits missing spec/code review. Both engines use the tracker: waves manages plan tasks, while loop calls `plan_tracker refresh` to project ledger rows and preserve conformance tasks. Loop implement completes when every row is closed, then verify step 1 runs the ledger's gate set in the owning checkouts. 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, and that every test command carries a passing base-commit result; a pass stamps the plan and spec content hashes for implementation and returns a bounded task manifest; see the [manifest contract and size limit](./doc/configuration.md#phase-tracker). `telemetry` publishes YAML records on the `refs/telemetry/records` ref, outside the product diff: one sealed brainstorming run plus one session leg per settled `shape-ticket`, `gatekeep-pr` or `check-delivery` invocation. In-progress runs live in Git common-dir metadata; `gauntlet-telemetry-seal` publishes before landing without changing checkout HEAD or index. Publication failure warns without blocking ship; plain jj keeps local metadata and reports no Git remote. A fetched shipped record blocks a brainstorm `write` into that spec. See [the record layout, settings and migration guide](./doc/configuration.md#telemetry). See [doc/configuration.md](./doc/configuration.md) for the settings each one reads. `phase_tracker`, `plan_check`, and `plan_tracker` are model-only (not callable from codemode scripts) because their state is rebuilt from transcript tool results, which nested calls do not persist.
103
109
 
@@ -142,7 +148,7 @@ Run by a human against pi-cohort >= 7.1.0, before a pi-gauntlet release claims t
142
148
  ## Requirements
143
149
 
144
150
  - [pi-coding-agent](https://github.com/earendil-works/pi) >= 1.5.0 - supports model-only tracker exposure and sequential tracker execution, so updates persist as transcript tool results rather than nested calls.
145
- - [pi-cohort](https://github.com/jjuraszek/pi-cohort) >= 7.7.3 - required peer package, including binary-safe worktree patches; it also lets a persona with `subagent` in `tools:` start with inherited extensions ([pi-cohort #26](https://github.com/jjuraszek/pi-cohort/issues/26)), which multi-PR `gatekeep-pr` needs. Skills that dispatch agents (`requesting-code-review`, `subagent-driven-development`, `dispatching-parallel-agents`, `writing-plans`, `forge-skill`, `shape-ticket`, `roasting-the-spec`) call `subagent({})`, which pi-cohort provides. pi-gauntlet does not vendor the dispatch tool; without pi-cohort those skills have nothing to call. Gauntlet execution dispatches explicitly use `async: false`; do not enable pi-cohort's incompatible `forceTopLevelAsync` setting. See pi-cohort's [dispatch configuration](https://github.com/jjuraszek/pi-cohort/blob/main/doc/configuration.md) for that setting's owner and semantics. A wave of N >= 1 tasks dispatches its implementers and first spec reviews as one chain call. `subagent-driven-development` stops before dispatch on unsupported installs; there is no older-runtime fallback.
151
+ - [pi-cohort](https://github.com/jjuraszek/pi-cohort) >= 7.7.4 - required peer package, including binary-safe worktree patches; it also lets a persona with `subagent` in `tools:` start with inherited extensions ([pi-cohort #26](https://github.com/jjuraszek/pi-cohort/issues/26)), which multi-PR `gatekeep-pr` needs. Skills that dispatch agents (`requesting-code-review`, `subagent-driven-development`, `dispatching-parallel-agents`, `writing-plans`, `forge-skill`, `shape-ticket`, `roasting-the-spec`) call `subagent({})`, which pi-cohort provides. pi-gauntlet does not vendor the dispatch tool; without pi-cohort those skills have nothing to call. Gauntlet execution dispatches explicitly use `async: false`; do not enable pi-cohort's incompatible `forceTopLevelAsync` setting. See pi-cohort's [dispatch configuration](https://github.com/jjuraszek/pi-cohort/blob/main/doc/configuration.md) for that setting's owner and semantics. A wave of N >= 1 tasks dispatches its implementers and first spec reviews as one chain call. `subagent-driven-development` stops before dispatch on unsupported installs; there is no older-runtime fallback.
146
152
 
147
153
  Both packages must be listed in your `.pi/settings.json#packages` array (pi adds them automatically when you `pi install`). pi-gauntlet and pi-cohort are versioned independently but release together whenever dispatch semantics change - pin compatible versions of both. At each pi-gauntlet release, set the pi-cohort peer minimum and README floor together to the newest released pi-cohort version; `/skill:release` checks the registry before proceeding.
148
154
 
@@ -345,7 +351,7 @@ The gauntlet overrides file is the one place a repo tunes gauntlet's Markdown su
345
351
  | `## using-git-worktrees` `- home: <path pattern>` | using-git-worktrees, finishing-a-development-branch, gatekeep-pr | `<primary>/.worktrees/<name>` | overrides | where worktrees live; `<name>` is the branch (`/` -> `-`) or `pr-<N>`; the ownership test at cleanup |
346
352
  | `## using-git-worktrees` `- create: <command>` | using-git-worktrees, gatekeep-pr | `git worktree add` at `home:` | overrides | creation command run from the primary; `<name>`, `<branch>` substituted, other `<token>`s resolved from context or asked once |
347
353
  | `## using-git-worktrees` `- destroy: <command>` | finishing-a-development-branch, gatekeep-pr | `git worktree remove` + `prune` when the path matches `home:` | overrides | removal command run from the primary; a failure keeps the branch |
348
- | `## cleanup` items `- <label>: <command or /skill:...>` | gatekeep-pr | git teardown and base sync only | extends | ordered post-gate steps with `<issue>`, `<pr>`, `<merge-sha>`; inherits `## PR gate` `timeout minutes:`; missing placeholders or unlanded-change steps are skipped with a reason; `/skill:` items keep their own gates |
354
+ | `## cleanup` items `- <label>: <command or /skill:...>` | gatekeep-pr | git teardown and base sync only | extends | separate post-menu steps with `<issue>`, `<pr>`, `<merge-sha>` selected individually or in the recommended combination; inherits `## PR gate` `timeout minutes:`; missing placeholders or unlanded-change steps are skipped with a reason; `/skill:` items keep their own gates |
349
355
  | `## PR gate` keys (`verification command`, `timeout minutes`, `requires credentials`, `local verification`, `ci checks`, `issue fetch`, `merge policy`) | gatekeep-pr | per-key defaults in the block below | overrides | the gate's verification, evidence, and merge configuration |
350
356
  | `## comms style` | gatekeep-pr | shipped wording rules | extends | tone and format of reports, menus, and external payloads |
351
357
  | `## verification-before-completion` | verification-before-completion; gatekeep-pr (verification command only); chase-bug (hotfix verification) | repo test entrypoint discovery | overrides | the canonical verification target |
@@ -585,7 +591,7 @@ customization lives in three places, never in the wrapper itself:
585
591
  - merge policy: squash | merge-commit | rebase # optional; default squash; chooses gh pr merge flag for final merge only, never sync strategy or approval
586
592
  ```
587
593
 
588
- - **The gauntlet overrides file, `## cleanup` section** - the ordered post-gate steps the `cleanup` menu row runs (contract under Project-specific overrides above).
594
+ - **The gauntlet overrides file, `## cleanup` section** - the ordered follow-up steps selected through the post menu (contract under Project-specific overrides above).
589
595
 
590
596
  An existing `## verification-before-completion` overrides section is an accepted
591
597
  equivalent source for the verification command only; all other PR-gate keys
@@ -1,6 +1,8 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { readFileSync, readdirSync } from "node:fs";
3
3
  import test from "node:test";
4
+ import { fileURLToPath } from "node:url";
5
+ import { lintGateContract } from "../scripts/gate-contract-lint.mjs";
4
6
  import { settlementItems } from "./lib/telemetry-leg.ts";
5
7
  import { MODELS } from "../eval/lib/models.mjs";
6
8
 
@@ -41,7 +43,7 @@ test("skill owners keep terminal rows after writes and cleanup", () => {
41
43
  const gate = read("skills/gatekeep-pr/reference/post-selection-loop.md");
42
44
  assert.ok(gate.includes("#<N>@<head sha>: <outcome>"));
43
45
  assert.match(gate, /full assessed `headRefOid`/);
44
- assert.match(gate, /cleanup pick is answered/);
46
+ assert.match(gate, /Emit the terminal message only after the selected post set finishes or stop declines it; a report or menu is not settlement/);
45
47
  assert.match(read("skills/gatekeep-pr/reference/batch.md"), /gauntlet: retired #<N> - <reason>/);
46
48
  assert.match(read("skills/gatekeep-pr/reference/cleanup.md"), /### Settlement rows/);
47
49
  const ci = read("scripts/ci.mjs");
@@ -52,6 +54,32 @@ test("skill owners keep terminal rows after writes and cleanup", () => {
52
54
  }
53
55
  });
54
56
 
57
+ test("gatekeep published gate markers and terminal settlements stay compatible", () => {
58
+ const contract = read("doc/gate-contract.md");
59
+ for (const id of ["initial", "obstacle", "contract-exception", "final", "assess-terminal", "own-merge", "approved", "closed-pr", "merge-queued"]) {
60
+ assert.ok(contract.includes(`| gatekeep-pr/${id} | gate | skills/gatekeep-pr/reference/menus.md |`), id);
61
+ }
62
+ assert.deepEqual(lintGateContract(fileURLToPath(new URL("../", import.meta.url))), []);
63
+ const terminals = contract.split("\n")
64
+ .filter((line) => line.startsWith("| gatekeep-pr |"))
65
+ .map((line) => line.split("|")[4].trim());
66
+ assert.deepEqual([...new Set(terminals)].sort(), [
67
+ "gauntlet: done approved", "gauntlet: done merged", "gauntlet: done closed-pr",
68
+ "gauntlet: done final", "gauntlet: done merge-queued", "gauntlet: stuck",
69
+ "gauntlet: done retired", "gauntlet: stuck retired",
70
+ ].sort());
71
+ for (const outcome of ["approved", "merged", "closed-pr", "final", "merge-queued", "stuck - CI failed"]) {
72
+ const marker = outcome.startsWith("stuck -") ? "gauntlet: stuck CI failed" : `gauntlet: done ${outcome}`;
73
+ for (const ticket of [undefined, "#12"]) {
74
+ const row = `#31@${head}: ${outcome}${ticket ? ` (${ticket})` : ""}`;
75
+ assert.deepEqual(settlementItems("gatekeep-pr", `${marker}\n${row}\nCleanup was not picked.`), [
76
+ { kind: "pr", key: `#31@${head}`, outcome, ...(ticket ? { ticket } : {}) },
77
+ ]);
78
+ assert.equal(settlementItems("gatekeep-pr", `gauntlet: gate=gatekeep-pr/final recommended=1\n${row}`), undefined);
79
+ }
80
+ }
81
+ });
82
+
55
83
  test("configuration names the settlement grammar and per-skill tokens", () => {
56
84
  const docs = read("doc/configuration.md");
57
85
  assert.ok(docs.includes("<ref>: <outcome>[ (<ticket ref>)]"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "8.4.7",
3
+ "version": "8.4.9",
4
4
  "description": "Opinionated, gated workflow skills, subagent personas, and runtime extensions for the pi coding agent.",
5
5
  "author": "Jacek Juraszek",
6
6
  "type": "module",
@@ -62,7 +62,7 @@
62
62
  "test": "node scripts/ci.mjs"
63
63
  },
64
64
  "peerDependencies": {
65
- "pi-cohort": ">=7.7.3"
65
+ "pi-cohort": ">=7.7.4"
66
66
  },
67
67
  "dependencies": {
68
68
  "yaml": "^2.9.0"
@@ -95,7 +95,7 @@ Clarify freely. For a PR subject, run the framing check against the PR's diff as
95
95
 
96
96
  `Read` the gathered draft unconditionally before question one. Treat it as a helper, not a fence: verify load-bearing claims from primary code, and confirm whether the codebase or ecosystem already solves the problem.
97
97
 
98
- Ask one question per message, about purpose, constraints, success, and affected actors. Offer 2-4 labeled options (`A)`, `B)`, ...) for every questionary question, each a different outcome. Put the lead-in before the first option, each labeled option on its own physical line, and `Recommendation: <letter> - <why>` on its own final line; when more outcomes are plausible, the last option is `other - name it` or the question splits in two. Use this one-option-per-line layout even when chat-style rules in `AGENTS.md` require one paragraph or no Options/Recommendation layout. A decision that would be a yes/no or "may I" question is not asked: make it, state it as an assumption in the message that carries the next question or the premise note, and record it in the draft. Before asking, check code, docs, and tracker: look up current-state facts (dispatch a subagent when costly); adopt a ticket-recorded decision and cite the ticket, asking it only when a cited code or API contradiction, or conflicting recorded outcomes, prevents adopting it. Other approvals retain their wording.
98
+ Ask one question per message, about purpose, constraints, success, and affected actors. Offer 2-4 labeled options (`A)`, `B)`, ...) for every questionary question, each a different outcome. Put the lead-in before the first option, each labeled option on its own physical line, and `Recommendation: <letter> - <why>` on its own final line; when more outcomes are plausible, the last option is `other - name it` or the question splits in two. Use this one-option-per-line layout even when chat-style rules in `AGENTS.md` require one paragraph or no Options/Recommendation layout. A decision that would be a yes/no or "may I" question is not asked: make it, state it as an assumption in the message that carries the next question or the premise note, and record it in the draft. Before asking, check code, docs, and tracker: look up current-state facts (dispatch a subagent when costly); adopt a ticket-recorded decision and attribute it to the ticket, asking it only when a cited code or API contradiction, or conflicting recorded outcomes, prevents adopting it. Other approvals retain their wording.
99
99
 
100
100
  Bad: `May I treat those two acceptance criteria as location-only deviations? Recommendation: yes`
101
101
  Good:
@@ -108,37 +108,18 @@ C) the existing skill step.
108
108
  Recommendation: A - the extension already holds the poll loop, so no new entrypoint.
109
109
  ```
110
110
 
111
- Append only citable findings - schemas, hard constraints, contradictions, and scope-changing answers - to `## Appended during questionary` using `edit`.
111
+ Before sending the opening message, append every checked claim, held ones included, with its source, the lookup, and the result, together with schemas, hard constraints, contradictions, and scope-changing answers, using `edit`: to `## Appended during questionary` when the draft is the gather draft; directly to `## Problem` when it is the approved or carried spec (redraw or the unmarked spec-only PR route), where gather is skipped and that section does not exist.
112
112
 
113
- Before approaches, state in chat the supported, disproved, corrected, and unverified premises with sources and attempted lookups, in full sentences - no status-keyword lists, no template; if the design depends on no claims, one sentence says so. Treat an implicit dependency found missing as a corrected premise and one not checkable as unverified; state each in the premise note with the lookup the repo or spec names and its result, and carry it into `## Problem`. An unverified claim is not a stop: put it in Open Questions or a stated assumption. If a load-bearing claim is contradicted, the premise note is your next message - even as question one - and states the corrected fact with its source; the design continues on it and the user overrides in reply. Record the corrected fact, and any override, in the draft for `## Problem` or the relevant design decision.
113
+ Before approaches, state in chat the supported, disproved, corrected, and unverified premises in the opening message's `## What I checked` section: supported renders as `Held:`, disproved and corrected as `Corrected:`, and unverified as `Not checked:`; that section is the premise note, and after the opening message a premise note is a `Corrected:` line. Treat an implicit dependency found missing as a corrected premise and one not checkable as unverified; state each in the premise note in everyday words that name what was looked at and what was found; record the lookup the repo or spec names and its result in the draft, and carry it into `## Problem`. An unverified claim is not a stop: put it in Open Questions or a stated assumption. If a load-bearing claim is contradicted, the premise note is your next message - even as question one - and states the corrected fact; the design continues on it and the user overrides in reply. Record the corrected fact, and any override, in the draft for `## Problem` or the relevant design decision; spec-writing carries every recorded claim, held ones included, into `## Problem`.
114
114
 
115
- Before question one, verify the draft's `Framing:` line against primary code and add your own recon. This check adds to the premise verification above and never replaces it: trace every consumer, caller, and data path the ask's constraints name, including retained loaders and every repro and verification step's implicit dependencies, before writing the holds-statement or the question; check those dependencies with the lookup the repo or spec names, and name the traces and dependencies in the holds-statement, not only the files read. A framing concern exists when you can state a cited case that the ask is misdiagnosed, wrong-sized, already solved, a band-aid where the root cause is reachable, or cuts across a sound pattern - including a repo convention that is itself the debt - and can name the alternative it implies; an objection without an alternative is a lead to investigate, not a concern. With a concern, question one is the framing question in the questionary format:
115
+ Before question one, verify the draft's `Framing:` line against primary code and add your own recon. This check adds to the premise verification above and never replaces it: trace every consumer, caller, and data path the ask's constraints name, including retained loaders and every repro and verification step's implicit dependencies, before writing the holds-statement or the question; check those dependencies with the lookup the repo or spec names, and record the traces and dependencies in the draft through the evidence write above. A framing concern exists when you can state a cited case that the ask is misdiagnosed, wrong-sized, already solved, a band-aid where the root cause is reachable, or cuts across a sound pattern - including a repo convention that is itself the debt - and can name the alternative it implies; an objection without an alternative is a lead to investigate, not a concern. With a concern, question one is the framing question; without a concern, the message carries the holds-statement and the first ordinary question.
116
116
 
117
- ```text
118
- gauntlet: question
119
- A) as framed
120
- B) pivot: <named alternative> - <cited reason>
121
- C) smaller or none: <what ships, or what happens if nothing ships>
122
- Recommendation: <letter> - <why>
123
- ```
124
-
125
- For a band-aid concern, put the root-cause alternative in B. Without a concern, follow the marker with one sentence naming what was checked - `The framing holds: checked <x>, <y>` - and continue to the first ordinary question; a scout objection that yielded no alternative is named among the things checked.
117
+ Read [Opening message](reference/opening-message.md) now and write question one's message against it; unreadable -> stop with a blocking error, never write it from memory.
126
118
 
127
119
  After any premise correction in the same message, ask the framing question or give the holds-statement. Ask the framing question at most once per brainstorm, including after pushback; the user's reply is final, and later doubts on the same point go to Open Questions or a `Pattern:` line. Recommend; never pivot without the reply. A ticket-recorded decision on the exact point follows the ticket rule above; an adopted decision records `Framing: kept (ticket <id>)`. A pivot never rewrites AC rows: rows it no longer satisfies take `deviates:` dispositions, and a pivot that changes the topic re-mints the slug.
128
120
 
129
121
  Record the outcome in the draft under `## Appended during questionary` as `Framing: kept` or `Framing: pivoted to <x> - <why>`; spec-writing carries that line into `## Problem`. On redraw entry (checklist step 4) the count restarts: run this evaluation once against the new draft. On redraw the draft is the approved spec: its `## Problem` `Framing:` line is context, not scout evidence, and the new outcome is recorded directly in `## Problem`. When the scout failed there is no `Framing:` line; supply the implicit-dependency check and the holds-statement or question from your own recon.
130
122
 
131
- Bad: `The ask adds another settings path - should we? Recommendation: no`
132
- Good:
133
- ```text
134
- gauntlet: question
135
- The ask adds a second settings path beside the resolver that already owns every setting (settings/resolver.ts:11).
136
- A) as framed
137
- B) pivot: a key under the existing resolver - one read path, no new boundary
138
- C) smaller or none: document the default and ship no setting.
139
- Recommendation: B - the owning schema already exists.
140
- ```
141
-
142
123
  ### 4. Explore approaches
143
124
 
144
125
  In a separate message after the premise note, restate each implicit dependency recorded as missing or not checkable, with its lookup and result, before presenting any approaches.
@@ -0,0 +1,96 @@
1
+ # Opening message
2
+
3
+ Use this wording for question one's message at checklist step 4, including fresh, redraw, restored, and unmarked spec-only PR entry. Apply [Menu layout](menu-layout.md) for the first-line rule. The relatedness question before provisioning belongs to `pr-subject.md`.
4
+
5
+ ## Shape
6
+
7
+ ```text
8
+ gauntlet: question
9
+
10
+ ## Today
11
+ <how the affected part works now>
12
+
13
+ ## The change
14
+ <what the request does to it, as something the user observes>
15
+
16
+ ## What I checked
17
+ - Held: <one short sentence naming the held claims by role>
18
+ - Corrected: <the claim as stated>; <the fact>, so <the consequence for the design>.
19
+ - Not checked: <the claim> - <why it could not be checked>, so <what the design assumes or waits on>.
20
+
21
+ The framing holds: checked <things, named by what they do>.
22
+ <lead-in question>
23
+ A) <outcome>
24
+ B) <outcome>
25
+ Recommendation: <letter> - <why>
26
+ ```
27
+
28
+ Keep the three titles in that order, with a blank line between sections. Give each body one short paragraph or one list. Use `##`: pi renders it as a bold title, while `###` keeps its hashes.
29
+
30
+ | Section | Write |
31
+ |---|---|
32
+ | `## Today` | Summarize how the affected part behaves now in one to three present-tense sentences total, combining related behaviors within a sentence. For a PR subject describe the PR's current state; on a redraw, the approved spec. |
33
+ | `## The change` | Write one to three sentences total about the changes the user observes once it ships; fit related outcomes and any stated assumption into those sentences. Put the held claims' consequence for the design here, not on the `Held:` line. For a ticket request, say what the ticket asks, without listing acceptance rows. Put an assumption made in place of a yes/no question last. |
34
+ | `## What I checked` | Group the held claims by role in one short `Held:` sentence; keep per-claim evidence in the draft. Give each corrected premise one `Corrected:` line stating the fact and its consequence for the design. Give each unverified premise, and a ticket gather could not fetch, one `Not checked:` line with the reason and what the design assumes or waits on. Use SKILL.md's premise paragraph to classify them. Omit labels with no entry. With no claims, write only `The design rests on no claims.` |
35
+
36
+ For a request that is only an unread ticket reference, make `## Today` and `## The change` one sentence each saying their content is not known until the ticket's content is supplied. Infer neither current behavior nor the requested change from the reference or repo recon. Keep the `Not checked:` line, the framing line, and the usual question slot; name the failed ticket fetch among the things checked in `The framing holds: checked ...`.
37
+
38
+ ## Words and length
39
+
40
+ Name each part by what it does. Use a literal name when the user used it in the request or when it is the finding itself: a missing file, a failing input, a quoted ticket row. Name a lookup by what was looked at, such as `the supplied templates/ directory listing`. Keep file-and-line references, commit ids, tracker ids, and trace counts in the draft. Use a tracker id only inside a command option such as `/skill:shape-ticket <ref>`, even when the user typed it; elsewhere, including an unfetched-ticket line, say `the ticket`, and render a lettered option with an unsupplied destination as a request to name that destination.
41
+
42
+ Keep at most 220 words from the `## Today` line through the line before the first lettered option, both included. Exclude the marker and options. Count whitespace-delimited tokens containing a letter or digit: titles and labels count; `##` and `-` do not. Over the cap, cut `## Today` and `## The change` to one sentence each; keep `Held:` to its one short sentence. Only `Corrected:` lines, `Not checked:` lines, and an intact quoted ticket row in the question may then carry the message past 220: none of them is dropped, and the row stays intact.
43
+
44
+ ## Framing and question
45
+
46
+ Write `The framing holds: checked <x>, <y>` with the checked things named by role, including a scout objection that yielded no alternative. For a concern, replace that line with the concern in one sentence, then offer `A) as framed`, `B) pivot: <named alternative> - <reason in everyday words>`, and `C) smaller or none: <what ships, or what happens if nothing ships>`, ending with `Recommendation: <letter> - <why>`. Put the root-cause alternative in B for a band-aid concern.
47
+
48
+ Use the question slot for one question: the framing question with a concern, otherwise the first ordinary question. Apply [Ticket acceptance - Cut question](ticket-acceptance.md#cut-question) and its trigger table for which question comes first, when a cut question takes the slot, and how its row is quoted.
49
+
50
+ Render the sections from the recon at hand; give anything it could not check a `Not checked:` line. Leave scout-failure flow to SKILL.md's `Framing:` record paragraph.
51
+
52
+ Open the next message after a later correction with one `Corrected:` line under the same register, with no section titles: the first line, or directly after the marker when the message has one.
53
+
54
+ ## Worked examples
55
+
56
+ Holds:
57
+
58
+ ```text
59
+ gauntlet: question
60
+
61
+ ## Today
62
+ The export uses the saved column order for every download.
63
+
64
+ ## The change
65
+ Users can choose a different order for one download. The saved order remains the default, so existing downloads stay the same.
66
+
67
+ ## What I checked
68
+ - Held: The export reads the saved order, and the download form can pass a different order.
69
+
70
+ The framing holds: checked the saved order and the download form.
71
+ Where should users choose the order?
72
+ A) in the download form
73
+ B) in a separate preview
74
+ Recommendation: A - users can choose without leaving the download.
75
+ ```
76
+
77
+ Concern:
78
+
79
+ ```text
80
+ gauntlet: question
81
+
82
+ ## Today
83
+ Every caller reads settings through the same resolver.
84
+
85
+ ## The change
86
+ The request adds another settings file to supply a timeout override.
87
+
88
+ ## What I checked
89
+ - Corrected: The draft says the resolver cannot accept overrides; it already accepts a timeout override, so the caller can pass it without another settings file.
90
+
91
+ Another settings file duplicates the existing read path. Which outcome should we pursue?
92
+ A) as framed
93
+ B) pivot: pass the override through the existing resolver - it already supports it
94
+ C) smaller or none: document the default and ship no setting
95
+ Recommendation: B - the existing resolver can handle the override.
96
+ ```
@@ -6,7 +6,7 @@ argument-hint: "[no argument: my open PRs] | <pr> [issue-ref] [--rebase [base]]
6
6
 
7
7
  # gatekeep-pr
8
8
 
9
- Verify, don't trust: a PR description is a claim, and over-claimed coverage, hallucinated references, and "tests pass" that never ran are the normal case on generated code. Judge the mechanism only - whether code, tests, and docs at the assessed head make each promised behavior possible - and leave what can be seen only after merge (a staging run, production data, an external system) to `/skill:check-delivery`. Persist one course through `reference/post-selection-loop.md`'s run record; keep final merge/approval and posts separately selected. Before the initial pick, permit only provisioning and the explicit `--rebase` sync; steps 1-5 read, verify and draft. Execute later writes under a set course, terminal pick or the sync's `--rebase` authority. A menu pick authorizes every command that pick needs: run them yourself, never print a command for the human to paste. Running the verification command executes PR code with your ambient credentials and no sandbox - gate only PRs you are willing to execute.
9
+ Judge whether code, tests and docs at the assessed head make each promised behavior possible; leave observations requiring the merged change to `/skill:check-delivery`. Persist one course through `reference/post-selection-loop.md`'s run record; keep final merge/approval and posts separately selected. Before the initial pick, permit only provisioning and the explicit `--rebase` sync; steps 1-5 read, verify and draft. Execute later writes under a set course, terminal pick or the sync's `--rebase` authority. Running the verification command executes PR code with your ambient credentials and no sandbox - gate only PRs you are willing to execute.
10
10
 
11
11
  ## Arguments
12
12
 
@@ -28,8 +28,8 @@ Verify (step 3) and Review (step 4) each run as a fresh helper with `cwd` the PR
28
28
  | 3 Verify | `verification-brief.md` Section B | evidence record (CI-first) and one disposition per material claim | `verify` |
29
29
  | 4 Review | `verification-brief.md` Section C | reviewer findings and the internal comment ledger | `review` |
30
30
  | 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` |
31
- | 6 Menu | `reference/decision-menu.md`, then `reference/menus.md` | initial for a repairable or pending-only not-ready first report, obstacle for undispositionable BLOCKED even with course unset, final for first-report readiness or a readiness exit, assess terminal after assess, obstacle at obstacle/cap, or the matching terminal/contract-exception template; exactly one `[recommended]` | `menu` |
32
- | 7 Loop | `reference/post-selection-loop.md` (delivery continues in `reference/fix-wave.md`) | the fixed delivery body until readiness, obstacle or cap; terminal picks and contract decisions through `reference/menus.md`; re-entry until stop | (re-opens the re-entered stage) |
31
+ | 6 Menu | `reference/menus.md` | state-selected action, final or post archetype; row 1 is the sole recommendation | `menu` |
32
+ | 7 Loop | `reference/post-selection-loop.md` (delivery continues in `reference/fix-wave.md`) | the fixed delivery body until readiness, obstacle or cap; terminal picks and contract decisions through `reference/menus.md`; re-entry until stop or completion of the terminal post set | (re-opens the re-entered stage) |
33
33
 
34
34
  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.
35
35
 
@@ -47,38 +47,49 @@ For pi file-finding row dispatch, use `subagent({ agent: "loop-implementer", con
47
47
 
48
48
  These bind every rendered report, menu, and external payload (review bodies, replies, commit subjects, tracker comments). A `## comms style` section in the overrides file extends them.
49
49
 
50
- 1. One item is one or two whole sentences: what is wrong, then why it matters. No fragments, no field separators.
51
- 2. Name the behavior, not the artifact: "retries forever" beats "attempts counter not incremented".
52
- 3. Locators trail in parentheses (`file:line`, check name, doc path), never inside the sentence.
53
- 4. No category tags, severity words, or IDs in prose - the section heading is the severity, the list number is the ID.
54
- 5. Report only non-conformance; passing checks, per-row AC outcomes, and matched claims print only under `show evidence`; `Delivers` names the covered and settled rows.
55
- 6. Omit empty sections.
56
- 7. `Delivers` is always present and names which spec rows this PR covers, which are `deferred per spec`, `deviates per spec`, or `elsewhere per spec`, and which observable half is checked after merge.
57
- 8. The verdict is one line in the fixed form.
58
- 9. Menu rows start with a verb selected numerically, carry one clause of consequence, and exactly one row is `[recommended]`; typed evidence/reply/check commands live in the hint line only.
59
- 10. ASCII only, American English, no hedges on checked facts, no intensifiers.
50
+ - Write for a reader with neither the code nor this run's notes open. Ground each claim in gathered facts; name the behavior and give one concrete example per problem. Explain a needed term in everyday words before using it.
51
+ - Write one or two whole sentences per finding: what is wrong, then why it matters. In a posted review body, trail each finding with its file and line in parentheses. Name files, lines and checks only where the reader must open or type them; keep exact supplied failing-test locators with their blocker.
52
+ - Omit empty sections. Use ASCII, American English, no hedges on checked facts and no intensifiers.
53
+ - Keep explanatory prose free of field names, run ids, commit ids and repair or sync counters outside `show evidence`; translate internal vocabulary and leave records unchanged. Write thread replies as plain sentences.
54
+ - Keep output unfenced. Preserve these exceptions verbatim: first-line markers, settlement rows, per-step result lines, typed commands, `[recommended]`, set queue/retirement lines, consent previews, the `AC coverage` heading and checkbox rows, and exact commands with substituted values in rows or closing messages. Keep repository-owned cleanup item text unchanged.
55
+
56
+ | Internal term | Say instead |
57
+ |---|---|
58
+ | binding pending check | a required check is still running |
59
+ | merge_state_status BLOCKED | GitHub won't let this merge yet |
60
+ | assessed head | the commit being reviewed |
61
+ | durable run record | saved progress |
62
+ | PR-named action | the step the PR asks for |
63
+ | coverage post | a comment saying which requirements the PR covers |
64
+ | withhold | the reason this cannot merge yet |
65
+ | terminal pick | your choice |
66
+ | post set | the steps you picked |
67
+ | disposition | how you classified the check |
68
+ | merge blocked, no bypass | GitHub reports the merge as blocked for you |
69
+ | bypass eligible | you can use GitHub's admin override |
70
+ | conflicting / behind | the branch conflicts with, or is behind, the base branch |
71
+ | evidence source ci / local | the checks ran in CI / on this machine |
72
+ | cap reached | the repair attempts allowed for this run are used up |
73
+ | nit | a smaller point that does not block merging |
74
+ | contradicted claim | the description says one thing and the code does another |
75
+ | observable half | the part that can only be seen after merge |
76
+ | deferred / deviates / elsewhere | the spec defers it, deliberately differs, or assigns it to another repo |
77
+ | pending reviewer placeholder | an automated review is still running |
78
+
79
+ ### Output done-check
80
+
81
+ Before sending any report, menu or external payload, reread it for internal jargon, raw field/value pairs, labeled-field scaffolding and instructions for the human to run a command the skill can run. Rewrite those lines in plain language, preserve the facts and concrete step commands needed for a choice, and execute selected commands yourself. Treat commands for steps not run as information, never a request to paste them.
60
82
 
61
83
  ## Red flags - STOP
62
84
 
63
- - A mutation without a set course, a terminal pick, or the sync's `--rebase` authority - owner: `reference/post-selection-loop.md` `### Continuation record`.
64
- - A code or doc edit sitting in the worktree when a menu renders - except helper residue that `reference/fix-wave.md` `## Wave` names in the re-rendered menu - owner: `reference/findings.md` `## Drafted payloads`.
65
- - Blocking on an AC's observation half, or on a PR-body claim the gate cannot check - owner: `reference/findings.md` `## AC outcomes`.
66
- - Judging a ticket row the spec does not carry, or treating a spec `deferred: <ref>` row as a gap - owner: `reference/findings.md` `## AC outcomes`.
67
- - 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`.
68
- - Raw command output, drafted payloads except the exact description consent preview, or internal IDs in the rendered report - owner: `reference/report.md`.
69
- - A menu rendered under deliver before an exit condition, other than the contract-exception template; a menu not rendered from `reference/menus.md`, two recommendations, stop not last or no show evidence hint - owner: `reference/menus.md`.
70
- - `cleanup` rendered on a non-terminal template, or cleanup steps 2-4 run on non-final stop - owner: `reference/cleanup.md` `## Non-final stop`.
71
- - A CI watch outside D3 step 4 or before the first report, or a second push inside one iteration - owner: `reference/post-selection-loop.md` `### Next action`.
72
- - The orchestrator resolving a merge conflict itself, or editing or committing a tracked file during a fix wave - owner: `reference/fix-wave.md` `## Sync` and `## Wave`.
73
- - A helper's duty run in the orchestrator's own context - owner: `## Harness notes`.
74
- - Approving your own PR - owner: `reference/decision-menu.md` `## Availability`.
75
85
  - Merging around an undispositioned failing check or a binding pending check, or a merge-state withhold under `reference/post-selection-loop.md` `### Merge preconditions` - owner: `reference/findings.md` `## Dispositions`.
86
+ - A menu rendered under deliver before an exit condition, other than the contract-exception template; a menu not rendered from `reference/menus.md`, two recommendations, a recommendation not on row 1 or no show evidence hint - owner: `reference/menus.md`.
87
+ - `cleanup` rendered on a non-terminal template, or cleanup steps 2-4 run on non-final stop - owner: `reference/cleanup.md` `## Non-final stop`.
88
+ - Approving your own PR - owner: `reference/menus.md` `## final`.
76
89
  - A menu rendered without the pre-menu refresh - owner: `reference/post-selection-loop.md` `### Pre-menu refresh`.
77
- - 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.
78
- - A claim verdict recorded behind a pending tracker stage - owner: `## Progress tracking`.
79
90
 
80
91
  ## Project overrides
81
92
 
82
- 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 once per session - a skill that already has the file's content in context from an earlier skill's read skips the read; a restored session has nothing in context and reads it. Read and apply `## conventions` whenever present, without a relevance judgment. Give this skill's named section precedence over conflicting `## conventions` rules. Use other relevant sections - by name match, by topic (routing, verification, worktrees, etc.), or by workflow convention - to 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. `## PR gate` keys are read in step 2 (`reference/assessment.md`); `## comms style` extends the wording rules. `## cleanup` steps run on the `cleanup` pick (`reference/cleanup.md`).
93
+ 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 once per session - a skill that already has the file's content in context from an earlier skill's read skips the read; a restored session has nothing in context and reads it. Read and apply `## conventions` whenever present, without a relevance judgment. Give this skill's named section precedence over conflicting `## conventions` rules. Use other relevant sections - by name match, by topic (routing, verification, worktrees, etc.), or by workflow convention - to 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. `## PR gate` keys are read in step 2 (`reference/assessment.md`); `## comms style` extends the wording rules. `## cleanup` items are separate steps selected through the post menu (`reference/menus.md` `## post`); execute them through `reference/cleanup.md`.
83
94
 
84
95
  The recognized headings, their readers, and the rules an override cannot change are listed in the [configuration front door](../../README.md#project-specific-overrides); an override extends or replaces the step it names and never lifts this skill's HARD CONSTRAINT or MUST text.
@@ -28,7 +28,7 @@ With rebase requested, read the viewer via `gh api user --jq .login`. After fetc
28
28
 
29
29
  Create the OS-temp roster with `mktemp "${TMPDIR:-/tmp}/gatekeep-roster.XXXXXX"`, never in a worktree. Store per PR number, worktree, record path, report path, status (`pending`, `report`, `conflicting`, `failed`, `stopped`, `retired`), reason, and queued pick. Pre-mint record/report paths outside worktrees.
30
30
 
31
- Initialize each existing-format continuation record once: `consent.target` (lowercase base owner/repo, PR number, gathered baseRefName), PR identity, worktree, `course` null, `repair.rounds_used` and `repair.syncs_used` zero, `approved_head` null, `cleanup_done` false. Use the continuation-record schema; initialize `repair.rounds_cap` and `repair.syncs_cap` to `2 x` the resolved `closureReview.maxFixRounds` under D4 (`fix-wave.md` `## Wave`). Only the first course pick may resolve its initial D4 allowance; re-dispatch never reinitializes usage or caps. The parent owns only initialization and direct queued picks; the child owns all assessment and execution fields, findings, digest, evidence, repair state, approved head, payloads, and comment ledger. Carry confirmations and answers in task text only; never overwrite course, counters, caps, deadline, ledger, or helper paths on refresh or re-dispatch. Carry `repair.wave.spec` and `repair.wave.ledger` once created, with their row history and counters, through every private handoff; wave settlement retains the pair until terminal teardown. Validate supplied records using the continuation-record identity and finding-artifact rules: a missing locator or missing, unreadable or malformed artifact exits `stuck - run record unreadable`, never resets them.
31
+ Initialize each existing-format continuation record once: `consent.target` (lowercase base owner/repo, PR number, gathered baseRefName), PR identity, worktree, `course` null, `repair.rounds_used` and `repair.syncs_used` zero, `approved_head` null. Use the continuation-record schema; initialize `repair.rounds_cap` and `repair.syncs_cap` to `2 x` the resolved `closureReview.maxFixRounds` under D4 (`fix-wave.md` `## Wave`). Only the first course pick may resolve its initial D4 allowance; re-dispatch never reinitializes usage or caps. The parent owns only initialization and direct queued picks; the child owns all assessment and execution fields, findings, digest, evidence, repair state, approved head, payloads, and comment ledger. Carry confirmations and answers in task text only; never overwrite course, counters, caps, deadline, ledger, or helper paths on refresh or re-dispatch. Carry `repair.wave.spec` and `repair.wave.ledger` once created, with their row history and counters, through every private handoff; wave settlement retains the pair until terminal teardown. Validate supplied records using the continuation-record identity and finding-artifact rules: a missing locator or missing, unreadable or malformed artifact exits `stuck - run record unreadable`, never resets them.
32
32
 
33
33
  ## Foreground dispatch
34
34
 
@@ -76,24 +76,24 @@ Re-assess from steps 1-5 with the same record; substitute the answer at that ask
76
76
 
77
77
  ## Summary and menu round
78
78
 
79
- The parent alone renders menus, gathers answers, and runs cleanup/stop consequences. Render the summary once, after the children's closing lines and before the first menu, without a standalone report block before it. Never rewrite, correct, or reconcile a child's report: preserve spelling, counts, verdict wording, missing lines, and off-contract lines, including under show evidence; add no corrections block or inserted report lines. Render summary rows in roster order:
79
+ The parent alone renders menus, gathers answers, and runs post/stop consequences. Render the summary once, after the children's closing lines and before the first menu, without a standalone report block before it. Preserve each supplied child report unchanged, including under show evidence. For each PR in roster order, print `#<N>` and copy the child's standing sentence unchanged on one line; add no verdict, evidence or blocker fields. For a child without a report, state its recorded conflict, failure or ask as a sentence. Describe a pending required check in that PR's deliver row; leave the supplied standing sentence unchanged. Follow with `Shared files:` and every changed path shared by two or more PRs, `<path> - #<N>, #<M>`. Include `#<N> - files unknown` for missing gathered files; print `none` when no path overlaps. Never schedule merges or choose landing order.
80
80
 
81
- `#<N> <verdict line> | evidence: <ci | local | pending | unknown> | blockers: <count | unknown>`
81
+ Print each supplied child report file verbatim in roster order, each under its own `#<N>` heading and directly above that PR's queued menu from `menus.md`; add no synthetic `Verdict:` restatement at the menu turn. Render only the first PR's report and menu in the summary turn; collect each PR's pick before rendering the next PR's report and menu in a new turn. Never put two PRs' menus in one message or ask for all picks in one combined reply. Render each child's template from `menus.md`: reports with their template rows, conflicts through `menus.md` `## batch summary`'s recovered CONFLICTING template, failures with retry/evidence/stop, STOPPED with the recorded ask and stop. Apply `menus.md`'s row ordering to every child menu. For retry use exactly `retry - re-dispatch this PR's assessment with the same record`, recommended only after clean recovery and no rebase. Resolve the actual gitdir with git (a worktree .git can be a file); require empty `git -C <worktree> status --porcelain` and neither rebase-merge nor rebase-apply there. Otherwise omit retry and explain that the worktree is dirty or mid-rebase, naming the observed porcelain line or rebase directory.
82
82
 
83
- Use the child's supplied Verdict text unchanged, `conflicting - <sync record>`, `failed - <reason>`, or `stopped - <ask>`. Express a pending binding check only in that PR's queued menu: describe watching the check in its deliver row and print `Not offered: merge (<check> pending)`; leave the supplied verdict unchanged. Evidence is the record's resolved source; use unknown before Verify. Count open P# findings; use unknown without review. Follow with `Shared files:` and every changed path shared by two or more PRs, `<path> - #<N>, #<M>`. Include `#<N> - files unknown` for missing gathered files; print `none` when no path overlaps. Never schedule merges or choose landing order.
83
+ Render one post menu per merged PR, each in its own message, using `menus.md` `## post`. Keep the batch summary in a separate message before these menus. Each post-menu message starts with `gauntlet: gate=gatekeep-pr/own-merge recommended=1`, never the batch-summary marker. Before substitution, assign a `## cleanup` item whose unresolved text holds none of `<issue>`, `<pr>`, `<merge-sha>` to the shared set: include it in the last merged PR's row 1 only, in roster order for this round. Offer its individual row on earlier merged PRs while it has not run. Keep PR-specific items with their PR. Record shared-item execution in the parent roster across rounds: a shared item runs at most once in the set. Omit it from later menus and skip it in queued picks only after its command runs with a recorded result, never because an earlier pick is queued.
84
84
 
85
- Print each supplied child report file verbatim in roster order, each under its own `#<N>` heading and directly above that PR's queued menu from `menus.md`; add no synthetic `Verdict:` restatement at the menu turn. Render only the first PR's report and menu in the summary turn; collect each PR's pick before rendering the next PR's report and menu in a new turn. Never put two PRs' menus in one message or ask for all picks in one combined reply. Render each child's template from `menus.md`: reports with their template rows, conflicts through `menus.md` `## batch summary`'s recovered CONFLICTING template, failures with retry/evidence/stop, STOPPED with the recorded ask and stop last. For retry use exactly `retry - re-dispatch this PR's assessment with the same record`, recommended only after clean recovery and no rebase. Resolve the actual gitdir with git (a worktree .git can be a file); require empty `git -C <worktree> status --porcelain` and neither rebase-merge nor rebase-apply there. Otherwise omit the row and print `Not offered: retry (worktree dirty or mid-rebase: <state>)`. Substitute `<observed state>` with the concrete observation - the porcelain line(s) or the rebase directory name - one line per snapshot, never the placeholder text.
85
+ Queue every parent-owned terminal pick (a post row or set of rows, merge, wait, stop) until every roster entry's pick for the round is collected. Print `Queued until all picks are collected (<roster summary>)` before the execution block for each queued pick, including a single-entry roster. Summarize each roster entry's collected pick or pending status inside `<roster summary>`.
86
86
 
87
- Queue every parent-owned terminal pick (post coverage to PR or ticket, merge, cleanup, wait, stop) until every roster entry's pick for the round is collected. Print `Queued until all picks are collected (<roster summary>)` before the execution block for each queued pick, including a single-entry roster. Summarize each roster entry's collected pick or pending status inside `<roster summary>`.
87
+ Show evidence in-place from retained record/helper paths and leave the same menu waiting. Queue all other picks and answers until the round's final pick. Record direct picks as the child's `course` or terminal selection; do not turn answers into grants. Execute queued terminal picks in roster order from the primary checkout through `post-selection-loop.md` `### Post steps`, preserving its step order, deduplication, guards, failed-step continuation and nested-skill pauses. Keep remaining steps and other PRs' queued picks waiting until a picked skill returns. The parent owns every post step, even after cleanup removes a PR worktree; exclude terminal picks from child dispatch and never use a removed worktree as cwd. Run no helper for terminal posts. Skip compare-and-swap only on merged or closed PRs; delegate approved/open reply freshness to `### Post steps`.
88
88
 
89
- Show evidence only in-place from record/helper paths, then re-ask. Queue all other picks and answers until the final menu. Record direct picks as the child's `course` or terminal verb; do not turn answers into grants. For terminal overlays, the parent handles `post coverage to ticket`, `post coverage to PR`, and `show evidence` from the primary checkout, even after cleanup removes the PR worktree; assign the parent the `merge queued` overlay's `wait` pick after all picks are collected: re-run `post-selection-loop.md` `### Merge course`'s post-merge read (`gh pr view <N> --json state,mergeCommit`), on `MERGED` persist `pr.state` and `pr.merge_commit` and render coverage/menu under `own merge`, otherwise re-render `merge queued` (`wait`/`show evidence`/`stop`), with no helper, child, or CI watch. Follow `decision-menu.md`'s terminal rows and `post-selection-loop.md` `### Merge course` and `### Output done-check`: execute the direct selected post only, with no compare-and-swap or helper dispatch; preserve successful-post suppression, failed-post rows, and terminal re-rendering. Queue terminal posts with all other external writes until every PR's pick is collected; keep evidence in-place from the retained record/helper paths. Exclude terminal picks from child dispatch; never use a removed worktree as cwd. Next batch mixes retry/answered-ask `report` with ordinary nonterminal `execute` tasks, always same record.
89
+ For `merge queued`, the parent handles `wait` after all picks: re-read `gh pr view <N> --json state,mergeCommit` per `post-selection-loop.md` `### Merge course`. On `MERGED`, persist `pr.state` and `pr.merge_commit` and render its one post menu under `own merge`; otherwise re-render `merge queued` with wait/stop and the evidence hint, without helper, child or CI watch. Next batch mixes retry/answered-ask `report` with ordinary nonterminal `execute` tasks, always the same record.
90
90
 
91
91
  ## Between batches and retirement
92
92
 
93
- Run parent cleanup/stop only after all children finish, never on a live child's tree. For cleanup apply cleanup guards and ordered overrides. Keep the PR and post rows in the roster, set cleanup_done, and re-render with cleanup gone. Cleanup does not retire.
93
+ Run parent post/stop consequences only after all children finish, never on a live child's tree. After all selected post steps finish for a PR, retire it with no re-render, including when a step failed. On stop retire it without running unpicked post steps. For non-final stop run `cleanup.md` `## Non-final stop`; under terminal overlays leave the checkout as found and print its leftover path/branch.
94
94
 
95
- For non-final stop run `cleanup.md` `## Non-final stop` after all children finish. Under terminal overlays leave checkout as found and print leftover path/branch. Retain the PR's row from `post-selection-loop.md` `### Settlement rows` in the parent roster before deleting only that PR's record, helper outputs, and log_path files under existing teardown rules, then retire it. While any PR remains, start a retirement message with `gauntlet: retired #<N> - <reason>`, never `gauntlet: done` or `gauntlet: stuck`; keep private child result lines unchanged. Await all next-batch results and re-render every nonretired PR; repeat until all retire.
95
+ Before teardown, retain each PR's settlement row and per-PR not-run sentences from `post-selection-loop.md` `### Teardown` in the parent roster for the final message. Keep payload files for unrun or failed posts; delete only that PR's other helper/log files and record under those teardown rules. Start every PR retirement message, including the last, with `gauntlet: retired #<N> - <reason>`, without a terminal marker or settlement row; keep private child result lines unchanged. Await all next-batch results and render only nonretired PRs until all retire.
96
96
 
97
- After the last PR retires and every selected cleanup or follow-on step finishes, render one final message. Start the message with `gauntlet: stuck retired` when any retained outcome is `stuck - <reason>`, otherwise `gauntlet: done retired`. Print every retained PR row below it in roster order, including PRs retired in earlier rounds. Emit no per-PR terminal message before this set settlement.
97
+ After the last PR retires and every selected step finishes, render one parent closing message. Start the message with `gauntlet: stuck retired` when any retained outcome is `stuck - <reason>`, otherwise `gauntlet: done retired`. Print every retained PR settlement row and its not-run sentences in roster order, including PRs retired in earlier rounds.
98
98
 
99
99
  Initialize parent tracker tasks `pr-<N> state: <record path>`; mark in_progress on dispatch, complete on retirement, failed on unretried failure. Child trackers are session-local stages, not parent roster tasks. Use a plain checklist if plan_tracker is unavailable.