@humwick/server 0.130.2 → 0.130.3

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 (570) 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 +73 -4
  8. package/package.json +8 -7
  9. package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-Bq9kr21N.js} +2 -2
  10. package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-Dqiroy06.js} +2 -2
  11. package/public/assets/{TraceView-Wimm0Czv.js → TraceView-DYU962-H.js} +2 -2
  12. package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DhPfzioE.js} +2 -2
  13. package/public/assets/{activity-CrSurn-r.js → activity-DzCcQTee.js} +2 -2
  14. package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-SgDt9i3T.js} +2 -2
  15. package/public/assets/agent-liveness-Bq-s73jW.js +2 -0
  16. package/public/assets/{agent-name-DKbiIhBo.js → agent-name-CcY3Aijy.js} +2 -2
  17. package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-cxtRBn6O.js} +2 -2
  18. package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-DGYbIhaI.js} +2 -2
  19. package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-DHbftV0N.js} +2 -2
  20. package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CY113sp-.js} +2 -2
  21. package/public/assets/{agents-B-v66C-4.js → agents-BPzY7IlQ.js} +2 -2
  22. package/public/assets/{analytics-BqCb-LKi.js → analytics-DENFPv_a.js} +2 -2
  23. package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-KRvSc7sl.js} +2 -2
  24. package/public/assets/{artifacts-DJevXcvg.js → artifacts-C9gSmL3s.js} +2 -2
  25. package/public/assets/{artifacts-D32zyw4Y.js → artifacts-DT-HkI5O.js} +2 -2
  26. package/public/assets/{automation-BpRajx_5.js → automation-BsmjQhKD.js} +2 -2
  27. package/public/assets/{backlog-D_UjTdzX.js → backlog-8NYH_62t.js} +2 -2
  28. package/public/assets/{busy-health-N3VEh4ao.js → busy-health-D3hwvYb4.js} +2 -2
  29. package/public/assets/{channels-9jopungq.js → channels-DCJ5WL7O.js} +2 -2
  30. package/public/assets/{chat-CSXrTB3C.js → chat-DAIl357U.js} +3 -3
  31. package/public/assets/{code-preview-p1NnmqK0.js → code-preview-CzrZfr9U.js} +2 -2
  32. package/public/assets/{connectors-CZdqlDNL.js → connectors-xTxtlyou.js} +2 -2
  33. package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BpebFLK2.js} +2 -2
  34. package/public/assets/{crate-agent-30aXTKva.js → crate-agent-BHvI5wXC.js} +2 -2
  35. package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-DGT0o3wJ.js} +2 -2
  36. package/public/assets/{deploys-DoN6bAb5.js → deploys-hqWOHSHE.js} +2 -2
  37. package/public/assets/{display-BrKWFGJz.js → display-CDeH4I8T.js} +2 -2
  38. package/public/assets/{files-D6XEu8Ng.js → files-BTeGVmTg.js} +2 -2
  39. package/public/assets/{fleet-C7jQWbw2.js → fleet-DgXtNgCa.js} +2 -2
  40. package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BHEt5yc_.js} +2 -2
  41. package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-MLtodyPp.js} +3 -3
  42. package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-CAe664Z8.js} +2 -2
  43. package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-mapF4Fj9.js} +2 -2
  44. package/public/assets/{index-CRHep3M9.js → index-CqqgOI5R.js} +5 -5
  45. package/public/assets/{insights-CzrtkpOE.js → insights-DNyf7IzF.js} +2 -2
  46. package/public/assets/{integrations-aVsUmigt.js → integrations-DlYOHq7T.js} +2 -2
  47. package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-CeWBNWn7.js} +2 -2
  48. package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-Cib8nqdE.js} +2 -2
  49. package/public/assets/{maintenance-NfjqFZAk.js → maintenance-DSn1cPm7.js} +2 -2
  50. package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-C3CkM81m.js} +2 -2
  51. package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-W4VQQPAv.js} +3 -3
  52. package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CMsTdhjq.js} +2 -2
  53. package/public/assets/{memory-DNsCl6UX.js → memory-BV17qtO4.js} +2 -2
  54. package/public/assets/{messages-DopU1L1X.js → messages-Bm_1AqSA.js} +2 -2
  55. package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-Dg-vfP09.js} +2 -2
  56. package/public/assets/{overview-C1DCTzzV.js → overview-DcYCrvlr.js} +2 -2
  57. package/public/assets/{pairs-DE_m6rrE.js → pairs-8o9nuG3V.js} +2 -2
  58. package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-BsRyBMea.js} +2 -2
  59. package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-DETKFn_E.js} +2 -2
  60. package/public/assets/{projects-Br2Bd2h1.js → projects-M_m6AHws.js} +2 -2
  61. package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-C_mNCgeL.js} +2 -2
  62. package/public/assets/provider-display-BEeLXC_D.js +2 -0
  63. package/public/assets/{registry-Cw38_6gW.js → registry-DDHHHE5N.js} +2 -2
  64. package/public/assets/run-card-Dt4I4jaP.js +2 -0
  65. package/public/assets/{secrets-DxI-vzSq.js → secrets-BkVwxI5O.js} +2 -2
  66. package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-Crq-6a-B.js} +2 -2
  67. package/public/assets/{settings-CKZTVGIx.js → settings-CYcavMFu.js} +2 -2
  68. package/public/assets/shipping-H6gzEeZB.js +2 -0
  69. package/public/assets/{stats-DyUBKyWI.js → stats-BJVeWkpV.js} +2 -2
  70. package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-BcHyw1Vw.js} +2 -2
  71. package/public/assets/store-DKPDIZBe.js +9 -0
  72. package/public/assets/{tasks-Bh7Chr4-.js → tasks-qJjlQPpx.js} +2 -2
  73. package/public/assets/{teams-bowI7qVY.js → teams-Cd5Sk1uX.js} +2 -2
  74. package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-CpY5_m7W.js} +3 -3
  75. package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-DI_2llFR.js} +2 -2
  76. package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-0yJA_buM.js} +2 -2
  77. package/public/assets/{use-decisions-samMQhQf.js → use-decisions-uJXHf34W.js} +2 -2
  78. package/public/assets/{use-project-list-59LheVP7.js → use-project-list-CpWl6eHQ.js} +2 -2
  79. package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-BdxBW8mL.js} +2 -2
  80. package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-3IndfpOc.js} +2 -2
  81. package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-CIVg7XB1.js} +2 -2
  82. package/public/assets/{work-queue-CSvpuNLz.js → work-queue-B9rnLwHZ.js} +2 -2
  83. package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-X8wxyhzC.js} +2 -2
  84. package/public/assets/{workflows-DdmxRSyg.js → workflows-o5YyMFW7.js} +2 -2
  85. package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CzEGoAng.js} +2 -2
  86. package/public/cosmos.html +4 -4
  87. package/public/index.html +28 -28
  88. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  89. package/runner/src/adapters/claude-headless.ts +1 -1
  90. package/runner/src/adapters/claude-launch-exit.ts +26 -0
  91. package/runner/src/adapters/claude-session-probe.ts +1 -1
  92. package/runner/src/adapters/claude-tmux.ts +38 -10
  93. package/runner/src/adapters/claude.ts +28 -75
  94. package/runner/src/adapters/provider-quota-harvest.ts +20 -71
  95. package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
  96. package/runner/src/agent-runner/liveness-exchange.ts +104 -0
  97. package/runner/src/claude-system-prompt.ts +49 -0
  98. package/runner/src/launch-assembly.ts +14 -10
  99. package/runner/src/profile-home.ts +1 -1
  100. package/runner/src/project-instructions.ts +2 -2
  101. package/runner/src/provider-command.ts +18 -0
  102. package/runner/src/provider-setup.ts +2 -1
  103. package/runner/src/runner-core.ts +14 -15
  104. package/runner/src/runner-helpers.ts +0 -8
  105. package/runner/src/session-scratch.ts +6 -2
  106. package/runner/test-support/provisioning-context.ts +4 -2
  107. package/runner/test-support/runner-harness.ts +2 -1
  108. package/scripts/steward-policy-cleanup.ts +2 -2
  109. package/src/agent-authz.ts +2 -2
  110. package/src/agent-drive-arbiter.ts +3 -8
  111. package/src/agent-exit-cleanup-async.ts +2 -2
  112. package/src/agent-exit-cleanup.ts +23 -19
  113. package/src/agent-lifecycle-events.ts +112 -85
  114. package/src/agent-ref.ts +3 -2
  115. package/src/agent-status-events.ts +43 -14
  116. package/src/auth/password.ts +4 -3
  117. package/src/auth/session-refresh.ts +1 -1
  118. package/src/auth/session.test-fixture.ts +2 -2
  119. package/src/auth/session.ts +7 -7
  120. package/src/automations/crud.ts +8 -81
  121. package/src/automations/dispatch.ts +33 -58
  122. package/src/automations/index.ts +1 -1
  123. package/src/automations/persistence.ts +65 -0
  124. package/src/automations/precheck.ts +18 -16
  125. package/src/automations/read-async.ts +11 -0
  126. package/src/automations/reconcile.ts +30 -39
  127. package/src/automations/storage.ts +33 -10
  128. package/src/automations/write-async.ts +33 -0
  129. package/src/branch-land-notice.ts +3 -3
  130. package/src/branch-landed.ts +25 -23
  131. package/src/bus.ts +21 -19
  132. package/src/cd-deploy-notify.ts +2 -2
  133. package/src/ci/notifications.ts +4 -4
  134. package/src/ci/root-missing.ts +3 -3
  135. package/src/ci/transition-notify.ts +5 -5
  136. package/src/claude-app/oauth-http.ts +3 -3
  137. package/src/cli/workflow.ts +12 -3
  138. package/src/commands-db.ts +4 -4
  139. package/src/commands-write.ts +8 -0
  140. package/src/compaction-watch.ts +10 -8
  141. package/src/config-store-write.ts +2 -2
  142. package/src/config.ts +4 -11
  143. package/src/connectors.ts +10 -8
  144. package/src/context-advisory.ts +22 -21
  145. package/src/db/agent-action-write.ts +36 -0
  146. package/src/db/agent-control-async.ts +9 -0
  147. package/src/db/agent-control-write.ts +49 -0
  148. package/src/db/agent-effort-observe.ts +22 -0
  149. package/src/db/agent-meta.ts +14 -3
  150. package/src/db/agents-async.ts +8 -5
  151. package/src/db/agents.ts +15 -12
  152. package/src/db/auth-identities.ts +2 -2
  153. package/src/db/automation-read-ops.ts +11 -0
  154. package/src/db/automation-write-ops.ts +46 -0
  155. package/src/db/blackboard.ts +2 -2
  156. package/src/db/browser-session-write.ts +32 -0
  157. package/src/db/bus-outbox-write-ops.ts +3 -1
  158. package/src/db/checkout-refresh-schema.ts +47 -0
  159. package/src/db/checkout-refresh.ts +5 -5
  160. package/src/db/command-agent-profile-backfill.ts +39 -0
  161. package/src/db/command-agent-profiles.ts +5 -1
  162. package/src/db/command-retention.ts +126 -0
  163. package/src/db/commands-planner-stats.test-fixture.ts +8 -0
  164. package/src/db/commands-read-ops.ts +16 -0
  165. package/src/db/connection.ts +1 -182
  166. package/src/db/context-carry-write.ts +74 -0
  167. package/src/db/continuations.ts +4 -4
  168. package/src/db/db-error-registry.ts +12 -0
  169. package/src/db/gateway.ts +26 -133
  170. package/src/db/history-retention-ops.ts +13 -0
  171. package/src/db/inbox-async.ts +2 -2
  172. package/src/db/inbox-read-ops.ts +1 -1
  173. package/src/db/inbox.ts +9 -3
  174. package/src/db/issue-associations.ts +2 -2
  175. package/src/db/issue-refs-async.ts +4 -1
  176. package/src/db/issue-refs-read-ops.ts +6 -0
  177. package/src/db/issue-refs.ts +8 -8
  178. package/src/db/kernel-event-log-schema.ts +2 -0
  179. package/src/db/land-boundaries.ts +51 -5
  180. package/src/db/land-gate-attestations.ts +2 -2
  181. package/src/db/landing-receipts.ts +2 -2
  182. package/src/db/lifecycle-context-write.ts +5 -0
  183. package/src/db/lifecycle-tail-async.ts +76 -0
  184. package/src/db/lifecycle-tail-write-ops.ts +62 -0
  185. package/src/db/lifecycle-write-window.ts +2 -1
  186. package/src/db/lineage-pair-dedupe-sql.ts +3 -3
  187. package/src/db/main-connection-mode.ts +11 -0
  188. package/src/db/main-connection-writes.ts +20 -2
  189. package/src/db/maintenance-job-runs-async.ts +2 -2
  190. package/src/db/maintenance-job-write-ops.ts +1 -1
  191. package/src/db/maintenance-pauses.ts +2 -2
  192. package/src/db/message-send.ts +4 -0
  193. package/src/db/messages.ts +31 -25
  194. package/src/db/migrations.ts +18 -1
  195. package/src/db/notification-write-ops.ts +19 -0
  196. package/src/db/operator-answer-write.ts +19 -0
  197. package/src/db/orchestrators.ts +10 -4
  198. package/src/db/pairs-async.ts +22 -1
  199. package/src/db/pairs-write-ops.ts +8 -3
  200. package/src/db/pairs.ts +41 -48
  201. package/src/db/plan-dispatch-schema.ts +5 -1
  202. package/src/db/plan-ready-notices.ts +43 -0
  203. package/src/db/plan-tools-include-migration.ts +36 -0
  204. package/src/db/plans-write-ops.ts +2 -0
  205. package/src/db/presence-buffer.ts +5 -0
  206. package/src/db/presence-loop.ts +18 -23
  207. package/src/db/presence-write.ts +0 -6
  208. package/src/db/project-entries-async.ts +14 -0
  209. package/src/db/project-entries-read-ops.ts +5 -1
  210. package/src/db/project-entries-write-ops.ts +8 -0
  211. package/src/db/project-entries.ts +15 -12
  212. package/src/db/project-journal.ts +2 -2
  213. package/src/db/project-trust-approvals.ts +2 -2
  214. package/src/db/prompt-templates.ts +4 -4
  215. package/src/db/read-op-marshaling-fixtures.ts +18 -0
  216. package/src/db/read-ops.ts +4 -0
  217. package/src/db/retention.ts +13 -2
  218. package/src/db/review-publish-write.ts +8 -0
  219. package/src/db/runtime-admin-async.ts +19 -0
  220. package/src/db/runtime-admin-write-ops.ts +36 -0
  221. package/src/db/runtime-tail-async.ts +288 -0
  222. package/src/db/runtime-tail-write-ops.ts +97 -0
  223. package/src/db/schema.ts +4 -1
  224. package/src/db/spawn-budget.ts +5 -0
  225. package/src/db/spawn-delegation.ts +1 -11
  226. package/src/db/sql-audit-indexes.ts +3 -0
  227. package/src/db/stats-async.ts +5 -3
  228. package/src/db/task-entity.ts +5 -4
  229. package/src/db/team-claim-policy.ts +60 -0
  230. package/src/db/teams-async.ts +4 -0
  231. package/src/db/teams-write-ops.ts +4 -0
  232. package/src/db/terminal-events.ts +15 -0
  233. package/src/db/timer-cutover-bodies.ts +292 -0
  234. package/src/db/timer-cutover-core.ts +37 -278
  235. package/src/db/timer-cutover-state.ts +40 -0
  236. package/src/db/timer-cutover-write-ops.ts +1 -40
  237. package/src/db/token-db-write-ops.ts +2 -0
  238. package/src/db/token-standing-async.ts +12 -0
  239. package/src/db/token-standing.ts +6 -9
  240. package/src/db/token-usage-retention.ts +33 -0
  241. package/src/db/token-usage-write-ops.ts +1 -1
  242. package/src/db/token-usage.ts +4 -1
  243. package/src/db/user-invites-async.ts +13 -0
  244. package/src/db/user-invites.ts +3 -3
  245. package/src/db/users.ts +14 -6
  246. package/src/db/workspaces.ts +8 -5
  247. package/src/db/write-ops.ts +31 -12
  248. package/src/db/writer-fault-seam.ts +2 -9
  249. package/src/db/writer-fault-test-ops.ts +18 -0
  250. package/src/db/writer-gateway.ts +42 -46
  251. package/src/dev.ts +8 -3
  252. package/src/event-loop-lag.ts +4 -2
  253. package/src/event-loop-yield.ts +2 -2
  254. package/src/events.ts +10 -8
  255. package/src/host-provider-store.ts +5 -4
  256. package/src/hub-metrics-report.ts +5 -1
  257. package/src/hub-metrics.ts +38 -18
  258. package/src/injection-sweep-pacing.ts +6 -8
  259. package/src/land-gate-notifications.ts +11 -10
  260. package/src/land-gate-queue-progress.ts +3 -3
  261. package/src/land-settle-dispatch.ts +52 -92
  262. package/src/lifecycle-context-pressure.ts +11 -10
  263. package/src/lifecycle-manager.ts +104 -98
  264. package/src/maintenance/breaker.ts +18 -16
  265. package/src/maintenance/checkout-refresh.ts +18 -1
  266. package/src/maintenance/critical-job-watchdog.ts +3 -3
  267. package/src/maintenance/events.ts +3 -3
  268. package/src/maintenance/history-retention-job.ts +30 -0
  269. package/src/maintenance/jobs.ts +29 -67
  270. package/src/maintenance/long-running-work.ts +3 -1
  271. package/src/maintenance/offline-agent-prune-job.ts +28 -0
  272. package/src/maintenance/orphaned-sessions.ts +5 -4
  273. package/src/maintenance/pauses.ts +4 -10
  274. package/src/maintenance/plan-ready-notice-job.ts +71 -0
  275. package/src/maintenance/scheduler.ts +18 -13
  276. package/src/maintenance/stalled-delivery-pager.ts +4 -4
  277. package/src/maintenance/teams.ts +19 -30
  278. package/src/maintenance/workspaces.ts +17 -27
  279. package/src/managed-policy-escalation.ts +7 -7
  280. package/src/managed-policy.ts +3 -3
  281. package/src/managed-provider-gate.ts +5 -4
  282. package/src/managed-spawn-reservation.ts +5 -4
  283. package/src/mcp/index.ts +8 -8
  284. package/src/mcp/output-descriptions.ts +1 -1
  285. package/src/mcp/presentation.ts +2 -1
  286. package/src/mcp/runtime-output.ts +6 -1
  287. package/src/mcp/self-care-visibility.ts +2 -2
  288. package/src/mcp/spawn-targets-matrix.ts +10 -1
  289. package/src/mcp/tool-groups.ts +3 -2
  290. package/src/mcp/tool-roles.ts +81 -0
  291. package/src/mcp/tools-workspace.ts +1 -1
  292. package/src/mcp-blackboard-tools.ts +6 -5
  293. package/src/mcp-decision-tools.ts +7 -7
  294. package/src/mcp-egress-tools.ts +10 -9
  295. package/src/mcp-issue-activity-tools.ts +2 -2
  296. package/src/mcp-issue-association-tools.ts +4 -4
  297. package/src/mcp-issue-ref-tools.ts +3 -3
  298. package/src/mcp-merge-tools.ts +5 -5
  299. package/src/mcp-notification-subscription-tools.ts +14 -15
  300. package/src/mcp-output-levels.test-fixture.ts +1 -1
  301. package/src/mcp-profile-trust-tools.ts +1 -1
  302. package/src/mcp-project-journal-tools.ts +4 -12
  303. package/src/mcp-project-tools.ts +17 -18
  304. package/src/mcp-routing-policy-tools.ts +3 -2
  305. package/src/mcp-team-tools.ts +6 -6
  306. package/src/mcp-workflow-tools.ts +5 -3
  307. package/src/memory-area-injection.ts +13 -12
  308. package/src/memory-boundary-capture.ts +6 -6
  309. package/src/memory-injection.ts +45 -43
  310. package/src/merge-pause-maintenance.ts +5 -4
  311. package/src/notification-router.ts +44 -17
  312. package/src/notify.ts +5 -5
  313. package/src/observed-reset-refresh.ts +4 -4
  314. package/src/operator-alarm.ts +2 -5
  315. package/src/project-ingest.ts +6 -6
  316. package/src/project-instructions.ts +8 -1
  317. package/src/project-trust.ts +12 -11
  318. package/src/prompt-resolver.ts +3 -3
  319. package/src/provider-quota-async.ts +5 -7
  320. package/src/provider-timeline-event.ts +4 -7
  321. package/src/quota-band-delivery.ts +2 -2
  322. package/src/recipe-runner.ts +4 -3
  323. package/src/retention-config-store.ts +4 -3
  324. package/src/reviewer-pipeline.ts +11 -10
  325. package/src/routes/_route.ts +4 -4
  326. package/src/routes/_shared.ts +13 -12
  327. package/src/routes/agent-effort.ts +5 -5
  328. package/src/routes/agent-heartbeat-quota.ts +23 -0
  329. package/src/routes/agent-keep.ts +1 -1
  330. package/src/routes/agent-reporting.ts +1 -1
  331. package/src/routes/agent-self-effort.ts +5 -5
  332. package/src/routes/agent-sessions.ts +24 -24
  333. package/src/routes/agents-spawn.ts +1 -1
  334. package/src/routes/agents.ts +17 -119
  335. package/src/routes/auth.ts +14 -15
  336. package/src/routes/authority-audit.ts +2 -2
  337. package/src/routes/automations.ts +6 -5
  338. package/src/routes/channel-binding-delete.ts +1 -1
  339. package/src/routes/channel-output.ts +3 -3
  340. package/src/routes/claude-api-budget.ts +5 -5
  341. package/src/routes/commands-listing.ts +1 -1
  342. package/src/routes/commands.ts +29 -27
  343. package/src/routes/context-resets.ts +2 -2
  344. package/src/routes/coordinator-teams.ts +1 -1
  345. package/src/routes/decisions.ts +2 -2
  346. package/src/routes/host-secret-files.ts +1 -1
  347. package/src/routes/hosts.ts +11 -11
  348. package/src/routes/inbox.ts +3 -2
  349. package/src/routes/index.ts +2 -1
  350. package/src/routes/integrations.ts +9 -7
  351. package/src/routes/issue-activity.ts +2 -2
  352. package/src/routes/issue-associations.ts +4 -4
  353. package/src/routes/issue-refs.ts +3 -3
  354. package/src/routes/kernel-timers-operator.ts +1 -1
  355. package/src/routes/memory.ts +1 -1
  356. package/src/routes/merges.ts +11 -11
  357. package/src/routes/messages.ts +23 -17
  358. package/src/routes/orchestrator-bootstrap.ts +1 -1
  359. package/src/routes/orchestrator-host-registration.ts +2 -0
  360. package/src/routes/orchestrator-host-utilization.ts +2 -2
  361. package/src/routes/orchestrator-proxy.ts +2 -2
  362. package/src/routes/orchestrator.ts +27 -18
  363. package/src/routes/pairs.ts +8 -8
  364. package/src/routes/project-profile-instructions.ts +1 -1
  365. package/src/routes/project-trust.ts +1 -1
  366. package/src/routes/projects.ts +16 -16
  367. package/src/routes/prompt-templates.ts +12 -12
  368. package/src/routes/provider-quotas.ts +10 -10
  369. package/src/routes/run-command.ts +1 -1
  370. package/src/routes/secrets-backup.ts +2 -2
  371. package/src/routes/secrets-import.ts +1 -1
  372. package/src/routes/secrets-vault.ts +7 -7
  373. package/src/routes/settings.ts +1 -1
  374. package/src/routes/shipping.ts +9 -1
  375. package/src/routes/spawn-policy.ts +8 -7
  376. package/src/routes/sse.ts +2 -2
  377. package/src/routes/team-templates.ts +7 -7
  378. package/src/routes/teams.ts +10 -10
  379. package/src/routes/tokens.ts +18 -17
  380. package/src/routes/users.ts +24 -24
  381. package/src/routes/viewer-read.ts +3 -3
  382. package/src/routes/web-tool-calls.ts +4 -4
  383. package/src/routes/workflows.ts +2 -2
  384. package/src/routes/workspaces.ts +17 -16
  385. package/src/routing-policy-push.ts +6 -6
  386. package/src/runtime-grant.ts +17 -11
  387. package/src/runtime-tokens.ts +20 -28
  388. package/src/secrets-broker/config.ts +1 -1
  389. package/src/secrets-broker/service.ts +2 -2
  390. package/src/security.ts +3 -1
  391. package/src/self-resume-carry.ts +21 -110
  392. package/src/self-resume-supersede.ts +4 -3
  393. package/src/self-resume.ts +54 -66
  394. package/src/server-background.ts +1 -1
  395. package/src/server.ts +21 -22
  396. package/src/services/agent-action.ts +12 -31
  397. package/src/services/agent-effort.ts +7 -32
  398. package/src/services/agent-hang-watchdog-job.ts +2 -2
  399. package/src/services/agent-heartbeat.ts +166 -0
  400. package/src/services/agent-reporting.ts +1 -1
  401. package/src/services/browser-inventory.ts +3 -3
  402. package/src/services/browser-session-state.ts +3 -2
  403. package/src/services/browser-session.ts +21 -27
  404. package/src/services/bus-command-update.ts +9 -9
  405. package/src/services/checkout-refresh-state.ts +1 -0
  406. package/src/services/checkout-refresh.test-fixture.ts +3 -2
  407. package/src/services/command-host.ts +7 -2
  408. package/src/services/coordinator-digest.ts +3 -2
  409. package/src/services/dispatch-command.ts +12 -13
  410. package/src/services/effective-launch-driver.ts +14 -9
  411. package/src/services/host-changed-paths.test-fixture.ts +5 -6
  412. package/src/services/host-project-readiness.ts +2 -2
  413. package/src/services/host-providers.ts +1 -1
  414. package/src/services/host-utilization.ts +9 -9
  415. package/src/services/idle-agent-digest.ts +17 -12
  416. package/src/services/idle-epics.ts +4 -4
  417. package/src/services/interrupt-agent.ts +12 -9
  418. package/src/services/issue-activity.ts +3 -3
  419. package/src/services/issue-associations.ts +15 -18
  420. package/src/services/issue-lifecycle-comments.ts +3 -2
  421. package/src/services/issue-lifecycle.ts +14 -13
  422. package/src/services/issue-refs.ts +4 -5
  423. package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
  424. package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
  425. package/src/services/kernel-timers-ingress.ts +3 -3
  426. package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
  427. package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
  428. package/src/services/kernel-timers-outbox.ts +1 -1
  429. package/src/services/kernel-timers-stage10-operator.ts +1 -1
  430. package/src/services/kernel-timers.ts +4 -3
  431. package/src/services/land-gate-placement.ts +11 -2
  432. package/src/services/land-issue-relations.ts +5 -4
  433. package/src/services/land-restart-retry.ts +7 -6
  434. package/src/services/landed-ancestry.ts +6 -5
  435. package/src/services/legacy-landing-host.ts +4 -1
  436. package/src/services/legacy-landing-recovery.ts +8 -6
  437. package/src/services/managed-running.ts +17 -17
  438. package/src/services/memory-areas.ts +3 -3
  439. package/src/services/merge-pause.ts +9 -15
  440. package/src/services/operator-decisions.ts +37 -41
  441. package/src/services/orchestrator-registration-recovery.ts +6 -2
  442. package/src/services/orchestrator-upgrade.ts +14 -12
  443. package/src/services/permission-decision.ts +4 -3
  444. package/src/services/plan-ci-notice.ts +1 -1
  445. package/src/services/plan-conformance.ts +4 -4
  446. package/src/services/plan-dispatch-policy.ts +10 -3
  447. package/src/services/plan-dispatch.ts +35 -14
  448. package/src/services/plan-graph.ts +39 -261
  449. package/src/services/plan-import.ts +1 -1
  450. package/src/services/plan-live-bindings.ts +8 -37
  451. package/src/services/plan-task-create.ts +1 -1
  452. package/src/services/plan-terminal-refs.ts +2 -12
  453. package/src/services/plan-wake-digest.ts +8 -3
  454. package/src/services/profile-trust.ts +2 -2
  455. package/src/services/project.ts +11 -10
  456. package/src/services/prompt-templates.ts +6 -5
  457. package/src/services/provider-hold-nudge.ts +9 -9
  458. package/src/services/provider-hold-record.ts +13 -12
  459. package/src/services/provider-state-audit.ts +19 -18
  460. package/src/services/provisioning-harness/driver.ts +1 -1
  461. package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
  462. package/src/services/provisioning-harness/lifecycle.ts +24 -22
  463. package/src/services/provisioning-harness/prepare.ts +16 -17
  464. package/src/services/rate-limit-resume.ts +10 -9
  465. package/src/services/red-main-auto-revert.ts +7 -5
  466. package/src/services/red-main-controller-deps.ts +20 -3
  467. package/src/services/red-main-incident.ts +3 -1
  468. package/src/services/red-main-quarantine.ts +2 -3
  469. package/src/services/register-agent.ts +22 -22
  470. package/src/services/restart-project-role.ts +1 -1
  471. package/src/services/restart-spawn.ts +17 -16
  472. package/src/services/review-publish.ts +7 -24
  473. package/src/services/review-ref-reconcile.ts +29 -38
  474. package/src/services/runtime-dependency-alert.ts +27 -0
  475. package/src/services/scheduler-command.ts +11 -9
  476. package/src/services/scheduler.ts +5 -5
  477. package/src/services/self-effort-grant.ts +5 -25
  478. package/src/services/send-message.ts +16 -33
  479. package/src/services/shutdown-agent.ts +17 -14
  480. package/src/services/spawn-agent.ts +12 -41
  481. package/src/services/spawn-chain-inspector.ts +1 -1
  482. package/src/services/spawn-cwd-placement.ts +21 -5
  483. package/src/services/spawn-issue-activity.ts +8 -6
  484. package/src/services/spawn-launch.ts +17 -1
  485. package/src/services/spawn-loss-escalation.ts +1 -1
  486. package/src/services/spawn-registration-watchdog.ts +2 -2
  487. package/src/services/spawn-repo-probe.ts +7 -5
  488. package/src/services/spawn-tree-cleanup.ts +7 -8
  489. package/src/services/steward-reaper.ts +4 -4
  490. package/src/services/task-context-hydration.ts +25 -1
  491. package/src/services/task-delivery.ts +20 -34
  492. package/src/services/task-entity.ts +40 -203
  493. package/src/services/task-issue-link.ts +13 -20
  494. package/src/services/task-issue-weave.ts +12 -11
  495. package/src/services/task-lifecycle.ts +27 -31
  496. package/src/services/task-repo-cwd.ts +37 -0
  497. package/src/services/task-review-hold.ts +4 -17
  498. package/src/services/task-semantic-events.ts +13 -11
  499. package/src/services/task-state-reconcile.ts +3 -4
  500. package/src/services/task-validation.ts +2 -2
  501. package/src/services/team-templates.ts +5 -5
  502. package/src/services/team.ts +31 -98
  503. package/src/services/terminal-provider-exit.ts +6 -5
  504. package/src/services/timer-message-fire-chaos-worker.ts +7 -3
  505. package/src/services/timer-message-fire-outbox.ts +1 -1
  506. package/src/services/transient-agent-reaper.ts +22 -19
  507. package/src/services/turn-reconcile-audit.ts +9 -9
  508. package/src/services/user-onboarding.ts +8 -6
  509. package/src/services/user-profile.ts +6 -9
  510. package/src/services/work-finality.ts +16 -4
  511. package/src/services/workflow-auto-start.ts +2 -1
  512. package/src/services/workflow-caller.ts +61 -4
  513. package/src/services/workflow-child-proof-worker.ts +1 -1
  514. package/src/services/workflow-control-proof-worker.ts +1 -1
  515. package/src/services/workflow-coordinator.ts +8 -2
  516. package/src/services/workflow-decisions.ts +5 -5
  517. package/src/services/workflow-deferred-cleanup.ts +1 -1
  518. package/src/services/workflow-effect-driver.ts +42 -19
  519. package/src/services/workflow-effects.ts +41 -27
  520. package/src/services/workflow-pins.ts +7 -4
  521. package/src/services/workflow-reconcile.ts +1 -0
  522. package/src/services/workflow-resume-pins.ts +3 -3
  523. package/src/services/workflow-routing-defaults.ts +18 -0
  524. package/src/services/workflow-run-view.ts +4 -3
  525. package/src/services/workflow-run.test-fixture.ts +1 -1
  526. package/src/services/workflow-runs.ts +34 -22
  527. package/src/services/workflow-supervisor-state.ts +21 -1
  528. package/src/services/workflow-supervisor.ts +4 -3
  529. package/src/services/workspace-recovery.ts +4 -3
  530. package/src/spawn-admission.ts +10 -2
  531. package/src/spawn-placement.ts +27 -9
  532. package/src/spawn-targets.ts +8 -8
  533. package/src/sse-agent-status.ts +15 -6
  534. package/src/sse.ts +49 -24
  535. package/src/startup-context-injection.ts +61 -57
  536. package/src/steward-handoff.ts +4 -3
  537. package/src/steward-policy-cleanup.ts +9 -8
  538. package/src/steward.ts +2 -2
  539. package/src/task-dispatch-live.ts +5 -2
  540. package/src/task-dispatcher.ts +2 -2
  541. package/src/token-db.ts +2 -2
  542. package/src/token-usage-async.ts +2 -4
  543. package/src/user-awaiting-reply.ts +5 -5
  544. package/src/workspace-actions.ts +24 -29
  545. package/src/workspace-auto-merge.ts +14 -12
  546. package/src/workspace-changed-paths-client.ts +16 -5
  547. package/src/workspace-cleanup-stale.ts +30 -7
  548. package/src/workspace-command-settle.ts +24 -16
  549. package/src/workspace-config.ts +1 -1
  550. package/src/workspace-conflict-resolver.ts +9 -7
  551. package/src/workspace-dead-owner-recovery.ts +5 -4
  552. package/src/workspace-escalation.ts +12 -11
  553. package/src/workspace-human-escalation.ts +3 -3
  554. package/src/workspace-land-gate-watchdog.ts +3 -3
  555. package/src/workspace-land-refusal.ts +20 -0
  556. package/src/workspace-land-stopped.ts +1 -0
  557. package/src/workspace-merge.ts +10 -26
  558. package/src/workspace-nothing-to-land-notice.ts +2 -2
  559. package/src/workspace-orphans.ts +18 -37
  560. package/src/workspace-pr-completion.ts +10 -8
  561. package/src/workspace-rekey.ts +3 -3
  562. package/src/workspace-steward-failure.ts +2 -2
  563. package/src/workspace-steward-wake.ts +2 -2
  564. package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
  565. package/public/assets/provider-display-DsuWMvVL.js +0 -2
  566. package/public/assets/run-card-CpKqRbhg.js +0 -2
  567. package/public/assets/shipping-B9YtnwtG.js +0 -2
  568. package/public/assets/store-DcClXLH0.js +0 -9
  569. package/runner/src/agent-runner/http-liveness.ts +0 -85
  570. package/src/db/on-loop-write-transaction.ts +0 -34
