@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,953 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { landingIncidentEntities, landingIncidentOps } from "./landing-incident";
|
|
3
|
-
import { type EntityStore, type JsonValue, type ModuleCtx, type StepResult, OpError, defineModule } from "./contract";
|
|
4
|
-
|
|
5
|
-
export const LANDING_STATES = [
|
|
6
|
-
"controller_wait",
|
|
7
|
-
"queued",
|
|
8
|
-
"supersession_wait",
|
|
9
|
-
"lane_wait",
|
|
10
|
-
"authorization_required",
|
|
11
|
-
"preparing",
|
|
12
|
-
"gating",
|
|
13
|
-
"gate_failed",
|
|
14
|
-
"publish_ready",
|
|
15
|
-
"publishing",
|
|
16
|
-
"verifying",
|
|
17
|
-
"cleanup",
|
|
18
|
-
"receipt_ready",
|
|
19
|
-
"external_retire_pending",
|
|
20
|
-
"retry_wait",
|
|
21
|
-
"reconciliation_required",
|
|
22
|
-
"supersede_pending",
|
|
23
|
-
"cancel_pending",
|
|
24
|
-
"landed",
|
|
25
|
-
"superseded",
|
|
26
|
-
"canceled",
|
|
27
|
-
"failed",
|
|
28
|
-
] as const;
|
|
29
|
-
|
|
30
|
-
const landingStateSchema = z.enum(LANDING_STATES);
|
|
31
|
-
const terminalProcessProofSchema = z.enum(["never_started", "completed", "process_tree_dead"]);
|
|
32
|
-
|
|
33
|
-
const workspaceHeadSchema = z.object({
|
|
34
|
-
id: z.string(),
|
|
35
|
-
workspaceId: z.string(),
|
|
36
|
-
currentRunId: z.string(),
|
|
37
|
-
generation: z.number().int(),
|
|
38
|
-
authorizationEventId: z.number().int(),
|
|
39
|
-
updatedAt: z.number().int(),
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const runSchema = z.object({
|
|
43
|
-
id: z.string(),
|
|
44
|
-
workspaceId: z.string(),
|
|
45
|
-
laneKey: z.string().optional(),
|
|
46
|
-
generation: z.number().int(),
|
|
47
|
-
authorizationEventId: z.number().int(),
|
|
48
|
-
workflowInstanceId: z.string().optional(),
|
|
49
|
-
sourceRef: z.string(),
|
|
50
|
-
sourceHeadSha: z.string(),
|
|
51
|
-
repoRoot: z.string(),
|
|
52
|
-
remoteIdentity: z.string(),
|
|
53
|
-
baseRef: z.string(),
|
|
54
|
-
strategy: z.string(),
|
|
55
|
-
gateSnapshot: z.string(),
|
|
56
|
-
requesterSnapshot: z.string(),
|
|
57
|
-
baseHeadSha: z.string().optional(),
|
|
58
|
-
candidateHeadSha: z.string().optional(),
|
|
59
|
-
candidateTreeSha: z.string().optional(),
|
|
60
|
-
pushRemote: z.string().optional(),
|
|
61
|
-
selectedOrchestratorId: z.string().optional(),
|
|
62
|
-
selectedCapabilityDigest: z.string().optional(),
|
|
63
|
-
selectedSafetyDigest: z.string().optional(),
|
|
64
|
-
candidateWorktreePath: z.string().optional(),
|
|
65
|
-
executionTreeHandle: z.string().optional(),
|
|
66
|
-
controllerVersion: z.number().int().optional(),
|
|
67
|
-
controllerToken: z.string().optional(),
|
|
68
|
-
laneToken: z.string().optional(),
|
|
69
|
-
gateTreeSha: z.string().optional(),
|
|
70
|
-
gateConfigDigest: z.string().optional(),
|
|
71
|
-
gateAttestation: z.string().optional(),
|
|
72
|
-
receiptId: z.string().optional(),
|
|
73
|
-
receiptTargetSha: z.string().optional(),
|
|
74
|
-
receiptVerifiedAt: z.number().int().optional(),
|
|
75
|
-
receiptVerificationGeneration: z.number().int().optional(),
|
|
76
|
-
receiptKind: z.enum(["kernel", "external"]).optional(),
|
|
77
|
-
state: landingStateSchema,
|
|
78
|
-
stateVersion: z.number().int(),
|
|
79
|
-
stateSince: z.number().int(),
|
|
80
|
-
queuedAt: z.number().int(),
|
|
81
|
-
attempt: z.number().int(),
|
|
82
|
-
phaseOrdinal: z.number().int(),
|
|
83
|
-
commandGeneration: z.number().int(),
|
|
84
|
-
// Optional for migration safety: Phase 3 rows predate this dedicated legacy projection clock.
|
|
85
|
-
// commandGeneration remains reserved for the Phase 4 Kernel command fence.
|
|
86
|
-
legacyProgressSequence: z.number().int().optional(),
|
|
87
|
-
commandId: z.string().optional(),
|
|
88
|
-
lastDryRunCommandId: z.string().optional(),
|
|
89
|
-
nextRetryAt: z.number().int().optional(),
|
|
90
|
-
transientFailures: z.number().int(),
|
|
91
|
-
gateFailures: z.number().int(),
|
|
92
|
-
pushRaces: z.number().int(),
|
|
93
|
-
executorRecoveries: z.number().int(),
|
|
94
|
-
blockerCode: z.string().optional(),
|
|
95
|
-
blockerDetail: z.string().optional(),
|
|
96
|
-
blockedAt: z.number().int().optional(),
|
|
97
|
-
supersededBy: z.string().optional(),
|
|
98
|
-
landedSha: z.string().optional(),
|
|
99
|
-
landedAt: z.number().int().optional(),
|
|
100
|
-
terminalProcessProof: terminalProcessProofSchema.optional(),
|
|
101
|
-
legacyCommandId: z.string().optional(),
|
|
102
|
-
legacyStatus: z.string().optional(),
|
|
103
|
-
shadowVerdict: z.enum(["parity", "mismatch"]).optional(),
|
|
104
|
-
shadowSummaryDigest: z.string().optional(),
|
|
105
|
-
lastComparisonDigest: z.string().optional(),
|
|
106
|
-
lastComparisonVersion: z.number().int().optional(),
|
|
107
|
-
consecutiveStaleComparisons: z.number().int().optional(),
|
|
108
|
-
shadowDetail: z.string().optional(),
|
|
109
|
-
durableExitKind: z.enum(["command_wait", "event_wait", "sleep", "supervisor_reconcile", "explicit_action"]).optional(),
|
|
110
|
-
observationGeneration: z.number().int().optional(),
|
|
111
|
-
lastObservedAt: z.number().int().optional(),
|
|
112
|
-
nextObservationAt: z.number().int().optional(),
|
|
113
|
-
lastObservationDigest: z.string().optional(),
|
|
114
|
-
observationRepeatCount: z.number().int().optional(),
|
|
115
|
-
predictedOutcome: z.enum(["none", "external_retire", "reconciliation_required"]).optional(),
|
|
116
|
-
lastReconciledAt: z.number().int().optional(),
|
|
117
|
-
createdAt: z.number().int(),
|
|
118
|
-
updatedAt: z.number().int(),
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const laneSchema = z.object({
|
|
122
|
-
id: z.string(),
|
|
123
|
-
laneKey: z.string(),
|
|
124
|
-
repoRoot: z.string(),
|
|
125
|
-
remoteIdentity: z.string(),
|
|
126
|
-
baseRef: z.string(),
|
|
127
|
-
identityDigest: z.string(),
|
|
128
|
-
currentRunId: z.string().optional(),
|
|
129
|
-
retiringRunId: z.string().optional(),
|
|
130
|
-
laneToken: z.string().optional(),
|
|
131
|
-
leaseExpiresAt: z.number().int().optional(),
|
|
132
|
-
activeCommandId: z.string().optional(),
|
|
133
|
-
activeExecutionGeneration: z.number().int().optional(),
|
|
134
|
-
drainEpochId: z.string().optional(),
|
|
135
|
-
lastTerminalProcessProof: terminalProcessProofSchema.optional(),
|
|
136
|
-
updatedAt: z.number().int(),
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const noticeSchema = z.object({
|
|
140
|
-
id: z.string(),
|
|
141
|
-
runId: z.string(),
|
|
142
|
-
stateVersion: z.number().int(),
|
|
143
|
-
audience: z.string(),
|
|
144
|
-
kind: z.string(),
|
|
145
|
-
status: z.enum(["pending", "sent"]),
|
|
146
|
-
payload: z.string(),
|
|
147
|
-
createdAt: z.number().int(),
|
|
148
|
-
sentAt: z.number().int().optional(),
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const effectSchema = z.object({
|
|
152
|
-
id: z.string(),
|
|
153
|
-
runId: z.string(),
|
|
154
|
-
stateVersion: z.number().int(),
|
|
155
|
-
kind: z.string(),
|
|
156
|
-
status: z.enum(["pending", "completed"]),
|
|
157
|
-
payload: z.string(),
|
|
158
|
-
createdAt: z.number().int(),
|
|
159
|
-
completedAt: z.number().int().optional(),
|
|
160
|
-
deliveryKey: z.string().optional(),
|
|
161
|
-
claimedAt: z.number().int().optional(),
|
|
162
|
-
attempts: z.number().int().optional(),
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
const comparisonSchema = z.object({
|
|
166
|
-
id: z.string(),
|
|
167
|
-
runId: z.string(),
|
|
168
|
-
generation: z.number().int(),
|
|
169
|
-
comparisonVersion: z.number().int(),
|
|
170
|
-
verdict: z.enum(["parity", "mismatch", "stale"]),
|
|
171
|
-
legacyState: z.string(),
|
|
172
|
-
shadowState: landingStateSchema,
|
|
173
|
-
summaryDigest: z.string(),
|
|
174
|
-
detail: z.string(),
|
|
175
|
-
createdAt: z.number().int(),
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const targetObservationSchema = z.object({
|
|
179
|
-
id: z.string(),
|
|
180
|
-
runId: z.string(),
|
|
181
|
-
generation: z.number().int(),
|
|
182
|
-
observationGeneration: z.number().int(),
|
|
183
|
-
sourceHeadSha: z.string(),
|
|
184
|
-
candidateHeadSha: z.string().optional(),
|
|
185
|
-
targetRef: z.string(),
|
|
186
|
-
targetHeadSha: z.string().optional(),
|
|
187
|
-
sourceContained: z.boolean().optional(),
|
|
188
|
-
candidateContained: z.boolean().optional(),
|
|
189
|
-
containmentMethod: z.string().optional(),
|
|
190
|
-
status: z.enum(["positive", "negative", "unknown"]),
|
|
191
|
-
observedAt: z.number().int(),
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
const receiptSchema = z.object({
|
|
195
|
-
id: z.string(),
|
|
196
|
-
runId: z.string(),
|
|
197
|
-
workspaceId: z.string(),
|
|
198
|
-
generation: z.number().int(),
|
|
199
|
-
laneKey: z.string(),
|
|
200
|
-
kind: z.enum(["kernel", "external"]),
|
|
201
|
-
sourceRef: z.string(),
|
|
202
|
-
sourceHeadSha: z.string(),
|
|
203
|
-
targetRef: z.string(),
|
|
204
|
-
targetHeadSha: z.string(),
|
|
205
|
-
candidateHeadSha: z.string().optional(),
|
|
206
|
-
gateAttestation: z.string().optional(),
|
|
207
|
-
verificationGeneration: z.number().int(),
|
|
208
|
-
verifiedAt: z.number().int(),
|
|
209
|
-
createdAt: z.number().int(),
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
type LandingStores = {
|
|
213
|
-
workspaceHead: EntityStore<{ schema: typeof workspaceHeadSchema }>;
|
|
214
|
-
run: EntityStore<{ schema: typeof runSchema }>;
|
|
215
|
-
lane: EntityStore<{ schema: typeof laneSchema }>;
|
|
216
|
-
notice: EntityStore<{ schema: typeof noticeSchema }>;
|
|
217
|
-
effect: EntityStore<{ schema: typeof effectSchema }>;
|
|
218
|
-
targetObservation: EntityStore<{ schema: typeof targetObservationSchema }>;
|
|
219
|
-
comparison: EntityStore<{ schema: typeof comparisonSchema }>;
|
|
220
|
-
receipt: EntityStore<{ schema: typeof receiptSchema }>;
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
function stores(ctx: ModuleCtx): LandingStores {
|
|
224
|
-
return ctx.entities as unknown as LandingStores;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export function landingRunId(workspaceId: string, authorizationEventId: number): string {
|
|
228
|
-
return `land:${workspaceId}:event:${authorizationEventId}`;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const frozenIntentSchema = z.object({
|
|
232
|
-
repoRoot: z.string().min(1),
|
|
233
|
-
remoteIdentity: z.string().min(1),
|
|
234
|
-
baseRef: z.string().min(1),
|
|
235
|
-
strategy: z.string().min(1),
|
|
236
|
-
gateSnapshot: z.record(z.string(), z.unknown()),
|
|
237
|
-
requester: z.object({ actor: z.string().min(1), agentId: z.string().optional() }),
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
const admissionInputSchema = z.object({
|
|
241
|
-
authorizationEventId: z.number().int().positive(),
|
|
242
|
-
workspaceId: z.string().min(1),
|
|
243
|
-
laneKey: z.string().min(1).optional(),
|
|
244
|
-
sourceRef: z.string().min(1),
|
|
245
|
-
sourceHeadSha: z.string().min(1),
|
|
246
|
-
queuedAt: z.number().int(),
|
|
247
|
-
frozenIntent: frozenIntentSchema,
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
const dryRunTupleSchema = z.object({
|
|
251
|
-
runId: z.string(), generation: z.number().int(), commandGeneration: z.number().int(),
|
|
252
|
-
attempt: z.number().int(), phaseOrdinal: z.number().int(), stateVersion: z.number().int(),
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
const dryRunCommandResultInputSchema = z.object({
|
|
256
|
-
commandId: z.string().min(1),
|
|
257
|
-
type: z.enum(["landing.prepare", "landing.gate", "landing.publish", "landing.verify", "landing.observe-target", "landing.cleanup"]),
|
|
258
|
-
tuple: dryRunTupleSchema,
|
|
259
|
-
terminalProcessProof: z.literal("completed"),
|
|
260
|
-
result: z.record(z.string(), z.unknown()),
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
const admissionOutputSchema = z.object({
|
|
264
|
-
runId: z.string(),
|
|
265
|
-
generation: z.number().int(),
|
|
266
|
-
state: landingStateSchema,
|
|
267
|
-
current: z.boolean(),
|
|
268
|
-
workflowInstanceId: z.string().optional(),
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const observationInputSchema = z.object({
|
|
272
|
-
runId: z.string(),
|
|
273
|
-
expectedGeneration: z.number().int(),
|
|
274
|
-
expectedStateVersion: z.number().int(),
|
|
275
|
-
observationGeneration: z.number().int().positive(),
|
|
276
|
-
factDigest: z.string().min(1).max(200),
|
|
277
|
-
sourceHeadSha: z.string(),
|
|
278
|
-
candidateHeadSha: z.string().optional(),
|
|
279
|
-
targetRef: z.string(),
|
|
280
|
-
targetHeadSha: z.string().optional(),
|
|
281
|
-
sourceContained: z.boolean().optional(),
|
|
282
|
-
candidateContained: z.boolean().optional(),
|
|
283
|
-
containmentMethod: z.string().optional(),
|
|
284
|
-
status: z.enum(["positive", "negative", "unknown"]),
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
const finalizeControlInputSchema = z.object({
|
|
288
|
-
runId: z.string(), expectedGeneration: z.number().int(), expectedStateVersion: z.number().int(),
|
|
289
|
-
commandId: z.string(), terminalProcessProof: terminalProcessProofSchema,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
const landingHostCommandInputSchema = z.object({
|
|
293
|
-
type: z.enum(["landing.prepare", "landing.gate", "landing.publish", "landing.verify", "landing.cleanup"]),
|
|
294
|
-
params: z.record(z.string(), z.unknown()),
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
const LANDING_ADMIN_GRANT = "landing:admin" as const;
|
|
298
|
-
|
|
299
|
-
type ControllerWaitState = { runId: string; commandId?: string };
|
|
300
|
-
type LandingState = z.output<typeof landingStateSchema>;
|
|
301
|
-
type LandingControlAction = "cancel" | "fail" | "supersede" | "retry" | "reconcile";
|
|
302
|
-
|
|
303
|
-
const NONTERMINAL_LANDING_STATES = LANDING_STATES.filter(
|
|
304
|
-
(state): state is Exclude<LandingState, "landed" | "superseded" | "canceled" | "failed"> =>
|
|
305
|
-
state !== "landed" && state !== "superseded" && state !== "canceled" && state !== "failed",
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
export type LandingDurableExit = "command_wait" | "event_wait" | "sleep" | "supervisor_reconcile" | "explicit_action";
|
|
309
|
-
|
|
310
|
-
// Persisted exit labels retained for historical landing command/control compatibility.
|
|
311
|
-
export const LANDING_DURABLE_EXITS = Object.freeze({
|
|
312
|
-
controller_wait: "event_wait",
|
|
313
|
-
queued: "supervisor_reconcile",
|
|
314
|
-
supersession_wait: "event_wait",
|
|
315
|
-
lane_wait: "event_wait",
|
|
316
|
-
authorization_required: "sleep",
|
|
317
|
-
preparing: "supervisor_reconcile",
|
|
318
|
-
gating: "supervisor_reconcile",
|
|
319
|
-
gate_failed: "explicit_action",
|
|
320
|
-
publish_ready: "supervisor_reconcile",
|
|
321
|
-
publishing: "supervisor_reconcile",
|
|
322
|
-
verifying: "supervisor_reconcile",
|
|
323
|
-
cleanup: "supervisor_reconcile",
|
|
324
|
-
receipt_ready: "supervisor_reconcile",
|
|
325
|
-
external_retire_pending: "supervisor_reconcile",
|
|
326
|
-
retry_wait: "sleep",
|
|
327
|
-
reconciliation_required: "explicit_action",
|
|
328
|
-
supersede_pending: "supervisor_reconcile",
|
|
329
|
-
cancel_pending: "supervisor_reconcile",
|
|
330
|
-
} satisfies Record<(typeof NONTERMINAL_LANDING_STATES)[number], LandingDurableExit>);
|
|
331
|
-
|
|
332
|
-
const LANDING_OBSERVATION_BASE_MS = 30_000;
|
|
333
|
-
const LANDING_OBSERVATION_MAX_MS = 30 * 60_000;
|
|
334
|
-
|
|
335
|
-
function repeatedObservationDelayMs(repeats: number): number {
|
|
336
|
-
return Math.min(LANDING_OBSERVATION_MAX_MS, LANDING_OBSERVATION_BASE_MS * 2 ** Math.min(6, repeats));
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function commandForRun(row: z.output<typeof runSchema>): z.input<typeof landingHostCommandInputSchema> | undefined {
|
|
340
|
-
if (!row.laneKey || !row.baseHeadSha || !row.controllerToken || !row.laneToken || !row.selectedOrchestratorId
|
|
341
|
-
|| !row.selectedCapabilityDigest || !row.selectedSafetyDigest || !row.executionTreeHandle) return undefined;
|
|
342
|
-
const common = { workspaceId: row.workspaceId, laneKey: row.laneKey, repoRoot: row.repoRoot,
|
|
343
|
-
sourceRef: row.sourceRef, sourceHeadSha: row.sourceHeadSha, baseRef: row.baseRef,
|
|
344
|
-
baseHeadSha: row.baseHeadSha, laneToken: row.laneToken,
|
|
345
|
-
runTuple: { runId: row.id, generation: row.generation, commandGeneration: row.commandGeneration,
|
|
346
|
-
attempt: row.attempt, phaseOrdinal: row.phaseOrdinal, stateVersion: row.stateVersion },
|
|
347
|
-
selectedHost: { orchestratorId: row.selectedOrchestratorId,
|
|
348
|
-
capabilityDigest: row.selectedCapabilityDigest, safetyDigest: row.selectedSafetyDigest },
|
|
349
|
-
executionTreeHandle: row.executionTreeHandle };
|
|
350
|
-
if (row.state === "preparing" && row.candidateWorktreePath) return {
|
|
351
|
-
type: "landing.prepare", params: { ...common, candidateWorktreePath: row.candidateWorktreePath },
|
|
352
|
-
};
|
|
353
|
-
if (row.state === "gating" && row.candidateWorktreePath && row.candidateHeadSha) return {
|
|
354
|
-
type: "landing.gate", params: { ...common, candidateWorktreePath: row.candidateWorktreePath,
|
|
355
|
-
candidateHeadSha: row.candidateHeadSha, gates: [] },
|
|
356
|
-
};
|
|
357
|
-
if (row.state === "publish_ready" && (row.observationGeneration ?? 0) > 0
|
|
358
|
-
&& row.candidateWorktreePath && row.candidateHeadSha
|
|
359
|
-
&& row.pushRemote && row.gateTreeSha && row.gateConfigDigest && row.gateAttestation) return {
|
|
360
|
-
type: "landing.publish", params: { ...common, candidateWorktreePath: row.candidateWorktreePath,
|
|
361
|
-
pushRemote: row.pushRemote, candidateHeadSha: row.candidateHeadSha, gateTreeSha: row.gateTreeSha,
|
|
362
|
-
gateConfigDigest: row.gateConfigDigest, gateAttestationDigest: row.gateAttestation,
|
|
363
|
-
observationGeneration: row.observationGeneration ?? 0, expectedRestartEpoch: null,
|
|
364
|
-
pushAttemptId: `push:${row.id}:${row.commandGeneration}`,
|
|
365
|
-
timing: { pushTimeoutMs: 300_000, reapTimeoutMs: 60_000, reportTimeoutMs: 30_000,
|
|
366
|
-
refreshedLaneLeaseMs: 15 * 60_000 } },
|
|
367
|
-
};
|
|
368
|
-
if (row.state === "verifying" && row.pushRemote) return { type: "landing.verify",
|
|
369
|
-
params: { ...common, pushRemote: row.pushRemote,
|
|
370
|
-
...(row.candidateHeadSha ? { candidateHeadSha: row.candidateHeadSha } : {}) } };
|
|
371
|
-
if (row.state === "cleanup" && row.candidateWorktreePath) return { type: "landing.cleanup",
|
|
372
|
-
params: { ...common, candidateWorktreePath: row.candidateWorktreePath,
|
|
373
|
-
...(row.candidateHeadSha ? { candidateHeadSha: row.candidateHeadSha } : {}) } };
|
|
374
|
-
return undefined;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const LANDING_CONTROL_TRANSITIONS = {
|
|
378
|
-
cancel: { allowedFrom: new Set<LandingState>(NONTERMINAL_LANDING_STATES), target: "canceled", workflow: "cancel" },
|
|
379
|
-
fail: { allowedFrom: new Set<LandingState>(NONTERMINAL_LANDING_STATES), target: "failed", workflow: "fail" },
|
|
380
|
-
supersede: { allowedFrom: new Set<LandingState>(NONTERMINAL_LANDING_STATES), target: "superseded", workflow: "cancel" },
|
|
381
|
-
retry: { allowedFrom: new Set<LandingState>(["failed", "gate_failed", "retry_wait", "authorization_required"]), target: "controller_wait", workflow: "reopen" },
|
|
382
|
-
reconcile: { allowedFrom: new Set<LandingState>(["reconciliation_required"]), target: "controller_wait", workflow: "reopen" },
|
|
383
|
-
} as const satisfies Record<LandingControlAction, {
|
|
384
|
-
allowedFrom: ReadonlySet<LandingState>;
|
|
385
|
-
target: LandingState;
|
|
386
|
-
workflow: "cancel" | "fail" | "reopen";
|
|
387
|
-
}>;
|
|
388
|
-
|
|
389
|
-
function landingControlTransition(action: LandingControlAction, source: LandingState) {
|
|
390
|
-
const transition = LANDING_CONTROL_TRANSITIONS[action];
|
|
391
|
-
return transition.allowedFrom.has(source) ? transition : undefined;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
async function stageWorkflowControl(
|
|
395
|
-
ctx: ModuleCtx,
|
|
396
|
-
operation: "cancel" | "fail" | "reopen",
|
|
397
|
-
workflowInstanceId: string | undefined,
|
|
398
|
-
failure?: JsonValue,
|
|
399
|
-
): Promise<void> {
|
|
400
|
-
if (!workflowInstanceId) throw new OpError("workflow_control_unavailable", 500, "landing run has no enlisted workflow instance");
|
|
401
|
-
if (!ctx.workflows.control) throw new OpError("workflow_control_unavailable", 500, "enlisted workflow controls are unavailable");
|
|
402
|
-
const workflow = await ctx.workflows.status(workflowInstanceId);
|
|
403
|
-
if (!workflow) throw new OpError("workflow_control_unavailable", 500,
|
|
404
|
-
`landing workflow instance '${workflowInstanceId}' was not found`);
|
|
405
|
-
// Terminal workflows are absorbing. For cancel/fail the desired convergence has already happened,
|
|
406
|
-
// even when its exact terminal status differs; staging another control would be refused during
|
|
407
|
-
// opCommit and roll the landing entity/event/comparison transaction back forever. Reopen remains
|
|
408
|
-
// strict because it is the one operation whose desired result is a live workflow.
|
|
409
|
-
if (operation !== "reopen" && ["done", "failed", "canceled"].includes(workflow.status)) return;
|
|
410
|
-
await ctx.workflows.control(operation, workflowInstanceId, failure);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
async function transitionRun(
|
|
414
|
-
ctx: ModuleCtx,
|
|
415
|
-
input: { runId: string; expectedGeneration: number; expectedStateVersion: number; blockerCode?: string; blockerDetail?: string },
|
|
416
|
-
action: LandingControlAction,
|
|
417
|
-
) {
|
|
418
|
-
const store = stores(ctx).run;
|
|
419
|
-
const row = await store.get(input.runId);
|
|
420
|
-
if (!row) throw new OpError("not_found", 404, `landing run '${input.runId}' was not found`);
|
|
421
|
-
if (row.generation !== input.expectedGeneration || row.stateVersion !== input.expectedStateVersion) {
|
|
422
|
-
throw new OpError("conflict", 409, `landing run '${input.runId}' fence mismatch`);
|
|
423
|
-
}
|
|
424
|
-
const transition = landingControlTransition(action, row.state);
|
|
425
|
-
if (!transition) {
|
|
426
|
-
throw new OpError(
|
|
427
|
-
"invalid_state_transition",
|
|
428
|
-
409,
|
|
429
|
-
`landing run '${input.runId}' cannot ${action} from state '${row.state}'`,
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
const now = ctx.clock.now();
|
|
433
|
-
const restart = action === "retry" || action === "reconcile";
|
|
434
|
-
if (restart && row.commandId && !row.terminalProcessProof) {
|
|
435
|
-
throw new OpError(
|
|
436
|
-
"retirement_required",
|
|
437
|
-
409,
|
|
438
|
-
`landing run '${input.runId}' cannot ${action} while command '${row.commandId}' is bound; cancel the landing to retire the active command before starting recovery`,
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
const retirementPending = Boolean(row.commandId) && !restart;
|
|
442
|
-
const target: LandingState = retirementPending
|
|
443
|
-
? action === "supersede" ? "supersede_pending" : "cancel_pending"
|
|
444
|
-
: restart ? "queued" : transition.target;
|
|
445
|
-
const updated = await store.update(input.runId, {
|
|
446
|
-
state: target,
|
|
447
|
-
stateVersion: row.stateVersion + 1,
|
|
448
|
-
stateSince: now,
|
|
449
|
-
updatedAt: now,
|
|
450
|
-
...(restart ? { attempt: row.attempt + 1, commandGeneration: row.commandGeneration + 1,
|
|
451
|
-
phaseOrdinal: 0, commandId: undefined, candidateHeadSha: undefined, candidateTreeSha: undefined,
|
|
452
|
-
gateTreeSha: undefined, gateConfigDigest: undefined, gateAttestation: undefined,
|
|
453
|
-
receiptId: undefined, receiptTargetSha: undefined, receiptVerifiedAt: undefined,
|
|
454
|
-
receiptVerificationGeneration: undefined, receiptKind: undefined,
|
|
455
|
-
laneToken: undefined, selectedOrchestratorId: undefined, selectedCapabilityDigest: undefined,
|
|
456
|
-
selectedSafetyDigest: undefined, candidateWorktreePath: undefined, executionTreeHandle: undefined,
|
|
457
|
-
blockerCode: undefined, blockerDetail: undefined, blockedAt: undefined } : {}),
|
|
458
|
-
...(input.blockerCode ? { blockerCode: input.blockerCode, blockerDetail: input.blockerDetail, blockedAt: now } : {}),
|
|
459
|
-
});
|
|
460
|
-
if (!retirementPending && row.laneKey) {
|
|
461
|
-
const lane = await stores(ctx).lane.get(row.laneKey);
|
|
462
|
-
if (lane?.currentRunId === row.id) await stores(ctx).lane.update(lane.id, {
|
|
463
|
-
currentRunId: undefined, retiringRunId: undefined, laneToken: undefined,
|
|
464
|
-
leaseExpiresAt: undefined, activeCommandId: undefined, activeExecutionGeneration: undefined,
|
|
465
|
-
lastTerminalProcessProof: row.terminalProcessProof, updatedAt: now,
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
const workflow = restart && row.workflowInstanceId
|
|
469
|
-
? await ctx.workflows.status(row.workflowInstanceId) : undefined;
|
|
470
|
-
if (!retirementPending && (!restart
|
|
471
|
-
|| (workflow !== undefined && !["pending", "running", "waiting", "sleeping"].includes(workflow.status)))) {
|
|
472
|
-
await stageWorkflowControl(ctx, transition.workflow, row.workflowInstanceId,
|
|
473
|
-
input.blockerCode ? { code: input.blockerCode, detail: input.blockerDetail ?? "" } : undefined);
|
|
474
|
-
}
|
|
475
|
-
await ctx.events.emit("landing.run_transitioned", { runId: row.id, state: target, stateVersion: updated.stateVersion });
|
|
476
|
-
if (restart || retirementPending) {
|
|
477
|
-
await ctx.events.emit("landing.controller_activated", { runId: row.id, correlationId: row.id });
|
|
478
|
-
}
|
|
479
|
-
return updated;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
export const landing = defineModule({
|
|
483
|
-
id: "landing",
|
|
484
|
-
title: "Durable landing controller",
|
|
485
|
-
description: "Kernel-owned durable landing authorization and controller state.",
|
|
486
|
-
entities: {
|
|
487
|
-
workspaceHead: { schema: workspaceHeadSchema, indexes: ["authorizationEventId"] },
|
|
488
|
-
run: { schema: runSchema, indexes: ["workspaceId", "authorizationEventId", "state", "blockerCode", "shadowVerdict", ["workspaceId", "state"]] },
|
|
489
|
-
lane: { schema: laneSchema, indexes: ["identityDigest", "currentRunId", "retiringRunId"] },
|
|
490
|
-
notice: { schema: noticeSchema, indexes: ["runId", "status", ["runId", "stateVersion"]] },
|
|
491
|
-
effect: { schema: effectSchema, indexes: ["runId", "status", ["runId", "stateVersion"]] },
|
|
492
|
-
targetObservation: { schema: targetObservationSchema, indexes: ["runId", ["runId", "observationGeneration"]] },
|
|
493
|
-
comparison: { schema: comparisonSchema, indexes: ["runId", "verdict", ["runId", "comparisonVersion"]] },
|
|
494
|
-
receipt: { schema: receiptSchema, indexes: ["runId", "workspaceId", ["laneKey", "createdAt"]] },
|
|
495
|
-
...landingIncidentEntities,
|
|
496
|
-
},
|
|
497
|
-
events: {
|
|
498
|
-
"landing.authorization_admitted": z.object({ runId: z.string(), authorizationEventId: z.number().int(), current: z.boolean() }),
|
|
499
|
-
"landing.run_transitioned": z.object({ runId: z.string(), state: landingStateSchema, stateVersion: z.number().int() }),
|
|
500
|
-
"landing.controller_activated": z.object({ runId: z.string(), correlationId: z.string() }),
|
|
501
|
-
"landing.target_observed": z.object({ runId: z.string(), status: z.enum(["positive", "negative", "unknown"]), observationGeneration: z.number().int() }),
|
|
502
|
-
"landing.receipt_finalized": z.object({ runId: z.string(), receiptId: z.string(), targetHeadSha: z.string(), kind: z.enum(["kernel", "external"]) }),
|
|
503
|
-
},
|
|
504
|
-
commands: {
|
|
505
|
-
execute: {
|
|
506
|
-
input: landingHostCommandInputSchema,
|
|
507
|
-
result: z.record(z.string(), z.unknown()),
|
|
508
|
-
target: "host-agent",
|
|
509
|
-
grant: "command:landing",
|
|
510
|
-
idempotencyKey: (input) => {
|
|
511
|
-
const tuple = input.params.runTuple as Record<string, unknown> | undefined;
|
|
512
|
-
return `${input.type}:${String(tuple?.runId)}:${String(tuple?.generation)}:${String(tuple?.commandGeneration)}`;
|
|
513
|
-
},
|
|
514
|
-
timeoutMs: 15 * 60_000,
|
|
515
|
-
whenDegraded: "queue",
|
|
516
|
-
},
|
|
517
|
-
},
|
|
518
|
-
workflows: {
|
|
519
|
-
controller: {
|
|
520
|
-
input: z.object({ runId: z.string() }),
|
|
521
|
-
state: z.object({ runId: z.string(), commandId: z.string().optional() }),
|
|
522
|
-
init: (input): ControllerWaitState => ({ runId: input.runId }),
|
|
523
|
-
start: "controller_wait",
|
|
524
|
-
steps: {
|
|
525
|
-
controller_wait: {
|
|
526
|
-
run: async (ctx, state: ControllerWaitState): Promise<StepResult<ControllerWaitState>> => {
|
|
527
|
-
const run = await stores(ctx).run.get(state.runId);
|
|
528
|
-
if (run && ["landed", "superseded", "canceled", "failed"].includes(run.state)) {
|
|
529
|
-
return { state, next: "done", output: { runId: state.runId, state: run.state } };
|
|
530
|
-
}
|
|
531
|
-
const command = run ? commandForRun(run) : undefined;
|
|
532
|
-
if (run && command && run.selectedOrchestratorId) {
|
|
533
|
-
const issued = await ctx.commands.issue("execute", command, { host: run.selectedOrchestratorId });
|
|
534
|
-
return { state: { ...state, commandId: issued.id }, next: "command_wait",
|
|
535
|
-
waitFor: { kind: "command", commandId: issued.id } };
|
|
536
|
-
}
|
|
537
|
-
return { state, next: "controller_wait", waitFor: { kind: "event", topic: "landing.controller_activated", correlationId: state.runId } };
|
|
538
|
-
},
|
|
539
|
-
},
|
|
540
|
-
command_wait: {
|
|
541
|
-
run: async (_ctx, state: ControllerWaitState): Promise<StepResult<ControllerWaitState>> => ({
|
|
542
|
-
state: { runId: state.runId }, next: "controller_wait",
|
|
543
|
-
}),
|
|
544
|
-
},
|
|
545
|
-
},
|
|
546
|
-
},
|
|
547
|
-
},
|
|
548
|
-
ops: {
|
|
549
|
-
...landingIncidentOps,
|
|
550
|
-
admitAuthorization: {
|
|
551
|
-
kind: "mutation",
|
|
552
|
-
idempotent: true,
|
|
553
|
-
grant: LANDING_ADMIN_GRANT,
|
|
554
|
-
summary: "Admit one immutable landing authorization in event order.",
|
|
555
|
-
expose: { http: false, mcp: false, cli: false },
|
|
556
|
-
emits: ["landing.authorization_admitted", "landing.run_transitioned"],
|
|
557
|
-
input: admissionInputSchema,
|
|
558
|
-
output: admissionOutputSchema,
|
|
559
|
-
handler: async (ctx, input) => {
|
|
560
|
-
const entity = stores(ctx);
|
|
561
|
-
const runId = landingRunId(input.workspaceId, input.authorizationEventId);
|
|
562
|
-
const existing = await entity.run.get(runId);
|
|
563
|
-
if (existing) {
|
|
564
|
-
const head = await entity.workspaceHead.get(input.workspaceId);
|
|
565
|
-
return {
|
|
566
|
-
runId,
|
|
567
|
-
generation: existing.generation,
|
|
568
|
-
state: existing.state,
|
|
569
|
-
current: head?.currentRunId === runId,
|
|
570
|
-
...(existing.workflowInstanceId ? { workflowInstanceId: existing.workflowInstanceId } : {}),
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
const head = await entity.workspaceHead.get(input.workspaceId);
|
|
575
|
-
const isCurrent = !head || input.authorizationEventId > head.authorizationEventId;
|
|
576
|
-
const now = ctx.clock.now();
|
|
577
|
-
if (!isCurrent) {
|
|
578
|
-
await entity.run.create({
|
|
579
|
-
id: runId,
|
|
580
|
-
workspaceId: input.workspaceId,
|
|
581
|
-
laneKey: input.laneKey,
|
|
582
|
-
generation: input.authorizationEventId,
|
|
583
|
-
authorizationEventId: input.authorizationEventId,
|
|
584
|
-
sourceRef: input.sourceRef,
|
|
585
|
-
sourceHeadSha: input.sourceHeadSha,
|
|
586
|
-
repoRoot: input.frozenIntent.repoRoot,
|
|
587
|
-
remoteIdentity: input.frozenIntent.remoteIdentity,
|
|
588
|
-
baseRef: input.frozenIntent.baseRef,
|
|
589
|
-
strategy: input.frozenIntent.strategy,
|
|
590
|
-
gateSnapshot: JSON.stringify(input.frozenIntent.gateSnapshot),
|
|
591
|
-
requesterSnapshot: JSON.stringify(input.frozenIntent.requester),
|
|
592
|
-
state: "superseded",
|
|
593
|
-
stateVersion: 0,
|
|
594
|
-
stateSince: now,
|
|
595
|
-
queuedAt: input.queuedAt,
|
|
596
|
-
attempt: 0,
|
|
597
|
-
phaseOrdinal: 0,
|
|
598
|
-
commandGeneration: 0,
|
|
599
|
-
legacyProgressSequence: 0,
|
|
600
|
-
transientFailures: 0,
|
|
601
|
-
gateFailures: 0,
|
|
602
|
-
pushRaces: 0,
|
|
603
|
-
executorRecoveries: 0,
|
|
604
|
-
supersededBy: head!.currentRunId,
|
|
605
|
-
terminalProcessProof: "never_started",
|
|
606
|
-
createdAt: now,
|
|
607
|
-
updatedAt: now,
|
|
608
|
-
});
|
|
609
|
-
await ctx.events.emit("landing.authorization_admitted", { runId, authorizationEventId: input.authorizationEventId, current: false });
|
|
610
|
-
return { runId, generation: input.authorizationEventId, state: "superseded", current: false };
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
const workflow = await ctx.workflows.start("controller", { runId }, { idempotencyKey: runId, enlist: "op-commit" });
|
|
614
|
-
await entity.run.create({
|
|
615
|
-
id: runId,
|
|
616
|
-
workspaceId: input.workspaceId,
|
|
617
|
-
laneKey: input.laneKey,
|
|
618
|
-
generation: input.authorizationEventId,
|
|
619
|
-
authorizationEventId: input.authorizationEventId,
|
|
620
|
-
workflowInstanceId: workflow.id,
|
|
621
|
-
sourceRef: input.sourceRef,
|
|
622
|
-
sourceHeadSha: input.sourceHeadSha,
|
|
623
|
-
repoRoot: input.frozenIntent.repoRoot,
|
|
624
|
-
remoteIdentity: input.frozenIntent.remoteIdentity,
|
|
625
|
-
baseRef: input.frozenIntent.baseRef,
|
|
626
|
-
strategy: input.frozenIntent.strategy,
|
|
627
|
-
gateSnapshot: JSON.stringify(input.frozenIntent.gateSnapshot),
|
|
628
|
-
requesterSnapshot: JSON.stringify(input.frozenIntent.requester),
|
|
629
|
-
state: "controller_wait",
|
|
630
|
-
stateVersion: 0,
|
|
631
|
-
stateSince: now,
|
|
632
|
-
queuedAt: input.queuedAt,
|
|
633
|
-
attempt: 0,
|
|
634
|
-
phaseOrdinal: 0,
|
|
635
|
-
commandGeneration: 0,
|
|
636
|
-
legacyProgressSequence: 0,
|
|
637
|
-
transientFailures: 0,
|
|
638
|
-
gateFailures: 0,
|
|
639
|
-
pushRaces: 0,
|
|
640
|
-
executorRecoveries: 0,
|
|
641
|
-
createdAt: now,
|
|
642
|
-
updatedAt: now,
|
|
643
|
-
});
|
|
644
|
-
if (head) {
|
|
645
|
-
const prior = await entity.run.get(head.currentRunId);
|
|
646
|
-
const supersession = prior ? landingControlTransition("supersede", prior.state) : undefined;
|
|
647
|
-
if (prior && supersession) {
|
|
648
|
-
await entity.run.update(prior.id, {
|
|
649
|
-
state: supersession.target,
|
|
650
|
-
stateVersion: prior.stateVersion + 1,
|
|
651
|
-
stateSince: now,
|
|
652
|
-
supersededBy: runId,
|
|
653
|
-
terminalProcessProof: "never_started",
|
|
654
|
-
updatedAt: now,
|
|
655
|
-
});
|
|
656
|
-
await stageWorkflowControl(ctx, supersession.workflow, prior.workflowInstanceId);
|
|
657
|
-
await ctx.events.emit("landing.run_transitioned", { runId: prior.id, state: supersession.target, stateVersion: prior.stateVersion + 1 });
|
|
658
|
-
}
|
|
659
|
-
await entity.workspaceHead.update(input.workspaceId, {
|
|
660
|
-
currentRunId: runId,
|
|
661
|
-
generation: input.authorizationEventId,
|
|
662
|
-
authorizationEventId: input.authorizationEventId,
|
|
663
|
-
updatedAt: now,
|
|
664
|
-
});
|
|
665
|
-
} else {
|
|
666
|
-
await entity.workspaceHead.create({
|
|
667
|
-
id: input.workspaceId,
|
|
668
|
-
workspaceId: input.workspaceId,
|
|
669
|
-
currentRunId: runId,
|
|
670
|
-
generation: input.authorizationEventId,
|
|
671
|
-
authorizationEventId: input.authorizationEventId,
|
|
672
|
-
updatedAt: now,
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
await ctx.events.emit("landing.authorization_admitted", { runId, authorizationEventId: input.authorizationEventId, current: true });
|
|
676
|
-
return { runId, generation: input.authorizationEventId, state: "controller_wait", current: true, workflowInstanceId: workflow.id };
|
|
677
|
-
},
|
|
678
|
-
},
|
|
679
|
-
acceptDryRunCommandResult: {
|
|
680
|
-
kind: "mutation", idempotent: true, grant: LANDING_ADMIN_GRANT,
|
|
681
|
-
summary: "Atomically commit one fenced terminal landing command attestation into the current run.",
|
|
682
|
-
expose: { http: false, mcp: false, cli: false }, emits: ["landing.run_transitioned", "landing.controller_activated"],
|
|
683
|
-
input: dryRunCommandResultInputSchema, output: runSchema,
|
|
684
|
-
handler: async (ctx, input) => {
|
|
685
|
-
const entity = stores(ctx);
|
|
686
|
-
const row = await entity.run.get(input.tuple.runId);
|
|
687
|
-
if (!row) throw new OpError("not_found", 404, `landing run '${input.tuple.runId}' was not found`);
|
|
688
|
-
if (row.lastDryRunCommandId === input.commandId) return row;
|
|
689
|
-
const tuple = input.tuple;
|
|
690
|
-
if (row.generation !== tuple.generation || row.commandGeneration !== tuple.commandGeneration
|
|
691
|
-
|| row.attempt !== tuple.attempt || row.phaseOrdinal !== tuple.phaseOrdinal
|
|
692
|
-
|| row.stateVersion !== tuple.stateVersion) {
|
|
693
|
-
throw new OpError("conflict", 409, `landing run '${row.id}' command tuple is stale`);
|
|
694
|
-
}
|
|
695
|
-
const result = input.result;
|
|
696
|
-
const status = typeof result.status === "string" ? result.status : "";
|
|
697
|
-
const required = (name: string): string => {
|
|
698
|
-
const value = result[name];
|
|
699
|
-
if (typeof value !== "string" || !value) throw new OpError("invalid_input", 400, `${input.type} result.${name} is required`);
|
|
700
|
-
return value;
|
|
701
|
-
};
|
|
702
|
-
const now = ctx.clock.now();
|
|
703
|
-
let state: LandingState = row.state;
|
|
704
|
-
const patch: Record<string, string | number | undefined> = {
|
|
705
|
-
lastDryRunCommandId: input.commandId,
|
|
706
|
-
commandId: undefined,
|
|
707
|
-
terminalProcessProof: input.terminalProcessProof,
|
|
708
|
-
commandGeneration: row.commandGeneration + 1,
|
|
709
|
-
phaseOrdinal: row.phaseOrdinal + 1,
|
|
710
|
-
updatedAt: now,
|
|
711
|
-
};
|
|
712
|
-
if (input.type === "landing.prepare") {
|
|
713
|
-
if (status === "prepared") {
|
|
714
|
-
patch.baseHeadSha = required("baseHeadSha");
|
|
715
|
-
patch.candidateHeadSha = required("candidateHeadSha");
|
|
716
|
-
patch.candidateTreeSha = required("candidateTreeSha");
|
|
717
|
-
patch.pushRemote = required("pushRemote");
|
|
718
|
-
state = "gating";
|
|
719
|
-
} else state = status === "source-drift" ? "authorization_required" : "retry_wait";
|
|
720
|
-
} else if (input.type === "landing.gate") {
|
|
721
|
-
if (status === "passed") {
|
|
722
|
-
patch.candidateHeadSha = required("candidateHeadSha");
|
|
723
|
-
patch.gateTreeSha = required("gateTreeSha");
|
|
724
|
-
patch.gateConfigDigest = required("gateConfigDigest");
|
|
725
|
-
patch.gateAttestation = required("gateAttestationDigest");
|
|
726
|
-
state = "publish_ready";
|
|
727
|
-
} else if (status === "red") {
|
|
728
|
-
patch.gateFailures = row.gateFailures + 1;
|
|
729
|
-
state = "gate_failed";
|
|
730
|
-
} else {
|
|
731
|
-
patch.transientFailures = row.transientFailures + 1;
|
|
732
|
-
state = "retry_wait";
|
|
733
|
-
}
|
|
734
|
-
} else if (input.type === "landing.publish") {
|
|
735
|
-
if (status === "published" || status === "ambiguous") state = "verifying";
|
|
736
|
-
else if (status === "base-race") {
|
|
737
|
-
patch.pushRaces = row.pushRaces + 1;
|
|
738
|
-
state = "retry_wait";
|
|
739
|
-
} else state = "authorization_required";
|
|
740
|
-
} else if (input.type === "landing.verify") {
|
|
741
|
-
if (status === "contained") {
|
|
742
|
-
const targetHeadSha = required("targetHeadSha");
|
|
743
|
-
if (!row.candidateHeadSha || targetHeadSha !== row.candidateHeadSha) {
|
|
744
|
-
throw new OpError("conflict", 409, "Kernel verification did not prove the exact gated candidate");
|
|
745
|
-
}
|
|
746
|
-
patch.receiptTargetSha = targetHeadSha;
|
|
747
|
-
patch.receiptVerifiedAt = now;
|
|
748
|
-
patch.receiptVerificationGeneration = row.commandGeneration + 1;
|
|
749
|
-
patch.receiptKind = "kernel";
|
|
750
|
-
state = "cleanup";
|
|
751
|
-
} else state = status === "unknown" ? "reconciliation_required" : "retry_wait";
|
|
752
|
-
} else if (input.type === "landing.observe-target") {
|
|
753
|
-
state = status === "unknown" ? "reconciliation_required" : row.state;
|
|
754
|
-
} else if (input.type === "landing.cleanup") {
|
|
755
|
-
state = status === "removed" || status === "already-clean" ? "receipt_ready" : "reconciliation_required";
|
|
756
|
-
}
|
|
757
|
-
const changedState = state !== row.state;
|
|
758
|
-
patch.state = state;
|
|
759
|
-
patch.stateVersion = row.stateVersion + 1;
|
|
760
|
-
patch.stateSince = changedState ? now : row.stateSince;
|
|
761
|
-
const updated = await entity.run.update(row.id, patch);
|
|
762
|
-
const lane = row.laneKey ? await entity.lane.get(row.laneKey) : undefined;
|
|
763
|
-
if (lane && lane.activeCommandId === input.commandId) await entity.lane.update(lane.id, {
|
|
764
|
-
activeCommandId: undefined, activeExecutionGeneration: undefined,
|
|
765
|
-
lastTerminalProcessProof: input.terminalProcessProof, updatedAt: now,
|
|
766
|
-
});
|
|
767
|
-
if (changedState) await ctx.events.emit("landing.run_transitioned", { runId: row.id, state, stateVersion: updated.stateVersion });
|
|
768
|
-
if (!["landed", "superseded", "canceled", "failed"].includes(state)) {
|
|
769
|
-
await ctx.events.emit("landing.controller_activated", { runId: row.id, correlationId: row.id });
|
|
770
|
-
}
|
|
771
|
-
return updated;
|
|
772
|
-
},
|
|
773
|
-
},
|
|
774
|
-
cancelLanding: {
|
|
775
|
-
kind: "mutation", idempotent: false, grant: LANDING_ADMIN_GRANT, summary: "Cancel a fenced inert landing run.", expose: { http: false, mcp: false, cli: false },
|
|
776
|
-
emits: ["landing.run_transitioned"],
|
|
777
|
-
input: z.object({ runId: z.string(), expectedGeneration: z.number().int(), expectedStateVersion: z.number().int() }), output: runSchema,
|
|
778
|
-
handler: (ctx, input) => transitionRun(ctx, input, "cancel"),
|
|
779
|
-
},
|
|
780
|
-
failLanding: {
|
|
781
|
-
kind: "mutation", idempotent: false, grant: LANDING_ADMIN_GRANT, summary: "Fail a fenced landing run with a structured blocker.", expose: { http: false, mcp: false, cli: false },
|
|
782
|
-
emits: ["landing.run_transitioned"],
|
|
783
|
-
input: z.object({ runId: z.string(), expectedGeneration: z.number().int(), expectedStateVersion: z.number().int(), blockerCode: z.string(), blockerDetail: z.string().optional() }), output: runSchema,
|
|
784
|
-
handler: (ctx, input) => transitionRun(ctx, input, "fail"),
|
|
785
|
-
},
|
|
786
|
-
supersedeLanding: {
|
|
787
|
-
kind: "mutation", idempotent: false, grant: LANDING_ADMIN_GRANT, summary: "Supersede a fenced inert landing run.", expose: { http: false, mcp: false, cli: false },
|
|
788
|
-
emits: ["landing.run_transitioned"],
|
|
789
|
-
input: z.object({ runId: z.string(), expectedGeneration: z.number().int(), expectedStateVersion: z.number().int() }), output: runSchema,
|
|
790
|
-
handler: (ctx, input) => transitionRun(ctx, input, "supersede"),
|
|
791
|
-
},
|
|
792
|
-
retryLanding: {
|
|
793
|
-
kind: "mutation", idempotent: false, grant: LANDING_ADMIN_GRANT, summary: "Reopen a fenced failed run into inert controller wait.", expose: { http: false, mcp: false, cli: false },
|
|
794
|
-
emits: ["landing.run_transitioned", "landing.controller_activated"],
|
|
795
|
-
input: z.object({ runId: z.string(), expectedGeneration: z.number().int(), expectedStateVersion: z.number().int() }), output: runSchema,
|
|
796
|
-
handler: (ctx, input) => transitionRun(ctx, input, "retry"),
|
|
797
|
-
},
|
|
798
|
-
reconcileLanding: {
|
|
799
|
-
kind: "mutation", idempotent: false, grant: LANDING_ADMIN_GRANT, summary: "Re-arm a fenced reconciliation blocker for canonical controller recovery.", expose: { http: false, mcp: false, cli: false },
|
|
800
|
-
emits: ["landing.run_transitioned", "landing.controller_activated"],
|
|
801
|
-
input: z.object({ runId: z.string(), expectedGeneration: z.number().int(), expectedStateVersion: z.number().int() }), output: runSchema,
|
|
802
|
-
handler: (ctx, input) => transitionRun(ctx, input, "reconcile"),
|
|
803
|
-
},
|
|
804
|
-
finalizeControl: {
|
|
805
|
-
kind: "mutation", idempotent: true, grant: LANDING_ADMIN_GRANT,
|
|
806
|
-
summary: "Finalize cancel/fail/supersede only after the exact active process is positively retired.",
|
|
807
|
-
expose: { http: false, mcp: false, cli: false }, emits: ["landing.run_transitioned"],
|
|
808
|
-
input: finalizeControlInputSchema, output: runSchema,
|
|
809
|
-
handler: async (ctx, input) => {
|
|
810
|
-
const entity = stores(ctx);
|
|
811
|
-
const row = await entity.run.get(input.runId);
|
|
812
|
-
if (!row) throw new OpError("not_found", 404, `landing run '${input.runId}' was not found`);
|
|
813
|
-
if (row.generation !== input.expectedGeneration || row.stateVersion !== input.expectedStateVersion
|
|
814
|
-
|| row.commandId !== input.commandId
|
|
815
|
-
|| (row.state !== "cancel_pending" && row.state !== "supersede_pending")) {
|
|
816
|
-
throw new OpError("conflict", 409, "landing control finalization fence mismatch");
|
|
817
|
-
}
|
|
818
|
-
const terminal: LandingState = row.state === "supersede_pending" ? "superseded"
|
|
819
|
-
: row.blockerCode ? "failed" : "canceled";
|
|
820
|
-
const now = ctx.clock.now();
|
|
821
|
-
const lane = row.laneKey ? await entity.lane.get(row.laneKey) : undefined;
|
|
822
|
-
if (lane?.activeCommandId && lane.activeCommandId !== input.commandId) {
|
|
823
|
-
throw new OpError("conflict", 409, "landing control active-command fence changed");
|
|
824
|
-
}
|
|
825
|
-
const updated = await entity.run.update(row.id, { state: terminal,
|
|
826
|
-
stateVersion: row.stateVersion + 1, stateSince: now, commandId: undefined,
|
|
827
|
-
terminalProcessProof: input.terminalProcessProof, durableExitKind: "explicit_action", updatedAt: now });
|
|
828
|
-
if (lane && lane.currentRunId === row.id) await entity.lane.update(lane.id, { currentRunId: undefined,
|
|
829
|
-
retiringRunId: undefined, laneToken: undefined, leaseExpiresAt: undefined,
|
|
830
|
-
activeCommandId: undefined, activeExecutionGeneration: undefined,
|
|
831
|
-
lastTerminalProcessProof: input.terminalProcessProof, updatedAt: now });
|
|
832
|
-
await stageWorkflowControl(ctx, terminal === "failed" ? "fail" : "cancel", row.workflowInstanceId,
|
|
833
|
-
terminal === "failed" ? { code: row.blockerCode ?? "landing_failed", detail: row.blockerDetail ?? "" } : undefined);
|
|
834
|
-
await ctx.events.emit("landing.run_transitioned", { runId: row.id, state: terminal,
|
|
835
|
-
stateVersion: updated.stateVersion });
|
|
836
|
-
return updated;
|
|
837
|
-
},
|
|
838
|
-
},
|
|
839
|
-
acceptTargetObservation: {
|
|
840
|
-
kind: "mutation", idempotent: true, grant: LANDING_ADMIN_GRANT,
|
|
841
|
-
summary: "Persist one fenced three-valued dry target observation and its shadow-only prediction.",
|
|
842
|
-
expose: { http: false, mcp: false, cli: false },
|
|
843
|
-
emits: ["landing.run_transitioned", "landing.controller_activated", "landing.target_observed"],
|
|
844
|
-
input: observationInputSchema,
|
|
845
|
-
output: z.object({ run: runSchema, observation: targetObservationSchema.optional() }),
|
|
846
|
-
handler: async (ctx, input) => {
|
|
847
|
-
const entity = stores(ctx);
|
|
848
|
-
const observationId = `${input.runId}:observation:${input.observationGeneration}`;
|
|
849
|
-
const replay = await entity.targetObservation.get(observationId);
|
|
850
|
-
if (replay) {
|
|
851
|
-
const replayRun = await entity.run.get(input.runId);
|
|
852
|
-
if (!replayRun) throw new OpError("not_found", 404, `landing run '${input.runId}' was not found`);
|
|
853
|
-
return { run: replayRun, observation: replay };
|
|
854
|
-
}
|
|
855
|
-
const row = await entity.run.get(input.runId);
|
|
856
|
-
if (!row) throw new OpError("not_found", 404, `landing run '${input.runId}' was not found`);
|
|
857
|
-
if (row.generation !== input.expectedGeneration || row.stateVersion !== input.expectedStateVersion) {
|
|
858
|
-
throw new OpError("conflict", 409, `landing run '${input.runId}' fence mismatch`);
|
|
859
|
-
}
|
|
860
|
-
if (input.observationGeneration <= (row.observationGeneration ?? 0)) {
|
|
861
|
-
throw new OpError("conflict", 409, `landing run '${input.runId}' observation is stale`);
|
|
862
|
-
}
|
|
863
|
-
if (input.status === "positive" && input.sourceContained !== true && input.candidateContained !== true) {
|
|
864
|
-
throw new OpError("invalid_input", 400, "positive target observation requires positive remote containment evidence");
|
|
865
|
-
}
|
|
866
|
-
const now = ctx.clock.now();
|
|
867
|
-
if (row.lastObservationDigest === input.factDigest) {
|
|
868
|
-
const observationRepeatCount = (row.observationRepeatCount ?? 0) + 1;
|
|
869
|
-
const repeated = await entity.run.update(row.id, { observationGeneration: input.observationGeneration,
|
|
870
|
-
lastObservedAt: now, nextObservationAt: now + repeatedObservationDelayMs(observationRepeatCount),
|
|
871
|
-
observationRepeatCount, updatedAt: now });
|
|
872
|
-
return { run: repeated };
|
|
873
|
-
}
|
|
874
|
-
const observation = await entity.targetObservation.create({ id: observationId, runId: row.id,
|
|
875
|
-
generation: row.generation, observationGeneration: input.observationGeneration,
|
|
876
|
-
sourceHeadSha: input.sourceHeadSha, candidateHeadSha: input.candidateHeadSha,
|
|
877
|
-
targetRef: input.targetRef, targetHeadSha: input.targetHeadSha,
|
|
878
|
-
sourceContained: input.sourceContained, candidateContained: input.candidateContained,
|
|
879
|
-
containmentMethod: input.containmentMethod, status: input.status, observedAt: now });
|
|
880
|
-
const terminal = ["landed", "superseded", "canceled", "failed"].includes(row.state);
|
|
881
|
-
const targetState = terminal ? row.state : input.status === "positive" ? "external_retire_pending"
|
|
882
|
-
: input.status === "unknown" ? "reconciliation_required" : row.state;
|
|
883
|
-
const updated = await entity.run.update(row.id, {
|
|
884
|
-
state: targetState,
|
|
885
|
-
stateVersion: row.stateVersion + (targetState === row.state ? 0 : 1),
|
|
886
|
-
stateSince: targetState === row.state ? row.stateSince : now,
|
|
887
|
-
observationGeneration: input.observationGeneration,
|
|
888
|
-
lastObservedAt: now,
|
|
889
|
-
nextObservationAt: now + LANDING_OBSERVATION_BASE_MS,
|
|
890
|
-
lastObservationDigest: input.factDigest,
|
|
891
|
-
observationRepeatCount: 0,
|
|
892
|
-
predictedOutcome: input.status === "positive" ? "external_retire" : input.status === "unknown" ? "reconciliation_required" : "none",
|
|
893
|
-
durableExitKind: terminal ? row.durableExitKind : LANDING_DURABLE_EXITS[targetState as keyof typeof LANDING_DURABLE_EXITS],
|
|
894
|
-
lastReconciledAt: now,
|
|
895
|
-
updatedAt: now,
|
|
896
|
-
});
|
|
897
|
-
if (input.status === "unknown") {
|
|
898
|
-
const noticeId = `${row.id}:observation-unknown:${input.factDigest}`;
|
|
899
|
-
const payload = JSON.stringify({ targetRef: input.targetRef,
|
|
900
|
-
containmentMethod: input.containmentMethod ?? "unavailable", action: "reconcile_remote_evidence" });
|
|
901
|
-
if (!await entity.notice.get(noticeId)) await entity.notice.create({ id: noticeId, runId: row.id,
|
|
902
|
-
stateVersion: updated.stateVersion, audience: "landing-operator", kind: "observation-unknown",
|
|
903
|
-
status: "pending", payload, createdAt: now });
|
|
904
|
-
if (!await entity.effect.get(noticeId)) await entity.effect.create({ id: noticeId, runId: row.id,
|
|
905
|
-
stateVersion: updated.stateVersion, kind: "observation-unknown", status: "pending", payload,
|
|
906
|
-
deliveryKey: noticeId, attempts: 0, createdAt: now });
|
|
907
|
-
}
|
|
908
|
-
if (targetState !== row.state) await ctx.events.emit("landing.run_transitioned", { runId: row.id, state: targetState, stateVersion: updated.stateVersion });
|
|
909
|
-
await ctx.events.emit("landing.target_observed", { runId: row.id, status: input.status, observationGeneration: input.observationGeneration });
|
|
910
|
-
if (targetState !== row.state || (input.status === "negative" && row.state === "publish_ready")) {
|
|
911
|
-
await ctx.events.emit("landing.controller_activated", { runId: row.id, correlationId: row.id });
|
|
912
|
-
}
|
|
913
|
-
return { run: updated, observation };
|
|
914
|
-
},
|
|
915
|
-
},
|
|
916
|
-
reconcileTerminal: {
|
|
917
|
-
kind: "mutation", idempotent: true, grant: LANDING_ADMIN_GRANT,
|
|
918
|
-
summary: "Converge generic workflow and landing-domain terminal state without guessing.",
|
|
919
|
-
expose: { http: false, mcp: false, cli: false }, emits: ["landing.run_transitioned", "landing.controller_activated"],
|
|
920
|
-
input: z.object({ runId: z.string(), expectedGeneration: z.number().int(), expectedStateVersion: z.number().int(), workflowStatus: z.enum(["pending", "running", "waiting", "sleeping", "paused", "done", "failed", "canceled"]) }),
|
|
921
|
-
output: runSchema,
|
|
922
|
-
handler: async (ctx, input) => {
|
|
923
|
-
const row = await stores(ctx).run.get(input.runId);
|
|
924
|
-
if (!row) throw new OpError("not_found", 404, `landing run '${input.runId}' was not found`);
|
|
925
|
-
if (row.generation !== input.expectedGeneration || row.stateVersion !== input.expectedStateVersion) throw new OpError("conflict", 409, `landing run '${input.runId}' fence mismatch`);
|
|
926
|
-
const domainTerminal = ["landed", "superseded", "canceled", "failed"].includes(row.state);
|
|
927
|
-
const workflowTerminal = ["done", "failed", "canceled"].includes(input.workflowStatus);
|
|
928
|
-
if (domainTerminal && !workflowTerminal) {
|
|
929
|
-
if (row.state === "canceled" || row.state === "superseded") await stageWorkflowControl(ctx, "cancel", row.workflowInstanceId);
|
|
930
|
-
else if (row.state === "failed") await stageWorkflowControl(ctx, "fail", row.workflowInstanceId, { code: "landing_domain_failed" });
|
|
931
|
-
else await ctx.events.emit("landing.controller_activated", { runId: row.id, correlationId: row.id });
|
|
932
|
-
return row;
|
|
933
|
-
}
|
|
934
|
-
if (!domainTerminal && workflowTerminal) {
|
|
935
|
-
const now = ctx.clock.now();
|
|
936
|
-
const state: LandingState = input.workflowStatus === "canceled" ? "canceled"
|
|
937
|
-
: input.workflowStatus === "failed" ? "failed" : "reconciliation_required";
|
|
938
|
-
if (row.state === state) return row;
|
|
939
|
-
const updated = await stores(ctx).run.update(row.id, { state, stateVersion: row.stateVersion + 1,
|
|
940
|
-
stateSince: now, durableExitKind: state === "reconciliation_required" ? "explicit_action" : row.durableExitKind,
|
|
941
|
-
blockerCode: "workflow_domain_disagreement", blockerDetail: `workflow=${input.workflowStatus}; domain=${row.state}`,
|
|
942
|
-
blockedAt: now, updatedAt: now });
|
|
943
|
-
await ctx.events.emit("landing.run_transitioned", { runId: row.id, state, stateVersion: updated.stateVersion });
|
|
944
|
-
return updated;
|
|
945
|
-
}
|
|
946
|
-
return row;
|
|
947
|
-
},
|
|
948
|
-
},
|
|
949
|
-
},
|
|
950
|
-
});
|
|
951
|
-
|
|
952
|
-
export type LandingRun = z.output<typeof runSchema>;
|
|
953
|
-
export type LandingWorkspaceHead = z.output<typeof workspaceHeadSchema>;
|