@moretea-labs/forge 1.8.0 → 1.8.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 (528) hide show
  1. package/AGENTS.md +70 -7
  2. package/CHANGELOG.md +14 -0
  3. package/CLAUDE.md +17 -2
  4. package/CONTRIBUTING.md +3 -2
  5. package/THIRD_PARTY_NOTICES.md +1 -1
  6. package/adapters/chatgpt/browser-delivery-host.ts +7 -1
  7. package/adapters/chatgpt/browser-delivery-runtime.ts +529 -56
  8. package/adapters/chatgpt/controller-binding-store.ts +101 -22
  9. package/adapters/chatgpt/controller-round-host.ts +15 -25
  10. package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
  11. package/adapters/chatgpt/provider-delivery.ts +228 -1
  12. package/adapters/chatgpt/work-conversation-binding-store.ts +79 -53
  13. package/adapters/computer/chatgpt-extension-target.ts +463 -0
  14. package/adapters/computer/chatgpt-target-router.ts +103 -0
  15. package/adapters/computer/chrome-extension/background.js +385 -0
  16. package/adapters/computer/chrome-extension/content.js +378 -0
  17. package/adapters/computer/chrome-extension/core.js +24 -0
  18. package/adapters/computer/chrome-extension/manifest.json +30 -0
  19. package/adapters/computer/desktop-operator-discovery.ts +11 -31
  20. package/adapters/computer/interaction-target-authority.ts +142 -18
  21. package/adapters/mcp/auth.ts +0 -1
  22. package/adapters/mcp/instructions.ts +5 -4
  23. package/adapters/mcp/multi-repository.ts +3 -19
  24. package/adapters/mcp/oauth.ts +73 -0
  25. package/adapters/mcp/paths.ts +4 -1
  26. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
  27. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  28. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  29. package/adapters/mcp/runtime-gateway/context-adapter.ts +411 -73
  30. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
  31. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
  32. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  33. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
  34. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  35. package/adapters/mcp/runtime-gateway/execution-tools.ts +8 -42
  36. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
  37. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +117 -45
  38. package/adapters/mcp/runtime-gateway/process-tools.ts +190 -22
  39. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
  40. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
  41. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
  42. package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
  43. package/adapters/mcp/runtime-gateway/router.ts +18 -504
  44. package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
  45. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +14 -65
  46. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  47. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +113 -173
  48. package/adapters/mcp/runtime-gateway/runtime-tools.ts +15 -5
  49. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +1 -0
  50. package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
  51. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +358 -266
  52. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
  53. package/adapters/mcp/runtime-gateway/work-adapter.ts +261 -3122
  54. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  55. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  56. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
  57. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  58. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  59. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  60. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  61. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  62. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
  63. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  64. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
  65. package/adapters/mcp/server.ts +67 -23
  66. package/adapters/mcp/setup.ts +1 -3
  67. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +44 -170
  68. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +4 -9
  69. package/adapters/mcp/tool-mapping/repository-tools.ts +133 -142
  70. package/adapters/mcp/toolset-names.ts +5 -0
  71. package/adapters/mcp/toolset.ts +6 -5
  72. package/adapters/mcp/transports/http-observation.ts +365 -0
  73. package/adapters/mcp/transports/http.ts +154 -677
  74. package/adapters/mcp/transports/oauth-http.ts +322 -0
  75. package/adapters/mcp/transports/session-registry.ts +6 -0
  76. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
  77. package/assets/plugin-registry.v1.json +6 -4
  78. package/assets/reference-configs/agentic-development-flow.md +25 -0
  79. package/assets/reference-configs/global-working-rules.md +19 -0
  80. package/assets/skill-version.json +2 -2
  81. package/docs/architecture/CURRENT.md +111 -41
  82. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  83. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  84. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
  85. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
  86. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  87. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  88. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
  89. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  90. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
  91. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
  92. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  93. package/docs/architecture/domains/verification.md +1 -0
  94. package/docs/architecture/domains/workflow-engine.md +6 -0
  95. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  96. package/docs/forge-plugin-management.md +2 -2
  97. package/docs/operations/controller-performance-and-502.md +17 -0
  98. package/docs/operations/controller-reliability-runbook.md +2 -2
  99. package/docs/operations/features.md +1 -1
  100. package/docs/operations/features.zh-CN.md +1 -1
  101. package/docs/operations/homebrew.md +2 -2
  102. package/docs/operations/mcp-tool-exposure.md +2 -0
  103. package/docs/operations/releasing.md +2 -0
  104. package/docs/operations/standalone-disaster-recovery.md +14 -6
  105. package/docs/wiki/Implementation.md +1 -1
  106. package/package.json +25 -14
  107. package/packages/kernel/cognition/api/index.ts +6 -0
  108. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  109. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  110. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  111. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  112. package/packages/kernel/cognition/domain/learning.ts +106 -0
  113. package/packages/kernel/cognition/domain/memory.ts +254 -0
  114. package/packages/kernel/controller/api/index.ts +1 -0
  115. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  116. package/packages/kernel/controller/application/controller-round-service.ts +12 -0
  117. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
  118. package/packages/kernel/controller/domain/controller-round.ts +4 -1
  119. package/packages/kernel/controller/domain/execution-quality.ts +3 -1
  120. package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
  121. package/packages/kernel/controller/infrastructure/controller-session-store.ts +297 -71
  122. package/packages/kernel/controller/ports/controller-host.ts +2 -0
  123. package/packages/kernel/identity/api/index.ts +10 -0
  124. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  125. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  126. package/packages/kernel/identity/domain/grant.ts +72 -0
  127. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  128. package/packages/kernel/identity/domain/user-request.ts +93 -0
  129. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
  130. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  131. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  132. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  133. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  134. package/packages/kernel/scheduler/api/index.ts +0 -2
  135. package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
  136. package/packages/kernel/scheduler/application/settlement.ts +0 -15
  137. package/packages/kernel/scheduler/domain/schedule.ts +87 -0
  138. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +153 -133
  139. package/packages/kernel/work/api/index.ts +3 -2
  140. package/packages/kernel/work/application/work-service.ts +17 -42
  141. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  142. package/packages/kernel/work/domain/engineering-design.ts +10 -5
  143. package/packages/kernel/work/domain/engineering-profile.ts +8 -12
  144. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  145. package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
  146. package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
  147. package/packages/kernel/work/domain/implementation-review.ts +14 -331
  148. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  149. package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
  150. package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
  151. package/packages/kernel/work/domain/state-machine.ts +25 -63
  152. package/packages/kernel/work/domain/types.ts +57 -51
  153. package/packages/kernel/work/infrastructure/work-contract-store.ts +819 -728
  154. package/packages/kernel/work/ports/work-contract-store.ts +5 -2
  155. package/packages/plugin-runtime/browser/session-authority.ts +2 -1
  156. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  157. package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
  158. package/packages/plugin-runtime/computer/index.ts +2 -0
  159. package/packages/plugin-runtime/computer/target-authority.ts +26 -1
  160. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
  161. package/packages/protocols/computer/contract.ts +27 -1
  162. package/scripts/activate-source-baseline.ts +7 -9
  163. package/scripts/canary-controller-semantic-wait.ts +12 -24
  164. package/scripts/check-mcp-compatibility.ts +48 -136
  165. package/scripts/check-runtime-architecture.mjs +622 -217
  166. package/scripts/forge-local-recovery-helper.mjs +93 -15
  167. package/scripts/lib/project-init-lib.sh +3 -0
  168. package/scripts/open-source-audit-allowlist.txt +1 -2
  169. package/scripts/run-bounded-command.mjs +5 -0
  170. package/scripts/run-governed-gate.ts +0 -1
  171. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  172. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  173. package/scripts/smoke-mcp-http-runtime.ts +5 -0
  174. package/scripts/smoke-runtime-recovery.ts +12 -43
  175. package/scripts/smoke-schedule-engine.ts +23 -15
  176. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  177. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  178. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
  179. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  180. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  181. package/scripts/smoke-workflow-supervisor.ts +103 -0
  182. package/scripts/verify-forge-runtime.sh +2 -1
  183. package/src/cli/agent-jobs/executor-health.ts +11 -34
  184. package/src/cli/agent-jobs/integration.ts +18 -49
  185. package/src/cli/agent-jobs/job-manager.ts +7 -3
  186. package/src/cli/commands/brain-assistant.ts +27 -13
  187. package/src/cli/commands/brain.ts +13 -15
  188. package/src/cli/commands/chatgpt.ts +1 -163
  189. package/src/cli/commands/recovery.ts +135 -121
  190. package/src/cli/commands/runtime.ts +3 -3
  191. package/src/cli/commands/setup-profile.ts +8 -0
  192. package/src/cli/controller/check-runner.ts +13 -2
  193. package/src/cli/controller/completion-backlog.ts +3 -3
  194. package/src/cli/controller/completion-orchestrator.ts +4 -43
  195. package/src/cli/controller/completion-target.ts +3 -72
  196. package/src/cli/controller/context/source-materializer.ts +81 -100
  197. package/src/cli/controller/context-pack.ts +10 -8
  198. package/src/cli/controller/execution-completion.ts +4 -7
  199. package/src/cli/controller/execution-policy.ts +36 -153
  200. package/src/cli/controller/governance.ts +2 -3
  201. package/src/cli/controller/issue-store.ts +4 -99
  202. package/src/cli/controller/launch-agents.ts +59 -18
  203. package/src/cli/controller/operational-plan.ts +24 -51
  204. package/src/cli/controller/runtime-config.ts +5 -1
  205. package/src/cli/controller/stuck-state-migration.ts +2 -3
  206. package/src/cli/controller/systemd-user.ts +39 -1
  207. package/src/cli/editing/edit-session.ts +1 -7
  208. package/src/cli/local-bridge/console-automations.ts +18 -13
  209. package/src/cli/local-bridge/console-view-models.ts +3 -12
  210. package/src/cli/local-bridge/facade-api.ts +64 -191
  211. package/src/cli/local-bridge/server.ts +24 -147
  212. package/src/cli/local-bridge/types.ts +0 -1
  213. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  214. package/src/cli/repositories/command-executor.ts +29 -23
  215. package/src/cli/repositories/command-scope.ts +35 -16
  216. package/src/cli/repositories/controller-home.ts +48 -0
  217. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  218. package/src/cli/repositories/git-command-executor.ts +2 -2
  219. package/src/cli/repositories/registry.ts +77 -11
  220. package/src/cli/repositories/structured-git.ts +62 -2
  221. package/src/cli/repository/session-cache.ts +92 -6
  222. package/src/runtime/context/assistant-context.ts +20 -7
  223. package/src/runtime/context/assistant-work-context.ts +89 -27
  224. package/src/runtime/context/automatic-learning.ts +565 -0
  225. package/src/runtime/context/cognitive-attention.ts +173 -0
  226. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  227. package/src/runtime/context/context-closure.ts +46 -9
  228. package/src/runtime/context/context-pack-process.ts +103 -0
  229. package/src/runtime/context/context-plane.ts +1 -9
  230. package/src/runtime/context/direct-controller-learning.ts +298 -0
  231. package/src/runtime/context/semantic-navigation-contract.ts +9 -0
  232. package/src/runtime/context/semantic-navigation.ts +6 -34
  233. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  234. package/src/runtime/context/typescript-navigation.ts +89 -3
  235. package/src/runtime/control-plane/cleanup-artifact-retention.ts +212 -9
  236. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
  237. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
  238. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
  239. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  240. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  241. package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
  242. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  243. package/src/runtime/control-plane/execution/validation.ts +6 -8
  244. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
  245. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  246. package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
  247. package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
  248. package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
  249. package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
  250. package/src/runtime/control-plane/execution/work-handle-authority.ts +188 -109
  251. package/src/runtime/control-plane/execution/work-handle-store.ts +19 -43
  252. package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
  253. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
  254. package/src/runtime/control-plane/execution/work-operation-service.ts +43 -27
  255. package/src/runtime/control-plane/execution/work-preparation-service.ts +126 -266
  256. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  257. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  258. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +173 -186
  259. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -84
  260. package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
  261. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
  262. package/src/runtime/control-plane/facade/capability-registry.ts +362 -37
  263. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  264. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  265. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  266. package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
  267. package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
  268. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
  269. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
  270. package/src/runtime/control-plane/facade/index.ts +0 -1
  271. package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
  272. package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
  273. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  274. package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
  275. package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
  276. package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
  277. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
  278. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  279. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
  280. package/src/runtime/control-plane/facade/types.ts +49 -108
  281. package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
  282. package/src/runtime/control-plane/generated-cache-retention.ts +97 -1
  283. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  284. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
  285. package/src/runtime/control-plane/global-scheduler/maintenance.ts +239 -112
  286. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +2 -1
  287. package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
  288. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  289. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  290. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  291. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
  292. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +28 -300
  293. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  294. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
  295. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
  296. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  297. package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
  298. package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
  299. package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
  300. package/src/runtime/control-plane/release-retention.ts +135 -47
  301. package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
  302. package/src/runtime/control-plane/runtime-cleanup.ts +290 -21
  303. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  304. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
  305. package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
  306. package/src/runtime/diagnostics/incident-repair.ts +313 -146
  307. package/src/runtime/diagnostics/mcp-timing.ts +114 -0
  308. package/src/runtime/evidence/artifact-store.ts +158 -40
  309. package/src/runtime/evidence/evidence-store.ts +112 -8
  310. package/src/runtime/evidence/output-handle-index.ts +225 -0
  311. package/src/runtime/evidence/result-store.ts +158 -44
  312. package/src/runtime/execution/jobs/store.ts +3 -4
  313. package/src/runtime/execution/managed-workspace.ts +57 -0
  314. package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
  315. package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
  316. package/src/runtime/execution/process-runtime/check-result.ts +105 -1
  317. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
  318. package/src/runtime/execution/process-runtime/command-facade.ts +79 -27
  319. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  320. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  321. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  322. package/src/runtime/execution/process-runtime/index.ts +3 -0
  323. package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
  324. package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
  325. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  326. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  327. package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
  328. package/src/runtime/execution/process-runtime/runtime.ts +35 -2
  329. package/src/runtime/execution/process-runtime/store.ts +30 -2
  330. package/src/runtime/execution/process-runtime/types.ts +6 -1
  331. package/src/runtime/execution/workers/executor.ts +2 -6
  332. package/src/runtime/platform/service-inventory.ts +130 -0
  333. package/src/runtime/platform/service-manager.ts +8 -0
  334. package/src/runtime/plugins/action-application.ts +85 -28
  335. package/src/runtime/plugins/app-store-connect-adapter.ts +38 -14
  336. package/src/runtime/plugins/browser-adapter.ts +757 -34
  337. package/src/runtime/plugins/browser-automation-service.ts +23 -3
  338. package/src/runtime/plugins/browser-macos-bridge.ts +321 -29
  339. package/src/runtime/plugins/browser-manifest-surface.ts +45 -11
  340. package/src/runtime/plugins/browser-native-session-service.ts +63 -13
  341. package/src/runtime/plugins/browser-registration.ts +16 -0
  342. package/src/runtime/plugins/browser-runtime.ts +14 -1
  343. package/src/runtime/plugins/browser-session-authority.ts +2 -2
  344. package/src/runtime/plugins/browser-session-store.ts +46 -76
  345. package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
  346. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
  347. package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
  348. package/src/runtime/plugins/computer-registration.ts +222 -11
  349. package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
  350. package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
  351. package/src/runtime/plugins/external-adapter.ts +42 -6
  352. package/src/runtime/plugins/external-registration.ts +21 -1
  353. package/src/runtime/plugins/gmail-adapter.ts +23 -2
  354. package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
  355. package/src/runtime/plugins/google-shared.ts +14 -7
  356. package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
  357. package/src/runtime/plugins/ios-adapter.ts +1 -1
  358. package/src/runtime/plugins/lightweight-action.ts +208 -2
  359. package/src/runtime/plugins/local-system-adapter.ts +12 -2
  360. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  361. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  362. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  363. package/src/runtime/plugins/store.ts +602 -428
  364. package/src/runtime/plugins/types.ts +20 -0
  365. package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
  366. package/src/runtime/projections/invalidation.ts +7 -3
  367. package/src/runtime/projections/materialized-view.ts +80 -36
  368. package/src/runtime/recovery/actions.ts +3 -3
  369. package/src/runtime/recovery/capability-action-application.ts +46 -45
  370. package/src/runtime/recovery/lifecycle-inventory.ts +29 -29
  371. package/src/runtime/recovery/maintenance-executor.ts +374 -123
  372. package/src/runtime/recovery/store.ts +29 -11
  373. package/src/runtime/release/release-coordinator.ts +296 -0
  374. package/src/runtime/release/release-session.ts +716 -0
  375. package/src/runtime/release/release-state-migration.ts +25 -0
  376. package/src/runtime/resources/leases/store.ts +56 -25
  377. package/src/runtime/root/computer-composition.ts +191 -6
  378. package/src/runtime/root/controller-progression-composition.ts +192 -0
  379. package/src/runtime/root/controller-round-composition.ts +20 -1
  380. package/src/runtime/root/deployment-topology.ts +106 -0
  381. package/src/runtime/root/entry.ts +4 -3
  382. package/src/runtime/root/known-good-recovery.ts +184 -0
  383. package/src/runtime/root/local-bridge.ts +0 -1
  384. package/src/runtime/root/mcp-transport.ts +111 -18
  385. package/src/runtime/root/ownership.ts +261 -9
  386. package/src/runtime/root/package-connector-service.ts +105 -19
  387. package/src/runtime/root/package-runtime-release.ts +117 -18
  388. package/src/runtime/root/package-runtime-service.ts +17 -11
  389. package/src/runtime/root/release-execution-canary.ts +44 -12
  390. package/src/runtime/root/release-loader.ts +90 -8
  391. package/src/runtime/root/release-manifest.ts +80 -6
  392. package/src/runtime/root/release-materialize.ts +372 -28
  393. package/src/runtime/root/release-store.ts +395 -36
  394. package/src/runtime/root/runtime-lane.ts +191 -0
  395. package/src/runtime/root/runtime.ts +159 -27
  396. package/src/runtime/root/scheduled-controller-composition.ts +28 -3
  397. package/src/runtime/root/service-runner.ts +2 -2
  398. package/src/runtime/root/service.ts +36 -33
  399. package/src/runtime/root/status.ts +48 -0
  400. package/src/runtime/root/types.ts +32 -3
  401. package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
  402. package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
  403. package/src/runtime/root/write-fence.ts +50 -2
  404. package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
  405. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  406. package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
  407. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  408. package/src/runtime/shared/process-environment.ts +20 -9
  409. package/src/runtime/shared/response-budget.ts +1 -3
  410. package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
  411. package/src/runtime/standalone-recovery/core.ts +3702 -342
  412. package/src/runtime/standalone-recovery/entry.ts +469 -39
  413. package/src/runtime/standalone-recovery/installer.ts +262 -148
  414. package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
  415. package/src/runtime/standalone-recovery/operation-lock.ts +1 -1
  416. package/src/runtime/standalone-recovery/performance.ts +54 -16
  417. package/src/runtime/standalone-recovery/release.ts +53 -5
  418. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  419. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  420. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  421. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +10 -3
  422. package/src/runtime/workflow/schedules/engine.ts +138 -172
  423. package/src/runtime/workflow/schedules/work-continuation.ts +44 -5
  424. package/src/runtime/workflows/runtime.ts +15 -19
  425. package/src/testing/test-governance.ts +5 -1
  426. package/supervisor/chatgpt-conversation.ts +23 -0
  427. package/supervisor/client.ts +201 -0
  428. package/supervisor/control-plane.ts +868 -0
  429. package/supervisor/entry.ts +15 -0
  430. package/supervisor/forge-validators.ts +80 -0
  431. package/supervisor/index.ts +5 -0
  432. package/supervisor/native-browser-adapter.ts +786 -0
  433. package/supervisor/native-messaging/host.ts +39 -0
  434. package/supervisor/native-messaging/manifest.ts +26 -0
  435. package/supervisor/native-messaging/protocol.ts +27 -0
  436. package/supervisor/paths.ts +18 -0
  437. package/supervisor/protocol.ts +202 -0
  438. package/supervisor/server.ts +404 -0
  439. package/supervisor/store.ts +1451 -0
  440. package/supervisor/tsconfig.json +8 -0
  441. package/supervisor/types.ts +255 -0
  442. package/adapters/chatgpt/controller-host.ts +0 -119
  443. package/adapters/mcp/controller-round-compatibility.ts +0 -184
  444. package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
  445. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
  446. package/packages/kernel/progression/api/index.ts +0 -2
  447. package/packages/kernel/progression/application/projector.ts +0 -211
  448. package/packages/kernel/progression/domain/types.ts +0 -116
  449. package/packages/kernel/progression/index.ts +0 -1
  450. package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
  451. package/packages/kernel/scheduler/domain/continuation.ts +0 -38
  452. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
  453. package/packages/kernel/work/domain/admission-policy.ts +0 -34
  454. package/scripts/benchmark-background-check-overlap.ts +0 -228
  455. package/scripts/benchmark-computer-hotpath.ts +0 -255
  456. package/scripts/benchmark-controller-hotpath.ts +0 -212
  457. package/scripts/benchmark-controller-round2.ts +0 -191
  458. package/scripts/benchmark-ios-agent-device.ts +0 -511
  459. package/scripts/benchmark-mcp-host.sh +0 -117
  460. package/scripts/benchmark-mcp-transport.ts +0 -169
  461. package/scripts/benchmark-operational-memory-activation.ts +0 -235
  462. package/scripts/benchmark-operational-shadow.ts +0 -28
  463. package/scripts/benchmark-quality-harness.ts +0 -146
  464. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  465. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  466. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  467. package/scripts/benchmark-thin-harness.ts +0 -251
  468. package/scripts/benchmark-typescript-navigation.ts +0 -186
  469. package/scripts/benchmark-work-efficiency.ts +0 -334
  470. package/scripts/build-controller-ui.ts +0 -28
  471. package/scripts/check-npm-release.sh +0 -32
  472. package/scripts/check-public-release-surface.sh +0 -19
  473. package/scripts/check-release-published.sh +0 -57
  474. package/scripts/check-release-readiness.sh +0 -37
  475. package/scripts/check-release-version.mjs +0 -74
  476. package/scripts/check-tarball-install-smoke.sh +0 -150
  477. package/scripts/package-source-archive.sh +0 -147
  478. package/scripts/public-release-files.txt +0 -39
  479. package/scripts/public-release.ts +0 -174
  480. package/scripts/publish-release-tarball.sh +0 -27
  481. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  482. package/scripts/stage-runtime-release.ts +0 -44
  483. package/src/cli/controller/progress.ts +0 -656
  484. package/src/cli/controller/work-mode.ts +0 -181
  485. package/src/cli/local-bridge/console-assets.ts +0 -17
  486. package/src/cli/local-bridge/dashboard.ts +0 -17
  487. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  488. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  489. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  490. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  491. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  492. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  493. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  494. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  495. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  496. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  497. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  498. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  499. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  500. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  501. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  502. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  503. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  504. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  505. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  506. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  507. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  508. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  509. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  510. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  511. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  512. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  513. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  514. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  515. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
  516. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
  517. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
  518. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
  519. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  520. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
  521. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
  522. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
  523. package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
  524. package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
  525. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  526. package/src/runtime/control-plane/routing/index.ts +0 -1
  527. package/src/runtime/control-plane/routing/route-policy.ts +0 -401
  528. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
