@humwick/server 0.130.0 → 0.130.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/docs/guides/tools-administration.md +6 -1
- package/docs/guides/tools-coordination.md +1 -0
- package/docs/guides/tools-tasks.md +8 -0
- package/docs/guides/workflows.md +58 -0
- package/docs/openapi.json +414 -1
- package/package.json +8 -17
- package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-BWLqMGKG.js} +2 -2
- package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-3TlIQpjk.js} +2 -2
- package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-Wimm0Czv.js} +2 -2
- package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-Bau_vNax.js} +2 -2
- package/public/assets/{activity-yLztXEBt.js → activity-CrSurn-r.js} +2 -2
- package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-CknAJKcb.js} +2 -2
- package/public/assets/agent-liveness-CaNwK0Zf.js +2 -0
- package/public/assets/{agent-name-CTZBrSD9.js → agent-name-DKbiIhBo.js} +2 -2
- package/public/assets/agent-profiles-DwF6EQ4R.js +2 -0
- package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-Diz4Sjjb.js} +2 -2
- package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-CRhpRQza.js} +2 -2
- package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-LrUEU3lB.js} +2 -2
- package/public/assets/{agents-BYDslkJc.js → agents-B-v66C-4.js} +2 -2
- package/public/assets/{analytics-B-XSbEMC.js → analytics-BqCb-LKi.js} +2 -2
- package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-kf4pGuAR.js} +2 -2
- package/public/assets/{artifacts-oBOqks-2.js → artifacts-D32zyw4Y.js} +2 -2
- package/public/assets/{artifacts-DPN1q4pA.js → artifacts-DJevXcvg.js} +2 -2
- package/public/assets/{automation-4KcRFXEu.js → automation-BpRajx_5.js} +2 -2
- package/public/assets/{backlog-Cw9xPTCF.js → backlog-D_UjTdzX.js} +2 -2
- package/public/assets/{busy-health-CTu32MZt.js → busy-health-N3VEh4ao.js} +2 -2
- package/public/assets/{channels-BiXzg1G9.js → channels-9jopungq.js} +2 -2
- package/public/assets/{chat-Ctk2qwvA.js → chat-CSXrTB3C.js} +2 -2
- package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-p1NnmqK0.js} +2 -2
- package/public/assets/{connectors-C36ax4Vw.js → connectors-CZdqlDNL.js} +2 -2
- package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BgdsE4dG.js} +2 -2
- package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-30aXTKva.js} +2 -2
- package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-B_jvw2GE.js} +2 -2
- package/public/assets/{deploys-Bwfq146q.js → deploys-DoN6bAb5.js} +2 -2
- package/public/assets/{display-CfO7rTuF.js → display-BrKWFGJz.js} +2 -2
- package/public/assets/{files-DhhZI2Ih.js → files-D6XEu8Ng.js} +2 -2
- package/public/assets/fleet-C7jQWbw2.js +2 -0
- package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BiDs3MLx.js} +2 -2
- package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-CQBwc1fb.js} +3 -3
- package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-DyG0WsQS.js} +2 -2
- package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-DKaW31dN.js} +2 -2
- package/public/assets/index-CRHep3M9.js +15 -0
- package/public/assets/index-MYAIwYTX.css +2 -0
- package/public/assets/{insights-fH1EL-qZ.js → insights-CzrtkpOE.js} +2 -2
- package/public/assets/{integrations-C5Oix6ng.js → integrations-aVsUmigt.js} +2 -2
- package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-BnSlM_95.js} +2 -2
- package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-qe7Ee47Z.js} +2 -2
- package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-NfjqFZAk.js} +2 -2
- package/public/assets/{managed-agents-egllbpij.js → managed-agents-BiH0HgaE.js} +2 -2
- package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-BixaT3Xr.js} +3 -3
- package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-DNGlWIS3.js} +2 -2
- package/public/assets/{memory-C8WmXsyr.js → memory-DNsCl6UX.js} +2 -2
- package/public/assets/{messages-BzQdqlO6.js → messages-DopU1L1X.js} +2 -2
- package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Cq7ckLQT.js} +2 -2
- package/public/assets/{overview-DCUnI4Z7.js → overview-C1DCTzzV.js} +2 -2
- package/public/assets/{pairs-ftCd2XcD.js → pairs-DE_m6rrE.js} +2 -2
- package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-B3rfh77F.js} +2 -2
- package/public/assets/project-profile-instructions-DQWZE0kA.js +2 -0
- package/public/assets/projects-Br2Bd2h1.js +2 -0
- package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-BKvFsvGD.js} +2 -2
- package/public/assets/provider-display-DsuWMvVL.js +2 -0
- package/public/assets/{registry-DWCU5c8q.js → registry-Cw38_6gW.js} +2 -2
- package/public/assets/run-card-CpKqRbhg.js +2 -0
- package/public/assets/scroll-text-BKXS1ahr.js +2 -0
- package/public/assets/{secrets-C7GIUATz.js → secrets-DxI-vzSq.js} +2 -2
- package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-D9s_VJbd.js} +2 -2
- package/public/assets/{settings-C6sZ9eBa.js → settings-CKZTVGIx.js} +2 -2
- package/public/assets/{shipping-Bu8p1gEw.js → shipping-B9YtnwtG.js} +2 -2
- package/public/assets/{stats-V9Z-GxUd.js → stats-DyUBKyWI.js} +2 -2
- package/public/assets/{status-dot-CVv0iGHL.js → status-dot-DYqlEUhJ.js} +2 -2
- package/public/assets/{store-BvtFdlGX.js → store-DcClXLH0.js} +3 -3
- package/public/assets/{tasks-CXU75HRF.js → tasks-Bh7Chr4-.js} +2 -2
- package/public/assets/{teams-BPf976zb.js → teams-bowI7qVY.js} +2 -2
- package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-BgVLHdsU.js} +3 -3
- package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-Dz22UPy6.js} +2 -2
- package/public/assets/token-estimate-Co-Jdphh.js +2 -0
- package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-DSQQ-iY7.js} +2 -2
- package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-samMQhQf.js} +2 -2
- package/public/assets/use-project-list-59LheVP7.js +2 -0
- package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BZa25xEB.js} +2 -2
- package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-DHxlvUvV.js} +2 -2
- package/public/assets/versioned-prompt-editor-BmED5DnJ.js +8 -0
- package/public/assets/{work-queue-TCjJxeT-.js → work-queue-CSvpuNLz.js} +2 -2
- package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-DAZcj5qt.js} +2 -2
- package/public/assets/{workflows-CXQX20Qj.js → workflows-DdmxRSyg.js} +2 -2
- package/public/assets/{workspaces-qSYzKYMA.js → workspaces-BxeKfVuF.js} +2 -2
- package/public/cosmos.html +4 -4
- package/public/index.html +31 -29
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
- package/runner/src/adapters/claude-effort.ts +2 -4
- package/runner/src/adapters/codex-launch-config.ts +4 -5
- package/runner/src/adapters/codex.ts +1 -1
- package/runner/src/adapters/opencode-provider.ts +7 -22
- package/runner/src/adapters/opencode.ts +4 -4
- package/runner/src/agent-runner/credentials.ts +42 -12
- package/runner/src/agent-runner/inbox.ts +27 -0
- package/runner/src/agent-runner/lifecycle-milestones.ts +0 -2
- package/runner/src/bin/claude.ts +4 -0
- package/runner/src/bin/codex.ts +4 -0
- package/runner/src/bin/opencode.ts +4 -0
- package/runner/src/index.ts +5 -2
- package/runner/src/provider-setup.ts +34 -0
- package/runner/src/providers.ts +2 -1
- package/runner/src/runner-core.ts +18 -23
- package/runner/src/spawn-input.ts +2 -1
- package/runner/src/test-run-dedup.ts +59 -56
- package/scripts/cd-config.ts +24 -3
- package/scripts/gate-run-lease.ts +10 -7
- package/scripts/gate.sh +5 -1
- package/src/branch-land-notice.ts +93 -0
- package/src/branch-landed.ts +7 -76
- package/src/bus.ts +19 -20
- package/src/cd-deploy-notify.ts +30 -2
- package/src/cd-host-lag-notify.ts +7 -6
- package/src/ci/incidents.ts +2 -2
- package/src/ci/monitor.ts +5 -8
- package/src/ci/root-missing.ts +2 -7
- package/src/ci/transition-notify.ts +7 -39
- package/src/cli/cd-pauses.ts +19 -0
- package/src/cli/orchestrator.ts +1 -1
- package/src/cli/state.ts +2 -19
- package/src/cli/task-edit.ts +22 -0
- package/src/cli/task.ts +3 -1
- package/src/cli/workflow.ts +8 -2
- package/src/commands-db.ts +10 -6
- package/src/commands-write.ts +8 -0
- package/src/config-store-write.ts +13 -1
- package/src/config-store.ts +25 -14
- package/src/db/activity-async.ts +6 -1
- package/src/db/activity-write-ops.ts +13 -1
- package/src/db/activity.ts +1 -14
- package/src/db/agent-lifecycle-write-ops.ts +1 -0
- package/src/db/agent-meta.ts +2 -2
- package/src/db/agent-ownership-write-ops.ts +4 -0
- package/src/db/agent-search.ts +1 -15
- package/src/db/agents-async.ts +58 -7
- package/src/db/agents-write-ops.ts +28 -5
- package/src/db/agents.ts +81 -54
- package/src/db/artifacts-write-ops.ts +8 -3
- package/src/db/bus-outbox-write-ops.ts +1 -0
- package/src/db/channels-write-ops.ts +4 -0
- package/src/db/ci-incidents-async.ts +5 -5
- package/src/db/ci-incidents-write-ops.ts +2 -2
- package/src/db/claude-api-budget-ops.ts +5 -5
- package/src/db/claude-app-oauth.ts +4 -4
- package/src/db/codex-reset-consumes-write-ops.ts +3 -0
- package/src/db/command-row.ts +10 -2
- package/src/db/commands-list-query.ts +2 -0
- package/src/db/commands-spawn-read.ts +1 -1
- package/src/db/commands-write-ops.ts +21 -0
- package/src/db/config-store-write-ops.ts +9 -2
- package/src/db/continuation-archives-async.ts +5 -0
- package/src/db/continuation-archives-write-ops.ts +9 -0
- package/src/db/continuations.ts +2 -2
- package/src/db/db-writer-worker.ts +8 -1
- package/src/db/delivery-write-ops.ts +5 -0
- package/src/db/deploy-events-async.ts +1 -0
- package/src/db/deploy-events-read-ops.ts +2 -1
- package/src/db/deploy-events.ts +35 -4
- package/src/db/hosts-registration.ts +10 -2
- package/src/db/hosts-write-ops.ts +14 -0
- package/src/db/hosts.ts +9 -6
- package/src/db/inbox-write-ops.ts +4 -0
- package/src/db/inbox.ts +0 -14
- package/src/db/index.ts +2 -0
- package/src/db/insights-write-ops.ts +1 -0
- package/src/db/integrations-async.ts +8 -0
- package/src/db/integrations-ops.ts +15 -0
- package/src/db/integrations.ts +15 -21
- package/src/db/issue-lifecycle.ts +3 -4
- package/src/db/issue-refs-async.ts +20 -0
- package/src/db/issue-refs-write-ops.ts +29 -0
- package/src/db/issue-refs.ts +7 -8
- package/src/db/long-running-work.ts +2 -2
- package/src/db/macos-privacy.ts +1 -1
- package/src/db/main-connection-writes.ts +23 -2
- package/src/db/maintenance-job-reconcile.ts +61 -0
- package/src/db/maintenance-job-runs-async.ts +14 -0
- package/src/db/maintenance-job-write-ops.ts +9 -0
- package/src/db/mappers.ts +2 -14
- package/src/db/merge-lease.ts +1 -15
- package/src/db/message-reads-write-ops.ts +3 -0
- package/src/db/message-write-ops.ts +4 -0
- package/src/db/migrations.ts +7 -15
- package/src/db/orchestrators-async.ts +7 -3
- package/src/db/orchestrators-write-ops.ts +7 -1
- package/src/db/orchestrators.ts +21 -77
- package/src/db/pairs.ts +1 -13
- package/src/db/project-profile-instructions.ts +50 -0
- package/src/db/prompt-versions-ops.ts +1 -0
- package/src/db/prompt-versions.ts +9 -2
- package/src/db/provider-catalog-write-ops.ts +4 -4
- package/src/db/provider-config-history.ts +8 -3
- package/src/db/provider-flap-messages-write-ops.ts +1 -0
- package/src/db/provider-model-seed.ts +34 -0
- package/src/db/provider-quota-lease-write-ops.ts +1 -0
- package/src/db/provider-quota-write-ops.ts +2 -0
- package/src/db/provisioning-write-ops.ts +9 -8
- package/src/db/read-op-marshaling-fixtures.ts +5 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/routing-policy.ts +3 -4
- package/src/db/schema.ts +9 -3
- package/src/db/secrets-broker-write-ops.ts +5 -5
- package/src/db/secrets-vault-write.ts +7 -7
- package/src/db/services-write-ops.ts +3 -0
- package/src/db/spawn-delegation-state.ts +11 -4
- package/src/db/spawn-tree-cleanup.ts +3 -0
- package/src/db/stats-async.ts +4 -3
- package/src/db/task-entity.ts +23 -2
- package/src/db/task-list.ts +3 -1
- package/src/db/tasks.ts +1 -17
- package/src/db/team-spawn-delegation.ts +1 -1
- package/src/db/teams-write-ops.ts +4 -4
- package/src/db/timer-cutover-write-ops.ts +7 -0
- package/src/db/timer-ingress-write-ops.ts +9 -0
- package/src/db/timer-ingress.ts +7 -1
- package/src/db/timer-operator-write-ops.ts +14 -0
- package/src/db/timer-transfer-write-ops.ts +4 -0
- package/src/db/timer-write-ops.ts +14 -0
- package/src/db/token-db-write-ops.ts +21 -0
- package/src/db/token-standing.ts +15 -11
- package/src/db/user-settings-write-ops.ts +1 -0
- package/src/db/vault-backup.ts +2 -2
- package/src/db/vault-expiry.ts +2 -2
- package/src/db/vault-health.ts +3 -3
- package/src/db/vault-token-ops.ts +8 -8
- package/src/db/web-tool-call-ops.ts +3 -3
- package/src/db/workflow-contact.ts +1 -1
- package/src/db/workflow-task-read-ops.ts +30 -0
- package/src/db/write-ops.ts +15 -1
- package/src/db/writer-fault-seam.ts +5 -2
- package/src/db/writer-gateway.ts +21 -12
- package/src/dispatch-availability.ts +10 -6
- package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
- package/src/host/disk-alert.ts +121 -0
- package/src/lifecycle-manager.ts +3 -2
- package/src/maintenance/cd-watcher.ts +3 -3
- package/src/maintenance/critical-job-watchdog.ts +16 -17
- package/src/maintenance/index.ts +1 -0
- package/src/maintenance/jobs.ts +3 -2
- package/src/maintenance/scheduler.ts +84 -128
- package/src/maintenance/stalled-delivery-pager.ts +6 -5
- package/src/maintenance/workspaces.ts +1 -1
- package/src/managed-policy.ts +31 -27
- package/src/mcp/index.ts +5 -2
- package/src/mcp/output-descriptions.ts +8 -8
- package/src/mcp/presentation.ts +4 -4
- package/src/mcp/project-profile-instructions.ts +32 -0
- package/src/mcp/tool-groups.ts +1 -1
- package/src/mcp/tools-spawn.ts +4 -3
- package/src/mcp-agent-action.ts +1 -2
- package/src/mcp-host-tools.ts +1 -1
- package/src/mcp-issue-ref-tools.ts +12 -11
- package/src/mcp-maintenance-tools.ts +2 -2
- package/src/mcp-profile-trust-tools.ts +4 -4
- package/src/mcp-provider-catalog.ts +2 -2
- package/src/mcp-task-tools.ts +11 -5
- package/src/mcp-team-template-tools.ts +3 -3
- package/src/mcp-workflow-tools.ts +10 -3
- package/src/notification-types.ts +2 -1
- package/src/operator-alarm.ts +26 -11
- package/src/package-pack.ts +42 -39
- package/src/project-instructions.ts +4 -0
- package/src/project-profile-instruction-text.ts +6 -0
- package/src/project-profile-instructions.ts +20 -0
- package/src/provider-catalog-store.ts +3 -49
- package/src/provider-config-store.ts +12 -11
- package/src/provider-model-seed.ts +1 -18
- package/src/provider-quota-config-store.ts +5 -4
- package/src/provider-secrets.test-fixture.ts +3 -3
- package/src/recipe-runner.ts +2 -2
- package/src/repo-config-source.ts +10 -4
- package/src/routes/agent-profiles.ts +6 -6
- package/src/routes/agents-spawn.ts +1 -1
- package/src/routes/agents.ts +6 -6
- package/src/routes/commands-listing.ts +4 -0
- package/src/routes/commands.ts +2 -2
- package/src/routes/continuation-archives.ts +2 -2
- package/src/routes/host-resource-control.ts +4 -1
- package/src/routes/index.ts +10 -1
- package/src/routes/integrations.ts +24 -50
- package/src/routes/issue-refs.ts +7 -7
- package/src/routes/orchestrator-bootstrap.ts +3 -3
- package/src/routes/orchestrator-host-registration.ts +2 -2
- package/src/routes/orchestrator-host-utilization.ts +3 -1
- package/src/routes/orchestrator-proxy.ts +2 -2
- package/src/routes/orchestrator.ts +4 -4
- package/src/routes/project-profile-instructions.ts +27 -0
- package/src/routes/project-trust.ts +2 -2
- package/src/routes/provider-config.ts +9 -8
- package/src/routes/provider-quota-config.ts +2 -2
- package/src/routes/settings.ts +5 -5
- package/src/routes/stats.ts +6 -6
- package/src/routes/task-http.ts +15 -1
- package/src/routes/task-issues.ts +11 -26
- package/src/routes/tasks.ts +22 -53
- package/src/routes/team-templates.ts +5 -5
- package/src/routes/teams-config.ts +2 -2
- package/src/routes/tokens.ts +12 -5
- package/src/routes/workflows.ts +20 -5
- package/src/routing-policy-normalize.ts +1 -2
- package/src/routing-step-defaults.ts +0 -2
- package/src/runner-token-mint-limit.ts +63 -0
- package/src/runtime-grant.ts +4 -2
- package/src/runtime-tokens.ts +50 -9
- package/src/security.ts +13 -20
- package/src/server-background.ts +1 -1
- package/src/server.ts +5 -2
- package/src/services/host-utilization.ts +23 -0
- package/src/services/idle-epics.ts +5 -6
- package/src/services/issue-lifecycle.ts +57 -61
- package/src/services/issue-refs.ts +9 -13
- package/src/services/kernel-timers-cutover-chaos-worker.ts +12 -7
- package/src/services/kernel-timers-ingress.ts +1 -1
- package/src/services/kernel-timers-request-key-migration-chaos-worker.ts +4 -1
- package/src/services/landed-ancestry.ts +19 -3
- package/src/services/memory-areas.ts +15 -51
- package/src/services/plan-ci-notice.ts +57 -0
- package/src/services/plan-ci-recovery.ts +2 -55
- package/src/services/plan-wake-digest.ts +1 -0
- package/src/services/profile-trust.ts +4 -3
- package/src/services/project-config-read.ts +70 -0
- package/src/services/project-profile-instructions.ts +54 -0
- package/src/services/provisioning-harness/budget-baseline.json +4 -4
- package/src/services/restart-spawn.ts +61 -49
- package/src/services/self-care-state.ts +2 -1
- package/src/services/self-effort.ts +5 -5
- package/src/services/send-message.ts +5 -1
- package/src/services/spawn-agent.ts +13 -2
- package/src/services/spawn-chain-inspector.ts +6 -13
- package/src/services/spawn-policy.ts +68 -0
- package/src/services/spawn-tree-cleanup.ts +2 -2
- package/src/services/task-dispatch.ts +3 -0
- package/src/services/task-state-reconcile.ts +6 -14
- package/src/services/team-templates.ts +9 -8
- package/src/services/transient-agent-reaper.ts +3 -1
- package/src/services/workflow-auto-start.ts +25 -13
- package/src/services/workflow-ci-main.ts +1 -1
- package/src/services/workflow-deferred-cleanup.ts +4 -2
- package/src/services/workflow-effect-driver.ts +2 -0
- package/src/services/workflow-effects.ts +18 -11
- package/src/services/workflow-land-notice.ts +10 -0
- package/src/services/workflow-pause-text.ts +43 -0
- package/src/services/workflow-reconcile.ts +13 -11
- package/src/services/workflow-run-view.ts +2 -0
- package/src/services/workflow-runs.ts +29 -13
- package/src/services/workflow-supervisor-state.ts +5 -0
- package/src/services/workflow-supervisor.ts +7 -3
- package/src/settings/registry.ts +1 -2
- package/src/spawn-command.ts +4 -4
- package/src/spawn-models.ts +10 -12
- package/src/spawn-placement.ts +1 -1
- package/src/spawn-targets.ts +1 -1
- package/src/startup-context-injection.ts +1 -0
- package/src/state-adoption/cd-cutover.ts +53 -7
- package/src/state-adoption/fleet-cutover.ts +35 -0
- package/src/state-adoption/fleet-plan.ts +36 -0
- package/src/state-adoption/index.ts +2 -2
- package/src/state-adoption/linux-plan.ts +2 -2
- package/src/state-adoption/linux.ts +2 -2
- package/src/state-adoption/macos-plan.ts +2 -2
- package/src/state-adoption/macos.ts +1 -1
- package/src/steward.ts +7 -6
- package/src/task-dispatch-live.ts +1 -1
- package/src/task-dispatcher.ts +1 -1
- package/src/teams-config-store.ts +4 -3
- package/src/token-db-write.ts +10 -0
- package/src/workspace-command-settle.ts +2 -1
- package/src/workspace-steward-wake.ts +2 -2
- package/node_modules/@humwick/kernel/LICENSE +0 -167
- package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
- package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
- package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
- package/node_modules/@humwick/kernel/config.ts +0 -111
- package/node_modules/@humwick/kernel/contract.ts +0 -572
- package/node_modules/@humwick/kernel/control-authz.ts +0 -281
- package/node_modules/@humwick/kernel/control.ts +0 -645
- package/node_modules/@humwick/kernel/effects.ts +0 -493
- package/node_modules/@humwick/kernel/event-log.ts +0 -319
- package/node_modules/@humwick/kernel/http-transport.ts +0 -384
- package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
- package/node_modules/@humwick/kernel/identity.ts +0 -248
- package/node_modules/@humwick/kernel/index.ts +0 -28
- package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
- package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
- package/node_modules/@humwick/kernel/landing.ts +0 -953
- package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
- package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
- package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
- package/node_modules/@humwick/kernel/message-authority.ts +0 -281
- package/node_modules/@humwick/kernel/module-host.ts +0 -722
- package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
- package/node_modules/@humwick/kernel/package.json +0 -47
- package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
- package/node_modules/@humwick/kernel/persistence.ts +0 -510
- package/node_modules/@humwick/kernel/registry.ts +0 -60
- package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
- package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
- package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
- package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
- package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
- package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
- package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
- package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
- package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
- package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
- package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
- package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
- package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
- package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
- package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
- package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
- package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
- package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
- package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
- package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
- package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
- package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
- package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
- package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
- package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
- package/node_modules/@humwick/offload/LICENSE +0 -167
- package/node_modules/@humwick/offload/README.md +0 -57
- package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
- package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
- package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
- package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.js +0 -80
- package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
- package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/index.js +0 -5
- package/node_modules/@humwick/offload/dist/index.js.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
- package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
- package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/types.js +0 -176
- package/node_modules/@humwick/offload/dist/types.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
- package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
- package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.js +0 -1
- package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
- package/node_modules/@humwick/offload/package.json +0 -46
- package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
- package/node_modules/@humwick/offload/src/envelope.ts +0 -92
- package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
- package/node_modules/@humwick/offload/src/index.ts +0 -4
- package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
- package/node_modules/@humwick/offload/src/types.ts +0 -235
- package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
- package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
- package/node_modules/@humwick/offload/src/worker.ts +0 -5
- package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
- package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
- package/public/assets/fleet-BL4UXS_k.js +0 -2
- package/public/assets/index-B7ZerHLY.css +0 -2
- package/public/assets/index-BK0WV6hF.js +0 -15
- package/public/assets/projects-B_lmcqfO.js +0 -2
- package/public/assets/provider-display-DbLWpP3j.js +0 -2
- package/public/assets/run-card-CO_bguQf.js +0 -2
- package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
- package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
- package/runner/src/gate-run-lease-probe.ts +0 -38
- package/src/db/claude-catalog-reconcile.ts +0 -27
- package/src/routing-model-aliases.ts +0 -21
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { relayEnv } from "@humwick/sdk/env";
|
|
2
|
+
import { relayEnv, writeChildEnv } from "@humwick/sdk/env";
|
|
3
3
|
/**
|
|
4
4
|
* #2461 — the gate-run lease CLI. ONE implementation of the per-agent single-run invariant.
|
|
5
5
|
*
|
|
@@ -49,6 +49,8 @@ import { readFileSync } from "node:fs";
|
|
|
49
49
|
import { join } from "node:path";
|
|
50
50
|
import {
|
|
51
51
|
claimGateRunLease,
|
|
52
|
+
gateRunContext,
|
|
53
|
+
GATE_RUN_CONTEXT_ENV,
|
|
52
54
|
gateRunLeaseDir,
|
|
53
55
|
gateRunLeaseEnabled,
|
|
54
56
|
gateRunWasSuperseded,
|
|
@@ -136,17 +138,18 @@ export async function claim(options: {
|
|
|
136
138
|
// here, which is one read and therefore no disagreement.
|
|
137
139
|
const category = carried ?? classifyGateRun(options.command, gateCommandsForCwd(options.cwd));
|
|
138
140
|
if (!category) return { state: "skip", dir: "", epoch: "" };
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
// Preflight captured HEAD. Reuse that identity even if the checkout moves before claim (#3754).
|
|
142
|
+
const carriedContext = options.dir?.split("/").at(-1);
|
|
143
|
+
const context = options.dir ? (/^[a-f0-9]{64}$/.test(carriedContext ?? "") ? carriedContext : undefined) : gateRunContext(options.cwd);
|
|
144
|
+
const scopedEnv = writeChildEnv({ ...env }, { [GATE_RUN_CONTEXT_ENV]: context });
|
|
145
|
+
if (options.dir && options.dir !== gateRunLeaseDir(agentId, scopedEnv)) return { state: "skip", dir: "", epoch: "" };
|
|
143
146
|
|
|
144
147
|
const handle = await claimGateRunLease({
|
|
145
148
|
agentId,
|
|
146
149
|
category,
|
|
147
150
|
command: options.command,
|
|
148
151
|
selfPid: options.pid,
|
|
149
|
-
env,
|
|
152
|
+
env: scopedEnv,
|
|
150
153
|
...(options.epoch ? { epoch: options.epoch } : {}),
|
|
151
154
|
...clampedLockWait(env),
|
|
152
155
|
...(options.log ? { log: options.log } : {}),
|
|
@@ -190,7 +193,7 @@ export function preflight(options: { cwd: string; command: string; env?: Record<
|
|
|
190
193
|
if ((relayEnv(env, GATE_RUN_HELD_ENV) ?? "").trim()) return { state: "nested", dir: gateRunLeaseDir(agentId, env), category: "" };
|
|
191
194
|
const category = classifyGateRun(options.command, gateCommandsForCwd(options.cwd));
|
|
192
195
|
if (!category) return { state: "skip", dir: "", category: "" };
|
|
193
|
-
return { state: "claim", dir: gateRunLeaseDir(agentId, env), category };
|
|
196
|
+
return { state: "claim", dir: gateRunLeaseDir(agentId, env, options.cwd), category };
|
|
194
197
|
}
|
|
195
198
|
|
|
196
199
|
/** A carried category is honoured only if it is one this protocol actually has. Anything else is
|
package/scripts/gate.sh
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env sh
|
|
2
2
|
# Explicit override, managed native delegate, then the bundled compatibility launcher.
|
|
3
|
+
# CI runs this checkout's own launcher: the managed delegate execs the host's DEPLOYED runtime,
|
|
4
|
+
# so a broken deploy would turn every CI run red (T7319). Both share the host admission ledger.
|
|
3
5
|
GS="${GATE_SLOT_BIN:-}"
|
|
6
|
+
BUNDLED="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/gate-slot"
|
|
7
|
+
[ -x "$GS" ] || { [ "${GITHUB_ACTIONS:-}" = true ] && GS="$BUNDLED"; }
|
|
4
8
|
[ -x "$GS" ] || GS="$HOME/.local/bin/gate-slot"
|
|
5
|
-
[ -x "$GS" ] || GS="$
|
|
9
|
+
[ -x "$GS" ] || GS="$BUNDLED"
|
|
6
10
|
exec "$GS" "$@"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { isHumanAgentId } from "@humwick/sdk";
|
|
2
|
+
import type { BranchLandedInput, resolveLandedTask } from "./branch-landed";
|
|
3
|
+
import { getAgent, getTaskDetail } from "./db";
|
|
4
|
+
import { renderTemplate } from "./prompt-resolver";
|
|
5
|
+
import { routeNotification, INFORMATIONAL_TTL_MS } from "./notification-router";
|
|
6
|
+
import { stampLifecycleSignal, taskSignalKey } from "./lifecycle-signal-registry";
|
|
7
|
+
import { resolveCoordinatorAgentId } from "./steward-identity";
|
|
8
|
+
import { resolveTaskOwnerAgentId } from "./task-owner";
|
|
9
|
+
import { audienceWithTaskOwnerRole } from "./task-owner-notices";
|
|
10
|
+
import { notifyLandUnconfirmed, verifyPublishedLand } from "./services/landed-ancestry";
|
|
11
|
+
|
|
12
|
+
export async function announceVerifiedLand(input: BranchLandedInput, landedTask: ReturnType<typeof resolveLandedTask>, idempotencyKey?: string): Promise<void> {
|
|
13
|
+
const { workspace } = input;
|
|
14
|
+
const base = workspace.baseRef ?? "main";
|
|
15
|
+
const verification = input.mergedSha && input.landVerifiedSha === input.mergedSha
|
|
16
|
+
? "confirmed" : await verifyPublishedLand(workspace.repoRoot, input.mergedSha, workspace.baseRef);
|
|
17
|
+
if (verification !== "confirmed") {
|
|
18
|
+
notifyLandUnconfirmed({ workspace, sha: input.mergedSha, targetRef: base.startsWith("origin/") ? base : `origin/${base}`, kind: verification });
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const landedBranch = workspace.branch;
|
|
22
|
+
const shortSha = input.mergedSha?.slice(0, 12);
|
|
23
|
+
const landedTaskPayload = landedTask ? { taskId: landedTask.taskId, taskLabel: landedTask.taskLabel } : {};
|
|
24
|
+
const author = workspace.ownerAgentId;
|
|
25
|
+
const shaLabel = shortSha ? ` as \`${shortSha}\`` : "";
|
|
26
|
+
const subjectLabel = input.subject ? ` — "${input.subject}"` : "";
|
|
27
|
+
const payload = {
|
|
28
|
+
verified: true,
|
|
29
|
+
kind: "branch.landed",
|
|
30
|
+
workspaceId: workspace.id,
|
|
31
|
+
repoRoot: workspace.repoRoot,
|
|
32
|
+
branch: landedBranch,
|
|
33
|
+
base,
|
|
34
|
+
sha: input.mergedSha,
|
|
35
|
+
author,
|
|
36
|
+
newBranch: input.newBranch,
|
|
37
|
+
pushed: input.pushed,
|
|
38
|
+
...landedTaskPayload,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// The branch author cares most — push regardless of online (store-ahead delivers it on
|
|
42
|
+
// next poll if they've moved on, #234). A live owner continues in its worktree.
|
|
43
|
+
if (author) {
|
|
44
|
+
const branchLabel = landedBranch ? `\`${landedBranch}\`` : "Your branch";
|
|
45
|
+
const continueLabel = input.newBranch
|
|
46
|
+
? ` You're now on \`${input.newBranch}\` — keep working there.`
|
|
47
|
+
: " Worktree reclaimed.";
|
|
48
|
+
// Durable (ttl null): the author's own branch landing must reach them even after they go
|
|
49
|
+
// offline (store-ahead #234) — not a bystander notice that goes stale.
|
|
50
|
+
const notices = routeNotification({
|
|
51
|
+
type: "branch.landed",
|
|
52
|
+
scope: { author, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
|
|
53
|
+
recipients: [author],
|
|
54
|
+
ttlMs: null,
|
|
55
|
+
message: {
|
|
56
|
+
subject: "Your branch landed",
|
|
57
|
+
body: renderTemplate("notify.land.success", {
|
|
58
|
+
branch: branchLabel,
|
|
59
|
+
base,
|
|
60
|
+
sha: `${shaLabel}${subjectLabel}`,
|
|
61
|
+
publishLabel: "",
|
|
62
|
+
continueLabel,
|
|
63
|
+
}) + " verified: true.",
|
|
64
|
+
payload: { ...payload, notification: "landed-success" },
|
|
65
|
+
idempotencyKey,
|
|
66
|
+
replyExpected: false,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
if (landedTask && input.mergedSha) {
|
|
70
|
+
for (const notice of notices) stampLifecycleSignal(notice.to, taskSignalKey(landedTask.taskId, "landed-on-main", input.mergedSha));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Coordinator copy (#2837), informational. No explicit recipients: the router records the rule decision (#446).
|
|
75
|
+
const mainScope = { author, repoRoot: workspace.repoRoot, workspaceId: workspace.id, teamId: landedTask?.teamId ?? (author ? getAgent(author)?.teamId : undefined), coordinatorAgentIds: [...new Set([resolveCoordinatorAgentId(author), landedTask && resolveTaskOwnerAgentId(getTaskDetail(landedTask.taskId))].filter((id): id is string => !!id && !isHumanAgentId(id)))] };
|
|
76
|
+
const branchLabel = landedBranch ? `\`${landedBranch}\`` : "A branch";
|
|
77
|
+
const authorLabel = author ? ` by \`${author}\`` : "";
|
|
78
|
+
const notices = routeNotification({
|
|
79
|
+
type: "branch.landed",
|
|
80
|
+
scope: mainScope, ...audienceWithTaskOwnerRole("branch.landed", mainScope, landedTask?.taskId), // #2862
|
|
81
|
+
ttlMs: INFORMATIONAL_TTL_MS,
|
|
82
|
+
message: {
|
|
83
|
+
subject: `Merged to ${base}`,
|
|
84
|
+
body: `🔀 ${branchLabel}${authorLabel} merged to \`${base}\`${shaLabel}${subjectLabel}. verified: true.`,
|
|
85
|
+
payload: { ...payload, notification: "landed-success" },
|
|
86
|
+
idempotencyKey,
|
|
87
|
+
replyExpected: false,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
if (landedTask && input.mergedSha) {
|
|
91
|
+
for (const notice of notices) stampLifecycleSignal(notice.to, taskSignalKey(landedTask.taskId, "landed-on-main", input.mergedSha));
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/branch-landed.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { announceVerifiedLand } from "./branch-land-notice";
|
|
1
2
|
import { errMessage, isHumanAgentId, type BaseWorktreeSyncResult } from "@humwick/sdk";
|
|
2
3
|
import { emitRelayEvent } from "./events";
|
|
3
4
|
import { routeNotification, INFORMATIONAL_TTL_MS } from "./notification-router";
|
|
@@ -32,6 +33,8 @@ export interface BranchLandedInput extends LandedTaskAttributionInput {
|
|
|
32
33
|
workspace: Pick<WorkspaceRecord, "id" | "repoRoot" | "branch" | "baseRef" | "baseSha" | "ownerAgentId" | "orchestratorId">;
|
|
33
34
|
/** SHA the base now points at after the land. */
|
|
34
35
|
mergedSha?: string;
|
|
36
|
+
/** Landing host proof from its fresh origin ancestry check. Must match mergedSha. */
|
|
37
|
+
landVerifiedSha?: string;
|
|
35
38
|
/** Original command settlement clock when recovering an older land. */
|
|
36
39
|
landedAt?: number;
|
|
37
40
|
/** Subject line of the landed commit, when the orchestrator reported it. */
|
|
@@ -168,7 +171,7 @@ async function landedCommitSubjects(input: BranchLandedInput, log: BranchLandedL
|
|
|
168
171
|
}, log);
|
|
169
172
|
}
|
|
170
173
|
|
|
171
|
-
function resolveLandedTask(input: LandedTaskAttributionInput): {
|
|
174
|
+
export function resolveLandedTask(input: LandedTaskAttributionInput): {
|
|
172
175
|
taskId: number;
|
|
173
176
|
taskLabel: string;
|
|
174
177
|
projectId?: string;
|
|
@@ -348,79 +351,7 @@ export function announceBranchLanded(input: BranchLandedInput): void {
|
|
|
348
351
|
}
|
|
349
352
|
})());
|
|
350
353
|
|
|
351
|
-
|
|
352
|
-
const shaLabel = shortSha ? ` as \`${shortSha}\`` : "";
|
|
353
|
-
const subjectLabel = input.subject ? ` — "${input.subject}"` : "";
|
|
354
|
-
const payload = {
|
|
355
|
-
kind: "branch.landed",
|
|
356
|
-
workspaceId: workspace.id,
|
|
357
|
-
repoRoot: workspace.repoRoot,
|
|
358
|
-
branch: landedBranch,
|
|
359
|
-
base,
|
|
360
|
-
sha: input.mergedSha,
|
|
361
|
-
author,
|
|
362
|
-
newBranch: input.newBranch,
|
|
363
|
-
pushed: input.pushed,
|
|
364
|
-
...landedTaskPayload,
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
// A local-only land (push disabled, or base has no upstream) has NOT reached the
|
|
368
|
-
// remote — say so rather than implying a published merge an agent would report as
|
|
369
|
-
// shipped (#285). `undefined` (older orchestrators) keeps the message generic.
|
|
370
|
-
const publishLabel = input.pushed === false ? ` Not yet pushed to \`origin/${base}\` — local only.` : "";
|
|
371
|
-
|
|
372
|
-
// The branch author cares most — push regardless of online (store-ahead delivers it on
|
|
373
|
-
// next poll if they've moved on, #234). A live owner continues in its worktree.
|
|
374
|
-
if (author) {
|
|
375
|
-
const branchLabel = landedBranch ? `\`${landedBranch}\`` : "Your branch";
|
|
376
|
-
const continueLabel = input.newBranch
|
|
377
|
-
? ` You're now on \`${input.newBranch}\` — keep working there.`
|
|
378
|
-
: " Worktree reclaimed.";
|
|
379
|
-
// Durable (ttl null): the author's own branch landing must reach them even after they go
|
|
380
|
-
// offline (store-ahead #234) — not a bystander notice that goes stale.
|
|
381
|
-
const notices = routeNotification({
|
|
382
|
-
type: "branch.landed",
|
|
383
|
-
scope: { author, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
|
|
384
|
-
recipients: [author],
|
|
385
|
-
ttlMs: null,
|
|
386
|
-
message: {
|
|
387
|
-
subject: "Your branch landed",
|
|
388
|
-
body: renderTemplate("notify.land.success", {
|
|
389
|
-
branch: branchLabel,
|
|
390
|
-
base,
|
|
391
|
-
sha: `${shaLabel}${subjectLabel}`,
|
|
392
|
-
publishLabel,
|
|
393
|
-
continueLabel,
|
|
394
|
-
}),
|
|
395
|
-
payload: { ...payload, notification: "landed-success" },
|
|
396
|
-
idempotencyKey,
|
|
397
|
-
replyExpected: false,
|
|
398
|
-
},
|
|
399
|
-
});
|
|
400
|
-
if (landedTask && input.mergedSha) {
|
|
401
|
-
for (const notice of notices) stampLifecycleSignal(notice.to, taskSignalKey(landedTask.taskId, "landed-on-main", input.mergedSha));
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Coordinator copy (#2837), informational. No explicit recipients: the router records the rule decision (#446).
|
|
406
|
-
const mainScope = { author, repoRoot: workspace.repoRoot, workspaceId: workspace.id, teamId: landedTask?.teamId ?? (author ? getAgent(author)?.teamId : undefined), coordinatorAgentIds: [...new Set([resolveCoordinatorAgentId(author), landedTask && resolveTaskOwnerAgentId(getTaskDetail(landedTask.taskId))].filter((id): id is string => !!id && !isHumanAgentId(id)))] };
|
|
407
|
-
const branchLabel = landedBranch ? `\`${landedBranch}\`` : "A branch";
|
|
408
|
-
const authorLabel = author ? ` by \`${author}\`` : "";
|
|
409
|
-
const notices = routeNotification({
|
|
410
|
-
type: "branch.landed",
|
|
411
|
-
scope: mainScope, ...audienceWithTaskOwnerRole("branch.landed", mainScope, landedTask?.taskId), // #2862
|
|
412
|
-
ttlMs: INFORMATIONAL_TTL_MS,
|
|
413
|
-
message: {
|
|
414
|
-
subject: `Merged to ${base}`,
|
|
415
|
-
body: `🔀 ${branchLabel}${authorLabel} merged to \`${base}\`${shaLabel}${subjectLabel}.${publishLabel}`,
|
|
416
|
-
payload: { ...payload, notification: "landed-success" },
|
|
417
|
-
idempotencyKey,
|
|
418
|
-
replyExpected: false,
|
|
419
|
-
},
|
|
420
|
-
});
|
|
421
|
-
if (landedTask && input.mergedSha) {
|
|
422
|
-
for (const notice of notices) stampLifecycleSignal(notice.to, taskSignalKey(landedTask.taskId, "landed-on-main", input.mergedSha));
|
|
423
|
-
}
|
|
354
|
+
trackLandLifecycleDrive(announceVerifiedLand(input, landedTask, idempotencyKey));
|
|
424
355
|
}
|
|
425
356
|
|
|
426
357
|
/**
|
|
@@ -682,7 +613,7 @@ const DRAINED_LAND_STRAND_BOOKKEEPING = {
|
|
|
682
613
|
* (record the receipt, announce a real land, drain the steward/strand bookkeeping so no
|
|
683
614
|
* watchdog re-wakes or re-pages a branch with nothing left to land).
|
|
684
615
|
*/
|
|
685
|
-
export async function reconcileLandedWorkspace(ws: WorkspaceRecord, preview: WorkspaceMergePreview): Promise<LandedWorkspaceReconciliation> {
|
|
616
|
+
export async function reconcileLandedWorkspace(ws: WorkspaceRecord, preview: WorkspaceMergePreview, landVerifiedSha?: string): Promise<LandedWorkspaceReconciliation> {
|
|
686
617
|
const observedSha = typeof preview.prMergeSha === "string"
|
|
687
618
|
? preview.prMergeSha
|
|
688
619
|
: typeof preview.landedSha === "string"
|
|
@@ -753,7 +684,7 @@ export async function reconcileLandedWorkspace(ws: WorkspaceRecord, preview: Wor
|
|
|
753
684
|
metadata: { source: "server", maintenanceJobId: "workspace-conflict-scan", workspaceId: ws.id, fromStatus: ws.status, toStatus: nextStatus, ...(ownerAlive ? { ownerAlive: true, keptLive: true } : {}), ...(sha ? { landedSha: sha } : {}), ...(landedCommitCount === 0 ? { landedCommitCount: 0 } : {}) },
|
|
754
685
|
});
|
|
755
686
|
try {
|
|
756
|
-
notifyBranchLanded({ workspace: ws, mergedSha: sha, landedTipSha: sha, commitShas: sha ? [sha] : [], pushed: true, prLand: preview.prMerged === true, ...(landedCommitCount === 0 ? { landedCommitCount } : {}) });
|
|
687
|
+
notifyBranchLanded({ workspace: ws, mergedSha: sha, landVerifiedSha, landedTipSha: sha, commitShas: sha ? [sha] : [], pushed: true, prLand: preview.prMerged === true, ...(landedCommitCount === 0 ? { landedCommitCount } : {}) });
|
|
757
688
|
} catch {
|
|
758
689
|
// Notification is best-effort; the status change + activity event still stand.
|
|
759
690
|
}
|
package/src/bus.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Server, ServerWebSocket } from "bun";
|
|
2
|
-
import { ValidationError,
|
|
2
|
+
import { ValidationError, applyAgentLifecycleUpdateAsync, getAgent, getDb, orphanTasksForAgentAsync, recordHeartbeatAsync, validateAgentSession } from "./db";
|
|
3
3
|
import { getOutboxCursor, hasReplayGap, replayEventsSlice, type BusEvent } from "./bus-outbox";
|
|
4
4
|
import { emitAgentStatus } from "./agent-status-events";
|
|
5
5
|
import { AgentStatusSubscribers, matchesSubscription, wantsAgentStatus } from "./transport-subscriptions";
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
type BusFrame,
|
|
35
35
|
type RegisterFrame,
|
|
36
36
|
} from "@humwick/sdk/protocol";
|
|
37
|
-
import { errMessage, isRecord } from "@humwick/sdk";
|
|
37
|
+
import { errMessage, fireAndForget, isRecord } from "@humwick/sdk";
|
|
38
38
|
import { getComponentAuth, isComponentAuthorizedFor, isAuthorized, isOriginAllowed, unauthorized } from "./security";
|
|
39
39
|
import type { AgentCard, ComponentToken, ContextState, Message, ProviderCapabilities, QuotaState, RegisterAgentInput, Task } from "./types";
|
|
40
40
|
|
|
@@ -138,8 +138,11 @@ export function busHandleClose(ws: BusWebSocket): void {
|
|
|
138
138
|
// on the establishing socket id, so a foreign socket's close cannot evict the owner's binding and a stale
|
|
139
139
|
// close cannot evict a newer register's binding).
|
|
140
140
|
if (!conn.transportReconnect) {
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
const agentId = conn.agentId;
|
|
142
|
+
void fireAndForget(`[bus] mark ${agentId} stale on close`, async () => {
|
|
143
|
+
await applyAgentLifecycleUpdateAsync(agentId, { status: "stale" }, { instanceId: conn.instanceId, epoch: conn.epoch });
|
|
144
|
+
queueAgentStatusEvent(agentId);
|
|
145
|
+
});
|
|
143
146
|
}
|
|
144
147
|
}
|
|
145
148
|
}
|
|
@@ -162,7 +165,7 @@ export async function expireStaleBusAgents(graceMs = busStaleGraceMs(), lagGrace
|
|
|
162
165
|
// but do NOT revoke the token — a reconnect accepts the EXISTING token and re-marks
|
|
163
166
|
// live. The #484 restart-strand revoke now fires on instance takeover in registerAgent
|
|
164
167
|
// (a fresh instanceId → epoch bump), never on a heartbeat lapse.
|
|
165
|
-
|
|
168
|
+
await applyAgentLifecycleUpdateAsync(row.id, { status: "offline" });
|
|
166
169
|
await emitAgentStatus(row.id);
|
|
167
170
|
const tasks = await orphanTasksForAgentAsync(row.id);
|
|
168
171
|
for (const task of tasks) {
|
|
@@ -187,7 +190,7 @@ async function handleFrame(ws: BusWebSocket, frame: ReturnType<typeof validateCl
|
|
|
187
190
|
await handleRegister(ws, frame);
|
|
188
191
|
return;
|
|
189
192
|
case "heartbeat":
|
|
190
|
-
await withRegistered(ws, frame, (conn) => {
|
|
193
|
+
await withRegistered(ws, frame, async (conn) => {
|
|
191
194
|
if (conn.agentId) {
|
|
192
195
|
// #633 — a runner whose provider terminally exited stays alive and keeps sending idle
|
|
193
196
|
// keepalives (semanticStatus is never "offline"). Those must NOT resurrect an agent the
|
|
@@ -199,11 +202,10 @@ async function handleFrame(ws: BusWebSocket, frame: ReturnType<typeof validateCl
|
|
|
199
202
|
if (!heldOffline) {
|
|
200
203
|
const wasUnavailable = current ? isAgentUnavailable(current) : false;
|
|
201
204
|
const runtime = runtimeFromMeta(frame.payload.meta);
|
|
202
|
-
// #1468 — coalesce the steady-state liveness write;
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
recordHeartbeat(conn.agentId, { guard: { instanceId: conn.instanceId, epoch: conn.epoch }, status: frame.payload.status, runtime });
|
|
205
|
+
// #1468 — coalesce the steady-state liveness write; only a status change / resurrection
|
|
206
|
+
// writes (last_seen + runtime), on the writer thread (#1870 S7). This replaces the old
|
|
207
|
+
// heartbeat()+setStatus() pair that rewrote the row twice per beat even when nothing changed.
|
|
208
|
+
await recordHeartbeatAsync(conn.agentId, { guard: { instanceId: conn.instanceId, epoch: conn.epoch }, status: frame.payload.status, runtime });
|
|
207
209
|
queueAgentStatusEvent(conn.agentId);
|
|
208
210
|
// #416 — flush queued messages if the agent transitioned from unavailable to available.
|
|
209
211
|
if (wasUnavailable) {
|
|
@@ -225,19 +227,16 @@ async function handleFrame(ws: BusWebSocket, frame: ReturnType<typeof validateCl
|
|
|
225
227
|
await withRegistered(ws, frame, async (conn) => {
|
|
226
228
|
if (!conn.agentId) return sendError(ws, frame.id, "AGENT_REQUIRED", "status frames require an agent registration");
|
|
227
229
|
const guard = { instanceId: conn.instanceId, epoch: conn.epoch };
|
|
228
|
-
// #
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
// thread hop mid-frame, so routing these writes through their async facades would not move
|
|
232
|
-
// them off-loop — it would only add a round-trip.
|
|
233
|
-
// #2111 — but the three writes ARE now one. This used to be getAgent(before) + setStatus
|
|
230
|
+
// #1870 S7 — the per-status-frame write runs on the writer thread; this socket's FIFO frame
|
|
231
|
+
// queue keeps its frames ordered across the hop, and the lifecycle ordinal fences it against
|
|
232
|
+
// every other lifecycle writer. #2111 — the three writes are one. This used to be getAgent(before) + setStatus
|
|
234
233
|
// (guard read + before read + UPDATE in its own BEGIN IMMEDIATE) + markReady (guard read +
|
|
235
234
|
// UPDATE) + mergeAgentMeta (two reads + UPDATE) + getAgent(after): ~7 row reads and 3 UPDATEs
|
|
236
235
|
// of one row per frame. `applyAgentLifecycleUpdate` does one guard read, one UPDATE and hands
|
|
237
236
|
// back the before/after cards the audit calls below need. The flap ledger is different: it is
|
|
238
237
|
// a cold durable history and must stay ordered with its status edge, so await it within this
|
|
239
238
|
// socket's existing FIFO frame queue.
|
|
240
|
-
const applied =
|
|
239
|
+
const applied = await applyAgentLifecycleUpdateAsync(
|
|
241
240
|
conn.agentId,
|
|
242
241
|
{ status: frame.payload.agentStatus, ready: frame.payload.ready, meta: frame.payload.meta },
|
|
243
242
|
guard,
|
|
@@ -378,7 +377,7 @@ async function handleRegister(ws: BusWebSocket, frame: RegisterFrame): Promise<v
|
|
|
378
377
|
machine: payload.machine,
|
|
379
378
|
capabilities: payload.capabilities,
|
|
380
379
|
status: runnerManaged ? "idle" : "online",
|
|
381
|
-
ready:
|
|
380
|
+
ready: !runnerManaged,
|
|
382
381
|
instanceId: payload.instanceId,
|
|
383
382
|
...(providerCapabilities ? { providerCapabilities } : {}),
|
|
384
383
|
...(context ? { context } : {}),
|
|
@@ -400,7 +399,7 @@ async function handleRegister(ws: BusWebSocket, frame: RegisterFrame): Promise<v
|
|
|
400
399
|
// newly registered agent stale. Fence the install on liveness and clean up the provisional
|
|
401
400
|
// registration with the same instance/epoch guard used by an established connection's close.
|
|
402
401
|
if (!liveBusSocketIds.has(ws.data.id)) {
|
|
403
|
-
if (payload.agentId &&
|
|
402
|
+
if (payload.agentId && (await applyAgentLifecycleUpdateAsync(payload.agentId, { status: "stale" }, { instanceId: payload.instanceId, epoch })).changed) {
|
|
404
403
|
queueAgentStatusEvent(payload.agentId);
|
|
405
404
|
}
|
|
406
405
|
return;
|
package/src/cd-deploy-notify.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { getDeployEventForCommitAsync, deployChangesForCommitsAsync } from "./db/deploy-events-async";
|
|
3
|
+
import type { DeployChanges } from "./db/deploy-events";
|
|
4
|
+
import { runGit } from "./services/git-process";
|
|
1
5
|
import { VERSION } from "./config";
|
|
2
6
|
import { reportedGitSha } from "./git-sha";
|
|
3
7
|
import { routeNotification } from "./notification-router";
|
|
@@ -17,6 +21,7 @@ registerDefaultAudienceRules("cd.deploy_live", [audiencePredicates.persistentCoo
|
|
|
17
21
|
export interface CdDeployLiveInput {
|
|
18
22
|
/** Defaults to this process's package version. */
|
|
19
23
|
version?: string;
|
|
24
|
+
repoRoot?: string;
|
|
20
25
|
/** Defaults to the running process's attested commit sha (undefined on an npm install). */
|
|
21
26
|
gitSha?: string;
|
|
22
27
|
}
|
|
@@ -32,7 +37,7 @@ export interface CdDeployLiveInput {
|
|
|
32
37
|
* there is no stable identity to key the one-per-deploy dedup on, and this notice is specifically
|
|
33
38
|
* about the CD-deploy restart glitch that only that deployment model produces.
|
|
34
39
|
*/
|
|
35
|
-
export function notifyCdDeployLive(input: CdDeployLiveInput = {}): void {
|
|
40
|
+
export async function notifyCdDeployLive(input: CdDeployLiveInput = {}): Promise<void> {
|
|
36
41
|
const version = input.version ?? VERSION;
|
|
37
42
|
const gitSha = input.gitSha ?? reportedGitSha();
|
|
38
43
|
if (!gitSha) {
|
|
@@ -43,6 +48,7 @@ export function notifyCdDeployLive(input: CdDeployLiveInput = {}): void {
|
|
|
43
48
|
return;
|
|
44
49
|
}
|
|
45
50
|
const shortSha = gitSha.slice(0, 12);
|
|
51
|
+
const contents = await deployNoticeContents(gitSha, input.repoRoot ?? resolve(import.meta.dir, ".."));
|
|
46
52
|
|
|
47
53
|
routeNotification({
|
|
48
54
|
type: "cd.deploy_live",
|
|
@@ -50,10 +56,32 @@ export function notifyCdDeployLive(input: CdDeployLiveInput = {}): void {
|
|
|
50
56
|
ttlMs: null,
|
|
51
57
|
message: {
|
|
52
58
|
subject: `Humwick ${version} is now running`,
|
|
53
|
-
body: `Humwick v${version} (\`${shortSha}\`) is now running after a restart.
|
|
59
|
+
body: `Humwick v${version} (\`${shortSha}\`) is now running after a restart. Interrupted tool calls were the deploy, not an incident; retry them.${contents}`,
|
|
54
60
|
payload: { kind: "cd.deploy-live", version, gitSha },
|
|
55
61
|
idempotencyKey: `cd-deploy-live:${gitSha}`,
|
|
56
62
|
replyExpected: false,
|
|
57
63
|
},
|
|
58
64
|
});
|
|
59
65
|
}
|
|
66
|
+
|
|
67
|
+
async function deployNoticeContents(gitSha: string, repoRoot: string): Promise<string> {
|
|
68
|
+
const event = await getDeployEventForCommitAsync(gitSha);
|
|
69
|
+
if (!event) return "";
|
|
70
|
+
let changes: DeployChanges | undefined = event.changes;
|
|
71
|
+
if (changes?.state !== "ready") {
|
|
72
|
+
if (!event.previousSha) return "\nContents unavailable: no previous deployed SHA.";
|
|
73
|
+
const result = await runGit(["-C", repoRoot, "log", "--format=%H%x00%s", `${event.previousSha}..${gitSha}`, "--"], 5000, 1024 * 1024);
|
|
74
|
+
if (result.unavailable || result.exitCode !== 0) return "\nContents unavailable: deploy range could not be read.";
|
|
75
|
+
const commits = result.stdout.trim().split("\n").filter(Boolean).map(line => {
|
|
76
|
+
const [sha, ...subject] = line.split("\x00");
|
|
77
|
+
return { sha: sha!, subject: subject.join("\x00") };
|
|
78
|
+
});
|
|
79
|
+
changes = { state: "ready", ...await deployChangesForCommitsAsync(commits) };
|
|
80
|
+
}
|
|
81
|
+
const lines = changes.tasks.flatMap(task => [
|
|
82
|
+
`T${task.id} ${task.label.replace(/\s+/g, " ")}${task.issues.length ? ` — ${task.issues.map(issue => `${issue.repo}#${issue.externalKey} (${issue.relation ?? "references"})`).join(", ")}` : ""}`,
|
|
83
|
+
...(task.onDeploy ? [` onDeploy: ${task.onDeploy.replace(/\s+/g, " ")}`] : []),
|
|
84
|
+
]);
|
|
85
|
+
lines.push(`${changes.unmatchedCommits.length} non-Task commit(s).`);
|
|
86
|
+
return `\n${lines.join("\n")}`;
|
|
87
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { resolveCdFleetAllowlist } from "../scripts/cd-config.ts";
|
|
2
2
|
import { isCdFleetHostAllowlisted } from "../scripts/cd-fleet-membership.ts";
|
|
3
|
-
import { getConfig
|
|
4
|
-
import {
|
|
3
|
+
import { getConfig } from "./config-store.ts";
|
|
4
|
+
import { setConfigAsync } from "./config-store-write.ts";
|
|
5
|
+
import { createActivityEventAsync } from "./db/activity-async.ts";
|
|
5
6
|
import { computeFleetConvergenceAsync, type FleetConvergence } from "./fleet-convergence.ts";
|
|
6
|
-
import {
|
|
7
|
+
import { raiseOperatorAlarmAsync } from "./operator-alarm.ts";
|
|
7
8
|
import { listOrchestratorsAsync } from "./db/orchestrators-async.ts";
|
|
8
9
|
|
|
9
10
|
/** Server-side: a durable alarm per lag episode, and a recovery record when it clears. */
|
|
@@ -17,7 +18,7 @@ export async function notifyCdHostLag(convergence?: FleetConvergence, allowlist
|
|
|
17
18
|
// Keep the episode's identity while targets advance, until this Host catches up.
|
|
18
19
|
const since = previous[host.id] ?? host.lag.since;
|
|
19
20
|
current[host.id] = since;
|
|
20
|
-
|
|
21
|
+
await raiseOperatorAlarmAsync({ clientId: `cd-host-lag:${host.id}:${since}`, source: "cd-host-lag", view: "deploys",
|
|
21
22
|
subject: `CD deploy lag on ${host.id}`, dedupeEvent: true,
|
|
22
23
|
detail: `${host.id} is ${Math.floor(host.lag.ageMs / 60_000)} minutes behind ${status.targetSha.slice(0, 12)}: ${host.deferred?.reason ?? host.reason}`,
|
|
23
24
|
metadata: { orchestratorId: host.id, since, targetSha: status.targetSha, deferred: host.deferred } });
|
|
@@ -29,8 +30,8 @@ export async function notifyCdHostLag(convergence?: FleetConvergence, allowlist
|
|
|
29
30
|
if (!host || !isCdFleetHostAllowlisted(host, allowlist)) { delete current[id]; continue; }
|
|
30
31
|
if (!status.targetSha || host.gitSha !== status.targetSha) { current[id] = since; continue; }
|
|
31
32
|
delete current[id];
|
|
32
|
-
|
|
33
|
+
await createActivityEventAsync({ clientId: `cd-host-lag-resolved:${id}:${since}`, kind: "state", view: "deploys",
|
|
33
34
|
title: `CD deploy lag cleared on ${id}`, metadata: { source: "cd-host-lag", orchestratorId: id, since, resolved: true } });
|
|
34
35
|
}
|
|
35
|
-
if (JSON.stringify(current) !== JSON.stringify(previous))
|
|
36
|
+
if (JSON.stringify(current) !== JSON.stringify(previous)) await setConfigAsync("cd-host-lag", "alarms", current);
|
|
36
37
|
}
|
package/src/ci/incidents.ts
CHANGED
|
@@ -169,7 +169,7 @@ export function ingestCiObservation(observation: CiObservation, now: number = Da
|
|
|
169
169
|
|
|
170
170
|
/** Gateway-routed form for async-capable polling jobs. The complete observation transition is one
|
|
171
171
|
* writer operation, so no gateway await exposes a stale watch state to a later incident decision. */
|
|
172
|
-
export async function ingestCiObservationAsync(observation: CiObservation): Promise<CiIncidentIngestResult> {
|
|
172
|
+
export async function ingestCiObservationAsync(observation: CiObservation, now?: number): Promise<CiIncidentIngestResult> {
|
|
173
173
|
const transition = transitionInput(observation);
|
|
174
|
-
return formatTransitionResult(await ingestCiObservationTransitionAsync(transition.input), transition.watchKey, transition.fingerprint);
|
|
174
|
+
return formatTransitionResult(await ingestCiObservationTransitionAsync(transition.input, now), transition.watchKey, transition.fingerprint);
|
|
175
175
|
}
|
package/src/ci/monitor.ts
CHANGED
|
@@ -12,8 +12,8 @@ import { isPathWithinBase } from "../utils";
|
|
|
12
12
|
import { computeCiWatchKey, isFailingJob } from "./incidents.ts";
|
|
13
13
|
import { ghApi, type GitHubApi } from "../issue-tracker/github.ts";
|
|
14
14
|
import { resolveCoordinatorAgentId } from "../steward-identity.ts";
|
|
15
|
-
import { notifyCiRootMissing,
|
|
16
|
-
import { notifyCiTransition,
|
|
15
|
+
import { notifyCiRootMissing, type CiRootMissing } from "./root-missing.ts";
|
|
16
|
+
import { notifyCiTransition, type CiTransitionNotifyResult } from "./transition-notify.ts";
|
|
17
17
|
import { workflowNotTriggered } from "./not-triggered.ts";
|
|
18
18
|
import type { CiJobRef, CiObservation, CiObservationStatus, CiRunRef } from "./provider.ts";
|
|
19
19
|
|
|
@@ -343,7 +343,7 @@ export interface CiMonitorDeps {
|
|
|
343
343
|
rootsForProject: (projectId: string) => ProjectRoot[];
|
|
344
344
|
loadConfig: (root: ProjectRoot) => CiMonitorConfig | null | Promise<CiMonitorConfig | null>;
|
|
345
345
|
api: GitHubApi;
|
|
346
|
-
notify: typeof notifyCiTransition
|
|
346
|
+
notify: (...args: Parameters<typeof notifyCiTransition>) => CiTransitionNotifyResult | Promise<CiTransitionNotifyResult>;
|
|
347
347
|
resolveCoordinator: (rootPath: string) => string | undefined;
|
|
348
348
|
/** Commits workflows wait on: each watch of their root reports its verdict on them too. */
|
|
349
349
|
shaWaits: () => Promise<CiShaWait[]>;
|
|
@@ -419,7 +419,6 @@ export async function runCiMonitor(input: Partial<CiMonitorDeps> = {}): Promise<
|
|
|
419
419
|
readinessMs: 0, historyMs: 0, historyApiCalls: 0,
|
|
420
420
|
transitions: { went_red: 0, back_green: 0, never_matched: 0, config_mismatch: 0, none: 0 },
|
|
421
421
|
};
|
|
422
|
-
const useAsync = deps.notify === notifyCiTransition && !deps.now;
|
|
423
422
|
// A repository counts as missing only if no root of it has the config, so a duplicate root cannot flap the notice.
|
|
424
423
|
const missingRoots = new Map<string, CiRootMissing>();
|
|
425
424
|
const presentRepositories = new Set<string>();
|
|
@@ -464,9 +463,7 @@ export async function runCiMonitor(input: Partial<CiMonitorDeps> = {}): Promise<
|
|
|
464
463
|
const observation = await observeGitHubCiMonitorWatch(repository, watch, deps.api, runsPayload);
|
|
465
464
|
if (deps.signal?.aborted) break;
|
|
466
465
|
const notificationDeps = { resolveCoordinator: () => deps.resolveCoordinator(root.rootPath), ...(deps.now ? { now: deps.now() } : {}) };
|
|
467
|
-
const transition =
|
|
468
|
-
? await notifyCiTransitionAsync(observation, notificationDeps)
|
|
469
|
-
: deps.notify(observation, notificationDeps);
|
|
466
|
+
const transition = await deps.notify(observation, notificationDeps);
|
|
470
467
|
result.transitions[transition.transition] += 1;
|
|
471
468
|
// The branch-tip row keys the watch; the same key carries its verdict on each waited commit.
|
|
472
469
|
const watchKey = computeCiWatchKey(observation);
|
|
@@ -495,7 +492,7 @@ export async function runCiMonitor(input: Partial<CiMonitorDeps> = {}): Promise<
|
|
|
495
492
|
if (presentRepositories.has(key) || deps.signal?.aborted) continue;
|
|
496
493
|
const notifyDeps = { resolveCoordinator: () => deps.resolveCoordinator(missing.rootPath), ...(deps.now ? { now: deps.now() } : {}) };
|
|
497
494
|
try {
|
|
498
|
-
if (
|
|
495
|
+
if (await notifyCiRootMissing(missing, notifyDeps)) result.missingRoots += 1;
|
|
499
496
|
} catch (error) {
|
|
500
497
|
result.errors += 1;
|
|
501
498
|
console.warn(`ci monitor ${missing.repository} root-missing notice skipped: ${errMessage(error)}`);
|
package/src/ci/root-missing.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { markCiRootMissingNotified } from "../db/ci-incidents.ts";
|
|
2
1
|
import { markCiRootMissingNotifiedAsync } from "../db/ci-incidents-async.ts";
|
|
3
2
|
import { routeCiIncidentNotification } from "./notifications.ts";
|
|
4
3
|
import { resolveCoordinator, type CiTransitionNotifyDeps } from "./transition-notify.ts";
|
|
@@ -26,10 +25,6 @@ function notify(input: CiRootMissing, deps: Deps, fired: boolean): boolean {
|
|
|
26
25
|
return true;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
export function notifyCiRootMissing(input: CiRootMissing, deps: Deps = {}): boolean {
|
|
30
|
-
return notify(input, deps,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function notifyCiRootMissingAsync(input: CiRootMissing, deps: Deps = {}): Promise<boolean> {
|
|
34
|
-
return notify(input, deps, await markCiRootMissingNotifiedAsync(input.repository));
|
|
28
|
+
export async function notifyCiRootMissing(input: CiRootMissing, deps: Deps = {}): Promise<boolean> {
|
|
29
|
+
return notify(input, deps, await markCiRootMissingNotifiedAsync(input.repository, deps.now));
|
|
35
30
|
}
|