@moretea-labs/forge 1.6.0 → 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 (396) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +2 -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/03-orchestration.partial.md +1 -1
  17. package/assets/plugin-registry.v1.json +1 -1
  18. package/assets/reference-configs/document-generation.md +1 -1
  19. package/assets/reference-configs/harness-overview.md +1 -1
  20. package/assets/reference-configs/sprint-contracts.md +3 -3
  21. package/assets/skill-version.json +2 -2
  22. package/assets/templates/contract.template.md +2 -7
  23. package/assets/templates/helpers/capture-plan.sh +2 -2
  24. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  25. package/assets/templates/helpers/contract-worktree.sh +1 -21
  26. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  27. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  28. package/assets/templates/helpers/new-plan.sh +1 -1
  29. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  30. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  31. package/assets/templates/helpers/verify-contract.sh +1 -111
  32. package/assets/templates/helpers/verify-sprint.sh +3 -180
  33. package/assets/templates/plan.template.md +1 -1
  34. package/assets/templates/review.template.md +9 -34
  35. package/assets/workflow-contract.v1.json +10 -19
  36. package/docs/README.md +10 -10
  37. package/docs/architecture/CURRENT.md +170 -0
  38. package/docs/architecture/EVOLUTION.md +42 -0
  39. package/docs/architecture/current/README.md +4 -156
  40. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  41. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  42. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  43. package/docs/architecture/history/README.md +7 -0
  44. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  45. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  46. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  47. package/docs/architecture/history.md +3 -34
  48. package/docs/architecture/index.md +15 -98
  49. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  50. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  51. package/docs/architecture/versions/1.6.md +38 -0
  52. package/docs/architecture/versions/1.7.md +41 -0
  53. package/docs/forge-chatgpt-browser-engine.md +2 -2
  54. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  55. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  56. package/docs/forge-github-issue-launcher.md +1 -1
  57. package/docs/forge-runtime-self-healing-loop.md +1 -1
  58. package/docs/operations/controller-browser-plugin.md +26 -6
  59. package/docs/operations/controller-performance-and-502.md +124 -0
  60. package/docs/operations/controller-reliability-runbook.md +1 -1
  61. package/docs/operations/features.md +2 -2
  62. package/docs/operations/github-repository.md +1 -1
  63. package/docs/operations/homebrew.md +2 -2
  64. package/docs/operations/mcp-tool-exposure.md +5 -3
  65. package/docs/wiki/Architecture.md +6 -6
  66. package/docs/wiki/Implementation.md +2 -2
  67. package/docs/wiki/Integrations.md +2 -2
  68. package/docs/wiki/Work-Lifecycle.md +1 -1
  69. package/package.json +29 -10
  70. package/scripts/activate-source-baseline.ts +17 -13
  71. package/scripts/benchmark-background-check-overlap.ts +228 -0
  72. package/scripts/benchmark-mcp-host.sh +117 -0
  73. package/scripts/benchmark-mcp-transport.ts +170 -0
  74. package/scripts/benchmark-quality-harness.ts +146 -0
  75. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  76. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  77. package/scripts/benchmark-typescript-navigation.ts +186 -0
  78. package/scripts/benchmark-work-efficiency.ts +334 -0
  79. package/scripts/capture-plan.sh +3 -3
  80. package/scripts/check-agent-tooling.sh +8 -35
  81. package/scripts/check-architecture-sync.sh +17 -86
  82. package/scripts/check-mcp-compatibility.ts +14 -0
  83. package/scripts/check-runtime-architecture.mjs +107 -64
  84. package/scripts/check-stable-baseline.ts +92 -0
  85. package/scripts/check-task-workflow.sh +4 -4
  86. package/scripts/contract-worktree.sh +1 -21
  87. package/scripts/ensure-task-workflow.sh +22 -55
  88. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  89. package/scripts/install-standalone-recovery.ts +24 -0
  90. package/scripts/lib/project-init-lib.sh +15 -44
  91. package/scripts/migrate-project-template.sh +23 -1179
  92. package/scripts/new-plan.sh +1 -1
  93. package/scripts/plan-to-todo.sh +11 -36
  94. package/scripts/run-governed-gate.ts +2 -1
  95. package/scripts/ship-worktrees.sh +2 -29
  96. package/scripts/smoke-ios-physical-device.ts +4 -2
  97. package/scripts/smoke-runtime-recovery.ts +1 -35
  98. package/scripts/smoke-schedule-engine.ts +1 -20
  99. package/scripts/verify-contract.sh +1 -111
  100. package/scripts/verify-forge-runtime.sh +0 -1
  101. package/scripts/verify-sprint.sh +3 -180
  102. package/scripts/workflow-contract.ts +1 -5
  103. package/src/cli/commands/init-hook.ts +3 -3
  104. package/src/cli/commands/mcp.ts +9 -5
  105. package/src/cli/commands/plugin.ts +1 -1
  106. package/src/cli/commands/recovery.ts +25 -3
  107. package/src/cli/commands/runtime.ts +55 -3
  108. package/src/cli/commands/security.ts +12 -1
  109. package/src/cli/commands/setup-profile.ts +31 -21
  110. package/src/cli/controller/check-runner.ts +106 -3
  111. package/src/cli/controller/composite-result.ts +1 -1
  112. package/src/cli/controller/context/focus.ts +51 -0
  113. package/src/cli/controller/context/instruction-context.ts +114 -0
  114. package/src/cli/controller/context/known-paths.ts +76 -0
  115. package/src/cli/controller/context/query-planning.ts +110 -0
  116. package/src/cli/controller/context/rule-resolution.ts +532 -0
  117. package/src/cli/controller/context/source-materializer.ts +278 -0
  118. package/src/cli/controller/context/structural.ts +16 -0
  119. package/src/cli/controller/context/types.ts +286 -0
  120. package/src/cli/controller/context-pack.ts +785 -469
  121. package/src/cli/controller/launch-agents.ts +77 -2
  122. package/src/cli/controller/operational-plan.ts +2 -2
  123. package/src/cli/controller/work-mode.ts +6 -4
  124. package/src/cli/editing/edit-session.ts +60 -7
  125. package/src/cli/github/github.ts +133 -0
  126. package/src/cli/installer/managed-entries.ts +15 -1
  127. package/src/cli/local-bridge/console-automations.ts +54 -78
  128. package/src/cli/local-bridge/console-view-models.ts +1 -35
  129. package/src/cli/local-bridge/facade-api.ts +0 -217
  130. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  131. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  132. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  133. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  134. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  135. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  136. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  137. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  138. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  139. package/src/cli/local-bridge/job-store.ts +1 -490
  140. package/src/cli/local-bridge/server.ts +0 -513
  141. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  142. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  143. package/src/cli/mcp/auth.ts +5 -5
  144. package/src/cli/mcp/instructions.ts +3 -2
  145. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  146. package/src/cli/mcp/multi-repository.ts +16 -5
  147. package/src/cli/mcp/oauth.ts +175 -26
  148. package/src/cli/mcp/repository-tools.ts +247 -107
  149. package/src/cli/mcp/server.ts +537 -111
  150. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  151. package/src/cli/mcp/toolset-selection.ts +1 -1
  152. package/src/cli/mcp/toolset.ts +42 -5
  153. package/src/cli/mcp/transports/http.ts +101 -14
  154. package/src/cli/mcp/transports/session-registry.ts +43 -5
  155. package/src/cli/mcp/types.ts +3 -3
  156. package/src/cli/repositories/command-classifier.ts +101 -19
  157. package/src/cli/repositories/command-executor.ts +55 -521
  158. package/src/cli/repositories/command-process.ts +108 -0
  159. package/src/cli/repositories/command-scope.ts +99 -41
  160. package/src/cli/repositories/controller-home.ts +7 -8
  161. package/src/cli/repositories/git-command-executor.ts +2 -0
  162. package/src/cli/repositories/locks.ts +53 -16
  163. package/src/cli/repositories/repository-snapshot.ts +121 -0
  164. package/src/cli/repositories/selected-path-actions.ts +17 -9
  165. package/src/cli/repositories/structured-git.ts +61 -3
  166. package/src/cli/repository/inspector.ts +553 -63
  167. package/src/cli/repository/session-cache.ts +58 -17
  168. package/src/cli/v81-entry.ts +1 -4
  169. package/src/effects/fs-transaction.ts +85 -17
  170. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  171. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  172. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  173. package/src/runtime/context/lsp-client.ts +283 -0
  174. package/src/runtime/context/semantic-navigation.ts +275 -0
  175. package/src/runtime/context/swift-navigation.ts +565 -0
  176. package/src/runtime/context/typescript-navigation.ts +226 -0
  177. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  178. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +47 -8
  179. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  180. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  181. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  182. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  183. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  184. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  185. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  186. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +143 -8
  187. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  188. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +85 -8
  189. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  190. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  191. package/src/runtime/control-plane/facade/controller-session-store.ts +132 -1
  192. package/src/runtime/control-plane/facade/goal-workloop-access.ts +2 -0
  193. package/src/runtime/control-plane/facade/goal-workloop.ts +686 -129
  194. package/src/runtime/control-plane/facade/index.ts +1 -0
  195. package/src/runtime/control-plane/facade/plan-contract-store.ts +185 -20
  196. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  197. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  198. package/src/runtime/control-plane/facade/types.ts +22 -5
  199. package/src/runtime/control-plane/facade/work-contract-store.ts +175 -73
  200. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  201. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  202. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  203. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  204. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  205. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  206. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  207. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  208. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  209. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  210. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  211. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  212. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  213. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  214. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  215. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  216. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  217. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  218. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  219. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +355 -116
  220. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  221. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  222. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  223. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  224. package/src/runtime/control-plane/release-retention.ts +62 -2
  225. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  226. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  227. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  228. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  229. package/src/runtime/diagnostics/performance.ts +24 -1
  230. package/src/runtime/evidence/event-ledger.ts +1 -1
  231. package/src/runtime/execution/jobs/store.ts +58 -4
  232. package/src/runtime/execution/jobs/types.ts +1 -1
  233. package/src/runtime/execution/managed-workspace.ts +203 -1
  234. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  235. package/src/runtime/execution/process-runtime/check-result.ts +53 -0
  236. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  237. package/src/runtime/execution/process-runtime/command-facade.ts +345 -40
  238. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  239. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  240. package/src/runtime/execution/process-runtime/persisted-check.ts +47 -4
  241. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  242. package/src/runtime/execution/process-runtime/runtime.ts +43 -3
  243. package/src/runtime/execution/process-runtime/store.ts +1 -0
  244. package/src/runtime/execution/process-runtime/types.ts +2 -0
  245. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  246. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  247. package/src/runtime/execution/workers/executor.ts +0 -48
  248. package/src/runtime/gateway/mcp/execution-tools.ts +1192 -114
  249. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  250. package/src/runtime/gateway/mcp/router.ts +184 -82
  251. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  252. package/src/runtime/gateway/mcp/runtime-tools.ts +2224 -1589
  253. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  254. package/src/runtime/plugins/browser-adapter.ts +2901 -241
  255. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  256. package/src/runtime/plugins/browser-handoff.ts +92 -2
  257. package/src/runtime/plugins/browser-macos-bridge.ts +582 -49
  258. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  259. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  260. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  261. package/src/runtime/plugins/browser-runtime.ts +183 -0
  262. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  263. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  264. package/src/runtime/plugins/desktop-operator-registration.ts +8 -2
  265. package/src/runtime/plugins/external-adapter.ts +324 -3
  266. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  267. package/src/runtime/plugins/first-party-registry.ts +2 -0
  268. package/src/runtime/plugins/github-adapter.ts +125 -0
  269. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  270. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  271. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  272. package/src/runtime/plugins/ios-adapter.ts +106 -16
  273. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  274. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  275. package/src/runtime/plugins/lightweight-action.ts +153 -0
  276. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  277. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  278. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  279. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  280. package/src/runtime/plugins/resend-adapter.ts +1 -1
  281. package/src/runtime/plugins/store.ts +269 -29
  282. package/src/runtime/plugins/types.ts +35 -2
  283. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  284. package/src/runtime/projections/git-status-sampler.ts +16 -16
  285. package/src/runtime/projections/materialized-view.ts +28 -10
  286. package/src/runtime/recovery/maintenance-executor.ts +393 -28
  287. package/src/runtime/root/mcp-transport.ts +180 -10
  288. package/src/runtime/root/package-connector-service.ts +186 -8
  289. package/src/runtime/root/package-runtime-release.ts +143 -17
  290. package/src/runtime/root/package-runtime-service.ts +430 -22
  291. package/src/runtime/root/release-manifest.ts +7 -2
  292. package/src/runtime/root/release-materialize.ts +19 -0
  293. package/src/runtime/root/release-store.ts +48 -11
  294. package/src/runtime/root/runtime.ts +23 -5
  295. package/src/runtime/root/service.ts +100 -29
  296. package/src/runtime/root/types.ts +2 -0
  297. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  298. package/src/runtime/shared/trusted-executable.ts +62 -0
  299. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  300. package/src/runtime/standalone-recovery/core.ts +700 -92
  301. package/src/runtime/standalone-recovery/entry.ts +99 -23
  302. package/src/runtime/standalone-recovery/installer.ts +19 -3
  303. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  304. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  305. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  306. package/src/runtime/workflow/schedules/engine.ts +287 -151
  307. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  308. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  309. package/src/runtime/workflow/schedules/store.ts +131 -10
  310. package/src/runtime/workflow/schedules/types.ts +2 -4
  311. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  312. package/src/testing/test-governance.ts +8 -40
  313. package/assets/reference-configs/heartbeat-triage.md +0 -57
  314. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  315. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  316. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  317. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  318. package/docs/architecture/current/architecture-invariants.md +0 -331
  319. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  320. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  321. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  322. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  323. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  324. package/docs/architecture/current/entity-model.md +0 -612
  325. package/docs/architecture/current/external-plugin-governance.md +0 -396
  326. package/docs/architecture/current/failure-recovery.md +0 -447
  327. package/docs/architecture/current/governance.md +0 -213
  328. package/docs/architecture/current/human-interaction-plane.md +0 -87
  329. package/docs/architecture/current/implementation-status.md +0 -128
  330. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  331. package/docs/architecture/current/legacy-governance.md +0 -58
  332. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  333. package/docs/architecture/current/migration-roadmap.md +0 -105
  334. package/docs/architecture/current/multi-repository-execution.md +0 -371
  335. package/docs/architecture/current/operations-runbook.md +0 -45
  336. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  337. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  338. package/docs/architecture/current/repository-access-modes.md +0 -102
  339. package/docs/architecture/current/route-policy.md +0 -15
  340. package/docs/architecture/current/route-session-concurrency.md +0 -85
  341. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  342. package/docs/architecture/current/runtime-directory-map.md +0 -49
  343. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  344. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  345. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  346. package/docs/architecture/current/system-overview.md +0 -169
  347. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  348. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  349. package/docs/architecture/current/thin-harness-v1.md +0 -321
  350. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  351. package/docs/forge-autonomous-goal-loop.md +0 -9
  352. package/docs/forge-provider-configuration.md +0 -7
  353. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  354. package/docs/operations/gmail-assistant-routines.md +0 -186
  355. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  356. package/docs/operations/provider-configuration.md +0 -31
  357. package/scripts/harness-trace-grade.sh +0 -150
  358. package/scripts/heartbeat-triage.sh +0 -361
  359. package/scripts/maintenance-triage.sh +0 -167
  360. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  361. package/src/cli/repositories/goal-registry.ts +0 -239
  362. package/src/runtime/assistant/action-proposals.ts +0 -279
  363. package/src/runtime/assistant/intent.ts +0 -395
  364. package/src/runtime/assistant/model-provider.ts +0 -460
  365. package/src/runtime/assistant/openapi.ts +0 -223
  366. package/src/runtime/assistant/policy.ts +0 -89
  367. package/src/runtime/assistant/readiness.ts +0 -258
  368. package/src/runtime/assistant/routine-runtime.ts +0 -477
  369. package/src/runtime/assistant/schedule-binding.ts +0 -171
  370. package/src/runtime/assistant/standing-grants.ts +0 -375
  371. package/src/runtime/assistant/store.ts +0 -243
  372. package/src/runtime/assistant/types.ts +0 -135
  373. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  374. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  375. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  376. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  377. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  378. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  379. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  380. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  381. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  382. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  383. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  384. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  385. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  386. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  387. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  388. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  389. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  390. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  391. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  392. package/src/runtime/workflow/findings/store.ts +0 -204
  393. package/src/runtime/workflow/findings/types.ts +0 -58
  394. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  395. package/src/runtime/workflow/portfolio/store.ts +0 -185
  396. 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
+ }
@@ -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,