@humwick/server 0.130.0 → 0.130.2

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 (486) hide show
  1. package/README.md +5 -0
  2. package/docs/guides/tools-administration.md +6 -1
  3. package/docs/guides/tools-coordination.md +1 -0
  4. package/docs/guides/tools-tasks.md +8 -0
  5. package/docs/guides/workflows.md +58 -0
  6. package/docs/openapi.json +414 -1
  7. package/package.json +8 -17
  8. package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-BWLqMGKG.js} +2 -2
  9. package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-3TlIQpjk.js} +2 -2
  10. package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-Wimm0Czv.js} +2 -2
  11. package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-Bau_vNax.js} +2 -2
  12. package/public/assets/{activity-yLztXEBt.js → activity-CrSurn-r.js} +2 -2
  13. package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-CknAJKcb.js} +2 -2
  14. package/public/assets/agent-liveness-CaNwK0Zf.js +2 -0
  15. package/public/assets/{agent-name-CTZBrSD9.js → agent-name-DKbiIhBo.js} +2 -2
  16. package/public/assets/agent-profiles-DwF6EQ4R.js +2 -0
  17. package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-Diz4Sjjb.js} +2 -2
  18. package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-CRhpRQza.js} +2 -2
  19. package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-LrUEU3lB.js} +2 -2
  20. package/public/assets/{agents-BYDslkJc.js → agents-B-v66C-4.js} +2 -2
  21. package/public/assets/{analytics-B-XSbEMC.js → analytics-BqCb-LKi.js} +2 -2
  22. package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-kf4pGuAR.js} +2 -2
  23. package/public/assets/{artifacts-oBOqks-2.js → artifacts-D32zyw4Y.js} +2 -2
  24. package/public/assets/{artifacts-DPN1q4pA.js → artifacts-DJevXcvg.js} +2 -2
  25. package/public/assets/{automation-4KcRFXEu.js → automation-BpRajx_5.js} +2 -2
  26. package/public/assets/{backlog-Cw9xPTCF.js → backlog-D_UjTdzX.js} +2 -2
  27. package/public/assets/{busy-health-CTu32MZt.js → busy-health-N3VEh4ao.js} +2 -2
  28. package/public/assets/{channels-BiXzg1G9.js → channels-9jopungq.js} +2 -2
  29. package/public/assets/{chat-Ctk2qwvA.js → chat-CSXrTB3C.js} +2 -2
  30. package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-p1NnmqK0.js} +2 -2
  31. package/public/assets/{connectors-C36ax4Vw.js → connectors-CZdqlDNL.js} +2 -2
  32. package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BgdsE4dG.js} +2 -2
  33. package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-30aXTKva.js} +2 -2
  34. package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-B_jvw2GE.js} +2 -2
  35. package/public/assets/{deploys-Bwfq146q.js → deploys-DoN6bAb5.js} +2 -2
  36. package/public/assets/{display-CfO7rTuF.js → display-BrKWFGJz.js} +2 -2
  37. package/public/assets/{files-DhhZI2Ih.js → files-D6XEu8Ng.js} +2 -2
  38. package/public/assets/fleet-C7jQWbw2.js +2 -0
  39. package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BiDs3MLx.js} +2 -2
  40. package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-CQBwc1fb.js} +3 -3
  41. package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-DyG0WsQS.js} +2 -2
  42. package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-DKaW31dN.js} +2 -2
  43. package/public/assets/index-CRHep3M9.js +15 -0
  44. package/public/assets/index-MYAIwYTX.css +2 -0
  45. package/public/assets/{insights-fH1EL-qZ.js → insights-CzrtkpOE.js} +2 -2
  46. package/public/assets/{integrations-C5Oix6ng.js → integrations-aVsUmigt.js} +2 -2
  47. package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-BnSlM_95.js} +2 -2
  48. package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-qe7Ee47Z.js} +2 -2
  49. package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-NfjqFZAk.js} +2 -2
  50. package/public/assets/{managed-agents-egllbpij.js → managed-agents-BiH0HgaE.js} +2 -2
  51. package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-BixaT3Xr.js} +3 -3
  52. package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-DNGlWIS3.js} +2 -2
  53. package/public/assets/{memory-C8WmXsyr.js → memory-DNsCl6UX.js} +2 -2
  54. package/public/assets/{messages-BzQdqlO6.js → messages-DopU1L1X.js} +2 -2
  55. package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Cq7ckLQT.js} +2 -2
  56. package/public/assets/{overview-DCUnI4Z7.js → overview-C1DCTzzV.js} +2 -2
  57. package/public/assets/{pairs-ftCd2XcD.js → pairs-DE_m6rrE.js} +2 -2
  58. package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-B3rfh77F.js} +2 -2
  59. package/public/assets/project-profile-instructions-DQWZE0kA.js +2 -0
  60. package/public/assets/projects-Br2Bd2h1.js +2 -0
  61. package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-BKvFsvGD.js} +2 -2
  62. package/public/assets/provider-display-DsuWMvVL.js +2 -0
  63. package/public/assets/{registry-DWCU5c8q.js → registry-Cw38_6gW.js} +2 -2
  64. package/public/assets/run-card-CpKqRbhg.js +2 -0
  65. package/public/assets/scroll-text-BKXS1ahr.js +2 -0
  66. package/public/assets/{secrets-C7GIUATz.js → secrets-DxI-vzSq.js} +2 -2
  67. package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-D9s_VJbd.js} +2 -2
  68. package/public/assets/{settings-C6sZ9eBa.js → settings-CKZTVGIx.js} +2 -2
  69. package/public/assets/{shipping-Bu8p1gEw.js → shipping-B9YtnwtG.js} +2 -2
  70. package/public/assets/{stats-V9Z-GxUd.js → stats-DyUBKyWI.js} +2 -2
  71. package/public/assets/{status-dot-CVv0iGHL.js → status-dot-DYqlEUhJ.js} +2 -2
  72. package/public/assets/{store-BvtFdlGX.js → store-DcClXLH0.js} +3 -3
  73. package/public/assets/{tasks-CXU75HRF.js → tasks-Bh7Chr4-.js} +2 -2
  74. package/public/assets/{teams-BPf976zb.js → teams-bowI7qVY.js} +2 -2
  75. package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-BgVLHdsU.js} +3 -3
  76. package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-Dz22UPy6.js} +2 -2
  77. package/public/assets/token-estimate-Co-Jdphh.js +2 -0
  78. package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-DSQQ-iY7.js} +2 -2
  79. package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-samMQhQf.js} +2 -2
  80. package/public/assets/use-project-list-59LheVP7.js +2 -0
  81. package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BZa25xEB.js} +2 -2
  82. package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-DHxlvUvV.js} +2 -2
  83. package/public/assets/versioned-prompt-editor-BmED5DnJ.js +8 -0
  84. package/public/assets/{work-queue-TCjJxeT-.js → work-queue-CSvpuNLz.js} +2 -2
  85. package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-DAZcj5qt.js} +2 -2
  86. package/public/assets/{workflows-CXQX20Qj.js → workflows-DdmxRSyg.js} +2 -2
  87. package/public/assets/{workspaces-qSYzKYMA.js → workspaces-BxeKfVuF.js} +2 -2
  88. package/public/cosmos.html +4 -4
  89. package/public/index.html +31 -29
  90. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  91. package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
  92. package/runner/src/adapters/claude-effort.ts +2 -4
  93. package/runner/src/adapters/codex-launch-config.ts +4 -5
  94. package/runner/src/adapters/codex.ts +1 -1
  95. package/runner/src/adapters/opencode-provider.ts +7 -22
  96. package/runner/src/adapters/opencode.ts +4 -4
  97. package/runner/src/agent-runner/credentials.ts +42 -12
  98. package/runner/src/agent-runner/inbox.ts +27 -0
  99. package/runner/src/agent-runner/lifecycle-milestones.ts +0 -2
  100. package/runner/src/bin/claude.ts +4 -0
  101. package/runner/src/bin/codex.ts +4 -0
  102. package/runner/src/bin/opencode.ts +4 -0
  103. package/runner/src/index.ts +5 -2
  104. package/runner/src/provider-setup.ts +34 -0
  105. package/runner/src/providers.ts +2 -1
  106. package/runner/src/runner-core.ts +18 -23
  107. package/runner/src/spawn-input.ts +2 -1
  108. package/runner/src/test-run-dedup.ts +59 -56
  109. package/scripts/cd-config.ts +24 -3
  110. package/scripts/gate-run-lease.ts +10 -7
  111. package/scripts/gate.sh +5 -1
  112. package/src/branch-land-notice.ts +93 -0
  113. package/src/branch-landed.ts +7 -76
  114. package/src/bus.ts +19 -20
  115. package/src/cd-deploy-notify.ts +30 -2
  116. package/src/cd-host-lag-notify.ts +7 -6
  117. package/src/ci/incidents.ts +2 -2
  118. package/src/ci/monitor.ts +5 -8
  119. package/src/ci/root-missing.ts +2 -7
  120. package/src/ci/transition-notify.ts +7 -39
  121. package/src/cli/cd-pauses.ts +19 -0
  122. package/src/cli/orchestrator.ts +1 -1
  123. package/src/cli/state.ts +2 -19
  124. package/src/cli/task-edit.ts +22 -0
  125. package/src/cli/task.ts +3 -1
  126. package/src/cli/workflow.ts +8 -2
  127. package/src/commands-db.ts +10 -6
  128. package/src/commands-write.ts +8 -0
  129. package/src/config-store-write.ts +13 -1
  130. package/src/config-store.ts +25 -14
  131. package/src/db/activity-async.ts +6 -1
  132. package/src/db/activity-write-ops.ts +13 -1
  133. package/src/db/activity.ts +1 -14
  134. package/src/db/agent-lifecycle-write-ops.ts +1 -0
  135. package/src/db/agent-meta.ts +2 -2
  136. package/src/db/agent-ownership-write-ops.ts +4 -0
  137. package/src/db/agent-search.ts +1 -15
  138. package/src/db/agents-async.ts +58 -7
  139. package/src/db/agents-write-ops.ts +28 -5
  140. package/src/db/agents.ts +81 -54
  141. package/src/db/artifacts-write-ops.ts +8 -3
  142. package/src/db/bus-outbox-write-ops.ts +1 -0
  143. package/src/db/channels-write-ops.ts +4 -0
  144. package/src/db/ci-incidents-async.ts +5 -5
  145. package/src/db/ci-incidents-write-ops.ts +2 -2
  146. package/src/db/claude-api-budget-ops.ts +5 -5
  147. package/src/db/claude-app-oauth.ts +4 -4
  148. package/src/db/codex-reset-consumes-write-ops.ts +3 -0
  149. package/src/db/command-row.ts +10 -2
  150. package/src/db/commands-list-query.ts +2 -0
  151. package/src/db/commands-spawn-read.ts +1 -1
  152. package/src/db/commands-write-ops.ts +21 -0
  153. package/src/db/config-store-write-ops.ts +9 -2
  154. package/src/db/continuation-archives-async.ts +5 -0
  155. package/src/db/continuation-archives-write-ops.ts +9 -0
  156. package/src/db/continuations.ts +2 -2
  157. package/src/db/db-writer-worker.ts +8 -1
  158. package/src/db/delivery-write-ops.ts +5 -0
  159. package/src/db/deploy-events-async.ts +1 -0
  160. package/src/db/deploy-events-read-ops.ts +2 -1
  161. package/src/db/deploy-events.ts +35 -4
  162. package/src/db/hosts-registration.ts +10 -2
  163. package/src/db/hosts-write-ops.ts +14 -0
  164. package/src/db/hosts.ts +9 -6
  165. package/src/db/inbox-write-ops.ts +4 -0
  166. package/src/db/inbox.ts +0 -14
  167. package/src/db/index.ts +2 -0
  168. package/src/db/insights-write-ops.ts +1 -0
  169. package/src/db/integrations-async.ts +8 -0
  170. package/src/db/integrations-ops.ts +15 -0
  171. package/src/db/integrations.ts +15 -21
  172. package/src/db/issue-lifecycle.ts +3 -4
  173. package/src/db/issue-refs-async.ts +20 -0
  174. package/src/db/issue-refs-write-ops.ts +29 -0
  175. package/src/db/issue-refs.ts +7 -8
  176. package/src/db/long-running-work.ts +2 -2
  177. package/src/db/macos-privacy.ts +1 -1
  178. package/src/db/main-connection-writes.ts +23 -2
  179. package/src/db/maintenance-job-reconcile.ts +61 -0
  180. package/src/db/maintenance-job-runs-async.ts +14 -0
  181. package/src/db/maintenance-job-write-ops.ts +9 -0
  182. package/src/db/mappers.ts +2 -14
  183. package/src/db/merge-lease.ts +1 -15
  184. package/src/db/message-reads-write-ops.ts +3 -0
  185. package/src/db/message-write-ops.ts +4 -0
  186. package/src/db/migrations.ts +7 -15
  187. package/src/db/orchestrators-async.ts +7 -3
  188. package/src/db/orchestrators-write-ops.ts +7 -1
  189. package/src/db/orchestrators.ts +21 -77
  190. package/src/db/pairs.ts +1 -13
  191. package/src/db/project-profile-instructions.ts +50 -0
  192. package/src/db/prompt-versions-ops.ts +1 -0
  193. package/src/db/prompt-versions.ts +9 -2
  194. package/src/db/provider-catalog-write-ops.ts +4 -4
  195. package/src/db/provider-config-history.ts +8 -3
  196. package/src/db/provider-flap-messages-write-ops.ts +1 -0
  197. package/src/db/provider-model-seed.ts +34 -0
  198. package/src/db/provider-quota-lease-write-ops.ts +1 -0
  199. package/src/db/provider-quota-write-ops.ts +2 -0
  200. package/src/db/provisioning-write-ops.ts +9 -8
  201. package/src/db/read-op-marshaling-fixtures.ts +5 -0
  202. package/src/db/read-ops.ts +4 -0
  203. package/src/db/routing-policy.ts +3 -4
  204. package/src/db/schema.ts +9 -3
  205. package/src/db/secrets-broker-write-ops.ts +5 -5
  206. package/src/db/secrets-vault-write.ts +7 -7
  207. package/src/db/services-write-ops.ts +3 -0
  208. package/src/db/spawn-delegation-state.ts +11 -4
  209. package/src/db/spawn-tree-cleanup.ts +3 -0
  210. package/src/db/stats-async.ts +4 -3
  211. package/src/db/task-entity.ts +23 -2
  212. package/src/db/task-list.ts +3 -1
  213. package/src/db/tasks.ts +1 -17
  214. package/src/db/team-spawn-delegation.ts +1 -1
  215. package/src/db/teams-write-ops.ts +4 -4
  216. package/src/db/timer-cutover-write-ops.ts +7 -0
  217. package/src/db/timer-ingress-write-ops.ts +9 -0
  218. package/src/db/timer-ingress.ts +7 -1
  219. package/src/db/timer-operator-write-ops.ts +14 -0
  220. package/src/db/timer-transfer-write-ops.ts +4 -0
  221. package/src/db/timer-write-ops.ts +14 -0
  222. package/src/db/token-db-write-ops.ts +21 -0
  223. package/src/db/token-standing.ts +15 -11
  224. package/src/db/user-settings-write-ops.ts +1 -0
  225. package/src/db/vault-backup.ts +2 -2
  226. package/src/db/vault-expiry.ts +2 -2
  227. package/src/db/vault-health.ts +3 -3
  228. package/src/db/vault-token-ops.ts +8 -8
  229. package/src/db/web-tool-call-ops.ts +3 -3
  230. package/src/db/workflow-contact.ts +1 -1
  231. package/src/db/workflow-task-read-ops.ts +30 -0
  232. package/src/db/write-ops.ts +15 -1
  233. package/src/db/writer-fault-seam.ts +5 -2
  234. package/src/db/writer-gateway.ts +21 -12
  235. package/src/dispatch-availability.ts +10 -6
  236. package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
  237. package/src/host/disk-alert.ts +121 -0
  238. package/src/lifecycle-manager.ts +3 -2
  239. package/src/maintenance/cd-watcher.ts +3 -3
  240. package/src/maintenance/critical-job-watchdog.ts +16 -17
  241. package/src/maintenance/index.ts +1 -0
  242. package/src/maintenance/jobs.ts +3 -2
  243. package/src/maintenance/scheduler.ts +84 -128
  244. package/src/maintenance/stalled-delivery-pager.ts +6 -5
  245. package/src/maintenance/workspaces.ts +1 -1
  246. package/src/managed-policy.ts +31 -27
  247. package/src/mcp/index.ts +5 -2
  248. package/src/mcp/output-descriptions.ts +8 -8
  249. package/src/mcp/presentation.ts +4 -4
  250. package/src/mcp/project-profile-instructions.ts +32 -0
  251. package/src/mcp/tool-groups.ts +1 -1
  252. package/src/mcp/tools-spawn.ts +4 -3
  253. package/src/mcp-agent-action.ts +1 -2
  254. package/src/mcp-host-tools.ts +1 -1
  255. package/src/mcp-issue-ref-tools.ts +12 -11
  256. package/src/mcp-maintenance-tools.ts +2 -2
  257. package/src/mcp-profile-trust-tools.ts +4 -4
  258. package/src/mcp-provider-catalog.ts +2 -2
  259. package/src/mcp-task-tools.ts +11 -5
  260. package/src/mcp-team-template-tools.ts +3 -3
  261. package/src/mcp-workflow-tools.ts +10 -3
  262. package/src/notification-types.ts +2 -1
  263. package/src/operator-alarm.ts +26 -11
  264. package/src/package-pack.ts +42 -39
  265. package/src/project-instructions.ts +4 -0
  266. package/src/project-profile-instruction-text.ts +6 -0
  267. package/src/project-profile-instructions.ts +20 -0
  268. package/src/provider-catalog-store.ts +3 -49
  269. package/src/provider-config-store.ts +12 -11
  270. package/src/provider-model-seed.ts +1 -18
  271. package/src/provider-quota-config-store.ts +5 -4
  272. package/src/provider-secrets.test-fixture.ts +3 -3
  273. package/src/recipe-runner.ts +2 -2
  274. package/src/repo-config-source.ts +10 -4
  275. package/src/routes/agent-profiles.ts +6 -6
  276. package/src/routes/agents-spawn.ts +1 -1
  277. package/src/routes/agents.ts +6 -6
  278. package/src/routes/commands-listing.ts +4 -0
  279. package/src/routes/commands.ts +2 -2
  280. package/src/routes/continuation-archives.ts +2 -2
  281. package/src/routes/host-resource-control.ts +4 -1
  282. package/src/routes/index.ts +10 -1
  283. package/src/routes/integrations.ts +24 -50
  284. package/src/routes/issue-refs.ts +7 -7
  285. package/src/routes/orchestrator-bootstrap.ts +3 -3
  286. package/src/routes/orchestrator-host-registration.ts +2 -2
  287. package/src/routes/orchestrator-host-utilization.ts +3 -1
  288. package/src/routes/orchestrator-proxy.ts +2 -2
  289. package/src/routes/orchestrator.ts +4 -4
  290. package/src/routes/project-profile-instructions.ts +27 -0
  291. package/src/routes/project-trust.ts +2 -2
  292. package/src/routes/provider-config.ts +9 -8
  293. package/src/routes/provider-quota-config.ts +2 -2
  294. package/src/routes/settings.ts +5 -5
  295. package/src/routes/stats.ts +6 -6
  296. package/src/routes/task-http.ts +15 -1
  297. package/src/routes/task-issues.ts +11 -26
  298. package/src/routes/tasks.ts +22 -53
  299. package/src/routes/team-templates.ts +5 -5
  300. package/src/routes/teams-config.ts +2 -2
  301. package/src/routes/tokens.ts +12 -5
  302. package/src/routes/workflows.ts +20 -5
  303. package/src/routing-policy-normalize.ts +1 -2
  304. package/src/routing-step-defaults.ts +0 -2
  305. package/src/runner-token-mint-limit.ts +63 -0
  306. package/src/runtime-grant.ts +4 -2
  307. package/src/runtime-tokens.ts +50 -9
  308. package/src/security.ts +13 -20
  309. package/src/server-background.ts +1 -1
  310. package/src/server.ts +5 -2
  311. package/src/services/host-utilization.ts +23 -0
  312. package/src/services/idle-epics.ts +5 -6
  313. package/src/services/issue-lifecycle.ts +57 -61
  314. package/src/services/issue-refs.ts +9 -13
  315. package/src/services/kernel-timers-cutover-chaos-worker.ts +12 -7
  316. package/src/services/kernel-timers-ingress.ts +1 -1
  317. package/src/services/kernel-timers-request-key-migration-chaos-worker.ts +4 -1
  318. package/src/services/landed-ancestry.ts +19 -3
  319. package/src/services/memory-areas.ts +15 -51
  320. package/src/services/plan-ci-notice.ts +57 -0
  321. package/src/services/plan-ci-recovery.ts +2 -55
  322. package/src/services/plan-wake-digest.ts +1 -0
  323. package/src/services/profile-trust.ts +4 -3
  324. package/src/services/project-config-read.ts +70 -0
  325. package/src/services/project-profile-instructions.ts +54 -0
  326. package/src/services/provisioning-harness/budget-baseline.json +4 -4
  327. package/src/services/restart-spawn.ts +61 -49
  328. package/src/services/self-care-state.ts +2 -1
  329. package/src/services/self-effort.ts +5 -5
  330. package/src/services/send-message.ts +5 -1
  331. package/src/services/spawn-agent.ts +13 -2
  332. package/src/services/spawn-chain-inspector.ts +6 -13
  333. package/src/services/spawn-policy.ts +68 -0
  334. package/src/services/spawn-tree-cleanup.ts +2 -2
  335. package/src/services/task-dispatch.ts +3 -0
  336. package/src/services/task-state-reconcile.ts +6 -14
  337. package/src/services/team-templates.ts +9 -8
  338. package/src/services/transient-agent-reaper.ts +3 -1
  339. package/src/services/workflow-auto-start.ts +25 -13
  340. package/src/services/workflow-ci-main.ts +1 -1
  341. package/src/services/workflow-deferred-cleanup.ts +4 -2
  342. package/src/services/workflow-effect-driver.ts +2 -0
  343. package/src/services/workflow-effects.ts +18 -11
  344. package/src/services/workflow-land-notice.ts +10 -0
  345. package/src/services/workflow-pause-text.ts +43 -0
  346. package/src/services/workflow-reconcile.ts +13 -11
  347. package/src/services/workflow-run-view.ts +2 -0
  348. package/src/services/workflow-runs.ts +29 -13
  349. package/src/services/workflow-supervisor-state.ts +5 -0
  350. package/src/services/workflow-supervisor.ts +7 -3
  351. package/src/settings/registry.ts +1 -2
  352. package/src/spawn-command.ts +4 -4
  353. package/src/spawn-models.ts +10 -12
  354. package/src/spawn-placement.ts +1 -1
  355. package/src/spawn-targets.ts +1 -1
  356. package/src/startup-context-injection.ts +1 -0
  357. package/src/state-adoption/cd-cutover.ts +53 -7
  358. package/src/state-adoption/fleet-cutover.ts +35 -0
  359. package/src/state-adoption/fleet-plan.ts +36 -0
  360. package/src/state-adoption/index.ts +2 -2
  361. package/src/state-adoption/linux-plan.ts +2 -2
  362. package/src/state-adoption/linux.ts +2 -2
  363. package/src/state-adoption/macos-plan.ts +2 -2
  364. package/src/state-adoption/macos.ts +1 -1
  365. package/src/steward.ts +7 -6
  366. package/src/task-dispatch-live.ts +1 -1
  367. package/src/task-dispatcher.ts +1 -1
  368. package/src/teams-config-store.ts +4 -3
  369. package/src/token-db-write.ts +10 -0
  370. package/src/workspace-command-settle.ts +2 -1
  371. package/src/workspace-steward-wake.ts +2 -2
  372. package/node_modules/@humwick/kernel/LICENSE +0 -167
  373. package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
  374. package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
  375. package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
  376. package/node_modules/@humwick/kernel/config.ts +0 -111
  377. package/node_modules/@humwick/kernel/contract.ts +0 -572
  378. package/node_modules/@humwick/kernel/control-authz.ts +0 -281
  379. package/node_modules/@humwick/kernel/control.ts +0 -645
  380. package/node_modules/@humwick/kernel/effects.ts +0 -493
  381. package/node_modules/@humwick/kernel/event-log.ts +0 -319
  382. package/node_modules/@humwick/kernel/http-transport.ts +0 -384
  383. package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
  384. package/node_modules/@humwick/kernel/identity.ts +0 -248
  385. package/node_modules/@humwick/kernel/index.ts +0 -28
  386. package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
  387. package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
  388. package/node_modules/@humwick/kernel/landing.ts +0 -953
  389. package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
  390. package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
  391. package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
  392. package/node_modules/@humwick/kernel/message-authority.ts +0 -281
  393. package/node_modules/@humwick/kernel/module-host.ts +0 -722
  394. package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
  395. package/node_modules/@humwick/kernel/package.json +0 -47
  396. package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
  397. package/node_modules/@humwick/kernel/persistence.ts +0 -510
  398. package/node_modules/@humwick/kernel/registry.ts +0 -60
  399. package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
  400. package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
  401. package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
  402. package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
  403. package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
  404. package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
  405. package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
  406. package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
  407. package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
  408. package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
  409. package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
  410. package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
  411. package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
  412. package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
  413. package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
  414. package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
  415. package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
  416. package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
  417. package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
  418. package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
  419. package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
  420. package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
  421. package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
  422. package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
  423. package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
  424. package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
  425. package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
  426. package/node_modules/@humwick/offload/LICENSE +0 -167
  427. package/node_modules/@humwick/offload/README.md +0 -57
  428. package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
  429. package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
  430. package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
  431. package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
  432. package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
  433. package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
  434. package/node_modules/@humwick/offload/dist/envelope.js +0 -80
  435. package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
  436. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
  437. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
  438. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
  439. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
  440. package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
  441. package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
  442. package/node_modules/@humwick/offload/dist/index.js +0 -5
  443. package/node_modules/@humwick/offload/dist/index.js.map +0 -1
  444. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
  445. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
  446. package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
  447. package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
  448. package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
  449. package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
  450. package/node_modules/@humwick/offload/dist/types.js +0 -176
  451. package/node_modules/@humwick/offload/dist/types.js.map +0 -1
  452. package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
  453. package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
  454. package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
  455. package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
  456. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
  457. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
  458. package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
  459. package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
  460. package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
  461. package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
  462. package/node_modules/@humwick/offload/dist/worker.js +0 -1
  463. package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
  464. package/node_modules/@humwick/offload/package.json +0 -46
  465. package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
  466. package/node_modules/@humwick/offload/src/envelope.ts +0 -92
  467. package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
  468. package/node_modules/@humwick/offload/src/index.ts +0 -4
  469. package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
  470. package/node_modules/@humwick/offload/src/types.ts +0 -235
  471. package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
  472. package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
  473. package/node_modules/@humwick/offload/src/worker.ts +0 -5
  474. package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
  475. package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
  476. package/public/assets/fleet-BL4UXS_k.js +0 -2
  477. package/public/assets/index-B7ZerHLY.css +0 -2
  478. package/public/assets/index-BK0WV6hF.js +0 -15
  479. package/public/assets/projects-B_lmcqfO.js +0 -2
  480. package/public/assets/provider-display-DbLWpP3j.js +0 -2
  481. package/public/assets/run-card-CO_bguQf.js +0 -2
  482. package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
  483. package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
  484. package/runner/src/gate-run-lease-probe.ts +0 -38
  485. package/src/db/claude-catalog-reconcile.ts +0 -27
  486. package/src/routing-model-aliases.ts +0 -21
