@kungfu-tech/buildchain 3.0.9-alpha.1 → 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 (100) hide show
  1. package/bin/buildchain.mjs +9 -1
  2. package/contracts/dev-delivery-authority-v2.schema.json +253 -0
  3. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
  4. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
  5. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
  6. package/contracts/next-development-request-v1.schema.json +66 -0
  7. package/contracts/next-development-transition-v1.schema.json +292 -0
  8. package/dist/site/agent-index.json +1 -0
  9. package/dist/site/artifact-schemas.json +2 -0
  10. package/dist/site/buildchain-contract.json +74 -14
  11. package/dist/site/buildchain-site.json +334 -36
  12. package/dist/site/capability-registry.json +7 -6
  13. package/dist/site/cli-registry.json +41 -2
  14. package/dist/site/kfd-claims.json +193 -9
  15. package/dist/site/kfd-upstream-aggregate.json +9 -9
  16. package/dist/site/manual-registry.json +23 -7
  17. package/dist/site/node-api-registry.json +7789 -4648
  18. package/dist/site/page-registry.json +315 -25
  19. package/dist/site/public-surface-audit.json +353 -8
  20. package/dist/site/publication-registry.json +4 -4
  21. package/dist/site/release-passport-check-manifest.json +1 -0
  22. package/dist/site/release-provenance.json +12 -0
  23. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +442 -0
  24. package/dist/site/schemas/release-passport-v1.schema.json +3 -0
  25. package/dist/site/site-manifest.json +19 -11
  26. package/dist/site/workflow-registry.json +15 -7
  27. package/docs/MAP.md +5 -2
  28. package/docs/adopter-delivery-gate.md +106 -0
  29. package/docs/aws-us-elastic-runner-burst-plane.md +30 -3
  30. package/docs/cli-reference.md +200 -13
  31. package/docs/dev-alpha-candidate-patrol.md +8 -0
  32. package/docs/dev-delivery-qualification-landing-adr.md +137 -0
  33. package/docs/dev-delivery-warrant.md +149 -22
  34. package/docs/kfd-support.md +7 -2
  35. package/docs/next-development-transition.md +119 -0
  36. package/docs/node-api-reference.md +381 -204
  37. package/docs/release-governance.md +10 -7
  38. package/docs/release-passport.md +8 -2
  39. package/docs/versioning.md +4 -1
  40. package/package.json +21 -6
  41. package/packages/core/README.md +25 -0
  42. package/packages/core/adopter-delivery-gate.js +576 -0
  43. package/packages/core/adopter-delivery-json.js +66 -0
  44. package/packages/core/adopter-delivery-passport.js +259 -0
  45. package/packages/core/adopter-delivery-vectors.js +158 -0
  46. package/packages/core/adopter-delivery-vectors.json +77 -0
  47. package/packages/core/artifact-verification-envelope.js +5 -5
  48. package/packages/core/buildchain-agent-manuals.js +1 -0
  49. package/packages/core/buildchain-channel-identity.js +2 -3
  50. package/packages/core/buildchain-compatibility-proof.js +22 -0
  51. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  52. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  53. package/packages/core/channel-candidate.js +8 -0
  54. package/packages/core/channel-promotion-baseline.js +52 -0
  55. package/packages/core/dev-alpha-candidate-selection.js +10 -2
  56. package/packages/core/dev-delivery-authority.js +996 -0
  57. package/packages/core/dev-delivery-native-proof.js +418 -0
  58. package/packages/core/dev-delivery-warrant-settlement.js +67 -20
  59. package/packages/core/dev-delivery-warrant.js +128 -46
  60. package/packages/core/kfd-adopter-category-driver.js +167 -0
  61. package/packages/core/kfd-adopter-manifest.js +49 -50
  62. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  63. package/packages/core/next-development-candidate-reservation.js +186 -0
  64. package/packages/core/next-development-controller.js +726 -0
  65. package/packages/core/next-development-projection.js +288 -0
  66. package/packages/core/next-development-transition.js +738 -0
  67. package/packages/core/paper-agent-entry.js +7 -4
  68. package/packages/core/paper.js +14 -7
  69. package/packages/core/release-passport-contract.js +10 -6
  70. package/packages/core/release-passport.js +29 -12
  71. package/scripts/aws-macos-jit-controller-core.mjs +239 -4
  72. package/scripts/aws-macos-jit-controller.mjs +198 -2
  73. package/scripts/aws-macos-jit-instance-rehydrate.mjs +260 -0
  74. package/scripts/aws-macos-jit-source-rebind.mjs +235 -14
  75. package/scripts/aws-macos-jit.mjs +12 -0
  76. package/scripts/buildchain-cli-help.mjs +9 -2
  77. package/scripts/check-internal-architecture.mjs +1 -0
  78. package/scripts/check-inventory.mjs +12 -1
  79. package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
  80. package/scripts/dev-delivery-authority.mjs +368 -0
  81. package/scripts/dev-delivery-native-run.mjs +235 -0
  82. package/scripts/dev-delivery-proof.mjs +64 -2
  83. package/scripts/dev-delivery-two-phase-resume.mjs +90 -0
  84. package/scripts/dev-delivery-two-phase.mjs +577 -0
  85. package/scripts/dev-delivery-warrant.mjs +47 -5
  86. package/scripts/dev-pr-delivery-warrant.mjs +19 -14
  87. package/scripts/generate-buildchain-kfd-witnesses.mjs +2 -2
  88. package/scripts/generate-channel-build-workflow.mjs +4 -0
  89. package/scripts/generate-next-development-guidance.mjs +49 -0
  90. package/scripts/generate-site-bundle.mjs +8 -3
  91. package/scripts/init-repo.mjs +135 -62
  92. package/scripts/next-development-self-dogfood-harness.mjs +409 -0
  93. package/scripts/next-development-self-dogfood.mjs +532 -0
  94. package/scripts/next-development-transition.mjs +47 -0
  95. package/scripts/release-candidate-tail-reseal.mjs +426 -0
  96. package/scripts/seal-artifact-signing-requests.mjs +7 -4
  97. package/scripts/site-capability-metadata.mjs +23 -1
  98. package/scripts/stable-candidate-qualification.mjs +7 -7
  99. package/scripts/workflow-call-contract.mjs +1 -1
  100. package/templates/native-dev-delivery.yml +86 -0
