@kungfu-tech/buildchain 3.0.6-alpha.0 → 3.0.6-alpha.2

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 (72) hide show
  1. package/README.md +4 -4
  2. package/actions/promote-buildchain-ref/README.md +8 -0
  3. package/bin/buildchain.mjs +13 -1
  4. package/contracts/auditable-demo-scenario-v1.schema.json +52 -0
  5. package/dist/site/buildchain-contract.json +47 -27
  6. package/dist/site/buildchain-site.json +165 -44
  7. package/dist/site/capability-registry.json +3 -3
  8. package/dist/site/cli-registry.json +40 -4
  9. package/dist/site/controller-registry.json +20 -4
  10. package/dist/site/kfd-claims.json +140 -19
  11. package/dist/site/kfd-upstream-aggregate.json +9 -9
  12. package/dist/site/manual-registry.json +9 -9
  13. package/dist/site/node-api-registry.json +1161 -180
  14. package/dist/site/page-registry.json +152 -31
  15. package/dist/site/public-surface-audit.json +386 -19
  16. package/dist/site/publication-authority-registry.json +61 -1
  17. package/dist/site/publication-registry.json +4 -4
  18. package/dist/site/release-provenance.json +1 -0
  19. package/dist/site/site-manifest.json +13 -13
  20. package/dist/site/workflow-registry.json +151 -13
  21. package/docs/MAP.md +2 -0
  22. package/docs/auditable-demo.md +58 -11
  23. package/docs/aws-us-elastic-runner-burst-plane.md +114 -80
  24. package/docs/cli-reference.md +154 -0
  25. package/docs/dev-alpha-candidate-patrol.md +13 -5
  26. package/docs/dev-delivery-warrant.md +158 -0
  27. package/docs/node-api-reference.md +54 -15
  28. package/docs/publication-authority.md +11 -0
  29. package/docs/release-candidate.md +19 -2
  30. package/docs/release-governance.md +60 -1
  31. package/docs/reusable-build-surface.md +11 -1
  32. package/docs/shifu-gate-profiles.md +12 -1
  33. package/docs/versioning.md +2 -0
  34. package/package.json +4 -2
  35. package/packages/core/buildchain-publication-authority.js +3 -1
  36. package/packages/core/channel-candidate.js +2 -21
  37. package/packages/core/channel-promotion-baseline.js +199 -0
  38. package/packages/core/dev-delivery-candidate-identity.js +94 -0
  39. package/packages/core/dev-delivery-common.js +73 -0
  40. package/packages/core/dev-delivery-proof.js +252 -0
  41. package/packages/core/dev-delivery-warrant-cancellation.js +94 -0
  42. package/packages/core/dev-delivery-warrant-settlement.js +73 -0
  43. package/packages/core/dev-delivery-warrant.js +591 -0
  44. package/scripts/auditable-demo-bundle-verification.mjs +148 -0
  45. package/scripts/auditable-demo-platform.mjs +86 -50
  46. package/scripts/auditable-demo-presentation.mjs +83 -0
  47. package/scripts/auditable-demo-renditions.mjs +264 -0
  48. package/scripts/auditable-demo.mjs +24 -30
  49. package/scripts/aws-windows-jit-campaign-core.mjs +7 -8
  50. package/scripts/aws-windows-jit-controller.mjs +1 -0
  51. package/scripts/aws-windows-jit-core.mjs +1 -1
  52. package/scripts/build-contract-core.mjs +58 -3
  53. package/scripts/buildchain-cli-help.mjs +8 -0
  54. package/scripts/buildchain-patrol.mjs +9 -0
  55. package/scripts/check-inventory.mjs +1 -0
  56. package/scripts/dev-alpha-candidate-patrol.mjs +45 -48
  57. package/scripts/dev-delivery-proof.mjs +193 -0
  58. package/scripts/dev-delivery-warrant.mjs +426 -0
  59. package/scripts/dev-pr-auto-merge.mjs +497 -55
  60. package/scripts/dev-pr-delivery-warrant.mjs +209 -0
  61. package/scripts/dispatch-artifact-signing-authority.mjs +2 -4
  62. package/scripts/gate-profile-core.mjs +24 -0
  63. package/scripts/generate-site-bundle.mjs +2 -2
  64. package/scripts/git-fetch-process-tree.mjs +142 -0
  65. package/scripts/lifecycle-substage-evidence.mjs +274 -0
  66. package/scripts/locked-source-checkout.mjs +6 -3
  67. package/scripts/resolve-artifact-transfer-mode.mjs +9 -0
  68. package/scripts/resolve-build-contract.mjs +7 -0
  69. package/scripts/route-offline-runners.mjs +1 -0
  70. package/scripts/run-lifecycle-core.mjs +9 -9
  71. package/scripts/shifu-gate-profile.mjs +10 -16
  72. package/scripts/site-capability-metadata.mjs +14 -0
