pi-gauntlet 7.40.0 → 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 +17 -0
- package/README.md +16 -4
- 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 +2 -2
- package/skills/brainstorming/gatherer.md +20 -7
- package/skills/brainstorming/reference/amendment-surface.md +13 -1
- package/skills/brainstorming/reference/legs.md +187 -0
- package/skills/brainstorming/reference/pr-subject.md +15 -9
- package/skills/brainstorming/reference/spec-finalization.md +16 -2
- package/skills/brainstorming/reference/ticket-acceptance.md +2 -2
- package/skills/finishing-a-development-branch/SKILL.md +21 -22
- 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 +40 -6
- package/skills/gauntlet-resume/reference/reconstruction.md +9 -7
- package/skills/gauntlet-resume/reference/seed.md +2 -0
- package/skills/loop-implementation/SKILL.md +89 -2
- package/skills/subagent-driven-development/SKILL.md +12 -0
- package/skills/using-git-worktrees/SKILL.md +59 -22
- package/skills/using-git-worktrees/reference/pr-head.md +36 -15
- package/skills/writing-plans/SKILL.md +16 -1
- package/skills/writing-plans/reference/plan-contract.md +9 -1
|
@@ -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>
|
|
@@ -181,7 +218,7 @@ If `git symbolic-ref -q HEAD` returns nothing, you're on a detached HEAD. Do not
|
|
|
181
218
|
|
|
182
219
|
## Keeping a Worktree Current
|
|
183
220
|
|
|
184
|
-
For a PR-subject branch, follow [reference/pr-head.md](reference/pr-head.md):
|
|
221
|
+
For a PR-subject branch, follow [reference/pr-head.md](reference/pr-head.md): sync per that reference. For other longer-running work the base branch advances:
|
|
185
222
|
|
|
186
223
|
```bash
|
|
187
224
|
git -C "$WORKTREE" fetch origin
|
|
@@ -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
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Select home and stop before writes
|
|
4
4
|
|
|
5
|
-
Consume the ordered array of admitted PR values, home first; use home's `headRefName` for checkout and `PRE_REWORK_HEAD=<home headRefOid>`. Carry the remaining entries as MEMBER_HEADS `{ number, url, title, headRefName, headRefOid }`. For a caller passing PR values, follow this reference instead of `using-git-worktrees/SKILL.md` Steps 0 and 1. Derive ROOT from the absolute git common directory as in Step 0, applying its submodule guard. Resolve the worktree home through the existing overrides/native-tool/wrapper contract; default to `$ROOT/.worktrees/<name>`. Set `<name>` to `headRefName` with every `/` replaced by `-`. Never adopt the session's linked tree unless it is the selected home on `headRefName`.
|
|
5
|
+
Consume SYNC beside the ordered array of admitted PR values, home first; use home's `headRefName` for checkout and `PRE_REWORK_HEAD=<home headRefOid>`. Carry the remaining entries as MEMBER_HEADS `{ number, url, title, headRefName, headRefOid }`. For a caller passing PR values, follow this reference instead of `using-git-worktrees/SKILL.md` Steps 0 and 1. Derive ROOT from the absolute git common directory as in Step 0, applying its submodule guard. Resolve the worktree home through the existing overrides/native-tool/wrapper contract; default to `$ROOT/.worktrees/<name>`. Set `<name>` to `headRefName` with every `/` replaced by `-`. Never adopt the session's linked tree unless it is the selected home on `headRefName`.
|
|
6
6
|
|
|
7
7
|
Run `git -C "$ROOT" fetch origin`, then check reachability and remote equality for every admitted PR (home and members) before any worktree write: require each original `headRefOid` object and `origin/<headRefName>` equal to it; use the branch-moved stop below naming that PR. Resolve `BASE_SHA=$(git -C "$ROOT" rev-parse origin/<baseRefName>)` as a full SHA before any conflict helper needs fetched-base overrides. Then check these stops in order before creating or modifying any file in the primary or a worktree, or running `git worktree add`.
|
|
8
8
|
|
|
@@ -11,13 +11,17 @@ Run `git -C "$ROOT" fetch origin`, then check reachability and remote equality f
|
|
|
11
11
|
| `isCrossRepository` true | `PR #N's head lives on fork <headRepository.nameWithOwner>; rework needs an in-repo branch.` |
|
|
12
12
|
| `headRefName` listed by `git -C "$ROOT" worktree list --porcelain` at another path, including `.worktrees/pr-<N>` | `branch <headRefName> is checked out at <path>; finish or remove that worktree first.` |
|
|
13
13
|
| `git -C "$ROOT" rev-parse --verify -q <headRefOid>^{commit}` fails after fetch, or `origin/<headRefName>` differs from `headRefOid` | `PR #N head <headRefOid7> is not reachable (origin/<headRefName> is at <sha7>); the branch moved.` |
|
|
14
|
-
| N=1
|
|
14
|
+
| Local branch exists without validated recovery (after recovery below; N=1 without a rework-base ref, with or without a worktree, always takes this check) and `git -C "$ROOT" merge-base --is-ancestor <headRefName> origin/<headRefName>` exits 1 | `local branch <headRefName> is at <sha7>, not an ancestor of origin/<headRefName> (<sha7>); rework needs the PR head.` |
|
|
15
15
|
|
|
16
|
-
## Recover interrupted
|
|
16
|
+
## Recover an interrupted sync
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Before the local-branch ancestry stop and exact-head rows, resolve the existing selected worktree's rebase state paths with `git -C "$WORKTREE" rev-parse --path-format=absolute --git-path rebase-merge` and `git -C "$WORKTREE" rev-parse --path-format=absolute --git-path rebase-apply`; stop on either command failing; stop when either resolved path is an existing directory: `worktree <path> has a rebase in progress; finish or abort it by hand`; treat neither path being a directory as no active rebase (`REBASE_HEAD` lingers after a completed rebase and is not the signal). Require a clean existing home tree on every recovery path. With no worktree, inspect refs from ROOT before any worktree operation.
|
|
19
19
|
|
|
20
|
-
Continue from the validated tip without reset
|
|
20
|
+
Inspect the actual local home branch tip, not caller HEAD. When `refs/gauntlet/rework-base/pr-<home>` exists, require its ancestry of that tip and every `refs/gauntlet/pre-rework/pr-<N>` to equal the recorded `headRefOid`. On mismatch use the carries-commits-beyond stop below; on successful validation set `SYNC=rebase` regardless of the token. Continue from the validated tip without reset. Per member run `git -C "$ROOT" cherry <tip> <member headRefOid> <BASE_SHA>`; no `+` line records `Member replay: #<N> (already landed)`, otherwise replay that member in the sync procedure after provisioning the validated branch.
|
|
21
|
+
|
|
22
|
+
Without a rework-base ref, for a list with an existing home worktree or local branch, inspect the actual local home branch tip with `git -C "$ROOT" rev-parse refs/heads/<headRefName>`, never caller HEAD. For a tip differing from home headRefOid, require home `headRefOid` ancestry, then walk `git -C "$ROOT" log --first-parent --format='%H %P %s' <headRefOid>..<local home tip>`. Recognize recovery only for a nonempty walk; require every entry to be a two-parent merge with canonical subject `merge PR #<N> head <sha7> into PR #<home>`, where N is an admitted member, sha7 matches its original head, and its full original `headRefOid` is the second parent. Require a clean existing home tree. On unrelated commits, failed ancestry, or dirt, stop before writes: `worktree <path> on <headRefName> carries commits beyond PR #<home>'s head (<sha7>) that are not member merges from this list; resolve by hand or remove the worktree.` Use the selected home path even when no worktree exists.
|
|
23
|
+
|
|
24
|
+
Continue from the validated tip without reset or branch deletion; force `SYNC=merge` for recognized canonical merges. For recognized merges skip the member and report `Member merge: #<N> <sha7> (already landed)` using the existing merge commit SHA. Otherwise apply the exact-head checks below for every N.
|
|
21
25
|
|
|
22
26
|
## Provision the exact head
|
|
23
27
|
|
|
@@ -25,14 +29,14 @@ Apply the first matching row after all stops pass. Keep the process cwd unchange
|
|
|
25
29
|
|
|
26
30
|
| State | Action |
|
|
27
31
|
|---|---|
|
|
28
|
-
|
|
|
29
|
-
| Selected home is already on `headRefName`
|
|
32
|
+
| Recovery validated an existing home branch | Reuse its clean selected worktree unchanged; with no worktree, run `git -C "$ROOT" worktree add "$WORKTREE" <headRefName>` on that existing branch. Verify `git -C "$WORKTREE" rev-parse HEAD` equals the validated local tip. Never use update-ref, -b, reset, clean, or deletion on recovery. |
|
|
33
|
+
| Selected home is already on `headRefName` | Run Step 3's dirty-tree ask first; do not stash or clean automatically. Then run `git -C "$WORKTREE" merge --ff-only origin/<headRefName>` and require HEAD equal to `headRefOid`; otherwise stop naming both SHAs. |
|
|
30
34
|
| No worktree, local branch exists | Only after ancestry proof, run `git -C "$ROOT" update-ref refs/heads/<headRefName> origin/<headRefName>`, then `git -C "$ROOT" worktree add "<home>" <headRefName>`. Never use `git -C "$ROOT" branch -f`. |
|
|
31
35
|
| No worktree, no local branch | Run `git -C "$ROOT" worktree add "<home>" <headRefName>`. Git's unique-remote DWIM resolves exactly one matching remote-tracking branch as `--track -b <headRefName> "<home>" origin/<headRefName>`; do not substitute the generic new-branch `-b` creation path. |
|
|
32
36
|
|
|
33
37
|
For fresh creation, apply Step 3's source-clean ask and the existing home safety checks after the ordered stops. Respect the resolved home and native tool/wrapper, but use an overrides `create:` only when it takes `<branch>` as an existing branch and contains no `-b`/`-B`. Otherwise run the built-in checkout above and add `create: override skipped (contains -b)` to the report. Resolve tokens and surface wrapper failures through the existing contract.
|
|
34
38
|
|
|
35
|
-
Verify `git -C "$WORKTREE" rev-parse HEAD` equals `headRefOid` for
|
|
39
|
+
Verify `git -C "$WORKTREE" rev-parse HEAD` equals home `headRefOid` for every list size without recovery, or the validated recovery tip. On a post-create head race, leave the tree intact and stop naming its path and both SHAs. Capture `PRE_REWORK_HEAD=<headRefOid>` and `BASE_SHA=$(git -C "$ROOT" rev-parse origin/<baseRefName>)` as full SHAs. In the Step 4 report use `Base: <headRefName> @ <sha7>` and add immediately after it:
|
|
36
40
|
|
|
37
41
|
```text
|
|
38
42
|
PR: <owner/repo#N>
|
|
@@ -40,21 +44,36 @@ Pre-rework head: <sha>
|
|
|
40
44
|
Base SHA: <sha7>
|
|
41
45
|
```
|
|
42
46
|
|
|
43
|
-
##
|
|
47
|
+
## Record the pre-rework heads
|
|
48
|
+
|
|
49
|
+
After exact-head verification, run `git -C "$WORKTREE" update-ref refs/gauntlet/pre-rework/pr-<N> <headRefOid>` for every admitted PR; stop on any error. Preserve original refs on recovery and require their identities before proceeding. Keep the appended-spec approval `commit --amend --no-edit` and appended-wave fold `reset --soft "$WAVE_BASE_SHA"`; require WAVE_BASE_SHA to descend from the rework base in rebase mode, or PRE_REWORK_HEAD in merge mode.
|
|
50
|
+
|
|
51
|
+
## Sync onto the fetched base
|
|
52
|
+
|
|
53
|
+
| SYNC | Procedure |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `rebase` | Save last-good SHA (PRE_REWORK_HEAD for a fresh run, validated local tip on recovery). Run `git -C "$WORKTREE" rebase --no-rebase-merges origin/<baseRefName>`; save last-good on success. Then replay remaining members in listed order: `git -C "$WORKTREE" rebase --no-rebase-merges --onto HEAD <BASE_SHA> <member headRefOid>`. After each success run `git -C "$WORKTREE" update-ref refs/heads/<home headRefName> HEAD`, then `git -C "$WORKTREE" checkout <home headRefName>` and save last-good. Never update member refs locally or on origin. On complete success run `git -C "$WORKTREE" update-ref refs/gauntlet/rework-base/pr-<home> HEAD`; stop on ref errors. |
|
|
56
|
+
| `merge` | Follow the member-then-base recipe below verbatim; do not write rework-base. |
|
|
57
|
+
|
|
58
|
+
On every sync outcome read the fetched-base constraints owned by `brainstorming/reference/pr-subject.md`. For rebase conflicts use the helper and fetched-base scoped-runner contract below, adding `git -C "$WORKTREE" rebase --show-current-patch` to its payload. Forbid helper commits and `rebase --continue`, `rebase --abort`, or `rebase --skip`. Require every listed file staged, empty unmerged index, and contained status delta before the orchestrator runs `GIT_EDITOR=true git -C "$WORKTREE" rebase --continue`. For an empty stopped commit the orchestrator runs `git -C "$WORKTREE" rebase --skip`. Dispatch a fresh helper at each later stop.
|
|
59
|
+
|
|
60
|
+
On helper or completion failure run `git -C "$WORKTREE" rebase --abort`; for a member additionally run `git -C "$WORKTREE" checkout <home headRefName>` before proving restoration. Require abort exit 0, HEAD equal to saved last-good SHA, branch equal to home, and successful empty `git -C "$WORKTREE" status --porcelain --untracked-files=all`. On failure stop with the complete restore-failure report in the merge recipe below, quoting the literal observed branch and HEAD output; never reset, clean, or delete to recover. After clean home abort record `sync: unresolved - <files>` and all members `pending`; continue on the saved pre-attempt home tip, opening Design with `rebase onto origin/<base> and resolve <files>` as Task 1. After clean member abort retain earlier outcomes, record `Member replay: #<N> unresolved - <files>` and later members `pending`; open Design with `replay #<N> onto home and resolve <files>` as Task 1. Do not replay later members on either failure. Task 1 completes pending replays and persists Rework base before finishing.
|
|
44
61
|
|
|
45
|
-
|
|
62
|
+
Record final differing tip as `Base: <base> @ <sha7> - rebased: <pre7>..<new7>, <n> stop(s) in <files>`; only final equality permits `Base: <base> @ <sha7> - sync: no-op`. Record one `Member replay: #<N> <outcome>` in listed order: actual first/last added SHAs `<first7>..<last7>`, `empty` for all commits dropped, `(already landed)` on recovery, `unresolved - <files>`, or `pending`. Replay from BASE_SHA even for shared commits; equivalent commits drop, textual conflicts use the helper. All-dropped is not an error.
|
|
46
63
|
|
|
47
|
-
|
|
64
|
+
After complete rebase print `Rework base: <full sha>` immediately after `Base SHA:` in Step 4. Keep every PR/Pre-rework head pair and carry MEMBER_HEADS to gather.
|
|
65
|
+
|
|
66
|
+
### Merge admitted member heads
|
|
48
67
|
|
|
49
68
|
Before the base merge, iterate remaining MEMBER_HEADS in listed order. Save the last-good home SHA before each merge. Run `git -C "$WORKTREE" merge --no-ff -m "merge PR #<N> head <sha7> into PR #<home>" <headRefOid>` using the original supplied head, not a newly advanced remote tip. On success record `Member merge: #<N> <merge sha7>`; an ancestor no-op records no new commit.
|
|
50
69
|
|
|
51
70
|
For conflicts use the fetched-base scoped-runner selection and helper/completion recipe in "Merge the fetched base": fresh foreground implementer, exact conflicted paths, staging, empty unmerged index, contained status delta, and orchestrator `git -C "$WORKTREE" commit --no-edit`. Preserve the canonical subject, including in a manual resolution. Record any contract exception for the spec's Rework record.
|
|
52
71
|
|
|
53
|
-
On helper or completion failure run `git -C "$WORKTREE" merge --abort` and capture its exit status. Before claiming restoration, require abort exit status 0, `git -C "$WORKTREE" rev-parse HEAD` equal to the saved last-good full SHA, `git -C "$WORKTREE" branch --show-current` equal to home's `headRefName`, and successful `git -C "$WORKTREE" status --porcelain --untracked-files=all` with empty output (checkout and index clean). On any failed check or residual edit, stop naming the failing PR, conflicted files, selected worktree, observed branch and HEAD, abort exit status, remaining status paths or inspection errors, and saved last-good SHA with earlier merged members; require manual recovery before rerunning and do not claim restoration. Never reset or clean to force recovery. Only after all checks pass, stop with `PR #<N> conflicts with home PR #<home> in <files>; the worktree at <path> is left on <headRefName> at <sha7> with #<earlier members> already merged; merge #<N> by hand there (git merge --no-ff <headRefOid>) and rerun the same list, or rerun without #<N>.` Tell the developer to retain the canonical subject for manual merging. Stop before the base merge, spec, plan, later member merges, push, or close. Do not use the base-conflict continuation for member failures.
|
|
72
|
+
On helper or completion failure run `git -C "$WORKTREE" merge --abort` and capture its exit status. Before claiming restoration, require abort exit status 0, `git -C "$WORKTREE" rev-parse HEAD` equal to the saved last-good full SHA, `git -C "$WORKTREE" branch --show-current` equal to home's `headRefName`, and successful `git -C "$WORKTREE" status --porcelain --untracked-files=all` with empty output (checkout and index clean). On any failed check or residual edit, stop naming the failing PR, conflicted files, selected worktree, the observed branch and HEAD quoted as the literal `branch --show-current` and `rev-parse HEAD` output (or that command's error text), never as a reference to an earlier step, abort exit status, remaining status paths or inspection errors, and saved last-good SHA with earlier merged members; require manual recovery before rerunning and do not claim restoration. Never reset or clean to force recovery. Only after all checks pass, stop with `PR #<N> conflicts with home PR #<home> in <files>; the worktree at <path> is left on <headRefName> at <sha7> with #<earlier members> already merged; merge #<N> by hand there (git merge --no-ff <headRefOid>) and rerun the same list, or rerun without #<N>.` Tell the developer to retain the canonical subject for manual merging. Stop before the base merge, spec, plan, later member merges, push, or close. Do not use the base-conflict continuation for member failures.
|
|
54
73
|
|
|
55
74
|
After all members, merge the fetched base. In the Step 4 report print one `PR:` / `Pre-rework head:` pair per admitted PR, home first, followed by member outcome lines in listed order, then the base outcome; print Base SHA once. Carry MEMBER_HEADS to gather.
|
|
56
75
|
|
|
57
|
-
|
|
76
|
+
### Merge the fetched base
|
|
58
77
|
|
|
59
78
|
Run `git -C "$WORKTREE" merge --no-ff --no-edit origin/<baseRefName>` and record the matching outcome:
|
|
60
79
|
|
|
@@ -72,9 +91,11 @@ Continue only with an empty unmerged index (`git -C "$WORKTREE" diff --name-only
|
|
|
72
91
|
|
|
73
92
|
For any failed helper/completion checks, run `git -C "$WORKTREE" merge --abort`, record `merge: unresolved - <files>`, and continue on the original unmerged head. Open the spec's Design with `merge origin/<base> and resolve <files>` as eventual Task 1. On every outcome, require the base-SHA constraint reads owned by `brainstorming/reference/pr-subject.md`.
|
|
74
93
|
|
|
75
|
-
|
|
94
|
+
## Validate the sync outcome
|
|
95
|
+
|
|
96
|
+
Validate once per outcome: rebase success - require `git -C "$WORKTREE" merge-base --is-ancestor refs/gauntlet/rework-base/pr-<home> HEAD`; home rebase aborted - HEAD equals saved pre-attempt last-good home SHA (PRE_REWORK_HEAD for a fresh run, validated local tip on recovery); member replay aborted - HEAD equals saved last-good rebased home tip; merge (any outcome) - require `git -C "$WORKTREE" merge-base --is-ancestor <each original head> HEAD` exit 0 for home and every member. Stop on failure naming PR and SHAs. Original-head ancestry is never asserted against a rebased tip.
|
|
76
97
|
|
|
77
|
-
Treat the base merge and each
|
|
98
|
+
Treat the base sync (rebase or merge) and each member replay or merge, their conflict helpers' edits to the conflicted files, and their scoped conflict tests as the sole preapproval write/run exception outside spec directories. Run no other brainstorming tests, dispatch no reviewer, and push nothing; leave review of the sync to end-of-flow code review.
|
|
78
99
|
|
|
79
100
|
## Fetch unresolved review threads
|
|
80
101
|
|
|
@@ -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).
|
|
@@ -186,7 +194,14 @@ The full verification entrypoint appears only on the `**Verification:**` line
|
|
|
186
194
|
|
|
187
195
|
Each task uses `- [ ]` checkbox steps so execution tools (and humans) can track progress.
|
|
188
196
|
|
|
189
|
-
**PR-subject tasks.** Read the approved spec's `## Rework record
|
|
197
|
+
**PR-subject tasks.** Read the approved spec's `## Rework record`; select Task 1 by its recorded form:
|
|
198
|
+
| Record | Task 1 |
|
|
199
|
+
|---|---|
|
|
200
|
+
| `merge: unresolved` | Merge the named base and resolve the listed files; preserve the original commits. |
|
|
201
|
+
| `sync: unresolved - <files>` | Run `git rebase --no-rebase-merges origin/<base>` and resolve `<files>`. |
|
|
202
|
+
| `Member replay: #<N> unresolved - <files>` | Replay #<N> onto home and resolve `<files>`. |
|
|
203
|
+
Place a rebase-form Task 1 alone in Wave 1 marked `in-place`; keep the merge-form Task 1 as an ordinary wave task.
|
|
204
|
+
For both rebase forms, consume every `pending` member in listed order, run `git update-ref refs/gauntlet/rework-base/pr-<home> HEAD`, and persist `Rework base: <sha>` plus final `Member replay:` outcomes through the existing [amendment path](../brainstorming/SKILL.md#amending-an-approved-spec) before finishing. Use [pr-head's sync and helper contract](../using-git-worktrees/reference/pr-head.md), including replay commands, conflict checks, continuation and abort assertions; do not duplicate it. Make each `fix` thread exactly one task whose title carries that thread id. Name the thread's request in its `Tests:` block through the scoped command and `via:` seam that exercises it. Keep `keep open` and `decline: <reason>` as recorded dispositions, not repair tasks. Apply the existing file-disjoint wave, spec-anchor, baseline and checker rules to these tasks without changing executor or spec-commit mechanics.
|
|
190
205
|
|
|
191
206
|
```markdown
|
|
192
207
|
### Task N: [Component Name]
|
|
@@ -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
|
|