@humwick/server 0.130.2 → 0.130.4

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 (587) hide show
  1. package/bundled/prompts/manifest.json +1 -0
  2. package/bundled/prompts/task.hydration.md +1 -1
  3. package/bundled/roles/builder.md +1 -1
  4. package/docs/guides/sub-coordinators.md +12 -5
  5. package/docs/guides/tools-coordination.md +1 -0
  6. package/docs/guides/workflows.md +16 -0
  7. package/docs/openapi.json +175 -4
  8. package/package.json +8 -7
  9. package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-BOkmEvgZ.js} +2 -2
  10. package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-BkaDJp0r.js} +2 -2
  11. package/public/assets/{TraceView-Wimm0Czv.js → TraceView-B99Uu2yT.js} +2 -2
  12. package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DUIfTTFF.js} +2 -2
  13. package/public/assets/{activity-CrSurn-r.js → activity-ZIX0N1El.js} +2 -2
  14. package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-kq9TPYFw.js} +2 -2
  15. package/public/assets/agent-liveness-DSQY1Aa_.js +2 -0
  16. package/public/assets/{agent-name-DKbiIhBo.js → agent-name-C_7F4DFR.js} +2 -2
  17. package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-BNOcrGcY.js} +2 -2
  18. package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-B6yAh70a.js} +2 -2
  19. package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-CcrhmOzF.js} +2 -2
  20. package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CXSLrKiq.js} +2 -2
  21. package/public/assets/{agents-B-v66C-4.js → agents-DIZG97_X.js} +2 -2
  22. package/public/assets/{analytics-BqCb-LKi.js → analytics-rasmoPjR.js} +2 -2
  23. package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-tBpTrEPb.js} +2 -2
  24. package/public/assets/{artifacts-DJevXcvg.js → artifacts-BNkrynP1.js} +2 -2
  25. package/public/assets/{artifacts-D32zyw4Y.js → artifacts-Bjq-ZyBA.js} +2 -2
  26. package/public/assets/{automation-BpRajx_5.js → automation-Cx422L3L.js} +2 -2
  27. package/public/assets/{backlog-D_UjTdzX.js → backlog-RuFQ8PUu.js} +2 -2
  28. package/public/assets/{busy-health-N3VEh4ao.js → busy-health-BvNQ9g5x.js} +2 -2
  29. package/public/assets/{channels-9jopungq.js → channels-DNbKAS0I.js} +2 -2
  30. package/public/assets/{chat-CSXrTB3C.js → chat-Bp3Fx73h.js} +3 -3
  31. package/public/assets/{code-preview-p1NnmqK0.js → code-preview-KIRZA6cz.js} +2 -2
  32. package/public/assets/{connectors-CZdqlDNL.js → connectors-k7vO4vvc.js} +2 -2
  33. package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BuVoq1aw.js} +2 -2
  34. package/public/assets/{crate-agent-30aXTKva.js → crate-agent-irqlt4NH.js} +2 -2
  35. package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-D_rurNBz.js} +2 -2
  36. package/public/assets/{deploys-DoN6bAb5.js → deploys-yzZqIpxE.js} +2 -2
  37. package/public/assets/{display-BrKWFGJz.js → display-BemJG1f_.js} +2 -2
  38. package/public/assets/{files-D6XEu8Ng.js → files-DXWcrxOz.js} +2 -2
  39. package/public/assets/{fleet-C7jQWbw2.js → fleet-TEuOCZXE.js} +2 -2
  40. package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BGoDAFI-.js} +2 -2
  41. package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-BB9eq9nN.js} +2 -2
  42. package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-qy2nTD_1.js} +3 -3
  43. package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-BGPHxuwT.js} +2 -2
  44. package/public/assets/{index-CRHep3M9.js → index-BC5XZc0z.js} +5 -5
  45. package/public/assets/{index-MYAIwYTX.css → index-D8Rp9pz2.css} +1 -1
  46. package/public/assets/{insights-CzrtkpOE.js → insights-C-KQG0h-.js} +2 -2
  47. package/public/assets/{integrations-aVsUmigt.js → integrations-ClPRPyKU.js} +2 -2
  48. package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-DbH8J9Ak.js} +2 -2
  49. package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-PH4zVn24.js} +2 -2
  50. package/public/assets/{maintenance-NfjqFZAk.js → maintenance-5lV43uTL.js} +2 -2
  51. package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-BdgU35aX.js} +2 -2
  52. package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-DNT4OUwQ.js} +3 -3
  53. package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CnCcPy0A.js} +2 -2
  54. package/public/assets/{memory-DNsCl6UX.js → memory--GgWH1gH.js} +2 -2
  55. package/public/assets/{messages-DopU1L1X.js → messages-CzFl_97O.js} +2 -2
  56. package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-CVvJVkzv.js} +2 -2
  57. package/public/assets/overview-DsrZvE86.js +2 -0
  58. package/public/assets/{pairs-DE_m6rrE.js → pairs-D2d0d2py.js} +2 -2
  59. package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-_SMuSbYF.js} +2 -2
  60. package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-Apn15y43.js} +2 -2
  61. package/public/assets/{projects-Br2Bd2h1.js → projects-D3B6kto5.js} +2 -2
  62. package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-DnPB6Ez6.js} +2 -2
  63. package/public/assets/provider-display-bWwiuedV.js +2 -0
  64. package/public/assets/{registry-Cw38_6gW.js → registry-D790cIhN.js} +2 -2
  65. package/public/assets/run-card-BCro-Odj.js +2 -0
  66. package/public/assets/{secrets-DxI-vzSq.js → secrets-CVXvfvbU.js} +2 -2
  67. package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-D3Xxqb9w.js} +2 -2
  68. package/public/assets/{settings-CKZTVGIx.js → settings-CoUC7Jci.js} +2 -2
  69. package/public/assets/shipping-D_Lp9n_J.js +2 -0
  70. package/public/assets/{stats-DyUBKyWI.js → stats-CXiNjB9S.js} +2 -2
  71. package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-C703YIy1.js} +2 -2
  72. package/public/assets/store-BgDALGIX.js +9 -0
  73. package/public/assets/{tasks-Bh7Chr4-.js → tasks-DO0WpJCx.js} +2 -2
  74. package/public/assets/{teams-bowI7qVY.js → teams-FGkdi_tL.js} +2 -2
  75. package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-Dh8dSC7e.js} +3 -3
  76. package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-B5Zriip3.js} +2 -2
  77. package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-CRUVDvLC.js} +2 -2
  78. package/public/assets/{use-decisions-samMQhQf.js → use-decisions-CoaqRh4l.js} +2 -2
  79. package/public/assets/{use-project-list-59LheVP7.js → use-project-list-X-BgT53X.js} +2 -2
  80. package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-0X_UZkk-.js} +2 -2
  81. package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-CkLhVBGu.js} +2 -2
  82. package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-BkTi7eim.js} +2 -2
  83. package/public/assets/{work-queue-CSvpuNLz.js → work-queue-DDOLEyKo.js} +2 -2
  84. package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-CzOsB8BM.js} +2 -2
  85. package/public/assets/{workflows-DdmxRSyg.js → workflows-DJilvmRM.js} +2 -2
  86. package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CHcfbLR3.js} +2 -2
  87. package/public/cosmos.html +4 -4
  88. package/public/index.html +29 -29
  89. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  90. package/runner/src/adapters/claude-api.ts +1 -1
  91. package/runner/src/adapters/claude-headless.ts +1 -1
  92. package/runner/src/adapters/claude-launch-exit.ts +26 -0
  93. package/runner/src/adapters/claude-session-probe.ts +1 -1
  94. package/runner/src/adapters/claude-tmux.ts +38 -10
  95. package/runner/src/adapters/claude.ts +28 -75
  96. package/runner/src/adapters/provider-quota-harvest.ts +20 -71
  97. package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
  98. package/runner/src/agent-runner/liveness-exchange.ts +104 -0
  99. package/runner/src/claude-system-prompt.ts +49 -0
  100. package/runner/src/launch-assembly.ts +14 -10
  101. package/runner/src/profile-home.ts +1 -1
  102. package/runner/src/project-instructions.ts +2 -2
  103. package/runner/src/provider-command.ts +18 -0
  104. package/runner/src/provider-setup.ts +3 -2
  105. package/runner/src/runner-core.ts +14 -15
  106. package/runner/src/runner-helpers.ts +0 -8
  107. package/runner/src/session-scratch.ts +6 -2
  108. package/runner/test-support/provisioning-context.ts +4 -2
  109. package/runner/test-support/runner-harness.ts +2 -1
  110. package/scripts/steward-policy-cleanup.ts +2 -2
  111. package/src/agent-authz.ts +2 -2
  112. package/src/agent-drive-arbiter.ts +3 -8
  113. package/src/agent-exit-cleanup-async.ts +2 -2
  114. package/src/agent-exit-cleanup.ts +23 -19
  115. package/src/agent-lifecycle-events.ts +112 -85
  116. package/src/agent-ref.ts +3 -2
  117. package/src/agent-status-events.ts +43 -14
  118. package/src/auth/login-link.ts +35 -0
  119. package/src/auth/password.ts +4 -3
  120. package/src/auth/session-refresh.ts +1 -1
  121. package/src/auth/session.test-fixture.ts +2 -2
  122. package/src/auth/session.ts +7 -7
  123. package/src/automations/crud.ts +8 -81
  124. package/src/automations/dispatch.ts +33 -58
  125. package/src/automations/index.ts +1 -1
  126. package/src/automations/persistence.ts +65 -0
  127. package/src/automations/precheck.ts +18 -16
  128. package/src/automations/read-async.ts +11 -0
  129. package/src/automations/reconcile.ts +30 -39
  130. package/src/automations/storage.ts +33 -10
  131. package/src/automations/write-async.ts +33 -0
  132. package/src/branch-land-notice.ts +3 -3
  133. package/src/branch-landed.ts +25 -23
  134. package/src/bus.ts +21 -19
  135. package/src/cd-deploy-notify.ts +2 -2
  136. package/src/ci/notifications.ts +4 -4
  137. package/src/ci/root-missing.ts +3 -3
  138. package/src/ci/transition-notify.ts +5 -5
  139. package/src/claude-app/oauth-http.ts +3 -3
  140. package/src/cli/index.ts +7 -1
  141. package/src/cli/init.ts +53 -0
  142. package/src/cli/orchestrator.ts +1 -7
  143. package/src/cli/setup.ts +6 -4
  144. package/src/cli/workflow.ts +12 -3
  145. package/src/commands-db.ts +4 -4
  146. package/src/commands-write.ts +8 -0
  147. package/src/compaction-watch.ts +10 -8
  148. package/src/config-store-write.ts +2 -2
  149. package/src/config.ts +4 -11
  150. package/src/connectors.ts +10 -8
  151. package/src/context-advisory.ts +22 -21
  152. package/src/db/agent-action-write.ts +36 -0
  153. package/src/db/agent-control-async.ts +9 -0
  154. package/src/db/agent-control-write.ts +49 -0
  155. package/src/db/agent-effort-observe.ts +22 -0
  156. package/src/db/agent-meta.ts +14 -3
  157. package/src/db/agents-async.ts +8 -5
  158. package/src/db/agents.ts +15 -12
  159. package/src/db/auth-identities.ts +2 -2
  160. package/src/db/automation-read-ops.ts +11 -0
  161. package/src/db/automation-write-ops.ts +46 -0
  162. package/src/db/blackboard.ts +2 -2
  163. package/src/db/browser-session-write.ts +32 -0
  164. package/src/db/bus-outbox-write-ops.ts +3 -1
  165. package/src/db/checkout-refresh-schema.ts +47 -0
  166. package/src/db/checkout-refresh.ts +5 -5
  167. package/src/db/command-agent-profile-backfill.ts +39 -0
  168. package/src/db/command-agent-profiles.ts +5 -1
  169. package/src/db/command-retention.ts +126 -0
  170. package/src/db/commands-planner-stats.test-fixture.ts +8 -0
  171. package/src/db/commands-read-ops.ts +16 -0
  172. package/src/db/connection.ts +1 -182
  173. package/src/db/context-carry-write.ts +74 -0
  174. package/src/db/continuations.ts +4 -4
  175. package/src/db/db-error-registry.ts +12 -0
  176. package/src/db/gateway.ts +26 -133
  177. package/src/db/history-retention-ops.ts +13 -0
  178. package/src/db/inbox-async.ts +2 -2
  179. package/src/db/inbox-read-ops.ts +1 -1
  180. package/src/db/inbox.ts +9 -3
  181. package/src/db/issue-associations.ts +2 -2
  182. package/src/db/issue-refs-async.ts +4 -1
  183. package/src/db/issue-refs-read-ops.ts +6 -0
  184. package/src/db/issue-refs.ts +8 -8
  185. package/src/db/kernel-event-log-schema.ts +2 -0
  186. package/src/db/land-boundaries.ts +51 -5
  187. package/src/db/land-gate-attestations.ts +2 -2
  188. package/src/db/landing-receipts.ts +2 -2
  189. package/src/db/lifecycle-context-write.ts +5 -0
  190. package/src/db/lifecycle-tail-async.ts +76 -0
  191. package/src/db/lifecycle-tail-write-ops.ts +62 -0
  192. package/src/db/lifecycle-write-window.ts +2 -1
  193. package/src/db/lineage-pair-dedupe-sql.ts +3 -3
  194. package/src/db/main-connection-mode.ts +11 -0
  195. package/src/db/main-connection-writes.ts +20 -2
  196. package/src/db/maintenance-job-runs-async.ts +2 -2
  197. package/src/db/maintenance-job-write-ops.ts +1 -1
  198. package/src/db/maintenance-pauses.ts +2 -2
  199. package/src/db/message-send.ts +4 -0
  200. package/src/db/messages.ts +31 -25
  201. package/src/db/migrations.ts +18 -1
  202. package/src/db/notification-write-ops.ts +19 -0
  203. package/src/db/operator-answer-write.ts +19 -0
  204. package/src/db/orchestrators.ts +10 -4
  205. package/src/db/pairs-async.ts +22 -1
  206. package/src/db/pairs-write-ops.ts +8 -3
  207. package/src/db/pairs.ts +41 -48
  208. package/src/db/plan-dispatch-schema.ts +5 -1
  209. package/src/db/plan-ready-notices.ts +43 -0
  210. package/src/db/plan-tools-include-migration.ts +36 -0
  211. package/src/db/plans-write-ops.ts +2 -0
  212. package/src/db/presence-buffer.ts +5 -0
  213. package/src/db/presence-loop.ts +18 -23
  214. package/src/db/presence-write.ts +0 -6
  215. package/src/db/project-entries-async.ts +14 -0
  216. package/src/db/project-entries-read-ops.ts +5 -1
  217. package/src/db/project-entries-write-ops.ts +8 -0
  218. package/src/db/project-entries.ts +15 -12
  219. package/src/db/project-journal.ts +2 -2
  220. package/src/db/project-trust-approvals.ts +2 -2
  221. package/src/db/prompt-templates.ts +4 -4
  222. package/src/db/read-op-marshaling-fixtures.ts +18 -0
  223. package/src/db/read-ops.ts +4 -0
  224. package/src/db/retention.ts +13 -2
  225. package/src/db/review-publish-write.ts +8 -0
  226. package/src/db/runtime-admin-async.ts +19 -0
  227. package/src/db/runtime-admin-write-ops.ts +36 -0
  228. package/src/db/runtime-tail-async.ts +288 -0
  229. package/src/db/runtime-tail-write-ops.ts +97 -0
  230. package/src/db/schema.ts +4 -1
  231. package/src/db/spawn-budget.ts +5 -0
  232. package/src/db/spawn-delegation.ts +3 -12
  233. package/src/db/sql-audit-indexes.ts +3 -0
  234. package/src/db/stats-async.ts +5 -3
  235. package/src/db/task-entity.ts +5 -4
  236. package/src/db/team-claim-policy.ts +60 -0
  237. package/src/db/teams-async.ts +4 -0
  238. package/src/db/teams-write-ops.ts +4 -0
  239. package/src/db/terminal-events.ts +15 -0
  240. package/src/db/timer-cutover-bodies.ts +292 -0
  241. package/src/db/timer-cutover-core.ts +37 -278
  242. package/src/db/timer-cutover-state.ts +40 -0
  243. package/src/db/timer-cutover-write-ops.ts +1 -40
  244. package/src/db/token-db-write-ops.ts +2 -0
  245. package/src/db/token-standing-async.ts +12 -0
  246. package/src/db/token-standing.ts +6 -9
  247. package/src/db/token-usage-retention.ts +33 -0
  248. package/src/db/token-usage-write-ops.ts +1 -1
  249. package/src/db/token-usage.ts +4 -1
  250. package/src/db/user-invites-async.ts +13 -0
  251. package/src/db/user-invites.ts +3 -3
  252. package/src/db/users.ts +14 -6
  253. package/src/db/workflow-execution.ts +3 -3
  254. package/src/db/workspaces.ts +8 -5
  255. package/src/db/write-ops.ts +31 -12
  256. package/src/db/writer-fault-seam.ts +2 -9
  257. package/src/db/writer-fault-test-ops.ts +18 -0
  258. package/src/db/writer-gateway.ts +42 -46
  259. package/src/dev.ts +12 -16
  260. package/src/event-loop-lag.ts +4 -2
  261. package/src/event-loop-yield.ts +2 -2
  262. package/src/events.ts +10 -8
  263. package/src/host-provider-store.ts +5 -4
  264. package/src/hub-metrics-report.ts +5 -1
  265. package/src/hub-metrics.ts +38 -18
  266. package/src/init.ts +299 -0
  267. package/src/injection-sweep-pacing.ts +6 -8
  268. package/src/invite-link.ts +16 -9
  269. package/src/land-gate-notifications.ts +11 -10
  270. package/src/land-gate-queue-progress.ts +3 -3
  271. package/src/land-settle-dispatch.ts +52 -92
  272. package/src/lifecycle-context-pressure.ts +11 -10
  273. package/src/lifecycle-manager.ts +104 -98
  274. package/src/maintenance/breaker.ts +18 -16
  275. package/src/maintenance/checkout-refresh.ts +18 -1
  276. package/src/maintenance/critical-job-watchdog.ts +3 -3
  277. package/src/maintenance/events.ts +3 -3
  278. package/src/maintenance/history-retention-job.ts +30 -0
  279. package/src/maintenance/jobs.ts +29 -67
  280. package/src/maintenance/long-running-work.ts +3 -1
  281. package/src/maintenance/offline-agent-prune-job.ts +28 -0
  282. package/src/maintenance/orphaned-sessions.ts +5 -4
  283. package/src/maintenance/pauses.ts +4 -10
  284. package/src/maintenance/plan-ready-notice-job.ts +71 -0
  285. package/src/maintenance/scheduler.ts +18 -13
  286. package/src/maintenance/stalled-delivery-pager.ts +4 -4
  287. package/src/maintenance/teams.ts +19 -30
  288. package/src/maintenance/workspaces.ts +17 -27
  289. package/src/managed-policy-escalation.ts +7 -7
  290. package/src/managed-policy.ts +3 -3
  291. package/src/managed-provider-gate.ts +5 -4
  292. package/src/managed-spawn-reservation.ts +5 -4
  293. package/src/mcp/index.ts +8 -8
  294. package/src/mcp/output-descriptions.ts +1 -1
  295. package/src/mcp/presentation.ts +2 -1
  296. package/src/mcp/runtime-output.ts +6 -1
  297. package/src/mcp/self-care-visibility.ts +2 -2
  298. package/src/mcp/spawn-targets-matrix.ts +10 -1
  299. package/src/mcp/tool-groups.ts +3 -2
  300. package/src/mcp/tool-roles.ts +81 -0
  301. package/src/mcp/tools-workspace.ts +1 -1
  302. package/src/mcp-blackboard-tools.ts +6 -5
  303. package/src/mcp-decision-tools.ts +7 -7
  304. package/src/mcp-egress-tools.ts +10 -9
  305. package/src/mcp-issue-activity-tools.ts +2 -2
  306. package/src/mcp-issue-association-tools.ts +4 -4
  307. package/src/mcp-issue-ref-tools.ts +3 -3
  308. package/src/mcp-merge-tools.ts +5 -5
  309. package/src/mcp-notification-subscription-tools.ts +14 -15
  310. package/src/mcp-output-levels.test-fixture.ts +1 -1
  311. package/src/mcp-profile-trust-tools.ts +1 -1
  312. package/src/mcp-project-journal-tools.ts +4 -12
  313. package/src/mcp-project-tools.ts +17 -18
  314. package/src/mcp-routing-policy-tools.ts +3 -2
  315. package/src/mcp-team-tools.ts +6 -6
  316. package/src/mcp-workflow-tools.ts +5 -3
  317. package/src/memory-area-injection.ts +13 -12
  318. package/src/memory-boundary-capture.ts +6 -6
  319. package/src/memory-injection.ts +45 -43
  320. package/src/merge-pause-maintenance.ts +5 -4
  321. package/src/notification-router.ts +44 -17
  322. package/src/notify.ts +5 -5
  323. package/src/observed-reset-refresh.ts +4 -4
  324. package/src/operator-alarm.ts +2 -5
  325. package/src/project-ingest.ts +6 -6
  326. package/src/project-instructions.ts +8 -1
  327. package/src/project-trust.ts +12 -11
  328. package/src/prompt-resolver.ts +3 -3
  329. package/src/provider-quota-async.ts +5 -7
  330. package/src/provider-timeline-event.ts +4 -7
  331. package/src/quota-band-delivery.ts +2 -2
  332. package/src/recipe-runner.ts +4 -3
  333. package/src/retention-config-store.ts +4 -3
  334. package/src/reviewer-pipeline.ts +11 -10
  335. package/src/routes/_route.ts +4 -4
  336. package/src/routes/_shared.ts +13 -12
  337. package/src/routes/agent-effort.ts +5 -5
  338. package/src/routes/agent-heartbeat-quota.ts +23 -0
  339. package/src/routes/agent-keep.ts +1 -1
  340. package/src/routes/agent-reporting.ts +1 -1
  341. package/src/routes/agent-self-effort.ts +5 -5
  342. package/src/routes/agent-sessions.ts +24 -24
  343. package/src/routes/agents-spawn.ts +1 -1
  344. package/src/routes/agents.ts +17 -119
  345. package/src/routes/auth.ts +44 -17
  346. package/src/routes/authority-audit.ts +2 -2
  347. package/src/routes/automations.ts +6 -5
  348. package/src/routes/channel-binding-delete.ts +1 -1
  349. package/src/routes/channel-output.ts +3 -3
  350. package/src/routes/claude-api-budget.ts +5 -5
  351. package/src/routes/commands-listing.ts +1 -1
  352. package/src/routes/commands.ts +29 -27
  353. package/src/routes/context-resets.ts +2 -2
  354. package/src/routes/coordinator-teams.ts +1 -1
  355. package/src/routes/decisions.ts +2 -2
  356. package/src/routes/host-providers.ts +18 -0
  357. package/src/routes/host-secret-files.ts +1 -1
  358. package/src/routes/hosts.ts +11 -11
  359. package/src/routes/inbox.ts +3 -2
  360. package/src/routes/index.ts +7 -3
  361. package/src/routes/integrations.ts +9 -7
  362. package/src/routes/issue-activity.ts +2 -2
  363. package/src/routes/issue-associations.ts +4 -4
  364. package/src/routes/issue-refs.ts +3 -3
  365. package/src/routes/kernel-timers-operator.ts +1 -1
  366. package/src/routes/memory.ts +1 -1
  367. package/src/routes/merges.ts +11 -11
  368. package/src/routes/messages.ts +23 -17
  369. package/src/routes/orchestrator-bootstrap.ts +3 -1
  370. package/src/routes/orchestrator-host-registration.ts +2 -0
  371. package/src/routes/orchestrator-host-utilization.ts +2 -2
  372. package/src/routes/orchestrator-proxy.ts +2 -2
  373. package/src/routes/orchestrator.ts +27 -18
  374. package/src/routes/pairs.ts +8 -8
  375. package/src/routes/project-profile-instructions.ts +1 -1
  376. package/src/routes/project-trust.ts +1 -1
  377. package/src/routes/projects.ts +16 -16
  378. package/src/routes/prompt-templates.ts +12 -12
  379. package/src/routes/provider-quotas.ts +10 -10
  380. package/src/routes/run-command.ts +1 -1
  381. package/src/routes/secrets-backup.ts +4 -5
  382. package/src/routes/secrets-import.ts +3 -4
  383. package/src/routes/secrets-vault.ts +12 -14
  384. package/src/routes/settings.ts +1 -1
  385. package/src/routes/shipping.ts +9 -1
  386. package/src/routes/spawn-policy.ts +8 -7
  387. package/src/routes/sse.ts +2 -2
  388. package/src/routes/team-templates.ts +7 -7
  389. package/src/routes/teams.ts +10 -10
  390. package/src/routes/tokens.ts +18 -17
  391. package/src/routes/users.ts +24 -24
  392. package/src/routes/viewer-read.ts +3 -3
  393. package/src/routes/web-tool-calls.ts +4 -4
  394. package/src/routes/workflows.ts +2 -2
  395. package/src/routes/workspaces.ts +17 -16
  396. package/src/routing-policy-push.ts +6 -6
  397. package/src/runtime-grant.ts +17 -11
  398. package/src/runtime-install.ts +30 -0
  399. package/src/runtime-tokens.ts +20 -28
  400. package/src/secrets-broker/config.ts +1 -1
  401. package/src/secrets-broker/service.ts +2 -2
  402. package/src/security.ts +3 -1
  403. package/src/self-resume-carry.ts +21 -110
  404. package/src/self-resume-supersede.ts +4 -3
  405. package/src/self-resume.ts +54 -66
  406. package/src/server-background.ts +1 -1
  407. package/src/server.ts +25 -26
  408. package/src/services/agent-action.ts +12 -31
  409. package/src/services/agent-effort.ts +7 -32
  410. package/src/services/agent-hang-watchdog-job.ts +2 -2
  411. package/src/services/agent-heartbeat.ts +166 -0
  412. package/src/services/agent-reporting.ts +1 -1
  413. package/src/services/auth-context.ts +6 -0
  414. package/src/services/browser-inventory.ts +3 -3
  415. package/src/services/browser-session-state.ts +3 -2
  416. package/src/services/browser-session.ts +21 -27
  417. package/src/services/bus-command-update.ts +9 -9
  418. package/src/services/checkout-refresh-state.ts +1 -0
  419. package/src/services/checkout-refresh.test-fixture.ts +3 -2
  420. package/src/services/command-host.ts +7 -2
  421. package/src/services/coordinator-digest.ts +3 -2
  422. package/src/services/dispatch-command.ts +12 -13
  423. package/src/services/effective-launch-driver.ts +14 -9
  424. package/src/services/host-changed-paths.test-fixture.ts +5 -6
  425. package/src/services/host-project-readiness.ts +2 -2
  426. package/src/services/host-providers.ts +1 -1
  427. package/src/services/host-utilization.ts +9 -9
  428. package/src/services/idle-agent-digest.ts +17 -12
  429. package/src/services/idle-epics.ts +4 -4
  430. package/src/services/interrupt-agent.ts +12 -9
  431. package/src/services/issue-activity.ts +3 -3
  432. package/src/services/issue-associations.ts +15 -18
  433. package/src/services/issue-lifecycle-comments.ts +3 -2
  434. package/src/services/issue-lifecycle.ts +14 -13
  435. package/src/services/issue-refs.ts +4 -5
  436. package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
  437. package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
  438. package/src/services/kernel-timers-ingress.ts +3 -3
  439. package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
  440. package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
  441. package/src/services/kernel-timers-outbox.ts +1 -1
  442. package/src/services/kernel-timers-stage10-operator.ts +1 -1
  443. package/src/services/kernel-timers.ts +4 -3
  444. package/src/services/land-gate-placement.ts +11 -2
  445. package/src/services/land-issue-relations.ts +5 -4
  446. package/src/services/land-restart-retry.ts +7 -6
  447. package/src/services/landed-ancestry.ts +6 -5
  448. package/src/services/legacy-landing-host.ts +4 -1
  449. package/src/services/legacy-landing-recovery.ts +8 -6
  450. package/src/services/managed-running.ts +17 -17
  451. package/src/services/memory-areas.ts +3 -3
  452. package/src/services/merge-pause.ts +9 -15
  453. package/src/services/operator-decisions.ts +37 -41
  454. package/src/services/orchestrator-registration-recovery.ts +6 -2
  455. package/src/services/orchestrator-upgrade.ts +14 -12
  456. package/src/services/permission-decision.ts +4 -3
  457. package/src/services/plan-ci-notice.ts +1 -1
  458. package/src/services/plan-conformance.ts +4 -4
  459. package/src/services/plan-dispatch-policy.ts +10 -3
  460. package/src/services/plan-dispatch.ts +35 -14
  461. package/src/services/plan-graph.ts +39 -261
  462. package/src/services/plan-import.ts +1 -1
  463. package/src/services/plan-live-bindings.ts +8 -37
  464. package/src/services/plan-task-create.ts +1 -1
  465. package/src/services/plan-terminal-refs.ts +2 -12
  466. package/src/services/plan-wake-digest.ts +8 -3
  467. package/src/services/profile-trust.ts +2 -2
  468. package/src/services/project.ts +11 -10
  469. package/src/services/prompt-templates.ts +6 -5
  470. package/src/services/provider-api-key.ts +73 -0
  471. package/src/services/provider-hold-nudge.ts +9 -9
  472. package/src/services/provider-hold-record.ts +13 -12
  473. package/src/services/provider-state-audit.ts +19 -18
  474. package/src/services/provisioning-harness/driver.ts +1 -1
  475. package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
  476. package/src/services/provisioning-harness/lifecycle.ts +24 -22
  477. package/src/services/provisioning-harness/prepare.ts +16 -17
  478. package/src/services/rate-limit-resume.ts +10 -9
  479. package/src/services/red-main-auto-revert.ts +7 -5
  480. package/src/services/red-main-controller-deps.ts +20 -3
  481. package/src/services/red-main-incident.ts +3 -1
  482. package/src/services/red-main-quarantine.ts +2 -3
  483. package/src/services/register-agent.ts +22 -22
  484. package/src/services/restart-project-role.ts +1 -1
  485. package/src/services/restart-spawn.ts +17 -16
  486. package/src/services/review-publish.ts +7 -24
  487. package/src/services/review-ref-reconcile.ts +29 -38
  488. package/src/services/runtime-dependency-alert.ts +27 -0
  489. package/src/services/scheduler-command.ts +11 -9
  490. package/src/services/scheduler.ts +5 -5
  491. package/src/services/self-effort-grant.ts +5 -25
  492. package/src/services/send-message.ts +16 -33
  493. package/src/services/shutdown-agent.ts +17 -14
  494. package/src/services/spawn-agent.ts +12 -41
  495. package/src/services/spawn-chain-inspector.ts +1 -1
  496. package/src/services/spawn-cwd-placement.ts +21 -5
  497. package/src/services/spawn-issue-activity.ts +8 -6
  498. package/src/services/spawn-launch.ts +17 -1
  499. package/src/services/spawn-loss-escalation.ts +1 -1
  500. package/src/services/spawn-registration-watchdog.ts +2 -2
  501. package/src/services/spawn-repo-probe.ts +7 -5
  502. package/src/services/spawn-tree-cleanup.ts +7 -8
  503. package/src/services/steward-reaper.ts +4 -4
  504. package/src/services/task-context-hydration.ts +25 -1
  505. package/src/services/task-delivery.ts +20 -34
  506. package/src/services/task-entity.ts +40 -203
  507. package/src/services/task-issue-link.ts +13 -20
  508. package/src/services/task-issue-weave.ts +12 -11
  509. package/src/services/task-lifecycle.ts +27 -31
  510. package/src/services/task-repo-cwd.ts +37 -0
  511. package/src/services/task-review-hold.ts +4 -17
  512. package/src/services/task-semantic-events.ts +13 -11
  513. package/src/services/task-state-reconcile.ts +3 -4
  514. package/src/services/task-validation.ts +2 -2
  515. package/src/services/team-templates.ts +5 -5
  516. package/src/services/team.ts +31 -98
  517. package/src/services/terminal-provider-exit.ts +6 -5
  518. package/src/services/timer-message-fire-chaos-worker.ts +7 -3
  519. package/src/services/timer-message-fire-outbox.ts +1 -1
  520. package/src/services/transient-agent-reaper.ts +22 -19
  521. package/src/services/turn-reconcile-audit.ts +9 -9
  522. package/src/services/user-onboarding.ts +8 -6
  523. package/src/services/user-profile.ts +6 -9
  524. package/src/services/work-finality.ts +16 -4
  525. package/src/services/workflow-auto-start.ts +2 -1
  526. package/src/services/workflow-caller.ts +61 -4
  527. package/src/services/workflow-child-proof-worker.ts +1 -1
  528. package/src/services/workflow-control-proof-worker.ts +1 -1
  529. package/src/services/workflow-coordinator.ts +8 -2
  530. package/src/services/workflow-decisions.ts +5 -5
  531. package/src/services/workflow-deferred-cleanup.ts +1 -1
  532. package/src/services/workflow-effect-driver.ts +42 -19
  533. package/src/services/workflow-effects.ts +41 -27
  534. package/src/services/workflow-pins.ts +7 -4
  535. package/src/services/workflow-reconcile.ts +1 -0
  536. package/src/services/workflow-resume-pins.ts +3 -3
  537. package/src/services/workflow-routing-defaults.ts +18 -0
  538. package/src/services/workflow-run-view.ts +4 -3
  539. package/src/services/workflow-run.test-fixture.ts +1 -1
  540. package/src/services/workflow-runs.ts +34 -22
  541. package/src/services/workflow-supervisor-state.ts +21 -1
  542. package/src/services/workflow-supervisor.ts +4 -3
  543. package/src/services/workspace-recovery.ts +4 -3
  544. package/src/setup.ts +47 -8
  545. package/src/spawn-admission.ts +10 -2
  546. package/src/spawn-placement.ts +27 -9
  547. package/src/spawn-targets.ts +8 -8
  548. package/src/sse-agent-status.ts +15 -6
  549. package/src/sse.ts +49 -24
  550. package/src/startup-context-injection.ts +61 -57
  551. package/src/steward-handoff.ts +4 -3
  552. package/src/steward-policy-cleanup.ts +9 -8
  553. package/src/steward.ts +2 -2
  554. package/src/task-dispatch-live.ts +5 -2
  555. package/src/task-dispatcher.ts +2 -2
  556. package/src/token-db.ts +2 -2
  557. package/src/token-usage-async.ts +2 -4
  558. package/src/user-awaiting-reply.ts +5 -5
  559. package/src/utils.ts +7 -0
  560. package/src/workspace-actions.ts +24 -29
  561. package/src/workspace-auto-merge.ts +14 -12
  562. package/src/workspace-changed-paths-client.ts +16 -5
  563. package/src/workspace-cleanup-stale.ts +30 -7
  564. package/src/workspace-command-settle.ts +24 -16
  565. package/src/workspace-config.ts +1 -1
  566. package/src/workspace-conflict-resolver.ts +9 -7
  567. package/src/workspace-dead-owner-recovery.ts +5 -4
  568. package/src/workspace-escalation.ts +12 -11
  569. package/src/workspace-human-escalation.ts +3 -3
  570. package/src/workspace-land-gate-watchdog.ts +3 -3
  571. package/src/workspace-land-refusal.ts +20 -0
  572. package/src/workspace-land-stopped.ts +1 -0
  573. package/src/workspace-merge.ts +10 -26
  574. package/src/workspace-nothing-to-land-notice.ts +2 -2
  575. package/src/workspace-orphans.ts +18 -37
  576. package/src/workspace-pr-completion.ts +10 -8
  577. package/src/workspace-rekey.ts +3 -3
  578. package/src/workspace-steward-failure.ts +2 -2
  579. package/src/workspace-steward-wake.ts +2 -2
  580. package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
  581. package/public/assets/overview-C1DCTzzV.js +0 -2
  582. package/public/assets/provider-display-DsuWMvVL.js +0 -2
  583. package/public/assets/run-card-CpKqRbhg.js +0 -2
  584. package/public/assets/shipping-B9YtnwtG.js +0 -2
  585. package/public/assets/store-DcClXLH0.js +0 -9
  586. package/runner/src/agent-runner/http-liveness.ts +0 -85
  587. package/src/db/on-loop-write-transaction.ts +0 -34
