@moretea-labs/forge 1.7.2 → 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 (900) hide show
  1. package/AGENTS.md +121 -62
  2. package/CHANGELOG.md +40 -0
  3. package/CLAUDE.md +57 -62
  4. package/CONTRIBUTING.md +3 -2
  5. package/README.md +2 -2
  6. package/SKILL.md +0 -1
  7. package/THIRD_PARTY_NOTICES.md +5 -2
  8. package/adapters/browser/index.ts +2 -0
  9. package/adapters/chatgpt/browser-delivery-host.ts +77 -0
  10. package/adapters/chatgpt/browser-delivery-runtime.ts +1256 -0
  11. package/adapters/chatgpt/controller-binding-store.ts +134 -0
  12. package/adapters/chatgpt/controller-round-host.ts +68 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +99 -0
  14. package/adapters/chatgpt/provider-delivery.ts +340 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +218 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/chatgpt-extension-target.ts +463 -0
  18. package/adapters/computer/chatgpt-target-router.ts +103 -0
  19. package/adapters/computer/chrome-extension/background.js +385 -0
  20. package/adapters/computer/chrome-extension/content.js +378 -0
  21. package/adapters/computer/chrome-extension/core.js +24 -0
  22. package/adapters/computer/chrome-extension/manifest.json +30 -0
  23. package/adapters/computer/desktop-operator-contract.ts +7 -0
  24. package/adapters/computer/desktop-operator-discovery.ts +181 -0
  25. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  26. package/adapters/computer/desktop-operator-provider.ts +312 -0
  27. package/adapters/computer/index.ts +1 -0
  28. package/adapters/computer/interaction-target-authority.ts +1068 -0
  29. package/adapters/controller-process/binding-store.ts +34 -0
  30. package/adapters/controller-process/controller-host.ts +26 -0
  31. package/adapters/mcp/access-mode.ts +132 -0
  32. package/adapters/mcp/audit.ts +32 -0
  33. package/adapters/mcp/auth.ts +406 -0
  34. package/adapters/mcp/instructions.ts +37 -0
  35. package/adapters/mcp/legacy-context.ts +18 -0
  36. package/adapters/mcp/multi-repository.ts +421 -0
  37. package/adapters/mcp/oauth.ts +464 -0
  38. package/adapters/mcp/paths.ts +143 -0
  39. package/adapters/mcp/policy.ts +232 -0
  40. package/adapters/mcp/proxy-env.ts +62 -0
  41. package/adapters/mcp/readiness-projection.ts +24 -0
  42. package/adapters/mcp/redaction.ts +21 -0
  43. package/adapters/mcp/repo.ts +24 -0
  44. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  45. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
  46. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  47. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  48. package/adapters/mcp/runtime-gateway/context-adapter.ts +1167 -0
  49. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
  50. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +206 -0
  51. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  52. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +130 -0
  53. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  54. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  55. package/adapters/mcp/runtime-gateway/execution-tools.ts +173 -0
  56. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  57. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +412 -0
  58. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  59. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  60. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  61. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  62. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +380 -0
  63. package/adapters/mcp/runtime-gateway/process-tools.ts +457 -0
  64. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +88 -0
  65. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +285 -0
  66. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  67. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  68. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +79 -0
  69. package/adapters/mcp/runtime-gateway/result-adapter.ts +114 -0
  70. package/adapters/mcp/runtime-gateway/router.ts +242 -0
  71. package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
  72. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1200 -0
  73. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  74. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +574 -0
  75. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  76. package/adapters/mcp/runtime-gateway/runtime-tools.ts +78 -0
  77. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +187 -0
  78. package/adapters/mcp/runtime-gateway/shared-adapter.ts +45 -0
  79. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +962 -0
  80. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
  81. package/adapters/mcp/runtime-gateway/work-adapter.ts +325 -0
  82. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  83. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  84. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
  85. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  86. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  87. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  88. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  89. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  90. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
  91. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  92. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  93. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
  94. package/adapters/mcp/server.ts +1092 -0
  95. package/adapters/mcp/setup.ts +789 -0
  96. package/adapters/mcp/tool-capability-cache.ts +87 -0
  97. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  98. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5139 -0
  99. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +145 -0
  100. package/adapters/mcp/tool-mapping/repository-tools.ts +1682 -0
  101. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  102. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  103. package/adapters/mcp/toolset-names.ts +79 -0
  104. package/adapters/mcp/toolset-selection.ts +41 -0
  105. package/adapters/mcp/toolset.ts +337 -0
  106. package/adapters/mcp/transports/http-observation.ts +365 -0
  107. package/adapters/mcp/transports/http.ts +933 -0
  108. package/adapters/mcp/transports/oauth-http.ts +322 -0
  109. package/adapters/mcp/transports/session-registry.ts +519 -0
  110. package/adapters/mcp/transports/stdio.ts +8 -0
  111. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +223 -0
  112. package/adapters/mcp/types.ts +41 -0
  113. package/assets/AGENTS.md +2 -16
  114. package/assets/CLAUDE.md +2 -16
  115. package/assets/hooks/AGENTS.md +3 -14
  116. package/assets/hooks/CLAUDE.md +3 -14
  117. package/assets/hooks/hook-input.sh +9 -2
  118. package/assets/hooks/lib/workflow-state.sh +28 -10
  119. package/assets/hooks/prompt-guard.sh +63 -37
  120. package/assets/hooks/security-sentinel.sh +2 -1
  121. package/assets/hooks/session-start-context.sh +7 -5
  122. package/assets/hooks/stop-orchestrator.sh +13 -3
  123. package/assets/initializer-question-pack.v4.json +11 -7
  124. package/assets/partials/05-workflow.partial.md +18 -73
  125. package/assets/partials/07-footer.partial.md +1 -1
  126. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  127. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  128. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  129. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  130. package/assets/plan-map.json +11 -11
  131. package/assets/plugin-registry.v1.json +11 -3
  132. package/assets/plugin-trust-policy.v1.json +6 -0
  133. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  134. package/assets/recovery/forge-wsl-rescue +271 -0
  135. package/assets/reference-configs/agentic-development-flow.md +25 -0
  136. package/assets/reference-configs/development-protocol.md +11 -15
  137. package/assets/reference-configs/document-generation.md +1 -1
  138. package/assets/reference-configs/global-working-rules.md +28 -1
  139. package/assets/reference-configs/harness-overview.md +22 -112
  140. package/assets/reference-configs/hook-operations.md +1 -1
  141. package/assets/reference-configs/sprint-contracts.md +2 -2
  142. package/assets/skill-version.json +2 -2
  143. package/assets/templates/helpers/architecture-event.ts +3 -34
  144. package/assets/templates/helpers/architecture-queue.sh +61 -10
  145. package/assets/templates/helpers/archive-workflow.sh +90 -180
  146. package/assets/templates/helpers/capability-config.ts +8 -11
  147. package/assets/templates/helpers/capability-resolver.ts +37 -19
  148. package/assets/templates/helpers/capture-plan.sh +2 -2
  149. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  150. package/assets/templates/helpers/check-task-sync.sh +82 -11
  151. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  152. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  153. package/assets/templates/helpers/contract-worktree.sh +26 -24
  154. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  155. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  156. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  157. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  158. package/assets/templates/helpers/new-plan.sh +2 -2
  159. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  160. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  161. package/assets/templates/helpers/switch-plan.sh +1 -1
  162. package/assets/templates/helpers/workflow-contract.ts +1 -0
  163. package/assets/workflow-contract.v1.json +63 -171
  164. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  165. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  166. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  167. package/docs/architecture/CURRENT.md +233 -28
  168. package/docs/architecture/EVOLUTION.md +13 -0
  169. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  170. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  171. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  172. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  173. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  174. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  175. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +37 -0
  176. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +311 -0
  177. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  178. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  179. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  180. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
  181. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  182. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
  183. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
  184. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  185. package/docs/architecture/domains/verification.md +8 -0
  186. package/docs/architecture/domains/workflow-engine.md +7 -1
  187. package/docs/architecture/index.md +3 -3
  188. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  189. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  190. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  191. package/docs/forge-plugin-management.md +20 -4
  192. package/docs/forge-runtime-self-healing-loop.md +2 -0
  193. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  194. package/docs/operations/controller-browser-plugin.md +3 -1
  195. package/docs/operations/controller-performance-and-502.md +17 -0
  196. package/docs/operations/controller-reliability-runbook.md +2 -2
  197. package/docs/operations/features.md +1 -1
  198. package/docs/operations/features.zh-CN.md +1 -1
  199. package/docs/operations/homebrew.md +2 -2
  200. package/docs/operations/mcp-tool-exposure.md +2 -0
  201. package/docs/operations/platform-support.md +16 -8
  202. package/docs/operations/platform-support.zh-CN.md +2 -2
  203. package/docs/operations/releasing.md +2 -0
  204. package/docs/operations/standalone-disaster-recovery.md +38 -5
  205. package/docs/operations/uu-remote-rescue.md +41 -0
  206. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  207. package/docs/public-usage-guide.md +1 -1
  208. package/docs/public-usage-guide.zh-CN.md +1 -1
  209. package/docs/tutorials/01-install-and-start.md +1 -1
  210. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  211. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  212. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  213. package/docs/wiki/Implementation.md +1 -1
  214. package/package.json +46 -26
  215. package/packages/kernel/cognition/api/index.ts +6 -0
  216. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  217. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  218. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  219. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  220. package/packages/kernel/cognition/domain/learning.ts +106 -0
  221. package/packages/kernel/cognition/domain/memory.ts +254 -0
  222. package/packages/kernel/controller/api/index.ts +3 -0
  223. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  224. package/packages/kernel/controller/application/controller-round-service.ts +41 -0
  225. package/packages/kernel/controller/application/controller-service.ts +30 -0
  226. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +529 -0
  227. package/packages/kernel/controller/domain/controller-round.ts +110 -0
  228. package/packages/kernel/controller/domain/execution-quality.ts +135 -0
  229. package/packages/kernel/controller/domain/types.ts +56 -0
  230. package/packages/kernel/controller/index.ts +1 -0
  231. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  232. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1970 -0
  233. package/packages/kernel/controller/infrastructure/controller-session-store.ts +804 -0
  234. package/packages/kernel/controller/ports/controller-host.ts +18 -0
  235. package/packages/kernel/goal/api/index.ts +1 -0
  236. package/packages/kernel/goal/domain/types.ts +40 -0
  237. package/packages/kernel/goal/index.ts +1 -0
  238. package/packages/kernel/identity/api/index.ts +15 -0
  239. package/packages/kernel/identity/application/identity-service.ts +67 -0
  240. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  241. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  242. package/packages/kernel/identity/domain/grant.ts +72 -0
  243. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  244. package/packages/kernel/identity/domain/scope.ts +63 -0
  245. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  246. package/packages/kernel/identity/domain/types.ts +44 -0
  247. package/packages/kernel/identity/domain/user-request.ts +93 -0
  248. package/packages/kernel/identity/domain/workspace.ts +56 -0
  249. package/packages/kernel/identity/index.ts +1 -0
  250. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
  251. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  252. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  253. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  254. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  255. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  256. package/packages/kernel/memory/api/index.ts +5 -0
  257. package/packages/kernel/memory/application/experience-service.ts +118 -0
  258. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  259. package/packages/kernel/memory/domain/experience.ts +103 -0
  260. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  261. package/packages/kernel/memory/index.ts +1 -0
  262. package/packages/kernel/scheduler/api/index.ts +4 -0
  263. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  264. package/packages/kernel/scheduler/application/schedule-service.ts +27 -0
  265. package/packages/kernel/scheduler/application/settlement.ts +182 -0
  266. package/packages/kernel/scheduler/domain/schedule.ts +236 -0
  267. package/packages/kernel/scheduler/index.ts +1 -0
  268. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +805 -0
  269. package/packages/kernel/work/api/index.ts +16 -0
  270. package/packages/kernel/work/application/work-service.ts +123 -0
  271. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  272. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  273. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  274. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  275. package/packages/kernel/work/domain/engineering-design.ts +286 -0
  276. package/packages/kernel/work/domain/engineering-profile.ts +238 -0
  277. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  278. package/packages/kernel/work/domain/execution-concurrency.ts +247 -0
  279. package/packages/kernel/work/domain/execution-snapshot.ts +2 -0
  280. package/packages/kernel/work/domain/implementation-review.ts +193 -0
  281. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  282. package/packages/kernel/work/domain/project-engineering-contract.ts +224 -0
  283. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  284. package/{src/runtime/control-plane/facade/work-state-machine.ts → packages/kernel/work/domain/state-machine.ts} +37 -66
  285. package/packages/kernel/work/domain/types.ts +534 -0
  286. package/packages/kernel/work/index.ts +1 -0
  287. package/packages/kernel/work/infrastructure/work-contract-store.ts +1967 -0
  288. package/packages/kernel/work/ports/work-contract-store.ts +21 -0
  289. package/packages/plugin-runtime/browser/index.ts +3 -0
  290. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  291. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  292. package/packages/plugin-runtime/browser/session-authority.ts +62 -0
  293. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  294. package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
  295. package/packages/plugin-runtime/computer/index.ts +8 -0
  296. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  297. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  298. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  299. package/packages/plugin-runtime/computer/provider.ts +19 -0
  300. package/packages/plugin-runtime/computer/target-authority.ts +215 -0
  301. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  302. package/packages/plugin-runtime/external/index.ts +1 -0
  303. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +581 -0
  304. package/packages/plugin-runtime/index.ts +3 -0
  305. package/packages/protocols/browser/index.ts +1 -0
  306. package/packages/protocols/browser/session.ts +98 -0
  307. package/packages/protocols/computer/contract.ts +225 -0
  308. package/packages/protocols/computer/index.ts +1 -0
  309. package/packages/protocols/failure/contract.ts +158 -0
  310. package/packages/protocols/failure/index.ts +1 -0
  311. package/packages/protocols/handoff/index.ts +1 -0
  312. package/packages/protocols/handoff/status.ts +22 -0
  313. package/packages/protocols/mcp/execution-context.ts +11 -0
  314. package/packages/protocols/mcp/index.ts +2 -0
  315. package/packages/protocols/mcp/tool-contract.ts +42 -0
  316. package/packages/workflow-runtime/api/index.ts +2 -0
  317. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  318. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  319. package/packages/workflow-runtime/index.ts +1 -0
  320. package/scripts/activate-source-baseline.ts +7 -9
  321. package/scripts/assemble-template.ts +1 -1
  322. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  323. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  324. package/scripts/canary-controller-semantic-wait.ts +183 -0
  325. package/scripts/canary-global-hook.sh +2 -2
  326. package/scripts/check-mcp-compatibility.ts +85 -7
  327. package/scripts/check-platform-support.mjs +28 -2
  328. package/scripts/check-public-docs.sh +1 -1
  329. package/scripts/check-repository-hygiene.mjs +92 -0
  330. package/scripts/check-runtime-architecture.mjs +1643 -127
  331. package/scripts/check-source-duplication.mjs +9 -1
  332. package/scripts/controller-ngrok-rotation.sh +5 -4
  333. package/scripts/create-project-dirs.sh +10 -74
  334. package/scripts/forge-local-recovery-helper.mjs +282 -0
  335. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  336. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  337. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  338. package/scripts/hook-shim.sh +2 -2
  339. package/scripts/init-project.sh +12 -70
  340. package/scripts/install-desktop-operator-registration.ts +4 -2
  341. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  342. package/scripts/install-standalone-recovery.ts +122 -10
  343. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  344. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  345. package/scripts/lib/controller-home-env.sh +6 -3
  346. package/scripts/lib/project-init-lib.sh +42 -582
  347. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  348. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  349. package/scripts/open-source-audit-allowlist.txt +11 -1
  350. package/scripts/portability-bindings.mjs +70 -0
  351. package/scripts/run-bounded-command.mjs +60 -0
  352. package/scripts/run-bun-test-file.ts +22 -6
  353. package/scripts/run-governed-gate.ts +4 -2
  354. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  355. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  356. package/scripts/smoke-mcp-http-runtime.ts +6 -1
  357. package/scripts/smoke-runtime-recovery.ts +13 -11
  358. package/scripts/smoke-schedule-engine.ts +24 -16
  359. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  360. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  361. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
  362. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  363. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  364. package/scripts/smoke-workflow-supervisor.ts +103 -0
  365. package/scripts/test-governance.ts +51 -2
  366. package/scripts/verify-forge-runtime.sh +2 -1
  367. package/src/cli/agent-jobs/executor-health.ts +11 -34
  368. package/src/cli/agent-jobs/integration.ts +18 -49
  369. package/src/cli/agent-jobs/job-manager.ts +17 -4
  370. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  371. package/src/cli/chatgpt-browser/binding.ts +20 -10
  372. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  373. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  374. package/src/cli/chatgpt-browser/engine.ts +10 -11
  375. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  376. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  377. package/src/cli/chatgpt-browser/types.ts +1 -0
  378. package/src/cli/commands/bootstrap-control.ts +334 -0
  379. package/src/cli/commands/brain-assistant.ts +52 -0
  380. package/src/cli/commands/brain.ts +191 -40
  381. package/src/cli/commands/chatgpt.ts +32 -67
  382. package/src/cli/commands/computer.ts +437 -0
  383. package/src/cli/commands/doctor.ts +18 -1
  384. package/src/cli/commands/init-hook.ts +71 -61
  385. package/src/cli/commands/init.ts +3 -1
  386. package/src/cli/commands/mcp.ts +16 -10
  387. package/src/cli/commands/plugin.ts +38 -12
  388. package/src/cli/commands/recovery.ts +507 -80
  389. package/src/cli/commands/runtime.ts +12 -7
  390. package/src/cli/commands/setup-profile.ts +97 -39
  391. package/src/cli/commands/status.ts +2 -1
  392. package/src/cli/controller/check-runner.ts +500 -100
  393. package/src/cli/controller/completion-backlog.ts +3 -3
  394. package/src/cli/controller/completion-orchestrator.ts +4 -43
  395. package/src/cli/controller/completion-target.ts +3 -72
  396. package/src/cli/controller/context/source-materializer.ts +81 -100
  397. package/src/cli/controller/context/types.ts +7 -1
  398. package/src/cli/controller/context-pack.ts +28 -6
  399. package/src/cli/controller/execution-completion.ts +4 -7
  400. package/src/cli/controller/execution-policy.ts +36 -153
  401. package/src/cli/controller/governance.ts +2 -3
  402. package/src/cli/controller/issue-store.ts +4 -99
  403. package/src/cli/controller/launch-agents.ts +59 -18
  404. package/src/cli/controller/operational-plan.ts +24 -51
  405. package/src/cli/controller/runtime-config.ts +5 -1
  406. package/src/cli/controller/stuck-state-migration.ts +2 -3
  407. package/src/cli/controller/systemd-user.ts +148 -0
  408. package/src/cli/controller/types.ts +10 -70
  409. package/src/cli/controller/worklog.ts +67 -13
  410. package/src/cli/editing/edit-operation-contract.ts +92 -0
  411. package/src/cli/editing/edit-session.ts +290 -20
  412. package/src/cli/github/plugin.ts +17 -12
  413. package/src/cli/hook/route-registry.ts +3 -5
  414. package/src/cli/hook/runtime.ts +49 -3
  415. package/src/cli/index.ts +3 -0
  416. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  417. package/src/cli/local-bridge/console-automations.ts +20 -15
  418. package/src/cli/local-bridge/console-view-models.ts +3 -12
  419. package/src/cli/local-bridge/facade-api.ts +105 -205
  420. package/src/cli/local-bridge/job-store.ts +1 -1
  421. package/src/cli/local-bridge/server.ts +48 -174
  422. package/src/cli/local-bridge/types.ts +0 -1
  423. package/src/cli/mcp/access-mode.ts +2 -132
  424. package/src/cli/mcp/access-tools.ts +2 -387
  425. package/src/cli/mcp/audit.ts +2 -32
  426. package/src/cli/mcp/auth.ts +2 -404
  427. package/src/cli/mcp/instructions.ts +2 -34
  428. package/src/cli/mcp/legacy-context.ts +2 -16
  429. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  430. package/src/cli/mcp/multi-repository.ts +2 -436
  431. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  432. package/src/cli/mcp/oauth.ts +2 -375
  433. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  434. package/src/cli/mcp/paths.ts +2 -140
  435. package/src/cli/mcp/policy.ts +2 -232
  436. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  437. package/src/cli/mcp/proxy-env.ts +2 -62
  438. package/src/cli/mcp/readiness-projection.ts +2 -24
  439. package/src/cli/mcp/redaction.ts +2 -21
  440. package/src/cli/mcp/repo.ts +2 -24
  441. package/src/cli/mcp/repository-tools.ts +2 -1613
  442. package/src/cli/mcp/server.ts +2 -951
  443. package/src/cli/mcp/setup.ts +2 -737
  444. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  445. package/src/cli/mcp/tool-contract.ts +2 -14
  446. package/src/cli/mcp/tools.ts +2 -9
  447. package/src/cli/mcp/toolset-names.ts +2 -70
  448. package/src/cli/mcp/toolset-selection.ts +2 -41
  449. package/src/cli/mcp/toolset.ts +2 -336
  450. package/src/cli/mcp/transports/AGENTS.md +1 -10
  451. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  452. package/src/cli/mcp/transports/http.ts +2 -1414
  453. package/src/cli/mcp/transports/session-registry.ts +2 -490
  454. package/src/cli/mcp/transports/stdio.ts +2 -8
  455. package/src/cli/mcp/types.ts +2 -41
  456. package/src/cli/mcp/umbrella-tools.ts +2 -117
  457. package/src/cli/repositories/command-executor.ts +91 -39
  458. package/src/cli/repositories/command-scope.ts +139 -4
  459. package/src/cli/repositories/controller-home.ts +167 -1
  460. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  461. package/src/cli/repositories/git-command-executor.ts +2 -2
  462. package/src/cli/repositories/identity.ts +13 -0
  463. package/src/cli/repositories/registry.ts +145 -60
  464. package/src/cli/repositories/repository-snapshot.ts +53 -7
  465. package/src/cli/repositories/runtime-storage.ts +27 -2
  466. package/src/cli/repositories/safe-patch.ts +34 -38
  467. package/src/cli/repositories/selected-path-actions.ts +22 -2
  468. package/src/cli/repositories/structured-git.ts +167 -27
  469. package/src/cli/repository/session-cache.ts +92 -6
  470. package/src/cli/runtime/helper-runner.ts +7 -0
  471. package/src/cli/runtime-invocation.ts +8 -1
  472. package/src/cli/tools/codegraph.ts +44 -25
  473. package/src/core/adoption/manifest-templates.ts +1 -1
  474. package/src/core/adoption/plan.ts +3 -26
  475. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  476. package/src/effects/process-runner.ts +8 -14
  477. package/src/runtime/context/assistant-context.ts +167 -0
  478. package/src/runtime/context/assistant-work-context.ts +209 -0
  479. package/src/runtime/context/automatic-learning.ts +565 -0
  480. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  481. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  482. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  483. package/src/runtime/context/cognitive-attention.ts +173 -0
  484. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  485. package/src/runtime/context/context-closure.ts +305 -0
  486. package/src/runtime/context/context-pack-process.ts +103 -0
  487. package/src/runtime/context/context-plane.ts +203 -0
  488. package/src/runtime/context/direct-controller-learning.ts +298 -0
  489. package/src/runtime/context/project-engineering-contract.ts +60 -0
  490. package/src/runtime/context/semantic-navigation-contract.ts +9 -0
  491. package/src/runtime/context/semantic-navigation.ts +6 -34
  492. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  493. package/src/runtime/context/typescript-navigation.ts +89 -3
  494. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  495. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  496. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  497. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  498. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  499. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  500. package/src/runtime/control-plane/cleanup-artifact-retention.ts +531 -0
  501. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  502. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +282 -0
  503. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +380 -0
  504. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +113 -16
  505. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  506. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  507. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  508. package/src/runtime/control-plane/execution/retained-work-resume.ts +439 -102
  509. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  510. package/src/runtime/control-plane/execution/validation.ts +34 -11
  511. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +87 -0
  512. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  513. package/src/runtime/control-plane/execution/work-completion-authority.ts +31 -44
  514. package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
  515. package/src/runtime/control-plane/execution/work-evidence-policy.ts +46 -53
  516. package/src/runtime/control-plane/execution/work-execution-support.ts +85 -164
  517. package/src/runtime/control-plane/execution/work-handle-authority.ts +349 -73
  518. package/src/runtime/control-plane/execution/work-handle-store.ts +55 -2
  519. package/src/runtime/control-plane/execution/work-head-settlement.ts +108 -0
  520. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +173 -28
  521. package/src/runtime/control-plane/execution/work-operation-service.ts +92 -40
  522. package/src/runtime/control-plane/execution/work-preparation-service.ts +145 -269
  523. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  524. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  525. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +543 -225
  526. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +59 -45
  527. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  528. package/src/runtime/control-plane/execution/work-verification-service.ts +662 -52
  529. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +205 -16
  530. package/src/runtime/control-plane/facade/capability-registry.ts +380 -44
  531. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  532. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  533. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  534. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  535. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -22
  536. package/src/runtime/control-plane/facade/goal-workloop.ts +565 -1202
  537. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +145 -0
  538. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +266 -52
  539. package/src/runtime/control-plane/facade/index.ts +4 -3
  540. package/src/runtime/control-plane/facade/operation-digest.ts +34 -73
  541. package/src/runtime/control-plane/facade/plan-contract-store.ts +671 -245
  542. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  543. package/src/runtime/control-plane/facade/repository-work-admission.ts +31 -23
  544. package/src/runtime/control-plane/facade/requirement-authority.ts +121 -0
  545. package/src/runtime/control-plane/facade/requirement-board.ts +9 -4
  546. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +85 -0
  547. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  548. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  549. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -15
  550. package/src/runtime/control-plane/facade/types.ts +218 -620
  551. package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
  552. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  553. package/src/runtime/control-plane/generated-cache-retention.ts +336 -19
  554. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  555. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +13 -2
  556. package/src/runtime/control-plane/global-scheduler/maintenance.ts +274 -84
  557. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  558. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  559. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -2
  560. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  561. package/src/runtime/control-plane/global-scheduler/scheduler.ts +227 -109
  562. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  563. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  564. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  565. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  566. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +160 -0
  567. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  568. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +139 -670
  569. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  570. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +25 -6
  571. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -194
  572. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  573. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  574. package/src/runtime/control-plane/persistence/experience-store.ts +302 -0
  575. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  576. package/src/runtime/control-plane/persistence/requirement-store.ts +230 -80
  577. package/src/runtime/control-plane/persistence/sqlite-store.ts +654 -78
  578. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  579. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  580. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  581. package/src/runtime/control-plane/release-retention.ts +154 -26
  582. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  583. package/src/runtime/control-plane/runtime-cleanup.ts +844 -38
  584. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  585. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  586. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +479 -0
  587. package/src/runtime/control-plane/workspace/workspace-store.ts +249 -0
  588. package/src/runtime/diagnostics/incident-repair.ts +573 -0
  589. package/src/runtime/diagnostics/mcp-timing.ts +239 -6
  590. package/src/runtime/diagnostics/performance.ts +6 -14
  591. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  592. package/src/runtime/evidence/artifact-store.ts +216 -35
  593. package/src/runtime/evidence/evidence-store.ts +112 -8
  594. package/src/runtime/evidence/operational-shadow.ts +38 -0
  595. package/src/runtime/evidence/output-handle-index.ts +225 -0
  596. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  597. package/src/runtime/evidence/result-store.ts +158 -44
  598. package/src/runtime/evidence/sensitive-output.ts +4 -4
  599. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  600. package/src/runtime/execution/jobs/store.ts +124 -6
  601. package/src/runtime/execution/managed-workspace.ts +112 -5
  602. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  603. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  604. package/src/runtime/execution/process-runtime/check-facade.ts +24 -10
  605. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  606. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  607. package/src/runtime/execution/process-runtime/check-result.ts +177 -2
  608. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +58 -9
  609. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  610. package/src/runtime/execution/process-runtime/command-facade.ts +117 -114
  611. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  612. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  613. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  614. package/src/runtime/execution/process-runtime/index.ts +4 -0
  615. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  616. package/src/runtime/execution/process-runtime/lightweight-managed.ts +283 -73
  617. package/src/runtime/execution/process-runtime/persisted-check.ts +130 -23
  618. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  619. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  620. package/src/runtime/execution/process-runtime/resource-claims.ts +125 -64
  621. package/src/runtime/execution/process-runtime/runtime.ts +167 -17
  622. package/src/runtime/execution/process-runtime/store.ts +30 -2
  623. package/src/runtime/execution/process-runtime/types.ts +27 -0
  624. package/src/runtime/execution/workers/executor.ts +18 -19
  625. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  626. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  627. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  628. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  629. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  630. package/src/runtime/gateway/mcp/router.ts +2 -1125
  631. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  632. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
  633. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  634. package/src/runtime/maintenance/cleanup.ts +38 -24
  635. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  636. package/src/runtime/platform/computer-platform.ts +14 -0
  637. package/src/runtime/platform/executable-discovery.ts +54 -0
  638. package/src/runtime/platform/fetch-http-server.ts +90 -0
  639. package/src/runtime/platform/service-inventory.ts +130 -0
  640. package/src/runtime/platform/service-manager.ts +146 -0
  641. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  642. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  643. package/src/runtime/plugins/action-application.ts +192 -0
  644. package/src/runtime/plugins/app-store-connect-adapter.ts +47 -35
  645. package/src/runtime/plugins/browser-adapter.ts +898 -135
  646. package/src/runtime/plugins/browser-automation-service.ts +33 -34
  647. package/src/runtime/plugins/browser-macos-bridge.ts +386 -46
  648. package/src/runtime/plugins/browser-manifest-surface.ts +75 -17
  649. package/src/runtime/plugins/browser-native-session-service.ts +64 -14
  650. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  651. package/src/runtime/plugins/browser-registration.ts +32 -0
  652. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  653. package/src/runtime/plugins/browser-runtime.ts +55 -10
  654. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  655. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  656. package/src/runtime/plugins/browser-session-store.ts +243 -80
  657. package/src/runtime/plugins/browser-session-types.ts +2 -98
  658. package/src/runtime/plugins/capability-authorization-grants.ts +272 -59
  659. package/src/runtime/plugins/catalog-trust.ts +82 -0
  660. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
  661. package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
  662. package/src/runtime/plugins/computer-registration.ts +1050 -0
  663. package/src/runtime/plugins/config-store.ts +119 -0
  664. package/src/runtime/plugins/desktop-operator-external-policy.ts +964 -0
  665. package/src/runtime/plugins/desktop-operator-registration.ts +123 -12
  666. package/src/runtime/plugins/errors.ts +8 -1
  667. package/src/runtime/plugins/external-adapter.ts +82 -323
  668. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  669. package/src/runtime/plugins/external-registration.ts +30 -2
  670. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  671. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  672. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  673. package/src/runtime/plugins/first-party-registry.ts +3 -1
  674. package/src/runtime/plugins/github-adapter.ts +1 -2
  675. package/src/runtime/plugins/gmail-adapter.ts +29 -8
  676. package/src/runtime/plugins/google-calendar-adapter.ts +8 -8
  677. package/src/runtime/plugins/google-shared.ts +45 -53
  678. package/src/runtime/plugins/google-tasks-adapter.ts +8 -8
  679. package/src/runtime/plugins/ios-adapter.ts +2 -3
  680. package/src/runtime/plugins/lightweight-action.ts +208 -2
  681. package/src/runtime/plugins/local-system-adapter.ts +192 -2
  682. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  683. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  684. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  685. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  686. package/src/runtime/plugins/resend-adapter.ts +15 -24
  687. package/src/runtime/plugins/store.ts +1003 -387
  688. package/src/runtime/plugins/types.ts +28 -1
  689. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  690. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  691. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  692. package/src/runtime/plugins/xiaohongshu-publish.ts +102 -430
  693. package/src/runtime/projections/controller-context.ts +12 -6
  694. package/src/runtime/projections/git-status-sampler.ts +73 -22
  695. package/src/runtime/projections/invalidation.ts +7 -3
  696. package/src/runtime/projections/materialized-view.ts +93 -43
  697. package/src/runtime/recovery/actions.ts +3 -3
  698. package/src/runtime/recovery/capability-action-application.ts +229 -0
  699. package/src/runtime/recovery/index.ts +3 -0
  700. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  701. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  702. package/src/runtime/recovery/maintenance-executor.ts +701 -168
  703. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  704. package/src/runtime/recovery/store.ts +29 -11
  705. package/src/runtime/release/identity.ts +6 -0
  706. package/src/runtime/release/release-coordinator.ts +296 -0
  707. package/src/runtime/release/release-session.ts +716 -0
  708. package/src/runtime/release/release-state-migration.ts +25 -0
  709. package/src/runtime/resources/leases/store.ts +56 -25
  710. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  711. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  712. package/src/runtime/root/browser-session-composition.ts +59 -0
  713. package/src/runtime/root/computer-composition.ts +376 -0
  714. package/src/runtime/root/computer-target-composition.ts +16 -0
  715. package/src/runtime/root/computer-target-persistence.ts +39 -0
  716. package/src/runtime/root/controller-progression-composition.ts +192 -0
  717. package/src/runtime/root/controller-round-composition.ts +126 -0
  718. package/src/runtime/root/controller-services.ts +2 -0
  719. package/src/runtime/root/deployment-topology.ts +106 -0
  720. package/src/runtime/root/entry.ts +8 -6
  721. package/src/runtime/root/gateway-adapter.ts +3 -3
  722. package/src/runtime/root/known-good-recovery.ts +184 -0
  723. package/src/runtime/root/local-bridge.ts +5 -3
  724. package/src/runtime/root/mcp-transport.ts +116 -23
  725. package/src/runtime/root/ownership.ts +261 -9
  726. package/src/runtime/root/package-connector-service.ts +212 -74
  727. package/src/runtime/root/package-runtime-release.ts +199 -33
  728. package/src/runtime/root/package-runtime-service.ts +77 -108
  729. package/src/runtime/root/release-execution-canary.ts +103 -0
  730. package/src/runtime/root/release-loader.ts +150 -0
  731. package/src/runtime/root/release-manifest.ts +185 -7
  732. package/src/runtime/root/release-materialize.ts +474 -31
  733. package/src/runtime/root/release-store.ts +425 -37
  734. package/src/runtime/root/runtime-lane.ts +191 -0
  735. package/src/runtime/root/runtime.ts +379 -32
  736. package/src/runtime/root/scheduled-controller-composition.ts +76 -0
  737. package/src/runtime/root/scheduler.ts +12 -4
  738. package/src/runtime/root/service-runner.ts +3 -3
  739. package/src/runtime/root/service.ts +73 -45
  740. package/src/runtime/root/status.ts +49 -0
  741. package/src/runtime/root/types.ts +55 -3
  742. package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
  743. package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
  744. package/src/runtime/root/write-fence.ts +50 -2
  745. package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
  746. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  747. package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
  748. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  749. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  750. package/src/runtime/shared/process-environment.ts +36 -10
  751. package/src/runtime/shared/response-budget.ts +1 -3
  752. package/src/runtime/shared/storage-capacity.ts +68 -0
  753. package/src/runtime/standalone-recovery/controller-home-migration.ts +1108 -0
  754. package/src/runtime/standalone-recovery/core.ts +4609 -495
  755. package/src/runtime/standalone-recovery/entry.ts +642 -81
  756. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  757. package/src/runtime/standalone-recovery/installer.ts +399 -177
  758. package/src/runtime/standalone-recovery/mcp-server.ts +79 -0
  759. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  760. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  761. package/src/runtime/standalone-recovery/performance.ts +173 -0
  762. package/src/runtime/standalone-recovery/release.ts +58 -5
  763. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  764. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  765. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  766. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  767. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +11 -4
  768. package/src/runtime/workflow/schedules/engine.ts +298 -484
  769. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  770. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  771. package/src/runtime/workflow/schedules/store.ts +2 -607
  772. package/src/runtime/workflow/schedules/types.ts +2 -127
  773. package/src/runtime/workflow/schedules/work-continuation.ts +220 -16
  774. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  775. package/src/runtime/workflows/runtime.ts +482 -0
  776. package/src/testing/test-governance.ts +95 -30
  777. package/supervisor/chatgpt-conversation.ts +23 -0
  778. package/supervisor/client.ts +201 -0
  779. package/supervisor/control-plane.ts +868 -0
  780. package/supervisor/entry.ts +15 -0
  781. package/supervisor/forge-validators.ts +80 -0
  782. package/supervisor/index.ts +5 -0
  783. package/supervisor/native-browser-adapter.ts +786 -0
  784. package/supervisor/native-messaging/host.ts +39 -0
  785. package/supervisor/native-messaging/manifest.ts +26 -0
  786. package/supervisor/native-messaging/protocol.ts +27 -0
  787. package/supervisor/paths.ts +18 -0
  788. package/supervisor/protocol.ts +202 -0
  789. package/supervisor/server.ts +404 -0
  790. package/supervisor/store.ts +1451 -0
  791. package/supervisor/tsconfig.json +8 -0
  792. package/supervisor/types.ts +255 -0
  793. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  794. package/docs/architecture/history/README.md +0 -7
  795. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  796. package/docs/architecture/history/global-hook-runtime.md +0 -166
  797. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  798. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  799. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  800. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  801. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  802. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  803. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  804. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  805. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  806. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  807. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  808. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  809. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  810. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  811. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  812. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  813. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  814. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  815. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  816. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  817. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  818. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  819. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  820. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  821. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  822. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  823. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  824. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  825. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  826. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  827. package/docs/architecture/requests/archive/2026/root.md +0 -68
  828. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  829. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  830. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  831. package/scripts/archive-architecture-request.sh +0 -4
  832. package/scripts/benchmark-background-check-overlap.ts +0 -228
  833. package/scripts/benchmark-controller-hotpath.ts +0 -212
  834. package/scripts/benchmark-controller-round2.ts +0 -139
  835. package/scripts/benchmark-ios-agent-device.ts +0 -514
  836. package/scripts/benchmark-mcp-host.sh +0 -117
  837. package/scripts/benchmark-mcp-transport.ts +0 -170
  838. package/scripts/benchmark-quality-harness.ts +0 -146
  839. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  840. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  841. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  842. package/scripts/benchmark-thin-harness.ts +0 -251
  843. package/scripts/benchmark-typescript-navigation.ts +0 -186
  844. package/scripts/benchmark-work-efficiency.ts +0 -334
  845. package/scripts/build-controller-ui.ts +0 -28
  846. package/scripts/check-npm-release.sh +0 -32
  847. package/scripts/check-public-release-surface.sh +0 -19
  848. package/scripts/check-release-published.sh +0 -57
  849. package/scripts/check-release-readiness.sh +0 -37
  850. package/scripts/check-release-version.mjs +0 -74
  851. package/scripts/check-tarball-install-smoke.sh +0 -67
  852. package/scripts/package-source-archive.sh +0 -147
  853. package/scripts/public-release-files.txt +0 -39
  854. package/scripts/public-release.ts +0 -174
  855. package/scripts/publish-release-tarball.sh +0 -27
  856. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  857. package/scripts/run-skill-evals.ts +0 -1022
  858. package/scripts/stage-runtime-release.ts +0 -39
  859. package/src/cli/controller/progress.ts +0 -656
  860. package/src/cli/controller/work-mode.ts +0 -181
  861. package/src/cli/local-bridge/console-assets.ts +0 -17
  862. package/src/cli/local-bridge/dashboard.ts +0 -17
  863. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  864. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  865. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  866. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  867. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  868. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  869. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  870. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  871. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  872. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  873. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  874. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  875. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  876. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  877. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  878. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  879. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  880. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  881. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  882. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  883. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  884. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  885. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  886. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  887. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  888. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  889. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  890. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  891. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -385
  892. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -1913
  893. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  894. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -449
  895. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -1118
  896. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -93
  897. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  898. package/src/runtime/control-plane/routing/index.ts +0 -1
  899. package/src/runtime/control-plane/routing/route-policy.ts +0 -382
  900. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
