@moretea-labs/forge 1.5.1 → 1.7.0

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 (434) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +46 -0
  3. package/CLAUDE.md +2 -0
  4. package/DESIGN.md +7 -2
  5. package/README.md +4 -4
  6. package/README.zh-CN.md +2 -2
  7. package/SKILL.md +2 -2
  8. package/THIRD_PARTY_NOTICES.md +1 -1
  9. package/agents/openai.yaml +1 -1
  10. package/assets/hooks/hook-input.sh +11 -8
  11. package/assets/hooks/lib/session-state.sh +10 -7
  12. package/assets/hooks/lib/workflow-state.sh +13 -177
  13. package/assets/hooks/post-tool-observer.sh +8 -2
  14. package/assets/hooks/pre-edit-guard.sh +8 -39
  15. package/assets/hooks/prompt-guard.sh +7 -83
  16. package/assets/hooks/session-start-context.sh +56 -1
  17. package/assets/partials/05-workflow.partial.md +1 -1
  18. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  19. package/assets/plugin-registry.v1.json +54 -3
  20. package/assets/reference-configs/document-generation.md +1 -1
  21. package/assets/reference-configs/harness-overview.md +1 -1
  22. package/assets/reference-configs/hook-operations.md +1 -1
  23. package/assets/reference-configs/sprint-contracts.md +3 -3
  24. package/assets/skill-version.json +2 -2
  25. package/assets/templates/contract.template.md +2 -7
  26. package/assets/templates/helpers/capture-plan.sh +2 -2
  27. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  28. package/assets/templates/helpers/contract-worktree.sh +1 -21
  29. package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/new-plan.sh +1 -1
  32. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  33. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  34. package/assets/templates/helpers/sprint-backlog.sh +6 -6
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/plan.template.md +1 -1
  38. package/assets/templates/review.template.md +9 -34
  39. package/assets/workflow-contract.v1.json +10 -19
  40. package/docs/README.md +10 -10
  41. package/docs/architecture/CURRENT.md +170 -0
  42. package/docs/architecture/EVOLUTION.md +42 -0
  43. package/docs/architecture/current/README.md +4 -156
  44. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  45. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  46. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  47. package/docs/architecture/history/README.md +7 -0
  48. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  49. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  50. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  51. package/docs/architecture/history.md +3 -34
  52. package/docs/architecture/index.md +15 -98
  53. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  54. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  55. package/docs/architecture/versions/1.6.md +38 -0
  56. package/docs/architecture/versions/1.7.md +41 -0
  57. package/docs/forge-chatgpt-browser-engine.md +2 -2
  58. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  59. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  60. package/docs/forge-github-issue-launcher.md +1 -1
  61. package/docs/forge-runtime-self-healing-loop.md +1 -1
  62. package/docs/operations/controller-browser-plugin.md +38 -22
  63. package/docs/operations/controller-performance-and-502.md +124 -0
  64. package/docs/operations/controller-reliability-runbook.md +1 -1
  65. package/docs/operations/features.md +3 -3
  66. package/docs/operations/github-repository.md +1 -1
  67. package/docs/operations/homebrew.md +2 -2
  68. package/docs/operations/mcp-tool-exposure.md +5 -3
  69. package/docs/tutorials/01-install-and-start.md +1 -1
  70. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Installation.md +1 -1
  74. package/docs/wiki/Integrations.md +2 -2
  75. package/docs/wiki/Quick-Start.md +1 -1
  76. package/docs/wiki/Work-Lifecycle.md +1 -1
  77. package/package.json +29 -10
  78. package/scripts/activate-source-baseline.ts +17 -13
  79. package/scripts/benchmark-background-check-overlap.ts +228 -0
  80. package/scripts/benchmark-mcp-host.sh +117 -0
  81. package/scripts/benchmark-mcp-transport.ts +170 -0
  82. package/scripts/benchmark-quality-harness.ts +146 -0
  83. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  84. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  85. package/scripts/benchmark-typescript-navigation.ts +186 -0
  86. package/scripts/benchmark-work-efficiency.ts +334 -0
  87. package/scripts/capture-plan.sh +3 -3
  88. package/scripts/check-agent-tooling.sh +8 -35
  89. package/scripts/check-architecture-sync.sh +17 -86
  90. package/scripts/check-mcp-compatibility.ts +14 -0
  91. package/scripts/check-public-docs.sh +14 -1
  92. package/scripts/check-runtime-architecture.mjs +107 -64
  93. package/scripts/check-stable-baseline.ts +92 -0
  94. package/scripts/check-task-workflow.sh +4 -4
  95. package/scripts/contract-worktree.sh +1 -21
  96. package/scripts/ensure-task-workflow.sh +22 -55
  97. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  98. package/scripts/install-standalone-recovery.ts +24 -0
  99. package/scripts/lib/project-init-lib.sh +15 -44
  100. package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
  101. package/scripts/migrate-project-template.sh +23 -1179
  102. package/scripts/new-plan.sh +1 -1
  103. package/scripts/plan-to-todo.sh +11 -36
  104. package/scripts/run-governed-gate.ts +2 -1
  105. package/scripts/ship-worktrees.sh +2 -29
  106. package/scripts/smoke-ios-physical-device.ts +4 -2
  107. package/scripts/smoke-runtime-recovery.ts +1 -35
  108. package/scripts/smoke-schedule-engine.ts +1 -20
  109. package/scripts/sprint-backlog.sh +1 -1
  110. package/scripts/verify-contract.sh +1 -111
  111. package/scripts/verify-forge-runtime.sh +0 -1
  112. package/scripts/verify-sprint.sh +3 -180
  113. package/scripts/workflow-contract.ts +1 -5
  114. package/src/cli/commands/chatgpt.ts +31 -2
  115. package/src/cli/commands/init-hook.ts +3 -3
  116. package/src/cli/commands/mcp.ts +9 -5
  117. package/src/cli/commands/plugin.ts +37 -10
  118. package/src/cli/commands/recovery.ts +25 -3
  119. package/src/cli/commands/runtime.ts +55 -3
  120. package/src/cli/commands/security.ts +12 -1
  121. package/src/cli/commands/setup-profile.ts +31 -21
  122. package/src/cli/controller/check-runner.ts +106 -3
  123. package/src/cli/controller/composite-result.ts +1 -1
  124. package/src/cli/controller/context/focus.ts +51 -0
  125. package/src/cli/controller/context/instruction-context.ts +114 -0
  126. package/src/cli/controller/context/known-paths.ts +76 -0
  127. package/src/cli/controller/context/query-planning.ts +110 -0
  128. package/src/cli/controller/context/rule-resolution.ts +532 -0
  129. package/src/cli/controller/context/source-materializer.ts +278 -0
  130. package/src/cli/controller/context/structural.ts +16 -0
  131. package/src/cli/controller/context/types.ts +286 -0
  132. package/src/cli/controller/context-pack.ts +898 -430
  133. package/src/cli/controller/launch-agents.ts +77 -2
  134. package/src/cli/controller/operational-plan.ts +2 -2
  135. package/src/cli/controller/work-mode.ts +6 -4
  136. package/src/cli/editing/edit-session.ts +60 -7
  137. package/src/cli/github/github.ts +133 -0
  138. package/src/cli/hook/prompt-guard-decision.ts +17 -18
  139. package/src/cli/hook/prompt-intents.ts +1 -1
  140. package/src/cli/installer/managed-entries.ts +15 -1
  141. package/src/cli/local-bridge/console-automations.ts +225 -67
  142. package/src/cli/local-bridge/console-view-models.ts +3 -35
  143. package/src/cli/local-bridge/facade-api.ts +2 -217
  144. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  145. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
  146. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  147. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
  148. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
  149. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
  150. package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
  151. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  152. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
  153. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
  154. package/src/cli/local-bridge/frontend/src/styles.css +27 -11
  155. package/src/cli/local-bridge/frontend/src/types.ts +8 -7
  156. package/src/cli/local-bridge/job-store.ts +1 -490
  157. package/src/cli/local-bridge/server.ts +29 -532
  158. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  159. package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
  160. package/src/cli/mcp/auth.ts +5 -5
  161. package/src/cli/mcp/instructions.ts +3 -2
  162. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  163. package/src/cli/mcp/multi-repository.ts +16 -5
  164. package/src/cli/mcp/oauth.ts +175 -26
  165. package/src/cli/mcp/paths.ts +8 -2
  166. package/src/cli/mcp/repository-tools.ts +356 -101
  167. package/src/cli/mcp/server.ts +625 -54
  168. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  169. package/src/cli/mcp/toolset-selection.ts +1 -1
  170. package/src/cli/mcp/toolset.ts +42 -5
  171. package/src/cli/mcp/transports/http.ts +125 -15
  172. package/src/cli/mcp/transports/session-registry.ts +67 -5
  173. package/src/cli/mcp/types.ts +3 -3
  174. package/src/cli/repositories/command-classifier.ts +209 -14
  175. package/src/cli/repositories/command-executor.ts +55 -521
  176. package/src/cli/repositories/command-process.ts +108 -0
  177. package/src/cli/repositories/command-scope.ts +99 -41
  178. package/src/cli/repositories/controller-home.ts +7 -8
  179. package/src/cli/repositories/git-command-executor.ts +2 -0
  180. package/src/cli/repositories/locks.ts +53 -16
  181. package/src/cli/repositories/repository-snapshot.ts +121 -0
  182. package/src/cli/repositories/safe-patch.ts +6 -4
  183. package/src/cli/repositories/selected-path-actions.ts +17 -9
  184. package/src/cli/repositories/structured-git.ts +110 -10
  185. package/src/cli/repository/inspector.ts +565 -62
  186. package/src/cli/repository/session-cache.ts +58 -17
  187. package/src/cli/v81-entry.ts +1 -4
  188. package/src/effects/fs-transaction.ts +85 -17
  189. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  190. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  191. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  192. package/src/runtime/context/lsp-client.ts +283 -0
  193. package/src/runtime/context/semantic-navigation.ts +275 -0
  194. package/src/runtime/context/swift-navigation.ts +565 -0
  195. package/src/runtime/context/typescript-navigation.ts +226 -0
  196. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  197. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +196 -2
  198. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  199. package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
  200. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  201. package/src/runtime/control-plane/execution/validation.ts +15 -2
  202. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  203. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  204. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  205. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  206. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  207. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
  208. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
  209. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
  210. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  211. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  212. package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
  213. package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
  214. package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
  215. package/src/runtime/control-plane/facade/index.ts +1 -0
  216. package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
  217. package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
  218. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  219. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  220. package/src/runtime/control-plane/facade/types.ts +55 -11
  221. package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
  222. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  223. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  224. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  225. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  226. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  227. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  228. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  229. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  230. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  231. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  232. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  233. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  234. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  235. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  236. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  237. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  238. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  239. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  240. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  241. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
  242. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  243. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  244. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  245. package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
  246. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  247. package/src/runtime/control-plane/release-retention.ts +62 -2
  248. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  249. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  250. package/src/runtime/diagnostics/mcp-timing.ts +19 -0
  251. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  252. package/src/runtime/diagnostics/performance.ts +24 -1
  253. package/src/runtime/evidence/event-ledger.ts +1 -1
  254. package/src/runtime/execution/jobs/store.ts +58 -4
  255. package/src/runtime/execution/jobs/types.ts +1 -1
  256. package/src/runtime/execution/managed-workspace.ts +203 -1
  257. package/src/runtime/execution/process-runtime/check-facade.ts +52 -14
  258. package/src/runtime/execution/process-runtime/check-result.ts +63 -2
  259. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  260. package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
  261. package/src/runtime/execution/process-runtime/gc.ts +98 -12
  262. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  263. package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
  264. package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
  265. package/src/runtime/execution/process-runtime/runtime.ts +59 -4
  266. package/src/runtime/execution/process-runtime/store.ts +7 -0
  267. package/src/runtime/execution/process-runtime/types.ts +2 -0
  268. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  269. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  270. package/src/runtime/execution/workers/executor.ts +0 -48
  271. package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
  272. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  273. package/src/runtime/gateway/mcp/router.ts +185 -83
  274. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  275. package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
  276. package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
  277. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  278. package/src/runtime/plugins/browser-adapter.ts +2925 -318
  279. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  280. package/src/runtime/plugins/browser-handoff-host.ts +6 -31
  281. package/src/runtime/plugins/browser-handoff.ts +92 -5
  282. package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
  283. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  284. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  285. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  286. package/src/runtime/plugins/browser-runtime.ts +183 -0
  287. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  288. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  289. package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
  290. package/src/runtime/plugins/external-adapter.ts +324 -3
  291. package/src/runtime/plugins/external-registration.ts +84 -5
  292. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  293. package/src/runtime/plugins/first-party-registry.ts +4 -2
  294. package/src/runtime/plugins/github-adapter.ts +125 -0
  295. package/src/runtime/plugins/gmail-adapter.ts +28 -2
  296. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  297. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  298. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  299. package/src/runtime/plugins/ios-adapter.ts +106 -16
  300. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  301. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  302. package/src/runtime/plugins/lightweight-action.ts +153 -0
  303. package/src/runtime/plugins/local-system-adapter.ts +125 -8
  304. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  305. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  306. package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
  307. package/src/runtime/plugins/resend-adapter.ts +1 -1
  308. package/src/runtime/plugins/store.ts +366 -24
  309. package/src/runtime/plugins/types.ts +39 -2
  310. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  311. package/src/runtime/projections/git-status-sampler.ts +16 -16
  312. package/src/runtime/projections/materialized-view.ts +28 -10
  313. package/src/runtime/recovery/actions.ts +0 -10
  314. package/src/runtime/recovery/classifier.ts +0 -9
  315. package/src/runtime/recovery/maintenance-executor.ts +415 -18
  316. package/src/runtime/recovery/probe.ts +0 -1
  317. package/src/runtime/recovery/types.ts +0 -1
  318. package/src/runtime/root/mcp-transport.ts +180 -10
  319. package/src/runtime/root/package-connector-service.ts +186 -8
  320. package/src/runtime/root/package-runtime-release.ts +143 -17
  321. package/src/runtime/root/package-runtime-service.ts +430 -22
  322. package/src/runtime/root/release-manifest.ts +7 -2
  323. package/src/runtime/root/release-materialize.ts +162 -19
  324. package/src/runtime/root/release-store.ts +48 -11
  325. package/src/runtime/root/runtime.ts +23 -5
  326. package/src/runtime/root/service.ts +100 -27
  327. package/src/runtime/root/types.ts +2 -0
  328. package/src/runtime/safe-tooling/index.ts +0 -1
  329. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  330. package/src/runtime/safe-tooling/types.ts +0 -30
  331. package/src/runtime/shared/trusted-executable.ts +62 -0
  332. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  333. package/src/runtime/standalone-recovery/core.ts +702 -94
  334. package/src/runtime/standalone-recovery/entry.ts +99 -23
  335. package/src/runtime/standalone-recovery/installer.ts +19 -3
  336. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  337. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  338. package/src/runtime/workflow/schedules/browser-probe.ts +258 -0
  339. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
  340. package/src/runtime/workflow/schedules/engine.ts +509 -80
  341. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  342. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  343. package/src/runtime/workflow/schedules/store.ts +131 -10
  344. package/src/runtime/workflow/schedules/types.ts +6 -3
  345. package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
  346. package/src/testing/test-governance.ts +8 -40
  347. package/assets/reference-configs/heartbeat-triage.md +0 -57
  348. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  349. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  350. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  351. package/bin/forge-desktop-helper.mjs +0 -204
  352. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  353. package/docs/architecture/current/architecture-invariants.md +0 -331
  354. package/docs/architecture/current/automation-and-schedule-engine.md +0 -453
  355. package/docs/architecture/current/codegraph-context-provider.md +0 -226
  356. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  357. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  358. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -354
  359. package/docs/architecture/current/entity-model.md +0 -592
  360. package/docs/architecture/current/external-plugin-governance.md +0 -396
  361. package/docs/architecture/current/failure-recovery.md +0 -447
  362. package/docs/architecture/current/governance.md +0 -213
  363. package/docs/architecture/current/human-interaction-plane.md +0 -87
  364. package/docs/architecture/current/implementation-status.md +0 -128
  365. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  366. package/docs/architecture/current/legacy-governance.md +0 -58
  367. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  368. package/docs/architecture/current/migration-roadmap.md +0 -105
  369. package/docs/architecture/current/multi-repository-execution.md +0 -371
  370. package/docs/architecture/current/operations-runbook.md +0 -45
  371. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  372. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  373. package/docs/architecture/current/repository-access-modes.md +0 -102
  374. package/docs/architecture/current/route-policy.md +0 -15
  375. package/docs/architecture/current/route-session-concurrency.md +0 -85
  376. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  377. package/docs/architecture/current/runtime-directory-map.md +0 -49
  378. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  379. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  380. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  381. package/docs/architecture/current/system-overview.md +0 -144
  382. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  383. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  384. package/docs/architecture/current/thin-harness-v1.md +0 -311
  385. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  386. package/docs/forge-autonomous-goal-loop.md +0 -9
  387. package/docs/forge-provider-configuration.md +0 -7
  388. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -83
  389. package/docs/operations/controller-desktop-plugin.md +0 -112
  390. package/docs/operations/gmail-assistant-routines.md +0 -186
  391. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  392. package/docs/operations/provider-configuration.md +0 -31
  393. package/scripts/harness-trace-grade.sh +0 -150
  394. package/scripts/heartbeat-triage.sh +0 -361
  395. package/scripts/maintenance-triage.sh +0 -167
  396. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  397. package/src/cli/repositories/goal-registry.ts +0 -239
  398. package/src/runtime/assistant/action-proposals.ts +0 -279
  399. package/src/runtime/assistant/intent.ts +0 -395
  400. package/src/runtime/assistant/model-provider.ts +0 -460
  401. package/src/runtime/assistant/openapi.ts +0 -223
  402. package/src/runtime/assistant/policy.ts +0 -89
  403. package/src/runtime/assistant/readiness.ts +0 -258
  404. package/src/runtime/assistant/routine-runtime.ts +0 -477
  405. package/src/runtime/assistant/schedule-binding.ts +0 -171
  406. package/src/runtime/assistant/standing-grants.ts +0 -375
  407. package/src/runtime/assistant/store.ts +0 -243
  408. package/src/runtime/assistant/types.ts +0 -135
  409. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  410. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  411. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  412. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  413. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  414. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  415. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  416. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  417. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  418. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  419. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  420. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  421. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  422. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  423. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  424. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  425. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  426. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  427. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  428. package/src/runtime/plugins/desktop-adapter.ts +0 -374
  429. package/src/runtime/safe-tooling/browser-safe.ts +0 -133
  430. package/src/runtime/workflow/findings/store.ts +0 -204
  431. package/src/runtime/workflow/findings/types.ts +0 -58
  432. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  433. package/src/runtime/workflow/portfolio/store.ts +0 -185
  434. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -0,0 +1,487 @@
