@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
@@ -8,12 +8,12 @@ import { ensureRepositoryRuntimeStorage, type RepositoryRuntimeStorageReport } f
8
8
  import { getRepository, selectRepositoryCheckout, setRepositoryCheckoutLifecycle } from '../../cli/repositories/registry';
9
9
  import type { RepositoryRecord } from '../../cli/repositories/types';
10
10
  import { rebuildRepositoryProjection } from '../projections/materialized-view';
11
- import { cancelWorkContract, getWorkContract, readWorkContractStore, updateWorkContract } from '../../../packages/kernel/work/api/index';
12
- import { getControllerSession, listControllerSessions, withControllerSessionTerminalizationFence } from '../../../packages/kernel/controller/api/index';
11
+ import { getWorkContract, readWorkContractStore, semanticWorkState, updateWorkContract } from '../../../packages/kernel/work/api/index';
12
+ import { assertOwnedResourceCleanupTarget, listOwnedResources, markOwnedResourceCleaned, type OwnedResource } from '../../../packages/kernel/identity/api/index';
13
13
  import { listPlanContracts } from '../control-plane/facade/plan-contract-store';
14
14
  import { readRequirement } from '../control-plane/persistence/requirement-store';
15
15
  import { listControlPlaneRecords, type ControlPlaneRecord } from '../control-plane/persistence/sqlite-store';
16
- import { isTerminalWorkContractStatus, type WorkContract } from '../control-plane/facade/types';
16
+ import type { WorkContract } from '../control-plane/facade/types';
17
17
  import { listSchedules } from '../../../packages/kernel/scheduler/api/index';
18
18
  import {
19
19
  collectRuntimeProcesses,
@@ -29,6 +29,8 @@ import {
29
29
  } from './local-jobs-repair';
30
30
  import { gcTerminalProcesses, type ProcessGcResult } from '../execution/process-runtime/gc';
31
31
  import { cleanupStaleWorkVerificationSnapshots, type WorkVerificationSnapshotRetentionReport } from '../control-plane/execution/work-verification-snapshot';
32
+ import { cleanupGeneratedRepositoryCheckoutCaches, type RepositoryGeneratedCacheRetentionReport } from '../control-plane/generated-cache-retention';
33
+ import { cleanupWorkPreservationArtifacts, type CleanupArtifactRetentionReport } from '../control-plane/cleanup-artifact-retention';
32
34
  import {
33
35
  cleanupRuntimeQuarantine,
34
36
  quarantineRuntimePath,
@@ -38,6 +40,7 @@ import {
38
40
  import { readWorkHandle, resolveWorkDeliveryTargetBranch } from '../control-plane/execution/work-handle-store';
39
41
  import { listRecoverableProcessRecords } from '../execution/process-runtime/store';
40
42
  import { isManagedProcessActive } from '../execution/process-runtime/types';
43
+ import { managedBranchOwnedResourceId, managedBranchOwnedResourceLocator, managedWorkspaceOwnedResourceId } from '../execution/managed-workspace';
41
44
 
42
45
  export type RuntimeMaintenanceActionId =
43
46
  | 'local_jobs_reconcile'
@@ -151,6 +154,8 @@ export interface RuntimeMaintenanceApplyResult extends Omit<RuntimeMaintenanceSt
151
154
  processGc?: ProcessGcResult;
152
155
  verificationSnapshotGc?: WorkVerificationSnapshotRetentionReport;
153
156
  quarantineGc?: RuntimeQuarantineRetentionReport;
157
+ generatedCacheGc?: RepositoryGeneratedCacheRetentionReport;
158
+ cleanupArtifactGc?: CleanupArtifactRetentionReport;
154
159
  projection?: unknown;
155
160
  }
156
161
 
@@ -405,7 +410,11 @@ function scanRuntimeTempCandidates(
405
410
  minAgeMinutes: RUNTIME_TEMP_RETENTION_MINUTES,
406
411
  maxEntries: Math.max(maxCandidates * 3, 500),
407
412
  });
408
- const ownershipStatus = explicitRoots ? 'explicit' as const : 'unknown' as const;
413
+ // scanRuntimeTempEntries is the mechanical temp-ownership fence: it only
414
+ // returns direct Forge-prefixed children of the approved temp roots, and
415
+ // removeRuntimeTempEntry revalidates root/name/symlink/age/process occupancy
416
+ // immediately before mutation. Explicit roots narrow scope; they do not create
417
+ // a second ownership authority.
409
418
  return scan.entries
410
419
  .filter((entry) => entry.cleanupCandidate)
411
420
  .slice(0, maxCandidates)
@@ -413,17 +422,15 @@ function scanRuntimeTempCandidates(
413
422
  kind: 'stale_runtime_temp_entry',
414
423
  id: `runtime-temp-${safeId(entry.path)}`,
415
424
  path: entry.path,
416
- safe: entry.cleanupCandidate && ownershipStatus === 'explicit',
417
- reason: ownershipStatus === 'unknown'
418
- ? 'Temp entry has unknown durable ownership; it is protected until an owning lifecycle record or safe reconciliation proves eligibility.'
419
- : entry.symbolicLink
420
- ? 'Symbolic links are never eligible for automatic cleanup.'
421
- : entry.occupiedByPid
422
- ? `Temp entry is referenced by live process ${entry.occupiedByPid}.`
423
- : `Forge temp entry is unoccupied, not a symbolic link, and ${entry.ageMinutes} minute(s) old.`,
425
+ safe: entry.cleanupCandidate,
426
+ reason: entry.symbolicLink
427
+ ? 'Symbolic links are never eligible for automatic cleanup.'
428
+ : entry.occupiedByPid
429
+ ? `Temp entry is referenced by live process ${entry.occupiedByPid}.`
430
+ : `Forge temp entry is unoccupied, not a symbolic link, and ${entry.ageMinutes} minute(s) old.`,
424
431
  ageMinutes: entry.ageMinutes,
425
432
  suggestedAction: 'full_maintenance_pass',
426
- ownershipStatus,
433
+ ownershipStatus: 'explicit' as const,
427
434
  }));
428
435
  }
429
436
 
