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.
@@ -12,7 +12,6 @@ export interface OrchestratorConfig {
12
12
  max_lanes: number;
13
13
  worktree_location: "sibling" | "subdirectory";
14
14
  worktree_prefix: string;
15
- integration_branch: string;
16
15
  batch_id_format: "timestamp" | "sequential";
17
16
  spawn_mode: "tmux" | "subprocess";
18
17
  tmux_prefix: string;
@@ -60,6 +59,10 @@ export interface ParsedTask {
60
59
  promptPath: string;
61
60
  areaName: string;
62
61
  status: "pending" | "complete";
62
+ /** Repo ID declared in the PROMPT metadata (e.g., "api", "frontend"). Undefined if not declared. */
63
+ promptRepoId?: string;
64
+ /** Resolved repo ID after routing precedence (workspace mode only). Undefined in repo mode. */
65
+ resolvedRepoId?: string;
63
66
  }
64
67
 
65
68
  /** A wave: a group of tasks whose dependencies are all satisfied */
@@ -106,6 +109,8 @@ export interface TaskArea {
106
109
  path: string;
107
110
  prefix: string;
108
111
  context: string;
112
+ /** Optional repo ID for routing tasks in this area (workspace mode only). */
113
+ repoId?: string;
109
114
  }
110
115
 
111
116
  /** Subset of task-runner.yaml that the orchestrator needs */
@@ -136,7 +141,6 @@ export const DEFAULT_ORCHESTRATOR_CONFIG: OrchestratorConfig = {
136
141
  max_lanes: 3,
137
142
  worktree_location: "subdirectory",
138
143
  worktree_prefix: "taskplane-wt",
139
- integration_branch: "main",
140
144
  batch_id_format: "timestamp",
141
145
  spawn_mode: "subprocess",
142
146
  tmux_prefix: "orch",
@@ -361,12 +365,31 @@ export interface DiscoveryError {
361
365
  | "DEP_UNRESOLVED"
362
366
  | "DEP_PENDING"
363
367
  | "DEP_AMBIGUOUS"
364
- | "DEP_SOURCE_FALLBACK";
368
+ | "DEP_SOURCE_FALLBACK"
369
+ | "TASK_REPO_UNRESOLVED"
370
+ | "TASK_REPO_UNKNOWN";
365
371
  message: string;
366
372
  taskPath?: string;
367
373
  taskId?: string;
368
374
  }
369
375
 
376
+ /**
377
+ * Discovery error codes that are fatal (block planning/execution).
378
+ *
379
+ * Used by formatDiscoveryResults, extension.ts, and engine.ts for
380
+ * consistent fatal-error classification. Keep in sync with the
381
+ * DiscoveryError.code union above.
382
+ */
383
+ export const FATAL_DISCOVERY_CODES: ReadonlyArray<DiscoveryError["code"]> = [
384
+ "DUPLICATE_ID",
385
+ "DEP_UNRESOLVED",
386
+ "DEP_PENDING",
387
+ "DEP_AMBIGUOUS",
388
+ "PARSE_MISSING_ID",
389
+ "TASK_REPO_UNRESOLVED",
390
+ "TASK_REPO_UNKNOWN",
391
+ ] as const;
392
+
370
393
  /** Result of the full discovery pipeline */
371
394
  export interface DiscoveryResult {
372
395
  pending: Map<string, ParsedTask>;
@@ -790,6 +813,8 @@ export interface OrchBatchRuntimeState {
790
813
  phase: OrchBatchPhase;
791
814
  /** Unique batch identifier (timestamp format, e.g., "20260308T214300") */
792
815
  batchId: string;
816
+ /** Branch that was active when /orch started — used as base for worktrees and merge target */
817
+ baseBranch: string;
793
818
  /** Shared pause signal — set by /orch-pause, read by executeLane/executeWave */
794
819
  pauseSignal: { paused: boolean };
795
820
  /** All wave results in order (grows as waves complete) */
@@ -866,6 +891,7 @@ export function freshOrchBatchState(): OrchBatchRuntimeState {
866
891
  return {
867
892
  phase: "idle",
868
893
  batchId: "",
894
+ baseBranch: "",
869
895
  pauseSignal: { paused: false },
870
896
  waveResults: [],
871
897
  currentWaveIndex: -1,
@@ -1208,6 +1234,8 @@ export interface PersistedBatchState {
1208
1234
  phase: OrchBatchPhase;
1209
1235
  /** Unique batch identifier (timestamp format) */
1210
1236
  batchId: string;
1237
+ /** Branch that was active when /orch started — used as base for worktrees and merge target */
1238
+ baseBranch: string;
1211
1239
  /** Epoch ms when batch started */
1212
1240
  startedAt: number;
1213
1241
  /** Epoch ms when state was last written */
@@ -1516,3 +1544,215 @@ export interface BatchHistorySummary {
1516
1544
  /** Max number of batch history entries to retain. */
1517
1545
  export const BATCH_HISTORY_MAX_ENTRIES = 100;
1518
1546
 
1547
+
1548
+ // ── Workspace Mode Types ─────────────────────────────────────────────
1549
+
1550
+ /**
1551
+ * Workspace execution mode.
1552
+ *
1553
+ * Mode behavior contract:
1554
+ * - **"repo"** (default): No workspace config file present. The orchestrator
1555
+ * treats `cwd` as both the workspace root and the single repo root.
1556
+ * All existing monorepo behavior is preserved unchanged.
1557
+ * - **"workspace"**: A `.pi/taskplane-workspace.yaml` file is present and
1558
+ * valid. The orchestrator runs from a non-git workspace root that
1559
+ * coordinates multiple repos and a shared task root.
1560
+ *
1561
+ * Mode determination rules:
1562
+ * 1. No workspace config file → repo mode (non-fatal default, silent).
1563
+ * 2. Workspace config file present + invalid → fatal error with actionable
1564
+ * `WorkspaceConfigError` (never silently falls back to repo mode).
1565
+ * 3. Workspace config file present + valid → workspace mode.
1566
+ */
1567
+ export type WorkspaceMode = "repo" | "workspace";
1568
+
1569
+ /**
1570
+ * Configuration for a single repository within a workspace.
1571
+ *
1572
+ * Each repo is identified by a stable ID (e.g., "api", "frontend")
1573
+ * that is used for routing tasks to repos and for display purposes.
1574
+ */
1575
+ export interface WorkspaceRepoConfig {
1576
+ /** Stable identifier for this repo (e.g., "api", "frontend") */
1577
+ id: string;
1578
+ /** Absolute filesystem path to the repo root (must be a git repo) */
1579
+ path: string;
1580
+ /** Optional default branch override (e.g., "develop", "main"). Falls back to repo HEAD. */
1581
+ defaultBranch?: string;
1582
+ }
1583
+
1584
+ /**
1585
+ * Routing configuration for workspace mode.
1586
+ *
1587
+ * Controls where tasks are discovered and which repo receives
1588
+ * unqualified operations.
1589
+ */
1590
+ export interface WorkspaceRoutingConfig {
1591
+ /**
1592
+ * Absolute path to the shared tasks root directory.
1593
+ * All task areas are resolved relative to this path.
1594
+ * Must exist on disk.
1595
+ */
1596
+ tasksRoot: string;
1597
+ /**
1598
+ * Default repo ID for operations that don't specify a repo.
1599
+ * Must reference a valid key in `WorkspaceConfig.repos`.
1600
+ */
1601
+ defaultRepo: string;
1602
+ }
1603
+
1604
+ /**
1605
+ * Top-level workspace configuration.
1606
+ *
1607
+ * Loaded from `.pi/taskplane-workspace.yaml` when present.
1608
+ * Immutable after initial validation — never mutated at runtime.
1609
+ */
1610
+ export interface WorkspaceConfig {
1611
+ /** Active workspace mode */
1612
+ mode: WorkspaceMode;
1613
+ /** Map of repo ID → repo configuration. At least one repo required in workspace mode. */
1614
+ repos: Map<string, WorkspaceRepoConfig>;
1615
+ /** Routing configuration (tasks root, default repo) */
1616
+ routing: WorkspaceRoutingConfig;
1617
+ /** Absolute path to the workspace config file that was loaded */
1618
+ configPath: string;
1619
+ }
1620
+
1621
+ /**
1622
+ * Canonical execution context for the orchestrator.
1623
+ *
1624
+ * This is the primary runtime context threaded through orchestrator
1625
+ * entry points. It replaces the previous pattern of passing raw `cwd`
1626
+ * as the sole repo root.
1627
+ *
1628
+ * In repo mode, `workspaceRoot` and `repoRoot` are the same directory.
1629
+ * In workspace mode, `workspaceRoot` is the non-git coordination root
1630
+ * and `repoRoot` is the default repo from the workspace config.
1631
+ *
1632
+ * Design rationale:
1633
+ * - Step 2 (wire orchestrator startup) will construct this from config
1634
+ * loading results and thread it into `executeOrchBatch()` and friends.
1635
+ * - `repoRoot` is always a git repository, preserving the invariant
1636
+ * that git operations (worktree, branch, merge) have a valid target.
1637
+ * - `workspaceConfig` is null in repo mode (no workspace file loaded).
1638
+ */
1639
+ export interface ExecutionContext {
1640
+ /** Absolute path to the workspace root (cwd in repo mode, workspace dir in workspace mode) */
1641
+ workspaceRoot: string;
1642
+ /** Absolute path to the default/primary git repo root */
1643
+ repoRoot: string;
1644
+ /** Active workspace mode */
1645
+ mode: WorkspaceMode;
1646
+ /** Workspace configuration (null in repo mode) */
1647
+ workspaceConfig: WorkspaceConfig | null;
1648
+ /** Loaded task runner configuration */
1649
+ taskRunnerConfig: TaskRunnerConfig;
1650
+ /** Loaded orchestrator configuration */
1651
+ orchestratorConfig: OrchestratorConfig;
1652
+ }
1653
+
1654
+
1655
+ // ── Workspace Validation Error Types ─────────────────────────────────
1656
+
1657
+ /**
1658
+ * Error codes for workspace configuration validation failures.
1659
+ *
1660
+ * Each code maps to a deterministic validation rule from the workspace
1661
+ * config loading pipeline. Codes are stable and machine-branchable.
1662
+ *
1663
+ * - WORKSPACE_FILE_READ_ERROR: Config file exists but cannot be read (permissions, encoding)
1664
+ * - WORKSPACE_FILE_PARSE_ERROR: Config file contains invalid YAML
1665
+ * - WORKSPACE_MISSING_REPOS: No repos defined in workspace config (at least one required)
1666
+ * - WORKSPACE_REPO_PATH_MISSING: A repo entry has no `path` field
1667
+ * - WORKSPACE_REPO_PATH_NOT_FOUND: A repo's `path` does not exist on disk
1668
+ * - WORKSPACE_REPO_NOT_GIT: A repo's `path` exists but is not a git repository
1669
+ * - WORKSPACE_MISSING_TASKS_ROOT: `routing.tasks_root` is missing or empty
1670
+ * - WORKSPACE_TASKS_ROOT_NOT_FOUND: `routing.tasks_root` path does not exist on disk
1671
+ * - WORKSPACE_MISSING_DEFAULT_REPO: `routing.default_repo` is missing or empty
1672
+ * - WORKSPACE_DEFAULT_REPO_NOT_FOUND: `routing.default_repo` references a repo ID not in the repos map
1673
+ * - WORKSPACE_DUPLICATE_REPO_PATH: Two or more repos share the same filesystem path
1674
+ * - WORKSPACE_SCHEMA_INVALID: Config file has valid YAML but missing/invalid top-level structure
1675
+ */
1676
+ export type WorkspaceConfigErrorCode =
1677
+ | "WORKSPACE_FILE_READ_ERROR"
1678
+ | "WORKSPACE_FILE_PARSE_ERROR"
1679
+ | "WORKSPACE_MISSING_REPOS"
1680
+ | "WORKSPACE_REPO_PATH_MISSING"
1681
+ | "WORKSPACE_REPO_PATH_NOT_FOUND"
1682
+ | "WORKSPACE_REPO_NOT_GIT"
1683
+ | "WORKSPACE_MISSING_TASKS_ROOT"
1684
+ | "WORKSPACE_TASKS_ROOT_NOT_FOUND"
1685
+ | "WORKSPACE_MISSING_DEFAULT_REPO"
1686
+ | "WORKSPACE_DEFAULT_REPO_NOT_FOUND"
1687
+ | "WORKSPACE_DUPLICATE_REPO_PATH"
1688
+ | "WORKSPACE_SCHEMA_INVALID";
1689
+
1690
+ /**
1691
+ * Typed error class for workspace configuration failures.
1692
+ *
1693
+ * Thrown during workspace config loading/validation when the config file
1694
+ * is present but invalid. Never thrown when no config file exists (that
1695
+ * case silently falls back to repo mode).
1696
+ *
1697
+ * Follows the established pattern of typed error classes in this module
1698
+ * (WorktreeError, ExecutionError, MergeError, StateFileError, ResumeError).
1699
+ */
1700
+ export class WorkspaceConfigError extends Error {
1701
+ code: WorkspaceConfigErrorCode;
1702
+ /** Optional repo ID that triggered the error (for repo-specific validation failures) */
1703
+ repoId?: string;
1704
+ /** Optional filesystem path related to the error */
1705
+ relatedPath?: string;
1706
+
1707
+ constructor(code: WorkspaceConfigErrorCode, message: string, repoId?: string, relatedPath?: string) {
1708
+ super(message);
1709
+ this.name = "WorkspaceConfigError";
1710
+ this.code = code;
1711
+ this.repoId = repoId;
1712
+ this.relatedPath = relatedPath;
1713
+ }
1714
+ }
1715
+
1716
+
1717
+ // ── Workspace Defaults ───────────────────────────────────────────────
1718
+
1719
+ /**
1720
+ * Canonical filename for workspace configuration.
1721
+ * Resolved relative to workspace root: `.pi/taskplane-workspace.yaml`
1722
+ */
1723
+ export const WORKSPACE_CONFIG_FILENAME = "taskplane-workspace.yaml";
1724
+
1725
+ /**
1726
+ * Resolve the absolute path to the workspace config file.
1727
+ * @param workspaceRoot - Absolute path to the workspace root
1728
+ */
1729
+ export function workspaceConfigPath(workspaceRoot: string): string {
1730
+ return join(workspaceRoot, ".pi", WORKSPACE_CONFIG_FILENAME);
1731
+ }
1732
+
1733
+ /**
1734
+ * Create a default ExecutionContext for repo mode.
1735
+ *
1736
+ * Used when no workspace config file is present. The workspace root
1737
+ * and repo root are the same directory (cwd), preserving existing
1738
+ * monorepo behavior exactly.
1739
+ *
1740
+ * @param cwd - Current working directory (treated as both workspace and repo root)
1741
+ * @param taskRunnerConfig - Loaded task runner config (or defaults)
1742
+ * @param orchestratorConfig - Loaded orchestrator config (or defaults)
1743
+ */
1744
+ export function createRepoModeContext(
1745
+ cwd: string,
1746
+ taskRunnerConfig: TaskRunnerConfig,
1747
+ orchestratorConfig: OrchestratorConfig,
1748
+ ): ExecutionContext {
1749
+ return {
1750
+ workspaceRoot: cwd,
1751
+ repoRoot: cwd,
1752
+ mode: "repo",
1753
+ workspaceConfig: null,
1754
+ taskRunnerConfig,
1755
+ orchestratorConfig,
1756
+ };
1757
+ }
1758
+