@moretea-labs/forge 1.6.0 → 1.7.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 (469) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +4 -0
  4. package/README.md +2 -2
  5. package/README.zh-CN.md +1 -1
  6. package/SKILL.md +2 -2
  7. package/THIRD_PARTY_NOTICES.md +1 -1
  8. package/agents/openai.yaml +1 -1
  9. package/assets/hooks/hook-input.sh +11 -8
  10. package/assets/hooks/lib/session-state.sh +10 -7
  11. package/assets/hooks/lib/workflow-state.sh +13 -177
  12. package/assets/hooks/post-tool-observer.sh +8 -2
  13. package/assets/hooks/prompt-guard.sh +5 -81
  14. package/assets/hooks/session-start-context.sh +56 -1
  15. package/assets/partials/05-workflow.partial.md +1 -1
  16. package/assets/partials-agents/02-operating-mode.partial.md +2 -0
  17. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  18. package/assets/plugin-registry.v1.json +3 -3
  19. package/assets/reference-configs/document-generation.md +1 -1
  20. package/assets/reference-configs/harness-overview.md +1 -1
  21. package/assets/reference-configs/sprint-contracts.md +3 -3
  22. package/assets/skill-version.json +2 -2
  23. package/assets/templates/contract.template.md +2 -7
  24. package/assets/templates/helpers/capture-plan.sh +2 -2
  25. package/assets/templates/helpers/check-architecture-sync.sh +17 -0
  26. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  27. package/assets/templates/helpers/contract-worktree.sh +1 -21
  28. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  29. package/assets/templates/helpers/inspect-project-state.ts +8 -6
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
  32. package/assets/templates/helpers/new-plan.sh +1 -1
  33. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  34. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/helpers/workflow-contract.ts +5 -1
  38. package/assets/templates/plan.template.md +1 -1
  39. package/assets/templates/review.template.md +9 -34
  40. package/assets/workflow-contract.v1.json +10 -19
  41. package/docs/README.md +10 -10
  42. package/docs/architecture/CURRENT.md +178 -0
  43. package/docs/architecture/EVOLUTION.md +42 -0
  44. package/docs/architecture/current/README.md +4 -156
  45. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  46. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  47. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  48. package/docs/architecture/history/README.md +7 -0
  49. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  50. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  51. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  52. package/docs/architecture/history.md +3 -34
  53. package/docs/architecture/index.md +15 -98
  54. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  55. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  56. package/docs/architecture/versions/1.6.md +38 -0
  57. package/docs/architecture/versions/1.7.md +41 -0
  58. package/docs/forge-chatgpt-browser-engine.md +2 -2
  59. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  60. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  61. package/docs/forge-github-issue-launcher.md +1 -1
  62. package/docs/forge-plugin-management.md +1 -1
  63. package/docs/forge-runtime-self-healing-loop.md +1 -1
  64. package/docs/operations/controller-browser-plugin.md +26 -6
  65. package/docs/operations/controller-performance-and-502.md +143 -0
  66. package/docs/operations/controller-reliability-runbook.md +1 -1
  67. package/docs/operations/features.md +2 -2
  68. package/docs/operations/github-repository.md +1 -1
  69. package/docs/operations/homebrew.md +2 -2
  70. package/docs/operations/mcp-tool-exposure.md +5 -3
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Integrations.md +2 -2
  74. package/docs/wiki/Work-Lifecycle.md +1 -1
  75. package/package.json +33 -11
  76. package/scripts/activate-source-baseline.ts +17 -13
  77. package/scripts/architecture-event.ts +2 -820
  78. package/scripts/architecture-queue.sh +2 -645
  79. package/scripts/archive-architecture-request.sh +2 -242
  80. package/scripts/archive-workflow.sh +1 -291
  81. package/scripts/benchmark-background-check-overlap.ts +228 -0
  82. package/scripts/benchmark-mcp-host.sh +117 -0
  83. package/scripts/benchmark-mcp-transport.ts +170 -0
  84. package/scripts/benchmark-quality-harness.ts +146 -0
  85. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  86. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  87. package/scripts/benchmark-typescript-navigation.ts +186 -0
  88. package/scripts/benchmark-work-efficiency.ts +334 -0
  89. package/scripts/capability-config.ts +2 -392
  90. package/scripts/capability-resolver.ts +2 -503
  91. package/scripts/capture-plan.sh +1 -332
  92. package/scripts/check-agent-tooling.sh +2 -1558
  93. package/scripts/check-architecture-sync.sh +2 -371
  94. package/scripts/check-brain-manifest.sh +2 -253
  95. package/scripts/check-context-files.sh +2 -47
  96. package/scripts/check-deploy-sql-order.sh +2 -115
  97. package/scripts/check-mcp-compatibility.ts +14 -0
  98. package/scripts/check-runtime-architecture.mjs +349 -79
  99. package/scripts/check-skill-version.ts +3 -191
  100. package/scripts/check-source-duplication.mjs +61 -0
  101. package/scripts/check-stable-baseline.ts +92 -0
  102. package/scripts/check-task-sync.sh +2 -63
  103. package/scripts/check-task-workflow.sh +2 -1000
  104. package/scripts/codex-handoff-resume.sh +2 -267
  105. package/scripts/context-contract-sync.sh +2 -514
  106. package/scripts/contract-run.ts +2 -463
  107. package/scripts/contract-worktree.sh +1 -722
  108. package/scripts/ensure-task-workflow.sh +1 -1225
  109. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  110. package/scripts/inspect-project-state.ts +4 -279
  111. package/scripts/install-standalone-recovery.ts +24 -0
  112. package/scripts/lib/project-init-lib.sh +22 -46
  113. package/scripts/lib/run-forge-helper.sh +19 -0
  114. package/scripts/lib/run-forge-helper.ts +26 -0
  115. package/scripts/migrate-project-template.sh +1 -1189
  116. package/scripts/migrate-workflow-docs.ts +4 -412
  117. package/scripts/new-plan.sh +1 -180
  118. package/scripts/new-spec.sh +1 -47
  119. package/scripts/new-sprint.sh +1 -46
  120. package/scripts/plan-to-todo.sh +1 -796
  121. package/scripts/prepare-codex-handoff.sh +2 -201
  122. package/scripts/prepare-handoff.sh +1 -78
  123. package/scripts/refresh-current-status.sh +2 -467
  124. package/scripts/run-governed-gate.ts +5 -1
  125. package/scripts/select-agent-context-blocks.sh +2 -64
  126. package/scripts/ship-worktrees.sh +1 -558
  127. package/scripts/smoke-ios-physical-device.ts +4 -2
  128. package/scripts/smoke-runtime-recovery.ts +1 -35
  129. package/scripts/smoke-schedule-engine.ts +1 -20
  130. package/scripts/sprint-backlog.sh +1 -834
  131. package/scripts/stage-runtime-release.ts +2 -1
  132. package/scripts/summarize-failures.sh +2 -123
  133. package/scripts/switch-plan.sh +1 -150
  134. package/scripts/sync-brain-docs.sh +2 -379
  135. package/scripts/sync-generated-authority.mjs +175 -0
  136. package/scripts/verify-contract.sh +2 -703
  137. package/scripts/verify-forge-runtime.sh +0 -1
  138. package/scripts/verify-sprint.sh +1 -677
  139. package/scripts/workflow-contract.ts +4 -205
  140. package/scripts/workstream-sync.sh +2 -261
  141. package/src/cli/agent-jobs/executor-health.ts +1 -1
  142. package/src/cli/commands/init-hook.ts +3 -3
  143. package/src/cli/commands/mcp.ts +9 -5
  144. package/src/cli/commands/plugin.ts +12 -3
  145. package/src/cli/commands/recovery.ts +25 -3
  146. package/src/cli/commands/runtime.ts +55 -3
  147. package/src/cli/commands/security.ts +12 -1
  148. package/src/cli/commands/setup-profile.ts +31 -21
  149. package/src/cli/controller/check-runner.ts +113 -5
  150. package/src/cli/controller/composite-result.ts +1 -1
  151. package/src/cli/controller/context/focus.ts +51 -0
  152. package/src/cli/controller/context/instruction-context.ts +114 -0
  153. package/src/cli/controller/context/known-paths.ts +76 -0
  154. package/src/cli/controller/context/query-planning.ts +110 -0
  155. package/src/cli/controller/context/rule-resolution.ts +532 -0
  156. package/src/cli/controller/context/source-materializer.ts +278 -0
  157. package/src/cli/controller/context/structural.ts +16 -0
  158. package/src/cli/controller/context/types.ts +286 -0
  159. package/src/cli/controller/context-pack.ts +828 -487
  160. package/src/cli/controller/launch-agents.ts +77 -2
  161. package/src/cli/controller/operational-plan.ts +2 -2
  162. package/src/cli/controller/work-mode.ts +6 -4
  163. package/src/cli/editing/edit-session.ts +60 -7
  164. package/src/cli/github/contracts.ts +16 -0
  165. package/src/cli/github/github.ts +135 -16
  166. package/src/cli/github/session.ts +2 -1
  167. package/src/cli/installer/managed-entries.ts +15 -1
  168. package/src/cli/local-bridge/console-automations.ts +54 -78
  169. package/src/cli/local-bridge/console-view-models.ts +1 -35
  170. package/src/cli/local-bridge/facade-api.ts +37 -264
  171. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  172. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  173. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  174. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  175. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  176. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  177. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  178. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  179. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  180. package/src/cli/local-bridge/job-store.ts +1 -490
  181. package/src/cli/local-bridge/server.ts +2 -515
  182. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  183. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  184. package/src/cli/mcp/auth.ts +5 -5
  185. package/src/cli/mcp/instructions.ts +3 -2
  186. package/src/cli/mcp/legacy-tool-service.ts +10 -222
  187. package/src/cli/mcp/multi-repository.ts +16 -5
  188. package/src/cli/mcp/oauth.ts +175 -26
  189. package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
  190. package/src/cli/mcp/repository-tools.ts +282 -146
  191. package/src/cli/mcp/server.ts +586 -116
  192. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  193. package/src/cli/mcp/tool-contract.ts +14 -0
  194. package/src/cli/mcp/toolset-selection.ts +1 -1
  195. package/src/cli/mcp/toolset.ts +44 -7
  196. package/src/cli/mcp/transports/http.ts +124 -14
  197. package/src/cli/mcp/transports/session-registry.ts +43 -5
  198. package/src/cli/mcp/types.ts +3 -3
  199. package/src/cli/repositories/command-classifier.ts +103 -21
  200. package/src/cli/repositories/command-executor.ts +78 -526
  201. package/src/cli/repositories/command-process.ts +108 -0
  202. package/src/cli/repositories/command-scope.ts +99 -41
  203. package/src/cli/repositories/controller-home.ts +7 -8
  204. package/src/cli/repositories/git-command-executor.ts +2 -0
  205. package/src/cli/repositories/locks.ts +53 -16
  206. package/src/cli/repositories/repository-snapshot.ts +124 -0
  207. package/src/cli/repositories/selected-path-actions.ts +17 -9
  208. package/src/cli/repositories/structured-git.ts +118 -6
  209. package/src/cli/repository/inspector.ts +563 -65
  210. package/src/cli/repository/session-cache.ts +58 -17
  211. package/src/cli/v81-entry.ts +1 -4
  212. package/src/effects/fs-transaction.ts +85 -17
  213. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  214. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  215. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  216. package/src/runtime/context/lsp-client.ts +283 -0
  217. package/src/runtime/context/semantic-navigation-contract.ts +61 -0
  218. package/src/runtime/context/semantic-navigation.ts +223 -0
  219. package/src/runtime/context/swift-navigation.ts +565 -0
  220. package/src/runtime/context/typescript-navigation.ts +226 -0
  221. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  222. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
  223. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  224. package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
  225. package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
  226. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  227. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  228. package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
  229. package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
  230. package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
  231. package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
  232. package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
  233. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  234. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  235. package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
  236. package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
  237. package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
  238. package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
  239. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  240. package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
  241. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
  242. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  243. package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
  244. package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
  245. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
  246. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  247. package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
  248. package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
  249. package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
  250. package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
  251. package/src/runtime/control-plane/facade/index.ts +3 -0
  252. package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
  253. package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
  254. package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
  255. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  256. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  257. package/src/runtime/control-plane/facade/types.ts +90 -10
  258. package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
  259. package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
  260. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  261. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  262. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  263. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  264. package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
  265. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  266. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  267. package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
  268. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  269. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  270. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  271. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  272. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  273. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  274. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  275. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  276. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  277. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  278. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
  279. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
  280. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
  281. package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
  282. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  283. package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
  284. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  285. package/src/runtime/control-plane/release-retention.ts +62 -2
  286. package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
  287. package/src/runtime/control-plane/routing/route-policy.ts +24 -21
  288. package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
  289. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  290. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  291. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  292. package/src/runtime/diagnostics/performance.ts +24 -1
  293. package/src/runtime/evidence/event-ledger.ts +1 -1
  294. package/src/runtime/evidence/process-check-execution.ts +18 -0
  295. package/src/runtime/execution/jobs/child-reference.ts +6 -2
  296. package/src/runtime/execution/jobs/store.ts +58 -4
  297. package/src/runtime/execution/jobs/types.ts +1 -1
  298. package/src/runtime/execution/managed-workspace.ts +203 -1
  299. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  300. package/src/runtime/execution/process-runtime/check-result.ts +88 -0
  301. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
  302. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  303. package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
  304. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  305. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
  306. package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
  307. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  308. package/src/runtime/execution/process-runtime/runtime.ts +53 -3
  309. package/src/runtime/execution/process-runtime/store.ts +1 -0
  310. package/src/runtime/execution/process-runtime/types.ts +4 -12
  311. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  312. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  313. package/src/runtime/execution/work-activity.ts +21 -0
  314. package/src/runtime/execution/workers/executor.ts +18 -61
  315. package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
  316. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
  317. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  318. package/src/runtime/gateway/mcp/router.ts +186 -84
  319. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
  320. package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
  321. package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
  322. package/src/runtime/plugins/browser-adapter.ts +2662 -829
  323. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  324. package/src/runtime/plugins/browser-handoff-host.ts +2 -25
  325. package/src/runtime/plugins/browser-handoff.ts +110 -5
  326. package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
  327. package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
  328. package/src/runtime/plugins/browser-native-session-service.ts +222 -0
  329. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  330. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  331. package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
  332. package/src/runtime/plugins/browser-runtime.ts +193 -0
  333. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  334. package/src/runtime/plugins/browser-session-store.ts +121 -0
  335. package/src/runtime/plugins/browser-session-types.ts +98 -0
  336. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  337. package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
  338. package/src/runtime/plugins/external-adapter.ts +358 -3
  339. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  340. package/src/runtime/plugins/first-party-registry.ts +2 -0
  341. package/src/runtime/plugins/github-adapter.ts +125 -0
  342. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  343. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  344. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  345. package/src/runtime/plugins/ios-adapter.ts +106 -16
  346. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  347. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  348. package/src/runtime/plugins/lightweight-action.ts +153 -0
  349. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  350. package/src/runtime/plugins/macos-capability-broker.ts +47 -9
  351. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  352. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  353. package/src/runtime/plugins/resend-adapter.ts +1 -1
  354. package/src/runtime/plugins/store.ts +330 -33
  355. package/src/runtime/plugins/types.ts +38 -3
  356. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  357. package/src/runtime/projections/git-status-sampler.ts +16 -16
  358. package/src/runtime/projections/materialized-view.ts +28 -10
  359. package/src/runtime/recovery/maintenance-executor.ts +426 -25
  360. package/src/runtime/root/mcp-transport.ts +180 -10
  361. package/src/runtime/root/package-connector-service.ts +230 -10
  362. package/src/runtime/root/package-runtime-release.ts +143 -17
  363. package/src/runtime/root/package-runtime-service.ts +461 -22
  364. package/src/runtime/root/release-manifest.ts +144 -18
  365. package/src/runtime/root/release-materialize.ts +125 -32
  366. package/src/runtime/root/release-store.ts +48 -11
  367. package/src/runtime/root/runtime.ts +23 -5
  368. package/src/runtime/root/service.ts +100 -29
  369. package/src/runtime/root/types.ts +12 -0
  370. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  371. package/src/runtime/shared/trusted-executable.ts +62 -0
  372. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  373. package/src/runtime/standalone-recovery/core.ts +1079 -240
  374. package/src/runtime/standalone-recovery/entry.ts +100 -24
  375. package/src/runtime/standalone-recovery/installer.ts +20 -4
  376. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  377. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  378. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  379. package/src/runtime/workflow/schedules/engine.ts +364 -202
  380. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  381. package/src/runtime/workflow/schedules/settlement.ts +64 -17
  382. package/src/runtime/workflow/schedules/store.ts +209 -19
  383. package/src/runtime/workflow/schedules/types.ts +2 -4
  384. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  385. package/src/testing/test-governance.ts +8 -40
  386. package/assets/reference-configs/heartbeat-triage.md +0 -57
  387. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  388. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  389. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  390. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  391. package/docs/architecture/current/architecture-invariants.md +0 -331
  392. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  393. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  394. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  395. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  396. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  397. package/docs/architecture/current/entity-model.md +0 -612
  398. package/docs/architecture/current/external-plugin-governance.md +0 -396
  399. package/docs/architecture/current/failure-recovery.md +0 -447
  400. package/docs/architecture/current/governance.md +0 -213
  401. package/docs/architecture/current/human-interaction-plane.md +0 -87
  402. package/docs/architecture/current/implementation-status.md +0 -128
  403. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  404. package/docs/architecture/current/legacy-governance.md +0 -58
  405. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  406. package/docs/architecture/current/migration-roadmap.md +0 -105
  407. package/docs/architecture/current/multi-repository-execution.md +0 -371
  408. package/docs/architecture/current/operations-runbook.md +0 -45
  409. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  410. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  411. package/docs/architecture/current/repository-access-modes.md +0 -102
  412. package/docs/architecture/current/route-policy.md +0 -15
  413. package/docs/architecture/current/route-session-concurrency.md +0 -85
  414. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  415. package/docs/architecture/current/runtime-directory-map.md +0 -49
  416. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  417. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  418. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  419. package/docs/architecture/current/system-overview.md +0 -169
  420. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  421. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  422. package/docs/architecture/current/thin-harness-v1.md +0 -321
  423. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  424. package/docs/forge-autonomous-goal-loop.md +0 -9
  425. package/docs/forge-provider-configuration.md +0 -7
  426. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  427. package/docs/operations/gmail-assistant-routines.md +0 -186
  428. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  429. package/docs/operations/provider-configuration.md +0 -31
  430. package/scripts/harness-trace-grade.sh +0 -150
  431. package/scripts/heartbeat-triage.sh +0 -361
  432. package/scripts/maintenance-triage.sh +0 -167
  433. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  434. package/src/cli/repositories/goal-registry.ts +0 -239
  435. package/src/runtime/assistant/action-proposals.ts +0 -279
  436. package/src/runtime/assistant/intent.ts +0 -395
  437. package/src/runtime/assistant/model-provider.ts +0 -460
  438. package/src/runtime/assistant/openapi.ts +0 -223
  439. package/src/runtime/assistant/policy.ts +0 -89
  440. package/src/runtime/assistant/readiness.ts +0 -258
  441. package/src/runtime/assistant/routine-runtime.ts +0 -477
  442. package/src/runtime/assistant/schedule-binding.ts +0 -171
  443. package/src/runtime/assistant/standing-grants.ts +0 -375
  444. package/src/runtime/assistant/store.ts +0 -243
  445. package/src/runtime/assistant/types.ts +0 -135
  446. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  447. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  448. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  449. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  450. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  451. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  452. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  453. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  454. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  455. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  456. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  457. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  458. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  459. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  460. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  461. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  462. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  463. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  464. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  465. package/src/runtime/workflow/findings/store.ts +0 -204
  466. package/src/runtime/workflow/findings/types.ts +0 -58
  467. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  468. package/src/runtime/workflow/portfolio/store.ts +0 -185
  469. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -1,14 +1,16 @@
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 { runProcess } from '../../effects/process-runner';
4
5
  import { cleanupEditSession, getEditSession, listEditSessions, reconcileEditSession } from '../../cli/editing/edit-session';
