@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
@@ -1,4 +1,4 @@
1
- import { existsSync, readdirSync, rmSync, statSync } from 'fs';
1
+ import { existsSync, lstatSync, readdirSync, rmdirSync, rmSync, statSync } from 'fs';
2
2
  import { spawnSync } from 'child_process';
3
3
  import { join, relative, resolve } from 'path';
4
4
  import { repositoryControllerRoot } from '../../cli/repositories/controller-home';
@@ -37,6 +37,8 @@ export interface CleanupArtifactRetentionOptions {
37
37
  graceMs?: number;
38
38
  maxEntries?: number;
39
39
  maxRemovals?: number;
40
+ scanSequence?: number;
41
+ repositoryIds?: readonly string[];
40
42
  }
41
43
 
42
44
  export interface CleanupArtifactRetentionReport {
@@ -190,6 +192,48 @@ function retentionTimestamp(receipt: WorkCleanupReceipt, bundlePath: string): nu
190
192
  }
191
193
  }
192
194
 
195
+ function bundleProtectedRevision(repositoryRoot: string, bundlePath: string): string | undefined {
196
+ if (!git(repositoryRoot, ['bundle', 'verify', bundlePath]).ok) return undefined;
197
+ const heads = git(repositoryRoot, ['bundle', 'list-heads', bundlePath]);
198
+ if (!heads.ok) return undefined;
199
+ const revisions = [...new Set(heads.stdout.split('\n').map((line) => line.trim().split(/\s+/, 1)[0]).filter((value) => /^[a-f0-9]{40,64}$/i.test(value ?? '')))];
200
+ return revisions.length === 1 ? revisions[0] : undefined;
201
+ }
202
+
203
+ function proveOrphanBundleContained(
204
+ repositoryRoot: string,
205
+ bundlePath: string,
206
+ targetBranch: string,
207
+ ): { contained: boolean; reason: string } {
208
+ const protectedRevision = bundleProtectedRevision(repositoryRoot, bundlePath);
209
+ if (!protectedRevision) return { contained: false, reason: 'orphan_bundle_invalid_or_ambiguous' };
210
+ if (!revision(repositoryRoot, protectedRevision)) {
211
+ return { contained: false, reason: 'orphan_protected_revision_unavailable' };
212
+ }
213
+ const targetRevision = revision(repositoryRoot, `refs/heads/${targetBranch}`);
214
+ if (!targetRevision) return { contained: false, reason: 'orphan_target_revision_unavailable' };
215
+ const remoteRevision = revision(repositoryRoot, `refs/remotes/origin/${targetBranch}`);
216
+ if (!remoteRevision) return { contained: false, reason: 'orphan_remote_revision_unavailable' };
217
+ // Anchor the inferred delta against the durable remote target. Using the
218
+ // local target here could collapse the delta to empty when only the local
219
+ // branch contains the preserved tip, falsely treating an undelivered bundle
220
+ // as redundant.
221
+ const base = git(repositoryRoot, ['merge-base', protectedRevision, remoteRevision]);
222
+ if (!base.ok || !base.stdout) return { contained: false, reason: 'orphan_merge_base_unavailable' };
223
+ const paths = changedPaths(repositoryRoot, base.stdout, protectedRevision);
224
+ if (!paths) return { contained: false, reason: 'orphan_diff_unavailable' };
225
+ for (const path of paths) {
226
+ const protectedEntry = treeEntry(repositoryRoot, protectedRevision, path);
227
+ const targetEntry = treeEntry(repositoryRoot, targetRevision, path);
228
+ const remoteEntry = treeEntry(repositoryRoot, remoteRevision, path);
229
+ if (protectedEntry === undefined || targetEntry === undefined || remoteEntry === undefined
230
+ || protectedEntry !== targetEntry || protectedEntry !== remoteEntry) {
231
+ return { contained: false, reason: 'orphan_content_mismatch' };
232
+ }
233
+ }
234
+ return { contained: true, reason: paths.length === 0 ? 'orphan_no_source_delta' : 'orphan_target_and_remote_content_contained' };
235
+ }
236
+
193
237
  function retirementFromProof(
194
238
  proof: WorkPreservationContainmentProof,
195
239
  status: 'eligible' | 'removed' | 'not_needed',
@@ -232,27 +276,36 @@ export function cleanupWorkPreservationArtifacts(
232
276
  const repositoriesRoot = join(controllerHome, 'repositories');
233
277
  let repositoryIds: string[] = [];
234
278
  try {
279
+ const repositoryFilter = options.repositoryIds ? new Set(options.repositoryIds.map((id) => id.trim()).filter(Boolean)) : undefined;
235
280
  repositoryIds = readdirSync(repositoriesRoot, { withFileTypes: true })
236
- .filter((entry) => entry.isDirectory())
281
+ .filter((entry) => entry.isDirectory() && (!repositoryFilter || repositoryFilter.has(entry.name)))
237
282
  .map((entry) => entry.name)
238
283
  .sort();
239
284
  } catch {
240
285
  return report;
241
286
  }
287
+ const scanSequence = Math.trunc(options.scanSequence ?? Math.floor(nowMs / 60_000));
288
+ if (repositoryIds.length > 1) {
289
+ const offset = ((scanSequence % repositoryIds.length) + repositoryIds.length) % repositoryIds.length;
290
+ repositoryIds.push(...repositoryIds.splice(0, offset));
291
+ }
292
+ let handleInspected = 0;
293
+ let physicalInspected = 0;
242
294
 
243
- outer: for (const repositoryId of repositoryIds) {
295
+ for (const repositoryId of repositoryIds) {
244
296
  let repository;
245
297
  try {
246
298
  repository = getRepository(repositoryId, controllerHome, { includeRemoved: true });
247
299
  } catch {
248
300
  continue;
249
301
  }
250
- for (let handle of listWorkHandles(controllerHome, repositoryId)) {
251
- if (report.inspected >= maxEntries) {
252
- report.budgetExhausted = true;
253
- break outer;
254
- }
255
- report.inspected += 1;
302
+ const repositoryHandles = listWorkHandles(controllerHome, repositoryId)
303
+ .sort((left, right) => left.workId.localeCompare(right.workId));
304
+ if (repositoryHandles.length > 1) {
305
+ const offset = ((scanSequence % repositoryHandles.length) + repositoryHandles.length) % repositoryHandles.length;
306
+ repositoryHandles.push(...repositoryHandles.splice(0, offset));
307
+ }
308
+ for (let handle of repositoryHandles) {
256
309
  const receipt = handle.cleanupReceipt;
257
310
  const bundlePath = receipt?.preservation.bundlePath;
258
311
  const retirement = receipt?.preservation.bundleRetirement;
@@ -265,6 +318,12 @@ export function cleanupWorkPreservationArtifacts(
265
318
  }
266
319
  }
267
320
  if (!receipt || !bundlePath) continue;
321
+ if (handleInspected >= maxEntries) {
322
+ report.budgetExhausted = true;
323
+ break;
324
+ }
325
+ handleInspected += 1;
326
+ report.inspected += 1;
268
327
  const expectedPath = resolve(expectedBundlePath(controllerHome, handle));
269
328
  if (resolve(bundlePath) !== expectedPath) {
270
329
  report.retained += 1;
@@ -303,6 +362,7 @@ export function cleanupWorkPreservationArtifacts(
303
362
  handle = writeWorkHandle(controllerHome, { ...handle, cleanupReceipt: eligibleReceipt, updatedAt: nowIso });
304
363
  const measurement = existsSync(bundlePath) ? measureReclaimablePath(bundlePath) : { bytes: 0, entries: 0, complete: true };
305
364
  if (existsSync(bundlePath)) rmSync(bundlePath, { force: true });
365
+ try { rmdirSync(join(bundlePath, '..')); } catch { /* Preserve non-empty or concurrently changed directories. */ }
306
366
  if (measurement.complete) report.reclaimedBytes += measurement.bytes;
307
367
  else report.unknownReclaimedByteCount += 1;
308
368
  const currentReceipt = handle.cleanupReceipt!;
@@ -323,6 +383,149 @@ export function cleanupWorkPreservationArtifacts(
323
383
  report.errors.push(`${handle.workId}: ${error instanceof Error ? error.message : String(error)}`);
324
384
  }
325
385
  }
386
+
387
+ // WorkHandle metadata is not physical-retention authority. Discover direct
388
+ // preservation directories as well so a missing or drifted handle cannot
389
+ // make an owned bundle immortal. This pass remains fail-closed: only the
390
+ // canonical direct child containing exactly branch.bundle is considered;
391
+ // symlinks and extra content are retained, and source containment still
392
+ // requires proof.
393
+ const artifactRoot = join(repositoryControllerRoot(controllerHome, repositoryId), 'cleanup-artifacts');
394
+ let artifactEntries;
395
+ try {
396
+ if (!lstatSync(artifactRoot).isDirectory()) continue;
397
+ artifactEntries = readdirSync(artifactRoot, { withFileTypes: true })
398
+ .sort((left, right) => left.name.localeCompare(right.name));
399
+ if (artifactEntries.length > 1) {
400
+ const offset = ((scanSequence % artifactEntries.length) + artifactEntries.length) % artifactEntries.length;
401
+ artifactEntries.push(...artifactEntries.splice(0, offset));
402
+ }
403
+ } catch {
404
+ continue;
405
+ }
406
+ const physicalHandles = repositoryHandles;
407
+ const handles = new Map(physicalHandles.map((handle) => [handle.workId, handle]));
408
+ const referencedBundlePaths = new Set(physicalHandles.flatMap((handle) => {
409
+ const path = handle.cleanupReceipt?.preservation.bundlePath;
410
+ return path ? [resolve(path)] : [];
411
+ }));
412
+ for (const entry of artifactEntries) {
413
+ if (!entry.isDirectory()) continue;
414
+ const directoryPath = join(artifactRoot, entry.name);
415
+ const handle = handles.get(entry.name);
416
+ const canonicalBundlePath = resolve(join(directoryPath, 'branch.bundle'));
417
+ if (referencedBundlePaths.has(canonicalBundlePath)) continue;
418
+ if (physicalInspected >= maxEntries) {
419
+ report.budgetExhausted = true;
420
+ break;
421
+ }
422
+ physicalInspected += 1;
423
+ report.inspected += 1;
424
+ let contents: string[];
425
+ let bundlePath: string;
426
+ try {
427
+ contents = readdirSync(directoryPath).sort();
428
+ if (contents.length !== 1 || contents[0] !== 'branch.bundle') {
429
+ report.retained += 1;
430
+ skip(report, 'physical_bundle_unrecognized_content');
431
+ continue;
432
+ }
433
+ bundlePath = join(directoryPath, 'branch.bundle');
434
+ const bundleStat = lstatSync(bundlePath);
435
+ if (!bundleStat.isFile() || bundleStat.isSymbolicLink()) {
436
+ report.retained += 1;
437
+ skip(report, 'physical_bundle_unrecognized_content');
438
+ continue;
439
+ }
440
+ } catch (error) {
441
+ report.retained += 1;
442
+ skip(report, 'physical_bundle_unreadable');
443
+ report.errors.push(`${repositoryId}:${entry.name}: ${error instanceof Error ? error.message : String(error)}`);
444
+ continue;
445
+ }
446
+ if (handle?.cleanupReceipt?.preservation.bundlePath
447
+ && resolve(handle.cleanupReceipt.preservation.bundlePath) !== resolve(bundlePath)) {
448
+ report.retained += 1;
449
+ skip(report, 'bundle_metadata_conflict');
450
+ continue;
451
+ }
452
+ let timestamp: number | undefined;
453
+ try {
454
+ timestamp = handle?.cleanupReceipt
455
+ ? retentionTimestamp(handle.cleanupReceipt, bundlePath)
456
+ : statSync(bundlePath).mtimeMs;
457
+ } catch (error) {
458
+ report.retained += 1;
459
+ skip(report, 'physical_bundle_unreadable');
460
+ report.errors.push(`${repositoryId}:${entry.name}: ${error instanceof Error ? error.message : String(error)}`);
461
+ continue;
462
+ }
463
+ if (timestamp === undefined || nowMs - timestamp < graceMs) {
464
+ report.retained += 1;
465
+ skip(report, 'retention_grace');
466
+ continue;
467
+ }
468
+
469
+ const receipt = handle?.cleanupReceipt;
470
+ const handleProof = handle && receipt?.complete
471
+ ? proveWorkPreservationContained(repository.canonicalRoot, handle, receipt.targetBranch)
472
+ : undefined;
473
+ const orphanProof = !handle && repository.defaultBranch
474
+ ? proveOrphanBundleContained(repository.canonicalRoot, bundlePath, repository.defaultBranch)
475
+ : undefined;
476
+ if (handle && (!receipt?.complete || !handleProof?.contained)) {
477
+ report.retained += 1;
478
+ skip(report, !receipt?.complete ? 'physical_bundle_incomplete_handle' : `containment_${handleProof!.reason}`);
479
+ continue;
480
+ }
481
+ if (!handle && !orphanProof?.contained) {
482
+ report.retained += 1;
483
+ skip(report, orphanProof?.reason ?? (repository.defaultBranch
484
+ ? 'orphan_source_containment_unproven'
485
+ : 'orphan_target_branch_unavailable'));
486
+ continue;
487
+ }
488
+ report.eligible += 1;
489
+ if (remaining <= 0) {
490
+ report.budgetExhausted = true;
491
+ skip(report, 'cleanup_budget_exhausted');
492
+ continue;
493
+ }
494
+ report.attempted += 1;
495
+ remaining -= 1;
496
+ try {
497
+ const measurement = measureReclaimablePath(bundlePath);
498
+ if (handle && receipt && handleProof) {
499
+ const eligibleReceipt: WorkCleanupReceipt = {
500
+ ...receipt,
501
+ preservation: { ...receipt.preservation, bundlePath, bundleRetirement: retirementFromProof(handleProof, 'eligible', nowIso) },
502
+ updatedAt: nowIso,
503
+ };
504
+ const updated = writeWorkHandle(controllerHome, { ...handle, cleanupReceipt: eligibleReceipt, updatedAt: nowIso });
505
+ rmSync(bundlePath, { force: true });
506
+ const removedReceipt: WorkCleanupReceipt = {
507
+ ...updated.cleanupReceipt!,
508
+ preservation: {
509
+ ...updated.cleanupReceipt!.preservation,
510
+ bundlePath: undefined,
511
+ bundleSha256: undefined,
512
+ bundleRetirement: retirementFromProof(handleProof, 'removed', nowIso),
513
+ recoveryInstructions: `Preservation bundle retired after ${handleProof.reason}; exact proof is stored in cleanupReceipt.preservation.bundleRetirement.`,
514
+ },
515
+ updatedAt: nowIso,
516
+ };
517
+ writeWorkHandle(controllerHome, { ...updated, cleanupReceipt: removedReceipt, updatedAt: nowIso });
518
+ } else {
519
+ rmSync(bundlePath, { force: true });
520
+ }
521
+ try { rmdirSync(directoryPath); } catch { /* Preserve concurrently populated directories. */ }
522
+ if (measurement.complete) report.reclaimedBytes += measurement.bytes;
523
+ else report.unknownReclaimedByteCount += 1;
524
+ report.removedPaths.push(relative(controllerHome, bundlePath).replace(/\\/g, '/'));
525
+ } catch (error) {
526
+ report.errors.push(`${repositoryId}:${entry.name}: ${error instanceof Error ? error.message : String(error)}`);
527
+ }
528
+ }
326
529
  }
327
530
  return report;
328
531
  }
@@ -2,7 +2,7 @@ import {
2
2
  buildWorkExecutionConcurrencyContract,
3
3
  evaluateWorkExecutionCompatibility,
4
4
  getWorkContract,
5
- isTerminalWorkContractStatus,
5
+ semanticWorkState,
6
6
  readActiveWorkCandidates,
7
7
  workExecutionLaneMutates,
8
8
  updateWorkContract,
@@ -16,7 +16,6 @@ import type { ResourceClaimSpec } from '../../execution/jobs/types';
16
16
  import { listProcessRecords } from '../../execution/process-runtime/store';
17
17
  import { listActiveLeases } from '../../resources/leases/store';
18
18
  import { claimsConflict } from '../../resources/claims/conflicts';
19
- import { getControllerSession } from '../../../../packages/kernel/controller/api/index';
20
19
  import {
21
20
  isManagedProcessActive,
22
21
  type ExecutionConcurrencyWaitProjection,
@@ -101,34 +100,6 @@ export interface RuntimeWorkCompatibilityDecision {
101
100
  wait?: ExecutionConcurrencyWaitProjection;
102
101
  }
103
102
 
104
- function invalidActiveWorkCompatibility(
105
- candidate: WorkExecutionConcurrencyContract,
106
- invalid: ReturnType<typeof readActiveWorkCandidates>['invalid'],
107
- ): ExecutionConcurrencyWaitProjection | undefined {
108
- if (!workExecutionLaneMutates(candidate.lane)) return undefined;
109
- for (const current of invalid) {
110
- if (current.workId === candidate.workId) continue;
111
- const currentScope = new Set(current.semanticScopeKeys);
112
- const overlap = candidate.semanticScopeKeys.filter((key) => currentScope.has(key));
113
- if (overlap.length === 0
114
- && candidate.lane === 'isolated_write'
115
- && candidate.isolation === 'isolated'
116
- && current.isolation === 'isolated') continue;
117
- return executionConcurrencyWaitProjection({
118
- source: 'work_compatibility',
119
- blockerCode: 'work_contract_invalid',
120
- disposition: 'invalid',
121
- blockingWorkId: current.workId,
122
- semanticScopeKeys: overlap.length > 0 ? overlap : current.semanticScopeKeys,
123
- resourceKeys: candidate.resourceIntents
124
- .filter((intent) => intent.mode !== 'read')
125
- .map((intent) => intent.resourceKey),
126
- wakeTrigger: { kind: 'work_contract_change', workId: current.workId },
127
- });
128
- }
129
- return undefined;
130
- }
131
-
132
103
  function contractForProcess(
133
104
  controllerHome: string,
134
105
  repoId: string,
@@ -181,18 +152,12 @@ export function evaluateManagedProcessWorkCompatibility(input: {
181
152
  .filter((record) => record.processId !== input.processId && record.workId && isManagedProcessActive(record))
182
153
  .map((record) => contractForProcess(input.controllerHome, input.repoId, record))
183
154
  .filter((value): value is WorkExecutionConcurrencyContract => Boolean(value));
184
- const worksWithActiveProcesses = new Set(activeProcessContracts.map((contract) => contract.workId));
185
- const activeSnapshot = readActiveWorkCandidates({
186
- controllerHome: input.controllerHome,
187
- repoId: input.repoId,
188
- limit: 1_000,
189
- });
190
- const invalidWait = invalidActiveWorkCompatibility(candidate, activeSnapshot.invalid);
191
- if (invalidWait) return { compatible: false, hardBlocked: true, wait: invalidWait };
192
- const activeWorkContracts = activeSnapshot.contracts
193
- .filter((activeWork) => activeWork.workId !== input.workId && !worksWithActiveProcesses.has(activeWork.workId))
194
- .map((activeWork) => buildWorkExecutionConcurrencyContract(activeWork));
195
- const decision = evaluateWorkExecutionCompatibility(candidate, [...activeProcessContracts, ...activeWorkContracts]);
155
+ // Thin Work is semantic context, not a physical execution lease. An open Work
156
+ // with no running Process must not reserve the shared checkout or make another
157
+ // Process wait for semantic terminalization. Concrete Process contracts own
158
+ // in-flight compatibility here; durable WorkHandle and resource-lease fences
159
+ // separately protect repository mutation across Process lifetimes.
160
+ const decision = evaluateWorkExecutionCompatibility(candidate, activeProcessContracts);
196
161
  const blocker = decision.blockers[0];
197
162
  return blocker
198
163
  ? { compatible: false, hardBlocked: blocker.disposition === 'invalid', wait: blockerProjection(blocker) }
@@ -225,7 +190,7 @@ export function recordWorkExecutionConcurrencyWait(input: {
225
190
  const workId = input.workId?.trim();
226
191
  if (!workId) return undefined;
227
192
  const work = getWorkContract({ controllerHome: input.controllerHome, repoId: input.repoId }, workId);
228
- if (!work || isTerminalWorkContractStatus(work.status)) return undefined;
193
+ if (!work || semanticWorkState(work) !== 'open') return undefined;
229
194
  const projection: WorkExecutionConcurrencyProjection = {
230
195
  schemaVersion: 1,
231
196
  status: input.wait.disposition === 'invalid' ? 'invalid' : 'waiting',
@@ -275,10 +240,7 @@ function workConcurrencyWakeResolved(input: {
275
240
  if (input.projection.status === 'invalid' || wake.kind === 'work_contract_change' || wake.kind === 'scheduler_capacity') return false;
276
241
  if (wake.kind === 'work_terminal') {
277
242
  const blocking = getWorkContract({ controllerHome: input.controllerHome, repoId: input.repoId }, wake.workId);
278
- return Boolean(blocking && isTerminalWorkContractStatus(blocking.status));
279
- }
280
- if (wake.kind === 'controller_release') {
281
- return !getControllerSession({ controllerHome: input.controllerHome, repoId: input.repoId }, wake.workId);
243
+ return Boolean(blocking && semanticWorkState(blocking) !== 'open');
282
244
  }
283
245
  const activeLeases = listActiveLeases(input.controllerHome, input.projection.leaseRepoId?.trim() || input.repoId);
284
246
  return !input.projection.resourceIntents.some((claim) => activeLeases.some((lease) => claimsConflict(claim, lease)));
@@ -12,7 +12,7 @@ import {
12
12
  type ControllerTerminalizationAuthority,
13
13
  type ControllerType,
14
14
  } from '../../../../packages/kernel/controller/api/index';
15
- import { getWorkContract } from '../../../../packages/kernel/work/api/index';
15
+ import { getWorkContract, semanticWorkState } from '../../../../packages/kernel/work/api/index';
16
16
  import { currentPermissionSnapshotVersion } from './validation';
17
17
  import { startExecutionSession, updateExecutionSession } from './session-store';
18
18
 
@@ -55,8 +55,8 @@ export function recoverDirectControllerAuthority(input: {
55
55
  const store = { controllerHome: input.controllerHome, repoId: input.repoId };
56
56
  const work = getWorkContract(store, workId);
57
57
  if (!work) throw new Error(`WORK_NOT_FOUND: ${workId}`);
58
- if (['completed', 'failed', 'cancelled'].includes(work.status)) {
59
- throw new Error(`WORK_CONTROLLER_AUTHORITY_RECOVERY_TERMINAL: ${workId}:${work.status}`);
58
+ if (semanticWorkState(work) !== 'open') {
59
+ throw new Error(`WORK_CONTROLLER_AUTHORITY_RECOVERY_TERMINAL: ${workId}:${semanticWorkState(work)}`);
60
60
  }
61
61
  if (getControllerRoundRelay(store, workId)) {
62
62
  throw new Error(`WORK_CONTROLLER_AUTHORITY_RECOVERY_RELAY_CONFLICT: ${workId}; relay-bound Work must recover through its exact per-round authority.`);
@@ -130,6 +130,7 @@ export function recoverControllerAuthority(input: {
130
130
  repositoryActiveCheckoutId?: string;
131
131
  workId: string;
132
132
  requestedBy?: string;
133
+ recoveryReason?: string;
133
134
  identity: DirectControllerAuthorityRecoveryIdentity;
134
135
  runtime: { running?: boolean; runtimeInstanceId?: string };
135
136
  leaseMs?: number;
@@ -142,6 +143,8 @@ export function recoverControllerAuthority(input: {
142
143
  const relay = recoverControllerRoundRelayAuthority(store, {
143
144
  workId: input.workId,
144
145
  requestedBy: input.requestedBy,
146
+ recoveryReason: input.recoveryReason,
147
+ allowCanonicalRuntimeMigration: true,
145
148
  identity: {
146
149
  controllerId: input.identity.controllerId,
147
150
  controllerType: input.identity.controllerType,
@@ -258,6 +261,31 @@ function assertControllerLifecycleInvocationAuthority(
258
261
  return undefined;
259
262
  }
260
263
 
264
+ /**
265
+ * Modern sessionless MCP replaces its request binding on every call. For a
266
+ * direct Work, omission of an opaque capability is not a semantic ownership
267
+ * change when the exact Work, authenticated controller/principal/type and live
268
+ * canonical Runtime still agree. Relay-bound rounds remain capability-scoped,
269
+ * and an explicitly supplied wrong capability never uses this path.
270
+ */
271
+ export function controllerInvocationCanMechanicallyRebindDirectOwnership(
272
+ input: ControllerInvocationAuthorityContext & { runtime: { running?: boolean; runtimeInstanceId?: string } },
273
+ owner: ControllerSession | undefined = getControllerSession(
274
+ { controllerHome: input.controllerHome, repoId: input.repoId },
275
+ input.workId.trim(),
276
+ ),
277
+ ): boolean {
278
+ const workId = input.workId.trim();
279
+ const store = { controllerHome: input.controllerHome, repoId: input.repoId };
280
+ if (!owner || getControllerRoundRelay(store, workId)) return false;
281
+ if (input.identity.transportSessionId || input.identity.controllerAuthorityId) return false;
282
+ if (owner.controllerId !== input.identity.controllerId || owner.controllerType !== input.identity.controllerType) return false;
283
+ if (controllerSessionPrincipalId(owner) !== input.identity.principalId) return false;
284
+ const requestedInstanceId = input.identity.controllerInstanceId.trim();
285
+ const canonicalRuntimeInstanceId = input.runtime.running ? input.runtime.runtimeInstanceId?.trim() || '' : '';
286
+ return Boolean(requestedInstanceId && canonicalRuntimeInstanceId === requestedInstanceId);
287
+ }
288
+
261
289
  /** Bind one exact Work owner to the current transport/runtime without changing semantic ownership. */
262
290
  export function bindControllerOwnershipForInvocation(input: ControllerInvocationAuthorityContext & {
263
291
  runtime: { running?: boolean; runtimeInstanceId?: string };
@@ -267,7 +295,9 @@ export function bindControllerOwnershipForInvocation(input: ControllerInvocation
267
295
  const store = { controllerHome: input.controllerHome, repoId: input.repoId };
268
296
  const workId = input.workId.trim();
269
297
  const existingOwner = getControllerSession(store, workId);
270
- assertControllerLifecycleInvocationAuthority(input, existingOwner);
298
+ if (!controllerInvocationCanMechanicallyRebindDirectOwnership(input, existingOwner)) {
299
+ assertControllerLifecycleInvocationAuthority(input, existingOwner);
300
+ }
271
301
  return bindControllerSessionToCurrentRuntime(store, {
272
302
  workId,
273
303
  controllerId: input.identity.controllerId,
@@ -4,13 +4,13 @@ import {
4
4
  recordEditSessionProcessCheckReceipts,
5
5
  type EditSession,
6
6
  } from '../../../cli/editing/edit-session';
7
- import { listControllerChecks } from '../../../cli/controller/check-runner';
7
+ import { listControllerChecks, readLatestControllerCheckEvidence } from '../../../cli/controller/check-runner';
8
8
  import type { RepositoryRecord } from '../../../cli/repositories/types';
9
9
  import { repositoryGitStatus } from '../../../cli/repositories/structured-git';
10
10
  import { selectRepositoryCheckout } from '../../../cli/repositories/registry';
11
11
  import { executionIdentityForRepository } from './execution-identity';
12
12
  import { getWorkContract } from '../../../../packages/kernel/work/api/index';
13
- import { verifyGoalWorkloop } from '../facade/goal-workloop';
13
+ import { recordWorkCheckEvidence } from './work-check-evidence';
14
14
  import { commandFingerprint, verificationInputFingerprint, workspaceValidationFingerprint } from './verification-evidence';
15
15
  import {
16
16
  claimsForCheck,
@@ -20,6 +20,7 @@ import {
20
20
  runPersistedCheckViaProcessRuntime,
21
21
  type ProcessCheckCompletionReceipt,
22
22
  } from '../../execution/process-runtime';
23
+ import { projectTerminalCheckVerification } from '../../execution/process-runtime/check-result';
23
24
  import { resourceClaimsConflict } from '../../resources/claims/conflicts';
24
25
  import {
25
26
  mutateControlPlaneRecord,
@@ -451,10 +452,10 @@ export async function reconcileEditValidationRun(
451
452
  issueId: session.issueId,
452
453
  taskId: session.taskId,
453
454
  },
454
- verificationSnapshot: work ? {
455
- workId: work.workId,
456
- allowedPaths: work.allowedPaths,
457
- forbiddenPaths: work.forbiddenPaths,
455
+ verificationSnapshot: session.workId ? {
456
+ workId: session.workId,
457
+ allowedPaths: session.allowedPaths,
458
+ forbiddenPaths: [],
458
459
  } : undefined,
459
460
  leaseWaitMs: run.leaseWaitMs,
460
461
  });
@@ -492,36 +493,44 @@ export async function reconcileEditValidationRun(
492
493
 
493
494
  try {
494
495
  const receipts = run.checkIds.map((checkId) => receiptFor(controllerHome, validationRepository, run, checkId));
496
+ const projections = receipts.map((receipt, index) => {
497
+ const record = records[index]!;
498
+ const legacyEvidence = record.origin?.checkResultReceiptPath
499
+ ? undefined
500
+ : readLatestControllerCheckEvidence(validationRepository.canonicalRoot, receipt.checkId);
501
+ return projectTerminalCheckVerification(record, receipt.checkId, receipt, { legacyEvidence });
502
+ });
503
+ const infrastructureProjectionIndex = projections.findIndex((projection) => projection.outcome === 'infrastructure_failure');
504
+ if (infrastructureProjectionIndex >= 0) {
505
+ const receipt = receipts[infrastructureProjectionIndex]!;
506
+ const projection = projections[infrastructureProjectionIndex]!;
507
+ throw new Error(`EDIT_VALIDATION_CHECK_INFRASTRUCTURE_FAILURE: ${receipt.checkId}: ${projection.infrastructureReason ?? projection.evidence.warning ?? receipt.summary}`);
508
+ }
495
509
  if (session.workId && work) {
496
510
  if (!run.sourceRevision || !run.workspaceFingerprint) throw new Error('EDIT_VALIDATION_WORK_IDENTITY_REQUIRED');
497
511
  const availableChecks = listControllerChecks(validationRepository.canonicalRoot);
498
- const workloopCtx = {
499
- workStore: { controllerHome, repoId: validationRepository.repoId },
500
- handoffStore: { controllerHome, repoId: validationRepository.repoId },
501
- repoId: validationRepository.repoId,
502
- availableChecks,
503
- sourceRevision: run.sourceRevision,
504
- workspaceFingerprint: run.workspaceFingerprint,
505
- };
506
- for (const receipt of receipts) {
512
+ const workStore = { controllerHome, repoId: validationRepository.repoId };
513
+ for (const [index, receipt] of receipts.entries()) {
507
514
  const checkId = receipt.checkId;
515
+ const projection = projections[index]!;
508
516
  const inputFingerprint = verificationInputFingerprint({
509
517
  sourceRevision: run.sourceRevision,
510
518
  workspaceFingerprint: run.workspaceFingerprint,
511
519
  checkId,
512
520
  requestedChecks: work.checks.length ? work.checks : run.checkIds,
513
521
  });
514
- const infrastructureFailed = receipt.timedOut || receipt.cancelled || (!receipt.ok && receipt.status !== 'failed');
515
- verifyGoalWorkloop(workloopCtx, {
522
+ recordWorkCheckEvidence({
523
+ store: workStore,
516
524
  workId: session.workId,
517
525
  checkId,
526
+ availableChecks,
518
527
  sourceRevision: run.sourceRevision,
519
528
  workspaceFingerprint: run.workspaceFingerprint,
520
529
  verificationInputFingerprint: inputFingerprint,
521
530
  commandFingerprint: commandFingerprint(checkId, receipt.commandId),
522
531
  receipt,
523
- infrastructureFailed,
524
- checkFailed: !receipt.ok && !infrastructureFailed,
532
+ infrastructureFailed: projection.isInfrastructureIssue,
533
+ checkFailed: projection.isAcceptanceFailure,
525
534
  });
526
535
  }
527
536
  }
@@ -535,7 +544,7 @@ export async function reconcileEditValidationRun(
535
544
  run = saveRun(controllerHome, {
536
545
  ...run,
537
546
  status: 'completed',
538
- ok: receipts.every((receipt) => receipt.ok),
547
+ ok: projections.every((projection) => projection.outcome === 'valid_pass'),
539
548
  receipts,
540
549
  error: undefined,
541
550
  });
@@ -25,12 +25,15 @@ export type ExecutionIdentityErrorCode =
25
25
  | 'REPOSITORY_NOT_EXECUTABLE'
26
26
  | 'LEGACY_WORK_IDENTITY_AMBIGUOUS'
27
27
  | 'LEGACY_WORK_IDENTITY_REJECTED'
28
- | 'WORKSPACE_SCOPE_MISMATCH';
28
+ | 'WORKSPACE_SCOPE_MISMATCH'
29
+ | 'FORGE_INSTANCE_SCOPE_MISMATCH';
29
30
 
30
31
  export interface ResolvedExecutionIdentity {
31
32
  readonly schemaVersion: 1;
32
- readonly authority?: 'repository' | 'ephemeral_workspace';
33
+ readonly authority?: 'repository' | 'ephemeral_workspace' | 'forge_instance';
34
+ /** Legacy storage coordinate. For forge_instance authority this is the reserved Process scope key `instance`, not a Repository id. */
33
35
  readonly repositoryId: string;
36
+ /** Legacy storage coordinate. For forge_instance authority this is `instance`, not a checkout. */
34
37
  readonly checkoutId: string;
35
38
  readonly canonicalRoot: string;
36
39
  readonly workId?: string;
@@ -253,7 +256,7 @@ export function executionIdentityFromCoordinates(input: {
253
256
  branch?: string;
254
257
  expectedHead?: string;
255
258
  allowArchived?: boolean;
256
- authority?: 'repository' | 'ephemeral_workspace';
259
+ authority?: 'repository' | 'ephemeral_workspace' | 'forge_instance';
257
260
  }): ResolvedExecutionIdentity {
258
261
  if (!input.repositoryId.trim() || !input.checkoutId.trim() || !input.canonicalRoot.trim()) {
259
262
  fail('EXECUTION_IDENTITY_REQUIRED', 'repositoryId, checkoutId, and canonicalRoot are required');
@@ -331,6 +334,23 @@ export function assertExecutionIdentity(input: {
331
334
  });
332
335
  }
333
336
 
337
+ if (identity.authority === 'forge_instance') {
338
+ const instanceRoot = realpathOrFail(identity.canonicalRoot, 'WORKTREE_MISSING', 'ForgeInstance execution root');
339
+ const requestedCwd = isAbsolute(input.cwd) ? input.cwd : resolve(instanceRoot, input.cwd);
340
+ const resolvedCwd = realpathOrFail(requestedCwd, 'WORKTREE_MISSING', 'process cwd');
341
+ if (!statSync(resolvedCwd).isDirectory()) {
342
+ fail('FORGE_INSTANCE_SCOPE_MISMATCH', 'process cwd is not a directory', { actual: resolvedCwd });
343
+ }
344
+ const cwdRelative = relative(instanceRoot, resolvedCwd);
345
+ if (cwdRelative === '..' || cwdRelative.startsWith('../') || cwdRelative.startsWith('..\\')) {
346
+ fail('FORGE_INSTANCE_SCOPE_MISMATCH', 'process cwd escapes ForgeInstance execution root', {
347
+ expected: instanceRoot,
348
+ actual: resolvedCwd,
349
+ });
350
+ }
351
+ return Object.freeze({ ...identity, canonicalRoot: instanceRoot, resolvedCwd });
352
+ }
353
+
334
354
  if (identity.authority === 'ephemeral_workspace') {
335
355
  const workspaceRoot = realpathOrFail(identity.canonicalRoot, 'WORKTREE_MISSING', 'ephemeral workspace root');
336
356
  const requestedCwd = isAbsolute(input.cwd) ? input.cwd : resolve(workspaceRoot, input.cwd);