pi-gauntlet 7.3.2 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v7.4.0 - 2026-10-06
4
+
5
+ ### Added
6
+
7
+ - **gatekeep-pr `--rebase [base]`.** An opt-in step 2b rebases the PR head onto the fetched base before verification, hands each rebase stop to a fresh implementer, has a fresh reviewer gate every rewritten head, lease-pushes with `--force-with-lease=<head_ref>:<pre_head>`, and verifies the pushed head. Another author's branch asks before the rewrite; a non-pushable head, a missing base, a harness without helpers, or a moved remote head stops the run; without the flag nothing changes. New `eval/gatekeep-pr/` target with seven samples and two recorded runs. (#58)
8
+
3
9
  ## v7.3.2 - 2026-10-06
4
10
 
5
11
  ### Changed
package/README.md CHANGED
@@ -69,7 +69,7 @@ Everything between gate 1 and gate 2 - task breakdown, implementation, both revi
69
69
 
70
70
  pi-gauntlet ships three kinds of pieces, layered on top of pi-cohort's dispatch:
71
71
 
72
- - **20 skills** - the workflow logic. Twelve activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `forge-skill`, `linear` (reads/searches/comments on/manages Linear tickets via the `linearis` CLI; owns all linearis mechanics and the `## Issue tracker` overrides schema; tracker-facing skills route to it). Eight more require explicit entry (`disable-model-invocation: true`): `brainstorming` starts the gated workflow on explicit consent - use `/skill:brainstorming` for reliable discovery, or opt in through reachable explicit prose or a human-selected handoff; after spec approval, downstream planning and implementation still chain automatically. `shape-ticket` creates or repairs one tracker issue per run against a Context/Problem/Idea/Acceptance-Criteria template, gated by an AC integrity check, a cheap council roast, and a single human-confirmed write - run it with `/skill:shape-ticket`. `gatekeep-pr` is consent-gated pre-merge verification of a PR against its issue's acceptance criteria - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; the project's verification command runs only as fallback), a rubric-based review, a compact bottom-up report (what the PR delivers, ticket changes, PR comments, nits, blockers, then a `mergeable` / `fixable` verdict) and a verb menu whose rows derive from the actor's permissions on the PR; nothing mutates (fixes, pushes, reviews, merges, tracker comments) until you pick a row - run it with `/skill:gatekeep-pr <pr>`. `check-delivery` is a post-merge detective control: proves an issue actually shipped (default-branch landing, delivery target, per-AC evidence) before its tracker status advances; it never writes a terminal status - run it with `/skill:check-delivery <ref>`. `chase-bug` is human-only bug triage: read-only root-cause discovery to an evidenced verdict menu (real bug -> ticket/brainstorm/hotfix/respond; five negative verdicts), then a gated response to the reporter for addressable origins (GitHub issue / tracker ticket) and a rendered verdict summary otherwise - it never fixes during triage; the hotfix row hands off to `skills/chase-bug/hotfix.md` after the menu - run it with `/skill:chase-bug`. `gauntlet-performance` reads the committed run telemetry (current repo by default, `--dir <path>` adds others) through the parse-only `gauntlet-performance` CLI and answers with one example-led recommendation, 3-5 cornerstone numbers, a council assessment when available, and a menu of at most three actions (render a report file, open the recommendation as a ticket via `shape-ticket`, drill into one run); it writes nothing unless you pick render - run it with `/skill:gauntlet-performance [--dir <path>]... [--since <version>]`. `gauntlet-handoff` ends a session whose flow a fresh session will continue: it invokes pi-cohort's `handoff` skill (`--out <path>` or `--key <name>`, default key = the run worktree's branch with `/` flattened to `-`, default file `<tmpdir>/pi-handoff/<key>.md`), then appends the gauntlet process-state section (phase/plan tracker status) per the shared contract `skills/gauntlet-resume/reference/brief-contract.md` - run it with `/skill:gauntlet-handoff [--out <path> | --key <name>]`. `gauntlet-resume` is the only way back into an interrupted flow from a fresh session: it takes a `gauntlet-handoff` brief (a file path, pasted text, or - with no arguments - a pick from the briefs under `<tmpdir>/pi-handoff/`), a bare worktree that already holds a spec, or a spec tracked on main under a configured spec dir (a spec seed: it creates or reuses the worktree through `using-git-worktrees` and pins the spec), restores phase/plan tracker state through the legal arming sequence (`start brainstorm`, `skip` with `resume:` reasons, `plan_check` before implement-or-later), and never infers approval from artifacts - run it with `/skill:gauntlet-resume [<brief-file> | <spec>.md | <worktree-name-or-path>]`.
72
+ - **20 skills** - the workflow logic. Twelve activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `forge-skill`, `linear` (reads/searches/comments on/manages Linear tickets via the `linearis` CLI; owns all linearis mechanics and the `## Issue tracker` overrides schema; tracker-facing skills route to it). Eight more require explicit entry (`disable-model-invocation: true`): `brainstorming` starts the gated workflow on explicit consent - use `/skill:brainstorming` for reliable discovery, or opt in through reachable explicit prose or a human-selected handoff; after spec approval, downstream planning and implementation still chain automatically. `shape-ticket` creates or repairs one tracker issue per run against a Context/Problem/Idea/Acceptance-Criteria template, gated by an AC integrity check, a cheap council roast, and a single human-confirmed write - run it with `/skill:shape-ticket`. `gatekeep-pr` is consent-gated pre-merge verification of a PR against its issue's acceptance criteria - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; the project's verification command runs only as fallback), a rubric-based review, a compact bottom-up report (what the PR delivers, ticket changes, PR comments, nits, blockers, then a `mergeable` / `fixable` verdict) and a verb menu whose rows derive from the actor's permissions on the PR; nothing mutates (fixes, pushes, reviews, merges, tracker comments) until you pick a row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification - run it with `/skill:gatekeep-pr <pr> [--rebase [base]]`. `check-delivery` is a post-merge detective control: proves an issue actually shipped (default-branch landing, delivery target, per-AC evidence) before its tracker status advances; it never writes a terminal status - run it with `/skill:check-delivery <ref>`. `chase-bug` is human-only bug triage: read-only root-cause discovery to an evidenced verdict menu (real bug -> ticket/brainstorm/hotfix/respond; five negative verdicts), then a gated response to the reporter for addressable origins (GitHub issue / tracker ticket) and a rendered verdict summary otherwise - it never fixes during triage; the hotfix row hands off to `skills/chase-bug/hotfix.md` after the menu - run it with `/skill:chase-bug`. `gauntlet-performance` reads the committed run telemetry (current repo by default, `--dir <path>` adds others) through the parse-only `gauntlet-performance` CLI and answers with one example-led recommendation, 3-5 cornerstone numbers, a council assessment when available, and a menu of at most three actions (render a report file, open the recommendation as a ticket via `shape-ticket`, drill into one run); it writes nothing unless you pick render - run it with `/skill:gauntlet-performance [--dir <path>]... [--since <version>]`. `gauntlet-handoff` ends a session whose flow a fresh session will continue: it invokes pi-cohort's `handoff` skill (`--out <path>` or `--key <name>`, default key = the run worktree's branch with `/` flattened to `-`, default file `<tmpdir>/pi-handoff/<key>.md`), then appends the gauntlet process-state section (phase/plan tracker status) per the shared contract `skills/gauntlet-resume/reference/brief-contract.md` - run it with `/skill:gauntlet-handoff [--out <path> | --key <name>]`. `gauntlet-resume` is the only way back into an interrupted flow from a fresh session: it takes a `gauntlet-handoff` brief (a file path, pasted text, or - with no arguments - a pick from the briefs under `<tmpdir>/pi-handoff/`), a bare worktree that already holds a spec, or a spec tracked on main under a configured spec dir (a spec seed: it creates or reuses the worktree through `using-git-worktrees` and pins the spec), restores phase/plan tracker state through the legal arming sequence (`start brainstorm`, `skip` with `resume:` reasons, `plan_check` before implement-or-later), and never infers approval from artifacts - run it with `/skill:gauntlet-resume [<brief-file> | <spec>.md | <worktree-name-or-path>]`.
73
73
  - **7 subagent personas** - the specialized child agents the skills dispatch via pi-cohort: `implementer`, `code-reviewer`, `spec-reviewer`, `conformance-reviewer`, `spec-summarizer`, `spec-council-member`, `spec-council-synthesizer`. See [doc/personas.md](./doc/personas.md) for what each one does and why its permissions are scoped the way they are.
74
74
  - **4 runtime extensions** - the enforcement layer. `plan-tracker` and `phase-tracker` are tools skills call to track progress (with a TUI widget); `verify-before-ship` is a hook that warns if you push or open a PR without a passing test run since your last edit; a phase-tracker flow guard reminds on implement-phase commits missing spec/code review. In a brainstorming-entered flow, phase-tracker rejects `implement` or `verify` completion while tracker tasks remain pending or in progress; see [its configuration reference](./doc/configuration.md#phase-tracker). phase-tracker also registers `plan_check`, which verifies a plan against its spec and against the grammar in [skills/writing-plans/reference/plan-contract.md](./skills/writing-plans/reference/plan-contract.md), including that each task's `Tests:` commands are selective and never the full suite; a pass stamps the plan for implementation. `telemetry` records one YAML record per gauntlet run (phase timing, models, personas, gate/fix rounds, diff at ship) that ships in the squash beside the spec - it is written only inside a brainstorming-entered flow, kept out of history until `finishing-a-development-branch` seals it once with `gauntlet-telemetry-seal` (a `telemetry:` commit under git; the working-copy snapshot in a plain jj workspace) - and blocks a brainstorm `write` into an already-shipped spec; see [its configuration reference](./doc/configuration.md#telemetry). See [doc/configuration.md](./doc/configuration.md) for the settings each one reads.
75
75
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "7.3.2",
3
+ "version": "7.4.0",
4
4
  "description": "Opinionated, gated workflow skills, subagent personas, and runtime extensions for the pi coding agent.",
5
5
  "author": "Jacek Juraszek",
6
6
  "type": "module",
@@ -1,18 +1,19 @@
1
1
  ---
2
2
  name: gatekeep-pr
3
- description: Use when gating a pull request before it merges - whether you authored it or are reviewing someone else's work. Judges whether the diff delivers the mechanism each acceptance criterion needs (code, tests, docs) and conforms to the review rubric; what can be seen only after merge belongs to /skill:check-delivery. Consent-gated - every mutation (fixes, pushes, reviews, merges, tracker comments) waits for an explicit menu pick.
3
+ description: Use when gating a pull request before it merges - whether you authored it or are reviewing someone else's work. Judges whether the diff delivers the mechanism each acceptance criterion needs (code, tests, docs) and conforms to the review rubric; what can be seen only after merge belongs to /skill:check-delivery. Consent-gated - every mutation (fixes, pushes, reviews, merges, tracker comments) waits for an explicit menu pick, except the sync `--rebase` asks for.
4
4
  disable-model-invocation: true
5
- argument-hint: "<pr> [issue-ref] (e.g. 123, or 123 gh-45)"
5
+ argument-hint: "<pr> [issue-ref] [--rebase [base]] (e.g. 123, 123 gh-45, 123 --rebase, 123 gh-45 --rebase release/2)"
6
6
  ---
7
7
 
8
8
  # gatekeep-pr
9
9
 
10
- Verify, don't trust: a PR description is a claim, and over-claimed coverage, hallucinated references, and "tests pass" that never ran are the normal case on generated code. Judge the mechanism only - whether code, tests, and docs at the assessed head make each promised behavior possible - and leave what can be seen only after merge (a staging run, production data, an external system) to `/skill:check-delivery`. Nothing mutates before a menu pick: steps 1-5 read, provision a worktree, and draft; fixes, pushes, reviews, comments, merges, and tracker writes happen only on explicit selection. Running the verification command executes PR code with your ambient credentials and no sandbox - gate only PRs you are willing to execute.
10
+ Verify, don't trust: a PR description is a claim, and over-claimed coverage, hallucinated references, and "tests pass" that never ran are the normal case on generated code. Judge the mechanism only - whether code, tests, and docs at the assessed head make each promised behavior possible - and leave what can be seen only after merge (a staging run, production data, an external system) to `/skill:check-delivery`. Nothing mutates before a menu pick except the `--rebase` sync: steps 1-5 read, provision a worktree, run the `--rebase` sync when asked, and draft; fixes, pushes, reviews, comments, merges, and tracker writes happen only on explicit selection or, for the sync's rebase and lease push, on `--rebase`. Running the verification command executes PR code with your ambient credentials and no sandbox - gate only PRs you are willing to execute.
11
11
 
12
12
  ## Arguments
13
13
 
14
14
  - PR number or URL; omitted -> resolved from the current branch per `verification-brief.md` Section A.
15
15
  - Optional issue reference; omitted -> resolved in step 1 (`verification-brief.md` Section A) and fetched in step 2; none found -> judge the PR's stated intent only, never invent acceptance criteria.
16
+ - Optional `--rebase [base]`, parsed after `<pr> [issue-ref]`: the token after it, when present, is the base branch name (`123 --rebase gh-45` means base `gh-45`, not an issue ref); absent, the base is the digest's `baseRefName`. Step 2b fetches `origin/<base>` and validates it: a missing ref stops with a one-line error before any rebase or push. Without `--rebase` the run performs no additional fetch, rebase, or push before a menu pick and renders no sync line.
16
17
 
17
18
  ## Steps
18
19
 
@@ -22,21 +23,22 @@ Verify (step 3) and Review (step 4) each run as a fresh helper with `cwd` the PR
22
23
  |---|---|---|---|
23
24
  | 1 Gather | `verification-brief.md` Section A | PR digest: metadata, diff, checks, comments, worktree discovery | `gather` |
24
25
  | 2 Provision + configure | `reference/assessment.md` | provisioned worktree; config resolved from the merge-base; ticket fetched; AC rows extracted per `../brainstorming/reference/ticket-acceptance.md` | `provision` |
26
+ | 2b Sync (only with `--rebase`) | `reference/sync.md` | the PR worktree on the rebased, pushed head with a refreshed digest, or a `sync` record and the unsynced head | `sync` |
25
27
  | 3 Verify | `verification-brief.md` Section B | evidence record (CI-first) and one disposition per material claim | `verify` |
26
28
  | 4 Review | `verification-brief.md` Section C | reviewer findings and the internal comment ledger | `review` |
27
29
  | 5 Integrate + report | `reference/findings.md`, then `reference/report.md` | AC outcomes, blockers, nits, drafted payloads, the rendered report | `report` |
28
30
  | 6 Menu | `reference/decision-menu.md` | the menu under the verdict line, exactly one `[recommended]` | `menu` |
29
31
  | 7 Loop | `reference/post-selection-loop.md` (a `fix` pick continues in `reference/fix-wave.md`) | the executed pick; re-entry until `merge` or `stop` | (re-opens the re-entered stage) |
30
32
 
31
- Re-entry: a fix wave's own push re-enters step 4 through the own-push sequence (`reference/fix-wave.md` `## Evidence after push`, then `reference/post-selection-loop.md` `### Re-render`); any other head move re-enters step 3; an unchanged head re-enters step 5.
33
+ Re-entry: a fix wave's own push re-enters step 4 through the own-push sequence (`reference/fix-wave.md` `## Evidence after push`, then `reference/post-selection-loop.md` `### Re-render`); any other head move re-enters step 3; an unchanged head re-enters step 5. A loop re-entry never re-runs step 2b; the sync runs once per invocation.
32
34
 
33
35
  ## Progress tracking
34
36
 
35
- Use `plan_tracker`, never `phase_tracker`. `init` with the first three stages of the table. In step 3 `add` one task per material claim and record each verdict: `complete` matched, `failed` contradicted, `skipped` unverifiable-pre-merge. Once every claim is terminal and `verify` is closed, `add` the remaining three stages - the tracker rejects a verdict recorded behind a still-pending stage. A failed stage or claim stays `failed` while the skill stops at the menu. Without a `plan_tracker` tool, keep a plain checklist; behavior is unchanged.
37
+ Use `plan_tracker`, never `phase_tracker`. `init` with the first three stages of the table, four when `--rebase` is present (`gather, provision, sync, verify, ...`); `reference/sync.md` closes `sync` before step 3 starts - `complete` after a push or a `no-op`, `skipped` when step 3 runs unsynced, `failed` on a terminal stop. In step 3 `add` one task per material claim and record each verdict: `complete` matched, `failed` contradicted, `skipped` unverifiable-pre-merge. Once every claim is terminal and `verify` is closed, `add` the remaining three stages - the tracker rejects a verdict recorded behind a still-pending stage. A failed stage or claim stays `failed` while the skill stops at the menu. Without a `plan_tracker` tool, keep a plain checklist; behavior is unchanged.
36
38
 
37
39
  ## Harness notes
38
40
 
39
- Every helper is a fresh context with `cwd` the PR worktree; its task names its output path and ends with the output contract. Every dispatch on either harness gets its own output path from `mktemp "${TMPDIR:-/tmp}/gatekeep-<role>.XXXXXX"` outside the worktree. On pi: `subagent({ agent: <persona>, context: "fresh", async: false, cwd: <worktree> })` with `worker` for Verify, `code-reviewer` for Review and the pre-push review, `implementer` for payloads; `gauntlet_setting({ key: "closureReview" })` supplies the fix-round cap. On Claude Code: the harness's subagent dispatch tool with the duty text as the prompt - Verify: `verification-brief.md` Section B; Review, pre-push review, and comment-delta review: Section C plus `review-baseline.md` (the pre-push review adds the closure-line contract); implementer: the payload task of `reference/fix-wave.md` `## Wave`; the cap is 3. A Claude Code helper inherits the session's directory, so its prompt opens with the absolute worktree path and the rule that every command runs as `git -C <worktree> ...` or `(cd <worktree> && ...)`, checks that `git -C <worktree> rev-parse HEAD` equals the head its task names (the assessed head for Verify, Review, and the comment-delta reviewer; the current wave HEAD for wave helpers) before working, and names the output path; the orchestrator awaits the tool result before reading the file. A harness with neither facility is unsupported: stop at step 3 and say so.
41
+ Every helper is a fresh context with `cwd` the PR worktree; its task names its output path and ends with the output contract. Every dispatch on either harness gets its own output path from `mktemp "${TMPDIR:-/tmp}/gatekeep-<role>.XXXXXX"` outside the worktree. On pi: `subagent({ agent: <persona>, context: "fresh", async: false, cwd: <worktree> })` with `worker` for Verify, `code-reviewer` for Review and the pre-push review, `implementer` for payloads; `gauntlet_setting({ key: "closureReview" })` supplies the fix-round cap. On Claude Code: the harness's subagent dispatch tool with the duty text as the prompt - Verify: `verification-brief.md` Section B; Review, pre-push review, and comment-delta review: Section C plus `review-baseline.md` (the pre-push review adds the closure-line contract); implementer: the payload task of `reference/fix-wave.md` `## Wave`; sync conflict stop: the task `reference/sync.md` `## Conflict stop` builds; sync review: Section C plus `review-baseline.md` with the `## Review before push` subject, no closure lines; the cap is 3. A Claude Code helper inherits the session's directory, so its prompt opens with the absolute worktree path and the rule that every command runs as `git -C <worktree> ...` or `(cd <worktree> && ...)`, checks that `git -C <worktree> rev-parse HEAD` equals the head its task names (the assessed head for Verify, Review, and the comment-delta reviewer; the current wave HEAD for wave helpers) before working, and names the output path; the orchestrator awaits the tool result before reading the file. A harness with neither facility is unsupported: stop at step 3 and say so - at step 2b, before any sync write, when `--rebase` was passed (`reference/sync.md`).
40
42
 
41
43
  ## Wording rules
42
44
 
@@ -55,14 +57,14 @@ These bind every rendered report, menu, and external payload (review bodies, rep
55
57
 
56
58
  ## Red flags - STOP
57
59
 
58
- - Any mutation (fix, push, review, comment, merge, tracker write) without an explicit menu pick - owner: the intro.
60
+ - Any mutation (fix, push, review, comment, merge, tracker write) without an explicit menu pick or the `--rebase` argument - owner: the intro.
59
61
  - A code or doc edit sitting in the worktree when a menu renders - except helper residue that `reference/fix-wave.md` `## Wave` names in the re-rendered menu - owner: `reference/findings.md` `## Drafted payloads`.
60
62
  - Blocking on an AC's observation half, or on a PR-body claim the gate cannot check - owner: `reference/findings.md` `## AC outcomes`.
61
63
  - `impossible` without all four conditions and a source read this run - owner: `reference/findings.md` `## AC outcomes`.
62
64
  - Deferring behavior the ticket promises to a later PR: it is a blocker or an explicit tracker split, nothing else - owner: `reference/findings.md` `## Namespaces`.
63
65
  - Reading the rubric, the verification command, or any ladder source from the PR's head instead of the base branch's merge-base - owner: `reference/assessment.md` `## Configuration`.
64
66
  - Raw command output, drafted payloads, or internal IDs in the rendered report - owner: `reference/report.md`.
65
- - An open-PR menu, other than the conflict menu, without `fix` while a helper facility exists and the fix-round cap is neither `0` nor reached (a merged or closed PR offers `show evidence` and `stop` only); any menu without `show evidence`, with two `[recommended]`, or with `stop` not last - owner: `reference/decision-menu.md`.
67
+ - An open-PR menu, other than the conflict menu or a `--rebase` terminal stop (`stop` and `show evidence` only, `reference/sync.md`), without `fix` while a helper facility exists and the fix-round cap is neither `0` nor reached (a merged or closed PR offers `show evidence` and `stop` only); any menu without `show evidence`, with two `[recommended]`, or with `stop` not last - owner: `reference/decision-menu.md`.
66
68
  - A local verification run while CI is pending, or a second push inside one round - owner: `reference/fix-wave.md` `## Evidence after push`.
67
69
  - The orchestrator resolving a merge conflict itself, or editing or committing a tracked file during a fix wave - owner: `reference/fix-wave.md` `## Conflicts` and `## Wave`.
68
70
  - A helper's duty run in the orchestrator's own context - owner: `## Harness notes`.
@@ -1,6 +1,6 @@
1
1
  # gatekeep-pr: step 2 - provision and configure
2
2
 
3
- Read from SKILL.md step 2. Input: the step-1 digest. Output: a provisioned worktree, the resolved configuration, and the ticket's AC rows. This step is the orchestrator's only mutation before the menu.
3
+ Read from SKILL.md step 2. Input: the step-1 digest. Output: a provisioned worktree, the resolved configuration, and the ticket's AC rows. This step is the orchestrator's only mutation before the menu, besides the `--rebase` sync of step 2b (`sync.md`).
4
4
 
5
5
  ## Provision the worktree
6
6
 
@@ -29,11 +29,15 @@ Nits:
29
29
  Blockers: (numbered; menu picks reference these numbers)
30
30
  1. <1-2 sentences: what is wrong, why it matters>. (<file:line> | <check name> | <doc path>)
31
31
 
32
+ Sync: <one whole sentence from the digest's sync field; only when --rebase was passed>
33
+
32
34
  Verdict: mergeable - <evidence clause> | fixable - <N> blockers | fixable - <withhold reason>
33
35
 
34
36
  <menu>
35
37
  ```
36
38
 
39
+ The `Sync:` line renders only when `--rebase` was passed, as one whole sentence built from the digest's `sync` field (for example `Sync: rebased a1b2c3d..e4f5a6b onto origin/main, one stop resolved in src/parser.ts and src/parser.test.ts.`). SKILL.md wording rule 5 (passing facts only under `show evidence`) does not apply to it: it names the resolved files even when the sync succeeded.
40
+
37
41
  ## Verdict
38
42
 
39
43
  Two states: `mergeable` or `fixable`. `mergeable` carries the evidence clause from `findings.md` `## Provenance`: `CI green on the assessed head (<check name>)` or `verification command passed locally`. `fixable - <N> blockers` when `Blockers` is non-empty; `fixable - <withhold reason>` when it is empty but a merge prerequisite is unmet: `ticket change pending on AC<n>` (for example `ticket change pending on AC4`), `reviewer run in progress`, `required check pending`, `failed gate` (listed for spec parity; a red gate mints a blocker per `findings.md` `## Namespaces`, so `Blockers` is non-empty), `comments not refreshed`, `comment source review incomplete`, `verification not run` (the command needed credentials or no command resolved, with nothing else blocking), `mergeable unknown`, `behind base`, `merge conflict` (for `mergeable == CONFLICTING`), `verification evidence pending` (the Pending row: >=1 check pending, none blocking).
@@ -0,0 +1,61 @@
1
+ # gatekeep-pr: step 2b - sync (`--rebase [base]`)
2
+
3
+ Read from SKILL.md step 2b, only when `--rebase` was passed. Input: the step-1 digest, the provisioned worktree, and the base name (`<base>` = the argument's value, else `baseRefName`; `--rebase release-1.x` rebases onto `origin/release-1.x`). Output: the worktree on the rebased, pushed head with a refreshed digest, or a `sync` record and the unsynced head. The sync runs once per invocation; a loop re-entry never returns here. This file is self-contained: `fix-wave.md` is the post-menu contract and is not loaded here; `assessment.md` owns step 2.
4
+
5
+ ## Permitted orchestrator git writes
6
+
7
+ `git fetch origin <base>`, `git rebase origin/<base>`, `GIT_EDITOR=true git rebase --continue`, `git rebase --abort` (only while a rebase this step started is in progress), `git reset --hard <pre_head>` (only after that rebase completed, to restore the recorded SHA), and the lease push below. No `add`, no `commit`, no `rebase --skip`, no edit to a tracked file, and no `--continue` while `git diff --name-only --diff-filter=U` is non-empty. Every command runs as `git -C <worktree> ...`.
8
+
9
+ ## Preconditions
10
+
11
+ Check in order after step 2. A terminal stop - here, at `## Baseline`, or at `## Push` - closes the `sync` stage `failed`, prints the reason, offers `stop` and `show evidence` only, and runs no step 3; on such a stop `show evidence` prints the digest's head-dependent fields and the `sync` record.
12
+
13
+ 1. The harness has a helper facility (SKILL.md `## Harness notes`); none -> terminal stop before any write: `sync: stopped - no helper facility for conflict resolution and review`.
14
+ 2. `permissions.head_pushable` is `true`; `false` or `unreadable` -> terminal stop naming the reason; no rebase, no push.
15
+ 3. `origin/<base>` exists after `git fetch origin <base>`; missing -> terminal stop with one line naming the missing ref.
16
+ 4. `git merge-base --is-ancestor origin/<base> HEAD` exits 1 (the head is behind); 0 -> `sync: no-op - head already contains origin/<base>`, stage `complete`, continue to step 3 with no push and no review; any other exit -> terminal stop quoting it.
17
+ 5. `git rev-list --merges origin/<base>..HEAD` prints nothing; a head holding merge commits is not rewritten -> `sync: skipped - head contains merge commits`, stage `skipped`, continue to step 3 unsynced.
18
+
19
+ The fetch in precondition 3 is the only git write that may precede the confirmation below.
20
+
21
+ A reused worktree holding local-only commits or a foreign in-progress operation never reaches this step: step 2's provisioning stop (`assessment.md`) fires first.
22
+
23
+ ## Baseline
24
+
25
+ Refresh `headRefOid` with `gh pr view <N> --json headRefOid` and set `<pre_head>` = `git -C <worktree> rev-parse HEAD`. The two must agree; a mismatch is a terminal stop (stage `failed`) naming both SHAs (the remote moved during provisioning).
26
+
27
+ ## Other-author confirmation
28
+
29
+ When `pr.author.login` differs from `gh api user --jq .login`, or that login is `unreadable`, print one explanation before any rewrite: the rebase replays the author's commits under new SHAs (authorship stays; the committer becomes this checkout's local git identity), review comments anchored on the old commits may render as outdated on the forge, and branch protection may dismiss existing approvals. Then ask for a yes. Any other reply -> `sync: declined - other author's branch`, stage `skipped`, step 3 runs unsynced. A yes covers the whole sync (rebase, helper commits, review, lease push, and any restore). Same-author PRs run on `--rebase` alone.
30
+
31
+ ## Rebase
32
+
33
+ `git -C <worktree> rebase origin/<base>`. A non-zero exit with no unmerged paths (`git diff --name-only --diff-filter=U` empty) takes `## Failure path`. A non-zero exit with unmerged paths is a conflict stop.
34
+
35
+ ## Conflict stop
36
+
37
+ One fresh `implementer` per stop, sequential, `cwd` the PR worktree, dispatched per SKILL.md `## Harness notes` (pi: the `implementer` persona; Claude Code: the sync conflict-stop duty). Before dispatch, record the stop's `git status --porcelain` (the clean replays git already staged belong to it) and the conflicted file list. Resolve `SCOPED_TEST_COMMANDS` yourself: the resolved `verification command` from the configuration ladder (`assessment.md` `## Configuration`) followed by the paths of the test files among the stopped commit's changed files when the ladder documents a file-scoped form, otherwise `none`. The task carries the conflicted file list, the stopped commit (`git rebase --show-current-patch`), the base being rebased onto, `SCOPED_TEST_COMMANDS`, and this rule verbatim: "resolve every listed file so both sides' intent survives, run exactly the supplied scoped test commands and report each result (or report that none were supplied), `git add` each resolved file, return `done` with the list of files you changed; never run `git rebase --continue`, `git rebase --abort`, `git rebase --skip`, or `git commit`; never edit a file outside the list".
38
+
39
+ Continue (`GIT_EDITOR=true git rebase --continue`) only when the helper returned `done` with the scoped results, `--diff-filter=U` is empty, every listed file is fully staged, and `git status --porcelain` differs from the recorded snapshot only in the listed files; a non-zero `--continue` with unmerged paths is the next conflict stop; without unmerged paths it takes `## Failure path`. Anything else (`open`, residue outside the listed files, unmerged paths left) takes `## Failure path`. One attempt per stop, no round counter; the fix-round cap (`closureReview.maxFixRounds`) is untouched. Resolved file names accumulate across stops.
40
+
41
+ ## Review before push
42
+
43
+ Runs on every rewritten head, with or without a stop; only a `no-op` skips it. One fresh `code-reviewer`, `cwd` the PR worktree, read-only, dispatched per SKILL.md `## Harness notes` (Claude Code: the sync review duty), over `git range-diff origin/<base> <pre_head> HEAD` and, when stops were resolved, `git diff <pre_head> HEAD -- <resolved files>`; the task asks for the reviewer's native findings report with "rebase replay and conflict resolutions" as the review subject and no closure lines. A Critical or Moderate finding takes `## Failure path`; no second helper.
44
+
45
+ ## Push
46
+
47
+ `git -C <worktree> push --no-follow-tags --force-with-lease=<head_ref>:<pre_head> <head_url> HEAD:refs/heads/<head_ref>`. The destination is the digest's `head_url` and `head_ref` (the same destination `fix-wave.md` pushes to), never `origin`.
48
+
49
+ A refusal whose message names the lease (`stale info`, the remote head moved since `## Baseline`): restore `<pre_head>` with `git reset --hard <pre_head>`, assert `HEAD == <pre_head>` and a clean `git status --porcelain`, then terminal stop (stage `failed`, menu `stop` and `show evidence` only) with the reason and both SHAs. No retry. Any other rejection (server policy, branch protection) takes `## Failure path` with `push rejected: <first server line>`.
50
+
51
+ ## Re-gather
52
+
53
+ Re-run every step-1 forge read for the PR (`../verification-brief.md` Section A fixed command set): `headRefOid`, `mergeable`, `mergeStateStatus`, `reviewDecision`, status checks and `actions_runs`, commits, changed files and diff, comments and reviews; the step-1 ticket reads are not repeated. Unreadable fields fail closed as in step 1. Re-resolve the configuration ladder (`assessment.md` `## Configuration`) at `git merge-base origin/<baseRefName> HEAD` - `baseRefName` stays the trust source even when `<base>` names another branch. Run no CI poll here: step 3's first pass is the single poll window (every 30 s for up to `timeout minutes`; a check still pending at the limit renders the `wait` overlay, never green, and a head with no check within that poll takes the `Fallback local run` row) and step 3 resolves the pushed head as any assessed head - the sync push is not an own push for `### Re-render` and the `Fix wave` row of Section B does not apply; a head that moves during that poll takes the existing head-move rule (`post-selection-loop.md` `### Re-render`), with step 2's divergence stop as the backstop. Red CI on the pushed head triggers no automatic fix: step 3 records the failing checks and the menu offers `fix` - `fix [recommended]` only when `decision-menu.md` and `../verification-brief.md` Section B make it `[recommended]`. Set the digest's `sync` field (`../verification-brief.md` Section A), close the `sync` stage `complete`, and enter step 3 with the refreshed digest.
54
+
55
+ ## Failure path
56
+
57
+ Triggers: an unresolved stop, `open`, residue, a non-zero `rebase` or `--continue` without unmerged paths, a reviewer Critical or Moderate finding, a push rejected by server policy. Restore `<pre_head>`: `git rebase --abort` while the rebase is in progress, `git reset --hard <pre_head>` after it completed; assert `HEAD == <pre_head>` and a clean `git status --porcelain` - a mismatch is a hard stop with the error, never a silent continue. Record `sync: failed - <reason>` with reason one of `unresolved stop in <commit7>`, `implementer returned open`, `residue outside conflicted files`, `rebase failed: <first line>`, `reviewer: <top finding>`, `push rejected: <first server line>`. Close the `sync` stage `skipped` and continue to step 3 on the unsynced head; step 5's conflict rows (`update branch`, `merge base`, `reply`, `stop`) carry what remains.
58
+
59
+ ## Sync record
60
+
61
+ The digest's `sync` field is one of: `rebased <pre_head7>..<new_head7> onto origin/<base>, <n> stop(s) resolved in <files>, <m> commit(s) dropped as already applied`, `no-op - ...` (short form `sync: no-op`), `skipped - ...` (`sync: skipped`), `declined - ...` (`sync: declined`), `failed - ...` (for example `sync: failed - reviewer: ...` or `sync: failed - push rejected: <first server line>`), `stopped - ...`. `report.md` renders it as the `Sync:` line immediately before `Verdict:`. `<m>` is `git rev-list --count origin/<base>..<pre_head>` minus `git rev-list --count origin/<base>..HEAD`; omit the stops clause when no stop occurred.
@@ -70,6 +70,7 @@ provisioning, so it never re-polls; step 2 re-polls once after provisioning (`re
70
70
  - issue_ref: <ref> | null
71
71
  - issue: null # shape and fill: step 2, reference/assessment.md ## Fetch the ticket
72
72
  - worktree_discovery: { expected_path, exists, branch, dirty, ahead, behind }
73
+ - sync: <record> # only with --rebase; grammar: reference/sync.md ## Sync record
73
74
  - truncation_notes: []
74
75
  ```
75
76
 
@@ -144,7 +145,7 @@ On the first pass the orchestrator re-runs Section A's fixed `gh pr view` and `g
144
145
  | Pending | >=1 pending check, no blocking conclusion, and the poll window (first pass or after a push) elapsed | the `verification evidence pending` overlay renders (`reference/decision-menu.md` `## Overlays`); `wait` polls another `timeout minutes`; never a local run, never an evidence-less merge; when a later re-resolve moves this head to CI-sufficient, the orchestrator dispatches a fresh Section B helper in claim-check-only mode (no local run) so every material claim gets a disposition | n/a (waiting) |
145
146
  | CI-sufficient | >=1 `success`, zero blocking, zero pending | Skip local run | CI claim: check name(s), conclusion, assessed SHA, run URL |
146
147
  | Fallback | No conclusive check: no checks on the assessed head, or only inert / fail-safe conclusions, after `timeout minutes` | Run local command (protocol below) | Local command + `log_path`; `local run: no conclusive check` |
147
- | Fix wave | A push by this gate | Poll the pushed head per `reference/fix-wave.md` `## Evidence after push`, then resolve this table on the normalized set | The resolved row's evidence for the pushed SHA |
148
+ | Fix wave | A fix-wave push by this gate (not the step-2b sync push) | Poll the pushed head per `reference/fix-wave.md` `## Evidence after push`, then resolve this table on the normalized set | The resolved row's evidence for the pushed SHA |
148
149
  | Stale head | Head advances since evidence was resolved (another actor's push); fires across runs - a single gather is same-head by construction | All prior evidence (CI or local) is stale; re-resolve this table for the new head before merge is offered | Fresh evidence for the new head |
149
150
 
150
151
  CI-sufficient predicate, stated once (the rows implement exactly this): **>=1