@kici-dev/orchestrator 0.15.0 → 0.16.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 (146) hide show
  1. package/dist/__test-helpers__/scratch-db.d.ts +16 -0
  2. package/dist/agent/registry.d.ts +1 -8
  3. package/dist/app-on-error.d.ts +1 -1
  4. package/dist/artifacts/artifact-store.d.ts +0 -2
  5. package/dist/audit/access-log.d.ts +1 -1
  6. package/dist/cache/source-cache.d.ts +1 -1
  7. package/dist/cache/user-cache.d.ts +0 -28
  8. package/dist/cli/api-client.d.ts +27 -1
  9. package/dist/cli/commands/access-log.d.ts +1 -1
  10. package/dist/cli/commands/cold-store-impl.d.ts +7 -84
  11. package/dist/cli/commands/db.d.ts +1 -0
  12. package/dist/cli/commands/orchestrator-service/status.d.ts +2 -0
  13. package/dist/cli/commands/org.d.ts +12 -0
  14. package/dist/cli/commands/scaler.d.ts +14 -0
  15. package/dist/cli/commands/secret.d.ts +1 -45
  16. package/dist/cli/commands/service-health.d.ts +21 -13
  17. package/dist/cli/commands/shared/cli-action.d.ts +17 -0
  18. package/dist/cli/commands/shared/db.d.ts +5 -5
  19. package/dist/cli/commands/shared/org-id.d.ts +5 -0
  20. package/dist/cli/commands/shared/service-verbs.d.ts +19 -0
  21. package/dist/cli/commands/shared/table.d.ts +3 -0
  22. package/dist/cli/commands/shared/unbound-context-warning.d.ts +2 -22
  23. package/dist/cli/commands/trust-policy.d.ts +7 -31
  24. package/dist/cli/kici-admin.d.ts +1 -1
  25. package/dist/cli.js +9863 -8201
  26. package/dist/cluster/coordinator.d.ts +2 -2
  27. package/dist/cluster/peer-client.d.ts +27 -1
  28. package/dist/cluster/peer-forget.d.ts +4 -9
  29. package/dist/cluster/peer-handler.d.ts +11 -1
  30. package/dist/cluster/peer-request-waiters.d.ts +26 -0
  31. package/dist/cluster/scaler-orphans-peer.d.ts +4 -9
  32. package/dist/cluster/scaler-reload-fanout.d.ts +29 -0
  33. package/dist/cluster/scaler-reload-peer.d.ts +16 -0
  34. package/dist/cluster/worker-cluster-settings.d.ts +1 -0
  35. package/dist/cold-store/load-access-log-range.d.ts +3 -3
  36. package/dist/cold-store/tables/access-log.d.ts +4 -12
  37. package/dist/cold-store/tables/event-log.d.ts +8 -19
  38. package/dist/cold-store/tables/execution-jobs.d.ts +7 -24
  39. package/dist/cold-store/tables/execution-runs.d.ts +6 -20
  40. package/dist/cold-store/tables/execution-steps.d.ts +8 -26
  41. package/dist/cold-store/tables/secret-audit-log.d.ts +4 -11
  42. package/dist/config/env-overlay.d.ts +3 -8
  43. package/dist/config/reload.d.ts +9 -2
  44. package/dist/config/schema.d.ts +2 -2
  45. package/dist/config/types.d.ts +2 -2
  46. package/dist/config.d.ts +13 -17
  47. package/dist/contexts/held-runs.d.ts +3 -6
  48. package/dist/dashboard/cursor.d.ts +12 -0
  49. package/dist/dashboard/handler.d.ts +5 -12
  50. package/dist/db/deferred-index-build.d.ts +42 -0
  51. package/dist/db/deferred-indexes.d.ts +3 -37
  52. package/dist/db/migrations/100_held_runs_hold_type_vocabulary.d.ts +3 -5
  53. package/dist/db/migrations/158_normalize_legacy_values.d.ts +4 -0
  54. package/dist/db/repos/org-ids-repo.d.ts +87 -0
  55. package/dist/db/schema-snapshot.generated.d.ts +3 -0
  56. package/dist/db/types.d.ts +5 -22
  57. package/dist/fault-injection-types.d.ts +0 -6
  58. package/dist/git/dispatch-git-auth.d.ts +6 -12
  59. package/dist/helpers/json.d.ts +3 -0
  60. package/dist/index.js +133 -136
  61. package/dist/metrics/prometheus.d.ts +0 -5
  62. package/dist/oidc/id-token-claims.d.ts +1 -1
  63. package/dist/oidc/resolve-signer.d.ts +2 -4
  64. package/dist/orchestrator-core.d.ts +10 -3
  65. package/dist/pipeline/global-eval-round.d.ts +2 -26
  66. package/dist/pipeline/install-secrets-resolver.d.ts +0 -3
  67. package/dist/pipeline/manual-schedule.d.ts +1 -7
  68. package/dist/provenance/dispatch-context.d.ts +5 -7
  69. package/dist/provenance/pending-attestations-repo.d.ts +1 -1
  70. package/dist/provenance/trust-root.d.ts +4 -5
  71. package/dist/provenance/verify-deferred-capture.d.ts +3 -3
  72. package/dist/provider-registry.d.ts +13 -40
  73. package/dist/providers/universal-git/ssh-auth.d.ts +0 -5
  74. package/dist/reporting/check-run-reporter.d.ts +8 -21
  75. package/dist/reporting/check-run-summary.d.ts +2 -2
  76. package/dist/reporting/execution-tracker.d.ts +1 -1
  77. package/dist/reporting/log-chunk-sink.d.ts +1 -1
  78. package/dist/reporting/run-summary-fields.d.ts +25 -0
  79. package/dist/reporting/s3-log-storage.d.ts +2 -2
  80. package/dist/routes/admin-access-log.d.ts +2 -9
  81. package/dist/routes/admin-backends.d.ts +2 -9
  82. package/dist/routes/admin-cluster-name.d.ts +2 -8
  83. package/dist/routes/admin-cluster-settings.d.ts +2 -8
  84. package/dist/routes/admin-config.d.ts +0 -3
  85. package/dist/routes/admin-contexts.d.ts +3 -11
  86. package/dist/routes/admin-db.d.ts +2 -9
  87. package/dist/routes/admin-env.d.ts +33 -0
  88. package/dist/routes/admin-event-dlq.d.ts +2 -9
  89. package/dist/routes/admin-event-inspect.d.ts +2 -9
  90. package/dist/routes/admin-event-log.d.ts +2 -9
  91. package/dist/routes/admin-events.d.ts +3 -13
  92. package/dist/routes/admin-held-runs.d.ts +2 -8
  93. package/dist/routes/admin-list-limit.d.ts +3 -0
  94. package/dist/routes/admin-maintenance.d.ts +2 -9
  95. package/dist/routes/admin-orchestrator-drain.d.ts +2 -9
  96. package/dist/routes/admin-org-settings.d.ts +2 -8
  97. package/dist/routes/admin-orgs.d.ts +27 -0
  98. package/dist/routes/admin-peer-forget.d.ts +2 -9
  99. package/dist/routes/admin-queue-execution.d.ts +3 -10
  100. package/dist/routes/admin-registrations.d.ts +3 -14
  101. package/dist/routes/admin-runs.d.ts +3 -14
  102. package/dist/routes/admin-scaler-orphans.d.ts +2 -9
  103. package/dist/routes/admin-scaler-reload.d.ts +41 -0
  104. package/dist/routes/admin-scheduled-jobs.d.ts +2 -9
  105. package/dist/routes/admin-signing-keys.d.ts +2 -9
  106. package/dist/routes/admin-sources.d.ts +2 -9
  107. package/dist/routes/admin-trust-policy.d.ts +2 -8
  108. package/dist/routes/admin.d.ts +15 -13
  109. package/dist/scaler/config.d.ts +12 -84
  110. package/dist/scaler/file-reload.d.ts +48 -0
  111. package/dist/scaler/manager.d.ts +47 -33
  112. package/dist/scaler/scaler-state-store.d.ts +5 -0
  113. package/dist/scaler/types.d.ts +1 -4
  114. package/dist/secrets/job-secret-gate.d.ts +2 -5
  115. package/dist/secrets/secret-resolver.d.ts +2 -4
  116. package/dist/security/trust-policy-gate.d.ts +0 -4
  117. package/dist/server.d.ts +6 -17
  118. package/dist/server.js +6693 -8005
  119. package/dist/standalone.js +5826 -6555
  120. package/dist/worker/reroute-dispatch.d.ts +1 -0
  121. package/dist/worker/reroute-spawn-control.d.ts +1 -3
  122. package/dist/worker-core.d.ts +9 -2
  123. package/dist/ws/agent-handler.d.ts +3 -3
  124. package/dist/ws/dashboard-access.d.ts +57 -0
  125. package/dist/ws/dashboard-backends-handler.d.ts +1 -6
  126. package/dist/ws/dashboard-context-handler.d.ts +10 -15
  127. package/dist/ws/dashboard-fleet-write-handler.d.ts +1 -6
  128. package/dist/ws/dashboard-global-workflows-handler.d.ts +1 -6
  129. package/dist/ws/dashboard-registrations-handler.d.ts +1 -7
  130. package/dist/ws/platform-client.d.ts +85 -330
  131. package/installer-image-digests.json +3 -3
  132. package/package.json +4 -4
  133. package/sbom.spdx.json +53 -53
  134. package/dist/cache/legacy-prefixes.d.ts +0 -19
  135. package/dist/cli/commands/agent-service/logs.d.ts +0 -14
  136. package/dist/cli/commands/agent-service/restart.d.ts +0 -12
  137. package/dist/cli/commands/agent-service/start.d.ts +0 -12
  138. package/dist/cli/commands/agent-service/stop.d.ts +0 -12
  139. package/dist/cli/commands/agent-service/uninstall.d.ts +0 -13
  140. package/dist/cli/commands/cache.d.ts +0 -51
  141. package/dist/cli/commands/orchestrator-service/logs.d.ts +0 -14
  142. package/dist/cli/commands/orchestrator-service/restart.d.ts +0 -12
  143. package/dist/cli/commands/orchestrator-service/start.d.ts +0 -12
  144. package/dist/cli/commands/orchestrator-service/stop.d.ts +0 -12
  145. package/dist/cli/commands/orchestrator-service/uninstall.d.ts +0 -13
  146. package/dist/git/inherited-secret.d.ts +0 -29