@@ -1,18 +1,20 @@
1
1
  import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'fs';
2
2
  import { basename, dirname, join, relative, resolve } from 'path';
3
3
  import { tmpdir } from 'os';
4
+ import { assertStorageHeadroom } from '../shared/storage-capacity';
4
5
  import { runProcess } from '../../effects/process-runner';
5
- import { cleanupEditSession, getEditSession, listEditSessions, reconcileEditSession } from '../../cli/editing/edit-session';
6
+ import { cleanupEditSession, getEditSession, listEditSessions } from '../../cli/editing/edit-session';
6
7
  import { ensureRepositoryRuntimeStorage, type RepositoryRuntimeStorageReport } from '../../cli/repositories/runtime-storage';
8
+ import { getRepository, selectRepositoryCheckout, setRepositoryCheckoutLifecycle } from '../../cli/repositories/registry';
7
9
  import type { RepositoryRecord } from '../../cli/repositories/types';
8
10
  import { rebuildRepositoryProjection } from '../projections/materialized-view';
9
- import { getWorkContract, readWorkContractStore, transitionWorkContractPhase } from '../control-plane/facade/work-contract-store';
10
- import { getControllerSession, withControllerSessionTerminalizationFence } from '../control-plane/facade/controller-session-store';
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';
11
13
  import { listPlanContracts } from '../control-plane/facade/plan-contract-store';
