pi-gauntlet 7.11.2 → 7.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## v7.12.0 - 2026-10-16
4
+
5
+ ### Changed
6
+
7
+ - The carried-open conformance menu and the spec-amendment batch menu render as numbered decision briefs - the problem in plain words first, lettered options with one-line consequences, `Recommended: <letter> - <reason>` or `No recommendation - pick one` - and accept one reply line per item (`1. B`); bare `1` applies every recommendation and re-asks no-recommendation items by number, so the old `recommended: none` withholding is gone. (#63)
8
+
9
+ ## v7.11.3 - 2026-10-16
10
+
11
+ ### Added
12
+
13
+ - **gatekeep-pr `cleanup` row.** Every terminal menu (own merge, approved, already merged or closed) offers `cleanup` as row 1 `[recommended]`: it removes the gate's worktree and local branch, fast-forwards the base, then runs the overrides file's ordered `## cleanup` items with `<issue>`, `<pr>`, and `<merge-sha>` substituted; steps that need a landed change are skipped with a reason when nothing merged. `stop` under those menus now leaves the checkout as found and names it instead of tearing it down silently. New reference `skills/gatekeep-pr/reference/cleanup.md`; the digest carries `pr.url`, `pr.merge_commit`, and a `worktree` record; this repo's overrides close the ticket, then release.
14
+
3
15
  ## v7.11.2 - 2026-10-15
4
16
 
5
17
  ### Changed
package/README.md CHANGED
@@ -39,7 +39,7 @@ Concretely, one change through the gauntlet:
39
39
  1. Opt in with `/skill:brainstorming` and describe the change. The command is the reliable discovery-and-entry path; explicit prose requesting brainstorming/the gauntlet also supplies consent when the skill is already loaded or an available instruction or route identifies it. An ordinary implementation request or an agent recommendation is not consent. **`brainstorming`** sets up an isolated worktree, explores the codebase, and turns your description into a written spec - when its recon finds a cited case that the ask is the wrong change (misdiagnosed, wrong-sized, already solved, a band-aid, or cutting across a sound pattern - including a repo convention that is itself debt) and can name the alternative, question one is a framing question (`as framed`, `pivot: <alternative>`, or `smaller or none`); otherwise it opens with one sentence naming what it checked, and every approach it proposes ends with a `Pattern:` line naming what it follows or cuts across; when the run starts from a ticket, the spec carries the ticket's acceptance criteria verbatim, each with a disposition (`in-scope`, `deviates:`, `deferred:`, `venue:`, `elsewhere:` for a row another repo's part delivers), scope cuts come only from your answer to a cut question and are listed at the gate, the conformance gate checks the in-scope ones, and `/skill:check-delivery` verifies `venue:` rows before the prod deploy. A multi-model critique runs on it automatically. If the spec replaces a known prior spec, brainstorming marks the predecessor with a `> **Superseded by:**` banner under its title (default format, syntax overridable via `.pi/gauntlet-overrides.md`; candidates come from brainstorming's scout recon, never a mechanical sweep). **You read and approve the spec - human gate 1.** No implementation code exists yet.
40
40
  2. **`writing-plans`** decomposes the approved spec into atomic, independently-verifiable tasks, grouped into parallel waves where they don't touch the same files.
41
41
  3. **`subagent-driven-development`** executes the plan one task at a time, each in a fresh subagent, behind spec-compliance review then code-quality review. TDD-locked: red, green, refactor. Independent implementation, review, and council batches remain parallel, but their dispatches are foreground: the orchestrator waits for terminal results before accepting work or advancing a phase.
42
- 4. **verify**: the parent first runs the plan's full verification command set; only a passing result permits whole-diff code review, then the **conformance gate**. A review fix invalidates that result, so the parent reruns full verification before the next review or conformance gate. When the overrides file declares a happy path for the touched paths (see [Project-specific overrides](#project-specific-overrides)), the parent runs it once here, between code review and the conformance gate, and the reviewer reads its transcript as runtime evidence - a hang or a failed request shows up in the audit with the transcript quoted - as a gap when an origin clause ties to it, otherwise as a `happy-path: failed - unattributable` line - never as a silent green. The conformance subagent reads the finished code and docs against your *original words* from step 1, not the plan, and reports per-requirement: delivered, partial, missing, drifted, or unauthorized. Unauthorized rows - shipped surface no human input asked for, whether it crept in or was laundered through the spec - follow their recommendation like every other row: a contained removal auto-runs, anything another requirement leans on is deferred with a plain-language "I'd cut it / I'd keep it" recommendation. Inside a brainstorming-entered flow this gate is machine-blocked from being skipped. Compatible executable recommendations auto-run through an isolated fix-and-re-audit loop with no prompt; anything still open surfaces as a dense list - one line per decision, plain-language, with its recommended choice inline. Reply `1` to take every recommendation, or `2:` with per-item overrides; a current `CONFORMS` / no-concerns result goes straight to the branch options with no extra conformance sign-off.
42
+ 4. **verify**: the parent first runs the plan's full verification command set; only a passing result permits whole-diff code review, then the **conformance gate**. A review fix invalidates that result, so the parent reruns full verification before the next review or conformance gate. When the overrides file declares a happy path for the touched paths (see [Project-specific overrides](#project-specific-overrides)), the parent runs it once here, between code review and the conformance gate, and the reviewer reads its transcript as runtime evidence - a hang or a failed request shows up in the audit with the transcript quoted - as a gap when an origin clause ties to it, otherwise as a `happy-path: failed - unattributable` line - never as a silent green. The conformance subagent reads the finished code and docs against your *original words* from step 1, not the plan, and reports per-requirement: delivered, partial, missing, drifted, or unauthorized. Unauthorized rows - shipped surface no human input asked for, whether it crept in or was laundered through the spec - follow their recommendation like every other row: a contained removal auto-runs, anything another requirement leans on is deferred with a plain-language "I'd cut it / I'd keep it" recommendation. Inside a brainstorming-entered flow this gate is machine-blocked from being skipped. Compatible executable recommendations auto-run through an isolated fix-and-re-audit loop with no prompt; anything still open surfaces as a numbered decision brief - one item per decision, the problem in plain words first, lettered options each with a one-line consequence, and `Recommended: <letter> - <reason>` or `No recommendation - pick one`. Reply with one line per item (`1. B`), or bare `1` to take every recommendation (no-recommendation items are re-asked by number); a current `CONFORMS` / no-concerns result goes straight to the branch options with no extra conformance sign-off.
43
43
  5. **`finishing-a-development-branch`**: PR, draft PR, squash, keep, or discard. Once a PR exists, run `/skill:gatekeep-pr <pr>` to verify it before merging - it judges the diff against the spec at the PR's head (the acceptance-criteria dispositions and Design you approved; the PR description when no spec applies) - rows the spec defers or deviates are settled, every other row needs its mechanism (code, tests, docs) - and against the rubric; whatever can be seen only after merge is left to step 6. **Human gate 2** - the only other decision you make.
44
44
  6. *(Optional)* Once the merge lands, `/skill:check-delivery <ref>` can prove delivery - default-branch landing, delivery target, per-AC evidence - before the tracker status advances. Explicit ask only, no auto-chain: deploys commonly lag merges by minutes to hours, so an auto-run would routinely check too early.
45
45
 
@@ -77,7 +77,7 @@ The run's grants, target/scope baseline, repair usage, and poll deadline live in
77
77
 
78
78
  pi-gauntlet ships three kinds of pieces, layered on top of pi-cohort's dispatch:
79
79
 
80
- - **20 skills** - the workflow logic. Fifteen activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `shape-ticket` (creates or repairs one tracker issue per run against a Context/Problem/Idea/Acceptance-Criteria template, gated by an AC integrity check, a cheap council roast, and a single human-confirmed write; 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, an unknown merge state, and every other merge precondition still withhold; the project's verification command runs only as fallback), a rubric-based review, a compact bottom-up report (what the PR delivers, including rows deferred or deviating per spec, PR comments, nits, blockers, then a `mergeable` / `fixable` verdict) and a verb menu whose rows derive from the actor's permissions on the PR; nothing mutates (fixes, pushes, reviews, merges, tracker comments) until you pick a row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification, or holds the rebased head locally when a conflict stop's scoped tests fail, so the normal `fix` row 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 - run it with `/skill:gatekeep-pr <pr> [--rebase [base]]`), `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>`), `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `forge-skill`, `linear` (reads/searches/comments on/manages Linear tickets via the `linearis` CLI; owns all linearis mechanics and the `## Issue tracker` overrides schema; tracker-facing skills route to it). 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>]`.
80
+ - **20 skills** - the workflow logic. Fifteen activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `shape-ticket` (creates or repairs one tracker issue per run against a Context/Problem/Idea/Acceptance-Criteria template, gated by an AC integrity check, a cheap council roast, and a single human-confirmed write; 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, an unknown merge state, and every other merge precondition still withhold; the project's verification command runs only as fallback), a rubric-based review, a compact bottom-up report (what the PR delivers, including rows deferred or deviating per spec, PR comments, nits, blockers, then a `mergeable` / `fixable` verdict) and a verb menu whose rows derive from the actor's permissions on the PR; nothing mutates (fixes, pushes, reviews, merges, tracker comments) until you pick a row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification, or holds the rebased head locally when a conflict stop's scoped tests fail, so the normal `fix` row 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; every terminal menu offers 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]]`), `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>`), `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `forge-skill`, `linear` (reads/searches/comments on/manages Linear tickets via the `linearis` CLI; owns all linearis mechanics and the `## Issue tracker` overrides schema; tracker-facing skills route to it). 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>]`.
81
81
  - **7 subagent personas** - the specialized child agents the skills dispatch via pi-cohort: `implementer`, `code-reviewer`, `spec-reviewer`, `conformance-reviewer`, `spec-summarizer`, `spec-council-member`, `spec-council-synthesizer`. See [doc/personas.md](./doc/personas.md) for what each one does and why its permissions are scoped the way they are.
82
82
  - **4 runtime extensions** - the enforcement layer. `plan-tracker` and `phase-tracker` are tools skills call to track progress (with a TUI widget); `verify-before-ship` is a hook that warns if you push or open a PR without a passing test run since your last edit; a phase-tracker flow guard reminds on implement-phase commits missing spec/code review. In a brainstorming-entered flow, phase-tracker rejects `implement` or `verify` completion while tracker tasks remain pending or in progress; see [its configuration reference](./doc/configuration.md#phase-tracker). phase-tracker also registers `plan_check`, which verifies a plan against its spec and against the grammar in [skills/writing-plans/reference/plan-contract.md](./skills/writing-plans/reference/plan-contract.md), including that each task's `Tests:` commands are selective and never the full suite; a pass stamps the plan for implementation. `telemetry` records one YAML record per gauntlet run (phase timing, models, personas, gate/fix rounds, diff at ship) that ships in the squash beside the spec - it is written only inside a brainstorming-entered flow, kept out of history until `finishing-a-development-branch` seals it once with `gauntlet-telemetry-seal` (a `telemetry:` commit under git; the working-copy snapshot in a plain jj workspace) - and blocks a brainstorm `write` into an already-shipped spec; see [its configuration reference](./doc/configuration.md#telemetry). See [doc/configuration.md](./doc/configuration.md) for the settings each one reads.
83
83
 
@@ -109,7 +109,7 @@ Run by a human against pi-cohort >= 7.1.0, before a pi-gauntlet release claims t
109
109
  | --- | --- |
110
110
  | Gate | A machine-enforced checkpoint between phases (e.g. within a brainstorming-entered flow, `complete verify` is blocked until conformance review has run). Not a suggestion. |
111
111
  | Spec council | Multi-model critique of the spec before you see it (`roasting-the-spec`); members also flag unrelated extras or sufficient in-scope simplifications (`over-spec`) for cuts or replacements before planning, every member closes with a `lean:` line - "nothing to cut" is a first-class answer - and the chair tallies them (`lean: <k> of <n> members found nothing to cut`). Falls back to a single-model critique if no council is configured. |
112
- | Conformance gate | The closing check: does the delivered code + docs match your *original prompt*, not the derived plan? Compatible executable recommendations auto-fix first; anything still open renders as a dense one-line-per-decision list with each recommended choice inline. Reply `1` to accept all recommendations or `2:` with per-item overrides; a current `CONFORMS` / no-concerns handoff goes straight to branch options with no extra sign-off. |
112
+ | Conformance gate | The closing check: does the delivered code + docs match your *original prompt*, not the derived plan? Compatible executable recommendations auto-fix first; anything still open renders as a numbered decision brief - one item per decision, the problem in plain words first, lettered options each with a one-line consequence, and `Recommended: <letter> - <reason>` or `No recommendation - pick one`. Reply with one line per item (`1. B`), or bare `1` to take every recommendation (no-recommendation items are re-asked by number); a current `CONFORMS` / no-concerns handoff goes straight to branch options with no extra sign-off. |
113
113
  | Wave | A batch of plan tasks that don't touch the same files, dispatched to implementers in parallel. |
114
114
  | Overrides file | `.pi/gauntlet-overrides.md` - where you put project-specific detail the generic skills don't know (CI command, worktree wrapper, routing rules). |
115
115
 
@@ -311,6 +311,16 @@ Use the project's wrapper: `script/worktree create <name>`. It provisions an iso
311
311
  database and copies `.env.local`. Never call `git worktree add` directly.
312
312
  ```
313
313
 
314
+ A `## cleanup` section lists what gatekeep-pr runs after a PR is merged, approved, or found already merged or closed, in order, after the implied git teardown (worktree, local branch, base fast-forward):
315
+
316
+ ```markdown
317
+ ## cleanup
318
+ - close ticket: gh issue close <issue> --comment "Delivered in <pr>, merged <merge-sha>."
319
+ - release: /skill:release
320
+ ```
321
+
322
+ Each item is `<label>: <command or /skill:... invocation>`. Placeholders `<issue>`, `<pr>`, and `<merge-sha>` are filled from the gate's digest; an item whose placeholder has no value is skipped with a reason, and items that need a landed change (`<merge-sha>`, `/skill:release`) are skipped when the PR was only approved or closed. A `/skill:` item keeps its own gates; anything else runs as a shell command in the primary checkout under the PR gate's `timeout minutes`.
323
+
314
324
  Beyond `## conventions` and skill-named sections, headings a skill reads by name are documented in their owning skills; other headings retain topic/workflow-convention matching. The override file is read by skill instructions and, for its `## Spec index` section only, by `gauntlet-spec-index`; the Pi runtime itself never reads it. Missing or empty `## conventions` adds no rules; a lower-priority file cannot supplement the selected file.
315
325
 
316
326
  **Discovery ladder:** skills check three locations, in order, and use the first one found - never merged: `.pi/gauntlet-overrides.md`, then `<repo root>/gauntlet-overrides.md`, then `<repo root>/doc/gauntlet-overrides.md` (`<repo root>` = `git rev-parse --show-toplevel`, or the current directory outside a repo). Pick one location per repo.
@@ -457,7 +467,7 @@ Everything else follows the shipped baseline rubric.
457
467
  A consumer repo that wants its own trigger phrases for the pre-merge gate (e.g. "gate
458
468
  this PR", "ready to merge?") adds a wrapper skill that carries **zero data** - only a
459
469
  name, its trigger phrases, and an instruction to follow `/skill:gatekeep-pr`. All
460
- customization lives in two places, never in the wrapper itself:
470
+ customization lives in three places, never in the wrapper itself:
461
471
 
462
472
  - **`REVIEW.md`** - the review rubric (see above).
463
473
  - **The gauntlet overrides file, `## PR gate` section** - everything operational:
@@ -474,6 +484,8 @@ customization lives in two places, never in the wrapper itself:
474
484
  - merge policy: squash | merge-commit # optional
475
485
  ```
476
486
 
487
+ - **The gauntlet overrides file, `## cleanup` section** - the ordered post-gate steps the `cleanup` menu row runs (contract under Project-specific overrides above).
488
+
477
489
  An existing `## verification-before-completion` overrides section is an accepted
478
490
  equivalent source for the verification command only; all other PR-gate keys
479
491
  still live under `## PR gate`. A `## comms style` section in the same overrides
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "7.11.2",
3
+ "version": "7.12.0",
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",
@@ -102,19 +102,24 @@ Fail closed: a dispatch error, an async handle, a silence-kill, or a missing or
102
102
 
103
103
  ## 4. Human batch - one menu
104
104
 
105
- Render only escalated and prefiltered items; `<N>` counts them. Nothing symbol-dense above the fold; each item's `old -> new` sits under `Details`, after the footer.
105
+ Decide every recommendation and its reason before writing the menu text: the menu text is written after the recommendation is decided, never while deciding it. Never leak deliberation: `recommended: custom? Hmm. Dispositions: fix-now ... Actually, the amended spec makes G2 ...`.
106
+
107
+ Render only escalated and prefiltered items; `<N>` counts them. Build the opening sentence from the prepared item's `what` and `why`; take the human-call sentence from the prefilter rule or reviewer's escalate reason in plain words, with `Reviewer:` verbatim beneath it. Nothing symbol-dense above the fold; each item's `old -> new` sits under `Details`, after the footer. This menu's grammar governs the amendment batch only; brainstorming's questionary formats (the labeled recommendation question and the ticket cut question) keep their own rules - including the cut question's re-ask after a cancelled repair, re-rendered in full with the row unchanged.
106
108
 
107
109
  ```
108
110
  Spec amendments: <N> need your call - from <trigger>; applying as recommended <reopens | adds | removes> <task ids | no tasks>; no phase change.
109
111
 
110
- * <handle> - <title>: <what>. <why>.
111
- Example: <before -> after>
112
- Reviewer: <one-line reason verbatim | not reviewed - <prefilter rule> | reviewer unavailable: <reason>>
113
- Impact: <spec section>: <approved contract> -> <new contract>; ...
114
- Recommended: <accept | alt-n> (<one-clause why>).
115
- Alternatives: alt-1 <one line>; alt-2 <one line>
116
-
117
- Reply: 1 (apply all recommendations) | 2: <handle>=<accept|alt-n|custom(<effect>)>, ...
112
+ 1. <title> - <what>. <why>
113
+ <why this is a human call: the prefilter rule or the reviewer's escalate reason in plain words>
114
+ Example: <before -> after>
115
+ Reviewer: <one-line reason verbatim | not reviewed - <prefilter rule> | reviewer unavailable: <reason>>
116
+ Impact: <spec section>: <approved contract> -> <new contract>; ...
117
+ A) accept - the spec reads the new text
118
+ B) alt-1 - <the alternative's effect, one line>
119
+ Custom: <your own answer> - executed as given; may redirect away from the spec
120
+ Recommended: A - <one-clause why>
121
+
122
+ Reply with one line per item: `1. A`, `2. <your own text>`. Bare `1` takes every recommendation.
118
123
  Standing grant: reply "auto-apply amends" - every later spec amendment in this flow (corrected facts, paths, verification lines, and scope, acceptance-criteria, or public-contract edits alike) applies without asking; only a redraw (changed problem statement, component added, removed, or re-bounded) still stops for you, and the grant never stands in for a spec approval.
119
124
 
120
125
  Details
@@ -125,7 +130,9 @@ Details
125
130
 
126
131
  `Reviewer:` quotes the `<one-line reason>` of the section 3 reply verbatim; the `probed:` half stays in the commit body; a prefiltered item carries `not reviewed - <the rule that prefiltered it>`. `Impact:` restates the design-contract shift from `location` in the reader's words, never quoted spec text, one clause per touched decision, `;`-joined; `(none) -> <new>` for a contract added, `<old> -> (removed)` for a contract removed. `Details` keeps the verbatim `old -> new`.
127
132
 
128
- `Alternatives:` appears only when genuine ones exist; otherwise the item's choices are exactly `accept` and `custom(...)`. Reply grammar: `1` applies every recommendation; `2:` overrides the named handles, omitted handles keep theirs, a handle at most once; `custom(<effect>)` is free text and may redirect anywhere ("keep the spec, fix the parser"). A redirect away from the spec drops the item (still recorded in the batch commit body as `custom(<effect>)`) and returns the finding to its calling loop. Invalid handle or choice -> reprompt for that item only, keep every valid pick, never reopen the gate; a bare `2` without a colon is an invalid tier-2 reply (reprompt the same way) and never a grant. Take no action before the reply.
133
+ Letter options in fixed order: `A) accept`, then `B) alt-1`, `C) alt-2` in index order, only for genuine alternatives. Give each option its one-line effect; tie the recommendation's reason to the end goal or a development practice. Record a letter as its shown option's token. Keep custom unlettered as shown. Keep an unavailable option visible and unlettered: `Not available: <option> - <missing prerequisite>`. Use exactly `Recommended: <letter> - <reason>` or `No recommendation - pick one`; the prepared-item schema requires a recommendation, so this amendment menu never renders the second form.
134
+
135
+ Reply grammar: one line per item, `<n>. <letter or free text>`; bare `1` applies every recommendation and re-asks any item without one by number, never re-rendering the menu (vacuous here: the prepared-item schema always supplies a recommendation); an omitted item keeps its recommendation; free text is `custom(<text>)` and may redirect anywhere ("keep the spec, fix the parser") - a redirect drops the item (still recorded in the batch commit body as `custom(<effect>)`) and returns the finding to its calling loop; a letter always picks its shown option, even when free text would spell its name. A legacy `2: <handle>=<choice>` reply is accepted silently as the same picks; the form is never rendered in a prompt. Invalid number or letter -> reprompt for that item only, keep every valid pick, never reopen the gate; a bare digit other than `1` is an invalid reply and never a grant (reprompt the same way). A bare letter or bare `1` mixed with numbered lines is invalid; free text matching an option's name remains `custom(<text>)`. Resolve every item before applying; retain original numbers during reprompts. Take no action before the reply. After the reply lands and the batch applies, print one confirmation line `Recorded: <handle>=<choice>, ...` covering the batch's decided items (a custom choice keeps the reply's free text verbatim; grant-applied items record their recommended choice); this is the only place `<handle>=<choice>` appears to the human.
129
136
 
130
137
  ## 5. Apply, aftermath, commit
131
138
 
@@ -153,7 +160,7 @@ Called from `finishing-a-development-branch` Step 3.5, before the carried-open m
153
160
  1. Draft an item (step 1) for each gap with `recommended: accept`, verdict `DRIFTED` or `PARTIAL`, not `UNAUTHORIZED`, whose `origin` is not an acceptance criterion - the `accept-into-spec` edit built from its `origin` + `evidence`. Every other gap skips the funnel and stays a menu row.
154
161
  2. Review (step 3), apply and commit (step 5).
155
162
  3. Re-audit against the amended spec; regenerate the inventory. Only concerns the re-audit closed drop out; sibling concerns keep their rows.
156
- 4. Render the disposition menu for what remains - escalated items are ordinary rows there, never a second menu. Rows whose recommended disposition edits the spec carry the readable card fields (`what`, `why`, `Example:`, `Reviewer:`, `Impact:`) on the bullet, adapted to the disposition bullet grammar. A human-selected spec-changing disposition (`accept-into-spec`, `rescope-into-spec`, state-changing `custom`) is already approved: it applies at the protocol's execute-order step 2, bypasses steps 2-4 of this surface, and is recorded as today (`Gn - <title>: <disposition>`); an auto-applied item is recorded `Gn - <title>: accept-into-spec (auto-applied)`.
163
+ 4. Render the disposition menu for what remains - escalated items are ordinary rows there, never a second menu. Rows whose recommended disposition edits the spec carry the readable card fields (`what`, `why`, `Example:`, `Reviewer:`, `Impact:`) on the item, adapted to the numbered item render. A human-selected spec-changing disposition (`accept-into-spec`, `rescope-into-spec`, state-changing `custom`) is already approved: it applies at the protocol's execute-order step 2, bypasses steps 2-4 of this surface, and is recorded as today (`Gn - <title>: <disposition>`); an auto-applied item is recorded `Gn - <title>: accept-into-spec (auto-applied)`.
157
164
 
158
165
  ## Worked example
159
166
 
@@ -175,24 +182,33 @@ The reviewer clears items 1-5; nothing is applied yet. The batch renders items 6
175
182
  ```
176
183
  Spec amendments: 3 need your call - from Task 7 BLOCKED; applying as recommended reopens Tasks 4, 6; no phase change.
177
184
 
178
- * scope - ROP feed out of scope: adds an out-of-scope line for the ROP feed to Non-goals. Drops a deliverable you approved.
179
- Example: regulator feed = NERC filings + ROP announcements -> NERC filings only
180
- Reviewer: not reviewed - removes approved scope
181
- Impact: Non-goals: the ROP feed ships this release -> (removed)
182
- Recommended: accept (the ROP source has no stable page this release).
183
- * count - Fewer announcements per fetch: the acceptance criterion drops from at least 3 to at least 1. Lowers the bar you set.
184
- Example: 3 items per fetch -> 1
185
- Reviewer: not reviewed - acceptance-criteria location
186
- Impact: Acceptance criteria: a fetch yields three or more announcements -> one or more
187
- Recommended: accept (the captured fixture has one item; the criterion assumed three).
188
- * bytes - Fixture byte count: the verification line changes from 41,208 to 43,117 bytes. The verification line would assert a size nobody measured.
189
- Example: 41,208 bytes -> 43,117 bytes
190
- Reviewer: rubric (a) fails - no measurement cited for the new byte count
191
- Impact: Verification: the fixture measures 41,208 bytes -> 43,117 bytes
192
- Recommended: alt-1 (measure first; apply whatever `wc -c` reports).
193
- Alternatives: alt-1 replace the number with the `wc -c` result
194
-
195
- Reply: 1 (apply all recommendations) | 2: <handle>=<accept|alt-n|custom(<effect>)>, ...
185
+ 1. Fewer announcements per fetch - the acceptance criterion drops from at least 3 to at least 1. Lowers the bar you set.
186
+ Only you can change the acceptance criterion.
187
+ Example: 3 items per fetch -> 1
188
+ Reviewer: not reviewed - acceptance-criteria location
189
+ Impact: Acceptance criteria: a fetch yields three or more announcements -> one or more
190
+ A) accept - the spec reads the new text
191
+ Custom: <your own answer> - executed as given; may redirect away from the spec
192
+ Recommended: A - the captured fixture has one item; the criterion assumed three
193
+ 2. Fixture byte count - the verification line changes from 41,208 to 43,117 bytes. The verification line would assert a size nobody measured.
194
+ The reviewer cannot clear a correction without a cited measurement.
195
+ Example: 41,208 bytes -> 43,117 bytes
196
+ Reviewer: rubric (a) fails - no measurement cited for the new byte count
197
+ Impact: Verification: the fixture measures 41,208 bytes -> 43,117 bytes
198
+ A) accept - the spec reads the new text
199
+ B) alt-1 - replace the number with the `wc -c` result
200
+ Custom: <your own answer> - executed as given; may redirect away from the spec
201
+ Recommended: B - measure first so the verification line matches the fixture
202
+ 3. ROP feed out of scope - adds an out-of-scope line for the ROP feed to Non-goals. Drops a deliverable you approved.
203
+ Only you can remove approved scope.
204
+ Example: regulator feed = NERC filings + ROP announcements -> NERC filings only
205
+ Reviewer: not reviewed - removes approved scope
206
+ Impact: Non-goals: the ROP feed ships this release -> (removed)
207
+ A) accept - the spec reads the new text
208
+ Custom: <your own answer> - executed as given; may redirect away from the spec
209
+ Recommended: A - the ROP source has no stable page this release
210
+
211
+ Reply with one line per item: `1. A`, `2. <your own text>`. Bare `1` takes every recommendation.
196
212
  Standing grant: reply "auto-apply amends" - every later spec amendment in this flow (corrected facts, paths, verification lines, and scope, acceptance-criteria, or public-contract edits alike) applies without asking; only a redraw (changed problem statement, component added, removed, or re-bounded) still stops for you, and the grant never stands in for a spec approval.
