@moretea-labs/forge 1.7.1 → 1.8.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 (685) hide show
  1. package/AGENTS.md +58 -62
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +42 -62
  4. package/README.md +2 -2
  5. package/SKILL.md +0 -1
  6. package/THIRD_PARTY_NOTICES.md +5 -2
  7. package/adapters/browser/index.ts +2 -0
  8. package/adapters/chatgpt/browser-delivery-host.ts +71 -0
  9. package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
  10. package/adapters/chatgpt/controller-binding-store.ts +55 -0
  11. package/adapters/chatgpt/controller-host.ts +119 -0
  12. package/adapters/chatgpt/controller-round-host.ts +78 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
  14. package/adapters/chatgpt/provider-delivery.ts +113 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/desktop-operator-contract.ts +7 -0
  18. package/adapters/computer/desktop-operator-discovery.ts +201 -0
  19. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  20. package/adapters/computer/desktop-operator-provider.ts +312 -0
  21. package/adapters/computer/index.ts +1 -0
  22. package/adapters/computer/interaction-target-authority.ts +944 -0
  23. package/adapters/controller-process/binding-store.ts +34 -0
  24. package/adapters/controller-process/controller-host.ts +26 -0
  25. package/adapters/mcp/access-mode.ts +132 -0
  26. package/adapters/mcp/audit.ts +32 -0
  27. package/adapters/mcp/auth.ts +407 -0
  28. package/adapters/mcp/controller-round-compatibility.ts +184 -0
  29. package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
  30. package/adapters/mcp/instructions.ts +36 -0
  31. package/adapters/mcp/legacy-context.ts +18 -0
  32. package/adapters/mcp/multi-repository.ts +437 -0
  33. package/adapters/mcp/oauth.ts +391 -0
  34. package/adapters/mcp/paths.ts +140 -0
  35. package/adapters/mcp/policy.ts +232 -0
  36. package/adapters/mcp/proxy-env.ts +62 -0
  37. package/adapters/mcp/readiness-projection.ts +24 -0
  38. package/adapters/mcp/redaction.ts +21 -0
  39. package/adapters/mcp/repo.ts +24 -0
  40. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  41. package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
  42. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
  43. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
  44. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  45. package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
  46. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  47. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
  48. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  49. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  50. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  51. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  52. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
  53. package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
  54. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
  55. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
  56. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  57. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  58. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
  59. package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
  60. package/adapters/mcp/runtime-gateway/router.ts +728 -0
  61. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
  62. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
  63. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  64. package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
  65. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
  66. package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
  67. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
  68. package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
  69. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
  70. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  71. package/adapters/mcp/server.ts +1048 -0
  72. package/adapters/mcp/setup.ts +791 -0
  73. package/adapters/mcp/tool-capability-cache.ts +87 -0
  74. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  75. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
  76. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
  77. package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
  78. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  79. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  80. package/adapters/mcp/toolset-names.ts +74 -0
  81. package/adapters/mcp/toolset-selection.ts +41 -0
  82. package/adapters/mcp/toolset.ts +336 -0
  83. package/adapters/mcp/transports/http.ts +1456 -0
  84. package/adapters/mcp/transports/session-registry.ts +513 -0
  85. package/adapters/mcp/transports/stdio.ts +8 -0
  86. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
  87. package/adapters/mcp/types.ts +41 -0
  88. package/assets/AGENTS.md +2 -16
  89. package/assets/CLAUDE.md +2 -16
  90. package/assets/hooks/AGENTS.md +3 -14
  91. package/assets/hooks/CLAUDE.md +3 -14
  92. package/assets/hooks/hook-input.sh +9 -2
  93. package/assets/hooks/lib/workflow-state.sh +28 -10
  94. package/assets/hooks/prompt-guard.sh +63 -37
  95. package/assets/hooks/security-sentinel.sh +2 -1
  96. package/assets/hooks/session-start-context.sh +7 -5
  97. package/assets/hooks/stop-orchestrator.sh +13 -3
  98. package/assets/initializer-question-pack.v4.json +11 -7
  99. package/assets/partials/05-workflow.partial.md +18 -73
  100. package/assets/partials/07-footer.partial.md +1 -1
  101. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  102. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  103. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  104. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  105. package/assets/plan-map.json +11 -11
  106. package/assets/plugin-registry.v1.json +9 -3
  107. package/assets/plugin-trust-policy.v1.json +6 -0
  108. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  109. package/assets/recovery/forge-wsl-rescue +271 -0
  110. package/assets/reference-configs/development-protocol.md +11 -15
  111. package/assets/reference-configs/document-generation.md +1 -1
  112. package/assets/reference-configs/global-working-rules.md +9 -1
  113. package/assets/reference-configs/harness-overview.md +22 -112
  114. package/assets/reference-configs/hook-operations.md +1 -1
  115. package/assets/reference-configs/sprint-contracts.md +2 -2
  116. package/assets/skill-version.json +2 -2
  117. package/assets/templates/helpers/architecture-event.ts +3 -34
  118. package/assets/templates/helpers/architecture-queue.sh +61 -10
  119. package/assets/templates/helpers/archive-workflow.sh +90 -180
  120. package/assets/templates/helpers/capability-config.ts +8 -11
  121. package/assets/templates/helpers/capability-resolver.ts +37 -19
  122. package/assets/templates/helpers/capture-plan.sh +2 -2
  123. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  124. package/assets/templates/helpers/check-task-sync.sh +82 -11
  125. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  126. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  127. package/assets/templates/helpers/contract-worktree.sh +26 -24
  128. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  129. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  130. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  131. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  132. package/assets/templates/helpers/new-plan.sh +2 -2
  133. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  134. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  135. package/assets/templates/helpers/switch-plan.sh +1 -1
  136. package/assets/templates/helpers/workflow-contract.ts +1 -0
  137. package/assets/workflow-contract.v1.json +63 -171
  138. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  139. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  140. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  141. package/docs/architecture/CURRENT.md +148 -13
  142. package/docs/architecture/EVOLUTION.md +13 -0
  143. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  144. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  145. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  146. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  147. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
  148. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
  149. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  150. package/docs/architecture/domains/verification.md +7 -0
  151. package/docs/architecture/domains/workflow-engine.md +1 -1
  152. package/docs/architecture/index.md +3 -3
  153. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  154. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  155. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  156. package/docs/forge-plugin-management.md +20 -4
  157. package/docs/forge-runtime-self-healing-loop.md +2 -0
  158. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  159. package/docs/operations/controller-browser-plugin.md +3 -1
  160. package/docs/operations/homebrew.md +2 -2
  161. package/docs/operations/platform-support.md +16 -8
  162. package/docs/operations/platform-support.zh-CN.md +2 -2
  163. package/docs/operations/standalone-disaster-recovery.md +26 -1
  164. package/docs/operations/uu-remote-rescue.md +41 -0
  165. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  166. package/docs/public-usage-guide.md +1 -1
  167. package/docs/public-usage-guide.zh-CN.md +1 -1
  168. package/docs/tutorials/01-install-and-start.md +1 -1
  169. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  170. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  171. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  172. package/docs/wiki/Implementation.md +1 -1
  173. package/package.json +23 -14
  174. package/packages/kernel/controller/api/index.ts +2 -0
  175. package/packages/kernel/controller/application/controller-round-service.ts +29 -0
  176. package/packages/kernel/controller/application/controller-service.ts +30 -0
  177. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
  178. package/packages/kernel/controller/domain/controller-round.ts +107 -0
  179. package/packages/kernel/controller/domain/execution-quality.ts +133 -0
  180. package/packages/kernel/controller/domain/types.ts +56 -0
  181. package/packages/kernel/controller/index.ts +1 -0
  182. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  183. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
  184. package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
  185. package/packages/kernel/controller/ports/controller-host.ts +16 -0
  186. package/packages/kernel/goal/api/index.ts +1 -0
  187. package/packages/kernel/goal/domain/types.ts +40 -0
  188. package/packages/kernel/goal/index.ts +1 -0
  189. package/packages/kernel/identity/api/index.ts +5 -0
  190. package/packages/kernel/identity/application/identity-service.ts +67 -0
  191. package/packages/kernel/identity/domain/scope.ts +63 -0
  192. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  193. package/packages/kernel/identity/domain/types.ts +44 -0
  194. package/packages/kernel/identity/domain/workspace.ts +56 -0
  195. package/packages/kernel/identity/index.ts +1 -0
  196. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  197. package/packages/kernel/memory/api/index.ts +5 -0
  198. package/packages/kernel/memory/application/experience-service.ts +118 -0
  199. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  200. package/packages/kernel/memory/domain/experience.ts +103 -0
  201. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  202. package/packages/kernel/memory/index.ts +1 -0
  203. package/packages/kernel/progression/api/index.ts +2 -0
  204. package/packages/kernel/progression/application/projector.ts +211 -0
  205. package/packages/kernel/progression/domain/types.ts +116 -0
  206. package/packages/kernel/progression/index.ts +1 -0
  207. package/packages/kernel/scheduler/api/index.ts +6 -0
  208. package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
  209. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  210. package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
  211. package/packages/kernel/scheduler/application/settlement.ts +197 -0
  212. package/packages/kernel/scheduler/domain/continuation.ts +38 -0
  213. package/packages/kernel/scheduler/domain/schedule.ts +149 -0
  214. package/packages/kernel/scheduler/index.ts +1 -0
  215. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
  216. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
  217. package/packages/kernel/work/api/index.ts +15 -0
  218. package/packages/kernel/work/application/work-service.ts +148 -0
  219. package/packages/kernel/work/domain/admission-policy.ts +34 -0
  220. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  221. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  222. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  223. package/packages/kernel/work/domain/engineering-design.ts +281 -0
  224. package/packages/kernel/work/domain/engineering-profile.ts +242 -0
  225. package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
  226. package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
  227. package/packages/kernel/work/domain/implementation-review.ts +510 -0
  228. package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
  229. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  230. package/packages/kernel/work/domain/state-machine.ts +259 -0
  231. package/packages/kernel/work/domain/types.ts +528 -0
  232. package/packages/kernel/work/index.ts +1 -0
  233. package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
  234. package/packages/kernel/work/ports/work-contract-store.ts +18 -0
  235. package/packages/plugin-runtime/browser/index.ts +3 -0
  236. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  237. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  238. package/packages/plugin-runtime/browser/session-authority.ts +61 -0
  239. package/packages/plugin-runtime/computer/index.ts +6 -0
  240. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  241. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  242. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  243. package/packages/plugin-runtime/computer/provider.ts +19 -0
  244. package/packages/plugin-runtime/computer/target-authority.ts +190 -0
  245. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  246. package/packages/plugin-runtime/external/index.ts +1 -0
  247. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
  248. package/packages/plugin-runtime/index.ts +3 -0
  249. package/packages/protocols/browser/index.ts +1 -0
  250. package/packages/protocols/browser/session.ts +98 -0
  251. package/packages/protocols/computer/contract.ts +199 -0
  252. package/packages/protocols/computer/index.ts +1 -0
  253. package/packages/protocols/failure/contract.ts +158 -0
  254. package/packages/protocols/failure/index.ts +1 -0
  255. package/packages/protocols/handoff/index.ts +1 -0
  256. package/packages/protocols/handoff/status.ts +22 -0
  257. package/packages/protocols/mcp/execution-context.ts +11 -0
  258. package/packages/protocols/mcp/index.ts +2 -0
  259. package/packages/protocols/mcp/tool-contract.ts +42 -0
  260. package/packages/workflow-runtime/api/index.ts +2 -0
  261. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  262. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  263. package/packages/workflow-runtime/index.ts +1 -0
  264. package/scripts/assemble-template.ts +1 -1
  265. package/scripts/benchmark-computer-hotpath.ts +255 -0
  266. package/scripts/benchmark-controller-round2.ts +54 -2
  267. package/scripts/benchmark-ios-agent-device.ts +3 -6
  268. package/scripts/benchmark-mcp-transport.ts +3 -4
  269. package/scripts/benchmark-operational-memory-activation.ts +235 -0
  270. package/scripts/benchmark-operational-shadow.ts +28 -0
  271. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  272. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  273. package/scripts/canary-controller-semantic-wait.ts +195 -0
  274. package/scripts/canary-global-hook.sh +2 -2
  275. package/scripts/check-mcp-compatibility.ts +173 -7
  276. package/scripts/check-platform-support.mjs +28 -2
  277. package/scripts/check-public-docs.sh +1 -1
  278. package/scripts/check-release-readiness.sh +1 -1
  279. package/scripts/check-repository-hygiene.mjs +92 -0
  280. package/scripts/check-runtime-architecture.mjs +1206 -95
  281. package/scripts/check-source-duplication.mjs +9 -1
  282. package/scripts/check-tarball-install-smoke.sh +98 -15
  283. package/scripts/controller-ngrok-rotation.sh +5 -4
  284. package/scripts/create-project-dirs.sh +10 -74
  285. package/scripts/forge-local-recovery-helper.mjs +204 -0
  286. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  287. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  288. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  289. package/scripts/hook-shim.sh +2 -2
  290. package/scripts/init-project.sh +12 -70
  291. package/scripts/install-desktop-operator-registration.ts +4 -2
  292. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  293. package/scripts/install-standalone-recovery.ts +122 -10
  294. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  295. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  296. package/scripts/lib/controller-home-env.sh +6 -3
  297. package/scripts/lib/project-init-lib.sh +39 -582
  298. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  299. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  300. package/scripts/open-source-audit-allowlist.txt +12 -1
  301. package/scripts/portability-bindings.mjs +70 -0
  302. package/scripts/run-bounded-command.mjs +55 -0
  303. package/scripts/run-bun-test-file.ts +22 -6
  304. package/scripts/run-governed-gate.ts +4 -1
  305. package/scripts/smoke-mcp-http-runtime.ts +1 -1
  306. package/scripts/smoke-runtime-recovery.ts +34 -1
  307. package/scripts/smoke-schedule-engine.ts +1 -1
  308. package/scripts/stage-runtime-release.ts +6 -1
  309. package/scripts/test-governance.ts +51 -2
  310. package/src/cli/agent-jobs/job-manager.ts +10 -1
  311. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  312. package/src/cli/chatgpt-browser/binding.ts +20 -10
  313. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  314. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  315. package/src/cli/chatgpt-browser/engine.ts +10 -11
  316. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  317. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  318. package/src/cli/chatgpt-browser/types.ts +1 -0
  319. package/src/cli/commands/bootstrap-control.ts +334 -0
  320. package/src/cli/commands/brain-assistant.ts +38 -0
  321. package/src/cli/commands/brain.ts +193 -40
  322. package/src/cli/commands/chatgpt.ts +127 -0
  323. package/src/cli/commands/computer.ts +437 -0
  324. package/src/cli/commands/doctor.ts +18 -1
  325. package/src/cli/commands/init-hook.ts +71 -61
  326. package/src/cli/commands/init.ts +3 -1
  327. package/src/cli/commands/mcp.ts +16 -10
  328. package/src/cli/commands/plugin.ts +38 -12
  329. package/src/cli/commands/recovery.ts +493 -80
  330. package/src/cli/commands/runtime.ts +9 -4
  331. package/src/cli/commands/setup-profile.ts +89 -39
  332. package/src/cli/commands/status.ts +2 -1
  333. package/src/cli/controller/check-runner.ts +487 -98
  334. package/src/cli/controller/context/types.ts +7 -1
  335. package/src/cli/controller/context-pack.ts +23 -3
  336. package/src/cli/controller/issue-store.ts +1 -1
  337. package/src/cli/controller/systemd-user.ts +110 -0
  338. package/src/cli/controller/types.ts +10 -70
  339. package/src/cli/controller/worklog.ts +67 -13
  340. package/src/cli/editing/edit-operation-contract.ts +92 -0
  341. package/src/cli/editing/edit-session.ts +289 -13
  342. package/src/cli/github/plugin.ts +17 -12
  343. package/src/cli/hook/route-registry.ts +3 -5
  344. package/src/cli/hook/runtime.ts +49 -3
  345. package/src/cli/index.ts +3 -0
  346. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  347. package/src/cli/local-bridge/console-automations.ts +2 -2
  348. package/src/cli/local-bridge/facade-api.ts +41 -14
  349. package/src/cli/local-bridge/job-store.ts +1 -1
  350. package/src/cli/local-bridge/server.ts +24 -27
  351. package/src/cli/mcp/access-mode.ts +2 -132
  352. package/src/cli/mcp/access-tools.ts +2 -387
  353. package/src/cli/mcp/audit.ts +2 -32
  354. package/src/cli/mcp/auth.ts +2 -404
  355. package/src/cli/mcp/instructions.ts +2 -34
  356. package/src/cli/mcp/legacy-context.ts +2 -16
  357. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  358. package/src/cli/mcp/multi-repository.ts +2 -436
  359. package/src/cli/mcp/oauth.ts +2 -375
  360. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  361. package/src/cli/mcp/paths.ts +2 -140
  362. package/src/cli/mcp/policy.ts +2 -232
  363. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  364. package/src/cli/mcp/proxy-env.ts +2 -62
  365. package/src/cli/mcp/readiness-projection.ts +2 -24
  366. package/src/cli/mcp/redaction.ts +2 -21
  367. package/src/cli/mcp/repo.ts +2 -24
  368. package/src/cli/mcp/repository-tools.ts +2 -1585
  369. package/src/cli/mcp/server.ts +2 -951
  370. package/src/cli/mcp/setup.ts +2 -737
  371. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  372. package/src/cli/mcp/tool-contract.ts +2 -14
  373. package/src/cli/mcp/tools.ts +2 -9
  374. package/src/cli/mcp/toolset-names.ts +2 -70
  375. package/src/cli/mcp/toolset-selection.ts +2 -41
  376. package/src/cli/mcp/toolset.ts +2 -336
  377. package/src/cli/mcp/transports/AGENTS.md +1 -10
  378. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  379. package/src/cli/mcp/transports/http.ts +2 -1414
  380. package/src/cli/mcp/transports/session-registry.ts +2 -490
  381. package/src/cli/mcp/transports/stdio.ts +2 -8
  382. package/src/cli/mcp/types.ts +2 -41
  383. package/src/cli/mcp/umbrella-tools.ts +2 -117
  384. package/src/cli/repositories/command-executor.ts +64 -18
  385. package/src/cli/repositories/command-scope.ts +117 -1
  386. package/src/cli/repositories/controller-home.ts +119 -1
  387. package/src/cli/repositories/identity.ts +13 -0
  388. package/src/cli/repositories/registry.ts +68 -49
  389. package/src/cli/repositories/repository-snapshot.ts +53 -7
  390. package/src/cli/repositories/runtime-storage.ts +27 -2
  391. package/src/cli/repositories/safe-patch.ts +34 -38
  392. package/src/cli/repositories/selected-path-actions.ts +22 -2
  393. package/src/cli/repositories/structured-git.ts +105 -25
  394. package/src/cli/runtime/helper-runner.ts +7 -0
  395. package/src/cli/runtime-invocation.ts +8 -1
  396. package/src/cli/tools/codegraph.ts +44 -25
  397. package/src/core/adoption/manifest-templates.ts +1 -1
  398. package/src/core/adoption/plan.ts +3 -26
  399. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  400. package/src/effects/process-runner.ts +8 -14
  401. package/src/runtime/context/assistant-context.ts +154 -0
  402. package/src/runtime/context/assistant-work-context.ts +147 -0
  403. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  404. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  405. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  406. package/src/runtime/context/context-closure.ts +268 -0
  407. package/src/runtime/context/context-plane.ts +211 -0
  408. package/src/runtime/context/project-engineering-contract.ts +60 -0
  409. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  410. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  411. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  412. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  413. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  414. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  415. package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
  416. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  417. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
  418. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
  419. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
  420. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
  421. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +417 -100
  422. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
  423. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  424. package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
  425. package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
  426. package/src/runtime/control-plane/execution/validation.ts +28 -3
  427. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
  428. package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
  429. package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
  430. package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
  431. package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
  432. package/src/runtime/control-plane/execution/work-handle-authority.ts +267 -16
  433. package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
  434. package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
  435. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
  436. package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
  437. package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
  438. package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
  439. package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
  440. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
  441. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
  442. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  443. package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
  444. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
  445. package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
  446. package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
  447. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  448. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
  449. package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
  450. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
  451. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
  452. package/src/runtime/control-plane/facade/index.ts +4 -2
  453. package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
  454. package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
  455. package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
  456. package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
  457. package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
  458. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
  459. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  460. package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
  461. package/src/runtime/control-plane/facade/types.ts +189 -532
  462. package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
  463. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  464. package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
  465. package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
  466. package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
  467. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
  468. package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
  469. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  470. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  471. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
  472. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  473. package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
  474. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  475. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
  476. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  477. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
  478. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
  479. package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
  480. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  481. package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
  482. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  483. package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
  484. package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
  485. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  486. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  487. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  488. package/src/runtime/control-plane/release-retention.ts +56 -16
  489. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  490. package/src/runtime/control-plane/routing/route-policy.ts +19 -0
  491. package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
  492. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  493. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
  494. package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
  495. package/src/runtime/diagnostics/incident-repair.ts +406 -0
  496. package/src/runtime/diagnostics/mcp-timing.ts +125 -6
  497. package/src/runtime/diagnostics/performance.ts +6 -14
  498. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  499. package/src/runtime/evidence/artifact-store.ts +65 -2
  500. package/src/runtime/evidence/operational-shadow.ts +38 -0
  501. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  502. package/src/runtime/evidence/sensitive-output.ts +4 -4
  503. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  504. package/src/runtime/execution/jobs/store.ts +121 -2
  505. package/src/runtime/execution/managed-workspace.ts +55 -5
  506. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  507. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  508. package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
  509. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  510. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  511. package/src/runtime/execution/process-runtime/check-result.ts +73 -2
  512. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
  513. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  514. package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
  515. package/src/runtime/execution/process-runtime/index.ts +1 -0
  516. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  517. package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
  518. package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
  519. package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
  520. package/src/runtime/execution/process-runtime/runtime.ts +132 -15
  521. package/src/runtime/execution/process-runtime/types.ts +22 -0
  522. package/src/runtime/execution/workers/executor.ts +16 -13
  523. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  524. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  525. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  526. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  527. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  528. package/src/runtime/gateway/mcp/router.ts +2 -1125
  529. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  530. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6651
  531. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  532. package/src/runtime/maintenance/cleanup.ts +38 -24
  533. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  534. package/src/runtime/platform/computer-platform.ts +14 -0
  535. package/src/runtime/platform/executable-discovery.ts +54 -0
  536. package/src/runtime/platform/fetch-http-server.ts +90 -0
  537. package/src/runtime/platform/service-manager.ts +138 -0
  538. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  539. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  540. package/src/runtime/plugins/action-application.ts +135 -0
  541. package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
  542. package/src/runtime/plugins/browser-adapter.ts +140 -100
  543. package/src/runtime/plugins/browser-automation-service.ts +15 -36
  544. package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
  545. package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
  546. package/src/runtime/plugins/browser-native-session-service.ts +1 -1
  547. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  548. package/src/runtime/plugins/browser-registration.ts +16 -0
  549. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  550. package/src/runtime/plugins/browser-runtime.ts +41 -9
  551. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  552. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  553. package/src/runtime/plugins/browser-session-store.ts +229 -36
  554. package/src/runtime/plugins/browser-session-types.ts +2 -98
  555. package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
  556. package/src/runtime/plugins/catalog-trust.ts +82 -0
  557. package/src/runtime/plugins/computer-registration.ts +839 -0
  558. package/src/runtime/plugins/config-store.ts +119 -0
  559. package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
  560. package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
  561. package/src/runtime/plugins/errors.ts +8 -1
  562. package/src/runtime/plugins/external-adapter.ts +45 -322
  563. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  564. package/src/runtime/plugins/external-registration.ts +9 -1
  565. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  566. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  567. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  568. package/src/runtime/plugins/first-party-registry.ts +3 -1
  569. package/src/runtime/plugins/github-adapter.ts +1 -2
  570. package/src/runtime/plugins/gmail-adapter.ts +6 -6
  571. package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
  572. package/src/runtime/plugins/google-shared.ts +31 -46
  573. package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
  574. package/src/runtime/plugins/ios-adapter.ts +1 -2
  575. package/src/runtime/plugins/local-system-adapter.ts +180 -0
  576. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  577. package/src/runtime/plugins/resend-adapter.ts +15 -24
  578. package/src/runtime/plugins/store.ts +493 -51
  579. package/src/runtime/plugins/types.ts +8 -1
  580. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  581. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  582. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  583. package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
  584. package/src/runtime/projections/controller-context.ts +12 -6
  585. package/src/runtime/projections/git-status-sampler.ts +73 -22
  586. package/src/runtime/projections/materialized-view.ts +14 -8
  587. package/src/runtime/recovery/capability-action-application.ts +228 -0
  588. package/src/runtime/recovery/index.ts +3 -0
  589. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  590. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  591. package/src/runtime/recovery/maintenance-executor.ts +358 -76
  592. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  593. package/src/runtime/release/identity.ts +6 -0
  594. package/src/runtime/resources/leases/store.ts +1 -1
  595. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  596. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  597. package/src/runtime/root/browser-session-composition.ts +59 -0
  598. package/src/runtime/root/computer-composition.ts +191 -0
  599. package/src/runtime/root/computer-target-composition.ts +16 -0
  600. package/src/runtime/root/computer-target-persistence.ts +39 -0
  601. package/src/runtime/root/controller-round-composition.ts +107 -0
  602. package/src/runtime/root/controller-services.ts +2 -0
  603. package/src/runtime/root/entry.ts +4 -3
  604. package/src/runtime/root/gateway-adapter.ts +3 -3
  605. package/src/runtime/root/local-bridge.ts +5 -2
  606. package/src/runtime/root/mcp-transport.ts +5 -5
  607. package/src/runtime/root/package-connector-service.ts +108 -56
  608. package/src/runtime/root/package-runtime-release.ts +86 -18
  609. package/src/runtime/root/package-runtime-service.ts +63 -86
  610. package/src/runtime/root/release-execution-canary.ts +71 -0
  611. package/src/runtime/root/release-loader.ts +68 -0
  612. package/src/runtime/root/release-manifest.ts +106 -2
  613. package/src/runtime/root/release-materialize.ts +103 -4
  614. package/src/runtime/root/release-store.ts +31 -2
  615. package/src/runtime/root/runtime.ts +240 -25
  616. package/src/runtime/root/scheduled-controller-composition.ts +51 -0
  617. package/src/runtime/root/scheduler.ts +12 -4
  618. package/src/runtime/root/service-runner.ts +1 -1
  619. package/src/runtime/root/service.ts +41 -16
  620. package/src/runtime/root/status.ts +1 -0
  621. package/src/runtime/root/types.ts +24 -1
  622. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  623. package/src/runtime/shared/process-environment.ts +19 -4
  624. package/src/runtime/shared/storage-capacity.ts +68 -0
  625. package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
  626. package/src/runtime/standalone-recovery/core.ts +984 -230
  627. package/src/runtime/standalone-recovery/entry.ts +182 -51
  628. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  629. package/src/runtime/standalone-recovery/installer.ts +212 -104
  630. package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
  631. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  632. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  633. package/src/runtime/standalone-recovery/performance.ts +135 -0
  634. package/src/runtime/standalone-recovery/release.ts +5 -0
  635. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  636. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
  637. package/src/runtime/workflow/schedules/engine.ts +191 -343
  638. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  639. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  640. package/src/runtime/workflow/schedules/store.ts +2 -607
  641. package/src/runtime/workflow/schedules/types.ts +2 -127
  642. package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
  643. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  644. package/src/runtime/workflows/runtime.ts +486 -0
  645. package/src/testing/test-governance.ts +90 -29
  646. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  647. package/docs/architecture/history/README.md +0 -7
  648. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  649. package/docs/architecture/history/global-hook-runtime.md +0 -166
  650. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  651. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  652. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  653. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  654. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  655. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  656. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  657. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  658. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  659. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  660. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  661. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  662. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  663. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  664. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  665. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  666. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  667. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  668. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  669. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  670. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  671. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  672. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  673. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  674. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  675. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  676. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  677. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  678. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  679. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  680. package/docs/architecture/requests/archive/2026/root.md +0 -68
  681. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  682. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  683. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  684. package/scripts/archive-architecture-request.sh +0 -4
  685. package/scripts/run-skill-evals.ts +0 -1022
