taskplane 0.1.14 → 0.1.16
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/bin/taskplane.mjs +317 -9
- package/extensions/taskplane/abort.ts +461 -466
- package/extensions/taskplane/config.ts +17 -12
- package/extensions/taskplane/discovery.ts +168 -32
- package/extensions/taskplane/engine.ts +781 -758
- package/extensions/taskplane/execution.ts +112 -48
- package/extensions/taskplane/extension.ts +780 -693
- package/extensions/taskplane/git.ts +25 -7
- package/extensions/taskplane/index.ts +23 -22
- package/extensions/taskplane/merge.ts +18 -16
- package/extensions/taskplane/messages.ts +146 -134
- package/extensions/taskplane/persistence.ts +1136 -1121
- package/extensions/taskplane/resume.ts +1102 -1092
- package/extensions/taskplane/types.ts +243 -3
- package/extensions/taskplane/waves.ts +894 -900
- package/extensions/taskplane/workspace.ts +382 -0
- package/extensions/taskplane/worktree.ts +113 -11
- package/package.json +1 -1
- package/templates/config/task-orchestrator.yaml +86 -89
- package/templates/config/task-runner.yaml +95 -99
|
@@ -1,10 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git command runner
|
|
3
|
-
* @module orch/git
|
|
4
|
-
*/
|
|
5
|
-
import { execFileSync } from "child_process";
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Git command runner
|
|
3
|
+
* @module orch/git
|
|
4
|
+
*/
|
|
5
|
+
import { execFileSync } from "child_process";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// ── Branch Helpers ───────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get the current branch name (the branch checked out in the given directory).
|
|
12
|
+
*
|
|
13
|
+
* Uses `git rev-parse --abbrev-ref HEAD`. Returns the branch name or null
|
|
14
|
+
* if HEAD is detached or git fails.
|
|
15
|
+
*
|
|
16
|
+
* @param cwd - Working directory (defaults to process.cwd())
|
|
17
|
+
*/
|
|
18
|
+
export function getCurrentBranch(cwd?: string): string | null {
|
|
19
|
+
const result = runGit(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
20
|
+
if (!result.ok || !result.stdout.trim() || result.stdout.trim() === "HEAD") {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return result.stdout.trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
8
26
|
// ── Git Command Runner ───────────────────────────────────────────────
|
|
9
27
|
|
|
10
28
|
/**
|
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Task Orchestrator — barrel re-export
|
|
3
|
-
*
|
|
4
|
-
* Provides a single import point for all orchestrator modules.
|
|
5
|
-
* Usage: import { executeOrchBatch, ... } from "./taskplane/index.ts";
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export * from "./types.ts";
|
|
9
|
-
export * from "./config.ts";
|
|
10
|
-
export * from "./git.ts";
|
|
11
|
-
export * from "./worktree.ts";
|
|
12
|
-
export * from "./discovery.ts";
|
|
13
|
-
export * from "./waves.ts";
|
|
14
|
-
export * from "./formatting.ts";
|
|
15
|
-
export * from "./execution.ts";
|
|
16
|
-
export * from "./merge.ts";
|
|
17
|
-
export * from "./messages.ts";
|
|
18
|
-
export * from "./sessions.ts";
|
|
19
|
-
export * from "./persistence.ts";
|
|
20
|
-
export * from "./engine.ts";
|
|
21
|
-
export * from "./resume.ts";
|
|
22
|
-
export * from "./abort.ts";
|
|
1
|
+
/**
|
|
2
|
+
* Task Orchestrator — barrel re-export
|
|
3
|
+
*
|
|
4
|
+
* Provides a single import point for all orchestrator modules.
|
|
5
|
+
* Usage: import { executeOrchBatch, ... } from "./taskplane/index.ts";
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export * from "./types.ts";
|
|
9
|
+
export * from "./config.ts";
|
|
10
|
+
export * from "./git.ts";
|
|
11
|
+
export * from "./worktree.ts";
|
|
12
|
+
export * from "./discovery.ts";
|
|
13
|
+
export * from "./waves.ts";
|
|
14
|
+
export * from "./formatting.ts";
|
|
15
|
+
export * from "./execution.ts";
|
|
16
|
+
export * from "./merge.ts";
|
|
17
|
+
export * from "./messages.ts";
|
|
18
|
+
export * from "./sessions.ts";
|
|
19
|
+
export * from "./persistence.ts";
|
|
20
|
+
export * from "./engine.ts";
|
|
21
|
+
export * from "./resume.ts";
|
|
22
|
+
export * from "./abort.ts";
|
|
23
|
+
export * from "./workspace.ts";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Merge orchestration, merge agents, merge worktree
|
|
3
|
-
* @module orch/merge
|
|
4
|
-
*/
|
|
5
|
-
import { readFileSync, writeFileSync, existsSync, unlinkSync } from "fs";
|
|
6
|
-
import { spawnSync } from "child_process";
|
|
7
|
-
import { join } from "path";
|
|
8
|
-
|
|
9
|
-
import { buildLaneEnvVars, buildTmuxSpawnArgs, execLog, tmuxHasSession, tmuxKillSession, toTmuxPath } from "./execution.ts";
|
|
10
|
-
import { MERGE_POLL_INTERVAL_MS, MERGE_RESULT_GRACE_MS, MERGE_RESULT_READ_RETRIES, MERGE_RESULT_READ_RETRY_DELAY_MS, MERGE_SPAWN_RETRY_MAX, MERGE_TIMEOUT_MS, MergeError, VALID_MERGE_STATUSES } from "./types.ts";
|
|
11
|
-
import type { AllocatedLane, LaneExecutionResult, MergeLaneResult, MergeResult, MergeResultStatus, MergeWaveResult, OrchestratorConfig, WaveExecutionResult } from "./types.ts";
|
|
12
|
-
import { sleepSync } from "./worktree.ts";
|
|
13
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Merge orchestration, merge agents, merge worktree
|
|
3
|
+
* @module orch/merge
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync } from "fs";
|
|
6
|
+
import { spawnSync } from "child_process";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
|
|
9
|
+
import { buildLaneEnvVars, buildTmuxSpawnArgs, execLog, tmuxHasSession, tmuxKillSession, toTmuxPath } from "./execution.ts";
|
|
10
|
+
import { MERGE_POLL_INTERVAL_MS, MERGE_RESULT_GRACE_MS, MERGE_RESULT_READ_RETRIES, MERGE_RESULT_READ_RETRY_DELAY_MS, MERGE_SPAWN_RETRY_MAX, MERGE_TIMEOUT_MS, MergeError, VALID_MERGE_STATUSES } from "./types.ts";
|
|
11
|
+
import type { AllocatedLane, LaneExecutionResult, MergeLaneResult, MergeResult, MergeResultStatus, MergeWaveResult, OrchestratorConfig, WaveExecutionResult } from "./types.ts";
|
|
12
|
+
import { sleepSync } from "./worktree.ts";
|
|
13
|
+
|
|
14
14
|
// ── Merge Implementation ─────────────────────────────────────────────
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -451,7 +451,7 @@ export function waitForMergeResult(
|
|
|
451
451
|
}
|
|
452
452
|
|
|
453
453
|
/**
|
|
454
|
-
* Merge a completed wave's lane branches into the
|
|
454
|
+
* Merge a completed wave's lane branches into the base branch.
|
|
455
455
|
*
|
|
456
456
|
* Orchestration flow:
|
|
457
457
|
* 1. Filter to only succeeded lanes (failed lanes are not merged)
|
|
@@ -464,7 +464,7 @@ export function waitForMergeResult(
|
|
|
464
464
|
* e. Handle result (continue, log, or pause)
|
|
465
465
|
* 4. Return MergeWaveResult
|
|
466
466
|
*
|
|
467
|
-
* Sequential execution is mandatory — the
|
|
467
|
+
* Sequential execution is mandatory — the base branch is a shared
|
|
468
468
|
* resource, and each merge must see the prior merge's result.
|
|
469
469
|
*
|
|
470
470
|
* On CONFLICT_UNRESOLVED or BUILD_FAILURE: stops merging remaining lanes
|
|
@@ -479,6 +479,7 @@ export function waitForMergeResult(
|
|
|
479
479
|
* @param config - Orchestrator configuration
|
|
480
480
|
* @param repoRoot - Main repository root
|
|
481
481
|
* @param batchId - Batch ID for session naming
|
|
482
|
+
* @param baseBranch - Branch to merge into (captured at batch start)
|
|
482
483
|
* @returns MergeWaveResult with per-lane outcomes
|
|
483
484
|
*/
|
|
484
485
|
export function mergeWave(
|
|
@@ -488,10 +489,11 @@ export function mergeWave(
|
|
|
488
489
|
config: OrchestratorConfig,
|
|
489
490
|
repoRoot: string,
|
|
490
491
|
batchId: string,
|
|
492
|
+
baseBranch: string,
|
|
491
493
|
): MergeWaveResult {
|
|
492
494
|
const startTime = Date.now();
|
|
493
495
|
const tmuxPrefix = config.orchestrator.tmux_prefix;
|
|
494
|
-
const targetBranch =
|
|
496
|
+
const targetBranch = baseBranch;
|
|
495
497
|
const laneResults: MergeLaneResult[] = [];
|
|
496
498
|
|
|
497
499
|
// Build lane outcome lookup for merge eligibility checks.
|
|
@@ -1,134 +1,146 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* User-facing message templates (ORCH_MESSAGES)
|
|
3
|
-
* @module orch/messages
|
|
4
|
-
*/
|
|
5
|
-
import type { AbortMode } from "./types.ts";
|
|
6
|
-
|
|
7
|
-
// ── Message Templates ────────────────────────────────────────────────
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Deterministic message templates for user-facing /orch commands.
|
|
11
|
-
* Ensures consistent UX across invocations.
|
|
12
|
-
*/
|
|
13
|
-
export const ORCH_MESSAGES = {
|
|
14
|
-
// /orch
|
|
15
|
-
orchStarting: (batchId: string, waves: number, tasks: number) =>
|
|
16
|
-
`🚀 Starting batch ${batchId}: ${waves} wave(s), ${tasks} task(s)`,
|
|
17
|
-
orchWaveStart: (waveNum: number, totalWaves: number, tasks: number, lanes: number) =>
|
|
18
|
-
`\n🌊 Wave ${waveNum}/${totalWaves}: ${tasks} task(s) across ${lanes} lane(s)`,
|
|
19
|
-
orchWaveComplete: (waveNum: number, succeeded: number, failed: number, skipped: number, elapsedSec: number) =>
|
|
20
|
-
`✅ Wave ${waveNum} complete: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped (${elapsedSec}s)`,
|
|
21
|
-
orchMergeStart: (waveNum: number, laneCount: number) =>
|
|
22
|
-
`🔀 [Wave ${waveNum}] Merging ${laneCount} lane(s) into develop...`,
|
|
23
|
-
orchMergeLaneSuccess: (laneNum: number, commit: string, durationSec: number) =>
|
|
24
|
-
` ✅ Lane ${laneNum} merged (${commit.slice(0, 8)}, ${durationSec}s)`,
|
|
25
|
-
orchMergeLaneConflictResolved: (laneNum: number, conflictCount: number, durationSec: number) =>
|
|
26
|
-
` ⚡ Lane ${laneNum} merged with ${conflictCount} auto-resolved conflict(s) (${durationSec}s)`,
|
|
27
|
-
orchMergeLaneFailed: (laneNum: number, reason: string) =>
|
|
28
|
-
` ❌ Lane ${laneNum} merge failed: ${reason}`,
|
|
29
|
-
orchMergeComplete: (waveNum: number, mergedCount: number, totalSec: number) =>
|
|
30
|
-
`🔀 [Wave ${waveNum}] Merge complete: ${mergedCount} lane(s) merged (${totalSec}s)`,
|
|
31
|
-
orchMergeFailed: (waveNum: number, laneNum: number, reason: string) =>
|
|
32
|
-
`❌ [Wave ${waveNum}] Merge failed at lane ${laneNum}: ${reason}`,
|
|
33
|
-
orchMergeSkipped: (waveNum: number) =>
|
|
34
|
-
`📝 [Wave ${waveNum}] No successful lanes to merge`,
|
|
35
|
-
orchMergePlaceholder: (waveNum: number) =>
|
|
36
|
-
`🔀 [Wave ${waveNum}] Merge: placeholder — Step 3 (TS-008) will replace with mergeWave()`,
|
|
37
|
-
orchWorktreeReset: (waveNum: number, lanes: number) =>
|
|
38
|
-
`🔄 Resetting ${lanes} worktree(s) to develop HEAD after wave ${waveNum}`,
|
|
39
|
-
orchBatchComplete: (batchId: string, succeeded: number, failed: number, skipped: number, blocked: number, elapsedSec: number) =>
|
|
40
|
-
`\n🏁 Batch ${batchId} complete: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped, ${blocked} blocked (${elapsedSec}s)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
|
|
1
|
+
/**
|
|
2
|
+
* User-facing message templates (ORCH_MESSAGES)
|
|
3
|
+
* @module orch/messages
|
|
4
|
+
*/
|
|
5
|
+
import type { AbortMode } from "./types.ts";
|
|
6
|
+
|
|
7
|
+
// ── Message Templates ────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Deterministic message templates for user-facing /orch commands.
|
|
11
|
+
* Ensures consistent UX across invocations.
|
|
12
|
+
*/
|
|
13
|
+
export const ORCH_MESSAGES = {
|
|
14
|
+
// /orch
|
|
15
|
+
orchStarting: (batchId: string, waves: number, tasks: number) =>
|
|
16
|
+
`🚀 Starting batch ${batchId}: ${waves} wave(s), ${tasks} task(s)`,
|
|
17
|
+
orchWaveStart: (waveNum: number, totalWaves: number, tasks: number, lanes: number) =>
|
|
18
|
+
`\n🌊 Wave ${waveNum}/${totalWaves}: ${tasks} task(s) across ${lanes} lane(s)`,
|
|
19
|
+
orchWaveComplete: (waveNum: number, succeeded: number, failed: number, skipped: number, elapsedSec: number) =>
|
|
20
|
+
`✅ Wave ${waveNum} complete: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped (${elapsedSec}s)`,
|
|
21
|
+
orchMergeStart: (waveNum: number, laneCount: number) =>
|
|
22
|
+
`🔀 [Wave ${waveNum}] Merging ${laneCount} lane(s) into develop...`,
|
|
23
|
+
orchMergeLaneSuccess: (laneNum: number, commit: string, durationSec: number) =>
|
|
24
|
+
` ✅ Lane ${laneNum} merged (${commit.slice(0, 8)}, ${durationSec}s)`,
|
|
25
|
+
orchMergeLaneConflictResolved: (laneNum: number, conflictCount: number, durationSec: number) =>
|
|
26
|
+
` ⚡ Lane ${laneNum} merged with ${conflictCount} auto-resolved conflict(s) (${durationSec}s)`,
|
|
27
|
+
orchMergeLaneFailed: (laneNum: number, reason: string) =>
|
|
28
|
+
` ❌ Lane ${laneNum} merge failed: ${reason}`,
|
|
29
|
+
orchMergeComplete: (waveNum: number, mergedCount: number, totalSec: number) =>
|
|
30
|
+
`🔀 [Wave ${waveNum}] Merge complete: ${mergedCount} lane(s) merged (${totalSec}s)`,
|
|
31
|
+
orchMergeFailed: (waveNum: number, laneNum: number, reason: string) =>
|
|
32
|
+
`❌ [Wave ${waveNum}] Merge failed at lane ${laneNum}: ${reason}`,
|
|
33
|
+
orchMergeSkipped: (waveNum: number) =>
|
|
34
|
+
`📝 [Wave ${waveNum}] No successful lanes to merge`,
|
|
35
|
+
orchMergePlaceholder: (waveNum: number) =>
|
|
36
|
+
`🔀 [Wave ${waveNum}] Merge: placeholder — Step 3 (TS-008) will replace with mergeWave()`,
|
|
37
|
+
orchWorktreeReset: (waveNum: number, lanes: number) =>
|
|
38
|
+
`🔄 Resetting ${lanes} worktree(s) to develop HEAD after wave ${waveNum}`,
|
|
39
|
+
orchBatchComplete: (batchId: string, succeeded: number, failed: number, skipped: number, blocked: number, elapsedSec: number) => {
|
|
40
|
+
const lines = [`\n🏁 Batch ${batchId} complete: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped, ${blocked} blocked (${elapsedSec}s)`];
|
|
41
|
+
if (failed > 0 || blocked > 0) {
|
|
42
|
+
lines.push("");
|
|
43
|
+
if (blocked > 0) {
|
|
44
|
+
lines.push(` ${blocked} task(s) were blocked because upstream tasks failed.`);
|
|
45
|
+
}
|
|
46
|
+
lines.push(" Next steps:");
|
|
47
|
+
lines.push(" • /orch-status — review what failed and why");
|
|
48
|
+
lines.push(" • /orch-resume — retry from the failed wave");
|
|
49
|
+
lines.push(" • /orch-abort — clean up and start fresh");
|
|
50
|
+
}
|
|
51
|
+
return lines.join("\n");
|
|
52
|
+
},
|
|
53
|
+
orchBatchFailed: (batchId: string, reason: string) =>
|
|
54
|
+
`\n❌ Batch ${batchId} failed: ${reason}`,
|
|
55
|
+
orchBatchStopped: (batchId: string, policy: string) =>
|
|
56
|
+
`\n⛔ Batch ${batchId} stopped by ${policy} policy`,
|
|
57
|
+
|
|
58
|
+
// /orch-pause
|
|
59
|
+
pauseNoBatch: () => "No active batch is running. Use /orch <areas|all> to start.",
|
|
60
|
+
pauseAlreadyPaused: (batchId: string) => `Batch ${batchId} is already paused.`,
|
|
61
|
+
pauseActivated: (batchId: string) =>
|
|
62
|
+
`⏸️ Pausing batch ${batchId}... lanes will stop after their current tasks complete.`,
|
|
63
|
+
|
|
64
|
+
// /orch-sessions
|
|
65
|
+
sessionsNone: () => "No orchestrator TMUX sessions found.",
|
|
66
|
+
sessionsHeader: (count: number) => `🖥️ ${count} orchestrator session(s):`,
|
|
67
|
+
|
|
68
|
+
// /orch orphan detection
|
|
69
|
+
orphanDetectionResume: (batchId: string, sessionCount: number) =>
|
|
70
|
+
`🔄 Found ${sessionCount} running orchestrator session(s) from batch ${batchId}.\n` +
|
|
71
|
+
` Use /orch-resume to continue, or /orch-abort to clean up.`,
|
|
72
|
+
orphanDetectionAbort: (sessionCount: number) =>
|
|
73
|
+
`⚠️ Found ${sessionCount} orphan orchestrator session(s) without usable state.\n` +
|
|
74
|
+
` Use /orch-abort to clean up before starting a new batch.`,
|
|
75
|
+
orphanDetectionCleanup: () =>
|
|
76
|
+
`🧹 Cleaned up stale batch state file. Starting fresh.`,
|
|
77
|
+
|
|
78
|
+
// /orch-resume
|
|
79
|
+
resumeStarting: (batchId: string, phase: string) =>
|
|
80
|
+
`🔄 Resuming batch ${batchId} (was: ${phase})...`,
|
|
81
|
+
resumeReconciled: (batchId: string, completed: number, pending: number, failed: number, reconnecting: number, reExecuting: number = 0) =>
|
|
82
|
+
`📊 Batch ${batchId} reconciliation: ${completed} completed, ${pending} pending, ${failed} failed, ${reconnecting} reconnecting` +
|
|
83
|
+
(reExecuting > 0 ? `, ${reExecuting} re-executing` : ""),
|
|
84
|
+
resumeSkippedWaves: (skippedCount: number) =>
|
|
85
|
+
`⏭️ Skipping ${skippedCount} completed wave(s)`,
|
|
86
|
+
resumeReconnecting: (sessionCount: number) =>
|
|
87
|
+
`🔗 Reconnecting to ${sessionCount} alive session(s)...`,
|
|
88
|
+
resumeNoState: () =>
|
|
89
|
+
`❌ No batch to resume. No batch-state.json file found.\n` +
|
|
90
|
+
` Use /orch <areas|all> to start a new batch.`,
|
|
91
|
+
resumeInvalidState: (error: string) =>
|
|
92
|
+
`❌ Cannot resume: batch state file is invalid.\n` +
|
|
93
|
+
` Error: ${error}\n` +
|
|
94
|
+
` Delete .pi/batch-state.json and start a new batch.`,
|
|
95
|
+
resumePhaseNotResumable: (batchId: string, phase: string, reason: string) =>
|
|
96
|
+
`❌ Cannot resume batch ${batchId} (phase: ${phase}).\n` +
|
|
97
|
+
` ${reason}`,
|
|
98
|
+
resumeComplete: (batchId: string, succeeded: number, failed: number, skipped: number, blocked: number, elapsedSec: number) =>
|
|
99
|
+
`\n🏁 Resumed batch ${batchId} complete: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped, ${blocked} blocked (${elapsedSec}s total)`,
|
|
100
|
+
|
|
101
|
+
// /orch-abort
|
|
102
|
+
abortGracefulStarting: (batchId: string, sessionCount: number) =>
|
|
103
|
+
`⏳ Graceful abort of batch ${batchId}: signaling ${sessionCount} session(s) to checkpoint and exit...`,
|
|
104
|
+
abortGracefulWaiting: (batchId: string, graceSec: number) =>
|
|
105
|
+
`⏳ Waiting up to ${graceSec}s for sessions to checkpoint and exit...`,
|
|
106
|
+
abortGracefulForceKill: (count: number) =>
|
|
107
|
+
`⚠️ Force-killing ${count} session(s) that did not exit within timeout`,
|
|
108
|
+
abortGracefulComplete: (batchId: string, graceful: number, forceKilled: number, durationSec: number) =>
|
|
109
|
+
`✅ Graceful abort complete for batch ${batchId}: ${graceful} exited gracefully, ${forceKilled} force-killed (${durationSec}s)`,
|
|
110
|
+
abortHardStarting: (batchId: string, sessionCount: number) =>
|
|
111
|
+
`⚡ Hard abort of batch ${batchId}: killing ${sessionCount} session(s) immediately...`,
|
|
112
|
+
abortHardComplete: (batchId: string, killed: number, durationSec: number) =>
|
|
113
|
+
`✅ Hard abort complete for batch ${batchId}: ${killed} session(s) killed (${durationSec}s)`,
|
|
114
|
+
abortPartialFailure: (failureCount: number) =>
|
|
115
|
+
`⚠️ ${failureCount} error(s) during abort (see details above)`,
|
|
116
|
+
abortNoBatch: () =>
|
|
117
|
+
`No active batch to abort. Use /orch <areas|all> to start a batch.`,
|
|
118
|
+
abortComplete: (mode: AbortMode, sessionsKilled: number) =>
|
|
119
|
+
`🏁 Abort (${mode}) complete: ${sessionsKilled} session(s) terminated. Worktrees and branches preserved.`,
|
|
120
|
+
} as const;
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
// ── Resume ORCH_MESSAGES ─────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
// Note: These are added via extension to the ORCH_MESSAGES object below.
|
|
126
|
+
|
|
127
|
+
// ── Resume Orchestration ─────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Resume an interrupted batch from persisted state.
|
|
131
|
+
*
|
|
132
|
+
* Flow:
|
|
133
|
+
* 1. Load and validate batch-state.json
|
|
134
|
+
* 2. Check phase eligibility (paused/executing/merging only)
|
|
135
|
+
* 3. Check for alive TMUX sessions and .DONE files
|
|
136
|
+
* 4. Reconcile persisted state against live signals
|
|
137
|
+
* 5. Compute resume point (which wave to start from)
|
|
138
|
+
* 6. Reconstruct runtime state and continue execution
|
|
139
|
+
*
|
|
140
|
+
* @param orchConfig - Orchestrator configuration
|
|
141
|
+
* @param runnerConfig - Task runner configuration
|
|
142
|
+
* @param cwd - Repository root
|
|
143
|
+
* @param batchState - Mutable batch state (will be populated from persisted state)
|
|
144
|
+
* @param onNotify - Callback for user-facing messages
|
|
145
|
+
* @param onMonitorUpdate - Optional callback for dashboard updates
|
|
146
|
+
*/
|