macro-agent 0.1.8 → 0.1.11

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 (306) hide show
  1. package/CLAUDE.md +263 -33
  2. package/README.md +781 -131
  3. package/dist/acp/claude-code-replay.d.ts +11 -0
  4. package/dist/acp/claude-code-replay.d.ts.map +1 -0
  5. package/dist/acp/claude-code-replay.js +190 -0
  6. package/dist/acp/claude-code-replay.js.map +1 -0
  7. package/dist/acp/macro-agent.d.ts.map +1 -1
  8. package/dist/acp/macro-agent.js +192 -7
  9. package/dist/acp/macro-agent.js.map +1 -1
  10. package/dist/acp/types.d.ts +9 -0
  11. package/dist/acp/types.d.ts.map +1 -1
  12. package/dist/acp/types.js.map +1 -1
  13. package/dist/adapters/tasks-adapter.d.ts.map +1 -1
  14. package/dist/adapters/tasks-adapter.js +3 -0
  15. package/dist/adapters/tasks-adapter.js.map +1 -1
  16. package/dist/adapters/types.d.ts +1 -0
  17. package/dist/adapters/types.d.ts.map +1 -1
  18. package/dist/agent/agent-manager-v2.d.ts +21 -0
  19. package/dist/agent/agent-manager-v2.d.ts.map +1 -1
  20. package/dist/agent/agent-manager-v2.js +308 -54
  21. package/dist/agent/agent-manager-v2.js.map +1 -1
  22. package/dist/agent/agent-manager.d.ts +12 -0
  23. package/dist/agent/agent-manager.d.ts.map +1 -1
  24. package/dist/agent/agent-manager.js.map +1 -1
  25. package/dist/agent/agent-store.d.ts +10 -0
  26. package/dist/agent/agent-store.d.ts.map +1 -1
  27. package/dist/agent/agent-store.js +22 -0
  28. package/dist/agent/agent-store.js.map +1 -1
  29. package/dist/agent/types.d.ts +15 -2
  30. package/dist/agent/types.d.ts.map +1 -1
  31. package/dist/agent/types.js.map +1 -1
  32. package/dist/boot-v2.d.ts +129 -1
  33. package/dist/boot-v2.d.ts.map +1 -1
  34. package/dist/boot-v2.js +359 -8
  35. package/dist/boot-v2.js.map +1 -1
  36. package/dist/cli/acp.js +4 -0
  37. package/dist/cli/acp.js.map +1 -1
  38. package/dist/cli/index.js +56 -0
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/cognitive/macro-agent-backend.d.ts.map +1 -1
  41. package/dist/cognitive/macro-agent-backend.js +40 -22
  42. package/dist/cognitive/macro-agent-backend.js.map +1 -1
  43. package/dist/integrations/skilltree.d.ts.map +1 -1
  44. package/dist/integrations/skilltree.js +1 -0
  45. package/dist/integrations/skilltree.js.map +1 -1
  46. package/dist/lifecycle/cascade.d.ts +25 -2
  47. package/dist/lifecycle/cascade.d.ts.map +1 -1
  48. package/dist/lifecycle/cascade.js +70 -2
  49. package/dist/lifecycle/cascade.js.map +1 -1
  50. package/dist/lifecycle/cleanup.d.ts +33 -2
  51. package/dist/lifecycle/cleanup.d.ts.map +1 -1
  52. package/dist/lifecycle/cleanup.js +28 -6
  53. package/dist/lifecycle/cleanup.js.map +1 -1
  54. package/dist/lifecycle/handlers-v2.d.ts +7 -0
  55. package/dist/lifecycle/handlers-v2.d.ts.map +1 -1
  56. package/dist/lifecycle/handlers-v2.js +28 -2
  57. package/dist/lifecycle/handlers-v2.js.map +1 -1
  58. package/dist/lifecycle/types.d.ts +11 -0
  59. package/dist/lifecycle/types.d.ts.map +1 -1
  60. package/dist/lifecycle/types.js.map +1 -1
  61. package/dist/map/acp-bridge.d.ts +9 -0
  62. package/dist/map/acp-bridge.d.ts.map +1 -1
  63. package/dist/map/acp-bridge.js +15 -2
  64. package/dist/map/acp-bridge.js.map +1 -1
  65. package/dist/map/cascade-action-handler.d.ts +24 -0
  66. package/dist/map/cascade-action-handler.d.ts.map +1 -0
  67. package/dist/map/cascade-action-handler.js +170 -0
  68. package/dist/map/cascade-action-handler.js.map +1 -0
  69. package/dist/map/cascade-bridge.d.ts +44 -0
  70. package/dist/map/cascade-bridge.d.ts.map +1 -0
  71. package/dist/map/cascade-bridge.js +294 -0
  72. package/dist/map/cascade-bridge.js.map +1 -0
  73. package/dist/map/coordination-handler.d.ts.map +1 -1
  74. package/dist/map/coordination-handler.js +12 -1
  75. package/dist/map/coordination-handler.js.map +1 -1
  76. package/dist/map/lifecycle-bridge.d.ts +1 -1
  77. package/dist/map/lifecycle-bridge.d.ts.map +1 -1
  78. package/dist/map/lifecycle-bridge.js +58 -23
  79. package/dist/map/lifecycle-bridge.js.map +1 -1
  80. package/dist/map/server.d.ts.map +1 -1
  81. package/dist/map/server.js +219 -7
  82. package/dist/map/server.js.map +1 -1
  83. package/dist/map/sidecar.d.ts.map +1 -1
  84. package/dist/map/sidecar.js +49 -2
  85. package/dist/map/sidecar.js.map +1 -1
  86. package/dist/map/types.d.ts +22 -0
  87. package/dist/map/types.d.ts.map +1 -1
  88. package/dist/mcp/tools/done-v2.d.ts.map +1 -1
  89. package/dist/mcp/tools/done-v2.js +8 -0
  90. package/dist/mcp/tools/done-v2.js.map +1 -1
  91. package/dist/teams/team-manager-v2.d.ts.map +1 -1
  92. package/dist/teams/team-manager-v2.js +26 -0
  93. package/dist/teams/team-manager-v2.js.map +1 -1
  94. package/dist/teams/team-runtime-v2.d.ts.map +1 -1
  95. package/dist/teams/team-runtime-v2.js +16 -3
  96. package/dist/teams/team-runtime-v2.js.map +1 -1
  97. package/dist/workspace/config.d.ts +10 -10
  98. package/dist/workspace/config.d.ts.map +1 -1
  99. package/dist/workspace/config.js +4 -4
  100. package/dist/workspace/config.js.map +1 -1
  101. package/dist/workspace/git-cascade-adapter.d.ts +510 -0
  102. package/dist/workspace/git-cascade-adapter.d.ts.map +1 -0
  103. package/dist/workspace/git-cascade-adapter.js +934 -0
  104. package/dist/workspace/git-cascade-adapter.js.map +1 -0
  105. package/dist/workspace/index.d.ts +3 -3
  106. package/dist/workspace/index.d.ts.map +1 -1
  107. package/dist/workspace/index.js +4 -4
  108. package/dist/workspace/index.js.map +1 -1
  109. package/dist/workspace/landing/direct-push.d.ts +20 -0
  110. package/dist/workspace/landing/direct-push.d.ts.map +1 -0
  111. package/dist/workspace/landing/direct-push.js +74 -0
  112. package/dist/workspace/landing/direct-push.js.map +1 -0
  113. package/dist/workspace/landing/index.d.ts +29 -0
  114. package/dist/workspace/landing/index.d.ts.map +1 -0
  115. package/dist/workspace/landing/index.js +37 -0
  116. package/dist/workspace/landing/index.js.map +1 -0
  117. package/dist/workspace/landing/merge-to-parent.d.ts +41 -0
  118. package/dist/workspace/landing/merge-to-parent.d.ts.map +1 -0
  119. package/dist/workspace/landing/merge-to-parent.js +186 -0
  120. package/dist/workspace/landing/merge-to-parent.js.map +1 -0
  121. package/dist/workspace/landing/optimistic-push.d.ts +16 -0
  122. package/dist/workspace/landing/optimistic-push.d.ts.map +1 -0
  123. package/dist/workspace/landing/optimistic-push.js +27 -0
  124. package/dist/workspace/landing/optimistic-push.js.map +1 -0
  125. package/dist/workspace/landing/queue-to-branch.d.ts +24 -0
  126. package/dist/workspace/landing/queue-to-branch.d.ts.map +1 -0
  127. package/dist/workspace/landing/queue-to-branch.js +79 -0
  128. package/dist/workspace/landing/queue-to-branch.js.map +1 -0
  129. package/dist/workspace/merge-queue/merge-queue.d.ts +10 -0
  130. package/dist/workspace/merge-queue/merge-queue.d.ts.map +1 -1
  131. package/dist/workspace/merge-queue/merge-queue.js +10 -0
  132. package/dist/workspace/merge-queue/merge-queue.js.map +1 -1
  133. package/dist/workspace/merge-queue/types.d.ts +16 -2
  134. package/dist/workspace/merge-queue/types.d.ts.map +1 -1
  135. package/dist/workspace/merge-queue/types.js +9 -0
  136. package/dist/workspace/merge-queue/types.js.map +1 -1
  137. package/dist/workspace/pool/types.d.ts +1 -0
  138. package/dist/workspace/pool/types.d.ts.map +1 -1
  139. package/dist/workspace/pool/worktree-pool.d.ts.map +1 -1
  140. package/dist/workspace/pool/worktree-pool.js +1 -0
  141. package/dist/workspace/pool/worktree-pool.js.map +1 -1
  142. package/dist/workspace/recovery/abandon.d.ts +15 -0
  143. package/dist/workspace/recovery/abandon.d.ts.map +1 -0
  144. package/dist/workspace/recovery/abandon.js +45 -0
  145. package/dist/workspace/recovery/abandon.js.map +1 -0
  146. package/dist/workspace/recovery/auto-resolve.d.ts +27 -0
  147. package/dist/workspace/recovery/auto-resolve.d.ts.map +1 -0
  148. package/dist/workspace/recovery/auto-resolve.js +99 -0
  149. package/dist/workspace/recovery/auto-resolve.js.map +1 -0
  150. package/dist/workspace/recovery/defer.d.ts +15 -0
  151. package/dist/workspace/recovery/defer.d.ts.map +1 -0
  152. package/dist/workspace/recovery/defer.js +16 -0
  153. package/dist/workspace/recovery/defer.js.map +1 -0
  154. package/dist/workspace/recovery/escalate.d.ts +16 -0
  155. package/dist/workspace/recovery/escalate.d.ts.map +1 -0
  156. package/dist/workspace/recovery/escalate.js +24 -0
  157. package/dist/workspace/recovery/escalate.js.map +1 -0
  158. package/dist/workspace/recovery/index.d.ts +32 -0
  159. package/dist/workspace/recovery/index.d.ts.map +1 -0
  160. package/dist/workspace/recovery/index.js +45 -0
  161. package/dist/workspace/recovery/index.js.map +1 -0
  162. package/dist/workspace/recovery/spawn-resolver.d.ts +45 -0
  163. package/dist/workspace/recovery/spawn-resolver.d.ts.map +1 -0
  164. package/dist/workspace/recovery/spawn-resolver.js +118 -0
  165. package/dist/workspace/recovery/spawn-resolver.js.map +1 -0
  166. package/dist/workspace/recovery/types.d.ts +63 -0
  167. package/dist/workspace/recovery/types.d.ts.map +1 -0
  168. package/dist/workspace/recovery/types.js +12 -0
  169. package/dist/workspace/recovery/types.js.map +1 -0
  170. package/dist/workspace/topology/index.d.ts +9 -0
  171. package/dist/workspace/topology/index.d.ts.map +1 -0
  172. package/dist/workspace/topology/index.js +8 -0
  173. package/dist/workspace/topology/index.js.map +1 -0
  174. package/dist/workspace/topology/no-workspace.d.ts +18 -0
  175. package/dist/workspace/topology/no-workspace.d.ts.map +1 -0
  176. package/dist/workspace/topology/no-workspace.js +25 -0
  177. package/dist/workspace/topology/no-workspace.js.map +1 -0
  178. package/dist/workspace/topology/types.d.ts +97 -0
  179. package/dist/workspace/topology/types.d.ts.map +1 -0
  180. package/dist/workspace/topology/types.js +20 -0
  181. package/dist/workspace/topology/types.js.map +1 -0
  182. package/dist/workspace/topology/yaml-driven.d.ts +69 -0
  183. package/dist/workspace/topology/yaml-driven.d.ts.map +1 -0
  184. package/dist/workspace/topology/yaml-driven.js +273 -0
  185. package/dist/workspace/topology/yaml-driven.js.map +1 -0
  186. package/dist/workspace/types-v3.d.ts +117 -0
  187. package/dist/workspace/types-v3.d.ts.map +1 -0
  188. package/dist/workspace/types-v3.js +20 -0
  189. package/dist/workspace/types-v3.js.map +1 -0
  190. package/dist/workspace/types.d.ts +162 -17
  191. package/dist/workspace/types.d.ts.map +1 -1
  192. package/dist/workspace/workspace-manager.d.ts +101 -13
  193. package/dist/workspace/workspace-manager.d.ts.map +1 -1
  194. package/dist/workspace/workspace-manager.js +416 -13
  195. package/dist/workspace/workspace-manager.js.map +1 -1
  196. package/dist/workspace/yaml-schema.d.ts +254 -0
  197. package/dist/workspace/yaml-schema.d.ts.map +1 -0
  198. package/dist/workspace/yaml-schema.js +170 -0
  199. package/dist/workspace/yaml-schema.js.map +1 -0
  200. package/docs/conflict-recovery.md +472 -0
  201. package/docs/design/task-dispatcher.md +880 -0
  202. package/docs/git-cascade-integration-gaps.md +678 -0
  203. package/docs/workspace-interfaces.md +731 -0
  204. package/docs/workspace-redesign-plan.md +302 -0
  205. package/package.json +6 -5
  206. package/src/__tests__/boot-v2.test.ts +435 -0
  207. package/src/__tests__/e2e/acp-over-map.e2e.test.ts +92 -0
  208. package/src/__tests__/e2e/auto-sync.e2e.test.ts +257 -0
  209. package/src/__tests__/e2e/bootstrap.e2e.test.ts +319 -0
  210. package/src/__tests__/e2e/cascade-rebase.e2e.test.ts +254 -0
  211. package/src/__tests__/e2e/cli-run.e2e.test.ts +167 -0
  212. package/src/__tests__/e2e/dispatch-coordination.e2e.test.ts +495 -0
  213. package/src/__tests__/e2e/dispatch-live.e2e.test.ts +564 -0
  214. package/src/__tests__/e2e/dispatch-opentasks.e2e.test.ts +496 -0
  215. package/src/__tests__/e2e/dispatch-phase2-live.e2e.test.ts +456 -0
  216. package/src/__tests__/e2e/dispatch-phase2.e2e.test.ts +386 -0
  217. package/src/__tests__/e2e/dispatch.e2e.test.ts +376 -0
  218. package/src/__tests__/e2e/self-driving-v3.e2e.test.ts +197 -0
  219. package/src/__tests__/e2e/spawn-resolver.e2e.test.ts +200 -0
  220. package/src/__tests__/e2e/workspace-lifecycle.e2e.test.ts +30 -22
  221. package/src/__tests__/e2e/workspace-v3.e2e.test.ts +413 -0
  222. package/src/acp/__tests__/claude-code-replay.test.ts +225 -0
  223. package/src/acp/__tests__/macro-agent.test.ts +39 -1
  224. package/src/acp/claude-code-replay.ts +208 -0
  225. package/src/acp/macro-agent.ts +203 -10
  226. package/src/acp/types.ts +10 -0
  227. package/src/adapters/__tests__/tasks-adapter.test.ts +1 -0
  228. package/src/adapters/tasks-adapter.ts +3 -0
  229. package/src/adapters/types.ts +1 -0
  230. package/src/agent/__tests__/agent-manager-topology.test.ts +73 -0
  231. package/src/agent/__tests__/agent-manager-v2.test.ts +66 -0
  232. package/src/agent/__tests__/agent-store.test.ts +52 -0
  233. package/src/agent/__tests__/task-ref-resolution.test.ts +231 -0
  234. package/src/agent/agent-manager-v2.ts +372 -59
  235. package/src/agent/agent-manager.ts +14 -0
  236. package/src/agent/agent-store.ts +24 -0
  237. package/src/agent/types.ts +16 -2
  238. package/src/boot-v2.ts +589 -35
  239. package/src/cli/acp.ts +4 -0
  240. package/src/cli/index.ts +61 -0
  241. package/src/cognitive/macro-agent-backend.ts +45 -29
  242. package/src/integrations/skilltree.ts +1 -0
  243. package/src/lifecycle/__tests__/cascade-consolidation.test.ts +240 -0
  244. package/src/lifecycle/cascade.ts +77 -2
  245. package/src/lifecycle/cleanup.ts +52 -3
  246. package/src/lifecycle/handlers-v2.ts +40 -3
  247. package/src/lifecycle/types.ts +12 -0
  248. package/src/map/__tests__/cascade-bridge.test.ts +229 -0
  249. package/src/map/__tests__/emit-event.test.ts +71 -0
  250. package/src/map/__tests__/lifecycle-bridge.test.ts +86 -10
  251. package/src/map/acp-bridge.ts +26 -3
  252. package/src/map/cascade-action-handler.ts +205 -0
  253. package/src/map/cascade-bridge.ts +339 -0
  254. package/src/map/coordination-handler.ts +13 -1
  255. package/src/map/lifecycle-bridge.ts +52 -17
  256. package/src/map/server.ts +225 -7
  257. package/src/map/sidecar.ts +48 -1
  258. package/src/map/types.ts +23 -0
  259. package/src/mcp/tools/done-v2.ts +9 -0
  260. package/src/teams/team-manager-v2.ts +37 -0
  261. package/src/teams/team-runtime-v2.ts +23 -3
  262. package/src/workspace/__tests__/{dataplane-adapter.test.ts → git-cascade-adapter.test.ts} +209 -14
  263. package/src/workspace/__tests__/land-dispatch.test.ts +214 -0
  264. package/src/workspace/__tests__/self-driving-yaml.test.ts +114 -0
  265. package/src/workspace/__tests__/shared-worktree-refcount.test.ts +154 -0
  266. package/src/workspace/__tests__/standalone-mode.test.ts +118 -0
  267. package/src/workspace/__tests__/workspace-manager-v3.test.ts +245 -0
  268. package/src/workspace/__tests__/yaml-schema.test.ts +210 -0
  269. package/src/workspace/config.ts +11 -11
  270. package/src/workspace/git-cascade-adapter.ts +1213 -0
  271. package/src/workspace/index.ts +11 -11
  272. package/src/workspace/landing/__tests__/strategies.test.ts +184 -0
  273. package/src/workspace/landing/direct-push.ts +91 -0
  274. package/src/workspace/landing/index.ts +40 -0
  275. package/src/workspace/landing/merge-to-parent.ts +229 -0
  276. package/src/workspace/landing/optimistic-push.ts +36 -0
  277. package/src/workspace/landing/queue-to-branch.ts +108 -0
  278. package/src/workspace/merge-queue/merge-queue.ts +10 -0
  279. package/src/workspace/merge-queue/types.ts +16 -2
  280. package/src/workspace/pool/__tests__/worktree-pool.integration.test.ts +5 -5
  281. package/src/workspace/pool/types.ts +1 -0
  282. package/src/workspace/pool/worktree-pool.ts +1 -0
  283. package/src/workspace/recovery/__tests__/auto-resolve-integration.test.ts +127 -0
  284. package/src/workspace/recovery/__tests__/spawn-resolver.test.ts +139 -0
  285. package/src/workspace/recovery/__tests__/strategies.test.ts +145 -0
  286. package/src/workspace/recovery/abandon.ts +51 -0
  287. package/src/workspace/recovery/auto-resolve.ts +119 -0
  288. package/src/workspace/recovery/defer.ts +23 -0
  289. package/src/workspace/recovery/escalate.ts +30 -0
  290. package/src/workspace/recovery/index.ts +58 -0
  291. package/src/workspace/recovery/spawn-resolver.ts +152 -0
  292. package/src/workspace/recovery/types.ts +54 -0
  293. package/src/workspace/topology/__tests__/yaml-driven.test.ts +345 -0
  294. package/src/workspace/topology/index.ts +18 -0
  295. package/src/workspace/topology/no-workspace.ts +39 -0
  296. package/src/workspace/topology/types.ts +116 -0
  297. package/src/workspace/topology/yaml-driven.ts +316 -0
  298. package/src/workspace/types-v3.ts +162 -0
  299. package/src/workspace/types.ts +211 -20
  300. package/src/workspace/workspace-manager.ts +533 -19
  301. package/src/workspace/yaml-schema.ts +216 -0
  302. package/dist/workspace/dataplane-adapter.d.ts +0 -260
  303. package/dist/workspace/dataplane-adapter.d.ts.map +0 -1
  304. package/dist/workspace/dataplane-adapter.js +0 -416
  305. package/dist/workspace/dataplane-adapter.js.map +0 -1
  306. package/src/workspace/dataplane-adapter.ts +0 -546
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WorkspaceManager Implementation
3
3
  *