12
14
  import { readRequirement } from '../control-plane/persistence/requirement-store';
13
15
  import { listControlPlaneRecords, type ControlPlaneRecord } from '../control-plane/persistence/sqlite-store';
14
- import { isTerminalWorkContractStatus, type WorkContract } from '../control-plane/facade/types';
15
- import { listSchedules } from '../workflow/schedules/store';
16
+ import type { WorkContract } from '../control-plane/facade/types';
17
+ import { listSchedules } from '../../../packages/kernel/scheduler/api/index';
16
18
  import {
17
19
  collectRuntimeProcesses,
18
20
  removeRuntimeTempEntry,
@@ -26,6 +28,19 @@ import {
26
28
  type RuntimeStorageRepairPreview,
27
29
  } from './local-jobs-repair';
28
30
  import { gcTerminalProcesses, type ProcessGcResult } from '../execution/process-runtime/gc';
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';
34
+ import {
35
+ cleanupRuntimeQuarantine,
36
+ quarantineRuntimePath,
37
+ runtimeLegacyCheckQuarantineRoot,
38
+ type RuntimeQuarantineRetentionReport,
39
+ } from './quarantine-retention';
40
+ import { readWorkHandle, resolveWorkDeliveryTargetBranch } from '../control-plane/execution/work-handle-store';
41
+ import { listRecoverableProcessRecords } from '../execution/process-runtime/store';
42
+ import { isManagedProcessActive } from '../execution/process-runtime/types';
43
+ import { managedBranchOwnedResourceId, managedBranchOwnedResourceLocator, managedWorkspaceOwnedResourceId } from '../execution/managed-workspace';
29
44
 
30
45
  export type RuntimeMaintenanceActionId =
31
46
  | 'local_jobs_reconcile'
@@ -137,6 +152,10 @@ export interface RuntimeMaintenanceApplyResult extends Omit<RuntimeMaintenanceSt
137
152
  runtimeStorageRepairApply?: RuntimeStorageRepairApplyResult;
138
153
  /** Existing bounded terminal-process retention, run only during explicit full maintenance. */
139
154
  processGc?: ProcessGcResult;
155
+ verificationSnapshotGc?: WorkVerificationSnapshotRetentionReport;
156
+ quarantineGc?: RuntimeQuarantineRetentionReport;
157
+ generatedCacheGc?: RepositoryGeneratedCacheRetentionReport;
158
+ cleanupArtifactGc?: CleanupArtifactRetentionReport;
140
159
  projection?: unknown;
141
160
  }
