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