@@ -1,9 +1,10 @@
1
+ import { taskLinkedRepoCwd } from "./task-repo-cwd";
2
+ export { taskLinkedRepoCwd } from "./task-repo-cwd";
1
3
  import { writeChildEnv } from "@humwick/sdk/env";
2
4
  import { bindSpawnProjectCheckout } from "./project-checkout-binding";
3
- import { REPORT_VERBOSITIES, resolveReportVerbosity } from "@humwick/sdk";
5
+ import { REPORT_VERBOSITIES, resolveReportVerbosity, DEFAULT_USER_ID, errMessage, isRecord, stringValue, type LaunchMode, type ReportVerbosity, type SpawnProvider, type TaskDispatch } from "@humwick/sdk";
4
6
  import { cleanCallerSpawnEnv } from "../spawn-env";
5
- import { optionalEnum } from "../validation";
6
- import { optionalNonNegativeInt } from "../validation";
7
+ import { optionalEnum, optionalNonNegativeInt } from "../validation";
7
8
  import { cleanSpawnBudget } from "../spawn-budget";
8
9
  import { teamId as newTeamId } from "../db/teams";
9
10
  import { getGateway } from "../db/gateway";
@@ -35,8 +36,7 @@ import type { WorkflowSpawnQuota } from "../db/spawn-in-flight";
35
36
  // deliberately out of scope here — this slice converges the two external TRANSPORTS.
