@kici-dev/orchestrator 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/dist/__test-helpers__/fake-scaler-state-store.d.ts +24 -0
  2. package/dist/__test-helpers__/mock-db-query.d.ts +130 -0
  3. package/dist/__test-helpers__/mock-db.d.ts +23 -0
  4. package/dist/agent/dispatcher.d.ts +81 -13
  5. package/dist/agent/host-roster.d.ts +37 -5
  6. package/dist/agent/registry.d.ts +2 -0
  7. package/dist/app.d.ts +33 -6
  8. package/dist/approvals/apply-decision.d.ts +67 -19
  9. package/dist/approvals/held-run-release-wiring.d.ts +45 -0
  10. package/dist/approvals/independent-wiring.d.ts +55 -0
  11. package/dist/approvals/resume-router.d.ts +9 -0
  12. package/dist/approvals/triggerer-subject.d.ts +37 -0
  13. package/dist/cache/dep-cache.d.ts +54 -10
  14. package/dist/cache/global-eval-round-cache.d.ts +7 -0
  15. package/dist/cli/api-client.d.ts +18 -0
  16. package/dist/cli/commands/cluster-settings.d.ts +11 -0
  17. package/dist/cli/commands/diagnose.d.ts +6 -0
  18. package/dist/cli/commands/held-run.d.ts +58 -0
  19. package/dist/cli/commands/trust-policy.d.ts +117 -12
  20. package/dist/cli/kici-admin.d.ts +10 -0
  21. package/dist/cli.js +2272 -1025
  22. package/dist/cluster/cluster-identity.d.ts +3 -3
  23. package/dist/cluster/cluster-settings-reader.d.ts +7 -1
  24. package/dist/cluster/coordinator.d.ts +44 -0
  25. package/dist/cluster/index.d.ts +4 -0
  26. package/dist/cluster/peer-handler.d.ts +19 -0
  27. package/dist/cluster/peer-registry.d.ts +17 -0
  28. package/dist/cluster/plan-headroom-store.d.ts +26 -0
  29. package/dist/cluster/worker-eviction.d.ts +69 -0
  30. package/dist/cold-store/load-event-log-range.d.ts +10 -3
  31. package/dist/cold-store/load-secret-audit-log-range.d.ts +4 -3
  32. package/dist/cold-store/tables/secret-audit-log.d.ts +4 -3
  33. package/dist/config.d.ts +21 -21
  34. package/dist/contexts/held-runs.d.ts +123 -16
  35. package/dist/contexts/protection/aggregate.d.ts +3 -2
  36. package/dist/contexts/protection/branch-gate.d.ts +15 -0
  37. package/dist/contexts/protection/concurrency-gate.d.ts +8 -2
  38. package/dist/contexts/protection/pipeline.d.ts +18 -0
  39. package/dist/contexts/release-queued-holds.d.ts +35 -0
  40. package/dist/dashboard/attestation-filters.d.ts +8 -0
  41. package/dist/dashboard/handler.d.ts +6 -24
  42. package/dist/db/migrations/116_org_plan_headroom.d.ts +4 -0
  43. package/dist/db/migrations/117_invoke_gate_proxy_jobs.d.ts +4 -0
  44. package/dist/db/migrations/118_invoke_gate_scheduling.d.ts +4 -0
  45. package/dist/db/migrations/119_scaler_ha_ownership.d.ts +4 -0
  46. package/dist/db/migrations/120_cluster_settings_scaler_reaper.d.ts +4 -0
  47. package/dist/db/migrations/121_org_settings_allow_untrusted_dockerfile_builds.d.ts +19 -0
  48. package/dist/db/migrations/122_host_roster_scaler_managed.d.ts +25 -0
  49. package/dist/db/migrations/123_workflow_registrations_default_branch.d.ts +24 -0
  50. package/dist/db/migrations/124_cluster_settings_provision_backoff.d.ts +4 -0
  51. package/dist/db/migrations/125_org_trust_directory.d.ts +30 -0
  52. package/dist/db/migrations/126_held_runs_posted_pending_check.d.ts +28 -0
  53. package/dist/db/migrations/127_org_trust_policy_approval_expiry_seconds.d.ts +26 -0
  54. package/dist/db/migrations/128_execution_runs_global_eval_round.d.ts +23 -0
  55. package/dist/db/migrations/129_execution_runs_dispatch_routing_key.d.ts +24 -0
  56. package/dist/db/migrations/130_scaler_provision_outcomes.d.ts +32 -0
  57. package/dist/db/types.d.ts +368 -10
  58. package/dist/entry-helpers.d.ts +28 -0
  59. package/dist/events/event-emitter.d.ts +22 -0
  60. package/dist/events/event-router.d.ts +87 -1
  61. package/dist/events/types.d.ts +2 -23
  62. package/dist/git/credential-broker.d.ts +94 -0
  63. package/dist/git/inherited-secret.d.ts +24 -0
  64. package/dist/git/installation-token.d.ts +38 -0
  65. package/dist/git/job-context.d.ts +13 -0
  66. package/dist/index.js +324 -84
  67. package/dist/metrics/agent-metrics-aggregator.d.ts +2 -2
  68. package/dist/metrics/prometheus.d.ts +111 -3
  69. package/dist/oidc/oidc-mint-registration.d.ts +7 -3
  70. package/dist/orchestrator-core.d.ts +79 -23
  71. package/dist/pipeline/direct-ingress-deps.d.ts +100 -0
  72. package/dist/pipeline/dispatch-matched-workflow.d.ts +302 -31
  73. package/dist/pipeline/global-eval-round.d.ts +14 -0
  74. package/dist/pipeline/internal-event-pipeline.d.ts +140 -0
  75. package/dist/pipeline/invoke-gate.d.ts +164 -0
  76. package/dist/pipeline/needs-scheduler.d.ts +13 -0
  77. package/dist/pipeline/process-webhook.d.ts +119 -31
  78. package/dist/pipeline/processor.d.ts +226 -21
  79. package/dist/pipeline/rerun.d.ts +21 -0
  80. package/dist/pipeline/resume-workflow.d.ts +40 -7
  81. package/dist/pipeline/route-or-dispatch-jobs.d.ts +9 -0
  82. package/dist/pipeline/security-hold-check.d.ts +217 -0
  83. package/dist/pipeline/undispatched-hold-checks.d.ts +23 -0
  84. package/dist/policy/dashboard-write-policy-listener.d.ts +62 -0
  85. package/dist/policy/dashboard-write-policy.d.ts +14 -0
  86. package/dist/provider-registry.d.ts +10 -2
  87. package/dist/providers/github/check-status-poster.d.ts +11 -0
  88. package/dist/providers/github/index.d.ts +0 -2
  89. package/dist/providers/github/normalizer.d.ts +3 -2
  90. package/dist/providers/local/normalizer.d.ts +3 -5
  91. package/dist/providers/universal-git/index.d.ts +11 -2
  92. package/dist/queue/job-queue.d.ts +43 -1
  93. package/dist/registration/extractor.d.ts +3 -3
  94. package/dist/registration/registration-index.d.ts +7 -0
  95. package/dist/registration/registration-store.d.ts +22 -0
  96. package/dist/reporting/check-run-reporter.d.ts +94 -1
  97. package/dist/reporting/check-run-summary.d.ts +25 -1
  98. package/dist/reporting/execution-tracker.d.ts +207 -2
  99. package/dist/reporting/run-aggregator.d.ts +4 -14
  100. package/dist/reporting/step-display-order.d.ts +43 -0
  101. package/dist/routes/admin-held-runs.d.ts +142 -0
  102. package/dist/routes/admin-trust-policy.d.ts +34 -6
  103. package/dist/routes/admin.d.ts +9 -0
  104. package/dist/scaler/backend-factory.d.ts +55 -0
  105. package/dist/scaler/bare-metal-backend.d.ts +43 -5
  106. package/dist/scaler/claim-store.d.ts +119 -0
  107. package/dist/scaler/config.d.ts +4 -0
  108. package/dist/scaler/container-backend.d.ts +18 -4
  109. package/dist/scaler/container-routing.d.ts +23 -0
  110. package/dist/scaler/container-spawn.d.ts +28 -0
  111. package/dist/scaler/event-backend.d.ts +124 -0
  112. package/dist/scaler/event-provision-reaper.d.ts +350 -0
  113. package/dist/scaler/failure-tracker.d.ts +1 -1
  114. package/dist/scaler/firecracker-backend.d.ts +33 -6
  115. package/dist/scaler/index.d.ts +11 -1
  116. package/dist/scaler/label-matcher.d.ts +4 -3
  117. package/dist/scaler/manager.d.ts +899 -72
  118. package/dist/scaler/resolve-container-auth.d.ts +45 -0
  119. package/dist/scaler/scaler-events.d.ts +11 -0
  120. package/dist/scaler/scaler-state-store.d.ts +294 -10
  121. package/dist/scaler/types.d.ts +131 -13
  122. package/dist/scaler/warm-pool.d.ts +132 -29
  123. package/dist/security/comment-handler.d.ts +50 -11
  124. package/dist/security/identity-link.d.ts +60 -0
  125. package/dist/security/lock-source.d.ts +11 -13
  126. package/dist/security/reduced-privilege-note.d.ts +59 -0
  127. package/dist/security/trust-directory-store.d.ts +233 -0
  128. package/dist/security/trust-policy-gate.d.ts +94 -59
  129. package/dist/security/trust-policy-store.d.ts +3 -0
  130. package/dist/security/trust-resolver.d.ts +14 -98
  131. package/dist/server.d.ts +11 -1
  132. package/dist/server.js +17671 -7350
  133. package/dist/stale-detector/gate-deadline-detector.d.ts +52 -0
  134. package/dist/stale-detector/stale-run-detector.d.ts +61 -3
  135. package/dist/standalone.js +24238 -13822
  136. package/dist/worker/in-memory-job-queue.d.ts +1 -1
  137. package/dist/ws/agent-handler.d.ts +28 -4
  138. package/dist/ws/dashboard-context-handler.d.ts +28 -4
  139. package/dist/ws/dashboard-global-workflows-handler.d.ts +6 -0
  140. package/dist/ws/failure-messages.d.ts +2 -0
  141. package/dist/ws/git-credential-relay.d.ts +39 -0
  142. package/dist/ws/oidc-token-relay.d.ts +6 -11
  143. package/dist/ws/platform-client.d.ts +54 -9
  144. package/installer-image-digests.json +3 -3
  145. package/package.json +24 -19
  146. package/sbom.spdx.json +864 -822
  147. package/dist/approvals/team-membership-lookup.d.ts +0 -13
  148. package/dist/providers/github/contributor-resolver.d.ts +0 -30
  149. package/dist/security/contributor-cache.d.ts +0 -83
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Invoke-gate executor.
3
+ *
4
+ * An invoke gate is a standard job whose only novel attribute is `invoke`: it
5
+ * never dispatches steps to an agent. When the gate becomes ready the
6
+ * orchestrator emits the gate's kici event at the source repo, matches the
7
+ * repo's opt-in subscribers, dispatches each as a normal in-repo run, and
8
+ * tracks each spawned run as a **proxy job** in the gate's own run.
9
+ *
10
+ * Correlation is a synchronous in-process match-and-dispatch — one orchestrator
11
+ * serves one org, so the executor matches + dispatches through an injected
12
+ * `summon` callback and gets the spawned run ids back directly. It ALSO persists
13
+ * `summoned_by_run_id` / `summoned_by_proxy_job` on each spawned run so proxy
14
+ * completion still resolves if the run finalizes on another HA instance.
15
+ *
16
+ * The gate itself is the aggregating graph node: downstream `needs` edges point
17
+ * at the gate, and its proxies are its fan-out children (`base_job_name` = the
18
+ * gate name). A zero-subscriber emit fails the gate by default; `optional: true`
19
+ * makes it a green skip.
20
+ */
21
+ import { ExecutionJobStatus } from '@kici-dev/engine';
22
+ import type { LockInvoke, LockJob } from '@kici-dev/engine';
23
+ import type { Kysely } from 'kysely';
24
+ import type { Database } from '../db/types.js';
25
+ import type { ExecutionTracker } from '../reporting/execution-tracker.js';
26
+ /**
27
+ * The invoke parameters carried from a gate's lock job to its (possibly deferred)
28
+ * execution. Persisted on the pending job context so a gate released later — or
29
+ * after a crash-recovery restore — still summons instead of reaching an agent.
30
+ */
31
+ export interface InvokeGateParams {
32
+ event: string;
33
+ payload?: Record<string, unknown>;
34
+ /** Require-by-default: false fails the gate on zero subscribers. */
35
+ optional: boolean;
36
+ /** Fan-out concurrency width applied to the gate's proxy children. */
37
+ maxParallel?: number;
38
+ /** Fan-out fail-fast policy applied to the gate's proxy children. */
39
+ failFast?: boolean;
40
+ /** The gate's own wall-clock timeout in ms (orchestrator-swept). */
41
+ timeoutMs?: number;
42
+ }
43
+ /** Extract the invoke parameters from a lock job, or undefined when it is not a gate. */
44
+ export declare function invokeParamsFromLockJob(lockJob: Pick<LockJob, 'invoke' | 'maxParallel' | 'failFast' | 'timeout'> & {
45
+ invoke?: LockInvoke;
46
+ }): InvokeGateParams | undefined;
47
+ /** True when a lock job is an invoke gate (carries an `invoke` action). */
48
+ export declare function isInvokeGate(lockJob: Pick<LockJob, 'invoke'>): boolean;
49
+ /**
50
+ * A summon that was REFUSED, as opposed to one that matched nothing.
51
+ *
52
+ * The two are not the same verdict and must never render the same. A gate
53
+ * carrying `optional: true` treats zero summoned runs as a green skip, so a
54
+ * refusal returned as `[]` would report success for work that was declined.
55
+ * Throwing this makes the confusion unrepresentable: {@link runInvokeGate}
56
+ * fails the gate job with the reason, whatever order its own checks run in.
57
+ */
58
+ export declare class SummonRefusedError extends Error {
59
+ constructor(reason: string);
60
+ }
61
+ /** One source-repo run summoned by an invoke gate. */
62
+ export interface SummonedRun {
63
+ /** The spawned run's id. */
64
+ runId: string;
65
+ /** The source repo the run belongs to (`owner/repo`). */
66
+ repo: string;
67
+ /** The subscribing workflow's name. */
68
+ workflow: string;
69
+ }
70
+ /** Arguments to the injected `summon` callback. */
71
+ export interface SummonArgs {
72
+ event: string;
73
+ payload?: Record<string, unknown>;
74
+ sourceRepo: string;
75
+ /** The chain depth to stamp on the summoned runs (already incremented). */
76
+ chainDepth: number;
77
+ /** The summoning gate's run id — tag each spawned run so its proxy resolves. */
78
+ summonedByRunId: string;
79
+ }
80
+ /** Dependencies for {@link runInvokeGate}. */
81
+ export interface InvokeGateDeps {
82
+ db: Kysely<Database>;
83
+ /** Insert the proxy rows + mark the gate terminal on a zero-match emit. */
84
+ executionTracker: Pick<ExecutionTracker, 'addJobsToRun' | 'onJobStatus' | 'reconcileSummonedRunIfTerminal'>;
85
+ /**
86
+ * Match the source repo's subscribers to `event`, dispatch each as a normal
87
+ * in-repo run, and return the spawned run ids. Implemented at the composition
88
+ * root, where it reuses the orchestrator's event match + trust-policy path.
89
+ *
90
+ * An empty array means the event matched no subscriber — a real outcome a
91
+ * gate may declare `optional`. A REFUSAL raises
92
+ * {@link SummonRefusedError} instead, so the two can never be read as the
93
+ * same thing.
94
+ */
95
+ summon: (args: SummonArgs) => Promise<SummonedRun[]>;
96
+ /** Max chain depth allowed for a summon (the circuit breaker's bound). */
97
+ maxChainDepth: number;
98
+ }
99
+ /** Arguments identifying the gate to run. */
100
+ export interface RunInvokeGateArgs {
101
+ runId: string;
102
+ gateJobId: string;
103
+ gateJobName: string;
104
+ event: string;
105
+ payload?: Record<string, unknown>;
106
+ /** When true, a zero-subscriber emit succeeds; when false it fails the gate. */
107
+ optional: boolean;
108
+ sourceRepo: string;
109
+ chainDepth: number;
110
+ /** Fan-out concurrency width stamped on the proxy children (`maxParallel`). */
111
+ maxParallel?: number;
112
+ /** Fan-out fail-fast policy stamped on the proxy children (`failFast`). */
113
+ failFast?: boolean;
114
+ }
115
+ /**
116
+ * Aggregate an invoke gate's status from its proxies' terminal statuses. The
117
+ * gate stays open (`allTerminal: false`) until every proxy is terminal; then it
118
+ * is `failed` if any proxy is a failure, else `success`. Pure — the tracker's
119
+ * aggregation hook drives the DB reads and the gate transition.
120
+ */
121
+ export declare function aggregateGateStatus(proxyStatuses: readonly string[]): {
122
+ allTerminal: boolean;
123
+ status?: ExecutionJobStatus;
124
+ };
125
+ /**
126
+ * The message a gate naming a reserved event fails with.
127
+ *
128
+ * Exported so the gate executor and its test state the same string, and so the
129
+ * SDK-side error can be compared against it.
130
+ */
131
+ export declare function reservedEventMessage(event: string, prefix: string): string;
132
+ /** The message a require-by-default gate fails with when nothing subscribes. */
133
+ export declare function zeroSubscriberMessage(sourceRepo: string, event: string): string;
134
+ /**
135
+ * The proxy job name for a summoned run — the gate name, a `repo:workflow`
136
+ * variant, and the summoned run id. The run id makes the name **unique per run**:
137
+ * two summoned runs with the same `repo`+`workflow` (duplicate registrations)
138
+ * would otherwise share one `job_name`, so the mirror's `job_name` lookup would
139
+ * resolve one proxy and leave the other hanging. The human-readable label for the
140
+ * graph is carried separately in `variant_label` (`repo:workflow`).
141
+ */
142
+ export declare function proxyJobName(gateJobName: string, run: SummonedRun): string;
143
+ /**
144
+ * Run an invoke gate: summon the source repo's subscribers and create one proxy
145
+ * child per spawned run. A zero-subscriber emit terminalizes the gate — failed
146
+ * by default, success under `optional`. When the chain-depth bound is reached
147
+ * the gate fails without summoning, so an invoke chain cannot loop.
148
+ */
149
+ export declare function runInvokeGate(deps: InvokeGateDeps, args: RunInvokeGateArgs): Promise<void>;
150
+ /** Dependencies for {@link releaseInvokeGate}. */
151
+ export interface ReleaseInvokeGateDeps {
152
+ db: Kysely<Database>;
153
+ executionTracker: ExecutionTracker;
154
+ invokeGateDeps: InvokeGateDeps;
155
+ }
156
+ /**
157
+ * Release a ready invoke gate: swap its synthetic needs-pending row for a real
158
+ * `gate` job row, then run the gate executor. Reads the summoning run's source
159
+ * repo and chain depth from `execution_runs` (the chain depth bounds recursion).
160
+ * Shared by the needs-release path (`dispatchReadyJob`) and the root-gate
161
+ * post-registration nudge, so both go through one code path.
162
+ */
163
+ export declare function releaseInvokeGate(deps: ReleaseInvokeGateDeps, runId: string, gateJobName: string, params: InvokeGateParams): Promise<void>;
164
+ //# sourceMappingURL=invoke-gate.d.ts.map
@@ -31,6 +31,19 @@ export interface SchedulerResult {
31
31
  action: 'dispatch' | 'skip';
32
32
  reason?: string;
33
33
  }