@@ -464,7 +471,7 @@ function listRetainedMigratedWork(destinationHomeInput: string, repoId: string):
464
471
  }
465
472
  for (const source of sourceWork) {
466
473
  const work = source.value;
467
- if (work.repoId !== repoId || !isTerminalWorkContractStatus(work.status)) continue;
474
+ if (work.repoId !== repoId || semanticWorkState(work) === 'open') continue;
468
475
  if (!work.worktreeRef?.trim() || !existsSync(work.worktreeRef)) continue;
469
476
  if (destinationWorkIds.has(work.workId)) continue;
470
477
  retained.push({ migrationId: migration.migrationId, sourceHome, work });
@@ -497,6 +504,121 @@ function looksLikeForgeManagedWorktree(path: string): boolean {
497
504
 
498
505
  type StaleWorkSourceState = NonNullable<RuntimeMaintenanceCandidate['sourceState']>;
499
506
 
507
+ interface OwnedManagedWorktreeInspection {
508
+ safe: boolean;
509
+ path: string;
510
+ sourceState: StaleWorkSourceState;
511
+ detail: string;
512
+ workspaceResourceId?: string;
513
+ checkoutId?: string;
514
+ branch?: string;
515
+ canonicalBranch?: string;
516
+ }
517
+
518
+ function activeOwnedWorktreeResource(
519
+ controllerHome: string,
520
+ repoId: string,
521
+ path: string,
522
+ ): OwnedResource | undefined {
523
+ const target = resolve(path);
524
+ return listOwnedResources(controllerHome, { kind: 'worktree', status: 'active', repoId })
525
+ .find((resource) => resource.cleanupCapable === true && resolve(resource.targetRef) === target);
526
+ }
527
+
528
+ function inspectOwnedManagedWorktree(
529
+ repository: RuntimeMaintenanceRepository,
530
+ controllerHome: string,
531
+ pathInput: string,
532
+ ): OwnedManagedWorktreeInspection {
533
+ const path = resolve(pathInput);
534
+ if (!looksLikeForgeManagedWorktree(path)) {
535
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: 'Managed worktree is outside Forge-managed storage; automatic cleanup is forbidden.' };
536
+ }
537
+ if (!existsSync(path)) {
538
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: 'Owned managed worktree path is absent; registry/resource reconciliation is required before cleanup.' };
539
+ }
540
+ const workspaceResource = activeOwnedWorktreeResource(controllerHome, repository.repoId, path);
541
+ if (!workspaceResource) {
542
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: 'No active cleanup-capable OwnedResource matches this managed worktree path.' };
543
+ }
544
+ try {
545
+ assertOwnedResourceCleanupTarget(controllerHome, {
546
+ resourceId: workspaceResource.resourceId,
547
+ kind: 'worktree',
548
+ targetRef: path,
549
+ });
550
+ } catch (error) {
551
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: `Managed worktree OwnedResource failed exact identity validation: ${error instanceof Error ? error.message : String(error)}` };
552
+ }
553
+ const contractOwner = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts.find((contract) =>
554
+ contract.worktreeRef && resolve(contract.worktreeRef) === path);
555
+ if (contractOwner) {
556
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: `Managed worktree still has WorkContract owner ${contractOwner.workId}; cleanup must remain with that Work lifecycle.` };
557
+ }
558
+ let registered;
559
+ try { registered = getRepository(repository.repoId, controllerHome, { includeRemoved: true }); } catch (error) {
560
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: `Repository registry could not be read for owned managed worktree: ${error instanceof Error ? error.message : String(error)}` };
561
+ }
562
+ const checkout = registered.checkouts.find((candidate) =>
563
+ candidate.worktree === true
564
+ && (candidate.lifecycle ?? 'active') === 'active'
565
+ && (resolve(candidate.canonicalRoot) === path || resolve(candidate.localRoot) === path));
566
+ if (!checkout) {
567
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: 'Owned managed worktree has no active matching Repository Registry checkout.' };
568
+ }
569
+ if (registered.activeCheckoutId === checkout.checkoutId) {
570
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: 'Owned managed worktree is unexpectedly the canonical active checkout; automatic cleanup is forbidden.' };
571
+ }
572
+ const status = runProcess('git', ['status', '--porcelain', '--untracked-files=all'], { cwd: path, timeoutMs: 10_000, maxOutputBytes: 500_000 });
573
+ if (!status.ok) {
574
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: 'Owned managed worktree status could not be read.' };
575
+ }
576
+ if (status.stdout.trim()) {
577
+ return { safe: false, path, sourceState: 'dirty_worktree', detail: 'Owned managed worktree is dirty; unique source must be preserved.' };
578
+ }
579
+ const head = runProcess('git', ['rev-parse', 'HEAD'], { cwd: path, timeoutMs: 10_000, maxOutputBytes: 100_000 });
580
+ const branch = runProcess('git', ['branch', '--show-current'], { cwd: path, timeoutMs: 10_000, maxOutputBytes: 100_000 });
581
+ if (!head.ok || !head.stdout.trim() || !branch.ok || !branch.stdout.trim()) {
582
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: 'Owned managed worktree HEAD/branch identity could not be resolved.' };
583
+ }
584
+ const branchName = branch.stdout.trim();
585
+ if (checkout.branch?.trim() && checkout.branch.trim() !== branchName) {
586
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: 'Owned managed worktree branch no longer matches its Repository Registry checkout identity.' };
587
+ }
588
+ let canonical;
589
+ try { canonical = selectRepositoryCheckout(registered, registered.activeCheckoutId); } catch (error) {
590
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: `Canonical repository checkout could not be resolved: ${error instanceof Error ? error.message : String(error)}` };
591
+ }
592
+ const canonicalBranch = runProcess('git', ['branch', '--show-current'], { cwd: canonical.canonicalRoot, timeoutMs: 10_000, maxOutputBytes: 100_000 });
593
+ if (!canonicalBranch.ok || !canonicalBranch.stdout.trim()) {
594
+ return { safe: false, path, sourceState: 'source_state_unknown', detail: 'Canonical integration branch could not be resolved for owned managed-worktree cleanup.' };
595
+ }
596
+ const canonicalBranchName = canonicalBranch.stdout.trim();
597
+ const contained = runProcess('git', ['merge-base', '--is-ancestor', head.stdout.trim(), `refs/heads/${canonicalBranchName}`], {
598
+ cwd: canonical.canonicalRoot,
599
+ timeoutMs: 10_000,
600
+ maxOutputBytes: 100_000,
601
+ });
602
+ if (!contained.ok) {
603
+ return {
604
+ safe: false,
605
+ path,
606
+ sourceState: 'unintegrated_commits',
607
+ detail: `Owned managed worktree HEAD ${head.stdout.trim()} is not contained in canonical branch ${canonicalBranchName}; source preservation remains required.`,
608
+ };
609
+ }
610
+ return {
611
+ safe: true,
612
+ path,
613
+ sourceState: 'clean_integrated',
614
+ workspaceResourceId: workspaceResource.resourceId,
615
+ checkoutId: checkout.checkoutId,
616
+ branch: branchName,
617
+ canonicalBranch: canonicalBranchName,
618
+ detail: `Canonical OwnedResource proves cleanup authority for a clean managed worktree whose HEAD ${head.stdout.trim()} is already contained in canonical branch ${canonicalBranchName}.`,
619
+ };
620
+ }
621
+
500
622
  interface StaleWorkSourceInspection {
501
623
  safeToCancel: boolean;
502
624
  state: StaleWorkSourceState;
@@ -535,9 +657,8 @@ function resolveStaleWorkDeliveryTarget(
535
657
 
536
658
  function isLegacyImplicitRemoteEffectPlacement(contract: WorkContract): boolean {
537
659
  if (contract.workKind !== 'remote_effect' || contract.worktreePolicy.required !== true || !contract.worktreeRef || !contract.checkoutId) return false;
538
- if (contract.routeDecision?.requiresIsolation === true) return false;
539
660
  const reason = contract.worktreePolicy.reason?.trim() ?? '';
540
- if (/Typed placement|Route Policy requires isolated execution/i.test(reason)) return false;
661
+ if (/Typed placement/i.test(reason)) return false;
541
662
  return true;
542
663
  }
543
664
 
@@ -573,7 +694,7 @@ function inspectLegacyRemoteEffectPlacement(
573
694
  if (registeredPath !== path && resolve(checkout.localRoot) !== path) return { safe: false, path, detail: 'Legacy remote_effect WorkContract path does not match its registered checkout identity.' };
574
695
  const otherOwner = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts.find((candidate) =>
575
696
  candidate.workId !== contract.workId
576
- && !isTerminalWorkContractStatus(candidate.status)
697
+ && semanticWorkState(candidate) === 'open'
577
698
  && candidate.worktreeRef
578
699
  && resolve(candidate.worktreeRef) === path);
579
700
  if (otherOwner) return { safe: false, path, detail: `Managed worktree is also owned by active Work ${otherOwner.workId}; resource detachment is fenced.` };
@@ -771,20 +892,11 @@ function inspectStaleWorkRepositorySource(
771
892
  };
772
893
  }
773
894
 
774
- function staleWorkSemanticTerminalizationReady(contract: WorkContract): boolean {
775
- if (contract.phase !== 'cleanup') return false;
776
- return (['implementation', 'verification', 'delivery'] as const).every((phase) =>
777
- ['satisfied', 'skipped'].includes(contract.phaseEvidence[phase].state));
778
- }
779
-
780
- function staleWorkCandidateReason(source: StaleWorkSourceInspection, semanticReady: boolean): string {
895
+ function staleWorkCandidateReason(source: StaleWorkSourceInspection): string {
781
896
  if (!source.safeToCancel) {
782
- return `Protected stale Work: no active lifecycle authority remains, but repository source preservation is required. ${source.detail} The Work must remain nonterminal until source is explicitly adopted, delivered, or cleanup is semantically authorized.`;
897
+ return `Protected stale Work: repository source preservation is required. ${source.detail} Maintenance may reconcile only concrete owned resources; semantic Work remains model/user-owned.`;
783
898
  }
784
- if (!semanticReady) {
785
- return `Protected stale Work: repository source has no unique live changes, but the Work semantic lifecycle has not reached cleanup with implementation, verification, and delivery explicitly satisfied or skipped. ${source.detail} Maintenance may clean source debt, but it cannot infer that the objective or acceptance criteria are complete.`;
786
- }
787
- return `Cleanup-ready WorkContract exceeded the explicit maintenance age threshold and has no active Plan, Requirement, Schedule, or Controller authority. ${source.detail} Explicit full maintenance may finish cleanup while retaining durable evidence.`;
899
+ return `Observed stale Work: repository source has no unique live changes, but liveness/legacy phase facts do not authorize semantic cancellation or completion. ${source.detail} Use explicit Work semantic CAS to close the Work.`;
788
900
  }
789
901
 
790
902
  function scanRetainedWorktreeCandidates(
@@ -811,12 +923,12 @@ function scanRetainedWorktreeCandidates(
811
923
  if (normalized === resolve(repository.canonicalRoot) || currentOwnedPaths.has(normalized)) continue;
812
924
  const migrated = retainedByPath.get(normalized);
813
925
  if (migrated) {
814
- const completed = migrated.work.status === 'completed';
926
+ const completed = semanticWorkState(migrated.work) === 'completed';
815
927
  candidates.push({
816
928
  kind: 'retained_migrated_work',
817
929
  id: migrated.work.workId,
818
930
  path: normalized,
819
- status: migrated.work.status,
931
+ status: migrated.work.semanticState,
820
932
  safe: false,
821
933
  reason: completed
822
934
  ? 'A terminal Work from a retired Controller Home still owns a Git worktree and requires completion/cleanup reconciliation under the current Controller before lifecycle health can be reported.'
@@ -831,12 +943,28 @@ function scanRetainedWorktreeCandidates(
831
943
  continue;
832
944
  }
833
945
  if (!looksLikeForgeManagedWorktree(normalized)) continue;
946
+ const ownedResource = activeOwnedWorktreeResource(controllerHome, repository.repoId, normalized);
947
+ if (ownedResource) {
948
+ const inspection = inspectOwnedManagedWorktree(repository, controllerHome, normalized);
949
+ candidates.push({
950
+ kind: 'unowned_managed_worktree',
951
+ id: `managed-worktree:${basename(normalized)}`,
952
+ path: normalized,
953
+ safe: inspection.safe,
954
+ reason: inspection.detail,
955
+ suggestedAction: 'full_maintenance_pass',
956
+ ownershipStatus: 'explicit',
957
+ sourceState: inspection.sourceState,
958
+ disposition: inspection.safe ? undefined : 'source_preservation_required',
959
+ });
960
+ continue;
961
+ }
834
962
  candidates.push({
835
963
  kind: 'unowned_managed_worktree',
836
964
  id: `managed-worktree:${basename(normalized)}`,
837
965
  path: normalized,
838
966
  safe: false,
839
- reason: 'Git reports a Forge-managed worktree for this repository, but the current Controller has no WorkContract ownership and no retained migration identity for it. Ownership must be reconciled before lifecycle health can be reported.',
967
+ reason: 'Git reports a Forge-managed worktree for this repository, but neither the current Work store, retained migration state, nor canonical OwnedResource authority owns it. Ownership must be reconciled before lifecycle health can be reported.',
840
968
  suggestedAction: 'full_maintenance_pass',
841
969
  ownershipStatus: 'unknown',
842
970
  disposition: 'ownership_reconciliation',
@@ -860,12 +988,12 @@ function scanRetainedWorktreeCandidates(
860
988
  // path outside current Git inventory only remains lifecycle debt when its
861
989
  // worktree .git marker still exists for manual reconciliation.
862
990
  if (!existsSync(join(normalized, '.git'))) continue;
863
- const completed = migrated.work.status === 'completed';
991
+ const completed = semanticWorkState(migrated.work) === 'completed';
864
992
  candidates.push({
865
993
  kind: 'retained_migrated_work',
866
994
  id: migrated.work.workId,
867
995
  path: normalized,
868
- status: migrated.work.status,
996
+ status: migrated.work.semanticState,
869
997
  safe: false,
870
998
  reason: 'A terminal Work retained by a migrated Controller Home still has preserved filesystem state, but Git no longer reports it as a registered worktree. Manual lifecycle reconciliation is required; automatic cleanup is forbidden.',
871
999
  suggestedAction: 'full_maintenance_pass',
@@ -891,7 +1019,6 @@ function normalizedOptions(options: RuntimeMaintenanceOptions = {}): Required<Ru
891
1019
  interface RuntimeMaintenanceAuthoritySnapshot {
892
1020
  contracts: WorkContract[];
893
1021
  workById: Map<string, WorkContract>;
894
- controllerWorkIds: Set<string>;
895
1022
  planByWorkId: Map<string, string>;
896
1023
  activeRequirementStateById: Map<string, string>;
897
1024
  scheduleByWorkId: Map<string, string>;
@@ -911,7 +1038,6 @@ function buildRuntimeMaintenanceAuthoritySnapshot(
911
1038
  ): RuntimeMaintenanceAuthoritySnapshot {
912
1039
  const contracts = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts;
913
1040
  const workById = new Map(contracts.map((contract) => [contract.workId, contract]));
914
- const controllerWorkIds = new Set(listControllerSessions({ controllerHome, repoId: repository.repoId }).map((session) => session.workId));
915
1041
  const planByWorkId = new Map<string, string>();
916
1042
  for (const plan of listPlanContracts({ controllerHome, repoId: repository.repoId, status: 'active', limit: 100 })) {
917
1043
  for (const step of plan.steps) if (step.workId) planByWorkId.set(step.workId, plan.planId);
@@ -934,12 +1060,11 @@ function buildRuntimeMaintenanceAuthoritySnapshot(
934
1060
  for (const process of listRecoverableProcessRecords(controllerHome, repository.repoId)) {
935
1061
  if (process.workId && isManagedProcessActive(process) && !processByWorkId.has(process.workId)) processByWorkId.set(process.workId, process.processId);
936
1062
  }
937
- return { contracts, workById, controllerWorkIds, planByWorkId, activeRequirementStateById, scheduleByWorkId, processByWorkId };
1063
+ return { contracts, workById, planByWorkId, activeRequirementStateById, scheduleByWorkId, processByWorkId };
938
1064
  }
939
1065
 
940
1066
  function activeWorkAuthorityRefs(contract: WorkContract, snapshot: RuntimeMaintenanceAuthoritySnapshot): string[] {
941
1067
  const refs: string[] = [];
942
- if (snapshot.controllerWorkIds.has(contract.workId)) refs.push('controller_session');
943
1068
  const planId = snapshot.planByWorkId.get(contract.workId);
944
1069
  if (planId) refs.push(`plan:${planId}`);
945
1070
  if (contract.requirementId) {
@@ -961,7 +1086,7 @@ function scanStaleWorkContractCandidates(
961
1086
  ): RuntimeMaintenanceCandidate[] {
962
1087
  const nowMs = Date.now();
963
1088
  return snapshot.contracts
964
- .filter((contract) => !isTerminalWorkContractStatus(contract.status))
1089
+ .filter((contract) => semanticWorkState(contract) === 'open')
965
1090
  .map((contract) => {
966
1091
  const updatedMs = Date.parse(contract.updatedAt);
967
1092
  const ageMinutes = Number.isFinite(updatedMs) ? Math.max(0, Math.floor((nowMs - updatedMs) / 60_000)) : 0;
@@ -969,7 +1094,7 @@ function scanStaleWorkContractCandidates(
969
1094
  })
970
1095
  .filter(({ ageMinutes }) => ageMinutes >= options.minAgeMinutes)
971
1096
  .filter(({ contract }) => {
972
- // A live Plan/Requirement/Schedule/Controller/Process reference is lifecycle authority,
1097
+ // A live Plan/Requirement/Schedule/Process reference is retained migration authority,
973
1098
  // not maintenance debt. The authority inventory was read once for this status pass.
974
1099
  return activeWorkAuthorityRefs(contract, snapshot).length === 0;
975
1100
  })
@@ -981,21 +1106,20 @@ function scanStaleWorkContractCandidates(
981
1106
  }))
982
1107
  .map(({ contract, ageMinutes, source }) => {
983
1108
  const legacyRemotePlacement = isLegacyImplicitRemoteEffectPlacement(contract);
984
- const semanticReady = staleWorkSemanticTerminalizationReady(contract);
985
1109
  return {
986
1110
  kind: 'stale_work_contract' as const,
987
1111
  id: contract.workId,
988
1112
  path: source.path,
989
- status: contract.status,
990
- safe: legacyRemotePlacement ? source.safeToCancel : source.safeToCancel && semanticReady,
991
- reason: legacyRemotePlacement ? source.detail : staleWorkCandidateReason(source, semanticReady),
1113
+ status: contract.semanticState,
1114
+ safe: legacyRemotePlacement ? source.safeToCancel : false,
1115
+ reason: legacyRemotePlacement ? source.detail : staleWorkCandidateReason(source),
992
1116
  ageMinutes,
993
1117
  suggestedAction: 'full_maintenance_pass' as const,
994
1118
  ownershipStatus: 'explicit' as const,
995
1119
  sourceState: source.state,
996
1120
  disposition: !source.safeToCancel
997
1121
  ? ('source_preservation_required' as const)
998
- : !legacyRemotePlacement && !semanticReady
1122
+ : !legacyRemotePlacement
999
1123
  ? ('semantic_completion_required' as const)
1000
1124
  : undefined,
1001
1125
  };
@@ -1013,24 +1137,52 @@ function detachLegacyRemoteEffectPlacement(
1013
1137
  return { ...candidate, safe: false, reason: inspection?.detail ?? 'Legacy remote_effect placement is no longer eligible for resource detachment.', sourceState: 'source_state_unknown', disposition: 'source_preservation_required', applied: false, result: 'legacy_remote_effect_placement_preserved' };
1014
1138
  }
1015
1139
  const store = { controllerHome, repoId: repository.repoId };
1016
- const previous = { checkoutId: current.checkoutId, worktreeRef: current.worktreeRef, constraints: current.constraints, driver: current.driver, worktreePolicy: current.worktreePolicy };
1140
+ try {
1141
+ assertOwnedResourceCleanupTarget(controllerHome, {
1142
+ resourceId: managedWorkspaceOwnedResourceId(repository.repoId, inspection.checkoutId),
1143
+ kind: 'worktree',
1144
+ targetRef: inspection.path,
1145
+ });
1146
+ } catch (error) {
1147
+ return {
1148
+ ...candidate,
1149
+ safe: false,
1150
+ reason: `Canonical OwnedResource authority is required before legacy worktree detachment: ${error instanceof Error ? error.message : String(error)}`,
1151
+ sourceState: 'source_state_unknown',
1152
+ disposition: 'source_preservation_required',
1153
+ applied: false,
1154
+ result: 'legacy_remote_effect_owned_resource_required',
1155
+ };
1156
+ }
1157
+ const previous = { checkoutId: current.checkoutId, worktreeRef: current.worktreeRef, constraints: current.constraints, worktreePolicy: current.worktreePolicy };
1017
1158
  updateWorkContract(store, current.workId, {
1018
1159
  checkoutId: inspection.canonicalCheckoutId,
1019
1160
  worktreeRef: undefined,
1020
- constraints: { ...current.constraints, workspaceMode: 'auto', requireWorktree: false },
1021
- driver: { ...current.driver, preferred: 'direct_edit', allowDirectEdit: true },
1161
+ constraints: { ...current.constraints, workspaceMode: 'current' },
1022
1162
  worktreePolicy: { required: false, reason: 'Legacy pure remote_effect repository placement detached by explicit runtime maintenance; external-effect execution has no repository source authority.' },
1023
1163
  });
1024
1164
  try {
1025
1165
  setRepositoryCheckoutLifecycle({ controllerHome, repoId: repository.repoId, checkoutId: inspection.checkoutId, lifecycle: 'removed', reason: `Legacy remote_effect placement detached for ${current.workId}.` });
1026
1166
  const removed = runProcess('git', ['worktree', 'remove', '--force', inspection.path], { cwd: repository.canonicalRoot, timeoutMs: 60_000, maxOutputBytes: 500_000 });
1027
1167
  if (!removed.ok && existsSync(inspection.path)) throw new Error(`LEGACY_REMOTE_EFFECT_WORKTREE_REMOVE_FAILED: ${removed.stderr || removed.stdout}`);
1168
+ markOwnedResourceCleaned(controllerHome, managedWorkspaceOwnedResourceId(repository.repoId, inspection.checkoutId), 'forge:legacy-remote-effect-detach');
1028
1169
  if (inspection.branch !== inspection.canonicalBranch) {
1170
+ try {
1171
+ assertOwnedResourceCleanupTarget(controllerHome, {
1172
+ resourceId: managedBranchOwnedResourceId(repository.repoId, inspection.checkoutId),
1173
+ kind: 'git_branch',
1174
+ targetRef: inspection.branch,
1175
+ locator: managedBranchOwnedResourceLocator(repository.canonicalRoot, inspection.checkoutId, inspection.branch),
1176
+ });
1177
+ } catch {
1178
+ return { ...candidate, safe: true, reason: inspection.detail, sourceState: 'clean_integrated', disposition: undefined, applied: true, result: `legacy_remote_effect_placement_detached_branch_retained:${inspection.branch}` };
1179
+ }
1029
1180
  const deleted = runProcess('git', ['branch', '--delete', inspection.branch], { cwd: repository.canonicalRoot, timeoutMs: 10_000, maxOutputBytes: 100_000 });
1030
1181
  if (!deleted.ok) {
1031
1182
  const forced = runProcess('git', ['branch', '--delete', '--force', inspection.branch], { cwd: repository.canonicalRoot, timeoutMs: 10_000, maxOutputBytes: 100_000 });
1032
1183
  if (!forced.ok) return { ...candidate, safe: true, reason: inspection.detail, sourceState: 'clean_integrated', disposition: undefined, applied: true, result: `legacy_remote_effect_placement_detached_branch_retained:${inspection.branch}` };
1033
1184
  }
1185
+ markOwnedResourceCleaned(controllerHome, managedBranchOwnedResourceId(repository.repoId, inspection.checkoutId), 'forge:legacy-remote-effect-detach');
1034
1186
  }
1035
1187
  return { ...candidate, safe: true, reason: inspection.detail, sourceState: 'clean_integrated', disposition: undefined, applied: true, result: 'legacy_remote_effect_placement_detached' };
1036
1188
  } catch (error) {
@@ -1042,99 +1194,169 @@ function detachLegacyRemoteEffectPlacement(
1042
1194
  }
1043
1195
  }
1044
1196
 
1045
- export function applyStaleWorkContractMaintenanceCandidate(
1197
+ function detachOwnedManagedWorktree(
1046
1198
  repository: RuntimeMaintenanceRepository,
1047
1199
  controllerHome: string,
1048
1200
  candidate: RuntimeMaintenanceCandidate,
1049
1201
  ): RuntimeMaintenanceCandidate & { applied: boolean; result: string } {
1050
- const work = getWorkContract({ controllerHome, repoId: repository.repoId }, candidate.id);
1051
- if (!work || isTerminalWorkContractStatus(work.status)) {
1052
- return { ...candidate, applied: false, result: 'already_terminal' };
1202
+ if (!candidate.path) return { ...candidate, applied: false, result: 'owned_managed_worktree_path_missing' };
1203
+ const inspection = inspectOwnedManagedWorktree(repository, controllerHome, candidate.path);
1204
+ if (!inspection.safe || !inspection.workspaceResourceId || !inspection.checkoutId || !inspection.branch || !inspection.canonicalBranch) {
1205
+ return {
1206
+ ...candidate,
1207
+ safe: false,
1208
+ reason: inspection.detail,
1209
+ sourceState: inspection.sourceState,
1210
+ disposition: 'source_preservation_required',
1211
+ applied: false,
1212
+ result: 'owned_managed_worktree_preserved',
1213
+ };
1053
1214
  }
1054
-
1055
- // The stale scan is discovery only. Re-evaluate every non-Controller authority
1056
- // while holding the canonical ControllerSession lock, then perform the Work
1057
- // transition before releasing it. A newer controller_claim therefore either
1058
- // wins the lock first and fences this mutation, or starts only after an already
1059
- // completed maintenance terminalization; there is no check-then-cancel gap.
1060
- const fenced = withControllerSessionTerminalizationFence(
1061
- { controllerHome, repoId: repository.repoId },
1062
- { workId: work.workId, actor: `runtime-maintenance-terminalize:${work.workId}` },
1063
- () => {
1064
- const current = getWorkContract({ controllerHome, repoId: repository.repoId }, work.workId);
1065
- if (!current || isTerminalWorkContractStatus(current.status)) {
1066
- return { ...candidate, applied: false, result: 'already_terminal' };
1067
- }
1068
- const authorityRefs = activeWorkAuthorityRefs(current, buildRuntimeMaintenanceAuthoritySnapshot(repository, controllerHome));
1069
- if (authorityRefs.length > 0) {
1215
+ try {
1216
+ setRepositoryCheckoutLifecycle({
1217
+ controllerHome,
1218
+ repoId: repository.repoId,
1219
+ checkoutId: inspection.checkoutId,
1220
+ lifecycle: 'removed',
1221
+ reason: 'Canonical OwnedResource cleanup reconciled a WorkContract-less managed worktree whose source is already integrated.',
1222
+ });
1223
+ const removed = runProcess('git', ['worktree', 'remove', '--force', inspection.path], {
1224
+ cwd: repository.canonicalRoot,
1225
+ timeoutMs: 60_000,
1226
+ maxOutputBytes: 500_000,
1227
+ });
1228
+ if (!removed.ok && existsSync(inspection.path)) {
1229
+ throw new Error(`OWNED_MANAGED_WORKTREE_REMOVE_FAILED: ${removed.stderr || removed.stdout}`);
1230
+ }
1231
+ markOwnedResourceCleaned(controllerHome, inspection.workspaceResourceId, 'forge:runtime-maintenance-owned-worktree');
1232
+ if (inspection.branch !== inspection.canonicalBranch) {
1233
+ try {
1234
+ assertOwnedResourceCleanupTarget(controllerHome, {
1235
+ resourceId: managedBranchOwnedResourceId(repository.repoId, inspection.checkoutId),
1236
+ kind: 'git_branch',
1237
+ targetRef: inspection.branch,
1238
+ locator: managedBranchOwnedResourceLocator(repository.canonicalRoot, inspection.checkoutId, inspection.branch),
1239
+ });
1240
+ } catch {
1070
1241
  return {
1071
1242
  ...candidate,
1072
- applied: false,
1073
- result: `work_authority_became_active:${authorityRefs.join(',')}`,
1243
+ safe: true,
1244
+ reason: inspection.detail,
1245
+ sourceState: 'clean_integrated',
1246
+ disposition: undefined,
1247
+ applied: true,
1248
+ result: `owned_managed_worktree_detached_branch_retained:${inspection.branch}`,
1074
1249
  };
1075
1250
  }
1076
- // Discovery and mutation are separated by an arbitrary Controller/MCP delay.
1077
- // Re-probe the live worktree while the canonical ControllerSession fence is
1078
- // held so neither a newer Controller claim nor a late source write can be
1079
- // raced by stale maintenance terminalization.
1080
- const source = inspectStaleWorkRepositorySource(repository, controllerHome, current);
1081
- if (!source.safeToCancel) {
1251
+ const deleted = runProcess('git', ['branch', '--delete', inspection.branch], {
1252
+ cwd: repository.canonicalRoot,
1253
+ timeoutMs: 10_000,
1254
+ maxOutputBytes: 100_000,
1255
+ });
1256
+ if (!deleted.ok) {
1257
+ const forced = runProcess('git', ['branch', '--delete', '--force', inspection.branch], {
1258
+ cwd: repository.canonicalRoot,
1259
+ timeoutMs: 10_000,
1260
+ maxOutputBytes: 100_000,
1261
+ });
1262
+ if (!forced.ok) {
1263
+ return {
1264
+ ...candidate,
1265
+ safe: true,
1266
+ reason: inspection.detail,
1267
+ sourceState: 'clean_integrated',
1268
+ disposition: undefined,
1269
+ applied: true,
1270
+ result: `owned_managed_worktree_detached_branch_retained:${inspection.branch}`,
1271
+ };
1272
+ }
1273
+ }
1274
+ markOwnedResourceCleaned(
1275
+ controllerHome,
1276
+ managedBranchOwnedResourceId(repository.repoId, inspection.checkoutId),
1277
+ 'forge:runtime-maintenance-owned-worktree',
1278
+ );
1279
+ }
1280
+ return {
1281
+ ...candidate,
1282
+ safe: true,
1283
+ reason: inspection.detail,
1284
+ sourceState: 'clean_integrated',
1285
+ disposition: undefined,
1286
+ applied: true,
1287
+ result: 'owned_managed_worktree_detached',
1288
+ };
1289
+ } catch (error) {
1290
+ if (existsSync(inspection.path)) {
1291
+ try {
1292
+ setRepositoryCheckoutLifecycle({
1293
+ controllerHome,
1294
+ repoId: repository.repoId,
1295
+ checkoutId: inspection.checkoutId,
1296
+ lifecycle: 'active',
1297
+ reason: 'Rollback failed OwnedResource managed-worktree cleanup.',
1298
+ });
1299
+ } catch { /* preserve primary failure */ }
1300
+ }
1301
+ throw error;
1302
+ }
1303
+ }
1304
+
1305
+ export function applyStaleWorkContractMaintenanceCandidate(
1306
+ repository: RuntimeMaintenanceRepository,
1307
+ controllerHome: string,
1308
+ candidate: RuntimeMaintenanceCandidate,
1309
+ ): RuntimeMaintenanceCandidate & { applied: boolean; result: string } {
1310
+ const work = getWorkContract({ controllerHome, repoId: repository.repoId }, candidate.id);
1311
+ if (!work || semanticWorkState(work) !== 'open') {
1312
+ return { ...candidate, applied: false, result: 'already_terminal' };
1313
+ }
1314
+
1315
+ // The stale scan is discovery only. Re-read the exact Work and every
1316
+ // non-Controller authority immediately before mutation. WorkContract writes are
1317
+ // serialized by the canonical Work store writer; ControllerSession ownership is
1318
+ // continuation state and cannot fence maintenance of semantic Work.
1319
+ const current = getWorkContract({ controllerHome, repoId: repository.repoId }, work.workId);
1320
+ if (!current || semanticWorkState(current) !== 'open') {
1321
+ return { ...candidate, applied: false, result: 'already_terminal' };
1322
+ }
1323
+ const authorityRefs = activeWorkAuthorityRefs(current, buildRuntimeMaintenanceAuthoritySnapshot(repository, controllerHome));
1324
+ if (authorityRefs.length > 0) {
1325
+ return {
1326
+ ...candidate,
1327
+ applied: false,
1328
+ result: `work_authority_became_active:${authorityRefs.join(',')}`,
1329
+ };
1330
+ }
1331
+ // Discovery and mutation are separated by an arbitrary Runtime delay.
1332
+ // Re-probe the live worktree immediately before mutation; concrete Git/worktree
1333
+ // state is the relevant safety authority, not a Controller claim.
1334
+ const source = inspectStaleWorkRepositorySource(repository, controllerHome, current);
1335
+ if (!source.safeToCancel) {
1082
1336
  return {
1083
1337
  ...candidate,
1084
1338
  path: source.path ?? candidate.path,
1085
1339
  safe: false,
1086
- reason: isLegacyImplicitRemoteEffectPlacement(current) ? source.detail : staleWorkCandidateReason(source, staleWorkSemanticTerminalizationReady(current)),
1340
+ reason: isLegacyImplicitRemoteEffectPlacement(current) ? source.detail : staleWorkCandidateReason(source),
1087
1341
  sourceState: source.state,
1088
1342
  disposition: 'source_preservation_required' as const,
1089
1343
  applied: false,
1090
1344
  result: `work_source_preserved:${source.state}`,
1091
1345
  };
1092
1346
  }
1093
- if (isLegacyImplicitRemoteEffectPlacement(current)) {
1347
+ if (isLegacyImplicitRemoteEffectPlacement(current)) {
1094
1348
  return detachLegacyRemoteEffectPlacement(repository, controllerHome, current, candidate);
1095
1349
  }
1096
- const semanticReady = staleWorkSemanticTerminalizationReady(current);
1097
- if (!semanticReady) {
1098
- return {
1099
- ...candidate,
1100
- path: source.path ?? candidate.path,
1101
- safe: false,
1102
- reason: staleWorkCandidateReason(source, false),
1103
- sourceState: source.state,
1104
- disposition: 'semantic_completion_required' as const,
1105
- applied: false,
1106
- result: 'work_semantic_completion_required',
1107
- };
1108
- }
1109
- cancelWorkContract(
1110
- { controllerHome, repoId: repository.repoId },
1111
- current.workId,
1112
- {
1113
- summary: 'Cancelled by explicit full maintenance after the Work had already reached cleanup with prior semantic phases satisfied and no unique live repository source remained; durable evidence retained.',
1114
- evidenceRefs: current.evidenceRefs,
1115
- },
1116
- );
1117
- return {
1118
- ...candidate,
1119
- path: source.path ?? candidate.path,
1120
- safe: true,
1121
- reason: staleWorkCandidateReason(source, true),
1122
- sourceState: source.state,
1123
- disposition: undefined,
1124
- applied: true,
1125
- result: 'work_contract_cancelled_evidence_retained',
1126
- };
1127
- },
1128
- );
1129
- if (!fenced.allowed) {
1130
- const generation = fenced.owner?.claimGeneration;
1131
- return {
1132
- ...candidate,
1133
- applied: false,
1134
- result: `work_terminalization_fenced:${fenced.reason}${typeof generation === 'number' ? `:claim_generation=${generation}` : ''}`,
1135
- };
1136
- }
1137
- return fenced.value;
1350
+ return {
1351
+ ...candidate,
1352
+ path: source.path ?? candidate.path,
1353
+ safe: false,
1354
+ reason: staleWorkCandidateReason(source),
1355
+ sourceState: source.state,
1356
+ disposition: 'semantic_completion_required' as const,
1357
+ applied: false,
1358
+ result: 'work_semantic_completion_required',
1359
+ };
1138
1360
  }
1139
1361
 
1140
1362
  function scanStaleEditSessionCandidates(
@@ -1157,7 +1379,7 @@ function scanStaleEditSessionCandidates(
1157
1379
  .slice(0, options.maxCandidates)
1158
1380
  .flatMap(({ session, ageMinutes }) => {
1159
1381
  const work = session.workId ? snapshot.workById.get(session.workId) : undefined;
1160
- const terminalWork = Boolean(work && isTerminalWorkContractStatus(work.status));
1382
+ const terminalWork = Boolean(work && semanticWorkState(work) !== 'open');
1161
1383
  if (work && !terminalWork && activeWorkAuthorityRefs(work, snapshot).length > 0) {
1162
1384
  // The Edit Session inherits the live Work lifecycle authority. Reporting it
1163
1385
  // as stale maintenance debt while that authority is active would duplicate
@@ -1269,7 +1491,7 @@ export function buildRuntimeMaintenanceStatus(
1269
1491
  'Pending approvals are not cancelled unless cancel_pending_approvals is explicitly enabled.',
1270
1492
  'System temp cleanup only removes direct forge-prefixed children of approved temp roots after a 24-hour retention period and a fresh process-occupancy check.',
1271
1493
  'Runtime lifecycle changes and source repair are outside the runtime maintenance executor.',
1272
- 'Retained or unowned managed worktrees are review-only lifecycle blockers. Runtime maintenance never deletes or modifies their preserved implementation state.',
1494
+ 'Retained or unknown-ownership managed worktrees remain review-only lifecycle blockers. A WorkContract-less managed worktree is cleanup-eligible only when canonical OwnedResource identity is exact, the worktree is clean, and its HEAD is already contained in the canonical branch.',
1273
1495
  ],
1274
1496
  };
1275
1497
  }
@@ -1482,6 +1704,9 @@ export function applyRuntimeMaintenance(
1482
1704
  if (candidate.kind === 'stale_work_contract') {
1483
1705
  return applyStaleWorkContractMaintenanceCandidate(repository, controllerHome, candidate);
1484
1706
  }
1707
+ if (candidate.kind === 'unowned_managed_worktree') {
1708
+ return detachOwnedManagedWorktree(repository, controllerHome, candidate);
1709
+ }
1485
1710
  if (candidate.kind === 'stale_edit_session') {
1486
1711
  const cleaned = cleanupEditSession(repository.canonicalRoot, candidate.id, {
1487
1712
  reviewer: 'runtime-maintenance',
@@ -1506,7 +1731,7 @@ export function applyRuntimeMaintenance(
1506
1731
  : undefined;
1507
1732
  const currentWork = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts;
1508
1733
  const protectedWorkIds = currentWork
1509
- .filter((contract) => !isTerminalWorkContractStatus(contract.status))
1734
+ .filter((contract) => semanticWorkState(contract) === 'open')
1510
1735
  .map((contract) => contract.workId);
1511
1736
  const verificationSnapshotGc = options.actionId === 'full_maintenance_pass'
1512
1737
  ? cleanupStaleWorkVerificationSnapshots(controllerHome, repository.repoId, {
@@ -1522,6 +1747,30 @@ export function applyRuntimeMaintenance(
1522
1747
  additionalRoots: [runtimeLegacyCheckQuarantineRoot(controllerHome, repository.repoId)],
1523
1748
  })
1524
1749
  : undefined;
1750
+ let generatedCacheGc: RepositoryGeneratedCacheRetentionReport | undefined;
1751
+ let cleanupArtifactGc: CleanupArtifactRetentionReport | undefined;
1752
+ if (options.actionId === 'full_maintenance_pass') {
1753
+ let cacheRepository: Parameters<typeof cleanupGeneratedRepositoryCheckoutCaches>[0] = {
1754
+ canonicalRoot: repository.canonicalRoot,
1755
+ checkouts: [],
1756
+ };
1757
+ try {
1758
+ cacheRepository = getRepository(repository.repoId, controllerHome, { includeRemoved: true });
1759
+ } catch {
1760
+ // Embedded/test repositories may not be present in the Controller registry.
1761
+ // The explicit maintenance action still safely covers their canonical root.
1762
+ }
1763
+ generatedCacheGc = cleanupGeneratedRepositoryCheckoutCaches(cacheRepository, {
1764
+ graceMs: 60_000,
1765
+ maxEntries: Math.max(1_000, (options.maxCandidates ?? 50) * 20),
1766
+ maxRemovals: Math.max(1, options.maxCandidates ?? 50),
1767
+ });
1768
+ cleanupArtifactGc = cleanupWorkPreservationArtifacts(controllerHome, {
1769
+ maxEntries: Math.max(1_000, (options.maxCandidates ?? 50) * 20),
1770
+ maxRemovals: Math.max(1, options.maxCandidates ?? 50),
1771
+ repositoryIds: [repository.repoId],
1772
+ });
1773
+ }
1525
1774
 
1526
1775
  if (options.actionId === 'runtime_storage_finalize_relocation' || options.actionId === 'full_maintenance_pass' || applied.some((candidate) => candidate.applied) || runtimeStorageRepairApply) {
1527
1776
  rebuildActiveIndex(repository.canonicalRoot);
@@ -1541,6 +1790,8 @@ export function applyRuntimeMaintenance(
1541
1790
  processGc,
1542
1791
  verificationSnapshotGc,
1543
1792
  quarantineGc,
1793
+ generatedCacheGc,
1794
+ cleanupArtifactGc,
1544
1795
  projection,
1545
1796
  };
1546
1797
  }