36
37
  import { resolveSpawnApprovalMode } from "./spawn-approval";
37
38
  import { assertOrchestratorSupportsLaunchMode, resolveSpawnLaunchMode } from "../launch-mode";
38
- import { DEFAULT_USER_ID, errMessage, isRecord, stringValue, type LaunchMode, type ReportVerbosity, type SpawnProvider, type TaskDispatch } from "@humwick/sdk";
39
- import { addProjectRootAsync, countLiveSpawnedAgents, createActivityEventAsync, getAgent, getProject, getTaskDetailAsync, listAgents, listProjects, listWorkspaces, recordTaskSpawnRequestedAsync, reportedRepoRootForCwd, resolveAgentOwnershipAsync, resolveProjectForCwd, rootsForProject, ValidationError, withResolvedProvisioningAsync } from "../db";
39
+ import { addProjectRootAsync, countLiveSpawnedAgents, createActivityEventAsync, getAgent, getProject, listAgents, listWorkspaces, recordTaskSpawnRequestedAsync, reportedRepoRootForCwd, resolveAgentOwnershipAsync, resolveProjectForCwd, rootsForProject, ValidationError, withResolvedProvisioningAsync } from "../db";
40
40
  import { acquireCoordinatorTeam } from "./team";
41
41
  import { countInFlightSpawnCommands } from "../commands-db";