@@ -2,10 +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";
5
6
  import { compareReleaseBlockerPriority, normalizeReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
6
7
  export { devDeliveryContentRoot } from "./dev-delivery-common.js";
7
8
  export { RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA, createReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
8
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";
9
11
  export { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA };
10
12
  export const DEV_DELIVERY_QUEUE_CONTRACT = "kungfu-buildchain-dev-delivery-warrant-queue";
11
13
  export const DEV_DELIVERY_WARRANT_SCHEMA = "kungfu.buildchain.dev-delivery-warrant/v1";
@@ -21,7 +23,14 @@ export const DEV_DELIVERY_PRIORITIES = Object.freeze({
21
23
 
22
24
  export const DEV_DELIVERY_CLASSES = Object.freeze(["non-native-fast", "native-proof-required", "cross-platform", "release"]);
23
25
 
24
- 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
+ });
25
34
  function nonNegativeInteger(value, label, fallback = 0) {
26
35
  const parsed = Number(value ?? fallback);
27
36
  if (!Number.isInteger(parsed) || parsed < 0) throw new Error(`${label} must be a non-negative integer`);
@@ -59,7 +68,7 @@ function normalizeCandidate(input, expected) {
59
68
  throw new Error(`candidateId mismatch for PR #${identity.pullRequestNumber}`);
60
69
  }
61
70
  const status = text(input.status || "queued");
62
- if (!["queued", "selected", "proving", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
71
+ if (!["queued", "selected", "provisional", "proving", "qualified", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
63
72
  throw new Error(`unsupported candidate status ${status || "<empty>"}`);
64
73
  }
65
74
  const candidate = {
@@ -71,7 +80,14 @@ function normalizeCandidate(input, expected) {
71
80
  closureRoot: exactRoot(input.closureRoot, "closureRoot"),
72
81
  dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
73
82
  toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
74
- ...(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
+ : {}),
75
91
  priority: priority(input.priority),
76
92
  enqueuedAt: timestamp(input.enqueuedAt, "candidate enqueuedAt"),
77
93
  updatedAt: timestamp(input.updatedAt || input.enqueuedAt, "candidate updatedAt"),
@@ -80,6 +96,10 @@ function normalizeCandidate(input, expected) {
80
96
  status,
81
97
  terminal: input.terminal || null,
82
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
+ }
83
103
  if (Object.hasOwn(input, "releaseBlockerPriority")) candidate.releaseBlockerPriority = normalizeReleaseBlockerPriorityClaim(input.releaseBlockerPriority, candidate, expected);
84
104
  return candidate;
85
105
  }
@@ -140,12 +160,20 @@ export function normalizeDevDeliveryQueue(input, expected = {}) {
140
160
  warrant.generation = positiveInteger(warrant.generation, "Warrant generation");
141
161
  warrant.issuedAt = timestamp(warrant.issuedAt, "Warrant issuedAt");
142
162
  warrant.expiresAt = timestamp(warrant.expiresAt, "Warrant expiresAt");
143
- 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));
144
172
  if (activeCandidates.length !== 1 || activeCandidates[0].candidateId !== warrant.candidateId) {
145
173
  throw new Error("exactly one active candidate must match the active Warrant");
146
174
  }
147
175
  if (warrant.releaseBlockerPriority?.claimRoot !== activeCandidates[0].releaseBlockerPriority?.claimRoot) throw new Error("active Warrant release-blocker priority drift");
148
- } else if (queue.candidates.some((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status))) {
176
+ } else if (queue.candidates.some((candidate) => ["selected", "provisional", "proving", "qualified", "waiting", "blocked"].includes(candidate.status))) {
149
177
  throw new Error("active candidate exists without an active Warrant");
150
178
  }
151
179
  const rooted = withQueueRoot(queue);
@@ -183,7 +211,8 @@ function submissionReceipt({ before, after, candidate, action, now }) {
183
211
  planRoot: candidate.planRoot,
184
212
  closureRoot: candidate.closureRoot,
185
213
  dependencyRoot: candidate.dependencyRoot,
186
- toolchainRoot: candidate.toolchainRoot,
214
+ toolchainRoot: candidate.toolchainRoot, ...(candidate.environmentRoot ? { environmentRoot: candidate.environmentRoot } : {}),
215
+ ...(Object.hasOwn(candidate, "affectedPaths") ? { affectedPaths: candidate.affectedPaths } : {}),
187
216
  sourceWorkflowRunId: candidate.sourceWorkflowRunId,
188
217
  ...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
189
218
  deliveryClass: candidate.deliveryClass,
@@ -198,6 +227,21 @@ function submissionReceipt({ before, after, candidate, action, now }) {
198
227
  };
199
228
  }
200
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
+
201
245
  export function submitDevDeliveryCandidate(queueInput, input, { now = new Date().toISOString() } = {}) {
202
246
  const transaction = transition(
203
247
  queueInput,
@@ -213,7 +257,13 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
213
257
  },
214
258
  queue,
215
259
  );
216
- 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
+ });
217
267
  const attemptedCandidate = chainedInput ? normalizeCandidate(chainedInput, queue) : candidate;
218
268
  const conflicting = queue.candidates.find((entry) => entry.pullRequestNumber === attemptedCandidate.pullRequestNumber && entry.candidateId !== attemptedCandidate.candidateId && !TERMINAL_STATES.has(entry.status));
219
269
  if (conflicting) throw new Error(`PR #${candidate.pullRequestNumber} already has a different active semantic source`);
@@ -224,10 +274,12 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
224
274
  if (TERMINAL_STATES.has(existing.status)) {
225
275
  throw new Error(`candidate ${attemptedCandidate.candidateId} is terminal and cannot be resubmitted`);
226
276
  }
227
- const exactProofFields = ["sourcePatchRoot", "sourceProofRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot"];
228
- const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]) && existing.releaseBlockerPriority?.claimRoot === attemptedCandidate.releaseBlockerPriority?.claimRoot;
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);
229
280
  if (existing.sourceHead === attemptedCandidate.sourceHead && exactProofMatches) {
230
- 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";
231
283
  selected = existing;
232
284
  } else {
233
285
  if (before.activeWarrant?.candidateId === existing.candidateId) {
@@ -241,6 +293,8 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
241
293
  const headChanged = existing.sourceHead !== attemptedCandidate.sourceHead;
242
294
  existing.sourceHead = attemptedCandidate.sourceHead;
243
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;
244
298
  if (Object.hasOwn(attemptedCandidate, "releaseBlockerPriority")) existing.releaseBlockerPriority = attemptedCandidate.releaseBlockerPriority;
245
299
  else delete existing.releaseBlockerPriority;
246
300
  existing.updatedAt = currentTime;
@@ -308,26 +362,26 @@ function warrantToken(input) {
308
362
 
309
363
  export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOString(), leaseSeconds } = {}) {
310
364
  const currentTime = timestamp(now, "now");
311
- let queue = normalizeDevDeliveryQueue(queueInput);
365
+ const queue = normalizeDevDeliveryQueue(queueInput);
312
366
  let recoveryReceipt = null;
313
367
  if (queue.activeWarrant && Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(currentTime)) {
314
368
  const recovered = recoverExpiredDevDeliveryWarrant(queue, {
315
369
  now: currentTime,
316
370
  });
317
- queue = recovered.queue;
318
371
  recoveryReceipt = recovered.receipt;
319
372
  }
320
373
  if (queue.activeWarrant) {
374
+ const expired = Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(currentTime);
321
375
  const receipt = {
322
376
  schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
323
- selected: true,
324
- reason: "non-preemptive-active-warrant",
377
+ selected: !expired,
378
+ reason: expired ? "expired-active-warrant-awaits-fenced-settlement" : "non-preemptive-active-warrant",
325
379
  candidateId: queue.activeWarrant.candidateId,
326
380
  fencingToken: queue.activeWarrant.fencingToken,
327
381
  leaseGeneration: queue.activeWarrant.generation,
328
382
  expectedOldStateRoot: recoveryReceipt?.expectedOldStateRoot || queue.stateRoot,
329
383
  nextStateRoot: queue.stateRoot,
330
- 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.",
331
385
  };
332
386
  return {
333
387
  queue,
@@ -378,16 +432,18 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
378
432
  planRoot: candidate.planRoot,
379
433
  closureRoot: candidate.closureRoot,
380
434
  dependencyRoot: candidate.dependencyRoot,
381
- toolchainRoot: candidate.toolchainRoot,
435
+ toolchainRoot: candidate.toolchainRoot, ...(candidate.environmentRoot ? { environmentRoot: candidate.environmentRoot } : {}),
436
+ ...(Object.hasOwn(candidate, "affectedPaths") ? { affectedPaths: candidate.affectedPaths } : {}),
382
437
  sourceWorkflowRunId: candidate.sourceWorkflowRunId,
383
438
  ...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
384
439
  deliveryClass: candidate.deliveryClass,
385
440
  generation: next.fencingCounter,
441
+ phase: "provisional",
386
442
  expectedOldStateRoot: before.stateRoot,
387
443
  issuedAt,
388
444
  expiresAt: new Date(Date.parse(issuedAt) + effectiveLeaseSeconds * 1000).toISOString(),
389
445
  fencingToken: "",
390
- 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.",
391
447
  };
392
448
  warrant.fencingToken = warrantToken(warrant);
393
449
  next.activeWarrant = warrant;
@@ -414,9 +470,13 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
414
470
  planRoot: transaction.result.candidate.planRoot,
415
471
  closureRoot: transaction.result.candidate.closureRoot,
416
472
  dependencyRoot: transaction.result.candidate.dependencyRoot,
417
- toolchainRoot: transaction.result.candidate.toolchainRoot,
473
+ toolchainRoot: transaction.result.candidate.toolchainRoot, ...(transaction.result.candidate.environmentRoot ? { environmentRoot: transaction.result.candidate.environmentRoot } : {}),
418
474
  sourceWorkflowRunId: transaction.result.candidate.sourceWorkflowRunId,
419
- ...(Object.hasOwn(transaction.result.candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: transaction.result.candidate.releaseBlockerPriority } : {}),
475
+ ...(Object.hasOwn(transaction.result.candidate, "releaseBlockerPriority")
476
+ ? {
477
+ releaseBlockerPriority: transaction.result.candidate.releaseBlockerPriority,
478
+ }
479
+ : {}),
420
480
  deliveryClass: transaction.result.candidate.deliveryClass,
421
481
  queueAgeSeconds: selected.priority.ageSeconds,
422
482
  basePriority: selected.priority.basePriority,
@@ -454,7 +514,7 @@ export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Dat
454
514
  const effectiveLeaseSeconds = positiveInteger(leaseSeconds, "leaseSeconds", queue.policy.leaseSeconds);
455
515
  queue.activeWarrant.expiresAt = new Date(Date.parse(currentTime) + effectiveLeaseSeconds * 1000).toISOString();
456
516
  const candidate = queue.candidates.find((entry) => entry.candidateId === queue.activeWarrant.candidateId);
457
- candidate.status = "proving";
517
+ candidate.status = queue.activeWarrant.phase === "qualified" ? "qualified" : "proving";
458
518
  candidate.updatedAt = currentTime;
459
519
  return { candidate, warrant: queue.activeWarrant };
460
520
  },
@@ -478,6 +538,13 @@ export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Dat
478
538
  };
479
539
  }