5
6
  import { ensureRepositoryRuntimeStorage, type RepositoryRuntimeStorageReport } from '../../cli/repositories/runtime-storage';
6
7
  import type { RepositoryRecord } from '../../cli/repositories/types';
7
8
  import { rebuildRepositoryProjection } from '../projections/materialized-view';
8
9
  import { getWorkContract, readWorkContractStore, transitionWorkContractPhase } from '../control-plane/facade/work-contract-store';
9
- import { getControllerSession } from '../control-plane/facade/controller-session-store';
10
+ import { getControllerSession, withControllerSessionTerminalizationFence } from '../control-plane/facade/controller-session-store';
10
11
  import { listPlanContracts } from '../control-plane/facade/plan-contract-store';
11
12
  import { readRequirement } from '../control-plane/persistence/requirement-store';
13
+ import { listControlPlaneRecords, type ControlPlaneRecord } from '../control-plane/persistence/sqlite-store';
12
14
  import { isTerminalWorkContractStatus, type WorkContract } from '../control-plane/facade/types';
13
15
  import { listSchedules } from '../workflow/schedules/store';
14
16
  import {
@@ -40,11 +42,14 @@ export type RuntimeMaintenanceCandidateKind =
40
42
  | 'runtime_storage_warning'
41
43
  | 'stale_runtime_temp_entry'
42
44
  | 'stale_work_contract'
43
- | 'stale_edit_session';
45
+ | 'stale_edit_session'
46
+ | 'retained_migrated_work'
47
+ | 'unowned_managed_worktree';
44
48
 
45
49
  export interface RuntimeMaintenanceRepository {
46
50
  repoId: string;
47
51
  canonicalRoot: string;
52
+ defaultBranch?: string;
48
53
  /** Test-only or embedded-runtime override. Production defaults to system temp roots. */
49
54
  runtimeTempRoots?: string[];
50
55
  }
@@ -83,6 +88,11 @@ export interface RuntimeMaintenanceCandidate {
83
88
  deadlineAt?: string;
84
89
  suggestedAction: RuntimeMaintenanceActionId;
85
90
  ownershipStatus?: 'explicit' | 'unknown';
91
+ sourceControllerHome?: string;
92
+ objective?: string;
93
+ continuationPrompt?: string;
94
+ disposition?: 'resume_or_supersede_review' | 'completion_reconciliation' | 'ownership_reconciliation' | 'source_preservation_required' | 'semantic_completion_required';
95
+ sourceState?: 'no_managed_source' | 'clean_integrated' | 'dirty_worktree' | 'unintegrated_commits' | 'source_state_unknown';
86
96
  }
87
97
 
88
98
  export interface RuntimeMaintenanceSummary {
@@ -97,6 +107,8 @@ export interface RuntimeMaintenanceSummary {
97
107
  staleRuntimeTempEntries: number;
98
108
  staleWorkContracts: number;
99
109
  staleEditSessions: number;
110
+ retainedMigratedWork: number;
111
+ unownedManagedWorktrees: number;
100
112
  }
101
113
 
102
114
  export interface RuntimeMaintenanceStatus {
@@ -227,6 +239,8 @@ function summarize(candidates: RuntimeMaintenanceCandidate[]): RuntimeMaintenanc
227
239
  staleRuntimeTempEntries: candidates.filter((candidate) => candidate.kind === 'stale_runtime_temp_entry').length,
228
240
  staleWorkContracts: candidates.filter((candidate) => candidate.kind === 'stale_work_contract').length,
229
241
  staleEditSessions: candidates.filter((candidate) => candidate.kind === 'stale_edit_session').length,
242
+ retainedMigratedWork: candidates.filter((candidate) => candidate.kind === 'retained_migrated_work').length,
243
+ unownedManagedWorktrees: candidates.filter((candidate) => candidate.kind === 'unowned_managed_worktree').length,
230
244
  };
231
245
  }
232
246
 
@@ -393,6 +407,292 @@ function scanRuntimeTempCandidates(
393
407
  }));