142
161
 
@@ -189,10 +208,16 @@ function readJson(path: string): unknown {
189
208
  }
190
209
 
191
210
  function writeJsonAtomic(path: string, value: unknown): void {
211
+ const content = `${JSON.stringify(value, null, 2)}\n`;
212
+ assertStorageHeadroom(path, {
213
+ operation: 'runtime_maintenance_state_write',
214
+ requiredBytes: Buffer.byteLength(content),
215
+ reserveBytes: 16 * 1024 * 1024,
216
+ });
192
217
  mkdirSync(dirname(path), { recursive: true });
193
218
  const temporaryPath = `${path}.${process.pid}.${Date.now()}.tmp`;
194
219
  try {
195
- writeFileSync(temporaryPath, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
220
+ writeFileSync(temporaryPath, content, { encoding: 'utf8', mode: 0o600 });
196
221
  renameSync(temporaryPath, path);
197
222
  } finally {
198
223
  rmSync(temporaryPath, { force: true });
@@ -385,7 +410,11 @@ function scanRuntimeTempCandidates(
385
410
  minAgeMinutes: RUNTIME_TEMP_RETENTION_MINUTES,
386
411
  maxEntries: Math.max(maxCandidates * 3, 500),
387
412
  });
388
- 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.
389
418
  return scan.entries
390
419
  .filter((entry) => entry.cleanupCandidate)
391
420
  .slice(0, maxCandidates)
@@ -393,17 +422,15 @@ function scanRuntimeTempCandidates(
393
422
  kind: 'stale_runtime_temp_entry',
394
423
  id: `runtime-temp-${safeId(entry.path)}`,
395
424
  path: entry.path,
396
- safe: entry.cleanupCandidate && ownershipStatus === 'explicit',
397
- reason: ownershipStatus === 'unknown'
398
- ? 'Temp entry has unknown durable ownership; it is protected until an owning lifecycle record or safe reconciliation proves eligibility.'
399
- : entry.symbolicLink
400
- ? 'Symbolic links are never eligible for automatic cleanup.'
401
- : entry.occupiedByPid
402
- ? `Temp entry is referenced by live process ${entry.occupiedByPid}.`
403
- : `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.`,
404
431
  ageMinutes: entry.ageMinutes,
405
432
  suggestedAction: 'full_maintenance_pass',
406
- ownershipStatus,
433
+ ownershipStatus: 'explicit' as const,
407
434
  }));