197
213
 
198
214
  Details
@@ -201,4 +217,4 @@ count: Acceptance criteria - old: at least 3 announcements per fetch -> new: at
201
217
  bytes: Verification - old: fixture is 41,208 bytes -> new: fixture is 43,117 bytes
202
218
  ```
203
219
 
204
- Reply `2: scope=custom(keep scope; fix the parser instead)` drops `scope`, returns it to the fix loop, and applies `count` and `bytes` as recommended.
220
+ Reply `3. keep scope; fix the parser instead` drops item 3 (recorded `scope=custom(keep scope; fix the parser instead)`), returns it to the fix loop, and applies items 1 and 2 as recommended, recorded `count=accept`, `bytes=alt-1`.
@@ -90,32 +90,60 @@ This is an **enforced disposition gate**, not a surface-only notice. The user is
90
90
  Closure / conformance: CONFORMS
91
91
  ```
92
92
 
93
- then continue directly to Step 4. No approval prompt, no menu, no shared options line, no sign-off. If the run auto-applied fixes, surface the flat `auto-applied fix commits: <Gn: SHA>, ...` index from the durable block as **one informational, non-blocking line** with a one-line revert offer (see "Revert semantics") - a gap that auto-converged mid-verify has no bullet, so this index is the only place its fix commit stays revertable. Do not wait for acknowledgment.
93
+ then continue directly to Step 4 and render its menu in full, options included. Place no approval prompt, conformance menu, or sign-off between the CONFORMS line and that menu. The render never stops at Step 4's question line; the numbered options are part of the same message. If the run auto-applied fixes, surface the flat `auto-applied fix commits: <Gn: SHA>, ...` index from the durable block as **one informational, non-blocking line** with a one-line revert offer (see "Revert semantics") - a gap that auto-converged mid-verify has no bullet, so this index is the only place its fix commit stays revertable. Do not wait for acknowledgment.
94
94
 