480
540
 
541
+ const qualifyDevDeliveryWarrantTransition = createDevDeliveryWarrantQualifier({
542
+ transition,
543
+ assertWarrantMutation,
544
+ });
545
+ export function qualifyDevDeliveryWarrant(queueInput, warrant, options) {
546
+ return qualifyDevDeliveryWarrantTransition(queueInput, warrant, options);
547
+ }
481
548
  export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().toISOString() } = {}) {
482
549
  const currentTime = timestamp(now, "now");
483
550
  const before = normalizeDevDeliveryQueue(queueInput);
@@ -496,33 +563,21 @@ export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().
496
563
  receiptRoot: devDeliveryContentRoot(receipt),
497
564
  };
498
565
  }
499
- const transaction = transition(
500
- before,
501
- (queue) => {
502
- const expired = clone(queue.activeWarrant);
503
- const candidate = queue.candidates.find((entry) => entry.candidateId === expired.candidateId);
504
- candidate.status = "queued";
505
- candidate.recoveries += 1;
506
- candidate.attempts += 1;
507
- candidate.updatedAt = currentTime;
508
- queue.activeWarrant = null;
509
- return { candidate, expired };
510
- },
511
- currentTime,
512
- );
566
+ const expired = before.activeWarrant;
567
+ const candidate = before.candidates.find((entry) => entry.candidateId === expired.candidateId);
513
568
  const receipt = {
514
569
  schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
515
- action: "recovered-expired-lease",
516
- candidateId: transaction.result.candidate.candidateId,
517
- rejectedFencingToken: transaction.result.expired.fencingToken,
518
- expiredLeaseGeneration: transaction.result.expired.generation,
519
- retainedEnqueuedAt: transaction.result.candidate.enqueuedAt,
520
- expectedOldStateRoot: transaction.expectedOldStateRoot,
521
- nextStateRoot: transaction.after.stateRoot,
522
- 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.",
523
578
  };
