@kungfu-tech/buildchain 3.0.9-alpha.0 → 3.0.9-alpha.10

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 (117) hide show
  1. package/bin/buildchain.mjs +9 -1
  2. package/contracts/dev-delivery-authority-v2.schema.json +253 -0
  3. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +16 -0
  4. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +57 -0
  5. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
  6. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
  7. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
  8. package/contracts/next-development-request-v1.schema.json +66 -0
  9. package/contracts/next-development-transition-v1.schema.json +292 -0
  10. package/contracts/release-cut-v1.schema.json +1 -0
  11. package/contracts/release-train-transition-v1.schema.json +1 -0
  12. package/dist/site/agent-index.json +1 -0
  13. package/dist/site/artifact-schemas.json +2 -0
  14. package/dist/site/buildchain-contract.json +91 -32
  15. package/dist/site/buildchain-site.json +353 -45
  16. package/dist/site/capability-registry.json +7 -6
  17. package/dist/site/cli-registry.json +41 -2
  18. package/dist/site/controller-registry.json +8 -4
  19. package/dist/site/kfd-claims.json +193 -9
  20. package/dist/site/kfd-upstream-aggregate.json +9 -9
  21. package/dist/site/manual-registry.json +26 -10
  22. package/dist/site/node-api-registry.json +8012 -4574
  23. package/dist/site/page-registry.json +331 -31
  24. package/dist/site/public-surface-audit.json +353 -8
  25. package/dist/site/publication-registry.json +4 -4
  26. package/dist/site/release-passport-check-manifest.json +1 -0
  27. package/dist/site/release-provenance.json +12 -0
  28. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +442 -0
  29. package/dist/site/schemas/release-passport-v1.schema.json +3 -0
  30. package/dist/site/site-manifest.json +22 -14
  31. package/dist/site/workflow-registry.json +15 -7
  32. package/docs/MAP.md +5 -2
  33. package/docs/adopter-delivery-gate.md +106 -0
  34. package/docs/aws-us-elastic-runner-burst-plane.md +30 -3
  35. package/docs/cli-reference.md +200 -13
  36. package/docs/dev-alpha-candidate-patrol.md +8 -0
  37. package/docs/dev-delivery-qualification-landing-adr.md +137 -0
  38. package/docs/dev-delivery-warrant.md +164 -27
  39. package/docs/kfd-support.md +7 -2
  40. package/docs/next-development-transition.md +119 -0
  41. package/docs/node-api-reference.md +464 -277
  42. package/docs/release-governance.md +10 -7
  43. package/docs/release-passport.md +15 -2
  44. package/docs/release-train.md +46 -4
  45. package/docs/reusable-build-surface.md +26 -9
  46. package/docs/runtime-train-validation.md +6 -0
  47. package/docs/versioning.md +6 -2
  48. package/package.json +21 -6
  49. package/packages/core/README.md +25 -0
  50. package/packages/core/adopter-delivery-gate.js +576 -0
  51. package/packages/core/adopter-delivery-json.js +66 -0
  52. package/packages/core/adopter-delivery-passport.js +259 -0
  53. package/packages/core/adopter-delivery-vectors.js +158 -0
  54. package/packages/core/adopter-delivery-vectors.json +77 -0
  55. package/packages/core/artifact-verification-envelope.js +5 -5
  56. package/packages/core/buildchain-agent-manuals.js +1 -0
  57. package/packages/core/buildchain-channel-identity.js +118 -0
  58. package/packages/core/buildchain-compatibility-proof.js +54 -0
  59. package/packages/core/buildchain-contract.js +7 -0
  60. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  61. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  62. package/packages/core/channel-candidate.js +8 -0
  63. package/packages/core/channel-promotion-baseline.js +52 -0
  64. package/packages/core/controller-evidence.js +2 -1
  65. package/packages/core/dev-alpha-candidate-selection.js +10 -2
  66. package/packages/core/dev-delivery-authority.js +996 -0
  67. package/packages/core/dev-delivery-native-proof.js +418 -0
  68. package/packages/core/dev-delivery-warrant-settlement.js +67 -20
  69. package/packages/core/dev-delivery-warrant.js +141 -48
  70. package/packages/core/kfd-adopter-category-driver.js +167 -0
  71. package/packages/core/kfd-adopter-manifest.js +49 -50
  72. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  73. package/packages/core/next-development-candidate-reservation.js +186 -0
  74. package/packages/core/next-development-controller.js +726 -0
  75. package/packages/core/next-development-projection.js +288 -0
  76. package/packages/core/next-development-transition.js +738 -0
  77. package/packages/core/paper-agent-entry.js +7 -4
  78. package/packages/core/paper.js +14 -7
  79. package/packages/core/release-blocker-priority.js +192 -0
  80. package/packages/core/release-passport-contract.js +10 -6
  81. package/packages/core/release-passport.js +29 -12
  82. package/packages/core/release-train.js +220 -0
  83. package/scripts/aws-macos-jit-controller-core.mjs +239 -4
  84. package/scripts/aws-macos-jit-controller.mjs +198 -2
  85. package/scripts/aws-macos-jit-instance-rehydrate.mjs +260 -0
  86. package/scripts/aws-macos-jit-source-rebind.mjs +235 -14
  87. package/scripts/aws-macos-jit.mjs +12 -0
  88. package/scripts/buildchain-channel-router.mjs +18 -2
  89. package/scripts/buildchain-cli-help.mjs +9 -2
  90. package/scripts/buildchain-contract-lock.mjs +11 -1
  91. package/scripts/check-internal-architecture.mjs +1 -0
  92. package/scripts/check-inventory.mjs +14 -3
  93. package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
  94. package/scripts/dev-delivery-authority.mjs +368 -0
  95. package/scripts/dev-delivery-native-run.mjs +235 -0
  96. package/scripts/dev-delivery-proof.mjs +64 -2
  97. package/scripts/dev-delivery-two-phase-resume.mjs +90 -0
  98. package/scripts/dev-delivery-two-phase.mjs +577 -0
  99. package/scripts/dev-delivery-warrant.mjs +47 -5
  100. package/scripts/dev-pr-delivery-warrant.mjs +19 -14
  101. package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
  102. package/scripts/generate-channel-build-workflow.mjs +82 -31
  103. package/scripts/generate-next-development-guidance.mjs +49 -0
  104. package/scripts/generate-site-bundle.mjs +8 -3
  105. package/scripts/init-repo.mjs +135 -62
  106. package/scripts/next-development-self-dogfood-harness.mjs +409 -0
  107. package/scripts/next-development-self-dogfood.mjs +532 -0
  108. package/scripts/next-development-transition.mjs +47 -0
  109. package/scripts/promotion-channel-router.mjs +5 -3
  110. package/scripts/release-candidate-tail-reseal.mjs +426 -0
  111. package/scripts/release-train-self-dogfood.mjs +579 -0
  112. package/scripts/runtime-ref-core.mjs +4 -17
  113. package/scripts/seal-artifact-signing-requests.mjs +7 -4
  114. package/scripts/site-capability-metadata.mjs +23 -1
  115. package/scripts/stable-candidate-qualification.mjs +7 -7
  116. package/scripts/workflow-call-contract.mjs +1 -1
  117. package/templates/native-dev-delivery.yml +86 -0
