taskplane 0.29.2 → 0.30.1

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.
Files changed (43) hide show
  1. package/bin/gitignore-patterns.mjs +11 -8
  2. package/bin/rpc-wrapper.mjs +410 -357
  3. package/bin/taskplane.mjs +533 -250
  4. package/dashboard/public/app.js +124 -15
  5. package/dashboard/public/style.css +83 -2
  6. package/extensions/reviewer-extension.ts +17 -11
  7. package/extensions/taskplane/abort.ts +50 -18
  8. package/extensions/taskplane/agent-bridge-extension.ts +232 -105
  9. package/extensions/taskplane/agent-host.ts +224 -97
  10. package/extensions/taskplane/cleanup.ts +71 -42
  11. package/extensions/taskplane/config-loader.ts +142 -58
  12. package/extensions/taskplane/config-schema.ts +6 -13
  13. package/extensions/taskplane/config.ts +10 -2
  14. package/extensions/taskplane/diagnostic-reports.ts +59 -47
  15. package/extensions/taskplane/diagnostics.ts +13 -13
  16. package/extensions/taskplane/discovery.ts +78 -63
  17. package/extensions/taskplane/engine-worker.ts +53 -46
  18. package/extensions/taskplane/engine.ts +1760 -602
  19. package/extensions/taskplane/execution.ts +469 -207
  20. package/extensions/taskplane/extension.ts +1073 -598
  21. package/extensions/taskplane/formatting.ts +136 -124
  22. package/extensions/taskplane/git.ts +0 -2
  23. package/extensions/taskplane/lane-runner.ts +652 -319
  24. package/extensions/taskplane/mailbox.ts +57 -49
  25. package/extensions/taskplane/merge.ts +662 -383
  26. package/extensions/taskplane/messages.ts +109 -51
  27. package/extensions/taskplane/migrations.ts +1 -1
  28. package/extensions/taskplane/path-resolver.ts +8 -9
  29. package/extensions/taskplane/persistence.ts +425 -262
  30. package/extensions/taskplane/process-registry.ts +36 -7
  31. package/extensions/taskplane/quality-gate.ts +107 -55
  32. package/extensions/taskplane/resume.ts +832 -280
  33. package/extensions/taskplane/sessions.ts +1 -1
  34. package/extensions/taskplane/settings-tui.ts +505 -164
  35. package/extensions/taskplane/sidecar-telemetry.ts +25 -10
  36. package/extensions/taskplane/supervisor.ts +477 -270
  37. package/extensions/taskplane/task-executor-core.ts +178 -53
  38. package/extensions/taskplane/types.ts +209 -108
  39. package/extensions/taskplane/verification.ts +27 -22
  40. package/extensions/taskplane/waves.ts +59 -43
  41. package/extensions/taskplane/workspace.ts +14 -12
  42. package/extensions/taskplane/worktree.ts +218 -196
  43. package/package.json +14 -2
@@ -24,6 +24,17 @@ export interface OrchestratorConfig {
24
24
  operator_id: string;
25
25
  /** How completed batches are integrated. manual = user runs /orch-integrate. supervised = supervisor proposes plan, asks confirmation. auto = supervisor executes without asking. */
26
26
  integration: "manual" | "supervised" | "auto";
27
+ /**
28
+ * Optional pre-resolved batch ID injected by callers that already
29
+ * know the batch identity (e.g., resumed orchestrations). When
30
+ * absent, callers fall back to the `ORCH_BATCH_ID` env var or a
31
+ * timestamp. Read by `executeLaneV2` (execution.ts).
32
+ *
33
+ * @since TP-195 (#TBD) — documented field that was already being
34
+ * read at runtime via `config.orchestrator?.batchId` and asserted
35
+ * by the source-grep invariant in `runtime-model-fallback.test.ts`.
36
+ */
37
+ batchId?: string;
27
38
  };