@@ -5,18 +5,20 @@ import { listOrchestratorsAsync } from "./db/orchestrators-async";
5
5
  import { listHostViewsAsync } from "./db/hosts-async";
6
6
  import { providerQuotaBandsFor, advertisedProvidersFrom } from "./spawn-targets";
7
7
  import { dispatchPolicyFor } from "./routing-policy-store";
8
- import { providerPlacementReady } from "./spawn-placement";
8
+ import { providerPlacementReady, spawnHostCapAdmission } from "./spawn-placement";
9
9
  import { apiOverflowContextAsync, CLAUDE_API_PROVIDER } from "./claude-api-overflow";
10
10
  import { apiExpiryPaceAsync } from "./claude-api-expiry";
11
11
  import { liveTargetAdmission } from "./dispatch-availability";
12
12
 
13
13
  /** Reader-gateway shell for production callers. The ranking core remains the pure sync function. */
14
- export type DispatchSnapshot = DispatchContext & Required<Pick<DispatchContext, "now" | "catalog" | "bands" | "policy" | "preferences" | "advertisedProviders" | "targetAvailable">> & { probeUncertainty: Set<string> };
14
+ export type DispatchSnapshot = DispatchContext & Required<Pick<DispatchContext, "now" | "catalog" | "bands" | "policy" | "preferences" | "advertisedProviders" | "targetAvailable">> & { probeUncertainty: Set<string>; pinnedHostAtAgentCap?: boolean };
15
15
 