394
408
  }
395
409
 
410
+ interface RetainedMigratedWork {
411
+ migrationId: string;
412
+ sourceHome: string;
413
+ work: WorkContract;
414
+ }
415
+
416
+ interface AppliedControllerHomeMigrationRecord {
417
+ migrationId: string;
418
+ status: 'applied' | 'rolled_back';
419
+ sourceHome: string;
420
+ }
421
+
422
+ function listRetainedMigratedWork(destinationHomeInput: string, repoId: string): RetainedMigratedWork[] {
423
+ const destinationHome = resolve(destinationHomeInput);
424
+ const destinationWorkIds = new Set(
425
+ listControlPlaneRecords<WorkContract>(destinationHome, { namespace: 'work_contract', scope: repoId, limit: 5_000 })
426
+ .map((record) => record.key),
427
+ );
428
+ const migrations = listControlPlaneRecords<AppliedControllerHomeMigrationRecord>(destinationHome, {
429
+ namespace: 'controller_home_migration',
430
+ scope: 'controller',
431
+ limit: 100,
432
+ });
433
+ const retained: RetainedMigratedWork[] = [];
434
+ for (const record of migrations) {
435
+ const migration = record.value;
436
+ if (migration.status !== 'applied') continue;
437
+ const sourceHome = resolve(migration.sourceHome);
438
+ if (!existsSync(join(sourceHome, 'control-plane.sqlite'))) continue;
439
+ let sourceWork: ControlPlaneRecord<WorkContract>[];
440
+ try {
441
+ sourceWork = listControlPlaneRecords<WorkContract>(sourceHome, { namespace: 'work_contract', limit: 5_000 });
442
+ } catch {
443
+ continue;
444
+ }
445
+ for (const source of sourceWork) {
446
+ const work = source.value;
447
+ if (work.repoId !== repoId || !isTerminalWorkContractStatus(work.status)) continue;
448
+ if (!work.worktreeRef?.trim() || !existsSync(work.worktreeRef)) continue;
449
+ if (destinationWorkIds.has(work.workId)) continue;
450
+ retained.push({ migrationId: migration.migrationId, sourceHome, work });
451
+ }
452
+ }
453
+ return retained.sort((left, right) => left.work.updatedAt.localeCompare(right.work.updatedAt));
454
+ }
455
+
456
+ function gitWorktreePaths(repoRoot: string): string[] {
457
+ const result = runProcess('git', ['worktree', 'list', '--porcelain'], {
458
+ cwd: repoRoot,
459
+ timeoutMs: 10_000,
460
+ maxOutputBytes: 500_000,
461
+ });
462
+ if (!result.ok) return [];
463
+ return result.stdout
464
+ .split(/\r?\n/)
465
+ .filter((line) => line.startsWith('worktree '))
466
+ .map((line) => resolve(line.slice('worktree '.length).trim()))
467
+ .filter(Boolean);
468
+ }
469
+
470
+ function looksLikeForgeManagedWorktree(path: string): boolean {
471
+ const normalized = resolve(path).replace(/\\/g, '/');
472
+ return normalized.includes('/.forge/managed-worktrees/')
473
+ || normalized.includes('/.forge/controller/managed-worktrees/')
474
+ || normalized.includes('/.repo-harness/managed-worktrees/')
475
+ || normalized.includes('/managed-worktrees/repo_');
476
+ }
477
+
478
+ type StaleWorkSourceState = NonNullable<RuntimeMaintenanceCandidate['sourceState']>;
479
+
480
+ interface StaleWorkSourceInspection {
481
+ safeToCancel: boolean;
482
+ state: StaleWorkSourceState;
483
+ path?: string;
484
+ detail: string;
485
+ }
486
+
487
+ function inspectStaleWorkRepositorySource(
488
+ repository: RuntimeMaintenanceRepository,
489
+ contract: WorkContract,
490
+ ): StaleWorkSourceInspection {
491
+ const recordedPath = contract.worktreeRef?.trim();
492
+ if (!recordedPath) {
493
+ return {
494
+ safeToCancel: true,
495
+ state: 'no_managed_source',
496
+ detail: 'Work has no recorded managed worktree source.',
497
+ };
498
+ }
499
+ const path = resolve(recordedPath);
500
+ if (!looksLikeForgeManagedWorktree(path)) {
501
+ return {
502
+ safeToCancel: true,
503
+ state: 'no_managed_source',
504
+ path,
505
+ detail: 'Recorded Work path is not a Forge-managed worktree and is not disposable maintenance source.',
506
+ };
507
+ }
508
+ if (!existsSync(path)) {
509
+ return {
510
+ safeToCancel: false,
511
+ state: 'source_state_unknown',
512
+ 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.',
514
+ };
515
+ }
516
+
517
+ const status = runProcess('git', ['status', '--porcelain', '--untracked-files=all'], {
518
+ cwd: path,
519
+ timeoutMs: 10_000,
520
+ maxOutputBytes: 500_000,
521
+ });
522
+ if (!status.ok) {
523
+ return {
524
+ safeToCancel: false,
525
+ state: 'source_state_unknown',
526
+ path,
527
+ detail: `Managed worktree source state could not be read (${status.stderr || 'git status failed'}); destructive stale-Work cancellation is fenced.`,
528
+ };
529
+ }
530
+ if (status.stdout.trim()) {
531
+ return {
532
+ safeToCancel: false,
533
+ state: 'dirty_worktree',
534
+ path,
535
+ detail: 'Managed worktree contains uncommitted or untracked repository source; durable metadata/process evidence is not a substitute for preserving the live source checkout.',
536
+ };
537
+ }
538
+
539
+ const head = runProcess('git', ['rev-parse', 'HEAD'], {
540
+ cwd: path,
541
+ timeoutMs: 10_000,
542
+ maxOutputBytes: 100_000,
543
+ });
544
+ const targetBranch = repository.defaultBranch?.trim() || 'main';
545
+ const target = runProcess('git', ['rev-parse', '--verify', `refs/heads/${targetBranch}`], {
546
+ cwd: repository.canonicalRoot,
547
+ timeoutMs: 10_000,
548
+ maxOutputBytes: 100_000,
549
+ });
550
+ if (!head.ok || !head.stdout.trim() || !target.ok || !target.stdout.trim()) {
551
+ return {
552
+ safeToCancel: false,
553
+ state: 'source_state_unknown',
554
+ path,
555
+ detail: `Managed worktree HEAD or target branch ${targetBranch} could not be resolved; destructive stale-Work cancellation is fenced.`,
556
+ };
557
+ }
558
+ const unique = runProcess('git', ['rev-list', '--count', `${target.stdout.trim()}..${head.stdout.trim()}`], {
559
+ cwd: repository.canonicalRoot,
560
+ timeoutMs: 10_000,
561
+ maxOutputBytes: 100_000,
562
+ });
563
+ if (!unique.ok || !/^\d+$/.test(unique.stdout.trim())) {
564
+ return {
565
+ safeToCancel: false,
566
+ state: 'source_state_unknown',
567
+ path,
568
+ detail: 'Managed worktree unique-commit state could not be proven; destructive stale-Work cancellation is fenced.',
569
+ };
570
+ }
571
+ if (Number(unique.stdout.trim()) > 0) {
572
+ return {
573
+ safeToCancel: false,
574
+ state: 'unintegrated_commits',
575
+ path,
576
+ detail: `Managed worktree HEAD has ${unique.stdout.trim()} commit(s) not reachable from ${targetBranch}; explicit semantic adoption or cleanup authority is required.`,
577
+ };
578
+ }
579
+ return {
580
+ safeToCancel: true,
581
+ state: 'clean_integrated',
582
+ path,
583
+ detail: `Managed worktree is clean and its HEAD is already reachable from ${targetBranch}.`,
584
+ };
585
+ }
586
+
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 {
594
+ 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.`;
599
+ }
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.`;
601
+ }
602
+
603
+ function scanRetainedWorktreeCandidates(
604
+ repository: RuntimeMaintenanceRepository,
605
+ controllerHome: string,
606
+ maxCandidates: number,
607
+ ): RuntimeMaintenanceCandidate[] {
608
+ const worktreePaths = gitWorktreePaths(repository.canonicalRoot);
609
+ if (worktreePaths.length === 0) return [];
610
+ const normalizedInventory = new Set(worktreePaths.map((path) => resolve(path)));
611
+ const currentContracts = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts;
612
+ const currentOwnedPaths = new Set(
613
+ currentContracts
614
+ .map((contract) => contract.worktreeRef?.trim())
615
+ .filter((path): path is string => Boolean(path))
616
+ .map((path) => resolve(path)),
617
+ );
618
+ const candidates: RuntimeMaintenanceCandidate[] = [];
619
+ const retained = listRetainedMigratedWork(controllerHome, repository.repoId);
620
+ const retainedByPath = new Map(retained.map((entry) => [resolve(entry.work.worktreeRef!), entry]));
621
+
622
+ for (const path of worktreePaths) {
623
+ const normalized = resolve(path);
624
+ if (normalized === resolve(repository.canonicalRoot) || currentOwnedPaths.has(normalized)) continue;
625
+ const migrated = retainedByPath.get(normalized);
626
+ if (migrated) {
627
+ const completed = migrated.work.status === 'completed';
628
+ candidates.push({
629
+ kind: 'retained_migrated_work',
630
+ id: migrated.work.workId,
631
+ path: normalized,
632
+ status: migrated.work.status,
633
+ safe: false,
634
+ reason: completed
635
+ ? '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.'
636
+ : 'A terminal Work from a retired Controller Home still owns preserved implementation state in a Git worktree. Review and explicitly resume or supersede it under current authority; automatic maintenance must not delete it.',
637
+ suggestedAction: 'full_maintenance_pass',
638
+ ownershipStatus: 'explicit',
639
+ sourceControllerHome: migrated.sourceHome,
640
+ objective: migrated.work.objective,
641
+ continuationPrompt: migrated.work.continuationPrompt,
642
+ disposition: completed ? 'completion_reconciliation' : 'resume_or_supersede_review',
643
+ });
644
+ continue;
645
+ }
646
+ if (!looksLikeForgeManagedWorktree(normalized)) continue;
647
+ candidates.push({
648
+ kind: 'unowned_managed_worktree',
649
+ id: `managed-worktree:${basename(normalized)}`,
650
+ path: normalized,
651
+ 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.',
653
+ suggestedAction: 'full_maintenance_pass',
654
+ ownershipStatus: 'unknown',
655
+ disposition: 'ownership_reconciliation',
656
+ });
657
+ }
658
+
659
+ // A retained migrated Work whose referenced path still exists but is no longer
660
+ // in Git inventory remains preservation debt rather than disappearing silently.
661
+ for (const migrated of retained) {
662
+ const normalized = resolve(migrated.work.worktreeRef!);
663
+ if (normalizedInventory.has(normalized) || currentOwnedPaths.has(normalized)) continue;
664
+ // Legacy WorkContract records sometimes stored the repository checkout/root
665
+ // in worktreeRef even though it was not a Controller-managed worktree. Such
666
+ // historical references remain audit evidence, but must not turn every old
667
+ // repository root into current lifecycle debt. Only known Forge/RepoHarness
668
+ // managed-worktree storage can represent preserved implementation here.
669
+ if (!looksLikeForgeManagedWorktree(normalized)) continue;
670
+ // A removed legacy campaign may leave an empty managed-storage directory
671
+ // behind after its Git metadata and contents are already gone. That is not
672
+ // preserved implementation and must not block lifecycle health. A retained
673
+ // path outside current Git inventory only remains lifecycle debt when its
674
+ // worktree .git marker still exists for manual reconciliation.
675
+ if (!existsSync(join(normalized, '.git'))) continue;
676
+ const completed = migrated.work.status === 'completed';
677
+ candidates.push({
678
+ kind: 'retained_migrated_work',
679
+ id: migrated.work.workId,
680
+ path: normalized,
681
+ status: migrated.work.status,
682
+ safe: false,
683
+ 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
+ suggestedAction: 'full_maintenance_pass',
685
+ ownershipStatus: 'explicit',
686
+ sourceControllerHome: migrated.sourceHome,
687
+ objective: migrated.work.objective,
688
+ continuationPrompt: migrated.work.continuationPrompt,
689
+ disposition: completed ? 'completion_reconciliation' : 'resume_or_supersede_review',
690
+ });
691
+ }
692
+
693
+ return candidates.slice(0, maxCandidates);
694
+ }
695
+
396
696
  function normalizedOptions(options: RuntimeMaintenanceOptions = {}): Required<RuntimeMaintenanceOptions> {
397
697
  return {
398
698
  minAgeMinutes: clampNumber(options.minAgeMinutes, DEFAULT_MIN_AGE_MINUTES, 0, 7 * 24 * 60),
@@ -438,25 +738,129 @@ function scanStaleWorkContractCandidates(
438
738
  return { contract, ageMinutes };
439
739
  })
440
740
  .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) }];