@@ -1,5 +1,6 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { createRequire } from 'node:module';
2
- import { copyFileSync, existsSync, mkdirSync, renameSync, unlinkSync } from 'fs';
3
+ import { copyFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync } from 'fs';
3
4
  import { dirname, join } from 'path';
4
5
  import { durableControllerHome } from '../../../cli/repositories/controller-home';
5
6
 
@@ -15,6 +16,8 @@ interface SqliteStatement {
15
16
  get(...parameters: unknown[]): unknown;
16
17
  all(...parameters: unknown[]): unknown[];
17
18
  run(...parameters: unknown[]): unknown;
19
+ /** Bun statements retain native resources until finalized; Node's StatementSync does not expose this method. */
20
+ finalize?(): void;
18
21
  }
19
22
 
20
23
  export interface SqliteDatabase {
@@ -59,10 +62,84 @@ export interface ControlPlaneDatabaseInspection {
59
62
  orphanRecordCount: number;
60
63
  }
61
64
 
65
+ export const CONTROL_PLANE_SQLITE_MAINTENANCE_POLICY_VERSION = 'control-plane-sqlite-maintenance-v1' as const;
66
+ export const DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_BYTES = 64 * 1024 * 1024;
67
+ export const DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_RATIO = 0.20;
68
+
69
+ export interface ControlPlaneDatabaseMaintenanceOptions {
70
+ minimumReclaimableBytes?: number;
71
+ minimumReclaimableRatio?: number;
72
+ allowVacuum?: boolean;
73
+ }
74
+
75
+ export interface ControlPlaneDatabaseMaintenanceReport {
76
+ policyVersion: typeof CONTROL_PLANE_SQLITE_MAINTENANCE_POLICY_VERSION;
77
+ path: string;
78
+ checkpointMode: 'passive';
79
+ checkpointed: boolean;
80
+ pageSize: number;
81
+ pageCountBefore: number;
82
+ freePageCountBefore: number;
83
+ reclaimableBytesBefore: number;
84
+ reclaimableRatioBefore: number;
85
+ vacuumEligible: boolean;
86
+ vacuumAttempted: boolean;
87
+ vacuumed: boolean;
88
+ pageCountAfter: number;
89
+ freePageCountAfter: number;
90
+ reclaimableBytesAfter: number;
91
+ reclaimableRatioAfter: number;
92
+ reclaimedBytes: number;
93
+ skippedReason?: 'database_missing' | 'below_threshold' | 'vacuum_disabled' | 'database_busy';
94
+ }
95
+
62
96
  export const CONTROL_PLANE_SCHEMA_VERSION = 1;
63
97
  const DATABASE_FILE = 'control-plane.sqlite';
64
98
  const require = createRequire(import.meta.url);
65
99
 
100
+ interface ReusableReadDatabase {
101
+ database: SqliteDatabase;
102
+ fileIdentity: string;
103
+ }
104
+
105
+ const reusableReadDatabasePaths = new Set<string>();
106
+ const reusableReadDatabases = new Map<string, ReusableReadDatabase>();
107
+
108
+ function databaseFileIdentity(path: string): string | undefined {
109
+ try {
110
+ const stat = statSync(path);
111
+ // Content changes must not invalidate a WAL reader. Identity changes only
112
+ // when the live database file itself is replaced.
113
+ return stat.ino
114
+ ? `${stat.dev}:${stat.ino}`
115
+ : `${stat.dev}:birth:${stat.birthtimeMs}`;
116
+ } catch {
117
+ return undefined;
118
+ }
119
+ }
120
+
121
+ function closeReusableReadDatabase(path: string): void {
122
+ const cached = reusableReadDatabases.get(path);
123
+ if (!cached) return;
124
+ reusableReadDatabases.delete(path);
125
+ cached.database.close();
126
+ }
127
+
128
+ /**
129
+ * Opt in only for the long-lived Canonical Runtime. CLI, tests, and short-lived
130
+ * workers retain one-open-per-operation semantics so process exit and temporary
131
+ * Controller Home cleanup never depend on hidden SQLite handles.
132
+ */
133
+ export function enableControlPlaneReadConnectionReuse(controllerHome: string): void {
134
+ reusableReadDatabasePaths.add(controlPlaneDatabasePath(controllerHome));
135
+ }
136
+
137
+ export function disableControlPlaneReadConnectionReuse(controllerHome: string): void {
138
+ const path = controlPlaneDatabasePath(controllerHome);
139
+ reusableReadDatabasePaths.delete(path);
140
+ closeReusableReadDatabase(path);
141
+ }
142
+
66
143
  function now(): string {
67
144
  return new Date().toISOString();
68
145
  }
@@ -98,9 +175,28 @@ function scalar(row: unknown): unknown {
98
175
  return Object.values(row as Record<string, unknown>)[0];
99
176
  }
100
177
 
178
+ /**
179
+ * Keep native statement lifetime inside the SQLite adapter. Bun retains a
180
+ * Windows file handle for each unfinalized statement even after db.close(),
181
+ * while Node's StatementSync has no explicit finalizer. The optional call
182
+ * gives both runtimes identical Controller Home release semantics.
183
+ */
184
+ function withSqliteStatement<T>(
185
+ database: SqliteDatabase,
186
+ sql: string,
187
+ operation: (statement: SqliteStatement) => T,
188
+ ): T {
189
+ const statement = database.prepare(sql);
190
+ try {
191
+ return operation(statement);
192
+ } finally {
193
+ statement.finalize?.();
194
+ }
195
+ }
196
+
101
197
  function assertDatabaseIntegrity(database: SqliteDatabase, path: string): void {
102
198
  try {
103
- const result = scalar(database.prepare('PRAGMA quick_check').get());
199
+ const result = withSqliteStatement(database, 'PRAGMA quick_check', (statement) => scalar(statement.get()));
104
200
  if (result !== 'ok') throw new Error(String(result ?? 'missing quick_check result'));
105
201
  } catch (error) {
106
202
  const message = error instanceof Error ? error.message : String(error);
@@ -110,13 +206,21 @@ function assertDatabaseIntegrity(database: SqliteDatabase, path: string): void {
110
206
  }
111
207
 
112
208
  function tableExists(database: SqliteDatabase, table: string): boolean {
113
- const row = database.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) as { name?: string } | undefined;
209
+ const row = withSqliteStatement(
210
+ database,
211
+ "SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?",
212
+ (statement) => statement.get(table) as { name?: string } | undefined,
213
+ );
114
214
  return row?.name === table;
115
215
  }
116
216
 
117
217
  function currentSchemaVersion(database: SqliteDatabase): number | undefined {
118
218
  if (!tableExists(database, 'control_plane_schema')) return undefined;
119
- const value = scalar(database.prepare('SELECT MAX(version) AS version FROM control_plane_schema').get());
219
+ const value = withSqliteStatement(
220
+ database,
221
+ 'SELECT MAX(version) AS version FROM control_plane_schema',
222
+ (statement) => scalar(statement.get()),
223
+ );
120
224
  return typeof value === 'number' && Number.isInteger(value) ? value : undefined;
121
225
  }
122
226
 
@@ -163,7 +267,11 @@ function initializeSchema(database: SqliteDatabase): void {
163
267
  CREATE INDEX IF NOT EXISTS control_plane_audit_lookup
164
268
  ON control_plane_audit (namespace, scope, record_key, revision);
165
269
  `);
166
- database.prepare('INSERT OR IGNORE INTO control_plane_schema (version, applied_at) VALUES (?, ?)').run(CONTROL_PLANE_SCHEMA_VERSION, now());
270
+ withSqliteStatement(
271
+ database,
272
+ 'INSERT OR IGNORE INTO control_plane_schema (version, applied_at) VALUES (?, ?)',
273
+ (statement) => statement.run(CONTROL_PLANE_SCHEMA_VERSION, now()),
274
+ );
167
275
  }
168
276
 
169
277
  export function controlPlaneDatabasePath(controllerHome: string): string {
@@ -238,6 +346,42 @@ function openDatabaseForRead(controllerHome: string): SqliteDatabase {
238
346
  return openDatabase(controllerHome);
239
347
  }
240
348
 
349
+
350
+ function withDatabaseForRead<T>(controllerHome: string, operation: (database: SqliteDatabase) => T): T {
351
+ const path = controlPlaneDatabasePath(controllerHome);
352
+ if (!reusableReadDatabasePaths.has(path)) {
353
+ const database = openDatabaseForRead(controllerHome);
354
+ try {
355
+ return operation(database);
356
+ } finally {
357
+ database.close();
358
+ }
359
+ }
360
+
361
+ const liveIdentity = databaseFileIdentity(path);
362
+ const cached = reusableReadDatabases.get(path);
363
+ if (cached && liveIdentity && cached.fileIdentity === liveIdentity) {
364
+ return operation(cached.database);
365
+ }
366
+ if (cached) closeReusableReadDatabase(path);
367
+
368
+ const database = openDatabaseForRead(controllerHome);
369
+ const openedIdentity = databaseFileIdentity(path);
370
+ if (!openedIdentity) {
371
+ database.close();
372
+ throw new Error(`CONTROL_PLANE_SQLITE_MISSING: ${path}`);
373
+ }
374
+ reusableReadDatabases.set(path, { database, fileIdentity: openedIdentity });
375
+ try {
376
+ return operation(database);
377
+ } catch (error) {
378
+ // Do not retain a reader that failed while evaluating a query/schema. The
379
+ // next operation gets a clean connection and repeats the fail-closed checks.
380
+ closeReusableReadDatabase(path);
381
+ throw error;
382
+ }
383
+ }
384
+
241
385
  function rowToRecord<T>(row: StoredRecordRow): ControlPlaneRecord<T> {
242
386
  let value: T;
243
387
  try {
@@ -258,11 +402,11 @@ function rowToRecord<T>(row: StoredRecordRow): ControlPlaneRecord<T> {
258
402
  }
259
403
 
260
404
  function selectRecord<T>(database: SqliteDatabase, namespace: string, scope: string, key: string): ControlPlaneRecord<T> | undefined {
261
- const row = database.prepare(`
262
- SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
263
- FROM control_plane_records
264
- WHERE namespace = ? AND scope = ? AND record_key = ?
265
- `).get(namespace, scope, key) as StoredRecordRow | undefined;
405
+ const row = withSqliteStatement(database, `
406
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
407
+ FROM control_plane_records
408
+ WHERE namespace = ? AND scope = ? AND record_key = ?
409
+ `, (statement) => statement.get(namespace, scope, key) as StoredRecordRow | undefined);
266
410
  return row ? rowToRecord<T>(row) : undefined;
267
411
  }
268
412
 
@@ -272,22 +416,30 @@ function writeRecord<T>(
272
416
  existing?: ControlPlaneRecord<T>,
273
417
  ): ControlPlaneRecord<T> {
274
418
  const at = now();
275
- const revision = (existing?.revision ?? 0) + 1;
419
+ const previousAuditRevision = Number(withSqliteStatement(database, `
420
+ SELECT MAX(revision) AS revision FROM control_plane_audit
421
+ WHERE namespace = ? AND scope = ? AND record_key = ?
422
+ `, (statement) => scalar(statement.get(input.namespace, input.scope, input.key))) ?? 0);
423
+ const revision = existing
424
+ ? existing.revision + 1
425
+ : Math.max(0, Number.isSafeInteger(previousAuditRevision) ? previousAuditRevision : 0) + 1;
276
426
  const createdAt = existing?.createdAt ?? at;
277
- database.prepare(`
278
- INSERT INTO control_plane_records (
279
- namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
280
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
281
- ON CONFLICT(namespace, scope, record_key) DO UPDATE SET
282
- schema_version = excluded.schema_version,
283
- revision = excluded.revision,
284
- payload = excluded.payload,
285
- updated_at = excluded.updated_at
286
- `).run(input.namespace, input.scope, input.key, input.schemaVersion, revision, JSON.stringify(input.value), createdAt, at);
287
- database.prepare(`
288
- INSERT INTO control_plane_audit (namespace, scope, record_key, action, revision, occurred_at)
289
- VALUES (?, ?, ?, ?, ?, ?)
290
- `).run(input.namespace, input.scope, input.key, input.action, revision, at);
427
+ withSqliteStatement(database, `
428
+ INSERT INTO control_plane_records (
429
+ namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
430
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
431
+ ON CONFLICT(namespace, scope, record_key) DO UPDATE SET
432
+ schema_version = excluded.schema_version,
433
+ revision = excluded.revision,
434
+ payload = excluded.payload,
435
+ updated_at = excluded.updated_at
436
+ `, (statement) => statement.run(
437
+ input.namespace, input.scope, input.key, input.schemaVersion, revision, JSON.stringify(input.value), createdAt, at,
438
+ ));
439
+ withSqliteStatement(database, `
440
+ INSERT INTO control_plane_audit (namespace, scope, record_key, action, revision, occurred_at)
441
+ VALUES (?, ?, ?, ?, ?, ?)
442
+ `, (statement) => statement.run(input.namespace, input.scope, input.key, input.action, revision, at));
291
443
  return {
292
444
  namespace: input.namespace,
293
445
  scope: input.scope,
@@ -352,12 +504,8 @@ export function withControlPlaneTransaction<T>(controllerHome: string, operation
352
504
  }
353
505
 
354
506
  export function readControlPlaneRecord<T>(controllerHome: string, namespace: string, scope: string, key: string): ControlPlaneRecord<T> | undefined {
355
- const database = openDatabaseForRead(controllerHome);
356
- try {
357
- return selectRecord<T>(database, namespace, scope, key);
358
- } finally {
359
- database.close();
360
- }
507
+ return withDatabaseForRead(controllerHome, (database) =>
508
+ selectRecord<T>(database, namespace, scope, key));
361
509
  }
362
510
 
363
511
  export function writeControlPlaneRecord<T>(
@@ -376,6 +524,72 @@ export function writeControlPlaneRecord<T>(
376
524
  writeControlPlaneRecordWithinTransaction(database, input));
377
525
  }
378
526
 
527
+ export function deleteControlPlaneRecordWithinTransaction(
528
+ database: SqliteDatabase,
529
+ input: { namespace: string; scope: string; key: string; action?: string; expectedRevision?: number },
530
+ ): boolean {
531
+ const existing = selectRecord(database, input.namespace, input.scope, input.key);
532
+ if (input.expectedRevision !== undefined && existing?.revision !== input.expectedRevision) {
533
+ throw new ControlPlaneConflictError(input.namespace, input.scope, input.key, input.expectedRevision, existing?.revision);
534
+ }
535
+ if (!existing) return false;
536
+ const revision = existing.revision + 1;
537
+ const at = now();
538
+ withSqliteStatement(database, `
539
+ DELETE FROM control_plane_records
540
+ WHERE namespace = ? AND scope = ? AND record_key = ?
541
+ `, (statement) => statement.run(input.namespace, input.scope, input.key));
542
+ withSqliteStatement(database, `
543
+ INSERT INTO control_plane_audit (namespace, scope, record_key, action, revision, occurred_at)
544
+ VALUES (?, ?, ?, ?, ?, ?)
545
+ `, (statement) => statement.run(input.namespace, input.scope, input.key, input.action ?? 'delete', revision, at));
546
+ return true;
547
+ }
548
+
549
+ export function deleteControlPlaneRecord(
550
+ controllerHome: string,
551
+ input: { namespace: string; scope: string; key: string; action?: string; expectedRevision?: number },
552
+ ): boolean {
553
+ return withControlPlaneTransaction(controllerHome, (database) =>
554
+ deleteControlPlaneRecordWithinTransaction(database, input));
555
+ }
556
+
557
+ export function deleteControlPlaneRecordsWithinTransaction(
558
+ database: SqliteDatabase,
559
+ input: { namespace: string; scope: string; action?: string },
560
+ ): number {
561
+ const rows = withSqliteStatement(database, `
562
+ SELECT record_key, revision
563
+ FROM control_plane_records
564
+ WHERE namespace = ? AND scope = ?
565
+ ORDER BY record_key ASC
566
+ `, (statement) => statement.all(input.namespace, input.scope) as Array<{ record_key: string; revision: number }>);
567
+ if (rows.length === 0) return 0;
568
+
569
+ const at = now();
570
+ withSqliteStatement(database, `
571
+ DELETE FROM control_plane_records
572
+ WHERE namespace = ? AND scope = ? AND record_key = ?
573
+ `, (remove) => withSqliteStatement(database, `
574
+ INSERT INTO control_plane_audit (namespace, scope, record_key, action, revision, occurred_at)
575
+ VALUES (?, ?, ?, ?, ?, ?)
576
+ `, (audit) => {
577
+ for (const row of rows) {
578
+ remove.run(input.namespace, input.scope, row.record_key);
579
+ audit.run(input.namespace, input.scope, row.record_key, input.action ?? 'delete_scope', row.revision + 1, at);
580
+ }
581
+ }));
582
+ return rows.length;
583
+ }
584
+
585
+ export function deleteControlPlaneRecords(
586
+ controllerHome: string,
587
+ input: { namespace: string; scope: string; action?: string },
588
+ ): number {
589
+ return withControlPlaneTransaction(controllerHome, (database) =>
590
+ deleteControlPlaneRecordsWithinTransaction(database, input));
591
+ }
592
+
379
593
  export class ControlPlaneConflictError extends Error {
380
594
  readonly expectedRevision: number | null;
381
595
  readonly actualRevision: number | undefined;
@@ -388,32 +602,134 @@ export class ControlPlaneConflictError extends Error {
388
602
  }
389
603
  }
390
604
 
605
+ export function listControlPlaneRecordsWithinTransaction<T>(
606
+ database: SqliteDatabase,
607
+ input: { namespace: string; scope?: string; limit?: number },
608
+ ): ControlPlaneRecord<T>[] {
609
+ const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 1000), 5000));
610
+ const rows = input.scope === undefined
611
+ ? withSqliteStatement(database, `
612
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
613
+ FROM control_plane_records
614
+ WHERE namespace = ?
615
+ ORDER BY updated_at ASC, record_key ASC
616
+ LIMIT ?
617
+ `, (statement) => statement.all(input.namespace, limit))
618
+ : withSqliteStatement(database, `
619
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
620
+ FROM control_plane_records
621
+ WHERE namespace = ? AND scope = ?
622
+ ORDER BY updated_at ASC, record_key ASC
623
+ LIMIT ?
624
+ `, (statement) => statement.all(input.namespace, input.scope, limit));
625
+ return (rows as StoredRecordRow[]).map((row) => rowToRecord<T>(row));
626
+ }
627
+
391
628
  export function listControlPlaneRecords<T>(
392
629
  controllerHome: string,
393
630
  input: { namespace: string; scope?: string; limit?: number },
394
631
  ): ControlPlaneRecord<T>[] {
395
- const database = openDatabaseForRead(controllerHome);
396
- try {
397
- const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 1000), 5000));
398
- const rows = input.scope === undefined
399
- ? database.prepare(`
400
- SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
401
- FROM control_plane_records
402
- WHERE namespace = ?
403
- ORDER BY updated_at ASC, record_key ASC
404
- LIMIT ?
405
- `).all(input.namespace, limit)
406
- : database.prepare(`
407
- SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
408
- FROM control_plane_records
409
- WHERE namespace = ? AND scope = ?
410
- ORDER BY updated_at ASC, record_key ASC
411
- LIMIT ?
412
- `).all(input.namespace, input.scope, limit);
413
- return (rows as StoredRecordRow[]).map((row) => rowToRecord<T>(row));
414
- } finally {
415
- database.close();
632
+ return withDatabaseForRead(controllerHome, (database) =>
633
+ listControlPlaneRecordsWithinTransaction<T>(database, input));
634
+ }
635
+
636
+ /**
637
+ * Candidate-only read optimization for top-level JSON text fields. This never
638
+ * establishes domain authority: missing/null/non-text values remain candidates,
639
+ * and callers must still run their canonical semantic validation.
640
+ */
641
+ export interface PayloadTextExclusion {
642
+ namespace: string;
643
+ field: string;
644
+ excludedValues: readonly string[];
645
+ }
646
+
647
+ function payloadTextExclusionPredicate(input: PayloadTextExclusion): string {
648
+ const field = input.field.trim();
649
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(field)) {
650
+ throw new Error(`CONTROL_PLANE_JSON_FIELD_INVALID: ${input.field}`);
416
651
  }
652
+ const values = [...new Set(input.excludedValues.map((value) => value.trim()).filter(Boolean))].sort();
653
+ if (values.length > 32) throw new Error('CONTROL_PLANE_JSON_EXCLUSION_LIMIT_EXCEEDED');
654
+ // These literals are escaped here, never supplied as SQL by a domain caller.
655
+ // SQLite must see identical literals in the query and partial-index predicate.
656
+ const literal = (value: string) => `'${value.replaceAll("'", "''")}'`;
657
+ const namespace = `namespace = ${literal(input.namespace)}`;
658
+ if (values.length === 0) return namespace;
659
+ const extraction = `json_extract(payload, ${literal(`$.${field}`)})`;
660
+ return `${namespace} AND (${extraction} IS NULL OR ${extraction} NOT IN (${values.map(literal).join(', ')}))`;
661
+ }
662
+
663
+ /** Explicit startup DDL. JSON remains authority; SQLite maintains the derived index in every write transaction. */
664
+ export function initializeControlPlanePayloadTextExclusionIndex(
665
+ controllerHome: string,
666
+ input: PayloadTextExclusion,
667
+ ): void {
668
+ const predicate = payloadTextExclusionPredicate(input);
669
+ const prefix = `control_plane_candidate_${createHash('sha256').update(`${input.namespace}/${input.field}`).digest('hex').slice(0, 16)}_`;
670
+ const name = `${prefix}${createHash('sha256').update(predicate).digest('hex').slice(0, 16)}`;
671
+ withControlPlaneTransaction(controllerHome, (database) => {
672
+ database.exec(`CREATE INDEX IF NOT EXISTS ${name} ON control_plane_records (scope, updated_at, record_key) WHERE ${predicate}`);
673
+ // Retire obsolete predicates for this domain field in the same transaction.
674
+ const old = withSqliteStatement(database, "SELECT name FROM sqlite_master WHERE type = 'index' AND substr(name, 1, ?) = ?",
675
+ (statement) => statement.all(prefix.length, prefix)) as Array<{ name: string }>;
676
+ for (const index of old) {
677
+ if (index.name !== name && /^control_plane_candidate_[a-f0-9]{16}_[a-f0-9]{16}$/.test(index.name)) {
678
+ database.exec(`DROP INDEX ${index.name}`);
679
+ }
680
+ }
681
+ });
682
+ }
683
+
684
+ export function listControlPlaneRecordsExcludingPayloadTextValues<T>(
685
+ controllerHome: string,
686
+ input: PayloadTextExclusion & { scope: string; limit?: number },
687
+ ): ControlPlaneRecord<T>[] {
688
+ const predicate = payloadTextExclusionPredicate(input);
689
+ const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 1_000), 5_000));
690
+ return withDatabaseForRead(controllerHome, (database) => {
691
+ const rows = withSqliteStatement(database, `
692
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
693
+ FROM control_plane_records
694
+ WHERE ${predicate} AND scope = ?
695
+ ORDER BY updated_at ASC, record_key ASC
696
+ LIMIT ?
697
+ `, (statement) => statement.all(input.scope, limit));
698
+ return (rows as StoredRecordRow[]).map((row) => rowToRecord<T>(row));
699
+ });
700
+ }
701
+
702
+ /**
703
+ * Internal correctness surface for authorities that must observe every durable
704
+ * fact in a namespace/scope. User-facing and diagnostic list APIs should keep
705
+ * using listControlPlaneRecords so their output remains bounded.
706
+ */
707
+ export function listAllControlPlaneRecordsWithinTransaction<T>(
708
+ database: SqliteDatabase,
709
+ input: { namespace: string; scope?: string },
710
+ ): ControlPlaneRecord<T>[] {
711
+ const rows = input.scope === undefined
712
+ ? withSqliteStatement(database, `
713
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
714
+ FROM control_plane_records
715
+ WHERE namespace = ?
716
+ ORDER BY updated_at ASC, record_key ASC
717
+ `, (statement) => statement.all(input.namespace))
718
+ : withSqliteStatement(database, `
719
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
720
+ FROM control_plane_records
721
+ WHERE namespace = ? AND scope = ?
722
+ ORDER BY updated_at ASC, record_key ASC
723
+ `, (statement) => statement.all(input.namespace, input.scope));
724
+ return (rows as StoredRecordRow[]).map((row) => rowToRecord<T>(row));
725
+ }
726
+
727
+ export function listAllControlPlaneRecords<T>(
728
+ controllerHome: string,
729
+ input: { namespace: string; scope?: string },
730
+ ): ControlPlaneRecord<T>[] {
731
+ return withDatabaseForRead(controllerHome, (database) =>
732
+ listAllControlPlaneRecordsWithinTransaction<T>(database, input));
417
733
  }
418
734
 
419
735
  function inspectOpenDatabase(database: SqliteDatabase, path: string): ControlPlaneDatabaseInspection {
@@ -422,34 +738,42 @@ function inspectOpenDatabase(database: SqliteDatabase, path: string): ControlPla
422
738
  if (!tableExists(database, 'control_plane_records') || !tableExists(database, 'control_plane_audit')) {
423
739
  throw new Error(`CONTROL_PLANE_BACKUP_INVALID: ${path}: required tables are missing`);
424
740
  }
425
- const recordCount = Number(scalar(database.prepare('SELECT COUNT(*) AS count FROM control_plane_records').get()) ?? 0);
426
- const auditEventCount = Number(scalar(database.prepare('SELECT COUNT(*) AS count FROM control_plane_audit').get()) ?? 0);
427
- const orphanRecordCount = Number(scalar(database.prepare(`
428
- SELECT COUNT(*) AS count
429
- FROM control_plane_records record
430
- WHERE (
431
- SELECT COUNT(DISTINCT audit.revision)
432
- FROM control_plane_audit audit
433
- WHERE audit.namespace = record.namespace
434
- AND audit.scope = record.scope
435
- AND audit.record_key = record.record_key
436
- AND audit.revision BETWEEN 1 AND record.revision
437
- ) != record.revision
438
- OR COALESCE((
439
- SELECT MIN(audit.revision)
440
- FROM control_plane_audit audit
441
- WHERE audit.namespace = record.namespace
442
- AND audit.scope = record.scope
443
- AND audit.record_key = record.record_key
444
- ), 0) != 1
445
- OR COALESCE((
446
- SELECT MAX(audit.revision)
741
+ const recordCount = Number(withSqliteStatement(
742
+ database,
743
+ 'SELECT COUNT(*) AS count FROM control_plane_records',
744
+ (statement) => scalar(statement.get()),
745
+ ) ?? 0);
746
+ const auditEventCount = Number(withSqliteStatement(
747
+ database,
748
+ 'SELECT COUNT(*) AS count FROM control_plane_audit',
749
+ (statement) => scalar(statement.get()),
750
+ ) ?? 0);
751
+ const orphanRecordCount = Number(withSqliteStatement(database, `
752
+ SELECT COUNT(*) AS count
753
+ FROM control_plane_records record
754
+ WHERE (
755
+ SELECT COUNT(DISTINCT audit.revision)
447
756
  FROM control_plane_audit audit
448
757
  WHERE audit.namespace = record.namespace
449
758
  AND audit.scope = record.scope
450
759
  AND audit.record_key = record.record_key
451
- ), 0) != record.revision
452
- `).get()) ?? 0);
760
+ AND audit.revision BETWEEN 1 AND record.revision
761
+ ) != record.revision
762
+ OR COALESCE((
763
+ SELECT MIN(audit.revision)
764
+ FROM control_plane_audit audit
765
+ WHERE audit.namespace = record.namespace
766
+ AND audit.scope = record.scope
767
+ AND audit.record_key = record.record_key
768
+ ), 0) != 1
769
+ OR COALESCE((
770
+ SELECT MAX(audit.revision)
771
+ FROM control_plane_audit audit
772
+ WHERE audit.namespace = record.namespace
773
+ AND audit.scope = record.scope
774
+ AND audit.record_key = record.record_key
775
+ ), 0) != record.revision
776
+ `, (statement) => scalar(statement.get())) ?? 0);
453
777
  if (orphanRecordCount > 0) {
454
778
  throw new Error(`CONTROL_PLANE_AUDIT_CONTINUITY_INVALID: ${path}: discontinuous_records=${orphanRecordCount}`);
455
779
  }
@@ -476,6 +800,147 @@ export function inspectControlPlaneDatabaseFile(path: string): ControlPlaneDatab
476
800
  }
477
801
  }
