@kungfu-tech/buildchain 4.0.1-alpha.2 → 4.0.1-alpha.21

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 (203) hide show
  1. package/actions/release-tail/README.md +8 -0
  2. package/architecture/ci-lane-change-budget.json +187 -0
  3. package/architecture/decisions/0002-next-development-transition.md +101 -0
  4. package/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
  5. package/architecture/internal-capabilities.json +211 -8
  6. package/architecture/maintainability-policy.json +113 -28
  7. package/architecture/release-tail-contract-inventory.json +5 -2
  8. package/architecture/v3-core-mechanism-inventory.json +91 -0
  9. package/architecture/v4-adopter-delivery-parity.json +32 -5
  10. package/architecture/v4-capability-state-machine-manifest.json +73 -0
  11. package/architecture/v4-compatibility-facts-parity.json +77 -0
  12. package/architecture/v4-delivery-authority-parity.json +250 -0
  13. package/architecture/v4-delivery-warrant-shadow-fixtures.json +41 -9
  14. package/architecture/v4-floating-consumer-policy.json +6 -0
  15. package/architecture/v4-next-development-parity.json +192 -0
  16. package/architecture/v4-publication-rehearsal-parity.json +84 -0
  17. package/architecture/v4-release-train-parity.json +1 -0
  18. package/architecture/v4-tail-reseal-parity.json +150 -0
  19. package/bin/buildchain.mjs +32 -3
  20. package/bin/internal/adopter-delivery-cli.mjs +70 -0
  21. package/bin/internal/command-registry.mjs +3 -0
  22. package/bin/internal/compatibility-facts-cli.mjs +101 -0
  23. package/contracts/dev-delivery-authority-v2.schema.json +662 -0
  24. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
  25. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
  26. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
  27. package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
  28. package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
  29. package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
  30. package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
  31. package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
  32. package/contracts/fixtures/v4-compatibility-facts-v1/shared.json +1129 -0
  33. package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +7 -1
  34. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
  35. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
  36. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
  37. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
  38. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
  39. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
  40. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
  41. package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
  42. package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
  43. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +229 -0
  44. package/contracts/next-development-request-v1.schema.json +66 -0
  45. package/contracts/next-development-transition-v1.schema.json +292 -0
  46. package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
  47. package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
  48. package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
  49. package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  50. package/contracts/v4-tail-reseal-v1.schema.json +276 -0
  51. package/dist/site/agent-index.json +5 -0
  52. package/dist/site/artifact-schemas.json +10 -0
  53. package/dist/site/buildchain-contract.json +1475 -27
  54. package/dist/site/buildchain-site.json +627 -31
  55. package/dist/site/capability-registry.json +17 -14
  56. package/dist/site/cli-registry.json +205 -3
  57. package/dist/site/kfd-claims.json +490 -16
  58. package/dist/site/kfd-upstream-aggregate.json +1 -1
  59. package/dist/site/manual-registry.json +52 -6
  60. package/dist/site/node-api-registry.json +14736 -8851
  61. package/dist/site/page-registry.json +593 -21
  62. package/dist/site/public-surface-audit.json +837 -48
  63. package/dist/site/publication-authority-registry.json +44 -8
  64. package/dist/site/publication-registry.json +4 -4
  65. package/dist/site/release-provenance.json +24 -0
  66. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -0
  67. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
  68. package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
  69. package/dist/site/schemas/v4-compatibility-facts-v1.schema.json +230 -0
  70. package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  71. package/dist/site/site-manifest.json +34 -10
  72. package/dist/site/workflow-registry.json +160 -10
  73. package/docs/MAP.md +8 -2
  74. package/docs/cli-reference.md +404 -13
  75. package/docs/cli.md +8 -0
  76. package/docs/dev-alpha-candidate-patrol.md +8 -0
  77. package/docs/dev-delivery-qualification-landing-adr.md +251 -0
  78. package/docs/dev-delivery-warrant.md +351 -38
  79. package/docs/next-development-transition.md +118 -0
  80. package/docs/node-api-reference.md +646 -293
  81. package/docs/release-tail-provider-plane.md +30 -0
  82. package/docs/v4-adopter-delivery.md +133 -0
  83. package/docs/v4-compatibility-facts.md +91 -0
  84. package/docs/v4-publication-rehearsal.md +130 -0
  85. package/docs/v4-tail-reseal.md +73 -0
  86. package/docs/versioning.md +4 -3
  87. package/package.json +34 -6
  88. package/packages/core/adopter-delivery-passport.js +259 -0
  89. package/packages/core/adopter-delivery-vectors.js +158 -0
  90. package/packages/core/adopter-delivery-vectors.json +77 -0
  91. package/packages/core/buildchain-agent-manuals.js +3 -0
  92. package/packages/core/buildchain-compatibility-authority.js +336 -0
  93. package/packages/core/buildchain-compatibility-fact.js +394 -0
  94. package/packages/core/buildchain-compatibility-facts.json +217 -0
  95. package/packages/core/buildchain-compatibility-proof.js +566 -0
  96. package/packages/core/buildchain-config.js +84 -0
  97. package/packages/core/buildchain-contract.js +96 -143
  98. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  99. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  100. package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
  101. package/packages/core/buildchain-publication-authority.js +1 -0
  102. package/packages/core/channel-candidate.js +8 -0
  103. package/packages/core/channel-promotion-baseline.js +55 -0
  104. package/packages/core/ci-lane-change-budget.js +247 -0
  105. package/packages/core/dev-alpha-candidate-selection.js +10 -2
  106. package/packages/core/dev-delivery-authority-candidate.js +270 -0
  107. package/packages/core/dev-delivery-authority-evidence.js +146 -0
  108. package/packages/core/dev-delivery-authority-landing.js +461 -0
  109. package/packages/core/dev-delivery-authority-observation.js +48 -0
  110. package/packages/core/dev-delivery-authority-qualification.js +591 -0
  111. package/packages/core/dev-delivery-authority-settlement.js +213 -0
  112. package/packages/core/dev-delivery-authority-state.js +583 -0
  113. package/packages/core/dev-delivery-candidate-identity.js +13 -0
  114. package/packages/core/dev-delivery-contract-surface.js +76 -0
  115. package/packages/core/dev-delivery-execution-failure.js +133 -0
  116. package/packages/core/dev-delivery-execution-transfer.js +592 -0
  117. package/packages/core/dev-delivery-landing-admission-core.js +119 -0
  118. package/packages/core/dev-delivery-landing-readback.js +598 -0
  119. package/packages/core/dev-delivery-landing-terminal-evidence.js +271 -0
  120. package/packages/core/dev-delivery-landing-testing-port.js +6 -0
  121. package/packages/core/dev-delivery-native-execution.js +110 -0
  122. package/packages/core/dev-delivery-native-proof.js +562 -0
  123. package/packages/core/dev-delivery-process-boundary.js +563 -0
  124. package/packages/core/dev-delivery-provider-attempt.js +127 -0
  125. package/packages/core/dev-delivery-provider-heartbeat.js +382 -0
  126. package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
  127. package/packages/core/dev-delivery-warrant-qualification.js +145 -0
  128. package/packages/core/dev-delivery-warrant-settlement.js +237 -36
  129. package/packages/core/dev-delivery-warrant-state.js +587 -0
  130. package/packages/core/dev-delivery-warrant.js +328 -370
  131. package/packages/core/github-governance-authority.js +1 -0
  132. package/packages/core/index.js +5 -0
  133. package/packages/core/kungfu-temporal-fact.js +557 -0
  134. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  135. package/packages/core/next-development-candidate-reservation.js +186 -0
  136. package/packages/core/next-development-controller.js +728 -0
  137. package/packages/core/next-development-projection.js +287 -0
  138. package/packages/core/next-development-transition.js +738 -0
  139. package/packages/core/paper-agent-entry.js +11 -5
  140. package/packages/core/paper.js +16 -5
  141. package/packages/core/published-delivery-authority.js +266 -0
  142. package/packages/core/release-candidate-recovery.js +70 -15
  143. package/packages/core/release-candidate.js +2 -2
  144. package/packages/core/release-passport.js +36 -9
  145. package/packages/core/v4-adopter-delivery-parity.js +3 -3
  146. package/packages/core/v4-adopter-delivery.js +359 -0
  147. package/packages/core/v4-canonical-contracts.js +5 -0
  148. package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
  149. package/packages/core/v4-floating-consumer-evidence.js +18 -4
  150. package/packages/core/v4-floating-consumer-policy.js +143 -20
  151. package/packages/core/v4-floating-consumer-release-passport.js +32 -5
  152. package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
  153. package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
  154. package/packages/core/v4-publication-rehearsal.js +481 -0
  155. package/packages/core/v4-tail-reseal-contract.js +26 -0
  156. package/packages/core/v4-tail-reseal-github.js +178 -0
  157. package/packages/core/v4-tail-reseal-receipt.js +225 -0
  158. package/packages/core/v4-tail-reseal.js +594 -0
  159. package/packages/core/workflow-yaml-contract.js +24 -2
  160. package/scripts/buildchain-cli-help.mjs +41 -2
  161. package/scripts/check-ci-lane-change-budget.mjs +100 -0
  162. package/scripts/check-inventory.mjs +14 -3
  163. package/scripts/check-v4-floating-consumer-policy-contract.mjs +23 -4
  164. package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
  165. package/scripts/dev-delivery-authority-command-adapters.mjs +206 -0
  166. package/scripts/dev-delivery-authority-provider.mjs +28 -0
  167. package/scripts/dev-delivery-authority.mjs +490 -0
  168. package/scripts/dev-delivery-native-run.mjs +177 -0
  169. package/scripts/dev-delivery-process-boundary.mjs +260 -0
  170. package/scripts/dev-delivery-proof.mjs +67 -2
  171. package/scripts/dev-delivery-provider-heartbeat.mjs +215 -0
  172. package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
  173. package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
  174. package/scripts/dev-delivery-two-phase-resume.mjs +345 -0
  175. package/scripts/dev-delivery-two-phase.mjs +573 -0
  176. package/scripts/dev-delivery-warrant-options.mjs +266 -0
  177. package/scripts/dev-delivery-warrant-store.mjs +231 -0
  178. package/scripts/dev-delivery-warrant.mjs +232 -194
  179. package/scripts/dev-pr-auto-merge.mjs +1 -1
  180. package/scripts/dev-pr-delivery-warrant.mjs +11 -0
  181. package/scripts/dev-qualification-patrol.mjs +9 -9
  182. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  183. package/scripts/generate-channel-promotion-workflow.mjs +2 -2
  184. package/scripts/generate-next-development-guidance.mjs +49 -0
  185. package/scripts/generate-release-candidate-passport.mjs +152 -0
  186. package/scripts/generate-site-bundle.mjs +69 -4
  187. package/scripts/init-repo.mjs +22 -4
  188. package/scripts/next-development-self-dogfood-harness.mjs +460 -0
  189. package/scripts/next-development-self-dogfood.mjs +597 -0
  190. package/scripts/next-development-transition.mjs +47 -0
  191. package/scripts/npm-publish-transaction.mjs +2 -2
  192. package/scripts/promotion-channel-router.mjs +4 -4
  193. package/scripts/release-candidate-anchor-provenance.mjs +99 -0
  194. package/scripts/release-tail.mjs +51 -3
  195. package/scripts/release-train-self-dogfood.mjs +94 -92
  196. package/scripts/resume-from-candidate-run.mjs +557 -30
  197. package/scripts/site-capability-metadata.mjs +41 -0
  198. package/scripts/stable-candidate-qualification.mjs +9 -10
  199. package/scripts/v4-consumer-policy.mjs +5 -5
  200. package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
  201. package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
  202. package/scripts/v4-tail-reseal.mjs +416 -0
  203. package/templates/native-dev-delivery.yml +143 -0
