pi-gauntlet 7.6.0 → 7.6.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 +6 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/skills/gatekeep-pr/SKILL.md +2 -2
- package/skills/gatekeep-pr/reference/decision-menu.md +3 -2
- package/skills/gatekeep-pr/reference/findings.md +1 -1
- package/skills/gatekeep-pr/reference/fix-wave.md +7 -7
- package/skills/gatekeep-pr/reference/post-selection-loop.md +4 -2
- package/skills/gatekeep-pr/reference/sync.md +9 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v7.6.1 - 2026-10-07
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **gatekeep-pr `--rebase` sync** - a conflict stop whose helper resolves the listed files but reports a failing scoped test in a cleanly replayed file no longer aborts the rebase: the sync finishes, holds the push (`sync: rebased locally ...`), carries the failing tests as blocker findings, withholds `update branch`/`review`/`merge`/`merge anyway`/`push` until the normal `fix` wave resolves them, then lease-pushes; `stop` restores the pre-rebase head.
|
|
8
|
+
|
|
3
9
|
## v7.6.0 - 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 the scope contract at its head - the approved spec's acceptance-criteria dispositions and Design when the PR carries one, else the PR's stated intent; the ticket is cross-checked, never judged here (`check-delivery` owns it) - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; a pending commit status that GitHub does not report as blocking the gating actor (`mergeStateStatus` not `BLOCKED`) is not waited for - external-CI commit statuses included - while a running Actions check, an unknown merge state, and every other merge precondition still withhold; the project's verification command runs only as fallback), a rubric-based review, a compact bottom-up report (what the PR delivers, including rows deferred or deviating per spec, PR comments, nits, blockers, then a `mergeable` / `fixable` verdict) and a verb menu whose rows derive from the actor's permissions on the PR; nothing mutates (fixes, pushes, reviews, merges, tracker comments) until you pick a row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification; after your own merge two opt-in rows post the `AC coverage` block to the ticket or the PR - run it with `/skill:gatekeep-pr <pr> [--rebase [base]]`. `check-delivery` 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 the scope contract at its head - the approved spec's acceptance-criteria dispositions and Design when the PR carries one, else the PR's stated intent; the ticket is cross-checked, never judged here (`check-delivery` owns it) - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; a pending commit status that GitHub does not report as blocking the gating actor (`mergeStateStatus` not `BLOCKED`) is not waited for - external-CI commit statuses included - while a running Actions check, an unknown merge state, and every other merge precondition still withhold; the project's verification command runs only as fallback), a rubric-based review, a compact bottom-up report (what the PR delivers, including rows deferred or deviating per spec, PR comments, nits, blockers, then a `mergeable` / `fixable` verdict) and a verb menu whose rows derive from the actor's permissions on the PR; nothing mutates (fixes, pushes, reviews, merges, tracker comments) until you pick a row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification, or holds the rebased head locally when a conflict stop's scoped tests fail, so the normal `fix` row resolves it before the lease push; after your own merge two opt-in rows post the `AC coverage` block to the ticket or the PR - run it with `/skill:gatekeep-pr <pr> [--rebase [base]]`. `check-delivery` 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
|
@@ -23,7 +23,7 @@ Verify (step 3) and Review (step 4) each run as a fresh helper with `cwd` the PR
|
|
|
23
23
|
|---|---|---|---|
|
|
24
24
|
| 1 Gather | `verification-brief.md` Section A | PR digest: metadata, diff, checks, comments, worktree discovery | `gather` |
|
|
25
25
|
| 2 Provision + configure | `reference/assessment.md` | provisioned worktree; config resolved from the merge-base; ticket fetched; the `scope` block selected (spec rows and `design`, or stated intent) | `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` |
|
|
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, or a rebased, unpushed head (`rebased locally`, `complete`) | `sync` |
|
|
27
27
|
| 3 Verify | `verification-brief.md` Section B | evidence record (CI-first) and one disposition per material claim | `verify` |
|
|
28
28
|
| 4 Review | `verification-brief.md` Section C | reviewer findings and the internal comment ledger | `review` |
|
|
29
29
|
| 5 Integrate + report | `reference/post-selection-loop.md` `### Pre-menu refresh`, then `reference/findings.md`, then `reference/report.md` | AC outcomes, blockers, nits, drafted payloads, the rendered report | `report` |
|
|
@@ -34,7 +34,7 @@ Re-entry: a fix wave's own push re-enters step 4 through the own-push sequence (
|
|
|
34
34
|
|
|
35
35
|
## Progress tracking
|
|
36
36
|
|
|
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
|
|
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, a `no-op`, or a rebased, unpushed head (`rebased locally`), `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.
|
|
38
38
|
|
|
39
39
|
## Harness notes
|
|
40
40
|
|
|
@@ -8,7 +8,7 @@ Read from SKILL.md step 6. Input: the report's verdict and the PR state. Output:
|
|
|
8
8
|
|
|
9
9
|
Rows are `<n>. <verb> - <consequence>`; every row, `stop` included, carries its consequence clause. `stop` is always last, before the compose hint. `show evidence` is always offered and never `[recommended]`. A row the actor cannot execute - by `## Availability` below, or because the forge would refuse it (branch protection, a withhold) - is omitted and named in the compose line's `Not offered:` sentence; it is never `[recommended]`. Exactly one row is `[recommended]`.
|
|
10
10
|
|
|
11
|
-
- `fix` - apply the drafted payloads for the named blockers (default: all open blockers; with no open blockers, the nit payloads) through fresh implementer helpers in the provisioned worktree, review the wave before any push, push when `push` is available (`fix-wave.md`); `fix nits` applies nit payloads only, `fix + nits` blockers and nits; with no drafted payload at all, the human names the change
|
|
11
|
+
- `fix` - apply the drafted payloads for the named blockers (default: all open blockers; with no open blockers, the nit payloads) through fresh implementer helpers in the provisioned worktree, review the wave before any push, push when `push` is available (`fix-wave.md`); on a `rebased locally` head, lease-push once every carried blocker is resolved (`fix-wave.md` `## Evidence after push`); `fix nits` applies nit payloads only, `fix + nits` blockers and nits; with no drafted payload at all, the human names the change
|
|
12
12
|
- `push` - push local fix commits (renders only when unpushed commits exist)
|
|
13
13
|
- `review` - post the blockers as a GitHub review: request-changes on someone else's PR, a comment on your own
|
|
14
14
|
- `approve` - approve the PR (never your own)
|
|
@@ -21,7 +21,7 @@ Rows are `<n>. <verb> - <consequence>`; every row, `stop` included, carries its
|
|
|
21
21
|
- `post coverage to PR` - post the same block with `gh pr comment <N> --body-file`; renders only under the `own merge` overlay
|
|
22
22
|
- `show evidence` - print the evidence record, claim and AC outcomes, dispositions, drafted payloads and replies (`report.md` `## What stays out`)
|
|
23
23
|
- `wait` - poll the reviewer run, every **binding** pending check in the resolved set (required or not), `mergeStateStatus` while it is `BLOCKED`, `UNKNOWN`, or `unreadable`, and the comment set, then re-render
|
|
24
|
-
- `stop` - leave the PR as-is
|
|
24
|
+
- `stop` - leave the PR as-is; on a `rebased locally` head run `git reset --hard <pre_head>` before `### Teardown`, discarding the rewrite and any unpushed fix commits, and assert `HEAD == <pre_head>` with a clean tree; a mismatch is the existing hard stop with the error, and the remote stays untouched
|
|
25
25
|
|
|
26
26
|
Compose hint: `Type a number, or compose: "fix 2", "fix nits", "fix + nits", "fix 1 + reply". Not offered: push (needs push to <head_url>:<head_ref>), merge (needs WRITE on <owner>/<repo>).` The first sentence lists only compositions whose every verb is a rendered row: no `Nits` section, no `fix nits`; no `reply` row, no `fix 1 + reply`; no composable row, `Type a number.` alone. The second sentence names every omitted row as `<verb> (<why it is omitted>)` and is dropped when nothing is omitted. `merge anyway` renders as its own row when `## Availability` allows it, so the rendered-row rule does not apply to it. A composed line never bundles a push-producing verb (`fix`, `push`) with `merge`. When a failing check withholds `merge`, the hint line also offers `check <name> flaky | real | ci-broken`, which records the disposition on that check (`findings.md` `## Dispositions`): `flaky` lifts the blocker, `real` keeps it until green, `ci-broken` triggers the fallback local run (`ci-broken` records `ci-infrastructure-broken`).
|
|
27
27
|
|
|
@@ -39,6 +39,7 @@ Rows derive from the digest's `permissions` block (`../verification-brief.md` Se
|
|
|
39
39
|
| `merge anyway` | its own overlay's withhold is the only unmet merge prerequisite (`post-selection-loop.md` `### Merge preconditions`) and `viewer_permission` is `WRITE`, `MAINTAIN`, or `ADMIN`; otherwise omitted and named in `Not offered:` |
|
|
40
40
|
| `approve workflow run` | `viewer_permission` is `WRITE`, `MAINTAIN`, or `ADMIN`, and a held run exists on the assessed head |
|
|
41
41
|
| `update branch` | `can_update_branch` is true and no unpushed wave commit exists |
|
|
42
|
+
| `update branch`, `review`, `merge`, `merge anyway`, `push`, `wait` | omitted while the sync record is `rebased locally`, named in `Not offered:` as `(head not pushed)`; the fix wave's lease push is the only write to the PR head |
|
|
42
43
|
| `approve`, `review`, `reply`, `post coverage to ticket`, `post coverage to PR`, `show evidence`, `wait`, `stop` | per the consent table and overlays below |
|
|
43
44
|
|
|
44
45
|
## Consent table
|
|
@@ -66,6 +66,6 @@ How each disposition changes the menu: `decision-menu.md` `## Overlays`, CI chec
|
|
|
66
66
|
|
|
67
67
|
## Drafted payloads
|
|
68
68
|
|
|
69
|
-
Step 5 drafts, never applies. For every blocker and nit with a file-level fix - code and doc drift alike - draft the concrete edit as a payload keyed to the finding's internal ID. For every replyable `C#`, draft the reply. The worktree stays tracked-clean (`git status --porcelain --untracked-files=no` empty) at every menu render; a payload is applied only on a `fix` pick (the same path for code and docs) and dropped at teardown otherwise.
|
|
69
|
+
Step 5 drafts, never applies. For every blocker and nit with a file-level fix - code and doc drift alike - draft the concrete edit as a payload keyed to the finding's internal ID. For every replyable `C#`, draft the reply. The worktree stays tracked-clean (`git status --porcelain --untracked-files=no` empty) at every menu render; a payload is applied only on a `fix` pick (the same path for code and docs) and dropped at teardown otherwise. A `P#` carried by a `rebased locally` sync record is drafted like any blocker, from the helper's test output and `git show <stopped commit>`; a `P#` with no draftable edit stays open and `fix` reports it as not fixable. The human sees the draft at the `fix` consent and decides whether code or test expectations change; the implementer never decides it.
|
|
70
70
|
|
|
71
71
|
A posted review body is composed at post time from the blockers being addressed - one summary sentence, then the numbered items, ending on the fix or asked action.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# gatekeep-pr: fix wave
|
|
2
2
|
|
|
3
|
-
Read from `post-selection-loop.md` `### Fix wave` once the payloads for a `fix` pick are selected. Input: the selected payloads (`findings.md` `## Drafted payloads`), the pre-wave findings list, and the digest. Output: pushed commits with current evidence, or unpushed commits with the menu re-rendered. The orchestrator edits no tracked file and runs no git write other than `fetch`, `merge --ff-only`, and `
|
|
3
|
+
Read from `post-selection-loop.md` `### Fix wave` once the payloads for a `fix` pick are selected. Input: the selected payloads (`findings.md` `## Drafted payloads`), the pre-wave findings list, and the digest. Output: pushed commits with current evidence, or unpushed commits with the menu re-rendered. The orchestrator edits no tracked file and runs no git write other than `fetch`, `merge --ff-only`, `push`, and `reset --hard <pre_head>` after a refused lease or a remote head move on a `rebased locally` head (`post-selection-loop.md` `### Compare-and-swap`); every edit and every commit belongs to a fresh helper (`../SKILL.md` `## Harness notes`).
|
|
4
4
|
|
|
5
5
|
## Conflicts
|
|
6
6
|
|
|
@@ -17,10 +17,10 @@ This local check stands in for the remote `mergeable` field inside the wave: the
|
|
|
17
17
|
|
|
18
18
|
On a conflict, the conflict menu replaces the step. Each row carries its precondition in its consequence clause; a row whose precondition fails is omitted and named in the compose line's `Not offered:` sentence (`decision-menu.md`):
|
|
19
19
|
|
|
20
|
-
1. `update branch` - `gh pr update-branch <N>`; only when no wave commit exists and `can_update_branch` is true
|
|
20
|
+
1. `update branch` - `gh pr update-branch <N>`; only when no wave commit exists and `can_update_branch` is true and the sync record is not `rebased locally`. Afterwards run `git -C <worktree> fetch origin refs/pull/<N>/head && git -C <worktree> merge --ff-only FETCH_HEAD`; the head-move rule then refreshes the digest and the ledger (re-enter step 3). Report an error from `gh pr update-branch` (a conflict the forge cannot merge, or a 403) verbatim and re-render the menu without that row.
|
|
21
21
|
2. `merge base` - one fresh implementer helper merges the just-fetched `origin/<baseRefName>` into the branch and resolves the conflicts in one commit; the wave then continues with the selected payloads, and that merge commit joins the round's single `## Review before push` - its delta is given under `## Fix delta` as `git show --remerge-diff <merge-sha>` instead of the range diff - and consumes no extra round; only when `head_pushable` is true. When picked at the pre-push check, dispatch one fresh reviewer over `git show --remerge-diff <merge-sha>` alone, gate the push on it as in `## Review before push`, and count no extra round. When that reviewer reports Critical or Moderate, the commits stay unpushed and `push` is omitted as `push (reviewer blocked the wave)`.
|
|
22
22
|
3. `reply` - post the drafted text "conflicts with <baseRefName>, please update".
|
|
23
|
-
4. `stop` -
|
|
23
|
+
4. `stop` - as `decision-menu.md` `stop`.
|
|
24
24
|
|
|
25
25
|
A worktree holding unpushed wave commits never shows `update branch`. The orchestrator resolving a conflict itself is a red flag (`../SKILL.md`).
|
|
26
26
|
|
|
@@ -31,23 +31,23 @@ A worktree holding unpushed wave commits never shows `update branch`. The orches
|
|
|
31
31
|
For each payload in review order, dispatch one fresh implementer helper with `cwd` the PR worktree. Its task holds:
|
|
32
32
|
|
|
33
33
|
- the finding's evidence pack (`findings.md` `## Drafted payloads` shape) and the instruction to apply the consented payload - or, for a reviewer finding that has no drafted payload, the reviewer's finding text - never to re-solve the finding;
|
|
34
|
-
- the scoped test commands: the test it writes, or the test file the payload touches - never the resolved `verification command`;
|
|
34
|
+
- the scoped test commands: the test it writes, or the test file the payload touches, and for a `P#` carried by a `rebased locally` sync record the failing test file named in that record - never the resolved `verification command`;
|
|
35
35
|
- the commit rule: commit on the PR branch only when the scoped test passes, one commit per payload, subject naming the fix; otherwise discard its edits (`git checkout -- <its files>`, remove files it created) and return `open - <why>`;
|
|
36
36
|
- the report shape: the scoped test's failing run before the edit and passing run after it; a doc-only payload with no test reports `no scoped test` and its diff, and the reviewer below is its only gate.
|
|
37
37
|
|
|
38
|
-
After each helper returns, read `git -C <worktree> status --porcelain` and `git -C <worktree> rev-parse HEAD`. A `done` return shows exactly one new commit and a clean tree; an `open` return shows no new commit and a clean tree. Any other state - staged, unstaged, or untracked residue, or a moved HEAD on `open` - stops the round: dispatch no further implementer, and re-render the menu naming the files and the payload. An `open` payload is never reported as fixed, whatever the reviewer reports. The orchestrator never commits, resets
|
|
38
|
+
After each helper returns, read `git -C <worktree> status --porcelain` and `git -C <worktree> rev-parse HEAD`. A `done` return shows exactly one new commit and a clean tree; an `open` return shows no new commit and a clean tree. Any other state - staged, unstaged, or untracked residue, or a moved HEAD on `open` - stops the round: dispatch no further implementer, and re-render the menu naming the files and the payload. An `open` payload is never reported as fixed, whatever the reviewer reports. The orchestrator never commits or checks out files, and resets only to `<pre_head>` after a refused lease or a remote head move (`## Evidence after push`, `post-selection-loop.md` `### Compare-and-swap`).
|
|
39
39
|
|
|
40
40
|
## Review before push
|
|
41
41
|
|
|
42
42
|
When HEAD equals `pre_wave_head` after a round (every payload returned `open`), skip the review and the push and re-render the menu with the `open` reasons.
|
|
43
43
|
|
|
44
|
-
After the round's payloads, dispatch one fresh reviewer helper, `cwd` the PR worktree, read-only, over `<pushed_head>..HEAD`, where `pushed_head` is the digest's `headRefOid` (the
|
|
44
|
+
After the round's payloads, dispatch one fresh reviewer helper, `cwd` the PR worktree, read-only, over `<pushed_head>..HEAD`, where `pushed_head` is the digest's `headRefOid` (the assessed head), so commits a previous round left unpushed are always inside the reviewed range; `pre_wave_head` serves only the empty-round test. Its task carries the pre-wave findings list, numbered with this skill's finding ids, under `## Previous review report (re-review trigger)`, and the output of `git diff <pushed_head> HEAD` under `## Fix delta`, and asks for the reviewer's native report plus one closure line per pre-wave finding, accepted anywhere after the findings list: `<finding id>: resolved` or `<finding id>: open - <why>`. When the round holds a `merge base` commit, `## Fix delta` is `git diff <pushed_head> <merge-sha>^1`, then `git show --remerge-diff <merge-sha>`, then `git diff <merge-sha> HEAD`. Ignore a `TRAJECTORY:` line in the report.
|
|
45
45
|
|
|
46
46
|
Mark a finding resolved only from a `resolved` closure line; the orchestrator never marks one. Push only when the report has no Critical or Moderate finding. Otherwise re-dispatch implementers with the report verbatim for the `open` payloads and the new Critical or Moderate findings - one more round, up to the cap. At the cap, the commits stay unpushed on the PR branch; the menu re-renders with the reviewer's verdict line and its Critical and Moderate titles, and `push` is omitted, named as `push (reviewer blocked the wave)` in the `Not offered:` sentence.
|
|
47
47
|
|
|
48
48
|
## Evidence after push
|
|
49
49
|
|
|
50
|
-
Run `## Conflicts` once more, then push to the recorded destination: `git -C <worktree> push --no-follow-tags <head_url> HEAD:refs/heads/<head_ref>`.
|
|
50
|
+
Run `## Conflicts` once more, then push to the recorded destination: `git -C <worktree> push --no-follow-tags <head_url> HEAD:refs/heads/<head_ref>`. On a `rebased locally` head push only when every carried `P#` has a `resolved` closure line, with `--force-with-lease=<head_ref>:<pre_head>`; otherwise the commits stay unpushed and the menu re-renders with the open `P#`. A refusal naming the lease (`stale info`): `git reset --hard <pre_head>`, assert `HEAD == <pre_head>` and a clean tree; a mismatch is a hard stop with the error, record `sync: failed - push rejected: <first server line>`, and render `stop` and `show evidence` only - the fix commits are discarded with the rewrite, and the reason says so. After a successful lease push rewrite the sync record to the plain `rebased ...` form. Report any other rejected push verbatim and re-render the menu with the commits unpushed.
|
|
51
51
|
|
|
52
52
|
Poll the pushed head only, every 30 seconds for up to `timeout minutes` (default 15): `gh pr view <N> --json headRefOid,statusCheckRollup,mergeStateStatus` and `gh run list -R <base-owner>/<base-repo> -c <head> --json databaseId,status,conclusion`. A moved `headRefOid` stops the poll and takes the head-move rule (re-enter step 3, `post-selection-loop.md` intro). Normalize first: a `CheckRun` is terminal at `status: COMPLETED` with its `conclusion`; a `StatusContext` maps `state` `SUCCESS` to success, `FAILURE` and `ERROR` to blocking, `PENDING` and `EXPECTED` to pending; apply the `ci checks` filter and the reviewer-check exception (`../verification-brief.md` Section B) before the table, then the binding classification of every pending check on the pushed head's `mergeStateStatus`.
|
|
53
53
|
|
|
@@ -20,7 +20,7 @@ All must hold before `merge` executes; a `merge` pick while one fails is refused
|
|
|
20
20
|
|
|
21
21
|
### Compare-and-swap
|
|
22
22
|
|
|
23
|
-
Before every external write, re-fetch `headRefOid`, `state`, `mergeable`, and `mergeStateStatus`.
|
|
23
|
+
Before every external write, re-fetch `headRefOid`, `state`, `mergeable`, and `mergeStateStatus`. While the sync record is `rebased locally`, compare the fetched `headRefOid` against the record's `<pre_head>`: a move takes the lease-refusal path of `fix-wave.md` `## Evidence after push` (`git reset --hard <pre_head>`, assert `HEAD == <pre_head>` and a clean tree, record `sync: failed - remote head moved`, render `stop` and `show evidence` only) instead of the head-move re-sync; an unmoved head never re-syncs, and a `state` change to closed or merged renders `stop` and `show evidence` only. On a pushed head: a head or state change since assessment invalidates the current state - re-sync the worktree, re-enter step 3, and re-render the menu; a `mergeStateStatus` change alone (head unchanged) invalidates a `merge` or `merge anyway` pick and re-enters step 5 through `### Pre-menu refresh`; `reply`, `review`, and `approve` proceed, and the next menu renders from the refreshed value. Exception: a pick's own push updates the assessed head to the pushed SHA as part of that pick's execution - this self-inflicted head move does not invalidate the pick; the next compare-and-swap runs against the new head on the next external write. Before a merge executes (plain or `anyway`), run the full comment refetch and reconciliation (`### Re-render` steps 1-5), not just placeholder detection. If the head changed, re-enter step 3 instead. A new or changed-body delta since the consent render aborts the selected merge, plain or `anyway`: show the reconciled report and request a fresh pick even when no blocker resulted. A newly `pending` row, a queued/in-progress reviewer run, or a failed refetch (`comments not refreshed (<reason>)`) refuses a plain merge and re-renders; `anyway` overrides only those existing pending/refetch-failure overlays and prints what it overrode, never a new blocker or unreviewed delta.
|
|
24
24
|
|
|
25
25
|
### Fix wave
|
|
26
26
|
|
|
@@ -66,7 +66,7 @@ After every push or any mutation that can change readiness (fix wave pushed, PR
|
|
|
66
66
|
|
|
67
67
|
### Pre-menu refresh
|
|
68
68
|
|
|
69
|
-
The first read of step 5, before the report and the menu render from one state - the first pass included (a long local run or review can stale the gather), every `### Re-render` entry, the post-`wait` re-fetch, `update branch`, a `merge base` round, and a fix-wave push (`fix-wave.md` `## Evidence after push`). Run `gh pr view <N> --json headRefOid,state,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup` and `gh run list -R <owner>/<repo> -c <headRefOid> --json databaseId,status,conclusion,workflowName,url`.
|
|
69
|
+
The first read of step 5, before the report and the menu render from one state - the first pass included (a long local run or review can stale the gather), every `### Re-render` entry, the post-`wait` re-fetch, `update branch`, a `merge base` round, and a fix-wave push (`fix-wave.md` `## Evidence after push`). Run `gh pr view <N> --json headRefOid,state,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup` and `gh run list -R <owner>/<repo> -c <headRefOid> --json databaseId,status,conclusion,workflowName,url`. While the sync record is `rebased locally`, compare the fetched `headRefOid` against the record's `<pre_head>` and render from that alone: a move takes the lease-refusal path of `fix-wave.md` `## Evidence after push` (`git reset --hard <pre_head>`, assert `HEAD == <pre_head>` and a clean tree, record `sync: failed - remote head moved`, render `stop` and `show evidence` only); an unmoved head keeps `status_checks[]` and `actions_runs[]` as they are, skips the evidence re-resolve, and ignores `mergeable` and `mergeStateStatus` changes - `source: pending` holds until the lease push rewrites the record; a `state` change to closed or merged renders `stop` and `show evidence` only. On a pushed head: head moved -> the head-move rule (re-enter step 3). `state` or `mergeable` changed (head unchanged) -> route through `### Compare-and-swap` re-assessment. Head unchanged with `state` and `mergeable` unchanged -> replace `permissions.merge_state_status`, `pr.reviewDecision`, `status_checks[]`, and `actions_runs[]` in the digest together, re-normalize and re-classify every pending check (`../verification-brief.md` Section B, binding classification), and re-resolve the Evidence resolution table on the fresh set, so a check whose run completed since the last menu is never paired with its cached run status; when that re-resolve moves `source: pending` to CI-sufficient, dispatch the Section B helper in claim-check-only mode (`../verification-brief.md` Section B) before rendering. A `gh` failure keeps the prior values, classifies every pending check as binding (`../verification-brief.md` Section B, binding classification), and renders the `merge state` overlay (`decision-menu.md` `## Overlays`) through `## Withhold reason resolver` reason 2 (locator `merge state not refreshed (<reason>)`). The refresh is read-only and never mints `C#` rows - comment reconciliation stays in `### Re-render`.
|
|
70
70
|
|
|
71
71
|
### Wait course
|
|
72
72
|
|
|
@@ -85,4 +85,6 @@ Before posting or committing any external payload - review bodies, replies, comm
|
|
|
85
85
|
| Own merge | tear down when the own-merge or merge-queued menu exits on `stop` or after the last post row leaves it | same |
|
|
86
86
|
| Non-merge stop | offer teardown, never autonomous; say when unpushed fix commits would be discarded | leave as found; say so explicitly when unpushed fix commits remain, and let the user choose leave-or-discard |
|
|
87
87
|
|
|
88
|
+
On a `rebased locally` head the `stop` reset (`decision-menu.md`) runs first; the Non-merge stop row then applies with no unpushed fix commits.
|
|
89
|
+
|
|
88
90
|
Drafted payloads that were never applied are dropped with the run; the worktree was never dirtied by them. Delete every helper output file minted this run and every `log_path` they name on every exit.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# gatekeep-pr: step 2b - sync (`--rebase [base]`)
|
|
2
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.
|
|
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, or the worktree on a rebased, unpushed head with `rebased locally` in the digest. 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
4
|
|
|
5
5
|
## Permitted orchestrator git writes
|
|
6
6
|
|
|
@@ -34,9 +34,9 @@ When `pr.author.login` differs from `gh api user --jq .login`, or that login is
|
|
|
34
34
|
|
|
35
35
|
## Conflict stop
|
|
36
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".
|
|
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; report every failing scoped test with its file and test names; never run `git rebase --continue`, `git rebase --abort`, `git rebase --skip`, or `git commit`; never edit a file outside the list".
|
|
38
38
|
|
|
39
|
-
Continue (`GIT_EDITOR=true git rebase --continue`) only when
|
|
39
|
+
Continue (`GIT_EDITOR=true git rebase --continue`) only when `--diff-filter=U` is empty, every listed file is fully staged, `git status --porcelain` differs from the recorded snapshot only in the listed files, and the helper returned `done` with the scoped results or `open` whose only stated reason is a failing scoped test in a file outside the list; a non-zero `--continue` with unmerged paths is the next conflict stop; without unmerged paths it takes `## Failure path`. Anything else (`open` for any other reason, residue outside the listed files, unmerged paths left) takes `## Failure path`. Record each reported failing test with its stop. One attempt per stop, no round counter; the fix-round cap (`closureReview.maxFixRounds`) is untouched. Resolved file names accumulate across stops.
|
|
40
40
|
|
|
41
41
|
## Review before push
|
|
42
42
|
|
|
@@ -44,6 +44,8 @@ Runs on every rewritten head, with or without a stop; only a `no-op` skips it. O
|
|
|
44
44
|
|
|
45
45
|
## Push
|
|
46
46
|
|
|
47
|
+
When any stop reported a failing scoped test, skip the push, set the digest's `headRefOid` to the local HEAD, keep `<pre_head>` in the record, and record `sync: rebased locally <pre_head7>..<new_head7> onto origin/<base>, <n> stop(s) resolved in <files>, <m> commit(s) dropped as already applied, scoped tests red in <file>: <tests>` - one `scoped tests red in` clause per stop; then continue at `## Re-gather`.
|
|
48
|
+
|
|
47
49
|
`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
50
|
|
|
49
51
|
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>`.
|
|
@@ -52,10 +54,12 @@ A refusal whose message names the lease (`stale info`, the remote head moved sin
|
|
|
52
54
|
|
|
53
55
|
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; re-run `assessment.md` `## Select the scope contract` on the pushed head. 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 binding check still pending at the limit renders the `verification evidence pending` overlay (`../verification-brief.md` Section B, binding classification), never green, and a head with no check, or only not-binding pending checks, takes the `Fallback` 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
56
|
|
|
57
|
+
On a `rebased locally` head the forge re-read returns `<pre_head>` as `headRefOid`; keep it in the record and leave the digest's `headRefOid` at the local HEAD, which the scope-contract read and step 3 use, and take the digest's changed files and diff from `git diff origin/<base>...HEAD`. Mint one blocker `P#` per `scoped tests red in` clause before step 3 - evidence the helper's scoped-test output, the blocker contract of `findings.md` (IDs append-only, surviving re-renders); skip the step-3 check poll and the local verification run - step 3 records `source: pending` for the local head (no claims) - the carried blockers are the verification evidence and the verdict is `fixable`; `plan_tracker` closes the `sync` stage `complete`.
|
|
58
|
+
|
|
55
59
|
## Failure path
|
|
56
60
|
|
|
57
|
-
Triggers: an unresolved stop, `open
|
|
61
|
+
Triggers: an unresolved stop, `open` for any reason other than a failing scoped test in a file outside the list, 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
62
|
|
|
59
63
|
## Sync record
|
|
60
64
|
|
|
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.
|
|
65
|
+
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`, `rebased locally <pre_head7>..<new_head7> onto origin/<base>, ..., scoped tests red in <file>: <tests>` (the plain `rebased ...` form is this record with the `scoped tests red in` clauses removed), `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.
|