34
+ /**
35
+ * Check if ALL upstreams of a job are satisfied.
36
+ * Returns { satisfied: true, action: 'dispatch' } when every upstream is
37
+ * terminal and its status is a member of the edge's run_on set. Returns
38
+ * { satisfied: true, action: 'skip', reason } when an upstream is terminal but
39
+ * its status is not in the edge's run_on set. Returns { satisfied: false } when
40
+ * any upstream is not yet terminal.
41
+ */
42
+ export declare function checkAllUpstreamsSatisfied(db: Kysely<Database>, runId: string, jobName: string): Promise<{
43
+ satisfied: boolean;
44
+ action?: 'dispatch' | 'skip';
45
+ reason?: string;
46
+ }>;
34
47
  /**
35
48
  * Insert dependency edges at run start for static-to-static needs.
36
49
  *
@@ -21,9 +21,10 @@ import type { LockFile as FullLockFile, SimulatedEvent } from '@kici-dev/engine'
21
21
  import type { OrchestratorMode } from '@kici-dev/engine';
22
22
  import type { WebhookInfo } from '../webhook/handler.js';
23
23
  import type { ProviderBundle } from '../provider-registry.js';
24
- import type { TrustResolution } from '../security/trust-resolver.js';
24
+ import { type TrustResolution } from '../security/trust-resolver.js';
25
25
  import { type TrustPolicyOutcome } from '../security/trust-policy-gate.js';
26
26
  import { type WorkflowModification } from '../security/workflow-diff.js';
27
+ import type { IdentityLink, PermissionLevel } from '../security/identity-link.js';
27
28
  import { type ProcessingDeps } from './processor.js';
28
29
  /**
29
30
  * Outcome of a single inbound-webhook ingestion. `duplicate` lets a direct-
@@ -45,27 +46,76 @@ export declare const WebhookIngestOutcome: z.ZodEnum<{
45
46
  skipped: "skipped";
46
47
  }>;
47
48
  export type WebhookIngestOutcome = z.infer<typeof WebhookIngestOutcome>;
49
+ /**
50
+ * Resolve the directory a `/kici approve` comment is authorized against.
51
+ *
52
+ * Two sources, and which one applies is decided by the caller that assembled
53
+ * these deps rather than by the orchestrator's mode:
54
+ *
55
+ * - `server.ts` holds the Platform-pushed directory in memory and refreshes it
56
+ * on every `trust_policy.update`, so it always supplies both fields. An
57
+ * in-memory directory therefore wins outright — reading the row instead
58
+ * would answer from a cache the push has already superseded.
59
+ * - Every other assembly supplies neither. That includes the direct-ingress
60
+ * pipeline in `app.ts`, which is the only path an independent orchestrator
61
+ * has, and where the operator's own `kici-admin trust-policy directory-set`
62
+ * registrations live only in the row. Reading it per command is also what
63
+ * makes a registration take effect immediately rather than at the next
64
+ * restart.
65
+ *
66
+ * A failed read degrades to an empty directory: the commenter is not resolved
67
+ * and the command is refused, which is the same fail-closed answer an absent
68
+ * directory already gives.
69
+ */
70
+ export declare function resolveApprovalDirectory(deps: ProcessingDeps, orgId: string): Promise<{
71
+ identityLinks: IdentityLink[];
72
+ orgMemberPermissions: Map<string, PermissionLevel>;
73
+ }>;
48
74
  interface TrustOutcome {
49
75
  trustResolution: TrustResolution | undefined;
50
76
  /** Default 'base' for PR events; trust resolution may override to 'head'. */
51
77
  lockFileSource: 'head' | 'base';
52
78
  }