42
42
  import { createSpawnCommandWithinQuotaAsync } from "../commands-write";
@@ -54,7 +54,7 @@ import { profileNotFoundMessage } from "../spawn-profile-errors";
54
54
  import { composeSpawnInstructionsWithEventsAsync } from "../project-instructions";
55
55
  import { applyDefaultSharedMcp, type SharedMcpSpawnOverride } from "../shared-mcp";
56
56
  import { findGitRepoRoot, isInsideGitWorkTree, pathVisibleLocally } from "./canonical-root";
57
- import { selectSpawnCwdPlacement } from "./spawn-cwd-placement";
57
+ import { selectSpawnCwdPlacement, spawnBuildWork } from "./spawn-cwd-placement";
58
58
  import { isolatedSpawnGitRepoError, resolveSpawnRepoRoot } from "./spawn-repo-probe";
59
59
  import { autoSeedProjectForCwdAsync } from "./project"; import { applyProjectCapabilityGates } from "../project-trust";
60
60
  import { assertProjectRoleMcpCwd, assertProjectRoleMcpTrusted, projectRoleProfile, projectRoleProvenance, projectRoleSpawnDefaults, withRoleProfile } from "../project-roles";
@@ -152,35 +152,6 @@ export function acquisitionManifestFor(projectId: string, cwd: string): {
152
152
  };