408
435
  }
409
436
 
@@ -444,7 +471,7 @@ function listRetainedMigratedWork(destinationHomeInput: string, repoId: string):
444
471
  }
445
472
  for (const source of sourceWork) {
446
473
  const work = source.value;
447
- if (work.repoId !== repoId || !isTerminalWorkContractStatus(work.status)) continue;
474
+ if (work.repoId !== repoId || semanticWorkState(work) === 'open') continue;
448
475
  if (!work.worktreeRef?.trim() || !existsSync(work.worktreeRef)) continue;
449
476
  if (destinationWorkIds.has(work.workId)) continue;
450
477
  retained.push({ migrationId: migration.migrationId, sourceHome, work });
@@ -477,6 +504,121 @@ function looksLikeForgeManagedWorktree(path: string): boolean {
477
504
 
478
505
  type StaleWorkSourceState = NonNullable<RuntimeMaintenanceCandidate['sourceState']>;
479
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
+
480
622
  interface StaleWorkSourceInspection {
481
623
  safeToCancel: boolean;
482
624
  state: StaleWorkSourceState;
@@ -484,10 +626,116 @@ interface StaleWorkSourceInspection {
484
626
  detail: string;
485
627
  }
486
628
 
629
+ interface StaleWorkDeliveryTargetResolution {
630
+ targetBranch: string;
631
+ handle?: NonNullable<ReturnType<typeof readWorkHandle>>;
632
+ identityMismatch: boolean;
633
+ }
634
+
635
+ function resolveStaleWorkDeliveryTarget(
636
+ repository: RuntimeMaintenanceRepository,
637
+ controllerHome: string,
638
+ contract: WorkContract,
639
+ ): StaleWorkDeliveryTargetResolution {
640
+ const handle = readWorkHandle(controllerHome, repository.repoId, contract.workId);
641
+ if (!handle) {
642
+ return { targetBranch: repository.defaultBranch?.trim() || 'main', identityMismatch: false };
643
+ }
644
+ if (!contract.checkoutId || handle.checkoutId !== contract.checkoutId) {
645
+ return {
646
+ targetBranch: repository.defaultBranch?.trim() || 'main',
647
+ handle,
648
+ identityMismatch: true,
649
+ };
650
+ }
651
+ return {
652
+ targetBranch: resolveWorkDeliveryTargetBranch(handle, repository.defaultBranch),
653
+ handle,
654
+ identityMismatch: false,
655
+ };
656
+ }
657
+
658
+ function isLegacyImplicitRemoteEffectPlacement(contract: WorkContract): boolean {
659
+ if (contract.workKind !== 'remote_effect' || contract.worktreePolicy.required !== true || !contract.worktreeRef || !contract.checkoutId) return false;
660
+ const reason = contract.worktreePolicy.reason?.trim() ?? '';
661
+ if (/Typed placement/i.test(reason)) return false;
662
+ return true;
663
+ }
664
+
665
+ interface LegacyRemoteEffectPlacementInspection {
666
+ safe: boolean;
667
+ path?: string;
668
+ detail: string;
669
+ checkoutId?: string;
670
+ canonicalCheckoutId?: string;
671
+ branch?: string;
672
+ canonicalBranch?: string;
673
+ }
674
+
675
+ function inspectLegacyRemoteEffectPlacement(
676
+ repository: RuntimeMaintenanceRepository,
677
+ controllerHome: string,
678
+ contract: WorkContract,
679
+ ): LegacyRemoteEffectPlacementInspection | undefined {
680
+ if (!isLegacyImplicitRemoteEffectPlacement(contract)) return undefined;
681
+ const recordedPath = contract.worktreeRef?.trim();
682
+ if (!recordedPath) return undefined;
683
+ const path = resolve(recordedPath);
684
+ if (!looksLikeForgeManagedWorktree(path)) return { safe: false, path, detail: 'Legacy remote_effect placement is outside Forge-managed worktree storage; automatic resource detachment is forbidden.' };
685
+ if (readWorkHandle(controllerHome, repository.repoId, contract.workId)) return { safe: false, path, detail: 'Legacy remote_effect placement unexpectedly has a WorkHandle; repository source authority must be reconciled explicitly.' };
686
+ let registered;
687
+ try { registered = getRepository(repository.repoId, controllerHome, { includeRemoved: true }); } catch (error) {
688
+ return { safe: false, path, detail: `Repository registry could not be read for legacy remote_effect placement: ${error instanceof Error ? error.message : String(error)}` };
689
+ }
690
+ const checkout = registered.checkouts.find((candidate) => candidate.checkoutId === contract.checkoutId);
691
+ if (!checkout) return { safe: false, path, detail: 'Legacy remote_effect checkout identity is missing from the Repository Registry.' };
692
+ if ((checkout.lifecycle ?? 'active') !== 'active' || checkout.worktree !== true) return { safe: false, path, detail: `Legacy remote_effect checkout ${checkout.checkoutId} is not an active registered worktree.` };
693
+ const registeredPath = resolve(checkout.canonicalRoot);
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.' };
695
+ const otherOwner = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts.find((candidate) =>
696
+ candidate.workId !== contract.workId
697
+ && semanticWorkState(candidate) === 'open'
698
+ && candidate.worktreeRef
699
+ && resolve(candidate.worktreeRef) === path);
700
+ if (otherOwner) return { safe: false, path, detail: `Managed worktree is also owned by active Work ${otherOwner.workId}; resource detachment is fenced.` };
701
+ if (!existsSync(path)) return { safe: false, path, detail: 'Legacy remote_effect managed worktree path is absent; registry/source reconciliation is required before detachment.' };
702
+ const status = runProcess('git', ['status', '--porcelain', '--untracked-files=all'], { cwd: path, timeoutMs: 10_000, maxOutputBytes: 500_000 });
703
+ if (!status.ok || status.stdout.trim()) return { safe: false, path, detail: status.ok ? 'Legacy remote_effect managed worktree is dirty; live source must be preserved.' : 'Legacy remote_effect managed worktree status could not be read.' };
704
+ const head = runProcess('git', ['rev-parse', 'HEAD'], { cwd: path, timeoutMs: 10_000, maxOutputBytes: 100_000 });
705
+ const baseRevision = contract.baseRevision?.trim();
706
+ if (!head.ok || !head.stdout.trim() || !baseRevision || head.stdout.trim() !== baseRevision) return { safe: false, path, detail: 'Legacy remote_effect worktree HEAD does not exactly equal the Work base revision; repository delta ownership is ambiguous.' };
707
+ const branch = runProcess('git', ['branch', '--show-current'], { cwd: path, timeoutMs: 10_000, maxOutputBytes: 100_000 });
708
+ if (!branch.ok || !branch.stdout.trim()) return { safe: false, path, detail: 'Legacy remote_effect managed worktree branch could not be resolved.' };
709
+ let canonical;
710
+ try { canonical = selectRepositoryCheckout(registered, registered.activeCheckoutId); } catch (error) {
711
+ return { safe: false, path, detail: `Canonical repository checkout could not be resolved: ${error instanceof Error ? error.message : String(error)}` };
712
+ }
713
+ if (canonical.activeCheckoutId === checkout.checkoutId) return { safe: false, path, detail: 'Legacy remote_effect placement unexpectedly points at the canonical active checkout; automatic detachment is forbidden.' };
714
+ const canonicalBranch = runProcess('git', ['branch', '--show-current'], { cwd: canonical.canonicalRoot, timeoutMs: 10_000, maxOutputBytes: 100_000 });
715
+ if (!canonicalBranch.ok || !canonicalBranch.stdout.trim()) return { safe: false, path, detail: 'Canonical integration branch could not be resolved for legacy remote_effect detachment.' };
716
+ const contained = runProcess('git', ['merge-base', '--is-ancestor', baseRevision, `refs/heads/${canonicalBranch.stdout.trim()}`], { cwd: canonical.canonicalRoot, timeoutMs: 10_000, maxOutputBytes: 100_000 });
717
+ if (!contained.ok) return { safe: false, path, detail: `Legacy remote_effect base revision ${baseRevision} is not contained in canonical branch ${canonicalBranch.stdout.trim()}; source preservation remains required.` };
718
+ return {
719
+ safe: true, path, checkoutId: checkout.checkoutId, canonicalCheckoutId: canonical.activeCheckoutId,
720
+ branch: branch.stdout.trim(), canonicalBranch: canonicalBranch.stdout.trim(),
721
+ detail: `Legacy remote_effect owns a clean no-delta managed worktree at base revision ${baseRevision}, which is already contained in canonical branch ${canonicalBranch.stdout.trim()}; repository placement may be detached without changing Work semantics.`,
722
+ };
723
+ }
724
+
487
725
  function inspectStaleWorkRepositorySource(
488
726
  repository: RuntimeMaintenanceRepository,
727
+ controllerHome: string,
489
728
  contract: WorkContract,
490
729
  ): StaleWorkSourceInspection {
730
+ const legacyRemoteEffect = inspectLegacyRemoteEffectPlacement(repository, controllerHome, contract);
731
+ if (legacyRemoteEffect) {
732
+ return {
733
+ safeToCancel: legacyRemoteEffect.safe,
734
+ state: legacyRemoteEffect.safe ? 'clean_integrated' : 'source_state_unknown',
735
+ path: legacyRemoteEffect.path,
736
+ detail: legacyRemoteEffect.detail,
737
+ };
738
+ }
491
739
  const recordedPath = contract.worktreeRef?.trim();
492
740
  if (!recordedPath) {
493
741
  return {
@@ -506,11 +754,62 @@ function inspectStaleWorkRepositorySource(
506
754
  };
507
755
  }
508
756
  if (!existsSync(path)) {
757
+ const deliveryTarget = resolveStaleWorkDeliveryTarget(repository, controllerHome, contract);
758
+ const handle = deliveryTarget.handle;
759
+ const expectedHead = handle?.expectedHead?.trim();
760
+ if (deliveryTarget.identityMismatch || !handle || !expectedHead) {
761
+ return {
762
+ safeToCancel: false,
763
+ state: 'source_state_unknown',
764
+ path,
765
+ detail: 'Recorded managed worktree path is absent and no identity-matched durable WorkHandle head can prove source preservation; destructive stale-Work cancellation is fenced.',
766
+ };
767
+ }
768
+ const targetBranch = deliveryTarget.targetBranch;
769
+ const head = runProcess('git', ['rev-parse', '--verify', `${expectedHead}^{commit}`], {
770
+ cwd: repository.canonicalRoot,
771
+ timeoutMs: 10_000,
772
+ maxOutputBytes: 100_000,
773
+ });
774
+ const target = runProcess('git', ['rev-parse', '--verify', `refs/heads/${targetBranch}`], {
775
+ cwd: repository.canonicalRoot,
776
+ timeoutMs: 10_000,
777
+ maxOutputBytes: 100_000,
778
+ });
779
+ if (!head.ok || !head.stdout.trim() || !target.ok || !target.stdout.trim()) {
780
+ return {
781
+ safeToCancel: false,
782
+ state: 'source_state_unknown',
783
+ path,
784
+ detail: `Recorded managed worktree path is absent and durable source head/target ${targetBranch} could not be resolved; destructive stale-Work cancellation is fenced.`,
785
+ };
786
+ }
787
+ const unique = runProcess('git', ['rev-list', '--count', `${target.stdout.trim()}..${head.stdout.trim()}`], {
788
+ cwd: repository.canonicalRoot,
789
+ timeoutMs: 10_000,
790
+ maxOutputBytes: 100_000,
791
+ });
792
+ if (!unique.ok || !/^\d+$/.test(unique.stdout.trim())) {
793
+ return {
794
+ safeToCancel: false,
795
+ state: 'source_state_unknown',
796
+ path,
797
+ detail: 'Durable WorkHandle source head could not be compared with its delivery target; destructive stale-Work cancellation is fenced.',
798
+ };
799
+ }
800
+ if (Number(unique.stdout.trim()) > 0) {
801
+ return {
802
+ safeToCancel: false,
803
+ state: 'unintegrated_commits',
804
+ path,
805
+ detail: `Managed worktree is absent, but durable WorkHandle head ${expectedHead} has ${unique.stdout.trim()} commit(s) not reachable from ${targetBranch}; exact source remains preserved in Git and requires explicit adoption or cleanup authority.`,
806
+ };
807
+ }
509
808
  return {
510
- safeToCancel: false,
511
- state: 'source_state_unknown',
809
+ safeToCancel: true,
810
+ state: 'clean_integrated',
512
811
  path,
513
- detail: 'Recorded managed worktree path is absent, so maintenance cannot prove that no unique branch/source remains recoverable through Git or checkout metadata; destructive stale-Work cancellation is fenced.',
812
+ detail: `Managed worktree is absent, but its identity-matched durable WorkHandle head ${expectedHead} is already reachable from ${targetBranch}; no unique source is being discarded.`,
514
813
  };
515
814
  }
516
815
 
@@ -541,7 +840,16 @@ function inspectStaleWorkRepositorySource(
541
840
  timeoutMs: 10_000,
542
841
  maxOutputBytes: 100_000,
543
842
  });
544
- const targetBranch = repository.defaultBranch?.trim() || 'main';
843
+ const deliveryTarget = resolveStaleWorkDeliveryTarget(repository, controllerHome, contract);
844
+ if (deliveryTarget.identityMismatch) {
845
+ return {
846
+ safeToCancel: false,
847
+ state: 'source_state_unknown',
848
+ path,
849
+ detail: 'Managed worktree has a durable WorkHandle whose checkout identity does not match the WorkContract; source containment cannot be proven against that handle and destructive stale-Work cancellation is fenced.',
850
+ };
851
+ }
852
+ const targetBranch = deliveryTarget.targetBranch;
545
853
  const target = runProcess('git', ['rev-parse', '--verify', `refs/heads/${targetBranch}`], {
546
854
  cwd: repository.canonicalRoot,
547
855
  timeoutMs: 10_000,
@@ -584,20 +892,11 @@ function inspectStaleWorkRepositorySource(
584
892
  };
585
893
  }
586
894
 
587
- function staleWorkSemanticTerminalizationReady(contract: WorkContract): boolean {
588
- if (contract.phase !== 'cleanup') return false;
589
- return (['implementation', 'verification', 'delivery'] as const).every((phase) =>
590
- ['satisfied', 'skipped'].includes(contract.phaseEvidence[phase].state));
591
- }
592
-
593
- function staleWorkCandidateReason(source: StaleWorkSourceInspection, semanticReady: boolean): string {
895
+ function staleWorkCandidateReason(source: StaleWorkSourceInspection): string {
594
896
  if (!source.safeToCancel) {
595
- 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.`;
596
- }
597
- if (!semanticReady) {
598
- 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.`;
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.`;
599
898
  }
600
- 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.`;
601
900
  }
602
901
 
603
902
  function scanRetainedWorktreeCandidates(
@@ -624,12 +923,12 @@ function scanRetainedWorktreeCandidates(
624
923
  if (normalized === resolve(repository.canonicalRoot) || currentOwnedPaths.has(normalized)) continue;
625
924
  const migrated = retainedByPath.get(normalized);
626
925
  if (migrated) {
627
- const completed = migrated.work.status === 'completed';
926
+ const completed = semanticWorkState(migrated.work) === 'completed';
628
927
  candidates.push({
629
928
  kind: 'retained_migrated_work',
630
929
  id: migrated.work.workId,
631
930
  path: normalized,
632
- status: migrated.work.status,
931
+ status: migrated.work.semanticState,
633
932
  safe: false,
634
933
  reason: completed
635
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.'
@@ -644,12 +943,28 @@ function scanRetainedWorktreeCandidates(
644
943
  continue;
645
944
  }
646
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
+ }
647
962
  candidates.push({
648
963
  kind: 'unowned_managed_worktree',
649
964
  id: `managed-worktree:${basename(normalized)}`,
650
965
  path: normalized,
651
966
  safe: false,
652
- 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.',
653
968
  suggestedAction: 'full_maintenance_pass',
654
969
  ownershipStatus: 'unknown',
655
970
  disposition: 'ownership_reconciliation',
@@ -673,12 +988,12 @@ function scanRetainedWorktreeCandidates(
673
988
  // path outside current Git inventory only remains lifecycle debt when its
674
989
  // worktree .git marker still exists for manual reconciliation.
675
990
  if (!existsSync(join(normalized, '.git'))) continue;
676
- const completed = migrated.work.status === 'completed';
991
+ const completed = semanticWorkState(migrated.work) === 'completed';
677
992
  candidates.push({
678
993
  kind: 'retained_migrated_work',
679
994
  id: migrated.work.workId,
680
995
  path: normalized,
681
- status: migrated.work.status,
996
+ status: migrated.work.semanticState,
682
997
  safe: false,
683
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.',
684
999
  suggestedAction: 'full_maintenance_pass',
@@ -701,26 +1016,65 @@ function normalizedOptions(options: RuntimeMaintenanceOptions = {}): Required<Ru
701
1016
  };
702
1017
  }
703
1018
 
704
- function activeWorkAuthorityRefs(
1019
+ interface RuntimeMaintenanceAuthoritySnapshot {
1020
+ contracts: WorkContract[];
1021
+ workById: Map<string, WorkContract>;
1022
+ planByWorkId: Map<string, string>;
1023
+ activeRequirementStateById: Map<string, string>;
1024
+ scheduleByWorkId: Map<string, string>;
1025
+ processByWorkId: Map<string, string>;
1026
+ }
1027
+
1028
+ /**
1029
+ * Build one bounded read snapshot for a maintenance status pass. Discovery must
1030
+ * never turn N candidate Works/EditSessions into N full Plan/Schedule/Process
1031
+ * scans. Process liveness comes from Process Runtime's recovery index instead of
1032
+ * enumerating terminal process history. This snapshot is advisory only; mutation
1033
+ * paths still re-read current authority before terminalization.
1034
+ */
1035
+ function buildRuntimeMaintenanceAuthoritySnapshot(
705
1036
  repository: RuntimeMaintenanceRepository,
706
1037
  controllerHome: string,
707
- contract: WorkContract,
708
- ): string[] {
1038
+ ): RuntimeMaintenanceAuthoritySnapshot {
1039
+ const contracts = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts;
1040
+ const workById = new Map(contracts.map((contract) => [contract.workId, contract]));
1041
+ const planByWorkId = new Map<string, string>();
1042
+ for (const plan of listPlanContracts({ controllerHome, repoId: repository.repoId, status: 'active', limit: 100 })) {
1043
+ for (const step of plan.steps) if (step.workId) planByWorkId.set(step.workId, plan.planId);
1044
+ if (plan.planId) {
1045
+ for (const contract of contracts) if (contract.planId === plan.planId) planByWorkId.set(contract.workId, plan.planId);
1046
+ }
1047
+ }
1048
+ const activeRequirementStateById = new Map<string, string>();
1049
+ for (const requirementId of new Set(contracts.flatMap((contract) => contract.requirementId ? [contract.requirementId] : []))) {
1050
+ const requirement = readRequirement({ controllerHome }, requirementId)?.value;
1051
+ if (requirement && !['done', 'cancelled'].includes(requirement.state)) activeRequirementStateById.set(requirementId, requirement.state);
1052
+ }
1053
+ const scheduleByWorkId = new Map<string, string>();
1054
+ for (const schedule of listSchedules(controllerHome, repository.repoId)) {
1055
+ if (!schedule.enabled) continue;
1056
+ const workId = String((schedule.action.arguments as Record<string, unknown> | undefined)?.work_id ?? '').trim();
1057
+ if (workId && !scheduleByWorkId.has(workId)) scheduleByWorkId.set(workId, schedule.scheduleId);
1058
+ }
1059
+ const processByWorkId = new Map<string, string>();
1060
+ for (const process of listRecoverableProcessRecords(controllerHome, repository.repoId)) {
1061
+ if (process.workId && isManagedProcessActive(process) && !processByWorkId.has(process.workId)) processByWorkId.set(process.workId, process.processId);
1062
+ }
1063
+ return { contracts, workById, planByWorkId, activeRequirementStateById, scheduleByWorkId, processByWorkId };
1064
+ }
1065
+
1066
+ function activeWorkAuthorityRefs(contract: WorkContract, snapshot: RuntimeMaintenanceAuthoritySnapshot): string[] {
709
1067
  const refs: string[] = [];
710
- if (getControllerSession({ controllerHome, repoId: repository.repoId }, contract.workId)) refs.push('controller_session');
711
- const activePlans = listPlanContracts({ controllerHome, repoId: repository.repoId, status: 'active', limit: 100 });
712
- const plan = activePlans.find((candidate) => candidate.planId === contract.planId || candidate.steps.some((step) => step.workId === contract.workId));
713
- if (plan) refs.push(`plan:${plan.planId}`);
1068
+ const planId = snapshot.planByWorkId.get(contract.workId);
1069
+ if (planId) refs.push(`plan:${planId}`);
714
1070
  if (contract.requirementId) {
715
- const requirement = readRequirement({ controllerHome }, contract.requirementId)?.value;
716
- if (requirement && !['done', 'cancelled'].includes(requirement.state)) refs.push(`requirement:${requirement.requirementId}:${requirement.state}`);
1071
+ const state = snapshot.activeRequirementStateById.get(contract.requirementId);
1072
+ if (state) refs.push(`requirement:${contract.requirementId}:${state}`);
717
1073
  }
718
- const boundSchedule = listSchedules(controllerHome, repository.repoId).find((schedule) => {
719
- if (!schedule.enabled) return false;
720
- const args = schedule.action.arguments as Record<string, unknown> | undefined;
721
- return args?.work_id === contract.workId;
722
- });
723
- if (boundSchedule) refs.push(`schedule:${boundSchedule.scheduleId}`);
1074
+ const scheduleId = snapshot.scheduleByWorkId.get(contract.workId);
1075
+ if (scheduleId) refs.push(`schedule:${scheduleId}`);
1076
+ const processId = snapshot.processByWorkId.get(contract.workId);
1077
+ if (processId) refs.push(`process:${processId}`);
724
1078
  return refs;
725
1079
  }
726
1080
 
@@ -728,170 +1082,305 @@ function scanStaleWorkContractCandidates(
728
1082
  repository: RuntimeMaintenanceRepository,
729
1083
  controllerHome: string,
730
1084
  options: Required<RuntimeMaintenanceOptions>,
1085
+ snapshot: RuntimeMaintenanceAuthoritySnapshot,
731
1086
  ): RuntimeMaintenanceCandidate[] {
732
1087
  const nowMs = Date.now();
733
- return readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts
734
- .filter((contract) => !isTerminalWorkContractStatus(contract.status))
1088
+ return snapshot.contracts
1089
+ .filter((contract) => semanticWorkState(contract) === 'open')
735
1090
  .map((contract) => {
736
1091
  const updatedMs = Date.parse(contract.updatedAt);
737
1092
  const ageMinutes = Number.isFinite(updatedMs) ? Math.max(0, Math.floor((nowMs - updatedMs) / 60_000)) : 0;
738
1093
  return { contract, ageMinutes };
739
1094
  })
740
1095
  .filter(({ ageMinutes }) => ageMinutes >= options.minAgeMinutes)
741
- .flatMap(({ contract, ageMinutes }) => {
742
- const authorityRefs = activeWorkAuthorityRefs(repository, controllerHome, contract);
743
- // A live Plan/Requirement/Schedule/Controller reference is lifecycle authority,
744
- // not maintenance debt. Keep the Work fenced until that authority disappears;
745
- // if it later becomes unowned, the next scan will surface it as stale.
746
- if (authorityRefs.length > 0) return [];
747
- return [{ contract, ageMinutes, source: inspectStaleWorkRepositorySource(repository, contract) }];
1096
+ .filter(({ contract }) => {
1097
+ // A live Plan/Requirement/Schedule/Process reference is retained migration authority,
1098
+ // not maintenance debt. The authority inventory was read once for this status pass.
1099
+ return activeWorkAuthorityRefs(contract, snapshot).length === 0;
748
1100
  })
749
1101
  .sort((left, right) => right.ageMinutes - left.ageMinutes)
1102
+ // Expensive Git source inspection belongs after the visible candidate budget.
750
1103
  .slice(0, options.maxCandidates)
1104
+ .map(({ contract, ageMinutes }) => ({
1105
+ contract, ageMinutes, source: inspectStaleWorkRepositorySource(repository, controllerHome, contract),
1106
+ }))
751
1107
  .map(({ contract, ageMinutes, source }) => {
752
- const semanticReady = staleWorkSemanticTerminalizationReady(contract);
1108
+ const legacyRemotePlacement = isLegacyImplicitRemoteEffectPlacement(contract);
753
1109
  return {
754
1110
  kind: 'stale_work_contract' as const,
755
1111
  id: contract.workId,
756
1112
  path: source.path,
757
- status: contract.status,
758
- safe: source.safeToCancel && semanticReady,
759
- reason: staleWorkCandidateReason(source, semanticReady),
1113
+ status: contract.semanticState,
1114
+ safe: legacyRemotePlacement ? source.safeToCancel : false,
1115
+ reason: legacyRemotePlacement ? source.detail : staleWorkCandidateReason(source),
760
1116
  ageMinutes,
761
1117
  suggestedAction: 'full_maintenance_pass' as const,
762
1118
  ownershipStatus: 'explicit' as const,
763
1119
  sourceState: source.state,
764
1120
  disposition: !source.safeToCancel
765
1121
  ? ('source_preservation_required' as const)
766
- : !semanticReady
1122
+ : !legacyRemotePlacement
767
1123
  ? ('semantic_completion_required' as const)
768
1124
  : undefined,
769
1125
  };
770
1126
  });
771
1127
  }
772
1128
 
773
- export function applyStaleWorkContractMaintenanceCandidate(
1129
+ function detachLegacyRemoteEffectPlacement(
774
1130
  repository: RuntimeMaintenanceRepository,
775
1131
  controllerHome: string,
1132
+ current: WorkContract,
776
1133
  candidate: RuntimeMaintenanceCandidate,
777
1134
  ): RuntimeMaintenanceCandidate & { applied: boolean; result: string } {
778
- const work = getWorkContract({ controllerHome, repoId: repository.repoId }, candidate.id);
779
- if (!work || isTerminalWorkContractStatus(work.status)) {
780
- return { ...candidate, applied: false, result: 'already_terminal' };
1135
+ const inspection = inspectLegacyRemoteEffectPlacement(repository, controllerHome, current);
1136
+ if (!inspection || !inspection.safe || !inspection.path || !inspection.checkoutId || !inspection.canonicalCheckoutId || !inspection.branch || !inspection.canonicalBranch) {
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' };
781
1138
  }
782
-
783
- // The stale scan is discovery only. Re-evaluate every non-Controller authority
784
- // while holding the canonical ControllerSession lock, then perform the Work
785
- // transition before releasing it. A newer controller_claim therefore either
786
- // wins the lock first and fences this mutation, or starts only after an already
787
- // completed maintenance terminalization; there is no check-then-cancel gap.
788
- const fenced = withControllerSessionTerminalizationFence(
789
- { controllerHome, repoId: repository.repoId },
790
- { workId: work.workId, actor: `runtime-maintenance-terminalize:${work.workId}` },
791
- () => {
792
- const current = getWorkContract({ controllerHome, repoId: repository.repoId }, work.workId);
793
- if (!current || isTerminalWorkContractStatus(current.status)) {
794
- return { ...candidate, applied: false, result: 'already_terminal' };
1139
+ const store = { controllerHome, repoId: repository.repoId };
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 };
1158
+ updateWorkContract(store, current.workId, {
1159
+ checkoutId: inspection.canonicalCheckoutId,
1160
+ worktreeRef: undefined,
1161
+ constraints: { ...current.constraints, workspaceMode: 'current' },
1162
+ worktreePolicy: { required: false, reason: 'Legacy pure remote_effect repository placement detached by explicit runtime maintenance; external-effect execution has no repository source authority.' },
1163
+ });
1164
+ try {
1165
+ setRepositoryCheckoutLifecycle({ controllerHome, repoId: repository.repoId, checkoutId: inspection.checkoutId, lifecycle: 'removed', reason: `Legacy remote_effect placement detached for ${current.workId}.` });
1166
+ const removed = runProcess('git', ['worktree', 'remove', '--force', inspection.path], { cwd: repository.canonicalRoot, timeoutMs: 60_000, maxOutputBytes: 500_000 });
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');
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
+ }
1180
+ const deleted = runProcess('git', ['branch', '--delete', inspection.branch], { cwd: repository.canonicalRoot, timeoutMs: 10_000, maxOutputBytes: 100_000 });
1181
+ if (!deleted.ok) {
1182
+ const forced = runProcess('git', ['branch', '--delete', '--force', inspection.branch], { cwd: repository.canonicalRoot, timeoutMs: 10_000, maxOutputBytes: 100_000 });
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}` };
795
1184
  }
796
- const authorityRefs = activeWorkAuthorityRefs(repository, controllerHome, current);
797
- if (authorityRefs.length > 0) {
1185
+ markOwnedResourceCleaned(controllerHome, managedBranchOwnedResourceId(repository.repoId, inspection.checkoutId), 'forge:legacy-remote-effect-detach');
1186
+ }
1187
+ return { ...candidate, safe: true, reason: inspection.detail, sourceState: 'clean_integrated', disposition: undefined, applied: true, result: 'legacy_remote_effect_placement_detached' };
1188
+ } catch (error) {
1189
+ if (existsSync(inspection.path)) {
1190
+ try { setRepositoryCheckoutLifecycle({ controllerHome, repoId: repository.repoId, checkoutId: inspection.checkoutId, lifecycle: 'active', reason: `Rollback failed legacy remote_effect detach for ${current.workId}.` }); } catch { /* preserve primary failure */ }
1191
+ try { updateWorkContract(store, current.workId, previous); } catch { /* preserve primary failure */ }
1192
+ }
1193
+ throw error;
1194
+ }
1195
+ }
1196
+
1197
+ function detachOwnedManagedWorktree(
1198
+ repository: RuntimeMaintenanceRepository,
1199
+ controllerHome: string,
1200
+ candidate: RuntimeMaintenanceCandidate,
1201
+ ): RuntimeMaintenanceCandidate & { applied: boolean; result: string } {
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
+ };
1214
+ }
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 {
798
1241
  return {
799
1242
  ...candidate,
800
- applied: false,
801
- 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}`,
802
1249
  };
803
1250
  }
804
- // Discovery and mutation are separated by an arbitrary Controller/MCP delay.
805
- // Re-probe the live worktree while the canonical ControllerSession fence is
806
- // held so neither a newer Controller claim nor a late source write can be
807
- // raced by stale maintenance terminalization.
808
- const source = inspectStaleWorkRepositorySource(repository, current);
809
- 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) {
810
1336
  return {
811
1337
  ...candidate,
812
1338
  path: source.path ?? candidate.path,
813
1339
  safe: false,
814
- reason: staleWorkCandidateReason(source, staleWorkSemanticTerminalizationReady(current)),
1340
+ reason: isLegacyImplicitRemoteEffectPlacement(current) ? source.detail : staleWorkCandidateReason(source),
815
1341
  sourceState: source.state,
816
1342
  disposition: 'source_preservation_required' as const,
817
1343
  applied: false,
818
1344
  result: `work_source_preserved:${source.state}`,
819
1345
  };
820
1346
  }
821
- const semanticReady = staleWorkSemanticTerminalizationReady(current);
822
- if (!semanticReady) {
823
- return {
824
- ...candidate,
825
- path: source.path ?? candidate.path,
826
- safe: false,
827
- reason: staleWorkCandidateReason(source, false),
828
- sourceState: source.state,
829
- disposition: 'semantic_completion_required' as const,
830
- applied: false,
831
- result: 'work_semantic_completion_required',
832
- };
1347
+ if (isLegacyImplicitRemoteEffectPlacement(current)) {
1348
+ return detachLegacyRemoteEffectPlacement(repository, controllerHome, current, candidate);
833
1349
  }
834
- transitionWorkContractPhase({ controllerHome, repoId: repository.repoId }, current.workId, {
835
- phase: 'cleanup',
836
- status: 'cancelled',
837
- state: 'skipped',
838
- 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.',
839
- evidenceRefs: current.evidenceRefs,
840
- });
841
- return {
842
- ...candidate,
843
- path: source.path ?? candidate.path,
844
- safe: true,
845
- reason: staleWorkCandidateReason(source, true),
846
- sourceState: source.state,
847
- disposition: undefined,
848
- applied: true,
849
- result: 'work_contract_cancelled_evidence_retained',
850
- };
851
- },
852
- );
853
- if (!fenced.allowed) {
854
- const generation = fenced.owner?.claimGeneration;
855
- return {
856
- ...candidate,
857
- applied: false,
858
- result: `work_terminalization_fenced:${fenced.reason}${typeof generation === 'number' ? `:claim_generation=${generation}` : ''}`,
859
- };
860
- }
861
- 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
+ };
862
1360
  }
863
1361
 
864
1362
  function scanStaleEditSessionCandidates(
865
1363
  repository: RuntimeMaintenanceRepository,
866
- controllerHome: string,
867
1364
  options: Required<RuntimeMaintenanceOptions>,
1365
+ snapshot: RuntimeMaintenanceAuthoritySnapshot,
868
1366
  ): RuntimeMaintenanceCandidate[] {
869
1367
  const nowMs = Date.now();
870
1368
  return listEditSessions(repository.canonicalRoot, Math.min(options.maxCandidates * 3, 500))
871
1369
  .filter((summary) => ['open', 'dirty', 'checked', 'check_failed'].includes(summary.status))
872
- .flatMap((summary) => {
873
- try {
874
- const session = reconcileEditSession(repository.canonicalRoot, summary.sessionId, {
875
- reviewer: 'runtime-maintenance',
876
- note: 'Reconciled during maintenance discovery; source files were not modified.',
877
- });
878
- if (['finalized', 'superseded', 'rolled_back'].includes(session.status)) return [];
879
- const updatedMs = Date.parse(session.updatedAt);
880
- const ageMinutes = Number.isFinite(updatedMs) ? Math.max(0, Math.floor((nowMs - updatedMs) / 60_000)) : 0;
881
- return [{ session, ageMinutes }];
882
- } catch {
883
- return [];
884
- }
1370
+ // Status discovery is read-only. Workspace hashing/Git reconciliation belongs
1371
+ // exclusively to explicit full maintenance for the selected candidate.
1372
+ .map((session) => {
1373
+ const updatedMs = Date.parse(session.updatedAt);
1374
+ const ageMinutes = Number.isFinite(updatedMs) ? Math.max(0, Math.floor((nowMs - updatedMs) / 60_000)) : 0;
1375
+ return { session, ageMinutes };
885
1376
  })
886
1377
  .filter(({ ageMinutes }) => ageMinutes >= options.minAgeMinutes)
887
1378
  .sort((left, right) => right.ageMinutes - left.ageMinutes)
888
1379
  .slice(0, options.maxCandidates)
889
1380
  .flatMap(({ session, ageMinutes }) => {
890
- const work = session.workId
891
- ? getWorkContract({ controllerHome, repoId: repository.repoId }, session.workId)
892
- : undefined;
893
- const terminalWork = Boolean(work && isTerminalWorkContractStatus(work.status));
894
- if (work && !terminalWork && activeWorkAuthorityRefs(repository, controllerHome, work).length > 0) {
1381
+ const work = session.workId ? snapshot.workById.get(session.workId) : undefined;
1382
+ const terminalWork = Boolean(work && semanticWorkState(work) !== 'open');
1383
+ if (work && !terminalWork && activeWorkAuthorityRefs(work, snapshot).length > 0) {
895
1384
  // The Edit Session inherits the live Work lifecycle authority. Reporting it
896
1385
  // as stale maintenance debt while that authority is active would duplicate
897
1386
  // ownership and can permanently block release readiness for valid long work.
@@ -952,7 +1441,10 @@ export function buildRuntimeMaintenanceStatus(
952
1441
  options: RuntimeMaintenanceOptions = {},
953
1442
  ): RuntimeMaintenanceStatus {
954
1443
  const normalized = normalizedOptions(options);
1444
+ // Runtime-storage binding is the compatibility cutover authority for legacy test/
1445
+ // migrated repositories. Read lifecycle owners only after that binding is resolved.
955
1446
  const storage = safeRuntimeStorage(repository, controllerHome);
1447
+ const authoritySnapshot = buildRuntimeMaintenanceAuthoritySnapshot(repository, controllerHome);
956
1448
  const runtimeStorageRepair = previewRuntimeStorageRepair(coerceRepository(repository), controllerHome, {
957
1449
  minAgeMinutes: normalized.minAgeMinutes,
958
1450
  maxCandidates: normalized.maxCandidates,
@@ -960,8 +1452,8 @@ export function buildRuntimeMaintenanceStatus(
960
1452
  const localJobCandidates = scanLocalJobCandidates(repository.canonicalRoot, normalized);
961
1453
  const storageCandidates = runtimeStorageCandidates(storage.report);
962
1454
  const tempCandidates = scanRuntimeTempCandidates(repository, normalized.maxCandidates);
963
- const staleWorkCandidates = scanStaleWorkContractCandidates(repository, controllerHome, normalized);
964
- const staleEditCandidates = scanStaleEditSessionCandidates(repository, controllerHome, normalized);
1455
+ const staleWorkCandidates = scanStaleWorkContractCandidates(repository, controllerHome, normalized, authoritySnapshot);
1456
+ const staleEditCandidates = scanStaleEditSessionCandidates(repository, normalized, authoritySnapshot);
965
1457
  const retainedWorktreeCandidates = scanRetainedWorktreeCandidates(repository, controllerHome, normalized.maxCandidates);
966
1458
  const candidates = [...localJobCandidates, ...storageCandidates, ...staleWorkCandidates, ...staleEditCandidates, ...retainedWorktreeCandidates, ...tempCandidates].slice(0, normalized.maxCandidates);
967
1459
  const summary = summarize(candidates);
@@ -999,7 +1491,7 @@ export function buildRuntimeMaintenanceStatus(
999
1491
  'Pending approvals are not cancelled unless cancel_pending_approvals is explicitly enabled.',
1000
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.',
1001
1493
  'Runtime lifecycle changes and source repair are outside the runtime maintenance executor.',
1002
- '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.',
1003
1495
  ],
1004
1496
  };
1005
1497
  }
@@ -1110,11 +1602,6 @@ export function previewAutomaticRuntimeMaintenance(
1110
1602
  };
1111
1603
  }
1112
1604
 
1113
- function quarantinePath(repoRoot: string, id: string): string {
1114
- const stamp = now().replace(/[:.]/g, '-');
1115
- return join(repoRoot, '.ai', 'harness', 'local-jobs-quarantine', `${stamp}-${safeId(id)}`);
1116
- }
1117
-
1118
1605
  function terminalizeLocalJob(candidate: RuntimeMaintenanceCandidate, status: 'orphaned' | 'cancelled' = 'orphaned'): string {
1119
1606
  if (!candidate.path) throw new Error('LOCAL_JOB_PATH_MISSING');
1120
1607
  const jobPath = join(candidate.path, 'job.json');
@@ -1132,14 +1619,11 @@ function terminalizeLocalJob(candidate: RuntimeMaintenanceCandidate, status: 'or
1132
1619
  return status;
1133
1620
  }
1134
1621
 
1135
- function quarantineLocalJob(repoRoot: string, candidate: RuntimeMaintenanceCandidate): string {
1622
+ function quarantineLocalJob(controllerHome: string, repoId: string, repoRoot: string, candidate: RuntimeMaintenanceCandidate): string {
1136
1623
  if (!candidate.path) throw new Error('LOCAL_JOB_PATH_MISSING');
1137
1624
  const root = localJobsRoot(repoRoot);
1138
1625
  if (!isWithin(root, candidate.path)) throw new Error('LOCAL_JOB_PATH_OUTSIDE_ROOT');
1139
- const destination = quarantinePath(repoRoot, candidate.id);
1140
- mkdirSync(dirname(destination), { recursive: true });
1141
- renameSync(candidate.path, destination);
1142
- return destination;
1626
+ return quarantineRuntimePath(controllerHome, repoId, candidate.path, candidate.id);
1143
1627
  }
1144
1628
 
1145
1629
  function rebuildActiveIndex(repoRoot: string): void {
@@ -1204,7 +1688,7 @@ export function applyRuntimeMaintenance(
1204
1688
  return { ...candidate, applied: true, result: terminalizeLocalJob(candidate, 'cancelled') };
1205
1689
  }
1206
1690
  if (candidate.kind === 'unreadable_local_job' || candidate.kind === 'missing_job_metadata') {
1207
- return { ...candidate, applied: true, result: quarantineLocalJob(repository.canonicalRoot, candidate) };
1691
+ return { ...candidate, applied: true, result: quarantineLocalJob(controllerHome, repository.repoId, repository.canonicalRoot, candidate) };
1208
1692
  }
1209
1693
  if (candidate.kind === 'stale_runtime_temp_entry') {
1210
1694
  if (!candidate.path) throw new Error('RUNTIME_TEMP_PATH_MISSING');
@@ -1220,6 +1704,9 @@ export function applyRuntimeMaintenance(
1220
1704
  if (candidate.kind === 'stale_work_contract') {
1221
1705
  return applyStaleWorkContractMaintenanceCandidate(repository, controllerHome, candidate);
1222
1706
  }
1707
+ if (candidate.kind === 'unowned_managed_worktree') {
1708
+ return detachOwnedManagedWorktree(repository, controllerHome, candidate);
1709
+ }
1223
1710
  if (candidate.kind === 'stale_edit_session') {
1224
1711
  const cleaned = cleanupEditSession(repository.canonicalRoot, candidate.id, {
1225
1712
  reviewer: 'runtime-maintenance',
@@ -1242,6 +1729,48 @@ export function applyRuntimeMaintenance(
1242
1729
  const processGc = options.actionId === 'full_maintenance_pass'
1243
1730
  ? gcTerminalProcesses({ controllerHome, repoId: repository.repoId })
1244
1731
  : undefined;
1732
+ const currentWork = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts;
1733
+ const protectedWorkIds = currentWork
1734
+ .filter((contract) => semanticWorkState(contract) === 'open')
1735
+ .map((contract) => contract.workId);
1736
+ const verificationSnapshotGc = options.actionId === 'full_maintenance_pass'
1737
+ ? cleanupStaleWorkVerificationSnapshots(controllerHome, repository.repoId, {
1738
+ protectedWorkIds,
1739
+ maxEntries: Math.max(100, (options.maxCandidates ?? 50) * 3),
1740
+ maxRemovals: Math.max(1, options.maxCandidates ?? 50),
1741
+ })
1742
+ : undefined;
1743
+ const quarantineGc = options.actionId === 'full_maintenance_pass'
1744
+ ? cleanupRuntimeQuarantine(controllerHome, repository.repoId, repository.canonicalRoot, {
1745
+ maxEntries: Math.max(100, (options.maxCandidates ?? 50) * 3),
1746
+ maxRemovals: Math.max(1, options.maxCandidates ?? 50),
1747
+ additionalRoots: [runtimeLegacyCheckQuarantineRoot(controllerHome, repository.repoId)],
1748
+ })
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
+ }
1245
1774
 
1246
1775
  if (options.actionId === 'runtime_storage_finalize_relocation' || options.actionId === 'full_maintenance_pass' || applied.some((candidate) => candidate.applied) || runtimeStorageRepairApply) {
1247
1776
  rebuildActiveIndex(repository.canonicalRoot);
@@ -1259,6 +1788,10 @@ export function applyRuntimeMaintenance(
1259
1788
  applied,
1260
1789
  runtimeStorageRepairApply,
1261
1790
  processGc,
1791
+ verificationSnapshotGc,
1792
+ quarantineGc,
1793
+ generatedCacheGc,
1794
+ cleanupArtifactGc,
1262
1795
  projection,
1263
1796
  };
1264
1797
  }