package/dist/index.js CHANGED
@@ -10,8 +10,9 @@ import { createHash, createHmac, randomBytes, randomUUID } from "node:crypto";
10
10
  import { chmod, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
11
11
  import { Kysely, PostgresDialect, sql } from "kysely";
12
12
  import pg from "pg";
13
- import { DEFAULT_APPROVAL_EXPIRY_HOURS, DEFAULT_APPROVAL_EXPIRY_SECONDS, ForkPolicy, HoldScope, HoldType, OrchRole, TriggerSource, approvalExpiryHoursOf, approvalExpirySecondsOf, persistedHoldTypeSpellings } from "@kici-dev/engine";
13
+ import { DEFAULT_APPROVAL_EXPIRY_SECONDS, ForkPolicy, HoldScope, HoldType, OrchRole, TriggerSource } from "@kici-dev/engine";
14
14
  import { z } from "zod";
15
+ import { ORCHESTRATOR_DEFAULT_PORT } from "@kici-dev/shared/env";
15
16
  import { platform, tmpdir } from "node:os";
16
17
  import { createInterface } from "node:readline";
17
18
  import { Octokit } from "@octokit/rest";
@@ -702,7 +703,7 @@ function createCacheStorage(config) {
702
703
  * defect this layout closes — so serving it would serve the bug. For a v6 lock
703
704
  * the fallback is unreachable anyway, since the schema bump moves every hash.
704
705
  * Old objects are orphaned. The cache TTL is enforced lazily on access, so
705
- * nothing here removes them; `kici-admin cache purge-legacy` does.
706
+ * nothing here removes them.
706
707
  */
707
708
  const logger$4 = createLogger({ prefix: "source-cache" });
708
709
  /**
@@ -1105,8 +1106,7 @@ const DEFAULT_USER_CACHE_TTL_MS = 6048e5;
1105
1106
  const TAR_SUFFIX = ".tar.gz";
1106
1107
  /**
1107
1108
  * Stem prefix of an in-flight upload, `.tmp-<uuid>.tar.gz`: the presigned PUT
1108
- * target a save lands on before commit copies it to its final key. Shared with
1109
- * the retired-layout classifier so both agree on what an upload looks like.
1109
+ * target a save lands on before commit copies it to its final key.
1110
1110
  */
1111
1111
  const TEMP_UPLOAD_STEM = ".tmp-";
1112
1112
  /** Whether a listed object is an in-flight upload rather than a cache entry. */
@@ -1122,7 +1122,7 @@ const KEY_DISCRIMINATOR_TAIL = new RegExp(`^(.*)-([0-9a-f]{32})$`);
1122
1122
  * is what keeps two other kinds of object out of a restore: an in-flight
1123
1123
  * `.tmp-<uuid>.tar.gz` upload, which is a partial tarball until commit copies
1124
1124
  * it to its final key, and an object under the retired un-discriminated layout,
1125
- * which nothing writes and which `kici-admin cache purge-legacy` removes.
1125
+ * which nothing writes.
1126
1126
  */
1127
1127
  function isCommittedEntry(key) {
1128
1128
  return key.endsWith(TAR_SUFFIX) && KEY_DISCRIMINATOR_TAIL.test(key.slice(0, -7));
@@ -1147,9 +1147,6 @@ function stripKeyDiscriminator(stem) {
1147
1147
  * signature on a pre-signed PUT/GET. Repo identifiers like `.` (the internal
1148
1148
  * provider's repo id) hit exactly this case, so the all-dots guard keeps the
1149
1149
  * object key canonical and the namespace boundary intact.
1150
- *
1151
- * Exported so an operator tool that addresses an org's prefix from a raw org id
1152
- * (`kici-admin cache purge-legacy --org`) lands on the segment the writer used.
1153
1150
  */
1154
1151
  function sanitizeSegment(s) {
1155
1152
  const cleaned = s.replace(/[^A-Za-z0-9._-]/g, "_");
@@ -1199,10 +1196,8 @@ function finalKey(prefix, key) {
1199
1196
  }
1200
1197
  /**
1201
1198
  * The object key a committed save under `key` lands on for this ref. This is
1202
- * the writer's own path — `beginSave` and `commitSave` address the entry
1203
- * through it — so anything that must recognise a current-layout key (the
1204
- * legacy-layout classifier tests) takes it from here rather than restating
1205
- * the format.
1199
+ * the writer's own path: `beginSave` and `commitSave` address the entry
1200
+ * through it.
1206
1201
  */
1207
1202
  function userCacheEntryKey(ref, key) {
1208
1203
  return finalKey(writePrefix(ref), key);
@@ -1451,7 +1446,7 @@ var PeerCredentialStore = class {
1451
1446
  }
1452
1447
  /** The most recently revoked credential row for `instanceId`, or null. */
1453
1448
  async findLatestRevokedByInstanceId(instanceId) {
1454
- const row = await this.db.selectFrom("peer_credentials").selectAll().where("instance_id", "=", instanceId).where("revoked_at", "is not", null).orderBy("revoked_at", "desc").executeTakeFirst();
1449
+ const row = await this.db.selectFrom("peer_credentials").selectAll().where("instance_id", "=", instanceId).where("revoked_at", "is not", null).orderBy("revoked_at", "desc").orderBy("created_at", "desc").executeTakeFirst();
1455
1450
  return row ? mapRow(row) : null;
1456
1451
  }
1457
1452
  /**
@@ -2324,18 +2319,15 @@ var HeldRunStore = class {
2324
2319
  * incidental: `expireOverdue` is not scope-filtered, so a released-but-not-yet-
2325
2320
  * resumed row would otherwise be expired out from under its resume.
2326
2321
  *
2327
- * The filter matches every persisted spelling of the timer hold type, so a row
2328
- * an un-upgraded orchestrator wrote as `wait_timer` still resumes rather than
2329
- * falling through to the expire-and-fail sweep. It deliberately does NOT
2330
- * filter on `hold_scope`: a job-scoped timer hold used to be excluded here,
2331
- * which left it with no release path at all — created, never released,
2332
- * eventually expired, its job never dispatched.
2322
+ * The filter deliberately does NOT filter on `hold_scope`: a job-scoped timer
2323
+ * hold used to be excluded here, which left it with no release path at all —
2324
+ * created, never released, eventually expired, its job never dispatched.
2333
2325
  */
2334
2326
  async releaseDueWaitHolds() {
2335
2327
  return (await this.db.updateTable("held_runs").set({
2336
2328
  status: "released",
2337
2329
  resolved_at: sql`now()`
2338
- }).where("status", "=", "pending").where("hold_type", "in", persistedHoldTypeSpellings(HoldType.enum.timer)).where("expires_at", "<", sql`now()`).returningAll().execute()).map((row) => ({
2330
+ }).where("status", "=", "pending").where("hold_type", "=", HoldType.enum.timer).where("expires_at", "<", sql`now()`).returningAll().execute()).map((row) => ({
2339
2331
  holdId: row.id,
2340
2332
  runId: row.run_id,
2341
2333
  jobId: row.job_id,
@@ -2353,7 +2345,7 @@ var HeldRunStore = class {
2353
2345
  * `listQueuedHoldsForContext` fetches the rows it actually releases.
2354
2346
  */
2355
2347
  async listAllQueuedHolds() {
2356
- return await this.db.selectFrom("held_runs").innerJoin("execution_runs", "execution_runs.run_id", "held_runs.run_id").select(["held_runs.org_id as orgId", "execution_runs.context as concurrencyGroup"]).where("held_runs.status", "=", "pending").where("held_runs.hold_type", "in", persistedHoldTypeSpellings(HoldType.enum.concurrency)).execute();
2348
+ return await this.db.selectFrom("held_runs").innerJoin("execution_runs", "execution_runs.run_id", "held_runs.run_id").select(["held_runs.org_id as orgId", "execution_runs.context as concurrencyGroup"]).where("held_runs.status", "=", "pending").where("held_runs.hold_type", "=", HoldType.enum.concurrency).execute();
2357
2349
  }
2358
2350
  /**
2359
2351
  * List the pending queued (concurrency) holds for one context's concurrency
@@ -2366,7 +2358,7 @@ var HeldRunStore = class {
2366
2358
  * starve whoever waited longest.
2367
2359
  */
2368
2360
  async listQueuedHoldsForContext(orgId, concurrencyGroup) {
2369
- return this.db.selectFrom("held_runs").innerJoin("execution_runs", "execution_runs.run_id", "held_runs.run_id").selectAll("held_runs").where("held_runs.org_id", "=", orgId).where("held_runs.status", "=", "pending").where("held_runs.hold_type", "in", persistedHoldTypeSpellings(HoldType.enum.concurrency)).where("execution_runs.context", "=", concurrencyGroup).where("execution_runs.customer_id", "=", orgId).orderBy("held_runs.created_at", "asc").execute();
2361
+ return this.db.selectFrom("held_runs").innerJoin("execution_runs", "execution_runs.run_id", "held_runs.run_id").selectAll("held_runs").where("held_runs.org_id", "=", orgId).where("held_runs.status", "=", "pending").where("held_runs.hold_type", "=", HoldType.enum.concurrency).where("execution_runs.context", "=", concurrencyGroup).where("execution_runs.customer_id", "=", orgId).orderBy("held_runs.created_at", "asc").execute();
2370
2362
  }
2371
2363
  /**
2372
2364
  * Expire overdue pending runs. Called by the stale detector.
@@ -2406,12 +2398,10 @@ function createHeldRunStoreFromUrl(databaseUrl, opts) {
2406
2398
  const DEFAULT_FORK_POLICY = ForkPolicy.enum.ignore;
2407
2399
  Object.freeze({
2408
2400
  forkPolicy: DEFAULT_FORK_POLICY,
2409
- approvalExpiryHours: DEFAULT_APPROVAL_EXPIRY_HOURS,
2410
2401
  approvalExpirySeconds: DEFAULT_APPROVAL_EXPIRY_SECONDS
2411
2402
  });
2412
2403
  Object.freeze({
2413
2404
  forkPolicy: ForkPolicy.enum.hold,
2414
- approvalExpiryHours: DEFAULT_APPROVAL_EXPIRY_HOURS,
2415
2405
  approvalExpirySeconds: DEFAULT_APPROVAL_EXPIRY_SECONDS
2416
2406
  });
2417
2407
  z.enum([
@@ -2441,34 +2431,9 @@ const TrustPolicySource = z.enum(["platform", "local"]);
2441
2431
  */
2442
2432
  const DEFAULT_TRUST_POLICY = {
2443
2433
  forkPolicy: DEFAULT_FORK_POLICY,
2444
- approvalExpiryHours: DEFAULT_APPROVAL_EXPIRY_HOURS,
2445
2434
  approvalExpirySeconds: DEFAULT_APPROVAL_EXPIRY_SECONDS
2446
2435
  };
2447
2436
  /**
2448
- * Resolve a local patch's approval-expiry window against what is already stored.
2449
- *
2450
- * One value is being set, spelled two ways, so the pair is merged as a unit and
2451
- * always re-derived from the winning seconds value:
2452
- *
2453
- * - a patch naming `approvalExpirySeconds` sets the window, and the hours field
2454
- * is recomputed from it — a stale hours value left behind would be the coarse
2455
- * view of a window that is no longer stored;
2456
- * - a patch naming only `approvalExpiryHours` sets the window too, and the
2457
- * seconds field is recomputed from it — keeping the old seconds value would
2458
- * let it out-rank the operator's explicit hours and silently discard the
2459
- * write;
2460
- * - a patch naming both takes the seconds value, because it is the more
2461
- * specific of the two (`approvalExpirySecondsOf`);
2462
- * - a patch naming neither leaves the stored window untouched.
2463
- */
2464
- function mergedExpiry(patch, existing) {
2465
- const seconds = (patch.approvalExpirySeconds != null || patch.approvalExpiryHours != null ? approvalExpirySecondsOf(patch) : null) ?? existing?.approvalExpirySeconds ?? DEFAULT_TRUST_POLICY.approvalExpirySeconds ?? DEFAULT_APPROVAL_EXPIRY_SECONDS;
2466
- return {
2467
- approvalExpiryHours: approvalExpiryHoursOf(seconds),
2468
- approvalExpirySeconds: seconds
2469
- };
2470
- }
2471
- /**
2472
2437
  * Namespace for the per-org advisory lock taken by `upsertLocal`, so the key
2473
2438
  * cannot collide with another feature's advisory lock in the same database.
2474
2439
  */
@@ -2484,11 +2449,7 @@ var TrustPolicyStore = class {
2484
2449
  if (!row) return null;
2485
2450
  return {
2486
2451
  forkPolicy: row.fork_policy,
2487
- approvalExpiryHours: Number(row.approval_expiry_hours),
2488
- approvalExpirySeconds: approvalExpirySecondsOf({
2489
- approvalExpiryHours: Number(row.approval_expiry_hours),
2490
- approvalExpirySeconds: row.approval_expiry_seconds == null ? null : Number(row.approval_expiry_seconds)
2491
- }),
2452
+ approvalExpirySeconds: Number(row.approval_expiry_seconds),
2492
2453
  source: row.source,
2493
2454
  updatedAt: row.updated_at
2494
2455
  };
@@ -2528,7 +2489,7 @@ var TrustPolicyStore = class {
2528
2489
  const existing = await this.get(orgId, trx);
2529
2490
  const merged = {
2530
2491
  forkPolicy: patch.forkPolicy ?? existing?.forkPolicy ?? DEFAULT_TRUST_POLICY.forkPolicy,
2531
- ...mergedExpiry(patch, existing)
2492
+ approvalExpirySeconds: patch.approvalExpirySeconds ?? existing?.approvalExpirySeconds ?? DEFAULT_TRUST_POLICY.approvalExpirySeconds
2532
2493
  };
2533
2494
  await this.write(orgId, merged, TrustPolicySource.enum.local, trx);
2534
2495
  await onWrite?.(trx, merged);
@@ -2536,11 +2497,9 @@ var TrustPolicyStore = class {
2536
2497
  });
2537
2498
  }
2538
2499
  async write(orgId, policy, source, executor = this.db) {
2539
- const seconds = approvalExpirySecondsOf(policy);
2540
2500
  const columns = {
2541
2501
  fork_policy: policy.forkPolicy,
2542
- approval_expiry_hours: approvalExpiryHoursOf(seconds),
2543
- approval_expiry_seconds: seconds,
2502
+ approval_expiry_seconds: policy.approvalExpirySeconds,
2544
2503
  source,
2545
2504
  updated_at: sql`now()`
2546
2505
  };
@@ -2629,6 +2588,8 @@ function firstCauseMessage(err) {
2629
2588
  function isTransportFailure(err) {
2630
2589
  return err instanceof TypeError && err.message === "fetch failed";
2631
2590
  }
2591
+ /** The admin API address kici-admin dials when neither --url nor KICI_ADMIN_URL is set. */
2592
+ const DEFAULT_ADMIN_URL = `http://localhost:${ORCHESTRATOR_DEFAULT_PORT}`;
2632
2593
  async function fetchAdminApi(url, init, baseUrl) {
2633
2594
  try {
2634
2595
  return await fetch(url, init);
@@ -2639,9 +2600,20 @@ async function fetchAdminApi(url, init, baseUrl) {
2639
2600
  }
2640
2601
  const detail = firstCauseMessage(err);
2641
2602
  const cause = detail ? ` (${detail})` : "";
2642
- throw new Error(`cannot reach the orchestrator admin API at ${baseUrl}${cause}. Set KICI_ADMIN_URL to the orchestrator's HTTP address, or pass --base-url where the subcommand accepts it. Note KICI_ORCHESTRATOR_URL is NOT read by this CLI; if other subcommands appear to work, they are using the --database-url / KICI_DATABASE_URL direct-DB path rather than HTTP.`, { cause: err });
2603
+ const defaultHint = baseUrl === DEFAULT_ADMIN_URL ? ` ${DEFAULT_ADMIN_URL} is kici-admin's built-in default, the orchestrator's default port on this host; an orchestrator on another host, or one whose KICI_PORT sets another port, needs KICI_ADMIN_URL or --url.` : "";
2604
+ throw new Error(`cannot reach the orchestrator admin API at ${baseUrl}${cause}. Set KICI_ADMIN_URL to the orchestrator's HTTP address, or pass --base-url where the subcommand accepts it. Note KICI_ORCHESTRATOR_URL is NOT read by this CLI; if other subcommands appear to work, they are using the --database-url / KICI_DATABASE_URL direct-DB path rather than HTTP.${defaultHint}`, { cause: err });
2643
2605
  }
2644
2606
  }
2607
+ /** Build a `?a=1&b=2` query string, skipping undefined, null and empty-string values. */
2608
+ function toQuery(params) {
2609
+ const usp = new URLSearchParams();
2610
+ for (const [k, v] of Object.entries(params)) {
2611
+ if (v === void 0 || v === null || v === "") continue;
2612
+ usp.set(k, String(v));
2613
+ }
2614
+ const qs = usp.toString();
2615
+ return qs ? `?${qs}` : "";
2616
+ }
2645
2617
  var AdminApiClient = class {
2646
2618
  baseUrl;
2647
2619
  token;
@@ -2841,17 +2813,17 @@ var AdminApiClient = class {
2841
2813
  return this.request("POST", `/api/v1/admin/generic-sources/${encodeURIComponent(id)}/disable`);
2842
2814
  }
2843
2815
  async queryAudit(opts) {
2844
- const params = new URLSearchParams();
2845
- if (opts?.contextName) params.set("contextName", opts.contextName);
2846
- if (opts?.routingKey) params.set("routingKey", opts.routingKey);
2847
- if (opts?.action) params.set("action", opts.action);
2848
- if (opts?.from) params.set("from", opts.from);
2849
- if (opts?.to) params.set("to", opts.to);
2850
- if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
2851
- if (opts?.offset !== void 0) params.set("offset", String(opts.offset));
2852
- if (opts?.includeArchived) params.set("includeArchived", "true");
2853
- const qs = params.toString();
2854
- return (await this.request("GET", `/api/v1/admin/audit${qs ? `?${qs}` : ""}`)).entries;
2816
+ const qs = toQuery({
2817
+ contextName: opts?.contextName,
2818
+ routingKey: opts?.routingKey,
2819
+ action: opts?.action,
2820
+ from: opts?.from,
2821
+ to: opts?.to,
2822
+ limit: opts?.limit,
2823
+ offset: opts?.offset,
2824
+ includeArchived: opts?.includeArchived || void 0
2825
+ });
2826
+ return (await this.request("GET", `/api/v1/admin/audit${qs}`)).entries;
2855
2827
  }
2856
2828
  async createToken(data) {
2857
2829
  return this.request("POST", "/api/v1/admin/tokens", data);
@@ -2942,7 +2914,23 @@ var AdminApiClient = class {
2942
2914
  * other failure throws.
2943
2915
  */
2944
2916
  async forgetPeer(body) {
2945
- const res = await fetchAdminApi(`${this.baseUrl}/api/v1/admin/peers/forget`, {
2917
+ const { res, json, text } = await this.postForBody("/api/v1/admin/peers/forget", body);
2918
+ if (res.ok) return json;
2919
+ if (res.status === 409 && json.acknowledgementRequired === true) return {
2920
+ instanceId: body.instanceId,
2921
+ results: json.results ?? [],
2922
+ acknowledgementRequired: true,
2923
+ ...json.error ? { error: json.error } : {}
2924
+ };
2925
+ throw new Error(`HTTP ${res.status}: ${json.error ?? text}`);
2926
+ }
2927
+ /**
2928
+ * POST `body` and parse the answer whatever its status, for a route that
2929
+ * reports per-instance results on a non-2xx status too. An unparseable body
2930
+ * reads as `{}`.
2931
+ */
2932
+ async postForBody(path, body) {
2933
+ const res = await fetchAdminApi(`${this.baseUrl}${path}`, {
2946
2934
  method: "POST",
2947
2935
  headers: {
2948
2936
  Authorization: `Bearer ${this.token}`,
@@ -2957,14 +2945,25 @@ var AdminApiClient = class {
2957
2945
  } catch {
2958
2946
  parsed = void 0;
2959
2947
  }
2960
- const json = parsed ?? {};
2961
- if (res.ok) return json;
2962
- if (res.status === 409 && json.acknowledgementRequired === true) return {
2963
- instanceId: body.instanceId,
2964
- results: json.results ?? [],
2965
- acknowledgementRequired: true,
2966
- ...json.error ? { error: json.error } : {}
2948
+ return {
2949
+ res,
2950
+ json: parsed ?? {},
2951
+ text
2967
2952
  };
2953
+ }
2954
+ /**
2955
+ * Reload the scaler config on this orchestrator and, unless `single`, on every
2956
+ * peer it is connected to. A response where an instance refused its file or
2957
+ * was not reached (422) is returned like a success, so the caller can show
2958
+ * every instance; a 404 means the orchestrator predates the route.
2959
+ */
2960
+ async scalerReload(body) {
2961
+ const { res, json, text } = await this.postForBody("/api/v1/admin/scaler/reload", body);
2962
+ if ((res.ok || res.status === 422) && Array.isArray(json.results)) return {
2963
+ scope: json.scope ?? (body.single ? "single" : "cluster"),
2964
+ results: json.results
2965
+ };
2966
+ if (res.status === 404) throw new Error("this orchestrator predates kici-admin scaler reload; send SIGHUP to it or upgrade it");
2968
2967
  throw new Error(`HTTP ${res.status}: ${json.error ?? text}`);
2969
2968
  }
2970
2969
  /** List the live Firecracker VMs on this coordinator, or on the `target` peer. */
@@ -3016,15 +3015,15 @@ var AdminApiClient = class {
3016
3015
  * are returned.
3017
3016
  */
3018
3017
  async listRuns(opts) {
3019
- const params = new URLSearchParams();
3020
- if (opts?.status) params.set("status", opts.status);
3021
- if (opts?.workflowName) params.set("workflowName", opts.workflowName);
3022
- if (opts?.repo) params.set("repo", opts.repo);
3023
- if (opts?.since) params.set("since", opts.since);
3024
- if (opts?.limit) params.set("limit", String(opts.limit));
3025
- if (opts?.offset) params.set("offset", String(opts.offset));
3026
- const qs = params.toString();
3027
- return this.request("GET", `/api/v1/admin/runs${qs ? `?${qs}` : ""}`);
3018
+ const qs = toQuery({
3019
+ status: opts?.status,
3020
+ workflowName: opts?.workflowName,
3021
+ repo: opts?.repo,
3022
+ since: opts?.since,
3023
+ limit: opts?.limit || void 0,
3024
+ offset: opts?.offset || void 0
3025
+ });
3026
+ return this.request("GET", `/api/v1/admin/runs${qs}`);
3028
3027
  }
3029
3028
  /**
3030
3029
  * Count matching runs without returning the row list. Wraps the list
@@ -3077,27 +3076,27 @@ var AdminApiClient = class {
3077
3076
  * secret_audit_log table.
3078
3077
  */
3079
3078
  async getRunSecretOutputs(runId, opts) {
3080
- const params = new URLSearchParams();
3081
- if (opts?.outputKey) params.set("outputKey", opts.outputKey);
3082
- if (opts?.reveal) params.set("reveal", "true");
3083
- const qs = params.toString();
3084
- return this.request("GET", `/api/v1/admin/runs/${encodeURIComponent(runId)}/secret-outputs${qs ? `?${qs}` : ""}`);
3079
+ const qs = toQuery({
3080
+ outputKey: opts?.outputKey,
3081
+ reveal: opts?.reveal || void 0
3082
+ });
3083
+ return this.request("GET", `/api/v1/admin/runs/${encodeURIComponent(runId)}/secret-outputs${qs}`);
3085
3084
  }
3086
3085
  async listEventLog(opts) {
3087
- const params = new URLSearchParams();
3088
- if (opts?.orgId) params.set("orgId", opts.orgId);
3089
- if (opts?.routingKey) params.set("routingKey", opts.routingKey);
3090
- if (opts?.event) params.set("event", opts.event);
3091
- if (opts?.action) params.set("action", opts.action);
3092
- if (opts?.status) params.set("status", opts.status);
3093
- if (opts?.from) params.set("from", opts.from);
3094
- if (opts?.to) params.set("to", opts.to);
3095
- if (opts?.deliveryId) params.set("deliveryId", opts.deliveryId);
3096
- if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
3097
- if (opts?.offset !== void 0) params.set("offset", String(opts.offset));
3098
- if (opts?.includeArchived) params.set("includeArchived", "true");
3099
- const qs = params.toString();
3100
- return this.request("GET", `/api/v1/admin/event-log${qs ? `?${qs}` : ""}`);
3086
+ const qs = toQuery({
3087
+ orgId: opts?.orgId,
3088
+ routingKey: opts?.routingKey,
3089
+ event: opts?.event,
3090
+ action: opts?.action,
3091
+ status: opts?.status,
3092
+ from: opts?.from,
3093
+ to: opts?.to,
3094
+ deliveryId: opts?.deliveryId,
3095
+ limit: opts?.limit,
3096
+ offset: opts?.offset,
3097
+ includeArchived: opts?.includeArchived || void 0
3098
+ });
3099
+ return this.request("GET", `/api/v1/admin/event-log${qs}`);
3101
3100
  }
3102
3101
  async getEventLog(deliveryId, opts) {
3103
3102
  const params = new URLSearchParams({ orgId: opts.orgId });
@@ -3107,44 +3106,42 @@ var AdminApiClient = class {
3107
3106
  }
3108
3107
  /** List internal events, newest first (`GET /api/v1/admin/events`). */
3109
3108
  async listEvents(opts) {
3110
- const params = new URLSearchParams();
3111
- if (opts?.name) params.set("name", opts.name);
3112
- if (opts?.outcome) params.set("outcome", opts.outcome);
3113
- if (opts?.since) params.set("since", opts.since);
3114
- if (opts?.before) params.set("before", opts.before);
3115
- if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
3116
- const qs = params.toString();
3117
- return this.request("GET", `/api/v1/admin/events${qs ? `?${qs}` : ""}`);
3109
+ const qs = toQuery({
3110
+ name: opts?.name,
3111
+ outcome: opts?.outcome,
3112
+ since: opts?.since,
3113
+ before: opts?.before,
3114
+ limit: opts?.limit
3115
+ });
3116
+ return this.request("GET", `/api/v1/admin/events${qs}`);
3118
3117
  }
3119
3118
  /** One internal event with its redacted payload and dispatched runs. */
3120
3119
  async getEvent(id) {
3121
3120
  return this.request("GET", `/api/v1/admin/events/${encodeURIComponent(id)}`);
3122
3121
  }
3123
3122
  async listAccessLog(opts) {
3124
- const params = new URLSearchParams();
3125
- if (opts?.orgId) params.set("orgId", opts.orgId);
3126
- if (opts?.actorType) params.set("actorType", opts.actorType);
3127
- if (opts?.actorId) params.set("actorId", opts.actorId);
3128
- if (opts?.action) params.set("action", opts.action);
3129
- if (opts?.source) params.set("source", opts.source);
3130
- if (opts?.outcome) params.set("outcome", opts.outcome);
3131
- if (opts?.targetType) params.set("targetType", opts.targetType);
3132
- if (opts?.targetId) params.set("targetId", opts.targetId);
3133
- if (opts?.from) params.set("from", opts.from);
3134
- if (opts?.to) params.set("to", opts.to);
3135
- if (opts?.q) params.set("q", opts.q);
3136
- if (opts?.agentLabel) params.set("agentLabel", opts.agentLabel);
3137
- if (opts?.agentOnly) params.set("agentOnly", "true");
3138
- if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
3139
- if (opts?.cursor) params.set("cursor", opts.cursor);
3140
- const qs = params.toString();
3141
- return this.request("GET", `/api/v1/admin/access-log${qs ? `?${qs}` : ""}`);
3123
+ const qs = toQuery({
3124
+ orgId: opts?.orgId,
3125
+ actorType: opts?.actorType,
3126
+ actorId: opts?.actorId,
3127
+ action: opts?.action,
3128
+ source: opts?.source,
3129
+ outcome: opts?.outcome,
3130
+ targetType: opts?.targetType,
3131
+ targetId: opts?.targetId,
3132
+ from: opts?.from,
3133
+ to: opts?.to,
3134
+ q: opts?.q,
3135
+ agentLabel: opts?.agentLabel,
3136
+ agentOnly: opts?.agentOnly || void 0,
3137
+ limit: opts?.limit,
3138
+ cursor: opts?.cursor
3139
+ });
3140
+ return this.request("GET", `/api/v1/admin/access-log${qs}`);
3142
3141
  }
3143
3142
  async getAccessLogEntry(id, opts) {
3144
- const params = new URLSearchParams();
3145
- if (opts?.orgId) params.set("orgId", opts.orgId);
3146
- const qs = params.toString();
3147
- return this.request("GET", `/api/v1/admin/access-log/${encodeURIComponent(id)}${qs ? `?${qs}` : ""}`);
3143
+ const qs = toQuery({ orgId: opts?.orgId });
3144
+ return this.request("GET", `/api/v1/admin/access-log/${encodeURIComponent(id)}${qs}`);
3148
3145
  }
3149
3146
  async diagnose() {
3150
3147
  return this.request("GET", "/admin/diagnose");
@@ -703,11 +703,6 @@ export declare const wsUnsupportedMessageSentTotal: import("@opentelemetry/api")
703
703
  * `received_type`.
704
704
  */
705
705
  export declare const wsNackReceivedTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
706
- /**
707
- * Feature-gated sends the orchestrator SUPPRESSED because the Platform advertised
708
- * a capability set that lacks the required flag. Labelled by `capability` + `type`.
709
- */
710
- export declare const wsPlatformCapabilityGapTotal: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
711
706
  /**
712
707
  * Register every queued orchestrator observable gauge on the real meter.
713
708
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * OIDC ID-token claims for orchestrator-side minting — shared by the
3
3
  * production orchestrator-owned mint (`orchestrator-mint.ts`) and the local
4
- * dev-signed identity (`local-mint.ts`). Mirrors the Platform's claim shape so
4
+ * dev-signed identity (`local-mint.ts`). One claim shape, so
5
5
  * every bundle carries the exact same claims the agent's statement builder +
6
6
  * the engine verifier expect — the cross-check between the in-toto statement
7
7
  * and the token claims is identical across all minters. The dev-signed path
@@ -1,10 +1,8 @@
1
1
  /**
2
2
  * Bounded wait for the orchestrator's provenance signer.
3
3
  *
4
- * A mint that arrives before any node has generated the key must NOT fall
5
- * back to a Platform-signed bundle (it would fail verification against the
6
- * orchestrator trust root), so the resolver waits for the key instead of
7
- * deferring on the first miss. But the wait exists for ONE condition — a
4
+ * A mint that arrives before any node has generated the key must not fail,
5
+ * so the resolver waits for the key instead of deferring on the first miss. But the wait exists for ONE condition — a
8
6
  * reconcile that returns `null` because a non-leader is still leaving key
9
7
  * creation to the leader. A reconcile that THROWS is a different answer:
10
8
  * a signing key sealed under a master key this process does not hold, or `db`
@@ -31,6 +31,7 @@ import { AgentMetricsAggregator } from './metrics/agent-metrics-aggregator.js';
31
31
  import { SourceLocationStore } from './app.js';
32
32
  import { ExecutionJobStatus, type PeerHeartbeat, type PeerLogsCollectRequest, type PeerToPeerMessage, type InvokeResult } from '@kici-dev/engine';
33
33
  import { ScalerManager } from './scaler/index.js';
34
+ import { type ScalerFileReload } from './scaler/file-reload.js';
34
35
  import type { ScalerConfig } from './scaler/index.js';
35
36
  import type { CacheStorage } from './storage/index.js';
36
37
  import { type ProvenanceTrustRoot } from './provenance/trust-root.js';
@@ -90,9 +91,9 @@ export interface OrchestratorSubsystems {
90
91
  scalerConfig: ScalerConfig | null;
91
92
  cacheStorage: CacheStorage | undefined;
92
93
  /**
93
- * Provenance trust root used to verify build-provenance bundles at ingest.
94
- * The mode-specific hook (server.ts) wires the live issuer onto it from the
95
- * Platform `auth.success` message via `onProvenanceIssuer`.
94
+ * Provenance trust root used to verify build-provenance bundles at ingest:
95
+ * the orchestrator's own key set, the local dev plane's signer, or the
96
+ * configured `KICI_PROVENANCE_ISSUER`.
96
97
  */
97
98
  provenanceTrustRoot: ProvenanceTrustRoot;
98
99
  /**
@@ -174,6 +175,12 @@ export interface OrchestratorSubsystems {
174
175
  * in setupCluster).
175
176
  */
176
177
  answerPeerForgetRequest: PeerForgetRequestHandler;
178
+ /**
179
+ * Reloads this orchestrator's scaler config (`kici-admin scaler reload`,
180
+ * SIGHUP). Wired into every outgoing PeerClient to answer a
181
+ * peer.scaler.reload.request.
182
+ */
183
+ scalerFileReload: ScalerFileReload;
177
184
  getLocalInventory: () => Omit<PeerHeartbeat, 'type'>;
178
185
  broadcastHeartbeatToAllPeers: () => void;
179
186
  broadcastAgentTokenRevoke: (tokenId: string) => void;
@@ -16,7 +16,7 @@
16
16
  * This module owns the partition, the grouping, and the dispatch-and-await. The
17
17
  * caller decides what to do with the verdicts.
18
18
  */
19
- import type { AgentCapabilities, GlobalEvalCandidateResult, LockJobOrFactory, LockWorkflow, SimulatedEvent, WorkflowDecision } from '@kici-dev/engine';
19
+ import type { GlobalEvalCandidateResult, LockJobOrFactory, LockWorkflow, SimulatedEvent, WorkflowDecision } from '@kici-dev/engine';
20
20
  import { type GlobalEvalRoundCache } from '../cache/global-eval-round-cache.js';
21
21
  import type { PendingGlobalEvalTracker } from '../cache/pending-global-evals.js';
22
22
  import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
@@ -135,17 +135,12 @@ export interface GlobalEvalAgentRegistry {
135
135
  arch: string;
136
136
  version?: string | null;
137
137
  }>;
138
- /**
139
- * Every registered agent, busy or idle. Optional so a test double can omit
140
- * it; without it the up-front result-aware refusal never fires.
141
- */
138
+ /** Every registered agent, busy or idle. Optional so a test double can omit it. */
142
139
  getAllEntries?(): Iterable<{
143
140
  labels: ReadonlySet<string>;
144
141
  platform: string;
145
142
  arch: string;
146
143
  version: string | null;
147
- /** What the agent advertised on `agent.register`; `null` = supports nothing optional. */
148
- capabilities: AgentCapabilities | null;
149
144
  }>;
150
145
  }
151
146
  /**
@@ -226,25 +221,6 @@ export interface GlobalEvalRoundArgs {
226
221
  * timeout message that says nothing about agent versions.
227
222
  */
228
223
  export declare function unsupportedFleetReason(agentRegistry?: GlobalEvalAgentRegistry): string | null;
229
- /**
230
- * Why a candidate with a result-aware generator is refused before any round.
231
- *
232
- * An agent without the capability runs every generator in the round, a
233
- * result-aware one included; that generator sees no upstream outputs and
234
- * returns the wrong jobs.
235
- */
236
- export declare const RESULT_AWARE_UNSUPPORTED_REASON: string;
237
- /**
238
- * Why no agent can run a round holding a result-aware generator, or `null`
239
- * when one might.
240
- *
241
- * The same bar as {@link unsupportedFleetReason}: refused only on proof. That
242
- * means init-runners ARE registered, every one reports a readable version, and
243
- * none advertises the capability. An empty fleet, a busy capable agent, or an
244
- * agent whose version cannot be read is not refused — the round queues on the
245
- * agent-feature label, and a scaler or a registering agent can still serve it.
246
- */
247
- export declare function resultAwareUnsupportedReason(agentRegistry?: GlobalEvalAgentRegistry): string | null;
248
224
  /**
249
225
  * Narrow one wire verdict into a value the dispatch path can trust.
250
226
  *
@@ -116,9 +116,6 @@ export declare function validateRegistryUrlScheme(url: string, allowHttp: boolea
116
116
  * it set one, otherwise the `HoldType` member its action implies. Column and
117
117
  * gate share one vocabulary, so nothing is translated here.
118
118
  *
119
- * Distinct from the engine's `normalizePersistedHoldType`, which maps a value
120
- * read back OUT of the column onto that same vocabulary.
121
- *
122
119
  * Exported for tests.
123
120
  */
124
121
  export declare function resolveHoldType(action: 'hold' | 'wait' | 'queue', raw: string | undefined): string;
@@ -41,13 +41,7 @@ export declare function buildManualJobConfig(workflow: LockWorkflow, mat: Materi
41
41
  source: import("@kici-dev/engine").LockSource | undefined;
42
42
  workflowName: string;
43
43
  steps: readonly import("@kici-dev/engine").LockStepEntry[];
44
- needs: readonly (string | {
45
- name: string;
46
- runOn: ("cancelled" | "cancelling" | "drift_dropped" | "failed" | "pending" | "queued" | "recovering" | "running" | "skipped" | "success" | "timed_out_stale" | "unroutable")[];
47
- } | {
48
- group: string;
49
- runOn: ("cancelled" | "cancelling" | "drift_dropped" | "failed" | "pending" | "queued" | "recovering" | "running" | "skipped" | "success" | "timed_out_stale" | "unroutable")[];
50
- })[];
44
+ needs: readonly (string | import("@kici-dev/engine").LockNeedsEntry | import("@kici-dev/engine").LockNeedsGroupEntry)[];
51
45
  rules: readonly import("@kici-dev/engine").LockRule[] | undefined;
52
46
  gitCredentials?: Readonly<Record<string, Readonly<Record<string, string>>>> | undefined;
53
47
  dispatchInputs?: Record<string, unknown> | undefined;
@@ -31,13 +31,11 @@ import type { Database } from '../db/types.js';
31
31
  * `repository` / `ref` / `sha` / `workflowRef` / `runId` / `jobId`, none of
32
32
  * which depends on who signs the token. Only the statement's `builder.id`
33
33
  * needs an issuer, and an unset one reproduces the `/orchestrator/unknown`
34
- * builder the local guess has always recorded.
35
- *
36
- * Gating on the issuer here dropped the context for every orchestrator still
37
- * on the deprecated Platform-relay mint, so its agent fell back to the local
38
- * guess — `repository: 'unknown/unknown'` for a `file://` clone, and a bare
39
- * workflow name where the claim is `<name>@<sha>` — and the capture check
40
- * then refused every one of its deferred attestations.
34
+ * builder the local guess has always recorded. Gating on the issuer would send
35
+ * an orchestrator without one back to the local guess —
36
+ * `repository: 'unknown/unknown'` for a `file://` clone, and a bare workflow
37
+ * name where the claim is `<name>@<sha>` — and the capture check would refuse
38
+ * every one of its deferred attestations.
41
39
  */
42
40
  export declare function loadProvenanceContext(db: Kysely<Database>, runId: string, jobId: string, opts: {
43
41
  issuer: string | undefined;
@@ -26,7 +26,7 @@ export declare class PendingAttestationsRepo {
26
26
  /**
27
27
  * List pending rows oldest-first, optionally scoped to a run. Terminally
28
28
  * rejected rows (`rejected_at IS NOT NULL`) are excluded — the retrier never
29
- * re-picks a row the Platform definitively cannot mint.
29
+ * re-picks a row the orchestrator definitively cannot mint.
30
30
  */
31
31
  list(opts?: {
32
32
  runId?: string;