pi-gauntlet 7.32.0 → 7.33.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 +13 -0
- package/README.md +7 -5
- package/agents/plan-writer.md +2 -2
- package/agents/pr-gatekeeper.md +2 -2
- package/extensions/lib/gauntlet-settings.test.ts +4 -4
- package/extensions/lib/gauntlet-settings.ts +1 -1
- package/extensions/phase-tracker.test.ts +5 -5
- package/package.json +1 -1
- package/skills/gatekeep-pr/SKILL.md +11 -11
- package/skills/gatekeep-pr/reference/assessment.md +2 -2
- package/skills/gatekeep-pr/reference/batch.md +11 -9
- package/skills/gatekeep-pr/reference/cleanup.md +19 -1
- package/skills/gatekeep-pr/reference/decision-menu.md +38 -160
- package/skills/gatekeep-pr/reference/findings.md +6 -4
- package/skills/gatekeep-pr/reference/fix-wave.md +24 -27
- package/skills/gatekeep-pr/reference/menus.md +80 -0
- package/skills/gatekeep-pr/reference/post-selection-loop.md +89 -46
- package/skills/gatekeep-pr/reference/report.md +13 -19
- package/skills/gatekeep-pr/reference/sync.md +15 -6
- package/skills/gatekeep-pr/verification-brief.md +13 -11
- package/skills/verification-before-completion/reference/conformance-check.md +1 -1
- package/skills/verification-before-completion/reference/settings-precedence.md +1 -1
- package/skills/writing-plans/SKILL.md +11 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v7.33.0 - 2026-10-26
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- `gatekeep-pr` uses one course per run: `assess` posts the blocker review without repairs; `deliver` repairs, syncs, pushes, and watches CI until readiness, an obstacle, or a cap; `deliver with nits` includes current and later nits. A ready first report goes directly to separate final merge and approval choices. The shared `closureReview.maxFixRounds` default is `5`; gatekeep initializes independent repair-round and sync budgets to `2 x` the resolved value while the verify-phase guard stays at `1 x`. Non-final `stop` removes eligible created or reused worktrees without branch deletion, base pull, or cleanup overrides, and keeps unsafe trees with a reason.
|
|
8
|
+
|
|
9
|
+
## v7.32.1 - 2026-10-26
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **The plan-writer retains baseline evidence across spec- and plan-only amendments instead of rerunning every `Tests:` command.** On a Findings or Amend re-dispatch, Baseline step 2 proves once per recorded SHA that the tree outside the spec and plan directories is unchanged (`git diff --quiet <recorded> <BASE> -- . ':!<spec dir>/' ':!<plan dir>/'`, or the jj `diff --from --to --summary` equivalent with an empty filtered summary), then re-stamps unchanged commands' bullets to the current base and appends one `retained <n> bullets: <recorded> -> <BASE>, ...` line to the step-1 provenance note. Any proof failure, a changed command, missing or invalid evidence, an admitted in-flight path outside those directories, or `Findings: rerun baseline` reruns everything and retains nothing; `plan_check` and the one-SHA rule are untouched. Supersedes only the "same base" retention condition of the gh-72 spec (`skills/writing-plans/SKILL.md` Baseline step 2, `agents/plan-writer.md` step 5; spec `doc/specs/2026-10-26-selective-baseline-rerun-on-amend.md`). (#85)
|
|
14
|
+
- Eval sample `baseline-equivalent-restamp` (two scenarios) under `eval/writing-plans/`; `fresh-child-authors-plan` and `pr-subject-threads` expectations clarified. (#85)
|
|
15
|
+
|
|
3
16
|
## v7.32.0 - 2026-10-25
|
|
4
17
|
|
|
5
18
|
### Added
|
package/README.md
CHANGED
|
@@ -73,13 +73,15 @@ Everything between gate 1 and gate 2 - task breakdown, implementation, both revi
|
|
|
73
73
|
|
|
74
74
|
## Architecture
|
|
75
75
|
|
|
76
|
-
`gatekeep-pr`
|
|
76
|
+
`gatekeep-pr` selects one course per run: `assess` posts open blockers as one review (`request-changes` on someone else's PR, `comment` on your own), posts nothing with zero blockers, and performs no repair or sync; `deliver` repairs blockers, syncs, pushes, watches CI, and re-reviews until readiness, an obstacle, or a cap exit. `deliver with nits` stores `deliver-nits` and includes current and later nits. A ready first report skips the course pick and renders the final menu directly. Delivery rebases your own non-bot branch with `--force-with-lease` unless its history contains merge commits; someone else's, bot-authored, and merge-containing heads merge base instead. Final `merge` and `approve` remain separate picks; approval is offered only on someone else's non-draft PR with zero blockers. See the [course loop](skills/gatekeep-pr/reference/post-selection-loop.md) and [menu templates](skills/gatekeep-pr/reference/menus.md). In pi, `/skill:gatekeep-pr 12 15 18 [--rebase [base]]` gates a set of PR numbers or same-repository URLs, with one fresh `pr-gatekeeper` child and one worktree per PR. The parent shows a summary and unchanged per-PR menus in input order, queues every pick until the last menu, then executes the queued work in parallel. The set must fit pi-cohort's `parallel.maxTasks` (default `8`); it is never chunked. With `--rebase`, the parent asks once per set for eligible other-author or unreadable-author heads, excluding no-op heads and heads with merge commits. Multi-PR mode rejects positional issue references and is pi-only; single-PR behavior on Claude Code is unchanged. See the [parent-child contract](skills/gatekeep-pr/reference/batch.md).
|
|
77
77
|
|
|
78
|
-
The
|
|
78
|
+
The course, target/scope baseline, repair and sync usage, and poll deadline live in one OS temporary continuation record, whose path appears in the tracker or checklist and `show evidence`. Gatekeep initializes independent repair-round and sync budgets to `2 x closureReview.maxFixRounds` (default `5`, no-tool fallback `2 x 5`). Only source-changing repair waves spend rounds; conflict-only syncs spend the separate sync budget, and description-only work, watches, and re-reviews spend neither. Obstacle `deliver` extends each cap by `2 x` the resolved value without resetting usage; a timeout continuation instead opens one more watch window without extending caps. Only the gate's own push automatically renews the CI window (`timeout minutes`, default `15`). The verify-phase runtime guard still uses `1 x` the resolved value and can block a gatekeep dispatch earlier.
|
|
79
|
+
|
|
80
|
+
Non-final `stop` removes an eligible tracked-clean, non-primary worktree, created or reused, with no unpushed commits and a matching configured home; otherwise it keeps the tree and reports why. It never deletes the branch, pulls base, or runs cleanup overrides. See the [stop lifecycle](skills/gatekeep-pr/reference/cleanup.md#non-final-stop). Missing or invalid state stops course work without resetting budgets. Terminal exit deletes the record; OS cleanup can remove it earlier, and a new session never discovers or resumes authority automatically. This is a skill protocol, not hard runtime enforcement.
|
|
79
81
|
|
|
80
82
|
pi-gauntlet ships three kinds of pieces, layered on top of pi-cohort's dispatch:
|
|
81
83
|
|
|
82
|
-
- **21 skills** - the workflow logic. Sixteen activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `shape-ticket` (creates one tracker issue or repairs a comma/whitespace list of refs against a Context/Problem/Idea/Acceptance-Criteria template, with a grounded repo audit, an AC integrity check, a cheap council roast, and sequential per-ticket exact-diff write confirmations; model-invocable so other skills and commands can route "file a ticket" to it, the gate still needs your yes), `gatekeep-pr` (model-invocable on an explicit ask that names the PR and the verb; the gates still need your selection - is consent-gated pre-merge verification of a PR against the scope contract at its head - the approved spec's acceptance-criteria dispositions and Design when the PR carries one, else the PR's stated intent; the ticket is cross-checked, never judged here (`check-delivery` owns it) - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; a pending commit status that GitHub does not report as blocking the gating actor (`mergeStateStatus` not `BLOCKED`) is not waited for - external-CI commit statuses included - while a running Actions check, 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
|
|
84
|
+
- **21 skills** - the workflow logic. Sixteen activate automatically when pi sees the matching kind of task, and the workflow skills chain through their gates: `shape-ticket` (creates one tracker issue or repairs a comma/whitespace list of refs against a Context/Problem/Idea/Acceptance-Criteria template, with a grounded repo audit, an AC integrity check, a cheap council roast, and sequential per-ticket exact-diff write confirmations; model-invocable so other skills and commands can route "file a ticket" to it, the gate still needs your yes), `gatekeep-pr` (model-invocable on an explicit ask that names the PR and the verb; the gates still need your selection - is consent-gated pre-merge verification of a PR against the scope contract at its head - the approved spec's acceptance-criteria dispositions and Design when the PR carries one, else the PR's stated intent; the ticket is cross-checked, never judged here (`check-delivery` owns it) - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; a pending commit status that GitHub does not report as blocking the gating actor (`mergeStateStatus` not `BLOCKED`) is not waited for - external-CI commit statuses included - while a running Actions check, 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 course and terminal menus whose rows derive from the actor's permissions on the PR; nothing mutates (repairs, pushes, reviews, merges, tracker comments) until you pick a course or terminal row, except the opt-in `--rebase [base]` sync that rebases, reviews, and lease-pushes the head before verification, or holds the rebased head locally when a conflict stop's scoped tests fail, so a selected delivery course resolves it before the lease push; after your own merge two opt-in rows post the `AC coverage` block to the ticket or the PR; own-merge, approved, and closed-PR menus offer a recommended `cleanup` row that tears down the gate's worktree and branch and runs the overrides file's `## cleanup` steps - run it with `/skill:gatekeep-pr <pr> [--rebase [base]]`), `check-delivery` (model-invocable on an explicit ask that names the ticket and the verb; the gates still need your selection - is a post-merge detective control: proves an issue actually shipped (default-branch landing, delivery target, per-AC evidence) before its tracker status advances; it never writes a terminal status - run it with `/skill:check-delivery <ref>`), `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `loop-implementation` (the plan-free implement driver that iterates a fresh `loop-implementer` over the spec's acceptance-criteria ledger when approval picks the loop engine), `dispatching-parallel-agents`, `verification-before-completion`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `forge-skill`, `linear` (reads/searches/comments on/manages Linear tickets via the `linearis` CLI; owns all linearis mechanics and the `## Issue tracker` overrides schema; tracker-facing skills route to it). Five more require explicit entry (`disable-model-invocation: true`): `brainstorming` starts the gated workflow on explicit consent - use `/skill:brainstorming` for reliable discovery, or opt in through reachable explicit prose or a human-selected handoff; after spec approval, downstream planning and implementation still chain automatically. `chase-bug` is human-only bug triage: read-only root-cause discovery to an evidenced verdict menu (real bug -> ticket/brainstorm/hotfix/respond; five negative verdicts), then a gated response to the reporter for addressable origins (GitHub issue / tracker ticket) and a rendered verdict summary otherwise - it never fixes during triage; the hotfix row hands off to `skills/chase-bug/hotfix.md` after the menu - run it with `/skill:chase-bug`. `gauntlet-performance` reads the committed run telemetry (current repo by default, `--dir <path>` adds others) through the parse-only `gauntlet-performance` CLI and answers with one example-led recommendation, 3-5 cornerstone numbers, a council assessment when available, and a menu of at most three actions (render a report file, open the recommendation as a ticket via `shape-ticket`, drill into one run); it writes nothing unless you pick render - run it with `/skill:gauntlet-performance [--dir <path>]... [--since <version>]`. `gauntlet-handoff` ends a session whose flow a fresh session will continue: it invokes pi-cohort's `handoff` skill (`--out <path>` or `--key <name>`, default key = the run worktree's branch with `/` flattened to `-`, default file `<tmpdir>/pi-handoff/<key>.md`), then appends the gauntlet process-state section (phase/plan tracker status) per the shared contract `skills/gauntlet-resume/reference/brief-contract.md` - run it with `/skill:gauntlet-handoff [--out <path> | --key <name>]`. `gauntlet-resume` is the only way back into an interrupted flow from a fresh session: it takes a `gauntlet-handoff` brief (a file path, pasted text, or - with no arguments - a pick from the briefs under `<tmpdir>/pi-handoff/`), a bare worktree that already holds a spec, or a spec tracked on main under a configured spec dir (a spec seed: it creates or reuses the worktree through `using-git-worktrees` and pins the spec), restores phase/plan tracker state through the legal arming sequence (`start brainstorm`, `skip` with `resume:` reasons, `plan_check` before implement-or-later), and never infers approval from artifacts - run it with `/skill:gauntlet-resume [<brief-file> | <spec>.md | <worktree-name-or-path>]`.
|
|
83
85
|
- **11 subagent personas** - the specialized child agents the skills dispatch via pi-cohort: `implementer`, `loop-implementer`, `code-reviewer`, `spec-reviewer`, `conformance-reviewer`, `spec-summarizer`, `plan-writer`, `spec-council-member`, `spec-council-synthesizer`, `spec-council-applier`, `pr-gatekeeper`. See [doc/personas.md](./doc/personas.md) for what each one does and why its permissions are scoped the way they are.
|
|
84
86
|
- **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, and that every test command carries a passing base-commit result; 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.
|
|
85
87
|
|
|
@@ -509,7 +511,7 @@ customization lives in three places, never in the wrapper itself:
|
|
|
509
511
|
- local verification: always # optional; default (absent) = CI-first; "always" forces the local run even when exact-head CI is green
|
|
510
512
|
- ci checks: <comma-separated check names> # optional; narrows which checks count as evidence; absent = all checks on the assessed head
|
|
511
513
|
- issue fetch: <command with <ref> placeholder> # optional, replaces gh issue view
|
|
512
|
-
- merge policy: squash | merge-commit | rebase # optional; default squash
|
|
514
|
+
- merge policy: squash | merge-commit | rebase # optional; default squash; chooses gh pr merge flag for final merge only, never sync strategy or approval
|
|
513
515
|
```
|
|
514
516
|
|
|
515
517
|
- **The gauntlet overrides file, `## cleanup` section** - the ordered post-gate steps the `cleanup` menu row runs (contract under Project-specific overrides above).
|
|
@@ -539,7 +541,7 @@ The conformance gate's model, the spec council's roster, and the phase-tracker's
|
|
|
539
541
|
| `specCouncil.chair` | same | unset (inherits the main loop) | chair model - [detail](./doc/configuration.md#spec-council) |
|
|
540
542
|
| `closureReview.enforce` | phase-tracker | `true` | conformance gate on/off - [detail](./doc/configuration.md#conformance-gate-model) |
|
|
541
543
|
| `closureReview.model` | phase-tracker | unset (inherits) | conformance reviewer model - [detail](./doc/configuration.md#conformance-gate-model) |
|
|
542
|
-
| `closureReview.maxFixRounds` | phase-tracker | `
|
|
544
|
+
| `closureReview.maxFixRounds` | phase-tracker | `5` | fix rounds before escalation - [detail](./doc/configuration.md#conformance-gate-model) |
|
|
543
545
|
| `escalationLoop.implModel` | phase-tracker | unset (main loop) | implementer model on escalation - [detail](./doc/configuration.md#escalation-loop) |
|
|
544
546
|
| `phaseModels.plan` / `.implement` / `.verify` / `.ship` | phase-tracker | unset (no switch) | `provider/id[:level]` applied persistently at that phase's start - [detail](./doc/configuration.md#phase-models) |
|
|
545
547
|
| `flowGuards.enforce` | phase-tracker | `true` | phase gates on/off - [detail](./doc/configuration.md#phase-tracker) |
|
package/agents/plan-writer.md
CHANGED
|
@@ -32,13 +32,13 @@ The task names five absolute paths: the worktree, the approved spec, the plan pa
|
|
|
32
32
|
2. Read the plan skill file (`SKILL.md`) from its `## File Structure` heading through the end of its `## Self-Review` section, and the whole plan contract file (`plan-contract.md`). That text is the plan contract; follow it as written.
|
|
33
33
|
3. Read the spec in full. Recon the code the spec touches by reading it directly: exact paths to create, modify, or delete; call sites and tests with line ranges; the scoped command that runs one test file; scoped and repo-wide lint and format commands; the full-suite verification entrypoint and whether it bundles style checks. Dispatch nothing.
|
|
34
34
|
4. Write the plan at the plan path with `write` as a full replacement. The plan path is the only file you write; never create or edit any other file.
|
|
35
|
-
5. Run Baseline from the skill's Self-Review before `plan_check({ planPath })`. On Findings/Amend re-dispatch, read provenance once for the current base; retain evidence
|
|
35
|
+
5. Run Baseline from the skill's Self-Review before `plan_check({ planPath })`. On Findings/Amend re-dispatch, read provenance once for the current base; retain evidence for unchanged commands whose recorded SHA is BASE or proven tree-equivalent per the skill's Baseline step 2. Rerun missing, invalid, or changed-command evidence, every command on any proof failure, and every command for `Findings: rerun baseline`. Rewrite SHAs only after execution or a recorded tree-equivalence. Baseline owns and regenerates its rows; preserve unresolved contract rows.
|
|
36
36
|
6. Run the deterministic checker and judgment checks in Self-Review, including Contract decisions. Repair `baseline-line` and substantive baseline `open-questions` findings only by Baseline execution, never by authoring evidence or deleting/rewording a row. Normalize malformed Open Questions into rows without changing substantive text. Fix every other finding; with substantive rows remaining, reply `OPEN QUESTIONS <abs plan path>` immediately, without a passing check or repair rounds for those rows. Any ordinary edit returns to step 5.
|
|
37
37
|
7. Stop when `plan_check` passes and judgment checks cause no edit. Apply the 3-round budget only to ordinary findings: convert an exhausted finding to an Open Questions row and stop. Findings blocks continue that finding's count across dispatches. Omit the section when no rows remain and reply per the output contract.
|
|
38
38
|
|
|
39
39
|
## Rules
|
|
40
40
|
|
|
41
|
-
- Use `bash` only for Baseline provenance (`git rev-parse`, `git status --porcelain`, or the contract's jj equivalents) and the plan's own `Tests:` commands in the worktree. Write only the plan. Never edit tracked source, commit, run `stash`, `checkout`, `restore`, `clean`, or mutate jj's working copy or revisions (`new`, `commit`, `abandon`, `restore`, `squash`).
|
|
41
|
+
- Use `bash` only for Baseline provenance (`git rev-parse`, `git status --porcelain`, `git diff --quiet`, `jj -R "<abs worktree path>" diff --from <recorded> --to <BASE> --summary`, or the contract's other jj equivalents) and the plan's own `Tests:` commands in the worktree. Write only the plan. Never edit tracked source, commit, run `stash`, `checkout`, `restore`, `clean`, or mutate jj's working copy or revisions (`new`, `commit`, `abandon`, `restore`, `squash`).
|
|
42
42
|
- Never leave a placeholder in authored plan prose; retain verbatim failure evidence in row continuation fences. Derive decisions from source per Contract decisions; name only an unsettled decision as an open question.
|
|
43
43
|
- Never call `plan_tracker`, `phase_tracker`, or `subagent`; the orchestrator owns them.
|
|
44
44
|
- Never reply with more than the one line.
|
package/agents/pr-gatekeeper.md
CHANGED
|
@@ -15,8 +15,8 @@ Use the supplied record and report paths only. Never re-initialize a supplied re
|
|
|
15
15
|
|
|
16
16
|
## Method
|
|
17
17
|
|
|
18
|
-
- In `
|
|
19
|
-
- In `execute`, refresh before writing. If the head moved or the picked row disappeared, return a fresh report without executing the stale pick. Otherwise run step 7 and
|
|
18
|
+
- In `report`, run steps 1-5 in the provisioned worktree. Skip the already completed provisioning fetch/gitignore/add only; verify HEAD against the supplied headRefOid and preserve sync's safety fetch. Keep trusted merge-base configuration and assessed-head binding. Dispatch Verify then Review sequentially as fresh helpers with cwd the same worktree, never new helper worktrees.
|
|
19
|
+
- In `execute`, refresh before writing. If the head moved or the picked row disappeared, return a fresh report without executing the stale pick. Otherwise run step 7 under the course rules; under `deliver` run through a D3 readiness, obstacle, or cap exit and return REPORT. Keep course, `repair.rounds_cap`, `repair.syncs_cap`, usage, deadline, ledger, helper paths, and no-progress decisions PR-local. Preserve usage and caps on re-entry; only the first course pick may resolve its initial allowance.
|
|
20
20
|
- Substitute supplied rebase confirmation at the existing ask only: yes proceeds; no records `sync: declined - other author's branch`, stage skipped, then assesses unsynced. Use supplied `verification command: <command>` or `spec: <path>` at that ask only, re-assessing from the start with the same record. Never treat answers as broader authority.
|
|
21
21
|
- Track single-PR stages with session-local plan_tracker or a checklist. Delegate tracked source edits and commits to implementer; perform only orchestrator writes authorized by the injected procedure. Never push, edit PR metadata, comment, review, or write to a tracker before direct human authority, except the requested sync push. Never choose a sibling's actions.
|
|
22
22
|
|
|
@@ -164,12 +164,12 @@ test("closureReview: enforce default true; false only when explicitly false", ()
|
|
|
164
164
|
assert.equal(resolveClosureReview({ closureReview: { enforce: true } }).enforce, true);
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
-
test("closureReview: maxFixRounds default
|
|
168
|
-
assert.equal(resolveClosureReview({}).maxFixRounds,
|
|
169
|
-
assert.equal(resolveClosureReview({ closureReview: { maxFixRounds:
|
|
167
|
+
test("closureReview: maxFixRounds default 5, <0 -> 0, non-int -> 5", () => {
|
|
168
|
+
assert.equal(resolveClosureReview({}).maxFixRounds, 5);
|
|
169
|
+
assert.equal(resolveClosureReview({ closureReview: { maxFixRounds: 3 } }).maxFixRounds, 3);
|
|
170
170
|
assert.equal(resolveClosureReview({ closureReview: { maxFixRounds: 0 } }).maxFixRounds, 0);
|
|
171
171
|
assert.equal(resolveClosureReview({ closureReview: { maxFixRounds: -3 } }).maxFixRounds, 0);
|
|
172
|
-
assert.equal(resolveClosureReview({ closureReview: { maxFixRounds: 1.5 } }).maxFixRounds,
|
|
172
|
+
assert.equal(resolveClosureReview({ closureReview: { maxFixRounds: 1.5 } }).maxFixRounds, 5);
|
|
173
173
|
});
|
|
174
174
|
|
|
175
175
|
test("flowGuards: defaults + overrides + normalization", () => {
|
|
@@ -89,7 +89,7 @@ export function resolveClosureReview(g: PiGauntlet): ClosureReviewResolved {
|
|
|
89
89
|
const model = nonEmptyString(cr?.model) ? cr!.model.trim() : undefined;
|
|
90
90
|
const enforce = cr?.enforce !== false;
|
|
91
91
|
const raw = cr?.maxFixRounds;
|
|
92
|
-
const maxFixRounds = typeof raw === "number" && Number.isInteger(raw) ? (raw < 0 ? 0 : raw) :
|
|
92
|
+
const maxFixRounds = typeof raw === "number" && Number.isInteger(raw) ? (raw < 0 ? 0 : raw) : 5;
|
|
93
93
|
return { model, enforce, maxFixRounds };
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -1549,16 +1549,16 @@ const okWave = (id: string) => waveResult(id, [{ agent: "implementer", exitCode:
|
|
|
1549
1549
|
const guardedHarness = (settings: unknown, extra: unknown[] = []) =>
|
|
1550
1550
|
harness({ cwd: tempCwd(settings), branch: verifyBranch([subagentResult(["conformance-reviewer"]), ...extra]) });
|
|
1551
1551
|
|
|
1552
|
-
test("cap guard: default
|
|
1552
|
+
test("cap guard: default 5 waves pass, the sixth is blocked with the escalation text", async () => {
|
|
1553
1553
|
const h = guardedHarness({ piGauntlet: { closureReview: { enforce: true } } });
|
|
1554
1554
|
await h.emit("session_start");
|
|
1555
|
-
for (let i = 1; i <=
|
|
1555
|
+
for (let i = 1; i <= 5; i++) {
|
|
1556
1556
|
assert.equal(await firstCallResult(h, `c${i}`, implementerWave(2)), undefined, `round ${i} passes`);
|
|
1557
1557
|
await h.emitEvent("tool_result", okWave(`c${i}`));
|
|
1558
1558
|
}
|
|
1559
|
-
const blocked = await firstCallResult(h, "
|
|
1559
|
+
const blocked = await firstCallResult(h, "c6", implementerWave(1));
|
|
1560
1560
|
assert.equal(blocked?.block, true);
|
|
1561
|
-
assert.match(blocked?.reason ?? "", /
|
|
1561
|
+
assert.match(blocked?.reason ?? "", /5 fix round\(s\) used against a cap of 5 \(granted rounds included\); escalate to the human/);
|
|
1562
1562
|
});
|
|
1563
1563
|
|
|
1564
1564
|
test("cap guard: maxFixRounds 0 blocks the first wave; 1 blocks the second; a chain implementer is cap-checked and counted", async () => {
|
|
@@ -1730,7 +1730,7 @@ const exhaust = async (h: ReturnType<typeof harness>, rounds: number, prefix = "
|
|
|
1730
1730
|
};
|
|
1731
1731
|
|
|
1732
1732
|
test("grant: funds extra waves and rejects another grant while credits remain", async () => {
|
|
1733
|
-
const h = guardedHarness({ piGauntlet: { closureReview: { enforce: true } } });
|
|
1733
|
+
const h = guardedHarness({ piGauntlet: { closureReview: { enforce: true, maxFixRounds: 3 } } });
|
|
1734
1734
|
await h.emit("session_start");
|
|
1735
1735
|
await exhaust(h, 3);
|
|
1736
1736
|
const res = await grant(h, "g1", { rounds: 2, reason: "I'm approving 2 more rounds" });
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ argument-hint: "<pr> [issue-ref] [--rebase [base]] | <pr> <pr>... [--rebase [bas
|
|
|
6
6
|
|
|
7
7
|
# gatekeep-pr
|
|
8
8
|
|
|
9
|
-
Verify, don't trust: a PR description is a claim, and over-claimed coverage, hallucinated references, and "tests pass" that never ran are the normal case on generated code. Judge the mechanism only - whether code, tests, and docs at the assessed head make each promised behavior possible - and leave what can be seen only after merge (a staging run, production data, an external system) to `/skill:check-delivery`.
|
|
9
|
+
Verify, don't trust: a PR description is a claim, and over-claimed coverage, hallucinated references, and "tests pass" that never ran are the normal case on generated code. Judge the mechanism only - whether code, tests, and docs at the assessed head make each promised behavior possible - and leave what can be seen only after merge (a staging run, production data, an external system) to `/skill:check-delivery`. Persist one course through `reference/post-selection-loop.md`'s run record; keep final merge/approval and posts separately selected. Before the initial pick, permit only provisioning and the explicit `--rebase` sync; steps 1-5 read, verify and draft. Execute later writes under a set course, terminal pick or the sync's `--rebase` authority. A menu pick authorizes every command that pick needs: run them yourself, never print a command for the human to paste. Running the verification command executes PR code with your ambient credentials and no sandbox - gate only PRs you are willing to execute.
|
|
10
10
|
|
|
11
11
|
## Arguments
|
|
12
12
|
|
|
@@ -28,8 +28,8 @@ Verify (step 3) and Review (step 4) each run as a fresh helper with `cwd` the PR
|
|
|
28
28
|
| 3 Verify | `verification-brief.md` Section B | evidence record (CI-first) and one disposition per material claim | `verify` |
|
|
29
29
|
| 4 Review | `verification-brief.md` Section C | reviewer findings and the internal comment ledger | `review` |
|
|
30
30
|
| 5 Integrate + report | `reference/post-selection-loop.md` `### Pre-menu refresh`, then `reference/findings.md`, then `reference/report.md` | AC outcomes, blockers, nits, drafted payloads, the rendered report | `report` |
|
|
31
|
-
| 6 Menu | `reference/decision-menu.md` |
|
|
32
|
-
| 7 Loop | `reference/post-selection-loop.md` (
|
|
31
|
+
| 6 Menu | `reference/decision-menu.md`, then `reference/menus.md` | initial for a repairable or pending-only not-ready first report, obstacle for undispositionable BLOCKED even with course unset, final for first-report readiness or a readiness exit, assess terminal after assess, obstacle at obstacle/cap, or the matching terminal/contract-exception template; exactly one `[recommended]` | `menu` |
|
|
32
|
+
| 7 Loop | `reference/post-selection-loop.md` (delivery continues in `reference/fix-wave.md`) | the fixed delivery body until readiness, obstacle or cap; terminal picks and contract decisions through `reference/menus.md`; re-entry until stop | (re-opens the re-entered stage) |
|
|
33
33
|
|
|
34
34
|
Re-entry: a fix wave's own push re-enters step 4 through the own-push sequence (`reference/fix-wave.md` `## Evidence after push`, then `reference/post-selection-loop.md` `### Re-render`); any other head move re-enters step 3; an unchanged head re-enters step 5 through `reference/post-selection-loop.md` `### Pre-menu refresh`. A loop re-entry never re-runs step 2b; the sync runs once per invocation.
|
|
35
35
|
|
|
@@ -39,7 +39,7 @@ Mint and initialize the continuation record before progress initialization (`ref
|
|
|
39
39
|
|
|
40
40
|
## Harness notes
|
|
41
41
|
|
|
42
|
-
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
|
|
42
|
+
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 no-tool accounting fallback is `2 x 5`. 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. For description recheck on pi, use the fresh worker duty in Section B with mode `description recheck`; on Claude Code, use Section B's separate recheck duty as the native helper prompt. Both tasks supply actual body/head, affected claim IDs, existing evidence, and the affected-only disposition/closure contract, never the CI-emitting claim-only contract. 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`).
|
|
43
43
|
|
|
44
44
|
## Wording rules
|
|
45
45
|
|
|
@@ -53,23 +53,23 @@ These bind every rendered report, menu, and external payload (review bodies, rep
|
|
|
53
53
|
6. Omit empty sections.
|
|
54
54
|
7. `Delivers` is always present and names which spec rows this PR covers, which are `deferred per spec`, `deviates per spec`, or `elsewhere per spec`, and which observable half is checked after merge.
|
|
55
55
|
8. The verdict is one line in the fixed form.
|
|
56
|
-
9. Menu rows start with a verb
|
|
56
|
+
9. Menu rows start with a verb selected numerically, carry one clause of consequence, and exactly one row is `[recommended]`; typed evidence/reply/check commands live in the hint line only.
|
|
57
57
|
10. ASCII only, American English, no hedges on checked facts, no intensifiers.
|
|
58
58
|
|
|
59
59
|
## Red flags - STOP
|
|
60
60
|
|
|
61
|
-
- A mutation without
|
|
61
|
+
- A mutation without a set course, a terminal pick, or the sync's `--rebase` authority - owner: `reference/post-selection-loop.md` `### Continuation record`.
|
|
62
62
|
- 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`.
|
|
63
63
|
- Blocking on an AC's observation half, or on a PR-body claim the gate cannot check - owner: `reference/findings.md` `## AC outcomes`.
|
|
64
64
|
- Judging a ticket row the spec does not carry, or treating a spec `deferred: <ref>` row as a gap - owner: `reference/findings.md` `## AC outcomes`.
|
|
65
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`.
|
|
66
66
|
- Raw command output, drafted payloads except the exact description consent preview, or internal IDs in the rendered report - owner: `reference/report.md`.
|
|
67
|
-
-
|
|
68
|
-
- `cleanup` rendered on
|
|
69
|
-
- A CI
|
|
70
|
-
- The orchestrator resolving a merge conflict itself, or editing or committing a tracked file during a fix wave - owner: `reference/fix-wave.md` `##
|
|
67
|
+
- A menu rendered under deliver before an exit condition, other than the contract-exception template; a menu not rendered from `reference/menus.md`, two recommendations, stop not last or no show evidence hint - owner: `reference/menus.md`.
|
|
68
|
+
- `cleanup` rendered on a non-terminal template, or cleanup steps 2-4 run on non-final stop - owner: `reference/cleanup.md` `## Non-final stop`.
|
|
69
|
+
- A CI watch outside D3 step 4 or before the first report, or a second push inside one iteration - owner: `reference/post-selection-loop.md` `### Next action`.
|
|
70
|
+
- The orchestrator resolving a merge conflict itself, or editing or committing a tracked file during a fix wave - owner: `reference/fix-wave.md` `## Sync` and `## Wave`.
|
|
71
71
|
- A helper's duty run in the orchestrator's own context - owner: `## Harness notes`.
|
|
72
|
-
- Approving your own PR - owner: `reference/decision-menu.md` `##
|
|
72
|
+
- Approving your own PR - owner: `reference/decision-menu.md` `## Availability`.
|
|
73
73
|
- Merging around an undispositioned failing check or a binding pending check, or a `BLOCKED`/`UNKNOWN`/`unreadable` merge state - owner: `reference/findings.md` `## Dispositions`.
|
|
74
74
|
- A menu rendered without the pre-menu refresh - owner: `reference/post-selection-loop.md` `### Pre-menu refresh`.
|
|
75
75
|
- Reading `mergeStateStatus` from anything but a `gh pr view` invocation, attributing a `BLOCKED` to a named rule, or treating a non-`BLOCKED` value as a merge verdict - owner: `verification-brief.md` Section A.
|
|
@@ -68,9 +68,9 @@ Record the result in the digest:
|
|
|
68
68
|
- scope: { source: spec | pr, path, rows[ { n, text, disposition, ref } ], design: <the spec's ## Design section text>, cross_check: matched | differs: <rows> | not run: <reason> }
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
`rows` are the spec's `## Acceptance criteria` rows verbatim with their disposition token (`../../brainstorming/reference/ticket-acceptance.md`); a section whose body is a `none - <reason>` line gives empty `rows`. `ref` is the text after `deferred:` when it contains a tracker ref (`#N`, `owner/repo#N`, `[A-Z][A-Z0-9]+-\d+`), a repo-relative path under a spec dir, or an `http(s)://` URL; otherwise `ref` is empty - a prose destination (`deferred: follow-up ticket "..."`) is empty. For an `elsewhere: <repo>` row, `ref` is `<repo>`. `design` is the whole `## Design` section: the spec's own requirements, the chosen readings of ambiguous rows, and the clauses a `deviates:` reason adopts all live there, so the judged contract is `rows` plus `design`. With `source: pr`, keep `rows` and `design` empty and retain the original `{ title, body }` as `original_intent` in the scope
|
|
71
|
+
`rows` are the spec's `## Acceptance criteria` rows verbatim with their disposition token (`../../brainstorming/reference/ticket-acceptance.md`); a section whose body is a `none - <reason>` line gives empty `rows`. `ref` is the text after `deferred:` when it contains a tracker ref (`#N`, `owner/repo#N`, `[A-Z][A-Z0-9]+-\d+`), a repo-relative path under a spec dir, or an `http(s)://` URL; otherwise `ref` is empty - a prose destination (`deferred: follow-up ticket "..."`) is empty. For an `elsewhere: <repo>` row, `ref` is `<repo>`. `design` is the whole `## Design` section: the spec's own requirements, the chosen readings of ambiguous rows, and the clauses a `deviates:` reason adopts all live there, so the judged contract is `rows` plus `design`. With `source: pr`, keep `rows` and `design` empty and retain the original `{ title, body }` as `original_intent` in the stored scope baseline; judge and render that intent after description edits. Never replace it with a shortened body to clear a mechanism gap. Settle a genuine scope change through the contract decision in `post-selection-loop.md`.
|
|
72
72
|
|
|
73
|
-
Cross-check, run when `source: spec` and `rows` is non-empty: compare `issue.acceptance_criteria[]` to `rows[].text`; equal -> `matched`; a differing or missing ticket row -> `differs: <row numbers>`; `issue: null` -> `not run: <issue_note>`. The cross-check is informational: it renders one `show evidence` line (`cross-check: differs on rows 2, 4 - the spec governs here; /skill:check-delivery judges the ticket`) and never a blocker. With `cross_check: differs` or `cross_check: not run` the judgment is unchanged; `post coverage to ticket` is not offered while `issue` is null or `source` is `pr`. The gate never judges a ticket row the spec does not carry.
|
|
73
|
+
Cross-check, run when `source: spec` and `rows` is non-empty: compare `issue.acceptance_criteria[]` to `rows[].text`; equal -> `matched`; a differing or missing ticket row -> `differs: <row numbers>`; `issue: null` -> `not run: <issue_note>`. The cross-check is informational: it renders one `show evidence` line (`cross-check: differs on rows 2, 4 - the spec governs here; /skill:check-delivery judges the ticket`) and never a blocker. With `cross_check: differs` or `cross_check: not run` the judgment is unchanged; `post coverage to ticket` is not offered while `issue` is null or `source` is `pr`. The gate never judges a ticket row the spec does not carry. End a differing-ticket cross-check line immediately after `/skill:check-delivery judges the ticket`; append no reconciliation of the ticket's extra wording against Design clauses.
|
|
74
74
|
|
|
75
75
|
Re-run this selection on every head move before step 3 or step 4 runs (`post-selection-loop.md`), so the spec on the new head is the contract for that head.
|
|
76
76
|
|
|
@@ -24,17 +24,17 @@ With rebase requested, read the viewer via `gh api user --jq .login`. After fetc
|
|
|
24
24
|
|
|
25
25
|
Create the OS-temp roster with `mktemp "${TMPDIR:-/tmp}/gatekeep-roster.XXXXXX"`, never in a worktree. Store per PR number, worktree, record path, report path, status (`pending`, `report`, `conflicting`, `failed`, `stopped`, `retired`), reason, and queued pick. Pre-mint record/report paths outside worktrees.
|
|
26
26
|
|
|
27
|
-
Initialize each existing-format continuation record once: `consent.target` (lowercase base owner/repo, PR number, gathered baseRefName), PR identity, worktree,
|
|
27
|
+
Initialize each existing-format continuation record once: `consent.target` (lowercase base owner/repo, PR number, gathered baseRefName), PR identity, worktree, `course` null, `repair.rounds_used` and `repair.syncs_used` zero, `approved_head` null, `cleanup_done` false. Use the continuation-record schema; initialize `repair.rounds_cap` and `repair.syncs_cap` to `2 x` the resolved `closureReview.maxFixRounds` under D4 (`fix-wave.md` `## Wave`). Only the first course pick may resolve its initial D4 allowance; re-dispatch never reinitializes usage or caps. The parent owns only initialization and direct queued picks; the child owns all assessment and execution fields, findings, digest, evidence, repair state, approved head, payloads, and comment ledger. Carry confirmations and answers in task text only; never overwrite course, counters, caps, deadline, ledger, or helper paths on refresh or re-dispatch. Validate supplied records using the continuation-record identity rules, never reset them.
|
|
28
28
|
|
|
29
29
|
## Foreground dispatch
|
|
30
30
|
|
|
31
31
|
Dispatch one foreground fresh `subagent` tasks batch, one `pr-gatekeeper` task per provisioned PR. Use `async: false`, `context: "fresh"`, each PR's worktree as cwd, and `skill: "gatekeep-pr"` for explicit injection. Use per-task `acceptance: { level: "none", reason: "the gatekeep child result line is the handoff contract" }`; the persona's completionGuard pin is separate. Let cohort concurrency bound running tasks, not alter membership.
|
|
32
32
|
|
|
33
|
-
Supply mode `
|
|
33
|
+
Supply mode `report` or `execute`, PR identity, rebase flag/base, confirmation, optional answer, record/report paths, queued pick for execute, and this statement:
|
|
34
34
|
|
|
35
35
|
`worktree provisioned at <path> on <headRefOid>; fetch and gitignore done - skip the fetch, run no worktree add, verify HEAD`
|
|
36
36
|
|
|
37
|
-
Supply the skill and this batch contract explicitly; do not depend on child discovery. Derive the installed procedure root from the selected skill's absolute `filePath` metadata (the path returned by the runtime's skill resolution), taking the directory containing `SKILL.md`. Supply `procedure root: <absolute installed skill directory>` in every
|
|
37
|
+
Supply the skill and this batch contract explicitly; do not depend on child discovery. Derive the installed procedure root from the selected skill's absolute `filePath` metadata (the path returned by the runtime's skill resolution), taking the directory containing `SKILL.md`. Supply `procedure root: <absolute installed skill directory>` in every report and execute task. Resolve all named procedure dependencies relative to this root, never the PR worktree or cwd; this includes sibling briefs and baseline files as well as files under `reference/`. Report mode runs steps 1-5 with sequential fresh Verify then Review in the supplied worktree, never separate helper worktrees. Preserve trusted configuration, digest/head binding, and PR-local tracking. Delegate tracked source edits and commits to implementer. Execute refreshes first, then runs step 7 using the course rules; under `deliver` run through a D3 readiness, obstacle, or cap exit and return `REPORT`. Refresh before any execute write: if HEAD moved or the row is unavailable, return a fresh report instead of doing the stale action. The course, `repair.rounds_cap`/`repair.syncs_cap`, usage, deadlines, and no-progress decisions stay PR-local; never borrow sibling consent. Parallel waits are independent bounded watches.
|
|
38
38
|
|
|
39
39
|
Write only the supplied report, never a second record/report. End with exactly one closing result line:
|
|
40
40
|
|
|
@@ -63,7 +63,7 @@ Apply only in multi-PR mode at existing decision points. Leave single-PR owners
|
|
|
63
63
|
|
|
64
64
|
Use only these pre-report asks; stop at the ask without further writes:
|
|
65
65
|
|
|
66
|
-
| Ask | Closing line | Re-dispatched
|
|
66
|
+
| Ask | Closing line | Re-dispatched report task answer |
|
|
67
67
|
|---|---|---|
|
|
68
68
|
| Verification configuration rung 4 | `STOPPED - verification command - <subproject dirs seen>` | `verification command: <command>` |
|
|
69
69
|
| Multiple spec candidates | `STOPPED - spec candidate - <paths>` | `spec: <path>` |
|
|
@@ -72,20 +72,22 @@ Re-assess from steps 1-5 with the same record; substitute the answer at that ask
|
|
|
72
72
|
|
|
73
73
|
## Summary and menu round
|
|
74
74
|
|
|
75
|
-
The parent alone renders menus,
|
|
75
|
+
The parent alone renders menus, gathers answers, and runs cleanup/stop consequences. Render the summary once, after the children's closing lines and before the first menu, without a standalone report block before it. Never rewrite, correct, or reconcile a child's report: preserve spelling, counts, verdict wording, missing lines, and off-contract lines, including under show evidence; add no corrections block or inserted report lines. Render summary rows in roster order:
|
|
76
76
|
|
|
77
77
|
`#<N> <verdict line> | evidence: <ci | local | pending | unknown> | blockers: <count | unknown>`
|
|
78
78
|
|
|
79
|
-
Use
|
|
79
|
+
Use the child's supplied Verdict text unchanged, `conflicting - <sync record>`, `failed - <reason>`, or `stopped - <ask>`. Express a pending binding check only in that PR's queued menu: describe watching the check in its deliver row and print `Not offered: merge (<check> pending)`; leave the supplied verdict unchanged. Evidence is the record's resolved source; use unknown before Verify. Count open P# findings; use unknown without review. Follow with `Shared files:` and every changed path shared by two or more PRs, `<path> - #<N>, #<M>`. Include `#<N> - files unknown` for missing gathered files; print `none` when no path overlaps. Never schedule merges or choose landing order.
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
Print each supplied child report file verbatim in roster order, each under its own `#<N>` heading and directly above that PR's queued menu from `menus.md`; add no synthetic `Verdict:` restatement at the menu turn. Render each child's template from `menus.md`: reports with their template rows, conflicts through `menus.md` `## batch summary`'s recovered CONFLICTING template, failures with retry/evidence/stop, STOPPED with the recorded ask and stop last. For retry use exactly `retry - re-dispatch this PR's assessment with the same record`, recommended only after clean recovery and no rebase. Resolve the actual gitdir with git (a worktree .git can be a file); require empty `git -C <worktree> status --porcelain` and neither rebase-merge nor rebase-apply there. Otherwise omit the row and print `Not offered: retry (worktree dirty or mid-rebase: <state>)`. Substitute `<observed state>` with the concrete observation - the porcelain line(s) or the rebase directory name - one line per snapshot, never the placeholder text.
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
Queue every parent-owned terminal pick (post coverage to PR or ticket, merge, cleanup, wait, stop) until every roster entry's pick for the round is collected. Print `Queued until all picks are collected (<roster summary>)` before the execution block for each queued pick, including a single-entry roster. Summarize each roster entry's collected pick or pending status inside `<roster summary>`.
|
|
84
|
+
|
|
85
|
+
Show evidence only in-place from record/helper paths, then re-ask. Queue all other picks and answers until the final menu. Record direct picks as the child's `course` or terminal verb; do not turn answers into grants. For terminal overlays, the parent handles `post coverage to ticket`, `post coverage to PR`, and `show evidence` from the primary checkout, even after cleanup removes the PR worktree; assign the parent the `merge queued` overlay's `wait` pick after all picks are collected: re-run `post-selection-loop.md` `### Merge course`'s post-merge read (`gh pr view <N> --json state,mergeCommit`), on `MERGED` persist `pr.state` and `pr.merge_commit` and render coverage/menu under `own merge`, otherwise re-render `merge queued` (`wait`/`show evidence`/`stop`), with no helper, child, or CI watch. Follow `decision-menu.md`'s terminal rows and `post-selection-loop.md` `### Merge course` and `### Output done-check`: execute the direct selected post only, with no compare-and-swap or helper dispatch; preserve successful-post suppression, failed-post rows, and terminal re-rendering. Queue terminal posts with all other external writes until every PR's pick is collected; keep evidence in-place from the retained record/helper paths. Exclude terminal picks from child dispatch; never use a removed worktree as cwd. Next batch mixes retry/answered-ask `report` with ordinary nonterminal `execute` tasks, always same record.
|
|
84
86
|
|
|
85
87
|
## Between batches and retirement
|
|
86
88
|
|
|
87
89
|
Run parent cleanup/stop only after all children finish, never on a live child's tree. For cleanup apply cleanup guards and ordered overrides. Keep the PR and post rows in the roster, set cleanup_done, and re-render with cleanup gone. Cleanup does not retire.
|
|
88
90
|
|
|
89
|
-
For
|
|
91
|
+
For non-final stop run `cleanup.md` `## Non-final stop` after all children finish. Under terminal overlays leave checkout as found and print leftover path/branch. Delete only that PR's record, helper outputs, and log_path files under existing teardown rules, then retire it. Await all next-batch results and re-render every nonretired PR; repeat until all retire.
|
|
90
92
|
|
|
91
93
|
Initialize parent tracker tasks `pr-<N> state: <record path>`; mark in_progress on dispatch, complete on retirement, failed on unretried failure. Child trackers are session-local stages, not parent roster tasks. Use a plain checklist if plan_tracker is unavailable.
|
|
@@ -11,9 +11,27 @@ Run the steps in order; a failing step prints its first error line and the loop
|
|
|
11
11
|
3. **Base.** Landed, the primary is on `<baseRefName>`, and its tree is tracked-clean -> `git fetch origin` then `git pull --ff-only`; print `base: synced <sha>` when `git merge-base --is-ancestor <pr.merge_commit> HEAD` succeeds, else `base: failed - merge commit not on local <baseRefName>`. Primary on another branch -> `base: skipped - primary on <branch>`; dirty -> `base: skipped - uncommitted changes`; not landed -> `base: skipped - PR not merged`. Never switch the primary's branch.
|
|
12
12
|
4. **Override steps**, one per `## cleanup` item in list order. An item is `<label>: <text>`; any other shape -> `skipped - malformed item: <text>`.
|
|
13
13
|
- Landed-change guard first: not landed and the text contains `<merge-sha>` or `/skill:release` -> `skipped - needs a landed change`. The guard decides before any placeholder is read, so an item it skips never prints a `no <name> resolved` line, whatever its placeholders hold. A `/skill:release` item when step 3 did not print `synced` -> `skipped - base not synced`.
|
|
14
|
-
- Substitute: `<issue>` is `issue.ref` with a leading `#` stripped for the bare `#N` form (other forms verbatim); `<pr>` is `pr.url`; `<merge-sha>` is `pr.merge_commit`. A null
|
|
14
|
+
- Substitute: `<issue>` is `issue.ref` with a leading `#` stripped for the bare `#N` form (other forms verbatim); `<pr>` is `pr.url`; `<merge-sha>` is `pr.merge_commit`. A null `<merge-sha>` -> `skipped - needs a landed change` in both the cleanup row text and the result block. Other null values -> `skipped - no <name> resolved`: `skipped - no issue resolved`, `skipped - no pr resolved`. Any other `<...>` token -> `skipped - malformed item: <text>`. Insert each value as literal argument text: inside an existing quoted span verbatim; outside, single-quoted as one shell word.
|
|
15
15
|
- Text starting with `/skill:` -> invoke that skill with the rest of the text as its arguments, gates intact: the step's line is `<label>: invoked /skill:<name> - <where it paused or what it returned>` (for example `release: invoked /skill:release - paused at its level pick`) - never a `skipped` or `failed` line about skill availability, because the gate itself runs as a skill and every `/skill:` item is invokable; `/skill:release` with no level runs the skill's propose-then-pick gate; a level written in the item (`patch`, `minor`, `major`) authorizes the release without a pause. Any other text -> run it as a non-interactive shell command with cwd the primary, bounded by the resolved `timeout minutes` (default 15) through the Safety contract ladder of `../verification-brief.md` Section B; a timeout prints `failed - timed out after <n> minutes` and later steps still run.
|
|
16
16
|
|
|
17
17
|
With no overrides file or no `## cleanup` section, run steps 1-3 only. On a closed-unmerged PR steps 2 and 3 print `PR not merged` and the landed-change guard skips release and `<merge-sha>` items before substitution. Do not delete the consent record, helper output files, or temporary body files here - `show evidence` and the post rows stay available after `cleanup`; `post-selection-loop.md` `### Teardown` deletes them on terminal exit.
|
|
18
18
|
|
|
19
19
|
Consequence clause for the menu row (`decision-menu.md`): `remove <worktree.path> (untracked files there are deleted) and branch <worktree.branch>, pull <baseRefName>, then <label> (<resolved command>), ...` with every placeholder already substituted, so the tracker comment text and the release invocation are confirmed by the one pick and `/skill:release` keeps its own level gate; steps the guards would skip render as `<label> (skipped - <reason>)`; with no `## cleanup` section the clause is `tear down the gate's checkout and local branch (untracked files there are deleted)`.
|
|
20
|
+
|
|
21
|
+
## Non-final stop
|
|
22
|
+
|
|
23
|
+
Enter here for `stop` on the initial, assess-terminal, obstacle, or contract-exception menu. On a `rebased locally` head, run `git -C <worktree.path> reset --hard <pre_head>` first; assert `HEAD == <pre_head>` and a clean tree. Stop on a failed assertion.
|
|
24
|
+
|
|
25
|
+
Reuse step 1's primary and keyed ownership computation: resolve `home:` and the `<name>` pattern against `$PRIMARY`, and pass both the pattern and `worktree.path` through `realpath`. Keep the primary even when clean; print `worktree: kept - primary at <path>`.
|
|
26
|
+
|
|
27
|
+
For a path that is not the primary, apply these checks in order; created and reused trees are both eligible:
|
|
28
|
+
|
|
29
|
+
| Check | On failure |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `git -C <worktree.path> status --porcelain --untracked-files=no` is empty | Keep: `uncommitted changes` |
|
|
32
|
+
| `git -C <worktree.path> merge-base --is-ancestor HEAD <last pushed head>` succeeds; use `repair.wave.pushed_head` when set, else the assessed `headRefOid` | Keep: `unpushed commits <shas>`; list commits from `<last pushed head>..HEAD` |
|
|
33
|
+
| Under a keyed `## using-git-worktrees` contract, step 1's ownership match is unique | Keep: `outside configured home` for no match, or `ambiguous home match` for multiple matches |
|
|
34
|
+
|
|
35
|
+
For a failed check print `worktree: kept - <reason> at <path>` and leave the tree. For `unpushed commits <shas>` also state that discarding those commits is a separate explicit human decision - never a consequence of `stop` - and that the run neither offers nor performs it. Untracked residue alone does not block removal and is deleted. For an eligible tree run recorded `worktree.destroy` from the primary; a failed custom destroy keeps the tree, prints `worktree: failed - <first error line>, left at <path>`, and must never fall back to forced removal. With no recorded destroy command, run `git -C <primary> worktree remove --force <worktree.path>`. After successful destruction run `git -C <primary> worktree prune` and print `worktree: done`. Touch nothing under the removed path.
|
|
36
|
+
|
|
37
|
+
Never run terminal steps 2-4 here: no branch deletion, base pull, or `## cleanup` overrides. Delegate record, helper-output, and temporary body-file deletion to `post-selection-loop.md` `### Teardown`.
|