@@ -33,11 +33,34 @@ function nearestExistingPath(candidate: string): string | undefined {
33
33
  return current;
34
34
  }
35
35
 
36
+ /**
37
+ * Minimal command execution target. Repository identity stays optional: an
38
+ * arbitrary workspace target carries only its scope key and root, so command
39
+ * policy/audit/cwd checks need no fabricated repository record.
40
+ */
41
+ export interface RepositoryCommandScopeTarget {
42
+ canonicalRoot: string;
43
+ /** Registered repository id (`RepositoryRecord.repoId`); absent for an arbitrary workspace target. */
44
+ repoId?: string;
45
+ /** Workspace scope key; present only when the target is not a repository. */
46
+ workspaceScopeKey?: string;
47
+ activeCheckoutId?: string;
48
+ defaultBranch?: string;
49
+ enabled?: boolean;
50
+ }
51
+
52
+ /** Canonical scope key for command policy/audit/approval storage. */
53
+ export function commandExecutionScopeKey(target: RepositoryCommandScopeTarget): string {
54
+ const key = target.repoId?.trim() || target.workspaceScopeKey?.trim();
55
+ if (!key) throw new Error('COMMAND_TARGET_SCOPE_REQUIRED: command target requires a repository id or workspace scope key');
56
+ return key;
57
+ }
58
+
36
59
  export function resolveRepositoryCommandCwd(
37
- repository: RepositoryRecord,
60
+ repository: RepositoryCommandScopeTarget,
38
61
  requestedCwd: string | undefined,
39
62
  ): { root: string; cwd: string; relativeCwd: string } {
40
- if (!repository.enabled) throw new Error(`REPOSITORY_DISABLED: ${repository.repoId}`);
63
+ if (repository.enabled === false) throw new Error(`REPOSITORY_DISABLED: ${commandExecutionScopeKey(repository)}`);
41
64
  const root = realpathSync(repository.canonicalRoot);
42
65
  const raw = requestedCwd?.trim() || '.';
43
66
  if (raw.includes('\0')) throw new Error('COMMAND_SCOPE_DENIED: cwd contains a null byte');
@@ -258,7 +281,7 @@ interface RepositoryCommandPathPolicyOptions {
258
281
  }
259
282
 
260
283
  interface SourceControllerRoundCompatibilityScope {
261
- operation: 'round-continue' | 'round-close';
284
+ operation: 'round-close';
262
285
  controllerHome: string;
263
286
  workId: string;
264
287
  controllerAuthorityId: string;
@@ -274,11 +297,9 @@ function sourceControllerRoundCompatibilityScope(
274
297
  const args = command.args ?? [];
275
298
  if (executable !== 'bun' || args[0] !== 'src/cli/index.ts' || args[1] !== 'chatgpt') return undefined;
276
299
  const operation = args[2];
277
- if (operation !== 'round-continue' && operation !== 'round-close') return undefined;
300
+ if (operation !== 'round-close') return undefined;
278
301
 
279
- const allowedFlags = operation === 'round-continue'
280
- ? new Set(['--controller-home', '--repo-id', '--work-id', '--controller-authority-id', '--relay-scope-id', '--reason', '--timeout-ms'])
281
- : new Set(['--controller-home', '--repo-id', '--work-id', '--controller-authority-id', '--relay-scope-id', '--disposition', '--handoff-id', '--reason']);
302
+ const allowedFlags = new Set(['--controller-home', '--repo-id', '--work-id', '--controller-authority-id', '--relay-scope-id', '--disposition', '--handoff-id', '--reason']);
282
303
  const values = new Map<string, string>();
283
304
  for (let index = 3; index < args.length; index += 2) {
284
305
  const flag = args[index];
@@ -300,14 +321,12 @@ function sourceControllerRoundCompatibilityScope(
300
321
  if (requestedControllerHome !== configuredControllerHome) {
301
322
  throw new Error(`SOURCE_CONTROLLER_ROUND_COMMAND_CONTROLLER_HOME_MISMATCH:${requestedControllerHome}`);
302
323
  }
303
- if (operation === 'round-close') {
304
- const disposition = values.get('--disposition');
305
- if (!disposition || !['wait', 'wait_for_user', 'goal_complete'].includes(disposition)) {
306
- throw new Error(`SOURCE_CONTROLLER_ROUND_COMMAND_DISPOSITION_INVALID:${disposition ?? 'missing'}`);
307
- }
308
- if (disposition === 'wait_for_user' && !values.get('--handoff-id')?.trim()) {
309
- throw new Error('SOURCE_CONTROLLER_ROUND_COMMAND_HANDOFF_REQUIRED');
310
- }
324
+ const disposition = values.get('--disposition');
325
+ if (!disposition || !['wait', 'wait_for_user', 'goal_complete'].includes(disposition)) {
326
+ throw new Error(`SOURCE_CONTROLLER_ROUND_COMMAND_DISPOSITION_INVALID:${disposition ?? 'missing'}`);
327
+ }
328
+ if (disposition === 'wait_for_user' && !values.get('--handoff-id')?.trim()) {
329
+ throw new Error('SOURCE_CONTROLLER_ROUND_COMMAND_HANDOFF_REQUIRED');
311
330
  }
312
331
  return {
313
332
  operation,
@@ -328,7 +347,7 @@ function grantCoversPath(grant: ExternalFilesystemGrant, canonicalPath: string,
328
347
  }
329
348
 
330
349
  function operationForToken(words: string[], index: number, external: string, cwd: string, root: string): RepositoryCommandExternalPathOperation | 'external_write' | 'unsupported' {
331
- const program = words[0]?.toLowerCase();
350
+ const program = words[0]?.split(/[\\/]/).at(-1)?.toLowerCase();
332
351
  if (!program) return 'unsupported';
333
352
  if (program === 'cp' || program === 'install') {
334
353
  const operands = words.slice(1).filter((word) => word && !word.startsWith('-'));
@@ -253,7 +253,10 @@ export function ensureRepoPreferredControllerHome(repoRoot?: string, explicit?:
253
253
  return ensureControllerHome(resolveRepoPreferredControllerHome(repoRoot, explicit));
254
254
  }
255
255
 
256
+ /** Legacy adapter-only sentinel. Never use this as a canonical storage or resource scope. */
256
257
  export const CONTROLLER_SCOPE_REPO_ID = '__controller__';
258
+ /** Canonical mechanical scope for state owned by the serving ForgeInstance itself. */
259
+ export const FORGE_INSTANCE_SCOPE_KEY = 'forge-instance';
257
260
 
258
261
  /** Human-editable user Workflow Asset content. Machine workflow state stays in control-plane persistence. */
259
262
  export function controllerWorkflowContentRoot(controllerHome: string): string {
@@ -283,6 +286,51 @@ export function repositoryControllerRoot(controllerHome: string, repoId: string)
283
286
  : join(resolveControllerHome(durableHome), 'repositories', repoId);
284
287
  }
285
288
 
289
+ /** Reserved prefix for arbitrary workspace scopes; registered repository ids are `repo_*`. */
290
+ export const WORKSPACE_SCOPE_PREFIX = 'workspace_';
291
+
292
+ /**
293
+ * Reserved scope key for portable semantic facts (Requirement/Plan/Work) that
294
+ * belong to the ForgeInstance rather than to one repository. Repository and
295
+ * project placement stays optional provenance on those records.
296
+ */
297
+ export const SEMANTIC_SCOPE_KEY = 'semantic';
298
+
299
+ export function isWorkspaceScopeKey(scopeKey: string): boolean {
300
+ return scopeKey.trim().startsWith(WORKSPACE_SCOPE_PREFIX);
301
+ }
302
+
303
+ export function isSemanticScopeKey(scopeKey: string): boolean {
304
+ return scopeKey.trim() === SEMANTIC_SCOPE_KEY;
305
+ }
306
+
307
+ /**
308
+ * Controller-Home root for an arbitrary workspace scope. A workspace has no
309
+ * repository semantics, so its operation facts own a sibling partition instead
310
+ * of a repository partition.
311
+ */
312
+ export function workspaceScopeRoot(controllerHome: string, scopeKey: string): string {
313
+ const key = scopeKey.trim();
314
+ if (!isWorkspaceScopeKey(key)) throw new Error(`WORKSPACE_SCOPE_KEY_REQUIRED: ${scopeKey}`);
315
+ return join(resolveControllerHome(durableControllerHome(controllerHome)), 'workspaces', key);
316
+ }
317
+
318
+ /**
319
+ * Controller-Home root for one operation scope key. Repository scopes (including
320
+ * the controller-system sentinel) keep their established root; workspace scopes
321
+ * own the workspace partition.
322
+ */
323
+ export function scopedOperationRoot(controllerHome: string, scopeKey: string): string {
324
+ const key = scopeKey.trim();
325
+ return key === FORGE_INSTANCE_SCOPE_KEY
326
+ ? controllerSystemRoot(controllerHome)
327
+ : isWorkspaceScopeKey(key)
328
+ ? workspaceScopeRoot(controllerHome, key)
329
+ : isSemanticScopeKey(key)
330
+ ? join(resolveControllerHome(durableControllerHome(controllerHome)), SEMANTIC_SCOPE_KEY)
331
+ : repositoryControllerRoot(controllerHome, key);
332
+ }
333
+
286
334
  export function ensureRepositoryControllerLayout(controllerHome: string, repoId: string): string {
287
335
  const root = repositoryControllerRoot(controllerHome, repoId);
288
336
  for (const child of [
@@ -1,7 +1,7 @@
1
1
  import { createHash } from 'crypto';
2
2
  import { existsSync, realpathSync, statSync } from 'fs';
3
- import { isAbsolute, join } from 'path';
4
- import type { RepositoryRecord } from './types';
3
+ import { isAbsolute } from 'path';
4
+ import type { RepositoryCommandScopeTarget } from './command-scope';
5
5
 
6
6
  export interface EphemeralWorkspaceCoordinates {
7
7
  workspaceId: string;
@@ -10,7 +10,11 @@ export interface EphemeralWorkspaceCoordinates {
10
10
  }
11
11
 
12
12
  export interface EphemeralWorkspaceTarget extends EphemeralWorkspaceCoordinates {
13
- repository: RepositoryRecord;
13
+ /**
14
+ * Typed command target for an arbitrary workspace: it carries the workspace
15
+ * scope key and root, and no repository identity at all.
16
+ */
17
+ commandTarget: RepositoryCommandScopeTarget;
14
18
  }
15
19
 
16
20
  export function ephemeralWorkspaceCoordinates(rootInput: string): EphemeralWorkspaceCoordinates {
@@ -29,34 +33,20 @@ export function ephemeralWorkspaceCoordinates(rootInput: string): EphemeralWorks
29
33
  };
30
34
  }
31
35
 
32
- export function resolveEphemeralWorkspaceTarget(rootInput: string, controllerHome: string): EphemeralWorkspaceTarget {
36
+ /**
37
+ * `controllerHome` is retained for call-site compatibility; an arbitrary
38
+ * workspace owns no controller-scoped repository record.
39
+ */
40
+ export function resolveEphemeralWorkspaceTarget(rootInput: string, _controllerHome?: string): EphemeralWorkspaceTarget {
33
41
  const coordinates = ephemeralWorkspaceCoordinates(rootInput);
34
- const now = new Date().toISOString();
35
- const repository: RepositoryRecord = {
36
- schemaVersion: 1,
37
- repoId: coordinates.workspaceId,
38
- displayName: `Ephemeral Workspace (${coordinates.canonicalRoot.split(/[\\/]/).at(-1) || 'root'})`,
39
- localRoot: coordinates.canonicalRoot,
40
- canonicalRoot: coordinates.canonicalRoot,
41
- activeCheckoutId: coordinates.checkoutId,
42
- checkouts: [{
43
- checkoutId: coordinates.checkoutId,
44
- localRoot: coordinates.canonicalRoot,
42
+ return {
43
+ ...coordinates,
44
+ commandTarget: {
45
45
  canonicalRoot: coordinates.canonicalRoot,
46
- worktree: false,
47
- branch: null,
48
- createdAt: now,
49
- updatedAt: now,
50
- lastSeenAt: now,
51
- lifecycle: 'active',
52
- }],
53
- repositoryType: 'unknown',
54
- enabled: true,
55
- createdAt: now,
56
- updatedAt: now,
57
- lastSeenAt: now,
58
- configurationPath: join(controllerHome, 'ephemeral-workspaces', `${coordinates.workspaceId}.json`),
59
- stateStorageStrategy: 'controller-home',
46
+ workspaceScopeKey: coordinates.workspaceId,
47
+ // The workspace's own checkout coordinate, never a repository checkout id.
48
+ activeCheckoutId: coordinates.checkoutId,
49
+ enabled: true,
50
+ },
60
51
  };
61
- return { ...coordinates, repository };
62
52
  }
@@ -3,7 +3,7 @@ import { spawnSync } from 'child_process';
3
3
  import { appendFileSync, mkdirSync, realpathSync } from 'fs';
4
4
  import { dirname, join, relative } from 'path';
5
5
  import { capProcessOutput, redactProcessOutput } from '../../effects/process-runner';
6
- import { repositoryControllerRoot } from './controller-home';
6
+ import { scopedOperationRoot } from './controller-home';
7
7
  import { classifyRepositoryCommand, type RepositoryCommandAuthorization, type RepositoryCommandClassification } from './command-classifier';
8
8
  import { resolveRepositoryCommandCwd } from './command-scope';
9
9
  import type { RepositoryRecord } from './types';
@@ -209,7 +209,7 @@ function snapshotChanged(before: RepositoryGitSnapshot, after: RepositoryGitSnap
209
209
  }
210
210
 
211
211
  function auditExecution(controllerHome: string, repository: RepositoryRecord, execution: RepositoryGitExecution): void {
212
- const path = join(repositoryControllerRoot(controllerHome, repository.repoId), 'audit', 'git-commands.jsonl');
212
+ const path = join(scopedOperationRoot(controllerHome, repository.repoId), 'audit', 'git-commands.jsonl');
213
213
  mkdirSync(dirname(path), { recursive: true });
214
214
  appendFileSync(path, `${JSON.stringify({
215
215
  timestamp: new Date().toISOString(),
@@ -6,6 +6,7 @@ import {
6
6
  realpathSync,
7
7
  renameSync,
8
8
  rmSync,
9
+ statSync,
9
10
  writeFileSync,
10
11
  } from 'fs';
11
12
  import { dirname, join, relative, resolve } from 'path';
@@ -291,6 +292,51 @@ function readRegistryFile(path: string, strict: boolean): RepositoryRegistry | u
291
292
  }
292
293
  }
293
294
 
295
+ interface RepositoryRegistryReadSnapshot {
296
+ fileIdentity: string;
297
+ registry: RepositoryRegistry;
298
+ }
299
+
300
+ const repositoryRegistryReadSnapshots = new Map<string, RepositoryRegistryReadSnapshot>();
301
+
302
+ function registryFileIdentity(path: string): string | undefined {
303
+ try {
304
+ const stat = statSync(path, { bigint: true });
305
+ return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
306
+ } catch {
307
+ return undefined;
308
+ }
309
+ }
310
+
311
+ function cloneRepositoryRecord(record: RepositoryRecord): RepositoryRecord {
312
+ return {
313
+ ...record,
314
+ checkouts: record.checkouts.map((checkout) => ({ ...checkout })),
315
+ github: record.github
316
+ ? { ...record.github, labels: record.github.labels ? [...record.github.labels] : undefined }
317
+ : undefined,
318
+ };
319
+ }
320
+
321
+ function loadRepositoryRegistryReadSnapshot(controllerHome?: string): RepositoryRegistry {
322
+ const path = registryPath(controllerHome);
323
+ const beforeIdentity = registryFileIdentity(path);
324
+ const cached = beforeIdentity ? repositoryRegistryReadSnapshots.get(path) : undefined;
325
+ if (cached && cached.fileIdentity === beforeIdentity) return cached.registry;
326
+
327
+ let registry = readRegistryFile(path, true) ?? defaultRegistry();
328
+ let afterIdentity = registryFileIdentity(path);
329
+ if (beforeIdentity && afterIdentity && beforeIdentity !== afterIdentity) {
330
+ // A cross-process atomic replacement raced this read. Re-read once from the
331
+ // new physical authority instead of caching a mixed-generation snapshot.
332
+ registry = readRegistryFile(path, true) ?? defaultRegistry();
333
+ afterIdentity = registryFileIdentity(path);
334
+ }
335
+ if (afterIdentity) repositoryRegistryReadSnapshots.set(path, { fileIdentity: afterIdentity, registry });
336
+ else repositoryRegistryReadSnapshots.delete(path);
337
+ return registry;
338
+ }
339
+
294
340
  function timestampValue(value: string | undefined): number {
295
341
  const parsed = value ? Date.parse(value) : Number.NaN;
296
342
  return Number.isFinite(parsed) ? parsed : 0;
@@ -555,17 +601,18 @@ export function repositorySummary(record: RepositoryRecord): RepositorySummary {
555
601
  }
556
602
 
557
603
  export function listRepositories(controllerHome?: string, options: { includeRemoved?: boolean } = {}): RepositoryRecord[] {
558
- return loadRepositoryRegistry(controllerHome).repositories
604
+ return loadRepositoryRegistryReadSnapshot(controllerHome).repositories
559
605
  .filter((record) => options.includeRemoved === true || !record.removedAt)
560
- .sort((a, b) => a.displayName.localeCompare(b.displayName) || a.repoId.localeCompare(b.repoId));
606
+ .sort((a, b) => a.displayName.localeCompare(b.displayName) || a.repoId.localeCompare(b.repoId))
607
+ .map(cloneRepositoryRecord);
561
608
  }
562
609
 
563
610
  export function getRepository(repoId: string, controllerHome?: string, options: { includeRemoved?: boolean } = {}): RepositoryRecord {
564
- const record = loadRepositoryRegistry(controllerHome).repositories.find((candidate) => candidate.repoId === repoId);
611
+ const record = loadRepositoryRegistryReadSnapshot(controllerHome).repositories.find((candidate) => candidate.repoId === repoId);
565
612
  if (!record || (record.removedAt && options.includeRemoved !== true)) {
566
613
  throw new Error(`repository not found: ${repoId}`);
567
614
  }
568
- return record;
615
+ return cloneRepositoryRecord(record);
569
616
  }
570
617
 
571
618
  export type RepositoryCheckoutSelectionErrorCode = 'CHECKOUT_NOT_FOUND' | 'CHECKOUT_NOT_ACTIVE';
@@ -635,13 +682,29 @@ export function registerRepository(input: RegisterRepositoryInput): RepositoryRe
635
682
  ? repositoryByGitCommonDirectory(registry.repositories, canonicalRoot)
636
683
  : undefined;
637
684
  const existingIdentity = existingByRoot ?? existingByCommonDirectory;
638
- const derivedRepoId = input.repoIdOverride?.trim()
685
+ const explicitRepoId = input.repoIdOverride?.trim();
686
+ const derivedRepoId = explicitRepoId
639
687
  || existingIdentity?.repoId
640
688
  || localIdentity.repoId
641
689
  || (canonicalRemote ? stableRemoteRepoId(canonicalRemote) : newLocalRepoId());
642
- const repoId = existingIdentity?.repoId ?? derivedRepoId;
690
+ const existingByDerivedId = registry.repositories.find((record) => record.repoId === derivedRepoId);
691
+ // Repository identity is rooted in one Git checkout family, not merely one
692
+ // remote URL. Two independent clones of the same remote have different Git
693
+ // common directories and must remain separate Repository records. The one
694
+ // intentional exception is an explicit registration replacement; legacy
695
+ // repo-local identity may also restore a record whose old canonical root no
696
+ // longer exists after a filesystem move.
697
+ const explicitReplacement = Boolean(explicitRepoId && existingByDerivedId);
698
+ const legacyMovedRoot = Boolean(
699
+ !explicitRepoId
700
+ && localIdentity.repoId
701
+ && existingByDerivedId
702
+ && !existsSync(existingByDerivedId.canonicalRoot),
703
+ );
704
+ const existing = existingIdentity ?? (explicitReplacement || legacyMovedRoot ? existingByDerivedId : undefined);
705
+ const repoId = existing?.repoId
706
+ ?? (existingByDerivedId && !existingIdentity ? newLocalRepoId() : derivedRepoId);
643
707
  const checkoutId = stableCheckoutId(repoId, canonicalRoot);
644
- const existing = existingIdentity ?? registry.repositories.find((record) => record.repoId === repoId);
645
708
  const checkout: RepositoryCheckout = {
646
709
  checkoutId,
647
710
  localRoot: canonicalRoot,
@@ -659,8 +722,8 @@ export function registerRepository(input: RegisterRepositoryInput): RepositoryRe
659
722
  // A repository-scoped command may start from a managed checkout (for example
660
723
  // immutable release construction). Registering that path must add/update the
661
724
  // checkout only; it must never promote the ephemeral worktree to repository
662
- // canonical authority. Explicit activation remains available through
663
- // addRepositoryCheckout({ activate: true }).
725
+ // canonical authority. Active checkout selection is separate authority held
726
+ // by activeCheckoutId; canonicalRoot remains the repository registration root.
664
727
  if (existing && worktree && existing.canonicalRemote && canonicalRemote !== existing.canonicalRemote) {
665
728
  throw new Error(`CHECKOUT_REPOSITORY_MISMATCH: ${canonicalRoot}`);
666
729
  }
@@ -776,6 +839,9 @@ export function addRepositoryCheckout(input: AddRepositoryCheckoutInput): Reposi
776
839
  if (current.canonicalRemote && canonicalRemote !== current.canonicalRemote) {
777
840
  throw new Error(`CHECKOUT_REPOSITORY_MISMATCH: ${canonicalRoot}`);
778
841
  }
842
+ if (!repositoryCheckoutRootMatches(current, canonicalRoot)) {
843
+ throw new Error(`CHECKOUT_REPOSITORY_MISMATCH: ${canonicalRoot}`);
844
+ }
779
845
  const timestamp = now();
780
846
  const checkoutId = stableCheckoutId(current.repoId, canonicalRoot);
781
847
  const checkout: RepositoryCheckout = {
@@ -792,14 +858,14 @@ export function addRepositoryCheckout(input: AddRepositoryCheckoutInput): Reposi
792
858
  const activate = input.activate === true;
793
859
  const next: RepositoryRecord = {
794
860
  ...current,
795
- ...(activate ? { localRoot: canonicalRoot, canonicalRoot, activeCheckoutId: checkoutId } : {}),
861
+ ...(activate ? { activeCheckoutId: checkoutId } : {}),
796
862
  checkouts: [...current.checkouts.filter((value) => value.checkoutId !== checkoutId), checkout],
797
863
  updatedAt: timestamp,
798
864
  lastSeenAt: timestamp,
799
865
  };
800
866
  registry.repositories[index] = next;
801
867
  saveRepositoryRegistry(registry, home);
802
- return next;
868
+ return activate ? selectRepositoryCheckout(next, checkoutId) : next;
803
869
  }
804
870
 
805
871
  export function setRepositoryCheckoutLifecycle(input: SetRepositoryCheckoutLifecycleInput): RepositoryRecord {
@@ -98,6 +98,16 @@ function gitText(repository: RepositoryRecord, args: string[]): string | null {
98
98
  return result.ok && result.stdout.trim() ? result.stdout.trim() : null;
99
99
  }
100
100
 
101
+ function stashSelectorForOid(repository: RepositoryRecord, oid: string): string | null {
102
+ const result = runGit(repository, ['stash', 'list', '--format=%H%x00%gd'], 64 * 1024);
103
+ if (!result.ok) return null;
104
+ for (const line of result.stdout.split(/\r?\n/)) {
105
+ const [candidateOid, selector] = line.split('\0');
106
+ if (candidateOid === oid && selector) return selector;
107
+ }
108
+ return null;
109
+ }
110
+
101
111
  function assertSafeBranchName(raw: unknown): string {
102
112
  try {
103
113
  return validateBranchName(raw, { purpose: 'GIT_BRANCH' });
@@ -414,6 +424,7 @@ export function repositoryGitFinishWorkflow(controllerHome: string, repository:
414
424
  const requestedPreservedDirtyPaths = normalizePaths(input.preserveDirtyTargetPaths);
415
425
  const currentDirtyPaths = [...new Set([...before.staged, ...before.unstaged, ...before.untracked])].sort();
416
426
  const preserveDirtyTargetInPlace = alreadyOnTarget && requestedPreservedDirtyPaths.length > 0;
427
+ let equivalentOverlappingDirtyPaths: string[] = [];
417
428
  if (preserveDirtyTargetInPlace) {
418
429
  const requested = new Set(requestedPreservedDirtyPaths);
419
430
  if (requested.size !== currentDirtyPaths.length || currentDirtyPaths.some((path) => !requested.has(path))) {
@@ -426,7 +437,19 @@ export function repositoryGitFinishWorkflow(controllerHome: string, repository:
426
437
  const candidatePaths = new Set(candidateDiff.stdout.split(/\r?\n/).map((path) => path.trim()).filter(Boolean));
427
438
  const overlapping = currentDirtyPaths.filter((path) => candidatePaths.has(path));
428
439
  if (overlapping.length > 0) {
429
- return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: before, completed: false, error: { code: 'GIT_DIRTY_TARGET_PATH_CONFLICT', message: `Work-owned dirty target path(s) overlap the integration candidate: ${overlapping.join(', ')}` } };
440
+ const staged = new Set(before.staged);
441
+ const unstaged = new Set(before.unstaged);
442
+ const untracked = new Set(before.untracked);
443
+ const equivalent = overlapping.every((path) => {
444
+ if (untracked.has(path) || (!staged.has(path) && !unstaged.has(path))) return false;
445
+ if (staged.has(path) && !runGit(repository, ['diff', '--cached', '--quiet', featureBranch, '--', path], 8 * 1024).ok) return false;
446
+ if (unstaged.has(path) && !runGit(repository, ['diff', '--quiet', featureBranch, '--', path], 8 * 1024).ok) return false;
447
+ return true;
448
+ });
449
+ if (!equivalent) {
450
+ return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: before, completed: false, error: { code: 'GIT_DIRTY_TARGET_PATH_CONFLICT', message: `Work-owned dirty target path(s) overlap the integration candidate: ${overlapping.join(', ')}` } };
451
+ }
452
+ equivalentOverlappingDirtyPaths = overlapping;
430
453
  }
431
454
  }
432
455
  if (!before.clean && !alreadyOnTarget) {
@@ -442,6 +465,30 @@ export function repositoryGitFinishWorkflow(controllerHome: string, repository:
442
465
  }
443
466
  const protectTrackedChanges = alreadyOnTarget && !preserveDirtyTargetInPlace && (before.staged.length > 0 || before.unstaged.length > 0);
444
467
  const finishAuthorization = 'explicit_user_request' as const;
468
+ let equivalentOverlapStashRef: string | null = null;
469
+ if (equivalentOverlappingDirtyPaths.length > 0) {
470
+ const stash = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'push', '-m', `forge-finish-equivalent:${input.workId ?? featureBranch}`, '--', ...equivalentOverlappingDirtyPaths], authorization: finishAuthorization, ...input });
471
+ steps.push({ name: 'stash_equivalent_target_overlap', execution: stash });
472
+ if (stash.status !== 'executed' || stash.ok !== true) {
473
+ return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_EQUIVALENT_TARGET_STASH_FAILED', message: stash.stderr || 'Unable to preserve candidate-equivalent dirty target paths before integration.' } };
474
+ }
475
+ equivalentOverlapStashRef = gitText(repository, ['rev-parse', 'refs/stash']);
476
+ if (!equivalentOverlapStashRef) {
477
+ return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_EQUIVALENT_TARGET_STASH_REFERENCE_MISSING', message: 'Candidate-equivalent target paths were stashed but the recovery stash reference could not be resolved.' } };
478
+ }
479
+ }
480
+ const restoreEquivalentOverlap = (name: string): RepositoryGitExecution | undefined => {
481
+ if (!equivalentOverlapStashRef) return undefined;
482
+ const execution = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'apply', '--index', equivalentOverlapStashRef], authorization: finishAuthorization, ...input });
483
+ steps.push({ name, execution });
484
+ return execution;
485
+ };
486
+ const dropEquivalentOverlapStash = (): void => {
487
+ if (!equivalentOverlapStashRef) return;
488
+ const selector = stashSelectorForOid(repository, equivalentOverlapStashRef) ?? equivalentOverlapStashRef;
489
+ const execution = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'drop', selector], authorization: finishAuthorization, ...input });
490
+ steps.push({ name: 'drop_equivalent_target_stash', execution });
491
+ };
445
492
  let stashRef: string | null = null;
446
493
  if (protectTrackedChanges) {
447
494
  const stash = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'push', '-m', `forge-finish:${input.workId ?? featureBranch}`], authorization: finishAuthorization, ...input });
@@ -468,17 +515,30 @@ export function repositoryGitFinishWorkflow(controllerHome: string, repository:
468
515
  };
469
516
  const dropTrackedStash = (): void => {
470
517
  if (!stashRef) return;
471
- const execution = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'drop', stashRef], authorization: finishAuthorization, ...input });
518
+ const selector = stashSelectorForOid(repository, stashRef) ?? stashRef;
519
+ const execution = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'drop', selector], authorization: finishAuthorization, ...input });
472
520
  steps.push({ name: 'drop_target_stash', execution });
473
521
  };
474
522
  const merge = executeRepositoryGitCommand(controllerHome, repository, { args: ['merge', ...(input.noFf === true ? ['--no-ff'] : ['--ff-only']), featureBranch], authorization: finishAuthorization, ...input });
475
523
  steps.push({ name: 'merge_feature', execution: merge });
476
524
  if (merge.status !== 'executed' || merge.ok !== true) {
525
+ const restoreEquivalent = restoreEquivalentOverlap('restore_equivalent_target_overlap_after_merge_failure');
526
+ if (restoreEquivalent && (restoreEquivalent.status !== 'executed' || restoreEquivalent.ok !== true)) return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_EQUIVALENT_TARGET_RESTORE_FAILED', message: restoreEquivalent.stderr || 'Candidate-equivalent target paths could not be restored after merge failure; recovery stash was retained.' } };
527
+ dropEquivalentOverlapStash();
477
528
  const restore = applyTrackedStash('restore_target_changes_after_merge_failure');
478
529
  if (restore && (restore.status !== 'executed' || restore.ok !== true)) return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_LOCAL_CHANGES_RESTORE_FAILED', message: restore.stderr || 'git stash apply failed while restoring the pre-merge target state' } };
479
530
  dropTrackedStash();
480
531
  return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_MERGE_FAILED', message: merge.stderr || 'git merge failed' } };
481
532
  }
533
+ if (equivalentOverlappingDirtyPaths.length > 0) {
534
+ const afterMerge = repositoryGitStatus(repository);
535
+ const remainingDirty = new Set([...afterMerge.staged, ...afterMerge.unstaged, ...afterMerge.untracked]);
536
+ const contentMatchesCandidate = runGit(repository, ['diff', '--quiet', featureBranch, '--', ...equivalentOverlappingDirtyPaths], 32 * 1024).ok;
537
+ if (!contentMatchesCandidate || equivalentOverlappingDirtyPaths.some((path) => remainingDirty.has(path))) {
538
+ return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: afterMerge, completed: false, error: { code: 'GIT_EQUIVALENT_TARGET_POSTCONDITION_FAILED', message: 'Integrated target did not retain the exact candidate content for a reconciled dirty overlap; recovery stash was retained.' } };
539
+ }
540
+ dropEquivalentOverlapStash();
541
+ }
482
542
  const reapply = applyTrackedStash('reapply_target_changes');
483
543
  if (reapply && (reapply.status !== 'executed' || reapply.ok !== true)) return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_LOCAL_CHANGES_RESTORE_FAILED', message: reapply.stderr || 'Tracked target changes passed merge-tree preflight but could not be reapplied after merge; recovery stash was retained.' } };
484
544
  dropTrackedStash();