1
+ import { createHash, randomUUID } from 'crypto';
2
+ import { chmodSync, createReadStream, existsSync, mkdirSync, renameSync, statSync, writeFileSync } from 'fs';
3
+ import { dirname, join, resolve } from 'path';
4
+ import {
5
+ backupControlPlaneDatabase,
6
+ CONTROL_PLANE_SCHEMA_VERSION,
7
+ inspectControlPlaneDatabaseFile,
8
+ listControlPlaneRecords,
9
+ readControlPlaneRecord,
10
+ readControlPlaneRecordWithinTransaction,
11
+ withControlPlaneTransaction,
12
+ writeControlPlaneRecord,
13
+ writeControlPlaneRecordWithinTransaction,
14
+ type ControlPlaneDatabaseInspection,
15
+ type ControlPlaneRecord,
16
+ } from './sqlite-store';
17
+ import { listRepositories } from '../../../cli/repositories/registry';
18
+
19
+ const MIGRATION_NAMESPACE = 'controller_home_migration';
20
+ const MIGRATION_SCOPE = 'controller';
21
+ const IMPORTABLE_REFERENCED_NAMESPACES = [
22
+ 'execution_work_validation_index',
23
+ 'execution_edit_validation_run',
24
+ 'execution_edit_validation_index',
25
+ 'chatgpt_work_conversation_binding',
26
+ 'controller_session_claim_store',
27
+ 'controller_round_relay',
28
+ 'external_controller_launch_reservation',
29
+ ] as const;
30
+ const TERMINAL_WORK_STATUSES = new Set(['completed', 'failed', 'cancelled']);
31
+
32
+ export interface ControllerHomeMigrationRecordRef {
33
+ namespace: string;
34
+ scope: string;
35
+ key: string;
36
+ schemaVersion: number;
37
+ sourceRevision: number;
38
+ sourceUpdatedAt: string;
39
+ }
40
+
41
+ export interface ControllerHomeMigrationConflict extends ControllerHomeMigrationRecordRef {
42
+ kind: 'payload_conflict';
43
+ }
44
+
45
+ export interface ControllerHomeMigrationPreview {
46
+ sourceHome: string;
47
+ destinationHome: string;
48
+ sourceDatabase: {
49
+ path: string;
50
+ sha256: string;
51
+ walPath?: string;
52
+ walSha256?: string;
53
+ inspection: ControlPlaneDatabaseInspection;
54
+ };
55
+ destinationDatabase: {
56
+ path: string;
57
+ exists: boolean;
58
+ inspection?: ControlPlaneDatabaseInspection;
59
+ };
60
+ selected: ControllerHomeMigrationRecordRef[];
61
+ selectedByNamespace: Record<string, number>;
62
+ identical: ControllerHomeMigrationRecordRef[];
63
+ conflicts: ControllerHomeMigrationConflict[];
64
+ archivedOnly: {
65
+ terminalWorkContracts: number;
66
+ excludedNamespaces: readonly string[];
67
+ auditChain: 'retained_in_source_sqlite';
68
+ };
69
+ }
70
+
71
+ interface ImportedRecord extends ControllerHomeMigrationRecordRef {
72
+ destinationRevision: number;
73
+ }
74
+
75
+ interface ControllerHomeMigrationRecord {
76
+ schemaVersion: 1;
77
+ migrationId: string;
78
+ status: 'applied' | 'rolled_back';
79
+ sourceHome: string;
80
+ destinationHome: string;
81
+ sourceDatabase: ControllerHomeMigrationPreview['sourceDatabase'];
82
+ destinationBackupPath: string;
83
+ selected: ControllerHomeMigrationRecordRef[];
84
+ imported: ImportedRecord[];
85
+ identical: ControllerHomeMigrationRecordRef[];
86
+ archivedOnly: ControllerHomeMigrationPreview['archivedOnly'];
87
+ sourceArchive: {
88
+ databaseModeBefore: number;
89
+ databaseModeAfter: number;
90
+ walModeBefore?: number;
91
+ walModeAfter?: number;
92
+ state: 'read_only' | 'restored';
93
+ };
94
+ appliedAt: string;
95
+ rolledBackAt?: string;
96
+ }
97
+
98
+ export interface AppliedControllerHomeMigration {
99
+ preview: ControllerHomeMigrationPreview;
100
+ migration: ControllerHomeMigrationRecord;
101
+ reportPath: string;
102
+ }
103
+
104
+ /**
105
+ * Archives imported Work that cannot be routed because its repository was not
106
+ * migrated. This is a migration operation, not a Work execution transition:
107
+ * original payloads remain in the frozen source SQLite and each target record
108
+ * receives a new auditable terminal revision.
109
+ */
110
+ export function archiveUnroutableMigratedWork(input: {
111
+ destinationHome: string;
112
+ migrationId: string;
113
+ }): { migrationId: string; archivedWorkIds: string[]; archivedHandleIds: string[]; skipped: string[] } {
114
+ const destinationHome = resolve(input.destinationHome);
115
+ const migration = readControlPlaneRecord<ControllerHomeMigrationRecord>(destinationHome, MIGRATION_NAMESPACE, MIGRATION_SCOPE, input.migrationId);
116
+ if (!migration?.value || migration.value.status !== 'applied') throw new Error(`CONTROLLER_HOME_MIGRATION_NOT_APPLIED: ${input.migrationId}`);
117
+ const knownRepositories = new Set(listRepositories(destinationHome, { includeRemoved: false }).map((repository) => repository.repoId));
118
+ const imported = new Set(migration.value.imported.map((record) => recordIdentity({ namespace: record.namespace, scope: record.scope, key: record.key })));
119
+ const archivedWorkIds: string[] = [];
120
+ const archivedHandleIds: string[] = [];
121
+ const skipped: string[] = [];
122
+ withControlPlaneTransaction(destinationHome, (database) => {
123
+ for (const record of listControlPlaneRecords<unknown>(destinationHome, { namespace: 'work_contract', limit: 5_000 })) {
124
+ if (!imported.has(recordIdentity(record)) || knownRepositories.has(record.scope)) continue;
125
+ const value = record.value as Record<string, unknown>;
126
+ if (TERMINAL_WORK_STATUSES.has(String(value.status ?? ''))) { skipped.push(record.key); continue; }
127
+ writeControlPlaneRecordWithinTransaction(database, {
128
+ namespace: record.namespace, scope: record.scope, key: record.key, schemaVersion: record.schemaVersion,
129
+ value: { ...value, status: 'cancelled', updatedAt: new Date().toISOString(), terminalReason: 'CONTROLLER_HOME_MIGRATION_UNROUTABLE_REPOSITORY_ARCHIVED' },
130
+ action: 'controller_home_migration_archive_unroutable_work', expectedRevision: record.revision,
131
+ });
132
+ archivedWorkIds.push(record.key);
133
+ }
134
+ for (const record of listControlPlaneRecords<unknown>(destinationHome, { namespace: 'execution_work_handle', limit: 5_000 })) {
135
+ if (!imported.has(recordIdentity(record)) || knownRepositories.has(record.scope)) continue;
136
+ const value = record.value as Record<string, unknown>;
137
+ if (String(value.state ?? '') === 'cleaned') { skipped.push(record.key); continue; }
138
+ const finalization = typeof value.finalization === 'object' && value.finalization ? value.finalization : {};
139
+ writeControlPlaneRecordWithinTransaction(database, {
140
+ namespace: record.namespace, scope: record.scope, key: record.key, schemaVersion: record.schemaVersion,
141
+ value: { ...value, state: 'cleaned', updatedAt: new Date().toISOString(), failureReason: 'CONTROLLER_HOME_MIGRATION_UNROUTABLE_REPOSITORY_ARCHIVED', finalization: { ...finalization, validation: 'skipped', commit: 'skipped', merge: 'skipped', branchCleanup: 'skipped', worktreeCleanup: 'skipped' } },
142
+ action: 'controller_home_migration_archive_unroutable_handle', expectedRevision: record.revision,
143
+ });
144
+ archivedHandleIds.push(record.key);
145
+ }
146
+ });
147
+ return { migrationId: input.migrationId, archivedWorkIds, archivedHandleIds, skipped };
148
+ }
149
+
150
+ function databasePath(home: string): string {
151
+ return join(resolve(home), 'control-plane.sqlite');
152
+ }
153
+
154
+ function recordRef(record: ControlPlaneRecord<unknown>): ControllerHomeMigrationRecordRef {
155
+ return {
156
+ namespace: record.namespace,
157
+ scope: record.scope,
158
+ key: record.key,
159
+ schemaVersion: record.schemaVersion,
160
+ sourceRevision: record.revision,
161
+ sourceUpdatedAt: record.updatedAt,
162
+ };
163
+ }
164
+
165
+ function recordIdentity(record: Pick<ControlPlaneRecord<unknown>, 'namespace' | 'scope' | 'key'>): string {
166
+ return `${record.namespace}\u0000${record.scope}\u0000${record.key}`;
167
+ }
168
+
169
+ function sameRecord(left: ControlPlaneRecord<unknown>, right: ControlPlaneRecord<unknown>): boolean {
170
+ return left.schemaVersion === right.schemaVersion && JSON.stringify(left.value) === JSON.stringify(right.value);
171
+ }
172
+
173
+ function referencesOneOf(value: unknown, ids: ReadonlySet<string>): boolean {
174
+ if (typeof value === 'string') return ids.has(value);
175
+ if (!value || typeof value !== 'object') return false;
176
+ if (Array.isArray(value)) return value.some((entry) => referencesOneOf(entry, ids));
177
+ return Object.values(value as Record<string, unknown>).some((entry) => referencesOneOf(entry, ids));
178
+ }
179
+
180
+ function addRecord(
181
+ selected: Map<string, ControlPlaneRecord<unknown>>,
182
+ record: ControlPlaneRecord<unknown>,
183
+ ): void {
184
+ selected.set(recordIdentity(record), record);
185
+ }
186
+
187
+ function allRecords(home: string, namespace: string): ControlPlaneRecord<unknown>[] {
188
+ return listControlPlaneRecords<unknown>(home, { namespace, limit: 5_000 });
189
+ }
190
+
191
+ function sourceSelection(sourceHome: string): {
192
+ records: ControlPlaneRecord<unknown>[];
193
+ terminalWorkContracts: number;
194
+ } {
195
+ const selected = new Map<string, ControlPlaneRecord<unknown>>();
196
+ const workContracts = allRecords(sourceHome, 'work_contract');
197
+ const activeWork = workContracts.filter((record) => {
198
+ const status = (record.value as { status?: unknown }).status;
199
+ return typeof status === 'string' && !TERMINAL_WORK_STATUSES.has(status);
200
+ });
201
+ for (const record of activeWork) addRecord(selected, record);
202
+
203
+ const planIds = new Set(
204
+ activeWork
205
+ .map((record) => (record.value as { planId?: unknown }).planId)
206
+ .filter((value): value is string => typeof value === 'string' && value.length > 0),
207
+ );
208
+ for (const record of allRecords(sourceHome, 'plan_contract')) {
209
+ if (planIds.has(record.key)) addRecord(selected, record);
210
+ }
211
+
212
+ const workIds = new Set(activeWork.map((record) => record.key));
213
+ const workHandles = allRecords(sourceHome, 'execution_work_handle').filter((record) => {
214
+ const value = record.value as { workId?: unknown; workContractId?: unknown };
215
+ return (typeof value.workId === 'string' && workIds.has(value.workId))
216
+ || (typeof value.workContractId === 'string' && workIds.has(value.workContractId));
217
+ });
218
+ for (const record of workHandles) addRecord(selected, record);
219
+
220
+ const sessionIds = new Set(
221
+ workHandles
222
+ .map((record) => (record.value as { sessionId?: unknown }).sessionId)
223
+ .filter((value): value is string => typeof value === 'string' && value.length > 0),
224
+ );
225
+ for (const record of allRecords(sourceHome, 'execution_session')) {
226
+ const sessionId = (record.value as { sessionId?: unknown }).sessionId;
227
+ if (sessionIds.has(record.key) || (typeof sessionId === 'string' && sessionIds.has(sessionId))) addRecord(selected, record);
228
+ }
229
+
230
+ const referenceIds = new Set<string>([...workIds, ...planIds, ...sessionIds]);
231
+ for (const namespace of IMPORTABLE_REFERENCED_NAMESPACES) {
232
+ for (const record of allRecords(sourceHome, namespace)) {
233
+ if (referencesOneOf(record.value, referenceIds)) addRecord(selected, record);
234
+ }
235
+ }
236
+
237
+ return {
238
+ records: [...selected.values()].sort((left, right) => recordIdentity(left).localeCompare(recordIdentity(right))),
239
+ terminalWorkContracts: workContracts.length - activeWork.length,
240
+ };
241
+ }
242
+
243
+ async function sha256(path: string): Promise<string> {
244
+ return await new Promise<string>((resolveHash, reject) => {
245
+ const hash = createHash('sha256');
246
+ const input = createReadStream(path);
247
+ input.on('error', reject);
248
+ input.on('data', (chunk: Buffer) => hash.update(chunk));
249
+ input.on('end', () => resolveHash(hash.digest('hex')));
250
+ });
251
+ }
252
+
253
+ function selectedByNamespace(records: readonly ControllerHomeMigrationRecordRef[]): Record<string, number> {
254
+ return records.reduce<Record<string, number>>((counts, record) => {
255
+ counts[record.namespace] = (counts[record.namespace] ?? 0) + 1;
256
+ return counts;
257
+ }, {});
258
+ }
259
+
260
+ function destinationRecordIfPresent(
261
+ destinationHome: string,
262
+ destinationExists: boolean,
263
+ record: ControlPlaneRecord<unknown>,
264
+ ): ControlPlaneRecord<unknown> | undefined {
265
+ if (!destinationExists) return undefined;
266
+ return readControlPlaneRecord<unknown>(destinationHome, record.namespace, record.scope, record.key);
267
+ }
268
+
269
+ export async function previewControllerHomeMigration(input: {
270
+ sourceHome: string;
271
+ destinationHome: string;
272
+ }): Promise<ControllerHomeMigrationPreview> {
273
+ const sourceHome = resolve(input.sourceHome);
274
+ const destinationHome = resolve(input.destinationHome);
275
+ if (sourceHome === destinationHome) throw new Error('CONTROLLER_HOME_MIGRATION_SAME_HOME');
276
+ const sourceDatabase = databasePath(sourceHome);
277
+ if (!existsSync(sourceDatabase)) throw new Error(`CONTROLLER_HOME_MIGRATION_SOURCE_DATABASE_MISSING: ${sourceDatabase}`);
278
+ const sourceWal = `${sourceDatabase}-wal`;
279
+ const sourceWalExists = existsSync(sourceWal);
280
+
281
+ const selection = sourceSelection(sourceHome);
282
+ const selected = selection.records.map(recordRef);
283
+ const destinationDatabase = databasePath(destinationHome);
284
+ const destinationExists = existsSync(destinationDatabase);
285
+ const identical: ControllerHomeMigrationRecordRef[] = [];
286
+ const conflicts: ControllerHomeMigrationConflict[] = [];
287
+ for (const record of selection.records) {
288
+ const destination = destinationRecordIfPresent(destinationHome, destinationExists, record);
289
+ if (!destination) continue;
290
+ if (sameRecord(record, destination)) identical.push(recordRef(record));
291
+ else conflicts.push({ ...recordRef(record), kind: 'payload_conflict' });
292
+ }
293
+
294
+ return {
295
+ sourceHome,
296
+ destinationHome,
297
+ sourceDatabase: {
298
+ path: sourceDatabase,
299
+ sha256: await sha256(sourceDatabase),
300
+ ...(sourceWalExists ? { walPath: sourceWal, walSha256: await sha256(sourceWal) } : {}),
301
+ inspection: inspectControlPlaneDatabaseFile(sourceDatabase),
302
+ },
303
+ destinationDatabase: {
304
+ path: destinationDatabase,
305
+ exists: destinationExists,
306
+ ...(destinationExists ? { inspection: inspectControlPlaneDatabaseFile(destinationDatabase) } : {}),
307
+ },
308
+ selected,
309
+ selectedByNamespace: selectedByNamespace(selected),
310
+ identical,
311
+ conflicts,
312
+ archivedOnly: {
313
+ terminalWorkContracts: selection.terminalWorkContracts,
314
+ excludedNamespaces: ['mcp credentials', 'runtime release authority', 'runtime owner', 'Recovery state', 'plugin configuration', 'process bindings and logs'],
315
+ auditChain: 'retained_in_source_sqlite',
316
+ },
317
+ };
318
+ }
319
+
320
+ function reportPath(destinationHome: string, migrationId: string): string {
321
+ return join(destinationHome, 'archives', 'controller-home-migrations', `${migrationId}.json`);
322
+ }
323
+
324
+ function writeReport(path: string, value: unknown): void {
325
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
326
+ const temporary = `${path}.${process.pid}.tmp`;
327
+ writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
328
+ renameSync(temporary, path);
329
+ }
330
+
331
+ export async function applyControllerHomeMigration(input: {
332
+ sourceHome: string;
333
+ destinationHome: string;
334
+ }): Promise<AppliedControllerHomeMigration> {
335
+ const preview = await previewControllerHomeMigration(input);
336
+ if (preview.conflicts.length > 0) {
337
+ throw new Error(`CONTROLLER_HOME_MIGRATION_CONFLICTS: ${preview.conflicts.length}`);
338
+ }
339
+ const migrationId = `controller-home-${randomUUID()}`;
340
+ const destinationBackupPath = join(
341
+ preview.destinationHome,
342
+ 'backups',
343
+ 'controller-home-migrations',
344
+ `${migrationId}.before.sqlite`,
345
+ );
346
+ backupControlPlaneDatabase(preview.destinationHome, destinationBackupPath);
347
+ const sourceMode = statSync(preview.sourceDatabase.path).mode & 0o777;
348
+ const sourceWalMode = preview.sourceDatabase.walPath && existsSync(preview.sourceDatabase.walPath)
349
+ ? statSync(preview.sourceDatabase.walPath).mode & 0o777
350
+ : undefined;
351
+ const imported: ImportedRecord[] = [];
352
+ const selectedRecords = sourceSelection(preview.sourceHome).records;
353
+ const migration = withControlPlaneTransaction(preview.destinationHome, (database) => {
354
+ for (const record of selectedRecords) {
355
+ const current = readControlPlaneRecordWithinTransaction<unknown>(database, record.namespace, record.scope, record.key);
356
+ if (current) {
357
+ if (sameRecord(record, current)) continue;
358
+ throw new Error(`CONTROLLER_HOME_MIGRATION_CONFLICT: ${record.namespace}/${record.scope}/${record.key}`);
359
+ }
360
+ const written = writeControlPlaneRecordWithinTransaction(database, {
361
+ namespace: record.namespace,
362
+ scope: record.scope,
363
+ key: record.key,
364
+ schemaVersion: record.schemaVersion,
365
+ value: record.value,
366
+ action: 'controller_home_migration_import',
367
+ expectedRevision: null,
368
+ });
369
+ imported.push({ ...recordRef(record), destinationRevision: written.revision });
370
+ }
371
+ const value: ControllerHomeMigrationRecord = {
372
+ schemaVersion: 1,
373
+ migrationId,
374
+ status: 'applied',
375
+ sourceHome: preview.sourceHome,
376
+ destinationHome: preview.destinationHome,
377
+ sourceDatabase: preview.sourceDatabase,
378
+ destinationBackupPath,
379
+ selected: preview.selected,
380
+ imported,
381
+ identical: preview.identical,
382
+ archivedOnly: preview.archivedOnly,
383
+ sourceArchive: {
384
+ databaseModeBefore: sourceMode,
385
+ databaseModeAfter: sourceMode,
386
+ ...(sourceWalMode === undefined ? {} : { walModeBefore: sourceWalMode, walModeAfter: sourceWalMode }),
387
+ state: 'restored',
388
+ },
389
+ appliedAt: new Date().toISOString(),
390
+ };
391
+ writeControlPlaneRecordWithinTransaction(database, {
392
+ namespace: MIGRATION_NAMESPACE,
393
+ scope: MIGRATION_SCOPE,
394
+ key: migrationId,
395
+ schemaVersion: 1,
396
+ value,
397
+ action: 'controller_home_migration_applied',
398
+ expectedRevision: null,
399
+ });
400
+ return value;
401
+ });
402
+
403
+ // Only the legacy SQLite authority is frozen. Its full audit history stays
404
+ // queryable in place; no OAuth, Runtime, Recovery, or plugin files move.
405
+ const readOnlyMode = sourceMode & ~0o222;
406
+ chmodSync(preview.sourceDatabase.path, readOnlyMode);
407
+ const readOnlyWalMode = sourceWalMode === undefined ? undefined : sourceWalMode & ~0o222;
408
+ if (preview.sourceDatabase.walPath && readOnlyWalMode !== undefined) chmodSync(preview.sourceDatabase.walPath, readOnlyWalMode);
409
+ const archived = {
410
+ ...migration,
411
+ sourceArchive: {
412
+ databaseModeBefore: sourceMode,
413
+ databaseModeAfter: readOnlyMode,
414
+ ...(sourceWalMode === undefined ? {} : { walModeBefore: sourceWalMode, walModeAfter: readOnlyWalMode }),
415
+ state: 'read_only' as const,
416
+ },
417
+ };
418
+ writeControlPlaneRecord(preview.destinationHome, {
419
+ namespace: MIGRATION_NAMESPACE,
420
+ scope: MIGRATION_SCOPE,
421
+ key: migrationId,
422
+ schemaVersion: 1,
423
+ value: archived,
424
+ action: 'controller_home_migration_archive_source',
425
+ expectedRevision: 1,
426
+ });
427
+ const path = reportPath(preview.destinationHome, migrationId);
428
+ writeReport(path, { preview, migration: archived });
429
+ return { preview, migration: archived, reportPath: path };
430
+ }
431
+
432
+ export function rollbackControllerHomeMigration(input: {
433
+ destinationHome: string;
434
+ migrationId: string;
435
+ }): ControllerHomeMigrationRecord {
436
+ const destinationHome = resolve(input.destinationHome);
437
+ const current = readControlPlaneRecord<ControllerHomeMigrationRecord>(
438
+ destinationHome,
439
+ MIGRATION_NAMESPACE,
440
+ MIGRATION_SCOPE,
441
+ input.migrationId,
442
+ );
443
+ if (!current) throw new Error(`CONTROLLER_HOME_MIGRATION_NOT_FOUND: ${input.migrationId}`);
444
+ if (current.value.status === 'rolled_back') return current.value;
445
+
446
+ const rolledBack = withControlPlaneTransaction(destinationHome, (database) => {
447
+ for (const imported of current.value.imported) {
448
+ const record = readControlPlaneRecordWithinTransaction<unknown>(database, imported.namespace, imported.scope, imported.key);
449
+ if (!record || record.revision !== imported.destinationRevision) {
450
+ throw new Error(`CONTROLLER_HOME_MIGRATION_ROLLBACK_CONFLICT: ${imported.namespace}/${imported.scope}/${imported.key}`);
451
+ }
452
+ database.prepare(`
453
+ DELETE FROM control_plane_audit
454
+ WHERE namespace = ? AND scope = ? AND record_key = ?
455
+ `).run(imported.namespace, imported.scope, imported.key);
456
+ database.prepare(`
457
+ DELETE FROM control_plane_records
458
+ WHERE namespace = ? AND scope = ? AND record_key = ?
459
+ `).run(imported.namespace, imported.scope, imported.key);
460
+ }
461
+ const value: ControllerHomeMigrationRecord = {
462
+ ...current.value,
463
+ status: 'rolled_back',
464
+ sourceArchive: {
465
+ ...current.value.sourceArchive,
466
+ state: 'restored',
467
+ },
468
+ rolledBackAt: new Date().toISOString(),
469
+ };
470
+ writeControlPlaneRecordWithinTransaction(database, {
471
+ namespace: MIGRATION_NAMESPACE,
472
+ scope: MIGRATION_SCOPE,
473
+ key: current.value.migrationId,
474
+ schemaVersion: CONTROL_PLANE_SCHEMA_VERSION,
475
+ value,
476
+ action: 'controller_home_migration_rolled_back',
477
+ expectedRevision: current.revision,
478
+ });
479
+ return value;
480
+ });
481
+ chmodSync(rolledBack.sourceDatabase.path, rolledBack.sourceArchive.databaseModeBefore);
482
+ if (rolledBack.sourceDatabase.walPath && rolledBack.sourceArchive.walModeBefore !== undefined) {
483
+ chmodSync(rolledBack.sourceDatabase.walPath, rolledBack.sourceArchive.walModeBefore);
484
+ }
485
+ writeReport(reportPath(destinationHome, rolledBack.migrationId), { migration: rolledBack });
486
+ return rolledBack;
487
+ }
@@ -20,6 +20,7 @@ export interface Requirement {
20
20
  outcomeStatement: string;
21
21
  acceptanceCriteria: string[];
22
22
  requiredDeliveryReferences: string[];
23
+ /** @deprecated Migration compatibility pointer only. Current Plan.requirementId is the relationship authority. */
23
24
  activePlanId?: string;
24
25
  state: RequirementState;
25
26
  needsAttention: boolean;
@@ -124,7 +125,7 @@ export function createRequirement(
124
125
  const ALLOWED_TRANSITIONS: Readonly<Record<RequirementState, readonly RequirementState[]>> = {
125
126
  planned: ['planned', 'active', 'waiting_for_user', 'cancelled'],
126
127
  active: ['active', 'waiting_for_user', 'done', 'cancelled'],
127
- waiting_for_user: ['waiting_for_user', 'planned', 'active', 'cancelled'],
128
+ waiting_for_user: ['waiting_for_user', 'planned', 'active', 'done', 'cancelled'],
128
129
  done: ['done', 'cancelled'],
129
130
  cancelled: ['cancelled'],
130
131
  };
@@ -164,6 +165,7 @@ export function updateRequirement(
164
165
  });
165
166
  }
