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:
Kjell Tore Guttormsen 2026-10-02 16:24:33 +02:00
commit d70e3d2af7
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
2 changed files with 72 additions and 45 deletions

View file

@ -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') {