pi-gauntlet 7.35.0 → 7.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## v7.36.1 - 2026-10-27
4
+
5
+ ### Fixed
6
+
7
+ - Claude Code marketplace installs no longer register pi-only personas. A versionless plugin manifest disables default agent discovery while the marketplace retains the six portable skills and owns the release version. Real Claude startup and cached-upgrade tests gate releases; pi persona files and installation behavior are unchanged.
8
+
9
+ ## v7.36.0 - 2026-10-26
10
+
11
+ ### Added
12
+
13
+ - Brainstorming accepts a leading list of related open, same-repository, non-fork PRs on one base as one rework subject. Admission and relatedness checks precede provisioning; member heads merge into the first retained PR's branch before the base, and each PR retains its claims, threads, and original-head provenance through the spec and handoff. Finish offers gated close comments for absorbed members only after a successful home push and description update, without deleting branches. (#89)
14
+
3
15
  ## v7.35.0 - 2026-10-26
4
16
 
5
17
  ### Added
package/README.md CHANGED
@@ -36,13 +36,13 @@ pi-gauntlet's only hard dependency is pi-cohort - every gate that dispatches a r
36
36
  Concretely, one change through the gauntlet:
37
37
 
38
38
  0. *(Optional)* Before there's even a spec, `/skill:shape-ticket` can create one tracker issue or repair a comma/whitespace list of issue refs - shaping each into a Context/Problem/Idea/Acceptance Criteria ticket, grounded by a repository audit, an AC integrity check, and a cheap council roast. Repair finishes preparation for all tickets before presenting exact-diff confirmations sequentially in ref order; each approval writes that ticket before the next gate, and may include one optional gated comment for a Reporter note or overlap evidence. The audit checks factual claims, Idea fit, AC mechanisms against cited repo guidance, and already-stated observable legs in configured sibling checkouts. It reports in-motion work and attempts a best-effort scenario through authorized current-system reads, without promising reproduction or a fix; missing access or nothing to observe is reported, not a new approval gate. Implementer detail moves into the gated Reporter note; brainstorming reads that note with the ticket so the spec retains the detail. A failed roast is retried once, then surfaced inline at the gate if it fails again. It's a tool, not a phase: no worktree, no plan/phase tracker, runs from any repo state. The agent may start it on its own when the ask is "file a ticket" or another skill routes there; the write still waits for your yes on the presented diff. Similarly, `/skill:chase-bug` triages a raw bug report into an evidenced verdict - and can hand off to shape-ticket, brainstorming, or a bounded hotfix - before any spec exists.
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. A leading GitHub reference (`#N`, `owner/repo#N`, or a `/pull/N` or `/issues/N` URL) is resolved as a PR first: an open PR, including a draft, starts rework on its existing in-repo branch; a non-PR follows the ticket path. Trailing text carries intent; mid-sentence references and other ticket/free-text subjects keep their existing paths. The PR diff is the starting state, judged against constraints from the fetched current base HEAD, not the fork point or the PR's proposed policy edits; see the [PR-subject contract](skills/brainstorming/reference/pr-subject.md) and [PR-head worktree owner](skills/using-git-worktrees/reference/pr-head.md). **`brainstorming`** sets up an isolated worktree (on the existing branch for PR rework), explores the codebase, and turns the subject 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.** Before approval, PR rework permits only the base merge, conflict resolution, and scoped conflict tests outside the spec directories; other subjects have no implementation code yet.
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. A leading run of GitHub references (`#N`, `owner/repo#N`, or a `/pull/N` or `/issues/N` URL), separated by whitespace or commas, resolves each reference as a PR first: related open, same-repository, non-fork PRs on one base form one rework subject, including drafts. Admission runs in listed order and relatedness is checked before provisioning; the first retained PR is home, and member heads merge into its existing branch in listed order before the fetched base merges. An unresolved member conflict after its scoped helper aborts that merge and stops the run before any spec, later merge, push, or close. A single non-PR reference follows the ticket path. The first non-reference token starts intent text; mid-sentence references and other ticket/free-text subjects keep their existing paths. The combined code, PR-carried specs, claims, and review threads are the brainstorm input, with each PR's provenance retained; only home's bound ticket supplies acceptance criteria, while member tickets supply context. A single PR retains the same rework path without member merges or a close offer. The combined state is judged against constraints from the fetched current base HEAD, not the fork point or the PRs' proposed policy edits; see the [PR-subject contract](skills/brainstorming/reference/pr-subject.md) and [PR-head worktree owner](skills/using-git-worktrees/reference/pr-head.md). **`brainstorming`** sets up an isolated worktree (on the existing branch for PR rework), explores the codebase, and turns the subject 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.** Before approval, PR rework's sole write/run exception outside the spec directories permits only the base merge, each admitted member-head merge, their conflict helpers' edits to the conflicted files, and their scoped conflict tests; other subjects have no implementation code yet.
40
40
  2. By default, **`writing-plans`** decomposes the approved spec into atomic, independently-verifiable tasks, grouped into parallel waves where they don't touch the same files. Experimental approval options 4/5 instead select **`loop-implementation`**: the plan phase is skipped and a stamped acceptance-criteria ledger replaces the task plan. Option 4 retains amendment review; option 5 grants auto-apply amends, with redraws still stopping. See [Loop engine operations](doc/configuration.md#loop-engine-operations).
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
42
  The loop alternative to step 3 runs one fresh `loop-implementer` per seed or row iteration, without implement-phase reviewers. It stops for a human decision on stagnation or a failed exit verification; passing full verification with every row closed enters the same verify gates.
43
43
 
44
44
  4. **verify**: the parent first runs the plan's full verification command set (the ledger's `verification:` command for loop); 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.
45
- 5. **`finishing-a-development-branch`**: PR, draft PR, squash, keep, or discard; PR rework also offers a non-force push to the existing PR branch. That push preserves history and appends a `## Rework` block while retaining the original description bytes. Its `Before:` handle includes the full portable pre-rework SHA and the local `refs/gauntlet/pre-rework/pr-<N>` ref in the rework checkout. The [finishing owner](skills/finishing-a-development-branch/SKILL.md#pr-subject-row-1-push-to-the-pr-branch) defines the landing and body-update contract. 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.
45
+ 5. **`finishing-a-development-branch`**: PR, draft PR, squash, keep, or discard; PR rework also offers a non-force push to the existing PR branch. That push proves every recorded original head remains an ancestor, preserves history, and appends a `## Rework` block naming absorbed PRs while retaining the original description bytes. Only after a successful push and home-description edit does one gated confirmation show the exact close comments and eligible member targets, linking home; declining or any other finish option leaves members open. Closing never deletes member branches, and finish posts no review-thread replies. Its `Before:` handle includes the full portable pre-rework SHA and the local `refs/gauntlet/pre-rework/pr-<N>` ref in the rework checkout. The [finishing owner](skills/finishing-a-development-branch/SKILL.md#pr-subject-row-1-push-to-the-pr-branch) defines the landing and body-update contract. 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.
46
46
  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.
47
47
 
48
48
  Only the machine-owned `plan -> implement` and `verify -> ship` handoffs receive a branch-local one-shot nudge after an unexpected settled stop; it is fire-and-forget, does not bypass either human gate, and older Pi hosts without `agent_settled` retain existing behavior.
@@ -126,7 +126,7 @@ Run by a human against pi-cohort >= 7.1.0, before a pi-gauntlet release claims t
126
126
  ## Requirements
127
127
 
128
128
  - [pi-coding-agent](https://github.com/earendil-works/pi) ≥ 0.85.1 - tested minimum for sequential `phase_tracker` tool execution, which persists a tracker update before a later tool call in the same model message.
129
- - [pi-cohort](https://github.com/jjuraszek/pi-cohort) >= 7.5.0 - required peer package, including binary-safe worktree patches; it also lets a persona with `subagent` in `tools:` start with inherited extensions ([pi-cohort #26](https://github.com/jjuraszek/pi-cohort/issues/26)), which multi-PR `gatekeep-pr` needs. Skills that dispatch agents (`requesting-code-review`, `subagent-driven-development`, `dispatching-parallel-agents`, `writing-plans`, `forge-skill`, `shape-ticket`, `roasting-the-spec`) call `subagent({})`, which pi-cohort provides. pi-gauntlet does not vendor the dispatch tool; without pi-cohort those skills have nothing to call. Gauntlet execution dispatches explicitly use `async: false`; do not enable pi-cohort's incompatible `forceTopLevelAsync` setting. See pi-cohort's [dispatch configuration](https://github.com/jjuraszek/pi-cohort/blob/main/doc/configuration.md) for that setting's owner and semantics. A wave of N >= 1 tasks dispatches its implementers and first spec reviews as one chain call. `subagent-driven-development` stops before dispatch on unsupported installs; there is no older-runtime fallback.
129
+ - [pi-cohort](https://github.com/jjuraszek/pi-cohort) >= 7.6.1 - required peer package, including binary-safe worktree patches; it also lets a persona with `subagent` in `tools:` start with inherited extensions ([pi-cohort #26](https://github.com/jjuraszek/pi-cohort/issues/26)), which multi-PR `gatekeep-pr` needs. Skills that dispatch agents (`requesting-code-review`, `subagent-driven-development`, `dispatching-parallel-agents`, `writing-plans`, `forge-skill`, `shape-ticket`, `roasting-the-spec`) call `subagent({})`, which pi-cohort provides. pi-gauntlet does not vendor the dispatch tool; without pi-cohort those skills have nothing to call. Gauntlet execution dispatches explicitly use `async: false`; do not enable pi-cohort's incompatible `forceTopLevelAsync` setting. See pi-cohort's [dispatch configuration](https://github.com/jjuraszek/pi-cohort/blob/main/doc/configuration.md) for that setting's owner and semantics. A wave of N >= 1 tasks dispatches its implementers and first spec reviews as one chain call. `subagent-driven-development` stops before dispatch on unsupported installs; there is no older-runtime fallback.
130
130
 
131
131
  Both packages must be listed in your `.pi/settings.json#packages` array (pi adds them automatically when you `pi install`). pi-gauntlet and pi-cohort are versioned independently but release together whenever dispatch semantics change - pin compatible versions of both. At each pi-gauntlet release, set the pi-cohort peer minimum and README floor together to the newest released pi-cohort version; `/skill:release` checks the registry before proceeding.
132
132
 
@@ -229,7 +229,9 @@ Invocation: `/gauntlet:shape-ticket` (or bare `/shape-ticket` when unambiguous).
229
229
 
230
230
  Updates: the plugin's marketplace `version` equals the npm version; the release
231
231
  script writes it on every release, so Claude Code sees each release as a new
232
- plugin version. Third-party marketplaces do not auto-update by default - run
232
+ plugin version. `.claude-plugin/plugin.json` intentionally omits `version`, so
233
+ it cannot override the marketplace's release version. Third-party marketplaces
234
+ do not auto-update by default - run
233
235
  `claude plugin update gauntlet@pi-gauntlet` or turn on auto-update for the
234
236
  marketplace in `/plugin`. `claude update` updates Claude Code itself, not plugins.
235
237
 
@@ -246,6 +248,20 @@ exact repo folder* in interactive Claude Code. Trusting a parent folder,
246
248
 
247
249
  ### Smoke test
248
250
 
251
+ `npm test` runs the pinned Claude Code development dependency against disposable
252
+ Git marketplaces and isolated homes. It checks actual session inventories for
253
+ fresh installs and cached upgrades: zero gauntlet agents, exactly the six skills,
254
+ and native helper availability. Negative controls reproduce the agent leak,
255
+ manifest conflict, and full-skill scan. No credentials or model responses are
256
+ used; artifacts remain under the printed temp directory. Run it separately with
257
+ `node scripts/claude-plugin-smoke.test.mjs`; `CLAUDE_BIN` selects another binary
258
+ for compatibility testing. This test gates local releases and CI publication.
259
+
260
+ Use the interactive smoke below to check model-driven skill behavior and helper
261
+ execution before a Claude packaging release. For a candidate that is not yet
262
+ published, add its local marketplace checkout in an isolated `CLAUDE_CONFIG_DIR`
263
+ instead of the GitHub source. Stop before tracker writes.
264
+
249
265
  1. Create a scratch repo and add the marketplace config:
250
266
 
251
267
  mkdir -p /tmp/cc-smoke/.claude && cd /tmp/cc-smoke && git init
@@ -289,8 +305,12 @@ exact repo folder* in interactive Claude Code. Trusting a parent folder,
289
305
  pi-gauntlet (strict mode if available). Expected: no schema errors.
290
306
  11. Agent check: type `@gauntlet:` in the mention typeahead (or open the
291
307
  plugin's details). Expected: the plugin registers no agents - `"agents": []`
292
- in the marketplace entry suppresses the default `agents/` scan of the
293
- repo's pi personas.
308
+ in `.claude-plugin/plugin.json` suppresses the default `agents/` scan of the
309
+ repo's pi personas. The marketplace uses `strict: true` so its skill allowlist
310
+ can coexist with that manifest. Keep the skill allowlist in the marketplace:
311
+ a manifest `skills` field adds to the default scan rather than restricting it.
312
+ Direct `--plugin-dir` loading of the repository bypasses the marketplace's
313
+ skill allowlist and is not the supported installation path.
294
314
 
295
315
  ## Project-specific overrides
296
316
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "7.35.0",
3
+ "version": "7.36.1",
4
4
  "description": "Opinionated, gated workflow skills, subagent personas, and runtime extensions for the pi coding agent.",
5
5
  "author": "Jacek Juraszek",
6
6
  "type": "module",
@@ -62,12 +62,13 @@
62
62
  "test": "node scripts/ci.mjs"
63
63
  },
64
64
  "peerDependencies": {
65
- "pi-cohort": ">=7.5.0"
65
+ "pi-cohort": ">=7.6.1"
66
66
  },
67
67
  "dependencies": {
68
68
  "yaml": "^2.9.0"
69
69
  },
70
70
  "devDependencies": {
71
+ "@anthropic-ai/claude-code": "2.1.309",
71
72
  "esbuild": "0.28.2"
72
73
  }
73
74
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: brainstorming
3
- description: "Use only when the user explicitly requests brainstorming or selects a handoff into it; the subject is a free prompt, one tracker ticket, or one open PR (`#N`, `owner/repo#N`, PR URL) reworked on its own branch."
3
+ description: "Use only when the user explicitly requests brainstorming or selects a handoff into it; the subject is a free prompt, one tracker ticket, or one open PR or a leading list of related open PRs reworked on the home PR's branch."
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
@@ -18,7 +18,7 @@ You **may** read code/docs, run the existing system to observe current behavior,
18
18
 
19
19
  You may **not** write outside the resolved spec dirs; build, deploy, validate, or exercise the proposed change; run implementation skills; commit the spec on `main`; or start `/skill:writing-plans`.
20
20
 
21
- For a PR subject, the base merge in `using-git-worktrees/reference/pr-head.md` - its merge commit, its conflict helper's edits to the conflicted files, and its scoped conflict tests - is the one pre-approval write and run outside the spec dirs; nothing else moves.
21
+ For a PR subject, the base merge and each admitted member-head merge, their conflict helpers' edits to the conflicted files, and their scoped conflict tests in `using-git-worktrees/reference/pr-head.md` are the sole pre-approval writes and runs outside the spec dirs; nothing else moves.
22
22
 
23
23
  The line: current-system observation is research; exercising the proposed change waits for approval.
24
24
 
@@ -44,7 +44,7 @@ Work through the items below **in order**. After a user answer or completed side
44
44
  phase_tracker({ action: "start", phase: "brainstorm" })
45
45
  ```
46
46
 
47
- 2. **Set up the worktree** - resolve a tracker reference first per `reference/pr-subject.md`; pass a PR subject's head to `/skill:using-git-worktrees`' PR-head path. Then see [Worktree First](#worktree-first).
47
+ 2. **Set up the worktree** - resolve the leading run per `reference/pr-subject.md`; pass the ordered per-PR array (home first) to `/skill:using-git-worktrees`' PR-head path. Then see [Worktree First](#worktree-first).
48
48
  3. **Gather context** - read [Ticket Handling](#ticket-handling), then follow [`gatherer.md`](gatherer.md) without a human stop.
49
49
  4. **Understand the idea against the draft** - if `reference/ticket-acceptance.md` is not already in context this session (restored entry, or a redraw after a fresh start), read [Ticket Handling](#ticket-handling) before the rest of this step; then see [Understand the idea](#3-understand-the-idea).
50
50
  5. **Propose 2-3 approaches** - see [Explore approaches](#4-explore-approaches).
@@ -142,7 +142,7 @@ Propose 2-3 genuinely distinct approaches and name what separates them (the axis
142
142
 
143
143
  ### 5. Design for clarity and isolation
144
144
 
145
- Prefer clear testable boundaries, YAGNI, existing conventions when they are sound - when the convention is the debt, say so and do not extend it - the owning schema/contract rather than parallel state, and explicit errors and edge cases. Retain every existing user-approved design detail in approaches and design, including when drafting or replacing the spec.
145
+ Prefer clear testable boundaries, YAGNI, existing conventions when they are sound - when the convention is the debt, say so and do not extend it - the owning schema/contract rather than parallel state, and explicit errors and edge cases. Retain every existing user-approved design detail in approaches and design, including supplied presentation details in the design recap and when drafting or replacing the spec.
146
146
 
147
147
  ### 6. Present the design in two rounds
148
148
 
@@ -128,25 +128,31 @@ Scout (always dispatched):
128
128
  For a PR subject, supply this additional scout block as values from
129
129
  `reference/pr-subject.md` (keep scout unconditional):
130
130
 
131
- > PR title and body verbatim: `<title>`, `<body>`.
132
- > PR-carried specs: `<paths and stated contracts>`.
133
- > Changed paths: `<git diff --name-only <BASE_SHA>...<PRE_REWORK_HEAD> output>`.
134
- > Read the diff itself from the worktree. Unresolved threads: `<full comment threads
135
- > in order, with ids and locations from using-git-worktrees/reference/pr-head.md,
136
- > or threads: not fetched (<reason>)>`.
137
- > Merge result: `<merge line, contract exceptions, scoped test results>`.
131
+ > Require every retained PR's title/body, carried specs, and full threads before
132
+ > gather, including N=1. Stop on query failure: `threads for #<N> not fetched (<gh error>)`.
133
+ > Per PR, home first: PR title and body verbatim: `<title>`, `<body>`;
134
+ > PR-carried specs: `<paths and stated contracts>`; own changed paths:
135
+ > `<that PR's BASE_SHA...headRefOid file list>`; full comment threads
136
+ > `<in order, with ids and locations from using-git-worktrees/reference/pr-head.md>`.
137
+ > Combined changed paths: `git diff --name-only <BASE_SHA>...HEAD` output, after the
138
+ > member merges and base merge. Read `git diff <BASE_SHA>...HEAD` from the worktree.
139
+ > Preserve PRE_REWORK_HEAD for home's Before provenance, not scout's code input.
140
+ > Member merge: `<per-member outcome lines>`; Merge result: `<base merge line,
141
+ > contract exceptions, scoped test results>`.
138
142
  > Authoritative base-HEAD constraints: `<git show <BASE_SHA>:<path> texts for
139
143
  > AGENTS.md, first-found overrides, REVIEW.md, and all cited constraint documents;
140
144
  > explicit missing-document results>`. These govern on every merge outcome,
141
145
  > including clean; worktree policy edits are proposals under `changes:` or
142
146
  > `dropped claims:`, never constraints.
143
- > The PR is the starting state, not the target. Judge every PR-stated claim against
144
- > the diff and supplied base rules. Name the smallest repair satisfying them;
145
- > propose a rewrite only where no repair can. Render `## PR under rework` with
146
- > `keeps:`, `changes:`, `dropped claims:`, `constraints added since fork:` (one line
147
- > per base rule absent at the fork point), and one proposed disposition per
148
- > unresolved thread: `fix | keep open | decline: <reason>`. Carry merge contract
149
- > exceptions into `changes:`.
147
+ > Treat the combined state as the starting state, not the target. Judge every
148
+ > PR-stated claim against the diff and supplied base rules. Name the smallest repair
149
+ > satisfying them; propose a rewrite only where no repair can. Render
150
+ > `## PR under rework` with one `### PR #<N> - <title>` subsection per PR, home first,
151
+ > each with `keeps:`, `changes:`, `dropped claims:`, and one proposed disposition per
152
+ > unresolved thread: `fix | keep open | decline: <reason>`. Render
153
+ > `constraints added since fork:` once for home, one line per base rule absent at its
154
+ > fork point. Carry merge contract exceptions into `changes:`. List member-carried
155
+ > spec paths under each PR in Problem for resume.
150
156
 
151
157
  Context-builder (conditional):
152
158
 
@@ -180,7 +186,7 @@ Context-builder (conditional):
180
186
 
181
187
  ## Context-builder trigger rule
182
188
 
183
- For a resolved PR subject, dispatch context-builder only for the bound ticket
189
+ For a resolved PR subject, dispatch context-builder only for the bound ticket of home
184
190
  using `reference/pr-subject.md`; an unbound PR supplies intent, not ticket ACs.
185
191
  Title wins a title/body-first-line ticket conflict; carry the other reference into
186
192
  `## Problem` as context. For other subjects, dispatch context-builder when the initial prompt (or a file it explicitly references)
@@ -224,7 +230,8 @@ convention; slug minted from the initial prompt):
224
230
  <scout output, or the degraded text>
225
231
 
226
232
  ## PR under rework
227
- <PR subject only: scout rework section and supplied PR/base/merge values per
233
+ <PR subject only: every per-PR scout subsection, ordered original heads, threads,
234
+ member-carried spec paths, and supplied Member merge/base values per
228
235
  reference/pr-subject.md; retain these values even when scout failed>
229
236
 
230
237
  ## External context
@@ -62,7 +62,7 @@ The working tree stays at pre-batch HEAD until apply (section 5) - nothing is ed
62
62
 
63
63
  Send an item straight to the human batch (step 4) when any holds:
64
64
 
65
- - the edit removes or narrows approved text (a descope or rescope);
65
+ - the edit removes or narrows approved text (a descope or rescope). Without a standing amend grant, send a re-bounded row to human review before applying it;
66
66
  - the location is a human-owned section: problem statement, goal, acceptance criteria, in/out scope or non-goals, component list or boundaries, public contracts (API, schema, config shape, CLI surface);
67
67
  - at the conformance entry: the gap is `UNAUTHORIZED`, or its `origin` quotes an acceptance criterion.
68
68
 
@@ -142,7 +142,7 @@ Before the aftermath, `read` this section again; never run it from memory.
142
142
 
143
143
  Apply accepted items only: reviewer clears, `accept`, `alt-n`, and state-changing `custom`. Print one line per applied item: `amended the spec: <title> - <what>`.
144
144
 
145
- Aftermath, once per batch, only when at least one item applied (nothing applied -> skip to the commit below). Ledger exists (line 1 `# Ledger - loop engine` in the resolved plan dir) -> skip `plan-writer`, `plan_check`, and `plan_tracker`. Reconcile the ledger against the approved spec dispositions: `deviates:`, `deferred:`, or `elsewhere:` -> `cut`, evidence = the disposition; factual or requirement-preserving amendment -> preserve rows; re-bounded `done` row -> `open`, evidence = `reopened by amendment`. Preserve every row's attempts and the ledger's iterations. Never auto-edit ticket or approved drafted AC row texts; reconcile dispositions only, never text/add/remove changes. Commit spec + ledger together with subject `loop: cut row <n>` for a single cut, otherwise `loop: amend`, carrying the amendment audit body below (including each `granted` record and granting sentence); continue without task-plan aftermath. No plan yet -> commit the spec; continue. Plan exists -> read `git -C "<abs worktree path>" status --porcelain` (plain jj: `jj -R "<abs worktree path>" diff --summary`); collect the parent's in-flight workflow edits as worktree-relative paths, including the amended spec and staged in-place wave-repair deliverables. Dispatch `plan-writer` per `writing-plans` `## Author the plan` step 3 with an `Amend:` block naming the amended spec sections, an `In-flight:` list of those paths (the parent supplies the set; the child never infers it), and the rule "preserve every task title and wave position the amendment does not touch; edit only affected anchors and tasks", then run that section's steps 5-6 (the parent `plan_check` and its branches), then read the amended plan once (step 7's read, without its phase completion or `plan_tracker` init) - the main loop never edits the plan text; then update the tracker: `plan_tracker` `add` for new tasks; anchor-changed completed tasks are reopened as `in_progress` and re-run the task loop (`update` never sets `pending`). A removed task is deleted from the plan; then re-`init` the tracker with `{ name, status }` elements: preserved tasks keep their order and statuses, reopened tasks are `in_progress` in place, every still-`pending` task (including newly added ones, whatever wave label they carry) trails the non-pending ones, removed tasks are the only deletions (the only permitted `init` after handoff; never `clear`). Commit spec + plan together; continue. A task reopened while `verify` or `ship` is in progress stays in that phase - no `phase_tracker` call. Repair it through the current phase's fix loop and `plan_tracker({ action: "update", index: N, status: "complete" })` it once that repair's review or re-audit accepts; open tasks block `complete verify`, and in `ship` nothing checks them, so close them explicitly.
145
+ Aftermath, once per batch, only when at least one item applied (nothing applied -> skip to the commit below). Ledger exists (line 1 `# Ledger - loop engine` in the resolved plan dir) -> skip `plan-writer`, `plan_check`, and `plan_tracker`. Reconcile the ledger against the approved spec dispositions: `deviates:`, `deferred:`, or `elsewhere:` -> `cut`, evidence = the disposition; factual or requirement-preserving amendment -> preserve rows; re-bounded `done` row -> `open`, evidence = `reopened by amendment`. Preserve every row's attempts and the ledger's iterations. Never auto-edit ticket or approved drafted AC row texts; reconcile dispositions only, never text/add/remove changes. Commit spec + ledger together with subject `loop: cut row <n>` for a batch containing only one cut, otherwise `loop: amend` (including mixed batches), carrying the amendment audit body below (including each `granted` record and granting sentence); continue without task-plan aftermath. No plan yet -> commit the spec; continue. Plan exists -> read `git -C "<abs worktree path>" status --porcelain` (plain jj: `jj -R "<abs worktree path>" diff --summary`); collect the parent's in-flight workflow edits as worktree-relative paths, including the amended spec and staged in-place wave-repair deliverables. Dispatch `plan-writer` per `writing-plans` `## Author the plan` step 3 with an `Amend:` block naming the amended spec sections, an `In-flight:` list of those paths (the parent supplies the set; the child never infers it), and the rule "preserve every task title and wave position the amendment does not touch; edit only affected anchors and tasks", then run that section's steps 5-6 (the parent `plan_check` and its branches), then read the amended plan once (step 7's read, without its phase completion or `plan_tracker` init) - the main loop never edits the plan text; then update the tracker: `plan_tracker` `add` for new tasks; anchor-changed completed tasks are reopened as `in_progress` and re-run the task loop (`update` never sets `pending`). A removed task is deleted from the plan; then re-`init` the tracker with `{ name, status }` elements: preserved tasks keep their order and statuses, reopened tasks are `in_progress` in place, every still-`pending` task (including newly added ones, whatever wave label they carry) trails the non-pending ones, removed tasks are the only deletions (the only permitted `init` after handoff; never `clear`). Commit spec + plan together; continue. A task reopened while `verify` or `ship` is in progress stays in that phase - no `phase_tracker` call. Repair it through the current phase's fix loop and `plan_tracker({ action: "update", index: N, status: "complete" })` it once that repair's review or re-audit accepts; open tasks block `complete verify`, and in `ship` nothing checks them, so close them explicitly.
146
146
 
147
147
  One commit per batch:
148
148
 
@@ -2,50 +2,86 @@
2
2
 
3
3
  ## Resolve before provisioning
4
4
 
5
- Match only a trimmed leading GitHub reference: `#N`, `owner/repo#N`, or a GitHub `/pull/N` or `/issues/N` URL, optionally followed by free text carried as intent. Keep mid-sentence mentions on the existing free-text/context-builder path. Keep Linear/Jira and other free-text subjects on their existing paths.
5
+ Parse only a trimmed leading run of one or more GitHub references: `#N`, `owner/repo#N`, or a GitHub `/pull/N` or `/issues/N` URL, separated by whitespace or commas. The first token that is not a ref starts intent text; carry it verbatim without looking up later references. Read `#12 #13 #14` as three PRs, `#12, #13 tighten the retry path` as two plus intent, and `#12 fix the retry path, see #13 for context` as one plus intent. Route mid-sentence mentions through the free-text path with context-builder: for `make the retry logic work like #55 did`, gather #55 as context, not a PR subject; skip PR classification and PR-head provisioning. Keep Linear/Jira and other free-text subjects on their existing paths.
6
6
 
7
- Complete classification in order, never in parallel: `gh issue view` can return a PR as an issue.
7
+ Resolve and admit in listed order before any write; stop at the first failing ref with its observed reason. Create no worktree and leave existing worktrees unchanged on admission failure. Complete classification in order, never in parallel: `gh issue view` can return a PR as an issue.
8
8
 
9
- 1. Run `gh pr view <N> [-R owner/repo] --json number,url,state,isDraft,title,body,headRefName,headRefOid,headRepository,isCrossRepository,baseRefName`. Accept exit 0 with `state: OPEN`, including `isDraft` true. For another state, stop: `PR #N is <state>; only an open PR can be reworked.`
10
- 2. Only a not-a-PR result reaches `gh issue view <N>` and the existing ticket path.
9
+ 1. Run `gh pr view <N> [-R owner/repo] --json number,url,state,isDraft,title,body,headRefName,headRefOid,headRepository,isCrossRepository,baseRefName`. Accept exit 0 with `state: OPEN`, including `isDraft` true.
10
+ 2. Only a not-a-PR result reaches `gh issue view <N>`. Keep an isolated issue on the existing ticket path; stop for an issue in a list.
11
11
  3. For neither, stop: `#N resolves to neither a pull request nor an issue in <owner/repo>.`
12
12
 
13
- Parse the PR base repository from `url` and this checkout's repository from `git remote get-url origin`; compare lowercase owner/repo identities. For unequal identities stop before provisioning: `PR <owner/repo#N> belongs to <repo>, not this checkout (<origin owner/repo>).` Resolve bare `#N` in this checkout.
13
+ Parse the PR base repository from `url` and this checkout's repository from `git remote get-url origin`; compare lowercase owner/repo identities. Resolve bare `#N` in this checkout. Compare duplicates by resolved repository and number, including URL/bare duplicates. Require every PR, home included, to pass these checks; instantiate all refs, states, repositories, fork names, and branches from observations:
14
14
 
15
- Bind the first tracker reference in the title, then the body's first line. Never bind from branch names, commit subjects, or `closingIssuesReferences`. Fetch a bound ticket, extract its AC rows using `reference/ticket-acceptance.md`, and dispatch context-builder for it. Preserve `none - ticket not fetched (<reason>)` on fetch failure. When title and first body line name different tickets, use the title's ticket and name the other in `## Problem` as context.
15
+ | Check | Stop text | Remedy |
16
+ |---|---|---|
17
+ | State not OPEN | `#13 is MERGED; only an open PR can be reworked.` | none |
18
+ | Issue in list | `#13 is an issue, not a pull request; a list admits only PRs. Drop #13 from the list, or brainstorm it alone as a ticket.` | direct correction |
19
+ | Foreign repository | `PR <owner/repo#N> belongs to <repo>, not this checkout (<origin owner/repo>).` | umbrella |
20
+ | isCrossRepository true | `PR #13's head lives on fork <nameWithOwner>; rework needs an in-repo branch.` | umbrella |
21
+ | Different baseRefName from first PR | `#13 targets release-2 but home #12 targets main; mixed bases are not admitted.` | umbrella |
22
+ | Duplicate resolved identity | `#13 is listed twice; list each PR once.` | direct correction |
16
23
 
17
- For an unbound PR, retain the gather-time result `none - no ticket`; at spec-writing use `ticket-acceptance.md`'s drafted branch from the agreed rework design. Carry raw title/body as stated intent, not invented AC rows; derive the drafted outcomes from the agreed design, not the title/body carry-over. Name its spec `YYYY-MM-DD-pr-<N>-<topic>.md`; emit the concrete filename with the actual date, PR number, and topic slug derived from the PR title. For a bound ticket keep `YYYY-MM-DD-<ticket-id>-<topic>.md`.
24
+ Append this paragraph only for foreign-repository, fork, and mixed-base failures: To rework these together anyway, merge the heads into one branch by hand, open one PR, and brainstorm that PR.
18
25
 
19
- Pass exactly `{ number, url, title, headRefName, headRefOid, baseRefName, isCrossRepository, headRepository.nameWithOwner }` by value to provisioning in `using-git-worktrees/reference/pr-head.md`; write nothing during resolution.
26
+ ## Relatedness before provisioning
27
+
28
+ For N>1, fetch every admitted PR's title/body with `gh pr view <N> -R <owner/repo> --json title,body` and actual diff with `gh pr diff <N> -R <owner/repo>`. On failure stop before provisioning naming the ref and observed reason: `<ref>: title/body not fetched (<gh error>)` or `<ref>: diff not fetched (<gh error>)`.
29
+
30
+ Group by the hunks' behavior, contract, or defect, using titles and bodies as stated intent, not shared paths. Treat unrelated hunks in one file as separate concerns and related hunks across files as one concern. Define groups here, not through shape-ticket's split axes. For one group continue; for multiple groups ask one question in the questionary format: a lead-in naming the concerns, one lettered option per group with its PRs and shared concern on its own line, then `Recommendation:` on its own final line recommending the initial-home group. Offer no all option. For an answer naming no offered group, re-ask once, then stop.
31
+
32
+ Retain the chosen group's listed order; make its first PR home. Name dropped PRs and exclude them from merges, records, and closure. Apply all per-PR fetch and record requirements even to a retained group of one. Keep this preflight separate from question one: gather, the framing question in SKILL.md ### 3, and the post-worktree scope check still run afterward.
33
+
34
+ ## Bind and pass the retained list
35
+
36
+ Bind the spec's ticket only from home's first tracker reference in the title, then the body's first line. Name each member binding in `## Problem` as `PR #13 binds <ref> (context)`; extract no member AC rows. Never bind from branch names, commit subjects, or `closingIssuesReferences`. Fetch a bound ticket, extract its AC rows using `reference/ticket-acceptance.md`, and dispatch context-builder for it. Preserve `none - ticket not fetched (<reason>)` on fetch failure. When title and first body line name different tickets, use the title's ticket and name the other in `## Problem` as context.
37
+
38
+ For an unbound home PR, retain the gather-time result `none - no ticket`; at spec-writing use `ticket-acceptance.md`'s drafted branch from the agreed rework design. Carry raw title/body as stated intent, not invented AC rows; derive the drafted outcomes from the agreed design, not the title/body carry-over. Name its spec `YYYY-MM-DD-pr-<N>-<topic>.md`; emit the concrete filename with the actual date, PR number, and topic slug derived from the PR title. For a bound ticket keep `YYYY-MM-DD-<ticket-id>-<topic>.md`.
39
+
40
+ Pass an ordered array, home first, of exactly `{ number, url, title, headRefName, headRefOid, baseRefName, isCrossRepository, headRepository.nameWithOwner }` by value to provisioning in `using-git-worktrees/reference/pr-head.md`; use a one-element array for N=1 and write nothing during resolution.
20
41
 
21
42
  ## Gather from the PR and fetched base
22
43
 
23
- Discover PR-carried specs with `git diff --name-only origin/<baseRefName>...<headRefOid> -- <each flowGuards.specDirs dir>` using the resolved spec directories. Read each as the PR's stated contract beside its title/body. Only when superseded, name it in `**Predecessor:**` and apply `reference/superseding.md`'s banner; that banner is the allowed spec-directory write.
44
+ Require every retained PR's title/body, carried specs, and full threads before spec-writing. Stop before gather on any query failure, N=1 included: `threads for #<N> not fetched (<gh error>)`; name the ref and reason for unreadable title/body/specs too. Discover PR-carried specs per PR with `git diff --name-only <BASE_SHA>...<that PR's headRefOid> -- <each flowGuards.specDirs dir>` using the resolved spec directories. Read every carried spec's contents with `git show <that PR's headRefOid>:<path>` as that PR's stated contract beside its title/body; preserve the owning PR, original head, and path even when multiple PRs carry different versions of the same path. List member-carried spec paths under their PR in `## Problem` so resume can exclude them from new-spec candidates. Only when superseded, name it in `**Predecessor:**` and apply `reference/superseding.md`'s banner; that banner is the allowed spec-directory write.
24
45
 
25
46
  On every merge outcome, including clean, no-op, and unresolved, read constraints using `git show <BASE_SHA>:<path>`: `AGENTS.md`, the first-found overrides file (check `.pi/gauntlet-overrides.md`, `gauntlet-overrides.md`, `doc/gauntlet-overrides.md` in order at BASE_SHA), `REVIEW.md`, and all cited constraint documents. Carry missing-document results explicitly. Use these fetched base-HEAD rules, never fork-point or worktree copies. Treat PR edits to policy documents as proposals recorded under `changes:` or `dropped claims:`, not governing constraints.
26
47
 
27
- Dispatch scout always, and context-builder when a ticket is bound. Supply scout with the title/body verbatim, PR-carried specs, file list from `git diff --name-only <BASE_SHA>...<PRE_REWORK_HEAD>`, base-SHA constraint documents, merge result, and unresolved threads from the query owned by `using-git-worktrees/reference/pr-head.md`. Have scout read the diff itself from the worktree and the full comments of every thread. On query failure carry `threads: not fetched (<reason>)` and continue.
48
+ Dispatch scout always, and context-builder only for the home-bound ticket. Supply per PR the title/body verbatim, own changed paths and PR-carried specs as attributed original-head text separately from the combined worktree code input, and full comment threads with ids and locations from the query owned by `using-git-worktrees/reference/pr-head.md`. Supply combined changed paths from `git diff --name-only <BASE_SHA>...HEAD` run in the worktree after member and base merges, and have scout read `git diff <BASE_SHA>...HEAD` there. Supply base-SHA constraint documents, `Member merge:` lines, and base merge outcomes. Keep `PRE_REWORK_HEAD` as home's original provenance anchor for Before, record, and brief, not the code-input boundary.
28
49
 
29
- Instruct scout: treat the PR as the starting state, not the target; judge every stated claim against its diff and current base rules. Name the smallest repair satisfying those rules; propose a rewrite only when no repair can satisfy them. Render `## PR under rework` after `## Codebase recon`, with `keeps:`, `changes:`, `dropped claims:`, `constraints added since fork:` (one line per base-HEAD rule absent at the fork point), and one proposed disposition per unresolved thread: `fix | keep open | decline: <reason>`. Carry any merge contract exception into `changes:`. Run the framing check in `SKILL.md` `### 3` against the diff as the ask.
50
+ Instruct scout: treat the combined PR state as the starting state, not the target; judge every stated claim against its diff and current base rules. Name the smallest repair satisfying those rules; propose a rewrite only when no repair can satisfy them. Render `## PR under rework` after `## Codebase recon`, with one `### PR #<N> - <title>` subsection per PR, home first, each with lowercase `keeps:`, `changes:`, `dropped claims:`, and one disposition per unresolved thread: `fix | keep open | decline: <reason>`. Render `constraints added since fork:` once for home, one line per base-HEAD rule absent at its fork point. Carry merge contract exceptions into `changes:`. Run the framing check in SKILL.md ### 3 against the combined diff as the ask.
30
51
 
31
52
  ## Record in the spec
32
53
 
33
- In `## Problem`, name `PR <owner/repo#N> - <title>`, its stated intent, every `constraints added since fork:` line as a constraint, and the existing `Framing:` line. Preserve the following grammar after `## Acceptance criteria`:
54
+ In `## Problem`, name every `PR <owner/repo#N> - <title>`, its stated intent, member ticket bindings and carried spec paths, every `constraints added since fork:` line as a constraint, and the existing `Framing:` line. Preserve this grammar after `## Acceptance criteria`: repeat the PR/Pre-rework head/Threads group for every retained PR, home first; write the combined fields once, then one provenance subsection per PR.
34
55
 
35
56
  ```markdown
36
57
  ## Rework record
37
58
 
38
59
  PR: <owner/repo#N> - <title>
39
60
  Pre-rework head: <40-char sha>
61
+ Threads:
62
+ - <path>:<line> <thread id> - fix | keep open | decline: <reason>
63
+ PR: <owner/repo#M> - <title>
64
+ Pre-rework head: <40-char sha>
65
+ Threads: none
40
66
  Base: <baseRefName> @ <sha7> - merge: <no-op | sha7 | unresolved - files>
41
- Keeps: <what of the original survives>
67
+ Keeps: <what of the combined original survives>
42
68
  Changes: <what the rework alters, repair over rewrite>
43
69
  Dropped claims: <one `- <claim>` per PR-stated claim no longer true | none>
44
- Threads:
45
- - <path>:<line> <thread id> - fix | keep open | decline: <reason>
46
70
  Scoped tests: none run | <file>: green | <file>: red - <tests>
71
+
72
+ ### PR #<N> - <title>
73
+ keeps: <copied claim>
74
+ changes: <copied change>
75
+ dropped claims: <copied claims | none>
76
+
77
+ ### PR #<M> - <title>
78
+ keeps: <copied claim>
79
+ changes: <copied change>
80
+ dropped claims: <copied claims | none>
47
81
  ```
48
82
 
49
- Use `Threads: none` for a successful empty fetch and `Threads: not fetched (<reason>)` for failure. Preserve the query owner's outdated/path-only location fallback. Instantiate `Scoped tests: none run (<reason>)` when no documented file-scoped runner exists or `requires credentials: true`; otherwise record the scoped result, including failures. Name landed contract exceptions in `Changes:`. For an unresolved merge, open Design with `merge origin/<base> and resolve <files>` as eventual plan Task 1. Preserve the full pre-rework SHA so `git log` history remains checkable.
83
+ For N=1 render one group and one subsection; place `Threads:` immediately after `Pre-rework head:`. Use `Threads: none` for a successful empty fetch. A failed fetch stops before gather, never continues with `Threads: not fetched`. Preserve the query owner's outdated/path-only location fallback and all thread dispositions. Preserve full original heads; add no member merge SHA field. Keep lowercase provenance distinct from the capitalized combined fields: finish/handoff copy only top-level capitalized fields, never subsection claims.
84
+
85
+ Instantiate `Scoped tests: none run (<reason>)` when no documented file-scoped runner exists or `requires credentials: true`; otherwise record the scoped result, including failures. Name landed contract exceptions in `Changes:`. For an unresolved base merge, open Design with `merge origin/<base> and resolve <files>` as eventual plan Task 1.
50
86
 
51
- Preserve this record when `reference/spec-finalization.md` replaces the draft; dropping it fails inline lint. Carry the PR title/body verbatim beside bound-ticket rows in the council's `Human input`. Do not attach a rework record to ticket or free-text specs.
87
+ Preserve this record when `reference/spec-finalization.md` replaces the draft; dropping any retained PR fails inline lint. Use provisioning Step 4's home plus `MEMBER_HEADS` as the expected inputs. Carry every retained PR title/body verbatim beside home-bound-ticket rows in the council's `Human input`. Do not attach a rework record to ticket or free-text specs.
@@ -18,9 +18,11 @@ Spec-writing replaces the context draft, in this exact order:
18
18
  rewrite carries `none` too - a redraw is a new approval, with no carry-over.
19
19
  For a PR subject, follow `pr-subject.md`'s spec contract: carry the draft's
20
20
  `## PR under rework` into `## Rework record` after `## Acceptance criteria`,
21
- preserving its exact fixed-line grammar, 40-character pre-rework SHA, merge result,
21
+ preserving its repeated PR/Pre-rework head/Threads groups, one per retained PR,
22
+ home first, each 40-character pre-rework SHA, merge result,
22
23
  keeps/changes/dropped claims, thread dispositions and fetch status, contract exceptions,
23
- and scoped test results (including failures and reasons none ran). Carry the PR's
24
+ and scoped test results (including failures and reasons none ran), then one
25
+ `### PR #<N>` provenance subsection per PR with lowercase claims. Carry every PR's
24
26
  stated intent and constraints added since fork into Problem. Open Design with the
25
27
  unresolved merge repair when present. Omit the record from a ticket or free-text spec.
26
28
  3. **Immediately after the write**, confirm line 1 of the file is no longer
@@ -51,7 +53,7 @@ After writing the spec to `<project>/doc/specs/<filename>.md` (per [Filename Con
51
53
  - **Placeholder scan.** Any `TODO`, `TBD`, `<fill in>`, `[example]`, `xxx`? Either resolve them or convert to explicit "Open Questions" with names.
52
54
  - **Internal consistency.** Does Section 4 contradict Section 2? Does the header block carry `**Amend-grant:**` exactly once with the value `none` (the [predicate](amendment-surface.md#standing-grants))? Are component names and field names consistent throughout? If the spec replaces a prior design, confirm the predecessor carries the supersession banner and its href resolves to this spec's final filename.
53
55
  - **Documentation named.** Does the spec name all three classes (feature/user-facing introduced; materially amended; derived/memory invalidated), or an explicit "none" for each? Enforce the materiality bar in `documentation-impact.md` without restating it: each listed doc names the category it clears, none is a code-mirror, amend-over-create was applied, and skill/agent bodies are implementation surface, not doc-impact entries here.
54
- - **Ticket contract present.** Require `## Acceptance criteria` in exactly three forms: ticket provenance (`Ticket <ref>, <heading or "checkbox list">, rows verbatim:`) with verbatim rows and dispositions; drafted provenance (`Drafted from the design, no ticket rows:` or `Drafted from the design, ticket <ref> has no acceptance criteria:`) with at least one `- [ ]` row and a disposition on every row; or the single line `none - ticket not fetched (<reason>)`. Reject `none - no ticket` and `none - ticket has no acceptance criteria` as final bodies. For a PR subject, require the complete `## Rework record` per `pr-subject.md`; absence fails inline lint, including after a full replacement or redraw.
56
+ - **Ticket contract present.** Require `## Acceptance criteria` in exactly three forms: ticket provenance (`Ticket <ref>, <heading or "checkbox list">, rows verbatim:`) with verbatim rows and dispositions; drafted provenance (`Drafted from the design, no ticket rows:` or `Drafted from the design, ticket <ref> has no acceptance criteria:`) with at least one `- [ ]` row and a disposition on every row; or the single line `none - ticket not fetched (<reason>)`. Reject `none - no ticket` and `none - ticket has no acceptance criteria` as final bodies. For a PR subject, require the complete `## Rework record` per `pr-subject.md`; absence fails inline lint, including after a full replacement or redraw. Build expected identities and original heads from the provisioning Step 4 report (home plus `MEMBER_HEADS`), not from the record itself. Require each expected identity's `PR:`/`Pre-rework head:`/`Threads:` group with its full matching head and a `### PR #<N>` provenance subsection; reject a missing group or subsection naming the missing PR. Apply the same predicate for N=1.
55
57
  - **Scope check.** Does every paragraph serve the goal? Cut filler. If something is out of scope, say it's out of scope.
56
58
  - **Ambiguity check.** Is every "we should..." backed by a concrete decision? Replace "we could probably" with "we will" or "we won't".
57
59
 
@@ -63,7 +65,7 @@ Exclude drafted rows from `Human input (verbatim; off-limits for over-spec)`; th
63
65
 
64
66
  Before dispatching the worker below, resolve `documentation-impact.md` relative to this loaded reference as one absolute `<DOCUMENTATION_IMPACT_GUIDELINE>` path value. Pass that value in the worker task; do not add it to the spec.
65
67
 
66
- After the inline lint and before the user review gate, **brainstorming owns the critique-pass gate**; council **apply mechanics** live in `/skill:roasting-the-spec` (single source of truth - link, don't restate). Resolve the council with `gauntlet_setting({ key: "specCouncil" })` - the tool returns the merged (repo-over-preset) value as `{ verdict, members, chair, malformed, warning, errors }`. **Do not** hand-roll a settings read. When `verdict` is `"council"`, the council *is* the critique pass - invoke `/skill:roasting-the-spec` automatically (no offer, no prompt), passing `members`/`chair`; for a PR subject include the PR title and body verbatim beside the bound ticket rows in `Human input`; also pass the verbatim human input (the original prompt, any ticket AC snapshot - the raw rows under the gather draft's `## Ticket acceptance criteria (verbatim)` heading, never the spec's section, which holds the author's dispositions - and, for every question that offered a disposition (the cut question and the unmatched-group question in `ticket-acceptance.md`), the full question and the user's answer) - roasting-the-spec forwards it to members, chair, and applier as the `Human input (verbatim; off-limits for over-spec)` block; its dispatched applier applies the spec edits and supplies the returned audit (Applied/Deferred/Rejected). When `verdict` is `"worker"`, dispatch the worker below. If `malformed` is true or `errors` is non-empty, emit the `warning`/error as one line, then branch strictly on `verdict` - `malformed` can accompany *either* verdict (e.g. a bad `chair` with valid `members` still returns `council`), so never infer the worker path from `malformed` alone. If `gauntlet_setting` is unavailable, stop and report - never fall back to a manual bash/JSON settings merge. The already-applied council edits (or the worker's in-place fixes) ride in the same worktree commit. The conceptual precedence rule lives in `../../verification-before-completion/reference/settings-precedence.md`.
68
+ After the inline lint and before the user review gate, **brainstorming owns the critique-pass gate**; council **apply mechanics** live in `/skill:roasting-the-spec` (single source of truth - link, don't restate). Resolve the council with `gauntlet_setting({ key: "specCouncil" })` - the tool returns the merged (repo-over-preset) value as `{ verdict, members, chair, malformed, warning, errors }`. **Do not** hand-roll a settings read. When `verdict` is `"council"`, the council *is* the critique pass - invoke `/skill:roasting-the-spec` automatically (no offer, no prompt), passing `members`/`chair`; for a PR subject include every retained PR title and body verbatim beside the home-bound ticket rows in `Human input`; also pass the verbatim human input (the original prompt, any ticket AC snapshot - the raw rows under the gather draft's `## Ticket acceptance criteria (verbatim)` heading, never the spec's section, which holds the author's dispositions - and, for every question that offered a disposition (the cut question and the unmatched-group question in `ticket-acceptance.md`), the full question and the user's answer) - roasting-the-spec forwards it to members, chair, and applier as the `Human input (verbatim; off-limits for over-spec)` block; its dispatched applier applies the spec edits and supplies the returned audit (Applied/Deferred/Rejected). When `verdict` is `"worker"`, dispatch the worker below. If `malformed` is true or `errors` is non-empty, emit the `warning`/error as one line, then branch strictly on `verdict` - `malformed` can accompany *either* verdict (e.g. a bad `chair` with valid `members` still returns `council`), so never infer the worker path from `malformed` alone. If `gauntlet_setting` is unavailable, stop and report - never fall back to a manual bash/JSON settings merge. The already-applied council edits (or the worker's in-place fixes) ride in the same worktree commit. The conceptual precedence rule lives in `../../verification-before-completion/reference/settings-precedence.md`.
67
69
 
68
70
  When `verdict` is `"worker"`, dispatch one fresh `worker` that applies the scope + ambiguity checks and fixes them in place:
69
71
 
@@ -42,7 +42,7 @@ Apply these integrity classes inline without invoking ticket shaping. Preserve e
42
42
  |---|---|
43
43
  | Wishful | Rewrite to the design's observable with design-sourced baseline, target, and measurement |
44
44
  | Tautological | Rewrite to a design-sourced, already-enumerated set or observable |
45
- | Tree-wide | Split a drafted row requiring zero/no findings of one or several kinds anywhere in or across a tree or set into one row per family from the explicit complete enumeration in Design for that row, covering every kind it names; give each family's own observable and preserve every outcome across the resulting rows. For an absent, partial, or unrelated enumeration, stop before writing, quote the row, and request the complete enumeration in Design. Keep this recovery drafted-only: a ticket row of this shape stays one verbatim row; require human ticket repair through `/skill:shape-ticket` before approval to split it |
45
+ | Tree-wide | Split a drafted row requiring zero/no findings of one or several kinds anywhere in or across a tree or set into one row per family from the explicit complete enumeration in Design for that row, covering every kind it names; give each family's own observable and preserve every outcome across the resulting rows. For an absent, partial, or unrelated enumeration, stop before writing, quote the row, and request the complete enumeration in Design; obtain it from the human or Design; never invent the list in question options or recommendations or infer completion of a partial list. Keep this recovery drafted-only: a ticket row of this shape stays one verbatim row; require human ticket repair through `/skill:shape-ticket` before approval to split it |
46
46
  | Unspecified-but-binding | Resolve the value from Design and record the resolution there; ask the user for a missing value |
47
47
  | External-input | Ask the user for the external yardstick before writing |
48
48
  | Post-deployment | Rewrite to a pre-production observation when available, retaining `venue:`; move production-only observations to a tracked, non-blocking `Post-deployment housekeeping` Design clause, never an AC row |
@@ -31,21 +31,12 @@ Then call `phase_tracker({ action: "start", phase: "ship" })`.
31
31
  ## The Process
32
32
 
33
33
  ### Step 1: Verify Tests
34
-
35
34
  **Hard verification gate.** Tests/format/lint must pass before presenting any options — including Discard. The user's stated intent to throw the branch away does not change whether the diff is in a verifiable state; verifying first surfaces accidental damage to unrelated code before the branch is gone forever. The skip rule below is the only exception.
36
-
37
- Read the selected run spec before verification. Detect a PR subject only by its `## Rework record`, never by an open PR on `$FEATURE`. Read `PR:` and the full `Pre-rework head:` into `PRE_REWORK_HEAD` from that record; missing pre-head -> stop naming the spec path. Confirm with `gh pr view <N> --json number,url,headRefName,baseRefName,state` (use the record's repository): non-`OPEN` -> stop: `PR #N is <state>; the rework cannot be pushed to it.`; headRefName != `$FEATURE` -> stop naming both branches. Retain baseRefName for Step 3.
38
-
35
+ Read the selected run spec before verification. Detect a PR subject only by its `## Rework record`, never by an open PR on `$FEATURE`. Read every top-level `PR:`/`Pre-rework head:` pair, home first, retaining each full SHA; missing pre-head -> stop naming the spec path. Set `PRE_REWORK_HEAD` to home's SHA. Read only combined capitalized `Base:`, `Keeps:`, `Changes:`, `Dropped claims:`, `Scoped tests:` fields; ignore lowercase provenance under `### PR #<N>`. For N=1, read `Threads:` directly after `Pre-rework head:` and retain the provenance subsection without changing finish output. Confirm only home with `gh pr view <N> --json number,url,headRefName,baseRefName,state` (use the record's repository): non-`OPEN` -> stop: `PR #N is <state>; the rework cannot be pushed to it.`; headRefName != `$FEATURE` -> stop naming both branches. Retain baseRefName for Step 3.
39
36
  Recognize a ledger by line 1 exactly `# Ledger - loop engine`. For a ledger, use its `verification:` command throughout finish, including the minors round. For a PR subject with a task plan, use the union of every plan task's `Tests:` commands plus the executable lint/format commands in the header's `**Verification:**` line; treat `none - PR subject, scoped` as no header commands, not an empty task union. Capture this set and accepted-complete task evidence before stripping the plan. Apply the hard gate and skip rule below to this set. For every other subject with a task plan, run the plan header's `**Verification:**` set in the worktree: `(cd "$WORKTREE" && <command>)`. With no plan in this session, run the verification command(s) of the affected services from the gauntlet overrides file or `AGENTS.md` (look for "verification", "CI", or "test" sections).
40
-
41
37
  **Skip rule.** Skip the run only when this set passed on a known clean commit in the verify phase of this session and no edit or write landed outside `<telemetry.dir>` since. Check the current tree: `git -C "$WORKTREE" diff --quiet <commit the run passed on> -- . ':!<telemetry.dir>'` must exit 0, and `git -C "$WORKTREE" status --porcelain --untracked-files=all -- . ':!<telemetry.dir>'` must exit 0 with empty output (`<telemetry.dir>` defaults to `.pi/gauntlet/telemetry`). These checks cover committed, staged, unstaged, and untracked changes outside telemetry. Otherwise run it once; a missing verified commit, a failed check, or uncertainty means run.
42
-
43
38
  **Scoping caveat — pre-existing findings.** Some services carry lint findings unrelated to the diff. If verification fails on lines you didn't touch:
44
-
45
- 1. Confirm with `git -C "$WORKTREE" diff <base>...HEAD --name-only` that the offending file isn't in your diff.
46
- 2. Surface the pre-existing finding to the user as a separate issue — do **not** auto-fix it in this completion ("surface, don't auto-fix").
47
- 3. Proceed only after the user acknowledges.
48
-
39
+ Confirm with `git -C "$WORKTREE" diff <base>...HEAD --name-only` that the offending file isn't in your diff. Surface the pre-existing finding to the user as a separate issue - do **not** auto-fix it in this completion ("surface, don't auto-fix"). Proceed only after the user acknowledges.
49
40
  **If tests fail (within your diff):**
50
41
  ```
51
42
  Tests failing (<N> failures). Must fix before completing:
@@ -54,13 +45,7 @@ Tests failing (<N> failures). Must fix before completing:
54
45
 
55
46
  Cannot proceed until tests pass.
56
47
  ```
57
-
58
- Stop. Don't proceed to Step 2.
59
-
60
- **If tests pass:** Continue to Step 2.
61
-
62
- No documentation prompt here: Documentation impact is decided at spec time (`/skill:brainstorming` section 6, gated by `brainstorming/reference/documentation-impact.md`) and has already shipped in the diff by the time you reach finishing.
63
-
48
+ Stop. Don't proceed to Step 2. **If tests pass:** Continue to Step 2. No documentation prompt here: Documentation impact is decided at spec time (`/skill:brainstorming` section 6, gated by `brainstorming/reference/documentation-impact.md`) and has already shipped in the diff by the time you reach finishing.
64
49
  ### Step 2: Detect Environment
65
50
 
66
51
  For a PR subject, use the Step 4 PR menu. For other subjects: detached HEAD (`git -C "$WORKTREE" symbolic-ref -q HEAD` prints nothing) -> reduced 4-option menu (no merge), no cleanup. Otherwise the standard 5 options.
@@ -187,7 +172,7 @@ Omit the block when N = 0.
187
172
  |---|---|---|
188
173
  | `git merge-base --is-ancestor <PRE_REWORK_HEAD> HEAD` | Continue | Stop: `PR branch history was rewritten: <PRE_REWORK_HEAD7> is not an ancestor of HEAD; the rework must be re-merged, not rebased.` |
189
174
  | `git merge-base --is-ancestor origin/<headRefName> HEAD` | Offer push | Withhold push: `push withheld: origin/<headRefName> is at <sha7>, local HEAD at <sha7>; a non-force push cannot land` |
190
- Run each proof with `git -C "$WORKTREE"`. Treat every exit other than 0 or 1 as a command error and stop with its output. For a withheld push only, separately prove `git -C "$WORKTREE" merge-base --is-ancestor <PRE_REWORK_HEAD> origin/<headRefName>`: exit 0 appends `(origin moved past the rework's base <PRE_REWORK_HEAD7>)`; exit 1 adds nothing; other exits stop as command errors. Divergence alone proves no origin advance.
175
+ Run the first proof for every recorded original head, home first; the first exit 1 stops with the table's exact message naming that PR's SHA. Run the second proof only for home. Run each proof with `git -C "$WORKTREE"`. Treat every exit other than 0 or 1 as a command error and stop with its output. For a withheld push only, separately prove `git -C "$WORKTREE" merge-base --is-ancestor <PRE_REWORK_HEAD> origin/<headRefName>`: exit 0 appends `(origin moved past the rework's base <PRE_REWORK_HEAD7>)`; exit 1 adds nothing; other exits stop as command errors. Divergence alone proves no origin advance.
191
176
  Read and apply [menu-layout](../brainstorming/reference/menu-layout.md) for rendering and numeric replies. Present:
192
177
  ```
193
178
  Implementation complete. What would you like to do?
@@ -267,7 +252,7 @@ node <bin>/gauntlet-telemetry-seal.mjs --worktree "$WORKTREE" --option <pr|squas
267
252
 
268
253
  The seal prints one line per record (`sealed <path>`, `already sealed <path>`) or one bare outcome (`no telemetry run`, `telemetry disabled`) and exits 0; print its stdout verbatim in the ship completion message. Any nonzero exit stops the flow before the first landing command, quoting the bin's line: exit 2 (`no record at <path>`, `unparseable record <path>: <reason>`) means this gauntlet run's recorder never armed or its record is corrupt; exit 1 is a usage, environment, or git/jj failure (`seal failed <path>: <reason>` restored the pre-seal bytes). The user decides; never land without the seal.
269
254
  #### PR-subject row 1: Push to the PR branch
270
- Capture the scoped verification set and thread evidence before the strip. Refetch unresolved threads with the query owned by [pr-head](../using-git-worktrees/reference/pr-head.md); preserve recorded dispositions, use the table below, and omit threads now resolved on GitHub. Use the query's location rendering.
255
+ Capture the scoped verification set and accepted-complete task/thread evidence before the strip. Refetch threads per recorded PR using the paginated single-`$n` query (one invocation per PR) owned by [pr-head](../using-git-worktrees/reference/pr-head.md); retain PR attribution and use the query's location rendering. For home, use the table below and omit threads now resolved on GitHub. For members, copy recorded dispositions and mark newly resolved or newly opened states from the refresh; never use home's first-parent history as member-fix proof.
271
256
  | Refetched thread / evidence | Resolution |
272
257
  |---|---|
273
258
  | Recorded `fix`, matching task title carries id, task accepted by spec and code review and `complete`, exactly one naming commit | `fixed in <sha7>` |
@@ -280,23 +265,39 @@ Count naming commits with `git -C "$WORKTREE" log --first-parent --format=%h --g
280
265
  Run the existing strip-and-seal block with `--option pr --base origin/<baseRefName>` and the selected spec; retain its stop-on-error behavior. This seal measures the whole PR, not rework-only; never use PRE_REWORK_HEAD as its base. Then:
281
266
  ```bash
282
267
  git -C "$WORKTREE" update-ref refs/gauntlet/pre-rework/pr-<N> <PRE_REWORK_HEAD>
283
- git -C "$WORKTREE" push --no-follow-tags origin HEAD:refs/heads/<headRefName>
284
268
  ```
285
- Stop on update-ref failure. Report a rejected push once, skip body editing, and return to the menu without retrying or forcing. Preserve the worktree.
286
- After a successful push, fetch the description fresh with `gh pr view <N> --json body` in the record's repository. Decode the JSON body without trimming or shell command substitution (which loses trailing newlines). Create a scratch body file with `mktemp` outside the repo; write the existing body bytes exactly, then one blank line, then this block:
269
+ Write this ref for every recorded PR with its own number and original SHA; stop on update-ref failure. Push only home, non-force:
270
+ ```bash
271
+ git -C "$WORKTREE" push --no-follow-tags origin HEAD:refs/heads/<home headRefName>
272
+ ```
273
+ Save the pushed home head SHA on success. Report a rejected push once, skip body editing and closure, name members left open, and return to the menu without retrying or forcing. Preserve the worktree.
274
+ After a successful push, fetch only home's description fresh with `gh pr view <N> --json body` in the record's repository. Decode the JSON body without trimming or shell command substitution (which loses trailing newlines). Create a scratch body file with `mktemp` outside the repo; write the existing body bytes exactly, then one blank line, then this block:
287
275
  ```markdown
288
276
  ## Rework
289
277
 
290
278
  Before: <PRE_REWORK_HEAD> (local ref `refs/gauntlet/pre-rework/pr-<N>` in the rework checkout; the SHA is the portable handle and stays on origin because the push was not forced)
279
+ Absorbs: #13 (<url>), #14 (<url>)
291
280
  Spec: <spec path> at <HEAD7>
292
281
  Keeps: <from the record>
293
282
  Changes: <from the record>
294
283
  Dropped claims: <none | one "No longer true: <claim>" line per dropped claim>
295
284
  Verification: scoped - <commands>; CI on this push holds the full run. Telemetry: whole PR against <baseRefName>.
296
285
  Threads:
297
- - <path>:<line> - <resolution from the table>
286
+ #<N>:
287
+ - <path>:<line> - <resolution for this PR>
288
+ ```
289
+ Instantiate `Absorbs:` from every non-home pair in record order; omit it for N=1. Label each PR Threads list `#<N>:` for N>1; retain the unchanged unlabeled list for N=1. Put `supersedes the description above` on the block's second line only when every original claim is dropped. Use `Threads: none` or `Threads: not fetched (<reason>)` instead of the list for those outcomes. Run `gh pr edit <N> --body-file <scratch-file>` in the record's repository without another approval. On body fetch/edit failure, keep the push and print the block for manual paste with the error; never replace the description with just the block. Skip closure on body fetch/edit failure and name members left open.
290
+ Only after a successful push and successful home body edit, refresh each member with `gh pr view <N> -R <owner/repo> --json state,headRefOid`. Fetch each refreshed head that is not yet local (`git -C "$WORKTREE" fetch origin <current headRefOid>`; for a fork member use its head repository URL) before the ancestry test. Skip and name a member whose head fetch fails: `#14 skipped: head <sha7> not fetchable`. Require state `OPEN` and `git -C "$WORKTREE" merge-base --is-ancestor <current headRefOid> <pushed home head>` exit 0. Exclude and name ineligible members: `#14 skipped: head moved to <sha7>, not in home` or `#14 skipped: state CLOSED`. Treat unexpected Git exits and failed refreshes as errors, withhold closure and report the reason; merge nothing implicitly.
291
+ Ask nothing when none qualify or N=1. Otherwise render one gated question before marking ship complete. Instantiate each actual eligible target and its exact comment using its recorded pre-rework SHA7 and home's number/URL; the two-member rendering is:
292
+ ```text
293
+ Close the absorbed PRs with these comments?
294
+ #13: "Reworked into #12 (<home url>) by the gauntlet; this PR's commits are merged there and remain reachable as <#13 pre-rework sha7>."
295
+ #14: "Reworked into #12 (<home url>) by the gauntlet; this PR's commits are merged there and remain reachable as <#14 pre-rework sha7>."
296
+ 1 - yes, close #13, #14
297
+ 2 - no, leave them open
298
298
  ```
299
- Put `supersedes the description above` on the block's second line only when every original claim is dropped. Use `Threads: none` or `Threads: not fetched (<reason>)` instead of the list for those outcomes. Run `gh pr edit <N> --body-file <scratch-file>` in the record's repository without another approval. On body fetch/edit failure, keep the push and print the block for manual paste with the error; never replace the description with just the block. Post no review-thread replies, merge no PR, and change no ticket state.
299
+ On `1`, re-fetch state/head immediately before each `gh pr close <N> --comment "<that line's text>" -R <owner/repo>`. Execute only the displayed target/text pairs in the displayed repository; never pass `--delete-branch`. Skip and report any target whose state or head changed after approval, or whose refresh fails, rather than acting under stale authorization. Report a failed close once, never retry. On `2`, leave members open. Then mark ship complete and report each member outcome: `Closed: #13, #14`, `Closed: #13; #14 close failed (<error>)`, or `Member PRs #13, #14 left open.` Instantiate actual members in all summaries, including partial outcomes and skipped members; for N=1 omit every member line and question.
300
+ Rows 2-6, withheld/failed ancestry, withheld/rejected push, and failed body fetch/edit never offer closure; name untouched members with `Member PRs #13, #14 left open.` Post no review-thread replies, merge no PR, change no ticket state, and close no PR other than the absorbed members through the question above.
300
301
  #### Option 1: Push and Create PR
301
302
 
302
303
  Run the strip-and-seal block above first (plan stripped, telemetry record sealed), then:
@@ -456,7 +457,6 @@ A host-owned worktree (Step 6: no `home:` match, or the legacy "Otherwise") keep
456
457
  - **Fix:** Never `git rm` under the telemetry dir. Until the seal the record is untracked and git-excluded, so a plan strip cannot reach it; after the seal it is a committed deliverable.
457
458
 
458
459
  ## Completion
459
-
460
460
  Once the chosen option (Options 1-4 or PR-subject row 1 - not Discard) is executed successfully, mark the ship phase complete:
461
461
 
462
462
  ```
@@ -83,7 +83,7 @@ loop: tracker state is extension state no subagent can read.
83
83
  - A phase `in_progress` (verify or ship included - resume stops at verify on its own)
84
84
  -> append the process-state section exactly as `## Producers` in
85
85
  `reference/brief-contract.md` lays it out: both status outputs verbatim
86
- (`No plan active.` verbatim when there is no plan), the contract's artifact-specific fields, and the gate-history line. Recognize the loop alternative by ledger line 1; read the ledger from the run worktree for its durable last decision. Read the run worktree's spec; when it carries `## Rework record`, copy its PR identity (without the title) and full pre-rework head into the optional fields in the contract's producer layout. Without that record, use the ordinary layout. Use only the contract's grammar; write no second state file. Append with a single
86
+ (`No plan active.` verbatim when there is no plan), the contract's artifact-specific fields, and the gate-history line. Recognize the loop alternative by ledger line 1; read the ledger from the run worktree for its durable last decision. Read the run worktree's spec; when it carries `## Rework record`, copy every top-level PR identity (without the title) and its full pre-rework head, home first in record order, into the optional fields in the contract's producer layout. Without that record, use the ordinary layout. Use only the contract's grammar; write no second state file. Append with a single
87
87
  `cat >> "<brief path>" <<'EOF' ... EOF` whose body is that block.
88
88
  - Re-read the file tail and confirm it ends with the gate-history line from the
89
89
  contract and nothing after.
@@ -142,7 +142,7 @@ owning skill **without** its reset-bearing entry:
142
142
  | Active after restore | Continue in | Entry point |
143
143
  |---|---|---|
144
144
  | brainstorm | [brainstorming checklist](../brainstorming/SKILL.md#checklist) | on-disk state decides the step: draft marker on line 1 of the spec file -> step 4; spec title -> step 8. Never `/skill:brainstorming` entry (it resets both trackers) |
145
- | approved spec with `**Engine:** loop`, no ledger | loop-implementation | after check 6, arm with `start brainstorm`, then `skip brainstorm resume: <spec path>` before driver admission; carry absolute worktree/spec paths and plan directory through Entry steps 1-5 per `reference/reconstruction.md` "Spec without plan": settings step 1, verification step 2, driver's plan skip step 3, ledger creation step 4, check/commit and implement start step 5; no writing-plans body or task tracker |
145
+ | approved spec with `**Engine:** loop`, no ledger | loop-implementation | after check 6, arm with `start brainstorm`, then `skip brainstorm resume: <spec path>` before driver admission; carry absolute worktree/spec paths and plan directory through Entry steps 1-5 per `reference/reconstruction.md` "Spec without plan": label Entry steps 1-5 explicitly in the response as Entry step 1: resolved settings; Entry step 2: verification; Entry step 3: driver's plan skip; Entry step 4: clean named-branch check and ledger creation; Entry step 5: `plan_check`, commit and implement start; no writing-plans body or task tracker |
146
146
  | plan with task engine | writing-plans body | skip its `start plan` call (already in_progress) |
147
147
  | implement with line-1 ledger | loop-implementation | complete `reference/reconstruction.md` "Spec with ledger" ordered restore, cited by `reference/brief-contract.md`'s call table: entry checks 1-6, brainstorm start/skip, plan start/check/skip with reason `resume: <spec> (loop engine)`, implement start, no task tracker; state the entire ordered sequence explicitly even when entry checks are already reported as passed; then continue at step 5 (`Before each dispatch`), applying the shared cap boundary in reconstruction step 4 before continuation and preserving ledger cap, iterations/retry data; dirty tree stops before dispatch; reuse the existing ledger |
148
148
  | implement with task plan | subagent-driven-development | re-validate `<task>` first (re-run its `Tests:`), then the task loop from the first non-complete task |
@@ -27,7 +27,7 @@ Headings, fixed order. A consumer keys on headings, never prose.
27
27
  | `## Repo state` | one field per line: `toplevel`, `worktree: yes <path>` or `worktree: no`, `branch` (`detached` when none), `HEAD`, `base` (`unknown` when no remote resolves), `dirty: <porcelain>` or `dirty: clean`, `diff-stat` (`unavailable` when base unknown), `test cmd`; any field `unavailable` when its command failed; heading `## Repo state: not a git repo` outside git | always |
28
28
  | `## Decisions` | bullets; rejected alternatives marked `rejected:` | always |
29
29
  | `## Skills loaded` | frontmatter `name`s; `## Skills loaded: none` when none | always |
30
- | `## Process state` | `phase_tracker status` then `plan_tracker status` verbatim; `Active task: <name|none>`; optional `PR: <owner/repo#N>` and `Pre-rework head: <40-char sha>` lines, present together only for a PR subject; the line `Gate history not restored - re-validate before advancing.` | only when a phase is `in_progress` and no hotfix flow is in context |
30
+ | `## Process state` | `phase_tracker status` then `plan_tracker status` verbatim; `Active task: <name|none>`; optional one or more consecutive `PR: <owner/repo#N>` / `Pre-rework head: <40-char sha>` pairs, one per record group, home first, in record order, only for a PR subject; the line `Gate history not restored - re-validate before advancing.` | only when a phase is `in_progress` and no hotfix flow is in context |
31
31
 
32
32
  For a loop ledger (line 1 exactly `# Ledger - loop engine`), replace `Active task` with these two fields, retaining both tracker status outputs and the gate-history line:
33
33
 
@@ -51,10 +51,11 @@ inline. `## Repo state` is matched by prefix: `## Repo state` or
51
51
  cohort's `handoff` skill wrote, which ends at `## Skills loaded`. Layout, byte-exact:
52
52
  one blank line, the heading on its own line, a blank line, the `phase_tracker status`
53
53
  output verbatim, a blank line, the `plan_tracker status` output verbatim, a blank line,
54
- `Active task: <name|none>`, then, only for a PR subject, exactly two lines:
55
- `PR: <owner/repo#N>` and `Pre-rework head: <40-char sha>`, then the gate-history line -
54
+ `Active task: <name|none>`, then, only for a PR subject, one or more consecutive
55
+ `PR: <owner/repo#N>` / `Pre-rework head: <40-char sha>` pairs, one per record group,
56
+ home first, in record order, then the gate-history line after the last pair -
56
57
  unfenced, nothing after.
57
- For a task plan, set `Active task` to the first `→` task name, else `none` (also for `No plan active.`). For a line-1 ledger, read its path and last decision and use the loop alternative above, retaining the optional PR pair before the gate-history line.
58
+ For a task plan, set `Active task` to the first `→` task name, else `none` (also for `No plan active.`). For a line-1 ledger, read its path and last decision and use the loop alternative above, retaining the optional PR pairs before the gate-history line. Copy only top-level record pairs, without titles; `Threads:` and record `### PR #<N>` subsections are not brief fields. For N=1 emit one pair; without a Rework record use the ordinary layout.
58
59
 
59
60
  Example appended block (the fence is documentation; the brief carries no fence):
60
61
 
@@ -114,12 +115,20 @@ Parse stops (print the offending line, make no tracker call):
114
115
 
115
116
  ## Process-state restore
116
117
 
117
- Before any tracker write, read the optional PR identity and fetch its branch with
118
+ Before any tracker write, read the optional ordered PR pairs. Fetch branch identity
119
+ from the first pair only (home) with
118
120
  `gh pr view <N> --repo <owner/repo> --json headRefName`. Compare `headRefName` with
119
121
  `git -C <worktree> branch --show-current`. A mismatch stops with
120
122
  `brief names PR #N on <headRefName>, worktree is on <branch>`, substituting the observed PR number and both actual branch names, never literal placeholders; a failed fetch stops
121
- with its error. Carry the paired full pre-rework head into reconstruction's
122
- candidate selection. Without the optional pair, retain ordinary restoration.
123
+ with its error. Do not fetch later pairs' branches for this identity check.
124
+ Read the worktree spec's `## Rework record` and compare its top-level `PR:` identities
125
+ (without titles) with the brief identities in order. A mismatch stops with
126
+ `brief lists <brief order>, record lists <record order>`, substituting both observed
127
+ ordered lists, before any tracker call. Resolve the spec through reconstruction's
128
+ candidate rules below before this comparison; artifact presence supplies no approval.
129
+ Carry all paired full pre-rework heads downstream in their original order;
130
+ reconstruction uses the first pair's head as its scan base. Without optional pairs,
131
+ retain ordinary restoration.
123
132
  Preserve the entry checks, standing-grant validation, plan repair, and task snapshot;
124
133
  PR identity implies no approval or gate evidence.
125
134
 
@@ -25,7 +25,7 @@ for a base ref before reading any artifact.
25
25
 
26
26
  A pinned spec (seed route, `seed.md`) skips this section.
27
27
 
28
- When the brief carries `Pre-rework head:`, use that full SHA as `<base>` for spec
28
+ When the brief carries PR pairs, use the first pair's full `Pre-rework head:` SHA as `<base>` for spec
29
29
  and plan candidate scans instead of the merge-base from "Base"; the PR's earlier
30
30
  spec remains input, not an added candidate, so it does not trigger a multi-spec pick.
31
31
  Keep pairing, untracked candidates, task evidence, and human confirmation below.
@@ -41,7 +41,13 @@ git -C <worktree> ls-files --others --exclude-standard -- <dirs>
41
41
  Candidates are files under those directories added after base, plus untracked files
42
42
  there. Paths returned by these commands are relative to `<worktree>`; resolve them to
43
43
  absolute paths under `<worktree>` before reading artifacts, showing paths in prompts,
44
- or calling `plan_check`. Read every plan-directory candidate's first line. A line exactly `# Ledger - loop engine` pairs by its path-only `**Spec:**` header, not basename; resolve that spec under the worktree's configured spec dirs, including a spec tracked from base. Stop on a missing or foreign spec. A task-plan candidate pairs with
44
+ or calling `plan_check`. Read candidate contents before choosing. For a PR-list brief,
45
+ read the rework candidate's `## Rework record` and the member-carried spec paths listed
46
+ under each member in `## Problem`; exclude exactly those paths from new-rework spec
47
+ candidates before the multi-spec prompt. Keep this explicit exclusion set authoritative through plan/ledger pairing: when a task plan's basename or a ledger's `**Spec:**` header resolves to an excluded member spec, skip that pair in route candidates. Never promote an excluded member spec through its carried plan or ledger. Keep all other plan/spec pairing below, including
48
+ base-tracked specs. Artifact presence never supplies approval. Never choose by filename
49
+ when remaining candidates are genuinely ambiguous; ask the human.
50
+ Read every plan-directory candidate's first line. A line exactly `# Ledger - loop engine` pairs by its path-only `**Spec:**` header, not basename; resolve that spec under the worktree's configured spec dirs, including a spec tracked from base. Stop on a missing or foreign spec. A task-plan candidate pairs with
45
51
  `<worktree>/<specDir>/<same basename>` whether that spec is a candidate or tracked
46
52
  from base; count a base-tracked spec so paired as a spec in the route table below.
47
53
  The plan commit is the first
@@ -2,16 +2,22 @@
2
2
 
3
3
  ## Select home and stop before writes
4
4
 
5
- For a caller passing PR values, follow this reference instead of `using-git-worktrees/SKILL.md` Steps 0 and 1. Derive ROOT from the absolute git common directory as in Step 0, applying its submodule guard. Resolve the worktree home through the existing overrides/native-tool/wrapper contract; default to `$ROOT/.worktrees/<name>`. Set `<name>` to `headRefName` with every `/` replaced by `-`. Never adopt the session's linked tree unless it is the selected home on `headRefName`.
5
+ Consume the ordered array of admitted PR values, home first; use home's `headRefName` for checkout and `PRE_REWORK_HEAD=<home headRefOid>`. Carry the remaining entries as MEMBER_HEADS `{ number, url, title, headRefName, headRefOid }`. For a caller passing PR values, follow this reference instead of `using-git-worktrees/SKILL.md` Steps 0 and 1. Derive ROOT from the absolute git common directory as in Step 0, applying its submodule guard. Resolve the worktree home through the existing overrides/native-tool/wrapper contract; default to `$ROOT/.worktrees/<name>`. Set `<name>` to `headRefName` with every `/` replaced by `-`. Never adopt the session's linked tree unless it is the selected home on `headRefName`.
6
6
 
7
- Run `git -C "$ROOT" fetch origin`, then check these stops in order before creating or modifying any file in the primary or a worktree, or running `git worktree add`.
7
+ Run `git -C "$ROOT" fetch origin`, then check reachability and remote equality for every admitted PR (home and members) before any worktree write: require each original `headRefOid` object and `origin/<headRefName>` equal to it; use the branch-moved stop below naming that PR. Resolve `BASE_SHA=$(git -C "$ROOT" rev-parse origin/<baseRefName>)` as a full SHA before any conflict helper needs fetched-base overrides. Then check these stops in order before creating or modifying any file in the primary or a worktree, or running `git worktree add`.
8
8
 
9
9
  | Observable case | Exact stop |
10
10
  |---|---|
11
11
  | `isCrossRepository` true | `PR #N's head lives on fork <headRepository.nameWithOwner>; rework needs an in-repo branch.` |
12
12
  | `headRefName` listed by `git -C "$ROOT" worktree list --porcelain` at another path, including `.worktrees/pr-<N>` | `branch <headRefName> is checked out at <path>; finish or remove that worktree first.` |
13
13
  | `git -C "$ROOT" rev-parse --verify -q <headRefOid>^{commit}` fails after fetch, or `origin/<headRefName>` differs from `headRefOid` | `PR #N head <headRefOid7> is not reachable (origin/<headRefName> is at <sha7>); the branch moved.` |
14
- | Local branch exists and `git -C "$ROOT" merge-base --is-ancestor <headRefName> origin/<headRefName>` exits 1 | `local branch <headRefName> is at <sha7>, not an ancestor of origin/<headRefName> (<sha7>); rework needs the PR head.` |
14
+ | N=1: local branch exists and `git -C "$ROOT" merge-base --is-ancestor <headRefName> origin/<headRefName>` exits 1 | `local branch <headRefName> is at <sha7>, not an ancestor of origin/<headRefName> (<sha7>); rework needs the PR head.` |
15
+
16
+ ## Recover interrupted member merges
17
+
18
+ For a list with an existing home worktree or local branch, inspect the actual local home branch tip with `git -C "$ROOT" rev-parse refs/heads/<headRefName>`, never caller HEAD. Require home `headRefOid` ancestry, then walk `git -C "$ROOT" log --first-parent --format='%H %P %s' <headRefOid>..<local home tip>`. Require every entry to be a two-parent merge with canonical subject `merge PR #<N> head <sha7> into PR #<home>`, where N is an admitted member, sha7 matches its original head, and its full original `headRefOid` is the second parent. Require a clean existing home tree. On unrelated commits, failed ancestry, or dirt, stop before writes: `worktree <path> on <headRefName> carries commits beyond PR #<home>'s head (<sha7>) that are not member merges from this list; resolve by hand or remove the worktree.` Use the selected home path even when no worktree exists.
19
+
20
+ Continue from the validated tip without reset, rebase, or branch deletion. For recognized merges skip the member and report `Member merge: #<N> <sha7> (already landed)` using the existing merge commit SHA. N=1 retains the exact-head checks below.
15
21
 
16
22
  ## Provision the exact head
17
23
 
@@ -19,13 +25,14 @@ Apply the first matching row after all stops pass. Keep the process cwd unchange
19
25
 
20
26
  | State | Action |
21
27
  |---|---|
22
- | Selected home is already on `headRefName` | Run Step 3's dirty-tree ask first; do not stash or clean automatically. Then run `git -C "$WORKTREE" merge --ff-only origin/<headRefName>` and require HEAD equal to `headRefOid`; otherwise stop naming both SHAs. |
28
+ | List recovery validated an existing home branch | Reuse its clean selected worktree unchanged; with no worktree, run `git -C "$ROOT" worktree add "$WORKTREE" <headRefName>` on that existing branch. Verify `git -C "$WORKTREE" rev-parse HEAD` equals the validated local tip. Never use update-ref, -b, reset, clean, or deletion on recovery. |
29
+ | Selected home is already on `headRefName` (N=1) | Run Step 3's dirty-tree ask first; do not stash or clean automatically. Then run `git -C "$WORKTREE" merge --ff-only origin/<headRefName>` and require HEAD equal to `headRefOid`; otherwise stop naming both SHAs. |
23
30
  | No worktree, local branch exists | Only after ancestry proof, run `git -C "$ROOT" update-ref refs/heads/<headRefName> origin/<headRefName>`, then `git -C "$ROOT" worktree add "<home>" <headRefName>`. Never use `git -C "$ROOT" branch -f`. |
24
31
  | No worktree, no local branch | Run `git -C "$ROOT" worktree add "<home>" <headRefName>`. Git's unique-remote DWIM resolves exactly one matching remote-tracking branch as `--track -b <headRefName> "<home>" origin/<headRefName>`; do not substitute the generic new-branch `-b` creation path. |
25
32
 
26
33
  For fresh creation, apply Step 3's source-clean ask and the existing home safety checks after the ordered stops. Respect the resolved home and native tool/wrapper, but use an overrides `create:` only when it takes `<branch>` as an existing branch and contains no `-b`/`-B`. Otherwise run the built-in checkout above and add `create: override skipped (contains -b)` to the report. Resolve tokens and surface wrapper failures through the existing contract.
27
34
 
28
- Verify `git -C "$WORKTREE" rev-parse HEAD` equals `headRefOid`. On a post-create head race, leave the tree intact and stop naming its path and both SHAs. Capture `PRE_REWORK_HEAD=<headRefOid>` and `BASE_SHA=$(git -C "$ROOT" rev-parse origin/<baseRefName>)` as full SHAs. In the Step 4 report use `Base: <headRefName> @ <sha7>` and add immediately after it:
35
+ Verify `git -C "$WORKTREE" rev-parse HEAD` equals `headRefOid` for fresh creation or N=1, or the validated recovery tip for a list. On a post-create head race, leave the tree intact and stop naming its path and both SHAs. Capture `PRE_REWORK_HEAD=<headRefOid>` and `BASE_SHA=$(git -C "$ROOT" rev-parse origin/<baseRefName>)` as full SHAs. In the Step 4 report use `Base: <headRefName> @ <sha7>` and add immediately after it:
29
36
 
30
37
  ```text
31
38
  PR: <owner/repo#N>
@@ -37,6 +44,16 @@ Base SHA: <sha7>
37
44
 
38
45
  Advance by merge: preserve every commit reachable from PRE_REWORK_HEAD. Never `git rebase`, or use `reset` or `commit --amend` to replace a commit at or below that boundary. Keep the flow's appended-spec approval `commit --amend --no-edit` in `brainstorming/reference/spec-finalization.md` and appended-wave fold `reset --soft "$WAVE_BASE_SHA"` in `subagent-driven-development/SKILL.md` unchanged; require WAVE_BASE_SHA to descend from PRE_REWORK_HEAD.
39
46
 
47
+ ## Merge admitted member heads
48
+
49
+ Before the base merge, iterate remaining MEMBER_HEADS in listed order. Save the last-good home SHA before each merge. Run `git -C "$WORKTREE" merge --no-ff -m "merge PR #<N> head <sha7> into PR #<home>" <headRefOid>` using the original supplied head, not a newly advanced remote tip. On success record `Member merge: #<N> <merge sha7>`; an ancestor no-op records no new commit.
50
+
51
+ For conflicts use the fetched-base scoped-runner selection and helper/completion recipe in "Merge the fetched base": fresh foreground implementer, exact conflicted paths, staging, empty unmerged index, contained status delta, and orchestrator `git -C "$WORKTREE" commit --no-edit`. Preserve the canonical subject, including in a manual resolution. Record any contract exception for the spec's Rework record.
52
+
53
+ On helper or completion failure run `git -C "$WORKTREE" merge --abort` and capture its exit status. Before claiming restoration, require abort exit status 0, `git -C "$WORKTREE" rev-parse HEAD` equal to the saved last-good full SHA, `git -C "$WORKTREE" branch --show-current` equal to home's `headRefName`, and successful `git -C "$WORKTREE" status --porcelain --untracked-files=all` with empty output (checkout and index clean). On any failed check or residual edit, stop naming the failing PR, conflicted files, selected worktree, observed branch and HEAD, abort exit status, remaining status paths or inspection errors, and saved last-good SHA with earlier merged members; require manual recovery before rerunning and do not claim restoration. Never reset or clean to force recovery. Only after all checks pass, stop with `PR #<N> conflicts with home PR #<home> in <files>; the worktree at <path> is left on <headRefName> at <sha7> with #<earlier members> already merged; merge #<N> by hand there (git merge --no-ff <headRefOid>) and rerun the same list, or rerun without #<N>.` Tell the developer to retain the canonical subject for manual merging. Stop before the base merge, spec, plan, later member merges, push, or close. Do not use the base-conflict continuation for member failures.
54
+
55
+ After all members, merge the fetched base. In the Step 4 report print one `PR:` / `Pre-rework head:` pair per admitted PR, home first, followed by member outcome lines in listed order, then the base outcome; print Base SHA once. Carry MEMBER_HEADS to gather.
56
+
40
57
  ## Merge the fetched base
41
58
 
42
59
  Run `git -C "$WORKTREE" merge --no-ff --no-edit origin/<baseRefName>` and record the matching outcome:
@@ -44,10 +61,10 @@ Run `git -C "$WORKTREE" merge --no-ff --no-edit origin/<baseRefName>` and record
44
61
  | Outcome | Record and continuation |
45
62
  |---|---|
46
63
  | Base already an ancestor | `merge: no-op - head contains origin/<base>`; no commit. |
47
- | Clean merge | `merge: <sha7>`; this merge is the first rework commit. |
64
+ | Clean merge | `merge: <sha7>`; this merge follows the member merges (the first rework commit for N=1). |
48
65
  | Conflicts | Follow the helper recipe below. |
49
66
 
50
- Snapshot `git -C "$WORKTREE" status --porcelain` before helper dispatch. Get conflicted paths with `git -C "$WORKTREE" diff --name-only --diff-filter=U`. Dispatch one fresh foreground `implementer`, `cwd` the worktree, `async: false`, with that exact file list, fetched base, and SCOPED_TEST_COMMANDS. Require resolution of every listed file preserving both sides' intent, exactly the supplied scoped commands, and `git -C "$WORKTREE" add` for every resolved file. Require return `done`, or `done - contract exception: <why>` when resolution changes a PR or bound-ticket obligation. Forbid helper commits, aborts, and edits outside the list.
67
+ Keep the process cwd unchanged; explicitly target the selected worktree in every helper discovery, status, staging, completion, commit, and abort command. Snapshot `git -C "$WORKTREE" status --porcelain` before helper dispatch. Get conflicted paths with `git -C "$WORKTREE" diff --name-only --diff-filter=U`. Dispatch one fresh foreground `implementer`, `cwd` the worktree, `async: false`, with that exact file list, fetched base, and SCOPED_TEST_COMMANDS. Require resolution of every listed file preserving both sides' intent, exactly the supplied scoped commands, and `git -C "$WORKTREE" add` for every resolved file. Require return `done`, or `done - contract exception: <why>` when resolution changes a PR or bound-ticket obligation. Forbid helper commits, aborts, and edits outside the list.
51
68
 
52
69
  Read the first-found overrides file from BASE_SHA, not the conflicted tree: `git -C "$ROOT" show <BASE_SHA>:<overrides path>`. Use SCOPED_TEST_COMMANDS only from a documented file-scoped runner under `## PR gate` or `## verification-before-completion` with `requires credentials` not `true`. Missing runner or `requires credentials: true` means `none`, with the reason recorded as `none run (<reason>)`. Never infer file scope from a full-suite script such as `npm test`.
53
70
 
@@ -55,11 +72,13 @@ Continue only with an empty unmerged index (`git -C "$WORKTREE" diff --name-only
55
72
 
56
73
  For any failed helper/completion checks, run `git -C "$WORKTREE" merge --abort`, record `merge: unresolved - <files>`, and continue on the original unmerged head. Open the spec's Design with `merge origin/<base> and resolve <files>` as eventual Task 1. On every outcome, require the base-SHA constraint reads owned by `brainstorming/reference/pr-subject.md`.
57
74
 
58
- Treat this merge, helper edits to the conflict list, and scoped conflict tests as the sole preapproval write/run exception outside spec directories. Run no other brainstorming tests, dispatch no reviewer, and push nothing; leave review of the merge to end-of-flow code review.
75
+ After every base-merge outcome, including clean, no-op, resolved, and aborted unresolved, prove home and every member's original head against final HEAD: require `git -C "$WORKTREE" merge-base --is-ancestor <each original head> HEAD` to exit 0. Stop on any nonzero exit, naming that PR and its original head.
76
+
77
+ Treat the base merge and each admitted member-head merge, their conflict helpers' edits to the conflicted files, and their scoped conflict tests as the sole preapproval write/run exception outside spec directories. Run no other brainstorming tests, dispatch no reviewer, and push nothing; leave review of the merge to end-of-flow code review.
59
78
 
60
79
  ## Fetch unresolved review threads
61
80
 
62
- Run this query; callers cite this owner rather than copying it:
81
+ Run this query per admitted PR, N=1 included, before gather; callers cite this owner rather than copying it:
63
82
 
64
83
  ```
65
84
  gh api graphql --paginate -f query='query($o:String!,$r:String!,$n:Int!,$endCursor:String){repository(owner:$o,name:$r){pullRequest(number:$n){reviewThreads(first:100,after:$endCursor){pageInfo{hasNextPage endCursor} nodes{id isResolved isOutdated path line originalLine comments(first:100){pageInfo{hasNextPage endCursor} totalCount nodes{url body author{login} createdAt}}}}}}}' -F o=<owner> -F r=<repo> -F n=<N>
@@ -71,4 +90,4 @@ Merge every reviewThreads page and filter to `isResolved == false`. For each thr
71
90
  gh api graphql --paginate -f query='query($id:ID!,$endCursor:String){node(id:$id){... on PullRequestReviewThread{comments(first:100,after:$endCursor){pageInfo{hasNextPage endCursor} nodes{url body author{login} createdAt}}}}}' -F id=<thread id>
72
91
  ```
73
92
 
74
- Start that thread's follow-up query at its first comment page, then replace its initial comments with the complete paginated connection to avoid duplicates. Keep each thread's comment cursor separate from the reviewThreads cursor and fetch until its last page. Carry all comments in order: read the first request and every reply because replies can change the request. Render location as `<path>:<line>`, else `<path>:<originalLine> (outdated)`, else `<path>`. On fetch failure carry `threads: not fetched (<reason>)` to the gather draft and `Threads: not fetched (<reason>)` to the record; continue. On successful empty fetch record `Threads: none`.
93
+ Start that thread's follow-up query at its first comment page, then replace its initial comments with the complete paginated connection to avoid duplicates. Keep each thread's comment cursor separate from the reviewThreads cursor and fetch until its last page. Carry all comments in order: read the first request and every reply because replies can change the request. Render location as `<path>:<line>`, else `<path>:<originalLine> (outdated)`, else `<path>`. On any thread or comment fetch failure stop before gather and spec with `threads for #<N> not fetched (<gh error>)`. Finish owns its distinct post-verification fetch-failure reporting. On successful empty fetch record `Threads: none`.