748
+ })
441
749
  .sort((left, right) => right.ageMinutes - left.ageMinutes)
442
750
  .slice(0, options.maxCandidates)
443
- .map(({ contract, ageMinutes }) => {
444
- const authorityRefs = activeWorkAuthorityRefs(repository, controllerHome, contract);
751
+ .map(({ contract, ageMinutes, source }) => {
752
+ const semanticReady = staleWorkSemanticTerminalizationReady(contract);
445
753
  return {
446
754
  kind: 'stale_work_contract' as const,
447
755
  id: contract.workId,
756
+ path: source.path,
448
757
  status: contract.status,
449
- safe: authorityRefs.length === 0,
450
- reason: authorityRefs.length > 0
451
- ? `Nonterminal WorkContract is old but still has active lifecycle authority (${authorityRefs.join(', ')}); maintenance must not cancel it from age alone.`
452
- : 'Nonterminal WorkContract exceeded the explicit maintenance age threshold and has no active Plan, Requirement, Schedule, or Controller authority; explicit full maintenance may cancel it while retaining evidence.',
758
+ safe: source.safeToCancel && semanticReady,
759
+ reason: staleWorkCandidateReason(source, semanticReady),
453
760
  ageMinutes,
454
761
  suggestedAction: 'full_maintenance_pass' as const,
455
762
  ownershipStatus: 'explicit' as const,
763
+ sourceState: source.state,
764
+ disposition: !source.safeToCancel
765
+ ? ('source_preservation_required' as const)
766
+ : !semanticReady
767
+ ? ('semantic_completion_required' as const)
768
+ : undefined,
456
769
  };
457
770
  });
458
771
  }
459
772
 
773
+ export function applyStaleWorkContractMaintenanceCandidate(
774
+ repository: RuntimeMaintenanceRepository,
775
+ controllerHome: string,
776
+ candidate: RuntimeMaintenanceCandidate,
777
+ ): 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' };
781
+ }
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' };
795
+ }
796
+ const authorityRefs = activeWorkAuthorityRefs(repository, controllerHome, current);
797
+ if (authorityRefs.length > 0) {
798
+ return {
799
+ ...candidate,
800
+ applied: false,
801
+ result: `work_authority_became_active:${authorityRefs.join(',')}`,
802
+ };
803
+ }
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) {
810
+ return {
811
+ ...candidate,
812
+ path: source.path ?? candidate.path,
813
+ safe: false,
814
+ reason: staleWorkCandidateReason(source, staleWorkSemanticTerminalizationReady(current)),
815
+ sourceState: source.state,
816
+ disposition: 'source_preservation_required' as const,
817
+ applied: false,
818
+ result: `work_source_preserved:${source.state}`,
819
+ };
820
+ }
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
+ };
833
+ }
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;
862
+ }
863
+
460
864
  function scanStaleEditSessionCandidates(
461
865
  repository: RuntimeMaintenanceRepository,
462
866
  controllerHome: string,
@@ -482,14 +886,20 @@ function scanStaleEditSessionCandidates(
482
886
  .filter(({ ageMinutes }) => ageMinutes >= options.minAgeMinutes)
483
887
  .sort((left, right) => right.ageMinutes - left.ageMinutes)
484
888
  .slice(0, options.maxCandidates)
485
- .map(({ session, ageMinutes }) => {
889
+ .flatMap(({ session, ageMinutes }) => {
486
890
  const work = session.workId
487
891
  ? getWorkContract({ controllerHome, repoId: repository.repoId }, session.workId)
488
892
  : undefined;
489
893
  const terminalWork = Boolean(work && isTerminalWorkContractStatus(work.status));
894
+ if (work && !terminalWork && activeWorkAuthorityRefs(repository, controllerHome, work).length > 0) {
895
+ // The Edit Session inherits the live Work lifecycle authority. Reporting it
896
+ // as stale maintenance debt while that authority is active would duplicate
897
+ // ownership and can permanently block release readiness for valid long work.
898
+ return [];
899
+ }
490
900
  const contractFreeDirect = !session.workId;
491
901
  const safeToReconcile = terminalWork || contractFreeDirect;
492
- return {
902
+ return [{
493
903
  kind: 'stale_edit_session' as const,
494
904
  id: session.sessionId,
495
905
  status: session.status,
@@ -498,11 +908,11 @@ function scanStaleEditSessionCandidates(
498
908
  ? 'Edit Session belongs to a terminal WorkContract; cleanup may reconcile metadata only and must refuse unique uncommitted source changes.'
499
909
  : contractFreeDirect
500
910
  ? 'Contract-free Direct Edit Session has no WorkContract by design; cleanup may reconcile metadata only and must refuse unique uncommitted source changes.'
501
- : 'Edit Session WorkContract is missing or still nonterminal; active-session ownership must remain fenced.',
911
+ : 'Edit Session WorkContract is missing or still nonterminal without live lifecycle authority; active-session ownership must remain fenced.',
502
912
  ageMinutes,
503
913
  suggestedAction: 'full_maintenance_pass' as const,
504
914
  ownershipStatus: safeToReconcile ? 'explicit' as const : 'unknown' as const,
505
- };
915
+ }];
506
916
  });
507
917
  }
508
918
 
@@ -552,7 +962,8 @@ export function buildRuntimeMaintenanceStatus(
552
962
  const tempCandidates = scanRuntimeTempCandidates(repository, normalized.maxCandidates);
553
963
  const staleWorkCandidates = scanStaleWorkContractCandidates(repository, controllerHome, normalized);
554
964
  const staleEditCandidates = scanStaleEditSessionCandidates(repository, controllerHome, normalized);
555
- const candidates = [...localJobCandidates, ...storageCandidates, ...staleWorkCandidates, ...staleEditCandidates, ...tempCandidates].slice(0, normalized.maxCandidates);
965
+ const retainedWorktreeCandidates = scanRetainedWorktreeCandidates(repository, controllerHome, normalized.maxCandidates);
966
+ const candidates = [...localJobCandidates, ...storageCandidates, ...staleWorkCandidates, ...staleEditCandidates, ...retainedWorktreeCandidates, ...tempCandidates].slice(0, normalized.maxCandidates);
556
967
  const summary = summarize(candidates);
557
968
  const blockingCandidates = candidates.filter((candidate) => candidate.kind !== 'stale_runtime_temp_entry');
558
969
  const readyForExecution = storage.report?.readyForExecution === true
@@ -588,6 +999,7 @@ export function buildRuntimeMaintenanceStatus(
588
999
  'Pending approvals are not cancelled unless cancel_pending_approvals is explicitly enabled.',
589
1000
  '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.',
590
1001
  '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.',
591
1003
  ],
592
1004
  };
593
1005
  }
@@ -806,18 +1218,7 @@ export function applyRuntimeMaintenance(
806
1218
  };
807
1219
  }
808
1220
  if (candidate.kind === 'stale_work_contract') {
809
- const work = getWorkContract({ controllerHome, repoId: repository.repoId }, candidate.id);
810
- if (!work || isTerminalWorkContractStatus(work.status)) {
811
- return { ...candidate, applied: false, result: 'already_terminal' };
812
- }
813
- transitionWorkContractPhase({ controllerHome, repoId: repository.repoId }, work.workId, {
814
- phase: 'cleanup',
815
- status: 'cancelled',
816
- state: 'skipped',
817
- summary: 'Cancelled by explicit full maintenance stale Work cleanup; durable evidence retained.',
818
- evidenceRefs: work.evidenceRefs,
819
- });
820
- return { ...candidate, applied: true, result: 'work_contract_cancelled_evidence_retained' };
1221
+ return applyStaleWorkContractMaintenanceCandidate(repository, controllerHome, candidate);
821
1222
  }
822
1223
  if (candidate.kind === 'stale_edit_session') {
823
1224
  const cleaned = cleanupEditSession(repository.canonicalRoot, candidate.id, {