fix(execute): clear the wave record only on the main-merge gate's confirm (green)
1. Step 6 cleared the orders dir before Phase 8's main-merge gate asked,
so a declined gate lost the merged-waves record and the next run
prepared wave 1 again on merged code. The clear now sits only under
the gate's "On confirm" (wave path, next-wave none, Step 4 passed);
Step 6 and a declined gate keep the record, so a re-run goes
next-wave none -> Step 4 -> back to the gate.
2. The gate's Decline row and Recovery surface now say what holds on the
wave path: the waves are already merged on the current branch,
nothing is finalized, the record is kept, and re-running the same
command (not --resume, which Phase 2.5 runs as a single session)
re-runs master verification and returns to the gate. The branch name
is trek/{slug}/session-N; the push-before-cleanup note now holds only
where the push succeeded. The single-session --resume line stays.
3. The report footer adds the multi-session way to continue.
4. wave.json stores the branch the wave was prepared on (write
--branch); Step 3's status call passes the current branch and stops,
touching nothing, when the main checkout is on another branch.
npm test: 1277 tests, 1275 pass, 0 fail, 2 skipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d9e091c143
commit
d70e3d2af7
2 changed files with 72 additions and 45 deletions
|
|
@ -620,9 +620,10 @@ Record `REPO_ROOT`, `PLAN_REL`, `SESSION_DIR`, `WORKTREE_DIR`, `ORDERS_DIR`,
|
|||
A number is `W`: continue to Step 1. `none` means earlier runs merged every
|
||||
wave: go to Step 4. The record it reads, `$ORDERS_DIR/merged-waves.json`
|
||||
(plan path + sha256 of the plan's content), is written by Step 3 after each
|
||||
merged wave, survives Step 5, and is cleared by Step 6 only when every wave is
|
||||
merged and Step 4 passed, so a re-run after a later wave failed never prepares
|
||||
an already-merged wave again.
|
||||
merged wave, survives Step 5, and is cleared only on confirm at Phase 8's
|
||||
main-merge gate, when every wave is merged and Step 4 passed, so a re-run
|
||||
after a later wave failed (or after a declined gate) never prepares an
|
||||
already-merged wave again.
|
||||
If `next-wave` exits 1, the record belongs to another plan or another version
|
||||
of this one (`{slug}` is the plan's file name, so plans can share
|
||||
`$SESSION_DIR`). Stop, touch nothing, and tell the user exactly what its
|
||||
|
|
@ -735,16 +736,17 @@ Source: brief Constraint 2.
|
|||
### Step 2 — Write the session orders for wave W, then stop
|
||||
|
||||
```bash
|
||||
(cd "$REPO_ROOT" && node "$VOYAGE_ROOT/lib/util/session-orders.mjs" write --plan "$PLAN_REL" --wave "{W}" --session-dir "$SESSION_DIR")
|
||||
(cd "$REPO_ROOT" && node "$VOYAGE_ROOT/lib/util/session-orders.mjs" write --plan "$PLAN_REL" --wave "{W}" --session-dir "$SESSION_DIR" --branch "$ORIGINAL_BRANCH")
|
||||
```
|
||||
|
||||
`$PLAN_REL` is relative to the main checkout (Step 0; the tool refuses an
|
||||
absolute path): each session resolves it inside its own worktree, which has the
|
||||
same repo content from HEAD. The tool writes `$ORDERS_DIR/wave-{W}-session-{N}.md`
|
||||
per session (naming its worktree, directory and progress file) and
|
||||
`$ORDERS_DIR/wave.json` (`status: awaiting-sessions`, the plan path and the
|
||||
sha256 of its content), and prints, per session, the directory to open and the
|
||||
starter line (`/trekexecute --session {N} $PLAN_REL`). A session's per-session `cwd:`
|
||||
`$ORDERS_DIR/wave.json` (`status: awaiting-sessions`, the plan path, the
|
||||
sha256 of its content and the branch the wave is prepared on), and prints, per
|
||||
session, the directory to open and the starter line
|
||||
(`/trekexecute --session {N} $PLAN_REL`). A session's per-session `cwd:`
|
||||
field in the Execution Strategy moves the directory it opens in to that
|
||||
subdirectory of its worktree (plugin-in-monorepo layouts, brief Constraint 1,
|
||||
Option B); default `.` is the worktree root.
|
||||
|
|
@ -764,12 +766,15 @@ the orders are the hand-over.
|
|||
### Step 3 — Collect wave W (a later run)
|
||||
|
||||
```bash
|
||||
(cd "$REPO_ROOT" && node "$VOYAGE_ROOT/lib/util/session-orders.mjs" status --plan "$PLAN_REL" --wave "{W}" --session-dir "$SESSION_DIR")
|
||||
(cd "$REPO_ROOT" && node "$VOYAGE_ROOT/lib/util/session-orders.mjs" status --plan "$PLAN_REL" --wave "{W}" --session-dir "$SESSION_DIR" --branch "$ORIGINAL_BRANCH")
|
||||
```
|
||||
|
||||
If it exits 1, `wave.json` was written for another plan or another version of
|
||||
this one: stop and report its message, touching nothing. Re-run with the plan
|
||||
(version) that wrote the wave, or abandon the wave (the way out below).
|
||||
If it exits 1, stop and report its message, touching nothing (`wave.json`
|
||||
stays). Either `wave.json` was written for another plan or another version of
|
||||
this one — re-run with the plan (version) that wrote the wave, or abandon the
|
||||
wave (the way out below) — or the main checkout is on another branch than the
|
||||
wave was prepared on (Step 0's `ORIGINAL_BRANCH` of that run): the user
|
||||
switches back to that branch and re-runs, so the wave never merges elsewhere.
|
||||
|
||||
The JSON names each session's progress file (the file `--session N` writes,
|
||||
Phase 3), its raw `status`, and its `state`, exactly one of:
|
||||
|
|
@ -915,7 +920,8 @@ it never forces: `git worktree remove` without force refuses a worktree with
|
|||
modified or untracked files, `git branch -d` refuses an unmerged branch, and
|
||||
whatever git refuses is kept and reported, never retried with force. From
|
||||
`$ORDERS_DIR` it deletes only `wave.json`: the orders, the archived progress
|
||||
copies and the merged-waves record stay until Step 6 clears them.
|
||||
copies and the merged-waves record stay until the main-merge gate's confirm
|
||||
(Phase 8) clears them.
|
||||
|
||||
```bash
|
||||
cd "$REPO_ROOT" || exit 1
|
||||
|
|
@ -956,16 +962,11 @@ Read every archived copy, `$ORDERS_DIR/wave-*-session-*.progress.json`, into an
|
|||
aggregated report — never the worktree copies, which Steps 3 and 5 remove with
|
||||
their worktrees (earlier waves' in earlier runs).
|
||||
|
||||
Then clear the orders directory — orders, archived copies and the merged-waves
|
||||
record — only when `next-wave` said `none` (every wave merged) AND
|
||||
Step 4 passed, so a later plan with the same `{slug}` starts clean:
|
||||
|
||||
```bash
|
||||
(cd "$REPO_ROOT" && node "$VOYAGE_ROOT/lib/util/session-orders.mjs" clear --session-dir "$SESSION_DIR")
|
||||
```
|
||||
|
||||
On any other outcome keep it: the next run resumes at the lowest wave not yet
|
||||
merged (Step 0), or re-runs Step 4 when every wave is merged. Jump to Phase 8.
|
||||
Keep the orders directory here, whatever the outcome. Only Phase 8's
|
||||
main-merge gate clears it, on confirm, when `next-wave` said `none` (every wave
|
||||
merged) and Step 4 passed. Until then the next run resumes at the lowest wave
|
||||
not yet merged (Step 0), or re-runs Step 4 and returns to the gate when every
|
||||
wave is merged. Jump to Phase 8.
|
||||
|
||||
### --session N mode
|
||||
|
||||
|
|
@ -1644,7 +1645,7 @@ and finalize the run?"
|
|||
| Option | Action |
|
||||
|--------|--------|
|
||||
| **Confirm — main-merge** | Emit `main-merge-approved`, proceed to write progress + state files. |
|
||||
| **Decline — hold for review** | Emit `main-merge-declined`. Wave commits remain on their session branches for inspection. The run is recorded as `partial`. |
|
||||
| **Decline — hold for review** | Emit `main-merge-declined`. Nothing is finalized: the run is recorded as `partial`. The commits stay where they are for review — on the multi-session wave path the waves are already merged on the current branch, and the merged-waves record is kept. |
|
||||
|
||||
This pause is **always on**, regardless of `gates_mode`. The `--gates` flag
|
||||
re-enables earlier per-wave pauses; this gate is the one boundary that
|
||||
|
|
@ -1658,6 +1659,15 @@ node ${CLAUDE_PLUGIN_ROOT}/lib/stats/event-emit.mjs \
|
|||
--payload "{\"plan\":\"${PLAN_PATH}\"}"
|
||||
```
|
||||
|
||||
On the multi-session wave path (Phase 2.6), and only when `next-wave` said
|
||||
`none` (every wave merged) and Step 4 passed, also clear the plan's orders
|
||||
directory — orders, archived progress copies and the merged-waves record — so a
|
||||
later plan with the same `{slug}` starts clean. This is the one place it is
|
||||
cleared:
|
||||
```bash
|
||||
(cd "$REPO_ROOT" && node "$VOYAGE_ROOT/lib/util/session-orders.mjs" clear --session-dir "$SESSION_DIR")
|
||||
```
|
||||
|
||||
On decline:
|
||||
```bash
|
||||
node ${CLAUDE_PLUGIN_ROOT}/lib/stats/event-emit.mjs \
|
||||
|
|
@ -1665,12 +1675,20 @@ node ${CLAUDE_PLUGIN_ROOT}/lib/stats/event-emit.mjs \
|
|||
--payload "{\"plan\":\"${PLAN_PATH}\",\"reason\":\"${reason}\"}"
|
||||
```
|
||||
|
||||
**Recovery surface:** if declined, `--resume re-enters at the gate` —
|
||||
re-running `/trekexecute --resume --project <dir>` jumps directly
|
||||
back to the main-merge gate AskUserQuestion (skipping completed waves).
|
||||
The wave session branches are preserved for inspection (Hard Rule 19's
|
||||
push-before-cleanup ensures they survive on the remote even if local
|
||||
cleanup ran). To inspect: `git log trekplan/<slug>/session-N` per wave.
|
||||
On decline, nothing is cleared: the merged-waves record is kept.
|
||||
|
||||
**Recovery surface:** on the single-session path, if declined,
|
||||
`--resume re-enters at the gate` — re-running
|
||||
`/trekexecute --resume --project <dir>` skips the completed steps and returns
|
||||
to the main-merge gate AskUserQuestion. On the multi-session wave path,
|
||||
re-run the same `/trekexecute` command instead, not `--resume` (Phase 2.5 runs
|
||||
a `--resume` as a single session): the waves are already merged on the current
|
||||
branch, the kept merged-waves record makes Step 0's `next-wave` say `none`,
|
||||
master verification (Step 4) runs again, and the run returns to this gate. The
|
||||
session branches (`trek/{slug}/session-N`) are merged and deleted unless
|
||||
Step 3 or 5 kept them; Step 3 pushed each one before merging it (Hard Rule 19),
|
||||
so where that push succeeded it also survives on the remote. Inspect the
|
||||
integrated result with `git log` on the current branch.
|
||||
|
||||
### Progress + state-file writes
|
||||
|
||||
|
|
@ -1786,7 +1804,8 @@ Attempts: {N}
|
|||
|
||||
{Numbered list of unexecuted steps}
|
||||
|
||||
To resume: /trekexecute --resume {path}
|
||||
To resume (single session): /trekexecute --resume {path}
|
||||
To continue (multi-session waves): re-run the same /trekexecute command (not --resume)
|
||||
```
|
||||
|
||||
**Result vocabulary (v1.7, strict):**
|
||||
|
|
@ -1832,10 +1851,11 @@ The `trekexecute_summary` key makes it grep-able in a session's output.
|
|||
**Multi-session path (Phase 2.6).** Per-session results come from Step 6's
|
||||
aggregate of the archived copies `$ORDERS_DIR/wave-*-session-*.progress.json`.
|
||||
The report lists every worktree and `trek/{slug}/*` branch Steps 3 and 5 kept,
|
||||
with what the user can do (Step 5). If Step 6 kept the orders directory (not
|
||||
every wave merged, or Step 4 did not pass), say so: the next run resumes at the
|
||||
lowest wave not yet merged, and the `clear` command in Step 0 starts the plan
|
||||
over from its first wave instead.
|
||||
with what the user can do (Step 5). If the orders directory was kept (not every
|
||||
wave merged, Step 4 did not pass, or the gate was declined), say so: the next
|
||||
run resumes at the lowest wave not yet merged, or re-runs Step 4 and returns to
|
||||
the gate, and the `clear` command in Step 0 starts the plan over from its first
|
||||
wave instead.
|
||||
|
||||
## Phase 9 — Stats tracking
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@
|
|||
// only reads the plan, writes order files and reads progress files.
|
||||
//
|
||||
// Subcommands (CLI shim at the bottom):
|
||||
// write --plan <repo-relative plan> --wave <W> --session-dir <dir>
|
||||
// write --plan <repo-relative plan> --wave <W> --session-dir <dir> [--branch <b>]
|
||||
// writes <dir>/orders/wave-<W>-session-<N>.md + <dir>/orders/wave.json
|
||||
// and prints the starters
|
||||
// status --plan <repo-relative plan> --wave <W> --session-dir <dir>
|
||||
// (with the branch the wave is prepared on) and prints the starters
|
||||
// status --plan <repo-relative plan> --wave <W> --session-dir <dir> [--branch <b>]
|
||||
// prints JSON: each session's raw progress-file status and its state
|
||||
// (completed | running | ended-bad), running, ended_bad, all_completed
|
||||
// (completed | running | ended-bad), running, ended_bad, all_completed;
|
||||
// refuses when the main checkout is on another branch than wave.json's
|
||||
// archive --plan <repo-relative plan> --wave <W> --session-dir <dir>
|
||||
// copies each progress file to <dir>/orders/wave-<W>-session-<N>.progress.json
|
||||
// (the worktree copy goes with the worktree)
|
||||
|
|
@ -164,13 +165,13 @@ export function buildWaveOrders({ strategy, wave, planPath, sessionDir }) {
|
|||
});
|
||||
}
|
||||
|
||||
export function writeWaveOrders({ strategy, wave, planPath, planSha256, sessionDir, now = new Date() }) {
|
||||
export function writeWaveOrders({ strategy, wave, planPath, planSha256, branch = null, sessionDir, now = new Date() }) {
|
||||
const orders = buildWaveOrders({ strategy, wave, planPath, sessionDir });
|
||||
const dir = join(sessionDir, 'orders');
|
||||
mkdirSync(dir, { recursive: true });
|
||||
for (const o of orders) writeFileSync(join(dir, o.orderFile), o.body);
|
||||
const state = {
|
||||
wave, plan: planPath, plan_sha256: planSha256, sessions: orders.map(o => o.session),
|
||||
wave, plan: planPath, plan_sha256: planSha256, original_branch: branch, sessions: orders.map(o => o.session),
|
||||
status: 'awaiting-sessions', created_at: now.toISOString(),
|
||||
};
|
||||
writeFileSync(join(dir, 'wave.json'), JSON.stringify(state, null, 2) + '\n');
|
||||
|
|
@ -196,11 +197,16 @@ function samePlan(file, rec, planPath, planSha256, what) {
|
|||
+ `not ${planPath} (plan sha256 ${short(planSha256)})`);
|
||||
}
|
||||
|
||||
export function waveStatus({ strategy, wave, planPath, planSha256, sessionDir }) {
|
||||
export function waveStatus({ strategy, wave, planPath, planSha256, branch = null, sessionDir }) {
|
||||
const waveFile = join(sessionDir, 'orders', 'wave.json');
|
||||
if (existsSync(waveFile)) {
|
||||
samePlan(waveFile, JSON.parse(readFileSync(waveFile, 'utf-8')), planPath, planSha256,
|
||||
'was written for another plan or another version of it:');
|
||||
const state = JSON.parse(readFileSync(waveFile, 'utf-8'));
|
||||
samePlan(waveFile, state, planPath, planSha256, 'was written for another plan or another version of it:');
|
||||
// The wave merges back into the branch it was prepared on, nowhere else.
|
||||
if (state.original_branch && state.original_branch !== branch) {
|
||||
throw new Error(`the wave in ${waveFile} was prepared on branch ${state.original_branch}, `
|
||||
+ `but the main checkout is on ${branch || 'an unnamed branch'}. Switch back to ${state.original_branch} and re-run.`);
|
||||
}
|
||||
}
|
||||
const members = strategy.sessions.filter(s => s.wave === wave);
|
||||
if (members.length === 0) throw new Error(`no sessions in wave ${wave}`);
|
||||
|
|
@ -285,6 +291,7 @@ function parseArgs(argv) {
|
|||
if (a === '--plan') out.plan = argv[++i];
|
||||
else if (a === '--wave') out.wave = Number(argv[++i]);
|
||||
else if (a === '--session-dir') out.sessionDir = argv[++i];
|
||||
else if (a === '--branch') out.branch = argv[++i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -296,7 +303,7 @@ const NEEDS = {
|
|||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
const usage = 'usage: session-orders.mjs write|status|archive|record-merged --plan <repo-relative plan> --wave <W> --session-dir <dir>\n'
|
||||
const usage = 'usage: session-orders.mjs write|status|archive|record-merged --plan <repo-relative plan> --wave <W> --session-dir <dir> [--branch <b>]\n'
|
||||
+ ' session-orders.mjs next-wave --plan <repo-relative plan> --session-dir <dir>\n'
|
||||
+ ' session-orders.mjs clear --session-dir <dir>';
|
||||
const needs = NEEDS[args.cmd];
|
||||
|
|
@ -312,7 +319,7 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|||
const planBytes = readFileSync(args.plan);
|
||||
const strategy = parseExecutionStrategy(planBytes.toString('utf-8'));
|
||||
const planSha256 = createHash('sha256').update(planBytes).digest('hex');
|
||||
const opts = { strategy, wave: args.wave, planPath: args.plan, planSha256, sessionDir: args.sessionDir };
|
||||
const opts = { strategy, wave: args.wave, planPath: args.plan, planSha256, branch: args.branch || null, sessionDir: args.sessionDir };
|
||||
if (args.cmd === 'status') {
|
||||
process.stdout.write(JSON.stringify(waveStatus(opts), null, 2) + '\n');
|
||||
} else if (args.cmd === 'archive') {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue