@kici-dev/orchestrator 0.4.0 → 0.6.0
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/dist/__test-helpers__/fake-scaler-state-store.d.ts +24 -0
- package/dist/__test-helpers__/mock-db-query.d.ts +130 -0
- package/dist/__test-helpers__/mock-db.d.ts +27 -0
- package/dist/agent/agent-version.d.ts +34 -0
- package/dist/agent/dispatcher.d.ts +151 -13
- package/dist/agent/host-roster.d.ts +37 -5
- package/dist/agent/registry.d.ts +2 -0
- package/dist/app.d.ts +56 -7
- package/dist/approvals/apply-decision.d.ts +67 -19
- package/dist/approvals/held-run-release-wiring.d.ts +45 -0
- package/dist/approvals/independent-wiring.d.ts +55 -0
- package/dist/approvals/resume-router.d.ts +9 -0
- package/dist/approvals/triggerer-subject.d.ts +37 -0
- package/dist/cache/dep-cache.d.ts +54 -10
- package/dist/cache/global-eval-round-cache.d.ts +95 -0
- package/dist/cache/index.d.ts +3 -0
- package/dist/cache/pending-global-evals.d.ts +42 -0
- package/dist/cache/pending-inits.d.ts +10 -0
- package/dist/cli/api-client.d.ts +18 -0
- package/dist/cli/commands/cluster-settings.d.ts +52 -3
- package/dist/cli/commands/diagnose.d.ts +6 -0
- package/dist/cli/commands/held-run.d.ts +58 -0
- package/dist/cli/commands/runs.d.ts +1 -0
- package/dist/cli/commands/trust-policy.d.ts +117 -12
- package/dist/cli/kici-admin.d.ts +10 -0
- package/dist/cli.js +2770 -938
- package/dist/cluster/cluster-identity.d.ts +3 -3
- package/dist/cluster/cluster-settings-reader.d.ts +59 -1
- package/dist/cluster/coordinator.d.ts +44 -0
- package/dist/cluster/index.d.ts +4 -0
- package/dist/cluster/peer-handler.d.ts +19 -0
- package/dist/cluster/peer-registry.d.ts +17 -0
- package/dist/cluster/plan-headroom-store.d.ts +26 -0
- package/dist/cluster/worker-eviction.d.ts +69 -0
- package/dist/cold-store/load-event-log-range.d.ts +10 -3
- package/dist/cold-store/load-secret-audit-log-range.d.ts +4 -3
- package/dist/cold-store/tables/secret-audit-log.d.ts +4 -3
- package/dist/config.d.ts +48 -21
- package/dist/content-requirements-cache.d.ts +55 -0
- package/dist/contexts/held-runs.d.ts +123 -16
- package/dist/contexts/protection/aggregate.d.ts +3 -2
- package/dist/contexts/protection/branch-gate.d.ts +15 -0
- package/dist/contexts/protection/concurrency-gate.d.ts +8 -2
- package/dist/contexts/protection/pipeline.d.ts +18 -0
- package/dist/contexts/release-queued-holds.d.ts +35 -0
- package/dist/dashboard/attestation-filters.d.ts +8 -0
- package/dist/dashboard/handler.d.ts +6 -24
- package/dist/db/migrations/109_cluster_settings_cache_knobs.d.ts +4 -0
- package/dist/db/migrations/110_cluster_settings_global_eval_knobs.d.ts +4 -0
- package/dist/db/migrations/111_cluster_settings_global_eval_wait.d.ts +4 -0
- package/dist/db/migrations/112_execution_runs_workflow_repo.d.ts +4 -0
- package/dist/db/migrations/113_execution_runs_workflow_repo_index.d.ts +30 -0
- package/dist/db/migrations/114_ingest_queue_claim.d.ts +4 -0
- package/dist/db/migrations/115_global_workflows_cluster_switch.d.ts +9 -0
- package/dist/db/migrations/116_org_plan_headroom.d.ts +4 -0
- package/dist/db/migrations/117_invoke_gate_proxy_jobs.d.ts +4 -0
- package/dist/db/migrations/118_invoke_gate_scheduling.d.ts +4 -0
- package/dist/db/migrations/119_scaler_ha_ownership.d.ts +4 -0
- package/dist/db/migrations/120_cluster_settings_scaler_reaper.d.ts +4 -0
- package/dist/db/migrations/121_org_settings_allow_untrusted_dockerfile_builds.d.ts +19 -0
- package/dist/db/migrations/122_host_roster_scaler_managed.d.ts +25 -0
- package/dist/db/migrations/123_workflow_registrations_default_branch.d.ts +24 -0
- package/dist/db/migrations/124_cluster_settings_provision_backoff.d.ts +4 -0
- package/dist/db/migrations/125_org_trust_directory.d.ts +30 -0
- package/dist/db/migrations/126_held_runs_posted_pending_check.d.ts +28 -0
- package/dist/db/migrations/127_org_trust_policy_approval_expiry_seconds.d.ts +26 -0
- package/dist/db/migrations/128_execution_runs_global_eval_round.d.ts +23 -0
- package/dist/db/migrations/129_execution_runs_dispatch_routing_key.d.ts +24 -0
- package/dist/db/migrations/130_scaler_provision_outcomes.d.ts +32 -0
- package/dist/db/types.d.ts +428 -12
- package/dist/entry-helpers.d.ts +28 -0
- package/dist/events/event-emitter.d.ts +22 -0
- package/dist/events/event-router.d.ts +87 -1
- package/dist/events/types.d.ts +2 -23
- package/dist/git/credential-broker.d.ts +94 -0
- package/dist/git/inherited-secret.d.ts +24 -0
- package/dist/git/installation-token.d.ts +38 -0
- package/dist/git/job-context.d.ts +13 -0
- package/dist/index.js +324 -84
- package/dist/metrics/agent-metrics-aggregator.d.ts +4 -4
- package/dist/metrics/prometheus.d.ts +170 -3
- package/dist/oidc/oidc-mint-registration.d.ts +7 -3
- package/dist/orchestrator-core.d.ts +91 -24
- package/dist/pipeline/content-filter.d.ts +71 -0
- package/dist/pipeline/direct-ingress-deps.d.ts +100 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +521 -11
- package/dist/pipeline/global-eval-round.d.ts +307 -0
- package/dist/pipeline/internal-event-pipeline.d.ts +140 -0
- package/dist/pipeline/invoke-gate.d.ts +164 -0
- package/dist/pipeline/job-contexts.d.ts +16 -17
- package/dist/pipeline/needs-scheduler.d.ts +13 -0
- package/dist/pipeline/process-webhook.d.ts +126 -31
- package/dist/pipeline/processor.d.ts +282 -23
- package/dist/pipeline/rerun.d.ts +21 -0
- package/dist/pipeline/resume-workflow.d.ts +40 -7
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +15 -0
- package/dist/pipeline/security-hold-check.d.ts +217 -0
- package/dist/pipeline/test-pipeline.d.ts +12 -0
- package/dist/pipeline/undispatched-hold-checks.d.ts +23 -0
- package/dist/pipeline/webhook-payload-store.d.ts +20 -0
- package/dist/policy/dashboard-write-policy-listener.d.ts +62 -0
- package/dist/policy/dashboard-write-policy.d.ts +14 -0
- package/dist/provenance/backfill-run.d.ts +10 -1
- package/dist/provider-registry.d.ts +47 -4
- package/dist/providers/github/check-status-poster.d.ts +33 -3
- package/dist/providers/github/commit-message.d.ts +20 -0
- package/dist/providers/github/file-contents.d.ts +40 -0
- package/dist/providers/github/index.d.ts +2 -2
- package/dist/providers/github/normalizer.d.ts +3 -2
- package/dist/providers/local/normalizer.d.ts +3 -5
- package/dist/providers/universal-git/config.d.ts +2 -0
- package/dist/providers/universal-git/index.d.ts +11 -2
- package/dist/providers/universal-git/normalizer.d.ts +10 -0
- package/dist/queue/cleanup.d.ts +7 -1
- package/dist/queue/job-queue.d.ts +112 -7
- package/dist/queue/terminalize-unroutable.d.ts +13 -0
- package/dist/registration/extractor.d.ts +3 -3
- package/dist/registration/registration-index.d.ts +7 -0
- package/dist/registration/registration-run-match.d.ts +47 -0
- package/dist/registration/registration-store.d.ts +22 -0
- package/dist/reporting/check-run-reporter.d.ts +146 -2
- package/dist/reporting/check-run-summary.d.ts +25 -1
- package/dist/reporting/execution-tracker.d.ts +324 -9
- package/dist/reporting/log-chunk-sink.d.ts +8 -5
- package/dist/reporting/run-aggregator.d.ts +4 -14
- package/dist/reporting/step-display-order.d.ts +43 -0
- package/dist/routes/admin-held-runs.d.ts +142 -0
- package/dist/routes/admin-org-settings.d.ts +5 -0
- package/dist/routes/admin-trust-policy.d.ts +34 -6
- package/dist/routes/admin.d.ts +15 -0
- package/dist/scaler/backend-factory.d.ts +55 -0
- package/dist/scaler/bare-metal-backend.d.ts +43 -5
- package/dist/scaler/claim-store.d.ts +119 -0
- package/dist/scaler/config.d.ts +4 -0
- package/dist/scaler/container-backend.d.ts +18 -4
- package/dist/scaler/container-routing.d.ts +23 -0
- package/dist/scaler/container-spawn.d.ts +28 -0
- package/dist/scaler/event-backend.d.ts +124 -0
- package/dist/scaler/event-provision-reaper.d.ts +350 -0
- package/dist/scaler/failure-tracker.d.ts +1 -1
- package/dist/scaler/firecracker-backend.d.ts +33 -6
- package/dist/scaler/index.d.ts +11 -1
- package/dist/scaler/label-matcher.d.ts +4 -3
- package/dist/scaler/manager.d.ts +905 -76
- package/dist/scaler/resolve-container-auth.d.ts +45 -0
- package/dist/scaler/scaler-events.d.ts +11 -0
- package/dist/scaler/scaler-state-store.d.ts +294 -10
- package/dist/scaler/types.d.ts +131 -13
- package/dist/scaler/warm-pool.d.ts +132 -29
- package/dist/security/comment-handler.d.ts +50 -11
- package/dist/security/global-workflow-policy.d.ts +52 -12
- package/dist/security/identity-link.d.ts +60 -0
- package/dist/security/lock-source.d.ts +11 -13
- package/dist/security/reduced-privilege-note.d.ts +59 -0
- package/dist/security/trust-directory-store.d.ts +233 -0
- package/dist/security/trust-policy-gate.d.ts +94 -59
- package/dist/security/trust-policy-store.d.ts +3 -0
- package/dist/security/trust-resolver.d.ts +14 -98
- package/dist/server.d.ts +11 -1
- package/dist/server.js +56329 -42234
- package/dist/stale-detector/gate-deadline-detector.d.ts +52 -0
- package/dist/stale-detector/stale-run-detector.d.ts +61 -3
- package/dist/standalone.js +37619 -23570
- package/dist/webhook/ingest-accept.d.ts +70 -0
- package/dist/webhook/ingest-overflow-buffer.d.ts +35 -4
- package/dist/webhook/ingest-overflow-replayer.d.ts +50 -6
- package/dist/worker/in-memory-job-queue.d.ts +1 -1
- package/dist/ws/agent-handler.d.ts +31 -4
- package/dist/ws/dashboard-context-handler.d.ts +28 -4
- package/dist/ws/dashboard-global-workflows-handler.d.ts +36 -9
- package/dist/ws/execution-status-frame.d.ts +32 -0
- package/dist/ws/failure-messages.d.ts +2 -0
- package/dist/ws/git-credential-relay.d.ts +39 -0
- package/dist/ws/oidc-token-relay.d.ts +6 -11
- package/dist/ws/platform-client.d.ts +68 -9
- package/dist/ws/test-relay-handlers.d.ts +35 -10
- package/installer-image-digests.json +3 -3
- package/package.json +24 -19
- package/sbom.spdx.json +868 -821
- package/dist/approvals/team-membership-lookup.d.ts +0 -13
- package/dist/pipeline/inline-eval.d.ts +0 -44
- package/dist/providers/github/contributor-resolver.d.ts +0 -30
- package/dist/security/contributor-cache.d.ts +0 -83
|
@@ -13,15 +13,17 @@
|
|
|
13
13
|
* exported function is a narrative orchestrator that threads the typed
|
|
14
14
|
* results through the pipeline.
|
|
15
15
|
*/
|
|
16
|
-
import { ExecutionJobStatus, InitFailureCategory, CacheRefScope } from '@kici-dev/engine';
|
|
17
|
-
import type { LabelMatcher, LockWorkflow, LockJob, HostTargetSelector, SimulatedEvent, WorkflowDecision, MaterializedJob, ResolvedHostAgent, HostFacts } from '@kici-dev/engine';
|
|
16
|
+
import { ExecutionJobStatus, InitFailureCategory, CacheRefScope, HoldScope, TriggerSource } from '@kici-dev/engine';
|
|
17
|
+
import type { LabelMatcher, LockWorkflow, LockJob, ApprovalRequirement, HostTargetSelector, SimulatedEvent, WorkflowDecision, MaterializedJob, ResolvedHostAgent, HostFacts } from '@kici-dev/engine';
|
|
18
18
|
import { type HostRosterStore } from '../agent/host-roster.js';
|
|
19
|
+
import { gatherInvokeResults } from '../orchestrator-core.js';
|
|
19
20
|
import type { WebhookInfo } from '../webhook/handler.js';
|
|
20
21
|
import type { ProviderBundle } from '../provider-registry.js';
|
|
21
22
|
import type { Dispatcher } from '../agent/dispatcher.js';
|
|
22
23
|
import type { QueuedJobInput } from '../queue/job-queue.js';
|
|
23
24
|
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
24
25
|
import type { TrustPolicyOutcome } from '../security/trust-policy-gate.js';
|
|
26
|
+
import type { CreateHeldRunData } from '../contexts/held-runs.js';
|
|
25
27
|
import { type ProcessingDeps } from './processor.js';
|
|
26
28
|
/**
|
|
27
29
|
* Trusted refs (write+ contributor, default-branch) get the org-shared cache
|
|
@@ -55,6 +57,24 @@ export interface WorkflowDispatchContext {
|
|
|
55
57
|
bundle?: ProviderBundle;
|
|
56
58
|
payload: unknown;
|
|
57
59
|
repoIdentifier: string;
|
|
60
|
+
/**
|
|
61
|
+
* The repository that DEFINES the workflow being dispatched. `repoIdentifier`
|
|
62
|
+
* is the repository the run acts on; for an organization-wide workflow the
|
|
63
|
+
* two are different repositories, and every run row this dispatch writes has
|
|
64
|
+
* to say which one defined it.
|
|
65
|
+
*
|
|
66
|
+
* REQUIRED, for the same reason `securityDecision` is: a dispatch path that
|
|
67
|
+
* does not state it must not compile. Left optional, a new caller omits it
|
|
68
|
+
* silently and every row it records claims the workflow lives in the
|
|
69
|
+
* repository the run acted on — a null marker is read as that fact, not as
|
|
70
|
+
* "unknown" (`registration/registration-run-match.ts`).
|
|
71
|
+
*
|
|
72
|
+
* Every caller today states `repoIdentifier` or a value equal to it, because
|
|
73
|
+
* no cross-repository global dispatch enters this function — the global path
|
|
74
|
+
* builds its job inputs directly and dispatches them itself. The recording
|
|
75
|
+
* sites narrow, so stating the acted-on repository records nothing.
|
|
76
|
+
*/
|
|
77
|
+
workflowRepoIdentifier: string;
|
|
58
78
|
credentials: Record<string, unknown>;
|
|
59
79
|
event: SimulatedEvent;
|
|
60
80
|
eventWithFiles: SimulatedEvent;
|
|
@@ -67,6 +87,21 @@ export interface WorkflowDispatchContext {
|
|
|
67
87
|
};
|
|
68
88
|
};
|
|
69
89
|
resolvedOrgId: string;
|
|
90
|
+
/**
|
|
91
|
+
* Siblings admitted against a context concurrency limit during THIS dispatch
|
|
92
|
+
* pass, keyed by {@link concurrencyAdmissionKey}.
|
|
93
|
+
*
|
|
94
|
+
* The gate's running count counts jobs whose status is already `running`, so
|
|
95
|
+
* it cannot see anything this pass has only just admitted. Without this tally
|
|
96
|
+
* every child of a fan-out is evaluated against that one blind value and all
|
|
97
|
+
* N are admitted against a single slot.
|
|
98
|
+
*
|
|
99
|
+
* Created lazily on first use and dropped with the ctx. It is per-pass state,
|
|
100
|
+
* never persisted and never shared between passes; two concurrent passes
|
|
101
|
+
* still race, which is accepted — this is a throughput control, not an
|
|
102
|
+
* isolation boundary.
|
|
103
|
+
*/
|
|
104
|
+
concurrencyAdmissions?: Map<string, number>;
|
|
70
105
|
workflow: LockWorkflow;
|
|
71
106
|
decision: WorkflowDecision;
|
|
72
107
|
runId: string;
|
|
@@ -74,8 +109,93 @@ export interface WorkflowDispatchContext {
|
|
|
74
109
|
lockFileSource: string | undefined;
|
|
75
110
|
/** True when this run executes an uploaded local working tree (CLI remote run). */
|
|
76
111
|
localWorkingTree: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Identity that initiated this run, for `execution_runs.triggered_by`.
|
|
114
|
+
*
|
|
115
|
+
* Set by the CLI remote-run path, which knows its caller: the Platform relays
|
|
116
|
+
* the developer's actor on `test.relay.trigger` and the test pipeline renders
|
|
117
|
+
* it here. Undefined on the webhook path, where the initiator is a provider
|
|
118
|
+
* account rather than a KiCI principal — that attribution is carried by
|
|
119
|
+
* `triggerActorUsername` / `triggerActorUserId` instead.
|
|
120
|
+
*/
|
|
121
|
+
triggeredBy?: string | null;
|
|
122
|
+
/** Agent provenance label when the run was initiated through an agent credential. */
|
|
123
|
+
triggeredByAgentLabel?: string | null;
|
|
77
124
|
/** True only when invoked from the cross-source dispatch shell. */
|
|
78
125
|
crossSource: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Trigger-event string for the run's `triggerEvent`, stated explicitly rather
|
|
128
|
+
* than derived from `event.type`.
|
|
129
|
+
*
|
|
130
|
+
* The internal-event adapter needs this because a user `kiciEvent()` renders
|
|
131
|
+
* two different values: `jobConfig.event.type` is the literal `kici_event`,
|
|
132
|
+
* while the run carries the raw event name. `event.type` carries the former,
|
|
133
|
+
* so the latter has to be stated.
|
|
134
|
+
*
|
|
135
|
+
* Absent ⇒ the value derived from the event, unchanged for the webhook,
|
|
136
|
+
* CLI-remote-run and resume callers.
|
|
137
|
+
*/
|
|
138
|
+
triggerEventOverride?: string;
|
|
139
|
+
/**
|
|
140
|
+
* The exact `jobConfig.event` envelope to ship, stated explicitly instead of
|
|
141
|
+
* spreading the `SimulatedEvent` this dispatch matched against.
|
|
142
|
+
*
|
|
143
|
+
* Same reason as `triggerEventOverride`: the matcher's event shape and the
|
|
144
|
+
* shape user code observes are not the same object. `SimulatedEvent` requires
|
|
145
|
+
* `targetBranch` and carries `changedFiles`; the envelope omits both. An
|
|
146
|
+
* internally triggered run genuinely has no changed files, and its branch is
|
|
147
|
+
* provenance the orchestrator evaluates (the trigger matcher and the context
|
|
148
|
+
* branch gate) rather than a field user code asked for — the envelope becomes
|
|
149
|
+
* `RuleContext.event` on the agent and the `event` half of
|
|
150
|
+
* `buildConcurrencyGroupContext`, so publishing the branch would silently
|
|
151
|
+
* re-key the documented `ctx.event.targetBranch ?? 'default'` concurrency
|
|
152
|
+
* group of every existing internal workflow.
|
|
153
|
+
*
|
|
154
|
+
* Absent ⇒ the envelope derived from the event, unchanged for the webhook,
|
|
155
|
+
* CLI-remote-run and resume callers.
|
|
156
|
+
*/
|
|
157
|
+
eventEnvelopeOverride?: Record<string, unknown>;
|
|
158
|
+
/**
|
|
159
|
+
* Chain depth to stamp on the started run. A run summoned by an invoke gate
|
|
160
|
+
* carries its summoner's depth + 1, which is what bounds the chain-depth
|
|
161
|
+
* circuit breaker. Absent ⇒ 0, the column default (a webhook-triggered run
|
|
162
|
+
* starts a chain).
|
|
163
|
+
*
|
|
164
|
+
* Threading this is load-bearing: the breaker fails OPEN if the value is
|
|
165
|
+
* lost, so an unbounded summon recursion would go undetected. It is stamped
|
|
166
|
+
* as soon as the run row exists, because `releaseInvokeGate` reads the column
|
|
167
|
+
* back at gate-release time — which happens inside this same dispatch.
|
|
168
|
+
*/
|
|
169
|
+
chainDepth?: number;
|
|
170
|
+
/**
|
|
171
|
+
* Marks a run dispatched by a failure-lifecycle trigger, so its own
|
|
172
|
+
* completion is excluded from batch accumulation and a broken notifier
|
|
173
|
+
* cannot re-trigger itself (`EventRouter.isFailureLifecycleRun`).
|
|
174
|
+
*
|
|
175
|
+
* Persisted as a field INSIDE the `trigger_decision` JSON blob, merged onto
|
|
176
|
+
* the decision summary rather than replacing it.
|
|
177
|
+
*/
|
|
178
|
+
dispatchedByFailureLifecycle?: boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Marks a run the orchestrator triggered itself — a schedule fire, a
|
|
181
|
+
* workflow/job completion, a failure batch, a user `kiciEvent()`, or an
|
|
182
|
+
* invoke-gate summon — as opposed to one a provider webhook triggered.
|
|
183
|
+
*
|
|
184
|
+
* Read by the context branch gate, and by nothing else. Such a run usually
|
|
185
|
+
* carries a real branch in `event.targetBranch` — a schedule fire presents
|
|
186
|
+
* its registration's default branch, every other internal trigger inherits
|
|
187
|
+
* the branch of the run that emitted its event — and the gate matches it like
|
|
188
|
+
* any other run's. The flag singles out the runs whose `targetBranch` is
|
|
189
|
+
* EMPTY: a failure batch or a scaler event (many runs behind it, or none), a
|
|
190
|
+
* registration whose default branch has never been captured, an emitting run
|
|
191
|
+
* that is gone. The gate rejects those naming that cause, instead of quoting
|
|
192
|
+
* an empty value as though it were a branch name. It does NOT weaken the
|
|
193
|
+
* gate — a run with no branch cannot satisfy a restriction, `*` included.
|
|
194
|
+
*
|
|
195
|
+
* Absent ⇒ webhook-triggered, unchanged for the webhook, CLI-remote-run and
|
|
196
|
+
* resume callers.
|
|
197
|
+
*/
|
|
198
|
+
internallyTriggered?: boolean;
|
|
79
199
|
/**
|
|
80
200
|
* Outcome of the org trust-policy gate for this PR event. `pass` dispatches
|
|
81
201
|
* normally; `hold` parks the run in the security queue; `reject` fails it
|
|
@@ -91,13 +211,35 @@ export interface WorkflowDispatchContext {
|
|
|
91
211
|
*/
|
|
92
212
|
securityDecision: TrustPolicyOutcome;
|
|
93
213
|
/**
|
|
94
|
-
* Set when this dispatch call took a pending-jobs token
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
214
|
+
* Set when this dispatch call took a pending-jobs token covering the window
|
|
215
|
+
* between registering the run and registering its jobs — the source-pack
|
|
216
|
+
* build window, or the plain dispatch window when there is no build. Tokens
|
|
217
|
+
* are fungible, so the `finally` must release only one it actually took — an
|
|
218
|
+
* unpaired release would consume a token held by a deferred init / dynamic
|
|
219
|
+
* task and un-hold the run while its jobs are still being registered.
|
|
220
|
+
*/
|
|
221
|
+
dispatchWindowTokenHeld?: boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Set when this dispatch call inserted the `execution_runs` row before
|
|
224
|
+
* handing the first job to an agent. A row that exists with zero jobs can
|
|
225
|
+
* never complete (`isRunComplete` ends `run.jobs.size > 0`) and no sweeper
|
|
226
|
+
* reaps it, so a throw inside that window has to terminalize the run
|
|
227
|
+
* explicitly rather than leave it `pending` forever.
|
|
99
228
|
*/
|
|
100
|
-
|
|
229
|
+
runRegisteredBeforeDispatch?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Set once `setupDispatchContext` has posted this dispatch's queued
|
|
232
|
+
* `kici/<workflow>` check and one `kici/<workflow>/job/<name>` per static job.
|
|
233
|
+
*
|
|
234
|
+
* Those checks go up BEFORE anything decides whether the run will start, so
|
|
235
|
+
* every exit after setup owes them a conclusion. The named early exits each
|
|
236
|
+
* complete their own; a THROW does not, and nothing else can — the workflow
|
|
237
|
+
* check keys off a run whose jobs never registered, and the stale sweep only
|
|
238
|
+
* touches check runs already `in_progress`. Left alone they stay `queued`
|
|
239
|
+
* forever, which on a pull request is a check that never finishes and a
|
|
240
|
+
* branch-protection blocker.
|
|
241
|
+
*/
|
|
242
|
+
pendingChecksPosted?: boolean;
|
|
101
243
|
/** Composite dedup key `${info.deliveryId}:${reg.id}` (cross-source only). */
|
|
102
244
|
crossSourceDeliveryId?: string;
|
|
103
245
|
/**
|
|
@@ -154,6 +296,14 @@ export interface WorkflowDispatchContext {
|
|
|
154
296
|
*/
|
|
155
297
|
dispatchInputs?: Record<string, unknown>;
|
|
156
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* The minimal slice of a dispatch context the needs catch-up + ready-recompute
|
|
301
|
+
* helpers read. Both the per-repository `WorkflowDispatchContext` and the
|
|
302
|
+
* global-workflow dispatch path (which builds its own inputs and never
|
|
303
|
+
* constructs a full `WorkflowDispatchContext`) can drive the needs scheduler
|
|
304
|
+
* through this narrow shape.
|
|
305
|
+
*/
|
|
306
|
+
export type NeedsSchedulingContext = Pick<WorkflowDispatchContext, 'deps' | 'runId'>;
|
|
157
307
|
/**
|
|
158
308
|
* Build the dispatch-envelope event, carrying the orchestrator's already-fetched
|
|
159
309
|
* changed-files list + status from `eventWithFiles` as a fast-path (the agent
|
|
@@ -166,6 +316,14 @@ export interface DispatchMatchedWorkflowResult {
|
|
|
166
316
|
dispatchedJobCount: number;
|
|
167
317
|
/** Execution job ids of every dispatched/tracked job (root, gated, synthetic). */
|
|
168
318
|
dispatchedJobIds: string[];
|
|
319
|
+
/**
|
|
320
|
+
* Jobs whose dispatch is deferred to the agent init round (a dynamic context
|
|
321
|
+
* or a deferred-init job) and therefore not yet in `dispatchedJobIds`. These
|
|
322
|
+
* still run — they are dispatched asynchronously by `startDeferredPhases` —
|
|
323
|
+
* so a caller must not treat a run with pending deferred work as "nothing
|
|
324
|
+
* dispatched". Absent/0 on the early-return paths.
|
|
325
|
+
*/
|
|
326
|
+
deferredJobCount?: number;
|
|
169
327
|
/** True when the workflow install gate paused the dispatch (held run). */
|
|
170
328
|
held?: boolean;
|
|
171
329
|
/**
|
|
@@ -182,8 +340,6 @@ export interface DispatchMatchedWorkflowOptions {
|
|
|
182
340
|
* so secrets resolve directly and the dispatch flows into job dispatch.
|
|
183
341
|
*/
|
|
184
342
|
skipInstallProtectionGate?: boolean;
|
|
185
|
-
/** The released held-run id being resumed (for logging / correlation). */
|
|
186
|
-
reuseHeldRunId?: string;
|
|
187
343
|
/**
|
|
188
344
|
* The run id whose `held` execution_runs row should be reused (flipped to
|
|
189
345
|
* pending) instead of inserting a fresh row.
|
|
@@ -214,6 +370,120 @@ interface DispatchSetup {
|
|
|
214
370
|
*/
|
|
215
371
|
checkMode: string | undefined;
|
|
216
372
|
}
|
|
373
|
+
interface BuildPrepResult {
|
|
374
|
+
sourceTarUrl: string | undefined;
|
|
375
|
+
sourceTarHash: string | undefined;
|
|
376
|
+
depsUrl: string | undefined;
|
|
377
|
+
depsHash: string | undefined;
|
|
378
|
+
contentHash: string | undefined;
|
|
379
|
+
lockfileHash: string | undefined;
|
|
380
|
+
hasDynamicEntries: boolean;
|
|
381
|
+
dynamicEntries: ReadonlyArray<Extract<LockWorkflow['jobs'][number], {
|
|
382
|
+
_type: 'dynamic';
|
|
383
|
+
}>>;
|
|
384
|
+
staticJobs: readonly LockJob[];
|
|
385
|
+
/**
|
|
386
|
+
* Static jobs expanded into dispatchable children (matrix fan-out). Non-matrix
|
|
387
|
+
* jobs pass through 1:1. Every dispatch phase iterates this list, keying by
|
|
388
|
+
* `expandedName`. Dynamic-matrix jobs are flagged `pendingDynamicMatrix`.
|
|
389
|
+
*/
|
|
390
|
+
materializedJobs: readonly MaterializedJob[];
|
|
391
|
+
/** baseName -> expanded child names; drives needs-edge expansion. */
|
|
392
|
+
expansionMap: ReadonlyMap<string, readonly string[]>;
|
|
393
|
+
/** Jobs whose matrix could not be materialized (cap / zero-combination). */
|
|
394
|
+
matrixFailures: readonly RejectedJob[];
|
|
395
|
+
targetPlatform: string;
|
|
396
|
+
targetArch: string;
|
|
397
|
+
buildJobId: string | undefined;
|
|
398
|
+
buildJobName: string | undefined;
|
|
399
|
+
buildJobLabels: string[] | undefined;
|
|
400
|
+
buildJobTrackedEarly: boolean;
|
|
401
|
+
/** True when the build failed but dynamic entries can still proceed. */
|
|
402
|
+
buildFailed: boolean;
|
|
403
|
+
/**
|
|
404
|
+
* True when the helper has fully short-circuited the dispatch (build failed
|
|
405
|
+
* and no dynamic entries to fall back on, or build job rejected). Caller
|
|
406
|
+
* MUST early-return with `dispatchedJobCount: 0`.
|
|
407
|
+
*/
|
|
408
|
+
abort: boolean;
|
|
409
|
+
}
|
|
410
|
+
interface JobEnvData {
|
|
411
|
+
contextName?: string;
|
|
412
|
+
/** Configured env id matched for the first declared context name. */
|
|
413
|
+
contextId?: string;
|
|
414
|
+
/**
|
|
415
|
+
* Ordered bound-context names persisted on the job row (`(dynamic)`
|
|
416
|
+
* placeholder for elements unresolved at dispatch; overwritten with the
|
|
417
|
+
* agent-resolved list for dynamic contexts). Empty/undefined = no binding.
|
|
418
|
+
*/
|
|
419
|
+
contextNames?: string[];
|
|
420
|
+
contextVars?: Record<string, string>;
|
|
421
|
+
jobEnv?: Record<string, string>;
|
|
422
|
+
jobSecrets?: Record<string, string>;
|
|
423
|
+
jobNamespacedSecrets?: Record<string, Record<string, string>>;
|
|
424
|
+
/**
|
|
425
|
+
* Registry credentials for this job's container image, resolved from the
|
|
426
|
+
* secret NAMES the lock carries. Lifted to a top-level dispatch field (and
|
|
427
|
+
* stripped from jobConfig) before the message reaches the agent.
|
|
428
|
+
*/
|
|
429
|
+
containerRegistryAuth?: {
|
|
430
|
+
username: string;
|
|
431
|
+
password: string;
|
|
432
|
+
serveraddress: string;
|
|
433
|
+
};
|
|
434
|
+
held?: boolean;
|
|
435
|
+
/**
|
|
436
|
+
* Pending approval hold for this job, set when a context policy or
|
|
437
|
+
* explicit lock `approval` requires human sign-off. The dispatch loop turns
|
|
438
|
+
* this into a `held_runs` row + a stored pending job context so `release()`
|
|
439
|
+
* can re-dispatch after approval.
|
|
440
|
+
*/
|
|
441
|
+
approvalHold?: PendingApprovalHold;
|
|
442
|
+
/**
|
|
443
|
+
* A non-reviewer hold (security / wait-timer / concurrency-queue) decided by a
|
|
444
|
+
* context's protection rules. Carried rather than written on the spot so
|
|
445
|
+
* `holdJobForApproval` can create the row and the job's resume path in ONE
|
|
446
|
+
* transaction — the reviewer branch uses `approvalHold` for the same purpose.
|
|
447
|
+
*/
|
|
448
|
+
nonApprovalHold?: CreateHeldRunData;
|
|
449
|
+
rejected?: boolean;
|
|
450
|
+
rejectReason?: string;
|
|
451
|
+
pendingInit?: boolean;
|
|
452
|
+
/** Bound contexts skipped on a test/local run because they disallow local execution. */
|
|
453
|
+
skippedEnvs?: string[];
|
|
454
|
+
/**
|
|
455
|
+
* User-visible warning set whenever any bound context was unavailable for
|
|
456
|
+
* a test run (non-test or unconfigured) and skipped — surfaced on the CLI run
|
|
457
|
+
* output and the dashboard run view.
|
|
458
|
+
*/
|
|
459
|
+
envWarning?: string;
|
|
460
|
+
}
|
|
461
|
+
/** A resolved approval requirement awaiting hold creation in the dispatch loop. */
|
|
462
|
+
interface PendingApprovalHold {
|
|
463
|
+
scope: HoldScope;
|
|
464
|
+
triggerSource: TriggerSource;
|
|
465
|
+
requirement: ApprovalRequirement;
|
|
466
|
+
contextId: string | null;
|
|
467
|
+
queueType: 'context' | 'security';
|
|
468
|
+
}
|
|
469
|
+
interface DeferredInitJob {
|
|
470
|
+
mat: MaterializedJob;
|
|
471
|
+
initJobInput: QueuedJobInput;
|
|
472
|
+
}
|
|
473
|
+
interface JobEnvEvalResult {
|
|
474
|
+
jobContextData: Map<string, JobEnvData>;
|
|
475
|
+
deferredInitJobs: DeferredInitJob[];
|
|
476
|
+
runContextName: string | undefined;
|
|
477
|
+
runContextId: string | undefined;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Synthetic job-id prefix every needs-gate site stamps on a job it holds back.
|
|
481
|
+
* The release path (`dispatchReadyJob` → `findSyntheticJobId` → `addJobsToRun`)
|
|
482
|
+
* keys on it, so it is load-bearing rather than cosmetic — which is what makes
|
|
483
|
+
* it a sound way to recover the gated set without threading a parallel list
|
|
484
|
+
* through both the single-orchestrator and cluster dispatch paths.
|
|
485
|
+
*/
|
|
486
|
+
export declare const NEEDS_PENDING_JOB_ID_PREFIX = "needs-pending-";
|
|
217
487
|
interface RejectedJob {
|
|
218
488
|
jobId: string;
|
|
219
489
|
jobName: string;
|
|
@@ -227,6 +497,32 @@ interface RejectedJob {
|
|
|
227
497
|
*/
|
|
228
498
|
terminalStatus?: ExecutionJobStatus;
|
|
229
499
|
}
|
|
500
|
+
/**
|
|
501
|
+
* Whether this dispatch must run a `__build__` job before its real jobs.
|
|
502
|
+
*
|
|
503
|
+
* Either cache missing is a reason to build. The two are keyed on different
|
|
504
|
+
* things — the source cache on the workflow source's contentHash, the dep cache
|
|
505
|
+
* on the lockfile hash — so they miss independently, and a dependency bump is
|
|
506
|
+
* exactly the case that leaves the source warm and the deps cold. Gating only on
|
|
507
|
+
* the source miss made that state permanent: no build job ran, so nothing ever
|
|
508
|
+
* uploaded the dep tarball, so every agent fell back to installing from the
|
|
509
|
+
* registry on every job — and an agent with no route to that registry (a
|
|
510
|
+
* cloud-hosted one-shot agent, an air-gapped runner) could not run the job at
|
|
511
|
+
* all. The `__build__` job already carries `buildSourceNeeded` /
|
|
512
|
+
* `buildDepsNeeded` separately and the agent already honors both, so a
|
|
513
|
+
* deps-only build was implemented and simply unreachable.
|
|
514
|
+
*
|
|
515
|
+
* A hash is required alongside its miss: with no hash there is no cache key to
|
|
516
|
+
* write, so building would produce an artifact nothing could ever look up.
|
|
517
|
+
*/
|
|
518
|
+
export declare function buildIsNeeded(args: {
|
|
519
|
+
/** False when there is nothing to cache into (no bundle, no coordinator, …). */
|
|
520
|
+
cacheInfraAvailable: boolean;
|
|
521
|
+
sourceHit: boolean;
|
|
522
|
+
contentHash: string | undefined;
|
|
523
|
+
depHit: boolean;
|
|
524
|
+
lockfileHash: string | undefined;
|
|
525
|
+
}): boolean;
|
|
230
526
|
/**
|
|
231
527
|
* Build the QueuedJobInput for a synthetic `__bringup__` job: the orchestrator
|
|
232
528
|
* dispatches one per declared-but-un-agented `includeUninitialized` child to an
|
|
@@ -278,6 +574,28 @@ export declare function runsOnSelectorsForLockJob(lockJob: {
|
|
|
278
574
|
runsOn?: readonly LabelMatcher[];
|
|
279
575
|
excludeLabels?: readonly LabelMatcher[];
|
|
280
576
|
}): JobRoutingSelectors;
|
|
577
|
+
/**
|
|
578
|
+
* Runtime facts a job's own shape demands of the host that runs it.
|
|
579
|
+
*
|
|
580
|
+
* Only a `container.dockerfile` job gets one. Building shells out to a
|
|
581
|
+
* `docker` / `podman` CLI, so a host without one cannot run the job at all, and
|
|
582
|
+
* the agent self-reports `kici:runtime:container-build` when it has one.
|
|
583
|
+
*
|
|
584
|
+
* A job that names a finalized `image` deliberately gets NOTHING added. Adding
|
|
585
|
+
* an implicit requirement to jobs that already work is how container jobs were
|
|
586
|
+
* stranded once before: they had been running fine, and a routing gate the
|
|
587
|
+
* orchestrator could not actually evaluate made them match nothing. A dockerfile
|
|
588
|
+
* job is new, so requiring the fact strands no existing workflow — and an agent
|
|
589
|
+
* old enough not to report the fact is an agent that cannot build anyway.
|
|
590
|
+
*
|
|
591
|
+
* Applied when matching REGISTERED agents (the dispatcher), and deliberately
|
|
592
|
+
* NOT when consulting the scaler. A scaler backend is chosen by exact label-set
|
|
593
|
+
* containment, so a required label the operator never wrote in a pool's label
|
|
594
|
+
* set matches no backend at all — the job would be stranded `queued-no-backend`
|
|
595
|
+
* rather than spawned. The pool's hosts are the operator's to describe; what an
|
|
596
|
+
* agent can actually do is known only once it registers and says so.
|
|
597
|
+
*/
|
|
598
|
+
export declare function requiredRuntimeLabelsFor(container: unknown): string[];
|
|
281
599
|
/**
|
|
282
600
|
* Resolve a generated job's single bare-`agentId` `runsOn` into a host pin.
|
|
283
601
|
*
|
|
@@ -345,6 +663,187 @@ export declare function findInvalidApprovalTimeout(workflow: LockWorkflow): {
|
|
|
345
663
|
* no host facts to scope by.
|
|
346
664
|
*/
|
|
347
665
|
export declare function hostCtxFromMat(mat: MaterializedJob): HostFacts | undefined;
|
|
666
|
+
/**
|
|
667
|
+
* Key for the in-pass admission tally.
|
|
668
|
+
*
|
|
669
|
+
* The org id is part of the key so the tally inherits the cross-tenant scoping
|
|
670
|
+
* the running-count query already enforces on `execution_runs.customer_id`: a
|
|
671
|
+
* context name shared across tenants must not leak concurrency between them.
|
|
672
|
+
* One dispatch pass carries a single `resolvedOrgId`, so the org component is a
|
|
673
|
+
* standing invariant rather than a live discriminator — it is here so a future
|
|
674
|
+
* pass dispatching for more than one org cannot silently merge two orgs'
|
|
675
|
+
* tallies. `JSON.stringify` over the pair is used rather than string
|
|
676
|
+
* concatenation so no separator character can make two different pairs collide.
|
|
677
|
+
*/
|
|
678
|
+
export declare function concurrencyAdmissionKey(orgId: string, concurrencyGroup: string): string;
|
|
679
|
+
/**
|
|
680
|
+
* Phase D — evaluate static jobs' context data, queue deferred-init jobs
|
|
681
|
+
* for jobs with dynamic fields, and pick the first `runContextName` for
|
|
682
|
+
* the run.
|
|
683
|
+
*/
|
|
684
|
+
export declare function evaluateJobContexts(args: {
|
|
685
|
+
ctx: WorkflowDispatchContext;
|
|
686
|
+
setup: DispatchSetup;
|
|
687
|
+
buildPrep: BuildPrepResult;
|
|
688
|
+
}): Promise<JobEnvEvalResult>;
|
|
689
|
+
/**
|
|
690
|
+
* Refuse a Dockerfile build on an untrusted ref, unless the org opted in.
|
|
691
|
+
*
|
|
692
|
+
* A job may build its container image from a Dockerfile in the repository. That
|
|
693
|
+
* build runs arbitrary `RUN` commands on the agent host's daemon, OUTSIDE the
|
|
694
|
+
* hardened posture the job's own steps get — `docker build` cannot be
|
|
695
|
+
* capability-restricted the way a container run can. So an untrusted ref (a fork
|
|
696
|
+
* PR, an unresolved contributor, or an internally-triggered run without a
|
|
697
|
+
* trusted emitter — the same classification the user-cache write scope uses)
|
|
698
|
+
* reaches it only where the operator said so.
|
|
699
|
+
*
|
|
700
|
+
* "Without a trusted emitter" covers both halves of the internal case: a
|
|
701
|
+
* `kiciEvent()` subscriber that inherited a `known` / `unknown` tier, and one
|
|
702
|
+
* that inherited nothing at all (no emitting run, no persisted tier, a lookup
|
|
703
|
+
* that failed) — the strict fallback, which is not an "untrusted emitter".
|
|
704
|
+
*
|
|
705
|
+
* Enforced here, at dispatch, and nowhere else: the agent applies only what
|
|
706
|
+
* dispatch authorized, exactly as it does for the sandbox capability grant. Deny
|
|
707
|
+
* is loud and total — the build never starts.
|
|
708
|
+
*/
|
|
709
|
+
export declare function resolveWorkflowDockerfileBuilds(workflow: LockWorkflow, opts: {
|
|
710
|
+
scope: CacheRefScope;
|
|
711
|
+
allowUntrusted: boolean;
|
|
712
|
+
}): {
|
|
713
|
+
allowed: true;
|
|
714
|
+
} | {
|
|
715
|
+
denied: {
|
|
716
|
+
reason: string;
|
|
717
|
+
};
|
|
718
|
+
};
|
|
719
|
+
/**
|
|
720
|
+
* Post the pending `KiCI Security` status, and record on every hold that gates
|
|
721
|
+
* this commit's check that the commit now carries it.
|
|
722
|
+
*
|
|
723
|
+
* The record is what a settle reads to decide whether a hold has a check to
|
|
724
|
+
* terminalize. Deriving that from the row's shape instead answers what the code
|
|
725
|
+
* INTENDED, and `postCheckStatus` CREATES the named run when it finds none — so
|
|
726
|
+
* a post the provider refused left a shape saying "posted" and a settle that
|
|
727
|
+
* put a completed `KiCI Security` run on a commit which never had one.
|
|
728
|
+
*
|
|
729
|
+
* `heldRunIds` is plural because the commit carries ONE check run: a job held on
|
|
730
|
+
* two independent requirements has both of them gating it, and marking only the
|
|
731
|
+
* one whose summary was rendered would let the first to end resolve a check the
|
|
732
|
+
* other is still gating.
|
|
733
|
+
*
|
|
734
|
+
* Awaited, where the post used to be fire-and-forget: the record can only be
|
|
735
|
+
* written once the provider has accepted, and a record racing the settle is a
|
|
736
|
+
* record the settle may not see. A failed post is still swallowed — the dispatch
|
|
737
|
+
* loop is never blocked by a provider error — but the round-trip is now
|
|
738
|
+
* serial and inside the per-job loop, so N held jobs cost N of them.
|
|
739
|
+
*
|
|
740
|
+
* On success and then a failed record the commit keeps a pending check the
|
|
741
|
+
* settle will decline to close. That is the residue the fire-and-forget post
|
|
742
|
+
* could already leave, now narrowed to whatever can fail between the accepted
|
|
743
|
+
* post and ONE statement. The record is therefore retried
|
|
744
|
+
* {@link PENDING_CHECK_MARK_ATTEMPTS} times before it is given up on, which
|
|
745
|
+
* closes the half of that window the doc above names as reachable without a
|
|
746
|
+
* process dying — a lost connection, a statement timeout, a deadlock. What is
|
|
747
|
+
* left is a process death inside the retry window, and nothing in reach closes
|
|
748
|
+
* that: a sweeper would have to ask the provider whether the check exists, and
|
|
749
|
+
* `CheckStatusPoster` has no read method. Adding one means a new method on a
|
|
750
|
+
* compat-protected engine interface, every implementation and every hand-built
|
|
751
|
+
* bundle, to recover a window measured in milliseconds — against a recovery
|
|
752
|
+
* that already exists, since pushing a new commit re-posts. A sweeper without
|
|
753
|
+
* that read could only guess, and a wrong guess FABRICATES a check, which is
|
|
754
|
+
* the worse direction either way: a fabricated failing check on a pull request
|
|
755
|
+
* is worse than a stuck one.
|
|
756
|
+
*
|
|
757
|
+
* The record is one statement over every id, never one per id. A partial mark
|
|
758
|
+
* would leave an unmarked hold uncounted by the contention query, so the first
|
|
759
|
+
* hold to end would terminalize the shared check while the other still gates
|
|
760
|
+
* the job — a fabricated PASSING check, the worse direction, and reachable
|
|
761
|
+
* without any process dying. See `markPendingCheckPosted`.
|
|
762
|
+
*/
|
|
763
|
+
/**
|
|
764
|
+
* How many times {@link postPendingHoldCheck} tries to record an accepted post
|
|
765
|
+
* on the hold rows before giving up and leaving the check unclosable.
|
|
766
|
+
*/
|
|
767
|
+
export declare const PENDING_CHECK_MARK_ATTEMPTS = 3;
|
|
768
|
+
/**
|
|
769
|
+
* Open the needs gate for any job whose upstreams already reached terminal
|
|
770
|
+
* before this run's edges existed.
|
|
771
|
+
*
|
|
772
|
+
* **The race this closes.** Root jobs are dispatched to agents inside the
|
|
773
|
+
* dispatch loop, but `execution_job_needs` is only written afterwards, here. An
|
|
774
|
+
* agent that reports a root job terminal in that window drives
|
|
775
|
+
* `evaluateDownstreams`, which reads zero edges, returns an empty result, and
|
|
776
|
+
* the gate never fires again — the downstream stays `pending` forever and the
|
|
777
|
+
* run hangs with no error anywhere. Nothing re-evaluates on its own: the
|
|
778
|
+
* scheduler is purely event-driven off job completion, and that event has
|
|
779
|
+
* already been consumed.
|
|
780
|
+
*
|
|
781
|
+
* The window is small (single-digit milliseconds) but entirely reachable: a job
|
|
782
|
+
* that fails immediately on arrival — a rejected dispatch, an init failure, a
|
|
783
|
+
* capability mismatch — reports terminal in about the time one DB write takes.
|
|
784
|
+
* It was invisible for as long as the webhook route answered only after the
|
|
785
|
+
* whole pipeline had run, because the caller could not learn a job had been
|
|
786
|
+
* dispatched until every edge was already committed.
|
|
787
|
+
*
|
|
788
|
+
* Recomputing here is the same guard the deferred result-aware eval registration
|
|
789
|
+
* already applies for its own edges, and it is safe to run unconditionally: the
|
|
790
|
+
* claim inside `recomputeNeedsSatisfied` is a conditional UPDATE, so a job the
|
|
791
|
+
* normal completion path already claimed is skipped rather than dispatched
|
|
792
|
+
* twice.
|
|
793
|
+
*
|
|
794
|
+
* Wave-held jobs share the synthetic-id prefix but are gated by the rolling-wave
|
|
795
|
+
* scheduler, not by needs, so they are excluded — opening their gate here would
|
|
796
|
+
* bypass the `maxParallel` window.
|
|
797
|
+
*/
|
|
798
|
+
export declare function catchUpNeedsGatedJobs(args: {
|
|
799
|
+
ctx: NeedsSchedulingContext;
|
|
800
|
+
dispatchedJobs: readonly {
|
|
801
|
+
jobId: string;
|
|
802
|
+
jobName: string;
|
|
803
|
+
waveGated?: boolean;
|
|
804
|
+
}[];
|
|
805
|
+
}): Promise<void>;
|
|
806
|
+
/** Why an init result must not lead to a dispatch. */
|
|
807
|
+
export declare enum InitDispatchSuppression {
|
|
808
|
+
/** The workflow's own `filter` decided the workflow does not apply. */
|
|
809
|
+
Filter = "filter",
|
|
810
|
+
/** The job is already rejected by a context rule, or held for approval. */
|
|
811
|
+
Gated = "gated"
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Decide whether an arrived init result may dispatch its job.
|
|
815
|
+
*
|
|
816
|
+
* `Filter` requires the workflow to actually declare a filter as well as the
|
|
817
|
+
* agent to have reported `false`: a buggy or rogue agent must not be able to
|
|
818
|
+
* suppress a filter-less workflow by inventing the field, and an agent that
|
|
819
|
+
* predates the filter reports no verdict at all — reading that absence as
|
|
820
|
+
* "suppress" would silently stop every dispatch it handles.
|
|
821
|
+
*
|
|
822
|
+
* `Gated` covers a job that is already rejected or held. Its two halves now have
|
|
823
|
+
* very different reachability, and saying so is the point of this paragraph:
|
|
824
|
+
*
|
|
825
|
+
* - **rejected** is live and load-bearing. The flow-back consumes it (that is
|
|
826
|
+
* the `suppression === Gated && jobEnvData.rejected` branch) to stop a job its
|
|
827
|
+
* context rules rejected from dispatching.
|
|
828
|
+
* - **held** is NOT consumed here any more. A held job deliberately DOES get an
|
|
829
|
+
* init job — nothing else can resolve a dynamic value, so suppressing the
|
|
830
|
+
* round would make the job undispatchable rather than merely gated. The
|
|
831
|
+
* flow-back handles it after resolution instead, routing it to
|
|
832
|
+
* `holdExecutionAfterInit` so the hold and its resolved dispatch context are
|
|
833
|
+
* stored together.
|
|
834
|
+
*
|
|
835
|
+
* So do not read this as a guarantee that a held job cannot dispatch: that
|
|
836
|
+
* guarantee lives at the call site's `jobEnvData.held` branch, and — for the
|
|
837
|
+
* needs-scheduler and cluster paths, which reach a job by other routes — in
|
|
838
|
+
* `dispatchReadyJob`'s pending-hold check and the cluster path's own
|
|
839
|
+
* `holdJobForApproval` call.
|
|
840
|
+
*
|
|
841
|
+
* Exported for its own test: inline, the second branch could not be exercised at
|
|
842
|
+
* all, and an untestable security check is one nobody can prove still works.
|
|
843
|
+
*/
|
|
844
|
+
export declare function initDispatchSuppression(workflow: Pick<LockWorkflow, 'hasFilter'>, initResult: {
|
|
845
|
+
filterPassed?: boolean;
|
|
846
|
+
}, jobEnvData: Pick<JobEnvData, 'rejected' | 'held'>): InitDispatchSuppression | null;
|
|
348
847
|
export interface GeneratedJobConfig {
|
|
349
848
|
/**
|
|
350
849
|
* The generated lock job with its `name` and `needs` rewritten to expanded
|
|
@@ -377,6 +876,18 @@ export declare function partitionGeneratedConfigsByPin(configs: readonly Generat
|
|
|
377
876
|
pinnedConfigs: GeneratedJobConfig[];
|
|
378
877
|
unpinnedConfigs: GeneratedJobConfig[];
|
|
379
878
|
};
|
|
879
|
+
/**
|
|
880
|
+
* Recompute the needs gate for `jobNames` and act on whatever became ready:
|
|
881
|
+
* open the gate for a satisfied job, terminalize a job an upstream's status
|
|
882
|
+
* excluded.
|
|
883
|
+
*
|
|
884
|
+
* `recomputeNeedsSatisfied` claims each job with a conditional
|
|
885
|
+
* `needs_satisfied = false → true` UPDATE, so a job a concurrent
|
|
886
|
+
* `evaluateDownstreams` already claimed is not returned here and cannot be
|
|
887
|
+
* dispatched twice.
|
|
888
|
+
*/
|
|
889
|
+
export declare function recomputeAndApplyReady(ctx: NeedsSchedulingContext, jobNames: readonly string[]): Promise<void>;
|
|
890
|
+
export { gatherInvokeResults };
|
|
380
891
|
/**
|
|
381
892
|
* Dispatch a single matched workflow.
|
|
382
893
|
*
|
|
@@ -393,5 +904,4 @@ export declare function partitionGeneratedConfigsByPin(configs: readonly Generat
|
|
|
393
904
|
* J. deferred dynamic dispatch (fire-and-forget per dynamic entry)
|
|
394
905
|
*/
|
|
395
906
|
export declare function dispatchMatchedWorkflow(ctx: WorkflowDispatchContext, opts?: DispatchMatchedWorkflowOptions): Promise<DispatchMatchedWorkflowResult>;
|
|
396
|
-
export {};
|
|
397
907
|
//# sourceMappingURL=dispatch-matched-workflow.d.ts.map
|