@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
@@ -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
+ }
@@ -165,18 +165,6 @@ export function updateRequirement(
165
165
  });
166
166
  }
167
167
 
168
- /** @deprecated Migration/legacy compatibility only. New Plan creation must bind Plan.requirementId and derive active Plan slices by query. */
169
- export function setRequirementPlan(
170
- options: RequirementStoreOptions,
171
- input: { requirementId: string; planId: string; action?: string },
172
- ): Requirement {
173
- return updateRequirement(options, {
174
- requirementId: input.requirementId,
175
- action: input.action ?? 'requirement_active_plan_bound',
176
- mutate: (current) => ({ ...current, activePlanId: id(input.planId) }),
177
- });
178
- }
179
-
180
168
  export interface RequirementCompletionInput {
181
169
  requirementId: string;
182
170
  work: Pick<WorkContract,
@@ -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
@@ -106,6 +106,60 @@ function releasePathFromAuthorityRecord(
106
106
  return canonical(releasePath);
107
107
  }
108
108
 
109
+ function loadPackageConnectorReleaseProtection(controllerHome: string, releasesRoot: string): string | undefined {
110
+ const connectorRoot = join(controllerHome, 'runtime', 'connector-service');
111
+ if (!existsSync(connectorRoot)) return undefined;
112
+ const authorityPath = join(connectorRoot, 'authority.json');
113
+ if (!existsSync(authorityPath)) throw new Error('package connector release authority is missing');
114
+
115
+ const parsed = JSON.parse(readFileSync(authorityPath, 'utf8')) as Record<string, unknown>;
116
+ const releaseId = typeof parsed.releaseId === 'string' ? parsed.releaseId.trim() : '';
117
+ const releaseRootValue = typeof parsed.releaseRoot === 'string' ? parsed.releaseRoot.trim() : '';
118
+ const packageRoot = typeof parsed.packageRoot === 'string' ? parsed.packageRoot.trim() : '';
119
+ const endpoint = typeof parsed.endpoint === 'string' ? parsed.endpoint.trim() : '';
120
+ if (parsed.schemaVersion !== 1 || !releaseId || !releaseRootValue || !packageRoot || !endpoint) {
121
+ throw new Error('package connector release authority is invalid');
122
+ }
123
+ const releaseRoot = resolve(releaseRootValue);
124
+ if (basename(releaseRoot) !== releaseId || !directChild(releasesRoot, releaseRoot) || !existsSync(releaseRoot)) {
125
+ throw new Error('package connector release is outside runtime release authority or missing');
126
+ }
127
+ return canonical(releaseRoot);
128
+ }
129
+
130
+ function loadRecoveryKnownGoodReleaseProtection(controllerHome: string, releasesRoot: string): Set<string> {
131
+ const knownGoodPath = join(controllerHome, 'recovery', 'state', 'known-good.json');
132
+ const protectedPaths = new Set<string>();
133
+ if (!existsSync(knownGoodPath)) return protectedPaths;
134
+ const parsed = JSON.parse(readFileSync(knownGoodPath, 'utf8')) as Record<string, unknown>;
135
+ if (parsed.schemaVersion !== 1 || !Array.isArray(parsed.releases)) {
136
+ throw new Error('standalone recovery known-good authority is invalid');
137
+ }
138
+ for (const raw of parsed.releases) {
139
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('standalone recovery known-good release record is invalid');
140
+ const record = raw as Record<string, unknown>;
141
+ const releaseId = typeof record.revision === 'string' ? record.revision.trim() : '';
142
+ const manifestPathValue = typeof record.path === 'string' ? record.path.trim() : '';
143
+ if (!releaseId || !manifestPathValue) throw new Error('standalone recovery known-good release identity is incomplete');
144
+ const manifestPath = resolve(manifestPathValue);
145
+ const releaseRoot = dirname(manifestPath);
146
+ if (
147
+ basename(manifestPath) !== 'manifest.json'
148
+ || basename(releaseRoot) !== releaseId
149
+ || !directChild(releasesRoot, releaseRoot)
150
+ ) {
151
+ throw new Error('standalone recovery known-good release is outside runtime releases');
152
+ }
153
+ // Known-good history is append-only recovery evidence. A historical record
154
+ // may legitimately outlive the retained release directory after a bounded
155
+ // cleanup pass. Missing historical artifacts therefore protect nothing and
156
+ // must not disable retention for the remaining authoritative releases.
157
+ if (!existsSync(manifestPath) || !existsSync(releaseRoot)) continue;
158
+ protectedPaths.add(canonical(releaseRoot));
159
+ }
160
+ return protectedPaths;
161
+ }
162
+
109
163
  function loadRuntimeProtection(controllerHome: string): RuntimeProtection | undefined {
110
164
  const releasesRoot = join(controllerHome, 'runtime', 'releases');
111
165
  if (!existsSync(releasesRoot)) return undefined;
@@ -124,6 +178,11 @@ function loadRuntimeProtection(controllerHome: string): RuntimeProtection | unde
124
178
  if (previous !== undefined) {
125
179
  releasePaths.add(releasePathFromAuthorityRecord(releasesRoot, previous, 'previous'));
126
180
  }
181
+ const connectorRelease = loadPackageConnectorReleaseProtection(controllerHome, releasesRoot);
182
+ if (connectorRelease) releasePaths.add(connectorRelease);
183
+ for (const knownGoodRelease of loadRecoveryKnownGoodReleaseProtection(controllerHome, releasesRoot)) {
184
+ releasePaths.add(knownGoodRelease);
185
+ }
127
186
 
128
187
  const backupPaths = new Set<string>();
129
188
  let backupAuthoritySafe = true;
@@ -409,8 +468,9 @@ function scanLinkedReleaseFamily(
409
468
  /**
410
469
  * Reclaim immutable Controller release history while preserving the exact
411
470
  * rollback authorities. Runtime keeps active + previous + the previous DB
412
- * backup; Recovery/Supervisor keep current + previous. Missing or malformed
413
- * authority always fails closed.
471
+ * backup plus the immutable release backing the persistent public MCP Gateway;
472
+ * Recovery/Supervisor keep current + previous. Missing or malformed authority
473
+ * always fails closed.
414
474
  */
415
475
  export function cleanupControllerReleaseHistory(
416
476
  controllerHome: string,
@@ -2,16 +2,18 @@ import { RepoActor } from './actor';
2
2
 
3
3
  export class RepoActorRegistry {
4
4
  private readonly controllerHome: string;
5
+ private readonly defaultMaxConcurrentWorkers: number;
5
6
  private readonly actors = new Map<string, RepoActor>();
6
- constructor(controllerHome: string) {
7
+ constructor(controllerHome: string, options: { maxConcurrentWorkers?: number } = {}) {
7
8
  this.controllerHome = controllerHome;
9
+ this.defaultMaxConcurrentWorkers = Math.max(1, options.maxConcurrentWorkers ?? 4);
8
10
  }
9
11
 
10
12
  get(repoId: string): RepoActor {
11
13
  let actor = this.actors.get(repoId);
12
14
  if (!actor) {
13
15
  actor = new RepoActor(this.controllerHome, repoId, {
14
- maxConcurrentWorkers: Number(process.env.FORGE_PER_REPO_WORKERS ?? 2),
16
+ maxConcurrentWorkers: Number(process.env.FORGE_PER_REPO_WORKERS ?? this.defaultMaxConcurrentWorkers),
15
17
  });
16
18
  this.actors.set(repoId, actor);
17
19
  }