478
802
 
803
+ interface ControlPlaneDatabasePageSnapshot {
804
+ pageSize: number;
805
+ pageCount: number;
806
+ freePageCount: number;
807
+ reclaimableBytes: number;
808
+ reclaimableRatio: number;
809
+ }
810
+
811
+ function integerPragma(database: SqliteDatabase, sql: string, label: string): number {
812
+ const value = Number(withSqliteStatement(database, sql, (statement) => scalar(statement.get())));
813
+ if (!Number.isFinite(value) || value < 0) throw new Error(`CONTROL_PLANE_SQLITE_PRAGMA_INVALID: ${label}`);
814
+ return Math.floor(value);
815
+ }
816
+
817
+ function controlPlaneDatabasePageSnapshot(database: SqliteDatabase): ControlPlaneDatabasePageSnapshot {
818
+ const pageSize = integerPragma(database, 'PRAGMA page_size', 'page_size');
819
+ const pageCount = integerPragma(database, 'PRAGMA page_count', 'page_count');
820
+ const freePageCount = integerPragma(database, 'PRAGMA freelist_count', 'freelist_count');
821
+ const reclaimableBytes = freePageCount * pageSize;
822
+ return {
823
+ pageSize,
824
+ pageCount,
825
+ freePageCount,
826
+ reclaimableBytes,
827
+ reclaimableRatio: pageCount > 0 ? freePageCount / pageCount : 0,
828
+ };
829
+ }
830
+
831
+ /**
832
+ * Physical SQLite maintenance only. Domain lifecycles own row deletion; this
833
+ * boundary never decides semantic terminality or removes canonical records.
834
+ * PASSIVE checkpointing is non-blocking, and VACUUM is attempted only when
835
+ * already-reclaimable free pages exceed both configured thresholds. A live
836
+ * writer causes a fail-closed busy skip instead of lock stealing or corruption
837
+ * recovery.
838
+ */
839
+ export function maintainControlPlaneDatabase(
840
+ controllerHome: string,
841
+ options: ControlPlaneDatabaseMaintenanceOptions = {},
842
+ ): ControlPlaneDatabaseMaintenanceReport {
843
+ const path = controlPlaneDatabasePath(controllerHome);
844
+ const report: ControlPlaneDatabaseMaintenanceReport = {
845
+ policyVersion: CONTROL_PLANE_SQLITE_MAINTENANCE_POLICY_VERSION,
846
+ path,
847
+ checkpointMode: 'passive',
848
+ checkpointed: false,
849
+ pageSize: 0,
850
+ pageCountBefore: 0,
851
+ freePageCountBefore: 0,
852
+ reclaimableBytesBefore: 0,
853
+ reclaimableRatioBefore: 0,
854
+ vacuumEligible: false,
855
+ vacuumAttempted: false,
856
+ vacuumed: false,
857
+ pageCountAfter: 0,
858
+ freePageCountAfter: 0,
859
+ reclaimableBytesAfter: 0,
860
+ reclaimableRatioAfter: 0,
861
+ reclaimedBytes: 0,
862
+ };
863
+ if (!existsSync(path)) {
864
+ report.skippedReason = 'database_missing';
865
+ return report;
866
+ }
867
+
868
+ let database: SqliteDatabase;
869
+ try {
870
+ database = openRawDatabase(path);
871
+ } catch (error) {
872
+ if ((error instanceof Error ? error.message : String(error)).startsWith('CONTROL_PLANE_SQLITE_BUSY:')) {
873
+ report.skippedReason = 'database_busy';
874
+ return report;
875
+ }
876
+ throw error;
877
+ }
878
+ try {
879
+ database.exec('PRAGMA busy_timeout = 0; PRAGMA foreign_keys = ON;');
880
+ assertSupportedSchema(database, path, true);
881
+ try {
882
+ database.exec('PRAGMA wal_checkpoint(PASSIVE);');
883
+ report.checkpointed = true;
884
+ } catch (error) {
885
+ const busy = sqliteBusyError(path, error);
886
+ if (busy) {
887
+ report.skippedReason = 'database_busy';
888
+ return report;
889
+ }
890
+ throw error;
891
+ }
892
+
893
+ const before = controlPlaneDatabasePageSnapshot(database);
894
+ report.pageSize = before.pageSize;
895
+ report.pageCountBefore = before.pageCount;
896
+ report.freePageCountBefore = before.freePageCount;
897
+ report.reclaimableBytesBefore = before.reclaimableBytes;
898
+ report.reclaimableRatioBefore = before.reclaimableRatio;
899
+ report.pageCountAfter = before.pageCount;
900
+ report.freePageCountAfter = before.freePageCount;
901
+ report.reclaimableBytesAfter = before.reclaimableBytes;
902
+ report.reclaimableRatioAfter = before.reclaimableRatio;
903
+
904
+ const minimumReclaimableBytes = Math.max(0, Math.floor(options.minimumReclaimableBytes ?? DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_BYTES));
905
+ const configuredRatio = Number(options.minimumReclaimableRatio ?? DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_RATIO);
906
+ const minimumReclaimableRatio = Number.isFinite(configuredRatio) ? Math.min(1, Math.max(0, configuredRatio)) : DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_RATIO;
907
+ report.vacuumEligible = before.freePageCount > 0
908
+ && before.reclaimableBytes >= minimumReclaimableBytes
909
+ && before.reclaimableRatio >= minimumReclaimableRatio;
910
+ if (!report.vacuumEligible) {
911
+ report.skippedReason = 'below_threshold';
912
+ return report;
913
+ }
914
+ if (options.allowVacuum === false) {
915
+ report.skippedReason = 'vacuum_disabled';
916
+ return report;
917
+ }
918
+
919
+ report.vacuumAttempted = true;
920
+ try {
921
+ database.exec('VACUUM;');
922
+ } catch (error) {
923
+ const busy = sqliteBusyError(path, error);
924
+ if (busy) {
925
+ report.skippedReason = 'database_busy';
926
+ return report;
927
+ }
928
+ throw error;
929
+ }
930
+ assertDatabaseIntegrity(database, path);
931
+ const after = controlPlaneDatabasePageSnapshot(database);
932
+ report.vacuumed = true;
933
+ report.pageCountAfter = after.pageCount;
934
+ report.freePageCountAfter = after.freePageCount;
935
+ report.reclaimableBytesAfter = after.reclaimableBytes;
936
+ report.reclaimableRatioAfter = after.reclaimableRatio;
937
+ report.reclaimedBytes = Math.max(0, (before.pageCount - after.pageCount) * before.pageSize);
938
+ return report;
939
+ } finally {
940
+ database.close();
941
+ }
942
+ }
943
+
479
944
  export function backupControlPlaneDatabase(controllerHome: string, destinationPath: string): ControlPlaneDatabaseInspection {
480
945
  const destination = join(destinationPath);
481
946
  if (existsSync(destination)) throw new Error(`CONTROL_PLANE_BACKUP_EXISTS: ${destination}`);
@@ -497,6 +962,10 @@ export function backupControlPlaneDatabase(controllerHome: string, destinationPa
497
962
  export function restoreControlPlaneDatabase(controllerHome: string, backupPath: string): ControlPlaneDatabaseInspection {
498
963
  if (!existsSync(backupPath)) throw new Error(`CONTROL_PLANE_BACKUP_MISSING: ${backupPath}`);
499
964
  const target = controlPlaneDatabasePath(controllerHome);
965
+ // A reusable Runtime reader refers to the current inode, not the pathname.
966
+ // Close it before replacing the live authority; reuse remains enabled and the
967
+ // next read opens the verified replacement.
968
+ closeReusableReadDatabase(target);
500
969
  const staging = `${target}.restore-${process.pid}-${Date.now()}`;
501
970
  copyFileSync(backupPath, staging);
502
971
  try {