166
167
 
168
+ /** @deprecated Migration/legacy compatibility only. New Plan creation must bind Plan.requirementId and derive active Plan slices by query. */
167
169
  export function setRequirementPlan(
168
170
  options: RequirementStoreOptions,
169
171
  input: { requirementId: string; planId: string; action?: string },
@@ -189,12 +191,13 @@ export interface RequirementCompletionInput {
189
191
  }
190
192
 
191
193
  /**
192
- * Complete a Requirement only from the Work-owned completion record.
194
+ * Project machine-complete Work delivery into a Requirement without asserting
195
+ * semantic acceptance. The Requirement waits for an explicit ChatGPT/user
196
+ * decision; a previously audited `done` Requirement is never reopened by
197
+ * historical execution evidence.
193
198
  *
194
- * This intentionally short-circuits an already audited `done` Requirement
195
- * before inspecting historical execution facts. A cancelled Run, missing
196
- * legacy Task receipt, or stale Work projection therefore cannot reopen or
197
- * rewrite a completed user outcome.
199
+ * The legacy function name is retained for compatibility with completion
200
+ * callers. Its authority is evidence projection only, not semantic completion.
198
201
  */
199
202
  export function completeRequirementFromWork(
200
203
  options: RequirementStoreOptions,
@@ -221,19 +224,20 @@ export function completeRequirementFromWork(
221
224
  if (receipt.delivery.kind === 'superseded' || receipt.delivery.status !== 'integrated' || !receipt.delivery.reachable) throw new Error('REQUIREMENT_WORK_DELIVERY_NOT_PROVEN');
222
225
  if (!['complete', 'maintenance_warning'].includes(receipt.cleanup.status) || receipt.cleanup.blockers.length > 0) throw new Error('REQUIREMENT_WORK_CLEANUP_NOT_PROVEN');
223
226
 
227
+ if (current.value.state === 'waiting_for_user' && current.value.auditRefs.includes(receipt.receiptId)) return current.value;
224
228
  return updateRequirement(options, {
225
229
  requirementId,
226
- action: 'requirement_completed_from_work_receipt',
230
+ action: 'requirement_delivery_evidence_recorded',
227
231
  mutate: (latest) => {
228
- // Preserve a concurrent audited completion; stale execution evidence is
229
- // never allowed to move a terminal Requirement backwards.
232
+ // Preserve a concurrent explicit semantic completion; stale execution
233
+ // evidence is never allowed to move a terminal Requirement backwards.
230
234
  if (latest.state === 'done') return latest;
231
235
  if (latest.state === 'cancelled') throw new Error('REQUIREMENT_CANCELLED');
232
236
  return {
233
237
  ...latest,
234
- state: 'done',
238
+ state: 'waiting_for_user',
235
239
  needsAttention: false,
236
- attentionSummary: undefined,
240
+ attentionSummary: `Work ${work.workId} delivered machine-valid evidence (${receipt.receiptId}); ChatGPT/user semantic acceptance is still required.`,
237
241
  auditRefs: Array.from(new Set([...latest.auditRefs, receipt.receiptId])).slice(-50),
238
242
  };
239
243
  },
@@ -207,6 +207,37 @@ function openDatabase(controllerHome: string): SqliteDatabase {
207
207
  }
208
208
  }
209
209
 
210
+ /**
211
+ * Open an already initialized authority for SELECT-only callers without
212
+ * re-running WAL/schema setup. In WAL mode a reader can coexist with a writer
213
+ * holding BEGIN IMMEDIATE; forcing PRAGMA journal_mode/DDL on every read turns
214
+ * that harmless writer reservation into a synchronous busy_timeout on the
215
+ * Canonical Runtime event loop.
216
+ */
217
+ function openDatabaseForRead(controllerHome: string): SqliteDatabase {
218
+ const path = controlPlaneDatabasePath(controllerHome);
219
+ if (!existsSync(path)) return openDatabase(controllerHome);
220
+
221
+ const database = openRawDatabase(path);
222
+ try {
223
+ database.exec('PRAGMA busy_timeout = 5000; PRAGMA foreign_keys = ON;');
224
+ const schemaVersion = assertSupportedSchema(database, path);
225
+ if (schemaVersion !== undefined) return database;
226
+ } catch (error) {
227
+ database.close();
228
+ const busy = sqliteBusyError(path, error);
229
+ if (busy) throw busy;
230
+ const message = error instanceof Error ? error.message : String(error);
231
+ if (message.startsWith('CONTROL_PLANE_')) throw error;
232
+ throw new Error(`CONTROL_PLANE_SQLITE_CORRUPT: ${path}: ${message}`);
233
+ }
234
+
235
+ // Preserve first-use/partially initialized compatibility: only a database
236
+ // with no schema authority falls back to the initializer/writer path.
237
+ database.close();
238
+ return openDatabase(controllerHome);
239
+ }
240
+
210
241
  function rowToRecord<T>(row: StoredRecordRow): ControlPlaneRecord<T> {
211
242
  let value: T;
212
243
  try {
@@ -321,7 +352,7 @@ export function withControlPlaneTransaction<T>(controllerHome: string, operation
321
352
  }
322
353
 
323
354
  export function readControlPlaneRecord<T>(controllerHome: string, namespace: string, scope: string, key: string): ControlPlaneRecord<T> | undefined {
324
- const database = openDatabase(controllerHome);
355
+ const database = openDatabaseForRead(controllerHome);
325
356
  try {
326
357
  return selectRecord<T>(database, namespace, scope, key);
327
358
  } finally {
@@ -361,7 +392,7 @@ export function listControlPlaneRecords<T>(
361
392
  controllerHome: string,
362
393
  input: { namespace: string; scope?: string; limit?: number },
363
394
  ): ControlPlaneRecord<T>[] {
364
- const database = openDatabase(controllerHome);
395
+ const database = openDatabaseForRead(controllerHome);
365
396
  try {
366
397
  const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 1000), 5000));
367
398
  const rows = input.scope === undefined