153
153
  }
154
154
 
155
- export async function taskLinkedRepoCwd(taskId: number, callerCwd?: string): Promise<string> {
156
- const task = await getTaskDetailAsync(taskId);
157
- if (!task) throw new ValidationError(`task ${taskId} not found or not a deliverable task`);
158
- // #2844 — a Task created with `repo:` but no linked issue still names its project repo.
159
- const repos = [...new Set([...task.issues.map((issue) => issue.repo), task.repo].filter((repo): repo is string => Boolean(repo)))];
160
- if (repos.length === 0) {
161
- throw new ValidationError(`task ${taskId} has no linked issue repo; pass cwd/orchestratorId explicitly`);
162
- }
163
- const candidates = listProjects()
164
- .filter((project) => project.issueRepo && repos.includes(project.issueRepo))
165
- .flatMap((project) => rootsForProject(project.id).map((root) => ({ project, root })));
166
- if (candidates.length === 0) {
167
- // #1627 — this refusal is real and names its own cause: with no registered root, relay has no
168
- // way to turn "repo owner/name" into a checkout path on a specific host. What made it a trap
169
- // was the NEXT step: following its advice (pass cwd + orchestratorId) used to hit the isolated
170
- // precondition's local-filesystem probe and get told the repo was "not a git repo". That lie is
171
- // fixed at assertIsolatedSpawnHasGitRepo, so the remedy below now works. Name the tool so it is
172
- // mechanical rather than a step you have to know exists.
173
- throw new ValidationError(`task ${taskId} links repo ${repos.join(", ")} but no Relay project root is registered for it, so relay cannot resolve a checkout path for it; pass cwd + orchestratorId explicitly, or register the checkout with relay_project_add_root`);
174
- }
175
- const matchingCallerRoot = callerCwd
176
- ? candidates
177
- .filter((candidate) => isPathWithinBase(callerCwd, candidate.root.rootPath))
178
- .sort((a, b) => b.root.rootPath.length - a.root.rootPath.length)[0]
179
- : undefined;
180
- if (matchingCallerRoot) return matchingCallerRoot.root.rootPath;
181
- if (candidates.length === 1) return candidates[0]!.root.rootPath;
182
- throw new ValidationError(`task ${taskId} links repo ${repos.join(", ")} with multiple registered roots; pass cwd explicitly`);
183
- }
184
155
 
185
156
  /**
186
157
  * A normalized spawn request — every transport validates its own wire shape (enums,
@@ -390,7 +361,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
390
361
  const lineageAgent: AgentCard | undefined = (lineageAgentId ? getAgent(lineageAgentId) : undefined) ?? undefined;
391
362
  const callerCwd = stringValue(caller?.meta?.cwd) ?? stringValue(lineageAgent?.meta?.cwd);
392
363
  const taskDefaultCwd = input.taskId !== undefined && (!input.orchestratorId || input.role !== undefined) && !input.cwd
393
- ? input.taskRepoCwd ?? await taskLinkedRepoCwd(input.taskId, callerCwd)
364
+ ? input.taskRepoCwd ?? await taskLinkedRepoCwd(input.taskId, callerCwd, caller ?? lineageAgent)
394
365
  : undefined;
395
366
  const requestedCwd = input.cwd ?? taskDefaultCwd ?? (input.role !== undefined ? callerCwd : undefined);
396
367
  const roleSpawn = input.roleSnapshot ? { role: input.roleSnapshot, warnings: [] } : input.role === undefined && input.roleSha === undefined ? undefined : await resolveSpawnProjectRole({ role: input.role, roleSha: input.roleSha, profile: input.profile, orchestratorId: input.orchestratorId, cwd: requestedCwd, preferHost: input.roleHost ?? caller?.machine ?? lineageAgent?.machine, allowedOrchestratorIds: ctx.constraints?.orchestrators, canSpawn: canSpawn(ctx) });
@@ -429,7 +400,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
429
400
  provider: resolvedProvider, orchestratorId: input.orchestratorId, requestedCwd: repoSource ? undefined : requestedCwd, repoSource,
430
401
  resumeWorkspace: input.resumeWorkspace, caller, lineageMachine: lineageAgent?.machine,
431
402
  callerCwd, allowedOrchestratorIds: ctx.constraints?.orchestrators,
432
- shared: workspaceMode === "shared", workspaceMode, canLand: !landBlockResolution.landBlocked, model: modelParams.model, effort: modelParams.effort,
403
+ shared: workspaceMode === "shared", workspaceMode, canLand: !landBlockResolution.landBlocked, buildWork: spawnBuildWork(profileName, (input.dispatch ?? linkedTask?.dispatch ?? profileSpawnDefaults.dispatch)?.type), model: modelParams.model, effort: modelParams.effort,
433
404
  });
434
405
 
435
406
  assertOrchestratorSupportsLaunchMode(orchestrator, launchMode, input.providerArgs);
@@ -486,7 +457,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
486
457
 
487
458
  if (shouldAutoScaffoldTask(input, profileName)) {
488
459
  const brief = spawnBrief(input)!;
489
- linkedTask = createTaskEntity({
460
+ linkedTask = await createTaskEntity({
490
461
  title: taskTitleFromBrief(brief, input.label),
491
462
  intent: brief,
492
463
  latestSteer: input.steer,
@@ -544,7 +515,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
544
515
  // disk. That's only trustworthy when the spawn's target orchestrator actually shares this
545
516
  // filesystem — a remote orchestrator's host can coincidentally have the same path with
546
517
  // DIFFERENT content, so the gate is told explicitly rather than inferring from path existence.
547
- if (agentProfile.sources?.project) agentProfile = applyProjectCapabilityGates(agentProfile, resolvedCwd, registeredProjectBeforeAutoSeed, { orchestratorLocal: isOrchestratorLocalToRelay(orchestrator), ...(projectRole ? { pinnedMcpCommit: projectRole.source.sha } : {}) });
518
+ if (agentProfile.sources?.project) agentProfile = (await applyProjectCapabilityGates(agentProfile, resolvedCwd, registeredProjectBeforeAutoSeed, { orchestratorLocal: isOrchestratorLocalToRelay(orchestrator), ...(projectRole ? { pinnedMcpCommit: projectRole.source.sha } : {}) }));
548
519
  if (projectRole) assertProjectRoleMcpTrusted(projectRole, agentProfile);
549
520
 
550
521
  // N7 — project hooks/MCP are executable code and are gated against the project
@@ -599,7 +570,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
599
570
  }
600
571
 
601
572
  // #3107 — the lineage coordinator's active team; a coordinator takes its project's seat, never a dissolved id.
602
- const callerTeam = lineageAgentId ? acquireCoordinatorTeam(lineageAgentId) : undefined;
573
+ const callerTeam = lineageAgentId ? (await acquireCoordinatorTeam(lineageAgentId)) : undefined;
603
574
  const privateTeamId = profileName === "sub-coordinator" ? newTeamId() : undefined;
604
575
  const launchTeamId = privateTeamId ?? callerTeam?.id;
605
576
 
@@ -885,7 +856,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
885
856
  requestedBy,
886
857
  ...(currentTask?.latestSteer ? { steer: currentTask.latestSteer } : {}),
887
858
  });
888
- autoSubscribeTaskCoordinator(requestedBy, input.taskId);
859
+ (await autoSubscribeTaskCoordinator(requestedBy, input.taskId));
889
860
  } catch (err) {
890
861
  console.warn(`task ${input.taskId} spawn attribution skipped: ${errMessage(err)}`);
891
862
  }
@@ -50,7 +50,7 @@ export interface SpawnChainInspection {
50
50
  settings: Record<string, unknown> | null;
51
51
  /** The effective launch argv the adapter would exec. */
52
52
  argv: string[];
53
- /** The launch command (claude / claude-rig). */
53
+ /** The launch command (e.g. claude). */
54
54
  command: string;
55
55
  /** relay-provisioned-hooks/<ref> dir for the baseline guard. */
56
56
  baselineHookAssetDir: string | undefined;
@@ -1,16 +1,21 @@
1
1
  import { basename, join, relative } from "node:path";
2
2
  import { hostname } from "node:os";
3
- import { normalizeGitRemoteIdentity, stringValue, type AgentCard, type Orchestrator, type SpawnProvider, type WorkspaceMode, type ProjectRoot } from "@humwick/sdk";
4
- import { listAgents, listOrchestrators, listWorkspaces, resolveProjectForCwd, rootsForProject, ValidationError } from "../db";
3
+ import { normalizeGitRemoteIdentity, stringValue, type AgentCard, type Orchestrator, type SpawnProvider, type WorkspaceMode, type ProjectRoot, type TaskDispatch } from "@humwick/sdk";
4
+ import { getOrchestrator, listAgents, listOrchestrators, listWorkspaces, resolveProjectForCwd, rootsForProject, ValidationError } from "../db";
5
+ import { listHostViews } from "../db/hosts-views";
5
6
  import { unboundOnHost, unboundProjectRoots } from "../db/project-lifecycle";
