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.
- package/CHANGELOG.md +11 -0
- package/README.md +14 -2
- package/agents/code-reviewer.md +2 -0
- package/agents/implementer.md +2 -0
- package/agents/loop-implementer.md +10 -0
- package/agents/plan-writer.md +1 -1
- package/agents/spec-reviewer.md +2 -0
- package/bin/gauntlet-telemetry-seal.mjs +18 -3
- package/bin/gauntlet-telemetry-seal.test.mjs +30 -0
- package/extensions/lib/plan-check.test.ts +116 -0
- package/extensions/lib/plan-check.ts +149 -20
- package/extensions/lib/telemetry-record.test.ts +11 -0
- package/extensions/lib/telemetry-record.ts +7 -0
- package/extensions/phase-tracker.test.ts +201 -3
- package/extensions/phase-tracker.ts +77 -9
- package/extensions/verify-before-ship.test.ts +240 -2
- package/extensions/verify-before-ship.ts +180 -25
- package/package.json +2 -2
- package/skills/brainstorming/SKILL.md +1 -1
- package/skills/brainstorming/gatherer.md +14 -1
- package/skills/brainstorming/reference/amendment-surface.md +13 -1
- package/skills/brainstorming/reference/legs.md +187 -0
- package/skills/brainstorming/reference/spec-finalization.md +14 -0
- package/skills/brainstorming/reference/ticket-acceptance.md +2 -2
- package/skills/finishing-a-development-branch/SKILL.md +2 -2
- package/skills/finishing-a-development-branch/reference/legs.md +76 -0
- package/skills/gauntlet-handoff/SKILL.md +2 -2
- package/skills/gauntlet-resume/SKILL.md +3 -2
- package/skills/gauntlet-resume/reference/brief-contract.md +37 -3
- package/skills/gauntlet-resume/reference/reconstruction.md +4 -0
- package/skills/gauntlet-resume/reference/seed.md +2 -0
- package/skills/loop-implementation/SKILL.md +89 -2
- package/skills/subagent-driven-development/SKILL.md +10 -0
- package/skills/using-git-worktrees/SKILL.md +58 -21
- package/skills/writing-plans/SKILL.md +8 -0
- 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
|
-
|
|
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
|
-
|
|
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/<
|
|
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
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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/<
|
|
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
|
|
111
|
-
|
|
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/$
|
|
141
|
+
git -C "$ROOT" worktree add "$ROOT/.worktrees/$NAME" -b "$BRANCH"
|
|
119
142
|
fi
|
|
120
|
-
WORKTREE="$ROOT/.worktrees/$
|
|
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
|
-
|
|
165
|
+
On a permission failure (read-only filesystem or container sandbox without write to parent dirs), use the caller-root boundary:
|
|
143
166
|
|
|
144
|
-
|
|
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:
|
|
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
|
-
|
|
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` |
|
|
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/<
|
|
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/<
|
|
240
|
+
| No native tool | Create `<repo>/.worktrees/<name>` (gitignore `.worktrees/` first) |
|
|
204
241
|
| Detached HEAD | Ask before branching |
|
|
205
|
-
| Sandbox/permission failure |
|
|
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
|
|
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
|
|