@@ -1,310 +1,108 @@
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
- import { compareReleaseBlockerPriority, normalizeReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
1
+ import {
2
+ devDeliveryClone as clone,
3
+ devDeliveryContentRoot,
4
+ devDeliveryExactRoot as exactRoot,
5
+ devDeliveryPositiveInteger as positiveInteger,
6
+ devDeliveryText as text,
7
+ devDeliveryTimestamp as timestamp,
8
+ } from "./dev-delivery-common.js";
9
+ import {
10
+ DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA,
11
+ createCancelQueuedDevDeliveryCandidate,
12
+ } from "./dev-delivery-warrant-cancellation.js";
13
+ import {
14
+ DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA,
15
+ createDevDeliveryTerminalSettler,
16
+ normalizeProviderFailureAuthorityBinding,
17
+ } from "./dev-delivery-warrant-settlement.js";
18
+ import {
19
+ normalizeNativeCommandContract,
20
+ validateActiveDevDeliveryWarrant,
21
+ } from "./dev-delivery-native-proof.js";
22
+ import { createDevDeliveryWarrantQualifier } from "./dev-delivery-warrant-qualification.js";
23
+ import {
24
+ DEV_DELIVERY_CLASSES,
25
+ DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
26
+ DEV_DELIVERY_PRIORITIES,
27
+ DEV_DELIVERY_QUEUE_CONTRACT,
28
+ DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
29
+ DEV_DELIVERY_SUBMISSION_RECEIPT_SCHEMA,
30
+ DEV_DELIVERY_WARRANT_SCHEMA,
31
+ TERMINAL_STATES,
32
+ createDevDeliveryQueue,
33
+ issueDevDeliveryWarrant,
34
+ normalizeDevDeliveryQueue,
35
+ rankDevDeliveryCandidates,
36
+ submitDevDeliveryCandidate,
37
+ transitionDevDeliveryQueue as transition,
38
+ } from "./dev-delivery-warrant-state.js";
39
+
6
40
  export { devDeliveryContentRoot } from "./dev-delivery-common.js";
7
- export { RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA, createReleaseBlockerPriorityClaim } from "./release-blocker-priority.js";
8
- export { SOURCE_QUALIFICATION_PROOF_SCHEMA, SOURCE_QUALIFICATION_PROOF_V2_SCHEMA, PROJECT_CUT_REPLAY_PROOF_SCHEMA, INTEGRATION_DELIVERY_PROOF_SCHEMA, classifyDevDeliveryDelta, createIntegrationDeliveryProof, createProjectCutReplayPlan, createProjectCutReplayProof, createSourceQualificationProof, createSourceQualificationProofV2, verifyIntegrationDeliveryProof, verifyProjectCutReplayProof, verifySourceQualificationProof } from "./dev-delivery-proof.js";
41
+ export {
42
+ RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA,
43
+ createReleaseBlockerPriorityClaim,
44
+ } from "./release-blocker-priority.js";
45
+ export {
46
+ SOURCE_QUALIFICATION_PROOF_SCHEMA,
47
+ SOURCE_QUALIFICATION_PROOF_V2_SCHEMA,
48
+ PROJECT_CUT_REPLAY_PROOF_SCHEMA,
49
+ INTEGRATION_DELIVERY_PROOF_SCHEMA,
50
+ classifyDevDeliveryDelta,
51
+ createIntegrationDeliveryProof,
52
+ createProjectCutReplayPlan,
53
+ createProjectCutReplayProof,
54
+ createSourceQualificationProof,
55
+ createSourceQualificationProofV2,
56
+ verifyIntegrationDeliveryProof,
57
+ verifyProjectCutReplayProof,
58
+ verifySourceQualificationProof,
59
+ } from "./dev-delivery-proof.js";
60
+ export {
61
+ DEV_DELIVERY_QUALIFICATION_RECEIPT_SCHEMA,
62
+ NATIVE_COMMAND_CONTRACT_SCHEMA,
63
+ NATIVE_EXECUTION_BINDING_SCHEMA,
64
+ NATIVE_EXECUTION_RECEIPT_SCHEMA,
65
+ NATIVE_PROOF_BASE_DELTA_SCHEMA,
66
+ NATIVE_PROOF_REUSE_DECISION_SCHEMA,
67
+ NATIVE_QUALIFICATION_PROOF_SCHEMA,
68
+ createNativeCommandContract,
69
+ createNativeExecutionBinding,
70
+ createNativeExecutionReceipt,
71
+ createNativeProofBaseDelta,
72
+ createNativeProofReuseDecision,
73
+ createNativeQualificationProof,
74
+ normalizeNativeCommandContract,
75
+ verifyNativeExecutionReceipt,
76
+ verifyNativeProofReuseDecision,
77
+ verifyNativeQualificationProof,
78
+ } from "./dev-delivery-native-proof.js";
79
+ export {
80
+ DEV_DELIVERY_CLASSES,
81
+ DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
82
+ DEV_DELIVERY_PRIORITIES,
83
+ DEV_DELIVERY_QUEUE_CONTRACT,
84
+ DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
85
+ DEV_DELIVERY_SUBMISSION_RECEIPT_SCHEMA,
86
+ DEV_DELIVERY_WARRANT_SCHEMA,
87
+ createDevDeliveryQueue,
88
+ normalizeDevDeliveryQueue,
89
+ rankDevDeliveryCandidates,
90
+ submitDevDeliveryCandidate,
91
+ };
9
92
  export { DEV_DELIVERY_CANCELLATION_RECEIPT_SCHEMA };
10
- export const DEV_DELIVERY_QUEUE_CONTRACT = "kungfu-buildchain-dev-delivery-warrant-queue";
11
- export const DEV_DELIVERY_WARRANT_SCHEMA = "kungfu.buildchain.dev-delivery-warrant/v1";
12
- export const DEV_DELIVERY_SUBMISSION_RECEIPT_SCHEMA = "kungfu.buildchain.dev-delivery-submission-receipt/v1";
13
- export const DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA = "kungfu.buildchain.dev-delivery-selection-receipt/v1";
14
- export const DEV_DELIVERY_LEASE_RECEIPT_SCHEMA = "kungfu.buildchain.dev-delivery-lease-receipt/v1";
15
93
  export { DEV_DELIVERY_SETTLEMENT_RECEIPT_SCHEMA };
16
- export const DEV_DELIVERY_PRIORITIES = Object.freeze({
17
- ordinary: 0,
18
- expedited: 1,
19
- emergency: 2,
20
- });
21
-
22
- export const DEV_DELIVERY_CLASSES = Object.freeze(["non-native-fast", "native-proof-required", "cross-platform", "release"]);
23
-
24
- const TERMINAL_STATES = new Set(["merged", "terminal-failure", "dequeued", "cancelled"]);
25
- function nonNegativeInteger(value, label, fallback = 0) {
26
- const parsed = Number(value ?? fallback);
27
- if (!Number.isInteger(parsed) || parsed < 0) throw new Error(`${label} must be a non-negative integer`);
28
- return parsed;
29
- }
30
-
31
- function priority(value = "ordinary") {
32
- const normalized = text(value || "ordinary").toLowerCase();
33
- if (!(normalized in DEV_DELIVERY_PRIORITIES)) {
34
- throw new Error(`priority must be one of ${Object.keys(DEV_DELIVERY_PRIORITIES).join(", ")}`);
35
- }
36
- return normalized;
37
- }
38
-
39
- function deliveryClass(value) {
40
- const normalized = text(value);
41
- if (!DEV_DELIVERY_CLASSES.includes(normalized)) {
42
- throw new Error(`deliveryClass must be one of ${DEV_DELIVERY_CLASSES.join(", ")}`);
43
- }
44
- return normalized;
45
- }
46
-
47
- function normalizePolicy(policy = {}) {
48
- return {
49
- agingSeconds: positiveInteger(policy.agingSeconds, "policy agingSeconds", 900),
50
- maxPriority: positiveInteger(policy.maxPriority, "policy maxPriority", 2),
51
- leaseSeconds: positiveInteger(policy.leaseSeconds, "policy leaseSeconds", 3600),
52
- emergencyPolicy: text(policy.emergencyPolicy || "reviewed-explicit-only"),
53
- };
54
- }
55
-
56
- function normalizeCandidate(input, expected) {
57
- const identity = createDevDeliveryCandidateIdentity(input, expected, deliveryClass);
58
- if (input.candidateId && input.candidateId !== identity.candidateId) {
59
- throw new Error(`candidateId mismatch for PR #${identity.pullRequestNumber}`);
60
- }
61
- const status = text(input.status || "queued");
62
- if (!["queued", "selected", "proving", "waiting", "blocked", ...TERMINAL_STATES].includes(status)) {
63
- throw new Error(`unsupported candidate status ${status || "<empty>"}`);
64
- }
65
- const candidate = {
66
- ...identity,
67
- sourceHead: exactSha(input.sourceHead, "sourceHead"),
68
- sourcePatchRoot: exactRoot(input.sourcePatchRoot, "sourcePatchRoot"),
69
- sourceProofRoot: exactRoot(input.sourceProofRoot, "sourceProofRoot"),
70
- planRoot: exactRoot(input.planRoot, "planRoot"),
71
- closureRoot: exactRoot(input.closureRoot, "closureRoot"),
72
- dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
73
- toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"), ...(input.environmentRoot ? { environmentRoot: exactRoot(input.environmentRoot, "environmentRoot") } : {}), ...(Object.hasOwn(input, "sourceWorkflowRunId") ? { sourceWorkflowRunId: nonNegativeInteger(input.sourceWorkflowRunId, "candidate sourceWorkflowRunId", 0) } : {}),
74
- priority: priority(input.priority),
75
- enqueuedAt: timestamp(input.enqueuedAt, "candidate enqueuedAt"),
76
- updatedAt: timestamp(input.updatedAt || input.enqueuedAt, "candidate updatedAt"),
77
- attempts: positiveInteger(input.attempts, "candidate attempts", 1),
78
- recoveries: nonNegativeInteger(input.recoveries, "candidate recoveries", 0),
79
- status,
80
- terminal: input.terminal || null,
81
- };
82
- if (Object.hasOwn(input, "affectedPaths")) { if (!Array.isArray(input.affectedPaths)) throw new Error("candidate affectedPaths must be an array"); candidate.affectedPaths = [...new Set(input.affectedPaths.map(text).filter(Boolean))].sort(); } if (Object.hasOwn(input, "releaseBlockerPriority")) candidate.releaseBlockerPriority = normalizeReleaseBlockerPriorityClaim(input.releaseBlockerPriority, candidate, expected);
83
- return candidate;
84
- }
85
-
86
- function queueBody(queue) {
87
- const body = clone(queue);
88
- delete body.stateRoot;
89
- return body;
90
- }
91
-
92
- function withQueueRoot(queue) {
93
- return {
94
- ...queueBody(queue),
95
- stateRoot: devDeliveryContentRoot(queueBody(queue)),
96
- };
97
- }
98
-
99
- export function createDevDeliveryQueue({ repository: repositoryInput, protectedBase: protectedBaseInput, policy = {}, now = new Date().toISOString() } = {}) {
100
- return withQueueRoot({
101
- schemaVersion: 1,
102
- contract: DEV_DELIVERY_QUEUE_CONTRACT,
103
- repository: repository(repositoryInput),
104
- protectedBase: protectedBase(protectedBaseInput),
105
- generation: 0,
106
- fencingCounter: 0,
107
- policy: normalizePolicy(policy),
108
- activeWarrant: null,
109
- candidates: [],
110
- updatedAt: timestamp(now, "now"),
111
- });
112
- }
113
-
114
- export function normalizeDevDeliveryQueue(input, expected = {}) {
115
- const queue = clone(input || {});
116
- if (queue.contract !== DEV_DELIVERY_QUEUE_CONTRACT || Number(queue.schemaVersion) !== 1) {
117
- throw new Error(`dev delivery queue must use ${DEV_DELIVERY_QUEUE_CONTRACT} schemaVersion 1`);
118
- }
119
- queue.repository = repository(queue.repository);
120
- queue.protectedBase = protectedBase(queue.protectedBase);
121
- if (expected.repository && queue.repository !== expected.repository) {
122
- throw new Error(`dev delivery queue repository mismatch: ${queue.repository} != ${expected.repository}`);
123
- }
124
- if (expected.protectedBase && queue.protectedBase !== expected.protectedBase) {
125
- throw new Error(`dev delivery queue protectedBase mismatch: ${queue.protectedBase} != ${expected.protectedBase}`);
126
- }
127
- queue.generation = nonNegativeInteger(queue.generation, "queue generation");
128
- queue.fencingCounter = nonNegativeInteger(queue.fencingCounter, "queue fencingCounter");
129
- queue.policy = normalizePolicy(queue.policy);
130
- queue.candidates = (queue.candidates || []).map((candidate) => normalizeCandidate(candidate, queue));
131
- validateDevDeliveryCandidateChain(queue.candidates, TERMINAL_STATES);
132
- queue.updatedAt = timestamp(queue.updatedAt, "queue updatedAt");
133
- if (queue.activeWarrant) {
134
- const warrant = queue.activeWarrant;
135
- if (warrant.schema !== DEV_DELIVERY_WARRANT_SCHEMA) throw new Error("active Warrant schema is unsupported");
136
- warrant.candidateId = exactRoot(warrant.candidateId, "Warrant candidateId");
137
- warrant.fencingToken = exactRoot(warrant.fencingToken, "Warrant fencingToken");
138
- warrant.expectedOldStateRoot = exactRoot(warrant.expectedOldStateRoot, "Warrant expectedOldStateRoot");
139
- warrant.generation = positiveInteger(warrant.generation, "Warrant generation");
140
- warrant.issuedAt = timestamp(warrant.issuedAt, "Warrant issuedAt");
141
- warrant.expiresAt = timestamp(warrant.expiresAt, "Warrant expiresAt");
142
- const activeCandidates = queue.candidates.filter((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status));
143
- if (activeCandidates.length !== 1 || activeCandidates[0].candidateId !== warrant.candidateId) {
144
- throw new Error("exactly one active candidate must match the active Warrant");
145
- }
146
- if (warrant.releaseBlockerPriority?.claimRoot !== activeCandidates[0].releaseBlockerPriority?.claimRoot) throw new Error("active Warrant release-blocker priority drift");
147
- } else if (queue.candidates.some((candidate) => ["selected", "proving", "waiting", "blocked"].includes(candidate.status))) {
148
- throw new Error("active candidate exists without an active Warrant");
149
- }
150
- const rooted = withQueueRoot(queue);
151
- if (input.stateRoot && input.stateRoot !== rooted.stateRoot) throw new Error("dev delivery queue stateRoot drift");
152
- return rooted;
153
- }
154
-
155
- function transition(queueInput, mutate, nowInput) {
156
- const before = normalizeDevDeliveryQueue(queueInput);
157
- const expectedOldStateRoot = before.stateRoot;
158
- const queue = clone(before);
159
- delete queue.stateRoot;
160
- const now = timestamp(nowInput, "now");
161
- const result = mutate(queue, before, now);
162
- queue.generation += 1;
163
- queue.updatedAt = now;
164
- const after = withQueueRoot(queue);
165
- return { before, after, expectedOldStateRoot, result };
166
- }
167
-
168
- function submissionReceipt({ before, after, candidate, action, now }) {
169
- const queueAgeSeconds = Math.max(0, Math.floor((Date.parse(now) - Date.parse(candidate.enqueuedAt)) / 1000));
170
- return {
171
- schema: DEV_DELIVERY_SUBMISSION_RECEIPT_SCHEMA,
172
- repository: after.repository,
173
- protectedBase: after.protectedBase,
174
- candidateId: candidate.candidateId,
175
- pullRequestNumber: candidate.pullRequestNumber,
176
- sourceHead: candidate.sourceHead,
177
- assignmentRoot: candidate.assignmentRoot,
178
- initiativeRoot: candidate.initiativeRoot,
179
- sourceIdentityRoot: candidate.sourceIdentityRoot,
180
- sourcePatchRoot: candidate.sourcePatchRoot,
181
- sourceProofRoot: candidate.sourceProofRoot,
182
- planRoot: candidate.planRoot,
183
- closureRoot: candidate.closureRoot,
184
- dependencyRoot: candidate.dependencyRoot,
185
- toolchainRoot: candidate.toolchainRoot,
186
- sourceWorkflowRunId: candidate.sourceWorkflowRunId,
187
- ...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
188
- deliveryClass: candidate.deliveryClass,
189
- priority: candidate.priority,
190
- retainedEnqueuedAt: candidate.enqueuedAt,
191
- queueAgeSeconds,
192
- attempts: candidate.attempts,
193
- action,
194
- expectedOldStateRoot: before.stateRoot,
195
- nextStateRoot: after.stateRoot,
196
- nextAction: "Wait for deterministic Warrant selection or observe the active predecessor.",
197
- };
198
- }
199
-
200
- export function submitDevDeliveryCandidate(queueInput, input, { now = new Date().toISOString() } = {}) {
201
- const transaction = transition(
202
- queueInput,
203
- (queue, before, currentTime) => {
204
- const candidate = normalizeCandidate(
205
- {
206
- ...input,
207
- enqueuedAt: input.enqueuedAt || currentTime,
208
- updatedAt: currentTime,
209
- attempts: input.attempts || 1,
210
- recoveries: input.recoveries || 0,
211
- status: "queued",
212
- },
213
- queue,
214
- );
215
- const chainedInput = chainedDevDeliveryAttemptInput({ queue, candidate, input, currentTime, terminalStates: TERMINAL_STATES });
216
- const attemptedCandidate = chainedInput ? normalizeCandidate(chainedInput, queue) : candidate;
217
- const conflicting = queue.candidates.find((entry) => entry.pullRequestNumber === attemptedCandidate.pullRequestNumber && entry.candidateId !== attemptedCandidate.candidateId && !TERMINAL_STATES.has(entry.status));
218
- if (conflicting) throw new Error(`PR #${candidate.pullRequestNumber} already has a different active semantic source`);
219
- const existing = queue.candidates.find((entry) => entry.candidateId === attemptedCandidate.candidateId);
220
- let action = "submitted";
221
- let selected = attemptedCandidate;
222
- if (existing) {
223
- if (TERMINAL_STATES.has(existing.status)) {
224
- throw new Error(`candidate ${attemptedCandidate.candidateId} is terminal and cannot be resubmitted`);
225
- }
226
- const exactProofFields = ["sourcePatchRoot", "sourceProofRoot", "planRoot", "closureRoot", "dependencyRoot", "toolchainRoot"];
227
- const exactProofMatches = exactProofFields.every((field) => existing[field] === attemptedCandidate[field]) && existing.releaseBlockerPriority?.claimRoot === attemptedCandidate.releaseBlockerPriority?.claimRoot;
228
- if (existing.sourceHead === attemptedCandidate.sourceHead && exactProofMatches) {
229
- action = "duplicate-noop";
230
- selected = existing;
231
- } else {
232
- if (before.activeWarrant?.candidateId === existing.candidateId) {
233
- throw new Error("selected candidate sourceHead cannot change before terminal Warrant closeout");
234
- }
235
- const headChanged = existing.sourceHead !== attemptedCandidate.sourceHead;
236
- existing.sourceHead = attemptedCandidate.sourceHead;
237
- for (const field of exactProofFields) existing[field] = attemptedCandidate[field];
238
- if (Object.hasOwn(attemptedCandidate, "releaseBlockerPriority")) existing.releaseBlockerPriority = attemptedCandidate.releaseBlockerPriority;
239
- else delete existing.releaseBlockerPriority;
240
- existing.updatedAt = currentTime;
241
- existing.attempts += 1;
242
- existing.status = "queued";
243
- action = headChanged ? "safe-head-repair-retained-age" : "safe-proof-refresh-retained-age";
244
- selected = existing;
245
- }
246
- } else {
247
- queue.candidates.push(attemptedCandidate);
248
- }
249
- return { candidate: selected, action };
250
- },
251
- now,
252
- );
253
- const receipt = submissionReceipt({
254
- before: transaction.before,
255
- after: transaction.after,
256
- candidate: transaction.result.candidate,
257
- action: transaction.result.action,
258
- now: timestamp(now, "now"),
259
- });
260
- return {
261
- queue: transaction.after,
262
- receipt,
263
- receiptRoot: devDeliveryContentRoot(receipt),
264
- };
265
- }
266
-
267
- function effectivePriority(candidate, policy, now) {
268
- const ageSeconds = Math.max(0, Math.floor((Date.parse(now) - Date.parse(candidate.enqueuedAt)) / 1000));
269
- const agingBoost = Math.floor(ageSeconds / policy.agingSeconds);
270
- return {
271
- ageSeconds,
272
- basePriority: DEV_DELIVERY_PRIORITIES[candidate.priority],
273
- agingBoost,
274
- score: Math.min(policy.maxPriority, DEV_DELIVERY_PRIORITIES[candidate.priority] + agingBoost),
275
- releaseBlocker: Boolean(candidate.releaseBlockerPriority),
276
- };
277
- }
278
94
 
279
- export function rankDevDeliveryCandidates(queueInput, { now = new Date().toISOString() } = {}) {
280
- const queue = normalizeDevDeliveryQueue(queueInput);
281
- const currentTime = timestamp(now, "now");
282
- return queue.candidates
283
- .filter((candidate) => candidate.status === "queued")
284
- .map((candidate) => ({
285
- candidate,
286
- priority: effectivePriority(candidate, queue.policy, currentTime),
287
- }))
288
- .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));
289
- }
290
-
291
- function warrantToken(input) {
292
- return devDeliveryContentRoot({
293
- schema: DEV_DELIVERY_WARRANT_SCHEMA,
294
- repository: input.repository,
295
- protectedBase: input.protectedBase,
296
- candidateId: input.candidateId,
297
- generation: input.generation,
298
- expectedOldStateRoot: input.expectedOldStateRoot,
299
- issuedAt: input.issuedAt,
300
- });
301
- }
302
-
303
- export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOString(), leaseSeconds } = {}) {
95
+ export function selectDevDeliveryWarrant(
96
+ queueInput,
97
+ { now = new Date().toISOString(), leaseSeconds } = {},
98
+ ) {
304
99
  const currentTime = timestamp(now, "now");
305
100
  let queue = normalizeDevDeliveryQueue(queueInput);
306
101
  let recoveryReceipt = null;
307
- if (queue.activeWarrant && Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(currentTime)) {
102
+ if (
103
+ queue.activeWarrant &&
104
+ Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(currentTime)
105
+ ) {
308
106
  const recovered = recoverExpiredDevDeliveryWarrant(queue, {
309
107
  now: currentTime,
310
108
  });
@@ -312,16 +110,22 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
312
110
  recoveryReceipt = recovered.receipt;
313
111
  }
314
112
  if (queue.activeWarrant) {
113
+ const stopRequired =
114
+ recoveryReceipt?.action === "expired-lease-fenced-stop-required";
315
115
  const receipt = {
316
116
  schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
317
- selected: true,
318
- reason: "non-preemptive-active-warrant",
117
+ selected: !stopRequired,
118
+ reason: stopRequired
119
+ ? "expired-provisional-warrant-stop-required"
120
+ : "non-preemptive-active-warrant",
319
121
  candidateId: queue.activeWarrant.candidateId,
320
122
  fencingToken: queue.activeWarrant.fencingToken,
321
123
  leaseGeneration: queue.activeWarrant.generation,
322
124
  expectedOldStateRoot: queue.stateRoot,
323
125
  nextStateRoot: queue.stateRoot,
324
- nextAction: "Continue the active delivery attempt; later candidates remain visibly queued.",
126
+ nextAction: stopRequired
127
+ ? recoveryReceipt.nextAction
128
+ : "Continue the active delivery attempt; later candidates remain visibly queued.",
325
129
  };
326
130
  return {
327
131
  queue,
@@ -353,37 +157,16 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
353
157
  const transaction = transition(
354
158
  queue,
355
159
  (next, before) => {
356
- const candidate = next.candidates.find((entry) => entry.candidateId === selected.candidate.candidateId);
357
- next.fencingCounter += 1;
358
- const issuedAt = currentTime;
359
- const effectiveLeaseSeconds = positiveInteger(leaseSeconds, "leaseSeconds", next.policy.leaseSeconds);
360
- const warrant = {
361
- schema: DEV_DELIVERY_WARRANT_SCHEMA,
362
- repository: next.repository,
363
- protectedBase: next.protectedBase,
364
- candidateId: candidate.candidateId,
365
- pullRequestNumber: candidate.pullRequestNumber,
366
- sourceHead: candidate.sourceHead,
367
- assignmentRoot: candidate.assignmentRoot,
368
- initiativeRoot: candidate.initiativeRoot,
369
- sourceIdentityRoot: candidate.sourceIdentityRoot,
370
- sourcePatchRoot: candidate.sourcePatchRoot,
371
- sourceProofRoot: candidate.sourceProofRoot,
372
- planRoot: candidate.planRoot,
373
- closureRoot: candidate.closureRoot,
374
- dependencyRoot: candidate.dependencyRoot,
375
- toolchainRoot: candidate.toolchainRoot,
376
- sourceWorkflowRunId: candidate.sourceWorkflowRunId,
377
- ...(Object.hasOwn(candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: candidate.releaseBlockerPriority } : {}),
378
- deliveryClass: candidate.deliveryClass,
379
- generation: next.fencingCounter,
380
- expectedOldStateRoot: before.stateRoot,
381
- issuedAt,
382
- expiresAt: new Date(Date.parse(issuedAt) + effectiveLeaseSeconds * 1000).toISOString(),
383
- fencingToken: "",
384
- nextAction: "Replay on the exact current dev base, prove required checks, then enqueue the unchanged PR head.",
385
- };
386
- warrant.fencingToken = warrantToken(warrant);
160
+ const candidate = next.candidates.find(
161
+ (entry) => entry.candidateId === selected.candidate.candidateId,
162
+ );
163
+ const warrant = issueDevDeliveryWarrant(
164
+ next,
165
+ before,
166
+ candidate,
167
+ currentTime,
168
+ leaseSeconds,
169
+ );
387
170
  next.activeWarrant = warrant;
388
171
  candidate.status = "selected";
389
172
  candidate.updatedAt = currentTime;
@@ -394,7 +177,11 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
394
177
  const receipt = {
395
178
  schema: DEV_DELIVERY_SELECTION_RECEIPT_SCHEMA,
396
179
  selected: true,
397
- reason: selected.priority.releaseBlocker ? "release-blocker-bounded-priority" : selected.priority.agingBoost > 0 ? "fifo-aging-bounded-priority" : "fifo-bounded-priority",
180
+ reason: selected.priority.releaseBlocker
181
+ ? "release-blocker-bounded-priority"
182
+ : selected.priority.agingBoost > 0
183
+ ? "fifo-aging-bounded-priority"
184
+ : "fifo-bounded-priority",
398
185
  repository: transaction.after.repository,
399
186
  protectedBase: transaction.after.protectedBase,
400
187
  candidateId: transaction.result.candidate.candidateId,
@@ -410,7 +197,23 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
410
197
  dependencyRoot: transaction.result.candidate.dependencyRoot,
411
198
  toolchainRoot: transaction.result.candidate.toolchainRoot,
412
199
  sourceWorkflowRunId: transaction.result.candidate.sourceWorkflowRunId,
413
- ...(Object.hasOwn(transaction.result.candidate, "releaseBlockerPriority") ? { releaseBlockerPriority: transaction.result.candidate.releaseBlockerPriority } : {}),
200
+ ...(transaction.result.candidate.nativeCommandContract
201
+ ? {
202
+ nativeCommandContract:
203
+ transaction.result.candidate.nativeCommandContract,
204
+ }
205
+ : {}),
206
+ ...(transaction.result.candidate.shardEvidenceRoots
207
+ ? {
208
+ shardEvidenceRoots: transaction.result.candidate.shardEvidenceRoots,
209
+ }
210
+ : {}),
211
+ ...(Object.hasOwn(transaction.result.candidate, "releaseBlockerPriority")
212
+ ? {
213
+ releaseBlockerPriority:
214
+ transaction.result.candidate.releaseBlockerPriority,
215
+ }
216
+ : {}),
414
217
  deliveryClass: transaction.result.candidate.deliveryClass,
415
218
  queueAgeSeconds: selected.priority.ageSeconds,
416
219
  basePriority: selected.priority.basePriority,
@@ -430,25 +233,49 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
430
233
  recoveryReceipt,
431
234
  };
432
235
  }
433
-
434
- function assertWarrantMutation(queue, warrant, now) {
236
+ function assertWarrantMutation(
237
+ queue,
238
+ warrant,
239
+ now,
240
+ { allowExpired = false } = {},
241
+ ) {
435
242
  if (!queue.activeWarrant) throw new Error("no active Delivery Warrant");
436
- if (text(warrant?.fencingToken) !== queue.activeWarrant.fencingToken) throw new Error("stale fencing token");
437
- if (Number(warrant?.generation) !== queue.activeWarrant.generation) throw new Error("stale lease generation");
438
- if (text(warrant?.candidateId) !== queue.activeWarrant.candidateId) throw new Error("Warrant candidate mismatch");
439
- if (Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(now)) throw new Error("Delivery Warrant lease expired");
243
+ if (text(warrant?.fencingToken) !== queue.activeWarrant.fencingToken)
244
+ throw new Error("stale fencing token");
245
+ if (Number(warrant?.generation) !== queue.activeWarrant.generation)
246
+ throw new Error("stale lease generation");
247
+ if (text(warrant?.candidateId) !== queue.activeWarrant.candidateId)
248
+ throw new Error("Warrant candidate mismatch");
249
+ if (
250
+ !allowExpired &&
251
+ Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(now)
252
+ )
253
+ throw new Error("Delivery Warrant lease expired");
440
254
  }
441
-
442
- export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Date().toISOString(), leaseSeconds } = {}) {
255
+ export function heartbeatDevDeliveryWarrant(
256
+ queueInput,
257
+ warrant,
258
+ { now = new Date().toISOString(), leaseSeconds } = {},
259
+ ) {
443
260
  const currentTime = timestamp(now, "now");
444
261
  const transaction = transition(
445
262
  queueInput,
446
263
  (queue, before) => {
447
264
  assertWarrantMutation(before, warrant, currentTime);
448
- const effectiveLeaseSeconds = positiveInteger(leaseSeconds, "leaseSeconds", queue.policy.leaseSeconds);
449
- queue.activeWarrant.expiresAt = new Date(Date.parse(currentTime) + effectiveLeaseSeconds * 1000).toISOString();
450
- const candidate = queue.candidates.find((entry) => entry.candidateId === queue.activeWarrant.candidateId);
451
- candidate.status = "proving";
265
+ const effectiveLeaseSeconds = positiveInteger(
266
+ leaseSeconds,
267
+ "leaseSeconds",
268
+ queue.policy.leaseSeconds,
269
+ );
270
+ queue.activeWarrant.heartbeatAt = currentTime;
271
+ queue.activeWarrant.expiresAt = new Date(
272
+ Date.parse(currentTime) + effectiveLeaseSeconds * 1000,
273
+ ).toISOString();
274
+ const candidate = queue.candidates.find(
275
+ (entry) => entry.candidateId === queue.activeWarrant.candidateId,
276
+ );
277
+ candidate.status =
278
+ queue.activeWarrant.phase === "qualified" ? "qualified" : "proving";
452
279
  candidate.updatedAt = currentTime;
453
280
  return { candidate, warrant: queue.activeWarrant };
454
281
  },
@@ -471,18 +298,44 @@ export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Dat
471
298
  receiptRoot: devDeliveryContentRoot(receipt),
472
299
  };
473
300
  }
474
-
475
- export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().toISOString() } = {}) {
301
+ export function recoverExpiredDevDeliveryWarrant(
302
+ queueInput,
303
+ { now = new Date().toISOString() } = {},
304
+ ) {
476
305
  const currentTime = timestamp(now, "now");
477
306
  const before = normalizeDevDeliveryQueue(queueInput);
478
- if (!before.activeWarrant || Date.parse(before.activeWarrant.expiresAt) > Date.parse(currentTime)) {
307
+ if (
308
+ !before.activeWarrant ||
309
+ Date.parse(before.activeWarrant.expiresAt) > Date.parse(currentTime)
310
+ ) {
479
311
  const receipt = {
480
312
  schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
481
313
  action: "recovery-noop",
482
314
  reason: before.activeWarrant ? "lease-active" : "no-active-warrant",
483
315
  expectedOldStateRoot: before.stateRoot,
484
316
  nextStateRoot: before.stateRoot,
485
- nextAction: before.activeWarrant ? "Continue the active delivery attempt." : "Select the next queued candidate.",
317
+ nextAction: before.activeWarrant
318
+ ? "Continue the active delivery attempt."
319
+ : "Select the next queued candidate.",
320
+ };
321
+ return {
322
+ queue: before,
323
+ receipt,
324
+ receiptRoot: devDeliveryContentRoot(receipt),
325
+ };
326
+ }
327
+ if (before.activeWarrant.phase === "provisional") {
328
+ const receipt = {
329
+ schema: DEV_DELIVERY_LEASE_RECEIPT_SCHEMA,
330
+ action: "expired-lease-fenced-stop-required",
331
+ reason: "provisional-native-worker-stop-unproven",
332
+ candidateId: before.activeWarrant.candidateId,
333
+ rejectedFencingToken: before.activeWarrant.fencingToken,
334
+ expiredLeaseGeneration: before.activeWarrant.generation,
335
+ expectedOldStateRoot: before.stateRoot,
336
+ nextStateRoot: before.stateRoot,
337
+ nextAction:
338
+ "Prove the fenced native worker stopped, then close this exact generation with rooted terminal evidence.",
486
339
  };
487
340
  return {
488
341
  queue: before,
@@ -494,7 +347,9 @@ export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().
494
347
  before,
495
348
  (queue) => {
496
349
  const expired = clone(queue.activeWarrant);
497
- const candidate = queue.candidates.find((entry) => entry.candidateId === expired.candidateId);
350
+ const candidate = queue.candidates.find(
351
+ (entry) => entry.candidateId === expired.candidateId,
352
+ );
498
353
  candidate.status = "queued";
499
354
  candidate.recoveries += 1;
500
355
  candidate.attempts += 1;
@@ -513,7 +368,8 @@ export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().
513
368
  retainedEnqueuedAt: transaction.result.candidate.enqueuedAt,
514
369
  expectedOldStateRoot: transaction.expectedOldStateRoot,
515
370
  nextStateRoot: transaction.after.stateRoot,
516
- nextAction: "Re-select through the queue to mint a new lease generation and fencing token.",
371
+ nextAction:
372
+ "Re-select through the queue to mint a new lease generation and fencing token.",
517
373
  };
518
374
  return {
519
375
  queue: transaction.after,
@@ -521,19 +377,54 @@ export function recoverExpiredDevDeliveryWarrant(queueInput, { now = new Date().
521
377
  receiptRoot: devDeliveryContentRoot(receipt),
522
378
  };
523
379
  }
524
-
525
- export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidenceRoot, reason = "", now = new Date().toISOString() } = {}) {
380
+ export const qualifyDevDeliveryWarrant = createDevDeliveryWarrantQualifier({
381
+ transition,
382
+ assertWarrantMutation,
383
+ });
384
+ export function closeDevDeliveryWarrant(
385
+ queueInput,
386
+ warrant,
387
+ {
388
+ outcome,
389
+ evidenceRoot,
390
+ reason = "",
391
+ providerFailureAuthority = null,
392
+ now = new Date().toISOString(),
393
+ } = {},
394
+ ) {
526
395
  const currentTime = timestamp(now, "now");
527
396
  const normalizedOutcome = text(outcome);
528
397
  if (!TERMINAL_STATES.has(normalizedOutcome)) {
529
- throw new Error(`outcome must be one of ${[...TERMINAL_STATES].join(", ")}`);
398
+ throw new Error(
399
+ `outcome must be one of ${[...TERMINAL_STATES].join(", ")}`,
400
+ );
401
+ }
402
+ const failureAuthority = normalizeProviderFailureAuthorityBinding(
403
+ providerFailureAuthority || {},
404
+ );
405
+ if (failureAuthority && normalizedOutcome !== "terminal-failure") {
406
+ throw new Error(
407
+ "provider failure authority is valid only for terminal-failure",
408
+ );
530
409
  }
531
410
  const transaction = transition(
532
411
  queueInput,
533
412
  (queue, before) => {
534
- assertWarrantMutation(before, warrant, currentTime);
413
+ assertWarrantMutation(before, warrant, currentTime, {
414
+ allowExpired: true,
415
+ });
416
+ if (
417
+ (before.activeWarrant.phase || "qualified") !== "qualified" &&
418
+ normalizedOutcome === "merged"
419
+ ) {
420
+ throw new Error(
421
+ "provisional Warrant cannot settle as merged before native qualification",
422
+ );
423
+ }
535
424
  const active = clone(queue.activeWarrant);
536
- const candidate = queue.candidates.find((entry) => entry.candidateId === active.candidateId);
425
+ const candidate = queue.candidates.find(
426
+ (entry) => entry.candidateId === active.candidateId,
427
+ );
537
428
  candidate.status = normalizedOutcome;
538
429
  candidate.updatedAt = currentTime;
539
430
  candidate.terminal = {
@@ -543,6 +434,7 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
543
434
  closedAt: currentTime,
544
435
  fencingToken: active.fencingToken,
545
436
  leaseGeneration: active.generation,
437
+ ...(failureAuthority || {}),
546
438
  };
547
439
  queue.activeWarrant = null;
548
440
  return { candidate, active };
@@ -558,8 +450,13 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
558
450
  fencingToken: transaction.result.active.fencingToken,
559
451
  leaseGeneration: transaction.result.active.generation,
560
452
  evidenceRoot: transaction.result.candidate.terminal.evidenceRoot,
453
+ ...(failureAuthority || {}),
561
454
  expectedOldStateRoot: transaction.expectedOldStateRoot,
562
455
  nextStateRoot: transaction.after.stateRoot,
456
+ successorWake: createDevDeliverySuccessorWake(
457
+ transaction.after,
458
+ currentTime,
459
+ ),
563
460
  nextAction: "Select the next queued candidate, if any.",
564
461
  };
565
462
  return {
@@ -568,17 +465,70 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
568
465
  receiptRoot: devDeliveryContentRoot(receipt),
569
466
  };
570
467
  }
571
-
572
- const cancelQueuedDevDeliveryCandidateTransition = createCancelQueuedDevDeliveryCandidate(normalizeDevDeliveryQueue);
573
- export function cancelQueuedDevDeliveryCandidate(queueInput, input, options) { return cancelQueuedDevDeliveryCandidateTransition(queueInput, input, options); }
574
- export const settleDevDeliveryTerminalEvent = createDevDeliveryTerminalSettler({ normalizeQueue: normalizeDevDeliveryQueue, closeWarrant: closeDevDeliveryWarrant, cancelQueued: cancelQueuedDevDeliveryCandidate, terminalStates: TERMINAL_STATES });
575
-
576
- export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOString() } = {}) {
577
- const queue = normalizeDevDeliveryQueue(queueInput);
468
+ function createDevDeliverySuccessorWake(queue, now) {
469
+ const next = rankDevDeliveryCandidates(queue, { now })[0]?.candidate;
470
+ if (!next) return null;
471
+ return {
472
+ schema: "kungfu.buildchain.dev-delivery-wake/v1",
473
+ targetBranch: queue.protectedBase,
474
+ ...Object.fromEntries(
475
+ [
476
+ "candidateId",
477
+ "pullRequestNumber",
478
+ "sourceHead",
479
+ "assignmentRoot",
480
+ "initiativeRoot",
481
+ "sourceIdentityRoot",
482
+ "sourcePatchRoot",
483
+ "sourceProofRoot",
484
+ "planRoot",
485
+ "closureRoot",
486
+ "dependencyRoot",
487
+ "toolchainRoot",
488
+ "deliveryClass",
489
+ "priority",
490
+ "nativeCommandContract",
491
+ "shardEvidenceRoots",
492
+ ]
493
+ .filter((field) => next[field] !== undefined)
494
+ .map((field) => [field, next[field]]),
495
+ ),
496
+ ...(next.environmentRoot ? { environmentRoot: next.environmentRoot } : {}),
497
+ ...(next.affectedPaths ? { affectedPaths: next.affectedPaths } : {}),
498
+ ...(Object.hasOwn(next, "sourceWorkflowRunId")
499
+ ? { sourceWorkflowRunId: next.sourceWorkflowRunId }
500
+ : {}),
501
+ ...(next.releaseBlockerPriority
502
+ ? { releaseBlockerPriority: next.releaseBlockerPriority }
503
+ : {}),
504
+ };
505
+ }
506
+ const cancelQueuedDevDeliveryCandidateTransition =
507
+ createCancelQueuedDevDeliveryCandidate(normalizeDevDeliveryQueue);
508
+ export function cancelQueuedDevDeliveryCandidate(queueInput, input, options) {
509
+ return cancelQueuedDevDeliveryCandidateTransition(queueInput, input, options);
510
+ }
511
+ export const settleDevDeliveryTerminalEvent = createDevDeliveryTerminalSettler({
512
+ normalizeQueue: normalizeDevDeliveryQueue,
513
+ closeWarrant: closeDevDeliveryWarrant,
514
+ cancelQueued: cancelQueuedDevDeliveryCandidate,
515
+ terminalStates: TERMINAL_STATES,
516
+ });
517
+ export function observeDevDeliveryQueue(
518
+ queueInput,
519
+ { now = new Date().toISOString(), allowLegacyV3Readback = false } = {},
520
+ ) {
521
+ const queue = normalizeDevDeliveryQueue(queueInput, {
522
+ allowLegacyV3Readback,
523
+ });
578
524
  const currentTime = timestamp(now, "now");
579
525
  const states = {};
580
- for (const candidate of queue.candidates) states[candidate.status] = (states[candidate.status] || 0) + 1;
581
- const queued = rankDevDeliveryCandidates(queue, { now: currentTime });
526
+ for (const candidate of queue.candidates)
527
+ states[candidate.status] = (states[candidate.status] || 0) + 1;
528
+ const queued = rankDevDeliveryCandidates(queue, {
529
+ now: currentTime,
530
+ allowLegacyV3Readback,
531
+ });
582
532
  return {
583
533
  schema: "kungfu.buildchain.dev-delivery-queue-observation/v1",
584
534
  repository: queue.repository,
@@ -586,7 +536,12 @@ export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOStri
586
536
  stateRoot: queue.stateRoot,
587
537
  generation: queue.generation,
588
538
  activeWarrant: queue.activeWarrant,
589
- activeCandidate: queue.activeWarrant ? queue.candidates.find((candidate) => candidate.candidateId === queue.activeWarrant.candidateId) || null : null,
539
+ activeCandidate: queue.activeWarrant
540
+ ? queue.candidates.find(
541
+ (candidate) =>
542
+ candidate.candidateId === queue.activeWarrant.candidateId,
543
+ ) || null
544
+ : null,
590
545
  states,
591
546
  queued: queued.map((entry, index) => ({
592
547
  position: index + 1,
@@ -598,7 +553,10 @@ export function observeDevDeliveryQueue(queueInput, { now = new Date().toISOStri
598
553
  queueAgeSeconds: entry.priority.ageSeconds,
599
554
  attempts: entry.candidate.attempts,
600
555
  recoveries: entry.candidate.recoveries,
601
- nextAction: index === 0 && !queue.activeWarrant ? "eligible-for-selection" : "wait-for-active-warrant",
556
+ nextAction:
557
+ index === 0 && !queue.activeWarrant
558
+ ? "eligible-for-selection"
559
+ : "wait-for-active-warrant",
602
560
  })),
603
561
  observedAt: currentTime,
604
562
  };