@@ -2,8 +2,12 @@ import { devDeliveryClone as clone, devDeliveryContentRoot, devDeliveryExactRoot
2
2
  import { chainedDevDeliveryAttemptInput, createDevDeliveryCandidateIdentity, validateDevDeliveryCandidateChain } from "./dev-delivery-candidate-identity.js";
3
3
  import { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA, createCancelQueuedDevDeliveryCandidate } from "./dev-delivery-warrant-cancellation.js";
4
4
  import { DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA, createDevDeliveryTerminalSettler } from "./dev-delivery-warrant-settlement.js";
5
+ import { createDevDeliveryWarrantQualifier } from "./dev-delivery-native-proof.js";
6
+ import { compareReleaseBlockerPriority, normalizeReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
5
7
  export { devDeliveryContentRoot } from "./dev-delivery-common.js";
8
+ export { RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA, createReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
6
9
  export { SOURCE_QUALIFICATION_PROOF_SCHEMA, PROJECT_CUT_REPLAY_PROOF_SCHEMA, INTEGRATION_DELIVERY_PROOF_SCHEMA, classifyDevDeliveryDelta, createIntegrationDeliveryProof, createProjectCutReplayPlan, createProjectCutReplayProof, createSourceQualificationProof, verifyIntegrationDeliveryProof, verifyProjectCutReplayProof, verifySourceQualificationProof } from "./dev-delivery-proof.js";
10
+ export { DEV_DELIVERY_QUALIFICATION_RECEIPT_SCHEMA, NATIVE_QUALIFICATION_PROOF_SCHEMA, NATIVE_PROOF_REUSE_DECISION_SCHEMA, NATIVE_PROOF_BASE_DELTA_SCHEMA, createNativeProofBaseDelta, createNativeProofReuseDecision, createNativeQualificationProof, verifyNativeProofReuseDecision, verifyNativeQualificationProof } from "./dev-delivery-native-proof.js";
7
11
  export { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA };
8
12
  export const DEV_DELIVERY_QUEUE_CONTRACT = "kungfu-buildchain-dev-delivery-warrant-queue";
9
13
  export const DEV_DELIVERY_WARRANT_SCHEMA = "kungfu.buildchain.dev-delivery-warrant/v1";
@@ -19,7 +23,14 @@ export const DEV_DELIVERY_PRIORITIES = Object.freeze({
19
23
 
20
24
  export const DEV_DELIVERY_CLASSES = Object.freeze(["non-native-fast", "native-proof-required", "cross-platform", "release"]);
21
25
 
22
- const TERMINAL_STATES = new Set(["merged", "terminal-failure", "dequeued", "cancelled"]);
26
+ const TERMINAL_STATES = new Set(["merged", "terminal-failure", "dequeued", "cancelled", "superseded"]);
27
+ const TERMINAL_CLASSES = Object.freeze({
28
+ merged: "authoritative-completion",
29
+ "terminal-failure": "authoritative-failure",
30
+ cancelled: "authoritative-cancellation",
31
+ superseded: "authoritative-supersession",
32
+ dequeued: "transient-dequeue",
33
+ });
23
34
  function nonNegativeInteger(value, label, fallback = 0) {
24
35
  const parsed = Number(value ?? fallback);
25
36
  if (!Number.isInteger(parsed) || parsed < 0) throw new Error(`${label} must be a non-negative integer`);
@@ -57,10 +68,10 @@ function normalizeCandidate(input, expected) {
57
68
  throw new Error(`candidateId mismatch for PR #${identity.pullRequestNumber}`);
58
69
  }
59
70
  const status = text(input.status || "queued");
60
- if (!["queued", "selected", "proving", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
71
+ if (!["queued", "selected", "provisional", "proving", "qualified", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
61
72
  throw new Error(`unsupported candidate status ${status || "<empty>"}`);
62
73
  }
63
- return {
74
+ const candidate = {
64
75
  ...identity,
65
76
  sourceHead: exactSha(input.sourceHead, "sourceHead"),
66
77
  sourcePatchRoot: exactRoot(input.sourcePatchRoot, "sourcePatchRoot"),
@@ -69,7 +80,14 @@ function normalizeCandidate(input, expected) {
69
80
  closureRoot: exactRoot(input.closureRoot, "closureRoot"),
70
81
  dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
71
82
  toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
72
- ...(Object.hasOwn(input, "sourceWorkflowRunId") ? { sourceWorkflowRunId: nonNegativeInteger(input.sourceWorkflowRunId, "candidate sourceWorkflowRunId", 0) } : {}),
83
+ ...(input.environmentRoot
84
+ ? { environmentRoot: exactRoot(input.environmentRoot, "environmentRoot") }
85
+ : {}),
86
+ ...(Object.hasOwn(input, "sourceWorkflowRunId")
87
+ ? {
88
+ sourceWorkflowRunId: nonNegativeInteger(input.sourceWorkflowRunId, "candidate sourceWorkflowRunId", 0),
89
+ }
90
+ : {}),
73
91
  priority: priority(input.priority),
74
92
  enqueuedAt: timestamp(input.enqueuedAt, "candidate enqueuedAt"),
75
93
  updatedAt: timestamp(input.updatedAt || input.enqueuedAt, "candidate updatedAt"),
@@ -78,6 +96,12 @@ function normalizeCandidate(input, expected) {
78
96
  status,
79
97
  terminal: input.terminal || null,
80
98
  };
99
+ if (Object.hasOwn(input, "affectedPaths")) {
100
+ if (!Array.isArray(input.affectedPaths)) throw new Error("candidate affectedPaths must be an array");
101
+ candidate.affectedPaths = [...new Set(input.affectedPaths.map(text).filter(Boolean))].sort();
102
+ }
103
+ if (Object.hasOwn(input, "releaseBlockerPriority")) candidate.releaseBlockerPriority = normalizeReleaseBlockerPriorityClaim(input.releaseBlockerPriority, candidate, expected);
104
+ return candidate;
81
105
  }
82
106
 
83
107
  function queueBody(queue) {
@@ -136,11 +160,20 @@ export function normalizeDevDeliveryQueue(input, expected = {}) {
136
160
  warrant.generation = positiveInteger(warrant.generation, "Warrant generation");
137
161
  warrant.issuedAt = timestamp(warrant.issuedAt, "Warrant issuedAt");
138
162
  warrant.expiresAt = timestamp(warrant.expiresAt, "Warrant expiresAt");
139
- const activeCandidates = queue.candidates.filter((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status));
163
+ const warrantPhase = text(warrant.phase || "qualified");
164
+ if (!["provisional", "qualified"].includes(warrantPhase)) throw new Error("active Warrant phase is unsupported");
165
+ if (Object.hasOwn(warrant, "phase")) warrant.phase = warrantPhase;
166
+ if (warrantPhase === "qualified") {
167
+ if (warrant.nativeProofRoot) warrant.nativeProofRoot = exactRoot(warrant.nativeProofRoot, "Warrant nativeProofRoot");
168
+ if (warrant.nativeProofReuseRoot) warrant.nativeProofReuseRoot = exactRoot(warrant.nativeProofReuseRoot, "Warrant nativeProofReuseRoot");
169
+ if (warrant.qualifiedAt) warrant.qualifiedAt = timestamp(warrant.qualifiedAt, "Warrant qualifiedAt");
170
+ }
171
+ const activeCandidates = queue.candidates.filter((candidate) => ["selected", "provisional", "proving", "qualified", "waiting", "blocked"].includes(candidate.status));
140
172
  if (activeCandidates.length !== 1 || activeCandidates[0].candidateId !== warrant.candidateId) {
141
173
  throw new Error("exactly one active candidate must match the active Warrant");
142
174
  }
143
- } else if (queue.candidates.some((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status))) {
175
+ if (warrant.releaseBlockerPriority?.claimRoot !== activeCandidates[0].releaseBlockerPriority?.claimRoot) throw new Error("active Warrant release-blocker priority drift");
176
+ } else if (queue.candidates.some((candidate) => ["selected", "provisional", "proving", "qualified", "waiting", "blocked"].includes(candidate.status))) {
144
177
  throw new Error("active candidate exists without an active Warrant");
145
178
  }
146
179
  const rooted = withQueueRoot(queue);
@@ -178,8 +211,10 @@ function submissionReceipt({ before, after, candidate, action, now }) {
178
211
  planRoot: candidate.planRoot,
179
212
  closureRoot: candidate.closureRoot,
180
213
  dependencyRoot: candidate.dependencyRoot,
181
- toolchainRoot: candidate.toolchainRoot,
214
+ toolchainRoot: candidate.toolchainRoot, ...(candidate.environmentRoot ? { environmentRoot: candidate.environmentRoot } : {}),
215
+ ...(Object.hasOwn(candidate, "affectedPaths") ? { affectedPaths: candidate.affectedPaths } : {}),
182
216
  sourceWorkflowRunId: candidate.sourceWorkflowRunId,
217
+ ...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
183
218
  deliveryClass: candidate.deliveryClass,
184
219
  priority: candidate.priority,
185
220
  retainedEnqueuedAt: candidate.enqueuedAt,
@@ -192,6 +227,21 @@ function submissionReceipt({ before, after, candidate, action, now }) {
192
227
  };
193
228
  }
194
229
 
230
+ function reconcileSourceWorkflowRunId(existing, attempted, activeCandidateId, currentTime) {
231
+ const current = existing.sourceWorkflowRunId || 0;
232
+ const next = attempted.sourceWorkflowRunId || 0;
233
+ if (existing.sourceHead !== attempted.sourceHead) {
234
+ existing.sourceWorkflowRunId = next;
235
+ return "head-change";
236
+ }
237
+ if (current > 0 && next > 0 && current !== next) throw new Error("candidate sourceWorkflowRunId cannot change for the same sourceHead");
238
+ if (current !== 0 || next === 0) return "retained";
239
+ if (activeCandidateId === existing.candidateId) return "active";
240
+ existing.sourceWorkflowRunId = next;
241
+ existing.updatedAt = currentTime;
242
+ return "upgraded";
243
+ }
244
+
195
245
  export function submitDevDeliveryCandidate(queueInput, input, { now = new Date().toISOString() } = {}) {
196
246
  const transaction = transition(
197
247
  queueInput,
@@ -207,7 +257,13 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
207
257
  },
208
258
  queue,
209
259
  );
210
- const chainedInput = chainedDevDeliveryAttemptInput({ queue, candidate, input, currentTime, terminalStates: TERMINAL_STATES });
260
+ const chainedInput = chainedDevDeliveryAttemptInput({
261
+ queue,
262
+ candidate,
263
+ input,
264
+ currentTime,
265
+ terminalStates: TERMINAL_STATES,
266
+ });
211
267
  const attemptedCandidate = chainedInput ? normalizeCandidate(chainedInput, queue) : candidate;
212
268
  const conflicting = queue.candidates.find((entry) => entry.pullRequestNumber === attemptedCandidate.pullRequestNumber && entry.candidateId !== attemptedCandidate.candidateId && !TERMINAL_STATES.has(entry.status));
213
269
  if (conflicting) throw new Error(`PR #${candidate.pullRequestNumber} already has a different active semantic source`);
@@ -218,10 +274,12 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
218
274
  if (TERMINAL_STATES.has(existing.status)) {
219
275
  throw new Error(`candidate ${attemptedCandidate.candidateId} is terminal and cannot be resubmitted`);
220
276
  }
221
- const exactProofFields = ["sourcePatchRoot", "sourceProofRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot"];
222
- const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]);
277
+ const exactProofFields = ["sourcePatchRoot", "sourceProofRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot", "environmentRoot"];
278
+ const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]) && JSON.stringify(existing.affectedPaths || []) === JSON.stringify(attemptedCandidate.affectedPaths || []) && existing.releaseBlockerPriority?.claimRoot === attemptedCandidate.releaseBlockerPriority?.claimRoot;
279
+ const bindingAction = reconcileSourceWorkflowRunId(existing, attemptedCandidate, before.activeWarrant?.candidateId, currentTime);
223
280
  if (existing.sourceHead === attemptedCandidate.sourceHead && exactProofMatches) {
224
- action = "duplicate-noop";
281
+ if (bindingAction === "active") return { candidate: existing, action: "active-warrant-retained-noop" };
282
+ action = bindingAction === "upgraded" ? "source-workflow-run-upgrade-retained-age" : "duplicate-noop";
225
283
  selected = existing;
226
284
  } else {
227
285
  if (before.activeWarrant?.candidateId === existing.candidateId) {
@@ -235,6 +293,10 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
235
293
  const headChanged = existing.sourceHead !== attemptedCandidate.sourceHead;
236
294
  existing.sourceHead = attemptedCandidate.sourceHead;
237
295
  for (const field of exactProofFields) existing[field] = attemptedCandidate[field];
296
+ if (Object.hasOwn(attemptedCandidate, "affectedPaths")) existing.affectedPaths = attemptedCandidate.affectedPaths;
297
+ else delete existing.affectedPaths;
298
+ if (Object.hasOwn(attemptedCandidate, "releaseBlockerPriority")) existing.releaseBlockerPriority = attemptedCandidate.releaseBlockerPriority;
299
+ else delete existing.releaseBlockerPriority;
238
300
  existing.updatedAt = currentTime;
239
301
  existing.attempts += 1;
240
302
  existing.status = "queued";
@@ -270,6 +332,7 @@ function effectivePriority(candidate, policy, now) {
270
332
  basePriority: DEV_DELIVERY_PRIORITIES[candidate.priority],
271
333
  agingBoost,
272
334
  score: Math.min(policy.maxPriority, DEV_DELIVERY_PRIORITIES[candidate.priority] + agingBoost),
335
+ releaseBlocker: Boolean(candidate.releaseBlockerPriority),
273
336
  };
274
337
  }
275
338
 
@@ -282,7 +345,7 @@ export function rankDevDeliveryCandidates(queueInput, { now = new Date().toISOSt
282
345
  candidate,
283
346
  priority: effectivePriority(candidate, queue.policy, currentTime),
284
347
  }))
285
- .sort((left, right) => right.priority.score - left.priority.score || left.candidate.enqueuedAt.localeCompare(right.candidate.enqueuedAt) || left.candidate.candidateId.localeCompare(right.candidate.candidateId));
348
+ .sort((left, right) => compareReleaseBlockerPriority(left, right) || right.priority.score - left.priority.score || left.candidate.enqueuedAt.localeCompare(right.candidate.enqueuedAt) || left.candidate.candidateId.localeCompare(right.candidate.candidateId));
286
349
  }
287
350
 
288
351
  function warrantToken(input) {
@@ -299,26 +362,26 @@ function warrantToken(input) {
299
362
 
300
363
  export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOString(), leaseSeconds } = {}) {
301
364
  const currentTime = timestamp(now, "now");
302
- let queue = normalizeDevDeliveryQueue(queueInput);
365
+ const queue = normalizeDevDeliveryQueue(queueInput);
303
366
  let recoveryReceipt = null;
304
367
  if (queue.activeWarrant && Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(currentTime)) {
305
368
  const recovered = recoverExpiredDevDeliveryWarrant(queue, {
306
369
  now: currentTime,
307
370
  });
308
- queue = recovered.queue;
309
371
  recoveryReceipt = recovered.receipt;
310
372
  }
311
373
  if (queue.activeWarrant) {
374
+ const expired = Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(currentTime);
312
375
  const receipt = {
313
376
  schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
314
- selected: true,
315
- reason: "non-preemptive-active-warrant",
377
+ selected: !expired,
378
+ reason: expired ? "expired-active-warrant-awaits-fenced-settlement" : "non-preemptive-active-warrant",
316
379
  candidateId: queue.activeWarrant.candidateId,
317
380
  fencingToken: queue.activeWarrant.fencingToken,
318
381
  leaseGeneration: queue.activeWarrant.generation,
319
382
  expectedOldStateRoot: recoveryReceipt?.expectedOldStateRoot || queue.stateRoot,
320
383
  nextStateRoot: queue.stateRoot,
321
- nextAction: "Continue the active delivery attempt; later candidates remain visibly queued.",
384
+ nextAction: expired ? "Prove the stale native worker stopped, then close this exact fenced generation before selecting a successor." : "Continue the active delivery attempt; later candidates remain visibly queued.",
322
385
  };
323
386
  return {
324
387
  queue,
@@ -369,15 +432,18 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
369
432
  planRoot: candidate.planRoot,
370
433
  closureRoot: candidate.closureRoot,
371
434
  dependencyRoot: candidate.dependencyRoot,
372
- toolchainRoot: candidate.toolchainRoot,
435
+ toolchainRoot: candidate.toolchainRoot, ...(candidate.environmentRoot ? { environmentRoot: candidate.environmentRoot } : {}),
436
+ ...(Object.hasOwn(candidate, "affectedPaths") ? { affectedPaths: candidate.affectedPaths } : {}),
373
437
  sourceWorkflowRunId: candidate.sourceWorkflowRunId,
438
+ ...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
374
439
  deliveryClass: candidate.deliveryClass,
375
440
  generation: next.fencingCounter,
441
+ phase: "provisional",
376
442
  expectedOldStateRoot: before.stateRoot,
377
443
  issuedAt,
378
444
  expiresAt: new Date(Date.parse(issuedAt) + effectiveLeaseSeconds * 1000).toISOString(),
379
445
  fencingToken: "",
380
- nextAction: "Replay on the exact current dev base, prove required checks, then enqueue the unchanged PR head.",
446
+ nextAction: "Run or reuse the exact semantic native proof, then atomically qualify this fenced Warrant.",
381
447
  };
382
448
  warrant.fencingToken = warrantToken(warrant);
383
449
  next.activeWarrant = warrant;
@@ -390,7 +456,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
390
456
  const receipt = {
391
457
  schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
392
458
  selected: true,
393
- reason: selected.priority.agingBoost > 0 ? "fifo-aging-bounded-priority" : "fifo-bounded-priority",
459
+ reason: selected.priority.releaseBlocker ? "release-blocker-bounded-priority" : selected.priority.agingBoost > 0 ? "fifo-aging-bounded-priority" : "fifo-bounded-priority",
394
460
  repository: transaction.after.repository,
395
461
  protectedBase: transaction.after.protectedBase,
396
462
  candidateId: transaction.result.candidate.candidateId,
@@ -404,8 +470,13 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
404
470
  planRoot: transaction.result.candidate.planRoot,
405
471
  closureRoot: transaction.result.candidate.closureRoot,
406
472
  dependencyRoot: transaction.result.candidate.dependencyRoot,
407
- toolchainRoot: transaction.result.candidate.toolchainRoot,
473
+ toolchainRoot: transaction.result.candidate.toolchainRoot, ...(transaction.result.candidate.environmentRoot ? { environmentRoot: transaction.result.candidate.environmentRoot } : {}),
408
474
  sourceWorkflowRunId: transaction.result.candidate.sourceWorkflowRunId,
475
+ ...(Object.hasOwn(transaction.result.candidate, "releaseBlockerPriority")
476
+ ? {
477
+ releaseBlockerPriority: transaction.result.candidate.releaseBlockerPriority,
478
+ }
479
+ : {}),
409
480
  deliveryClass: transaction.result.candidate.deliveryClass,
410
481
  queueAgeSeconds: selected.priority.ageSeconds,
411
482
  basePriority: selected.priority.basePriority,
@@ -443,7 +514,7 @@ export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Dat
443
514
  const effectiveLeaseSeconds = positiveInteger(leaseSeconds, "leaseSeconds", queue.policy.leaseSeconds);
444
515
  queue.activeWarrant.expiresAt = new Date(Date.parse(currentTime) + effectiveLeaseSeconds * 1000).toISOString();
445
516
  const candidate = queue.candidates.find((entry) => entry.candidateId === queue.activeWarrant.candidateId);
446
- candidate.status = "proving";
517
+ candidate.status = queue.activeWarrant.phase === "qualified" ? "qualified" : "proving";
447
518
  candidate.updatedAt = currentTime;
448
519
  return { candidate, warrant: queue.activeWarrant };
449
520
  },
@@ -467,6 +538,13 @@ export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Dat
467
538
  };
468
539
  }
469
540
 
541
+ const qualifyDevDeliveryWarrantTransition = createDevDeliveryWarrantQualifier({
542
+ transition,
543
+ assertWarrantMutation,
544
+ });
545
+ export function qualifyDevDeliveryWarrant(queueInput, warrant, options) {
546
+ return qualifyDevDeliveryWarrantTransition(queueInput, warrant, options);
547
+ }
470
548
  export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().toISOString() } = {}) {
471
549
  const currentTime = timestamp(now, "now");
472
550
  const before = normalizeDevDeliveryQueue(queueInput);
@@ -485,33 +563,21 @@ export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().
485
563
  receiptRoot: devDeliveryContentRoot(receipt),
486
564
  };
487
565
  }
488
- const transaction = transition(
489
- before,
490
- (queue) => {
491
- const expired = clone(queue.activeWarrant);
492
- const candidate = queue.candidates.find((entry) => entry.candidateId === expired.candidateId);
493
- candidate.status = "queued";
494
- candidate.recoveries += 1;
495
- candidate.attempts += 1;
496
- candidate.updatedAt = currentTime;
497
- queue.activeWarrant = null;
498
- return { candidate, expired };
499
- },
500
- currentTime,
501
- );
566
+ const expired = before.activeWarrant;
567
+ const candidate = before.candidates.find((entry) => entry.candidateId === expired.candidateId);
502
568
  const receipt = {
503
569
  schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
504
- action: "recovered-expired-lease",
505
- candidateId: transaction.result.candidate.candidateId,
506
- rejectedFencingToken: transaction.result.expired.fencingToken,
507
- expiredLeaseGeneration: transaction.result.expired.generation,
508
- retainedEnqueuedAt: transaction.result.candidate.enqueuedAt,
509
- expectedOldStateRoot: transaction.expectedOldStateRoot,
510
- nextStateRoot: transaction.after.stateRoot,
511
- nextAction: "Re-select through the queue to mint a new lease generation and fencing token.",
570
+ action: "expired-lease-fenced-stop-required",
571
+ candidateId: candidate.candidateId,
572
+ rejectedFencingToken: expired.fencingToken,
573
+ expiredLeaseGeneration: expired.generation,
574
+ retainedEnqueuedAt: candidate.enqueuedAt,
575
+ expectedOldStateRoot: before.stateRoot,
576
+ nextStateRoot: before.stateRoot,
577
+ nextAction: "Prove the stale native worker stopped, then close this exact fenced generation before selecting a successor.",
512
578
  };
513
579
  return {
514
- queue: transaction.after,
580
+ queue: before,
515
581
  receipt,
516
582
  receiptRoot: devDeliveryContentRoot(receipt),
517
583
  };
@@ -523,16 +589,22 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
523
589
  if (!TERMINAL_STATES.has(normalizedOutcome)) {
524
590
  throw new Error(`outcome must be one of ${[...TERMINAL_STATES].join(", ")}`);
525
591
  }
592
+ if (normalizedOutcome === "dequeued") {
593
+ throw new Error("transient dequeue cannot close an active Delivery Warrant");
594
+ }
526
595
  const transaction = transition(
527
596
  queueInput,
528
597
  (queue, before) => {
529
- assertWarrantMutation(before, warrant, currentTime, { allowExpired: true });
598
+ assertWarrantMutation(before, warrant, currentTime, {
599
+ allowExpired: true,
600
+ });
530
601
  const active = clone(queue.activeWarrant);
531
602
  const candidate = queue.candidates.find((entry) => entry.candidateId === active.candidateId);
532
603
  candidate.status = normalizedOutcome;
533
604
  candidate.updatedAt = currentTime;
534
605
  candidate.terminal = {
535
606
  outcome: normalizedOutcome,
607
+ terminalClass: TERMINAL_CLASSES[normalizedOutcome],
536
608
  reason: text(reason),
537
609
  evidenceRoot: exactRoot(evidenceRoot, "evidenceRoot"),
538
610
  closedAt: currentTime,
@@ -548,6 +620,7 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
548
620
  schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
549
621
  action: "terminal-closeout",
550
622
  outcome: normalizedOutcome,
623
+ terminalClass: TERMINAL_CLASSES[normalizedOutcome],
551
624
  reason: text(reason),
552
625
  candidateId: transaction.result.candidate.candidateId,
553
626
  fencingToken: transaction.result.active.fencingToken,
@@ -565,8 +638,15 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
565
638
  }
566
639
 
567
640
  const cancelQueuedDevDeliveryCandidateTransition = createCancelQueuedDevDeliveryCandidate(normalizeDevDeliveryQueue);
568
- export function cancelQueuedDevDeliveryCandidate(queueInput, input, options) { return cancelQueuedDevDeliveryCandidateTransition(queueInput, input, options); }
569
- export const settleDevDeliveryTerminalEvent = createDevDeliveryTerminalSettler({ normalizeQueue: normalizeDevDeliveryQueue, closeWarrant: closeDevDeliveryWarrant, cancelQueued: cancelQueuedDevDeliveryCandidate, terminalStates: TERMINAL_STATES });
641
+ export function cancelQueuedDevDeliveryCandidate(queueInput, input, options) {
642
+ return cancelQueuedDevDeliveryCandidateTransition(queueInput, input, options);
643
+ }
644
+ export const settleDevDeliveryTerminalEvent = createDevDeliveryTerminalSettler({
645
+ normalizeQueue: normalizeDevDeliveryQueue,
646
+ closeWarrant: closeDevDeliveryWarrant,
647
+ cancelQueued: cancelQueuedDevDeliveryCandidate,
648
+ terminalStates: TERMINAL_STATES,
649
+ });
570
650
 
571
651
  export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOString() } = {}) {
572
652
  const queue = normalizeDevDeliveryQueue(queueInput);
@@ -593,6 +673,19 @@ export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOStri
593
673
  queueAgeSeconds: entry.priority.ageSeconds,
594
674
  attempts: entry.candidate.attempts,
595
675
  recoveries: entry.candidate.recoveries,
676
+ assignmentRoot: entry.candidate.assignmentRoot,
677
+ initiativeRoot: entry.candidate.initiativeRoot,
678
+ sourceIdentityRoot: entry.candidate.sourceIdentityRoot,
679
+ sourcePatchRoot: entry.candidate.sourcePatchRoot,
680
+ sourceProofRoot: entry.candidate.sourceProofRoot,
681
+ planRoot: entry.candidate.planRoot,
682
+ closureRoot: entry.candidate.closureRoot,
683
+ dependencyRoot: entry.candidate.dependencyRoot,
684
+ toolchainRoot: entry.candidate.toolchainRoot, ...(entry.candidate.environmentRoot ? { environmentRoot: entry.candidate.environmentRoot } : {}),
685
+ sourceWorkflowRunId: entry.candidate.sourceWorkflowRunId || 0,
686
+ affectedPaths: entry.candidate.affectedPaths || [],
687
+ deliveryClass: entry.candidate.deliveryClass,
688
+ priority: entry.candidate.priority,
596
689
  nextAction: index === 0 && !queue.activeWarrant ? "eligible-for-selection" : "wait-for-active-warrant",
597
690
  })),
598
691
  observedAt: currentTime,
@@ -0,0 +1,167 @@
1
+ import kfdCategoryCatalog from "@kungfu-tech/kfd/adopter-conformance/category-profiles.json" with { type: "json" };
2
+ import kfdPackageJson from "@kungfu-tech/kfd/package.json" with { type: "json" };
3
+ import { resolveAdopterCategoryProfiles } from "@kungfu-tech/kfd/adopter-conformance/category-profile-resolver";
4
+ import { verifyAdopterCategoryInstanceManifest } from "@kungfu-tech/kfd/adopter-conformance/category-instance-verifier";
5
+ import { verifyAdopterManifestFromPackage } from "@kungfu-tech/kfd/adopter-conformance/toolchain";
6
+
7
+ import {
8
+ ADOPTER_PROTOCOL_DRIVER_INTERFACE,
9
+ adopterDeliveryGateDigest,
10
+ defineAdopterProtocolDriver,
11
+ } from "./adopter-delivery-gate.js";
12
+
13
+ export const KFD_ADOPTER_CATEGORY_PROTOCOL_ID =
14
+ "kfd.adopter-category/instance-manifest";
15
+ export const KFD_ADOPTER_CATEGORY_PROTOCOL_VERSION = "1.0.0";
16
+ export const KFD_ADOPTER_CATEGORY_DRIVER_REPORT_CONTRACT =
17
+ "kungfu-buildchain-kfd-adopter-category-driver-report";
18
+
19
+ function freezeJson(value) {
20
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
21
+ Object.freeze(value);
22
+ for (const child of Object.values(value)) freezeJson(child);
23
+ }
24
+ return value;
25
+ }
26
+
27
+ const publishedCatalog = freezeJson(structuredClone(kfdCategoryCatalog));
28
+
29
+ function catalogCopy() {
30
+ return structuredClone(publishedCatalog);
31
+ }
32
+
33
+ function sameJson(left, right) {
34
+ try {
35
+ return adopterDeliveryGateDigest(left) === adopterDeliveryGateDigest(right);
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ function issue(code, path, message) {
42
+ return { code, path, message };
43
+ }
44
+
45
+ export function resolvePublishedKfdAdopterCategoryProfiles(selection) {
46
+ return resolveAdopterCategoryProfiles(
47
+ structuredClone(selection),
48
+ catalogCopy(),
49
+ );
50
+ }
51
+
52
+ export function verifyPublishedKfdAdopterCategoryInstance(
53
+ instanceManifest,
54
+ { adopterManifest, verifiedAt, maxAgeSeconds } = {},
55
+ ) {
56
+ const adopterReport = verifyAdopterManifestFromPackage(adopterManifest, {
57
+ packageArtifactRoot: instanceManifest?.kfdCut?.packageRoot,
58
+ verifiedAt,
59
+ maxAgeSeconds,
60
+ });
61
+ return verifyAdopterCategoryInstanceManifest(instanceManifest, {
62
+ catalog: catalogCopy(),
63
+ adopterManifest,
64
+ adopterReport,
65
+ verifiedAt,
66
+ maxAgeSeconds,
67
+ });
68
+ }
69
+
70
+ function deliveryBindingIssues(request, adopterManifest) {
71
+ const instance = request.declaration;
72
+ const issues = [];
73
+ if (
74
+ request.project.instanceId !== instance?.instanceId ||
75
+ request.project.adopterId !== instance?.project?.adopterId
76
+ ) {
77
+ issues.push(
78
+ issue(
79
+ "delivery-kfd-instance-binding-mismatch",
80
+ "/project",
81
+ "Delivery project identity must match the KFD category instance.",
82
+ ),
83
+ );
84
+ }
85
+ if (!sameJson(request.artifact, instance?.project?.artifact)) {
86
+ issues.push(
87
+ issue(
88
+ "delivery-kfd-instance-binding-mismatch",
89
+ "/artifact",
90
+ "Delivery artifact must match the KFD category instance artifact.",
91
+ ),
92
+ );
93
+ }
94
+ if (
95
+ adopterManifest?.kfdCut?.package?.name !== kfdPackageJson.name ||
96
+ adopterManifest?.kfdCut?.package?.version !== kfdPackageJson.version ||
97
+ instance?.kfdCut?.packageVersion !== kfdPackageJson.version
98
+ ) {
99
+ issues.push(
100
+ issue(
101
+ "delivery-kfd-package-cut-mismatch",
102
+ "/declaration/kfdCut",
103
+ "KFD declarations must bind the exact installed published package version.",
104
+ ),
105
+ );
106
+ }
107
+ return issues;
108
+ }
109
+
110
+ function driverReport(categoryReport, adapterIssues) {
111
+ const valid = categoryReport?.valid === true && adapterIssues.length === 0;
112
+ const report = {
113
+ schemaVersion: 1,
114
+ contract: KFD_ADOPTER_CATEGORY_DRIVER_REPORT_CONTRACT,
115
+ valid,
116
+ conforming: valid,
117
+ qualifying: false,
118
+ selfCertified: false,
119
+ independentlyCertified: false,
120
+ kfdPackage: {
121
+ name: kfdPackageJson.name,
122
+ version: kfdPackageJson.version,
123
+ },
124
+ categoryReport,
125
+ adapterIssues,
126
+ nonClaims: [
127
+ "Category conformance does not grant runtime permission or release authorization.",
128
+ "Buildchain carries the published KFD decision and does not certify or reinterpret it.",
129
+ ],
130
+ };
131
+ return {
132
+ valid,
133
+ report,
134
+ reportRoot: adopterDeliveryGateDigest(report),
135
+ issues: [...(categoryReport?.issues ?? []), ...adapterIssues],
136
+ };
137
+ }
138
+
139
+ export function createKfdAdopterCategoryProtocolDriver() {
140
+ return defineAdopterProtocolDriver({
141
+ interface: ADOPTER_PROTOCOL_DRIVER_INTERFACE,
142
+ id: KFD_ADOPTER_CATEGORY_PROTOCOL_ID,
143
+ version: KFD_ADOPTER_CATEGORY_PROTOCOL_VERSION,
144
+ verify({ request, context }) {
145
+ const adapterIssues = deliveryBindingIssues(
146
+ request,
147
+ context?.adopterManifest,
148
+ );
149
+ let categoryReport = null;
150
+ try {
151
+ categoryReport = verifyPublishedKfdAdopterCategoryInstance(
152
+ request.declaration,
153
+ context,
154
+ );
155
+ } catch {
156
+ adapterIssues.push(
157
+ issue(
158
+ "delivery-kfd-verification-context-invalid",
159
+ "/protocol",
160
+ "Published KFD verification could not reproduce from the supplied exact context.",
161
+ ),
162
+ );
163
+ }
164
+ return driverReport(categoryReport, adapterIssues);
165
+ },
166
+ });
167
+ }