@kici-dev/orchestrator 0.1.16 → 0.1.18
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/agent/dispatcher.d.ts +27 -1
- package/dist/agent/host-roster-reaper.d.ts +42 -0
- package/dist/agent/host-roster.d.ts +110 -0
- package/dist/agent/registry.d.ts +65 -2
- package/dist/app.d.ts +3 -0
- package/dist/approvals/apply-decision.d.ts +11 -1
- package/dist/cache/pending-inits.d.ts +5 -0
- package/dist/cli/api-client.d.ts +11 -0
- package/dist/cli/commands/host.d.ts +13 -0
- package/dist/cli/commands/local-hook.d.ts +15 -0
- package/dist/cli/commands/local-trigger.d.ts +24 -0
- package/dist/cli/commands/remote-source.d.ts +17 -0
- package/dist/cli/commands/shared/versioned-upgrade.d.ts +37 -0
- package/dist/cli/kici-admin.d.ts +13 -0
- package/dist/cli/service/compose.d.ts +2 -1
- package/dist/cli/service/index.d.ts +2 -2
- package/dist/cli/service/instance/manifest.d.ts +12 -0
- package/dist/cli/service/instance/resolve.d.ts +12 -2
- package/dist/cli/service/launchd.d.ts +4 -1
- package/dist/cli/service/platform-detect.d.ts +5 -0
- package/dist/cli/service/systemd.d.ts +2 -1
- package/dist/cli/service/types.d.ts +37 -0
- package/dist/cli/service/windows.d.ts +2 -1
- package/dist/cli.js +1340 -198
- package/dist/cluster/coordinator.d.ts +5 -1
- package/dist/config/schema.d.ts +4 -0
- package/dist/config/types.d.ts +9 -0
- package/dist/config.d.ts +6 -4
- package/dist/dashboard/handler.d.ts +26 -2
- package/dist/dashboard/needs-edges.d.ts +13 -0
- package/dist/db/migrations/035_pending_workflow_contexts.d.ts +11 -0
- package/dist/db/migrations/036_attestations.d.ts +15 -0
- package/dist/db/migrations/037_generic_sources_provider_type_local.d.ts +19 -0
- package/dist/db/migrations/038_remote_sources.d.ts +14 -0
- package/dist/db/migrations/039_host_roster.d.ts +19 -0
- package/dist/db/migrations/040_runsonall_pin.d.ts +4 -0
- package/dist/db/migrations/041_wave_gated.d.ts +4 -0
- package/dist/db/migrations/042_dispatch_queue_patterns.d.ts +4 -0
- package/dist/db/types.d.ts +144 -10
- package/dist/diagnostics/fleet-collector.d.ts +1 -1
- package/dist/entry-helpers.d.ts +7 -29
- package/dist/environments/held-runs.d.ts +33 -0
- package/dist/index.js +1 -0
- package/dist/lockfile-redos-guard.d.ts +19 -0
- package/dist/metrics/prometheus.d.ts +17 -2
- package/dist/metrics/scheduled-jobs.d.ts +2 -2
- package/dist/orchestrator-core.d.ts +94 -1
- package/dist/pipeline/dispatch-matched-workflow.d.ts +60 -2
- package/dist/pipeline/install-secrets-resolver.d.ts +32 -5
- package/dist/pipeline/needs-scheduler.d.ts +12 -10
- package/dist/pipeline/pending-workflow-context.d.ts +44 -0
- package/dist/pipeline/processor.d.ts +31 -4
- package/dist/pipeline/remote-source-store.d.ts +21 -0
- package/dist/pipeline/resume-workflow.d.ts +26 -0
- package/dist/pipeline/wave-scheduler.d.ts +60 -0
- package/dist/providers/local/index.d.ts +33 -0
- package/dist/providers/local/local-source-config.d.ts +17 -0
- package/dist/providers/{internal → local}/lock-file-fetcher.d.ts +6 -6
- package/dist/providers/{internal → local}/normalizer.d.ts +28 -28
- package/dist/providers/{internal → local}/repo-url-builder.d.ts +6 -6
- package/dist/queue/job-queue.d.ts +61 -2
- package/dist/reporting/execution-tracker.d.ts +61 -0
- package/dist/routes/admin-events.d.ts +3 -4
- package/dist/routes/uploads.d.ts +36 -26
- package/dist/server.js +53755 -50888
- package/dist/sources/build-platform-sources.d.ts +4 -2
- package/dist/stale-detector/stale-run-detector.d.ts +14 -1
- package/dist/standalone.js +17234 -15725
- package/dist/webhook/generic-sources-listener.d.ts +4 -0
- package/dist/webhook/generic-sources.d.ts +37 -11
- package/dist/webhook/register-source-bundle.d.ts +14 -4
- package/dist/ws/agent-handler.d.ts +17 -0
- package/dist/ws/dashboard-dispatch-guard.d.ts +21 -0
- package/dist/ws/dashboard-env-handler.d.ts +13 -1
- package/dist/ws/oidc-token-relay.d.ts +59 -0
- package/dist/ws/platform-client.d.ts +30 -1
- package/dist/ws/test-relay-handlers.d.ts +112 -0
- package/installer-image-digests.json +3 -3
- package/package.json +22 -22
- package/sbom.spdx.json +3228 -6633
- package/dist/providers/internal/index.d.ts +0 -32
- package/dist/routes/test-trigger.d.ts +0 -41
- package/dist/ws/observer-handler.d.ts +0 -42
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lock-load ReDoS revalidation.
|
|
3
|
+
*
|
|
4
|
+
* Re-validates every regex matcher in a fetched lock file before it is cached
|
|
5
|
+
* or dispatched. Defense-in-depth against a hand-edited or non-compiled lock
|
|
6
|
+
* that smuggled a ReDoS-prone pattern past the compile-time gate: the compiler
|
|
7
|
+
* runs the same `assertMatchersSafe` check when it emits the lock, but the
|
|
8
|
+
* orchestrator does not trust that the lock it fetched was produced by our
|
|
9
|
+
* compiler.
|
|
10
|
+
*/
|
|
11
|
+
import type { LockFile } from '@kici-dev/engine';
|
|
12
|
+
/**
|
|
13
|
+
* Walk every static job's `runsOn` / `excludeLabels` / `runsOnAll` matchers and
|
|
14
|
+
* throw if any regex matcher is ReDoS-prone. Dynamic job generators carry no
|
|
15
|
+
* static routing matchers (they materialize jobs at eval time, which re-runs the
|
|
16
|
+
* compile-time gate), so only static jobs are checked.
|
|
17
|
+
*/
|
|
18
|
+
export declare function assertLockFileRegexesSafe(lockFile: LockFile): void;
|
|
19
|
+
//# sourceMappingURL=lockfile-redos-guard.d.ts.map
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export declare function setAgentsActive(value: number): void;
|
|
3
3
|
/** Set the current config version number. */
|
|
4
4
|
export declare function setConfigVersion(value: number): void;
|
|
5
|
+
/** Set the current number of declared (static) roster hosts that are unreachable. */
|
|
6
|
+
export declare function setDeclaredHostsUnreachable(value: number): void;
|
|
5
7
|
/** Set the current number of stale runs detected. */
|
|
6
8
|
export declare function setStaleRunsCurrent(value: number): void;
|
|
7
9
|
interface ScalerUsageRow {
|
|
@@ -264,6 +266,8 @@ export declare const InstallSecretsDecisionReason: {
|
|
|
264
266
|
readonly ProtectionRuleBlock: "protection_rule_block";
|
|
265
267
|
readonly MissingToken: "missing_token";
|
|
266
268
|
readonly MissingInstallEnv: "missing_install_env";
|
|
269
|
+
/** A protection gate held the workflow install (hold / wait / queue). */
|
|
270
|
+
readonly Held: "held";
|
|
267
271
|
};
|
|
268
272
|
export type InstallSecretsDecisionReason = (typeof InstallSecretsDecisionReason)[keyof typeof InstallSecretsDecisionReason];
|
|
269
273
|
/**
|
|
@@ -280,8 +284,8 @@ export type InstallSecretsChannel = (typeof InstallSecretsChannel)[keyof typeof
|
|
|
280
284
|
* Total install-secrets resolution decisions.
|
|
281
285
|
*
|
|
282
286
|
* Labels:
|
|
283
|
-
* - decision: pass | reject
|
|
284
|
-
* - reason: ok (only with `pass`), or one of the reject reasons enumerated in `InstallSecretsDecisionReason`
|
|
287
|
+
* - decision: pass | reject | hold
|
|
288
|
+
* - reason: ok (only with `pass`), held (only with `hold`), or one of the reject reasons enumerated in `InstallSecretsDecisionReason`
|
|
285
289
|
*
|
|
286
290
|
* Pass series are emitted exactly once per resolver call that does any work
|
|
287
291
|
* (workflows declaring neither `registries:` nor `installEnv:` are NOT
|
|
@@ -320,5 +324,16 @@ export declare const installSecretsContributorStrippedTotal: import("@openteleme
|
|
|
320
324
|
* - environment: the environment name referenced in the qualified `<environment>:<secret>` ref (per-org count is typically <10)
|
|
321
325
|
*/
|
|
322
326
|
export declare const installSecretsTokenResolutionDurationSeconds: import("@opentelemetry/api").Histogram<import("@opentelemetry/api").Attributes>;
|
|
327
|
+
/**
|
|
328
|
+
* Register every queued orchestrator observable gauge on the real meter.
|
|
329
|
+
*
|
|
330
|
+
* Must run AFTER `initTelemetry()` has wired the global MeterProvider —
|
|
331
|
+
* `createApp()` calls it once during bootstrap. Registering the gauges at
|
|
332
|
+
* module-eval time instead would bind them to the no-op provider (the
|
|
333
|
+
* bundler hoists some module init above the entry's `initTelemetry()` call),
|
|
334
|
+
* leaving every `kici_orch_*` gauge absent from the /metrics scrape and the
|
|
335
|
+
* Platform push. Idempotent: repeat calls are no-ops.
|
|
336
|
+
*/
|
|
337
|
+
export declare function registerOrchestratorMetrics(): void;
|
|
323
338
|
export {};
|
|
324
339
|
//# sourceMappingURL=prometheus.d.ts.map
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* - job: scheduled-job name (one of `OrchestratorScheduledJobName`)
|
|
5
5
|
* - result: success | failure
|
|
6
6
|
*/
|
|
7
|
-
export declare const jobRunsTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
7
|
+
export declare const jobRunsTotal: Pick<import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>, "add">;
|
|
8
8
|
/**
|
|
9
9
|
* Histogram of per-tick duration, seconds.
|
|
10
10
|
* Labels:
|
|
11
11
|
* - job: scheduled-job name (one of `OrchestratorScheduledJobName`)
|
|
12
12
|
*/
|
|
13
|
-
export declare const jobDurationSeconds: import("@opentelemetry/api").Histogram<import("@opentelemetry/api").Attributes>;
|
|
13
|
+
export declare const jobDurationSeconds: Pick<import("@opentelemetry/api").Histogram<import("@opentelemetry/api").Attributes>, "record">;
|
|
14
14
|
/**
|
|
15
15
|
* Unix timestamp (seconds) of the most recent successful tick.
|
|
16
16
|
* Labels:
|
|
@@ -13,6 +13,7 @@ import { type ColdStore } from '@kici-dev/shared';
|
|
|
13
13
|
import type { AppConfig } from './config.js';
|
|
14
14
|
import { ConfigReloader } from './config/reload.js';
|
|
15
15
|
import { AgentRegistry } from './agent/registry.js';
|
|
16
|
+
import { HostRosterStore } from './agent/host-roster.js';
|
|
16
17
|
import { JobQueue } from './queue/job-queue.js';
|
|
17
18
|
import { EventLogWriter } from './webhook/event-log.js';
|
|
18
19
|
import { AccessLogWriter } from './audit/access-log.js';
|
|
@@ -22,7 +23,7 @@ import { DedupCache } from './webhook/dedup.js';
|
|
|
22
23
|
import { ObserverRegistry } from './ws/observer-registry.js';
|
|
23
24
|
import { AgentMetricsAggregator } from './metrics/agent-metrics-aggregator.js';
|
|
24
25
|
import { SourceLocationStore } from './app.js';
|
|
25
|
-
import { type PeerHeartbeat, type PeerLogsCollectRequest, type PeerToPeerMessage } from '@kici-dev/engine';
|
|
26
|
+
import { type LabelMatcher, type PeerHeartbeat, type PeerLogsCollectRequest, type PeerToPeerMessage } from '@kici-dev/engine';
|
|
26
27
|
import { ScalerManager } from './scaler/index.js';
|
|
27
28
|
import type { ScalerConfig } from './scaler/index.js';
|
|
28
29
|
import type { CacheStorage } from './storage/index.js';
|
|
@@ -63,6 +64,7 @@ export interface OrchestratorSubsystems {
|
|
|
63
64
|
pool: pg.Pool;
|
|
64
65
|
providerRegistry: ProviderRegistry;
|
|
65
66
|
agentRegistry: AgentRegistry;
|
|
67
|
+
hostRosterStore: HostRosterStore;
|
|
66
68
|
dispatcher: Dispatcher;
|
|
67
69
|
queue: JobQueue;
|
|
68
70
|
scalerManager: ScalerManager | null;
|
|
@@ -210,6 +212,97 @@ export interface OrchestratorHooks {
|
|
|
210
212
|
*/
|
|
211
213
|
startupLogMessage: (port: number) => string;
|
|
212
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Normalize a lock job's `needs` array (strings, NeedsEntry objects, or
|
|
217
|
+
* NeedsGroupEntry objects) to the set of upstream BASE job names. Group entries
|
|
218
|
+
* are skipped here — group fan-in is resolved separately by the scheduler.
|
|
219
|
+
*/
|
|
220
|
+
export declare function upstreamBaseNamesFromNeeds(needs: unknown): string[];
|
|
221
|
+
/**
|
|
222
|
+
* Partition a lock job's `runsOn` / `excludeLabels` matchers into exact label
|
|
223
|
+
* strings and regex patterns for internal-event (cron / `ctx.emit`) dispatch.
|
|
224
|
+
* Lock jobs carry `runsOn` as `LabelMatcher[]`; the coordinator routing and the
|
|
225
|
+
* direct dispatcher both need exact labels for the indexed/SQL fast path and
|
|
226
|
+
* regex patterns as a separate JS post-filter — never the raw matcher objects.
|
|
227
|
+
*/
|
|
228
|
+
export declare function internalJobRunsOnSelectors(job: {
|
|
229
|
+
runsOn?: readonly LabelMatcher[];
|
|
230
|
+
excludeLabels?: readonly LabelMatcher[];
|
|
231
|
+
}): {
|
|
232
|
+
runsOnLabels: string[];
|
|
233
|
+
runsOnPatterns: LabelMatcher[];
|
|
234
|
+
excludeLabels: string[];
|
|
235
|
+
excludePatterns: LabelMatcher[];
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Parse an `execution_jobs.outputs` cell (string JSON or object) to a plain
|
|
239
|
+
* object, or null when empty / unparseable.
|
|
240
|
+
*/
|
|
241
|
+
export declare function parseOutputsCell(outputs: unknown): Record<string, unknown> | null;
|
|
242
|
+
/**
|
|
243
|
+
* Build the downstream `upstreamJobOutputs` map keyed by BASE name. A base name
|
|
244
|
+
* that fanned into matrix children (rows with `matrix_values`) gets the
|
|
245
|
+
* `{ byMatrix, merged }` envelope; a single non-fanned row keeps the flat shape.
|
|
246
|
+
* Returns undefined when no upstream produced outputs.
|
|
247
|
+
*/
|
|
248
|
+
export declare function buildUpstreamOutputsByBase(baseNames: string[], rows: Array<{
|
|
249
|
+
job_name: string;
|
|
250
|
+
outputs: unknown;
|
|
251
|
+
matrix_values: unknown;
|
|
252
|
+
variant_kind?: string | null;
|
|
253
|
+
variant_label?: string | null;
|
|
254
|
+
status?: string | null;
|
|
255
|
+
}>): Record<string, Record<string, unknown>> | undefined;
|
|
256
|
+
/**
|
|
257
|
+
* Fold a `runsOnAll` upstream's host children into the `byHost` envelope
|
|
258
|
+
* `{ byHost: { '<host>': outputs }, summary: { succeededHosts, failedHosts, outputs } }`.
|
|
259
|
+
* Unlike the matrix envelope, `summary.outputs[key]` is an array view across hosts
|
|
260
|
+
* (host order), never a last-write-wins scalar; `succeededHosts`/`failedHosts`
|
|
261
|
+
* record each host's terminal outcome.
|
|
262
|
+
*/
|
|
263
|
+
export declare function buildHostOutputsEnvelope(children: Array<{
|
|
264
|
+
host: string;
|
|
265
|
+
status: string | null;
|
|
266
|
+
parsed: Record<string, unknown>;
|
|
267
|
+
}>): {
|
|
268
|
+
byHost: Record<string, Record<string, unknown>>;
|
|
269
|
+
summary: {
|
|
270
|
+
succeededHosts: string[];
|
|
271
|
+
failedHosts: string[];
|
|
272
|
+
outputs: Record<string, unknown[]>;
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Group an upstream's child rows into the matrix outputs envelope
|
|
277
|
+
* `{ byMatrix: { '<suffix>': outputs }, merged: <last-write-wins> }`. The suffix
|
|
278
|
+
* is the text inside the `(...)` of each expanded child name; children are
|
|
279
|
+
* merged in name order (deterministic, matching dispatch order).
|
|
280
|
+
*/
|
|
281
|
+
export declare function buildMatrixOutputsEnvelope(baseName: string, children: Array<{
|
|
282
|
+
job_name: string;
|
|
283
|
+
parsed: Record<string, unknown>;
|
|
284
|
+
}>): {
|
|
285
|
+
byMatrix: Record<string, Record<string, unknown>>;
|
|
286
|
+
merged: Record<string, unknown>;
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Merge plain `outputs` and decrypted `secret outputs` from upstream jobs
|
|
290
|
+
* into the dispatch envelope's `secrets` + `upstreamJobOutputs` fields.
|
|
291
|
+
*
|
|
292
|
+
* For a fanned (matrix) upstream, the downstream sees a keyed envelope
|
|
293
|
+
* `{ byMatrix: { '<suffix>': outputs }, merged: <last-write-wins> }` under the
|
|
294
|
+
* BASE name; a non-fanned upstream keeps the flat outputs shape.
|
|
295
|
+
*
|
|
296
|
+
* The needs-aware scheduler guarantees upstreams are
|
|
297
|
+
* terminal before the downstream is dispatched, so the lookups below
|
|
298
|
+
* always find final values. Errors are swallowed (warn-logged) so a flaky
|
|
299
|
+
* upstream-outputs read doesn't block dispatch — the agent will still get
|
|
300
|
+
* its declared secrets, just without the merged upstream additions.
|
|
301
|
+
*/
|
|
302
|
+
export declare function mergeUpstreamOutputs(db: Kysely<Database>, runId: string, jobName: string, needs: unknown, dispatchSecrets: Record<string, string> | undefined, secretKey: string): Promise<{
|
|
303
|
+
mergedSecrets: Record<string, string> | undefined;
|
|
304
|
+
upstreamJobOutputs: Record<string, Record<string, unknown>> | undefined;
|
|
305
|
+
}>;
|
|
213
306
|
export declare function bootstrapOrchestrator(config: AppConfig, hooks: OrchestratorHooks, options?: {
|
|
214
307
|
otelSdk?: {
|
|
215
308
|
shutdown(): Promise<void>;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* results through the pipeline.
|
|
15
15
|
*/
|
|
16
16
|
import { CacheRefScope } from '@kici-dev/engine';
|
|
17
|
-
import type { LockWorkflow, SimulatedEvent, WorkflowDecision } from '@kici-dev/engine';
|
|
17
|
+
import type { LabelMatcher, LockWorkflow, SimulatedEvent, WorkflowDecision, MaterializedJob } from '@kici-dev/engine';
|
|
18
18
|
import type { WebhookInfo } from '../webhook/handler.js';
|
|
19
19
|
import type { ProviderBundle } from '../provider-registry.js';
|
|
20
20
|
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
@@ -91,7 +91,64 @@ export interface WorkflowDispatchContext {
|
|
|
91
91
|
export interface DispatchMatchedWorkflowResult {
|
|
92
92
|
/** Number of jobs successfully dispatched (non-rejected). */
|
|
93
93
|
dispatchedJobCount: number;
|
|
94
|
+
/** True when the workflow install gate paused the dispatch (held run). */
|
|
95
|
+
held?: boolean;
|
|
94
96
|
}
|
|
97
|
+
/** Options controlling a (re-)dispatch of a matched workflow. */
|
|
98
|
+
export interface DispatchMatchedWorkflowOptions {
|
|
99
|
+
/**
|
|
100
|
+
* Resume path: skip the workflow install protection gate (already satisfied)
|
|
101
|
+
* so secrets resolve directly and the dispatch flows into job dispatch.
|
|
102
|
+
*/
|
|
103
|
+
skipInstallProtectionGate?: boolean;
|
|
104
|
+
/** The released held-run id being resumed (for logging / correlation). */
|
|
105
|
+
reuseHeldRunId?: string;
|
|
106
|
+
/**
|
|
107
|
+
* The run id whose `held` execution_runs row should be reused (flipped to
|
|
108
|
+
* pending) instead of inserting a fresh row.
|
|
109
|
+
*/
|
|
110
|
+
reuseRunId?: string;
|
|
111
|
+
}
|
|
112
|
+
/** Exact labels + regex patterns partitioned from a lock job's selectors. */
|
|
113
|
+
interface JobRoutingSelectors {
|
|
114
|
+
runsOnLabels: string[];
|
|
115
|
+
runsOnPatterns: LabelMatcher[];
|
|
116
|
+
excludeLabels: string[];
|
|
117
|
+
excludePatterns: LabelMatcher[];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Partition a lock job's runsOn / excludeLabels matchers into exact labels (SQL
|
|
121
|
+
* `@>` prefilter + registry index) and regex patterns (JS post-filter). A
|
|
122
|
+
* `runsOnAll` host-fanout job has no `runsOn`; its pinned children carry no
|
|
123
|
+
* routing (the pin targets the resolved agent directly).
|
|
124
|
+
*/
|
|
125
|
+
export declare function runsOnSelectorsForLockJob(lockJob: {
|
|
126
|
+
runsOn?: readonly LabelMatcher[];
|
|
127
|
+
excludeLabels?: readonly LabelMatcher[];
|
|
128
|
+
}): JobRoutingSelectors;
|
|
129
|
+
/** Per-child rolling-wave plan: which children are held + the base's wave policy. */
|
|
130
|
+
export interface WavePlan {
|
|
131
|
+
/** `expandedName`s held behind the wave gate (beyond the maxParallel window). */
|
|
132
|
+
held: Set<string>;
|
|
133
|
+
/** `expandedName` → the base's `{maxParallel, failFast}`, stamped on every child of a bounded wave. */
|
|
134
|
+
policy: Map<string, {
|
|
135
|
+
maxParallel: number;
|
|
136
|
+
failFast: boolean;
|
|
137
|
+
}>;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Compute the rolling-wave plan for a materialized job set.
|
|
141
|
+
*
|
|
142
|
+
* For each base job declaring `maxParallel` whose fan-out produced more than one
|
|
143
|
+
* child, children are ordered deterministically by `variant_label` (the matrix
|
|
144
|
+
* suffix / hostname, via `expandedName`) and every child at index `>=
|
|
145
|
+
* maxParallel` is held (`wave_gated=true`). The first `maxParallel` dispatch
|
|
146
|
+
* immediately; held children release one-per-terminal via the wave-scheduler.
|
|
147
|
+
* Every child of a bounded-wave base — held or not — gets a `policy` entry so
|
|
148
|
+
* the wave-scheduler can read the width/failFast at terminal time. A non-fan-out
|
|
149
|
+
* job (single child) or one without `maxParallel` contributes nothing.
|
|
150
|
+
*/
|
|
151
|
+
export declare function computeWavePlan(materializedJobs: readonly MaterializedJob[]): WavePlan;
|
|
95
152
|
/**
|
|
96
153
|
* Dispatch a single matched workflow.
|
|
97
154
|
*
|
|
@@ -107,5 +164,6 @@ export interface DispatchMatchedWorkflowResult {
|
|
|
107
164
|
* I. deferred init dispatch (fire-and-forget per job)
|
|
108
165
|
* J. deferred dynamic dispatch (fire-and-forget per dynamic entry)
|
|
109
166
|
*/
|
|
110
|
-
export declare function dispatchMatchedWorkflow(ctx: WorkflowDispatchContext): Promise<DispatchMatchedWorkflowResult>;
|
|
167
|
+
export declare function dispatchMatchedWorkflow(ctx: WorkflowDispatchContext, opts?: DispatchMatchedWorkflowOptions): Promise<DispatchMatchedWorkflowResult>;
|
|
168
|
+
export {};
|
|
111
169
|
//# sourceMappingURL=dispatch-matched-workflow.d.ts.map
|
|
@@ -8,10 +8,13 @@
|
|
|
8
8
|
*
|
|
9
9
|
* 1. Parses every qualified ref and groups by environment name.
|
|
10
10
|
* 2. Fires the per-environment protection-rule pipeline once per unique env
|
|
11
|
-
* (branch / trust / concurrency / reviewer / wait-timer).
|
|
12
|
-
* result
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* (branch / trust / concurrency / reviewer / wait-timer). A `reject`
|
|
12
|
+
* result fails the whole workflow dispatch with a clear reason; a
|
|
13
|
+
* `hold` / `wait` / `queue` result returns a structured `hold` decision
|
|
14
|
+
* so the caller can pause the workflow dispatch as a workflow-scoped held
|
|
15
|
+
* run and resume it when the gate clears. On the resume path the caller
|
|
16
|
+
* sets `skipProtectionGate` so the gate (already satisfied) is bypassed
|
|
17
|
+
* and secrets are resolved directly.
|
|
15
18
|
* 3. Resolves secrets per environment via `secretResolver.resolveForJob`
|
|
16
19
|
* (which writes its own audit log lines).
|
|
17
20
|
* 4. Validates each registry URL scheme: HTTPS always allowed; `http://`
|
|
@@ -26,7 +29,7 @@
|
|
|
26
29
|
* Pure helper: no imports of the dispatch giant. Tested in isolation in
|
|
27
30
|
* `install-secrets-resolver.test.ts`.
|
|
28
31
|
*/
|
|
29
|
-
import type { LockRegistry } from '@kici-dev/engine';
|
|
32
|
+
import type { ApproverClause, LockRegistry } from '@kici-dev/engine';
|
|
30
33
|
import type { TrustResolution } from '../security/trust-resolver.js';
|
|
31
34
|
import type { SecretResolver } from '../secrets/secret-resolver.js';
|
|
32
35
|
import type { EnvironmentStore } from '../environments/environment-store.js';
|
|
@@ -47,6 +50,18 @@ export interface ResolveInstallSecretsArgs {
|
|
|
47
50
|
environmentStore: EnvironmentStore | undefined;
|
|
48
51
|
secretResolver: SecretResolver | undefined;
|
|
49
52
|
protectionContext: JobDispatchContext;
|
|
53
|
+
/**
|
|
54
|
+
* Resume path: skip the protection-rule gate (already satisfied) and resolve
|
|
55
|
+
* secrets directly. The untrusted-contributor strip still runs first.
|
|
56
|
+
*/
|
|
57
|
+
skipProtectionGate?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/** Normalized requirement carried on a `hold` decision. */
|
|
60
|
+
export interface InstallHoldRequirement {
|
|
61
|
+
clauses: ApproverClause[];
|
|
62
|
+
/** ISO timestamp after which the hold expires. */
|
|
63
|
+
expiresAt: string;
|
|
64
|
+
reason: string;
|
|
50
65
|
}
|
|
51
66
|
export type ResolveInstallSecretsResult = {
|
|
52
67
|
decision: 'pass';
|
|
@@ -56,6 +71,18 @@ export type ResolveInstallSecretsResult = {
|
|
|
56
71
|
} | {
|
|
57
72
|
decision: 'reject';
|
|
58
73
|
reason: string;
|
|
74
|
+
} | {
|
|
75
|
+
decision: 'hold';
|
|
76
|
+
/** The gate action that paused the dispatch. */
|
|
77
|
+
action: 'hold' | 'wait' | 'queue';
|
|
78
|
+
/** The environment whose install gate held. */
|
|
79
|
+
envName: string;
|
|
80
|
+
/** Resolved environment id (for the held row). */
|
|
81
|
+
environmentId: string;
|
|
82
|
+
/** Discriminates the release trigger: 'reviewer' | 'wait_timer' | 'concurrency' | 'security'. */
|
|
83
|
+
holdType: string;
|
|
84
|
+
queueType: 'environment' | 'security';
|
|
85
|
+
requirement: InstallHoldRequirement;
|
|
59
86
|
};
|
|
60
87
|
/** Parse `<environment>:<secret-name>`. Returns null on malformed input. */
|
|
61
88
|
export declare function parseQualifiedSecretRef(ref: string): {
|
|
@@ -19,25 +19,28 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import type { Kysely } from 'kysely';
|
|
21
21
|
import type { Database } from '../db/types.js';
|
|
22
|
-
import type {
|
|
22
|
+
import type { MaterializedJob } from '@kici-dev/engine';
|
|
23
23
|
/** Result of evaluating downstream jobs after an upstream completes. */
|
|
24
24
|
export interface SchedulerResult {
|
|
25
25
|
jobName: string;
|
|
26
26
|
action: 'dispatch' | 'skip';
|
|
27
27
|
reason?: string;
|
|
28
28
|
}
|
|
29
|
-
/** Minimal lock job shape needed by insertEdgesForRun. */
|
|
30
|
-
interface LockJobLike {
|
|
31
|
-
readonly name: string;
|
|
32
|
-
readonly needs: readonly (string | NeedsEntry | NeedsGroupEntry)[];
|
|
33
|
-
readonly dependsOnGroups?: readonly string[];
|
|
34
|
-
}
|
|
35
29
|
/**
|
|
36
30
|
* Insert dependency edges at run start for static-to-static needs.
|
|
31
|
+
*
|
|
32
|
+
* Operates on materialized jobs (matrix-expanded children) and an expansion map
|
|
33
|
+
* (base job name -> the expanded child names). An upstream reference to a base
|
|
34
|
+
* name `test` that fanned into `test (a)` / `test (b)` produces one edge per
|
|
35
|
+
* child, so a downstream `needs: ['test']` waits for ALL children to terminate.
|
|
36
|
+
* Each materialized child likewise inherits the full upstream edge set under its
|
|
37
|
+
* own expanded name.
|
|
38
|
+
*
|
|
37
39
|
* NeedsGroupEntry items are skipped (resolved later via resolveGroupEdges).
|
|
38
|
-
* Root jobs (no needs, no dependsOnGroups) are marked needs_satisfied=true
|
|
40
|
+
* Root jobs (no needs, no dependsOnGroups) are marked needs_satisfied=true,
|
|
41
|
+
* keyed by expanded name.
|
|
39
42
|
*/
|
|
40
|
-
export declare function insertEdgesForRun(db: Kysely<Database>, runId: string,
|
|
43
|
+
export declare function insertEdgesForRun(db: Kysely<Database>, runId: string, jobs: readonly MaterializedJob[], expansionMap: ReadonlyMap<string, readonly string[]>): Promise<void>;
|
|
41
44
|
/**
|
|
42
45
|
* Resolve dynamic group edges after eval completion.
|
|
43
46
|
*
|
|
@@ -81,5 +84,4 @@ export declare function checkSchedulerInvariant(db: Kysely<Database>, runId: str
|
|
|
81
84
|
* due to failure propagation. Only follows edges where if_failed='skip'.
|
|
82
85
|
*/
|
|
83
86
|
export declare function getFailurePropagationTargets(db: Kysely<Database>, runId: string, failedJobName: string): Promise<string[]>;
|
|
84
|
-
export {};
|
|
85
87
|
//# sourceMappingURL=needs-scheduler.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pending workflow dispatch context store — the workflow-level analogue of the
|
|
3
|
+
* `pending_job_contexts` store. Persists the serializable inputs of a
|
|
4
|
+
* `WorkflowDispatchContext` so a workflow whose install gate held can be
|
|
5
|
+
* resumed (reviewer approve, wait-timer expiry, concurrency slot free) by
|
|
6
|
+
* rebuilding the dispatch context and re-running `dispatchMatchedWorkflow`.
|
|
7
|
+
*
|
|
8
|
+
* Only the serializable inputs are stored — the live `deps` (ProcessingDeps)
|
|
9
|
+
* and `bundle` (ProviderBundle) are NOT persisted; they are rebuilt from the
|
|
10
|
+
* live orchestrator on resume.
|
|
11
|
+
*
|
|
12
|
+
* Writes to both an in-memory Map (fast read on the same process) and the DB
|
|
13
|
+
* (crash recovery + cross-orchestrator read), mirroring the pending-job store.
|
|
14
|
+
*/
|
|
15
|
+
import type { Kysely } from 'kysely';
|
|
16
|
+
import type { Database } from '../db/types.js';
|
|
17
|
+
import type { WorkflowDispatchContext } from './dispatch-matched-workflow.js';
|
|
18
|
+
/**
|
|
19
|
+
* The serializable subset of a `WorkflowDispatchContext` — everything except
|
|
20
|
+
* the live `deps` and `bundle`, which are rebuilt on resume. Every field here
|
|
21
|
+
* is JSON-safe (the event, payload, and lock file already ride the WS protocol
|
|
22
|
+
* as JSON).
|
|
23
|
+
*/
|
|
24
|
+
export type SerializableWorkflowDispatchInputs = Omit<WorkflowDispatchContext, 'deps' | 'bundle'>;
|
|
25
|
+
/** Extract the serializable inputs from a live dispatch context. */
|
|
26
|
+
export declare function toSerializableInputs(ctx: WorkflowDispatchContext): SerializableWorkflowDispatchInputs;
|
|
27
|
+
/** Persist the pending workflow context to the in-memory Map and the DB. */
|
|
28
|
+
export declare function storePendingWorkflowContext(db: Kysely<Database> | undefined, inputs: SerializableWorkflowDispatchInputs): Promise<void>;
|
|
29
|
+
/** Load the pending workflow context by run id (memory first, then DB). */
|
|
30
|
+
export declare function loadPendingWorkflowContext(db: Kysely<Database> | undefined, runId: string): Promise<SerializableWorkflowDispatchInputs | null>;
|
|
31
|
+
/** Delete the pending workflow context from the in-memory Map and the DB. */
|
|
32
|
+
export declare function deletePendingWorkflowContext(db: Kysely<Database> | undefined, runId: string): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Restore the in-memory Map from the DB on startup, skipping rows whose run has
|
|
35
|
+
* already reached a terminal state. Mirrors `restorePendingJobContexts`.
|
|
36
|
+
* Returns the number of restored contexts.
|
|
37
|
+
*/
|
|
38
|
+
export declare function restorePendingWorkflowContexts(db: Kysely<Database>): Promise<number>;
|
|
39
|
+
/**
|
|
40
|
+
* Clear all entries from the in-memory pending workflow contexts Map.
|
|
41
|
+
* @internal Exported for testing only.
|
|
42
|
+
*/
|
|
43
|
+
export declare function clearPendingWorkflowContextsMap(): void;
|
|
44
|
+
//# sourceMappingURL=pending-workflow-context.d.ts.map
|
|
@@ -29,6 +29,7 @@ import type { PendingDynamicTracker } from '../cache/pending-dynamics.js';
|
|
|
29
29
|
import type { CheckRunReporter } from '../reporting/check-run-reporter.js';
|
|
30
30
|
import type { ExecutionTracker } from '../reporting/execution-tracker.js';
|
|
31
31
|
import type { AgentRegistry } from '../agent/registry.js';
|
|
32
|
+
import type { HostRosterStore } from '../agent/host-roster.js';
|
|
32
33
|
import type { RunCoordinator } from '../cluster/coordinator.js';
|
|
33
34
|
import type { TeamMembershipLookup } from '../approvals/team-membership-lookup.js';
|
|
34
35
|
import type { LogStorage } from '../reporting/log-storage.js';
|
|
@@ -62,6 +63,21 @@ interface PendingJobContext {
|
|
|
62
63
|
jobInput: QueuedJobInput;
|
|
63
64
|
runsOnLabels: string[];
|
|
64
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Register an eval gate and return a promise that resolves when the scheduler
|
|
68
|
+
* opens it (the eval job's upstream needs are all satisfied).
|
|
69
|
+
*/
|
|
70
|
+
export declare function trackEvalGate(runId: string, evalJobName: string): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Open a registered eval gate, unblocking the deferred dispatch task. Returns
|
|
73
|
+
* true if a gate was registered for this eval job (so the scheduler knows it
|
|
74
|
+
* handled the ready signal itself and must not run the normal dispatch path).
|
|
75
|
+
*/
|
|
76
|
+
export declare function openEvalGate(runId: string, evalJobName: string): boolean;
|
|
77
|
+
/** True when a job name is a result-aware dynamic eval job awaiting its gate. */
|
|
78
|
+
export declare function isEvalGatePending(runId: string, evalJobName: string): boolean;
|
|
79
|
+
/** Clear all eval gates for a run (called on run completion / cleanup). */
|
|
80
|
+
export declare function clearEvalGatesForRun(runId: string): void;
|
|
65
81
|
/**
|
|
66
82
|
* Store a pending dispatch context for a job that will be dispatched later
|
|
67
83
|
* by the needs scheduler. The key is `${runId}:${jobName}`.
|
|
@@ -112,7 +128,7 @@ export declare function isRootJob(lockJob: LockJob): boolean;
|
|
|
112
128
|
* Why this exists
|
|
113
129
|
* ----------------
|
|
114
130
|
* The webhook pipeline binds `lockFileFetcher` to the inbound webhook's
|
|
115
|
-
* provider bundle. When
|
|
131
|
+
* provider bundle. When a local-sourced webhook (e.g., the staging
|
|
116
132
|
* stg-ha-smoke failover-dispatch test) arrives for a repo whose lock file
|
|
117
133
|
* is only accessible via a different provider (e.g., github), the inbound
|
|
118
134
|
* fetcher returns null and trigger matching silently drops the webhook.
|
|
@@ -143,7 +159,7 @@ export declare function isRootJob(lockJob: LockJob): boolean;
|
|
|
143
159
|
* -----------
|
|
144
160
|
* Each fallback fetcher is invoked with the REGISTRATION'S
|
|
145
161
|
* `providerContext`, NOT the inbound normalizer's credentials. This is
|
|
146
|
-
* load-bearing: the
|
|
162
|
+
* load-bearing: the LocalWebhookNormalizer returns `{}` as
|
|
147
163
|
* credentials, which would never satisfy a github fetcher that requires
|
|
148
164
|
* `installationId`. The registration carries the correct credentials
|
|
149
165
|
* because it was created via the owning provider's source.
|
|
@@ -178,8 +194,10 @@ export declare function resolveLockFileWithFallback(args: {
|
|
|
178
194
|
* Resolve the customer/org ID for a routing key.
|
|
179
195
|
*
|
|
180
196
|
* Checks the `sources` table first (GitHub App sources), then
|
|
181
|
-
* `generic_webhook_sources` (generic webhook sources)
|
|
182
|
-
*
|
|
197
|
+
* `generic_webhook_sources` (generic webhook sources), then `remote_sources`
|
|
198
|
+
* (the auto-provisioned anchor for Platform-relayed `kici run remote`, routing
|
|
199
|
+
* key `remote:<orgId>`). Falls back to '__default__' if none of the three
|
|
200
|
+
* tables has the routing key.
|
|
183
201
|
*/
|
|
184
202
|
export declare function resolveOrgId(db: Kysely<Database>, routingKey: string): Promise<string>;
|
|
185
203
|
/**
|
|
@@ -318,6 +336,15 @@ export interface ProcessingDeps {
|
|
|
318
336
|
/** Access-log writer for the orchestrator audit stream. Optional -- if not
|
|
319
337
|
* set, hold-creation audit rows (`held_run.request`) are skipped. */
|
|
320
338
|
accessLogWriter?: AccessLogWriter;
|
|
339
|
+
/** Host roster store for runsOnAll fan-out resolution. Optional -- if not set,
|
|
340
|
+
* runsOnAll jobs cannot be resolved and fail at materialize. */
|
|
341
|
+
hostRosterStore?: HostRosterStore;
|
|
342
|
+
/** This orchestrator instance id (for the cross-cluster host-fanout pin reroute). */
|
|
343
|
+
instanceId?: string;
|
|
344
|
+
/** Static-host grace before a disconnected static host reads unreachable (ms). */
|
|
345
|
+
rosterGraceMs?: number;
|
|
346
|
+
/** Cap on runsOnAll per-host children (default 1024). */
|
|
347
|
+
maxFanoutHosts?: number;
|
|
321
348
|
}
|
|
322
349
|
/**
|
|
323
350
|
* Check if any trigger in the lock file workflows uses path filters.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { Database, RemoteSourceRow } from '../db/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Deterministic routing key for an org's Platform-relayed remote runs. A
|
|
5
|
+
* Platform-relayed `test.trigger` carries this key; `resolveOrgId` maps it back
|
|
6
|
+
* to the canonical org id via the `remote_sources` row.
|
|
7
|
+
*/
|
|
8
|
+
export declare function remoteRoutingKeyFor(orgId: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Idempotently upsert the `remote_sources` anchor for an org. Called on every
|
|
11
|
+
* Platform (re)connect once the orchestrator learns its canonical org id from
|
|
12
|
+
* `auth.success`. The unique `(customer_id)` constraint makes this a safe
|
|
13
|
+
* self-heal — re-running updates `cluster_id` if the cluster identity changed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function provisionRemoteSource(db: Kysely<Database>, params: {
|
|
16
|
+
orgId: string;
|
|
17
|
+
clusterId: string | null;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
/** Read the auto-provisioned remote-source row for an org, if it exists. */
|
|
20
|
+
export declare function getRemoteSource(db: Kysely<Database>, orgId: string): Promise<RemoteSourceRow | undefined>;
|
|
21
|
+
//# sourceMappingURL=remote-source-store.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { Database } from '../db/types.js';
|
|
3
|
+
import type { ProcessingDeps } from './processor.js';
|
|
4
|
+
import type { ReleaseSignal } from '../environments/held-runs.js';
|
|
5
|
+
import { type WorkflowDispatchContext } from './dispatch-matched-workflow.js';
|
|
6
|
+
import { type SerializableWorkflowDispatchInputs } from './pending-workflow-context.js';
|
|
7
|
+
/**
|
|
8
|
+
* Rebuild a live `WorkflowDispatchContext` from the persisted serializable
|
|
9
|
+
* inputs by re-attaching the orchestrator's live `deps` and reconstructing the
|
|
10
|
+
* provider `bundle` from the live registry (keyed by the stored routing key).
|
|
11
|
+
* Returns null when the provider bundle can no longer be resolved.
|
|
12
|
+
*/
|
|
13
|
+
export declare function rebuildWorkflowDispatchContext(inputs: SerializableWorkflowDispatchInputs, deps: ProcessingDeps): WorkflowDispatchContext | null;
|
|
14
|
+
/**
|
|
15
|
+
* Resume a released workflow install-gate hold. Loads the pending context,
|
|
16
|
+
* rebuilds the dispatch context, and re-dispatches with the gate skipped. On a
|
|
17
|
+
* lost pending context (or unresolvable provider bundle) the run is failed
|
|
18
|
+
* loudly rather than silently dropped.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resumeWorkflow(signal: ReleaseSignal, deps: ProcessingDeps, db: Kysely<Database> | undefined): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Cancel a rejected workflow install-gate hold: mark the run cancelled and drop
|
|
23
|
+
* the pending context.
|
|
24
|
+
*/
|
|
25
|
+
export declare function rejectWorkflow(runId: string, deps: ProcessingDeps, db: Kysely<Database> | undefined, reason: string): Promise<void>;
|
|
26
|
+
//# sourceMappingURL=resume-workflow.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rolling-wave scheduler for bounded fan-out (`maxParallel` / `failFast`).
|
|
3
|
+
*
|
|
4
|
+
* Sibling of the needs-scheduler (`needs-scheduler.ts`): both fire on a job
|
|
5
|
+
* reaching terminal state and decide what to do with held downstream/sibling
|
|
6
|
+
* jobs. The needs-scheduler releases jobs whose `needs` edges are now satisfied;
|
|
7
|
+
* the wave-scheduler releases the next `wave_gated` sibling of a fan-out base
|
|
8
|
+
* whenever an in-flight slot frees up (or, under `failFast`, skips the held
|
|
9
|
+
* remainder on the first child failure).
|
|
10
|
+
*
|
|
11
|
+
* Pure DB — no in-memory state. Every decision is a fresh query against
|
|
12
|
+
* `execution_jobs` keyed by `(run_id, base_job_name)`, so the scheduler needs
|
|
13
|
+
* zero recovery code on orchestrator restart. The caller performs the DB write
|
|
14
|
+
* (clear `wave_gated` / mark skipped) and the dispatch (`onJobReady`), keeping
|
|
15
|
+
* this module a pure decision function.
|
|
16
|
+
*/
|
|
17
|
+
import type { Kysely } from 'kysely';
|
|
18
|
+
import type { Database } from '../db/types.js';
|
|
19
|
+
/** Inputs identifying a completed fan-out child. The wave policy is read from the base group. */
|
|
20
|
+
export interface WaveEvaluation {
|
|
21
|
+
runId: string;
|
|
22
|
+
/** The base (logical) job name shared by every fan-out child. */
|
|
23
|
+
baseJobName: string;
|
|
24
|
+
/** Terminal status of the child that just completed. */
|
|
25
|
+
completedStatus: string;
|
|
26
|
+
}
|
|
27
|
+
/** The wave-scheduler's decision for one completed child. */
|
|
28
|
+
export type WaveResult = {
|
|
29
|
+
action: 'release';
|
|
30
|
+
jobName: string;
|
|
31
|
+
baseJobName: string;
|
|
32
|
+
maxParallel: number;
|
|
33
|
+
failFast: boolean;
|
|
34
|
+
} | {
|
|
35
|
+
action: 'skip-remaining';
|
|
36
|
+
jobNames: string[];
|
|
37
|
+
} | {
|
|
38
|
+
action: 'noop';
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Decide what happens after a fan-out child of `baseJobName` reaches terminal.
|
|
42
|
+
*
|
|
43
|
+
* The wave policy (`maxParallel` / `failFast`) is read from the base group's
|
|
44
|
+
* own rows — every child of a bounded wave carries the same stamped
|
|
45
|
+
* `wave_max_parallel` / `wave_fail_fast`, so the just-completed child's slot
|
|
46
|
+
* being re-inserted without the policy on release does not break the chain
|
|
47
|
+
* (the still-held siblings carry it). If no sibling carries a policy, this is
|
|
48
|
+
* not a bounded wave → `noop`.
|
|
49
|
+
*
|
|
50
|
+
* - `failFast` + a child failure → `skip-remaining` every still-held sibling.
|
|
51
|
+
* - in-flight count `< maxParallel` AND a held sibling exists → `release` the
|
|
52
|
+
* next held sibling (lowest `variant_label`).
|
|
53
|
+
* - otherwise → `noop`.
|
|
54
|
+
*
|
|
55
|
+
* "In-flight" = a non-terminal, non-`wave_gated` child (it has been dispatched
|
|
56
|
+
* and not yet completed). The just-completed child is terminal, so it does not
|
|
57
|
+
* count against the window — its slot is the one we are filling.
|
|
58
|
+
*/
|
|
59
|
+
export declare function evaluateWave(db: Kysely<Database>, evaluation: WaveEvaluation): Promise<WaveResult>;
|
|
60
|
+
//# sourceMappingURL=wave-scheduler.d.ts.map
|