pi-gauntlet 7.40.1 → 7.41.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +14 -2
  3. package/agents/code-reviewer.md +2 -0
  4. package/agents/implementer.md +2 -0
  5. package/agents/loop-implementer.md +10 -0
  6. package/agents/plan-writer.md +1 -1
  7. package/agents/spec-reviewer.md +2 -0
  8. package/bin/gauntlet-telemetry-seal.mjs +18 -3
  9. package/bin/gauntlet-telemetry-seal.test.mjs +30 -0
  10. package/extensions/lib/plan-check.test.ts +116 -0
  11. package/extensions/lib/plan-check.ts +149 -20
  12. package/extensions/lib/telemetry-record.test.ts +11 -0
  13. package/extensions/lib/telemetry-record.ts +7 -0
  14. package/extensions/phase-tracker.test.ts +201 -3
  15. package/extensions/phase-tracker.ts +77 -9
  16. package/extensions/verify-before-ship.test.ts +240 -2
  17. package/extensions/verify-before-ship.ts +180 -25
  18. package/package.json +2 -2
  19. package/skills/brainstorming/SKILL.md +1 -1
  20. package/skills/brainstorming/gatherer.md +14 -1
  21. package/skills/brainstorming/reference/amendment-surface.md +13 -1
  22. package/skills/brainstorming/reference/legs.md +187 -0
  23. package/skills/brainstorming/reference/spec-finalization.md +14 -0
  24. package/skills/brainstorming/reference/ticket-acceptance.md +2 -2
  25. package/skills/finishing-a-development-branch/SKILL.md +2 -2
  26. package/skills/finishing-a-development-branch/reference/legs.md +76 -0
  27. package/skills/gauntlet-handoff/SKILL.md +2 -2
  28. package/skills/gauntlet-resume/SKILL.md +3 -2
  29. package/skills/gauntlet-resume/reference/brief-contract.md +37 -3
  30. package/skills/gauntlet-resume/reference/reconstruction.md +4 -0
  31. package/skills/gauntlet-resume/reference/seed.md +2 -0
  32. package/skills/loop-implementation/SKILL.md +89 -2
  33. package/skills/subagent-driven-development/SKILL.md +10 -0
  34. package/skills/using-git-worktrees/SKILL.md +58 -21
  35. package/skills/writing-plans/SKILL.md +8 -0
  36. package/skills/writing-plans/reference/plan-contract.md +9 -1
@@ -18,6 +18,8 @@ Run this driver in the main session. Carry the absolute implementation worktree
18
18
  | Otherwise overrides has `## verification-before-completion` | Use its exactly one backticked command; zero or several is a configuration error. |
19
19
  | Neither section supplies a command | Discover the project's test entrypoint from its actual manifest/build instructions; print discovery as source. Stop on an unresolved or ambiguous entrypoint. |
20
20
 
21
+ For more than one `### Leg:` block, apply coordinated Entry below instead of the home-only skeleton and cap calculation. Keep legacy and single-member ledger bytes unchanged, with no coordinated metadata.
22
+
21
23
  3. Call `phase_tracker({ action: "skip", phase: "plan", reason: "loop engine" })`.
22
24
  4. Require a clean named-branch implementation worktree. Write `<resolved plan dir>/<spec stem>-ledger.md`. Use this grammar with literal values, not placeholders:
23
25
 
@@ -41,6 +43,18 @@ Use the actual repo-relative spec path alone in the Spec header. Read base with
41
43
 
42
44
  For resume, use the admitted ledger, its base and verification command, and durable iterations and cap instead of overwriting it. Skip Entry step 1 for an admitted ledger; settings apply only to fresh seeding. Render rows, cap and decision history; stop on a dirty tree before dispatch.
43
45
 