@@ -25,6 +25,7 @@ import {
25
25
  type PruneExpiredTokenRecordsInput,
26
26
  } from "../token-db.ts";
27
27
  import type { TokenRecord } from "../types.ts";
28
+ import { recordLaunchFact, type LaunchFact } from "./launch-facts.ts";
28
29
  import type { TokenRevokeReason } from "../security.ts";
29
30
  import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
30
31
 
@@ -41,6 +42,11 @@ export interface HandoffTokenOpArgs {
41
42
  readonly newJti: string;
42
43
  }
43
44
 
45
+ /** Args for `tokens.createRunner`: a runner mint plus, for a fresh launch, its launch fact (#1870 S7). */
46
+ export interface CreateRunnerTokenOpArgs extends CreateTokenOpArgs {
47
+ readonly launch?: { readonly agentId: string; readonly fact: LaunchFact };
48
+ }
49
+
44
50
  export interface RevokeTokenOpArgs {
45
51
  readonly jti: string;
46
52
  readonly reason?: TokenRevokeReason;
@@ -67,12 +73,25 @@ function stripHandoffToken(result: unknown): unknown {
67
73
  export const TOKEN_DB_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
68
74
  // Non-idempotent (mints a new row each apply); the facade supplies `jti` as the ledger key.
69
75
  "tokens.create": {
76
+ offLoop: true,
70
77
  idempotent: false,
71
78
  fn: (args: CreateTokenOpArgs, ctx: WriterOpContext) => createTokenInternal(args.input, args.jti, ctx.now),
72
79
  serializeIdempotencyResult: stripCreateToken,
73
80
  },
81
+ // A runner mint and the launch fact its successors derive from, in one transaction.
82
+ "tokens.createRunner": {
83
+ offLoop: true,
84
+ idempotent: false,
85
+ fn: (args: CreateRunnerTokenOpArgs, ctx: WriterOpContext) => {
86
+ const issued = createTokenInternal(args.input, args.jti, ctx.now);
87
+ if (args.launch) recordLaunchFact(args.launch.agentId, args.launch.fact, ctx.now);
88
+ return issued;
89
+ },
90
+ serializeIdempotencyResult: stripCreateToken,
91
+ },
74
92
  // Non-idempotent (mints and links a successor); folds the eligibility read (ADR §3.4).
75
93
  "tokens.handoff": {
94
+ offLoop: true,
76
95
  idempotent: false,
77
96
  fn: (args: HandoffTokenOpArgs, ctx: WriterOpContext) => handoffTokenInternal(args.input, args.newJti, ctx.now),
78
97
  serializeIdempotencyResult: stripHandoffToken,
@@ -80,11 +99,13 @@ export const TOKEN_DB_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
80
99
  // Idempotent by construction (the `revoked_at IS NULL` guard makes a re-apply a no-op). No key, no
81
100
  // secret in the boolean result.
82
101
  "tokens.revoke": {
102
+ offLoop: true,
83
103
  idempotent: true,
84
104
  fn: (args: RevokeTokenOpArgs, ctx: WriterOpContext) => revokeTokenInternal(args.jti, args.reason, ctx.now),
85
105
  },
86
106
  // Idempotent (deleting an already-reaped row is a no-op). Returns pruned jtis — not secret.
87
107
  "tokens.pruneExpired": {
108
+ offLoop: true,
88
109
  idempotent: true,
89
110
  fn: (args: PruneExpiredTokensOpArgs, ctx: WriterOpContext) =>
90
111
  pruneExpiredTokenRecordsInternal(args.input, ctx.now),
@@ -61,24 +61,28 @@ export function tokenRevocationReason(jti: string): string | null | undefined {
61
61
  /** A rotation retires one token for its successor, so it is never a lineage revocation. */
62
62
  export type LineageRevokeReason = Exclude<TokenRevokeReason, "rotation">;
63
63
 
64
- /** #1245 — a token and every token handed off from it (token-db.ts handoffTokenInternal) are one credential, so
65
- * revoking any of them revokes the whole lineage: unretired predecessors, pending successors and their renewals.
66
- * A hard reason also hardens stale members, so no auth, renewal or orchestrator recovery reaches the lineage. A
67
- * stale walk stops at stale members: what follows them is an orchestrator recovery. It never enters a `keep` token. */
68
- export function revokeTokenLineage(seeds: readonly string[], reason: LineageRevokeReason | null, nowSeconds: number, keep: readonly string[] = []): string[] {
69
- if (seeds.length === 0) return [];
70
- const rows = getDb().query(`
64
+ // #3776 — each step must be a key lookup whatever sqlite_stat1 says: CROSS JOIN pins the join order and the unary
65
+ // `+` keeps the predecessor index off the backward step, so stale stats cannot make the walk scan per member.
66
+ export const TOKEN_LINEAGE_REVOKE_SQL = `
71
67
  WITH RECURSIVE kept(jti) AS (SELECT value FROM json_each(?1)),
72
68
  lineage(jti) AS (
73
69
  SELECT value FROM json_each(?2) WHERE value NOT IN kept
74
- UNION SELECT t.jti FROM lineage l JOIN tokens p ON p.jti = l.jti JOIN tokens t ON t.predecessor_jti = p.jti
70
+ UNION SELECT t.jti FROM lineage l CROSS JOIN tokens p ON p.jti = l.jti CROSS JOIN tokens t ON t.predecessor_jti = p.jti
75
71
  WHERE t.jti NOT IN kept AND (?4 IS NOT 'stale' OR p.revoked_reason IS NOT 'stale')
76
- UNION SELECT p.predecessor_jti FROM lineage l JOIN tokens p ON p.jti = l.jti
77
- WHERE p.predecessor_jti IS NOT NULL AND p.predecessor_jti NOT IN kept AND (?4 IS NOT 'stale' OR p.revoked_reason IS NOT 'stale')
72
+ UNION SELECT p.predecessor_jti FROM lineage l CROSS JOIN tokens p ON p.jti = l.jti
73
+ WHERE +p.predecessor_jti IS NOT NULL AND +p.predecessor_jti NOT IN kept AND (?4 IS NOT 'stale' OR p.revoked_reason IS NOT 'stale')
78
74
  )
79
75
  UPDATE tokens SET revoked_at = COALESCE(revoked_at, ?3), revoked_reason = ?4
80
76
  WHERE jti IN lineage AND (revoked_at IS NULL OR (?4 IS NOT 'stale' AND revoked_reason = 'stale'))
81
77
  RETURNING jti
82
- `).all(JSON.stringify(keep), JSON.stringify(seeds), nowSeconds, reason) as Array<{ jti: string }>;
78
+ `;
79
+
80
+ /** #1245 — a token and every token handed off from it (token-db.ts handoffTokenInternal) are one credential, so
81
+ * revoking any of them revokes the whole lineage: unretired predecessors, pending successors and their renewals.
82
+ * A hard reason also hardens stale members, so no auth, renewal or orchestrator recovery reaches the lineage. A
83
+ * stale walk stops at stale members: what follows them is an orchestrator recovery. It never enters a `keep` token. */
84
+ export function revokeTokenLineage(seeds: readonly string[], reason: LineageRevokeReason | null, nowSeconds: number, keep: readonly string[] = []): string[] {
85
+ if (seeds.length === 0) return [];
86
+ const rows = getDb().query(TOKEN_LINEAGE_REVOKE_SQL).all(JSON.stringify(keep), JSON.stringify(seeds), nowSeconds, reason) as Array<{ jti: string }>;
83
87
  return rows.map((row) => row.jti);
84
88
  }
@@ -36,6 +36,7 @@ export const USER_SETTINGS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = O
36
36
  },
37
37
  // Idempotent by construction: deleting again finds no row (`existing` null → early false).
38
38
  "userSettings.delete": {
39
+ offLoop: true,
39
40
  idempotent: true,
40
41
  fn: (args: DeleteUserSettingOpArgs) => deleteUserSetting(getDb(), args.userId, args.namespace, args.key),
41
42
  },
@@ -54,6 +54,6 @@ export function syncVaultBackupWarning(args: { kind: VaultBackupWarning | null;
54
54
  }
55
55
 
56
56
  export const VAULT_BACKUP_WRITE_OPS = {
57
- "secretsVault.backup.record": { idempotent: true, fn: recordVaultBackup },
58
- "secretsVault.backup.warn": { idempotent: true, fn: syncVaultBackupWarning },
57
+ "secretsVault.backup.record": { offLoop: true, idempotent: true, fn: recordVaultBackup },
58
+ "secretsVault.backup.warn": { offLoop: true, idempotent: true, fn: syncVaultBackupWarning },
59
59
  };
@@ -66,6 +66,6 @@ export function prepareVaultExpiryNotices(args: { now: number }) {
66
66
  }
67
67
 
68
68
  export const VAULT_EXPIRY_WRITE_OPS = {
69
- "secretsVault.expiry.prepare": { idempotent: true, fn: prepareVaultExpiryNotices },
70
- "secretsVault.expiry.sent": { idempotent: true, fn: (args: { id: number }) => { getDb().query("UPDATE vault_expiry_notices SET notified=1 WHERE decision_id=?").run(args.id); } },
69
+ "secretsVault.expiry.prepare": { offLoop: true, idempotent: true, fn: prepareVaultExpiryNotices },
70
+ "secretsVault.expiry.sent": { offLoop: true, idempotent: true, fn: (args: { id: number }) => { getDb().query("UPDATE vault_expiry_notices SET notified=1 WHERE decision_id=?").run(args.id); } },
71
71
  };
@@ -85,9 +85,9 @@ export function logPage(args: { profile?: string; before?: { at: number; id: num
85
85
  }
86
86
 
87
87
  export const VAULT_HEALTH_WRITE_OPS: Record<string, WriterOpSpec> = {
88
- "vaultHealth.host": { idempotent: true, fn: (host: ServiceAccessHostHealth, ctx: WriterOpContext) => { writeState("host", host, ctx.now); return null; } },
89
- "vaultHealth.collect": { idempotent: true, fn: (input: VaultLogCollect, ctx: WriterOpContext) => collectVaultLogs(input, ctx.now) },
90
- "vaultHealth.prune": { idempotent: true, fn: (args: { now: number }) => pruneVaultLogs(args.now) },
88
+ "vaultHealth.host": { offLoop: true, idempotent: true, fn: (host: ServiceAccessHostHealth, ctx: WriterOpContext) => { writeState("host", host, ctx.now); return null; } },
89
+ "vaultHealth.collect": { offLoop: true, idempotent: true, fn: (input: VaultLogCollect, ctx: WriterOpContext) => collectVaultLogs(input, ctx.now) },
90
+ "vaultHealth.prune": { offLoop: true, idempotent: true, fn: (args: { now: number }) => pruneVaultLogs(args.now) },
91
91
  };
92
92
 
93
93
  export const VAULT_HEALTH_READ_OPS = {
@@ -3,12 +3,12 @@ import { settleVaultToken, revokeVaultTokens, recordOrphanVaultToken, pruneVault
3
3
  import type { WriterOpContext, WriterOpSpec } from "./write-ops";
4
4
 
5
5
  export const VAULT_TOKEN_WRITE_OPS: Record<string, WriterOpSpec> = {
6
- "vaultTokens.orphan": { idempotent: true, fn: (row: VaultTokenRow) => recordOrphanVaultToken(row) },
7
- "vaultTokens.prune": { idempotent: true, fn: (args: { now: number }) => pruneVaultTokens(args.now) },
8
- "vaultTokens.begin": { idempotent: false, fn: (row: VaultTokenRow) => beginVaultToken(row) },
9
- "vaultTokens.activate": { idempotent: true, fn: (args: { id: string; sessionId: string; expiresAt: number }, ctx: WriterOpContext) => activateVaultToken(args.id, args.sessionId, args.expiresAt, ctx.now) },
10
- "vaultTokens.renew": { idempotent: true, fn: (args: { id: string; sessionId: string; expiresAt: number }, ctx: WriterOpContext) => renewVaultToken(args.id, args.sessionId, args.expiresAt, ctx.now) },
11
- "vaultTokens.check": { idempotent: true, fn: (args: { id: string; now: number }) => checkVaultToken(args.id, args.now) ?? null },
12
- "vaultTokens.settle": { idempotent: false, fn: (args: { id: string; error?: string }, ctx: WriterOpContext) => { settleVaultToken(args.id, ctx.now, args.error); return null; } },
13
- "vaultTokens.revoke": { idempotent: true, fn: (args: { agentId: string; instanceId: string; spawnRequestId: string; launchId: string }, ctx: WriterOpContext) => { revokeVaultTokens(args.agentId, ctx.now, "runner_release", args.instanceId, args.spawnRequestId, args.launchId); return null; } },
6
+ "vaultTokens.orphan": { offLoop: true, idempotent: true, fn: (row: VaultTokenRow) => recordOrphanVaultToken(row) },
7
+ "vaultTokens.prune": { offLoop: true, idempotent: true, fn: (args: { now: number }) => pruneVaultTokens(args.now) },
8
+ "vaultTokens.begin": { offLoop: true, idempotent: false, fn: (row: VaultTokenRow) => beginVaultToken(row) },
9
+ "vaultTokens.activate": { offLoop: true, idempotent: true, fn: (args: { id: string; sessionId: string; expiresAt: number }, ctx: WriterOpContext) => activateVaultToken(args.id, args.sessionId, args.expiresAt, ctx.now) },
10
+ "vaultTokens.renew": { offLoop: true, idempotent: true, fn: (args: { id: string; sessionId: string; expiresAt: number }, ctx: WriterOpContext) => renewVaultToken(args.id, args.sessionId, args.expiresAt, ctx.now) },
11
+ "vaultTokens.check": { offLoop: true, idempotent: true, fn: (args: { id: string; now: number }) => checkVaultToken(args.id, args.now) ?? null },
12
+ "vaultTokens.settle": { offLoop: true, idempotent: false, fn: (args: { id: string; error?: string }, ctx: WriterOpContext) => { settleVaultToken(args.id, ctx.now, args.error); return null; } },
13
+ "vaultTokens.revoke": { offLoop: true, idempotent: true, fn: (args: { agentId: string; instanceId: string; spawnRequestId: string; launchId: string }, ctx: WriterOpContext) => { revokeVaultTokens(args.agentId, ctx.now, "runner_release", args.instanceId, args.spawnRequestId, args.launchId); return null; } },
14
14
  };
@@ -9,7 +9,7 @@ export const WEB_TOOL_CALL_READ_OPS = {
9
9
  };
10
10
 
11
11
  export const WEB_TOOL_CALL_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = {
12
- "webSearch.reserveBrave": { idempotent: false, fn: (args: { month: string }) => reserveBraveSearch(args.month) },
13
- "webToolCalls.record": { idempotent: true, fn: (call: WebToolCall) => { recordWebToolCall(call); return null; } },
14
- "webToolCalls.prune": { idempotent: true, fn: (args: { before: number }) => pruneWebToolCalls(args.before) },
12
+ "webSearch.reserveBrave": { offLoop: true, idempotent: false, fn: (args: { month: string }) => reserveBraveSearch(args.month) },
13
+ "webToolCalls.record": { offLoop: true, idempotent: true, fn: (call: WebToolCall) => { recordWebToolCall(call); return null; } },
14
+ "webToolCalls.prune": { offLoop: true, idempotent: true, fn: (args: { before: number }) => pruneWebToolCalls(args.before) },
15
15
  };
@@ -20,7 +20,7 @@ export interface WorkflowHeldContact { runId: string; reason: WorkflowContactRea
20
20
 
21
21
  /** Notices that bypass the step-contact hold: the run's own coordinator notices (including GO awaiting an external
22
22
  * hold's release), and a step agent held on a provider block, which waits until someone acts (#3647). */
23
- const COORDINATOR_NOTICE_KINDS = new Set(["workflow.started", "workflow.landed", "workflow.escalated", "workflow.result", "workflow.review-passed", "agent.spawn_failed", "agent.spawn_recovered", "delegation.budget_exhausted", "agent.provider_blocked", "agent.provider_still_held"]);
23
+ const COORDINATOR_NOTICE_KINDS = new Set(["workflow.started", "workflow.landed", "workflow.escalated", "workflow.result", "workflow.review-passed", "workflow.paused", "workflow.unpaused", "agent.spawn_failed", "agent.spawn_recovered", "delegation.budget_exhausted", "agent.provider_blocked", "agent.provider_still_held"]);
24
24
  export const WORKFLOW_SCOPE = "local";
25
25
  export const WORKFLOW_RUN_TABLE = "kernel_entity__workflow__run";
26
26
  const LINK_TABLE = "kernel_entity__workflow__link";
@@ -185,3 +185,33 @@ export function openWorkflowRunHolders(): { teamIds: Set<string>; coordinatorIds
185
185
  }
186
186
  return holders;
187
187
  }
188
+
189
+ /** #3757 — a running run a coordinator paused, with its input Task's team; `pause` is the stored JSON. */
190
+ export interface PausedWorkflowRunRow {
191
+ runId: string; taskId: number; taskTitle: string | null; coordinator: string; callerAgentId: string | null;
192
+ teamId: string | null; owner: string | null; pause: string;
193
+ }
194
+
195
+ let pauseColumnOn: unknown;
196
+ /** The column appears when the workflow module first registers after #3757; until then nothing is paused. */
197
+ function pauseColumnReady(): boolean {
198
+ const db = getDb();
199
+ if (pauseColumnOn === db) return true;
200
+ const ready = (db.query(`PRAGMA table_info(${WORKFLOW_RUN_TABLE})`).all() as Array<{ name: string }>).some((column) => column.name === "pause");
201
+ if (ready) pauseColumnOn = db;
202
+ return ready;
203
+ }
204
+
205
+ export function pausedWorkflowRuns(): PausedWorkflowRunRow[] {
206
+ if (!workflowTablesReady() || !pauseColumnReady()) return [];
207
+ return getDb().query(`SELECT run.id AS runId, run.taskId AS taskId, task.title AS taskTitle, run.coordinator AS coordinator, run.callerAgentId AS callerAgentId,
208
+ task.team_id AS teamId, task.owner AS owner, run.pause AS pause
209
+ FROM ${WORKFLOW_RUN_TABLE} run LEFT JOIN tasks task ON task.id = run.taskId
210
+ WHERE run.scope = ? AND run.status = 'running' AND run.pause IS NOT NULL ORDER BY run.id`).all(WORKFLOW_SCOPE) as PausedWorkflowRunRow[];
211
+ }
212
+
213
+ /** #3757 — a paused run keeps its idle step agents: the idle reapers leave them alone until it is unpaused. */
214
+ export function workflowRunPaused(runId: string): boolean {
215
+ if (!workflowTablesReady() || !pauseColumnReady()) return false;
216
+ return Boolean(getDb().query(`SELECT 1 FROM ${WORKFLOW_RUN_TABLE} WHERE scope = ? AND id = ? AND status = 'running' AND pause IS NOT NULL`).get(WORKFLOW_SCOPE, runId));
217
+ }
@@ -1,3 +1,4 @@
1
+ import { INTEGRATIONS_WRITE_OPS } from "./integrations-ops.ts";
1
2
  import { bufferedLastSeen, type PresenceSnapshot } from "./presence-buffer";
2
3
  import { ensureDurablePayloadSchema, readRetryReceipt, storeDurablePayload } from "./durable-payloads";
3
4
  import { ledgerSlotPredicate, ledgerSlots, WRITER_IDEMPOTENCY_RETENTION_MS } from "./writer-ledger";
@@ -5,6 +6,7 @@ import { workspaceChangeListeners } from "./workspaces";
5
6
  import { withWriterOpState } from "./main-thread-state";
6
7
  import { importVaultRows } from "./secrets-vault-import";
7
8
  import { PROMPT_VERSION_WRITE_OPS } from "./prompt-versions-ops";
9
+ import { PROJECT_PROFILE_INSTRUCTION_WRITE_OPS } from "./project-profile-instructions";
8
10
  import { VAULT_WRITE_OPS } from "./secrets-vault-write";
9
11
  import { VAULT_EXPIRY_WRITE_OPS } from "./vault-expiry";
10
12
  import { VAULT_BACKUP_WRITE_OPS } from "./vault-backup";
@@ -117,6 +119,7 @@ import { RESTART_EPOCH_WRITE_OPS } from "./restart-epoch-write-ops.ts";
117
119
  import { TOKEN_USAGE_WRITE_OPS } from "./token-usage-write-ops.ts";
118
120
  // #1870 S3 — the 1 s raw writers: maintenance leases/runs, pending issue closes, workspace lifecycle.
119
121
  import { MAINTENANCE_JOB_WRITE_OPS } from "./maintenance-job-write-ops.ts";
122
+ import { ISSUE_REFS_WRITE_OPS } from "./issue-refs-write-ops.ts";
120
123
  import { LONG_RUNNING_WORK_WRITE_OPS } from "./long-running-work-write-ops.ts";
121
124
  import { WORKSPACE_WRITE_OPS } from "./workspace-write-ops.ts";
122
125
 
@@ -260,6 +263,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
260
263
  // Delete all probe rows — a test reset. Deleting again reaches the same end state, so re-applying is
261
264
  // safe: idempotent, no key required.
262
265
  "seam.reset": {
266
+ offLoop: true,
263
267
  idempotent: true,
264
268
  fn: () => {
265
269
  const before = (getDb().query("SELECT COUNT(*) AS n FROM _writer_seam_probe").get() as { n: number }).n;
@@ -286,6 +290,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
286
290
  // must NOT enter the retry ledger — the retry re-evaluates fresh and can succeed once the blocker
287
291
  // clears — while an applied outcome dedups exactly-once as usual.
288
292
  "seam.claimLike": {
293
+ offLoop: true,
289
294
  idempotent: false,
290
295
  fn: (args: { tag: string; refuse?: boolean }, ctx) => {
291
296
  if (args.refuse) return { applied: false };
@@ -301,6 +306,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
301
306
  // single transaction (or savepoint), the first insert MUST roll back — the atomic-multi-statement
302
307
  // proof across the boundary (§3.4). Inserts rows — NON-idempotent, key required.
303
308
  "seam.txPair": {
309
+ offLoop: true,
304
310
  idempotent: false,
305
311
  fn: (args: { a: string; b: string; failSecond?: boolean }, ctx) => {
306
312
  const first = getDb()
@@ -318,6 +324,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
318
324
  // the main thread as a real `ValidationError` (instanceof true), not a bare `Error`. No state change,
319
325
  // so re-applying is safe: idempotent, no key required.
320
326
  "seam.throwValidation": {
327
+ offLoop: true,
321
328
  idempotent: true,
322
329
  fn: (args: { message?: string }) => {
323
330
  throw new ValidationError(args.message ?? "seam.throwValidation");
@@ -327,6 +334,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
327
334
  // Insert-once keyed by `idem_key` in its OWN args — dedups by construction (`ON CONFLICT DO
328
335
  // NOTHING`), so it is idempotent and needs no gateway key.
329
336
  "seam.idempotent": {
337
+ offLoop: true,
330
338
  idempotent: true,
331
339
  fn: (args: { key: string; value?: string }, ctx) => {
332
340
  const inserted = getDb()
@@ -347,6 +355,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
347
355
  // marshaling seam (H1) clones args before the op and clones the result after, so the caller's object
348
356
  // is NEVER mutated and the caller sees only the cloned result — as across a real worker boundary.
349
357
  "seam.mutateArg": {
358
+ offLoop: true,
350
359
  idempotent: true,
351
360
  fn: (args: { n: number }) => {
352
361
  args.n += 1000;
@@ -358,6 +367,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
358
367
  // integration suite to force a writer-job deadline (H4 wedged-writer): Atomics.wait on a private
359
368
  // buffer is a true synchronous sleep the gateway deadline fires behind. No write, so idempotent.
360
369
  "seam.sleep": {
370
+ offLoop: true,
361
371
  idempotent: true,
362
372
  fn: (args: { ms: number }) => {
363
373
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, Math.max(0, args.ms));
@@ -368,6 +378,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
368
378
  // A writer-side read of committed state, for tests to verify persistence without a reader gateway
369
379
  // (the writer reading its own connection trivially sees its own writes — §3.5). Read-only: idempotent.
370
380
  "seam.snapshot": {
381
+ offLoop: true,
371
382
  idempotent: true,
372
383
  fn: () => {
373
384
  const rows = getDb().query("SELECT id, tag, value, idem_key FROM _writer_seam_probe ORDER BY id").all();
@@ -398,6 +409,7 @@ export function allWriterOps(): Readonly<Record<string, WriterOpSpec>> {
398
409
  ...ROUTING_POLICY_WRITE_OPS,
399
410
  ...USER_SETTINGS_WRITE_OPS,
400
411
  ...PROMPT_VERSION_WRITE_OPS,
412
+ ...PROJECT_PROFILE_INSTRUCTION_WRITE_OPS,
401
413
  ...TOKEN_DB_WRITE_OPS,
402
414
  ...DELIVERY_WRITE_OPS,
403
415
  ...TIMER_WRITE_OPS,
@@ -432,6 +444,7 @@ export function allWriterOps(): Readonly<Record<string, WriterOpSpec>> {
432
444
  ...SPAWN_TREE_CLEANUP_WRITE_OPS,
433
445
  ...PROJECTS_WRITE_OPS,
434
446
  ...CHANNELS_WRITE_OPS,
447
+ ...INTEGRATIONS_WRITE_OPS,
435
448
  ...CI_INCIDENTS_WRITE_OPS,
436
449
  ...TEAMS_WRITE_OPS,
437
450
  ...TEAM_SPAWN_DELEGATION_WRITE_OPS,
@@ -450,7 +463,7 @@ export function allWriterOps(): Readonly<Record<string, WriterOpSpec>> {
450
463
  ...VAULT_WRITE_OPS,
451
464
  ...VAULT_EXPIRY_WRITE_OPS,
452
465
  ...VAULT_BACKUP_WRITE_OPS,
453
- "secretsVault.import": { idempotent: false, fn: importVaultRows },
466
+ "secretsVault.import": { offLoop: true, idempotent: false, fn: importVaultRows },
454
467
  ...MACOS_PRIVACY_WRITE_OPS,
455
468
  ...PROVIDER_FLAP_MESSAGES_WRITE_OPS,
456
469
  ...PROVIDER_CATALOG_WRITE_OPS,
@@ -460,6 +473,7 @@ export function allWriterOps(): Readonly<Record<string, WriterOpSpec>> {
460
473
  ...TOKEN_USAGE_WRITE_OPS,
461
474
  ...MAINTENANCE_JOB_WRITE_OPS,
462
475
  ...LONG_RUNNING_WORK_WRITE_OPS,
476
+ ...ISSUE_REFS_WRITE_OPS,
463
477
  ...WORKSPACE_WRITE_OPS,
464
478
  ...WEB_TOOL_CALL_WRITE_OPS,
465
479
  ...CLAUDE_API_BUDGET_WRITE_OPS,
@@ -1,4 +1,3 @@
1
- import { mainThreadRegistry } from "./main-thread-state";
2
1
  // The writer-side fault-injection seam for relocated db ops (ADR 0001 §3.1 H6, S3 leaf L-C #1899).
3
2
  //
4
3
  // WHY THIS EXISTS. Several timer ops carry test-only crash hooks that must fire INSIDE the op's
@@ -37,7 +36,11 @@ interface ArmedWriterFault {
37
36
  readonly threadId: number;
38
37
  }
39
38
 
40
- const armed = mainThreadRegistry("writer fault callbacks", new Map<string, ArmedWriterFault>());
39
+ // Deliberately NOT a mainThreadRegistry (#1870 S6a): off-loop op bodies call `writerFault`, and on the
40
+ // writer thread this map is empty, so the lookup is a harmless no-op there. Delivery is kept honest at
41
+ // dispatch: while a fault is armed the in-process backend runs off-loop ops on the arming thread
42
+ // (writer-gateway `dispatchEntry`), and arming refuses under the worker backend.
43
+ const armed = new Map<string, ArmedWriterFault>();
41
44
 
42
45
  /**
43
46
  * Whether a main-thread fault closure can still reach an op body. False once the gateway stands up
@@ -43,6 +43,7 @@ import { WriterDrain } from "./writer-drain";
43
43
  import { errMessage } from "@humwick/sdk";
44
44
  import { createWorkerPool, type WorkerPool } from "@humwick/offload";
45
45
  import { getDbGeneration, getDbPath } from "./connection.ts";
46
+ import { armedWriterFaultCount } from "./writer-fault-seam.ts";
46
47
  import { onLoopWriteTransaction } from "./on-loop-write-transaction.ts";
47
48
  // On-loop-block attribution (#1882 / ADR §5). `resolveLoopCallsite` pins the effective callsite onto a
48
49
  // submission in the CALLER's context; `captureSyncFrame` times the op body where it actually runs — in
@@ -173,8 +174,9 @@ export interface WriterStats {
173
174
  * SQLite lock (H4 §3.6.1). A wedged writer is NOT idle — this is deliberately distinct from
174
175
  * `busy:0`, which is how the naive pool-replace path mis-reported the stall. New non-critical
175
176
  * writes shed and critical writes are held until the wedged op completes and the FIFO resumes.
177
+ * #3776 — names the op holding the writer and how long it has run, so health says WHICH job; null when not wedged.
176
178
  */
177
- wedged: boolean;
179
+ wedged: { op: string; runningMs: number } | null;
178
180
  /**
179
181
  * #2276 — how many times the IN-PROCESS drain has yielded the event loop mid-convoy because its
180
182
  * time slice was spent. 0 on the worker backend (nothing to slice). A steadily climbing count is
@@ -258,7 +260,7 @@ let drainYields = 0;
258
260
  // lock (§3.6.1). Cleared when that in-flight job finally settles (the op completed and released the
259
261
  // lock), which re-kicks the FIFO drain. Worker backend only — an in-process op runs synchronously on
260
262
  // the main thread, so it cannot be observed mid-stall.
261
- let wedged = false;
263
+ let wedgedOp: { op: string; startedAt: number } | undefined;
262
264
 
263
265
  // Test-only fault injection for the precise H5 window: the op has COMMITTED, but its
264
266
  // acknowledgement is lost before the caller observes the result. One-shot by design so a retry
@@ -354,14 +356,14 @@ function createFileWriter(dbPath: string): WorkerBackend {
354
356
 
355
357
  /** Server shutdown keeps already-admitted writes; test/reset teardown remains immediate. */
356
358
  export async function drainWriterOnShutdown(): Promise<void> {
357
- try { await shutdownDrain.start(() => !draining && !wedged && pendingWriteDepth() === 0 && inFlightWrites() === 0); }
359
+ try { await shutdownDrain.start(() => !draining && !wedgedOp && pendingWriteDepth() === 0 && inFlightWrites() === 0); }
358
360
  finally { await shutdownWriter(); }
359
361
  }
360
362
 
361
363
  export async function shutdownWriter(): Promise<void> {
362
364
  const current = backend;
363
365
  backend = undefined;
364
- wedged = false;
366
+ wedgedOp = undefined;
365
367
  // Reject anything still queued at the gateway so no caller hangs across a teardown.
366
368
  const stranded = submissionQueue.splice(0, submissionQueue.length);
367
369
  for (const entry of stranded) {
@@ -378,7 +380,8 @@ export function isWriterInitialized(): boolean {
378
380
  }
379
381
 
380
382
  export function writerStats(): WriterStats {
381
- if (!backend) return { backend: "uninitialized", queued: 0, busy: 0, highWater: queueHighWater, wedged: false, drainYields };
383
+ if (!backend) return { backend: "uninitialized", queued: 0, busy: 0, highWater: queueHighWater, wedged: null, drainYields };
384
+ const wedged = wedgedOp ? { op: wedgedOp.op, runningMs: Math.round(performance.now() - wedgedOp.startedAt) } : null;
382
385
  if (backend.kind === "in-process") {
383
386
  return { backend: "in-process", queued: pendingWriteDepth(), busy: inFlightWrites(), highWater: queueHighWater, wedged, drainYields };
384
387
  }
@@ -479,7 +482,7 @@ function admitOrThrow(op: string, opts: WriteOpts | undefined, count: number): v
479
482
  const occupancy = pendingWriteDepth() + inFlightWrites();
480
483
  if (occupancy + count > queueHardCap) throw new WriterOverloadedError(op, occupancy);
481
484
  if (opts?.critical) return;
482
- const workerWedged = wedged;
485
+ const workerWedged = wedgedOp !== undefined;
483
486
  if (workerWedged || occupancy + count > queueHighWater) throw new WriterOverloadedError(op, occupancy);
484
487
  }
485
488
 
@@ -510,7 +513,7 @@ async function drainLoop(): Promise<void> {
510
513
  // second concurrent drain and anything submitted meanwhile is picked up by this same loop when
511
514
  // it resumes — FIFO is preserved, the queue is only PAUSED.
512
515
  let sliceStartedAt = performance.now();
513
- while (submissionQueue.length > 0 && !wedged) {
516
+ while (submissionQueue.length > 0 && !wedgedOp) {
514
517
  const entry = submissionQueue.shift()!;
515
518
  const onLoop = backend?.kind === "in-process";
516
519
  await dispatchEntry(entry);
@@ -520,7 +523,7 @@ async function drainLoop(): Promise<void> {
520
523
  sliceStartedAt = performance.now();
521
524
  continue;
522
525
  }
523
- if (submissionQueue.length === 0 || wedged) continue;
526
+ if (submissionQueue.length === 0 || wedgedOp) continue;
524
527
  if (performance.now() - sliceStartedAt < drainSliceMs) continue;
525
528
  drainYields += 1;
526
529
  await yieldEventLoop();
@@ -528,7 +531,7 @@ async function drainLoop(): Promise<void> {
528
531
  }
529
532
  } finally {
530
533
  draining = false;
531
- shutdownDrain.notify(!wedged && pendingWriteDepth() === 0 && inFlightWrites() === 0);
534
+ shutdownDrain.notify(!wedgedOp && pendingWriteDepth() === 0 && inFlightWrites() === 0);
532
535
  }
533
536
  }
534
537
 
@@ -551,7 +554,9 @@ async function dispatchEntry(entry: Submission): Promise<void> {
551
554
  back.busy = true;
552
555
  try {
553
556
  const path = getDbPath();
554
- if (path && path !== ":memory:" && isOffLoopWriterOp(entry.op)) {
557
+ // An armed crash seam is a closure on THIS thread, so while one is armed the op runs here instead
558
+ // of on the file-DB worker (tests only; production never arms). Otherwise the fault would never fire.
559
+ if (path && path !== ":memory:" && isOffLoopWriterOp(entry.op) && armedWriterFaultCount() === 0) {
555
560
  if (back.offLoop && back.offLoopGeneration !== getDbGeneration()) {
556
561
  await back.offLoop.pool.destroy();
557
562
  back.offLoop = undefined;
@@ -647,6 +652,7 @@ async function dispatchWorker(entry: Submission, back: WorkerBackend): Promise<v
647
652
 
648
653
  // retrySafe stays false (default): a write must NEVER be auto-re-dispatched (it would repeat its
649
654
  // effect). No pool timeout is armed — the deadline is the gateway's own (§3.6.1).
655
+ const startedAt = performance.now();
650
656
  const poolPromise = back.pool.run<WriterJobInput, WriterJobResult>(WRITER_TASK_NAME, input, { labels });
651
657
 
652
658
  let timer: ReturnType<typeof setTimeout> | undefined;
@@ -662,13 +668,16 @@ async function dispatchWorker(entry: Submission, back: WorkerBackend): Promise<v
662
668
  // WEDGED (§3.6.1). The op is still running and still holds the lock; do NOT replace the worker.
663
669
  // Surface the caller's INDETERMINATE outcome, hold the FIFO, and resume when the op finally
664
670
  // settles (it commits/rolls back and releases the lock on the SAME warm connection).
665
- wedged = true;
671
+ // #3776 — name the op: "writer wedged" alone left an incident to guess which job held the lock.
672
+ wedgedOp = { op: entry.op, startedAt };
673
+ console.warn(`[writer-gateway] writer op ${entry.op} has held the writer past its ${writerTimeoutMs}ms deadline; writes are held until it settles`);
666
674
  rejectIndeterminate(entry);
667
675
  poolPromise.then(
668
676
  () => {},
669
677
  () => {},
670
678
  ).finally(() => {
671
- wedged = false;
679
+ console.warn(`[writer-gateway] wedged writer op ${entry.op} settled after ${Math.round(performance.now() - startedAt)}ms`);
680
+ wedgedOp = undefined;
672
681
  kickDrain();
673
682
  }).catch((error) => console.error(`[writer-gateway] resume after wedge failed: ${errMessage(error)}`));
674
683
  return;
@@ -1,5 +1,5 @@
1
- import type { HostView, Orchestrator } from "@humwick/sdk";
2
- import type { ProviderCatalogEntry } from "@humwick/sdk/provider-catalog";
1
+ import { errMessage, type HostView, type Orchestrator } from "@humwick/sdk";
2
+ import { resolveProviderSelection, type ProviderCatalogEntry } from "@humwick/sdk/provider-catalog";
3
3
  import type { DispatchTarget } from "./task-dispatcher";
4
4
  import { spawnPlacementRefusal, hostSpawnAdmission, hostIdentitiesById, spawnHostCapAdmission, type SpawnCapOptions } from "./spawn-placement";
5
5
  import { liveHostDisabledProviders } from "./host-provider-store";
@@ -17,13 +17,15 @@ export interface LiveTargetAdmission {
17
17
  targetUnavailableReason: (target: DispatchTarget) => string | undefined;
18
18
  }
19
19
 
20
- export function liveTargetAdmission(orchestrators: readonly Orchestrator[], views: readonly HostView[], orchestratorId?: string, capOpts: SpawnCapOptions = {}): LiveTargetAdmission {
20
+ export function liveTargetAdmission(orchestrators: readonly Orchestrator[], views: readonly HostView[], catalog: ProviderCatalogEntry[], orchestratorId?: string, capOpts: SpawnCapOptions = {}): LiveTargetAdmission {
21
21
  const identities = hostIdentitiesById(views);
22
22
  const byId = new Map(views.map((view) => [view.id, view]));
23
23
  const disabled = liveHostDisabledProviders();
24
24
  const capBlock = spawnHostCapAdmission(capOpts);
25
25
  const hosts = orchestrators.filter((host) => !orchestratorId || host.id === orchestratorId);
26
26
  const targetAdmission = ({ provider, model, effort }: DispatchTarget): TargetAdmission => {
27
+ try { resolveProviderSelection({ provider, model, effort }, catalog); }
28
+ catch { return { eligible: false, uncertainties: [] }; }
27
29
  let eligible = false;
28
30
  const uncertainties: TargetUncertainty[] = [];
29
31
  for (const host of hosts) {
@@ -34,9 +36,11 @@ export function liveTargetAdmission(orchestrators: readonly Orchestrator[], view
34
36
  }
35
37
  return { eligible, uncertainties };
36
38
  };
37
- const targetUnavailableReason = (target: DispatchTarget) => spawnPlacementRefusal(
38
- orchestrators, views, target.provider, target.model, target.effort, orchestratorId, capOpts,
39
- );
39
+ const targetUnavailableReason = (target: DispatchTarget) => {
40
+ try { resolveProviderSelection(target, catalog); }
41
+ catch (error) { return errMessage(error); }
42
+ return spawnPlacementRefusal(orchestrators, views, target.provider, target.model, target.effort, orchestratorId, capOpts);
43
+ };
40
44
  return { targetAdmission, targetUnavailableReason };
41
45
  }
42
46
 
@@ -1,7 +1,7 @@
1
1
  /** Precondition setup for the packaged-ephemeral landing-ingress Proof of Life.
2
2
  * Delivery and both crash boundaries run through the packaged Relay; this fixture only registers
3
3
  * the disposable workspace that its real ephemeral orchestrator will probe. */
4
- import { initDb } from "../db/schema.ts";
4
+ import { closeDb, openExistingDb } from "../db/connection.ts";
5
5
  import { upsertWorkspace } from "../db/workspaces.ts";
6
6
 
7
7
  const dbPath = process.env.DB_PATH;
@@ -13,7 +13,8 @@ if (!dbPath || !worktreePath || !orchestratorId || !baseSha) {
13
13
  throw new Error("usage: landing-ingress-proof-worker <worktree-path> <orchestrator-id> <base-sha>");
14
14
  }
15
15
 
16
- initDb(dbPath);
16
+ // The installed Relay already owns the schema; a fixture must not migrate its live DB (#3715).
17
+ openExistingDb(dbPath);
17
18
  upsertWorkspace({
18
19
  id: workspaceId,
19
20
  repoRoot: worktreePath,
@@ -27,4 +28,5 @@ upsertWorkspace({
27
28
  orchestratorId,
28
29
  metadata: {},
29
30
  });
31
+ closeDb();
30
32
  console.log(JSON.stringify({ workspaceId }));