95
95
  When the sentinel carries a `happy-path:` line, print it directly under `Closure / conformance: CONFORMS` as one informational, non-blocking line in the same shape as the `auto-applied fix commits` line: `happy-path: passed`, `happy-path: failed - unattributable`, or `happy-path: not run - <reason>` (a `failed - attributed to G<n>` value cannot reach this branch: its gaps are open). When the sentinel carries a `Deferred/deviates per spec:` line, print it verbatim as one informational line directly under the `happy-path:` line when present, else directly under `Closure / conformance: CONFORMS` or the `Conformance: N decisions needed before shipping.` header, and above the `whole-diff minors` line (both paths).
96
96
 
97
97
  **Pre-menu amendment funnel (GAPS only).** Before rendering the carried-open menu, `read` and run [`amendment-surface.md` § Conformance entry](../brainstorming/reference/amendment-surface.md) over the inventory once: gaps with `recommended: accept`, verdict `DRIFTED` or `PARTIAL`, not `UNAUTHORIZED`, whose `origin` is not an acceptance criterion are drafted as `accept-into-spec` items (the edit built from `origin` + `evidence`) and sent to the reviewer in one call; cleared items apply and land as one batch commit, the spec is re-audited, the inventory regenerated. Only concerns the re-audit actually closed drop out; sibling concerns in the same gap keep their rows and dispositions. Survivors and every other gap render as ordinary rows below - one menu, never two.
98
98
 
99
- **Carried-open (`status: GAPS (N open)`).** Read `reference/disposition-protocol.md` and follow it for the carried-open render (dense) grammar, the response grammar, and the 9-step execute order. Render the human decision menu in the shape below, with the sentinel's `happy-path:` line (when present) printed as one informational line directly under the `Conformance: N decisions needed before shipping.` header - a `failed - attributed to G<n>,...` value names the rows whose evidence is the transcript, a `rescope` recommendation on such a row means the failure lies in code the change never owned - drive the dispositions per that reference, then print the summary render and continue to Step 4. If that reference file cannot be read, stop and surface a blocking error — do **not** improvise the grammar from memory.
99
+ **Carried-open (`status: GAPS (N open)`).** Read `reference/disposition-protocol.md` and follow it for the carried-open render grammar, the response grammar, and the 9-step execute order. Render the human decision menu in the shape below; print the `Conformance: N decisions needed before shipping.` header below the item list, followed by the sentinel's informational lines (`happy-path:`, `Deferred/deviates per spec:`, `whole-diff minors:`) when present, in that order, above the reply instruction - a `failed - attributed to G<n>,...` value names the rows whose evidence is the transcript, a `rescope` recommendation on such a row means the failure lies in code the change never owned - drive the dispositions per that reference, then print the summary render and continue to Step 4. If that reference file cannot be read, stop and surface a blocking error — do **not** improvise the grammar from memory.
100
100
 
101
101
  Representative carried-open render (multi-concern gap split to `e2e`; single-concern gap `cache`; `UNAUTHORIZED` gap `auth`):
102
102
 
103
103
  ```
104
- Conformance: 3 decisions needed before shipping.
105
-
106
- * e2e - Source-image E2E validation: not run; blocked (HYDRA1.png, HTTP 401).
107
- Still in scope for this branch? Recommended: rescope (defer until image available) (fix-now N/A: needs HYDRA1.png).
108
- * cache - Cache coverage: implemented but the spec is silent on it.
109
- In scope? Recommended: accept into spec (behavior is intentional).
110
- * auth - Unrequested admin bypass: adds an unlisted route. Should this unrequested behavior become part of the current workflow? Recommended: fix-now = remove it (rescope N/A: scope creep).
104
+ 1. Source-image E2E validation - not run; blocked (HYDRA1.png, HTTP 401), so the branch ships without its end-to-end evidence.
105
+ The spec requires this validation, but HYDRA1.png is unavailable; decide whether it stays in scope for this branch.
106
+ what: The spec's Verification section gains a line deferring the source-image E2E run until the image is available.
107
+ why: The branch cannot produce that evidence without the image.
108
+ Example: Run the packed-image E2E before merge -> deferred until HYDRA1.png is available
109
+ Reviewer: not reviewed - the pre-menu funnel drafts only recommended-accept items
110
+ Impact: Verification: E2E evidence required before merge -> deferred until the image lands
111
+ A) accept - Ratify shipping without the end-to-end evidence in the spec.
112
+ B) rescope - Defer the validation requirement in the spec until the image is available.
113
+ C) follow-up - Transfer validation to separately owned work with a tracked ticket.
114
+ Custom: <your own answer> - Execute your disposition after clarifying its effect.
115
+ Recommended: B - Defer until the image is available.
116
+ Not available: fix-now - needs HYDRA1.png
117
+
118
+ 2. Cache coverage - implemented but the spec is silent on it, leaving the intended contract unclear.
119
+ Spec and code disagree; decide whether the implemented behavior is in scope.
120
+ what: The spec's Design section gains a clause ratifying the implemented cache coverage.
121
+ why: The behavior is intentional and the spec is silent.
122
+ Example: spec silent on cache coverage -> Design states the cache covers the rendered menu path
123
+ Reviewer: not reviewed - origin quotes an acceptance criterion
124
+ Impact: Design: no cache-coverage contract -> cache coverage is ratified as specified behavior
125
+ A) fix-now - Align the implementation with the current spec.
126
+ B) accept - Ratify the implemented cache behavior in the spec.
127
+ C) rescope - Explicitly remove or defer the origin requirement in the spec.
128
+ D) follow-up - Transfer the concern to separately owned work with a tracked ticket.
129
+ Custom: <your own answer> - Execute your disposition after clarifying its effect.
130
+ Recommended: B - The behavior is intentional.
131
+
132
+ 3. Unrequested admin bypass - adds an unlisted route, expanding the workflow beyond approved scope.
133
+ Should this unrequested behavior become part of the current workflow?
134
+ A) fix-now - Remove the unrequested admin bypass.
135
+ B) accept - Ratify the bypass as approved scope in the spec.
136
+ C) follow-up - Transfer the decision or removal task to separately owned work with a tracked ticket.
137
+ Custom: <your own answer> - Execute your disposition after clarifying its effect.
138
+ Recommended: A - Remove the unrequested behavior.
139
+ Not available: rescope - scope creep
111
140
 
112
- Other options per item: fix-now / accept / rescope / follow-up / custom.
141
+ Conformance: 3 decisions needed before shipping.
113
142
 
114
- 1. Go with recommended
115
- 2. Recommended except <handle>=<choice> e.g. "2: cache=follow-up"
143
+ Reply with one line per item: `1. C`, `2. A`. Bare `1` takes every recommendation and re-asks items without one by number.
116
144
  ```