79
+ /** Whether an inbound event name is one of the pull-request events. */
80
+ export declare function isPullRequestEvent(eventName: string): boolean;
53
81
  export declare function resolveTrustForPR(args: {
54
82
  info: WebhookInfo;
55
- deps: ProcessingDeps;
56
83
  bundle: ProviderBundle;
57
84
  event: SimulatedEvent;
85
+ /** Raw webhook payload — read only to decide whether a push targets the default branch. */
58
86
  payload: Record<string, unknown>;
59
- resolvedOrgId: string;
60
- repoIdentifier: string;
61
- credentials: Record<string, unknown>;
62
87
  }): Promise<TrustOutcome>;
88
+ /** What one organization-wide dispatch pass produced. */
89
+ interface GlobalDispatchOutcome {
90
+ matchedCount: number;
91
+ matchedRunIds: string[];
92
+ /** One summary per global decision this pass reached, dispatched or excluded. */
93
+ decisionSummaries: Record<string, unknown>[];
94
+ /**
95
+ * The workflow repository of every evaluation round this pass could not
96
+ * decide. Empty means every round the pass ran reached a verdict.
97
+ */
98
+ roundFailureWorkflowRepos: string[];
99
+ /**
100
+ * Every workflow repository this pass actually evaluated: it examined that
101
+ * repository's registrations and reached a real outcome for each of them —
102
+ * dispatched, filtered out, or excluded by a verdict.
103
+ *
104
+ * The pass's POSITIVE signal, and the one a re-run gates its success check
105
+ * on. An empty `roundFailureWorkflowRepos` cannot carry that meaning: a pass
106
+ * with no registration index, no pending-eval tracker, or an event the trust
107
+ * policy held evaluates nothing and reports no failure either, so reading its
108
+ * silence as a clean verdict is the false assurance the round exists to
109
+ * remove.
110
+ */
111
+ decidedWorkflowRepos: string[];
112
+ }
63
113
  interface SecurityState {
64
114
  workflowModifications: WorkflowModification[];
65
115
  /**
66
- * True when this PR changes `.kici/` workflow definitions. A *signal*, not a
67
- * decision the trust-policy gate turns it into an outcome, which the
68
- * dispatch gate then enforces as a held run or a rejected run.
116
+ * True when this PR changes `.kici/` workflow definitions. Reported, not
117
+ * enforced: the org trust policy is the fork switch, and it reads the fork
118
+ * signal and the tier never this one.
69
119
  */
70
120
  hasWorkflowModifications: boolean;
71
121
  }
