@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
@@ -0,0 +1,225 @@
1
+ import { deleteControlPlaneRecord, mutateControlPlaneRecord, readControlPlaneRecord } from '../control-plane/persistence/sqlite-store';
2
+
3
+ export const OUTPUT_HANDLE_INDEX_NAMESPACE = 'output_handle_index';
4
+ const OUTPUT_HANDLE_INDEX_SCOPE = 'instance';
5
+ const OUTPUT_HANDLE_INDEX_SCHEMA_VERSION = 1;
6
+
7
+ export type OutputHandleKind = 'result' | 'artifact' | 'evidence';
8
+ export type OutputHandleStorage =
9
+ | { scope: 'instance' }
10
+ | { scope: 'legacy_repository'; repositoryId: string };
11
+
12
+ export interface OutputHandleIndexEntry {
13
+ schemaVersion: 1;
14
+ handleId: string;
15
+ kind: OutputHandleKind;
16
+ storage: OutputHandleStorage;
17
+ /** Repository provenance is descriptive context, never the locator namespace. */
18
+ repositoryId?: string;
19
+ /** Opaque ids locate this record; authorization is bound separately. */
20
+ principalId?: string;
21
+ sessionId?: string;
22
+ workId?: string;
23
+ operationId?: string;
24
+ fingerprint: string;
25
+ recordedAt: string;
26
+ updatedAt: string;
27
+ }
28
+
29
+ function bounded(value: string | undefined): string | undefined {
30
+ const normalized = value?.trim();
31
+ return normalized ? normalized.slice(0, 512) : undefined;
32
+ }
33
+
34
+ function normalizedStorage(storage: OutputHandleStorage): OutputHandleStorage {
35
+ if (storage.scope === 'instance') return { scope: 'instance' };
36
+ const repositoryId = bounded(storage.repositoryId);
37
+ if (!repositoryId) throw new Error('OUTPUT_HANDLE_LEGACY_REPOSITORY_REQUIRED');
38
+ return { scope: 'legacy_repository', repositoryId };
39
+ }
40
+
41
+ function bindingsConflict(current: OutputHandleIndexEntry, input: {
42
+ principalId?: string;
43
+ sessionId?: string;
44
+ workId?: string;
45
+ operationId?: string;
46
+ }): boolean {
47
+ for (const key of ['principalId', 'sessionId', 'workId', 'operationId'] as const) {
48
+ const left = bounded(current[key]);
49
+ const right = bounded(input[key]);
50
+ if (left && right && left !== right) return true;
51
+ }
52
+ return false;
53
+ }
54
+
55
+ export function recordOutputHandleIndexEntry(
56
+ controllerHome: string,
57
+ input: {
58
+ handleId: string;
59
+ kind: OutputHandleKind;
60
+ storage: OutputHandleStorage;
61
+ repositoryId?: string;
62
+ principalId?: string;
63
+ sessionId?: string;
64
+ workId?: string;
65
+ operationId?: string;
66
+ fingerprint: string;
67
+ now?: string;
68
+ },
69
+ ): OutputHandleIndexEntry {
70
+ const handleId = bounded(input.handleId);
71
+ const fingerprint = bounded(input.fingerprint);
72
+ if (!handleId || !fingerprint) throw new Error('OUTPUT_HANDLE_IDENTITY_REQUIRED');
73
+ const storage = normalizedStorage(input.storage);
74
+ const now = input.now ?? new Date().toISOString();
75
+ const repositoryId = bounded(input.repositoryId);
76
+ const principalId = bounded(input.principalId);
77
+ const sessionId = bounded(input.sessionId);
78
+ const workId = bounded(input.workId);
79
+ const operationId = bounded(input.operationId);
80
+
81
+ const record = mutateControlPlaneRecord<OutputHandleIndexEntry>(controllerHome, {
82
+ namespace: OUTPUT_HANDLE_INDEX_NAMESPACE,
83
+ scope: OUTPUT_HANDLE_INDEX_SCOPE,
84
+ key: handleId,
85
+ schemaVersion: OUTPUT_HANDLE_INDEX_SCHEMA_VERSION,
86
+ action: 'output_handle_index_record',
87
+ mutate: (currentRecord) => {
88
+ const current = currentRecord?.value;
89
+ if (current) {
90
+ if (current.schemaVersion !== OUTPUT_HANDLE_INDEX_SCHEMA_VERSION
91
+ || current.handleId !== handleId
92
+ || current.kind !== input.kind
93
+ || current.fingerprint !== fingerprint
94
+ || bindingsConflict(current, { principalId, sessionId, workId, operationId })) {
95
+ throw new Error(`OUTPUT_HANDLE_ID_COLLISION: ${handleId}`);
96
+ }
97
+ // Identical historical duplicates collapse to one locator. Canonical
98
+ // instance storage always outranks a legacy repository location.
99
+ const nextStorage = current.storage.scope === 'instance' || storage.scope !== 'instance'
100
+ ? current.storage
101
+ : storage;
102
+ return {
103
+ ...current,
104
+ storage: nextStorage,
105
+ ...(current.repositoryId || repositoryId ? { repositoryId: current.repositoryId ?? repositoryId } : {}),
106
+ ...(current.principalId || principalId ? { principalId: current.principalId ?? principalId } : {}),
107
+ ...(current.sessionId || sessionId ? { sessionId: current.sessionId ?? sessionId } : {}),
108
+ ...(current.workId || workId ? { workId: current.workId ?? workId } : {}),
109
+ ...(current.operationId || operationId ? { operationId: current.operationId ?? operationId } : {}),
110
+ updatedAt: now,
111
+ };
112
+ }
113
+ return {
114
+ schemaVersion: OUTPUT_HANDLE_INDEX_SCHEMA_VERSION,
115
+ handleId,
116
+ kind: input.kind,
117
+ storage,
118
+ ...(repositoryId ? { repositoryId } : {}),
119
+ ...(principalId ? { principalId } : {}),
120
+ ...(sessionId ? { sessionId } : {}),
121
+ ...(workId ? { workId } : {}),
122
+ ...(operationId ? { operationId } : {}),
123
+ fingerprint,
124
+ recordedAt: now,
125
+ updatedAt: now,
126
+ };
127
+ },
128
+ });
129
+ return record.value;
130
+ }
131
+
132
+ export function refreshOutputHandleFingerprint(
133
+ controllerHome: string,
134
+ input: {
135
+ handleId: string;
136
+ kind: OutputHandleKind;
137
+ expectedFingerprint: string;
138
+ nextFingerprint: string;
139
+ now?: string;
140
+ },
141
+ ): OutputHandleIndexEntry {
142
+ const handleId = bounded(input.handleId);
143
+ const expectedFingerprint = bounded(input.expectedFingerprint);
144
+ const nextFingerprint = bounded(input.nextFingerprint);
145
+ if (!handleId || !expectedFingerprint || !nextFingerprint) throw new Error('OUTPUT_HANDLE_FINGERPRINT_REFRESH_REQUIRED');
146
+ const now = input.now ?? new Date().toISOString();
147
+ const record = mutateControlPlaneRecord<OutputHandleIndexEntry>(controllerHome, {
148
+ namespace: OUTPUT_HANDLE_INDEX_NAMESPACE,
149
+ scope: OUTPUT_HANDLE_INDEX_SCOPE,
150
+ key: handleId,
151
+ schemaVersion: OUTPUT_HANDLE_INDEX_SCHEMA_VERSION,
152
+ action: 'output_handle_index_fingerprint_refresh',
153
+ mutate: (currentRecord) => {
154
+ const current = currentRecord?.value;
155
+ if (!current
156
+ || current.schemaVersion !== OUTPUT_HANDLE_INDEX_SCHEMA_VERSION
157
+ || current.handleId !== handleId
158
+ || current.kind !== input.kind
159
+ || current.storage.scope !== 'instance') {
160
+ throw new Error(`OUTPUT_HANDLE_FINGERPRINT_REFRESH_SCOPE_MISMATCH: ${handleId}`);
161
+ }
162
+ if (current.fingerprint !== expectedFingerprint) {
163
+ throw new Error(`OUTPUT_HANDLE_FINGERPRINT_REFRESH_CONFLICT: ${handleId}`);
164
+ }
165
+ return { ...current, fingerprint: nextFingerprint, updatedAt: now };
166
+ },
167
+ });
168
+ return record.value;
169
+ }
170
+
171
+ export function deleteOutputHandleIndexEntry(
172
+ controllerHome: string,
173
+ input: { handleId: string; kind: OutputHandleKind; expectedFingerprint?: string },
174
+ ): boolean {
175
+ const handleId = bounded(input.handleId);
176
+ if (!handleId) return false;
177
+ const record = readControlPlaneRecord<OutputHandleIndexEntry>(
178
+ controllerHome,
179
+ OUTPUT_HANDLE_INDEX_NAMESPACE,
180
+ OUTPUT_HANDLE_INDEX_SCOPE,
181
+ handleId,
182
+ );
183
+ if (!record) return false;
184
+ const current = record.value;
185
+ if (current.schemaVersion !== OUTPUT_HANDLE_INDEX_SCHEMA_VERSION
186
+ || current.handleId !== handleId
187
+ || current.kind !== input.kind
188
+ || current.storage.scope !== 'instance') {
189
+ throw new Error(`OUTPUT_HANDLE_INDEX_DELETE_SCOPE_MISMATCH: ${handleId}`);
190
+ }
191
+ const expectedFingerprint = bounded(input.expectedFingerprint);
192
+ if (expectedFingerprint && current.fingerprint !== expectedFingerprint) {
193
+ throw new Error(`OUTPUT_HANDLE_INDEX_DELETE_CONFLICT: ${handleId}`);
194
+ }
195
+ return deleteControlPlaneRecord(controllerHome, {
196
+ namespace: OUTPUT_HANDLE_INDEX_NAMESPACE,
197
+ scope: OUTPUT_HANDLE_INDEX_SCOPE,
198
+ key: handleId,
199
+ action: 'output_handle_index_delete',
200
+ expectedRevision: record.revision,
201
+ });
202
+ }
203
+
204
+ export function readOutputHandleIndexEntry(
205
+ controllerHome: string,
206
+ handleId: string,
207
+ ): OutputHandleIndexEntry | undefined {
208
+ const key = bounded(handleId);
209
+ if (!key) return undefined;
210
+ const record = readControlPlaneRecord<OutputHandleIndexEntry>(
211
+ controllerHome,
212
+ OUTPUT_HANDLE_INDEX_NAMESPACE,
213
+ OUTPUT_HANDLE_INDEX_SCOPE,
214
+ key,
215
+ );
216
+ const value = record?.value;
217
+ if (!value) return undefined;
218
+ if (value.schemaVersion !== OUTPUT_HANDLE_INDEX_SCHEMA_VERSION || value.handleId !== key) {
219
+ throw new Error(`OUTPUT_HANDLE_INDEX_CORRUPT: ${key}`);
220
+ }
221
+ if (!value.fingerprint || !value.kind || !value.storage) {
222
+ throw new Error(`OUTPUT_HANDLE_INDEX_CORRUPT: ${key}`);
223
+ }
224
+ return value;
225
+ }
@@ -1,15 +1,17 @@
1
- import { randomUUID } from 'crypto';
1
+ import { createHash, randomUUID } from 'crypto';
2
2
  import { chmodSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs';
3
3
  import { join } from 'path';
4
- import { repositoryControllerRoot } from '../../cli/repositories/controller-home';
4
+ import { controllerSystemRoot, repositoryControllerRoot } from '../../cli/repositories/controller-home';
5
5
  import { readJsonFile, sanitizeFileComponent, writeJsonAtomic } from '../shared/json-files';
6
+ import { recordOutputHandleIndexEntry, readOutputHandleIndexEntry, refreshOutputHandleFingerprint, type OutputHandleStorage } from './output-handle-index';
6
7
  import { redactSensitiveValue, type SensitiveRedactionCount } from './sensitive-output';
7
8
 
8
9
  export interface ControllerResultRecord {
9
10
  schemaVersion: 1;
10
11
  resultId: string;
11
12
  resultRef: string;
12
- repoId: string;
13
+ /** Repository provenance only. It is not part of the canonical result locator. */
14
+ repoId?: string;
13
15
  sessionId: string;
14
16
  principalId: string;
15
17
  workId?: string;
@@ -24,19 +26,29 @@ export interface ControllerResultRecord {
24
26
  };
25
27
  }
26
28
 
27
- function resultRoot(controllerHome: string, repoId: string): string {
28
- const root = join(repositoryControllerRoot(controllerHome, repoId), 'results');
29
+ function canonicalResultRoot(controllerHome: string): string {
30
+ const root = join(controllerSystemRoot(controllerHome), 'outputs', 'results');
29
31
  mkdirSync(join(root, 'records'), { recursive: true, mode: 0o700 });
30
32
  mkdirSync(join(root, 'data'), { recursive: true, mode: 0o700 });
31
33
  return root;
32
34
  }
33
35
 
34
- function recordPath(controllerHome: string, repoId: string, resultId: string): string {
35
- return join(resultRoot(controllerHome, repoId), 'records', `${sanitizeFileComponent(resultId)}.json`);
36
+ function legacyResultRoot(controllerHome: string, repoId: string): string {
37
+ return join(repositoryControllerRoot(controllerHome, repoId), 'results');
36
38
  }
37
39
 
38
- function dataPath(controllerHome: string, repoId: string, resultId: string): string {
39
- return join(resultRoot(controllerHome, repoId), 'data', `${sanitizeFileComponent(resultId)}.json`);
40
+ function resultRoot(controllerHome: string, storage: OutputHandleStorage): string {
41
+ return storage.scope === 'instance'
42
+ ? canonicalResultRoot(controllerHome)
43
+ : legacyResultRoot(controllerHome, storage.repositoryId);
44
+ }
45
+
46
+ function recordPath(controllerHome: string, storage: OutputHandleStorage, resultId: string): string {
47
+ return join(resultRoot(controllerHome, storage), 'records', `${sanitizeFileComponent(resultId)}.json`);
48
+ }
49
+
50
+ function dataPath(controllerHome: string, storage: OutputHandleStorage, resultId: string): string {
51
+ return join(resultRoot(controllerHome, storage), 'data', `${sanitizeFileComponent(resultId)}.json`);
40
52
  }
41
53
 
42
54
  function writePrivateJson(path: string, value: unknown): void {
@@ -53,33 +65,131 @@ function redactionMetadata(redactions: SensitiveRedactionCount[]): NonNullable<C
53
65
  };
54
66
  }
55
67
 
56
- function sanitizeStoredResult(
68
+ function canonicalResultRef(resultId: string): string {
69
+ return `result://${resultId}`;
70
+ }
71
+
72
+ function resultFingerprint(record: ControllerResultRecord, value: unknown): string {
73
+ return createHash('sha256').update(JSON.stringify({
74
+ resultId: record.resultId,
75
+ sessionId: record.sessionId,
76
+ principalId: record.principalId,
77
+ workId: record.workId ?? null,
78
+ kind: record.kind,
79
+ createdAt: record.createdAt,
80
+ value,
81
+ })).digest('hex');
82
+ }
83
+
84
+ function registerResult(
57
85
  controllerHome: string,
58
86
  record: ControllerResultRecord,
87
+ value: unknown,
88
+ storage: OutputHandleStorage,
89
+ ): void {
90
+ recordOutputHandleIndexEntry(controllerHome, {
91
+ handleId: record.resultId,
92
+ kind: 'result',
93
+ storage,
94
+ repositoryId: record.repoId,
95
+ principalId: record.principalId,
96
+ sessionId: record.sessionId,
97
+ workId: record.workId,
98
+ fingerprint: resultFingerprint(record, value),
99
+ });
100
+ }
101
+
102
+ function parseRef(resultRef: string): { resultId: string; legacyRepoId?: string } {
103
+ const normalized = resultRef.trim();
104
+ const canonical = /^result:\/\/([^/]+)$/.exec(normalized);
105
+ if (canonical) return { resultId: canonical[1]! };
106
+ const legacy = /^result:\/\/([^/]+)\/([^/]+)$/.exec(normalized);
107
+ if (legacy) return { legacyRepoId: legacy[1]!, resultId: legacy[2]! };
108
+ throw new Error('RESULT_REF_INVALID: expected result://<resultId>');
109
+ }
110
+
111
+ function loadResultAt(
112
+ controllerHome: string,
113
+ resultId: string,
114
+ storage: OutputHandleStorage,
115
+ ): { record: ControllerResultRecord; value: unknown } {
116
+ const stored = readJsonFile<ControllerResultRecord>(recordPath(controllerHome, storage, resultId));
117
+ if (stored.resultId !== resultId) throw new Error('RESULT_IDENTITY_MISMATCH');
118
+ const value = readJsonFile<unknown>(dataPath(controllerHome, storage, resultId));
119
+ const record: ControllerResultRecord = {
120
+ ...stored,
121
+ resultRef: canonicalResultRef(resultId),
122
+ ...(storage.scope === 'legacy_repository' && !stored.repoId ? { repoId: storage.repositoryId } : {}),
123
+ };
124
+ return { record, value };
125
+ }
126
+
127
+ function resolveStoredResult(
128
+ controllerHome: string,
129
+ resultId: string,
130
+ legacyRepoId?: string,
131
+ ): { record: ControllerResultRecord; value: unknown; storage: OutputHandleStorage } {
132
+ if (legacyRepoId) {
133
+ const storage: OutputHandleStorage = { scope: 'legacy_repository', repositoryId: legacyRepoId };
134
+ const loaded = loadResultAt(controllerHome, resultId, storage);
135
+ registerResult(controllerHome, loaded.record, loaded.value, storage);
136
+ return { ...loaded, storage };
137
+ }
138
+
139
+ const indexed = readOutputHandleIndexEntry(controllerHome, resultId);
140
+ if (indexed) {
141
+ if (indexed.kind !== 'result') throw new Error(`OUTPUT_HANDLE_KIND_MISMATCH: ${resultId}`);
142
+ const loaded = loadResultAt(controllerHome, resultId, indexed.storage);
143
+ if (resultFingerprint(loaded.record, loaded.value) !== indexed.fingerprint) {
144
+ throw new Error(`OUTPUT_HANDLE_FINGERPRINT_MISMATCH: ${resultId}`);
145
+ }
146
+ return { ...loaded, storage: indexed.storage };
147
+ }
148
+
149
+ const storage: OutputHandleStorage = { scope: 'instance' };
150
+ const canonicalRecord = recordPath(controllerHome, storage, resultId);
151
+ if (!existsSync(canonicalRecord)) {
152
+ throw new Error(`RESULT_LOCATOR_NOT_FOUND: ${resultId}; legacy results require their historical result://<repoId>/<resultId> reference once`);
153
+ }
154
+ const loaded = loadResultAt(controllerHome, resultId, storage);
155
+ registerResult(controllerHome, loaded.record, loaded.value, storage);
156
+ return { ...loaded, storage };
157
+ }
158
+
159
+ function sanitizeResolvedResult(
160
+ controllerHome: string,
161
+ record: ControllerResultRecord,
162
+ value: unknown,
163
+ storage: OutputHandleStorage,
59
164
  ): { record: ControllerResultRecord; value: unknown; changed: boolean } {
60
- const path = dataPath(controllerHome, record.repoId, record.resultId);
61
- const raw = readJsonFile<unknown>(path);
62
- const sanitized = redactSensitiveValue(raw);
63
- if (!sanitized.changed) return { record, value: raw, changed: false };
64
- writePrivateJson(path, sanitized.value);
165
+ const sanitized = redactSensitiveValue(value);
166
+ if (!sanitized.changed) return { record, value, changed: false };
65
167
  const next: ControllerResultRecord = {
66
168
  ...record,
67
- byteLength: statSync(path).size,
68
169
  redaction: redactionMetadata(sanitized.redactions),
69
170
  };
70
- writePrivateJson(recordPath(controllerHome, record.repoId, record.resultId), next);
171
+ if (storage.scope === 'instance') {
172
+ const previousFingerprint = resultFingerprint(record, value);
173
+ const nextFingerprint = resultFingerprint(next, sanitized.value);
174
+ const path = dataPath(controllerHome, storage, record.resultId);
175
+ writePrivateJson(path, sanitized.value);
176
+ next.byteLength = statSync(path).size;
177
+ writePrivateJson(recordPath(controllerHome, storage, record.resultId), next);
178
+ refreshOutputHandleFingerprint(controllerHome, {
179
+ handleId: record.resultId,
180
+ kind: 'result',
181
+ expectedFingerprint: previousFingerprint,
182
+ nextFingerprint,
183
+ });
184
+ }
185
+ // Historical repository partitions are immutable after the cutover. Apply
186
+ // current redaction in-memory without rewriting legacy bytes.
71
187
  return { record: next, value: sanitized.value, changed: true };
72
188
  }
73
189
 
74
- function parseRef(resultRef: string): { repoId: string; resultId: string } {
75
- const match = /^result:\/\/([^/]+)\/([^/]+)$/.exec(resultRef.trim());
76
- if (!match) throw new Error('RESULT_REF_INVALID: expected result://<repoId>/<resultId>');
77
- return { repoId: match[1]!, resultId: match[2]! };
78
- }
79
-
80
190
  export function writeControllerResult(input: {
81
191
  controllerHome: string;
82
- repoId: string;
192
+ repoId?: string;
83
193
  sessionId: string;
84
194
  principalId: string;
85
195
  workId?: string;
@@ -87,15 +197,16 @@ export function writeControllerResult(input: {
87
197
  value: unknown;
88
198
  }): ControllerResultRecord {
89
199
  const resultId = `res_${randomUUID().replace(/-/g, '')}`;
90
- const resultRef = `result://${input.repoId}/${resultId}`;
91
- const path = dataPath(input.controllerHome, input.repoId, resultId);
200
+ const resultRef = canonicalResultRef(resultId);
201
+ const storage: OutputHandleStorage = { scope: 'instance' };
202
+ const path = dataPath(input.controllerHome, storage, resultId);
92
203
  const sanitized = redactSensitiveValue(input.value);
93
204
  writePrivateJson(path, sanitized.value);
94
205
  const record: ControllerResultRecord = {
95
206
  schemaVersion: 1,
96
207
  resultId,
97
208
  resultRef,
98
- repoId: input.repoId,
209
+ ...(input.repoId?.trim() ? { repoId: input.repoId.trim() } : {}),
99
210
  sessionId: input.sessionId,
100
211
  principalId: input.principalId,
101
212
  ...(input.workId ? { workId: input.workId } : {}),
@@ -104,7 +215,8 @@ export function writeControllerResult(input: {
104
215
  createdAt: new Date().toISOString(),
105
216
  ...(sanitized.changed ? { redaction: redactionMetadata(sanitized.redactions) } : {}),
106
217
  };
107
- writePrivateJson(recordPath(input.controllerHome, input.repoId, resultId), record);
218
+ writePrivateJson(recordPath(input.controllerHome, storage, resultId), record);
219
+ registerResult(input.controllerHome, record, sanitized.value, storage);
108
220
  return record;
109
221
  }
110
222
 
@@ -123,10 +235,9 @@ export function readControllerResult(input: {
123
235
  limit?: number;
124
236
  }): { record: ControllerResultRecord; items: unknown; cursor: number; nextCursor?: number; truncated: boolean } {
125
237
  const parsed = parseRef(input.resultRef);
126
- const record = readJsonFile<ControllerResultRecord>(recordPath(input.controllerHome, parsed.repoId, parsed.resultId));
127
- if (record.resultRef !== input.resultRef || record.repoId !== parsed.repoId) throw new Error('RESULT_IDENTITY_MISMATCH');
128
- authorizeRecord(record, input.sessionId, input.principalId, input.workId);
129
- const sanitized = sanitizeStoredResult(input.controllerHome, record);
238
+ const resolved = resolveStoredResult(input.controllerHome, parsed.resultId, parsed.legacyRepoId);
239
+ authorizeRecord(resolved.record, input.sessionId, input.principalId, input.workId);
240
+ const sanitized = sanitizeResolvedResult(input.controllerHome, resolved.record, resolved.value, resolved.storage);
130
241
  const value = sanitized.value;
131
242
  const cursor = Math.max(0, Math.trunc(input.cursor ?? 0));
132
243
  const limit = Math.max(1, Math.min(500, Math.trunc(input.limit ?? 100)));
@@ -143,6 +254,11 @@ export function readControllerResult(input: {
143
254
  const items = source.slice(cursor, cursor + limit);
144
255
  return { record: sanitized.record, items, cursor, ...(cursor + items.length < source.length ? { nextCursor: cursor + items.length } : {}), truncated: cursor + items.length < source.length };
145
256
  }
257
+ if (value && typeof value === 'object') {
258
+ const source = JSON.stringify(value, null, 2);
259
+ const items = source.slice(cursor, cursor + limit * 4_096);
260
+ return { record: sanitized.record, items, cursor, ...(cursor + items.length < source.length ? { nextCursor: cursor + items.length } : {}), truncated: cursor + items.length < source.length };
261
+ }
146
262
  return { record: sanitized.record, items: value, cursor, truncated: false };
147
263
  }
148
264
 
@@ -156,10 +272,9 @@ export function searchControllerResult(input: {
156
272
  limit?: number;
157
273
  }): { record: ControllerResultRecord; matches: Array<{ line: number; text: string }>; truncated: boolean } {
158
274
  const parsed = parseRef(input.resultRef);
159
- const record = readJsonFile<ControllerResultRecord>(recordPath(input.controllerHome, parsed.repoId, parsed.resultId));
160
- if (record.resultRef !== input.resultRef || record.repoId !== parsed.repoId) throw new Error('RESULT_IDENTITY_MISMATCH');
161
- authorizeRecord(record, input.sessionId, input.principalId, input.workId);
162
- const sanitized = sanitizeStoredResult(input.controllerHome, record);
275
+ const resolved = resolveStoredResult(input.controllerHome, parsed.resultId, parsed.legacyRepoId);
276
+ authorizeRecord(resolved.record, input.sessionId, input.principalId, input.workId);
277
+ const sanitized = sanitizeResolvedResult(input.controllerHome, resolved.record, resolved.value, resolved.storage);
163
278
  const value = sanitized.value;
164
279
  const query = input.query.trim().toLowerCase();
165
280
  if (!query) throw new Error('RESULT_QUERY_REQUIRED');
@@ -169,17 +284,15 @@ export function searchControllerResult(input: {
169
284
  return { record: sanitized.record, matches, truncated: lines.filter((text) => text.toLowerCase().includes(query)).length > matches.length };
170
285
  }
171
286
 
172
- /**
173
- * Bounded maintenance entry point for historical result artifacts. It returns
174
- * only counts and ids; sensitive contents are never included in the report.
175
- */
176
- export function sanitizeControllerResultStore(controllerHome: string, repoId: string, limit = 10_000): {
287
+ /** Bounded maintenance of the canonical instance Result store only. */
288
+ export function sanitizeControllerResultStore(controllerHome: string, _repoId: string, limit = 10_000): {
177
289
  scanned: number;
178
290
  changed: number;
179
291
  failed: number;
180
292
  resultIds: string[];
181
293
  } {
182
- const root = resultRoot(controllerHome, repoId);
294
+ const storage: OutputHandleStorage = { scope: 'instance' };
295
+ const root = canonicalResultRoot(controllerHome);
183
296
  const recordsDir = join(root, 'records');
184
297
  let scanned = 0;
185
298
  let changed = 0;
@@ -192,11 +305,12 @@ export function sanitizeControllerResultStore(controllerHome: string, repoId: st
192
305
  scanned += 1;
193
306
  try {
194
307
  const record = readJsonFile<ControllerResultRecord>(join(recordsDir, entry));
195
- if (!record?.resultId || record.repoId !== repoId || !existsSync(dataPath(controllerHome, repoId, record.resultId))) {
308
+ if (!record?.resultId || !existsSync(dataPath(controllerHome, storage, record.resultId))) {
196
309
  failed += 1;
197
310
  continue;
198
311
  }
199
- const sanitized = sanitizeStoredResult(controllerHome, record);
312
+ const raw = readJsonFile<unknown>(dataPath(controllerHome, storage, record.resultId));
313
+ const sanitized = sanitizeResolvedResult(controllerHome, record, raw, storage);
200
314
  if (sanitized.changed) {
201
315
  changed += 1;
202
316
  resultIds.push(record.resultId);
@@ -10,7 +10,7 @@ import { touchSchedulerWakeSignal } from '../../control-plane/global-scheduler/w
10
10
  import { readJsonFile, removeFile, sanitizeFileComponent, writeJsonAtomic } from '../../shared/json-files';
11
11
  import { terminateProcessTree } from '../../shared/process-tree';
12
12
  import { releaseExecutionLeases } from '../../resources/leases/store';
13
- import { assertRuntimeMayWriteOrThrow } from '../../root/write-fence';
13
+ import { assertRuntimeMayWriteOrThrow, isRuntimeWriteFenceError } from '../../root/write-fence';
14
14
  import {
15
15
  ACTIVE_JOB_STATUSES,
16
16
  TERMINAL_JOB_STATUSES,
@@ -122,11 +122,10 @@ function artifactPointerFor(job: ExecutionJob, artifact: ReturnType<typeof write
122
122
  artifactKind: artifact.kind,
123
123
  detailPointer: {
124
124
  tool: 'get_artifact',
125
- repoId: job.repoId,
126
125
  artifactId: artifact.artifactId,
127
126
  maxBytes: 512 * 1024,
128
127
  },
129
- next: `Call get_artifact with repo_id=${job.repoId} and artifact_id=${artifact.artifactId}.`,
128
+ next: `Call get_artifact with artifact_id=${artifact.artifactId}.`,
130
129
  };
131
130
  }
132
131
 
@@ -439,7 +438,7 @@ export function transitionExecutionJob(
439
438
  try {
440
439
  assertRuntimeMayWriteOrThrow('write_workflow_terminal', controllerHome);
441
440
  } catch (error) {
442
- if (error instanceof Error && error.message.startsWith('WRITER_FENCED:')) throw error;
441
+ if (isRuntimeWriteFenceError(error)) throw error;
443
442
  }
444
443
  }
445
444
  return updateExecutionJob(controllerHome, repoId, jobId, (current) => {
@@ -16,10 +16,13 @@ import { resolveGitExecutable } from '../../effects/git-executable';
16
16
  import { runProcess } from '../../effects/process-runner';
17
17
  import { repositoryChildProcessEnvironment, resolveBunExecutable } from '../shared/process-environment';
18
18
  import { readJsonFile, writeJsonAtomic } from '../shared/json-files';
19
+ import { ensureForgeInstanceIdentity, getOwnedResource, ownedResourceLocatorFingerprint, recordOwnedResource, type OwnedResourceLocator } from '../../../packages/kernel/identity/api/index';
19
20
 
20
21
  export interface EnsureManagedWorkspaceInput {
21
22
  requestId: string;
22
23
  title: string;
24
+ /** Semantic Work provenance for durable ownership; never an execution mutex. */
25
+ associatedWorkId?: string;
23
26
  baseRef?: string;
24
27
  branchName?: string;
25
28
  /** Explicitly materialize lockfile-backed Node dependencies in this isolated worktree. */
@@ -65,6 +68,21 @@ function suffix(repoId: string, requestId: string): string {
65
68
  return createHash('sha256').update(`${repoId}:${requestId}`).digest('hex').slice(0, 12);
66
69
  }
67
70
 
71
+ export function managedWorkspaceOwnedResourceId(repoId: string, checkoutId: string): string {
72
+ return `managed-worktree:${repoId}:${checkoutId}`;
73
+ }
74
+
75
+ export function managedBranchOwnedResourceId(repoId: string, checkoutId: string): string {
76
+ return `managed-branch:${repoId}:${checkoutId}`;
77
+ }
78
+
79
+ export function managedBranchOwnedResourceLocator(repositoryRoot: string, checkoutId: string, branch: string): OwnedResourceLocator {
80
+ return {
81
+ kind: 'git_ref',
82
+ value: `${realpathSync(repositoryRoot)}\0${checkoutId.trim()}\0refs/heads/${branch.trim()}`,
83
+ };
84
+ }
85
+
68
86
  function git(root: string, args: string[], timeoutMs = 30_000): string {
69
87
  const result = runProcess(resolveGitExecutable(), ['-C', root, ...args], {
70
88
  timeoutMs,
@@ -385,6 +403,10 @@ export function ensureManagedWorkspace(
385
403
  throw new Error(`MANAGED_WORKSPACE_REQUEST_CONFLICT: ${requestId}`);
386
404
  }
387
405
  const branch = manifest?.branch ?? requestedBranch;
406
+ // Persisted manifests are Forge provenance. Without one, an already-existing
407
+ // branch/worktree is never adopted as cleanup-owned merely because its name/path matches.
408
+ let workspaceOwnedByForge = Boolean(manifest);
409
+ let branchOwnedByForge = Boolean(manifest);
388
410
  // Persisted manifests are authoritative evidence; never move an active worktree in place.
389
411
  const path = manifest?.path ? resolve(manifest.path) : requestedPath;
390
412
  const baseRevision = manifest?.baseRevision ?? git(sourceRoot, ['rev-parse', '--verify', `${requestedBaseRef}^{commit}`]);
@@ -396,8 +418,11 @@ export function ensureManagedWorkspace(
396
418
  // A daemon crash may leave stale worktree administration behind even after the directory is gone.
397
419
  git(sourceRoot, ['worktree', 'prune', '--expire', 'now'], 120_000);
398
420
  git(sourceRoot, ['worktree', 'add', path, branch], 120_000);
421
+ workspaceOwnedByForge = true;
399
422
  } else {
400
423
  git(sourceRoot, ['worktree', 'add', '-b', branch, path, baseRevision], 120_000);
424
+ workspaceOwnedByForge = true;
425
+ branchOwnedByForge = true;
401
426
  }
402
427
  }
403
428
  if (!repositoryCheckoutRootMatches(repository, path)) {
@@ -425,6 +450,38 @@ export function ensureManagedWorkspace(
425
450
  );
426
451
  if (!checkout) throw new Error(`MANAGED_WORKSPACE_CHECKOUT_NOT_REGISTERED: ${path}`);
427
452
  const selected = selectRepositoryCheckout(record, checkout.checkoutId);
453
+ const ownerForgeInstanceId = ensureForgeInstanceIdentity({ controllerHome }).instanceId;
454
+ const workspaceResourceId = managedWorkspaceOwnedResourceId(repository.repoId, checkout.checkoutId);
455
+ const branchResourceId = managedBranchOwnedResourceId(repository.repoId, checkout.checkoutId);
456
+ const existingWorkspaceResource = getOwnedResource(controllerHome, workspaceResourceId);
457
+ const existingBranchResource = getOwnedResource(controllerHome, branchResourceId);
458
+ if (existingWorkspaceResource?.cleanupCapable === false) workspaceOwnedByForge = false;
459
+ if (existingBranchResource?.cleanupCapable === false) branchOwnedByForge = false;
460
+ recordOwnedResource(controllerHome, {
461
+ resourceId: workspaceResourceId,
462
+ kind: 'worktree',
463
+ targetRef: selected.canonicalRoot,
464
+ cleanupCapable: workspaceOwnedByForge,
465
+ creator: 'forge:managed-workspace',
466
+ ownerForgeInstanceId,
467
+ associatedWorkId: input.associatedWorkId,
468
+ repoId: repository.repoId,
469
+ retentionIntent: 'temporary',
470
+ });
471
+ const branchLocator = managedBranchOwnedResourceLocator(repository.canonicalRoot, checkout.checkoutId, branch);
472
+ recordOwnedResource(controllerHome, {
473
+ resourceId: branchResourceId,
474
+ kind: 'git_branch',
475
+ targetRef: branch,
476
+ locator: branchLocator,
477
+ identityFingerprint: ownedResourceLocatorFingerprint(ownerForgeInstanceId, branchLocator),
478
+ cleanupCapable: branchOwnedByForge,
479
+ creator: 'forge:managed-workspace',
480
+ ownerForgeInstanceId,
481
+ associatedWorkId: input.associatedWorkId,
482
+ repoId: repository.repoId,
483
+ retentionIntent: 'retain_on_failure',
484
+ });
428
485
  if (!manifest) {
429
486
  writeJsonAtomic(statePath, {
430
487
  schemaVersion: 1,
@@ -153,7 +153,7 @@ export async function runCheckViaProcessRuntime(
153
153
  check,
154
154
  durable: {
155
155
  reason: 'multi_phase_or_release_check_requires_durable_workflow',
156
- suggestedOperation: 'Claim the related WorkContract and run this check through an external Controller.',
156
+ suggestedOperation: 'Run this check through a Work-bound durable verification workflow.',
157
157
  },
158
158
  durableSideEffects: emptyEffects,
159
159
  };