@telora/daemon 0.23.1 → 0.23.7
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/build-info.json +6 -3
- package/dist/completion/review-exit/review-exit-deps.d.ts +2 -1
- package/dist/completion/review-exit/review-exit-deps.d.ts.map +1 -1
- package/dist/completion/review-exit/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit/review-exit-phase.js +5 -4
- package/dist/completion/review-exit/review-exit-phase.js.map +1 -1
- package/dist/completion/review-exit-holds/review-exit-verdict-gate.d.ts +2 -1
- package/dist/completion/review-exit-holds/review-exit-verdict-gate.d.ts.map +1 -1
- package/dist/completion/review-exit-holds/review-exit-verdict-gate.js +8 -5
- package/dist/completion/review-exit-holds/review-exit-verdict-gate.js.map +1 -1
- package/dist/completion/state-cascade/review-next-step.d.ts.map +1 -1
- package/dist/completion/state-cascade/review-next-step.js +15 -8
- package/dist/completion/state-cascade/review-next-step.js.map +1 -1
- package/dist/completion/team-exit/status-advance-phase.d.ts.map +1 -1
- package/dist/completion/team-exit/status-advance-phase.js +6 -6
- package/dist/completion/team-exit/status-advance-phase.js.map +1 -1
- package/dist/completion/team-liveness/supervision-firing-record.d.ts +6 -0
- package/dist/completion/team-liveness/supervision-firing-record.d.ts.map +1 -1
- package/dist/completion/team-liveness/supervision-firing-record.js +10 -1
- package/dist/completion/team-liveness/supervision-firing-record.js.map +1 -1
- package/dist/completion/team-outcome/decide-close-rows.d.ts +9 -0
- package/dist/completion/team-outcome/decide-close-rows.d.ts.map +1 -1
- package/dist/completion/team-outcome/decide-close-rows.js +18 -4
- package/dist/completion/team-outcome/decide-close-rows.js.map +1 -1
- package/dist/composition/completion-event-context.js +2 -2
- package/dist/composition/completion-event-context.js.map +1 -1
- package/dist/composition/daemon-contexts.d.ts.map +1 -1
- package/dist/composition/daemon-contexts.js +5 -1
- package/dist/composition/daemon-contexts.js.map +1 -1
- package/dist/composition/engine-start-context.d.ts +3 -0
- package/dist/composition/engine-start-context.d.ts.map +1 -0
- package/dist/composition/engine-start-context.js +24 -0
- package/dist/composition/engine-start-context.js.map +1 -0
- package/dist/composition/execution-spawn-context.d.ts.map +1 -1
- package/dist/composition/execution-spawn-context.js +13 -1
- package/dist/composition/execution-spawn-context.js.map +1 -1
- package/dist/composition/runtime-delivery-advance-context.js +3 -3
- package/dist/composition/runtime-delivery-advance-context.js.map +1 -1
- package/dist/composition/runtime-heartbeat-context.d.ts.map +1 -1
- package/dist/composition/runtime-heartbeat-context.js +3 -0
- package/dist/composition/runtime-heartbeat-context.js.map +1 -1
- package/dist/composition/runtime-poll-context.d.ts +29 -2
- package/dist/composition/runtime-poll-context.d.ts.map +1 -1
- package/dist/composition/runtime-poll-context.js +51 -4
- package/dist/composition/runtime-poll-context.js.map +1 -1
- package/dist/composition/runtime-prd-context.d.ts.map +1 -1
- package/dist/composition/runtime-prd-context.js +25 -1
- package/dist/composition/runtime-prd-context.js.map +1 -1
- package/dist/composition/runtime-spawn-pass-context.js +1 -1
- package/dist/composition/runtime-spawn-pass-context.js.map +1 -1
- package/dist/composition/spawn-pass-deps.d.ts +1 -1
- package/dist/composition/spawn-pass-deps.d.ts.map +1 -1
- package/dist/composition/spawn-pass-deps.js +3 -0
- package/dist/composition/spawn-pass-deps.js.map +1 -1
- package/dist/composition/standing-approval-context.d.ts.map +1 -1
- package/dist/composition/standing-approval-context.js +2 -1
- package/dist/composition/standing-approval-context.js.map +1 -1
- package/dist/composition/state-cascade-context.js +1 -1
- package/dist/composition/state-cascade-context.js.map +1 -1
- package/dist/composition/tower-pass-context.d.ts +9 -0
- package/dist/composition/tower-pass-context.d.ts.map +1 -0
- package/dist/composition/tower-pass-context.js +53 -0
- package/dist/composition/tower-pass-context.js.map +1 -0
- package/dist/composition/verify-probe-context.d.ts.map +1 -1
- package/dist/composition/verify-probe-context.js +5 -4
- package/dist/composition/verify-probe-context.js.map +1 -1
- package/dist/composition/verify-rework-context.d.ts +26 -2
- package/dist/composition/verify-rework-context.d.ts.map +1 -1
- package/dist/composition/verify-rework-context.js +34 -10
- package/dist/composition/verify-rework-context.js.map +1 -1
- package/dist/config/live-daemon-config.d.ts.map +1 -1
- package/dist/config/live-daemon-config.js +1 -19
- package/dist/config/live-daemon-config.js.map +1 -1
- package/dist/daemon-state/awaiting-slot.d.ts.map +1 -1
- package/dist/daemon-state/awaiting-slot.js +2 -1
- package/dist/daemon-state/awaiting-slot.js.map +1 -1
- package/dist/daemon-state/focus-team-state.js +1 -1
- package/dist/daemon-state/intake-control.d.ts.map +1 -1
- package/dist/daemon-state/intake-control.js +2 -1
- package/dist/daemon-state/intake-control.js.map +1 -1
- package/dist/delivery/recorded-concerns.d.ts +19 -2
- package/dist/delivery/recorded-concerns.d.ts.map +1 -1
- package/dist/delivery/recorded-concerns.js +32 -6
- package/dist/delivery/recorded-concerns.js.map +1 -1
- package/dist/directive/directive-queue.js +1 -1
- package/dist/focus/closed-review-window.d.ts +12 -0
- package/dist/focus/closed-review-window.d.ts.map +1 -1
- package/dist/focus/closed-review-window.js +18 -0
- package/dist/focus/closed-review-window.js.map +1 -1
- package/dist/focus/control-state.d.ts +30 -26
- package/dist/focus/control-state.d.ts.map +1 -1
- package/dist/focus/control-state.js +60 -46
- package/dist/focus/control-state.js.map +1 -1
- package/dist/focus/daemon-contexts.d.ts +23 -0
- package/dist/focus/daemon-contexts.d.ts.map +1 -1
- package/dist/focus/daemon-contexts.js +1 -1
- package/dist/focus/daemon-contexts.js.map +1 -1
- package/dist/focus/engine-ready-pass.d.ts +11 -0
- package/dist/focus/engine-ready-pass.d.ts.map +1 -0
- package/dist/focus/engine-ready-pass.js +25 -0
- package/dist/focus/engine-ready-pass.js.map +1 -0
- package/dist/focus/failed-head-wedge.d.ts +95 -0
- package/dist/focus/failed-head-wedge.d.ts.map +1 -0
- package/dist/focus/failed-head-wedge.js +85 -0
- package/dist/focus/failed-head-wedge.js.map +1 -0
- package/dist/focus/focus-engine.d.ts.map +1 -1
- package/dist/focus/focus-engine.js +20 -20
- package/dist/focus/focus-engine.js.map +1 -1
- package/dist/focus/focus-loop-registry.d.ts.map +1 -1
- package/dist/focus/focus-loop-registry.js +2 -1
- package/dist/focus/focus-loop-registry.js.map +1 -1
- package/dist/focus/focus-poller-auto-advance.d.ts.map +1 -1
- package/dist/focus/focus-poller-auto-advance.js +4 -4
- package/dist/focus/focus-poller-auto-advance.js.map +1 -1
- package/dist/focus/focus-poller-spawn.d.ts +42 -49
- package/dist/focus/focus-poller-spawn.d.ts.map +1 -1
- package/dist/focus/focus-poller-spawn.js +129 -83
- package/dist/focus/focus-poller-spawn.js.map +1 -1
- package/dist/focus/focus-poller-state.d.ts.map +1 -1
- package/dist/focus/focus-poller-state.js +1 -19
- package/dist/focus/focus-poller-state.js.map +1 -1
- package/dist/focus/focus-poller.d.ts.map +1 -1
- package/dist/focus/focus-poller.js +2 -1
- package/dist/focus/focus-poller.js.map +1 -1
- package/dist/focus/focus-respawn-guard.d.ts +7 -0
- package/dist/focus/focus-respawn-guard.d.ts.map +1 -1
- package/dist/focus/focus-respawn-guard.js +20 -1
- package/dist/focus/focus-respawn-guard.js.map +1 -1
- package/dist/focus/focus-spawn-decision.d.ts +11 -3
- package/dist/focus/focus-spawn-decision.d.ts.map +1 -1
- package/dist/focus/focus-spawn-decision.js +3 -1
- package/dist/focus/focus-spawn-decision.js.map +1 -1
- package/dist/focus/heartbeat-metadata.d.ts +3 -0
- package/dist/focus/heartbeat-metadata.d.ts.map +1 -1
- package/dist/focus/heartbeat-metadata.js +6 -1
- package/dist/focus/heartbeat-metadata.js.map +1 -1
- package/dist/focus/heartbeat.d.ts.map +1 -1
- package/dist/focus/heartbeat.js +2 -0
- package/dist/focus/heartbeat.js.map +1 -1
- package/dist/focus/pending-directive-gates.d.ts +47 -0
- package/dist/focus/pending-directive-gates.d.ts.map +1 -0
- package/dist/focus/pending-directive-gates.js +76 -0
- package/dist/focus/pending-directive-gates.js.map +1 -0
- package/dist/focus/pending-review-approved-head.d.ts +2 -1
- package/dist/focus/pending-review-approved-head.d.ts.map +1 -1
- package/dist/focus/pending-review-approved-head.js +3 -4
- package/dist/focus/pending-review-approved-head.js.map +1 -1
- package/dist/focus/pending-spawn-pass-deps.d.ts +5 -0
- package/dist/focus/pending-spawn-pass-deps.d.ts.map +1 -1
- package/dist/focus/pending-spawn-pass-deps.js +4 -1
- package/dist/focus/pending-spawn-pass-deps.js.map +1 -1
- package/dist/focus/pending-spawn-verify-hold.d.ts +13 -0
- package/dist/focus/pending-spawn-verify-hold.d.ts.map +1 -0
- package/dist/focus/pending-spawn-verify-hold.js +30 -0
- package/dist/focus/pending-spawn-verify-hold.js.map +1 -0
- package/dist/focus/ready-focus-spawn-action.d.ts +15 -2
- package/dist/focus/ready-focus-spawn-action.d.ts.map +1 -1
- package/dist/focus/ready-focus-spawn-action.js +25 -8
- package/dist/focus/ready-focus-spawn-action.js.map +1 -1
- package/dist/focus/spawn-demand-probe.d.ts +45 -0
- package/dist/focus/spawn-demand-probe.d.ts.map +1 -0
- package/dist/focus/spawn-demand-probe.js +74 -0
- package/dist/focus/spawn-demand-probe.js.map +1 -0
- package/dist/focus/stranded-review-reroute.d.ts.map +1 -1
- package/dist/focus/stranded-review-reroute.js +5 -3
- package/dist/focus/stranded-review-reroute.js.map +1 -1
- package/dist/focus/verify-routing-passes.d.ts +8 -3
- package/dist/focus/verify-routing-passes.d.ts.map +1 -1
- package/dist/focus/verify-routing-passes.js +1 -0
- package/dist/focus/verify-routing-passes.js.map +1 -1
- package/dist/git/focus-branch-containment.d.ts +39 -0
- package/dist/git/focus-branch-containment.d.ts.map +1 -0
- package/dist/git/focus-branch-containment.js +47 -0
- package/dist/git/focus-branch-containment.js.map +1 -0
- package/dist/git/types.d.ts.map +1 -1
- package/dist/git/types.js +2 -0
- package/dist/git/types.js.map +1 -1
- package/dist/poll-trigger.js +1 -1
- package/dist/poll-trigger.js.map +1 -1
- package/dist/prd/arm-readiness.d.ts.map +1 -1
- package/dist/prd/arm-readiness.js +2 -1
- package/dist/prd/arm-readiness.js.map +1 -1
- package/dist/prd/controller-shared.d.ts +32 -8
- package/dist/prd/controller-shared.d.ts.map +1 -1
- package/dist/prd/controller-shared.js +34 -18
- package/dist/prd/controller-shared.js.map +1 -1
- package/dist/prd/controller.d.ts.map +1 -1
- package/dist/prd/controller.js +3 -8
- package/dist/prd/controller.js.map +1 -1
- package/dist/prd/done-sweep.d.ts.map +1 -1
- package/dist/prd/done-sweep.js +2 -7
- package/dist/prd/done-sweep.js.map +1 -1
- package/dist/prd/frontier-arming.d.ts +13 -1
- package/dist/prd/frontier-arming.d.ts.map +1 -1
- package/dist/prd/frontier-arming.js +50 -2
- package/dist/prd/frontier-arming.js.map +1 -1
- package/dist/prd/index.d.ts.map +1 -1
- package/dist/prd/index.js +2 -1
- package/dist/prd/index.js.map +1 -1
- package/dist/queries/api-client/shared.d.ts +1 -0
- package/dist/queries/api-client/shared.d.ts.map +1 -1
- package/dist/queries/api-client/shared.js +16 -1
- package/dist/queries/api-client/shared.js.map +1 -1
- package/dist/queries/deliveries/issues.d.ts.map +1 -1
- package/dist/queries/deliveries/issues.js +3 -0
- package/dist/queries/deliveries/issues.js.map +1 -1
- package/dist/queries/focuses/focus-discovery.d.ts +15 -0
- package/dist/queries/focuses/focus-discovery.d.ts.map +1 -1
- package/dist/queries/focuses/focus-discovery.js +10 -1
- package/dist/queries/focuses/focus-discovery.js.map +1 -1
- package/dist/queries/prd/prd.d.ts.map +1 -1
- package/dist/queries/prd/prd.js +2 -1
- package/dist/queries/prd/prd.js.map +1 -1
- package/dist/queries/tower/tower-state.d.ts +56 -0
- package/dist/queries/tower/tower-state.d.ts.map +1 -0
- package/dist/queries/tower/tower-state.js +76 -0
- package/dist/queries/tower/tower-state.js.map +1 -0
- package/dist/queries/verification/verification.js +1 -1
- package/dist/queries/wire-schemas/schemas.d.ts +12 -0
- package/dist/queries/wire-schemas/schemas.d.ts.map +1 -1
- package/dist/queries/wire-schemas/schemas.js +11 -0
- package/dist/queries/wire-schemas/schemas.js.map +1 -1
- package/dist/spawn/lead-continuation.d.ts +9 -1
- package/dist/spawn/lead-continuation.d.ts.map +1 -1
- package/dist/spawn/lead-continuation.js +37 -11
- package/dist/spawn/lead-continuation.js.map +1 -1
- package/dist/spawn/spawn-decision.d.ts.map +1 -1
- package/dist/spawn/spawn-decision.js +4 -1
- package/dist/spawn/spawn-decision.js.map +1 -1
- package/dist/supervision/spawn-cooldown.js +1 -1
- package/dist/supervision/stop-rows-team-session.d.ts.map +1 -1
- package/dist/supervision/stop-rows-team-session.js +11 -10
- package/dist/supervision/stop-rows-team-session.js.map +1 -1
- package/dist/supervision/team-work.d.ts.map +1 -1
- package/dist/supervision/team-work.js +3 -1
- package/dist/supervision/team-work.js.map +1 -1
- package/dist/testing/fixtures/api-client.js +1 -1
- package/dist/testing/fixtures/approved-head-inert-deps.js +1 -1
- package/dist/testing/fixtures/approved-head-inert-deps.js.map +1 -1
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/testing/fixtures/pending-spawn-world.d.ts.map +1 -1
- package/dist/testing/fixtures/pending-spawn-world.js +3 -1
- package/dist/testing/fixtures/pending-spawn-world.js.map +1 -1
- package/dist/testing/fixtures/review-exit-deps.js +1 -1
- package/dist/testing/fixtures/review-exit-deps.js.map +1 -1
- package/dist/testing/fixtures/verification-deps.js +1 -1
- package/dist/testing/fixtures/verification-feedback-deps.d.ts.map +1 -1
- package/dist/testing/fixtures/verification-feedback-deps.js +2 -0
- package/dist/testing/fixtures/verification-feedback-deps.js.map +1 -1
- package/dist/testing/fixtures/worktree-provisioning-test-deps.d.ts.map +1 -1
- package/dist/testing/fixtures/worktree-provisioning-test-deps.js +1 -0
- package/dist/testing/fixtures/worktree-provisioning-test-deps.js.map +1 -1
- package/dist/tower/focus-landed.d.ts +4 -0
- package/dist/tower/focus-landed.d.ts.map +1 -0
- package/dist/tower/focus-landed.js +45 -0
- package/dist/tower/focus-landed.js.map +1 -0
- package/dist/tower/tower-admission.d.ts +91 -0
- package/dist/tower/tower-admission.d.ts.map +1 -0
- package/dist/tower/tower-admission.js +137 -0
- package/dist/tower/tower-admission.js.map +1 -0
- package/dist/tower/tower-graph.d.ts +23 -0
- package/dist/tower/tower-graph.d.ts.map +1 -0
- package/dist/tower/tower-graph.js +53 -0
- package/dist/tower/tower-graph.js.map +1 -0
- package/dist/tower/tower-heartbeat-status.d.ts +28 -0
- package/dist/tower/tower-heartbeat-status.d.ts.map +1 -0
- package/dist/tower/tower-heartbeat-status.js +11 -0
- package/dist/tower/tower-heartbeat-status.js.map +1 -0
- package/dist/tower/tower-mode.d.ts +9 -0
- package/dist/tower/tower-mode.d.ts.map +1 -0
- package/dist/tower/tower-mode.js +16 -0
- package/dist/tower/tower-mode.js.map +1 -0
- package/dist/tower/tower-pass-standing.d.ts +8 -0
- package/dist/tower/tower-pass-standing.d.ts.map +1 -0
- package/dist/tower/tower-pass-standing.js +50 -0
- package/dist/tower/tower-pass-standing.js.map +1 -0
- package/dist/tower/tower-pass.d.ts +55 -0
- package/dist/tower/tower-pass.d.ts.map +1 -0
- package/dist/tower/tower-pass.js +59 -0
- package/dist/tower/tower-pass.js.map +1 -0
- package/dist/tower/tower-production-pass.d.ts +3 -0
- package/dist/tower/tower-production-pass.d.ts.map +1 -0
- package/dist/tower/tower-production-pass.js +24 -0
- package/dist/tower/tower-production-pass.js.map +1 -0
- package/dist/tower/tower-spawn-intent.d.ts +72 -0
- package/dist/tower/tower-spawn-intent.d.ts.map +1 -0
- package/dist/tower/tower-spawn-intent.js +138 -0
- package/dist/tower/tower-spawn-intent.js.map +1 -0
- package/dist/tower/tower-verdicts.d.ts +81 -0
- package/dist/tower/tower-verdicts.d.ts.map +1 -0
- package/dist/tower/tower-verdicts.js +139 -0
- package/dist/tower/tower-verdicts.js.map +1 -0
- package/dist/types/index.js +1 -1
- package/dist/types/issue-status.d.ts +14 -0
- package/dist/types/issue-status.d.ts.map +1 -1
- package/dist/types/issue-status.js +14 -0
- package/dist/types/issue-status.js.map +1 -1
- package/dist/verify/deterministic-gate/verification-checkout.d.ts +8 -1
- package/dist/verify/deterministic-gate/verification-checkout.d.ts.map +1 -1
- package/dist/verify/deterministic-gate/verification-checkout.js +94 -15
- package/dist/verify/deterministic-gate/verification-checkout.js.map +1 -1
- package/dist/verify/deterministic-gate/verification-deterministic.d.ts.map +1 -1
- package/dist/verify/deterministic-gate/verification-deterministic.js +1 -0
- package/dist/verify/deterministic-gate/verification-deterministic.js.map +1 -1
- package/dist/verify/gate-verdicts/commit-verdict-record.js +1 -1
- package/dist/verify/outcome-routing/verification-rework-issue.d.ts +44 -5
- package/dist/verify/outcome-routing/verification-rework-issue.d.ts.map +1 -1
- package/dist/verify/outcome-routing/verification-rework-issue.js +85 -27
- package/dist/verify/outcome-routing/verification-rework-issue.js.map +1 -1
- package/dist/worktree/focus-worktree-state.d.ts.map +1 -1
- package/dist/worktree/focus-worktree-state.js +1 -0
- package/dist/worktree/focus-worktree-state.js.map +1 -1
- package/dist/worktree/verify-interlock.d.ts.map +1 -1
- package/dist/worktree/verify-interlock.js +1 -0
- package/dist/worktree/verify-interlock.js.map +1 -1
- package/dist/worktree/worktree-package-provisioning.d.ts +6 -1
- package/dist/worktree/worktree-package-provisioning.d.ts.map +1 -1
- package/dist/worktree/worktree-package-provisioning.js +37 -17
- package/dist/worktree/worktree-package-provisioning.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/tower/tower-entrypoints.test.ts,
|
|
3
|
+
// packages/daemon/src/tower/tower-heartbeat-status.test.ts, packages/daemon/src/tower/tower-pass.test.ts,
|
|
4
|
+
// packages/daemon/src/tower/tower-prd-arm.test.ts,
|
|
5
|
+
// packages/daemon/src/tower/tower-ready-row-ingestion.test.ts,
|
|
6
|
+
// packages/daemon/src/tower/tower-spawn-modes.test.ts
|
|
7
|
+
// PINNED-BY-END
|
|
8
|
+
/**
|
|
9
|
+
* Whether this daemon's tower pass for a product is STANDING -- the condition
|
|
10
|
+
* under which admission may obey a stored verdict at all.
|
|
11
|
+
*
|
|
12
|
+
* A stored verdict was written by some pass; it is only as good as the pass
|
|
13
|
+
* that keeps confirming it. When this process's most recent pass for the
|
|
14
|
+
* product failed -- or no pass for it has succeeded yet -- a still-fresh
|
|
15
|
+
* holding verdict in the database is a leftover, not a decision: admission
|
|
16
|
+
* fails open on it IMMEDIATELY (tower-admission.ts), rather than enforcing it
|
|
17
|
+
* until it ages out of the freshness bound. Another product's standing is
|
|
18
|
+
* untouched.
|
|
19
|
+
*
|
|
20
|
+
* ok -- the most recent pass for the product succeeded, within the
|
|
21
|
+
* freshness bound (TOWER_VERDICT_MAX_AGE_MS).
|
|
22
|
+
* failed -- the most recent pass for the product threw.
|
|
23
|
+
* not_run -- no pass for the product has succeeded within the bound (daemon
|
|
24
|
+
* start before the first pass, or a pass that stopped running).
|
|
25
|
+
*
|
|
26
|
+
* A leaf: in-process state, written by tower-pass.ts and read by admission
|
|
27
|
+
* through the spawn passes' deps (composition/spawn-pass-deps.ts).
|
|
28
|
+
*/
|
|
29
|
+
import { TOWER_VERDICT_MAX_AGE_MS } from '@telora/daemon-core';
|
|
30
|
+
const lastOutcome = new Map();
|
|
31
|
+
/** Record one product pass's outcome (tower-pass.ts runTowerPass). */
|
|
32
|
+
export function recordTowerPassOutcome(productId, ok, nowMs = Date.now()) {
|
|
33
|
+
lastOutcome.set(productId, { ok, atMs: nowMs });
|
|
34
|
+
}
|
|
35
|
+
/** The product's pass standing now. A focus with no product cannot be judged: not_run. */
|
|
36
|
+
export function towerPassStanding(productId, nowMs = Date.now()) {
|
|
37
|
+
if (!productId)
|
|
38
|
+
return 'not_run';
|
|
39
|
+
const last = lastOutcome.get(productId);
|
|
40
|
+
if (!last)
|
|
41
|
+
return 'not_run';
|
|
42
|
+
if (!last.ok)
|
|
43
|
+
return 'failed';
|
|
44
|
+
return nowMs - last.atMs > TOWER_VERDICT_MAX_AGE_MS ? 'not_run' : 'ok';
|
|
45
|
+
}
|
|
46
|
+
/** Test-only reset. */
|
|
47
|
+
export function resetTowerPassStanding() {
|
|
48
|
+
lastOutcome.clear();
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=tower-pass-standing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tower-pass-standing.js","sourceRoot":"","sources":["../../src/tower/tower-pass-standing.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,kEAAkE;AAClE,4GAA4G;AAC5G,qDAAqD;AACrD,iEAAiE;AACjE,wDAAwD;AACxD,gBAAgB;AAEhB;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAI/D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAyC,CAAC;AAErE,sEAAsE;AACtE,MAAM,UAAU,sBAAsB,CAAC,SAAiB,EAAE,EAAW,EAAE,QAAgB,IAAI,CAAC,GAAG,EAAE;IAC/F,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,iBAAiB,CAAC,SAAoC,EAAE,QAAgB,IAAI,CAAC,GAAG,EAAE;IAChG,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC9B,OAAO,KAAK,GAAG,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACzE,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,sBAAsB;IACpC,WAAW,CAAC,KAAK,EAAE,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tower pass: one pass per configured product, the SOLE writer of
|
|
3
|
+
* product_focuses.tower_verdict.
|
|
4
|
+
*
|
|
5
|
+
* It runs at the head of every ready-focus pass (so on daemon start and every
|
|
6
|
+
* poll), reads the product's limit and armed foci, computes every verdict
|
|
7
|
+
* (tower-verdicts.ts) from the armed set, the limit, focus rank, team presence
|
|
8
|
+
* and the spawn passes' last record of demand, and writes only what changed --
|
|
9
|
+
* or what is aging toward the bounded freshness age. So any change to those
|
|
10
|
+
* inputs is reflected on the next poll, and a verdict the pass keeps confirming
|
|
11
|
+
* never goes stale under the spawn pass.
|
|
12
|
+
*
|
|
13
|
+
* ISOLATION. A pass that throws for one product is logged and recorded
|
|
14
|
+
* (tower-pass-standing.ts): admission fails open on that product's foci at
|
|
15
|
+
* once, whatever verdicts they still carry, until a pass for it succeeds. It
|
|
16
|
+
* never stops another product's pass. Mode `off` runs no pass at all.
|
|
17
|
+
*
|
|
18
|
+
* The tower never arms or un-arms: nothing here writes assigned_agent_role_id.
|
|
19
|
+
*/
|
|
20
|
+
import type { TowerMode } from '@telora/daemon-core';
|
|
21
|
+
import type { TowerProductState } from '../queries/tower/tower-state.js';
|
|
22
|
+
import { type TowerVerdictWrite } from './tower-verdicts.js';
|
|
23
|
+
import type { TowerGraphInputs } from './tower-graph.js';
|
|
24
|
+
export interface TowerPassDeps {
|
|
25
|
+
products: ReadonlyArray<{
|
|
26
|
+
id: string;
|
|
27
|
+
}>;
|
|
28
|
+
mode: TowerMode;
|
|
29
|
+
readState: (productId: string) => Promise<TowerProductState>;
|
|
30
|
+
writeVerdicts: (productId: string, writes: readonly TowerVerdictWrite[]) => Promise<void>;
|
|
31
|
+
/** Foci holding a live coding or review team now. */
|
|
32
|
+
liveTeamFocusIds: () => ReadonlySet<string>;
|
|
33
|
+
/** Foci the spawn passes last found wanting a team (null = not yet known). */
|
|
34
|
+
wantingFocusIds: () => ReadonlySet<string> | null;
|
|
35
|
+
/**
|
|
36
|
+
* The product's graph inputs (depends_on / excludes / landed), judged from the
|
|
37
|
+
* state read (tower/tower-graph.ts over the product's git). Throwing fails the
|
|
38
|
+
* product's pass, which fails open.
|
|
39
|
+
*/
|
|
40
|
+
judgeGraph: (productId: string, state: TowerProductState) => TowerGraphInputs;
|
|
41
|
+
now: () => Date;
|
|
42
|
+
}
|
|
43
|
+
/** The pass's deps over the running daemon, or null before it has a live config (composition/tower-pass-context.ts). */
|
|
44
|
+
export type TowerPassDepsSource = () => TowerPassDeps | null;
|
|
45
|
+
export interface TowerProductOutcome {
|
|
46
|
+
productId: string;
|
|
47
|
+
ok: boolean;
|
|
48
|
+
writes: number;
|
|
49
|
+
error?: string;
|
|
50
|
+
}
|
|
51
|
+
/** Run one product's pass. Throws on a read/compute/write failure (the caller isolates it). */
|
|
52
|
+
export declare function runTowerProductPass(productId: string, deps: TowerPassDeps): Promise<number>;
|
|
53
|
+
/** Run every product's pass; one product's failure never stops another's. */
|
|
54
|
+
export declare function runTowerPass(deps: TowerPassDeps): Promise<TowerProductOutcome[]>;
|
|
55
|
+
//# sourceMappingURL=tower-pass.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tower-pass.d.ts","sourceRoot":"","sources":["../../src/tower/tower-pass.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAMzD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxC,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC7D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,iBAAiB,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,8EAA8E;IAC9E,eAAe,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAClD;;;;OAIG;IACH,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,KAAK,gBAAgB,CAAC;IAC9E,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAED,wHAAwH;AACxH,MAAM,MAAM,mBAAmB,GAAG,MAAM,aAAa,GAAG,IAAI,CAAC;AAE7D,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+FAA+F;AAC/F,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBjG;AAED,6EAA6E;AAC7E,wBAAsB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAmBtF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/tower/tower-entrypoints.test.ts,
|
|
3
|
+
// packages/daemon/src/tower/tower-pass.test.ts, packages/daemon/src/tower/tower-prd-arm.test.ts,
|
|
4
|
+
// packages/daemon/src/tower/tower-spawn-modes.test.ts,
|
|
5
|
+
// packages/daemon/src/tower/tower-state-ingestion.test.ts
|
|
6
|
+
// PINNED-BY-END
|
|
7
|
+
import { createLogger } from '../log/daemon-log.js';
|
|
8
|
+
import { computeTowerVerdicts, planTowerWrites, } from './tower-verdicts.js';
|
|
9
|
+
import { recordTowerPassOutcome } from './tower-pass-standing.js';
|
|
10
|
+
const log = createLogger({ module: 'tower' });
|
|
11
|
+
/** Run one product's pass. Throws on a read/compute/write failure (the caller isolates it). */
|
|
12
|
+
export async function runTowerProductPass(productId, deps) {
|
|
13
|
+
if (deps.mode === 'off')
|
|
14
|
+
return 0;
|
|
15
|
+
const state = await deps.readState(productId);
|
|
16
|
+
const foci = state.foci.map((f) => ({
|
|
17
|
+
focusId: f.focusId,
|
|
18
|
+
focusName: f.focusName,
|
|
19
|
+
priorityRank: f.priorityRank,
|
|
20
|
+
armedAt: f.agentAssignedAt ?? null,
|
|
21
|
+
previousVerdict: f.towerVerdict ?? null,
|
|
22
|
+
}));
|
|
23
|
+
const graph = deps.judgeGraph(productId, state);
|
|
24
|
+
const now = deps.now();
|
|
25
|
+
const computed = computeTowerVerdicts({
|
|
26
|
+
foci,
|
|
27
|
+
limit: state.concurrencyLimit ?? null,
|
|
28
|
+
liveTeamFocusIds: deps.liveTeamFocusIds(),
|
|
29
|
+
wantingFocusIds: deps.wantingFocusIds(),
|
|
30
|
+
...graph,
|
|
31
|
+
mode: deps.mode,
|
|
32
|
+
now: now.toISOString(),
|
|
33
|
+
});
|
|
34
|
+
const writes = planTowerWrites(foci, computed, state.unarmedWithVerdictFocusIds ?? [], now.getTime());
|
|
35
|
+
await deps.writeVerdicts(productId, writes);
|
|
36
|
+
return writes.length;
|
|
37
|
+
}
|
|
38
|
+
/** Run every product's pass; one product's failure never stops another's. */
|
|
39
|
+
export async function runTowerPass(deps) {
|
|
40
|
+
if (deps.mode === 'off')
|
|
41
|
+
return [];
|
|
42
|
+
const outcomes = [];
|
|
43
|
+
for (const product of deps.products) {
|
|
44
|
+
try {
|
|
45
|
+
const writes = await runTowerProductPass(product.id, deps);
|
|
46
|
+
recordTowerPassOutcome(product.id, true, deps.now().getTime());
|
|
47
|
+
outcomes.push({ productId: product.id, ok: true, writes });
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
51
|
+
recordTowerPassOutcome(product.id, false, deps.now().getTime());
|
|
52
|
+
log.warn(`[tower] pass failed for product ${product.id.slice(0, 8)}: ${error} -- admission fails open on its foci ` +
|
|
53
|
+
`until a pass for it succeeds (tower-pass-standing.ts)`);
|
|
54
|
+
outcomes.push({ productId: product.id, ok: false, writes: 0, error });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return outcomes;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=tower-pass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tower-pass.js","sourceRoot":"","sources":["../../src/tower/tower-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tower-production-pass.d.ts","sourceRoot":"","sources":["../../src/tower/tower-production-pass.ts"],"names":[],"mappings":"AAaA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAIzE,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOnF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/tower/tower-production-pass.test.ts,
|
|
3
|
+
// packages/daemon/src/tower/tower-spawn-modes.test.ts
|
|
4
|
+
// PINNED-BY-END
|
|
5
|
+
/**
|
|
6
|
+
* The tower pass at the head of every production ready-focus pass
|
|
7
|
+
* (focus/focus-poller-spawn.ts runProductionReadyFocusPass), over the deps the
|
|
8
|
+
* composition root binds (composition/tower-pass-context.ts). Never throws into
|
|
9
|
+
* the spawn pass it heads: a failed tower costs no spawn.
|
|
10
|
+
*/
|
|
11
|
+
import { createLogger } from '../log/daemon-log.js';
|
|
12
|
+
import { runTowerPass } from './tower-pass.js';
|
|
13
|
+
const log = createLogger({ module: 'tower' });
|
|
14
|
+
export async function runTowerPassSafely(source) {
|
|
15
|
+
try {
|
|
16
|
+
const deps = source();
|
|
17
|
+
if (deps)
|
|
18
|
+
await runTowerPass(deps);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
log.warn(`[tower] pass failed: ${err instanceof Error ? err.message : String(err)} -- admission fails open`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=tower-production-pass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tower-production-pass.js","sourceRoot":"","sources":["../../src/tower/tower-production-pass.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,sEAAsE;AACtE,wDAAwD;AACxD,gBAAgB;AAEhB;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,YAAY,EAA4B,MAAM,iBAAiB,CAAC;AAEzE,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAA2B;IAClE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC;QACtB,IAAI,IAAI;YAAE,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CAAC,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAC/G,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which armed foci the spawn passes last found WANTING a team -- the tower's
|
|
3
|
+
* memory of demand, so a free slot goes to a focus that would use it.
|
|
4
|
+
*
|
|
5
|
+
* A spawn pass records a focus as wanting only when the focus reached the
|
|
6
|
+
* tower's admission and was stopped there by the tower or the host cap --
|
|
7
|
+
* demand still OPEN. A focus that spawned has had its demand met (its live team
|
|
8
|
+
* holds a slot by the slot rule, not by wanting), and a focus a pass stopped
|
|
9
|
+
* for any other reason (no team work, a verify hold, an escalation, an operator
|
|
10
|
+
* hold, the review-cycle cap, a cooldown) cannot run, so it does not want.
|
|
11
|
+
*
|
|
12
|
+
* Two rules keep the record CURRENT rather than a snapshot of an older pass:
|
|
13
|
+
* - Each spawn pass is a unit (beginSpawnIntentPass .. commitSpawnIntentPass):
|
|
14
|
+
* every focus the pass OFFERED and did not record as wanting is committed as
|
|
15
|
+
* not wanting, whichever earlier gate stopped it -- so a queued directive
|
|
16
|
+
* parked behind a verify hold or an escalation reserves no product slot.
|
|
17
|
+
* - Before the tower allocates slots, the ready pass reconciles the record
|
|
18
|
+
* against what could run NOW (reconcileSurfacedSpawnIntent, run by
|
|
19
|
+
* focus/focus-poller-spawn.ts runReadyFocusPassThroughTower ahead of the
|
|
20
|
+
* tower pass): every surfaced focus is probed through its spawn pass's gates
|
|
21
|
+
* bar the tower and the host cap (focus/spawn-demand-probe.ts), and wants a
|
|
22
|
+
* slot exactly when they are open -- the first time it surfaces, and the
|
|
23
|
+
* pass its gate clears. A focus no longer surfaced (not ready, no queued
|
|
24
|
+
* directive) wants nothing.
|
|
25
|
+
*
|
|
26
|
+
* Before any pass or reconcile has recorded anything the set is UNKNOWN (null)
|
|
27
|
+
* and the tower treats every focus as wanting -- the safe reading on daemon
|
|
28
|
+
* start.
|
|
29
|
+
*
|
|
30
|
+
* A leaf: in-process state only, imported by the spawn passes (focus/), the
|
|
31
|
+
* admission (tower-admission.ts) and the tower pass (tower-pass.ts).
|
|
32
|
+
*/
|
|
33
|
+
/** A record older than this no longer counts (two missed 30s polls plus margin). */
|
|
34
|
+
export declare const SPAWN_INTENT_TTL_MS = 90000;
|
|
35
|
+
/** The spawn pass a record comes from (each runs as its own unit). */
|
|
36
|
+
export type SpawnIntentSource = 'ready' | 'pending';
|
|
37
|
+
/**
|
|
38
|
+
* Open a spawn pass over the foci it offers. Until it commits, what the pass
|
|
39
|
+
* records stays staged: the tower reads the last COMMITTED record, never a
|
|
40
|
+
* half-finished pass.
|
|
41
|
+
*/
|
|
42
|
+
export declare function beginSpawnIntentPass(source: SpawnIntentSource, offeredFocusIds: Iterable<string>): void;
|
|
43
|
+
/** Record what a spawn pass found for one focus (staged while that pass is open). */
|
|
44
|
+
export declare function recordSpawnIntent(focusId: string, wants: boolean, nowMs?: number, source?: SpawnIntentSource): void;
|
|
45
|
+
/**
|
|
46
|
+
* Close a spawn pass: every focus it offered is committed -- wanting when the
|
|
47
|
+
* pass recorded it so, not wanting otherwise. No-op when no pass is open (a pass
|
|
48
|
+
* that returned before offering anything changes nothing).
|
|
49
|
+
*/
|
|
50
|
+
export declare function commitSpawnIntentPass(source: SpawnIntentSource, nowMs?: number): void;
|
|
51
|
+
/** Drop an open pass uncommitted (the pass threw): the last committed record stands. */
|
|
52
|
+
export declare function abandonSpawnIntentPass(source: SpawnIntentSource): void;
|
|
53
|
+
/**
|
|
54
|
+
* Reconcile demand with what could run NOW. `demand` holds EVERY surfaced focus
|
|
55
|
+
* (ready, or holding a queued directive) with whether its spawn pass's gates,
|
|
56
|
+
* bar the tower and the host cap, are open on this pass
|
|
57
|
+
* (focus/spawn-demand-probe.ts), or null when its probe could not answer:
|
|
58
|
+
* - a focus not in `demand` is no longer surfaced: its record is dropped, so it
|
|
59
|
+
* reads as not wanting;
|
|
60
|
+
* - a probed focus's record becomes the probe's answer, whatever an earlier
|
|
61
|
+
* pass recorded -- a gate that has cleared since makes it want, a gate that
|
|
62
|
+
* holds it (even on the first pass that surfaces it) makes it not want;
|
|
63
|
+
* - a focus whose probe could not answer keeps a fresh record; with none (the
|
|
64
|
+
* first pass that surfaces it) or an expired one it WANTS: an unreadable gate
|
|
65
|
+
* fails open, so a read error never silently demotes a higher-ranked focus.
|
|
66
|
+
*/
|
|
67
|
+
export declare function reconcileSurfacedSpawnIntent(demand: ReadonlyMap<string, boolean | null>, nowMs?: number): void;
|
|
68
|
+
/** The foci last found wanting a team, or null before any pass has recorded. */
|
|
69
|
+
export declare function wantingFocusIds(nowMs?: number): ReadonlySet<string> | null;
|
|
70
|
+
/** Test-only reset. */
|
|
71
|
+
export declare function resetSpawnIntent(): void;
|
|
72
|
+
//# sourceMappingURL=tower-spawn-intent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tower-spawn-intent.d.ts","sourceRoot":"","sources":["../../src/tower/tower-spawn-intent.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAE1C,sEAAsE;AACtE,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,SAAS,CAAC;AAapD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,EAAE,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAEvG;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,GAAE,MAAmB,EAAE,MAAM,GAAE,iBAA2B,GAAG,IAAI,CASxI;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,GAAE,MAAmB,GAAG,IAAI,CAMjG;AAED,wFAAwF;AACxF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAEtE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC,EAAE,KAAK,GAAE,MAAmB,GAAG,IAAI,CAe1H;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,KAAK,GAAE,MAAmB,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAWtF;AAED,uBAAuB;AACvB,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/tower/tower-entrypoints.test.ts,
|
|
3
|
+
// packages/daemon/src/tower/tower-prd-arm.test.ts,
|
|
4
|
+
// packages/daemon/src/tower/tower-ready-row-ingestion.test.ts,
|
|
5
|
+
// packages/daemon/src/tower/tower-spawn-modes.test.ts
|
|
6
|
+
// PINNED-BY-END
|
|
7
|
+
/**
|
|
8
|
+
* Which armed foci the spawn passes last found WANTING a team -- the tower's
|
|
9
|
+
* memory of demand, so a free slot goes to a focus that would use it.
|
|
10
|
+
*
|
|
11
|
+
* A spawn pass records a focus as wanting only when the focus reached the
|
|
12
|
+
* tower's admission and was stopped there by the tower or the host cap --
|
|
13
|
+
* demand still OPEN. A focus that spawned has had its demand met (its live team
|
|
14
|
+
* holds a slot by the slot rule, not by wanting), and a focus a pass stopped
|
|
15
|
+
* for any other reason (no team work, a verify hold, an escalation, an operator
|
|
16
|
+
* hold, the review-cycle cap, a cooldown) cannot run, so it does not want.
|
|
17
|
+
*
|
|
18
|
+
* Two rules keep the record CURRENT rather than a snapshot of an older pass:
|
|
19
|
+
* - Each spawn pass is a unit (beginSpawnIntentPass .. commitSpawnIntentPass):
|
|
20
|
+
* every focus the pass OFFERED and did not record as wanting is committed as
|
|
21
|
+
* not wanting, whichever earlier gate stopped it -- so a queued directive
|
|
22
|
+
* parked behind a verify hold or an escalation reserves no product slot.
|
|
23
|
+
* - Before the tower allocates slots, the ready pass reconciles the record
|
|
24
|
+
* against what could run NOW (reconcileSurfacedSpawnIntent, run by
|
|
25
|
+
* focus/focus-poller-spawn.ts runReadyFocusPassThroughTower ahead of the
|
|
26
|
+
* tower pass): every surfaced focus is probed through its spawn pass's gates
|
|
27
|
+
* bar the tower and the host cap (focus/spawn-demand-probe.ts), and wants a
|
|
28
|
+
* slot exactly when they are open -- the first time it surfaces, and the
|
|
29
|
+
* pass its gate clears. A focus no longer surfaced (not ready, no queued
|
|
30
|
+
* directive) wants nothing.
|
|
31
|
+
*
|
|
32
|
+
* Before any pass or reconcile has recorded anything the set is UNKNOWN (null)
|
|
33
|
+
* and the tower treats every focus as wanting -- the safe reading on daemon
|
|
34
|
+
* start.
|
|
35
|
+
*
|
|
36
|
+
* A leaf: in-process state only, imported by the spawn passes (focus/), the
|
|
37
|
+
* admission (tower-admission.ts) and the tower pass (tower-pass.ts).
|
|
38
|
+
*/
|
|
39
|
+
/** A record older than this no longer counts (two missed 30s polls plus margin). */
|
|
40
|
+
export const SPAWN_INTENT_TTL_MS = 90_000;
|
|
41
|
+
const records = new Map();
|
|
42
|
+
let everRecorded = false;
|
|
43
|
+
/** A spawn pass in progress: the foci it offered and what it recorded for them so far. */
|
|
44
|
+
const openPasses = new Map();
|
|
45
|
+
/**
|
|
46
|
+
* Open a spawn pass over the foci it offers. Until it commits, what the pass
|
|
47
|
+
* records stays staged: the tower reads the last COMMITTED record, never a
|
|
48
|
+
* half-finished pass.
|
|
49
|
+
*/
|
|
50
|
+
export function beginSpawnIntentPass(source, offeredFocusIds) {
|
|
51
|
+
openPasses.set(source, { offered: new Set(offeredFocusIds), wanting: new Set() });
|
|
52
|
+
}
|
|
53
|
+
/** Record what a spawn pass found for one focus (staged while that pass is open). */
|
|
54
|
+
export function recordSpawnIntent(focusId, wants, nowMs = Date.now(), source = 'ready') {
|
|
55
|
+
const open = openPasses.get(source);
|
|
56
|
+
if (open && open.offered.has(focusId)) {
|
|
57
|
+
if (wants)
|
|
58
|
+
open.wanting.add(focusId);
|
|
59
|
+
else
|
|
60
|
+
open.wanting.delete(focusId);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
records.set(focusId, { wants, atMs: nowMs });
|
|
64
|
+
everRecorded = true;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Close a spawn pass: every focus it offered is committed -- wanting when the
|
|
68
|
+
* pass recorded it so, not wanting otherwise. No-op when no pass is open (a pass
|
|
69
|
+
* that returned before offering anything changes nothing).
|
|
70
|
+
*/
|
|
71
|
+
export function commitSpawnIntentPass(source, nowMs = Date.now()) {
|
|
72
|
+
const open = openPasses.get(source);
|
|
73
|
+
if (!open)
|
|
74
|
+
return;
|
|
75
|
+
openPasses.delete(source);
|
|
76
|
+
for (const focusId of open.offered)
|
|
77
|
+
records.set(focusId, { wants: open.wanting.has(focusId), atMs: nowMs });
|
|
78
|
+
if (open.offered.size > 0)
|
|
79
|
+
everRecorded = true;
|
|
80
|
+
}
|
|
81
|
+
/** Drop an open pass uncommitted (the pass threw): the last committed record stands. */
|
|
82
|
+
export function abandonSpawnIntentPass(source) {
|
|
83
|
+
openPasses.delete(source);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Reconcile demand with what could run NOW. `demand` holds EVERY surfaced focus
|
|
87
|
+
* (ready, or holding a queued directive) with whether its spawn pass's gates,
|
|
88
|
+
* bar the tower and the host cap, are open on this pass
|
|
89
|
+
* (focus/spawn-demand-probe.ts), or null when its probe could not answer:
|
|
90
|
+
* - a focus not in `demand` is no longer surfaced: its record is dropped, so it
|
|
91
|
+
* reads as not wanting;
|
|
92
|
+
* - a probed focus's record becomes the probe's answer, whatever an earlier
|
|
93
|
+
* pass recorded -- a gate that has cleared since makes it want, a gate that
|
|
94
|
+
* holds it (even on the first pass that surfaces it) makes it not want;
|
|
95
|
+
* - a focus whose probe could not answer keeps a fresh record; with none (the
|
|
96
|
+
* first pass that surfaces it) or an expired one it WANTS: an unreadable gate
|
|
97
|
+
* fails open, so a read error never silently demotes a higher-ranked focus.
|
|
98
|
+
*/
|
|
99
|
+
export function reconcileSurfacedSpawnIntent(demand, nowMs = Date.now()) {
|
|
100
|
+
for (const focusId of [...records.keys()]) {
|
|
101
|
+
if (!demand.has(focusId))
|
|
102
|
+
records.delete(focusId);
|
|
103
|
+
}
|
|
104
|
+
for (const [focusId, wants] of demand) {
|
|
105
|
+
if (wants !== null) {
|
|
106
|
+
records.set(focusId, { wants, atMs: nowMs });
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
// Unknown: a fresh record stands; with none (first surface) or an expired one, fail OPEN --
|
|
110
|
+
// the focus is ranked with the waiters, never silently demoted behind them by a read error.
|
|
111
|
+
const r = records.get(focusId);
|
|
112
|
+
if (!r || nowMs - r.atMs > SPAWN_INTENT_TTL_MS)
|
|
113
|
+
records.set(focusId, { wants: true, atMs: nowMs });
|
|
114
|
+
}
|
|
115
|
+
everRecorded = true;
|
|
116
|
+
}
|
|
117
|
+
/** The foci last found wanting a team, or null before any pass has recorded. */
|
|
118
|
+
export function wantingFocusIds(nowMs = Date.now()) {
|
|
119
|
+
if (!everRecorded)
|
|
120
|
+
return null;
|
|
121
|
+
const out = new Set();
|
|
122
|
+
for (const [focusId, r] of records) {
|
|
123
|
+
if (nowMs - r.atMs > SPAWN_INTENT_TTL_MS) {
|
|
124
|
+
records.delete(focusId);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (r.wants)
|
|
128
|
+
out.add(focusId);
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
/** Test-only reset. */
|
|
133
|
+
export function resetSpawnIntent() {
|
|
134
|
+
records.clear();
|
|
135
|
+
openPasses.clear();
|
|
136
|
+
everRecorded = false;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=tower-spawn-intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tower-spawn-intent.js","sourceRoot":"","sources":["../../src/tower/tower-spawn-intent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tower's verdict computation for ONE product -- pure (inputs in, verdicts
|
|
3
|
+
* out), so the slot rule, the rank order and the reason precedence are tested
|
|
4
|
+
* directly. The pass that feeds it and writes the result is tower-pass.ts.
|
|
5
|
+
*
|
|
6
|
+
* THE SLOT RULE. A focus occupies one of the product's slots only while it has a
|
|
7
|
+
* live coding or review team. A live focus is always cleared: the tower never
|
|
8
|
+
* stops a running team. The free slots (limit minus live foci; unbounded when
|
|
9
|
+
* the product has no limit) are cleared to the other foci in RANK order (lowest
|
|
10
|
+
* priority_rank first), foci the spawn pass last found WANTING a team ahead of
|
|
11
|
+
* foci it found idle -- so a focus that is armed but has nothing to run (waiting
|
|
12
|
+
* on CI, a verify hold, an escalation) never takes the slot from one that does.
|
|
13
|
+
* Every focus beyond the free slots holds with `product_limit_reached`.
|
|
14
|
+
*
|
|
15
|
+
* THE REASONS, in the precedence daemon-core TOWER_HOLD_REASONS declares:
|
|
16
|
+
* waits_on -- a depends_on predecessor has not landed;
|
|
17
|
+
* excluded_by -- an excludes edge joins it to a focus that has a live
|
|
18
|
+
* team or was cleared ahead of it in this pass (so two
|
|
19
|
+
* excluded foci are never cleared together);
|
|
20
|
+
* product_limit_reached -- the free slots went to foci ahead of it.
|
|
21
|
+
* A focus held for a graph reason takes no slot.
|
|
22
|
+
*
|
|
23
|
+
* A focus whose inputs could not be judged (UNAVAILABLE) gets no verdict (null):
|
|
24
|
+
* the spawn pass fails open on it rather than holding it.
|
|
25
|
+
*/
|
|
26
|
+
import { type TowerMode, type TowerVerdict } from '@telora/daemon-core';
|
|
27
|
+
/** One armed, active focus of the product. */
|
|
28
|
+
export interface TowerFocusInput {
|
|
29
|
+
focusId: string;
|
|
30
|
+
focusName: string;
|
|
31
|
+
priorityRank: number;
|
|
32
|
+
/** product_focuses.agent_assigned_at: the instant of the current arm. */
|
|
33
|
+
armedAt: string | null;
|
|
34
|
+
/** The stored product_focuses.tower_verdict (raw JSONB). */
|
|
35
|
+
previousVerdict: unknown;
|
|
36
|
+
}
|
|
37
|
+
/** An excludes edge seen from one side. */
|
|
38
|
+
export interface TowerExclusion {
|
|
39
|
+
otherFocusId: string;
|
|
40
|
+
otherFocusName: string;
|
|
41
|
+
evidence: string;
|
|
42
|
+
}
|
|
43
|
+
/** The first depends_on predecessor that has not landed. */
|
|
44
|
+
export interface TowerUnlandedPredecessor {
|
|
45
|
+
focusId: string;
|
|
46
|
+
focusName: string;
|
|
47
|
+
}
|
|
48
|
+
export interface TowerComputeInput {
|
|
49
|
+
foci: readonly TowerFocusInput[];
|
|
50
|
+
/** products.tower_concurrency_limit; null = no product limit. */
|
|
51
|
+
limit: number | null;
|
|
52
|
+
/** Foci holding a live coding or review team. */
|
|
53
|
+
liveTeamFocusIds: ReadonlySet<string>;
|
|
54
|
+
/** Foci the spawn pass last found wanting a team; null = not yet known (all want). */
|
|
55
|
+
wantingFocusIds: ReadonlySet<string> | null;
|
|
56
|
+
/** depends_on: focus -> its first unlanded predecessor. Absent = none. */
|
|
57
|
+
waitsOn?: ReadonlyMap<string, TowerUnlandedPredecessor>;
|
|
58
|
+
/** excludes: focus -> the foci an excludes edge joins it to. Absent = none. */
|
|
59
|
+
excludes?: ReadonlyMap<string, readonly TowerExclusion[]>;
|
|
60
|
+
/** Foci whose graph inputs could not be judged (e.g. a landed check failed). */
|
|
61
|
+
unavailableFocusIds?: ReadonlySet<string>;
|
|
62
|
+
mode: Exclude<TowerMode, 'off'>;
|
|
63
|
+
now: string;
|
|
64
|
+
}
|
|
65
|
+
/** focusId -> the verdict to hold, or null for "no verdict" (fail open). */
|
|
66
|
+
export type TowerComputed = Map<string, TowerVerdict | null>;
|
|
67
|
+
/** Compute every armed focus's verdict for one product. */
|
|
68
|
+
export declare function computeTowerVerdicts(input: TowerComputeInput): TowerComputed;
|
|
69
|
+
/** One verdict write: the verdict, or null to clear it. */
|
|
70
|
+
export interface TowerVerdictWrite {
|
|
71
|
+
focusId: string;
|
|
72
|
+
verdict: TowerVerdict | null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Which computed verdicts need writing: a changed verdict, a cleared-to-null
|
|
76
|
+
* one, or an unchanged one whose last confirmation is past HALF the bounded age
|
|
77
|
+
* (so a running tower's verdicts never go stale under the spawn pass). Plus a
|
|
78
|
+
* clear for every un-armed focus still carrying a verdict.
|
|
79
|
+
*/
|
|
80
|
+
export declare function planTowerWrites(foci: readonly TowerFocusInput[], computed: TowerComputed, unarmedWithVerdictFocusIds: readonly string[], nowMs: number, maxAgeMs?: number): TowerVerdictWrite[];
|
|
81
|
+
//# sourceMappingURL=tower-verdicts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tower-verdicts.d.ts","sourceRoot":"","sources":["../../src/tower/tower-verdicts.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAML,KAAK,SAAS,EACd,KAAK,YAAY,EAElB,MAAM,qBAAqB,CAAC;AAE7B,8CAA8C;AAC9C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,4DAA4D;IAC5D,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,eAAe,EAAE,CAAC;IACjC,iEAAiE;IACjE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iDAAiD;IACjD,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,sFAAsF;IACtF,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAC5C,0EAA0E;IAC1E,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IACxD,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC,CAAC;IAC1D,gFAAgF;IAChF,mBAAmB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC,CAAC;AAyC7D,2DAA2D;AAC3D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,aAAa,CAkD5E;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CAC9B;AAOD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,eAAe,EAAE,EAChC,QAAQ,EAAE,aAAa,EACvB,0BAA0B,EAAE,SAAS,MAAM,EAAE,EAC7C,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAiC,GAC1C,iBAAiB,EAAE,CAgBrB"}
|