117
145
 
118
- A single-concern render is identical minus the split: one bullet whose handle is the gap ID or word, no sibling.
146
+ A single-concern render is identical minus the split: one numbered item, no siblings.
119
147
 
120
148
  Execute the chosen dispositions per `reference/disposition-protocol.md` (Execute order), then print the closure summary and continue to Step 4:
121
149
 
@@ -1,34 +1,39 @@
1
1
  # Carried-open disposition protocol
2
2
 
3
- Consumed by `finishing-a-development-branch` Step 3.5 on the **GAPS branch only** — the CONFORMS fast path never reads this file. Antecedent map for the relocated grammar below: "the reference" = `../../verification-before-completion/reference/conformance-check.md` (canonical for the availability table, the `UNAUTHORIZED` question, the `recommended: none` preflight, the freshness rule, and the concern-scoped fix projection); "Revert semantics", "the zero-gap path", and every `Step N` (`Step 1`, `Step 3.5`, `Step 4`) reference = `../SKILL.md`.
3
+ Consumed by `finishing-a-development-branch` Step 3.5 on the **GAPS branch only** — the CONFORMS fast path never reads this file. Antecedent map for the relocated grammar below: "the reference" = `../../verification-before-completion/reference/conformance-check.md` (canonical for the availability table, the `UNAUTHORIZED` question, the `recommended: none` rule, the freshness rule, and the concern-scoped fix projection); "Revert semantics", "the zero-gap path", and every `Step N` (`Step 1`, `Step 3.5`, `Step 4`) reference = `../SKILL.md`.
4
4
 
5
- **Carried-open render (dense).** Read the `## Closure / conformance` block. Render a header with the decision count, one bullet per decision unit (a gap by default; a `Gn/Cn` concern only where the reference split it), then the shared options line, then the recommended-set reply. Never show durable-card internals (ownership, evidence tokens, identity, hashes) in the render.
5
+ **Carried-open render (numbered brief).** Read the `## Closure / conformance` block. The menu text is written after the recommendation is decided, never while deciding it. Negative example: `recommended: custom? Hmm. Dispositions: fix-now ... Actually, the amended spec makes G2 ...`.
6
6
 
7
- Each bullet:
7
+ Open with item 1's plain-words problem sentence, never metadata. Render one numbered item per decision unit (a gap by default; a `Gn/Cn` concern only where the reference split it):
8
8
 
9
- `* <handle> - <plain title>: <what's unresolved, one clause>. <short question> Recommended: <choice> (<one-clause why>).`
9
+ ```text
10
+ <n>. <plain title> - <what is unresolved and what it costs, one sentence>.
11
+ <why this is a human call, one sentence>.
12
+ A) <option> - <one-line consequence>
13
+ B) <option> - <one-line consequence>
14
+ Custom: <your own answer> - <the availability table's custom effect, one line>
15
+ Recommended: <letter> - <reason>
16
+ Not available: <option> - <missing prerequisite>
17
+ ```
10
18
 
11
- - `<handle>` leads the bullet and is a short unique human word derived from the title (`Cache coverage` -> `cache`); on collision append a digit. It is the token option 2 targets. When a gap split and no clean word fits, use the bare `Gn/Cn`; a single-concern gap uses its gap ID `Gn`.
12
- - The shared options line sits below the bullets: `Other options per item: fix-now / accept / rescope / follow-up / custom`, listing the options **generally available across items**. When a specific item's availability deviates - an option unavailable for it, or an `UNAUTHORIZED` item whose `rescope` is unavailable and whose `fix-now` means removal - note that deviation as a short parenthetical on **that item's bullet** (one clause, not a block), e.g. `(rescope N/A: scope creep)`. The shared line appears **only in the carried-open render**, never in the zero-gap path. Full per-option effects only on request, or when option 2 targets an unclear choice.
13
- - Group items under one recommended line only when they share a disposition and rationale; each grouped handle repeats its title.
14
- - A bullet whose recommendation edits the spec (`accept-into-spec`, `rescope-into-spec`, or an item the pre-menu funnel escalated) carries, indented under it, `what` and `why` as one sentence each, `Example: <before -> after>`, `Reviewer: <one-line reason verbatim | not reviewed - <the Conformance entry step 1 exclusion that kept it out> | reviewer unavailable: <reason>>`, and `Impact: <spec section>: <approved contract> -> <new contract>; ...` - the readable card from `../../brainstorming/reference/amendment-surface.md`, adapted to this bullet. Escalated items are ordinary rows here; no second amendment menu renders.
15
- - Availability per concern comes from the reference's single availability table - apply it against current context (worktree state, `maxFixRounds`, ownership, resource accessibility), do not restate it. `UNAUTHORIZED` bullets ask the reference's question verbatim (`Should this unrequested behavior become part of the current workflow?`); `rescope-into-spec` is shown **unavailable** (not dropped) and `fix-now` means **removal** of the unrequested code.
16
- - `revert conformance fix Gn`, when the gap has an auto-applied fix, renders on the shared options line as a **separate one-off action** - never inside a bullet's recommendation and never in the option-2 list. Name the parent gap and warn that revert undoes the entire gap-level commit (see "Revert semantics").
19
+ - Build the problem sentence from the concern card's existing `unresolved` and `impact`; invent no sentence sources at render time. Derive the human-call sentence from the verdict: `UNAUTHORIZED` asks `Should this unrequested behavior become part of the current workflow?` verbatim; `MISSING`/`PARTIAL` says the spec requires it and implementation is incomplete or blocked, naming the missing prerequisite; `DRIFTED` says spec and code disagree and only the human can say which is the contract. For a fix gap carried open because the fix loop cannot run (`maxFixRounds: 0`, no named-branch worktree, unknown ownership), say so and name the prerequisite.
20
+ - For a spec-editing recommendation (`accept-into-spec`, `rescope-into-spec`) or a pre-menu escalated item, indent the readable card between the problem sentence and lettered options: `what` and `why` as one sentence each, `Example: <before -> after>`, `Reviewer: <one-line reason verbatim | not reviewed - <the Conformance entry step 1 exclusion that kept it out> | reviewer unavailable: <reason>>`, and `Impact: <spec section>: <approved contract> -> <new contract>; ...`. Adapt the card from `../../brainstorming/reference/amendment-surface.md`; keep escalated items here, not in a second menu.
21
+ - Apply the reference's single availability table against current context (worktree state, `maxFixRounds`, ownership, resource accessibility); do not restate it. Letter available named options in table row order (`fix-now`, `accept`, `rescope`, `follow-up`), skipping unavailable ones. Lettering follows availability only - a `No recommendation - pick one` item letters its available options like any other. Give every lettered option the table's one-line effect for this verdict. Keep letters stable for the shown menu; map each pick to its shown disposition token. Use the menu's short forms `accept` and `rescope` for the canonical `accept-into-spec` and `rescope-into-spec`.
22
+ - Keep one unlettered `Custom:` line per item as shown. Keep unavailable options visible and unlettered as `Not available: <option> - <missing prerequisite>`; render `UNAUTHORIZED` rescope as `Not available: rescope - scope creep`, and its `fix-now` consequence as removal of the unrequested code. Show no bare option tokens.
23
+ - Use exactly `Recommended: <letter> - <reason>`, with a one-sentence reason tied to the end goal or a development practice, or `No recommendation - pick one` when no named choice is executable. Never recommend custom.
24
+ - Below the item list, render `Conformance: N decisions needed before shipping.` (when the count is 1, write `Conformance: 1 decision needed before shipping.`), then the informational `happy-path:`, `Deferred/deviates per spec:`, and `whole-diff minors:` lines in their current relative order, above the reply instruction: `Reply: one line per item: <n>. <letter or free text>; bare "1" takes every recommendation and re-asks items without one by number.` Keep handles and durable-card internals (ownership, evidence tokens, identity, hashes) out of the prompt.
25
+ - Render `revert conformance fix Gn` for an indexed auto-applied fix on its own line outside the items, never lettered. Name the parent gap and warn that revert undoes the entire gap-level commit (see "Revert semantics").
17
26
 
18
27
  #### Response grammar
19
28
 
20
- ```
21
- 1 -> apply every recommendation
22
- 2: cache=follow-up
23
- 2: e2e=custom(open ticket after image lands), cache=follow-up
24
- ```
29
+ One reply line per item: `<n>. <letter or free text>`. Free text is always the custom path (`custom(<text>)`), even when it matches a shown option's name - letters pick options, text instructs. No other forms: no bare letters, no mixing bare `1` with item lines.
25
30
 
26
- - `1` (or `apply recommended`) applies all recommendations.
27
- - `2:` takes a comma-separated override list, each `<handle>=<choice>`; omitted items keep their recommendation. A handle may appear at most once (repeat = invalid).
28
- - `custom(<concrete effect>)` supplies an inline effect. Manual fix-in-place is expressed only as `custom(...)` where isolated `fix-now` is unavailable.
29
- - `recommended: none` items follow the reference's preflight (linked, not restated): the item needs a `<handle>=custom(...)` decision, and option 1 is withheld until every open item has an executable recommendation; after the custom decision the menu re-renders for the remainder.
30
- - `revert conformance fix Gn` is a valid standalone reply, mutually exclusive with `1` and `2:`; it never appears inside a `2:` override list.
31
- - Invalid handle or choice -> focused reprompt naming only that item, retaining every valid pick and never reopening the gate. Unknown token: list the valid titled handles. Known item, bad choice: repeat its title + its available choices.
31
+ - Bare `1` (or `apply recommended`, accepted, never advertised) applies every item that has a recommendation and re-asks only the items without one, by number, never re-rendering the whole menu.
32
+ - An omitted item keeps its recommendation; an omitted no-recommendation item is re-asked by number, same as after a bare `1`.
33
+ - A legacy `2: <handle>=<choice>` reply is accepted silently as the same picks and recorded normally; the form is never rendered in a prompt.
34
+ - An invalid number or letter gets a focused reprompt naming only that item, retaining every valid pick, never reopening the gate. A bare digit other than `1` is an invalid reply and reprompts the same way.
35
+ - `revert conformance fix Gn` is a standalone reply, mutually exclusive with numbered lines and bare `1`; it never takes a letter and never waits on undecided items - the decide-everything-first barrier binds disposition batches only. Run execute-order step 8 without applying any recommendation.
36
+ - Nothing in a disposition batch executes until every item has a decision; then the execute order below runs once. Keep original item numbers through re-asks; no re-audit runs before the batch is decided.
32
37
 
33
38
  #### Execute order
34
39
 
@@ -44,4 +49,4 @@ Take **no** disposition action before the reply. Then, once, in order:
44
49
  8. **revert** (`revert conformance fix Gn`): light-revert the indexed commit, re-run Step 1's canonical tests; on failure stop; on pass re-audit and regenerate.
45
50
  9. Re-enter Step 3.5 with the re-audited block if any concern remains open.