28
39
  dependencies: {
29
40
  source: "prompt" | "agent";
@@ -158,13 +169,40 @@ export function parseSegmentIdRepo(segment: { repoId: string }): string {
158
169
  /** Build a dynamic segment expansion request ID (`exp-{timestamp}-{random5}`). */
159
170
  export function buildExpansionRequestId(timestamp = Date.now()): string {
160
171
  const ts = Number.isFinite(timestamp) ? Math.floor(timestamp) : Date.now();
161
- const base = Math.random().toString(36).slice(2).toLowerCase().replace(/[^a-z0-9]/g, "");
172
+ const base = Math.random()
173
+ .toString(36)
174
+ .slice(2)
175
+ .toLowerCase()
176
+ .replace(/[^a-z0-9]/g, "");
162
177
  const random5 = (base + "00000").slice(0, 5);
163
178
  return `exp-${ts}-${random5}`;
164
179
  }
165
180
 
166
181
  // ── Step-Segment Mapping (Phase A: segment-scoped worker visibility) ────
167
182
 
183
+ /**
184
+ * Authoritative segment-scope mode for a single worker iteration.
185
+ *
186
+ * - `FULL_TASK`: the worker sees the entire PROMPT.md, all steps, all checkboxes.
187
+ * No `Active segment ID` / `Your checkboxes for this step` prose is injected.
188
+ * Segment-related environment variables (`TASKPLANE_ACTIVE_SEGMENT_ID`,
189
+ * `TASKPLANE_SEGMENT_ID`) are hard-cleared so that runtime tools keyed on
190
+ * them (e.g., `request_segment_expansion`) cannot accidentally register.
191
+ *
192
+ * - `SEGMENT_SCOPED`: the worker is iterating a specific segment of a
193
+ * multi-segment task. Only that segment's steps and checkboxes are shown;
194
+ * `Active segment ID` is announced; segment-related env vars carry the
195
+ * active `segmentId`; the segment-overlay system prompt is appended.
196
+ *
197
+ * This is the single authoritative flag for the segment-scope decision
198
+ * (TP-196 / #502). Call sites should derive their behaviour from this mode
199
+ * rather than re-evaluating the underlying boolean conditions, which prevents
200
+ * the multiple branches drifting out of sync.
201
+ *
202
+ * @since TP-196
203
+ */
204
+ export type SegmentScopeMode = "FULL_TASK" | "SEGMENT_SCOPED";
205
+
168
206
  /** A group of checkboxes scoped to a single repo within a step. */
169
207
  export interface SegmentCheckboxGroup {
170
208
  repoId: string;
@@ -365,7 +403,6 @@ export interface PreflightCheck {
365
403
  hint?: string;
366
404
  }
367
405
 
368
-
369
406
  // ── Defaults ─────────────────────────────────────────────────────────
370
407
 
371
408
  export const DEFAULT_ORCHESTRATOR_CONFIG: OrchestratorConfig = {
@@ -428,7 +465,6 @@ export const DEFAULT_TASK_RUNNER_CONFIG: TaskRunnerConfig = {
428
465
  model_fallback: "inherit",
429
466
  };
430
467
 
431
-
432
468
  // ── Helpers ──────────────────────────────────────────────────────────
433
469
 
434
470
  export function freshBatchState(): BatchState {
@@ -598,7 +634,12 @@ export interface RemoveAllWorktreesResult {
598
634
  /** All per-worktree outcomes in order */
599
635
  outcomes: RemoveWorktreeOutcome[];
600
636
  /** Branches preserved (had unmerged commits) */
601
- preserved: Array<{ branch: string; savedBranch: string; laneNumber: number; unmergedCount?: number }>;
637
+ preserved: Array<{
638
+ branch: string;
639
+ savedBranch: string;
640
+ laneNumber: number;
641
+ unmergedCount?: number;
642
+ }>;
602
643
  }
603
644
 
604
645
  // ── Discovery Types ──────────────────────────────────────────────────
@@ -656,7 +697,6 @@ export interface DiscoveryResult {
656
697
  errors: DiscoveryError[];
657
698
  }
658
699
 
659
-
660
700
  // ── Wave Computation Types ───────────────────────────────────────────
661
701
 
662
702
  /** Dependency graph: adjacency list (task → tasks it depends on) */
@@ -683,7 +723,6 @@ export interface WaveComputationResult {
683
723
  segmentPlans?: TaskSegmentPlanMap;
684
724
  }
685
725
 
686
-
687
726
  // ── Lane Allocation (Phase 3) ────────────────────────────────────────
688
727
 
689
728
  /**
@@ -760,7 +799,6 @@ export interface AllocatedLane {
760
799
  repoId?: string;
761
800
  }
762
801
 
763
-
764
802
  // ── Execution Types & Contracts ──────────────────────────────────────
765
803
 
766
804
  /**
@@ -921,7 +959,8 @@ export type ExecutionErrorCode =
921
959
  | "EXEC_TASK_STAGE_FAILED"
922
960
  | "EXEC_TASK_COMMIT_FAILED"
923
961
  | "EXEC_TMUX_NOT_AVAILABLE"
924
- | "EXEC_WORKTREE_MISSING";
962
+ | "EXEC_WORKTREE_MISSING"
963
+ | "EXEC_MISSING_TASK_FOLDER";
925
964
 
926
965
  /** Typed error for lane execution failures. */
927
966
  export class ExecutionError extends Error {
@@ -938,7 +977,6 @@ export class ExecutionError extends Error {
938
977
  }
939
978
  }
940
979
 
941
-
942
980
  // ── Monitoring Types & Contracts ─────────────────────────────────────
943
981
 
944
982
  /**
@@ -1050,7 +1088,6 @@ export interface MtimeTracker {
1050
1088
  stallTimerStart: number | null;
1051
1089
  }
1052
1090
 
1053
-
1054
1091
  // ── Wave Execution Types & Contracts ─────────────────────────────────
1055
1092
 
1056
1093
  /**
@@ -1122,7 +1159,6 @@ export interface WaveExecutionResult {
1122
1159
  } | null;
1123
1160
  }
1124
1161
 
1125
-
1126
1162
  // ── Orchestrator Runtime State ───────────────────────────────────────
1127
1163
 
1128
1164
  /**
@@ -1135,7 +1171,16 @@ export interface WaveExecutionResult {
1135
1171
  * → paused (via /orch-pause)
1136
1172
  * Any active state → idle (via cleanup after completion/failure)
1137
1173
  */
1138
- export type OrchBatchPhase = "idle" | "launching" | "planning" | "executing" | "merging" | "paused" | "stopped" | "completed" | "failed";
1174
+ export type OrchBatchPhase =
1175
+ | "idle"
1176
+ | "launching"
1177
+ | "planning"
1178
+ | "executing"
1179
+ | "merging"
1180
+ | "paused"
1181
+ | "stopped"
1182
+ | "completed"
1183
+ | "failed";
1139
1184
 
1140
1185
  /**
1141
1186
  * Runtime state for a batch execution.
@@ -1288,14 +1333,17 @@ export function freshOrchBatchState(): OrchBatchRuntimeState {
1288
1333
  };
1289
1334
  }
1290
1335
 
1291
-
1292
1336
  // ── Merge Types ──────────────────────────────────────────────────────
1293
1337
 
1294
1338
  /**
1295
1339
  * Valid merge result statuses.
1296
1340
  * Matches the contract in .pi/agents/task-merger.md.
1297
1341
  */
1298
- export type MergeResultStatus = "SUCCESS" | "CONFLICT_RESOLVED" | "CONFLICT_UNRESOLVED" | "BUILD_FAILURE";
1342
+ export type MergeResultStatus =
1343
+ | "SUCCESS"
1344
+ | "CONFLICT_RESOLVED"
1345
+ | "CONFLICT_UNRESOLVED"
1346
+ | "BUILD_FAILURE";
1299
1347
 
1300
1348
  /** All valid status strings for runtime validation. */
1301
1349
  export const VALID_MERGE_STATUSES: ReadonlySet<string> = new Set([
@@ -1686,7 +1734,6 @@ export interface MergeSessionHealthState {
1686
1734
  deadEmitted: boolean;
1687
1735
  }
1688
1736
 
1689
-
1690
1737
  // ── Merge Retry Policy Matrix (TP-033 Step 2) ───────────────────────
1691
1738
 
1692
1739
  /**
@@ -1743,7 +1790,9 @@ export interface MergeRetryPolicy {
1743
1790
  *
1744
1791
  * @since TP-033
1745
1792
  */
1746
- export const MERGE_RETRY_POLICY_MATRIX: Readonly<Record<MergeFailureClassification, MergeRetryPolicy>> = {
1793
+ export const MERGE_RETRY_POLICY_MATRIX: Readonly<
1794
+ Record<MergeFailureClassification, MergeRetryPolicy>
1795
+ > = {
1747
1796
  verification_new_failure: {
1748
1797
  retriable: true,
1749
1798
  maxAttempts: 1,
@@ -1788,7 +1837,6 @@ export const MERGE_FAILURE_CLASSIFICATIONS: readonly MergeFailureClassification[
1788
1837
  "git_lock_file",
1789
1838
  ] as const;
1790
1839
 
1791
-
1792
1840
  // ── Tier 0 Watchdog Recovery Types (TP-039) ──────────────────────────
1793
1841
 
1794
1842
  /**
@@ -1923,7 +1971,11 @@ export interface EscalationContext {
1923
1971
  *
1924
1972
  * @since TP-039
1925
1973
  */
1926
- export function tier0ScopeKey(pattern: Tier0RecoveryPattern, taskId: string, waveIndex: number): string {
1974
+ export function tier0ScopeKey(
1975
+ pattern: Tier0RecoveryPattern,
1976
+ taskId: string,
1977
+ waveIndex: number,
1978
+ ): string {
1927
1979
  return `t0:${pattern}:${taskId}:w${waveIndex}`;
1928
1980
  }
1929
1981
 
@@ -2066,7 +2118,6 @@ export interface EngineEvent {
2066
2118
  */
2067
2119
  export type EngineEventCallback = (event: EngineEvent) => void;
2068
2120
 
2069
-
2070
2121
  // ── Supervisor Alert Types (TP-076) ──────────────────────────────────
2071
2122
 
2072
2123
  /**
@@ -2278,7 +2329,10 @@ export function buildSupervisorSegmentFrontierSnapshot(
2278
2329
  preferredSegmentId?: string | null,
2279
2330
  ): SupervisorSegmentFrontierSnapshot | undefined {
2280
2331
  const orderedSegmentIds = Array.isArray(segmentIds)
2281
- ? segmentIds.filter((segmentId): segmentId is string => typeof segmentId === "string" && segmentId.trim().length > 0)
2332
+ ? segmentIds.filter(
2333
+ (segmentId): segmentId is string =>
2334
+ typeof segmentId === "string" && segmentId.trim().length > 0,
2335
+ )
2282
2336
  : [];
2283
2337
  if (orderedSegmentIds.length === 0) return undefined;
2284
2338
 
@@ -2289,16 +2343,17 @@ export function buildSupervisorSegmentFrontierSnapshot(
2289
2343
  }
2290
2344
  }
2291
2345
 
2292
- const resolvedActiveSegmentId = (activeSegmentId && orderedSegmentIds.includes(activeSegmentId))
2293
- ? activeSegmentId
2294
- : (preferredSegmentId && orderedSegmentIds.includes(preferredSegmentId)
2295
- ? preferredSegmentId
2296
- : null);
2346
+ const resolvedActiveSegmentId =
2347
+ activeSegmentId && orderedSegmentIds.includes(activeSegmentId)
2348
+ ? activeSegmentId
2349
+ : preferredSegmentId && orderedSegmentIds.includes(preferredSegmentId)
2350
+ ? preferredSegmentId
2351
+ : null;
2297
2352
 
2298
2353
  const segments = orderedSegmentIds.map((segmentId) => {
2299
2354
  const persisted = bySegmentId.get(segmentId);
2300
- const status: PersistedSegmentStatus = persisted?.status
2301
- ?? (resolvedActiveSegmentId === segmentId ? "running" : "pending");
2355
+ const status: PersistedSegmentStatus =
2356
+ persisted?.status ?? (resolvedActiveSegmentId === segmentId ? "running" : "pending");
2302
2357
  return {
2303
2358
  segmentId,
2304
2359
  repoId: persisted ? parseSegmentIdRepo(persisted) : "unknown",
@@ -2307,11 +2362,12 @@ export function buildSupervisorSegmentFrontierSnapshot(
2307
2362
  };
2308
2363
  });
2309
2364
 
2310
- const terminalSegments = segments.filter((segment) =>
2311
- segment.status === "succeeded"
2312
- || segment.status === "failed"
2313
- || segment.status === "stalled"
2314
- || segment.status === "skipped",
2365
+ const terminalSegments = segments.filter(
2366
+ (segment) =>
2367
+ segment.status === "succeeded" ||
2368
+ segment.status === "failed" ||
2369
+ segment.status === "stalled" ||
2370
+ segment.status === "skipped",
2315
2371
  ).length;
2316
2372
 
2317
2373
  return {
@@ -2346,7 +2402,6 @@ export function buildEngineEventBase(
2346
2402
  };
2347
2403
  }
2348
2404
 
2349
-
2350
2405
  /**
2351
2406
  * Decision output from the merge retry policy evaluator.
2352
2407
  *
@@ -2383,50 +2438,50 @@ export interface MergeRetryDecision {
2383
2438
  */
2384
2439
  export type MergeRetryLoopOutcome =
2385
2440
  | {
2386
- /** Retry succeeded — caller should continue normal post-merge flow */
2387
- kind: "retry_succeeded";
2388
- mergeResult: MergeWaveResult;
2389
- /** Classification of the failure that was retried */
2390
- classification: MergeFailureClassification | null;
2391
- /** Scope key used for retry counter tracking */
2392
- scopeKey: string;
2393
- /** Last retry decision (carries attempt/maxAttempts for event emission) */
2394
- lastDecision: MergeRetryDecision;
2395
- }
2441
+ /** Retry succeeded — caller should continue normal post-merge flow */
2442
+ kind: "retry_succeeded";
2443
+ mergeResult: MergeWaveResult;
2444
+ /** Classification of the failure that was retried */
2445
+ classification: MergeFailureClassification | null;
2446
+ /** Scope key used for retry counter tracking */
2447
+ scopeKey: string;
2448
+ /** Last retry decision (carries attempt/maxAttempts for event emission) */
2449
+ lastDecision: MergeRetryDecision;
2450
+ }
2396
2451
  | {
2397
- /** Safe-stop triggered during retry — caller should break the wave loop */
2398
- kind: "safe_stop";
2399
- mergeResult: MergeWaveResult;
2400
- /** Classification of the failure that was retried */
2401
- classification: MergeFailureClassification | null;
2402
- /** Scope key used for retry counter tracking */
2403
- scopeKey: string;
2404
- /** Last retry decision (carries attempt/maxAttempts for event emission) */
2405
- lastDecision: MergeRetryDecision;
2406
- errorMessage: string;
2407
- notifyMessage: string;
2408
- }
2452
+ /** Safe-stop triggered during retry — caller should break the wave loop */
2453
+ kind: "safe_stop";
2454
+ mergeResult: MergeWaveResult;
2455
+ /** Classification of the failure that was retried */
2456
+ classification: MergeFailureClassification | null;
2457
+ /** Scope key used for retry counter tracking */
2458
+ scopeKey: string;
2459
+ /** Last retry decision (carries attempt/maxAttempts for event emission) */
2460
+ lastDecision: MergeRetryDecision;
2461
+ errorMessage: string;
2462
+ notifyMessage: string;
2463
+ }
2409
2464
  | {
2410
- /**
2411
- * Retry exhausted or failure is non-retriable — caller should
2412
- * force `paused` regardless of on_merge_failure config.
2413
- */
2414
- kind: "exhausted";
2415
- mergeResult: MergeWaveResult;
2416
- classification: MergeFailureClassification | null;
2417
- scopeKey: string;
2418
- lastDecision: MergeRetryDecision;
2419
- errorMessage: string;
2420
- notifyMessage: string;
2421
- }
2465
+ /**
2466
+ * Retry exhausted or failure is non-retriable — caller should
2467
+ * force `paused` regardless of on_merge_failure config.
2468
+ */
2469
+ kind: "exhausted";
2470
+ mergeResult: MergeWaveResult;
2471
+ classification: MergeFailureClassification | null;
2472
+ scopeKey: string;
2473
+ lastDecision: MergeRetryDecision;
2474
+ errorMessage: string;
2475
+ notifyMessage: string;
2476
+ }
2422
2477
  | {
2423
- /** No retry attempted (unclassifiable or non-retriable with 0 attempts).
2424
- * Caller should fall through to standard on_merge_failure policy. */
2425
- kind: "no_retry";
2426
- mergeResult: MergeWaveResult;
2427
- classification: MergeFailureClassification | null;
2428
- scopeKey: string;
2429
- };
2478
+ /** No retry attempted (unclassifiable or non-retriable with 0 attempts).
2479
+ * Caller should fall through to standard on_merge_failure policy. */
2480
+ kind: "no_retry";
2481
+ mergeResult: MergeWaveResult;
2482
+ classification: MergeFailureClassification | null;
2483
+ scopeKey: string;
2484
+ };
2430
2485
 
2431
2486
  /**
2432
2487
  * Callbacks provided to `applyMergeRetryLoop()` for side effects
@@ -2510,7 +2565,6 @@ export interface OrchDashboardViewModel {
2510
2565
  failurePolicy: string | null; // e.g., "stop-wave" if stopped by policy
2511
2566
  }
2512
2567
 
2513
-
2514
2568
  // ── State Persistence Types (TS-009) ─────────────────────────────────
2515
2569
 
2516
2570
  // ── v3 Resilience & Diagnostics Sections (TP-030) ────────────────────
@@ -2832,7 +2886,13 @@ export interface PersistedTaskRecord {
2832
2886
  *
2833
2887
  * @since v4 (TP-081)
2834
2888
  */
2835
- export type PersistedSegmentStatus = "pending" | "running" | "succeeded" | "failed" | "stalled" | "skipped";
2889
+ export type PersistedSegmentStatus =
2890
+ | "pending"
2891
+ | "running"
2892
+ | "succeeded"
2893
+ | "failed"
2894
+ | "stalled"
2895
+ | "skipped";
2836
2896
 
2837
2897
  /**
2838
2898
  * Persisted record of a single segment's execution state.
@@ -3095,7 +3155,6 @@ export interface PersistedBatchState {
3095
3155
  _extraFields?: Record<string, unknown>;
3096
3156
  }
3097
3157
 
3098
-
3099
3158
  // ── Resume (TS-009 Step 4) ───────────────────────────────────────────
3100
3159
 
3101
3160
  /**
@@ -3313,10 +3372,7 @@ export const DURATION_BASE_MINUTES = 30;
3313
3372
  * Get estimated duration in minutes for a task size.
3314
3373
  * Uses explicit mapping, falling back to weight × base.
3315
3374
  */
3316
- export function getTaskDurationMinutes(
3317
- size: string,
3318
- sizeWeights: Record<string, number>,
3319
- ): number {
3375
+ export function getTaskDurationMinutes(size: string, sizeWeights: Record<string, number>): number {
3320
3376
  if (SIZE_DURATION_MINUTES[size] !== undefined) {
3321
3377
  return SIZE_DURATION_MINUTES[size];
3322
3378
  }
@@ -3324,7 +3380,6 @@ export function getTaskDurationMinutes(
3324
3380
  return weight * DURATION_BASE_MINUTES;
3325
3381
  }
3326
3382
 
3327
-
3328
3383
  // ── Batch History ────────────────────────────────────────────────────
3329
3384
 
3330
3385
  /** Token counts for a task, wave, or batch. */
@@ -3341,8 +3396,8 @@ export interface BatchTaskSummary {
3341
3396
  taskId: string;
3342
3397
  taskName: string;
3343
3398
  status: "succeeded" | "failed" | "skipped" | "blocked" | "stalled" | "pending";
3344
- wave: number; // 1-based
3345
- lane: number; // 1-based
3399
+ wave: number; // 1-based
3400
+ lane: number; // 1-based
3346
3401
  durationMs: number;
3347
3402
  tokens: TokenCounts;
3348
3403
  exitReason: string | null;
@@ -3350,8 +3405,8 @@ export interface BatchTaskSummary {
3350
3405
 
3351
3406
  /** Per-wave summary for history. */
3352
3407
  export interface BatchWaveSummary {
3353
- wave: number; // 1-based
3354
- tasks: string[]; // task IDs
3408
+ wave: number; // 1-based
3409
+ tasks: string[]; // task IDs
3355
3410
  mergeStatus: "succeeded" | "failed" | "partial" | "skipped";
3356
3411
  durationMs: number;
3357
3412
  tokens: TokenCounts;
@@ -3380,7 +3435,6 @@ export interface BatchHistorySummary {
3380
3435
  /** Max number of batch history entries to retain. */
3381
3436
  export const BATCH_HISTORY_MAX_ENTRIES = 100;
3382
3437
 
3383
-
3384
3438
  // ── Workspace Mode Types ─────────────────────────────────────────────
3385
3439
 
3386
3440
  /**
@@ -3518,7 +3572,6 @@ export interface ExecutionContext {
3518
3572
  pointer: PointerResolution | null;
3519
3573
  }
3520
3574
 
3521
-
3522
3575
  // ── Workspace Validation Error Types ─────────────────────────────────
3523
3576
 
3524
3577
  /**
@@ -3560,7 +3613,7 @@ export type WorkspaceConfigErrorCode =
3560
3613
  | "WORKSPACE_TASK_AREA_OUTSIDE_TASKS_ROOT"
3561
3614
  | "WORKSPACE_SETUP_REQUIRED"
3562
3615
  | "WORKSPACE_DUPLICATE_REPO_PATH"
3563
- | "WORKSPACE_SCHEMA_INVALID";/**
3616
+ | "WORKSPACE_SCHEMA_INVALID"; /**
3564
3617
  * Typed error class for workspace configuration failures.
3565
3618
  *
3566
3619
  * Thrown during workspace config loading/validation when the config file
@@ -3577,7 +3630,12 @@ export class WorkspaceConfigError extends Error {
3577
3630
  /** Optional filesystem path related to the error */
3578
3631
  relatedPath?: string;
3579
3632
 
3580
- constructor(code: WorkspaceConfigErrorCode, message: string, repoId?: string, relatedPath?: string) {
3633
+ constructor(
3634
+ code: WorkspaceConfigErrorCode,
3635
+ message: string,
3636
+ repoId?: string,
3637
+ relatedPath?: string,
3638
+ ) {
3581
3639
  super(message);
3582
3640
  this.name = "WorkspaceConfigError";
3583
3641
  this.code = code;
@@ -3586,7 +3644,6 @@ export class WorkspaceConfigError extends Error {
3586
3644
  }
3587
3645
  }
3588
3646
 
3589
-
3590
3647
  // ── Pointer Resolution Types ─────────────────────────────────────────
3591
3648
 
3592
3649
  /**
@@ -3653,7 +3710,6 @@ export interface PointerResolution {
3653
3710
  warning?: string;
3654
3711
  }
3655
3712
 
3656
-
3657
3713
  // ── Workspace Defaults ───────────────────────────────────────────────
3658
3714
 
3659
3715
  /**
@@ -3697,7 +3753,6 @@ export function createRepoModeContext(
3697
3753
  };
3698
3754
  }
3699
3755
 
3700
-
3701
3756
  // ── Agent Mailbox Types (TP-089) ─────────────────────────────────────
3702
3757
 
3703
3758
  /**
@@ -3735,7 +3790,12 @@ export type MailboxMessageType = "steer" | "query" | "abort" | "info" | "reply"
3735
3790
  * @since TP-089
3736
3791
  */
3737
3792
  export const MAILBOX_MESSAGE_TYPES: ReadonlySet<string> = new Set<MailboxMessageType>([
3738
- "steer", "query", "abort", "info", "reply", "escalate",
3793
+ "steer",
3794
+ "query",
3795
+ "abort",
3796
+ "info",
3797
+ "reply",
3798
+ "escalate",
3739
3799
  ]);
3740
3800
 
3741
3801
  /**
@@ -3838,7 +3898,10 @@ export type RuntimeAgentStatus =
3838
3898
 
3839
3899
  /** Set of terminal agent statuses (process is no longer alive). @since TP-102 */
3840
3900
  export const TERMINAL_AGENT_STATUSES: ReadonlySet<RuntimeAgentStatus> = new Set([
3841
- "exited", "crashed", "timed_out", "killed",
3901
+ "exited",
3902
+ "crashed",
3903
+ "timed_out",
3904
+ "killed",
3842
3905
  ]);
3843
3906
 
3844
3907
  /**
@@ -4173,7 +4236,11 @@ export function runtimeRoot(stateRoot: string, batchId: string): string {
4173
4236
  *
4174
4237
  * @since TP-102
4175
4238
  */
4176
- export function runtimeAgentDir(stateRoot: string, batchId: string, agentId: RuntimeAgentId): string {
4239
+ export function runtimeAgentDir(
4240
+ stateRoot: string,
4241
+ batchId: string,
4242
+ agentId: RuntimeAgentId,
4243
+ ): string {
4177
4244
  return `${stateRoot}/.pi/runtime/${batchId}/agents/${agentId}`;
4178
4245
  }
4179
4246
 
@@ -4182,7 +4249,11 @@ export function runtimeAgentDir(stateRoot: string, batchId: string, agentId: Run
4182
4249
  *
4183
4250
  * @since TP-102
4184
4251
  */
4185
- export function runtimeManifestPath(stateRoot: string, batchId: string, agentId: RuntimeAgentId): string {
4252
+ export function runtimeManifestPath(
4253
+ stateRoot: string,
4254
+ batchId: string,
4255
+ agentId: RuntimeAgentId,
4256
+ ): string {
4186
4257
  return `${runtimeAgentDir(stateRoot, batchId, agentId)}/manifest.json`;
4187
4258
  }
4188
4259
 
@@ -4191,7 +4262,11 @@ export function runtimeManifestPath(stateRoot: string, batchId: string, agentId:
4191
4262
  *
4192
4263
  * @since TP-102
4193
4264
  */
4194
- export function runtimeAgentEventsPath(stateRoot: string, batchId: string, agentId: RuntimeAgentId): string {
4265
+ export function runtimeAgentEventsPath(
4266
+ stateRoot: string,
4267
+ batchId: string,
4268
+ agentId: RuntimeAgentId,
4269
+ ): string {
4195
4270
  return `${runtimeAgentDir(stateRoot, batchId, agentId)}/events.jsonl`;
4196
4271
  }
4197
4272
 
@@ -4200,7 +4275,11 @@ export function runtimeAgentEventsPath(stateRoot: string, batchId: string, agent
4200
4275
  *
4201
4276
  * @since TP-102
4202
4277
  */
4203
- export function runtimeLaneSnapshotPath(stateRoot: string, batchId: string, laneNumber: number): string {
4278
+ export function runtimeLaneSnapshotPath(
4279
+ stateRoot: string,
4280
+ batchId: string,
4281
+ laneNumber: number,
4282
+ ): string {
4204
4283
  return `${stateRoot}/.pi/runtime/${batchId}/lanes/lane-${laneNumber}.json`;
4205
4284
  }
4206
4285
 
@@ -4244,7 +4323,11 @@ export interface RuntimeMergeSnapshot {
4244
4323
  *
4245
4324
  * @since TP-164
4246
4325
  */
4247
- export function runtimeMergeSnapshotPath(stateRoot: string, batchId: string, mergeNumber: number): string {
4326
+ export function runtimeMergeSnapshotPath(
4327
+ stateRoot: string,
4328
+ batchId: string,
4329
+ mergeNumber: number,
4330
+ ): string {
4248
4331
  return `${stateRoot}/.pi/runtime/${batchId}/lanes/merge-${mergeNumber}.json`;
4249
4332
  }
4250
4333
 
@@ -4309,15 +4392,28 @@ export function validateAgentManifest(manifest: unknown): string[] {
4309
4392
  if (typeof m.role !== "string") errors.push("role must be a string");
4310
4393
  else {
4311
4394
  const validRoles: ReadonlySet<string> = new Set(["worker", "reviewer", "merger", "lane-runner"]);
4312
- if (!validRoles.has(m.role as string)) errors.push(`role must be one of: ${[...validRoles].join(", ")}`);
4395
+ if (!validRoles.has(m.role as string))
4396
+ errors.push(`role must be one of: ${[...validRoles].join(", ")}`);
4313
4397
  }
4314
- if (typeof m.pid !== "number" || !Number.isFinite(m.pid) || m.pid <= 0) errors.push("pid must be a positive finite number");
4315
- if (typeof m.parentPid !== "number" || !Number.isFinite(m.parentPid) || m.parentPid <= 0) errors.push("parentPid must be a positive finite number");
4316
- if (typeof m.startedAt !== "number" || !Number.isFinite(m.startedAt)) errors.push("startedAt must be a finite number");
4398
+ if (typeof m.pid !== "number" || !Number.isFinite(m.pid) || m.pid <= 0)
4399
+ errors.push("pid must be a positive finite number");
4400
+ if (typeof m.parentPid !== "number" || !Number.isFinite(m.parentPid) || m.parentPid <= 0)
4401
+ errors.push("parentPid must be a positive finite number");
4402
+ if (typeof m.startedAt !== "number" || !Number.isFinite(m.startedAt))
4403
+ errors.push("startedAt must be a finite number");
4317
4404
  if (typeof m.status !== "string") errors.push("status must be a string");
4318
4405
  else {
4319
- const validStatuses: ReadonlySet<string> = new Set(["spawning", "running", "wrapping_up", "exited", "crashed", "timed_out", "killed"]);
4320
- if (!validStatuses.has(m.status as string)) errors.push(`status must be one of: ${[...validStatuses].join(", ")}`);
4406
+ const validStatuses: ReadonlySet<string> = new Set([
4407
+ "spawning",
4408
+ "running",
4409
+ "wrapping_up",
4410
+ "exited",
4411
+ "crashed",
4412
+ "timed_out",
4413
+ "killed",
4414
+ ]);
4415
+ if (!validStatuses.has(m.status as string))
4416
+ errors.push(`status must be one of: ${[...validStatuses].join(", ")}`);
4321
4417
  }
4322
4418
  if (typeof m.cwd !== "string" || !m.cwd) errors.push("cwd must be a non-empty string");
4323
4419
  if (typeof m.repoId !== "string") errors.push("repoId must be a string");
@@ -4339,7 +4435,13 @@ export function validatePacketPaths(packet: unknown): string[] {
4339
4435
  }
4340
4436
  const p = packet as Record<string, unknown>;
4341
4437
 
4342
- for (const field of ["promptPath", "statusPath", "donePath", "reviewsDir", "taskFolder"] as const) {
4438
+ for (const field of [
4439
+ "promptPath",
4440
+ "statusPath",
4441
+ "donePath",
4442
+ "reviewsDir",
4443
+ "taskFolder",
4444
+ ] as const) {
4343
4445
  if (typeof p[field] !== "string" || !(p[field] as string)) {
4344
4446
  errors.push(`${field} must be a non-empty string`);
4345
4447
  }
@@ -4347,4 +4449,3 @@ export function validatePacketPaths(packet: unknown): string[] {
4347
4449
 
4348
4450
  return errors;
4349
4451
  }
4350
-