46
+ ## Coordinated Entry and resume
47
+
48
+ Read `## Legs` with [the scope grammar](../brainstorming/reference/legs.md#scope-grammar-and-validation) before skeleton commit or dispatch. Require exactly one `scope:` per block: `scope: AC 1, AC 3` or `scope: none`. Reject missing/duplicate scope lines, duplicate indices within a list, malformed tokens and out-of-range original indices through the amendment stop. Include cut original AC indices; never infer ownership from a `design:` mention or path. Default only indices absent from every valid list to home; repeat shared indices in every owner's list.
49
+
50
+ Retain the home skeleton's `**Spec:**`, `verification:`, `iterations:`, `cap:` and `## Decisions` fields and its `plan_check`/commit admission. Read each member's verification from the home spec's `## Legs` `contract:` `- verification:` line before dispatch; keep the existing home verification header.
51
+
52
+ Assign numeric entries in original AC order, then membership order for shared ACs, independent of scope-list order. Leave spec rows untouched and copy each cut disposition to every expanded entry. Keep the four-column table and add `## Leg mapping` with `row | ac | leg`, one mapping per entry exactly once. Reject duplicate/missing mappings. Keep `base:` as home base; record one actual `base repo-a: <sha>` / `seeded repo-a: no` pair per member, including home. Resolve bases with each member's `git -C`, not home HEAD.
53
+
54
+ Compute initial cap as non-cut entry count times `maxIterationsPerRow` plus one seed dispatch per leg with non-cut entries. One shared home/companion AC and one home-only AC produce three entries, two seeds and cap 14 at default 4; a wholly cut third leg adds no seed. All-cut cap stays 1 and exits without seed. Preserve cap after cuts, reopens, amendments, funding and recovery; only an explicit cap grant raises it.
55
+
56
+ Recover pending companion work before generic dirty checks and before any new dispatch. Read the durable home ledger's `pending:` tuple and consumed iteration, selected companion HEAD and saved pre-dispatch state. Reconstruct the pre-dispatch ledger from home `HEAD:<ledger path>` on resume and admit only the expected on-disk iterations bump and tuple against it. Admit only the known iterations bump and exact pending tuple as pre-dispatch dirt; stop on other dirt. Reapply companion return validation below only when exactly one new commit after the recorded HEAD matches attribution and result. Apply the home-ledger commit without another attempt or iteration bump, clear pending and retain prior decisions. Stop preserving state on zero commits, unrelated movement, multiple commits or attribution/result mismatch. Never rerun that row or seed. After a successful parent commit no pending tuple remains, so a subsequent resume cannot duplicate the update.
57
+
44
58
  ## Before each dispatch
45
59
 
46
60
  Read the ledger and require its `cap:` header before the ordered table, including before rule 1. On a missing header stop with `ledger has no cap: line - seeded under the old loopEngine contract`, even for a completed ledger; use the legacy grant below, never derive cap from settings. Count empty child commits and validation misses only after the latest `loop: retry row <n>` commit. Funding alone resets neither window. Preserve every row's attempts and the total iteration cap. Apply this ordered table, starting here after each return or human decision:
@@ -57,9 +71,13 @@ Set row allowance to 3 + funded N, increased to the latest `row <n> retry` decis
57
71
 
58
72
  Select `seed` until a validated `loop: seed evidence` commit with its `row 0 done` decision and evidence on every non-cut row survives on the branch, independently of `iterations`; otherwise select the still-unresolved human retry target, then the still-failed row named by the previous decision, then first failed row, then first open row. Use the latest operational `row <n> ...` decision for targeting and the child's `previous decision:` input, skipping cap grant history while retaining the entire Decisions section. Durable status beats a decision's suggestion. Keep the retry hint only for its target.
59
73
 
74
+ For coordinated targeting, select the next unseeded leg with non-cut entries in membership order before ordinary targeting. Use the existing retry/failed/open priority only after those seeds validate. Skip wholly cut legs; never mark foreign legs seeded. Map each numeric target to its original AC and leg via `## Leg mapping`.
75
+
60
76
  ## Dispatch
61
77
 
62
- Record `HEAD_BEFORE`, copy the ledger text, and take i from the current iterations count. Increment `iterations:` before dispatch in the working tree so the child's commit persists it. Create a unique absolute output path with `mktemp` outside the repository. Dispatch exactly one foreground child in the main implementation worktree, not a nested worktree:
78
+ For coordinated dispatch, capture home and selected leg heads and save a pre-dispatch ledger snapshot for the expected-dirt comparison. Select the leg worktree as `cwd` and git root, its own base, mapped original AC, `Leg:` and combined spec/task `Rules:` as additive task inputs. Read spec and ledger by absolute home paths. Bump iterations exactly once. Before every companion dispatch, including seed, persist the exact tuple such as `pending: 2 | repo-b | abc1234` (or `pending: seed | repo-b | abc1234`) with actual target, repo and pre-dispatch HEAD in the home ledger. Keep that expected bump/tuple durable on disk before generic dirty checks; admit no other dirt. After persisting the bump and pending tuple, bind `SAVED_LEDGER` to the durable home ledger for companion admission, not the pre-dispatch snapshot. Pass consumed `iteration:` and the exact `pending:` as attribution. Leave home iterations on the existing single child commit path, validating only home entries and home seeded state.
79
+
80
+ For home-only dispatch, record `HEAD_BEFORE`, copy the ledger text, and take i from the current iterations count. Increment `iterations:` before dispatch in the working tree so the child's commit persists it. Create a unique absolute output path with `mktemp` outside the repository. Dispatch exactly one foreground child in the main implementation worktree, not a nested worktree:
63
81
 
64
82
  ```text
65
83
  subagent({ agent: "loop-implementer", context: "fresh", async: false,
@@ -75,6 +93,12 @@ Omit the model override; let the persona override or parent inheritance resolve
75
93
 
76
94
  ## Validate the return
77
95
 
96
+ For a companion, validate from its commit, not the reply. Require a clean companion and exactly one commit after pending HEAD; require unchanged home HEAD and no home edits except the expected persisted bump/tuple. Match subject `loop: row <n> <status>` or `loop: seed evidence`, exact pending tuple and consumed `iteration:` in the body. Parse only the resulting four-column target row (all and only that leg's non-cut rows for seed), followed by one bounded decision. Apply the evidence and decision bounds below. Reject a wrong target, foreign entry, header changes, fabricated counts, malformed evidence or extra results. Validate seed counts against actual rows and baseline closures; preserve attempts and set only this leg's seeded value to `yes` after every non-cut entry validates. Keep seed misses a human stop. Apply ordinary repair only to the target, retaining consumed iterations and saved cap.
97
+
98
+ After validation update only that leg's target or seed entries, append the validated decision, record companion SHA in evidence, and remove `pending:` in one home-ledger commit. Preserve every other row, header and prior decision. That home-ledger commit (`loop: record <repo> row <n>`) is a parent commit, not a child `loop: row ...` commit: it never counts toward child-only empty-commit stagnation, and a rejected companion return records the rejection in the same parent commit while preserving the pending tuple and every row. Recover interrupted returns by this same validation, never trusted reply text.
99
+
100
+ For home-only returns, apply the following boundary and table. For coordinated home returns apply it only to home entries/seeded state, preserving foreign entries, mappings and bases.
101
+
78
102
  1. Require empty `git -C "<worktree>" status --porcelain` and exactly 1 from `git -C "<worktree>" rev-list HEAD_BEFORE..HEAD --count`. On dirty/zero/multiple commits stop, preserving changes and commits. Use `Stopped: iteration <i> left the tree dirty` or `Stopped: iteration <i> added <k> commits`. Read the ledger from HEAD only after this boundary passes.
79
103
  2. Validate by dispatched `target`, not iteration number, against the saved copy using this table. Preserve headers including cap and the bumped iterations, row identifiers and prior decisions.
80
104
 
@@ -87,9 +111,68 @@ Require ordinary decision grammar `^row <target> (done|failed) \| .{1,200} \| .{
87
111
 
88
112
  On an ordinary miss restore every non-target row, headers (including the saved cap) and prior decisions from the saved copy, retaining the incremented iterations. Set the target `failed`, attempts = saved attempts +1, evidence = `iteration <i>: <concrete miss>`. Remove malformed appended decisions and commit `loop: repair row <n>`. Set previous decision to `none`; otherwise use the new conforming line. Two consecutive validation misses stop because the child is not following the contract. A conforming return resets the consecutive-miss count; parent repair commits do not count toward child-only empty stagnation.
89
113
 
114
+ ### Companion commit admission
115
+
116
+ Require the unchanged home HEAD and expected-only home dirt boundary above, then run this read-only admission recipe on both immediate returns and pending recovery. Set `CHECKOUT` to the companion root, `PENDING` to the exact persisted tuple including `pending:`, `ITERATION` to the consumed count and `SAVED_LEDGER` to the durable home ledger after the iterations bump and pending write, including on recovery. Use the pre-dispatch snapshot only for the expected-dirt comparison. On failure preserve pending and stop. On success use the emitted object for the single parent ledger commit described above; never treat it as permission for another dispatch.
117
+
118
+ <!-- companion-return-shell -->
119
+ ```bash
120
+ node - "$CHECKOUT" "$PENDING" "$ITERATION" "$SAVED_LEDGER" <<'NODE'
121
+ const fs = require('node:fs');
122
+ const { execFileSync } = require('node:child_process');
123
+ const [root, pending, iteration, savedPath] = process.argv.slice(2);
124
+ const requireThat = (ok, why) => { if (!ok) throw new Error(why); };
125
+ const git = (...args) => execFileSync('git', ['-C', root, ...args], { encoding: 'utf8' }).trim();
126
+ const tuple = /^pending: (seed|\d+) \| (\S+) \| ([a-f0-9]{7,40})$/.exec(pending);
127
+ requireThat(tuple, 'invalid pending');
128
+ const saved = fs.readFileSync(savedPath, 'utf8');
129
+ requireThat(saved.split('\n').filter(line => line.startsWith('pending:')).length === 1 && saved.split('\n').includes(pending) && saved.split('\n').includes(`iterations: ${iteration}`), 'pending already applied or iteration mismatch');
130
+ const [, target, leg, before] = tuple;
131
+ requireThat(git('status', '--porcelain') === '', 'dirty companion');
132
+ requireThat(git('merge-base', '--is-ancestor', before, 'HEAD') === '', 'unrelated HEAD');
133
+ requireThat(git('rev-list', '--count', `${before}..HEAD`) === '1', 'expected one companion commit');
134
+ const commit = git('rev-parse', 'HEAD');
135
+ const message = git('show', '-s', '--format=%B', 'HEAD').split('\n');
136
+ const subject = message.shift();
137
+ requireThat(message.shift() === '', 'missing body separator');
138
+ requireThat(message.shift() === pending, 'pending mismatch');
139
+ requireThat(message.shift() === `iteration: ${iteration}`, 'iteration mismatch');
140
+ const resultLines = message.filter(line => line.trim() !== '');
141
+ const cells = line => line.split(/(?<!\\)\|/).slice(1, -1).map(s => s.trim());
142
+ const table = text => text.split('\n').filter(line => /^\|\s*\d+\s*\|/.test(line)).map(cells);
143
+ const [ledger, mapping] = saved.split('## Leg mapping\n');
144
+ requireThat(mapping, 'missing mapping');
145
+ const map = table(mapping.split('## Decisions')[0]);
146
+ const oldRows = table(ledger);
147
+ requireThat(new Set(map.map(r => r[0])).size === map.length && map.length === oldRows.length && oldRows.every(r => map.some(m => m[0] === r[0])), 'invalid mapping');
148
+ const expected = oldRows.filter(r => map.some(m => m[0] === r[0] && m[2] === leg) && (target === 'seed' ? r[1] !== 'cut' : r[0] === target));
149
+ requireThat(expected.length && expected.every(r => r[1] !== 'cut'), 'foreign or cut target');
150
+ const decision = resultLines.pop();
151
+ const rows = resultLines.map(cells);
152
+ requireThat(rows.length === expected.length && resultLines.every(l => l.startsWith('|') && l.endsWith('|')), 'extra result');
153
+ for (const [i, row] of rows.entries()) {
154
+ const old = expected[i];
155
+ requireThat(row.length === 4 && row[0] === old[0], 'foreign row');
156
+ requireThat((target === 'seed' ? /^(open|done)$/ : /^(done|failed)$/).test(row[1]), 'invalid status');
157
+ requireThat(/^\d+$/.test(row[2]) && Number(row[2]) === Number(old[2]) + (target === 'seed' ? 0 : 1), 'attempt mismatch');
158
+ requireThat(row[3].length > 0 && row[3].split(';').length <= 4 && !/[<>]/.test(row[3]), 'unbounded evidence');
159
+ requireThat(target !== 'seed' || old[1] !== 'done' || row[1] === 'done', 'lost baseline closure');
160
+ }
161
+ if (target === 'seed') {
162
+ requireThat(subject === 'loop: seed evidence', 'seed subject mismatch');
163
+ const d = /^row 0 done \| seeded (\d+) rows, closed (\d+) on baseline \| .{1,200}$/.exec(decision);
164
+ requireThat(d && Number(d[1]) === expected.length && Number(d[2]) === rows.filter(r => r[1] === 'done').length, 'seed counts mismatch');
165
+ } else {
166
+ requireThat(subject === `loop: row ${target} ${rows[0][1]}`, 'row subject mismatch');
167
+ requireThat(new RegExp(`^row ${target} ${rows[0][1]} \\| .{1,200} \\| .{1,200}$`).test(decision), 'invalid decision');
168
+ }
169
+ console.log(JSON.stringify({ sha: commit, rows: resultLines, decision }));
170
+ NODE
171
+ ```
172
+
90
173
  ## Exit verification
91
174
 
92
- Run the ledger's `verification:` command exactly once foreground in the worktree. Failure: human stop `Stopped: verification failed with every row closed`, Problem quoting failing output. Preserve closed rows: no reopening and no automatic rerun. Further work requires a human decision. Success: record verified HEAD and clean status; call `phase_tracker({ action: "complete", phase: "implement" })`, then continue immediately below.
175
+ For coordinated exit, read each leg's verification from the home spec's `## Legs` `contract:` `- verification:` line and run it once foreground in its owning checkout, with repair routed there; retain home flow policy. Require every leg to pass before completing implement. For legacy/single-member exit, run the ledger's `verification:` command exactly once foreground in the worktree. Failure: human stop `Stopped: verification failed with every row closed`, Problem quoting failing output. Preserve closed rows: no reopening and no automatic rerun. Further work requires a human decision. Success: record verified HEAD and clean status; call `phase_tracker({ action: "complete", phase: "implement" })`, then continue immediately below.
93
176
 
94
177
  ## Human stop
95
178
 
@@ -110,12 +193,16 @@ Serialize the bare decision as `cap +50 |` with an empty words cell. A cap grant
110
193
 
111
194
  For dirty/commit-boundary retry, ask the human to authorize discarding uncommitted changes/resetting to last valid commit. Preserve consumed dispatch count: set iterations to restored HEAD's value +1 before the next normal bump. An interrupted dispatch consumes the same count; recover it from disk/branch rather than silently repeating iteration 0.
112
195
 
196
+ For coordinated amendments, reconcile by original AC/leg mapping without resetting attempts, iterations or cap. Re-read explicit scope lists after approved ownership amendments and reconcile expanded entries; a disposition-only cut changes neither original indices nor ownership. Preserve unchanged mapped entries and their histories.
197
+
113
198
  For ledger amendments use its line-1 discriminant: no plan writer, plan_check or task-tracker lifecycle. Approved deviates/deferred/elsewhere becomes `cut` with disposition evidence; requirement-preserving amendments leave rows unchanged; a re-bounded done row becomes `open` with `reopened by amendment`. Preserve attempts, iterations and cap. Commit spec + ledger as `loop: cut row <n>` or `loop: amend`. Option 4 stops for amendments, option 5 auto-applies amends, redraw always stops and deletes the ledger with the plan. Never cut automatically.
114
199
 
115
200
  ## After the loop
116
201
 
117
202
  Read SDD's `After All Tasks Complete`, `../subagent-driven-development/reference/happy-path-run.md`, and `../verification-before-completion/reference/conformance-check.md` IN FULL now with read. Stop on an unreadable reference; do not reconstruct it. Execute this continuation, not the waves engine or a generic verification skill:
118
203
 
204
+ For coordinated runs, route each verification and review below to every leg with its own base, checkout, Rules and owned original ACs under home flow policy. Re-verify and re-review any changed leg; invoke coordinated finish once from home after every leg passes. Keep single-member/legacy paths free of a companion loop, leg file, `legs:` brief or `--leg` argument.
205
+
119
206
  0. Call `phase_tracker({ action: "start", phase: "verify" })`.
120
207
  1. Parent full verification uses the ledger command. Reuse the passing exit run only when HEAD and clean/no-change worktree still match. Commit verification-produced tracked changes. Any repair invalidates that run: rerun the full command successfully before the next gate. No task reopen/update is awaited.
121
208
  2. Dispatch foreground whole-diff `code-reviewer` per `/skill:requesting-code-review`, from ledger base through committed HEAD, with `SCOPED_TEST_COMMANDS: none`. Apply its fix rounds. Rerun full verification after every repair before re-review/next gate. Carry final SHIP Minor lines verbatim as `whole-diff minors:`, or `whole-diff minors: none`.
@@ -118,6 +118,14 @@ Implementer prompt templates must instruct subagents to return one of these stat
118
118
  - Omit `model:` when `closureReview.model` or `specCouncil.chair` is `undefined`; pass an `implModel` string verbatim, and stop with a note when `implModel` is `undefined` (Fix-Loop Rounds).
119
119
  - Apply suffix edits such as `:low` only to a runtime-resolved string, never to a name the skill wrote; never write a provider or model name, tier tasks by cost, or swap a pinned model for a cheaper or stronger one.
120
120
 
121
+ ## Leg routing
122
+
123
+ Read `## Legs` in the home spec and [the leg contract](../brainstorming/reference/legs.md). Admit task `Leg:` through `plan_check`; absent means home. Repair `leg-unknown` in the plan, never membership. Keep the existing flat tracker and task indices.
124
+
125
+ Require leg-homogeneous waves. Split a mixed wave into consecutive single-leg waves preserving dependencies before dispatch. Select its owning worktree before capturing `WAVE_BASE_SHA`; use that root for isolation, dispatch `cwd`, `git -C`, snapshots, integration, scoped tests, in-place repair and wave commit. In the following recipes, substitute that root for every implementation worktree placeholder. Keep authoritative spec reads at the absolute home spec path, including companion SR snapshots.
126
+
127
+ Carry `Leg:` and combined spec/task `Rules:` as additive task context to every implementer, spec-reviewer and code-reviewer, including repairs and re-reviews; require reading those files before acting. Resolve rules from the owning checkout, not home. Keep dispatch and acceptance shapes unchanged.
128
+
121
129
  ## Dispatch
122
130
 
123
131
  For the fan-out + worktree + patch-integration + conflict mechanics, see `dispatching-parallel-agents`.
@@ -326,6 +334,8 @@ Record its review tree per [Fix delta](#fix-delta) before every parent-dispatche
326
334
 
327
335
  ## After All Tasks Complete
328
336
 
337
+ For a coordinated run, execute verification and the following reviews for every leg in its own checkout against its owned original ACs and recorded verification contract, under home flow policy. Keep freshness per leg and route repairs to their owning checkout; repeat invalidated gates there. After every leg passes, finish the coordinated run once from home. Keep legacy and single-member execution free of companion loops, leg files, `legs:` briefs and `--leg` arguments.
338
+
329
339
  0. Call `phase_tracker({ action: "start", phase: "verify" })`. (The `implement` phase was started at execution start and auto-completes from `plan_tracker` once all tasks are done; this flow runs its own verify gate instead of `/skill:verification-before-completion`, so it must mark verify itself.)
330
340
  1. **Parent full verification.** Run the complete plan-header `**Verification:**` command set foreground in the worktree via `(cd "<worktree>" && <command>)`: tests plus every declared lint, type, format, and build check. A failure must be repaired and the full set rerun successfully before the next step. Before dispatching a verification repair, reopen (`in_progress`) every existing plan-task index whose `Files:` ownership includes its touched files; leave unowned cross-cutting repair work in the verification report. Those indices stay `in_progress` through the successful full rerun **and** step 2's whole-diff review accepting the repair — that acceptance is their completion point, not the passing rerun. Commit any verification-produced tracked changes; use the resulting `HEAD_SHA` in the review task and include the commands/results in its existing `DESCRIPTION`.
331
341
  2. **Whole-diff code review.** Only after passing full verification, dispatch one foreground whole-diff `code-reviewer` per `/skill:requesting-code-review` against that committed HEAD, with `SCOPED_TEST_COMMANDS: none`; the reviewer does not repeat the full suite. Act on the verdict per `/skill:requesting-code-review` Fix rounds; carry the final `SHIP` report's Minor finding lines verbatim into the closure block's `whole-diff minors:` section (`verification-before-completion/reference/conformance-check.md` "Closure / conformance"), or `whole-diff minors: none`. Before dispatching a review repair, reopen (`in_progress`) every existing plan-task index whose `Files:` ownership includes its touched files; leave unowned cross-cutting repair work in the review report. Mark each reopened index `complete` only once the repair is re-verified and the re-review accepts it — this is the same completion point step 1's reopened indices wait for, not an extra gate, and the gate order stays full verification -> whole-diff CR -> conformance. Any repair invalidates prior full verification, so rerun the full set successfully before the next gate.
@@ -11,25 +11,37 @@ description: Use when starting feature work that needs isolation from current wo
11
11
 
12
12
  Git worktrees create isolated workspaces sharing the same repository, allowing work on multiple branches simultaneously without switching.
13
13
 
14
- **Core principle:** Detect existing isolation first → prefer the project's native tool → default to `<repo>/.worktrees/<branch>`, creating it if missing.
14
+ **Core principle:** Detect existing isolation first → prefer the project's native tool → default to `<repo>/.worktrees/<name>`, creating it if missing.
15
15
 
16
16
  **Announce at start:** "I'm using the using-git-worktrees skill to set up an isolated workspace."
17
17
 
18
18
  ## Step 0 — Detect Existing Isolation (REQUIRED)
19
19
 
20
- For a caller passing resolved PR values, read [reference/pr-head.md](reference/pr-head.md) now and follow it instead of Steps 0 and 1, before generic linked-worktree reuse. For every other caller, check whether you are **already** inside an isolated worktree. Creating a worktree inside another worktree, or inside a submodule, produces silent corruption.
20
+ Accept caller-supplied `$ROOT` and `$BRANCH` without replacing them from session
21
+ state. Root isolation detection, clean-base, overrides/settings reads, wrapper
22
+ discovery and creation in `$ROOT`. For a coordinated member, require its own
23
+ primary as `$ROOT`; never reuse session isolation as another leg. Create a fresh
24
+ member tree even when the session is linked. Without caller inputs, derive the
25
+ primary as below and choose the feature branch by the existing convention.
26
+
27
+ For a caller passing resolved PR values for home, read [reference/pr-head.md](reference/pr-head.md) now and follow it instead of Steps 0 and 1, before generic linked-worktree reuse. For every other caller, check whether you are **already** inside an isolated worktree. Creating a worktree inside another worktree, or inside a submodule, produces silent corruption.
21
28
 
22
29
  ```bash
23
- read -r GIT_DIR GIT_COMMON <<<"$(git rev-parse --path-format=absolute --git-dir --git-common-dir | tr '\n' ' ')"
24
- ROOT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
25
- CURRENT_BRANCH=$(git branch --show-current)
30
+ CALLER_ROOT=${ROOT:-}
31
+ if [ -n "$CALLER_ROOT" ]; then
32
+ read -r GIT_DIR GIT_COMMON <<<"$(git -C "$ROOT" rev-parse --path-format=absolute --git-dir --git-common-dir | tr '\n' ' ')"
33
+ else
34
+ read -r GIT_DIR GIT_COMMON <<<"$(git rev-parse --path-format=absolute --git-dir --git-common-dir | tr '\n' ' ')"
35
+ ROOT=$(dirname "$GIT_COMMON")
36
+ fi
37
+ CURRENT_BRANCH=$(git -C "$ROOT" branch --show-current)
26
38
  ```
27
39
 
28
40
  **Submodule guard:** `GIT_DIR != GIT_COMMON` is also true inside git submodules. Before concluding "already in a worktree," verify you are not in a submodule:
29
41
 
30
42
  ```bash
31
43
  # If this returns a path, you're in a submodule, not a worktree — treat as normal repo
32
- git rev-parse --show-superproject-working-tree 2>/dev/null
44
+ git -C "$ROOT" rev-parse --show-superproject-working-tree 2>/dev/null
33
45
  ```
34
46
 
35
47
  **If `GIT_DIR != GIT_COMMON` (and not a submodule):** The session already runs inside a linked worktree. Its `$GIT_DIR` is `<primary>/.git/worktrees/<name>`, and `$GIT_DIR/gitdir` contains `<worktree>/.git`, so derive its full path:
@@ -51,13 +63,21 @@ For a PR head, use [reference/pr-head.md](reference/pr-head.md) instead of this
51
63
  Pause for explicit consent **only** when one of these holds:
52
64
  - A trivial one-off the user named (typo, format run, dependency bump)
53
65
  - The user already set up a workspace, or asked to work in place
54
- - The sandbox can't support multiple checkouts (see Sandbox fallback)
66
+ - The sandbox can't support multiple checkouts without a caller root (see Sandbox fallback)
55
67
 
56
68
  Otherwise create it. The gate is "is this real work?", not "did the user approve this worktree?"
57
69
 
70
+ For explicit caller roots, preflight the requested branch and resolved home in
71
+ that repo before any creator runs: use `git -C "$ROOT" show-ref --verify
72
+ refs/heads/"$BRANCH"`, `git -C "$ROOT" worktree list --porcelain`, and the
73
+ resolved path's existence. A branch, name, or path collision stops naming repo
74
+ and branch; never suffix or reuse an unrelated tree. On dirty-primary decline,
75
+ abort creation. On every failure, preserve created trees and list them using
76
+ [Step 4's coordinated-member inventory](#step-4--report-location).
77
+
58
78
  ## Step 1a - Read the `## using-git-worktrees` contract
59
79
 
60
- Read the gauntlet overrides file (ladder in Project overrides) before any tool or wrapper discovery. Its `## using-git-worktrees` section carries up to three key lines, written like `## cleanup` items; prose before, between, or after them is instruction too:
80
+ Read the gauntlet overrides file under `$ROOT` (ladder in Project overrides) before any tool or wrapper discovery. With a caller-supplied `$ROOT` (a coordinated member), read that member's overrides file and `$ROOT/.pi/settings.json` by file for its checkout configuration using existing preset rules - never the session-scoped `gauntlet_setting` - and run wrapper discovery under `$ROOT`. Its `## using-git-worktrees` section carries up to three key lines, written like `## cleanup` items; prose before, between, or after them is instruction too:
61
81
 
62
82
  | Key | Meaning | Absent (keyed contract active) |
63
83
  |---|---|---|
@@ -72,6 +92,8 @@ Keyed contract active:
72
92
  1. Run the Step 3 clean-base check in the source checkout.
73
93
  2. `NAME` is the branch name with every `/` replaced by `-` (`jacek/fix-x` -> `jacek-fix-x`). `HOME` is `home:` resolved against `$ROOT` with `<name>` -> `NAME`.
74
94
  3. `create:` set -> run it from `$ROOT` with each resolved token inserted as one single-quoted shell word (`'...'`, an embedded `'` escaped), including when the template already quotes it; a `<token>` the context rule cannot resolve is never executed - ask once for its value. A non-zero exit stops this skill with the command's output: no `git worktree add` fallback, no Step 4 report.
95
+ Resolve other wrapper tokens from this leg's touched directory, not the
96
+ session repo's touched paths.
75
97
  `create:` absent -> when `HOME` is inside `$ROOT`, gitignore its parent directory first (the Step 2b sequence with that directory in place of `.worktrees`); then `git -C "$ROOT" worktree add "$HOME" -b "$BRANCH"`.
76
98
  4. Post-create check, always: `git -C "$ROOT" worktree list --porcelain` must print `worktree $HOME`; otherwise stop and report the mismatch (a wrong `home:` pattern, or a wrapper that places trees elsewhere).
77
99
  5. `WORKTREE="$HOME"`; `$WORKTREE` is the value every later step carries (`home: ../acme-mono-<name>` with branch `fix-x` gives `/work/acme-mono-fix-x`, so the basename `acme-mono-fix-x` is not the name). Go to Step 4.
@@ -99,7 +121,7 @@ Only when no native tool exists:
99
121
 
100
122
  ### 2a. Pick a location
101
123
 
102
- The canonical home is `<repo>/.worktrees/<branch>`. A keyed `## using-git-worktrees` section never reaches this step (Step 1a creates at `home:`); on the legacy path a wrapper may define another path, otherwise use the canonical home. Do not ask local-vs-global or invent another path.
124
+ Derive `NAME` from `$BRANCH` by replacing every `/` with `-`. The canonical home is `<repo>/.worktrees/<name>`. A keyed `## using-git-worktrees` section never reaches this step (Step 1a creates at `home:`); on the legacy path a wrapper may define another path, otherwise use the canonical home. Do not ask local-vs-global or invent another path.
103
125
 
104
126
  ### 2b. Create - gitignore the home first
105
127
 
@@ -107,17 +129,18 @@ Run the Step 3 clean-base check in the source checkout before this sequence. `.w
107
129
 
108
130
  ```bash
109
131
  # ROOT is the primary checkout derived in Step 0.
110
- BRANCH=<new feature branch name>
111
- if git -C "$ROOT" worktree list --porcelain | grep -qx "worktree $ROOT/.worktrees/$BRANCH"; then
132
+ : "${BRANCH:?supply the shared or new feature branch name}"
133
+ NAME=${BRANCH//\//-}
134
+ if git -C "$ROOT" worktree list --porcelain | grep -qx "worktree $ROOT/.worktrees/$NAME"; then
112
135
  : # already exists - emit the Step 4 report, add nothing
113
136
  else
114
137
  if ! git -C "$ROOT" check-ignore -q .worktrees; then
115
138
  echo ".worktrees/" >> "$ROOT/.gitignore"
116
139
  git -C "$ROOT" add .gitignore && git -C "$ROOT" commit -m "Ignore .worktrees/" -- .gitignore
117
140
  fi
118
- git -C "$ROOT" worktree add "$ROOT/.worktrees/$BRANCH" -b "$BRANCH"
141
+ git -C "$ROOT" worktree add "$ROOT/.worktrees/$NAME" -b "$BRANCH"
119
142
  fi
120
- WORKTREE="$ROOT/.worktrees/$BRANCH"
143
+ WORKTREE="$ROOT/.worktrees/$NAME"
121
144
  ```
122
145
 
123
146
  The process cwd never changes. `$WORKTREE` is the value every later step carries; do not `cd` into it.
@@ -139,13 +162,14 @@ The process cwd never changes. `$WORKTREE` is the value every later step carries
139
162
 
140
163
  ### 2d. Sandbox fallback
141
164
 
142
- If worktree creation fails on permissions (read-only filesystem, container sandbox without write to parent dirs): stop, announce the failure, and continue in the current directory on a feature branch.
165
+ On a permission failure (read-only filesystem or container sandbox without write to parent dirs), use the caller-root boundary:
143
166
 
144
- Working in place is a degraded mode for this skill only; `/skill:finishing-a-development-branch` requires a worktree path and does not finish an in-place branch.
167
+ - With an explicit caller root, stop naming the repo, requested branch and error; never branch the session cwd. Preserve created trees and list them using [Step 4's coordinated-member inventory](#step-4--report-location).
168
+ - Without a caller root, announce the failure and continue in the current directory on a feature branch after the Step 1 consent. Treat this as degraded in-place mode: `/skill:finishing-a-development-branch` requires a worktree path and does not finish an in-place branch.
145
169
 
146
170
  ## Step 3 — Verify Clean Base
147
171
 
148
- The check: bare `git status --porcelain` — untracked files count as dirty. Never `--untracked-files=no` / `-uno`. Empty output means clean only when the command exits 0; a nonzero exit is an error to surface — stop; never treat a failed check as "clean".
172
+ The check: `git -C "$ROOT" status --porcelain` in the source checkout — untracked files count as dirty. Never `--untracked-files=no` / `-uno`. Empty output means clean only when the command exits 0; a nonzero exit is an error to surface — stop; never treat a failed check as "clean".
149
173
 
150
174
  **When and where it runs:**
151
175
 
@@ -158,10 +182,23 @@ The check: bare `git status --porcelain` — untracked files count as dirty. Nev
158
182
 
159
183
  Start the message with `gauntlet: question` alone at column zero on the first physical line, followed by a newline. Never append a colon to the marker. Follow the [first-line rule](../brainstorming/reference/menu-layout.md). Report the porcelain output verbatim and ask whether to clean up first (stash/commit) or proceed. Never run tests as a fallback; never auto-stash or auto-clean. On fresh paths the ask is about base hygiene — a user who *meant* the dirt to be part of the base commits it, and creation proceeds from the new HEAD. On the Step 0 path the ask is "continue working in a dirty workspace?" — the dirt is already in the workspace, not merely beside it.
160
184
 
161
- **Provenance note (report-only, never a gate).** Fresh-creation paths only — never Step 0 (an already-linked worktree was branched in some earlier invocation; there is no "created from" to compare this run). Resolve the default branch as `git symbolic-ref --short refs/remotes/origin/HEAD` with the leading `origin/` stripped; compare that short name to the source checkout's `git branch --show-current`. If they differ and the user did not name a base in the request, append one declarative line to the Step 4 report: `Note: branching from <ref>, not <default>.` — execution continues, no confirmation is awaited. If resolution fails (no remote, no `origin/HEAD`), skip the note silently. No other default-branch machinery.
185
+ For a dirty-primary decline, stop this member before creation and list preserved
186
+ created legs. Never auto-remove earlier members.
187
+
188
+ **Provenance note (report-only, never a gate).** Fresh-creation paths only — never Step 0 (an already-linked worktree was branched in some earlier invocation; there is no "created from" to compare this run). Resolve the default branch as `git -C "$ROOT" symbolic-ref --short refs/remotes/origin/HEAD` with the leading `origin/` stripped; compare that short name to the source checkout's `git -C "$ROOT" branch --show-current`. If they differ and the user did not name a base in the request, append one declarative line to the Step 4 report: `Note: branching from <ref>, not <default>.` — execution continues, no confirmation is awaited. If resolution fails (no remote, no `origin/HEAD`), skip the note silently. No other default-branch machinery.
162
189
 
163
190
  ## Step 4 — Report Location
164
191
 
192
+ Retain Step 1a's mandatory keyed-contract post-create path check. After every
193
+ creation path (keyed, native, wrapper or built-in), apply the caller's row:
194
+
195
+ | Caller supplied `$ROOT` | Read-back and ready condition |
196
+ |---|---|
197
+ | Yes (coordinated member) | Require `git -C "$ROOT" worktree list --porcelain` to print `worktree $WORKTREE` and `git -C "$WORKTREE" branch --show-current` to print exactly `$BRANCH`. On an absent, empty, or different result, stop this member, name repo, requested branch and actual result, and emit no `Worktree ready at` line for this member. In `Preserved created legs:`, list every tree created so far in this run, including the stopped member's own tree when its create command succeeded, each as `<repo> <branch or "branch unverified"> <path>`. Report `Worktree ready at <full-path>` only after both read-backs match. |
198
+ | No (single-member run) | Use successful creation as the ready condition; read back the path and branch with the commands above as advisory checks, stopping only on an observed different path or branch and naming repo, requested branch and actual result. Treat an absent or undescribed result as unobserved, not a mismatch; report `Worktree ready at <full-path>` after successful creation with no observed mismatch. |
199
+
200
+ Carry every verified result home first.
201
+
165
202
  ```
166
203
  Worktree ready at <full-path>
167
204
  Branch: <branch-name>
@@ -195,14 +232,14 @@ Re-run tests after rebasing.
195
232
  | Situation | Action |
196
233
  |---|---|
197
234
  | `GIT_DIR != GIT_COMMON` | Already in worktree - report its path, create nothing |
198
- | Worktree path already listed by `git worktree list` | Report it, create nothing |
235
+ | Worktree path already listed by `git worktree list` | Explicit caller root: stop on collision; otherwise report existing isolation |
199
236
  | `git rev-parse --show-superproject-working-tree` returns a path | Submodule — treat as normal repo |
200
237
  | `## using-git-worktrees` has `home:`/`create:`/`destroy:` | Keyed contract (Step 1a): run `create:` or the built-in add at `home:`, then the post-create check |
201
- | `## using-git-worktrees` absent or keyless | Legacy path: native tool, then wrapper discovery, then `<repo>/.worktrees/<branch>` |
238
+ | `## using-git-worktrees` absent or keyless | Legacy path: native tool, then wrapper discovery, then `<repo>/.worktrees/<name>` |
202
239
  | Project-native wrapper exists (legacy path) | Use the wrapper (commonly `script/worktree create`) |
203
- | No native tool | Create `<repo>/.worktrees/<branch>` (gitignore `.worktrees/` first) |
240
+ | No native tool | Create `<repo>/.worktrees/<name>` (gitignore `.worktrees/` first) |
204
241
  | Detached HEAD | Ask before branching |
205
- | Sandbox/permission failure | Work in place on a feature branch |
242
+ | Sandbox/permission failure | Follow Step 2d's caller-root boundary |
206
243
  | Source checkout dirty | Report + ask |
207
244
 
208
245
  ## Red Flags — STOP
@@ -112,6 +112,14 @@ List the files this implementation will create, modify, or delete. Group by comp
112
112
 
113
113
  If you can't list the files, the spec isn't ready: amend or redraw per brainstorming's [Amending an approved spec](../brainstorming/SKILL.md#amending-an-approved-spec).
114
114
 
115
+ ## Leg routing
116
+
117
+ Read the home spec's `## Legs` and [the leg contract](../brainstorming/reference/legs.md) before recon. Keep the plan and `**Spec:**` header home-owned. Resolve each member's checkout from its declared primary and recorded `home:` with shared `NAME`; admit task ownership through `plan_check`, never from `design:` prose. Follow [plan-contract.md](reference/plan-contract.md#legs-leg-unknown) for `Leg:`, per-leg baselines and path keys. Repair unknown task legs in the plan, not spec membership.
118
+
119
+ Re-walk every task's `Files:` using Gather rules in the leg contract. Append extra task `Rules:` and resolve `Rules: root only - paths unresolved`; retain missing-link evidence. Split mixed waves into consecutive single-leg waves without violating dependencies.
120
+
121
+ Run Baseline independently in each owning checkout. Apply every provenance, dirty-tree, tree-equivalence, created-anchor, selector, contamination, artifact and blocked-command rule below within that repo. Qualify provenance notes, dirty/global/Open Questions and blocked evidence by repo; compare and retain SHAs only within the same leg. Write one `Baseline <repo>: <sha>` per member and execute each task's `Tests:` at that leg's root. Preserve all evidence rules and keep full verification for verify.
122
+
115
123
  ## Wave Grouping
116
124
 
117
125
  Group tasks into **waves** so the executor can parallelize independent work (see `subagent-driven-development` wave loop). A wave is a maximal set of tasks that (a) have no ordering dependency on each other, (b) own **pairwise-disjoint files**, and (c) contend on **no shared mutable runtime resource** (same DB/schema, port, fixture file, external service, shared temp path).
@@ -8,6 +8,14 @@ A wave is a maximal set of tasks that have no ordering dependency on each other,
8
8
 
9
9
  - Tasks nest under `## Wave N — <label>` headers; `### Task N` headers sit inside a wave.
10
10
 
11
+ ## Legs (`leg-unknown`)
12
+
13
+ Read the home spec's `## Legs` using [the leg contract](../../brainstorming/reference/legs.md). Write `Leg: <repo>` immediately after each task heading, before `**Files:**`; absence means home. Keep the plan and its `**Spec:**` header at home, citing the home spec. Repair a `leg-unknown` task in the plan, never by adding spec membership.
14
+
15
+ Write one `Baseline <repo>: <sha>` header per member. Match each task's baseline bullets to its own leg's SHA; require equality within a leg, not across legs. Resolve `Modify:`/`Create:`/`Test:` paths and globs against the admitted leg checkout, using its recorded `home:` with shared `NAME` against its declared primary. Key file overlap and cross-task created anchors on `(leg, path)`.
16
+
17
+ Split mixed waves into consecutive leg-homogeneous waves, preserving dependencies. Re-walk each task's `Files:` under its leg using the leg contract's Gather rules procedure. Append task `Rules:` for files missing from the spec list; resolve `Rules: root only - paths unresolved`. Read the combined lists before execution and review.
18
+
11
19
  ## Files (`wave-file-disjointness`, `paths-exist`)
12
20
 
13
21
  **File-ownership contract.** The per-task `**Files:**` block *is* the ownership declaration — no new syntax. Rule: **within a wave, the union of every task's declared paths must be pairwise disjoint.** Globs are allowed for `Modify` when exact paths are unknown, but must not overlap another same-wave task's paths. A task that must touch another's file belongs in a later wave.
@@ -27,7 +35,7 @@ Every `### Task N` carries a `**Tests:**` block: the bare line `**Tests:**` dire
27
35
 
28
36
  Absence is never valid. A `- [ ]` step or any non-bullet line ends the block. `via:` and `none:` are unchecked beyond form.
29
37
 
30
- Baseline forms (`baseline-line`): `- baseline: <sha> pass`, `- baseline: <sha> pass via `<base command>`` (both allow ` - artifacts: <path>[, <path>]`), or `- baseline: <sha> skipped - <path>[, <path>] created by this task`. Use 7-40 lowercase hex characters for `<sha>`; every baseline SHA in the plan must match. Never put a baseline under `none:`. `skipped` is valid only when every command anchor is a listed `Create:` path and every listed path is one of those anchors. Every segment of a `pass via` base command must anchor an existing `Test:` path of the task and no `Create:` path. Obtain evidence through writing-plans Self-Review Baseline, never by editing evidence to satisfy a finding.
38
+ Baseline forms (`baseline-line`): `- baseline: <sha> pass`, `- baseline: <sha> pass via `<base command>`` (both allow ` - artifacts: <path>[, <path>]`), or `- baseline: <sha> skipped - <path>[, <path>] created by this task`. Use 7-40 lowercase hex characters for `<sha>`; every baseline SHA within the same leg must match (plan-wide for legacy home-only plans). Never put a baseline under `none:`. `skipped` is valid only when every command anchor is a listed `Create:` path and every listed path is one of those anchors. Every segment of a `pass via` base command must anchor an existing `Test:` path of the task and no `Create:` path. Obtain evidence through writing-plans Self-Review Baseline, never by editing evidence to satisfy a finding.
31
39
 
32
40
  Commands are written repo-relative and run in the worktree via `(cd "<abs worktree path>" && <command>)` - the checker never runs them; Baseline and the executor do. Each command is split into segments on `&&`, `||`, `;`, `|`; every segment must contain, as a whitespace-delimited token, a `Test:` path of the same task (the path alone, or followed by `::`, `#`, or `:` and a filter). A `Test:` value containing `*`, `?`, `[` or ending in `/` never anchors; any other argument token with those shapes is a broadening selector and fails. `cd `, `sh -c`, `bash -c`, `eval `, `$(` are unsupported. Each `Test:` path must exist or be a `Create:` path of some task. A segment equal to a header `**Verification:**` or `**Happy path:**` segment is a full-suite command and fails. Runners with no file-addressable form are out of scope (`go test ./pkg -run X`, `mvn -Dtest=`).
33
41