@kici-dev/orchestrator 0.5.0 → 0.6.1
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 +23 -0
- package/dist/agent/dispatcher.d.ts +81 -13
- package/dist/agent/host-roster.d.ts +37 -5
- package/dist/agent/registry.d.ts +2 -0
- package/dist/app.d.ts +33 -6
- 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 +7 -0
- package/dist/cli/api-client.d.ts +18 -0
- package/dist/cli/commands/cluster-settings.d.ts +11 -0
- package/dist/cli/commands/diagnose.d.ts +6 -0
- package/dist/cli/commands/held-run.d.ts +58 -0
- package/dist/cli/commands/trust-policy.d.ts +117 -12
- package/dist/cli/kici-admin.d.ts +10 -0
- package/dist/cli.js +2272 -1025
- package/dist/cluster/cluster-identity.d.ts +3 -3
- package/dist/cluster/cluster-settings-reader.d.ts +7 -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 +21 -21
- 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/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 +368 -10
- 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 +2 -2
- package/dist/metrics/prometheus.d.ts +111 -3
- package/dist/oidc/oidc-mint-registration.d.ts +7 -3
- package/dist/orchestrator-core.d.ts +79 -23
- package/dist/pipeline/direct-ingress-deps.d.ts +100 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +302 -31
- package/dist/pipeline/global-eval-round.d.ts +14 -0
- package/dist/pipeline/internal-event-pipeline.d.ts +140 -0
- package/dist/pipeline/invoke-gate.d.ts +164 -0
- package/dist/pipeline/needs-scheduler.d.ts +13 -0
- package/dist/pipeline/process-webhook.d.ts +119 -31
- package/dist/pipeline/processor.d.ts +226 -21
- 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 +9 -0
- package/dist/pipeline/security-hold-check.d.ts +217 -0
- package/dist/pipeline/undispatched-hold-checks.d.ts +23 -0
- package/dist/policy/dashboard-write-policy-listener.d.ts +62 -0
- package/dist/policy/dashboard-write-policy.d.ts +14 -0
- package/dist/provider-registry.d.ts +10 -2
- package/dist/providers/github/check-status-poster.d.ts +11 -0
- package/dist/providers/github/index.d.ts +0 -2
- package/dist/providers/github/normalizer.d.ts +3 -2
- package/dist/providers/local/normalizer.d.ts +3 -5
- package/dist/providers/universal-git/index.d.ts +11 -2
- package/dist/queue/job-queue.d.ts +43 -1
- package/dist/registration/extractor.d.ts +3 -3
- package/dist/registration/registration-index.d.ts +7 -0
- package/dist/registration/registration-store.d.ts +22 -0
- package/dist/reporting/check-run-reporter.d.ts +94 -1
- package/dist/reporting/check-run-summary.d.ts +25 -1
- package/dist/reporting/execution-tracker.d.ts +207 -2
- 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-trust-policy.d.ts +34 -6
- package/dist/routes/admin.d.ts +9 -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 +899 -72
- 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/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 +17684 -7351
- 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 +24238 -13822
- package/dist/worker/in-memory-job-queue.d.ts +1 -1
- package/dist/ws/agent-handler.d.ts +28 -4
- package/dist/ws/dashboard-context-handler.d.ts +28 -4
- package/dist/ws/dashboard-global-workflows-handler.d.ts +6 -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 +54 -9
- package/installer-image-digests.json +3 -3
- package/package.json +24 -19
- package/sbom.spdx.json +864 -822
- package/dist/approvals/team-membership-lookup.d.ts +0 -13
- package/dist/providers/github/contributor-resolver.d.ts +0 -30
- package/dist/security/contributor-cache.d.ts +0 -83
|
@@ -23,9 +23,9 @@ export interface ClusterIdentityDeps {
|
|
|
23
23
|
*/
|
|
24
24
|
storagePrefix?: string;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
* returns early.
|
|
28
|
-
*
|
|
26
|
+
* Test-only escape hatch — when true, validateS3Sentinel logs a warning and
|
|
27
|
+
* returns early. Supplied only by the build-time test double's injected
|
|
28
|
+
* fault-injection policy; the shipped orchestrator always validates.
|
|
29
29
|
*/
|
|
30
30
|
skipSentinelValidation?: boolean;
|
|
31
31
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Kysely } from 'kysely';
|
|
2
2
|
import type { Database } from '../db/types.js';
|
|
3
3
|
/** Numeric columns on cluster_settings readable via {@link ClusterSettingsReader}. */
|
|
4
|
-
export type ClusterNumberColumn = 'max_github_payload_bytes' | 'event_log_max_payload_bytes' | 'lock_file_max_bytes' | 'webhook_dedup_ttl_ms'
|
|
4
|
+
export type ClusterNumberColumn = 'max_github_payload_bytes' | 'event_log_max_payload_bytes' | 'lock_file_max_bytes' | 'webhook_dedup_ttl_ms'
|
|
5
|
+
/** @deprecated Readable, but no call site reads it. Removed at v1.0.0. */
|
|
6
|
+
| 'contributor_cache_ttl_ms' | 'event_router_event_ttl_seconds' | 'event_router_max_dispatch_attempts' | 'queue_max_depth' | 'reroute_flap_grace_ms' | 'max_fanout_hosts' | 'event_router_rate_limit_per_workflow_per_minute' | 'cache_max_tarball_bytes' | 'cache_ttl_days' | 'lockfile_cache_max' | 'lockfile_cache_max_bytes' | 'lockfile_cache_ttl_ms' | 'content_cache_max' | 'content_cache_max_bytes' | 'content_cache_ttl_ms' | 'global_eval_round_timeout_ms' | 'global_eval_candidate_timeout_ms' | 'global_eval_cache_max' | 'global_eval_wait_timeout_ms' | 'check_run_tracking_ttl_days' | 'concurrency_wait_timeout_ms' | 'agent_token_ttl_ms' | 'ownership_db_check_timeout_ms' | 'unroutable_grace_ms' | 'ingest_overflow_claim_timeout_ms' | 'scaler_reap_interval_ms' | 'scaler_reap_stranded_timeout_ms' | 'scaler_reap_reattempt_interval_ms' | 'scaler_claim_retention_ms' | 'scaler_provision_backoff_base_ms' | 'scaler_provision_backoff_max_ms' | 'scaler_provision_max_consecutive_failures';
|
|
5
7
|
/** Text columns on cluster_settings readable via {@link ClusterSettingsReader}. */
|
|
6
8
|
export type ClusterStringColumn = 'dashboard_verified_issuer';
|
|
7
9
|
/** Boolean columns on cluster_settings readable via {@link ClusterSettingsReader}. */
|
|
@@ -39,6 +41,10 @@ export declare const CACHE_MAX_ENTRIES_CEILING = 100000;
|
|
|
39
41
|
* A value that is not a usable positive count (NaN, non-finite, below 1) falls
|
|
40
42
|
* back to the configured default rather than clamping to 1: a 1-entry cache
|
|
41
43
|
* thrashes silently, which is harder to diagnose than simply ignoring garbage.
|
|
44
|
+
* The fallback is floored and bounded by the ceiling on the same path as the
|
|
45
|
+
* stored value, so neither a fractional nor an above-ceiling configured default
|
|
46
|
+
* can reach the LRU constructor as a non-integer or unclamped max — the
|
|
47
|
+
* constructor rejects both, and either would prevent boot.
|
|
42
48
|
*/
|
|
43
49
|
export declare function clampCacheMaxEntries(value: number, fallback: number): number;
|
|
44
50
|
/**
|
|
@@ -90,6 +90,8 @@ export interface RunCoordinatorDeps {
|
|
|
90
90
|
sendAndWaitAckViaHandler?: (targetInstanceId: string, msg: JobReroute, timeoutMs: number) => Promise<boolean>;
|
|
91
91
|
/** Fallback: send a message via server-side (incoming) peer connection (fire-and-forget). */
|
|
92
92
|
sendToPeerViaHandler?: (targetInstanceId: string, msg: PeerToPeerMessage) => boolean;
|
|
93
|
+
/** Close a peer's incoming socket by instanceId (used by worker eviction). */
|
|
94
|
+
closePeerViaHandler?: (targetInstanceId: string, code: number, reason: string) => void;
|
|
93
95
|
ackTimeoutMs?: number;
|
|
94
96
|
/** Stale peer timeout in ms. Default: 60000 (60s). */
|
|
95
97
|
staleTimeoutMs?: number;
|
|
@@ -137,7 +139,23 @@ export declare class RunCoordinator {
|
|
|
137
139
|
private readonly nakTracker;
|
|
138
140
|
/** Stale eviction timer handle. */
|
|
139
141
|
private staleEvictionTimer;
|
|
142
|
+
/** Plan-ceiling worker eviction (drain-then-disconnect). */
|
|
143
|
+
private readonly workerEviction;
|
|
144
|
+
/** Per-peer "no in-flight rerouted jobs" callbacks, fired by worker drain. */
|
|
145
|
+
private readonly evictionIdleCallbacks;
|
|
146
|
+
private readonly closePeerViaHandler?;
|
|
140
147
|
constructor(deps: RunCoordinatorDeps);
|
|
148
|
+
/** In-flight rerouted jobs currently attributed to a peer. */
|
|
149
|
+
private runningJobCountForPeer;
|
|
150
|
+
/** Fire a registered drain idle-callback once the peer has no in-flight jobs. */
|
|
151
|
+
private fireIdleIfDrained;
|
|
152
|
+
/**
|
|
153
|
+
* Reconcile this coordinator's workers against the pushed ceiling. Invoked on
|
|
154
|
+
* every `plan.headroom` frame: when `evictExcess` is set, the newest workers
|
|
155
|
+
* past `ceiling` are drained; otherwise any in-flight drain is cancelled and
|
|
156
|
+
* the worker restored, so a raised ceiling rescues it.
|
|
157
|
+
*/
|
|
158
|
+
reconcileWorkerEviction(ceiling: number, evictExcess: boolean): void;
|
|
141
159
|
/**
|
|
142
160
|
* Route jobs for a webhook run. Claims jobs that local agents can handle,
|
|
143
161
|
* reroutes the rest to peers with matching capacity.
|
|
@@ -268,6 +286,32 @@ export declare class RunCoordinator {
|
|
|
268
286
|
* its worker is connected.
|
|
269
287
|
*/
|
|
270
288
|
private trackReroutedJob;
|
|
289
|
+
/** Arm a spawn-window / reap timer that fires {@link handleRerouteSpawnTimeout}. */
|
|
290
|
+
private armRerouteWindow;
|
|
291
|
+
/**
|
|
292
|
+
* Whether the reroute target has actually started the job, read from the
|
|
293
|
+
* shared `execution_jobs` row.
|
|
294
|
+
*
|
|
295
|
+
* A read failure answers "not running" so a transient database fault cannot
|
|
296
|
+
* silently disable the backstop — the re-dispatch it then performs is the
|
|
297
|
+
* behavior that shipped before this check existed, and it is idempotent.
|
|
298
|
+
* Only the FIRST observation goes through this path: once `peerStarted`
|
|
299
|
+
* latches, a read fault can no longer bounce a running job.
|
|
300
|
+
*/
|
|
301
|
+
private rerouteTargetIsRunning;
|
|
302
|
+
/**
|
|
303
|
+
* The peer owns the job, so the backstop must not re-dispatch it. Release the
|
|
304
|
+
* tracking entry once the shared row is terminal, otherwise re-arm the window
|
|
305
|
+
* as a reap poll.
|
|
306
|
+
*
|
|
307
|
+
* The entry cannot simply be dropped here: cancel propagation still needs it
|
|
308
|
+
* while the job runs. It also cannot simply be kept, because a peer
|
|
309
|
+
* COORDINATOR relays no terminal `job.progress` — the shared row IS its
|
|
310
|
+
* report — so nothing else would ever release it and `reroutedJobs` would
|
|
311
|
+
* grow for the lifetime of the process. A read fault re-arms (never releases),
|
|
312
|
+
* so a transient fault costs one more poll rather than tracking.
|
|
313
|
+
*/
|
|
314
|
+
private reapOrDeferRerouteTracking;
|
|
271
315
|
/**
|
|
272
316
|
* Post-ACK spawn-window backstop. Fires when a rerouted job's peer accepted
|
|
273
317
|
* but produced no progress within the window (Layer A), or immediately when a
|
package/dist/cluster/index.d.ts
CHANGED
|
@@ -21,6 +21,10 @@ export type { RaftPersistentState } from './raft-state.js';
|
|
|
21
21
|
export { OrphanRecovery } from './orphan-recovery.js';
|
|
22
22
|
export { RunCoordinator } from './coordinator.js';
|
|
23
23
|
export type { RunContext, JobToRoute, RouteResult } from './coordinator.js';
|
|
24
|
+
export { PlanHeadroomStore } from './plan-headroom-store.js';
|
|
25
|
+
export type { StoredPlanHeadroom } from './plan-headroom-store.js';
|
|
26
|
+
export { WorkerEviction, selectEvictionTargets, WORKER_DRAIN_TIMEOUT_MS, } from './worker-eviction.js';
|
|
27
|
+
export type { EvictionHooks } from './worker-eviction.js';
|
|
24
28
|
export { createClusterHealthRoutes } from './health-api.js';
|
|
25
29
|
export type { ClusterHealthRoutesDeps } from './health-api.js';
|
|
26
30
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -33,6 +33,14 @@ export interface PeerHandlerDeps {
|
|
|
33
33
|
peerRegistry: PeerRegistry;
|
|
34
34
|
/** Callback to get this orchestrator's local agent inventory for heartbeats. */
|
|
35
35
|
getLocalInventory: () => Omit<PeerHeartbeat, 'type'>;
|
|
36
|
+
/**
|
|
37
|
+
* The Platform-pushed worker ceiling this coordinator enforces, or `null`
|
|
38
|
+
* when none was ever received (admit worker joins freely). Read at each
|
|
39
|
+
* worker-peer admission. Served from the persisted PlanHeadroomStore, so it
|
|
40
|
+
* survives a Platform disconnect — the coordinator keeps enforcing the last
|
|
41
|
+
* known ceiling rather than resetting to unlimited.
|
|
42
|
+
*/
|
|
43
|
+
getWorkerCeiling?: () => Promise<number | null>;
|
|
36
44
|
/** Heartbeat interval in ms. Default: 30000 (30s). */
|
|
37
45
|
heartbeatIntervalMs?: number;
|
|
38
46
|
/** Auth timeout in ms. Default: 15000 (15s). */
|
|
@@ -103,9 +111,20 @@ export interface PeerHandlerDeps {
|
|
|
103
111
|
* Returns `handleConnection(ws, remoteIp?)` which should be called when a new
|
|
104
112
|
* WebSocket connection is upgraded on the peer endpoint.
|
|
105
113
|
*/
|
|
114
|
+
/**
|
|
115
|
+
* Whether a joining peer may be admitted against the plan ceiling.
|
|
116
|
+
*
|
|
117
|
+
* Only workers are gated: a coordinator peer holds its own Platform connection
|
|
118
|
+
* and is counted there, and a peer advertising no role registers as a
|
|
119
|
+
* coordinator (see PeerRegistry.addPeer), so neither is gated here. A `null`
|
|
120
|
+
* ceiling — none ever received — admits freely. The ceiling is ABSOLUTE, so the
|
|
121
|
+
* join is admitted only while the currently-connected worker count is below it.
|
|
122
|
+
*/
|
|
123
|
+
export declare function shouldAdmitWorker(role: string | undefined, ceiling: number | null, connectedWorkerCount: number): boolean;
|
|
106
124
|
export declare function createPeerHandler(deps: PeerHandlerDeps): {
|
|
107
125
|
handleConnection: (ws: PeerWsLike, remoteIp?: string) => void;
|
|
108
126
|
sendToPeer: (targetInstanceId: string, msg: PeerToPeerMessage) => boolean;
|
|
127
|
+
closePeer: (targetInstanceId: string, code: number, reason: string) => void;
|
|
109
128
|
sendAndWaitAck: (targetInstanceId: string, msg: JobReroute, timeoutMs?: number) => Promise<boolean>;
|
|
110
129
|
sendConfigReloadAndWait: (targetInstanceId: string, msg: PeerConfigReload, timeoutMs?: number) => Promise<PeerConfigReloadResponse | null>;
|
|
111
130
|
sendLogsCollectAndWait: (targetInstanceId: string, msg: PeerLogsCollectRequest, timeoutMs: number) => Promise<Buffer>;
|
|
@@ -35,6 +35,8 @@ export interface PeerInfo {
|
|
|
35
35
|
routingKeys: string[];
|
|
36
36
|
connected: boolean;
|
|
37
37
|
lastHeartbeatAt: number;
|
|
38
|
+
/** When this peer first connected. Distinct from `lastHeartbeatAt`, which moves; this does not. */
|
|
39
|
+
connectedAt: number;
|
|
38
40
|
agents: PeerAgentInfo[];
|
|
39
41
|
draining: boolean;
|
|
40
42
|
capabilities: PeerCapabilities;
|
|
@@ -71,6 +73,12 @@ export interface PeerRegistryOptions {
|
|
|
71
73
|
onClusterSettingsVersionBehind?: (peerVersion: number) => void;
|
|
72
74
|
/** Called when a peer transitions to disconnected state. */
|
|
73
75
|
onPeerDisconnected?: (instanceId: string) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Called when the connected-peer set changes (a fresh add or a
|
|
78
|
+
* markDisconnected), so a coordinator can re-report its `cluster.membership`
|
|
79
|
+
* snapshot to the Platform without waiting for the periodic interval.
|
|
80
|
+
*/
|
|
81
|
+
onMembershipChange?: () => void;
|
|
74
82
|
}
|
|
75
83
|
export declare class PeerRegistry {
|
|
76
84
|
private readonly peers;
|
|
@@ -81,6 +89,7 @@ export declare class PeerRegistry {
|
|
|
81
89
|
private readonly onRegistryVersionBehind?;
|
|
82
90
|
private readonly onClusterSettingsVersionBehind?;
|
|
83
91
|
private readonly onPeerDisconnected?;
|
|
92
|
+
private readonly onMembershipChange?;
|
|
84
93
|
constructor(options?: PeerRegistryOptions);
|
|
85
94
|
/**
|
|
86
95
|
* Update the local config version for comparison with peer heartbeats.
|
|
@@ -196,6 +205,14 @@ export declare class PeerRegistry {
|
|
|
196
205
|
* Get all peers with role=worker.
|
|
197
206
|
*/
|
|
198
207
|
getWorkerPeers(): PeerInfo[];
|
|
208
|
+
/**
|
|
209
|
+
* Currently-connected worker peers.
|
|
210
|
+
*
|
|
211
|
+
* A peer that advertises no role registers as a coordinator (see `addPeer`),
|
|
212
|
+
* so a worker predating the role field is not counted here. That under-counts,
|
|
213
|
+
* which errs permissive, and it self-heals as workers upgrade.
|
|
214
|
+
*/
|
|
215
|
+
getConnectedWorkerPeers(): PeerInfo[];
|
|
199
216
|
/**
|
|
200
217
|
* Get all peers with role=coordinator.
|
|
201
218
|
*/
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { PlanHeadroom } from '@kici-dev/engine';
|
|
3
|
+
import type { Database } from '../db/types.js';
|
|
4
|
+
/** The ceiling as last pushed by the Platform. */
|
|
5
|
+
export interface StoredPlanHeadroom {
|
|
6
|
+
maxWorkerPeers: number;
|
|
7
|
+
orgLimit: number;
|
|
8
|
+
orgTotal: number;
|
|
9
|
+
evictExcess: boolean;
|
|
10
|
+
updatedAt: Date;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The orchestrator's cache of the Platform-owned worker ceiling.
|
|
14
|
+
*
|
|
15
|
+
* Persisted so a coordinator restarting during a Platform outage keeps
|
|
16
|
+
* enforcing the last known ceiling instead of resetting to unlimited. Same
|
|
17
|
+
* ownership shape as `org_trust_policy`: the Platform writes it, the
|
|
18
|
+
* orchestrator only reads it back.
|
|
19
|
+
*/
|
|
20
|
+
export declare class PlanHeadroomStore {
|
|
21
|
+
private readonly db;
|
|
22
|
+
constructor(db: Kysely<Database>);
|
|
23
|
+
read(): Promise<StoredPlanHeadroom | null>;
|
|
24
|
+
write(headroom: PlanHeadroom): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=plan-headroom-store.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { PeerInfo, PeerRegistry } from './peer-registry.js';
|
|
2
|
+
/** How long a draining worker may hold in-flight jobs before it is disconnected anyway. */
|
|
3
|
+
export declare const WORKER_DRAIN_TIMEOUT_MS: number;
|
|
4
|
+
/**
|
|
5
|
+
* The coordinator-side operations eviction needs.
|
|
6
|
+
*
|
|
7
|
+
* Declared here rather than reached for on the coordinator directly: the
|
|
8
|
+
* coordinator owns dispatch and job attribution, but exposes no ineligibility
|
|
9
|
+
* or idle-notification surface, and widening its public API for one caller
|
|
10
|
+
* would couple eviction to it. A fake implementation makes this class unit
|
|
11
|
+
* testable without a running cluster.
|
|
12
|
+
*/
|
|
13
|
+
export interface EvictionHooks {
|
|
14
|
+
/** Stop rerouting new jobs to this peer. */
|
|
15
|
+
markIneligible(instanceId: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Restore this peer to dispatch eligibility and drop any idle-notification
|
|
18
|
+
* registration. Called when a drain is cancelled (the ceiling rose so the
|
|
19
|
+
* peer is no longer excess) and after a drained peer is disconnected, so a
|
|
20
|
+
* later re-admission of the same instance starts dispatch-eligible.
|
|
21
|
+
*/
|
|
22
|
+
clearIneligible(instanceId: string): void;
|
|
23
|
+
/** In-flight rerouted jobs currently attributed to this peer. */
|
|
24
|
+
runningJobCount(instanceId: string): number;
|
|
25
|
+
/** Invoke the callback once this peer has no in-flight rerouted jobs. */
|
|
26
|
+
onPeerIdle(instanceId: string, callback: () => void): void;
|
|
27
|
+
/** Close this peer's connection with the given code and reason. */
|
|
28
|
+
closePeer(instanceId: string, code: number, reason: string): void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The workers to drain to bring this coordinator down to its ceiling.
|
|
32
|
+
*
|
|
33
|
+
* Newest-first by connect time: the joins that crossed the line are the ones
|
|
34
|
+
* that go, so long-running capacity is not shuffled arbitrarily.
|
|
35
|
+
*/
|
|
36
|
+
export declare function selectEvictionTargets(workers: readonly PeerInfo[], ceiling: number): PeerInfo[];
|
|
37
|
+
/**
|
|
38
|
+
* Drain-then-disconnect this coordinator's excess workers.
|
|
39
|
+
*
|
|
40
|
+
* A target is first marked ineligible for new dispatch, then closed once its
|
|
41
|
+
* in-flight rerouted jobs reach terminal status — which the worker's durable
|
|
42
|
+
* outbox and `job.progress.ack` already guarantee — or after
|
|
43
|
+
* `WORKER_DRAIN_TIMEOUT_MS`, after which the coordinator's existing
|
|
44
|
+
* orphan-recovery guard handles whatever remains.
|
|
45
|
+
*/
|
|
46
|
+
export declare class WorkerEviction {
|
|
47
|
+
private readonly peerRegistry;
|
|
48
|
+
private readonly hooks;
|
|
49
|
+
private readonly logger;
|
|
50
|
+
private readonly draining;
|
|
51
|
+
constructor(peerRegistry: PeerRegistry, hooks: EvictionHooks, logger: {
|
|
52
|
+
warn: (msg: string, meta?: unknown) => void;
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Reconcile the connected worker set against the ceiling.
|
|
56
|
+
*
|
|
57
|
+
* When `evictExcess` is true, drain the newest workers past `ceiling`;
|
|
58
|
+
* otherwise the target set is empty. Any drain in flight for a worker that is
|
|
59
|
+
* no longer a target is CANCELLED — its timer cleared and its dispatch
|
|
60
|
+
* eligibility restored — so a raised ceiling (or the Platform clearing
|
|
61
|
+
* `evictExcess` once the org drops back under) rescues a worker instead of
|
|
62
|
+
* disconnecting it at the drain timeout.
|
|
63
|
+
*/
|
|
64
|
+
reconcile(ceiling: number, evictExcess: boolean): void;
|
|
65
|
+
/** Stop an in-flight drain and restore the worker to dispatch eligibility. */
|
|
66
|
+
private cancelDrain;
|
|
67
|
+
private startDrain;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=worker-eviction.d.ts.map
|
|
@@ -13,10 +13,17 @@
|
|
|
13
13
|
* dashboard's payload-detail view continues to resolve archived
|
|
14
14
|
* deliveries identically to hot ones.
|
|
15
15
|
*/
|
|
16
|
-
import { type Kysely
|
|
16
|
+
import { type Kysely } from 'kysely';
|
|
17
17
|
import { type ColdStore } from '@kici-dev/shared';
|
|
18
|
-
import type { Database
|
|
19
|
-
|
|
18
|
+
import type { Database } from '../db/types.js';
|
|
19
|
+
import type { EventLogColdStoreRow } from './tables/event-log.js';
|
|
20
|
+
/**
|
|
21
|
+
* Re-exported, not re-declared: the alias is defined beside the cold-store
|
|
22
|
+
* adapter that owns the table, and a second identical declaration here is what
|
|
23
|
+
* a homonym sweep flags. Kept as an export so this module's public surface is
|
|
24
|
+
* unchanged for its consumers.
|
|
25
|
+
*/
|
|
26
|
+
export type { EventLogColdStoreRow };
|
|
20
27
|
export interface LoadOrchEventLogRangeArgs {
|
|
21
28
|
db: Kysely<Database>;
|
|
22
29
|
coldStore: ColdStore | undefined;
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
* rows after streaming. This is correct because cold chunks are
|
|
16
16
|
* partitioned by `(routing_key, day)`, not by the secondary filters.
|
|
17
17
|
*/
|
|
18
|
-
import { type Kysely
|
|
18
|
+
import { type Kysely } from 'kysely';
|
|
19
19
|
import { type ColdStore } from '@kici-dev/shared';
|
|
20
|
-
import type { Database,
|
|
21
|
-
|
|
20
|
+
import type { Database, SecretAuditLogRow } from '../db/types.js';
|
|
21
|
+
/** Re-exported rather than re-declared — see the note in `load-event-log-range`. */
|
|
22
|
+
export type { SecretAuditLogRow };
|
|
22
23
|
export interface LoadSecretAuditLogRangeArgs {
|
|
23
24
|
db: Kysely<Database>;
|
|
24
25
|
coldStore: ColdStore | undefined;
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
* synthetic prefix `__orchestrator__`. Mirrors the same pattern already
|
|
14
14
|
* used for `event_log` payload keys when the routing key is missing.
|
|
15
15
|
*/
|
|
16
|
-
import { type Kysely
|
|
16
|
+
import { type Kysely } from 'kysely';
|
|
17
17
|
import { type ChunkCommitMetadata, type ColdRetention, type ColdStoreTableConfig, type EligiblePartition, type TableAdapter } from '@kici-dev/shared';
|
|
18
|
-
import type { Database,
|
|
19
|
-
|
|
18
|
+
import type { Database, SecretAuditLogRow } from '../../db/types.js';
|
|
19
|
+
/** Re-exported rather than re-declared — see the note in `load-event-log-range`. */
|
|
20
|
+
export type { SecretAuditLogRow };
|
|
20
21
|
export interface SecretAuditLogAdapterOptions {
|
|
21
22
|
overrides?: Partial<ColdStoreTableConfig>;
|
|
22
23
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -97,6 +97,13 @@ declare const configSchema: z.ZodObject<{
|
|
|
97
97
|
scalerSpawnTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
98
98
|
backupStalenessWarnHours: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
99
99
|
scalerPendingSweepIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
100
|
+
scalerReapIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
101
|
+
scalerReapStrandedTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
102
|
+
scalerReapReattemptIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
103
|
+
scalerClaimRetentionMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
104
|
+
scalerProvisionBackoffBaseMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
105
|
+
scalerProvisionBackoffMaxMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
106
|
+
scalerProvisionMaxConsecutiveFailures: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
100
107
|
cacheStorageType: z.ZodOptional<z.ZodEnum<{
|
|
101
108
|
filesystem: "filesystem";
|
|
102
109
|
s3: "s3";
|
|
@@ -177,12 +184,6 @@ declare const configSchema: z.ZodObject<{
|
|
|
177
184
|
eventRouterRetryBaseBackoffMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
178
185
|
eventRouterRetryMaxBackoffMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
179
186
|
eventRouterRetryScanIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
180
|
-
testMode: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
181
|
-
testEventFailFirstN: z.ZodOptional<z.ZodString>;
|
|
182
|
-
testMintDeferAudience: z.ZodOptional<z.ZodString>;
|
|
183
|
-
testMintRejectAudience: z.ZodOptional<z.ZodString>;
|
|
184
|
-
testRerunDelayMs: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
185
|
-
testOmitDashboardRequestTypes: z.ZodOptional<z.ZodString>;
|
|
186
187
|
eventLogMaxPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
187
188
|
maxGithubPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
188
189
|
lockFileMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
@@ -202,7 +203,6 @@ declare const configSchema: z.ZodObject<{
|
|
|
202
203
|
}>>;
|
|
203
204
|
autoMigrate: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
204
205
|
agentMaxReconnectDelayMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
205
|
-
skipS3SentinelValidation: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
206
206
|
otelExporterOtlpEndpoint: z.ZodOptional<z.ZodString>;
|
|
207
207
|
clusterName: z.ZodOptional<z.ZodString>;
|
|
208
208
|
cluster: z.ZodPrefault<z.ZodObject<{
|
|
@@ -300,6 +300,13 @@ export declare const packagingConfigSchema: z.ZodObject<{
|
|
|
300
300
|
scalerSpawnTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
301
301
|
backupStalenessWarnHours: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
302
302
|
scalerPendingSweepIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
303
|
+
scalerReapIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
304
|
+
scalerReapStrandedTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
305
|
+
scalerReapReattemptIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
306
|
+
scalerClaimRetentionMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
307
|
+
scalerProvisionBackoffBaseMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
308
|
+
scalerProvisionBackoffMaxMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
309
|
+
scalerProvisionMaxConsecutiveFailures: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
303
310
|
cacheStorageType: z.ZodOptional<z.ZodEnum<{
|
|
304
311
|
filesystem: "filesystem";
|
|
305
312
|
s3: "s3";
|
|
@@ -380,12 +387,6 @@ export declare const packagingConfigSchema: z.ZodObject<{
|
|
|
380
387
|
eventRouterRetryBaseBackoffMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
381
388
|
eventRouterRetryMaxBackoffMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
382
389
|
eventRouterRetryScanIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
383
|
-
testMode: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
384
|
-
testEventFailFirstN: z.ZodOptional<z.ZodString>;
|
|
385
|
-
testMintDeferAudience: z.ZodOptional<z.ZodString>;
|
|
386
|
-
testMintRejectAudience: z.ZodOptional<z.ZodString>;
|
|
387
|
-
testRerunDelayMs: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
388
|
-
testOmitDashboardRequestTypes: z.ZodOptional<z.ZodString>;
|
|
389
390
|
eventLogMaxPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
390
391
|
maxGithubPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
391
392
|
lockFileMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
@@ -405,7 +406,6 @@ export declare const packagingConfigSchema: z.ZodObject<{
|
|
|
405
406
|
}>>;
|
|
406
407
|
autoMigrate: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
407
408
|
agentMaxReconnectDelayMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
408
|
-
skipS3SentinelValidation: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
409
409
|
otelExporterOtlpEndpoint: z.ZodOptional<z.ZodString>;
|
|
410
410
|
clusterName: z.ZodOptional<z.ZodString>;
|
|
411
411
|
cluster: z.ZodPrefault<z.ZodObject<{
|
|
@@ -533,6 +533,13 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
533
533
|
scalerSpawnTimeoutMs: number;
|
|
534
534
|
backupStalenessWarnHours: number;
|
|
535
535
|
scalerPendingSweepIntervalMs: number;
|
|
536
|
+
scalerReapIntervalMs: number;
|
|
537
|
+
scalerReapStrandedTimeoutMs: number;
|
|
538
|
+
scalerReapReattemptIntervalMs: number;
|
|
539
|
+
scalerClaimRetentionMs: number;
|
|
540
|
+
scalerProvisionBackoffBaseMs: number;
|
|
541
|
+
scalerProvisionBackoffMaxMs: number;
|
|
542
|
+
scalerProvisionMaxConsecutiveFailures: number;
|
|
536
543
|
cacheStorageType?: "filesystem" | "s3" | undefined;
|
|
537
544
|
cacheStoragePath?: string | undefined;
|
|
538
545
|
cacheStorageS3Bucket?: string | undefined;
|
|
@@ -595,12 +602,6 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
595
602
|
eventRouterRetryBaseBackoffMs: number;
|
|
596
603
|
eventRouterRetryMaxBackoffMs: number;
|
|
597
604
|
eventRouterRetryScanIntervalMs: number;
|
|
598
|
-
testMode: boolean;
|
|
599
|
-
testEventFailFirstN?: string | undefined;
|
|
600
|
-
testMintDeferAudience?: string | undefined;
|
|
601
|
-
testMintRejectAudience?: string | undefined;
|
|
602
|
-
testRerunDelayMs?: number | undefined;
|
|
603
|
-
testOmitDashboardRequestTypes?: string | undefined;
|
|
604
605
|
eventLogMaxPayloadBytes: number;
|
|
605
606
|
maxGithubPayloadBytes: number;
|
|
606
607
|
lockFileMaxBytes: number;
|
|
@@ -611,7 +612,6 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
611
612
|
nodeEnv: "development" | "production" | "test";
|
|
612
613
|
autoMigrate: boolean;
|
|
613
614
|
agentMaxReconnectDelayMs: number;
|
|
614
|
-
skipS3SentinelValidation: boolean;
|
|
615
615
|
otelExporterOtlpEndpoint?: string | undefined;
|
|
616
616
|
clusterName?: string | undefined;
|
|
617
617
|
cluster: {
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Manages the lifecycle: pending -> approved/rejected/expired.
|
|
5
5
|
*/
|
|
6
|
-
import { Kysely } from 'kysely';
|
|
6
|
+
import { Kysely, type Transaction } from 'kysely';
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { type ApprovalRequirement, type ApproverClause, type StepApprovalPayload, ApprovalDecision, HoldScope, TriggerSource } from '@kici-dev/engine';
|
|
9
9
|
import type { Database, HeldRun, HeldRunApproval } from '../db/types.js';
|
|
10
|
+
/** A Kysely root handle or an in-flight transaction — query builders accept either. */
|
|
11
|
+
type Executor = Kysely<Database> | Transaction<Database>;
|
|
10
12
|
/** Status values for held runs (held_runs table). */
|
|
11
13
|
export declare enum HeldRunStatus {
|
|
12
14
|
Pending = "pending",
|
|
@@ -17,7 +19,25 @@ export declare enum HeldRunStatus {
|
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Reason a run was held in the security queue. Persisted verbatim in
|
|
20
|
-
* `held_runs.reason` and
|
|
22
|
+
* `held_runs.reason` and whose vocabulary `buildSecurityHoldSummary` switches
|
|
23
|
+
* on.
|
|
24
|
+
*
|
|
25
|
+
* Each value names why a run sits in the queue. The enum stays whole because
|
|
26
|
+
* compiling code still names all four: `buildSecurityHoldSummary` renders one
|
|
27
|
+
* branch per value. Dropping a member breaks that, and breaks the parity test
|
|
28
|
+
* that pins `SECURITY_HOLD_JOB_IDS`' three policy-reason keys to this enum's
|
|
29
|
+
* options — the engine constant cannot import the enum, so that coupling lives
|
|
30
|
+
* only in the orchestrator's test. Nothing validates `held_runs.reason` against
|
|
31
|
+
* this enum, so a stored value is not rejected anywhere.
|
|
32
|
+
*
|
|
33
|
+
* - `fork_pr` — the org trust policy's fork switch held the run.
|
|
34
|
+
* - `context_trust` — a context's minimum-trust gate held the run.
|
|
35
|
+
* - `workflow_modification` — deprecated; no longer raised. Modifications to
|
|
36
|
+
* `.kici/` are surfaced on their own informational check and no longer feed
|
|
37
|
+
* a policy arm. Removed at v1.0.0.
|
|
38
|
+
* - `unknown_contributor` — deprecated; no longer raised. The policy turns on
|
|
39
|
+
* whether the pull request came from a fork, not on who opened it. Removed
|
|
40
|
+
* at v1.0.0.
|
|
21
41
|
*/
|
|
22
42
|
export declare const SecurityHoldReason: z.ZodEnum<{
|
|
23
43
|
context_trust: "context_trust";
|
|
@@ -37,6 +57,20 @@ export interface CreateHeldRunData {
|
|
|
37
57
|
expiresAt: Date;
|
|
38
58
|
/** Queue type: 'context' (default) or 'security'. */
|
|
39
59
|
queueType?: 'context' | 'security';
|
|
60
|
+
/**
|
|
61
|
+
* Granularity of the held element. Omit to leave the column at its `'job'`
|
|
62
|
+
* default. The org trust policy's PR-wide hold passes `'workflow'`: it fires
|
|
63
|
+
* before any job is materialized and resumes by rebuilding the whole workflow
|
|
64
|
+
* dispatch, so `routeRelease` must send it to the workflow resume path.
|
|
65
|
+
*/
|
|
66
|
+
scope?: HoldScope;
|
|
67
|
+
/**
|
|
68
|
+
* What triggered the hold. Omit to leave the column at its `'context'`
|
|
69
|
+
* default. Written explicitly by the trust-policy hold so its release signal
|
|
70
|
+
* carries the pair `routeRelease` discriminates on rather than relying on a
|
|
71
|
+
* column default to supply half of it.
|
|
72
|
+
*/
|
|
73
|
+
triggerSource?: TriggerSource;
|
|
40
74
|
}
|
|
41
75
|
/**
|
|
42
76
|
* Data required to create a generalized approval hold. Unlike the legacy
|
|
@@ -109,14 +143,57 @@ export interface ListHeldRunsOptions {
|
|
|
109
143
|
export declare class HeldRunStore {
|
|
110
144
|
private readonly db;
|
|
111
145
|
constructor(db: Kysely<Database>);
|
|
112
|
-
/**
|
|
113
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Create a new held run with pending status.
|
|
148
|
+
*
|
|
149
|
+
* `exec` is the executor the INSERT runs through, defaulting to the store's
|
|
150
|
+
* own connection. A caller that writes something the row cannot exist without
|
|
151
|
+
* — the job's pending dispatch context, without which the hold can never be
|
|
152
|
+
* resumed — passes its enclosing transaction, so the two land or roll back
|
|
153
|
+
* together. Handed in rather than taken from an ambient scope: Kysely has no
|
|
154
|
+
* such scope, so a `this.db` insert inside a `db.transaction()` callback runs
|
|
155
|
+
* on a different connection and commits on its own.
|
|
156
|
+
*/
|
|
157
|
+
create(orgId: string, data: CreateHeldRunData, exec?: Executor): Promise<HeldRun>;
|
|
114
158
|
/**
|
|
115
159
|
* Create a generalized approval hold (workflow/job/step scope, explicit or
|
|
116
160
|
* context trigger) carrying a normalized `ApprovalRequirement`. Returns
|
|
117
161
|
* the created row.
|
|
162
|
+
*
|
|
163
|
+
* `exec` carries the same meaning it does on {@link create}.
|
|
164
|
+
*/
|
|
165
|
+
createHold(orgId: string, data: CreateHoldData, exec?: Executor): Promise<HeldRun>;
|
|
166
|
+
/**
|
|
167
|
+
* Record that the pending `KiCI Security` check reached the provider, for
|
|
168
|
+
* every hold that gates it, so the settle that ends one knows it has a check
|
|
169
|
+
* to terminalize.
|
|
170
|
+
*
|
|
171
|
+
* Written AFTER the post returns, never before. The two orders fail
|
|
172
|
+
* differently and the failures are not equivalent: recording first and dying
|
|
173
|
+
* before the post leaves a row claiming a check the commit does not have, and
|
|
174
|
+
* the settle then CREATES one — a completed `KiCI Security` run appearing on a
|
|
175
|
+
* commit nothing ever held. Recording second leaves the opposite residue, a
|
|
176
|
+
* real pending check the settle declines to close, which is the same stuck
|
|
177
|
+
* check the fire-and-forget post could already produce. A fabricated failing
|
|
178
|
+
* check on a pull request is worse than a stuck one, so the write goes last.
|
|
179
|
+
*
|
|
180
|
+
* **One statement for all of them, not one per hold.** A commit's check run is
|
|
181
|
+
* shared by every hold on it, and the settle asks the contention query which
|
|
182
|
+
* of them still owns it. Marking them in a loop admits a PARTIAL mark: mark
|
|
183
|
+
* the reviewer hold, fail on the security hold, and the security row keeps
|
|
184
|
+
* `posted_pending_check: false` — so it is not counted as a contender, and
|
|
185
|
+
* approving the reviewer hold terminalizes the shared check `success` while
|
|
186
|
+
* the trust hold still gates the job. That is a fabricated PASSING check,
|
|
187
|
+
* which is the worse direction, reached without any process dying: a
|
|
188
|
+
* deadlock, a statement timeout or a lost connection between the two UPDATEs
|
|
189
|
+
* is enough. A single `WHERE id IN (…)` either marks every hold or none.
|
|
190
|
+
*
|
|
191
|
+
* The residual window is therefore one statement issued immediately after the
|
|
192
|
+
* provider call returns — narrow, and not only reachable by a process death,
|
|
193
|
+
* which is why the caller logs its failure rather than treating it as
|
|
194
|
+
* impossible.
|
|
118
195
|
*/
|
|
119
|
-
|
|
196
|
+
markPendingCheckPosted(orgId: string, heldRunIds: readonly string[]): Promise<void>;
|
|
120
197
|
/** INSERT one decision row using the given executor (root or transaction). */
|
|
121
198
|
private insertDecisionRow;
|
|
122
199
|
/** Flip a pending hold to 'approved' using the given executor. Undefined if not pending. */
|
|
@@ -190,20 +267,49 @@ export declare class HeldRunStore {
|
|
|
190
267
|
*/
|
|
191
268
|
listOverdue(): Promise<HeldRun[]>;
|
|
192
269
|
/**
|
|
193
|
-
* Release overdue
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* `
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
270
|
+
* Release overdue timer holds at ANY scope. A wait action pauses its element
|
|
271
|
+
* as a held run; on timer expiry it must RESUME (not fail like a reviewer-hold
|
|
272
|
+
* expiry). Flips each overdue pending timer row to `released` and returns a
|
|
273
|
+
* `ReleaseSignal` per row, carrying the row's own scope so the caller can
|
|
274
|
+
* route it — `routeRelease` sends a workflow-scoped one to the install-gate
|
|
275
|
+
* rebuild and a job-scoped one to the job re-dispatch path.
|
|
276
|
+
*
|
|
277
|
+
* Runs BEFORE `expireOverdue()` so these rows leave the pending pool before
|
|
278
|
+
* the expire-and-fail sweep sees them. That ordering is load-bearing, not
|
|
279
|
+
* incidental: `expireOverdue` is not scope-filtered, so a released-but-not-yet-
|
|
280
|
+
* resumed row would otherwise be expired out from under its resume.
|
|
200
281
|
*
|
|
201
|
-
* The filter matches every persisted spelling of the timer hold type, so a
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
282
|
+
* The filter matches every persisted spelling of the timer hold type, so a row
|
|
283
|
+
* an un-upgraded orchestrator wrote as `wait_timer` still resumes rather than
|
|
284
|
+
* falling through to the expire-and-fail sweep. It deliberately does NOT
|
|
285
|
+
* filter on `hold_scope`: a job-scoped timer hold used to be excluded here,
|
|
286
|
+
* which left it with no release path at all — created, never released,
|
|
287
|
+
* eventually expired, its job never dispatched.
|
|
205
288
|
*/
|
|
206
289
|
releaseDueWaitHolds(): Promise<ReleaseSignal[]>;
|
|
290
|
+
/**
|
|
291
|
+
* Every pending queued (concurrency) hold, with the org and concurrency group
|
|
292
|
+
* it belongs to — the input to the periodic release sweep, which needs to know
|
|
293
|
+
* WHICH groups have someone waiting before it looks up any limits.
|
|
294
|
+
*
|
|
295
|
+
* Returns the pair rather than full rows: the sweep only groups by it, and
|
|
296
|
+
* `listQueuedHoldsForContext` fetches the rows it actually releases.
|
|
297
|
+
*/
|
|
298
|
+
listAllQueuedHolds(): Promise<Array<{
|
|
299
|
+
orgId: string | null;
|
|
300
|
+
concurrencyGroup: string | null;
|
|
301
|
+
}>>;
|
|
302
|
+
/**
|
|
303
|
+
* List the pending queued (concurrency) holds for one context's concurrency
|
|
304
|
+
* group, oldest first.
|
|
305
|
+
*
|
|
306
|
+
* Joined to `execution_runs` and filtered by `customer_id` as well as
|
|
307
|
+
* `context`: a context NAME is not unique across tenants, so without the org
|
|
308
|
+
* predicate one org's completing job could release another org's queued hold.
|
|
309
|
+
* Oldest-first is the release order — a queue that released newest-first would
|
|
310
|
+
* starve whoever waited longest.
|
|
311
|
+
*/
|
|
312
|
+
listQueuedHoldsForContext(orgId: string, concurrencyGroup: string): Promise<HeldRun[]>;
|
|
207
313
|
/**
|
|
208
314
|
* Expire overdue pending runs. Called by the stale detector.
|
|
209
315
|
* Sets status to 'expired' and resolved_at to now() for all
|
|
@@ -224,4 +330,5 @@ export declare function createHeldRunStoreFromUrl(databaseUrl: string, opts?: {
|
|
|
224
330
|
store: HeldRunStore;
|
|
225
331
|
dispose: () => Promise<void>;
|
|
226
332
|
};
|
|
333
|
+
export {};
|
|
227
334
|
//# sourceMappingURL=held-runs.d.ts.map
|