524
579
  return {
525
- queue: transaction.after,
580
+ queue: before,
526
581
  receipt,
527
582
  receiptRoot: devDeliveryContentRoot(receipt),
528
583
  };
@@ -534,16 +589,22 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
534
589
  if (!TERMINAL_STATES.has(normalizedOutcome)) {
535
590
  throw new Error(`outcome must be one of ${[...TERMINAL_STATES].join(", ")}`);
536
591
  }
592
+ if (normalizedOutcome === "dequeued") {
593
+ throw new Error("transient dequeue cannot close an active Delivery Warrant");
594
+ }
537
595
  const transaction = transition(
538
596
  queueInput,
539
597
  (queue, before) => {
540
- assertWarrantMutation(before, warrant, currentTime, { allowExpired: true });
598
+ assertWarrantMutation(before, warrant, currentTime, {
599
+ allowExpired: true,
600
+ });
541
601
  const active = clone(queue.activeWarrant);
542
602
  const candidate = queue.candidates.find((entry) => entry.candidateId === active.candidateId);
543
603
  candidate.status = normalizedOutcome;
544
604
  candidate.updatedAt = currentTime;
545
605
  candidate.terminal = {
546
606
  outcome: normalizedOutcome,
607
+ terminalClass: TERMINAL_CLASSES[normalizedOutcome],
547
608
  reason: text(reason),
548
609
  evidenceRoot: exactRoot(evidenceRoot, "evidenceRoot"),
549
610
  closedAt: currentTime,
@@ -559,6 +620,7 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
559
620
  schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
560
621
  action: "terminal-closeout",
561
622
  outcome: normalizedOutcome,
623
+ terminalClass: TERMINAL_CLASSES[normalizedOutcome],
562
624
  reason: text(reason),
563
625
  candidateId: transaction.result.candidate.candidateId,
564
626
  fencingToken: transaction.result.active.fencingToken,
@@ -576,8 +638,15 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
576
638
  }
577
639
 
578
640
  const cancelQueuedDevDeliveryCandidateTransition = createCancelQueuedDevDeliveryCandidate(normalizeDevDeliveryQueue);
579
- export function cancelQueuedDevDeliveryCandidate(queueInput, input, options) { return cancelQueuedDevDeliveryCandidateTransition(queueInput, input, options); }
580
- 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
+ });
581
650
 
582
651
  export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOString() } = {}) {
583
652
  const queue = normalizeDevDeliveryQueue(queueInput);
@@ -604,6 +673,19 @@ export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOStri
604
673
  queueAgeSeconds: entry.priority.ageSeconds,
605
674
  attempts: entry.candidate.attempts,
606
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,
607
689
  nextAction: index === 0 && !queue.activeWarrant ? "eligible-for-selection" : "wait-for-active-warrant",
608
690
  })),
609
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
+ }