@kici-dev/orchestrator 0.1.26 → 0.1.27

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.
Files changed (83) hide show
  1. package/dist/app.d.ts +13 -4
  2. package/dist/approvals/apply-decision.d.ts +1 -1
  3. package/dist/approvals/step-approval-bridge.d.ts +1 -1
  4. package/dist/cache/pending-inits.d.ts +4 -4
  5. package/dist/cli/api-client.d.ts +29 -4
  6. package/dist/cli/commands/context.d.ts +25 -0
  7. package/dist/cli/commands/debug-bundle.d.ts +1 -1
  8. package/dist/cli/commands/variable.d.ts +6 -6
  9. package/dist/cli/service/windows.d.ts +6 -0
  10. package/dist/cli.js +526 -340
  11. package/dist/config.d.ts +16 -0
  12. package/dist/contexts/binding-store.d.ts +36 -0
  13. package/dist/contexts/context-store.d.ts +87 -0
  14. package/dist/{environments → contexts}/held-runs.d.ts +15 -15
  15. package/dist/{environments → contexts}/index.d.ts +1 -1
  16. package/dist/contexts/protection/aggregate.d.ts +43 -0
  17. package/dist/contexts/protection/branch-gate.d.ts +5 -0
  18. package/dist/contexts/protection/concurrency-gate.d.ts +7 -0
  19. package/dist/{environments → contexts}/protection/pipeline.d.ts +3 -3
  20. package/dist/contexts/protection/reviewer-gate.d.ts +7 -0
  21. package/dist/{environments → contexts}/protection/satisfiability.d.ts +21 -21
  22. package/dist/contexts/protection/trust-gate.d.ts +7 -0
  23. package/dist/contexts/protection/wait-timer-gate.d.ts +7 -0
  24. package/dist/contexts/variable-store.d.ts +39 -0
  25. package/dist/dashboard/attestation-filters.d.ts +10 -8
  26. package/dist/dashboard/handler.d.ts +28 -2
  27. package/dist/db/migrations/067_environments_to_contexts.d.ts +36 -0
  28. package/dist/db/migrations/068_request_idempotency.d.ts +17 -0
  29. package/dist/db/types.d.ts +78 -59
  30. package/dist/github-app-name-refresher/github-app-name-refresher.d.ts +1 -1
  31. package/dist/index.js +21 -6
  32. package/dist/metrics/prometheus.d.ts +2 -0
  33. package/dist/oidc/id-token-claims.d.ts +63 -0
  34. package/dist/oidc/jwt.d.ts +15 -0
  35. package/dist/oidc/local-dev-signer.d.ts +31 -0
  36. package/dist/oidc/local-mint.d.ts +77 -0
  37. package/dist/oidc/oidc-mint-registration.d.ts +49 -0
  38. package/dist/pipeline/decorating-secret-resolver.d.ts +4 -4
  39. package/dist/pipeline/dispatch-matched-workflow.d.ts +3 -3
  40. package/dist/pipeline/inline-eval.d.ts +2 -2
  41. package/dist/pipeline/install-secrets-resolver.d.ts +13 -13
  42. package/dist/pipeline/{job-environments.d.ts → job-contexts.d.ts} +23 -23
  43. package/dist/pipeline/manual-schedule.d.ts +8 -1
  44. package/dist/pipeline/processor.d.ts +6 -6
  45. package/dist/pipeline/request-idempotency.d.ts +28 -0
  46. package/dist/pipeline/rerun.d.ts +8 -0
  47. package/dist/pipeline/resume-workflow.d.ts +1 -1
  48. package/dist/pipeline/test-pipeline.d.ts +2 -2
  49. package/dist/provenance/trust-root.d.ts +7 -0
  50. package/dist/provider-registry.d.ts +8 -0
  51. package/dist/providers/local/index.d.ts +1 -0
  52. package/dist/providers/local/local-source-config.d.ts +1 -0
  53. package/dist/reporting/execution-tracker.d.ts +17 -7
  54. package/dist/reporting/run-aggregator.d.ts +5 -5
  55. package/dist/routes/admin-contexts.d.ts +47 -0
  56. package/dist/routes/admin-registrations.d.ts +2 -2
  57. package/dist/routes/github-webhook.d.ts +2 -2
  58. package/dist/secrets/context-secret-resolver.d.ts +30 -0
  59. package/dist/secrets/index.d.ts +1 -1
  60. package/dist/secrets/pg-secret-store.d.ts +3 -3
  61. package/dist/secrets/rbac.d.ts +1 -1
  62. package/dist/secrets/secret-resolver.d.ts +19 -19
  63. package/dist/security/comment-handler.d.ts +1 -1
  64. package/dist/server.js +1770 -1196
  65. package/dist/stale-detector/stale-run-detector.d.ts +1 -1
  66. package/dist/standalone.js +1649 -747
  67. package/dist/storage/blob-routes.d.ts +16 -0
  68. package/dist/ws/{dashboard-env-handler.d.ts → dashboard-context-handler.d.ts} +15 -15
  69. package/dist/ws/oidc-token-relay.d.ts +6 -0
  70. package/installer-image-digests.json +3 -3
  71. package/package.json +4 -4
  72. package/sbom.spdx.json +50 -50
  73. package/dist/cli/commands/environment.d.ts +0 -25
  74. package/dist/environments/binding-store.d.ts +0 -36
  75. package/dist/environments/environment-store.d.ts +0 -87
  76. package/dist/environments/protection/aggregate.d.ts +0 -43
  77. package/dist/environments/protection/branch-gate.d.ts +0 -5
  78. package/dist/environments/protection/concurrency-gate.d.ts +0 -7
  79. package/dist/environments/protection/reviewer-gate.d.ts +0 -7
  80. package/dist/environments/protection/trust-gate.d.ts +0 -7
  81. package/dist/environments/protection/wait-timer-gate.d.ts +0 -7
  82. package/dist/environments/variable-store.d.ts +0 -39
  83. package/dist/routes/admin-environments.d.ts +0 -47