4
- * Bridges macro-agent roles to dataplane streams and worktrees.
4
+ * Bridges macro-agent roles to git-cascade streams and worktrees.
5
5
  * Provides a higher-level API for workspace management.
6
6
  *
7
7
  * @module workspace/workspace-manager
@@ -9,8 +9,8 @@
9
9
  */
10
10
 
11
11
  import type { Stream, WorkerTask, StartTaskResult, AgentWorktree, CleanupWorkerBranchesOptions, CleanupResult } from 'git-cascade';
12
- import { DataplaneAdapter } from './dataplane-adapter.js';
13
- import type { DataplaneConfig, WorktreePoolConfig } from './config.js';
12
+ import { GitCascadeAdapter } from './git-cascade-adapter.js';
13
+ import type { GitCascadeConfig, WorktreePoolConfig } from './config.js';
14
14
  import { DEFAULT_POOL_CONFIG } from './config.js';
15
15
  import { WorktreePool } from './pool/worktree-pool.js';
16
16
  import type { AllocationStrategy, AcquireOptions } from './pool/types.js';
@@ -35,7 +35,7 @@ import { execSync } from 'child_process';
35
35
  /**
36
36
  * Configuration options for DefaultWorkspaceManager.
37
37
  */
38
- export interface WorkspaceManagerConfig extends DataplaneConfig {
38
+ export interface WorkspaceManagerConfig extends GitCascadeConfig {
39
39
  /**
40
40
  * Base directory for worktrees.
41
41
  * Defaults to `<repoPath>/.worktrees`.
@@ -53,14 +53,14 @@ export interface WorkspaceManagerConfig extends DataplaneConfig {
53
53
  * DefaultWorkspaceManager implements the WorkspaceManager interface.
54
54
  *
55
55
  * Responsibilities:
56
- * - Wraps DataplaneAdapter for stream/worktree operations
56
+ * - Wraps GitCascadeAdapter for stream/worktree operations
57
57
  * - Maintains agentId → workspace mappings
58
58
  * - Emits events on workspace lifecycle changes
59
59
  *
60
60
  * @see [[s-7ktd]] WorkspaceManager section
61
61
  */
62
62
  export class DefaultWorkspaceManager implements WorkspaceManager {
63
- private readonly adapter: DataplaneAdapter;
63
+ private readonly adapter: GitCascadeAdapter;
64
64
  private readonly config: Required<Pick<WorkspaceManagerConfig, 'worktreeBaseDir'>>;
65
65
  private readonly poolConfig: WorktreePoolConfig;
66
66
  private readonly workspaces: Map<AgentId, Workspace> = new Map();
@@ -72,10 +72,10 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
72
72
  /**
73
73
  * Create a new DefaultWorkspaceManager.
74
74
  *
75
- * @param adapter - DataplaneAdapter instance
75
+ * @param adapter - GitCascadeAdapter instance
76
76
  * @param config - Configuration options
77
77
  */
78
- constructor(adapter: DataplaneAdapter, config?: Partial<WorkspaceManagerConfig>) {
78
+ constructor(adapter: GitCascadeAdapter, config?: Partial<WorkspaceManagerConfig>) {
79
79
  this.adapter = adapter;
80
80
  this.config = {
81
81
  worktreeBaseDir: config?.worktreeBaseDir ?? `${adapter.repoPath}/.worktrees`,
@@ -86,6 +86,19 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
86
86
  };
87
87
  }
88
88
 
89
+ /**
90
+ * Access the underlying GitCascadeAdapter. Exposed for bridges that
91
+ * subscribe to the cascade event stream (e.g., the MAP cascade-bridge
92
+ * that forwards events to an OpenHive hub).
93
+ *
94
+ * Most callers should use the WorkspaceManager API surface. Use this only
95
+ * when direct access to the adapter's event stream or primitives is
96
+ * required.
97
+ */
98
+ getGitCascadeAdapter(): GitCascadeAdapter {
99
+ return this.adapter;
100
+ }
101
+
89
102
  // ─────────────────────────────────────────────────────────────────────────────
90
103
  // Event System
91
104
  // ─────────────────────────────────────────────────────────────────────────────
@@ -370,11 +383,59 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
370
383
  * @param agentId - ID of the agent whose workspace to deallocate
371
384
  */
372
385
  deallocateWorkspace(agentId: AgentId): void {
386
+ // Case 1: agentId is a sharer on someone else's worktree. Decrement
387
+ // the ref-count; only tear down if this was the last sharer AND the
388
+ // owner had previously departed.
389
+ for (const [path, entry] of this.sharedWorktreeRefs) {
390
+ if (entry.sharers.has(agentId)) {
391
+ entry.sharers.delete(agentId);
392
+ this.emit('worktree:released', {
393
+ agentId,
394
+ path,
395
+ kind: 'sharer',
396
+ });
397
+ if (entry.ownerDeparted && entry.sharers.size === 0) {
398
+ // Last sharer exiting after owner departed — finalize teardown
399
+ // under the original owner's id.
400
+ this.adapter.deallocateWorktree(entry.ownerId);
401
+ this.sharedWorktreeRefs.delete(path);
402
+ this.emit('workspace:deallocated', {
403
+ agentId: entry.ownerId,
404
+ path,
405
+ deferredUntilLastSharer: true,
406
+ });
407
+ }
408
+ return;
409
+ }
410
+ }
411
+
373
412
  const workspace = this.workspaces.get(agentId);
374
413
  if (!workspace) {
375
414
  return; // Already deallocated
376
415
  }
377
416
 
417
+ // Case 2: agentId is an owner of a shared worktree with active sharers.
418
+ // Defer git-cascade teardown until the last sharer leaves.
419
+ const sharedEntry = this.sharedWorktreeRefs.get(workspace.path);
420
+ if (
421
+ sharedEntry &&
422
+ sharedEntry.ownerId === agentId &&
423
+ sharedEntry.sharers.size > 0
424
+ ) {
425
+ sharedEntry.ownerDeparted = true;
426
+ this.workspaces.delete(agentId);
427
+ this.agentToStream.delete(agentId);
428
+ this.emit('worktree:released', {
429
+ agentId,
430
+ path: workspace.path,
431
+ kind: 'owner-departed',
432
+ remainingSharers: sharedEntry.sharers.size,
433
+ });
434
+ return;
435
+ }
436
+
437
+ // Case 3: normal teardown — owner with no sharers (or no sharing involved).
438
+
378
439
  // Remove from coordinator's child workspace map if this is a child
379
440
  if (workspace.role === 'worker' || workspace.role === 'integrator') {
380
441
  const streamId = workspace.streamId;
@@ -387,14 +448,18 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
387
448
  }
388
449
  }
389
450
 
390
- // Deallocate via dataplane
451
+ // Deallocate via git-cascade adapter
391
452
  this.adapter.deallocateWorktree(agentId);
392
453
 
393
454
  // Clean up mappings
394
455
  this.workspaces.delete(agentId);
395
456
  this.agentToStream.delete(agentId);
396
457
 
397
- // Emit event
458
+ // Also clean up any stale entry (owner with zero sharers at deallocation time)
459
+ if (sharedEntry && sharedEntry.ownerId === agentId) {
460
+ this.sharedWorktreeRefs.delete(workspace.path);
461
+ }
462
+
398
463
  this.emit('workspace:deallocated', {
399
464
  agentId,
400
465
  role: workspace.role,
@@ -511,11 +576,11 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
511
576
  }
512
577
 
513
578
  /**
514
- * Get the underlying DataplaneAdapter.
579
+ * Get the underlying GitCascadeAdapter.
515
580
  *
516
581
  * Use with caution - prefer manager methods for operations.
517
582
  */
518
- get rawAdapter(): DataplaneAdapter {
583
+ get rawAdapter(): GitCascadeAdapter {
519
584
  return this.adapter;
520
585
  }
521
586
 
@@ -527,7 +592,7 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
527
592
  * Get the merge queue for coordinating worker merges.
528
593
  *
529
594
  * The merge queue is lazily initialized on first access and uses
530
- * the same database as the dataplane adapter.
595
+ * the same database as the git-cascade adapter.
531
596
  *
532
597
  * @returns MergeQueue instance
533
598
  */
@@ -535,7 +600,7 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
535
600
  if (!this.mergeQueue) {
536
601
  this.mergeQueue = new MergeQueue({
537
602
  db: this.adapter.db,
538
- tablePrefix: 'macro_', // Use different prefix from dataplane tables
603
+ tablePrefix: 'macro_', // Use different prefix from git-cascade tables
539
604
  initSchema: true,
540
605
  });
541
606
  }
@@ -895,7 +960,7 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
895
960
  }
896
961
  }
897
962
 
898
- // Release to pool if enabled, otherwise deallocate via dataplane
963
+ // Release to pool if enabled, otherwise deallocate via git-cascade
899
964
  const pool = this.getPool();
900
965
  if (pool) {
901
966
  await pool.release(agentId, { clean });
@@ -963,6 +1028,431 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
963
1028
  return result;
964
1029
  }
965
1030
 
1031
+ // ═════════════════════════════════════════════════════════════════════════════
1032
+ // V3 — Stream-first surface
1033
+ //
1034
+ // Additive with the role-shaped methods above. Callers migrate piecewise
1035
+ // during Phases 3-4; Phase 9 removes the legacy methods. See
1036
+ // docs/workspace-interfaces.md §5 and docs/workspace-redesign-plan.md.
1037
+ // ═════════════════════════════════════════════════════════════════════════════
1038
+
1039
+ private readonly landingStrategies: Map<string, import('./types-v3.js').LandingStrategy> = new Map();
1040
+
1041
+ /**
1042
+ * Ref-counted sharing state keyed by worktree path.
1043
+ *
1044
+ * - `ownerId`: the principal that originally allocated the worktree (the
1045
+ * id that git-cascade's tracker associates with the worktree).
1046
+ * - `sharers`: other agents that allocated via `sharedWithAgent`.
1047
+ * - `ownerDeparted`: set when the owner deallocates but sharers remain —
1048
+ * the actual git-cascade teardown is deferred until `sharers` is empty.
1049
+ */
1050
+ private readonly sharedWorktreeRefs: Map<
1051
+ string,
1052
+ { ownerId: import('./types-v3.js').Principal; sharers: Set<AgentId>; ownerDeparted: boolean }
1053
+ > = new Map();
1054
+
1055
+ createStreamV3(spec: import('./types-v3.js').StreamSpec): StreamId {
1056
+ let streamId: StreamId;
1057
+ if (spec.parent) {
1058
+ streamId = this.adapter.forkStream({
1059
+ parentStreamId: spec.parent,
1060
+ name: spec.name,
1061
+ agentId: spec.ownerId,
1062
+ });
1063
+ // git-cascade's ForkStreamOptions doesn't accept metadata; apply via update.
1064
+ if (spec.metadata) {
1065
+ this.adapter.updateStream(streamId, { metadata: spec.metadata });
1066
+ }
1067
+ } else {
1068
+ streamId = this.adapter.createStream({
1069
+ name: spec.name,
1070
+ agentId: spec.ownerId,
1071
+ base: spec.forkFrom ?? 'main',
1072
+ metadata: spec.metadata,
1073
+ });
1074
+ }
1075
+ this.emit(spec.parent ? 'stream:forked' : 'stream:created', {
1076
+ streamId,
1077
+ ownerId: spec.ownerId,
1078
+ parentStreamId: spec.parent,
1079
+ });
1080
+ return streamId;
1081
+ }
1082
+
1083
+ forkStream(opts: {
1084
+ parentStreamId: StreamId;
1085
+ name: string;
1086
+ ownerId: import('./types-v3.js').Principal;
1087
+ metadata?: Record<string, unknown>;
1088
+ }): StreamId {
1089
+ const streamId = this.adapter.forkStream({
1090
+ parentStreamId: opts.parentStreamId,
1091
+ name: opts.name,
1092
+ agentId: opts.ownerId,
1093
+ });
1094
+ if (opts.metadata) {
1095
+ this.adapter.updateStream(streamId, { metadata: opts.metadata });
1096
+ }
1097
+ this.emit('stream:forked', {
1098
+ streamId,
1099
+ parentStreamId: opts.parentStreamId,
1100
+ ownerId: opts.ownerId,
1101
+ });
1102
+ return streamId;
1103
+ }
1104
+
1105
+ mergeStream(opts: {
1106
+ sourceStreamId: StreamId;
1107
+ targetStreamId: StreamId;
1108
+ agentId: import('./types-v3.js').Principal;
1109
+ worktree: string;
1110
+ /**
1111
+ * Free-form metadata forwarded into the `x-cascade/stream.merged` emit.
1112
+ * The canonical binding is `{ task_ref: { resource_id, node_id } }` —
1113
+ * landing strategies thread `LandingContext.taskRef` through here so the
1114
+ * hub's cascade_merges projection records which task drove the merge.
1115
+ */
1116
+ metadata?: import('git-cascade/events').EventMetadata;
1117
+ }): import('./types-v3.js').MergeResult {
1118
+ // git-cascade's MergeStreamOptions uses `sourceStream`/`targetStream` and
1119
+ // doesn't accept an opts.metadata field — the tagging is our concern, not
1120
+ // the tracker's. Carry `opts.metadata` through on the emitted event
1121
+ // instead, which is what the hub's cascade_merges projection reads.
1122
+ const result = this.adapter.mergeStream({
1123
+ sourceStream: opts.sourceStreamId,
1124
+ targetStream: opts.targetStreamId,
1125
+ agentId: opts.agentId,
1126
+ worktree: opts.worktree,
1127
+ });
1128
+ if (result.success) {
1129
+ this.emit('stream:merged', {
1130
+ sourceStreamId: opts.sourceStreamId,
1131
+ targetStreamId: opts.targetStreamId,
1132
+ mergeCommit: result.newHead,
1133
+ ...(opts.metadata ? { metadata: opts.metadata } : {}),
1134
+ });
1135
+ } else {
1136
+ this.emit('stream:conflicted', {
1137
+ streamId: opts.sourceStreamId,
1138
+ conflicts: result.conflicts,
1139
+ error: result.error,
1140
+ });
1141
+ }
1142
+ return result;
1143
+ }
1144
+
1145
+ syncWithParent(opts: {
1146
+ streamId: StreamId;
1147
+ agentId: import('./types-v3.js').Principal;
1148
+ worktree: string;
1149
+ onConflict?: import('./types-v3.js').ConflictStrategy;
1150
+ }): import('./types-v3.js').RebaseResult {
1151
+ return this.adapter.syncWithParent(
1152
+ opts.streamId,
1153
+ opts.agentId,
1154
+ opts.worktree,
1155
+ opts.onConflict
1156
+ );
1157
+ }
1158
+
1159
+ // abandonStream, pauseStream, resumeStream are inherited-by-name from the
1160
+ // adapter calls; expose thin wrappers that emit workspace-level events.
1161
+
1162
+ abandonStream(streamId: StreamId, opts?: { cascade?: boolean; reason?: string }): void {
1163
+ this.adapter.abandonStream(streamId, opts);
1164
+ this.emit('stream:abandoned', { streamId, ...opts });
1165
+ }
1166
+
1167
+ pauseStream(streamId: StreamId, reason?: string): void {
1168
+ this.adapter.pauseStream(streamId, reason);
1169
+ this.emit('stream:paused', { streamId, reason });
1170
+ }
1171
+
1172
+ resumeStream(streamId: StreamId): void {
1173
+ this.adapter.resumeStream(streamId);
1174
+ this.emit('stream:resumed', { streamId });
1175
+ }
1176
+
1177
+ listStreams(filter?: {
1178
+ ownerId?: import('./types-v3.js').Principal;
1179
+ status?: import('./types-v3.js').Stream['status'];
1180
+ }): import('./types-v3.js').Stream[] {
1181
+ return this.adapter.listStreams({
1182
+ agentId: filter?.ownerId,
1183
+ status: filter?.status,
1184
+ });
1185
+ }
1186
+
1187
+ commitChanges(opts: {
1188
+ agentId: import('./types-v3.js').Principal;
1189
+ streamId: StreamId;
1190
+ worktree: string;
1191
+ message: string;
1192
+ }): { commit: string; changeId: import('./types-v3.js').ChangeId } {
1193
+ const result = this.adapter.commitChanges(opts);
1194
+ this.emit('stream:committed', {
1195
+ streamId: opts.streamId,
1196
+ commit: result.commit,
1197
+ changeId: result.changeId,
1198
+ agentId: opts.agentId,
1199
+ });
1200
+ return result;
1201
+ }
1202
+
1203
+ markChangesMerged(changeIds: import('./types-v3.js').ChangeId[]): void {
1204
+ this.adapter.markChangesMerged(changeIds);
1205
+ for (const id of changeIds) {
1206
+ this.emit('change:merged', { changeId: id });
1207
+ }
1208
+ }
1209
+
1210
+ getChange(changeId: import('./types-v3.js').ChangeId): import('./types-v3.js').Change | null {
1211
+ return this.adapter.getChange(changeId);
1212
+ }
1213
+
1214
+ getChangeByCommit(commit: string): import('./types-v3.js').Change | null {
1215
+ return this.adapter.getChangeByCommit(commit);
1216
+ }
1217
+
1218
+ allocateWorktree(
1219
+ opts: import('./types-v3.js').AllocateWorktreeOpts
1220
+ ): import('./types-v3.js').Worktree {
1221
+ // Ref-counted sharing: if sharedWithAgent is set, reuse that agent's worktree
1222
+ if (opts.sharedWithAgent) {
1223
+ const owner = this.adapter.getWorktree(opts.sharedWithAgent);
1224
+ if (!owner) {
1225
+ throw new Error(
1226
+ `Cannot share worktree: agent ${opts.sharedWithAgent} has no allocated worktree`
1227
+ );
1228
+ }
1229
+ let entry = this.sharedWorktreeRefs.get(owner.path);
1230
+ if (!entry) {
1231
+ entry = {
1232
+ ownerId: opts.sharedWithAgent,
1233
+ sharers: new Set<AgentId>(),
1234
+ ownerDeparted: false,
1235
+ };
1236
+ this.sharedWorktreeRefs.set(owner.path, entry);
1237
+ }
1238
+ if (entry.ownerDeparted && entry.sharers.size === 0) {
1239
+ // Edge case: owner already left and all sharers left, but someone
1240
+ // is still trying to share. Reject — the teardown has been staged
1241
+ // but this would resurrect a dead reference.
1242
+ throw new Error(
1243
+ `Cannot share worktree at ${owner.path}: owner has departed and no active sharers`
1244
+ );
1245
+ }
1246
+ entry.sharers.add(opts.agentId);
1247
+ this.emit('worktree:shared', {
1248
+ path: owner.path,
1249
+ ownerAgentId: opts.sharedWithAgent,
1250
+ sharingAgentId: opts.agentId,
1251
+ });
1252
+ return owner;
1253
+ }
1254
+
1255
+ // Fresh worktree
1256
+ const baseDir = opts.baseDir ?? this.config.worktreeBaseDir;
1257
+ const sanitizedId = opts.agentId.replace(/[^a-zA-Z0-9_-]/g, '_');
1258
+ const path = `${baseDir}/${sanitizedId}`;
1259
+
1260
+ const worktreeArgs: import('git-cascade').CreateWorktreeOptions = {
1261
+ agentId: opts.agentId,
1262
+ path,
1263
+ };
1264
+ if (opts.streamId) {
1265
+ worktreeArgs.branch = opts.branch ?? this.adapter.getStreamBranchName(opts.streamId);
1266
+ } else if (opts.branch) {
1267
+ worktreeArgs.branch = opts.branch;
1268
+ }
1269
+
1270
+ const worktree = this.adapter.createWorktree(worktreeArgs);
1271
+
1272
+ if (opts.streamId) {
1273
+ this.agentToStream.set(opts.agentId, opts.streamId);
1274
+ }
1275
+
1276
+ // Track in workspaces map so legacy deallocateWorkspace can find this
1277
+ // V3-allocated worktree. Use role='v3' to bypass the legacy
1278
+ // worker/integrator coordinator-map cleanup path.
1279
+ this.workspaces.set(opts.agentId as AgentId, {
1280
+ agentId: opts.agentId as AgentId,
1281
+ path: worktree.path,
1282
+ branch: worktree.currentStream
1283
+ ? `stream/${worktree.currentStream}`
1284
+ : (opts.branch ?? 'unknown'),
1285
+ streamId: opts.streamId ?? '',
1286
+ role: 'v3',
1287
+ createdAt: worktree.createdAt,
1288
+ });
1289
+
1290
+ this.emit('worktree:allocated', {
1291
+ agentId: opts.agentId,
1292
+ path: worktree.path,
1293
+ streamId: opts.streamId,
1294
+ });
1295
+ return worktree;
1296
+ }
1297
+
1298
+ getWorktreeForAgent(
1299
+ agentId: import('./types-v3.js').Principal
1300
+ ): import('./types-v3.js').Worktree | null {
1301
+ return this.adapter.getWorktree(agentId);
1302
+ }
1303
+
1304
+ registerLandingStrategy(strategy: import('./types-v3.js').LandingStrategy): void {
1305
+ this.landingStrategies.set(strategy.name, strategy);
1306
+ }
1307
+
1308
+ async land(
1309
+ ctx: import('./types-v3.js').LandingContext,
1310
+ ): Promise<import('./types-v3.js').MergeResult> {
1311
+ const internalName = resolveLandingStrategyName(ctx.strategyName);
1312
+ if (internalName === 'none') {
1313
+ return { success: true, alreadyMerged: true } as import('./types-v3.js').MergeResult;
1314
+ }
1315
+ const strategy = this.landingStrategies.get(internalName);
1316
+ if (!strategy) {
1317
+ throw new Error(
1318
+ `No landing strategy registered for "${internalName}" (from ctx.strategyName="${ctx.strategyName ?? 'merge-to-parent'}"). Registered: ${Array.from(this.landingStrategies.keys()).join(', ') || '<none>'}.`,
1319
+ );
1320
+ }
1321
+ const resolved: import('./types-v3.js').LandingContext = {
1322
+ ...ctx,
1323
+ workspaceManager: this,
1324
+ };
1325
+ if (strategy.canLand && !strategy.canLand(resolved)) {
1326
+ throw new Error(
1327
+ `Landing strategy "${internalName}" rejected context for agent ${ctx.agentId}, stream ${ctx.streamId}`,
1328
+ );
1329
+ }
1330
+ return strategy.land(resolved);
1331
+ }
1332
+
1333
+ reconcileV3(): import('./types-v3.js').MacroReconcileResult {
1334
+ const result: import('./types-v3.js').MacroReconcileResult = {
1335
+ streamsChecked: 0,
1336
+ streamsFixed: 0,
1337
+ worktreesOrphaned: 0,
1338
+ worktreesCleaned: 0,
1339
+ poolEntriesPurged: 0,
1340
+ errors: [],
1341
+ };
1342
+
1343
+ // Delegate stream↔git sync to git-cascade
1344
+ try {
1345
+ const gcResult = this.adapter.reconcile();
1346
+ result.streamsChecked = (gcResult.updated?.length ?? 0) +
1347
+ (gcResult.branchesCreated?.length ?? 0) +
1348
+ (gcResult.failed?.length ?? 0);
1349
+ result.streamsFixed = (gcResult.updated?.length ?? 0) +
1350
+ (gcResult.branchesCreated?.length ?? 0);
1351
+ for (const f of gcResult.failed ?? []) {
1352
+ result.errors.push({
1353
+ context: `stream ${f.streamId}`,
1354
+ message: f.error,
1355
+ });
1356
+ }
1357
+ } catch (err) {
1358
+ result.errors.push({
1359
+ context: 'git-cascade reconcile',
1360
+ message: err instanceof Error ? err.message : String(err),
1361
+ });
1362
+ }
1363
+
1364
+ // Worktree pool and orphan cleanup deferred to Phase 3 (when TopologyPolicy
1365
+ // owns worktree lifecycle). For now, just count what git-cascade knows about.
1366
+ try {
1367
+ const worktrees = this.adapter.listWorktrees();
1368
+ // An "orphan" is a worktree without a corresponding record in our tracking
1369
+ // map. Count only; don't delete here — that's reserved for Phase 3.
1370
+ for (const wt of worktrees) {
1371
+ if (!this.workspaces.has(wt.agentId) && !this.agentToStream.has(wt.agentId)) {
1372
+ result.worktreesOrphaned++;
1373
+ }
1374
+ }
1375
+ } catch (err) {
1376
+ result.errors.push({
1377
+ context: 'worktree orphan scan',
1378
+ message: err instanceof Error ? err.message : String(err),
1379
+ });
1380
+ }
1381
+
1382
+ return result;
1383
+ }
1384
+
1385
+ resolveConflict(opts: {
1386
+ conflictId: string;
1387
+ resolvedBy: import('./types-v3.js').Principal;
1388
+ resolutionCommit?: string;
1389
+ /**
1390
+ * How the conflict was resolved. Defaults to 'agent' for the legacy
1391
+ * call shape; recovery strategies should pass an explicit method so
1392
+ * the OpenHive hub records the right resolution.
1393
+ */
1394
+ method?: import('git-cascade').ConflictResolution['method'] | 'auto-resolve' | 'spawn-resolver' | 'abandoned';
1395
+ /** Human-readable resolution summary (e.g., 'merged with -X ours'). */
1396
+ summary?: string;
1397
+ }): void {
1398
+ const conflict = this.adapter.getConflict(opts.conflictId);
1399
+ const method = opts.method ?? 'agent';
1400
+
1401
+ // Drive git-cascade's resolveConflict so the underlying conflict record
1402
+ // moves to status='resolved' AND the tracker emits stream.conflict_resolved.
1403
+ // Hub observers (cascade-bridge → OpenHive) update cascade_conflicts.status
1404
+ // accordingly. Falls back gracefully if cascade is older than 0.0.6.
1405
+ const trackerHasResolve = typeof (
1406
+ this.adapter as { resolveConflict?: unknown }
1407
+ ).resolveConflict === 'function';
1408
+ if (trackerHasResolve) {
1409
+ try {
1410
+ (
1411
+ this.adapter as unknown as {
1412
+ resolveConflict(args: {
1413
+ conflictId: string;
1414
+ resolution: import('git-cascade').ConflictResolution & { summary?: string };
1415
+ metadata?: Record<string, unknown>;
1416
+ }): void;
1417
+ }
1418
+ ).resolveConflict({
1419
+ conflictId: opts.conflictId,
1420
+ resolution: {
1421
+ method:
1422
+ method === 'auto-resolve' ||
1423
+ method === 'spawn-resolver' ||
1424
+ method === 'abandoned'
1425
+ ? 'agent'
1426
+ : method,
1427
+ resolvedBy: opts.resolvedBy,
1428
+ details: opts.summary,
1429
+ },
1430
+ metadata: {
1431
+ resolution_method_actual: method,
1432
+ resolution_commit: opts.resolutionCommit,
1433
+ },
1434
+ });
1435
+ } catch {
1436
+ // Best-effort; legacy resume path below remains.
1437
+ }
1438
+ }
1439
+
1440
+ if (conflict?.streamId) {
1441
+ try {
1442
+ this.adapter.resumeStream(conflict.streamId);
1443
+ } catch {
1444
+ // Stream may not be paused; safe to ignore.
1445
+ }
1446
+ }
1447
+
1448
+ this.emit('conflict:resolved', {
1449
+ conflictId: opts.conflictId,
1450
+ resolvedBy: opts.resolvedBy,
1451
+ resolutionCommit: opts.resolutionCommit,
1452
+ streamId: conflict?.streamId,
1453
+ });
1454
+ }
1455
+
966
1456
  // ─────────────────────────────────────────────────────────────────────────────
967
1457
  // Lifecycle
968
1458
  // ─────────────────────────────────────────────────────────────────────────────
@@ -1044,20 +1534,44 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
1044
1534
  export function createWorkspaceManager(
1045
1535
  config: WorkspaceManagerConfig
1046
1536
  ): DefaultWorkspaceManager {
1047
- const adapter = new DataplaneAdapter(config);
1537
+ const adapter = new GitCascadeAdapter(config);
1048
1538
  return new DefaultWorkspaceManager(adapter, config);
1049
1539
  }
1050
1540
 
1051
1541
  /**
1052
- * Create a WorkspaceManager with an existing DataplaneAdapter.
1542
+ * Create a WorkspaceManager with an existing GitCascadeAdapter.
1053
1543
  *
1054
- * @param adapter - DataplaneAdapter instance
1544
+ * @param adapter - GitCascadeAdapter instance
1055
1545
  * @param config - Configuration options
1056
1546
  * @returns WorkspaceManager instance
1057
1547
  */
1058
1548
  export function createWorkspaceManagerWithAdapter(
1059
- adapter: DataplaneAdapter,
1549
+ adapter: GitCascadeAdapter,
1060
1550
  config?: Partial<WorkspaceManagerConfig>
1061
1551
  ): DefaultWorkspaceManager {
1062
1552
  return new DefaultWorkspaceManager(adapter, config);
1063
1553
  }
1554
+
1555
+ // ═════════════════════════════════════════════════════════════════════════════
1556
+ // Landing strategy name resolution
1557
+ // ═════════════════════════════════════════════════════════════════════════════
1558
+ //
1559
+ // YAML uses snake_case (`merge_to_parent_stream`, `queue_to_branch`, …) to
1560
+ // match the team-config naming convention. Strategy classes expose
1561
+ // kebab-case internal names (`merge-to-parent`, …). `WorkspaceManager.land()`
1562
+ // accepts either and normalizes before dispatch so AgentManagerV2 can pass
1563
+ // `roleConfig.landing` directly without another translation layer.
1564
+
1565
+ const YAML_TO_INTERNAL_LANDING: Record<string, string> = {
1566
+ merge_to_parent_stream: 'merge-to-parent',
1567
+ queue_to_branch: 'queue-to-branch',
1568
+ direct_push: 'direct-push',
1569
+ optimistic_push: 'optimistic-push',
1570
+ cherry_pick_stack: 'cherry-pick-stack',
1571
+ none: 'none',
1572
+ };
1573
+
1574
+ export function resolveLandingStrategyName(input?: string): string {
1575
+ if (!input) return 'merge-to-parent';
1576
+ return YAML_TO_INTERNAL_LANDING[input] ?? input;
1577
+ }