46
51
 
47
- Record every final disposition with its **stable ID** as `Gn/Cn - <title>: <disposition>` (or `Gn - <title>: <disposition>` for a single-concern gap) - ticket ID/URL for `follow-up`, result for `custom`; never relabel a `custom` result as a recommendation. This durable record is the machine/audit surface; the interactive render stays handle-based. Then continue to Step 4:
52
+ Record every final disposition with its **stable ID** as `Gn/Cn - <title>: <disposition>` (or `Gn - <title>: <disposition>` for a single-concern gap) - ticket ID/URL for `follow-up`, result for `custom`; never relabel a `custom` result as a recommendation. This durable record is the machine/audit surface; keep canonical disposition tokens. After the reply, print each decided item's short-form choice in `Recorded: <handle>=<choice>, ...` (e.g. `Recorded: cache=accept, e2e=rescope`); keep custom choices as `custom(<text>)` with the reply's free text verbatim. Derive handles internally from a short unique word in the title; append a digit on collision. When no clean word fits, use `Gn/Cn` for a split concern or `Gn` for a single-concern gap. Use `<handle>=<choice>` only in this record line and silently accepted legacy replies, never in a prompt. Then continue to Step 4:
@@ -62,7 +62,8 @@ These bind every rendered report, menu, and external payload (review bodies, rep
62
62
  - 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`.
63
63
  - 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`.
64
64
  - Raw command output, drafted payloads except the exact description consent preview, or internal IDs in the rendered report - owner: `reference/report.md`.
65
- - An open-PR menu, other than the conflict menu, an own-merge or merge-queued menu, a pending contract-exception menu, or a `--rebase` terminal stop (`stop` and `show evidence` only, `reference/sync.md`), without `fix` while an open blocker exists, or without `fix nits` while a nit exists, when a helper facility exists and the fix-round cap is neither `0` nor reached (a PR already merged or closed at step 1 offers `show evidence` and `stop` only); any menu without `show evidence`, with two `[recommended]`, or with `stop` not last - owner: `reference/decision-menu.md`.
65
+ - An open-PR menu, other than the conflict menu, a terminal overlay (`reference/decision-menu.md`), a pending contract-exception menu, or a `--rebase` terminal stop (`stop` and `show evidence` only, `reference/sync.md`), without `fix` while an open blocker exists, or without `fix nits` while a nit exists, when a helper facility exists and the fix-round cap is neither `0` nor reached (a PR already merged or closed at step 1 offers `cleanup`, `show evidence`, and `stop` only); any menu without `show evidence`, with two `[recommended]`, or with `stop` not last - owner: `reference/decision-menu.md`.
66
+ - `cleanup` rendered on an ordinary open-PR menu, or its steps run without a pick - owner: `reference/cleanup.md`.
66
67
  - A CI wait while a blocker is open or before the first report, or a second push inside one round - owner: `reference/post-selection-loop.md` `### Pre-menu refresh`.
67
68
  - The orchestrator resolving a merge conflict itself, or editing or committing a tracked file during a fix wave - owner: `reference/fix-wave.md` `## Conflicts` and `## Wave`.
68
69
  - A helper's duty run in the orchestrator's own context - owner: `## Harness notes`.
@@ -74,4 +75,4 @@ These bind every rendered report, menu, and external payload (review bodies, rep
74
75
 
75
76
  ## Project overrides
76
77
 
77
- 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. 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.
78
+ 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. 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`).
@@ -10,7 +10,7 @@ Read from SKILL.md step 2. Input: the step-1 digest. Output: a provisioned workt
10
10
  | The default path `.worktrees/pr-<N>` exists but holds a different branch | STOP and surface; never repurpose | STOP and surface; never repurpose | - |
11
11
  | Nothing exists (an overrides worktree wrapper can relocate it, following `using-git-worktrees` conventions, gitignore-first) | Create at `.worktrees/pr-<N>`; `git fetch origin` + `git worktree add .worktrees/pr-<N> <headRefName>`; verify post-checkout that HEAD == the digest's `headRefOid` | Create at `.worktrees/pr-<N>`; `git fetch origin pull/<N>/head:pr-<N>` first, then add on that local branch; verify post-checkout that HEAD == the digest's `headRefOid` | - |
12
12
 
13
- Record create-vs-reuse; it drives teardown (`post-selection-loop.md` `### Teardown`).
13
+ Record the digest's `worktree` block (`../verification-brief.md`): `path`, `branch` (the actual local branch - `headRefName`, `pr-<N>`, or whatever a wrapper created), `created` (true when this run created it), and `destroy` (the exact destroy invocation when a `worktree wrapper` created it, else null). `cleanup.md` reads it; `post-selection-loop.md` `### Teardown` points there.
14
14
 
15
15
  Then set the digest's `permissions.head_pushable` (`../verification-brief.md` Section A): `git -C <worktree> push --dry-run --no-verify --no-follow-tags <head_url> HEAD:refs/heads/<head_ref>` exit 0 is `true`, a non-zero exit is `false`, when `head_url` is `unreadable`, `head_pushable` is `unreadable` and the probe does not run. The probe records observed authorization; a real push the server rejects (branch protection) is reported as that push's failure.
16
16
 
@@ -21,7 +21,7 @@ When the digest reported `mergeable: UNKNOWN` or `mergeStateStatus: UNKNOWN`, re
21
21
  Resolve per concern, first match wins, evaluated unconditionally - never delegated to a wrapper skill:
22
22
 
23
23
  1. **Repo root `REVIEW.md`** (rubric concerns only). Wins over the shipped baseline and reviewer-persona defaults on any conflict.
24
- 2. **Gauntlet overrides file** (`.pi/gauntlet-overrides.md`, `<repo root>/gauntlet-overrides.md`, `<repo root>/doc/gauntlet-overrides.md`; first found wins): the `## PR gate` section below. An existing `## verification-before-completion` section is an accepted equivalent source for the verification command.
24
+ 2. **Gauntlet overrides file** (`.pi/gauntlet-overrides.md`, `<repo root>/gauntlet-overrides.md`, `<repo root>/doc/gauntlet-overrides.md`; first found wins): the `## PR gate` section below. The same file's `## cleanup` section is the second gatekeep-read section: an ordered list of `<label>: <command or /skill:... invocation>` items with placeholders `<issue>`, `<pr>`, `<merge-sha>`, read from the merge-base like `## PR gate` and consumed by `cleanup.md`; a PR that edits it is review subject matter, never this run's config. Git teardown and base sync are implied and never listed; the release skill's own propose step supplies the recommended level and pauses for the pick, and a repo that wants no pause writes the level in the item. An existing `## verification-before-completion` section is an accepted equivalent source for the verification command.
25
25
  3. **Repo documentation** - an explicitly documented command or tool (`AGENTS.md`'s canonical test entrypoint, a documented worktree wrapper, a documented tracker CLI, documented merge policy or branch rules). Reading documentation is not inference. Discovery-only: never tell consumers to add gatekeep-pr configuration here.
26
26
  4. **Ask the user.** Never guess from lockfiles, file heuristics, or vibes.
27
27
 
@@ -39,7 +39,7 @@ Resolve per concern, first match wins, evaluated unconditionally - never delegat
39
39
 
40
40
  Read every ladder source 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: `MB=$(git merge-base origin/<baseRefName> <headRefOid>)`, then `git show "$MB:<path>"` per source (`git show "$MB:REVIEW.md"`, `git show "$MB:AGENTS.md"`). A plain cwd read (`cat REVIEW.md`, the file open in the PR worktree) is invalid for any ladder source. Exception: a PR that itself changes `REVIEW.md` or the overrides file makes that diff review subject matter, surfaced as a finding - never applied to this run's configuration.
41
41
 
42
- **Thin-wrapper contract.** A consumer wrapper skill is a pure proxy: trigger phrases plus "follow `/skill:gatekeep-pr`" - zero configuration data. Customization lives in the repo's `REVIEW.md` (rubric) and the overrides file's `## PR gate` section (everything else); a `## comms style` section extends the wording rules in SKILL.md.
42
+ **Thin-wrapper contract.** A consumer wrapper skill is a pure proxy: trigger phrases plus "follow `/skill:gatekeep-pr`" - zero configuration data. Customization lives in the repo's `REVIEW.md` (rubric) and the overrides file's `## PR gate` section (everything else) and `## cleanup` section (post-gate steps); a `## comms style` section extends the wording rules in SKILL.md.
43
43
 
44
44
  ## Fetch the ticket
45
45
 
@@ -0,0 +1,19 @@
1
+ # gatekeep-pr: cleanup
2
+
3
+ Read from `decision-menu.md` when `cleanup` is picked on a terminal overlay. Input: the digest (`pr.url`, `pr.merge_commit` - `mergeCommit.oid` from Section A or `### Merge course`, `pr.state`, `pr.headRefOid`, `pr.baseRefName`, `issue.ref` or `issue: null`, the `worktree` block) and the continuation record, plus the `## cleanup` section of the merge-base overrides file (`assessment.md` `## Configuration`). Output: one rendered line per step - `<label>: done`, `<label>: skipped - <reason>`, `<label>: failed - <first error line>`, or `<label>: invoked /skill:<name> - <where it paused or what it returned>` for a `/skill:` item - then set `cleanup_done` and re-render the same terminal menu without the `cleanup` row.
4
+
5
+ **Landed** means `pr.state` is `MERGED`. **Primary** is the parent directory of `git -C <worktree.path> rev-parse --git-common-dir`; it is the checkout the session runs in. Never remove it and never change its branch. Run every git command as `git -C <primary>`; touch nothing under the removed worktree path after step 1.
6
+
7
+ Run the steps in order; a failing step prints its first error line and the loop continues; retry nothing.
8
+
9
+ 1. **Worktree.** Eligible when `worktree.path` is not the primary, `git -C <worktree.path> status --porcelain --untracked-files=no` is empty (tracked-clean; untracked files never block removal), and - when landed - `git -C <worktree.path> log origin/<baseRefName>..HEAD` shows no commit outside the assessed head's history (`git -C <worktree.path> merge-base --is-ancestor HEAD <headRefOid>` succeeds). Eligible and (`worktree.created` or landed) -> run `worktree.destroy` when recorded, else `git worktree remove --force <worktree.path>` (the single `--force` removes a tree whose only dirt is untracked files; those files are deleted); then `git worktree prune`; print `worktree: done`. Reused and not landed -> `worktree: skipped - reused, left at <path>`. Dirty -> `worktree: skipped - uncommitted changes at <path>`. Commits beyond the assessed head -> `worktree: skipped - unmerged commits <shas> at <path>`. Under `approved` on a `rebased locally` head this step reports `unmerged commits` and leaves the tree, and `stop` leaves the rewritten local head in place and names it.
10
+ 2. **Branch.** Landed, the branch exists locally, and no worktree holds it (`git worktree list --porcelain` lists no `branch refs/heads/<worktree.branch>`) -> `git branch -D <worktree.branch>`; print `branch: done`. Not landed -> `branch: skipped - PR not merged`. Still checked out -> `branch: skipped - checked out at <path>`. Never delete a remote branch.
11
+ 3. **Base.** Landed, the primary is on `<baseRefName>`, and its tree is tracked-clean -> `git fetch origin` then `git pull --ff-only`; print `base: synced <sha>` when `git merge-base --is-ancestor <pr.merge_commit> HEAD` succeeds, else `base: failed - merge commit not on local <baseRefName>`. Primary on another branch -> `base: skipped - primary on <branch>`; dirty -> `base: skipped - uncommitted changes`; not landed -> `base: skipped - PR not merged`. Never switch the primary's branch.
12
+ 4. **Override steps**, one per `## cleanup` item in list order. An item is `<label>: <text>`; any other shape -> `skipped - malformed item: <text>`.
13
+ - Landed-change guard first: not landed and the text contains `<merge-sha>` or `/skill:release` -> `skipped - needs a landed change`. The guard decides before any placeholder is read, so an item it skips never prints a `no <name> resolved` line, whatever its placeholders hold. A `/skill:release` item when step 3 did not print `synced` -> `skipped - base not synced`.
14
+ - Substitute: `<issue>` is `issue.ref` with a leading `#` stripped for the bare `#N` form (other forms verbatim); `<pr>` is `pr.url`; `<merge-sha>` is `pr.merge_commit`. A null value -> `skipped - no <name> resolved`: `skipped - no issue resolved`, `skipped - no pr resolved`, `skipped - no merge-sha resolved`. Any other `<...>` token -> `skipped - malformed item: <text>`. Insert each value as literal argument text: inside an existing quoted span verbatim; outside, single-quoted as one shell word.
15
+ - Text starting with `/skill:` -> invoke that skill with the rest of the text as its arguments, gates intact: the step's line is `<label>: invoked /skill:<name> - <where it paused or what it returned>` (for example `release: invoked /skill:release - paused at its level pick`) - never a `skipped` or `failed` line about skill availability, because the gate itself runs as a skill and every `/skill:` item is invokable; `/skill:release` with no level runs the skill's propose-then-pick gate; a level written in the item (`patch`, `minor`, `major`) authorizes the release without a pause. Any other text -> run it as a non-interactive shell command with cwd the primary, bounded by the resolved `timeout minutes` (default 15) through the Safety contract ladder of `../verification-brief.md` Section B; a timeout prints `failed - timed out after <n> minutes` and later steps still run.
16
+
17
+ With no overrides file or no `## cleanup` section, run steps 1-3 only. On a closed-unmerged PR steps 2 and 3 print `PR not merged` and the landed-change guard skips release and `<merge-sha>` items before substitution. Do not delete the consent record, helper output files, or temporary body files here - `show evidence` and the post rows stay available after `cleanup`; `post-selection-loop.md` `### Teardown` deletes them on terminal exit.
18
+
19
+ Consequence clause for the menu row (`decision-menu.md`): `remove <worktree.path> (untracked files there are deleted) and branch <worktree.branch>, pull <baseRefName>, then <label> (<resolved command>), ...` with every placeholder already substituted, so the tracker comment text and the release invocation are confirmed by the one pick and `/skill:release` keeps its own level gate; steps the guards would skip render as `<label> (skipped - <reason>)`; with no `## cleanup` section the clause is `tear down the gate's checkout and local branch (untracked files there are deleted)`.
@@ -4,11 +4,11 @@ Read from SKILL.md step 6. Input: the report's verdict and the PR state. Output:
4
4
 
5
5
  ## Verbs
6
6
 
7
- `accept contract change`, `preserve contract`, `fix`, `fix nits`, `push`, `review`, `approve`, `approve workflow run`, `update branch`, `merge base`, `merge`, `merge anyway`, `reply`, `post coverage to ticket`, `post coverage to PR`, `show evidence`, `wait`, `stop`.
7
+ `accept contract change`, `preserve contract`, `fix`, `fix nits`, `push`, `review`, `approve`, `approve workflow run`, `update branch`, `merge base`, `merge`, `merge anyway`, `reply`, `post coverage to ticket`, `post coverage to PR`, `cleanup`, `show evidence`, `wait`, `stop`.
8
8
 
9
9
  Render fix consequences with the applicable lifetime from `post-selection-loop.md` `### Continuation record`. For named fixes, state that authority retains only the selected stable IDs for later independent reassessments, with the same lifetime as broad fixes; link active pre-push retries to `fix-wave.md` `## Review before push`. For clarification-only fixes, state one-shot authority for the exactly selected payload; keep nit consequences one-shot.
10
10
 
11
- Rows are `<n>. <verb> - <consequence>`; every row, `stop` included, carries its consequence clause. `stop` is always last, before the compose hint. `show evidence` is always offered and never `[recommended]`. A row the actor cannot execute - by `## Availability` below, or because the forge would refuse it (branch protection, a withhold) - is omitted and named in the compose line's `Not offered:` sentence; it is never `[recommended]`. Exactly one row is `[recommended]`.
11
+ Rows are `<n>. <verb> - <consequence>`; every row, `stop` included, carries its consequence clause. `stop` is always last, before the compose hint. `show evidence` is always offered and never `[recommended]`. A row the actor cannot execute - by `## Availability` below, or because the forge would refuse it (branch protection, a withhold) - is omitted and named in the compose line's `Not offered:` sentence; it is never `[recommended]`. Exactly one row is `[recommended]`. A **terminal overlay** is `own merge`, `merge queued` (once its read returns `MERGED` it re-renders as `own merge`), `approved`, or `merged or closed PR`; every other menu is an ordinary open-PR menu. **Landed** means the PR's `state` is `MERGED`; under `approved` and under a closed-unmerged PR nothing has landed.
12
12
 
13
13
  - `accept contract change` - invoke the concrete proposal's owning amendment process through `post-selection-loop.md` `### Contract decision`; settle the baseline only after completion
14
14
  - `preserve contract` - reject the concrete proposal and resume only permitted scope-preserving repair through `post-selection-loop.md` `### Contract decision`; keep independent blockers open
@@ -25,9 +25,10 @@ Rows are `<n>. <verb> - <consequence>`; every row, `stop` included, carries its
25
25
  - `reply` - post the drafted replies under `PR comments` (`reply <n>` for one)
26
26
  - `post coverage to ticket` - post the rendered `AC coverage` block as a comment on the source ticket via the resolved tracker verb (`gh issue comment`, the overrides' tracker mapping, or `/skill:linear`); renders only under the `own merge` overlay
27
27
  - `post coverage to PR` - post the same block with `gh pr comment <N> --body-file`; renders only under the `own merge` overlay
28
+ - `cleanup` - tear down the gate's checkout and local branch per `cleanup.md`, then run the repo's `## cleanup` steps in order; the consequence clause names each override step as `<label> (<resolved command>)` with placeholders already substituted, so the exact tracker comment or release invocation is visible before the pick; with no `## cleanup` section the clause reads `tear down the gate's checkout and local branch (untracked files there are deleted)`
28
29
  - `show evidence` - print the evidence record, claim and AC outcomes, dispositions, drafted payloads and replies (`report.md` `## What stays out`)
29
30
  - `wait` - run one bounded `gh pr checks --watch` on every **binding** pending check in the resolved set (required or not), then re-render
30
- - `stop` - leave the PR as-is; on a `rebased locally` head run `git reset --hard <pre_head>` before `### Teardown`, discarding the rewrite and any unpushed fix commits, and assert `HEAD == <pre_head>` with a clean tree; a mismatch is the existing hard stop with the error, and the remote stays untouched
31
+ - `stop` - leave the PR as-is; under a terminal overlay leave the checkout as found and print one line naming the leftover worktree path and local branch; on an ordinary open-PR menu with a `rebased locally` head run `git reset --hard <pre_head>` before `### Teardown`, discarding the rewrite and any unpushed fix commits, and assert `HEAD == <pre_head>` with a clean tree; a mismatch is the existing hard stop with the error, and the remote stays untouched
31
32
 
32
33
  Compose hint: `Type a number, or compose: "fix 2", "fix nits", "fix + nits", "fix 1 + reply". Not offered: push (needs push to <head_url>:<head_ref>), merge (needs WRITE on <owner>/<repo>).` The first sentence lists only compositions whose every verb is a rendered row: `fix nits` and `fix + nits` appear only when both the `fix` and `fix nits` rows render; no `reply` row, no `fix 1 + reply`; no composable row, `Type a number.` alone. The second sentence names every omitted row as `<verb> (<why it is omitted>)` and is dropped when nothing is omitted. `merge anyway` renders as its own row when `## Availability` allows it, so the rendered-row rule does not apply to it. Offer `revoke fix` and `revoke merge base` compose forms for held grants, persisting only the named revocation through the loop. A composed line never bundles a push-producing verb (`fix`, `fix nits`, `push`, `merge base`) with `merge`. When a failing check withholds `merge`, the hint line also offers `check <name> flaky | real | ci-broken`, which records the disposition on that check (`findings.md` `## Dispositions`): `flaky` lifts the blocker, `real` keeps it until green, `ci-broken` triggers the fallback local run (`ci-broken` records `ci-infrastructure-broken`).
33
34
 
@@ -49,6 +50,7 @@ Rows derive from the digest's `permissions` block (`../verification-brief.md` Se
49
50
  | `approve workflow run` | `viewer_permission` is `WRITE`, `MAINTAIN`, or `ADMIN`, and a held run exists on the assessed head |
50
51
  | `update branch` | `can_update_branch` is true and no unpushed wave commit exists |
51
52
  | `update branch`, `review`, `merge`, `merge anyway`, `push`, `wait` | omitted while the sync record is `rebased locally`, named in `Not offered:` as `(head not pushed)`; the fix wave's lease push is the only write to the PR head |
53
+ | `cleanup` | only under a terminal overlay, never on an ordinary open-PR menu; it leaves the menu after it runs, success or not (`cleanup.md`) |
52
54
  | `approve`, `review`, `reply`, `post coverage to ticket`, `post coverage to PR`, `show evidence`, `wait`, `stop` | per the consent table and overlays below |
53
55
 
54
56
  ## Consent table
@@ -80,13 +82,14 @@ The single source for the merge-withhold reason, the `PR comments` line, and the
80
82
 
81
83
  For a pending contract exception, print the concrete proposed contract change before the rows, using the recorded proposal; numeric and typed picks select these actual rows and route to `post-selection-loop.md` `### Contract decision`. Render the `preserve contract` consequence as "reject the proposal, keep <named requirement>, and resume authorized implementation repair within it". Existing terminal/sync stops take precedence; otherwise the contract-exception overlay replaces ordinary rows, and lower overlays cannot discard its decision rows.
82
84
 
83
- Apply overlays to the consent row; take additional rows only from `own merge` (post-coverage rows) or the pending `contract exception` (contract-decision rows). When two apply, the one higher in this table wins the `merge` reason and `[recommended]`.
85
+ Apply overlays to the consent row; take additional rows only from a terminal overlay (`cleanup` and the post-coverage rows) or the pending `contract exception` (contract-decision rows). When two apply, the one higher in this table wins the `merge` reason and `[recommended]`.
84
86
 
85
87
  | Overlay | Trigger | Effect |
86
88
  |---|---|---|
87
- | own merge | this run's `merge` pick ran and the post-merge read (`post-selection-loop.md` `### Merge course`) returned `state: MERGED` | the menu is `post coverage to ticket` (when `issue` is non-null and `scope.rows` is non-empty), `post coverage to PR`, `show evidence`, `stop`, with `stop` `[recommended]`; each post row renders once and leaves the menu after its post succeeds; a failed post keeps its row and prints the `gh` error; no compare-and-swap runs before a post |
89
+ | own merge | this run's `merge` pick ran and the post-merge read (`post-selection-loop.md` `### Merge course`) returned `state: MERGED` | the menu is `cleanup` `[recommended]`, `post coverage to ticket` (when `issue` is non-null and `scope.rows` is non-empty), `post coverage to PR`, `show evidence`, `stop`; each post row renders once and leaves the menu after its post succeeds, and a successful post re-renders this menu - posting never tears down and never ends the run; a failed post keeps its row and prints the `gh` error; no compare-and-swap runs before a post |
88
90
  | merge queued | this run's `merge` pick ran and the post-merge read returned a state other than `MERGED` | the menu is `wait` (re-run the post-merge read, then re-render), `show evidence`, `stop`, with `wait` `[recommended]`; one `PR comments` line `The merge is queued, so coverage posts wait. (<state>)` |
89
- | merged or closed PR | `state` was not `OPEN` at step 1 | the report renders; the menu is `show evidence` and `stop` only |
91
+ | approved | the continuation record's `approved_head` equals the assessed head (`post-selection-loop.md` sets it only when `gh pr review --approve` exits 0, before the re-render; a non-zero exit prints the `gh` error and the ordinary menu re-renders) | the menu is `cleanup` `[recommended]`, `show evidence`, `stop` |
92
+ | merged or closed PR | `state` was not `OPEN` at step 1 | the report renders; the menu is `cleanup` `[recommended]`, `show evidence`, `stop` |
90
93
  | contract exception | a recorded contract exception is pending, with no terminal/sync stop | replace the ordinary menu with `accept contract change`, `preserve contract`, `show evidence`, `stop`; recommend `preserve contract`; use `post-selection-loop.md` `### Contract decision` for every selection |
91
94
  | draft PR | `isDraft` | `merge` and `approve` are omitted (`Not offered: merge (draft)`, plus `approve (draft)` when the consent row holds `approve`) and never carry `[recommended]`; `review` is recommended on someone else's fixable draft, `fix nits` on their mergeable draft when a nit exists, else `stop`; on your own, `fix` when a blocker exists, else `stop` |
92
95
  | held run | an `action_required` conclusion on an Actions run of the assessed head (`../verification-brief.md` Evidence resolution, Held run) with no `local run: held run not approvable` evidence yet for that head | `approve workflow run` renders as row 1 when available (`## Availability`) and is `[recommended]` when `Blockers` is empty, else `[recommended]` stays on the consent row's first row; `merge` is omitted (`Not offered: merge (workflow run awaiting approval)`); once the held-run-not-approvable local run exists, this overlay no longer applies and `merge` follows the preconditions |
@@ -161,9 +164,10 @@ Informational - the ticket's rows are unchanged. PR https://github.com/acme/widg
161
164
  - [ ] The reports page offers an Export CSV button. - covered
162
165
  - [ ] Exports are emailed to the requesting user. - deferred per spec to acme/widgets#46
163
166
 
164
- 1. post coverage to ticket - comment the block above on acme/widgets#45
165
- 2. post coverage to PR - comment the block above on the PR
166
- 3. show evidence - gate output, CI run
167
- 4. stop - leave the PR as-is [recommended]
167
+ 1. cleanup - remove .worktrees/pr-210 and branch csv-export, pull main, then close ticket (gh issue close 45 --comment "Delivered in https://github.com/acme/widgets/pull/210, merged 9f1c2d3e4b5a6978.") [recommended]
168
+ 2. post coverage to ticket - comment the block above on acme/widgets#45
169
+ 3. post coverage to PR - comment the block above on the PR
170
+ 4. show evidence - gate output, CI run
171
+ 5. stop - leave the PR as-is; .worktrees/pr-210 and branch csv-export stay
168
172
  Type a number.
169
173
  ```
@@ -4,7 +4,7 @@ Read from SKILL.md step 7. Execute only the selected pick or validated retained
4
4
 
5
5
  ### Continuation record
6
6
 
7
- Before tracker initialization, mint `mktemp "${TMPDIR:-/tmp}/gatekeep-state.XXXXXX"` and write the continuation subset as JSON using the field schema in `../verification-brief.md`. Initialize grants to null and rounds to zero only for this new invocation. Put `state: <absolute-path>` in the first tracker task's name, the no-tool checklist header, continuation summaries, and `show evidence` on both harnesses. Use native file operations on Claude Code; no pi tool is required.
7
+ Before tracker initialization, mint `mktemp "${TMPDIR:-/tmp}/gatekeep-state.XXXXXX"` and write the continuation subset as JSON using the field schema in `../verification-brief.md`. Initialize grants to null and rounds to zero only for this new invocation. Put `state: <absolute-path>` in the first tracker task's name, the no-tool checklist header, continuation summaries, and `show evidence` on both harnesses. Use native file operations on Claude Code; no pi tool is required. Initialize `approved_head` to null and `cleanup_done` to false.
8
8
 
9
9
  Persist the assessed head/body, scope, finding IDs and independent contributions, exact selected payload/body snapshots, evidence and helper-output paths, prior comment identity/body baseline and ledger. Keep raw diff, logs, and bulk metadata outside this record. Update changed JSON fields, not a recreated memory summary; fetched fields never overwrite grants, usage, or scope-at-grant.
10
10
 
@@ -84,13 +84,13 @@ A retained grant never re-dispatches a finding the implementer returned `open -
84
84
 
85
85
  Apply per `fix-wave.md`: the local conflict check, one fresh implementer helper per payload with scoped tests only, the pre-push reviewer with its closure lines, and the push. The orchestrator never edits a tracked file and never commits. When `head_pushable` is false, the wave's commits stay in the local `pr-<N>` worktree: report the branch name and re-enter step 5.
86
86
 
87
- `approve workflow run` and `update branch` execute per `fix-wave.md` (`## Evidence after push`, `## Conflicts`). Execute `review`, `reply`, `approve`, `post coverage to ticket`, and `post coverage to PR` via `gh pr review`, `gh api`, `gh issue comment`, `gh pr comment`, or the resolved tracker tool, non-interactively, with the drafted payload - after the output done-check below.
87
+ `approve workflow run` and `update branch` execute per `fix-wave.md` (`## Evidence after push`, `## Conflicts`). Execute `review`, `reply`, `approve`, `post coverage to ticket`, and `post coverage to PR` via `gh pr review`, `gh api`, `gh issue comment`, `gh pr comment`, or the resolved tracker tool, non-interactively, with the drafted payload - after the output done-check below. When `gh pr review --approve` exits 0, write the assessed head into the record's `approved_head` before the re-render, so the `approved` overlay renders (`decision-menu.md`: `cleanup` `[recommended]`, `show evidence`, `stop`; it sits below `own merge` and `merge queued` and above `contract exception`); a non-zero exit prints the `gh` error and the ordinary menu re-renders.
88
88
 
89
89
  ### Merge course
90
90
 
91
91
  `merge` executes as `gh pr merge --match-head-commit <assessed-sha>` with the resolved merge policy. Push and merge are never one pick.
92
92
 
93
- After `gh pr merge` returns, read `gh pr view <N> --json state,mergeCommit`. `state: MERGED` -> render the `AC coverage` block once and re-render the menu under the `own merge` overlay (`decision-menu.md`); any other state (a merge queue) -> one `PR comments` line `The merge is queued, so coverage posts wait. (<state>)` and the menu `wait` (re-run the read) / `show evidence` / `stop`. The block, `<sha>` = `mergeCommit.oid`, one row per `scope.rows` entry in row order with its outcome (`covered`, `deferred per spec to <ref>`, `deviates per spec: <why>`, `elsewhere per spec: <repo>`, `venue: <env> - <observation>, checked before prod deploy`); with no rows (`source: pr`, or an empty `scope.rows`) the block has no row lines and `post coverage to ticket` is not offered. With `source: pr` the header line drops `, spec <path>`. An `in-scope` row whose observation half is `not judged here` renders `covered; observable half checked after merge`.
93
+ After `gh pr merge` returns, read `gh pr view <N> --json state,mergeCommit`. `state: MERGED` -> write the returned `state` into `pr.state` and `mergeCommit.oid` into `pr.merge_commit` (the same two writes when a queued merge's `wait` re-read returns `MERGED`), render the `AC coverage` block once and re-render the menu under the `own merge` overlay (`decision-menu.md`); any other state (a merge queue) -> one `PR comments` line `The merge is queued, so coverage posts wait. (<state>)` and the menu `wait` (re-run the read) / `show evidence` / `stop`. The block, `<sha>` = `mergeCommit.oid`, one row per `scope.rows` entry in row order with its outcome (`covered`, `deferred per spec to <ref>`, `deviates per spec: <why>`, `elsewhere per spec: <repo>`, `venue: <env> - <observation>, checked before prod deploy`); with no rows (`source: pr`, or an empty `scope.rows`) the block has no row lines and `post coverage to ticket` is not offered. With `source: pr` the header line drops `, spec <path>`. An `in-scope` row whose observation half is `not judged here` renders `covered; observable half checked after merge`.
94
94
 
95
95
  ```markdown
96
96
  ## AC coverage
@@ -123,7 +123,7 @@ After every push or any mutation that can change readiness (fix wave pushed, PR
123
123
 
124
124
  ### Pre-menu refresh
125
125
 
126
- The first read of step 5, before the report and the menu render from one state - the first pass included (a long local run or review can stale the gather), every `### Re-render` entry, the post-`wait` re-fetch, `update branch`, a `merge base` round, and a fix-wave push (`fix-wave.md` `## Evidence after push`). Run exactly one `gh pr view <N> --json headRefOid,state,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,body,baseRefName,number,title,author,isDraft,headRefName,isCrossRepository,files,additions,deletions,commits,reviews,closingIssuesReferences,headRepository,maintainerCanModify` and one `gh run list -R <owner>/<repo> -c <headRefOid> --json databaseId,status,conclusion,workflowName,url` (Section A's fixed fields, folding the `gh pr view <N> --json headRefOid,body,baseRefName` fields into the single call). While the sync record is `rebased locally`, compare the fetched `headRefOid` against the record's `<pre_head>` and render from that alone: a move takes the lease-refusal path of `fix-wave.md` `## Evidence after push` (`git reset --hard <pre_head>`, assert `HEAD == <pre_head>` and a clean tree, record `sync: failed - remote head moved`, render `stop` and `show evidence` only); an unmoved head keeps `status_checks[]` and `actions_runs[]` as they are, skips the evidence re-resolve, and ignores `mergeable` and `mergeStateStatus` changes - `source: pending` holds until the lease push rewrites the record; a `state` change to closed or merged renders `stop` and `show evidence` only. On a pushed head: head moved -> the head-move rule (re-enter step 3). `state` or `mergeable` changed (head unchanged) -> route through `### Compare-and-swap` re-assessment. Head unchanged with `state` and `mergeable` unchanged -> replace `permissions.merge_state_status`, `pr.reviewDecision`, `status_checks[]`, and `actions_runs[]` in the digest together, re-normalize and re-classify every pending check (`../verification-brief.md` Section B, binding classification), and re-resolve the Evidence resolution table on the fresh set, so a check whose run completed since the last menu is never paired with its cached run status; when that re-resolve moves `source: pending` to CI-sufficient, dispatch the Section B helper in claim-check-only mode (`../verification-brief.md` Section B) before rendering. A `gh` failure keeps the prior values, classifies every pending check as binding (`../verification-brief.md` Section B, binding classification), and renders the `merge state` overlay (`decision-menu.md` `## Overlays`) through `## Withhold reason resolver` reason 2 (locator `merge state not refreshed (<reason>)`). Apply `### Next action` target and body freshness from that same read before readiness. The refresh is read-only and never mints `C#` rows - comment reconciliation stays in `### Re-render`.
126
+ The first read of step 5, before the report and the menu render from one state - the first pass included (a long local run or review can stale the gather), every `### Re-render` entry, the post-`wait` re-fetch, `update branch`, a `merge base` round, and a fix-wave push (`fix-wave.md` `## Evidence after push`). Run exactly one `gh pr view <N> --json headRefOid,state,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,body,baseRefName,number,title,author,isDraft,headRefName,isCrossRepository,files,additions,deletions,commits,reviews,closingIssuesReferences,headRepository,maintainerCanModify,url,mergeCommit` and one `gh run list -R <owner>/<repo> -c <headRefOid> --json databaseId,status,conclusion,workflowName,url` (Section A's fixed fields, folding the `gh pr view <N> --json headRefOid,body,baseRefName` fields into the single call). While the sync record is `rebased locally`, compare the fetched `headRefOid` against the record's `<pre_head>` and render from that alone: a move takes the lease-refusal path of `fix-wave.md` `## Evidence after push` (`git reset --hard <pre_head>`, assert `HEAD == <pre_head>` and a clean tree, record `sync: failed - remote head moved`, render `stop` and `show evidence` only); an unmoved head keeps `status_checks[]` and `actions_runs[]` as they are, skips the evidence re-resolve, and ignores `mergeable` and `mergeStateStatus` changes - `source: pending` holds until the lease push rewrites the record; a `state` change to closed or merged renders `stop` and `show evidence` only. On a pushed head: head moved -> the head-move rule (re-enter step 3). `state` or `mergeable` changed (head unchanged) -> route through `### Compare-and-swap` re-assessment. Head unchanged with `state` and `mergeable` unchanged -> replace `permissions.merge_state_status`, `pr.reviewDecision`, `status_checks[]`, and `actions_runs[]` in the digest together, re-normalize and re-classify every pending check (`../verification-brief.md` Section B, binding classification), and re-resolve the Evidence resolution table on the fresh set, so a check whose run completed since the last menu is never paired with its cached run status; when that re-resolve moves `source: pending` to CI-sufficient, dispatch the Section B helper in claim-check-only mode (`../verification-brief.md` Section B) before rendering. A `gh` failure keeps the prior values, classifies every pending check as binding (`../verification-brief.md` Section B, binding classification), and renders the `merge state` overlay (`decision-menu.md` `## Overlays`) through `## Withhold reason resolver` reason 2 (locator `merge state not refreshed (<reason>)`). Apply `### Next action` target and body freshness from that same read before readiness. The refresh is read-only and never mints `C#` rows - comment reconciliation stays in `### Re-render`.
127
127
 
128
128
  Watch guard: when this is not the first report, Blockers are empty, and a binding check is pending (open nits do not count), run `### Wait course` once before rendering; a `wait` pick runs it too. After the watch returns, for any reason, re-run the two reads above once and re-resolve the Evidence resolution table (`../verification-brief.md` Section B). CI-sufficient -> the claim-check-only helper for `pending` claims (named in `fix-wave.md` `## Evidence after push`), then the menu. A failing check in the resolved set -> one `P#` per check with its failing line from `gh run view <id> -R <repo> --log-failed`, a drafted payload (`findings.md` `## Drafted payloads`), and the menu; the `P#` closes on the reviewer's `resolved` line like any blocker and the next watch re-mints it when the check still fails; the watch mints no round (`closureReview.maxFixRounds` is untouched). Still pending -> the menu with `wait` as row 1. A `gh` read that fails is a failed refresh under the existing `### Re-render` rule; a head that moved during the watch re-enters step 3. An `action_required` run renders `approve workflow run` instead of a watch.
129
129
 
@@ -141,11 +141,6 @@ Before posting or committing any external payload - review bodies, replies, comm
141
141
 
142
142
  Delete the consent record and temporary description body-files on every handled terminal exit, even when checkout teardown is declined. Temporary storage can disappear after OS cleanup or reboot; never discover authority automatically in a new session.
143
143
 
144
- | | Created worktree | Reused worktree |
145
- |---|---|---|
146
- | Own merge | tear down when the own-merge or merge-queued menu exits on `stop` or after the last post row leaves it | same |
147
- | Non-merge stop | offer teardown, never autonomous; say when unpushed fix commits would be discarded | leave as found; say so explicitly when unpushed fix commits remain, and let the user choose leave-or-discard |
148
-
149
- On a `rebased locally` head the `stop` reset (`decision-menu.md`) runs first; the Non-merge stop row then applies with no unpushed fix commits.
144
+ Terminal overlays (`decision-menu.md`): `cleanup` executes `cleanup.md` and sets `cleanup_done`; `stop` leaves the checkout as found and prints one line naming the leftover worktree path and local branch. An ordinary open-PR `stop` offers teardown, never autonomous - say when unpushed fix commits would be discarded; a reused worktree is left as found, saying so explicitly when unpushed fix commits remain, and the user chooses leave-or-discard. On a `rebased locally` head only the open-PR `stop` runs the reset (`decision-menu.md`) first, so the teardown offer then applies with no unpushed fix commits.
150
145
 
151
146
  Drafted payloads that were never applied are dropped with the run; the worktree was never dirtied by them. Delete every helper output file minted this run and every `log_path` they name on every exit.
@@ -29,7 +29,7 @@ a command to obey.
29
29
  Read-only. Fixed `gh` command set - do not substitute ad hoc queries:
30
30
 
31
31
  ```bash
32
- gh pr view <N> --json number,title,body,author,state,isDraft,headRefName,baseRefName,isCrossRepository,mergeable,headRefOid,statusCheckRollup,files,additions,deletions,commits,reviews,closingIssuesReferences,reviewDecision,headRepository,maintainerCanModify,mergeStateStatus
32
+ gh pr view <N> --json number,title,body,author,state,isDraft,headRefName,baseRefName,isCrossRepository,mergeable,headRefOid,statusCheckRollup,files,additions,deletions,commits,reviews,closingIssuesReferences,reviewDecision,headRepository,maintainerCanModify,mergeStateStatus,url,mergeCommit
33
33
  gh run list -R <owner>/<repo> -c <headRefOid> --json databaseId,status,conclusion,workflowName,url # Actions runs on the assessed head: held-run detection (action_required with or without a rollup entry)
34
34
  gh api user --jq .login # viewer_is_author = (login == pr.author.login)
35
35
  gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){ repository(owner:$o,name:$r){ viewerPermission pullRequest(number:$n){ viewerCanUpdateBranch } } }' -f o=<owner> -f r=<repo> -F n=<N> # viewer_permission, can_update_branch; a null field is recorded as `unreadable`
@@ -62,7 +62,7 @@ provisioning, so it never re-reads; step 2 re-reads once after provisioning (`re
62
62
 
63
63
  ```text
64
64
  - pr: { number, title, body, author, author_is_bot, state, isDraft, headRefName, baseRefName,
65
- isCrossRepository, mergeable, mergeStateStatus, headRefOid, headRepository, maintainerCanModify, files, additions, deletions, reviewDecision }
65
+ isCrossRepository, mergeable, mergeStateStatus, headRefOid, headRepository, maintainerCanModify, files, additions, deletions, reviewDecision, url, merge_commit } # pr.url from url; pr.merge_commit = mergeCommit.oid, null while unmerged
66
66
  - base_repository: <lowercase nameWithOwner from gh repo view in the resolved base context | unreadable>
67
67
  - consent.target: { base_repository, number, baseRefName }
68
68
  - consent.scope_at_grant: <selected scope; source: pr includes original_intent: { title, body }>
@@ -71,6 +71,8 @@ provisioning, so it never re-reads; step 2 re-reads once after provisioning (`re
71
71
  - repair.rounds_used: <nonnegative integer, initially 0>
72
72
  - repair.poll_deadline: null | <absolute timestamp for the watch window; reference/post-selection-loop.md ### Wait course>
73
73
  - repair.wave: null | { pre_wave_head, pushed_head, selected_ids, affected_claims, stage: apply | review | push, no_progress: { failed_prerequisite, observed_value, result_path }? }
74
+ - approved_head: null | <sha> # set by reference/post-selection-loop.md when gh pr review --approve exits 0; triggers the approved overlay
75
+ - cleanup_done: false | true # set by reference/cleanup.md after the cleanup pick runs
74
76
  - viewer: { login, is_author }
75
77
  - permissions: { viewer_permission, is_cross_repository, maintainer_can_modify, merge_state_status, can_update_branch, head_url, head_ref, head_pushable } # head_pushable is set in step 2 after provisioning (reference/assessment.md); merge_state_status is `BLOCKED`, `BEHIND`, `UNKNOWN`, `unreadable` (null), or any other GitHub value (`CLEAN`, `UNSTABLE`, `HAS_HOOKS`, `DIRTY`) and is written only from a `gh pr view` invocation that names `mergeStateStatus` (this call, the step-2 re-read, the step-2b re-gather (`reference/sync.md`), the pre-menu refresh before and after the single watch, compare-and-swap), never from an ad hoc GraphQL query or `gh pr list`, and never from a reply carrying `errors`; any field that cannot be read is the literal `unreadable`; menu availability derives from this block (reference/decision-menu.md ## Availability)
76
78
  - status_checks: [ { name, kind, status, conclusion, required, url, workflowName } ] # kind: check_run | status_context | unreadable, from the rollup entry's __typename; evidence semantics: Section B Evidence resolution; workflowName from the CheckRun rollup entry (absent on StatusContext)
@@ -79,6 +81,7 @@ provisioning, so it never re-reads; step 2 re-reads once after provisioning (`re
79
81
  - issue_ref: <ref> | null
80
82
  - issue: null # shape and fill: step 2, reference/assessment.md ## Fetch the ticket
81
83
  - worktree_discovery: { expected_path, exists, branch, dirty, ahead, behind }
84
+ - worktree: { path, branch, created, destroy } # filled in step 2, reference/assessment.md: worktree.path; worktree.branch is the actual local branch (headRefName, pr-<N>, or whatever a worktree wrapper created); worktree.created is true when this run created it; worktree.destroy is the wrapper's exact destroy invocation, else null
82
85
  - sync: <record> # only with --rebase; grammar: reference/sync.md ## Sync record
83
86
  - truncation_notes: []
84
87
  ```
@@ -312,21 +312,7 @@ executable choice exists" - and its `rationale` must name the exact missing
312
312
  prerequisites plus the concrete custom effect still required. `custom` is never
313
313
  auto-recommended, so `none` never silently resolves to `custom`.
314
314
 
315
- A concern with `recommended: none` is excluded from the recommended set. Before
316
- any exhaustive `Apply recommended set`, the finish gate asks for a targeted
317
- custom approval that supplies a concrete effect for that one concern (finishing
318
- owns the exact reply syntax - a per-item `custom(...)` decision - not an `apply
319
- recommended` variant). That reply approves only that concrete custom effect for
320
- that concern. Validate and normalize it immediately: if the effect is ambiguous or
321
- unexecutable, clarify and keep the concern open; if executable, record it as a
322
- **pending approved custom decision** and remove that concern from the still-open
323
- inventory used to build the recommended set. If other open concerns remain,
324
- render the exhaustive recommended set for them and visibly carry the pending
325
- approved custom decision through the existing action-order and freshness
326
- barriers; `apply recommended` approves only the remaining recommendations. If no
327
- other concerns remain, execute the approved custom directly through the existing
328
- mechanics. The final disposition record keeps this concern as a `custom` result;
329
- never relabel it as a model recommendation.
315
+ A concern with `recommended: none` is excluded from the recommended set. It renders as `No recommendation - pick one` with its lettered options (finishing owns the render and the exact reply syntax). When the human's reply decides every recommended item (a bare `1`, or numbered lines covering them) but leaves a `recommended: none` item unanswered, the item is re-asked by its number, alone, never re-rendering the whole menu. Use the same one-line reply form as any item: a letter picks the shown option and records that option's disposition token (`accept` -> `accept-into-spec`, `rescope` -> `rescope-into-spec`, `follow-up` stays `follow-up`); free text is `custom(<text>)` and requires a concrete effect. Validate and normalize the custom effect immediately: ambiguous or unexecutable -> clarify and keep the concern open; never relabel a lettered pick as `custom`, and never relabel a `custom` result as a model recommendation.
330
316
 
331
317
  **Never recommend an unavailable disposition or an unexecutable `custom`
332
318
  placeholder.**
@@ -399,8 +385,9 @@ For each carried-open gap, emit a durable gap header plus one card per concern
399
385
  lists, clarification lists, and final disposition records, every user-facing
400
386
  identifier carries both its ID **and** title; bare `Gn`/`Cn` or grouped
401
387
  identifier lists without titles are prohibited. The only exceptions are the
402
- machine/interaction tokens the grammar requires: bare `Gn/Cn` inside typed
403
- response tokens, the literal `conformance fix Gn` commit/action identifiers, and
388
+ machine/interaction tokens the grammar requires: bare handles inside the
389
+ `Recorded:` disposition line and accepted legacy replies, the literal
390
+ `conformance fix Gn` commit/action identifiers, and
404
391
  the flat `auto-applied fix commits` index below. The surrounding prompt or list
405
392
  must map each token to its titled concern or gap before asking for input.
406
393