16
16
  export async function captureDispatchContext(ctx: Omit<DispatchContext, "policy" | "bands" | "catalog"> = {}): Promise<DispatchSnapshot> {
17
17
  const now = ctx.now ?? Date.now();
18
18
  const [snapshot, orchestrators, hostViews] = await Promise.all([providerQuotaRoutingSnapshotAsync(now), listOrchestratorsAsync(), listHostViewsAsync()]);
19
19
  const { targetAdmission: assess, targetUnavailableReason } = liveTargetAdmission(orchestrators, hostViews, snapshot.catalog, ctx.orchestratorId, ctx.spawnCap);
20
+ const pinnedHost = orchestrators.find(host => host.id === ctx.orchestratorId);
21
+ const pinnedView = pinnedHost && hostViews.find(view => view.id === pinnedHost.hostId);
20
22
  const probeUncertainty = new Set<string>();
21
23
  const targetAdmission = (target: Parameters<typeof assess>[0]) => {
22
24
  const result = assess(target);
@@ -42,6 +44,7 @@ export async function captureDispatchContext(ctx: Omit<DispatchContext, "policy"
42
44
  targetUnavailableReason,
43
45
  targetAdmission,
44
46
  probeUncertainty,
47
+ pinnedHostAtAgentCap: Boolean(pinnedView && spawnHostCapAdmission(ctx.spawnCap)(pinnedView)),
45
48
  bands,
46
49
  policy: dispatchPolicyFor(snapshot.policy),
47
50
  catalog: snapshot.catalog,
@@ -1,4 +1,4 @@
1
- import { isOpenAiApiModel } from "@humwick/sdk";
1
+ import { isOpenAiApiModel, ValidationError } from "@humwick/sdk";
2
2
  // #839 — the declarative task dispatcher.
3
3
  //
4
4
  // A pure function from (Task work-shape + live system state) → a concrete spawn
@@ -311,7 +311,7 @@ export function dispatchTask(shape: TaskDispatch | undefined, pin: DispatchPin =
311
311
  if (ctx.mustResolveAvailable && bandRank(bandFor(target.provider, bands)) > bandRank("normal")) warnings.push(`[CRITICAL] mustResolveAvailable: degraded to ${target.provider} (${bandFor(target.provider, bands)} band). Contention likely.`);
312
312
  }
313
313
  if (ctx.targetAvailable && !ctx.targetAvailable(target)) {
314
- throw new Error(ctx.targetUnavailableReason?.(target) ?? `No eligible online host runs ${target.provider}/${target.model ?? "default"}${target.effort ? ` at effort ${target.effort}` : ""}.`);
314
+ throw new ValidationError(ctx.targetUnavailableReason?.(target) ?? `No eligible online host runs ${target.provider}/${target.model ?? "default"}${target.effort ? ` at effort ${target.effort}` : ""}.`);
315
315
  }
316
316
  if (factors.preferences) factors.preferences = { ...factors.preferences, excluded: preferenceExclusions(preferences, factors.ranking, (p) => eligibility(p) ?? (!advertised.has(p) ? "not-advertised" : undefined)) };
317
317
  // Record the FINAL provider's band + the chosen model's window for transparency.
package/src/token-db.ts CHANGED
@@ -324,8 +324,8 @@ export function getTokenProfile(id: string): TokenProfile | null {
324
324
  return row ? rowToProfile(row) : null;
325
325
  }
326
326
 
327
- export function upsertTokenProfile(input: CreateTokenProfileInput): TokenProfile {
328
- const now = Date.now();
327
+ export function upsertTokenProfile(input: CreateTokenProfileInput, submittedAt: number = Date.now()): TokenProfile {
328
+ const now = submittedAt;
329
329
  const id = input.id ?? slugify(input.name);
330
330
  const existing = getTokenProfile(id);
331
331
  if (existing?.builtIn) throw new Error("built-in token profiles cannot be modified");
@@ -5,12 +5,10 @@ import type { Message } from "./types";
5
5
  // of src/db directly (the transport-thinness ratchet, #348).
6
6
  import { getGateway } from "./db/gateway.ts";
7
7
  import type { SpawnBudgetReadback, WorkflowRunBudgetReadback } from "./db/spawn-budget.ts";
8
- import type { aggregateSessionTokenUsage, recordSessionTokenUsage, SessionTokenUsageAggregate, SessionTokenUsageRecord } from "./db/token-usage.ts";
8
+ import type { aggregateSessionTokenUsage, recordSessionTokenUsage, SessionTokenUsageAggregate } from "./db/token-usage.ts";
9
9
 
10
10
  export const recordSessionTokenUsageAsync = async (input: Parameters<typeof recordSessionTokenUsage>[0]) => {
11
- const { budgetNotices, ...result } = await getGateway().write("tokenUsage.record", { input }, { critical: false }) as unknown as {
12
- record: SessionTokenUsageRecord;
13
- inserted: boolean;
11
+ const { budgetNotices, ...result } = await getGateway().write("tokenUsage.record", { input }, { critical: false }) as unknown as ReturnType<typeof recordSessionTokenUsage> & {
14
12
  budgetNotices: Message[];
15
13
  };
16
14
  publishBudgetNotices(budgetNotices);
@@ -1,7 +1,7 @@
1
+ import { framed } from "./hub-metrics";
1
2
  import { userSinkId, canonicalHumanAgentId } from "@humwick/sdk";
2
3
  import { listUsers } from "./db/users";
3
- import { listPendingReplyObligationsAsync } from "./db/message-reads-async";
4
- import { getLatestMessageIdAsync } from "./db/message-reads-async";
4
+ import { listPendingReplyObligationsAsync, getLatestMessageIdAsync } from "./db/message-reads-async";
5
5
  import { optedInNotificationRecipients } from "./db/notification-subscriptions";
6
6
  import { routeNotification } from "./notification-router";
7
7
  import { subscribeRelayEvents } from "./events";
@@ -12,7 +12,7 @@ export async function reconcileUserAwaitingReply(previous?: string): Promise<str
12
12
  const [pending, latestId] = await Promise.all([Promise.all(listUsers().map(user => listPendingReplyObligationsAsync(canonicalHumanAgentId(userSinkId(user.id)), 1, true))).then(rows => rows.flat()), getLatestMessageIdAsync()]);
13
13
  const signature = JSON.stringify([recipients, pending.length > 0]);
14
14
  if (signature === previous) return signature;
15
- routeNotification({
15
+ await routeNotification({
16
16
  type: "user.awaiting_reply", scope: {}, recipients: [],
17
17
  message: {
18
18
  body: pending.length ? "The operator has an unanswered message." : "The operator has no unanswered messages.",
@@ -30,13 +30,13 @@ export function startUserAwaitingReply(): () => void {
30
30
  const unsubscribe = subscribeRelayEvents(event => {
31
31
  if (event.type.startsWith("message.") && event.data.to !== "hue:office") dirty = true;
32
32
  });
33
- const timer = setInterval(() => {
33
+ const timer = setInterval(framed("timer user-awaiting-reply", () => {
34
34
  ticks++;
35
35
  if ((!dirty && ticks % 30 !== 0) || running) return;
36
36
  dirty = false; running = true;
37
37
  void reconcileUserAwaitingReply(signature).then(value => { signature = value; }).catch(error => {
38
38
  dirty = true; console.error("user.awaiting_reply reconciliation failed", error);
39
39
  }).finally(() => { running = false; });
40
- }, 1000);
40
+ }), 1000);
41
41
  return () => { clearInterval(timer); unsubscribe(); };
42
42
  }
package/src/utils.ts CHANGED
@@ -40,3 +40,10 @@ export function isOrchestratorLocalToRelay(orchestrator: { hostname?: string | n
40
40
  const self = hostname();
41
41
  return sameHostIdentity(orchestrator.hostname, self) || sameHostIdentity(orchestrator.id, self);
42
42
  }
43
+
44
+ /** An orchestrator id from a hostname or operator input: dots and other separators become dashes. */
45
+ export function sanitizeOrchestratorId(value: string): string {
46
+ const clean = value.trim().replace(/\./g, "-").replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
47
+ if (!clean) throw new Error("--id must contain at least one letter or number");
48
+ return clean.slice(0, 120);
49
+ }
@@ -1,3 +1,6 @@
1
+ import { prepareCommandWriteAsync, createCommandAsync, submitPreparedCommandAsync } from "./commands-write";
2
+ import type { ReviewCleanupInput } from "./db/review-publish-write";
3
+ import { getGateway } from "./db/gateway";
1
4
  import { recordWorkspaceAudit } from "./workspace-action-audit";
2
5
  import { WORKSPACE_MERGE_HOLD_MESSAGE } from "./db/workspace-merge-hold";
3
6
  import { stewardGenerationCanceled } from "./db/workspace-steward-fence";
@@ -14,22 +17,13 @@ import { stringValue } from "@humwick/sdk";
14
17
  // `requestWorkspaceMerge`, the core does not emit the command event; it returns
15
18
  // the Command and the caller emits via its own `emitCommand` (kept module-local).
16
19
 
17
- import { createCommand } from "./commands-db";
20
+
18
21
  import { requestWorkspaceRekey } from "./workspace-rekey";
19
22
  import { isWorkspaceSteward, workspaceStewardRefusal } from "./workspace-steward-authority";
20
23
  import { getStewardConfig } from "./config-store";
21
24
  import { cancelPendingLegacyLandingsAsync } from "./commands-landing-async";
22
25
  import { workspaceOwnerOrchestrator } from "./workspace-host";
23
- import {
24
- getWorkspace,
25
- claimWorkspaceMetadataAsync,
26
- listOrchestrators,
27
- patchWorkspaceMetadataAsync,
28
- releaseWorkspaceClaimMetadataAsync,
29
- setWorkspaceMergeHoldAsync,
30
- transitionWorkspaceLandStateAsync,
31
- updateWorkspaceStatusAsync,
32
- } from "./db";
26
+ import { getWorkspace, claimWorkspaceMetadataAsync, listOrchestrators, patchWorkspaceMetadataAsync, releaseWorkspaceClaimMetadataAsync, setWorkspaceMergeHoldAsync, transitionWorkspaceLandStateAsync, updateWorkspaceStatusAsync, REVIEW_HOLD_REJECT_MESSAGE, workspaceLandBarrier } from "./db";
33
27
  import { isOwnerAlive, requestWorkspaceMerge } from "./workspace-merge";
34
28
  import { buildWorkspaceCleanupCommand } from "./workspace-cleanup-stale";
35
29
  // Re-export the land-strategy contract so the MCP land tool validates against the same
@@ -38,15 +32,13 @@ export { LAND_STRATEGIES } from "./workspace-merge";
38
32
  import { claimMetadataPatch, workspaceActiveClaim, workspaceStewardHandbackPending } from "./workspace-claim";
39
33
  import { DIRTY_WORKTREE_LAND_SKIP_REASON, LAND_IN_FLIGHT_STATUSES, READY_TO_LAND_STATUSES, TERMINAL_WORKSPACE_STATUSES, worktreeReapable } from "./workspace-phase";
40
34
  import { LAND_BLOCK_REJECT_MESSAGE, stripReservedWorkspaceMetadataKeys } from "./land-block";
41
- import { REVIEW_HOLD_REJECT_MESSAGE, workspaceLandBarrier } from "./db";
42
35
  import { CLEAR_MERGE_NO_PROGRESS, scheduleRepoMerge } from "./workspace-auto-merge";
43
36
  import { archiveLandAttempt, CLEAR_LAST_ATTEMPT_VERDICT, CLEAR_MERGE_RETRY } from "./workspace-merge-progress";
44
37
  import { fetchHostMergePreview, refreshWorkspaceGitState } from "./workspace-probe";
45
38
  import { recordLandStopped } from "./workspace-land-stopped";
46
39
  import { finishWorkspaceStewardHandoff } from "./steward-handoff";
47
40
  import { stewardHandoffIdentity } from "./steward-provenance";
48
- import type { Command, WorkspaceAutoMergePolicy, WorkspaceLandingPolicy, WorkspaceMergeStrategy, WorkspaceRecord, WorkspaceStatus } from "./types";
49
- import type { WorkspaceLandGateLevel } from "./types";
41
+ import type { Command, WorkspaceAutoMergePolicy, WorkspaceLandingPolicy, WorkspaceMergeStrategy, WorkspaceRecord, WorkspaceStatus, WorkspaceLandGateLevel } from "./types";
50
42
  import type { WorkspaceLandGateAuth } from "./workspace-land-gate";
51
43
 
52
44
  // Single source of truth for the action verb set. The route's `optionalEnum` and
@@ -326,7 +318,7 @@ export async function applyWorkspaceAction(workspace: WorkspaceRecord, input: Ap
326
318
  return { ok: false, httpStatus: 422, error: `workspace ${workspace.id} has no isolated worktree to refresh` };
327
319
  }
328
320
  const checkOnly = input.checkOnly === true;
329
- const built = buildWorkspaceDepsRefreshCommand(workspace, agentId ?? "agent", checkOnly);
321
+ const built = (await buildWorkspaceDepsRefreshCommand(workspace, agentId ?? "agent", checkOnly));
330
322
  if (!built.ok) return { ok: false, httpStatus: built.status, error: built.error };
331
323
  await recordWorkspaceAudit({
332
324
  action,
@@ -416,11 +408,11 @@ export async function applyWorkspaceAction(workspace: WorkspaceRecord, input: Ap
416
408
  error: `workspace ${workspace.id} cannot be cleaned up safely: ${safety.error}; pass force:true with a detail reason only if intentionally discarding work`,
417
409
  };
418
410
  }
419
- const built = buildWorkspaceCleanupCommand(workspace, agentId ?? "dashboard", {
411
+ const built = (await buildWorkspaceCleanupCommand(workspace, agentId ?? "dashboard", {
420
412
  force: input.force === true,
421
413
  reason: detail,
422
414
  autoReclaim: input.autoReclaim,
423
- });
415
+ }));
424
416
  if (!built.ok) return { ok: false, httpStatus: built.status, error: built.error };
425
417
  command = built.command;
426
418
  }
@@ -496,17 +488,17 @@ export async function applyWorkspaceAction(workspace: WorkspaceRecord, input: Ap
496
488
  // captured delivered `sha`. #1452 round-5 H4: the relay deliberately does NOT derive or return a
497
489
  // ref — the AUTHORITATIVE ref is whatever the HOST reports back on the command result (the host may
498
490
  // run a different sanitizer version), and the ledger records it only after that settlement.
499
- export function buildWorkspacePublishReviewCommand(
491
+ export async function buildWorkspacePublishReviewCommand(
500
492
  workspace: WorkspaceRecord,
501
493
  requestedBy: string,
502
- opts: { taskId?: number; sha?: string; reviewRef?: string } = {},
503
- ): { ok: true; command: Command } | { ok: false; status: number; error: string } {
494
+ opts: { taskId?: number; sha?: string; reviewRef?: string; cleanup?: ReviewCleanupInput } = {},
495
+ ): Promise<{ ok: true; command: Command } | { ok: false; status: number; error: string }> {
504
496
  if (workspace.mode !== "isolated" || !workspace.worktreePath) {
505
497
  return { ok: false, status: 422, error: `workspace ${workspace.id} has no worktree to publish for review` };
506
498
  }
507
499
  const owner = workspaceOwnerOrchestrator(workspace, listOrchestrators(), { onlineOnly: false });
508
500
  if (!owner) return { ok: false, status: 409, error: "no orchestrator owns this workspace path" };
509
- const command = createCommand({
501
+ const prepared = await prepareCommandWriteAsync({
510
502
  type: "workspace.publish-review",
511
503
  source: "system",
512
504
  target: owner.agentId,
@@ -527,6 +519,9 @@ export function buildWorkspacePublishReviewCommand(
527
519
  queued: owner.status !== "online",
528
520
  },
529
521
  });
522
+ const command = opts.cleanup
523
+ ? await getGateway().write("commands.createReviewPublish", { command: prepared, cleanup: opts.cleanup }) as unknown as Command
524
+ : await submitPreparedCommandAsync(prepared);
530
525
  return { ok: true, command };
531
526
  }
532
527
 
@@ -537,10 +532,10 @@ export function buildWorkspacePublishReviewCommand(
537
532
  // caller passes the EXACT host-confirmed `ref` from the ledger and the host deletes THAT ref verbatim
538
533
  // (after asserting it is under refs/review/*) — it no longer re-derives the target from branch/
539
534
  // workspaceId, which could diverge across a relay/host sanitizer skew and leak the real ref.
540
- export function buildReviewRefUnpublishCommandFromFields(
535
+ export async function buildReviewRefUnpublishCommandFromFields(
541
536
  fields: { workspaceId: string; repoRoot: string; worktreePath?: string; branch?: string; orchestratorId?: string; ref?: string },
542
537
  requestedBy: string,
543
- ): { ok: true; command: Command } | { ok: false; status: number; error: string } {
538
+ ): Promise<{ ok: true; command: Command } | { ok: false; status: number; error: string }> {
544
539
  const synthetic = {
545
540
  orchestratorId: fields.orchestratorId,
546
541
  sourceCwd: fields.repoRoot,
@@ -550,7 +545,7 @@ export function buildReviewRefUnpublishCommandFromFields(
550
545
  } as unknown as WorkspaceRecord;
551
546
  const owner = workspaceOwnerOrchestrator(synthetic, listOrchestrators(), { onlineOnly: false });
552
547
  if (!owner) return { ok: false, status: 409, error: "no orchestrator owns this repo path" };
553
- const command = createCommand({
548
+ const command = (await createCommandAsync({
554
549
  type: "workspace.unpublish-review",
555
550
  source: "system",
556
551
  target: owner.agentId,
@@ -566,20 +561,20 @@ export function buildReviewRefUnpublishCommandFromFields(
566
561
  requestedAt: Date.now(),
567
562
  queued: owner.status !== "online",
568
563
  },
569
- });
564
+ }));
570
565
  return { ok: true, command };
571
566
  }
572
567
 
573
568
  // Build a `workspace.deps-refresh` command for a worktree's owning orchestrator
574
569
  // (issue #51). checkOnly just reports staleness. Same owner-resolution as cleanup.
575
- function buildWorkspaceDepsRefreshCommand(
570
+ async function buildWorkspaceDepsRefreshCommand(
576
571
  workspace: WorkspaceRecord,
577
572
  requestedBy: string,
578
573
  checkOnly: boolean,
579
- ): { ok: true; command: Command } | { ok: false; status: number; error: string } {
574
+ ): Promise<{ ok: true; command: Command } | { ok: false; status: number; error: string }> {
580
575
  const owner = workspaceOwnerOrchestrator(workspace, listOrchestrators(), { onlineOnly: false });
581
576
  if (!owner) return { ok: false, status: 409, error: "no online orchestrator owns this workspace path" };
582
- const command = createCommand({
577
+ const command = (await createCommandAsync({
583
578
  type: "workspace.deps-refresh",
584
579
  source: "system",
585
580
  target: owner.agentId,
@@ -594,7 +589,7 @@ function buildWorkspaceDepsRefreshCommand(
594
589
  requestedAt: Date.now(),
595
590
  queued: owner.status !== "online",
596
591
  },
597
- });
592
+ }));
598
593
  return { ok: true, command };
599
594
  }
600
595
 
@@ -1,3 +1,5 @@
1
+ import { workspaceLandRefused } from "./workspace-land-refusal";
2
+ import { refreshPendingLandQueueLeaseAsync } from "./db/runtime-tail-async";
1
3
  // Event-driven workspace merge-back engine (#237).
2
4
  //
3
5
  // The branch merge-back used to be driven entirely by a 2-min maintenance poll, so an
@@ -30,8 +32,8 @@ import {
30
32
  patchWorkspaceMetadataAsync,
31
33
  updateWorkspaceStatus,
32
34
  workspaceLandBarrier, updateWorkspaceStatusAsync} from "./db";
33
- import { findLatestCommandByCorrelation } from "./commands-db";
34
- import { refreshPendingLandQueueLease } from "./db/commands-claim-admission";
35
+ import { findLatestCommandByCorrelationAsync } from "./commands-write";
36
+
35
37
  import { notifyBranchLandFailed } from "./branch-landed";
36
38
  import { resolveStewardIdentity } from "./steward-identity";
37
39
  import { emitCommandEvent } from "./command-events";
@@ -144,14 +146,14 @@ async function escalateNoProgressMerge(ws: WorkspaceRecord, _tip?: string): Prom
144
146
  const payload = { kind: "workspace.auto-merge-stuck", workspaceId: ws.id, repoRoot: ws.repoRoot, branch: ws.branch, baseRef: ws.baseRef, attempts: MAX_NO_PROGRESS_MERGE_ATTEMPTS, reason: mergeError };
145
147
  let escalated = false;
146
148
  if (recipients.length) {
147
- notifyBranchLandFailed({ workspace: ws,
149
+ await notifyBranchLandFailed({ workspace: ws,
148
150
  commandId: typeof meta.mergeCommandId === "string" ? meta.mergeCommandId : undefined,
149
151
  recipients,
150
152
  reason: `auto-merge backed off after ${MAX_NO_PROGRESS_MERGE_ATTEMPTS} no-progress attempts (${mergeError})`,
151
153
  });
152
154
  escalated = true;
153
155
  } else {
154
- const human = notifyWorkspaceHumanEscalation({ subject, body, payload, source: "workspace-auto-merge" });
156
+ const human = (await notifyWorkspaceHumanEscalation({ subject, body, payload, source: "workspace-auto-merge" }));
155
157
  escalated = human.accepted;
156
158
  if (!human.accepted) {
157
159
  console.warn(`[workspace-auto-merge] no-progress escalation for ${ws.id} had zero agent recipients and no human channel: ${human.reason ?? "unknown reason"}`);
@@ -180,7 +182,7 @@ async function notifyProtectedTipRestore(ws: WorkspaceRecord, preview: Workspace
180
182
  // whose delivered tip was discarded CANNOT reach landed-on-main by any land path until the carry is
181
183
  // explicitly confirmed. Returns ok=false if it could NOT durably record the obligation — the caller
182
184
  // fails closed on that (withholds the steward hand-off) rather than risk a silent divergent land.
183
- const carry = markWorkspaceTasksCarryPending(ws, discardedHead);
185
+ const carry = await markWorkspaceTasksCarryPending(ws, discardedHead);
184
186
  const body = `Workspace \`${ws.branch ?? ws.id}\` restored protected tip \`${preview.protectedTip?.slice(0, 12) ?? "unknown"}\`; discarded HEAD \`${discardedHead.slice(0, 12)}\` was preserved at \`${discardedRef}\`.`;
185
187
  await createActivityEventAsync({
186
188
  clientId: `workspace-protected-tip-restore-${ws.id}-${Date.now()}`,
@@ -198,7 +200,7 @@ async function notifyProtectedTipRestore(ws: WorkspaceRecord, preview: Workspace
198
200
  const { coordinatorAgentId } = resolveStewardIdentity(current);
199
201
  const recipients = [...new Set([coordinatorAgentId, current.ownerAgentId].filter((r): r is string => Boolean(r)))];
200
202
  if (recipients.length) {
201
- routeNotification({
203
+ await routeNotification({
202
204
  type: "workspace.land_gate_warning",
203
205
  scope: { workspaceId: current.id, repoRoot: current.repoRoot, author: current.ownerAgentId, parent: coordinatorAgentId ?? undefined },
204
206
  recipients,
@@ -492,7 +494,7 @@ async function landCandidates(
492
494
  }
493
495
  }
494
496
  if (p.conflict === true || protectedTipRestored) {
495
- emitWorkspaceTaskSemanticNotifications({
497
+ await emitWorkspaceTaskSemanticNotifications({
496
498
  workspace: ws,
497
499
  transition: "land-conflict",
498
500
  actor: "workspace-auto-merge",
@@ -616,7 +618,7 @@ function landableWorkspaces(repoRoot?: string, baseRef?: string): WorkspaceRecor
616
618
  // `review_requested` with NO authorization; after the coordinator's clear the flag is off
617
619
  // and the promoted delivery lands normally). Covers all three sweep triggers (event,
618
620
  // backstop, watchdog) and the conflict→steward hand-off at this single selection filter.
619
- && !workspaceLandBarrier(ws).blocked
621
+ && !workspaceLandBarrier(ws).blocked && !workspaceLandRefused(ws)
620
622
  // #1473 — nor is a workspace bound to a deliverable Task that has NOT authorized a land: a pushed
621
623
  // `agent/*` branch in a ready-to-land status is not, by itself, a land authorization (a "HOLD" is
622
624
  // coordinator convention only). Excluding it here keeps it out of BOTH the auto-land dispatch and
@@ -761,9 +763,9 @@ function commandIsTerminal(command: Command | null): boolean {
761
763
  return Boolean(command && TERMINAL_COMMAND_STATUSES.has(command.status));
762
764
  }
763
765
 
764
- function latestWorkspaceCommand(ws: WorkspaceRecord): Command | null {
766
+ async function latestWorkspaceCommand(ws: WorkspaceRecord): Promise<Command | null> {
765
767
  const type = LAND_IN_FLIGHT_STATUSES.has(ws.status) ? "workspace.merge" : "workspace.cleanup";
766
- return findLatestCommandByCorrelation(type, ws.id);
768
+ return findLatestCommandByCorrelationAsync(type, ws.id);
767
769
  }
768
770
 
769
771
  async function recoverAgedInFlightWorkspaces(now: number, signal?: AbortSignal): Promise<Record<string, unknown>> {
@@ -803,9 +805,9 @@ async function recoverAgedInFlightWorkspaces(now: number, signal?: AbortSignal):
803
805
  // young merge_planned row the 20-min age gate would otherwise skip. Only a merge_planned row can be
804
806
  // executor-orphaned, so a young cleanup_requested/non-merge row still pays NO command lookup (the
805
807
  // pre-#2094 cost) — fetch only when the row is old enough for the existing recovery OR could be an orphan.
806
- let command = (LAND_IN_FLIGHT_STATUSES.has(ws.status) || oldEnough(ws)) ? latestWorkspaceCommand(ws) : null;
808
+ let command = (LAND_IN_FLIGHT_STATUSES.has(ws.status) || oldEnough(ws)) ? await latestWorkspaceCommand(ws) : null;
807
809
  if (command?.status === "pending" && command.type === "workspace.merge") {
808
- if (refreshPendingLandQueueLease(command.id, now)) {
810
+ if ((await refreshPendingLandQueueLeaseAsync(command.id, now))) {
809
811
  stillActive.push(ws.id);
810
812
  continue;
811
813
  }
@@ -1,6 +1,7 @@
1
1
  import type { ChangedPathDiff } from "@humwick/sdk";
2
2
  import { decideDiffGate, type DiffGateDecision } from "./services/diff-gate";
3
3
  import { fetchOwningHostJson } from "./workspace-host-client";
4
+ import { workspaceHeadAlreadyOnBase } from "./services/task-nothing-to-land";
4
5
  import type { WorkspaceRecord } from "./types";
5
6
 
6
7
  /**
@@ -87,11 +88,21 @@ export async function resolveDeliveryDiffDecision(
87
88
  ...(reprobe.ok ? { headSha: reprobe.headSha } : {}),
88
89
  };
89
90
  }
90
- return { decision: decideDiffGate(reprobe), ...(reprobe.headSha ? { headSha: reprobe.headSha } : {}) };
91
+ return await classify(workspace, reprobe);
91
92
  }
93
+ return await classify(workspace, probe);
94
+ }
95
+
96
+ /** A head the owning host proves is already on the freshly fetched base lands nothing, so no
97
+ * gate can protect anything: accept it without one (#3214). Probed only on the would-gate path. */
98
+ async function classify(workspace: WorkspaceRecord, probe: ChangedPathDiff): Promise<DeliveryDiffEvidence> {
92
99
  const decision = decideDiffGate(probe);
93
- return {
94
- decision,
95
- ...(probe.ok && probe.headSha ? { headSha: probe.headSha } : {}),
96
- };
100
+ if (!probe.ok || !probe.headSha) return { decision };
101
+ if (decision.requireGates && await workspaceHeadAlreadyOnBase(workspace, probe.headSha)) {
102
+ return {
103
+ decision: { requireGates: false, reason: `delivered head ${probe.headSha.slice(0, 12)} is already on ${workspace.baseRef ?? "main"}: nothing lands`, paths: probe.paths },
104
+ headSha: probe.headSha,
105
+ };
106
+ }
107
+ return { decision, headSha: probe.headSha };
97
108
  }
@@ -1,3 +1,4 @@
1
+ import { createCommandAsync } from "./commands-write";
1
2
  // Cleanup-stale workspace reconciliation: builds the `workspace.cleanup` command and runs the
2
3
  // `workspace cleanup-stale --check/--execute` sweep (#208/#965/#1066). Kept as a standalone leaf
3
4
  // module (no workspace-auto-merge.ts dependency) so the transient-agent reap lifecycle (#1477)
@@ -7,7 +8,7 @@
7
8
 
8
9
  import { statSync } from "node:fs";
9
10
  import { emitCommandEvent } from "./command-events";
10
- import { createCommand } from "./commands-db";
11
+
11
12
  import { getAgent, listWorkspaces, updateWorkspaceStatusAsync } from "./db";
12
13
  import { fetchWorkspaceGitState } from "./workspace-git-state-client";
13
14
  import { workspaceOwnerOrchestrator } from "./workspace-host";
@@ -15,15 +16,15 @@ import { workspaceActiveClaim } from "./workspace-claim";
15
16
  import { isOwnerAlive, isOwnerProcessConfirmedGone, liveAgentCwds, worktreeInUseByLiveAgent } from "./workspace-merge";
16
17
  import { refResolvesInRepo, verifyLandedOrEquivalentContent } from "./services/landed-ancestry";
17
18
  import { TERMINAL_WORKSPACE_STATUSES } from "./workspace-phase";
18
- import type { Command, WorkspaceRecord } from "./types";
19
+ import type { Command, WorkspaceRecord, WorkspaceOrphan } from "./types";
19
20
 
20
21
  // Build a `workspace.cleanup` command for the worktree's owning orchestrator.
21
22
  // Moved here (from routes.ts) so the MCP surface reaches it without importing routes.
22
- export function buildWorkspaceCleanupCommand(
23
+ export async function buildWorkspaceCleanupCommand(
23
24
  workspace: WorkspaceRecord,
24
25
  requestedBy: string,
25
26
  opts: { force?: boolean; reason?: string; autoReclaim?: "origin-captured" } = {},
26
- ): { ok: true; command: Command } | { ok: false; status: number; error: string } {
27
+ ): Promise<{ ok: true; command: Command } | { ok: false; status: number; error: string }> {
27
28
  if (
28
29
  opts.force !== true &&
29
30
  workspace.mode === "isolated" &&
@@ -38,7 +39,7 @@ export function buildWorkspaceCleanupCommand(
38
39
  }
39
40
  const owner = workspaceOwnerOrchestrator(workspace);
40
41
  if (!owner) return { ok: false, status: 409, error: "no online orchestrator owns this workspace path; retry when the host is online or use DELETE /api/workspaces/:id to purge the record" };
41
- const command = createCommand({
42
+ const command = (await createCommandAsync({
42
43
  type: "workspace.cleanup",
43
44
  source: "system",
44
45
  target: owner.agentId,
@@ -58,7 +59,7 @@ export function buildWorkspaceCleanupCommand(
58
59
  ...(opts.autoReclaim === "origin-captured" ? { autoReclaim: "origin-captured" } : {}),
59
60
  queued: false,
60
61
  },
61
- });
62
+ }));
62
63
  return { ok: true, command };
63
64
  }
64
65
 
@@ -190,7 +191,7 @@ export async function cleanupStaleWorkspaces(opts: CleanupStaleWorkspacesOptions
190
191
  } else {
191
192
  // Owner re-confirmed offline just above; force past buildWorkspaceCleanupCommand's
192
193
  // live-owner guard and carry a reason so the host records an intentional force-discard.
193
- const built = buildWorkspaceCleanupCommand(ws, "cleanup-stale", forced ? { force: true, reason: "operator force-discard: abandoned branch, owner offline" } : {});
194
+ const built = (await buildWorkspaceCleanupCommand(ws, "cleanup-stale", forced ? { force: true, reason: "operator force-discard: abandoned branch, owner offline" } : {}));
194
195
  if (built.ok) {
195
196
  await updateWorkspaceStatusAsync(ws.id, "cleanup_requested", { lastWorkspaceAction: "cleanup-stale", lastWorkspaceActionAt: Date.now(), cleanupProof: { ...proof, forced } });
196
197
  emitCommandEvent(built.command, "command.requested");
@@ -229,3 +230,25 @@ export async function cleanupStaleWorkspaces(opts: CleanupStaleWorkspacesOptions
229
230
  }
230
231
  return { dryRun, landedOnly, force, repoRoot, ...(workspaceId ? { workspaceId } : {}), ...(workspaceIds ? { workspaceIds } : {}), scanned: candidates.length, eligible: rows.filter((r) => r.discardable).length, cleaned, candidates: rows };
231
232
  }
233
+
234
+ export async function dispatchOrphanCleanup(targetAgentId: string, orphan: WorkspaceOrphan): Promise<string> {
235
+ const command = (await createCommandAsync({
236
+ type: "workspace.cleanup",
237
+ source: "system",
238
+ target: targetAgentId,
239
+ params: {
240
+ action: "cleanup",
241
+ worktreePath: orphan.worktreePath,
242
+ repoRoot: orphan.repoRoot,
243
+ branch: orphan.branch,
244
+ baseRef: orphan.baseRef,
245
+ baseSha: orphan.baseSha,
246
+ deleteBranch: true,
247
+ reclaim: true,
248
+ requestedBy: "workspace-orphan-reaper",
249
+ requestedAt: Date.now(),
250
+ },
251
+ }));
252
+ emitCommandEvent(command, `command.${command.status}`);
253
+ return command.id;
254
+ }
@@ -1,9 +1,12 @@
1
+ import { DELEGATED_LAND_REFUSAL_CODE } from "@humwick/sdk/worktree-config";
2
+ import { refuseWorkspaceLand } from "./workspace-land-refusal";
3
+ import { releaseMergeLeaseAsync, insertLandGateAttestationAsync } from "./db/runtime-tail-async";
1
4
  import { commandFailureReason, settleWorkspaceCleanupCommand, type WorkspaceCommandSettleResult } from "./workspace-cleanup-settle";
2
5
  export { type WorkspaceCommandSettleResult } from "./workspace-cleanup-settle";
3
6
  import { fireAndForget, isRecord, sanitizeFsName, stringValue, type LandGateAttestation, type LandGateRunResult } from "@humwick/sdk";
4
7
  import { storeTextArtifact } from "./artifact-text";
5
8
  import { announceBranchLanded, notifyBaseWorktreeStrand, notifyBranchLandFailed, notifyLandGateFailed, notifyLandGateSkipped, notifyLandGateWarnings, recordLandingReceipt } from "./branch-landed";
6
- import { getWorkspace, getArtifactAsync, insertLandGateAttestation, listOrchestrators, markWorkspaceLanded, patchWorkspaceMetadataAsync, releaseMergeLease, setWorkspaceBranchAsync, updateWorkspaceStatusAsync, markWorkspaceLandedAsync} from "./db";
9
+ import { getWorkspace, getArtifactAsync, listOrchestrators, markWorkspaceLanded, patchWorkspaceMetadataAsync, setWorkspaceBranchAsync, updateWorkspaceStatusAsync, markWorkspaceLandedAsync } from "./db";
7
10
  import { parseOwnerRepoFromRemote, repoFromGitRemote } from "./services/git-remote";
8
11
  import { deferLandSettleSideEffect } from "./land-settle-dispatch";
9
12
  import { recordConfirmedLandBoundary } from "./services/land-boundary-settle";
@@ -205,7 +208,7 @@ export async function settleWorkspaceMergeCommand(command: Command, opts: Worksp
205
208
  command = proven;
206
209
  }
207
210
  const terminalSettled = WORKSPACE_MERGE_SETTLED_STATUSES.has(command.status);
208
- if (terminalSettled && command.landingContainmentMode === "legacy-unfenced-v0") releaseMergeLease({ commandId: command.id });
211
+ if (terminalSettled && command.landingContainmentMode === "legacy-unfenced-v0") (await releaseMergeLeaseAsync({ commandId: command.id }));
209
212
  const settledRepoRoot = typeof command.params.repoRoot === "string" ? command.params.repoRoot : undefined;
210
213
  const settledBaseRef = typeof command.params.baseRef === "string" ? command.params.baseRef : undefined;
211
214
  let transitionCompleted = false;
@@ -360,13 +363,13 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
360
363
  if (retryableNonLand) {
361
364
  if (ancestryUnconfirmed && landedWorkspace) {
362
365
  const ws = transitioned ?? landedWorkspace;
363
- deferLandSettleSideEffect("land-unconfirmed", () => notifyLandUnconfirmed({ workspace: ws, commandId: command.id, sha: authoritativeLandedSha, targetRef: landTargetRef, kind: landAncestry }));
366
+ deferLandSettleSideEffect("land-unconfirmed", async () => await notifyLandUnconfirmed({ workspace: ws, commandId: command.id, sha: authoritativeLandedSha, targetRef: landTargetRef, kind: landAncestry }));
364
367
  }
365
368
  return { settled: true };
366
369
  }
367
370
  const noopBase = nothingBase ?? (mergeResult.noop === true && !resultError && mergeResult.landMechanism !== "plain-git" && (resultStatus === "merged" || resultStatus === "active") && typeof baseSha === "string" && /^[0-9a-f]{40,64}$/.test(baseSha) ? baseSha : undefined);
368
371
  if (noopBase && landedWorkspace) await finishWorkspaceStewardHandoff(landedWorkspace);
369
- if (noopBase && landedWorkspace) deferLandSettleSideEffect("nothing-to-land", () => notifyNothingToLand(landedWorkspace, noopBase, command.id, typeof command.params.expectedHeadSha === "string" ? command.params.expectedHeadSha : undefined));
372
+ if (noopBase && landedWorkspace) deferLandSettleSideEffect("nothing-to-land", async () => await notifyNothingToLand(landedWorkspace, noopBase, command.id, typeof command.params.expectedHeadSha === "string" ? command.params.expectedHeadSha : undefined));
370
373
  const landConfirmed = !nothingBase && workspaceMergeProvesLand(mergeResult, command.params);
371
374
  const continuationBranch = normalizedStatus === "active" ? landedWorkspace?.branch : undefined;
372
375
  // #1790 F1 — build the land notice up front so the durable RECEIPT can be written SYNCHRONOUSLY,
@@ -393,7 +396,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
393
396
  ...(prLanded || landedCommitCount === undefined ? {} : { landedCommitCount }),
394
397
  }
395
398
  : undefined;
396
- if (landedNotice) recordLandingReceipt(landedNotice);
399
+ if (landedNotice) (await recordLandingReceipt(landedNotice));
397
400
  // #1744 F1 — record the tree-keyed land-gate attestation. Placed HERE, not at the executor, so
398
401
  // that "the full gate passed on tree T" only becomes deployable evidence once the relay has
399
402
  // CONFIRMED (ancestry-gated above, #1713) that T actually reached the base ref: a gated attempt
@@ -408,7 +411,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
408
411
  const repo = landRepo(landedWorkspace.repoRoot);
409
412
  // No resolvable origin ⇒ no shared identity with cd-watcher's detection clone ⇒ no row.
410
413
  if (repo) {
411
- insertLandGateAttestation({
414
+ (await insertLandGateAttestationAsync({
412
415
  repo,
413
416
  treeSha: attestation.treeSha,
414
417
  commitSha: authoritativeLandedSha,
@@ -419,7 +422,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
419
422
  gatesRan: attestation.gatesRan,
420
423
  workspaceId: landedWorkspace.id,
421
424
  ...(attestation.suiteCoverage ? { suiteCoverage: attestation.suiteCoverage } : {}),
422
- });
425
+ }));
423
426
  }
424
427
  } catch (error) {
425
428
  console.warn(`[land-gate-attestation] failed to record tree ${attestation.treeSha.slice(0, 12)} for ${workspaceId}: ${String(error)}`);
@@ -439,7 +442,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
439
442
  preservedWipPaths: Array.isArray(baseWorktreeSync.preservedWipPaths) ? baseWorktreeSync.preservedWipPaths.filter((p): p is string => typeof p === "string") : undefined,
440
443
  message: typeof baseWorktreeSync.message === "string" ? baseWorktreeSync.message : undefined,
441
444
  };
442
- deferLandSettleSideEffect("base-worktree-strand", () => notifyBaseWorktreeStrand({ workspace: ws, sync: strandSync }));
445
+ deferLandSettleSideEffect("base-worktree-strand", async () => await notifyBaseWorktreeStrand({ workspace: ws, sync: strandSync }));
443
446
  }
444
447
  // #1713 — reaching here means the land is either ancestry-CONFIRMED, a remote-verified PR land, or
445
448
  // a proven zero-commit no-op (handled inside announceBranchLanded, #1620); an unconfirmed
@@ -450,7 +453,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
450
453
  // i.e. the serialized writes this issue is about — so it runs off the settle hot path.
451
454
  if (landedNotice) {
452
455
  const notice = landedNotice;
453
- deferLandSettleSideEffect("branch-landed", () => announceBranchLanded(notice));
456
+ deferLandSettleSideEffect("branch-landed", async () => await announceBranchLanded(notice));
454
457
  }
455
458
  const gateSkipped = normalizeGateSkipped(mergeResult.gateSkipped);
456
459
  if (gateSkipped && landedWorkspace) {
@@ -462,7 +465,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
462
465
  },
463
466
  });
464
467
  const ws = landedWorkspace;
465
- deferLandSettleSideEffect("land-gate-skipped", () => notifyLandGateSkipped({ workspace: ws, ...gateSkipped }));
468
+ deferLandSettleSideEffect("land-gate-skipped", async () => await notifyLandGateSkipped({ workspace: ws, ...gateSkipped }));
466
469
  }
467
470
  const gateWarnings = mergeResult.merged === true ? normalizeGateWarnings(mergeResult.gateWarnings) : [];
468
471
  if (gateWarnings.length && landedWorkspace) {
@@ -500,7 +503,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
500
503
  console.warn(`land-gate-warnings artifact upload failed for ${warnWorkspace.id}: ${String(error)}`);
501
504
  }
502
505
  try {
503
- notifyLandGateWarnings({ workspace: warnWorkspace, warnings: gateWarnings, artifactId, fullOutput: !anyTruncated });
506
+ await notifyLandGateWarnings({ workspace: warnWorkspace, warnings: gateWarnings, artifactId, fullOutput: !anyTruncated });
504
507
  } catch (error) {
505
508
  console.warn(`land-gate-warnings notify threw for ${warnWorkspace.id}: ${String(error)}`);
506
509
  }
@@ -537,6 +540,11 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
537
540
  if (await settleRestartKilledLand(command, current)) markTransitionCompleted();
538
541
  return { settled: true };
539
542
  }
543
+ if (!alreadyLanded && isRecord(command.result) && command.result.landingBlockerCode === DELEGATED_LAND_REFUSAL_CODE) {
544
+ await refuseWorkspaceLand(current, reason, command.id);
545
+ markTransitionCompleted();
546
+ return { settled: true };
547
+ }
540
548
  const transitioned = await updateWorkspaceStatusAsync(command.correlationId, nextStatus, {
541
549
  mergeCommandId: command.id,
542
550
  landGateQueue: undefined,
@@ -553,12 +561,12 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
553
561
  // announcements off it, so they run off the settle hot path (same as the success path's fan-out).
554
562
  if (nextStatus === "conflict" && !alreadyLanded) {
555
563
  // #2704 — the settle is the one conflict entry that never woke a steward (case A): give it its resolver.
556
- deferLandSettleSideEffect("land-conflict-resolver", () => superviseLandConflictLater(current.id, reason, landedHead ? { tip: landedHead } : {}));
557
- deferLandSettleSideEffect("land-conflict-semantic", () => emitWorkspaceTaskSemanticNotifications({
564
+ deferLandSettleSideEffect("land-conflict-semantic", async () => await emitWorkspaceTaskSemanticNotifications({
558
565
  workspace: current, transition: "land-conflict", actor: "workspace-merge",
559
566
  idempotencyKey: landFailureEventKey(current, command.id),
560
567
  outcome: { reason, commandId: command.id, status: nextStatus },
561
568
  }));
569
+ deferLandSettleSideEffect("land-conflict-resolver", () => superviseLandConflictLater(current.id, reason, landedHead ? { tip: landedHead } : {}));
562
570
  }
563
571
  if (gateFailure) {
564
572
  const gateFailedAt = Date.now();
@@ -572,9 +580,9 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
572
580
  });
573
581
  } else if (!alreadyLanded && nextStatus !== "conflict") {
574
582
  // A conflict's owner hears from its resolver instead: the steward hand-off, or the hand-back (#2704).
575
- deferLandSettleSideEffect("land-failed", () => {
576
- notifyBranchLandFailed({ workspace: current, commandId: command.id, reason });
577
- notifyLandUnconfirmed({ workspace: current, commandId: command.id, targetRef: current.baseRef ?? "main", kind: "merge-failed", detail: reason });
583
+ deferLandSettleSideEffect("land-failed", async () => {
584
+ await notifyBranchLandFailed({ workspace: current, commandId: command.id, reason });
585
+ await notifyLandUnconfirmed({ workspace: current, commandId: command.id, targetRef: current.baseRef ?? "main", kind: "merge-failed", detail: reason });
578
586
  });
579
587
  }
580
588
  return { settled: true };