@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
|
@@ -36,15 +36,14 @@ import type { AgentRegistry } from '../agent/registry.js';
|
|
|
36
36
|
import type { HostRosterStore } from '../agent/host-roster.js';
|
|
37
37
|
import type { RunCoordinator } from '../cluster/coordinator.js';
|
|
38
38
|
import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
|
|
39
|
-
import type { TeamMembershipLookup } from '../approvals/team-membership-lookup.js';
|
|
40
39
|
import type { LogStorage } from '../reporting/log-storage.js';
|
|
41
40
|
import type { LogWriter } from '../reporting/log-writer.js';
|
|
42
41
|
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
43
|
-
import type { ContributorCache } from '../security/contributor-cache.js';
|
|
44
42
|
import type { AccessLogWriter } from '../audit/access-log.js';
|
|
45
43
|
import type { LockFile as FullLockFile, LockWorkflow, SimulatedEvent, WebhookNormalizer } from '@kici-dev/engine';
|
|
46
44
|
import { LockFileParseError } from '@kici-dev/engine';
|
|
47
45
|
import type { EventRouter } from '../events/event-router.js';
|
|
46
|
+
import type { InvokeGateDeps, InvokeGateParams } from './invoke-gate.js';
|
|
48
47
|
import type { RegistrationStore } from '../registration/registration-store.js';
|
|
49
48
|
import type { RegistrationIndex } from '../registration/registration-index.js';
|
|
50
49
|
import type { CronScheduler } from '../cron/cron-scheduler.js';
|
|
@@ -54,10 +53,12 @@ import { EventLogSource } from '@kici-dev/engine';
|
|
|
54
53
|
import type { LockJob } from '@kici-dev/engine';
|
|
55
54
|
import type { ContextStore } from '../contexts/context-store.js';
|
|
56
55
|
import type { VariableStore } from '../contexts/variable-store.js';
|
|
57
|
-
import type {
|
|
56
|
+
import type { IdentityLink, PermissionLevel } from '../security/identity-link.js';
|
|
58
57
|
import { type HeldRunStore } from '../contexts/held-runs.js';
|
|
58
|
+
import { type Context as EngineContext } from '@kici-dev/engine';
|
|
59
59
|
import type { OrchestratorMode, WorkflowDecision } from '@kici-dev/engine';
|
|
60
60
|
import type { TrustPolicyStore } from '../security/trust-policy-store.js';
|
|
61
|
+
import type { TrustDirectoryStore } from '../security/trust-directory-store.js';
|
|
61
62
|
/**
|
|
62
63
|
* Pending dispatch context for jobs gated by the needs scheduler.
|
|
63
64
|
* Keyed by `${runId}:${jobName}`. Populated at processWebhook time for all
|
|
@@ -69,6 +70,12 @@ import type { TrustPolicyStore } from '../security/trust-policy-store.js';
|
|
|
69
70
|
interface PendingJobContext {
|
|
70
71
|
jobInput: QueuedJobInput;
|
|
71
72
|
runsOnLabels: string[];
|
|
73
|
+
/**
|
|
74
|
+
* When set, this pending job is an invoke gate: on release it summons the
|
|
75
|
+
* source repo's subscribers (`runInvokeGate`) instead of dispatching `jobInput`
|
|
76
|
+
* to an agent.
|
|
77
|
+
*/
|
|
78
|
+
invoke?: InvokeGateParams;
|
|
72
79
|
}
|
|
73
80
|
/**
|
|
74
81
|
* Register an eval gate and return a promise that resolves when the scheduler
|
|
@@ -242,8 +249,21 @@ export declare function extractInboundRepoIdentifier(payload: unknown): string |
|
|
|
242
249
|
export declare function extractCommitMessage(event: string, payload: unknown): string | undefined;
|
|
243
250
|
/**
|
|
244
251
|
* Build a human-readable summary for a security hold check.
|
|
252
|
+
*
|
|
253
|
+
* `tier` is the resolved trust tier, or undefined when trust never resolved;
|
|
254
|
+
* the summary displays `unknown` for the latter. The caller passes the resolved
|
|
255
|
+
* value rather than forging `unknown` for an absent one, so this summary and
|
|
256
|
+
* `buildReducedPrivilegeNote` (`../security/reduced-privilege-note.ts`) cannot
|
|
257
|
+
* disagree about which case a run is in.
|
|
258
|
+
*
|
|
259
|
+
* Carries no reduced-privilege note of its own: the note is appended by the
|
|
260
|
+
* call site, because the two summaries this builds are read on checks with
|
|
261
|
+
* different fates. The trust-policy HOLD stores a resume context, so
|
|
262
|
+
* `/kici approve` replays its dispatch and its call site
|
|
263
|
+
* (`holdRunForSecurityPolicy`) appends the note; the trust-policy REJECTION
|
|
264
|
+
* never runs, so `buildSecurityRejectionSummary`'s call site appends nothing.
|
|
245
265
|
*/
|
|
246
|
-
export declare function buildSecurityHoldSummary(reason: string, tier: string, contributorUsername?: string): string;
|
|
266
|
+
export declare function buildSecurityHoldSummary(reason: string, tier: string | undefined, contributorUsername?: string): string;
|
|
247
267
|
/**
|
|
248
268
|
* Build the failure check-run description for a run the org trust policy
|
|
249
269
|
* REJECTED.
|
|
@@ -252,8 +272,13 @@ export declare function buildSecurityHoldSummary(reason: string, tier: string, c
|
|
|
252
272
|
* `held_runs` row, so telling the contributor to seek "approval from a user
|
|
253
273
|
* with ci_trust:write or higher" points at a queue the run will never appear
|
|
254
274
|
* in. Only an org policy change can unblock it.
|
|
275
|
+
*
|
|
276
|
+
* Carries no reduced-privilege note for the same reason: the summary's own next
|
|
277
|
+
* line says the run cannot be approved, so a posture note beside it would read
|
|
278
|
+
* as "it ran with reduced privileges and that is why it failed" for a run that
|
|
279
|
+
* never dispatched at all.
|
|
255
280
|
*/
|
|
256
|
-
export declare function buildSecurityRejectionSummary(reason: string, message: string, tier: string, contributorUsername?: string): string;
|
|
281
|
+
export declare function buildSecurityRejectionSummary(reason: string, message: string, tier: string | undefined, contributorUsername?: string): string;
|
|
257
282
|
/**
|
|
258
283
|
* Build the pending check-run description for a job/workflow approval hold,
|
|
259
284
|
* naming the clauses an approver must satisfy. `{team:X}` renders as
|
|
@@ -265,6 +290,19 @@ export declare function summarizeApprovalClauses(clauses: ReadonlyArray<{
|
|
|
265
290
|
} | {
|
|
266
291
|
user: string;
|
|
267
292
|
}>): string;
|
|
293
|
+
/**
|
|
294
|
+
* The line appended to an approval hold's check description when a security
|
|
295
|
+
* trust hold gates the SAME job.
|
|
296
|
+
*
|
|
297
|
+
* Both holds must be answered before the job runs, and the check run carries one
|
|
298
|
+
* description. Without this line the check names only the approval clauses — so
|
|
299
|
+
* the named approver approves, nothing runs, and the text does not change: a
|
|
300
|
+
* contributor is left with a satisfied requirement, no statement of what is
|
|
301
|
+
* still outstanding, and no idea that a different permission clears it. Naming
|
|
302
|
+
* the second gate and how it is released is the minimum that makes the check
|
|
303
|
+
* honest.
|
|
304
|
+
*/
|
|
305
|
+
export declare const SECURITY_HOLD_ALSO_GATES_NOTE: string;
|
|
268
306
|
/**
|
|
269
307
|
* Dependencies for the processing pipeline.
|
|
270
308
|
* All injected for testability. Fully provider-agnostic.
|
|
@@ -356,6 +394,12 @@ export interface ProcessingDeps {
|
|
|
356
394
|
} | undefined>) => void;
|
|
357
395
|
/** Event router for registering lock file event subscriptions. Optional -- if not set, event routing is inactive. */
|
|
358
396
|
eventRouter?: EventRouter;
|
|
397
|
+
/**
|
|
398
|
+
* Invoke-gate dependencies (summon callback + chain-depth bound). Optional --
|
|
399
|
+
* when absent an invoke gate cannot summon (it fails loudly rather than
|
|
400
|
+
* silently reaching an agent). Built at the composition root.
|
|
401
|
+
*/
|
|
402
|
+
invokeGateDeps?: InvokeGateDeps;
|
|
359
403
|
/** Registration store for persisting workflow registrations. Optional -- if not set, registration is skipped. */
|
|
360
404
|
registrationStore?: RegistrationStore;
|
|
361
405
|
/** Registration index for in-memory lookup. Optional -- if not set, registration is skipped. */
|
|
@@ -381,8 +425,6 @@ export interface ProcessingDeps {
|
|
|
381
425
|
variableStore?: VariableStore;
|
|
382
426
|
/** Held run store for persisting protection rule holds. Optional -- if not set, holds are not persisted. */
|
|
383
427
|
heldRunStore?: HeldRunStore;
|
|
384
|
-
/** Trust resolver for determining contributor trust tiers. Optional -- if not set, trust resolution is skipped. */
|
|
385
|
-
trustResolver?: TrustResolver;
|
|
386
428
|
/**
|
|
387
429
|
* Cache of the Platform-owned org trust policy. Read per PR event by the
|
|
388
430
|
* trust-policy gate. Optional so existing tests and independent deployments
|
|
@@ -397,16 +439,26 @@ export interface ProcessingDeps {
|
|
|
397
439
|
* so a hand-built deps object never accidentally opens the gate.
|
|
398
440
|
*/
|
|
399
441
|
orchestratorMode?: OrchestratorMode;
|
|
400
|
-
/** Identity links pushed from Platform
|
|
442
|
+
/** Identity links pushed from Platform, read by the comment-approval path. Optional -- defaults to empty. */
|
|
401
443
|
identityLinks?: IdentityLink[];
|
|
402
444
|
/** ci_trust permission levels per user ID from Platform push. Optional -- defaults to empty. */
|
|
403
445
|
orgMemberPermissions?: Map<string, PermissionLevel>;
|
|
404
446
|
/**
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
447
|
+
* Persisted approval directory, read at `/kici approve` time when neither
|
|
448
|
+
* `identityLinks` nor `orgMemberPermissions` was supplied.
|
|
449
|
+
*
|
|
450
|
+
* The two fields above are the Platform-push path: `server.ts` keeps them in
|
|
451
|
+
* memory and refreshes them on every `trust_policy.update`, so it always
|
|
452
|
+
* supplies both and this store is never consulted there. Every other
|
|
453
|
+
* assembly of these deps — the direct-ingress pipeline in `app.ts`, which is
|
|
454
|
+
* the ONLY one an independent orchestrator has — supplies neither, and
|
|
455
|
+
* without this store its approval path would resolve an empty directory and
|
|
456
|
+
* refuse every commenter forever.
|
|
457
|
+
*
|
|
458
|
+
* Optional so a hand-built deps object keeps working; the read is skipped
|
|
459
|
+
* when it is absent.
|
|
408
460
|
*/
|
|
409
|
-
|
|
461
|
+
trustDirectoryStore?: TrustDirectoryStore;
|
|
410
462
|
/** Global workflow policy for org-level permission enforcement. Optional -- if not set, global workflows are unrestricted. */
|
|
411
463
|
globalWorkflowPolicy?: GlobalWorkflowPolicy;
|
|
412
464
|
/** Inbound webhook delivery log writer. Optional -- if not set, deliveries are not persisted to event_log. */
|
|
@@ -415,11 +467,6 @@ export interface ProcessingDeps {
|
|
|
415
467
|
* Used by the eventLog writer to populate the source column. Defaults to
|
|
416
468
|
* 'direct' when omitted (independent / direct paths). */
|
|
417
469
|
eventLogSource?: EventLogSource;
|
|
418
|
-
/** Contributor permission cache. Optional -- if not set, membership-webhook
|
|
419
|
-
* invalidations silently no-op. In platform/hybrid mode the singleton is
|
|
420
|
-
* created in server.ts and threaded through both the Platform-relay WS
|
|
421
|
-
* path and the generic webhook HTTP path. */
|
|
422
|
-
contributorCache?: ContributorCache;
|
|
423
470
|
/** Access-log writer for the orchestrator audit stream. Optional -- if not
|
|
424
471
|
* set, hold-creation audit rows (`held_run.request`) are skipped. */
|
|
425
472
|
accessLogWriter?: AccessLogWriter;
|
|
@@ -443,6 +490,120 @@ export interface ProcessingDeps {
|
|
|
443
490
|
*/
|
|
444
491
|
export declare function anyTriggerHasPathPatterns(workflows: LockWorkflow[]): boolean;
|
|
445
492
|
export { processWebhook } from './process-webhook.js';
|
|
493
|
+
/** How many times {@link hasPendingHold} reads before it gives up and refuses. */
|
|
494
|
+
export declare const PENDING_HOLD_READ_ATTEMPTS = 3;
|
|
495
|
+
/** Backoff between {@link hasPendingHold} attempts, multiplied by the attempt number. */
|
|
496
|
+
export declare const PENDING_HOLD_RETRY_BASE_MS = 25;
|
|
497
|
+
/**
|
|
498
|
+
* Is there a still-pending hold for this (run, job)?
|
|
499
|
+
*
|
|
500
|
+
* `held_runs.job_id` carries the expanded job NAME for job-scoped holds, which
|
|
501
|
+
* is the same key the pending dispatch context uses. A missing row, or any
|
|
502
|
+
* non-pending status, means nothing is gating the job.
|
|
503
|
+
*
|
|
504
|
+
* **Fails CLOSED** once the read has genuinely failed: a job whose hold state
|
|
505
|
+
* cannot be read is treated as held and left for the release path. This gate is
|
|
506
|
+
* the enforcement point for "every requirement answered" on a job carrying two
|
|
507
|
+
* holds — the reviewer row and the security row are written together and BOTH
|
|
508
|
+
* must leave `pending` before dispatch — so answering `false` on a read error
|
|
509
|
+
* dispatched a job with neither hold released and the approval boundary
|
|
510
|
+
* bypassed entirely. That is unrecoverable; the failure in the other direction
|
|
511
|
+
* is not.
|
|
512
|
+
*
|
|
513
|
+
* A refusal leaves the job pending rather than losing it: the context is not
|
|
514
|
+
* consumed (the check runs before the consume), every release path re-drives
|
|
515
|
+
* `dispatchReadyJob`, and the needs-scheduler recovery loop on the next start
|
|
516
|
+
* recomputes `needs_satisfied` for every non-terminal run and re-fires the
|
|
517
|
+
* ready jobs. So the worst case of a closed failure is a delay, against a
|
|
518
|
+
* silently bypassed approval for an open one.
|
|
519
|
+
*
|
|
520
|
+
* The retries are what keep that trade cheap. The realistic error here is a
|
|
521
|
+
* transient one — a deadlock, a statement timeout, a lost connection — and a
|
|
522
|
+
* single blip must not park a job until the next restart, so the read is
|
|
523
|
+
* attempted {@link PENDING_HOLD_READ_ATTEMPTS} times with a short linear
|
|
524
|
+
* backoff before the refusal stands.
|
|
525
|
+
*/
|
|
526
|
+
export declare function hasPendingHold(db: Kysely<Database>, runId: string, jobName: string, opts?: {
|
|
527
|
+
attempts?: number;
|
|
528
|
+
retryBaseMs?: number;
|
|
529
|
+
}): Promise<boolean>;
|
|
530
|
+
/** Read attempts for the needs verdict, mirroring {@link PENDING_HOLD_READ_ATTEMPTS}. */
|
|
531
|
+
export declare const NEEDS_READ_ATTEMPTS = 3;
|
|
532
|
+
/** Linear backoff base between needs-verdict read attempts. */
|
|
533
|
+
export declare const NEEDS_RETRY_BASE_MS = 25;
|
|
534
|
+
/**
|
|
535
|
+
* Read whether a job's `needs` upstreams are satisfied, for the dispatch guard.
|
|
536
|
+
*
|
|
537
|
+
* Fails CLOSED, exactly as {@link hasPendingHold} does and for the same reason:
|
|
538
|
+
* an unreadable verdict must never be read as permission to dispatch. A closed
|
|
539
|
+
* failure returns `{ satisfied: false }`, which the guard treats as "upstream
|
|
540
|
+
* still pending" — the recoverable branch, since the start-up recovery loop
|
|
541
|
+
* recomputes `needs_satisfied` for every non-terminal run and re-fires its ready
|
|
542
|
+
* jobs, and the stale-run expiry sweep is the backstop.
|
|
543
|
+
*
|
|
544
|
+
* Reads only. It never writes `needs_satisfied` — the scheduler owns that claim.
|
|
545
|
+
*/
|
|
546
|
+
export declare function readNeedsVerdict(db: Kysely<Database>, runId: string, jobName: string, opts?: {
|
|
547
|
+
attempts?: number;
|
|
548
|
+
retryBaseMs?: number;
|
|
549
|
+
}): Promise<{
|
|
550
|
+
satisfied: boolean;
|
|
551
|
+
action?: 'dispatch' | 'skip';
|
|
552
|
+
reason?: string;
|
|
553
|
+
}>;
|
|
554
|
+
/**
|
|
555
|
+
* The subset of a context row the ready-dispatch re-gate reads.
|
|
556
|
+
*
|
|
557
|
+
* `ContextStore.matchContext` returns the raw `contexts` row, so the field names
|
|
558
|
+
* are snake_case. `concurrency_strategy` and `hold_expiry_seconds` are optional
|
|
559
|
+
* because the release-path callers already in the tree narrow their closure to
|
|
560
|
+
* `concurrency_limit` alone; an absent strategy resolves to
|
|
561
|
+
* {@link DEFAULT_CONCURRENCY_STRATEGY} and an absent hold window to
|
|
562
|
+
* {@link DEFAULT_HOLD_EXPIRY_SECONDS}, which are also the columns' own defaults.
|
|
563
|
+
*/
|
|
564
|
+
export interface ReadyDispatchContextRow {
|
|
565
|
+
id: string;
|
|
566
|
+
concurrency_limit: number | null;
|
|
567
|
+
concurrency_strategy?: string | null;
|
|
568
|
+
hold_expiry_seconds?: number | null;
|
|
569
|
+
}
|
|
570
|
+
/** Inputs the ready-dispatch concurrency re-gate needs. Absent = no re-gate. */
|
|
571
|
+
export interface ReadyDispatchGateDeps {
|
|
572
|
+
matchContext: (orgId: string, name: string) => Promise<ReadyDispatchContextRow | null>;
|
|
573
|
+
heldRunStore: Pick<HeldRunStore, 'create'>;
|
|
574
|
+
/**
|
|
575
|
+
* Audits each re-hold, mirroring the `held_run.request` row the dispatch-pass
|
|
576
|
+
* path writes for every hold it mints. Optional: a call site with no writer
|
|
577
|
+
* still gates, exactly as the dispatch path's own `accessLogWriter?.record`
|
|
578
|
+
* degrades.
|
|
579
|
+
*/
|
|
580
|
+
accessLogWriter?: Pick<AccessLogWriter, 'record'>;
|
|
581
|
+
/** The routing key the audit row is attributed to, when the call site knows it. */
|
|
582
|
+
routingKey?: string | null;
|
|
583
|
+
}
|
|
584
|
+
/** What {@link resolveRunConcurrency} needs to evaluate the concurrency gate. */
|
|
585
|
+
export interface RunConcurrency {
|
|
586
|
+
orgId: string;
|
|
587
|
+
group: string;
|
|
588
|
+
contextId: string;
|
|
589
|
+
limit: number | null;
|
|
590
|
+
strategy: EngineContext['concurrencyStrategy'];
|
|
591
|
+
/**
|
|
592
|
+
* The context's own hold window, seconds. A queued hold this gate mints must
|
|
593
|
+
* expire on the same schedule as one the dispatch pass mints for the same
|
|
594
|
+
* context, or an operator's configured queue timeout applies to one path and
|
|
595
|
+
* not the other.
|
|
596
|
+
*/
|
|
597
|
+
holdExpirySeconds: number;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Resolve a run's bound context to the inputs the concurrency gate needs.
|
|
601
|
+
*
|
|
602
|
+
* `null` means no concurrency constraint applies — the run has no bound context,
|
|
603
|
+
* or the context it names no longer exists. That is the common case, so the cost
|
|
604
|
+
* on the ready-dispatch path is one indexed lookup by `run_id`.
|
|
605
|
+
*/
|
|
606
|
+
export declare function resolveRunConcurrency(db: Kysely<Database>, matchContext: ReadyDispatchGateDeps['matchContext'], runId: string): Promise<RunConcurrency | null>;
|
|
446
607
|
/**
|
|
447
608
|
* Dispatch a job that has become ready via the needs scheduler.
|
|
448
609
|
*
|
|
@@ -451,20 +612,64 @@ export { processWebhook } from './process-webhook.js';
|
|
|
451
612
|
* dispatches the job through the normal dispatcher path, and updates the
|
|
452
613
|
* execution tracker with the real job ID.
|
|
453
614
|
*/
|
|
454
|
-
export declare function dispatchReadyJob(runId: string, jobName: string, dispatcher: Dispatcher, executionTracker?: ExecutionTracker, coordinator?: RunCoordinator, db?: Kysely<Database
|
|
615
|
+
export declare function dispatchReadyJob(runId: string, jobName: string, dispatcher: Dispatcher, executionTracker?: ExecutionTracker, coordinator?: RunCoordinator, db?: Kysely<Database>, invokeGateDeps?: InvokeGateDeps, gateDeps?: ReadyDispatchGateDeps): Promise<void>;
|
|
455
616
|
/**
|
|
456
|
-
*
|
|
457
|
-
* Used to trigger registration extraction for workflow event subscriptions.
|
|
617
|
+
* Read the repository's default branch out of a webhook payload.
|
|
458
618
|
*
|
|
459
|
-
* Resolution order
|
|
619
|
+
* Resolution order:
|
|
460
620
|
* 1. `normalizer.extractDefaultBranch?(payload)` — provider-specific hook
|
|
461
621
|
* (universal-git reads a JSONPath from the source's `payloadPaths.defaultBranch`).
|
|
462
622
|
* 2. Fallback to `payload.repository.default_branch` — the GitHub-shaped
|
|
463
623
|
* default that most forges mirror.
|
|
624
|
+
*
|
|
625
|
+
* `null` when neither source names one. Shared by `isDefaultBranchPush` (which
|
|
626
|
+
* compares it against the pushed branch) and the registration write path (which
|
|
627
|
+
* persists it, so a scheduled run can present it as its own branch).
|
|
628
|
+
*/
|
|
629
|
+
export declare function extractDefaultBranch(payload: Record<string, unknown>, normalizer: WebhookNormalizer): string | null;
|
|
630
|
+
/**
|
|
631
|
+
* Check whether a webhook event is a push to the repository's default branch.
|
|
632
|
+
* Used to trigger registration extraction for workflow event subscriptions.
|
|
464
633
|
*/
|
|
465
634
|
export declare function isDefaultBranchPush(info: WebhookInfo, event: SimulatedEvent, payload: Record<string, unknown>, normalizer: WebhookNormalizer): boolean;
|
|
635
|
+
/**
|
|
636
|
+
* Cap on the per-decision trace forwarded to the Platform.
|
|
637
|
+
*
|
|
638
|
+
* A fixed bound on a debug payload, not a behavior an operator tunes: the
|
|
639
|
+
* summary rides an `execution.event` and lands in a stored row, so an
|
|
640
|
+
* essay-length trace from a workflow with hundreds of triggers must not be able
|
|
641
|
+
* to grow either without limit.
|
|
642
|
+
*/
|
|
643
|
+
export declare const DECISION_TRACE_MAX_CHECKS = 50;
|
|
466
644
|
/**
|
|
467
645
|
* Create a serializable summary of a workflow decision for Platform forwarding.
|
|
646
|
+
*
|
|
647
|
+
* Carries the individual checks, capped, so the dashboard can answer "why did
|
|
648
|
+
* this workflow not fire" from the delivery alone. `checksCount` stays the
|
|
649
|
+
* untruncated total, and `checksTruncated` marks a trace the cap shortened.
|
|
468
650
|
*/
|
|
469
651
|
export declare function summarizeDecision(decision: WorkflowDecision): Record<string, unknown>;
|
|
652
|
+
/**
|
|
653
|
+
* Byte budget for the whole forwarded trace, across every workflow on the
|
|
654
|
+
* delivery.
|
|
655
|
+
*
|
|
656
|
+
* The per-decision check cap and the per-field text clamp bound one entry; this
|
|
657
|
+
* bounds the frame. One event is evaluated against every workflow in the lock
|
|
658
|
+
* file plus every organization-wide registration, so a repository with a
|
|
659
|
+
* hundred comment-triggered workflows multiplies a bounded entry into an
|
|
660
|
+
* unbounded message. A frame past the Platform's WebSocket payload ceiling
|
|
661
|
+
* closes the orchestrator's connection, stalling every delivery for that
|
|
662
|
+
* organization until it reconnects — so the budget sits well under the
|
|
663
|
+
* Platform's own storage guard, which is then a backstop rather than the only
|
|
664
|
+
* limit.
|
|
665
|
+
*/
|
|
666
|
+
export declare const DECISION_TRACE_MAX_BYTES = 131072;
|
|
667
|
+
/**
|
|
668
|
+
* Bound the serialized trace, replacing whatever did not fit with a marker.
|
|
669
|
+
*
|
|
670
|
+
* Truncating rather than dropping keeps the delivery's answer to "why did my
|
|
671
|
+
* workflow not fire" partially readable, and says out loud that the rest was
|
|
672
|
+
* dropped.
|
|
673
|
+
*/
|
|
674
|
+
export declare function capDecisionSummaries(summaries: readonly Record<string, unknown>[], maxBytes?: number): Record<string, unknown>[];
|
|
470
675
|
//# sourceMappingURL=processor.d.ts.map
|
package/dist/pipeline/rerun.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ import type { SourceCache } from '../cache/index.js';
|
|
|
27
27
|
import type { BuildCoordinator } from '../cache/index.js';
|
|
28
28
|
import type { DepCache } from '../cache/index.js';
|
|
29
29
|
import type { PendingBuildTracker } from '../cache/index.js';
|
|
30
|
+
import type { ProcessingDeps } from './processor.js';
|
|
30
31
|
/**
|
|
31
32
|
* Thrown when a rerun is attempted on a run whose row is absent from PG AND
|
|
32
33
|
* the cold-store replay path failed or is unavailable (chunk missing,
|
|
@@ -65,6 +66,17 @@ export interface RerunDeps {
|
|
|
65
66
|
* cold-store wired up.
|
|
66
67
|
*/
|
|
67
68
|
coldStore: ColdStore | null;
|
|
69
|
+
/**
|
|
70
|
+
* The live webhook-processing bag, assembled on demand.
|
|
71
|
+
*
|
|
72
|
+
* Only the re-run of a failed global evaluation round needs it: that re-run
|
|
73
|
+
* re-drives the organization-wide pass, which reaches deps an ordinary
|
|
74
|
+
* workflow re-run never touches (the registration index, the policy reader,
|
|
75
|
+
* the pending-eval tracker). Supplied by the entry point that already
|
|
76
|
+
* assembles the bag for the inbound webhook path, so the two cannot drift.
|
|
77
|
+
* Absent means round re-runs are not available on this deployment.
|
|
78
|
+
*/
|
|
79
|
+
processingDeps?: (() => ProcessingDeps) | null;
|
|
68
80
|
}
|
|
69
81
|
export declare function handleRerun(originalRunId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null, deps: RerunDeps,
|
|
70
82
|
/**
|
|
@@ -83,4 +95,13 @@ requestId: string,
|
|
|
83
95
|
routingKeyHint?: string): Promise<{
|
|
84
96
|
newRunId: string;
|
|
85
97
|
}>;
|
|
98
|
+
/**
|
|
99
|
+
* Settle every detached re-evaluation started so far.
|
|
100
|
+
*
|
|
101
|
+
* Nothing in a running orchestrator calls this — the re-evaluation is detached
|
|
102
|
+
* precisely so no request waits on it. It exists so a caller that needs the
|
|
103
|
+
* work to have finished (a test asserting on the pass) can wait for it
|
|
104
|
+
* deterministically instead of racing the microtask queue.
|
|
105
|
+
*/
|
|
106
|
+
export declare function settlePendingRoundReevaluations(): Promise<void>;
|
|
86
107
|
//# sourceMappingURL=rerun.d.ts.map
|
|
@@ -4,23 +4,56 @@ import type { ProcessingDeps } from './processor.js';
|
|
|
4
4
|
import type { ReleaseSignal } from '../contexts/held-runs.js';
|
|
5
5
|
import { type WorkflowDispatchContext } from './dispatch-matched-workflow.js';
|
|
6
6
|
import { type SerializableWorkflowDispatchInputs } from './pending-workflow-context.js';
|
|
7
|
+
import { type SecurityCheckHold } from './security-hold-check.js';
|
|
7
8
|
/**
|
|
8
9
|
* Rebuild a live `WorkflowDispatchContext` from the persisted serializable
|
|
9
10
|
* inputs by re-attaching the orchestrator's live `deps` and reconstructing the
|
|
10
11
|
* provider `bundle` from the live registry (keyed by the stored routing key).
|
|
11
12
|
* Returns null when the provider bundle can no longer be resolved.
|
|
13
|
+
*
|
|
14
|
+
* The key it looks the bundle up by is `effectiveRoutingKey ?? info.routingKey`
|
|
15
|
+
* — the post-overlay key, the same one `setupDispatchContext` builds
|
|
16
|
+
* `setup.info` from and the same one the run row records. `info.routingKey` is
|
|
17
|
+
* the INBOUND key and never carries the overlay, so on a cross-source or
|
|
18
|
+
* fallback-resolved dispatch it names the source the event arrived on rather
|
|
19
|
+
* than the source the workflow belongs to: the resumed run would get the wrong
|
|
20
|
+
* app, the wrong credentials, and a check poster writing to the wrong place.
|
|
12
21
|
*/
|
|
13
22
|
export declare function rebuildWorkflowDispatchContext(inputs: SerializableWorkflowDispatchInputs, deps: ProcessingDeps): WorkflowDispatchContext | null;
|
|
14
23
|
/**
|
|
15
|
-
* Resume a released workflow
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
24
|
+
* Resume a released workflow-scoped hold. Loads the pending context, rebuilds
|
|
25
|
+
* the dispatch context, and re-dispatches. On a lost pending context (or
|
|
26
|
+
* unresolvable provider bundle) the run is failed loudly rather than silently
|
|
27
|
+
* dropped.
|
|
19
28
|
*/
|
|
20
29
|
export declare function resumeWorkflow(signal: ReleaseSignal, deps: ProcessingDeps, db: Kysely<Database> | undefined): Promise<void>;
|
|
21
30
|
/**
|
|
22
|
-
* Cancel a rejected workflow
|
|
23
|
-
* the
|
|
31
|
+
* Cancel a rejected workflow-scoped hold (install gate or trust policy): mark
|
|
32
|
+
* the run cancelled, complete the check runs the dispatch already posted, and
|
|
33
|
+
* drop the pending context.
|
|
34
|
+
*
|
|
35
|
+
* The check runs are completed `cancelled`, matching the run row this writes —
|
|
36
|
+
* `cancelHeldRun` sets `execution_runs.status` to `cancelled` with failure class
|
|
37
|
+
* `cancelled`. Without this the workflow and per-job checks stay `queued` on the
|
|
38
|
+
* commit for a run that will never start; see `completeUndispatchedHoldChecks`.
|
|
39
|
+
* It runs before the delete because the pending context is what the check-run
|
|
40
|
+
* names are derived from.
|
|
41
|
+
*
|
|
42
|
+
* The hold row decides whether the `KiCI Security` check is terminalized too:
|
|
43
|
+
* the trust policy's PR-wide hold and the SDK's workflow-level `requireApproval`
|
|
44
|
+
* each posted one pending, while the install gate posted none and must not have
|
|
45
|
+
* one fabricated. That is `postedPendingSecurityCheck`'s job — see it for the
|
|
46
|
+
* per-shape derivation, and for why `queue_type` cannot make the distinction.
|
|
47
|
+
*
|
|
48
|
+
* This is the single writer of that check for a rejection, on both surfaces the
|
|
49
|
+
* shared applier serves: the dashboard / CLI / MCP reject reaches it through
|
|
50
|
+
* `applyDecision`'s `onWorkflowReject`, and `/kici reject` through the comment
|
|
51
|
+
* handler's. Both therefore render identically on the pull request, and the
|
|
52
|
+
* summary below is the one both check families carry. The returned boolean says
|
|
53
|
+
* whether a security check was actually WRITTEN, so a caller suppresses its own
|
|
54
|
+
* post on the strength of a write rather than of a delegate resolving —
|
|
55
|
+
* a rejection this declines to report (an install gate, or a commit whose other
|
|
56
|
+
* holds are still pending) leaves the caller free to decide for itself.
|
|
24
57
|
*/
|
|
25
|
-
export declare function rejectWorkflow(
|
|
58
|
+
export declare function rejectWorkflow(hold: SecurityCheckHold, deps: ProcessingDeps, db: Kysely<Database> | undefined, reason: string): Promise<boolean>;
|
|
26
59
|
//# sourceMappingURL=resume-workflow.d.ts.map
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { createLogger } from '@kici-dev/shared';
|
|
13
13
|
import type { LockJob, MaterializedJob } from '@kici-dev/engine';
|
|
14
|
+
import type { JobKind } from '../db/types.js';
|
|
14
15
|
import type { RunContext, RunCoordinator } from '../cluster/coordinator.js';
|
|
15
16
|
import type { Dispatcher } from '../agent/dispatcher.js';
|
|
16
17
|
import type { ExecutionTracker } from '../reporting/execution-tracker.js';
|
|
@@ -25,6 +26,14 @@ export interface DispatchedJobEntry {
|
|
|
25
26
|
* groups a wave's children by — a NULL there makes the wave gate bail.
|
|
26
27
|
*/
|
|
27
28
|
baseJobName?: string;
|
|
29
|
+
/**
|
|
30
|
+
* `gate` marks an invoke-gate row (runs the gate executor, never an agent).
|
|
31
|
+
* The global-workflow dispatch path registers a gate as a synthetic pending
|
|
32
|
+
* row and carries its kind through to `execution_jobs.job_kind`.
|
|
33
|
+
*/
|
|
34
|
+
jobKind?: JobKind;
|
|
35
|
+
/** For a gate job, its wall-clock timeout in ms (orchestrator-swept). */
|
|
36
|
+
timeoutMs?: number;
|
|
28
37
|
}
|
|
29
38
|
export interface RejectedJobEntry {
|
|
30
39
|
jobId: string;
|