package/dist/config.d.ts CHANGED
@@ -75,6 +75,15 @@ declare const configSchema: z.ZodObject<{
75
75
  secretKeyOld: z.ZodOptional<z.ZodString>;
76
76
  secretKeyFileOld: z.ZodOptional<z.ZodString>;
77
77
  bootstrapAdminToken: z.ZodOptional<z.ZodString>;
78
+ independentSecrets: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
79
+ true: "true";
80
+ false: "false";
81
+ }>>, z.ZodTransform<boolean, "true" | "false">>;
82
+ independentIdentity: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
83
+ true: "true";
84
+ false: "false";
85
+ }>>, z.ZodTransform<boolean, "true" | "false">>;
86
+ devIdentityKeyFile: z.ZodOptional<z.ZodString>;
78
87
  pgCustomerSecrets: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
79
88
  true: "true";
80
89
  false: "false";
@@ -101,6 +110,8 @@ declare const configSchema: z.ZodObject<{
101
110
  testMode: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
102
111
  testEventFailFirstN: z.ZodOptional<z.ZodString>;
103
112
  testMintDeferAudience: z.ZodOptional<z.ZodString>;
113
+ testMintRejectAudience: z.ZodOptional<z.ZodString>;
114
+ testRerunDelayMs: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
104
115
  eventLogMaxPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
105
116
  logLevel: z.ZodDefault<z.ZodEnum<{
106
117
  error: "error";
@@ -203,6 +214,8 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
203
214
  staleDetectorThresholdMultiplier: number;
204
215
  jobHeartbeatIntervalMs: number;
205
216
  githubAppNameRefreshIntervalMs: number;
217
+ independentSecrets: boolean;
218
+ independentIdentity: boolean;
206
219
  pgCustomerSecrets: boolean;
207
220
  agentAuth: "token" | "none";
208
221
  agentTokenTtlMs: number;
@@ -273,9 +286,12 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
273
286
  secretKeyOld?: string | undefined;
274
287
  secretKeyFileOld?: string | undefined;
275
288
  bootstrapAdminToken?: string | undefined;
289
+ devIdentityKeyFile?: string | undefined;
276
290
  orchestratorHostAgentId?: string | undefined;
277
291
  testEventFailFirstN?: string | undefined;
278
292
  testMintDeferAudience?: string | undefined;
293
+ testMintRejectAudience?: string | undefined;
294
+ testRerunDelayMs?: number | undefined;
279
295
  otelExporterOtlpEndpoint?: string | undefined;
280
296
  clusterName?: string | undefined;
281
297
  }>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Binding store -- scope-to-context binding CRUD.
3
+ *
4
+ * Bindings map scope patterns (e.g. 'aws/prod/**') to contexts,
5
+ * controlling which scoped secrets are available in each context.
6
+ */
7
+ import type { Kysely } from 'kysely';
8
+ import type { Database, ContextBinding } from '../db/types.js';
9
+ /**
10
+ * A scope→context binding with its host selector. `hostPattern` defaults to
11
+ * `'**'` (all hosts) when omitted.
12
+ */
13
+ export interface BindingInput {
14
+ scopePattern: string;
15
+ hostPattern?: string;
16
+ }
17
+ /**
18
+ * Data access layer for context bindings.
19
+ */
20
+ export declare class BindingStore {
21
+ private readonly db;
22
+ constructor(db: Kysely<Database>);
23
+ /** List all bindings for an context. */
24
+ list(orgId: string, contextId: string): Promise<ContextBinding[]>;
25
+ /**
26
+ * Replace all bindings for an context in a transaction.
27
+ *
28
+ * Deletes existing bindings and inserts the new set atomically. Each binding
29
+ * carries a `scopePattern` and an optional `hostPattern` (defaulting to
30
+ * `'**'`). Pass an empty array to clear all bindings.
31
+ */
32
+ set(orgId: string, contextId: string, bindings: BindingInput[]): Promise<void>;
33
+ /** Find bindings for an context (alias for list, used by secret resolver). */
34
+ findBindingsForContext(orgId: string, contextId: string): Promise<ContextBinding[]>;
35
+ }
36
+ //# sourceMappingURL=binding-store.d.ts.map
@@ -0,0 +1,87 @@
1
+ import { type Kysely } from 'kysely';
2
+ import type { Context as EngineContext } from '@kici-dev/engine';
3
+ import type { Database, Context } from '../db/types.js';
4
+ /** Thrown by `delete` when pending held runs reference the context. */
5
+ export declare class ContextDeleteBlockedError extends Error {
6
+ readonly pendingCount: number;
7
+ constructor(pendingCount: number);
8
+ }
9
+ /**
10
+ * Map a DB context row (snake_case) to the engine Context type (camelCase).
11
+ *
12
+ * Kysely returns JSONB columns as strings; this function parses them into arrays.
13
+ */
14
+ export declare function toContext(row: Context): EngineContext;
15
+ /** Fields accepted when creating a context. */
16
+ export interface ContextCreateInput {
17
+ name: string;
18
+ type?: 'fixed' | 'glob';
19
+ globPattern?: string | null;
20
+ branchRestrictions?: string[];
21
+ triggerTypeFilters?: string[];
22
+ repoPatterns?: string[];
23
+ concurrencyLimit?: number | null;
24
+ concurrencyStrategy?: 'queue' | 'cancel-pending';
25
+ concurrencyTimeoutMs?: number;
26
+ requiredReviewers?: string[] | null;
27
+ waitTimerSeconds?: number | null;
28
+ holdExpirySeconds?: number;
29
+ minimumTrust?: 'known' | 'trusted' | null;
30
+ allowLocalExecution?: boolean;
31
+ enabled?: boolean;
32
+ createdBy?: string | null;
33
+ }
34
+ /** Fields accepted when updating a context. */
35
+ export interface ContextUpdateInput {
36
+ name?: string;
37
+ type?: 'fixed' | 'glob';
38
+ globPattern?: string | null;
39
+ branchRestrictions?: string[];
40
+ triggerTypeFilters?: string[];
41
+ repoPatterns?: string[];
42
+ concurrencyLimit?: number | null;
43
+ concurrencyStrategy?: 'queue' | 'cancel-pending';
44
+ concurrencyTimeoutMs?: number;
45
+ requiredReviewers?: string[] | null;
46
+ waitTimerSeconds?: number | null;
47
+ holdExpirySeconds?: number;
48
+ minimumTrust?: 'known' | 'trusted' | null;
49
+ allowLocalExecution?: boolean;
50
+ enabled?: boolean;
51
+ }
52
+ /**
53
+ * Data access layer for contexts.
54
+ */
55
+ export declare class ContextStore {
56
+ private readonly db;
57
+ constructor(db: Kysely<Database>);
58
+ /** List all contexts for an org, ordered by name. */
59
+ list(orgId: string): Promise<Context[]>;
60
+ /** Get a single context by org + id. Returns null if not found. */
61
+ get(orgId: string, id: string): Promise<Context | null>;
62
+ /** Get a single context by org + name. Returns null if not found. */
63
+ getByName(orgId: string, name: string): Promise<Context | null>;
64
+ /** Create a new context. Returns the created row. */
65
+ create(orgId: string, data: ContextCreateInput): Promise<Context>;
66
+ /** Update a context. Returns the updated row, or null if not found. */
67
+ update(orgId: string, id: string, updates: ContextUpdateInput): Promise<Context | null>;
68
+ /**
69
+ * Delete a context.
70
+ *
71
+ * Bindings, variables, and source overrides cascade away via their FK.
72
+ * Terminal held-run history survives with a null `context_id` (the FK
73
+ * uses ON DELETE SET NULL). Pending held runs still reference the
74
+ * context, so deletion is blocked with `ContextDeleteBlockedError`
75
+ * until they are approved or rejected.
76
+ */
77
+ delete(orgId: string, id: string): Promise<boolean>;
78
+ /**
79
+ * Match a context name against org contexts.
80
+ *
81
+ * First tries exact name match (for fixed contexts).
82
+ * If no exact match, scans glob-type contexts and uses picomatch.
83
+ * Returns the first matching context or null.
84
+ */
85
+ matchContext(orgId: string, name: string): Promise<Context | null>;
86
+ }
87
+ //# sourceMappingURL=context-store.d.ts.map
@@ -18,16 +18,16 @@ export declare enum HeldRunStatus {
18
18
  export interface CreateHeldRunData {
19
19
  runId: string;
20
20
  jobId: string;
21
- environmentId: string;
21
+ contextId: string;
22
22
  holdType: string;
23
23
  reason: string;
24
24
  expiresAt: Date;
25
- /** Queue type: 'environment' (default) or 'security'. */
26
- queueType?: 'environment' | 'security';
25
+ /** Queue type: 'context' (default) or 'security'. */
26
+ queueType?: 'context' | 'security';
27
27
  }
28
28
  /**
29
29
  * Data required to create a generalized approval hold. Unlike the legacy
30
- * environment-only `create()`, this carries the hold scope, trigger source,
30
+ * context-only `create()`, this carries the hold scope, trigger source,
31
31
  * optional step index, and the normalized approval requirement.
32
32
  */
33
33
  export interface CreateHoldData {
@@ -37,14 +37,14 @@ export interface CreateHoldData {
37
37
  scope: HoldScope;
38
38
  /** Step index within the job for step-scoped holds; omit otherwise. */
39
39
  stepIndex?: number;
40
- /** What triggered the hold (environment policy vs SDK requireApproval). */
40
+ /** What triggered the hold (context policy vs SDK requireApproval). */
41
41
  triggerSource: TriggerSource;
42
42
  /** The normalized requirement the hold must satisfy. */
43
43
  requirement: ApprovalRequirement;
44
- /** Environment id, when the hold originates from an environment policy. */
45
- environmentId?: string | null;
46
- /** Queue type: 'environment' (default) or 'security'. */
47
- queueType?: 'environment' | 'security';
44
+ /** Context id, when the hold originates from a context policy. */
45
+ contextId?: string | null;
46
+ /** Queue type: 'context' (default) or 'security'. */
47
+ queueType?: 'context' | 'security';
48
48
  /**
49
49
  * Held-run `hold_type` discriminator. Defaults to `'approval'` (reviewer
50
50
  * holds). The workflow install gate sets `'wait_timer'` / `'concurrency'` so
@@ -79,7 +79,7 @@ export interface ReleaseSignal {
79
79
  stepIndex: number | null;
80
80
  /**
81
81
  * What kind of gate created the hold. `explicit` (SDK `requireApproval`) holds
82
- * a real root job and resumes by re-dispatching it; `environment` covers the
82
+ * a real root job and resumes by re-dispatching it; `context` covers the
83
83
  * workflow install-gate (wait-timer / concurrency / env approval) which resumes
84
84
  * by rebuilding the workflow dispatch context. The resume router keys off this
85
85
  * so a workflow-scoped explicit hold goes through the job re-dispatch path
@@ -100,7 +100,7 @@ export declare class HeldRunStore {
100
100
  create(orgId: string, data: CreateHeldRunData): Promise<HeldRun>;
101
101
  /**
102
102
  * Create a generalized approval hold (workflow/job/step scope, explicit or
103
- * environment trigger) carrying a normalized `ApprovalRequirement`. Returns
103
+ * context trigger) carrying a normalized `ApprovalRequirement`. Returns
104
104
  * the created row.
105
105
  */
106
106
  createHold(orgId: string, data: CreateHoldData): Promise<HeldRun>;
@@ -151,13 +151,13 @@ export declare class HeldRunStore {
151
151
  /** List held runs for an org with optional filters. */
152
152
  listAll(orgId: string, options?: ListHeldRunsOptions): Promise<HeldRun[]>;
153
153
  /** List held runs for an org filtered by queue type with optional filters. */
154
- listByQueueType(orgId: string, queueType: 'environment' | 'security', options?: ListHeldRunsOptions): Promise<HeldRun[]>;
154
+ listByQueueType(orgId: string, queueType: 'context' | 'security', options?: ListHeldRunsOptions): Promise<HeldRun[]>;
155
155
  /**
156
156
  * Approve a pending held run, enforcing queue_type boundary.
157
- * Prevents environment approvals from approving security holds and vice versa.
157
+ * Prevents context approvals from approving security holds and vice versa.
158
158
  * Throws if not found, not pending, or queue_type mismatch.
159
159
  */
160
- approveByQueueType(orgId: string, heldRunId: string, approvedBy: string, queueType: 'environment' | 'security'): Promise<HeldRun>;
160
+ approveByQueueType(orgId: string, heldRunId: string, approvedBy: string, queueType: 'context' | 'security'): Promise<HeldRun>;
161
161
  /** Get a held run by run ID and job ID. Returns null if not found. */
162
162
  getByRunAndJob(orgId: string, runId: string, jobId: string): Promise<HeldRun | null>;
163
163
  /**
@@ -181,7 +181,7 @@ export declare class HeldRunStore {
181
181
  * when a concurrency slot frees on run completion. Flips the oldest matching
182
182
  * pending row to `released` and returns its `ReleaseSignal`, or null when no
183
183
  * concurrency hold is queued for the group. `group` matches the held row's
184
- * `environment_id` (workflow-level install concurrency keys on the env id).
184
+ * `context_id` (workflow-level install concurrency keys on the env id).
185
185
  */
186
186
  releaseConcurrencyHold(orgId: string, group: string): Promise<ReleaseSignal | null>;
187
187
  /**
@@ -1,4 +1,4 @@
1
- export { EnvironmentStore, type EnvironmentCreateInput, type EnvironmentUpdateInput, } from './environment-store.js';
1
+ export { ContextStore, type ContextCreateInput, type ContextUpdateInput } from './context-store.js';
2
2
  export { BindingStore } from './binding-store.js';
3
3
  export { VariableStore } from './variable-store.js';
4
4
  export { HeldRunStore, type CreateHeldRunData, type ListHeldRunsOptions } from './held-runs.js';
@@ -0,0 +1,43 @@
1
+ import { ContextGateRejectReason, type Context } from '@kici-dev/engine';
2
+ import type { JobDispatchContext } from './pipeline.js';
3
+ /** A single context's rejection under all-must-pass aggregation. */
4
+ export interface ContextGateRejection {
5
+ context: string;
6
+ reason: ContextGateRejectReason;
7
+ detail: string;
8
+ }
9
+ /** Effective protection parameters after most-restrictive aggregation. */
10
+ export interface EffectiveProtection {
11
+ minimumTrust?: 'known' | 'trusted';
12
+ requiredReviewers: string[];
13
+ waitTimerSeconds: number | null;
14
+ holdExpirySeconds: number;
15
+ concurrencyLimit: number | null;
16
+ concurrencyStrategy: 'queue' | 'cancel-pending';
17
+ }
18
+ /**
19
+ * Evaluate each context's hard reject gates against the run context. A name
20
+ * with no `Context` record yields a `context_not_found` rejection. Returns all
21
+ * rejections (empty = every context passed the reject gates).
22
+ */
23
+ export declare function evaluateMultiContextGates(envs: ReadonlyArray<{
24
+ name: string;
25
+ env: Context | undefined;
26
+ }>, ctx: JobDispatchContext): ContextGateRejection[];
27
+ /**
28
+ * Aggregate hold/wait/queue parameters across all bound contexts, most
29
+ * restrictive wins: trust = max tier, reviewers = sorted dedup union, wait timer
30
+ * = max, hold expiry = min, concurrency limit = min (tightest). The concurrency
31
+ * strategy follows the primary (first) context.
32
+ */
33
+ export declare function aggregateProtectionParams(envs: ReadonlyArray<Context>): EffectiveProtection;
34
+ /**
35
+ * Build a synthetic `Context` carrying the aggregated protection parameters,
36
+ * so the existing per-rule gate functions (trust/concurrency/reviewer/wait) can
37
+ * evaluate the all-must-pass holds in one pass. Branch/trigger/repo/enabled are
38
+ * already handled by `evaluateMultiContextGates`, so they are neutralized here.
39
+ */
40
+ export declare function buildEffectiveContext(primary: Context, eff: EffectiveProtection): Context;
41
+ /** Format a human-readable rejection reason naming the env(s) and rule(s). */
42
+ export declare function formatMultiContextRejection(rejections: ReadonlyArray<ContextGateRejection>): string;
43
+ //# sourceMappingURL=aggregate.d.ts.map
@@ -0,0 +1,5 @@
1
+ import type { Context, ProtectionGateResult } from '@kici-dev/engine';
2
+ import type { JobDispatchContext } from './pipeline.js';
3
+ /** Evaluate branch restrictions, trigger type filters, and repo patterns. */
4
+ export declare function evaluateBranchGate(env: Context, ctx: JobDispatchContext): ProtectionGateResult;
5
+ //# sourceMappingURL=branch-gate.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Concurrency gate -- checks concurrency limits.
3
+ */
4
+ import type { Context, ProtectionGateResult } from '@kici-dev/engine';
5
+ /** Evaluate concurrency limits for the context. */
6
+ export declare function evaluateConcurrencyGate(env: Context, currentRunningCount: number, _concurrencyGroup: string): ProtectionGateResult;
7
+ //# sourceMappingURL=concurrency-gate.d.ts.map
@@ -4,7 +4,7 @@
4
4
  * Gates evaluated in order: branch -> trust -> concurrency -> reviewer -> timer.
5
5
  * First non-pass result stops evaluation.
6
6
  */
7
- import type { Environment, ProtectionGateResult, TrustTier } from '@kici-dev/engine';
7
+ import type { Context, ProtectionGateResult, TrustTier } from '@kici-dev/engine';
8
8
  /** Context needed for protection gate evaluation. */
9
9
  export interface JobDispatchContext {
10
10
  branch: string;
@@ -13,6 +13,6 @@ export interface JobDispatchContext {
13
13
  runId: string;
14
14
  jobId: string;
15
15
  }
16
- /** Evaluate all protection rules for an environment. */
17
- export declare function evaluateProtectionRules(env: Environment, ctx: JobDispatchContext, currentRunningCount: number, concurrencyGroup: string, trustTier?: TrustTier): Promise<ProtectionGateResult>;
16
+ /** Evaluate all protection rules for a context. */
17
+ export declare function evaluateProtectionRules(env: Context, ctx: JobDispatchContext, currentRunningCount: number, concurrencyGroup: string, trustTier?: TrustTier): Promise<ProtectionGateResult>;
18
18
  //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Reviewer gate -- checks required reviewers.
3
+ */
4
+ import type { Context, ProtectionGateResult } from '@kici-dev/engine';
5
+ /** Evaluate reviewer requirements for the context. */
6
+ export declare function evaluateReviewerGate(env: Context): ProtectionGateResult;
7
+ //# sourceMappingURL=reviewer-gate.d.ts.map
@@ -1,25 +1,25 @@
1
1
  /**
2
- * Registration-time satisfiability check for multi-environment job bindings.
2
+ * Registration-time satisfiability check for multi-context job bindings.
3
3
  *
4
- * When a job binds several environments (`environments: [...]`), every
5
- * environment's protection gates must pass for the job to ever dispatch
4
+ * When a job binds several contexts (`contexts: [...]`), every
5
+ * context's protection gates must pass for the job to ever dispatch
6
6
  * (all-must-pass aggregation — see `aggregate.ts`). Some of those gates are
7
- * decidable statically: if two bound environments restrict to disjoint fixed
7
+ * decidable statically: if two bound contexts restrict to disjoint fixed
8
8
  * branch sets, no run can ever satisfy both, so the binding is provably
9
9
  * unsatisfiable and should be rejected at registration rather than failing
10
10
  * silently at every future dispatch.
11
11
  *
12
12
  * This module intersects the statically-decidable set rules (branch, trigger
13
13
  * type, repository — only when every pattern is a literal, never a glob) plus
14
- * the `enabled` gate across the **resolved** bound environments, and reports the
15
- * first provably-empty intersection. Bound names that resolve to no environment
14
+ * the `enabled` gate across the **resolved** bound contexts, and reports the
15
+ * first provably-empty intersection. Bound names that resolve to no context
16
16
  * record are lenient — skipped, never rejected — matching the dispatch-time
17
17
  * behavior in `dispatch-matched-workflow.ts`. Any glob in a rule makes that rule
18
18
  * undecidable, so it is skipped here and left to the dispatch-time catch-all
19
- * (`evaluateMultiEnvGates`).
19
+ * (`evaluateMultiContextGates`).
20
20
  */
21
21
  import { z } from 'zod';
22
- import type { Environment } from '@kici-dev/engine';
22
+ import type { Context } from '@kici-dev/engine';
23
23
  /** Which decidable rule made a binding unsatisfiable. */
24
24
  export declare const UnsatisfiableRule: z.ZodEnum<{
25
25
  enabled: "enabled";
@@ -28,42 +28,42 @@ export declare const UnsatisfiableRule: z.ZodEnum<{
28
28
  trigger: "trigger";
29
29
  }>;
30
30
  export type UnsatisfiableRule = z.infer<typeof UnsatisfiableRule>;
31
- /** A provably-unsatisfiable multi-environment binding, naming the rule + reason. */
31
+ /** A provably-unsatisfiable multi-context binding, naming the rule + reason. */
32
32
  export interface UnsatisfiableBinding {
33
33
  jobName: string;
34
- environments: string[];
34
+ contexts: string[];
35
35
  rule: UnsatisfiableRule;
36
36
  message: string;
37
37
  }
38
38
  /**
39
- * Returns a precise problem when the bound environments can NEVER be jointly
40
- * satisfied (a disabled environment, or mutually-exclusive fixed restrictions
41
- * among the resolved environments), else `null`. Missing (unresolved) names are
42
- * skipped — a bound name with no environment record contributes no protection
39
+ * Returns a precise problem when the bound contexts can NEVER be jointly
40
+ * satisfied (a disabled context, or mutually-exclusive fixed restrictions
41
+ * among the resolved contexts), else `null`. Missing (unresolved) names are
42
+ * skipped — a bound name with no context record contributes no protection
43
43
  * rules and is lenient at dispatch, so it is not rejected here. Glob /
44
44
  * undecidable cases also return `null` and are caught at dispatch by
45
- * `evaluateMultiEnvGates`.
45
+ * `evaluateMultiContextGates`.
46
46
  *
47
- * `envs[i]` is the resolved `Environment` for `envNames[i]` (undefined when the
48
- * name has no environment record). Only the statically-known (non-dynamic) bound
47
+ * `envs[i]` is the resolved `Context` for `envNames[i]` (undefined when the
48
+ * name has no context record). Only the statically-known (non-dynamic) bound
49
49
  * names should be passed — dynamic elements are unknown at registration and the
50
50
  * all-must-pass semantics make the static subset's exclusivity still sound.
51
51
  */
52
- export declare function checkBindingSatisfiable(jobName: string, envs: ReadonlyArray<Environment | undefined>, envNames: readonly string[]): UnsatisfiableBinding | null;
52
+ export declare function checkBindingSatisfiable(jobName: string, envs: ReadonlyArray<Context | undefined>, envNames: readonly string[]): UnsatisfiableBinding | null;
53
53
  /** Minimal lock-workflow shape needed to walk its jobs for satisfiability. */
54
54
  interface SatisfiabilityLockWorkflow {
55
55
  jobs?: readonly unknown[];
56
56
  }
57
57
  /**
58
58
  * Walk every workflow's static jobs and reject the registration when a bound
59
- * environment list is provably unsatisfiable (a disabled environment, or
59
+ * context list is provably unsatisfiable (a disabled context, or
60
60
  * mutually-exclusive fixed branch/trigger/repo restrictions among the resolved
61
- * environments — missing names are lenient, never rejected). Dynamic elements
61
+ * contexts — missing names are lenient, never rejected). Dynamic elements
62
62
  * are skipped (unresolvable at registration); the all-must-pass semantics keep
63
63
  * the static subset's exclusivity sound. Throws the first
64
64
  * `UnsatisfiableBinding.message` so the registration route / direct helper
65
65
  * surfaces it to the caller.
66
66
  */
67
- export declare function assertWorkflowsSatisfiable(workflows: ReadonlyArray<SatisfiabilityLockWorkflow>, resolveEnv: (name: string) => Promise<Environment | null>): Promise<void>;
67
+ export declare function assertWorkflowsSatisfiable(workflows: ReadonlyArray<SatisfiabilityLockWorkflow>, resolveEnv: (name: string) => Promise<Context | null>): Promise<void>;
68
68
  export {};
69
69
  //# sourceMappingURL=satisfiability.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Trust gate -- checks contributor trust tier against context minimumTrust.
3
+ */
4
+ import type { Context, ProtectionGateResult, TrustTier } from '@kici-dev/engine';
5
+ /** Evaluate minimumTrust requirements for the context. */
6
+ export declare function evaluateTrustGate(env: Context, trustTier: TrustTier | undefined): ProtectionGateResult;
7
+ //# sourceMappingURL=trust-gate.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Wait timer gate -- checks wait timer configuration.
3
+ */
4
+ import type { Context, ProtectionGateResult } from '@kici-dev/engine';
5
+ /** Evaluate wait timer for the context. */
6
+ export declare function evaluateWaitTimerGate(env: Context): ProtectionGateResult;
7
+ //# sourceMappingURL=wait-timer-gate.d.ts.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Variable store -- context variable CRUD with lock enforcement.
3
+ *
4
+ * Manages org-level context variables and per-source overrides.
5
+ * Lock enforcement: locked org vars cannot be overridden by source overrides.
6
+ */
7
+ import { type Kysely } from 'kysely';
8
+ import type { Database, ContextVariable, ContextSourceOverride } from '../db/types.js';
9
+ /**
10
+ * Data access layer for context variables and source overrides.
11
+ */
12
+ export declare class VariableStore {
13
+ private readonly db;
14
+ constructor(db: Kysely<Database>);
15
+ /** List all org-level variables for a context. */
16
+ listVars(orgId: string, contextId: string): Promise<ContextVariable[]>;
17
+ /** Upsert an org-level variable. */
18
+ setVar(orgId: string, contextId: string, key: string, value: string, locked?: boolean): Promise<void>;
19
+ /** Delete an org-level variable. */
20
+ deleteVar(orgId: string, contextId: string, key: string): Promise<void>;
21
+ /** List source overrides for a specific source (routing key). */
22
+ listSourceOverrides(orgId: string, contextId: string, routingKey: string): Promise<ContextSourceOverride[]>;
23
+ /** Upsert a source override. */
24
+ setSourceOverride(orgId: string, contextId: string, routingKey: string, key: string, value: string): Promise<void>;
25
+ /** Delete a source override. */
26
+ deleteSourceOverride(orgId: string, contextId: string, routingKey: string, key: string): Promise<void>;
27
+ /**
28
+ * Get resolved variables for a context, optionally merged with source overrides.
29
+ *
30
+ * Merge rules:
31
+ * - Org-level vars form the base
32
+ * - Source overrides can add new keys and override unlocked org vars
33
+ * - Locked org vars are NOT overridden by source overrides
34
+ *
35
+ * @param routingKey - If provided, source overrides are merged in
36
+ */
37
+ getResolvedVars(orgId: string, contextId: string, routingKey?: string): Promise<Record<string, string>>;
38
+ }
39
+ //# sourceMappingURL=variable-store.d.ts.map
@@ -13,7 +13,7 @@ import type { Database } from '../db/types.js';
13
13
  */
14
14
  export declare function baseAttestationsQuery(db: Kysely<Database>): import("kysely").SelectQueryBuilder<{
15
15
  cluster_meta: import("../db/types.js").ClusterMetaTable;
16
- environments: import("../db/types.js").EnvironmentsTable;
16
+ contexts: import("../db/types.js").ContextsTable;
17
17
  host_roster: import("../db/types.js").HostRosterTable;
18
18
  dispatch_queue: import("../db/types.js").DispatchQueueTable;
19
19
  dedup_cache: import("../db/types.js").DedupCacheTable;
@@ -24,9 +24,9 @@ export declare function baseAttestationsQuery(db: Kysely<Database>): import("kys
24
24
  raft_state: import("../db/types.js").RaftStateTable;
25
25
  secret_audit_log: import("../db/types.js").SecretAuditLogTable;
26
26
  scoped_secrets: import("../db/types.js").ScopedSecretsTable;
27
- environment_bindings: import("../db/types.js").EnvironmentBindingsTable;
28
- environment_variables: import("../db/types.js").EnvironmentVariablesTable;
29
- environment_source_overrides: import("../db/types.js").EnvironmentSourceOverridesTable;
27
+ context_bindings: import("../db/types.js").ContextBindingsTable;
28
+ context_variables: import("../db/types.js").ContextVariablesTable;
29
+ context_source_overrides: import("../db/types.js").ContextSourceOverridesTable;
30
30
  held_runs: import("../db/types.js").HeldRunsTable;
31
31
  held_run_approvals: import("../db/types.js").HeldRunApprovalsTable;
32
32
  admin_tokens: import("../db/types.js").AdminTokenTable;
@@ -59,6 +59,7 @@ export declare function baseAttestationsQuery(db: Kysely<Database>): import("kys
59
59
  attestations: import("../db/types.js").AttestationsTable;
60
60
  pending_attestations: import("../db/types.js").PendingAttestationsTable;
61
61
  remote_sources: import("../db/types.js").RemoteSourcesTable;
62
+ request_idempotency: import("../db/types.js").RequestIdempotencyTable;
62
63
  }, "execution_runs" | "execution_jobs" | "attestations", {}>;
63
64
  export type AttestationsBaseQuery = ReturnType<typeof baseAttestationsQuery>;
64
65
  /**
@@ -74,7 +75,7 @@ export type AttestationsBaseQuery = ReturnType<typeof baseAttestationsQuery>;
74
75
  */
75
76
  export declare function basePendingAttestationsQuery(db: Kysely<Database>): import("kysely").SelectQueryBuilder<{
76
77
  cluster_meta: import("../db/types.js").ClusterMetaTable;
77
- environments: import("../db/types.js").EnvironmentsTable;
78
+ contexts: import("../db/types.js").ContextsTable;
78
79
  host_roster: import("../db/types.js").HostRosterTable;
79
80
  dispatch_queue: import("../db/types.js").DispatchQueueTable;
80
81
  dedup_cache: import("../db/types.js").DedupCacheTable;
@@ -85,9 +86,9 @@ export declare function basePendingAttestationsQuery(db: Kysely<Database>): impo
85
86
  raft_state: import("../db/types.js").RaftStateTable;
86
87
  secret_audit_log: import("../db/types.js").SecretAuditLogTable;
87
88
  scoped_secrets: import("../db/types.js").ScopedSecretsTable;
88
- environment_bindings: import("../db/types.js").EnvironmentBindingsTable;
89
- environment_variables: import("../db/types.js").EnvironmentVariablesTable;
90
- environment_source_overrides: import("../db/types.js").EnvironmentSourceOverridesTable;
89
+ context_bindings: import("../db/types.js").ContextBindingsTable;
90
+ context_variables: import("../db/types.js").ContextVariablesTable;
91
+ context_source_overrides: import("../db/types.js").ContextSourceOverridesTable;
91
92
  held_runs: import("../db/types.js").HeldRunsTable;
92
93
  held_run_approvals: import("../db/types.js").HeldRunApprovalsTable;
93
94
  admin_tokens: import("../db/types.js").AdminTokenTable;
@@ -120,6 +121,7 @@ export declare function basePendingAttestationsQuery(db: Kysely<Database>): impo
120
121
  attestations: import("../db/types.js").AttestationsTable;
121
122
  pending_attestations: import("../db/types.js").PendingAttestationsTable;
122
123
  remote_sources: import("../db/types.js").RemoteSourcesTable;
124
+ request_idempotency: import("../db/types.js").RequestIdempotencyTable;
123
125
  }, "execution_runs" | "execution_jobs" | "pending_attestations", {}>;
124
126
  /**
125
127
  * Apply org-wide attestation filters to the base query. Digest is exact-match;
@@ -70,7 +70,14 @@ interface DashboardHandlerDeps {
70
70
  * compat with mixed deploys; absent means the orchestrator falls
71
71
  * back to the legacy "no PG row → throw" path.
72
72
  */
73
- onRerun: (runId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null, routingKey?: string) => Promise<{
73
+ onRerun: (runId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null,
74
+ /**
75
+ * Platform-minted `requestId` for this rerun. Stable across an HA relay
76
+ * failover re-send, so the orchestrator uses it as an idempotency key: a
77
+ * re-sent request returns the first coordinator's run instead of minting a
78
+ * second one.
79
+ */
80
+ requestId: string, routingKey?: string) => Promise<{
74
81
  newRunId: string;
75
82
  }>;
76
83
  /**
@@ -85,7 +92,13 @@ interface DashboardHandlerDeps {
85
92
  * Callback for handling manual schedule trigger requests.
86
93
  * Returns { newRunId } on success or throws on failure.
87
94
  */
88
- onManualSchedule: (registrationId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null) => Promise<{
95
+ onManualSchedule: (registrationId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null,
96
+ /**
97
+ * Platform-minted `requestId` for this manual schedule. Used as an
98
+ * idempotency key so an HA relay failover re-send returns the first
99
+ * coordinator's run instead of minting a second one.
100
+ */
101
+ requestId: string) => Promise<{
89
102
  newRunId: string;
90
103
  }>;
91
104
  /**
@@ -95,6 +108,17 @@ interface DashboardHandlerDeps {
95
108
  * deployments the store is wired by `bootstrapOrchestrator`.
96
109
  */
97
110
  eventStore?: EventStore | null;
111
+ /**
112
+ * **Test-only.** Master switch for fault-injection knobs. When false the
113
+ * handler ignores every test-only knob below even if it is set.
114
+ */
115
+ testMode?: boolean;
116
+ /**
117
+ * **Test-only.** When set (and `testMode` is true), `handleRerunRequest`
118
+ * sleeps this many ms before invoking `onRerun`, so an HA E2E can force a
119
+ * slow first coordinator and trigger a relay failover. Ignored in production.
120
+ */
121
+ testRerunDelayMs?: number;
98
122
  }
99
123
  export declare class DashboardHandler {
100
124
  private readonly db;
@@ -111,6 +135,8 @@ export declare class DashboardHandler {
111
135
  private readonly coldStore;
112
136
  private readonly eventStore;
113
137
  private readonly retryAttestations;
138
+ private readonly testMode;
139
+ private readonly testRerunDelayMs;
114
140
  constructor(deps: DashboardHandlerDeps);
115
141
  /**
116
142
  * Update the bound orgId + routingKey. Called from server.ts after resolving