6
7
  import { isOrchestratorLocalToRelay, isPathWithinBase, sameHostIdentity } from "../utils";
7
8
  import { findGitRepoRoot } from "./canonical-root";
8
9
  import { TERMINAL_WORKSPACE_STATUSES } from "../workspace-phase";
9
- import { selectSpawnPlacement, spawnPlacementCandidates, isRelayHost } from "../spawn-placement";
10
+ import { selectSpawnPlacement, spawnPlacementCandidates, isRelayHost, type SpawnCapOptions } from "../spawn-placement";
10
11
  import { remoteUrlFromGitRemote } from "./git-remote";
11
12
  import { probeHostRepo, probeLocalSourceRepo } from "./spawn-repo-probe";
12
13
  import { hostProjectReadiness } from "./host-project-readiness";
13
14
 
15
+ export function spawnBuildWork(profile: string, type?: TaskDispatch["type"]): boolean {
16
+ return type ? !["review", "research", "ops"].includes(type) : !["reviewer", "qa", "probe", "sec-ops"].includes(profile.toLowerCase());
17
+ }
18
+
14
19
  /** Resolve host and working directory together so capacity routing keeps the caller's repo. */
15
20
  export async function selectSpawnCwdPlacement(input: {
16
21
  provider: SpawnProvider;
@@ -26,15 +31,16 @@ export async function selectSpawnCwdPlacement(input: {
26
31
  shared: boolean;
27
32
  workspaceMode?: WorkspaceMode;
28
33
  canLand?: boolean;
34
+ buildWork?: boolean;
29
35
  model?: string;
30
36
  effort?: string;
31
37
  }): Promise<{ orchestrator: Orchestrator; placementReason: string; resolvedCwd: string; lazyRoot?: ProjectRoot }> {
32
38
  const platformRule = input.workspaceMode === "isolated" && !input.shared && input.resumeWorkspace?.mode !== "attach"
33
39
  ? await sourcePlacementPlatforms(input) : undefined;
34
- const capOpts = { ...(input.caller ? { callerAgentId: input.caller.id } : {}), attach: input.resumeWorkspace?.mode === "attach" };
40
+ const capOpts: SpawnCapOptions = { ...(input.caller ? { callerAgentId: input.caller.id } : {}), attach: input.resumeWorkspace?.mode === "attach", buildWork: input.buildWork };
35
41
  const earlyPlacement = !input.orchestratorId && input.resumeWorkspace?.mode !== "attach"
36
42
  ? spawnPlacementCandidates(input.provider, input.requestedCwd, input.allowedOrchestratorIds,
37
- input.shared, input.model, input.effort, input.workspaceMode, platformRule, input.canLand, capOpts)
43
+ input.shared, input.model, input.effort, input.buildWork === false ? undefined : input.workspaceMode, platformRule, input.canLand, capOpts)
38
44
  : undefined;
39
45
  const survivors = earlyPlacement?.candidates.map((host) => host.id) ?? input.allowedOrchestratorIds;
40
46
  // Preserve selection's detailed exclusion error without probing hosts already ruled out.
@@ -43,6 +49,16 @@ export async function selectSpawnCwdPlacement(input: {
43
49
  input.model, input.effort, input.workspaceMode, platformRule, input.canLand, capOpts);
44
50
  const { attachWorkspace, hostCwds, allowedOrchestratorIds, lazyRoots, exclusions: checkoutExclusions = [] } = await omittedHostContext({ ...input, allowedOrchestratorIds: survivors });
45
51
  const requestedCwd = input.repoSource && !hostCwds ? input.repoSource : input.requestedCwd;
52
+ // Review/QA setup depends on the target checkout HEAD; a caller's policy base cannot vouch for it (#3805).
53
+ if (input.workspaceMode === "isolated" && input.buildWork === false && !input.resumeWorkspace) {
54
+ const views = new Map(listHostViews(Date.now()).map((view) => [view.id, view]));
55
+ const hosts = input.orchestratorId ? [getOrchestrator(input.orchestratorId)].filter((host): host is Orchestrator => !!host) : earlyPlacement?.candidates ?? [];
56
+ const lightHosts = await Promise.all(hosts.filter((host) => isRelayHost(host, host.hostId ? views.get(host.hostId) : undefined)).map(async (host) => {
57
+ const probe = await probeHostRepo(host, requestedCwd ?? hostCwds?.get(host.id) ?? input.callerCwd ?? host.baseDir, undefined, true);
58
+ return probe.kind === "present" && probe.workspaceDepsDisabled === true ? host.id : undefined;
59
+ }));
60
+ capOpts.workspaceDepsDisabledHosts = lightHosts.filter((id): id is string => id !== undefined);
61
+ }
46
62
  // #3169/#3625 — capacity ranks only hosts whose own checkout is ready for the project (refresh hook, toolchain).
47
63
  const readiness = hostProjectReadiness();
48
64
  const readinessExclusions: string[] = [];
@@ -1,6 +1,8 @@
1
+ import { resolveIssueRefFromStringAsync } from "../db/issue-refs-async";
2
+
1
3
  import { isRecord } from "@humwick/sdk";
2
4
  import { resolveProjectForCwd, spawnIssuePromptAsync } from "../db";
3
- import { resolveIssueRefFromString } from "../db/issue-refs";
5
+
4
6
  import { defaultIssueRepo } from "../config";
5
7
  import { parseJson } from "../utils";
6
8
  import { recordAndEmitIssueActivity } from "./issue-activity";
@@ -32,8 +34,8 @@ export async function recordSpawnPromptIssueActivity(agent: AgentCard, spawnRequ
32
34
  if (!num || seen.has(num)) continue;
33
35
  seen.add(num);
34
36
  try {
35
- const issueRef = resolveIssueRefFromString({ ref: `#${num}`, defaultRepo: issueRepo });
36
- recordAndEmitIssueActivity({
37
+ const issueRef = await resolveIssueRefFromStringAsync({ ref: `#${num}`, defaultRepo: issueRepo });
38
+ await recordAndEmitIssueActivity({
37
39
  agentId: agent.id,
38
40
  action: "edit",
39
41
  issueRefId: issueRef.id,
@@ -58,7 +60,7 @@ export async function recordSpawnPromptIssueActivity(agent: AgentCard, spawnRequ
58
60
  * as recordSpawnPromptIssueActivity so prompt + branch refs for the same issue
59
61
  * are deduplicated at the DB layer.
60
62
  */
61
- export function recordWorkspaceIssueActivity(workspace: WorkspaceRecord): void {
63
+ export async function recordWorkspaceIssueActivity(workspace: WorkspaceRecord): Promise<void> {
62
64
  const agentId = workspace.ownerAgentId;
63
65
  if (!agentId || workspace.mode !== "isolated") return;
64
66
 
@@ -86,8 +88,8 @@ export function recordWorkspaceIssueActivity(workspace: WorkspaceRecord): void {
86
88
 
87
89
  for (const num of seen) {
88
90
  try {
89
- const issueRef = resolveIssueRefFromString({ ref: `#${num}`, defaultRepo: issueRepo });
90
- recordAndEmitIssueActivity({
91
+ const issueRef = await resolveIssueRefFromStringAsync({ ref: `#${num}`, defaultRepo: issueRepo });
92
+ await recordAndEmitIssueActivity({
91
93
  agentId,
92
94
  action: "edit",
93
95
  issueRefId: issueRef.id,
@@ -1,7 +1,7 @@
1
1
  import { memoryRetirementMessage } from "../memory-retirement";
2
2
  // #3121 — the provider, model and effort a spawn launches with, resolved in one place: spawnAgent uses it, and a
3
3
  // caller that must know a launch before anything spawns (a workflow's start check) asks the same seam.
4
- import type { SpawnProvider, TaskDispatch } from "@humwick/sdk";
4
+ import { errMessage, type SpawnProvider, type TaskDispatch } from "@humwick/sdk";
5
5
  import { isProviderEnabled, type ProviderCatalogEntry } from "@humwick/sdk/provider-catalog";
6
6
  import { builtInProfileSpawnDefaults, profileProviderRefusal } from "../agent-profile-catalog";
7
7
  import { DEFAULT_SPAWN_PROFILE_NAME, getAgentProfile } from "../config-store";
@@ -14,6 +14,7 @@ import { captureDispatchContext, withProbeUncertainty, type DispatchSnapshot } f
14
14
  import { finalizeLaunchDecision } from "./spawn-launch-decision";
15
15
  import type { SpawnCapOptions } from "../spawn-placement";
16
16
  import { resolveProjectRoleForProvider } from "./spawn-project-role";
17
+ import { PinnedHostCapacityError } from "../spawn-admission";
17
18
 
18
19
  export interface SpawnLaunchInput {
19
20
  profile?: string;
@@ -82,6 +83,21 @@ export async function resolveSpawnLaunch(input: SpawnLaunchInput, captured?: Dis
82
83
  mustResolveAvailable: mustResolveAvailableFor(input.dispatchShape), orchestratorId: input.orchestratorId, spawnCap: input.spawnCap,
83
84
  avoidProviders: input.avoidProviders,
84
85
  });
86
+ try {
87
+ return await resolveLaunch(input, snapshot);
88
+ } catch (error) {
89
+ if (!input.orchestratorId || !snapshot.pinnedHostAtAgentCap) throw error;
90
+ // #3816 — prove capacity is the only refusal using the same full launch resolver.
91
+ try {
92
+ await resolveSpawnLaunch({ ...input, spawnCap: { ...input.spawnCap, attach: true } });
93
+ } catch {
94
+ throw error;
95
+ }
96
+ throw new PinnedHostCapacityError(errMessage(error), input.orchestratorId);
97
+ }
98
+ }
99
+
100
+ async function resolveLaunch(input: SpawnLaunchInput, snapshot: DispatchSnapshot): Promise<SpawnLaunch> {
85
101
  const { catalog } = snapshot;
86
102
  let provider = input.provider;
87
103
  const profile = input.profile ? getAgentProfile(input.profile)?.value : undefined;
@@ -224,7 +224,7 @@ export async function escalateLostSpawnCommand(command: Command): Promise<SpawnL
224
224
  // A child that DID register is alive regardless of what happened to its command row; never
225
225
  // convert a bookkeeping loss into a phantom failure for a running agent.
226
226
  if (spawnRequestId && await spawnWatchdogHasRegisteredAsync(spawnRequestId)) return null;
227
- notifyAgentSpawnFailed({
227
+ await notifyAgentSpawnFailed({
228
228
  parent,
229
229
  ...(spawnRequestId ? { spawnRequestId } : {}),
230
230
  ...(isRecord(command.params) && stringValue(command.params.provider) ? { provider: stringValue(command.params.provider)! } : {}),
@@ -209,12 +209,12 @@ export async function reapUnregisteredSpawnCommands(
209
209
  const representativeId = unconfirmedIds[unconfirmedIds.length - 1] ?? group.rows[group.rows.length - 1]!.id;
210
210
  if (undispatchable) {
211
211
  lostSpawnCommandIds.push(representativeId);
212
- notifyAgentSpawnFailed({ parent: group.parent, spawnRequestId: group.spawnRequestId, provider: group.provider, reason });
212
+ await notifyAgentSpawnFailed({ parent: group.parent, spawnRequestId: group.spawnRequestId, provider: group.provider, reason });
213
213
  continue;
214
214
  }
215
215
  if (group.latestUpdatedAt >= notifyCutoff) {
216
216
  notifiedUnconfirmedSpawnCommandIds.push(representativeId);
217
- notifyAgentSpawnUnconfirmed({
217
+ await notifyAgentSpawnUnconfirmed({
218
218
  parent: group.parent,
219
219
  spawnRequestId: group.spawnRequestId,
220
220
  provider: group.provider,
@@ -1,4 +1,5 @@
1
1
  import { cleanLandPlacement, readRepoLandPlacement } from "@humwick/sdk/land-gates";
2
+ import { repoWorkspaceDepsDisabled } from "@humwick/sdk/worktree-config";
2
3
  import { existsSync } from "node:fs";
3
4
  import { isAbsolute } from "node:path";
4
5
  import { isRecord, normalizeGitRemoteIdentity, ValidationError, type Orchestrator } from "@humwick/sdk";
@@ -9,7 +10,7 @@ import { findGitRepoRoot } from "./canonical-root";
9
10
  import { runGit, toProjectGitResult } from "./git-process";
10
11
  import { remoteUrlFromGitRemoteAsync } from "./git-remote";
11
12
 
12
- export type RepoProbe = { kind: "present"; repoRoot?: string; remoteIdentity?: string; placementPlatforms?: string[]; toolchainError?: string }
13
+ export type RepoProbe = { kind: "present"; repoRoot?: string; remoteIdentity?: string; placementPlatforms?: string[]; workspaceDepsDisabled?: boolean; toolchainError?: string }
13
14
  /** `occupied`: the path exists but is not inside a git checkout, so no clone can land there (#3347). */
14
15
  | { kind: "absent"; occupied?: boolean } | { kind: "timeout" } | { kind: "unavailable"; reason?: string };
15
16
 
@@ -77,17 +78,18 @@ export async function probeLocalSourceRepo(path: string, baseRef?: string | null
77
78
  if (!root) return { kind: "absent", occupied: true };
78
79
  const remote = await remoteUrlFromGitRemoteAsync(root, { refresh: true });
79
80
  let placementPlatforms: string[] | undefined;
81
+ const workspaceDepsDisabled = await repoWorkspaceDepsDisabled(placementGit(root));
80
82
  if (baseRef !== undefined) {
81
83
  try {
82
84
  placementPlatforms = await readRepoLandPlacement(baseRef ?? undefined, placementGit(root));
83
85
  } catch (error) { return { kind: "unavailable", reason: String(error) }; }
84
86
  }
85
- return { kind: "present", repoRoot: root, ...(placementPlatforms ? { placementPlatforms } : {}), ...(remote ? { remoteIdentity: normalizeGitRemoteIdentity(remote) } : {}) };
87
+ return { kind: "present", repoRoot: root, workspaceDepsDisabled, ...(placementPlatforms ? { placementPlatforms } : {}), ...(remote ? { remoteIdentity: normalizeGitRemoteIdentity(remote) } : {}) };
86
88
  }
87
89
 
88
90
  async function probeRemoteRepo(host: Orchestrator, path: string, baseRef?: string | null, toolchain = false): Promise<RepoProbe> {
89
91
  if (!host.apiUrl || !isTrustedOrchestratorApiUrl(host.apiUrl)) return { kind: "unavailable" };
90
- const result = await fetchHostJson<{ isGitRepo: boolean; definite?: boolean; pathExists?: boolean; repoRoot?: string; remoteIdentity?: string; placementPlatforms?: string[]; placementError?: string; toolchainError?: string }>(host.apiUrl, "/api/repo/identity", {
92
+ const result = await fetchHostJson<{ isGitRepo: boolean; definite?: boolean; pathExists?: boolean; repoRoot?: string; remoteIdentity?: string; placementPlatforms?: string[]; workspaceDepsDisabled?: boolean; placementError?: string; toolchainError?: string }>(host.apiUrl, "/api/repo/identity", {
91
93
  query: new URLSearchParams({ path, ...(toolchain ? { toolchain: "1" } : {}), ...(baseRef ? { baseRef } : baseRef === null ? { placement: "1" } : {}) }), timeoutMs: toolchain ? 50_000 : 20_000,
92
94
  validate: (body) => {
93
95
  if (!isRecord(body) || typeof body.isGitRepo !== "boolean"
@@ -101,14 +103,14 @@ async function probeRemoteRepo(host: Orchestrator, path: string, baseRef?: strin
101
103
  let placementPlatforms: string[] | undefined;
102
104
  try { placementPlatforms = cleanLandPlacement({ placement: { platforms: body.placementPlatforms } })?.platforms; }
103
105
  catch (error) { console.warn(`${host.id}: invalid repository placement: ${String(error)}`); }
104
- return { ok: true, value: { ...(typeof body.toolchainError === "string" ? { toolchainError: body.toolchainError } : {}), isGitRepo: body.isGitRepo, definite: body.definite === true, pathExists: body.pathExists === true, ...(typeof body.placementError === "string" ? { placementError: body.placementError } : {}), ...(placementPlatforms ? { placementPlatforms } : {}),
106
+ return { ok: true, value: { workspaceDepsDisabled: body.workspaceDepsDisabled === true, ...(typeof body.toolchainError === "string" ? { toolchainError: body.toolchainError } : {}), isGitRepo: body.isGitRepo, definite: body.definite === true, pathExists: body.pathExists === true, ...(typeof body.placementError === "string" ? { placementError: body.placementError } : {}), ...(placementPlatforms ? { placementPlatforms } : {}),
105
107
  ...(typeof body.repoRoot === "string" ? { repoRoot: body.repoRoot } : {}),
106
108
  ...(typeof body.remoteIdentity === "string" ? { remoteIdentity: body.remoteIdentity } : {}) } };
107
109
  },
108
110
  });
109
111
  if (result.ok && baseRef !== undefined && result.value.placementError) return { kind: "unavailable", reason: result.value.placementError };
110
112
  if (result.ok) return result.value.isGitRepo
111
- ? { kind: "present", ...(result.value.toolchainError ? { toolchainError: result.value.toolchainError } : {}), ...(result.value.repoRoot ? { repoRoot: result.value.repoRoot } : {}), ...(result.value.placementPlatforms ? { placementPlatforms: result.value.placementPlatforms } : {}), ...(result.value.remoteIdentity ? { remoteIdentity: result.value.remoteIdentity } : {}) }
113
+ ? { kind: "present", workspaceDepsDisabled: result.value.workspaceDepsDisabled, ...(result.value.toolchainError ? { toolchainError: result.value.toolchainError } : {}), ...(result.value.repoRoot ? { repoRoot: result.value.repoRoot } : {}), ...(result.value.placementPlatforms ? { placementPlatforms: result.value.placementPlatforms } : {}), ...(result.value.remoteIdentity ? { remoteIdentity: result.value.remoteIdentity } : {}) }
112
114
  // Old orchestrators also returned false for a killed/timed-out probe (#2715).
113
115
  : result.value.definite === true ? { kind: "absent", ...(result.value.pathExists ? { occupied: true } : {}) } : { kind: "unavailable" };
114
116
  const failure = result.failure;
@@ -2,36 +2,35 @@ import { errMessage, stringValue } from "@humwick/sdk";
2
2
  import type { WorkflowEffect } from "@humwick/kernel/workflow.pilot";
3
3
  import { getGateway } from "../db/gateway";
4
4
  import { getAgentAsync } from "../db";
5
+ import { isTerminalCommandStatus } from "../db/command-terminal-status";
5
6
  import { shutdownAgent } from "./shutdown-agent";
6
7
  import { currentStop } from "./workflow-effects";
7
- import { invoke, supervisorNow, type Supervisor } from "./workflow-supervisor-state";
8
+ import { invoke, retryResult, supervisorNow, type Supervisor } from "./workflow-supervisor-state";
8
9
 
9
10
  /** Every retry resumes the same outbox effect; only host witnesses release reservations. */
10
11
  export async function performSpawnTreeCleanup(sup: Supervisor, effect: WorkflowEffect): Promise<boolean> {
11
12
  const now = supervisorNow();
12
- if (typeof effect.result?.nextAttemptAt === "number" && effect.result.nextAttemptAt > now) return false;
13
13
  const agentId = String(effect.payload.agentId);
14
14
  await getGateway().write("spawnTree.prepareCleanup", { agentId });
15
15
  const targets = await getGateway().read("spawnTree.cleanupTargets", { agentId });
16
16
  const retry: Array<{ spawnRequestId: string; error: string }> = [];
17
+ let inFlight = false;
17
18
  for (const target of targets) {
18
19
  if (target.processGone) continue;
19
20
  try {
20
21
  const agent = await getAgentAsync(target.agentId);
21
22
  const generation = stringValue(agent?.meta?.spawnRequestId) ?? target.spawnRequestId;
22
- const { id, prior } = await currentStop(`${effect.id}:stop:${target.spawnRequestId}:${generation}`, command =>
23
- command.result?.targetNotFound !== true || now - command.updatedAt >= 15 * 60_000);
23
+ const { id, prior } = await currentStop(`${effect.id}:stop:${target.spawnRequestId}:${generation}`, now);
24
24
  const command = prior ?? (await shutdownAgent({
25
25
  ...(agent ? { agentId: agent.id } : {}), spawnRequestId: generation, orchestratorId: target.orchestratorId,
26
26
  commandId: id, requireProcessTreeProof: true, graceful: false, force: true,
27
27
  reason: String(effect.payload.reason), requestedVia: "bus", requestedBy: "workflow",
28
28
  }, { actor: { id: "workflow", kind: "system" }, scopes: [] })).command;
29
29
  if (command.status !== "succeeded" || command.result?.processTreeStopped !== true) retry.push({ spawnRequestId: target.spawnRequestId, error: `shutdown ${command.id}: ${command.status}` });
30
+ if (!isTerminalCommandStatus(command.status)) inFlight = true;
30
31
  } catch (error) { retry.push({ spawnRequestId: target.spawnRequestId, error: errMessage(error) }); }
31
32
  }
32
- const delay = Math.min(60_000, 1000 * 2 ** Math.min(effect.attempts, 6));
33
- await invoke(sup, "settleEffect", { id: effect.id, status: retry.length ? "pending" : "done", result: {
34
- targets: targets.length, retry, checkedAt: now, ...(retry.length ? { nextAttemptAt: now + delay } : {}),
35
- } });
33
+ const result = { targets: targets.length, retry, checkedAt: now, ...(inFlight ? { inFlight } : {}) };
34
+ await invoke(sup, "settleEffect", { id: effect.id, status: retry.length ? "pending" : "done", result: retry.length ? retryResult(effect.result, result, now) : result });
36
35
  return retry.length === 0;
37
36
  }
@@ -164,13 +164,13 @@ export async function reapIdleStewards(opts: { now?: number; signal?: AbortSigna
164
164
  if (entry.attempts >= STEWARD_REAP_MAX_ATTEMPTS) {
165
165
  if (!entry.alarmed) {
166
166
  entry.alarmed = true;
167
- raiseOperatorAlarm({
167
+ (await raiseOperatorAlarm({
168
168
  clientId: `steward-zombie-blocking-${agent.id}`,
169
169
  source: "steward-reaper",
170
170
  subject: "Steward zombie is blocking a repo/host steward slot",
171
171
  detail: `Steward \`${agent.id}\` (${agent.label ?? "unlabelled"}, host ${agent.machine ?? "unknown"}) ignored ${entry.attempts} shutdown attempt(s) and is holding the one-per-repo/host steward slot — no new steward can spawn there until it is killed. Investigate and terminate it manually.`,
172
172
  metadata: { maintenanceJobId: "steward-reaper", agentId: agent.id, label: agent.label, machine: agent.machine, attempts: entry.attempts },
173
- });
173
+ }));
174
174
  }
175
175
  reapTracker.set(agent.id, entry);
176
176
  alarmed.push(agent.id);
@@ -192,7 +192,7 @@ export async function reapIdleStewards(opts: { now?: number; signal?: AbortSigna
192
192
  entry.lastAt = now;
193
193
  reapTracker.set(agent.id, entry);
194
194
  if (graceful) {
195
- requestTransientAgentShutdown(agent, {
195
+ (await requestTransientAgentShutdown(agent, {
196
196
  reason: disabled ? "steward-disabled" : "steward-orphan-idle",
197
197
  requestedBy: "steward-reaper",
198
198
  activityTitle: disabled ? "Orphaned steward reaped (spawning disabled)" : "Orphaned steward reaped",
@@ -201,7 +201,7 @@ export async function reapIdleStewards(opts: { now?: number; signal?: AbortSigna
201
201
  : "Steward has no live managed policy state and has been idle past the keepalive grace; reaping.",
202
202
  landReason: disabled ? "steward-disabled" : "steward-orphan-idle",
203
203
  now,
204
- });
204
+ }));
205
205
  } else {
206
206
  // A plain agent-targeted `agent.shutdown` — graceful or not — is INERT against a runner that
207
207
  // has stopped reading its own commands (runner-core keys kill-hardness off the command TYPE,
@@ -48,6 +48,10 @@ export interface TaskHydrationFacets {
48
48
  /** #2728 — the Task's `context` facet (free-text facts a coordinator must not resend by message). */
49
49
  context?: string;
50
50
  doneWhen?: string;
51
+ /** #3808 — the Task's `terminalStage` (deployed = deploy/live proof happens after land). */
52
+ terminalStage?: string;
53
+ /** #3808 — the Task's `onDeploy` follow-up, run after the deploy, not by the worker. */
54
+ onDeploy?: string;
51
55
  issues: ResolvedIssueSpec[];
52
56
  /** Decisions/rules: discussion + milestone write-throughs and steers (the narrative ledger). */
53
57
  decisions: TaskEvent[];
@@ -165,12 +169,28 @@ function renderContextFacet(value: string | undefined): string {
165
169
  return `\nContext (Task data):\n${fence(truncate(text, TASK_CONTEXT_LIMIT))}\n`;
166
170
  }
167
171
 
172
+ /** #3808 — post-deploy criteria rendered apart from the worker's acceptance: they are checked after land
173
+ * (QA / onDeploy) and must never read as unmet delivery criteria. Empty when the Task has none, so the
174
+ * template line collapses to a blank line (the `{{context}}` seam's shape). */
175
+ function renderAfterLandFacet(facets: Pick<TaskHydrationFacets, "terminalStage" | "onDeploy">): string {
176
+ const onDeploy = facets.onDeploy?.trim();
177
+ const deployed = facets.terminalStage === "deployed";
178
+ if (!onDeploy && !deployed) return "";
179
+ const parts = [
180
+ "\nAfter land (checked by QA/onDeploy once the change is live; NOT yours and never blocks delivery — deliver `complete` when code, tests and the delivery gate pass):",
181
+ ];
182
+ if (deployed) parts.push("- Terminal stage is deployed: deploy, live-check and independent-reproduction criteria in the acceptance above belong here, not to your delivery.");
183
+ if (onDeploy) parts.push(`- onDeploy follow-up:\n${fence(truncate(onDeploy, TASK_CONTEXT_LIMIT))}`);
184
+ return `${parts.join("\n")}\n`;
185
+ }
186
+
168
187
  export function renderTaskHydration(facets: TaskHydrationFacets): string {
169
188
  const issues = facets.issues.length ? facets.issues.map(renderIssue).join("\n") : "- none linked";
170
189
  const decisions = facets.decisions.length ? facets.decisions.map(renderEvent).join("\n") : "- none recorded";
171
190
  const history = facets.history.length ? facets.history.map(renderEvent).join("\n") : "- none yet";
172
191
  const memory = facets.memory ? `\n${fence(facets.memory)}\n` : "";
173
192
  const context = renderContextFacet(facets.context);
193
+ const afterLand = renderAfterLandFacet(facets);
174
194
  const rendered = renderTemplate("task.hydration", {
175
195
  taskId: String(facets.taskId),
176
196
  title: inlineSafe(facets.title),
@@ -180,6 +200,7 @@ export function renderTaskHydration(facets: TaskHydrationFacets): string {
180
200
  intent: fence(facets.intent || "(no intent recorded)"),
181
201
  context,
182
202
  doneWhen: fence(facets.doneWhen || "(no explicit acceptance recorded)"),
203
+ afterLand,
183
204
  issues,
184
205
  decisions,
185
206
  history,
@@ -187,7 +208,8 @@ export function renderTaskHydration(facets: TaskHydrationFacets): string {
187
208
  steer: fence(facets.steer || "(no steer provided — proceed toward the acceptance above)"),
188
209
  }).trimEnd();
189
210
  // Saved operator templates can predate {{context}}; required Task data must still reach the worker (#3245).
190
- return context && !rendered.includes(context.trim()) ? `${rendered}\n${context}`.trimEnd() : rendered;
211
+ const withContext = context && !rendered.includes(context.trim()) ? `${rendered}\n${context}`.trimEnd() : rendered;
212
+ return afterLand && !withContext.includes(afterLand.trim()) ? `${withContext}\n${afterLand}`.trimEnd() : withContext;
191
213
  }
192
214
 
193
215
  function hasTaskMemory(envelope: TaskMemoryEnvelope): boolean {
@@ -346,6 +368,8 @@ export async function hydrateTaskContext(input: HydrateTaskContextInput): Promis
346
368
  intent: task.intent ?? task.title,
347
369
  ...(task.context?.trim() ? { context: task.context } : {}),
348
370
  ...(task.doneWhen ? { doneWhen: task.doneWhen } : {}),
371
+ ...(task.terminalStage ? { terminalStage: task.terminalStage } : {}),
372
+ ...(task.onDeploy?.trim() ? { onDeploy: task.onDeploy } : {}),
349
373
  issues,
350
374
  decisions,
351
375
  history,