taskplane 0.1.18 → 0.2.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.
@@ -15,6 +15,8 @@ export interface OrchestratorConfig {
15
15
  batch_id_format: "timestamp" | "sequential";
16
16
  spawn_mode: "tmux" | "subprocess";
17
17
  tmux_prefix: string;
18
+ /** Optional operator identifier. Auto-detected from OS username if empty. */
19
+ operator_id: string;
18
20
  };
19
21
  dependencies: {
20
22
  source: "prompt" | "agent";
@@ -76,6 +78,8 @@ export interface LaneAssignment {
76
78
  taskId: string;
77
79
  lane: number;
78
80
  task: ParsedTask;
81
+ /** Repo ID this task targets (workspace mode only). Undefined in repo mode. */
82
+ repoId?: string;
79
83
  }
80
84
 
81
85
  /** Runtime state of the entire batch execution */
@@ -144,6 +148,7 @@ export const DEFAULT_ORCHESTRATOR_CONFIG: OrchestratorConfig = {
144
148
  batch_id_format: "timestamp",
145
149
  spawn_mode: "subprocess",
146
150
  tmux_prefix: "orch",
151
+ operator_id: "",
147
152
  },
148
153
  dependencies: {
149
154
  source: "prompt",
@@ -222,6 +227,8 @@ export interface CreateWorktreeOptions {
222
227
  baseBranch: string;
223
228
  /** Worktree directory prefix (e.g. "taskplane-wt") */
224
229
  prefix: string;
230
+ /** Operator identifier (sanitized, e.g., "henrylach") */
231
+ opId: string;
225
232
  /** Full orchestrator config (optional; used for worktree_location) */
226
233
  config?: OrchestratorConfig;
227
234
  }
@@ -367,7 +374,8 @@ export interface DiscoveryError {
367
374
  | "DEP_AMBIGUOUS"
368
375
  | "DEP_SOURCE_FALLBACK"
369
376
  | "TASK_REPO_UNRESOLVED"
370
- | "TASK_REPO_UNKNOWN";
377
+ | "TASK_REPO_UNKNOWN"
378
+ | "TASK_ROUTING_STRICT";
371
379
  message: string;
372
380
  taskPath?: string;
373
381
  taskId?: string;
@@ -388,6 +396,7 @@ export const FATAL_DISCOVERY_CODES: ReadonlyArray<DiscoveryError["code"]> = [
388
396
  "PARSE_MISSING_ID",
389
397
  "TASK_REPO_UNRESOLVED",
390
398
  "TASK_REPO_UNKNOWN",
399
+ "TASK_ROUTING_STRICT",
391
400
  ] as const;
392
401
 
393
402
  /** Result of the full discovery pipeline */
@@ -477,7 +486,7 @@ export interface AllocatedTask {
477
486
  * between Step 1 (allocation) and Step 2 (execution).
478
487
  */
479
488
  export interface AllocatedLane {
480
- /** Lane number (1-indexed, deterministic) */
489
+ /** Lane number (1-indexed, deterministic, globally unique across repos) */
481
490
  laneNumber: number;
482
491
  /** Lane identifier for display and logging (e.g., "lane-1") */
483
492
  laneId: string;
@@ -495,6 +504,8 @@ export interface AllocatedLane {
495
504
  estimatedLoad: number;
496
505
  /** Total estimated duration in minutes (sum of task durations) */
497
506
  estimatedMinutes: number;
507
+ /** Repo ID this lane targets (workspace mode only). Undefined in repo mode. */
508
+ repoId?: string;
498
509
  }
499
510
 
500
511
 
@@ -815,6 +826,8 @@ export interface OrchBatchRuntimeState {
815
826
  batchId: string;
816
827
  /** Branch that was active when /orch started — used as base for worktrees and merge target */
817
828
  baseBranch: string;
829
+ /** Workspace execution mode (v2). Defaults to "repo" for backward compatibility. */
830
+ mode: WorkspaceMode;
818
831
  /** Shared pause signal — set by /orch-pause, read by executeLane/executeWave */
819
832
  pauseSignal: { paused: boolean };
820
833
  /** All wave results in order (grows as waves complete) */
@@ -892,6 +905,7 @@ export function freshOrchBatchState(): OrchBatchRuntimeState {
892
905
  phase: "idle",
893
906
  batchId: "",
894
907
  baseBranch: "",
908
+ mode: "repo",
895
909
  pauseSignal: { paused: false },
896
910
  waveResults: [],
897
911
  currentWaveIndex: -1,
@@ -965,6 +979,8 @@ export interface MergeLaneResult {
965
979
  result: MergeResult | null;
966
980
  error: string | null;
967
981
  durationMs: number;
982
+ /** Repo ID this lane targeted (workspace mode only). Undefined in repo mode. */
983
+ repoId?: string;
968
984
  }
969
985
 
970
986
  /** Overall wave merge outcome. */
@@ -975,6 +991,22 @@ export interface MergeWaveResult {
975
991
  failedLane: number | null;
976
992
  failureReason: string | null;
977
993
  totalDurationMs: number;
994
+ /** Per-repo merge outcomes (populated in workspace mode; empty in repo mode). */
995
+ repoResults?: RepoMergeOutcome[];
996
+ }
997
+
998
+ /** Per-repo merge outcome within a wave merge. */
999
+ export interface RepoMergeOutcome {
1000
+ /** Repo ID (undefined in repo mode default group). */
1001
+ repoId: string | undefined;
1002
+ /** Merge status for this repo. */
1003
+ status: "succeeded" | "failed" | "partial";
1004
+ /** Lane results belonging to this repo. */
1005
+ laneResults: MergeLaneResult[];
1006
+ /** Failed lane number within this repo (null if all succeeded). */
1007
+ failedLane: number | null;
1008
+ /** Failure reason within this repo (null if all succeeded). */
1009
+ failureReason: string | null;
978
1010
  }
979
1011
 
980
1012
  // ── Merge Error Types ────────────────────────────────────────────────
@@ -1107,9 +1139,21 @@ export interface OrchDashboardViewModel {
1107
1139
  /**
1108
1140
  * Current schema version for batch-state.json.
1109
1141
  * Increment when the persisted schema changes in incompatible ways.
1110
- * loadBatchState() rejects files with a different schemaVersion.
1142
+ *
1143
+ * Version history:
1144
+ * v1 — Original schema (TS-009). No repo-aware fields on task records.
1145
+ * Lane records had optional `repoId` but it was not validated.
1146
+ * v2 — Repo-aware records (TP-006). Adds `repoId` and `resolvedRepoId`
1147
+ * to task records. Formalizes `repoId` on lane records. Adds
1148
+ * `mode` field to top-level state.
1149
+ *
1150
+ * Compatibility policy:
1151
+ * - loadBatchState() accepts v1 files and auto-upconverts to v2 in memory
1152
+ * (via upconvertV1toV2()). The on-disk file is NOT rewritten.
1153
+ * - saveBatchState() always writes v2.
1154
+ * - Schema versions > 2 are rejected with STATE_SCHEMA_INVALID.
1111
1155
  */
1112
- export const BATCH_STATE_SCHEMA_VERSION = 1;
1156
+ export const BATCH_STATE_SCHEMA_VERSION = 2;
1113
1157
 
1114
1158
  /**
1115
1159
  * Canonical file path for persisted batch state.
@@ -1154,6 +1198,25 @@ export class StateFileError extends Error {
1154
1198
  *
1155
1199
  * Contains everything `/orch-resume` needs to reconstruct
1156
1200
  * task progress without re-running discovery.
1201
+ *
1202
+ * Repo-aware fields (v2):
1203
+ * `repoId` and `resolvedRepoId` capture task-to-repo attribution
1204
+ * so resume can reconstruct repo routing without re-running discovery.
1205
+ *
1206
+ * Mode semantics:
1207
+ * - **repo mode**: Both fields are `undefined`. Tasks implicitly target
1208
+ * the single repository (cwd). No repo routing needed.
1209
+ * - **workspace mode**: `repoId` is the repo ID declared in PROMPT.md
1210
+ * (may be `undefined` if the task didn't declare one). `resolvedRepoId`
1211
+ * is the final repo ID after applying the routing precedence chain
1212
+ * (prompt → area → workspace default). Always a non-empty string in
1213
+ * workspace mode for tasks that passed routing validation.
1214
+ *
1215
+ * Source of truth:
1216
+ * - For allocated tasks: derived from `ParsedTask.promptRepoId` and
1217
+ * `ParsedTask.resolvedRepoId` via `serializeBatchState()`.
1218
+ * - For unallocated/pending tasks: derived from the same ParsedTask
1219
+ * fields via discovery enrichment in `persistRuntimeState()`.
1157
1220
  */
1158
1221
  export interface PersistedTaskRecord {
1159
1222
  /** Task identifier (e.g., "TO-014") */
@@ -1174,6 +1237,17 @@ export interface PersistedTaskRecord {
1174
1237
  doneFileFound: boolean;
1175
1238
  /** Human-readable exit reason (if completed/failed) */
1176
1239
  exitReason: string;
1240
+ /**
1241
+ * Repo ID declared in the task's PROMPT.md metadata (v2).
1242
+ * Undefined in repo mode or if the task didn't declare a repo.
1243
+ */
1244
+ repoId?: string;
1245
+ /**
1246
+ * Resolved repo ID after applying routing precedence (v2).
1247
+ * Undefined in repo mode. In workspace mode, this is the final
1248
+ * repo target after prompt → area → workspace-default fallback.
1249
+ */
1250
+ resolvedRepoId?: string;
1177
1251
  }
1178
1252
 
1179
1253
  /**
@@ -1181,6 +1255,21 @@ export interface PersistedTaskRecord {
1181
1255
  *
1182
1256
  * Captures worktree/branch assignment so `/orch-resume` can
1183
1257
  * reconnect to existing worktrees without re-allocation.
1258
+ *
1259
+ * Repo-aware contract (v2):
1260
+ * `repoId` captures which repository this lane targets.
1261
+ *
1262
+ * Mode semantics:
1263
+ * - **repo mode**: `repoId` is `undefined`. The lane's worktree is
1264
+ * created from the single repository (cwd). All lanes share the
1265
+ * same repo implicitly.
1266
+ * - **workspace mode**: `repoId` is a non-empty string matching a
1267
+ * key in `WorkspaceConfig.repos`. All tasks assigned to this lane
1268
+ * target the same repo. Lane allocation guarantees repo affinity
1269
+ * (no lane mixes tasks from different repos).
1270
+ *
1271
+ * Source of truth: derived from `AllocatedLane.repoId` during
1272
+ * serialization in `serializeBatchState()`.
1184
1273
  */
1185
1274
  export interface PersistedLaneRecord {
1186
1275
  /** Lane number (1-indexed) */
@@ -1195,6 +1284,12 @@ export interface PersistedLaneRecord {
1195
1284
  branch: string;
1196
1285
  /** Task IDs assigned to this lane in execution order */
1197
1286
  taskIds: string[];
1287
+ /**
1288
+ * Repo ID this lane targets (v2).
1289
+ * Undefined in repo mode. Non-empty string in workspace mode,
1290
+ * matching a key in `WorkspaceConfig.repos`.
1291
+ */
1292
+ repoId?: string;
1198
1293
  }
1199
1294
 
1200
1295
  /**
@@ -1210,6 +1305,31 @@ export interface PersistedMergeResult {
1210
1305
  failedLane: number | null;
1211
1306
  /** Failure reason (null if all succeeded) */
1212
1307
  failureReason: string | null;
1308
+ /**
1309
+ * Per-repo merge outcomes (v2, TP-009).
1310
+ * Populated in workspace mode when MergeWaveResult.repoResults is available.
1311
+ * Undefined/absent in repo mode or for older state files. Dashboard treats
1312
+ * absence as single-repo merge.
1313
+ */
1314
+ repoResults?: PersistedRepoMergeOutcome[];
1315
+ }
1316
+
1317
+ /**
1318
+ * Persisted per-repo merge outcome within a wave merge.
1319
+ * Serializable subset of RepoMergeOutcome — excludes full MergeLaneResult
1320
+ * objects (which contain detailed merge agent result JSON) to keep state file compact.
1321
+ */
1322
+ export interface PersistedRepoMergeOutcome {
1323
+ /** Repo ID. Undefined for the default group in repo mode. */
1324
+ repoId: string | undefined;
1325
+ /** Merge status for this repo. */
1326
+ status: "succeeded" | "failed" | "partial";
1327
+ /** Lane numbers involved in this repo's merge. */
1328
+ laneNumbers: number[];
1329
+ /** Failed lane number within this repo (null if all succeeded). */
1330
+ failedLane: number | null;
1331
+ /** Failure reason within this repo (null if all succeeded). */
1332
+ failureReason: string | null;
1213
1333
  }
1214
1334
 
1215
1335
  /**
@@ -1226,9 +1346,16 @@ export interface PersistedMergeResult {
1226
1346
  * - Merge results are summarized (not full MergeWaveResult) for size
1227
1347
  * - `updatedAt` is monotonic (epoch ms) for staleness detection
1228
1348
  * - `lastError` captures most recent error without PII
1349
+ *
1350
+ * v2 additions (TP-006):
1351
+ * - `mode` field captures workspace vs repo mode at batch start
1352
+ * - Task records include `repoId` and `resolvedRepoId` for repo attribution
1353
+ * - Lane records formalize `repoId` contract per mode
1354
+ * - v1 files are auto-upconverted: `mode` defaults to "repo", task/lane
1355
+ * `repoId` fields default to `undefined` (omitted from JSON)
1229
1356
  */
1230
1357
  export interface PersistedBatchState {
1231
- /** Schema version — must equal BATCH_STATE_SCHEMA_VERSION */
1358
+ /** Schema version — must equal BATCH_STATE_SCHEMA_VERSION (currently 2) */
1232
1359
  schemaVersion: number;
1233
1360
  /** Current batch execution phase */
1234
1361
  phase: OrchBatchPhase;
@@ -1236,6 +1363,13 @@ export interface PersistedBatchState {
1236
1363
  batchId: string;
1237
1364
  /** Branch that was active when /orch started — used as base for worktrees and merge target */
1238
1365
  baseBranch: string;
1366
+ /**
1367
+ * Workspace execution mode at batch start (v2).
1368
+ * - "repo": Single-repo mode (default, backward-compatible).
1369
+ * - "workspace": Multi-repo workspace mode.
1370
+ * Defaults to "repo" when loading v1 state files.
1371
+ */
1372
+ mode: WorkspaceMode;
1239
1373
  /** Epoch ms when batch started */
1240
1374
  startedAt: number;
1241
1375
  /** Epoch ms when state was last written */
@@ -1326,7 +1460,7 @@ export interface ReconciledTaskState {
1326
1460
  /** Whether the lane worktree still exists on disk */
1327
1461
  worktreeExists: boolean;
1328
1462
  /** Action the resume engine should take */
1329
- action: "reconnect" | "mark-complete" | "mark-failed" | "re-execute" | "skip";
1463
+ action: "reconnect" | "mark-complete" | "mark-failed" | "re-execute" | "skip" | "pending";
1330
1464
  }
1331
1465
 
1332
1466
  /**
@@ -1599,6 +1733,19 @@ export interface WorkspaceRoutingConfig {
1599
1733
  * Must reference a valid key in `WorkspaceConfig.repos`.
1600
1734
  */
1601
1735
  defaultRepo: string;
1736
+ /**
1737
+ * When true, every task MUST declare an explicit execution target
1738
+ * (via `## Execution Target` section or inline `**Repo:**` in PROMPT.md).
1739
+ * Area-level and workspace-default fallbacks are still used for
1740
+ * validation (unknown-repo checks) but NOT for automatic resolution.
1741
+ *
1742
+ * This prevents accidental misrouting in large multi-team workspaces
1743
+ * where task authors must be intentional about which repo a task targets.
1744
+ *
1745
+ * Default: false (permissive — existing precedence chain applies).
1746
+ * Only meaningful in workspace mode.
1747
+ */
1748
+ strict?: boolean;
1602
1749
  }
1603
1750
 
1604
1751
  /**