@@ -74,10 +124,11 @@ interface SecurityState {
74
124
  * modifications by diffing base vs. head and post a neutral informational check
75
125
  * (on its own dedicated check name).
76
126
  *
77
- * Detection only. Whether a modification gates the run is the org trust policy's
78
- * call, evaluated in `evaluateSecurityPolicy` and enforced by the dispatch gate
79
- * which creates the real `held_runs` row and posts the pending
80
- * "Held for approval" check, so `/kici approve` / reject / expiry can resolve it.
127
+ * Detection and legibility only: the check tells a reviewer that this PR edits
128
+ * the workflows it would run under. It feeds no policy decision — a PR that
129
+ * edits `.kici/` from the base repo is written by a contributor who could
130
+ * already push to it, and one from a fork is governed by the fork switch
131
+ * whether or not it touches `.kici/`.
81
132
  */
82
133
  export declare function applyWorkflowModificationsAndSecurityHold(args: {
83
134
  info: WebhookInfo;
@@ -95,11 +146,17 @@ export declare function applyWorkflowModificationsAndSecurityHold(args: {
95
146
  /**
96
147
  * Evaluate the org trust policy for a PR event.
97
148
  *
98
- * Scoped to providers with a contributor model the same condition trust
99
- * resolution uses. A source without a `ContributorResolver` (generic, local,
100
- * universal-git) is trusted by construction, so it passes regardless of policy;
101
- * in particular, universal-git computes an `isForkPR` signal that must NOT gate
102
- * those sources.
149
+ * Scoped to providers with a fork model (`bundle.hasForkModel`): a source
150
+ * without one passes regardless of policy. That is not a trust claim about such
151
+ * a source — a PR from a fork-less provider resolves no tier at all. It is that
152
+ * the policy's one condition cannot be established for it: universal-git does
153
+ * compute an `isForkPR`, but from payload keys many forges omit, and it reads
154
+ * `false` when they are absent. Gating on a signal that fails toward trust
155
+ * would be worse than not gating.
156
+ *
157
+ * The policy read has two distinct failure shapes, and they get distinct
158
+ * answers: no stored row is the unconfigured default (`FAIL_CLOSED_POLICY`),
159
+ * while a read that THREW gets `READ_FAILURE_POLICY` — see there for why.
103
160
  */
104
161
  export declare function evaluateSecurityPolicy(args: {
105
162
  deps: ProcessingDeps;
@@ -109,23 +166,54 @@ export declare function evaluateSecurityPolicy(args: {
109
166
  mode: OrchestratorMode;
110
167
  trustResolution: TrustResolution | undefined;
111
168
  isForkPR: boolean;
112
- hasWorkflowModifications: boolean;
113
169
  }): Promise<TrustPolicyOutcome>;
114
170
  /**
115
- * Process a webhook through the complete pipeline.
171
+ * Phase J After per-repo workflows are dispatched, query the global index
172
+ * for workflows authored in OTHER repos that target this event type via
173
+ * cross-repo `repos` patterns. Same-org scope picks up both same-source and
174
+ * cross-source globals (cross-provider global workflows).
175
+ */
176
+ export declare function dispatchGlobalWorkflowsForOtherRepos(args: {
177
+ info: WebhookInfo;
178
+ deps: ProcessingDeps;
179
+ eventWithFiles: SimulatedEvent;
180
+ resolvedOrgId: string;
181
+ repoIdentifier: string;
182
+ ref: string;
183
+ dispatchBundle: ProviderBundle;
184
+ dispatchCredentials: Record<string, unknown>;
185
+ /** The inbound event's own bundle, used to post the globals-skipped check. */
186
+ bundle: ProviderBundle;
187
+ credentials: Record<string, unknown>;
188
+ securityDecision: TrustPolicyOutcome;
189
+ /**
190
+ * The source `dispatchCredentials` belongs to, when a cross-provider lock-file
191
+ * fallback made it a different source from `info.routingKey`. Persisted on a
192
+ * failed round's run row so its re-run can rebuild the same dispatch pair
193
+ * instead of handing this source's credentials to the inbound source's client.
194
+ */
195
+ dispatchRoutingKey?: string;
196
+ /**
197
+ * Restrict the pass to global workflows authored in this repository. Set by
198
+ * the re-run of a failed evaluation round, which re-decides that round alone.
199
+ */
200
+ onlyWorkflowRepo?: string;
201
+ }): Promise<GlobalDispatchOutcome>;
202
+ /**
203
+ * Process a webhook through the complete pipeline, recording a `failed` event-log
204
+ * row for a delivery the pipeline could not finish.
205
+ *
206
+ * The row is the only per-delivery record of such a failure. Nothing the pipeline
207
+ * decides after acknowledgement can reach the sender — `acceptWebhookDelivery`
208
+ * answers 202 and runs this detached — and the dashboard's webhook-activity view
209
+ * counts `event_log.status = 'failed'` for exactly this. Without the write that
210
+ * count is structurally always zero, and a post-acknowledgement failure appears
211
+ * in the orchestrator's own logs and nowhere a customer can see.
116
212
  *
117
- * Flow:
118
- * 1. Dedup + provider lookup -> contributor cache invalidation -> normalize
119
- * 2. Cross-source dispatch (generic webhook fan-out, optional)
120
- * 3. Extract repo + credentials, handle /kici approval comments
121
- * 4. Trust resolution for PR events
122
- * 5. Lock file fetch (with multi-provider fallback)
123
- * - No lock file: dispatch global workflows for this event (same org) and return
124
- * 6. Workflow modification detection + security hold check status
125
- * 7. Default-branch registration
126
- * 8. Match triggers + dispatch matched same-source workflows
127
- * 9. Match + dispatch global workflows for OTHER repos
128
- * 10. Forward Platform trace + record event log
213
+ * Best-effort and non-masking: the write is wrapped, and the original error is
214
+ * rethrown either way. The delivery still reverts to `buffered` for the drain
215
+ * pass to retry, so a redelivery that succeeds writes its own `processed` row
216
+ * beside this one — the two are distinct rows, not a mutation.
129
217
  */
130
218
  export declare function processWebhook(info: WebhookInfo, deps: ProcessingDeps): Promise<WebhookIngestOutcome>;
131
219
  export {};