@@ -0,0 +1,591 @@
1
+ import { devDeliveryClone as clone, devDeliveryContentRoot, devDeliveryExactRoot as exactRoot, devDeliveryExactSha as exactSha, devDeliveryPositiveInteger as positiveInteger, devDeliveryProtectedBase as protectedBase, devDeliveryRepository as repository, devDeliveryText as text, devDeliveryTimestamp as timestamp } from "./dev-delivery-common.js";
2
+ import { chainedDevDeliveryAttemptInput, createDevDeliveryCandidateIdentity, validateDevDeliveryCandidateChain } from "./dev-delivery-candidate-identity.js";
3
+ import { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA, createCancelQueuedDevDeliveryCandidate } from "./dev-delivery-warrant-cancellation.js";
4
+ import { DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA, createDevDeliveryTerminalSettler } from "./dev-delivery-warrant-settlement.js";
5
+ export { devDeliveryContentRoot } from "./dev-delivery-common.js";
6
+ 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";
7
+ export { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA };
8
+ export const DEV_DELIVERY_QUEUE_CONTRACT = "kungfu-buildchain-dev-delivery-warrant-queue";
9
+ export const DEV_DELIVERY_WARRANT_SCHEMA = "kungfu.buildchain.dev-delivery-warrant/v1";
10
+ export const DEV_DELIVERY_SUBMISSION_RECEIPT_SCHEMA = "kungfu.buildchain.dev-delivery-submission-receipt/v1";
11
+ export const DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA = "kungfu.buildchain.dev-delivery-selection-receipt/v1";
12
+ export const DEV_DELIVERY_LEASE_RECEIPT_SCHEMA = "kungfu.buildchain.dev-delivery-lease-receipt/v1";
13
+ export { DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA };
14
+ export const DEV_DELIVERY_PRIORITIES = Object.freeze({
15
+ ordinary: 0,
16
+ expedited: 1,
17
+ emergency: 2,
18
+ });
19
+
20
+ export const DEV_DELIVERY_CLASSES = Object.freeze(["non-native-fast", "native-proof-required", "cross-platform", "release"]);
21
+
22
+ const TERMINAL_STATES = new Set(["merged", "terminal-failure", "dequeued", "cancelled"]);
23
+ function nonNegativeInteger(value, label, fallback = 0) {
24
+ const parsed = Number(value ?? fallback);
25
+ if (!Number.isInteger(parsed) || parsed < 0) throw new Error(`${label} must be a non-negative integer`);
26
+ return parsed;
27
+ }
28
+
29
+ function priority(value = "ordinary") {
30
+ const normalized = text(value || "ordinary").toLowerCase();
31
+ if (!(normalized in DEV_DELIVERY_PRIORITIES)) {
32
+ throw new Error(`priority must be one of ${Object.keys(DEV_DELIVERY_PRIORITIES).join(", ")}`);
33
+ }
34
+ return normalized;
35
+ }
36
+
37
+ function deliveryClass(value) {
38
+ const normalized = text(value);
39
+ if (!DEV_DELIVERY_CLASSES.includes(normalized)) {
40
+ throw new Error(`deliveryClass must be one of ${DEV_DELIVERY_CLASSES.join(", ")}`);
41
+ }
42
+ return normalized;
43
+ }
44
+
45
+ function normalizePolicy(policy = {}) {
46
+ return {
47
+ agingSeconds: positiveInteger(policy.agingSeconds, "policy agingSeconds", 900),
48
+ maxPriority: positiveInteger(policy.maxPriority, "policy maxPriority", 2),
49
+ leaseSeconds: positiveInteger(policy.leaseSeconds, "policy leaseSeconds", 3600),
50
+ emergencyPolicy: text(policy.emergencyPolicy || "reviewed-explicit-only"),
51
+ };
52
+ }
53
+
54
+ function normalizeCandidate(input, expected) {
55
+ const identity = createDevDeliveryCandidateIdentity(input, expected, deliveryClass);
56
+ if (input.candidateId && input.candidateId !== identity.candidateId) {
57
+ throw new Error(`candidateId mismatch for PR #${identity.pullRequestNumber}`);
58
+ }
59
+ const status = text(input.status || "queued");
60
+ if (!["queued", "selected", "proving", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
61
+ throw new Error(`unsupported candidate status ${status || "<empty>"}`);
62
+ }
63
+ return {
64
+ ...identity,
65
+ sourceHead: exactSha(input.sourceHead, "sourceHead"),
66
+ sourcePatchRoot: exactRoot(input.sourcePatchRoot, "sourcePatchRoot"),
67
+ sourceProofRoot: exactRoot(input.sourceProofRoot, "sourceProofRoot"),
68
+ planRoot: exactRoot(input.planRoot, "planRoot"),
69
+ closureRoot: exactRoot(input.closureRoot, "closureRoot"),
70
+ dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
71
+ toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
72
+ priority: priority(input.priority),
73
+ enqueuedAt: timestamp(input.enqueuedAt, "candidate enqueuedAt"),
74
+ updatedAt: timestamp(input.updatedAt || input.enqueuedAt, "candidate updatedAt"),
75
+ attempts: positiveInteger(input.attempts, "candidate attempts", 1),
76
+ recoveries: nonNegativeInteger(input.recoveries, "candidate recoveries", 0),
77
+ status,
78
+ terminal: input.terminal || null,
79
+ };
80
+ }
81
+
82
+ function queueBody(queue) {
83
+ const body = clone(queue);
84
+ delete body.stateRoot;
85
+ return body;
86
+ }
87
+
88
+ function withQueueRoot(queue) {
89
+ return {
90
+ ...queueBody(queue),
91
+ stateRoot: devDeliveryContentRoot(queueBody(queue)),
92
+ };
93
+ }
94
+
95
+ export function createDevDeliveryQueue({ repository: repositoryInput, protectedBase: protectedBaseInput, policy = {}, now = new Date().toISOString() } = {}) {
96
+ return withQueueRoot({
97
+ schemaVersion: 1,
98
+ contract: DEV_DELIVERY_QUEUE_CONTRACT,
99
+ repository: repository(repositoryInput),
100
+ protectedBase: protectedBase(protectedBaseInput),
101
+ generation: 0,
102
+ fencingCounter: 0,
103
+ policy: normalizePolicy(policy),
104
+ activeWarrant: null,
105
+ candidates: [],
106
+ updatedAt: timestamp(now, "now"),
107
+ });
108
+ }
109
+
110
+ export function normalizeDevDeliveryQueue(input, expected = {}) {
111
+ const queue = clone(input || {});
112
+ if (queue.contract !== DEV_DELIVERY_QUEUE_CONTRACT || Number(queue.schemaVersion) !== 1) {
113
+ throw new Error(`dev delivery queue must use ${DEV_DELIVERY_QUEUE_CONTRACT} schemaVersion 1`);
114
+ }
115
+ queue.repository = repository(queue.repository);
116
+ queue.protectedBase = protectedBase(queue.protectedBase);
117
+ if (expected.repository && queue.repository !== expected.repository) {
118
+ throw new Error(`dev delivery queue repository mismatch: ${queue.repository} != ${expected.repository}`);
119
+ }
120
+ if (expected.protectedBase && queue.protectedBase !== expected.protectedBase) {
121
+ throw new Error(`dev delivery queue protectedBase mismatch: ${queue.protectedBase} != ${expected.protectedBase}`);
122
+ }
123
+ queue.generation = nonNegativeInteger(queue.generation, "queue generation");
124
+ queue.fencingCounter = nonNegativeInteger(queue.fencingCounter, "queue fencingCounter");
125
+ queue.policy = normalizePolicy(queue.policy);
126
+ queue.candidates = (queue.candidates || []).map((candidate) => normalizeCandidate(candidate, queue));
127
+ validateDevDeliveryCandidateChain(queue.candidates, TERMINAL_STATES);
128
+ queue.updatedAt = timestamp(queue.updatedAt, "queue updatedAt");
129
+ if (queue.activeWarrant) {
130
+ const warrant = queue.activeWarrant;
131
+ if (warrant.schema !== DEV_DELIVERY_WARRANT_SCHEMA) throw new Error("active Warrant schema is unsupported");
132
+ warrant.candidateId = exactRoot(warrant.candidateId, "Warrant candidateId");
133
+ warrant.fencingToken = exactRoot(warrant.fencingToken, "Warrant fencingToken");
134
+ warrant.expectedOldStateRoot = exactRoot(warrant.expectedOldStateRoot, "Warrant expectedOldStateRoot");
135
+ warrant.generation = positiveInteger(warrant.generation, "Warrant generation");
136
+ warrant.issuedAt = timestamp(warrant.issuedAt, "Warrant issuedAt");
137
+ warrant.expiresAt = timestamp(warrant.expiresAt, "Warrant expiresAt");
138
+ const activeCandidates = queue.candidates.filter((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status));
139
+ if (activeCandidates.length !== 1 || activeCandidates[0].candidateId !== warrant.candidateId) {
140
+ throw new Error("exactly one active candidate must match the active Warrant");
141
+ }
142
+ } else if (queue.candidates.some((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status))) {
143
+ throw new Error("active candidate exists without an active Warrant");
144
+ }
145
+ const rooted = withQueueRoot(queue);
146
+ if (input.stateRoot && input.stateRoot !== rooted.stateRoot) throw new Error("dev delivery queue stateRoot drift");
147
+ return rooted;
148
+ }
149
+
150
+ function transition(queueInput, mutate, nowInput) {
151
+ const before = normalizeDevDeliveryQueue(queueInput);
152
+ const expectedOldStateRoot = before.stateRoot;
153
+ const queue = clone(before);
154
+ delete queue.stateRoot;
155
+ const now = timestamp(nowInput, "now");
156
+ const result = mutate(queue, before, now);
157
+ queue.generation += 1;
158
+ queue.updatedAt = now;
159
+ const after = withQueueRoot(queue);
160
+ return { before, after, expectedOldStateRoot, result };
161
+ }
162
+
163
+ function submissionReceipt({ before, after, candidate, action, now }) {
164
+ const queueAgeSeconds = Math.max(0, Math.floor((Date.parse(now) - Date.parse(candidate.enqueuedAt)) / 1000));
165
+ return {
166
+ schema: DEV_DELIVERY_SUBMISSION_RECEIPT_SCHEMA,
167
+ repository: after.repository,
168
+ protectedBase: after.protectedBase,
169
+ candidateId: candidate.candidateId,
170
+ pullRequestNumber: candidate.pullRequestNumber,
171
+ sourceHead: candidate.sourceHead,
172
+ assignmentRoot: candidate.assignmentRoot,
173
+ initiativeRoot: candidate.initiativeRoot,
174
+ sourceIdentityRoot: candidate.sourceIdentityRoot,
175
+ sourcePatchRoot: candidate.sourcePatchRoot,
176
+ sourceProofRoot: candidate.sourceProofRoot,
177
+ planRoot: candidate.planRoot,
178
+ closureRoot: candidate.closureRoot,
179
+ dependencyRoot: candidate.dependencyRoot,
180
+ toolchainRoot: candidate.toolchainRoot,
181
+ deliveryClass: candidate.deliveryClass,
182
+ priority: candidate.priority,
183
+ retainedEnqueuedAt: candidate.enqueuedAt,
184
+ queueAgeSeconds,
185
+ attempts: candidate.attempts,
186
+ action,
187
+ expectedOldStateRoot: before.stateRoot,
188
+ nextStateRoot: after.stateRoot,
189
+ nextAction: "Wait for deterministic Warrant selection or observe the active predecessor.",
190
+ };
191
+ }
192
+
193
+ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date().toISOString() } = {}) {
194
+ const transaction = transition(
195
+ queueInput,
196
+ (queue, before, currentTime) => {
197
+ const candidate = normalizeCandidate(
198
+ {
199
+ ...input,
200
+ enqueuedAt: input.enqueuedAt || currentTime,
201
+ updatedAt: currentTime,
202
+ attempts: input.attempts || 1,
203
+ recoveries: input.recoveries || 0,
204
+ status: "queued",
205
+ },
206
+ queue,
207
+ );
208
+ const chainedInput = chainedDevDeliveryAttemptInput({ queue, candidate, input, currentTime, terminalStates: TERMINAL_STATES });
209
+ const attemptedCandidate = chainedInput ? normalizeCandidate(chainedInput, queue) : candidate;
210
+ const conflicting = queue.candidates.find((entry) => entry.pullRequestNumber === attemptedCandidate.pullRequestNumber && entry.candidateId !== attemptedCandidate.candidateId && !TERMINAL_STATES.has(entry.status));
211
+ if (conflicting) throw new Error(`PR #${candidate.pullRequestNumber} already has a different active semantic source`);
212
+ const existing = queue.candidates.find((entry) => entry.candidateId === attemptedCandidate.candidateId);
213
+ let action = "submitted";
214
+ let selected = attemptedCandidate;
215
+ if (existing) {
216
+ if (TERMINAL_STATES.has(existing.status)) {
217
+ throw new Error(`candidate ${attemptedCandidate.candidateId} is terminal and cannot be resubmitted`);
218
+ }
219
+ const exactProofFields = ["sourcePatchRoot", "sourceProofRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot"];
220
+ const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]);
221
+ if (existing.sourceHead === attemptedCandidate.sourceHead && exactProofMatches) {
222
+ action = "duplicate-noop";
223
+ selected = existing;
224
+ } else {
225
+ if (before.activeWarrant?.candidateId === existing.candidateId) {
226
+ throw new Error("selected candidate sourceHead cannot change before terminal Warrant closeout");
227
+ }
228
+ const headChanged = existing.sourceHead !== attemptedCandidate.sourceHead;
229
+ existing.sourceHead = attemptedCandidate.sourceHead;
230
+ for (const field of exactProofFields) existing[field] = attemptedCandidate[field];
231
+ existing.updatedAt = currentTime;
232
+ existing.attempts += 1;
233
+ existing.status = "queued";
234
+ action = headChanged ? "safe-head-repair-retained-age" : "safe-proof-refresh-retained-age";
235
+ selected = existing;
236
+ }
237
+ } else {
238
+ queue.candidates.push(attemptedCandidate);
239
+ }
240
+ return { candidate: selected, action };
241
+ },
242
+ now,
243
+ );
244
+ const receipt = submissionReceipt({
245
+ before: transaction.before,
246
+ after: transaction.after,
247
+ candidate: transaction.result.candidate,
248
+ action: transaction.result.action,
249
+ now: timestamp(now, "now"),
250
+ });
251
+ return {
252
+ queue: transaction.after,
253
+ receipt,
254
+ receiptRoot: devDeliveryContentRoot(receipt),
255
+ };
256
+ }
257
+
258
+ function effectivePriority(candidate, policy, now) {
259
+ const ageSeconds = Math.max(0, Math.floor((Date.parse(now) - Date.parse(candidate.enqueuedAt)) / 1000));
260
+ const agingBoost = Math.floor(ageSeconds / policy.agingSeconds);
261
+ return {
262
+ ageSeconds,
263
+ basePriority: DEV_DELIVERY_PRIORITIES[candidate.priority],
264
+ agingBoost,
265
+ score: Math.min(policy.maxPriority, DEV_DELIVERY_PRIORITIES[candidate.priority] + agingBoost),
266
+ };
267
+ }
268
+
269
+ export function rankDevDeliveryCandidates(queueInput, { now = new Date().toISOString() } = {}) {
270
+ const queue = normalizeDevDeliveryQueue(queueInput);
271
+ const currentTime = timestamp(now, "now");
272
+ return queue.candidates
273
+ .filter((candidate) => candidate.status === "queued")
274
+ .map((candidate) => ({
275
+ candidate,
276
+ priority: effectivePriority(candidate, queue.policy, currentTime),
277
+ }))
278
+ .sort((left, right) => right.priority.score - left.priority.score || left.candidate.enqueuedAt.localeCompare(right.candidate.enqueuedAt) || left.candidate.candidateId.localeCompare(right.candidate.candidateId));
279
+ }
280
+
281
+ function warrantToken(input) {
282
+ return devDeliveryContentRoot({
283
+ schema: DEV_DELIVERY_WARRANT_SCHEMA,
284
+ repository: input.repository,
285
+ protectedBase: input.protectedBase,
286
+ candidateId: input.candidateId,
287
+ generation: input.generation,
288
+ expectedOldStateRoot: input.expectedOldStateRoot,
289
+ issuedAt: input.issuedAt,
290
+ });
291
+ }
292
+
293
+ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOString(), leaseSeconds } = {}) {
294
+ const currentTime = timestamp(now, "now");
295
+ let queue = normalizeDevDeliveryQueue(queueInput);
296
+ let recoveryReceipt = null;
297
+ if (queue.activeWarrant && Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(currentTime)) {
298
+ const recovered = recoverExpiredDevDeliveryWarrant(queue, {
299
+ now: currentTime,
300
+ });
301
+ queue = recovered.queue;
302
+ recoveryReceipt = recovered.receipt;
303
+ }
304
+ if (queue.activeWarrant) {
305
+ const receipt = {
306
+ schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
307
+ selected: true,
308
+ reason: "non-preemptive-active-warrant",
309
+ candidateId: queue.activeWarrant.candidateId,
310
+ fencingToken: queue.activeWarrant.fencingToken,
311
+ leaseGeneration: queue.activeWarrant.generation,
312
+ expectedOldStateRoot: queue.stateRoot,
313
+ nextStateRoot: queue.stateRoot,
314
+ nextAction: "Continue the active delivery attempt; later candidates remain visibly queued.",
315
+ };
316
+ return {
317
+ queue,
318
+ warrant: queue.activeWarrant,
319
+ receipt,
320
+ receiptRoot: devDeliveryContentRoot(receipt),
321
+ recoveryReceipt,
322
+ };
323
+ }
324
+ const ranked = rankDevDeliveryCandidates(queue, { now: currentTime });
325
+ if (ranked.length === 0) {
326
+ const receipt = {
327
+ schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
328
+ selected: false,
329
+ reason: "no-qualified-candidates",
330
+ expectedOldStateRoot: queue.stateRoot,
331
+ nextStateRoot: queue.stateRoot,
332
+ nextAction: "Submit a qualified candidate.",
333
+ };
334
+ return {
335
+ queue,
336
+ warrant: null,
337
+ receipt,
338
+ receiptRoot: devDeliveryContentRoot(receipt),
339
+ recoveryReceipt,
340
+ };
341
+ }
342
+ const selected = ranked[0];
343
+ const transaction = transition(
344
+ queue,
345
+ (next, before) => {
346
+ const candidate = next.candidates.find((entry) => entry.candidateId === selected.candidate.candidateId);
347
+ next.fencingCounter += 1;
348
+ const issuedAt = currentTime;
349
+ const effectiveLeaseSeconds = positiveInteger(leaseSeconds, "leaseSeconds", next.policy.leaseSeconds);
350
+ const warrant = {
351
+ schema: DEV_DELIVERY_WARRANT_SCHEMA,
352
+ repository: next.repository,
353
+ protectedBase: next.protectedBase,
354
+ candidateId: candidate.candidateId,
355
+ pullRequestNumber: candidate.pullRequestNumber,
356
+ sourceHead: candidate.sourceHead,
357
+ assignmentRoot: candidate.assignmentRoot,
358
+ initiativeRoot: candidate.initiativeRoot,
359
+ sourceIdentityRoot: candidate.sourceIdentityRoot,
360
+ sourcePatchRoot: candidate.sourcePatchRoot,
361
+ sourceProofRoot: candidate.sourceProofRoot,
362
+ planRoot: candidate.planRoot,
363
+ closureRoot: candidate.closureRoot,
364
+ dependencyRoot: candidate.dependencyRoot,
365
+ toolchainRoot: candidate.toolchainRoot,
366
+ deliveryClass: candidate.deliveryClass,
367
+ generation: next.fencingCounter,
368
+ expectedOldStateRoot: before.stateRoot,
369
+ issuedAt,
370
+ expiresAt: new Date(Date.parse(issuedAt) + effectiveLeaseSeconds * 1000).toISOString(),
371
+ fencingToken: "",
372
+ nextAction: "Replay on the exact current dev base, prove required checks, then enqueue the unchanged PR head.",
373
+ };
374
+ warrant.fencingToken = warrantToken(warrant);
375
+ next.activeWarrant = warrant;
376
+ candidate.status = "selected";
377
+ candidate.updatedAt = currentTime;
378
+ return { candidate, warrant };
379
+ },
380
+ currentTime,
381
+ );
382
+ const receipt = {
383
+ schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
384
+ selected: true,
385
+ reason: selected.priority.agingBoost > 0 ? "fifo-aging-bounded-priority" : "fifo-bounded-priority",
386
+ repository: transaction.after.repository,
387
+ protectedBase: transaction.after.protectedBase,
388
+ candidateId: transaction.result.candidate.candidateId,
389
+ pullRequestNumber: transaction.result.candidate.pullRequestNumber,
390
+ sourceHead: transaction.result.candidate.sourceHead,
391
+ assignmentRoot: transaction.result.candidate.assignmentRoot,
392
+ initiativeRoot: transaction.result.candidate.initiativeRoot,
393
+ sourceIdentityRoot: transaction.result.candidate.sourceIdentityRoot,
394
+ sourcePatchRoot: transaction.result.candidate.sourcePatchRoot,
395
+ sourceProofRoot: transaction.result.candidate.sourceProofRoot,
396
+ planRoot: transaction.result.candidate.planRoot,
397
+ closureRoot: transaction.result.candidate.closureRoot,
398
+ dependencyRoot: transaction.result.candidate.dependencyRoot,
399
+ toolchainRoot: transaction.result.candidate.toolchainRoot,
400
+ deliveryClass: transaction.result.candidate.deliveryClass,
401
+ queueAgeSeconds: selected.priority.ageSeconds,
402
+ basePriority: selected.priority.basePriority,
403
+ agingBoost: selected.priority.agingBoost,
404
+ effectivePriority: selected.priority.score,
405
+ fencingToken: transaction.result.warrant.fencingToken,
406
+ leaseGeneration: transaction.result.warrant.generation,
407
+ expectedOldStateRoot: transaction.expectedOldStateRoot,
408
+ nextStateRoot: transaction.after.stateRoot,
409
+ nextAction: transaction.result.warrant.nextAction,
410
+ };
411
+ return {
412
+ queue: transaction.after,
413
+ warrant: transaction.result.warrant,
414
+ receipt,
415
+ receiptRoot: devDeliveryContentRoot(receipt),
416
+ recoveryReceipt,
417
+ };
418
+ }
419
+
420
+ function assertWarrantMutation(queue, warrant, now) {
421
+ if (!queue.activeWarrant) throw new Error("no active Delivery Warrant");
422
+ if (text(warrant?.fencingToken) !== queue.activeWarrant.fencingToken) throw new Error("stale fencing token");
423
+ if (Number(warrant?.generation) !== queue.activeWarrant.generation) throw new Error("stale lease generation");
424
+ if (text(warrant?.candidateId) !== queue.activeWarrant.candidateId) throw new Error("Warrant candidate mismatch");
425
+ if (Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(now)) throw new Error("Delivery Warrant lease expired");
426
+ }
427
+
428
+ export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Date().toISOString(), leaseSeconds } = {}) {
429
+ const currentTime = timestamp(now, "now");
430
+ const transaction = transition(
431
+ queueInput,
432
+ (queue, before) => {
433
+ assertWarrantMutation(before, warrant, currentTime);
434
+ const effectiveLeaseSeconds = positiveInteger(leaseSeconds, "leaseSeconds", queue.policy.leaseSeconds);
435
+ queue.activeWarrant.expiresAt = new Date(Date.parse(currentTime) + effectiveLeaseSeconds * 1000).toISOString();
436
+ const candidate = queue.candidates.find((entry) => entry.candidateId === queue.activeWarrant.candidateId);
437
+ candidate.status = "proving";
438
+ candidate.updatedAt = currentTime;
439
+ return { candidate, warrant: queue.activeWarrant };
440
+ },
441
+ currentTime,
442
+ );
443
+ const receipt = {
444
+ schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
445
+ action: "heartbeat",
446
+ candidateId: transaction.result.candidate.candidateId,
447
+ fencingToken: transaction.result.warrant.fencingToken,
448
+ leaseGeneration: transaction.result.warrant.generation,
449
+ expiresAt: transaction.result.warrant.expiresAt,
450
+ expectedOldStateRoot: transaction.expectedOldStateRoot,
451
+ nextStateRoot: transaction.after.stateRoot,
452
+ nextAction: "Continue the exact fenced delivery attempt.",
453
+ };
454
+ return {
455
+ queue: transaction.after,
456
+ receipt,
457
+ receiptRoot: devDeliveryContentRoot(receipt),
458
+ };
459
+ }
460
+
461
+ export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().toISOString() } = {}) {
462
+ const currentTime = timestamp(now, "now");
463
+ const before = normalizeDevDeliveryQueue(queueInput);
464
+ if (!before.activeWarrant || Date.parse(before.activeWarrant.expiresAt) > Date.parse(currentTime)) {
465
+ const receipt = {
466
+ schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
467
+ action: "recovery-noop",
468
+ reason: before.activeWarrant ? "lease-active" : "no-active-warrant",
469
+ expectedOldStateRoot: before.stateRoot,
470
+ nextStateRoot: before.stateRoot,
471
+ nextAction: before.activeWarrant ? "Continue the active delivery attempt." : "Select the next queued candidate.",
472
+ };
473
+ return {
474
+ queue: before,
475
+ receipt,
476
+ receiptRoot: devDeliveryContentRoot(receipt),
477
+ };
478
+ }
479
+ const transaction = transition(
480
+ before,
481
+ (queue) => {
482
+ const expired = clone(queue.activeWarrant);
483
+ const candidate = queue.candidates.find((entry) => entry.candidateId === expired.candidateId);
484
+ candidate.status = "queued";
485
+ candidate.recoveries += 1;
486
+ candidate.attempts += 1;
487
+ candidate.updatedAt = currentTime;
488
+ queue.activeWarrant = null;
489
+ return { candidate, expired };
490
+ },
491
+ currentTime,
492
+ );
493
+ const receipt = {
494
+ schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
495
+ action: "recovered-expired-lease",
496
+ candidateId: transaction.result.candidate.candidateId,
497
+ rejectedFencingToken: transaction.result.expired.fencingToken,
498
+ expiredLeaseGeneration: transaction.result.expired.generation,
499
+ retainedEnqueuedAt: transaction.result.candidate.enqueuedAt,
500
+ expectedOldStateRoot: transaction.expectedOldStateRoot,
501
+ nextStateRoot: transaction.after.stateRoot,
502
+ nextAction: "Re-select through the queue to mint a new lease generation and fencing token.",
503
+ };
504
+ return {
505
+ queue: transaction.after,
506
+ receipt,
507
+ receiptRoot: devDeliveryContentRoot(receipt),
508
+ };
509
+ }
510
+
511
+ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidenceRoot, reason = "", now = new Date().toISOString() } = {}) {
512
+ const currentTime = timestamp(now, "now");
513
+ const normalizedOutcome = text(outcome);
514
+ if (!TERMINAL_STATES.has(normalizedOutcome)) {
515
+ throw new Error(`outcome must be one of ${[...TERMINAL_STATES].join(", ")}`);
516
+ }
517
+ const transaction = transition(
518
+ queueInput,
519
+ (queue, before) => {
520
+ assertWarrantMutation(before, warrant, currentTime);
521
+ const active = clone(queue.activeWarrant);
522
+ const candidate = queue.candidates.find((entry) => entry.candidateId === active.candidateId);
523
+ candidate.status = normalizedOutcome;
524
+ candidate.updatedAt = currentTime;
525
+ candidate.terminal = {
526
+ outcome: normalizedOutcome,
527
+ reason: text(reason),
528
+ evidenceRoot: exactRoot(evidenceRoot, "evidenceRoot"),
529
+ closedAt: currentTime,
530
+ fencingToken: active.fencingToken,
531
+ leaseGeneration: active.generation,
532
+ };
533
+ queue.activeWarrant = null;
534
+ return { candidate, active };
535
+ },
536
+ currentTime,
537
+ );
538
+ const receipt = {
539
+ schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
540
+ action: "terminal-closeout",
541
+ outcome: normalizedOutcome,
542
+ reason: text(reason),
543
+ candidateId: transaction.result.candidate.candidateId,
544
+ fencingToken: transaction.result.active.fencingToken,
545
+ leaseGeneration: transaction.result.active.generation,
546
+ evidenceRoot: transaction.result.candidate.terminal.evidenceRoot,
547
+ expectedOldStateRoot: transaction.expectedOldStateRoot,
548
+ nextStateRoot: transaction.after.stateRoot,
549
+ nextAction: "Select the next queued candidate, if any.",
550
+ };
551
+ return {
552
+ queue: transaction.after,
553
+ receipt,
554
+ receiptRoot: devDeliveryContentRoot(receipt),
555
+ };
556
+ }
557
+
558
+ const cancelQueuedDevDeliveryCandidateTransition = createCancelQueuedDevDeliveryCandidate(normalizeDevDeliveryQueue);
559
+ export function cancelQueuedDevDeliveryCandidate(queueInput, input, options) { return cancelQueuedDevDeliveryCandidateTransition(queueInput, input, options); }
560
+ export const settleDevDeliveryTerminalEvent = createDevDeliveryTerminalSettler({ normalizeQueue: normalizeDevDeliveryQueue, closeWarrant: closeDevDeliveryWarrant, cancelQueued: cancelQueuedDevDeliveryCandidate, terminalStates: TERMINAL_STATES });
561
+
562
+ export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOString() } = {}) {
563
+ const queue = normalizeDevDeliveryQueue(queueInput);
564
+ const currentTime = timestamp(now, "now");
565
+ const states = {};
566
+ for (const candidate of queue.candidates) states[candidate.status] = (states[candidate.status] || 0) + 1;
567
+ const queued = rankDevDeliveryCandidates(queue, { now: currentTime });
568
+ return {
569
+ schema: "kungfu.buildchain.dev-delivery-queue-observation/v1",
570
+ repository: queue.repository,
571
+ protectedBase: queue.protectedBase,
572
+ stateRoot: queue.stateRoot,
573
+ generation: queue.generation,
574
+ activeWarrant: queue.activeWarrant,
575
+ activeCandidate: queue.activeWarrant ? queue.candidates.find((candidate) => candidate.candidateId === queue.activeWarrant.candidateId) || null : null,
576
+ states,
577
+ queued: queued.map((entry, index) => ({
578
+ position: index + 1,
579
+ candidateId: entry.candidate.candidateId,
580
+ pullRequestNumber: entry.candidate.pullRequestNumber,
581
+ sourceHead: entry.candidate.sourceHead,
582
+ priority: entry.candidate.priority,
583
+ effectivePriority: entry.priority.score,
584
+ queueAgeSeconds: entry.priority.ageSeconds,
585
+ attempts: entry.candidate.attempts,
586
+ recoveries: entry.candidate.recoveries,
587
+ nextAction: index === 0 && !queue.activeWarrant ? "eligible-for-selection" : "wait-for-active-warrant",
588
+ })),
589
+ observedAt: currentTime,
590
+ };
591
+ }