@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,16 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- import { cancelQueuedDevDeliveryCandidate, closeDevDeliveryWarrant, createDevDeliveryQueue, heartbeatDevDeliveryWarrant, observeDevDeliveryQueue, recoverExpiredDevDeliveryWarrant, selectDevDeliveryWarrant, settleDevDeliveryTerminalEvent, submitDevDeliveryCandidate } from "../packages/core/dev-delivery-warrant.js";
4
+ import {
5
+ cancelQueuedDevDeliveryCandidate,
6
+ closeDevDeliveryWarrant,
7
+ createDevDeliveryQueue,
8
+ createNativeCommandContract,
9
+ heartbeatDevDeliveryWarrant,
10
+ observeDevDeliveryQueue,
11
+ qualifyDevDeliveryWarrant,
12
+ recoverExpiredDevDeliveryWarrant,
13
+ selectDevDeliveryWarrant,
14
+ settleDevDeliveryTerminalEvent,
15
+ submitDevDeliveryCandidate,
16
+ } from "../packages/core/dev-delivery-warrant.js";
5
17
  import { runV4DeliveryWarrantReadCandidate } from "../packages/core/v4-delivery-warrant-read-candidate.js";
6
18
 
7
- const STATE_PATH = "queue.json";
19
+ import { GitHubDevDeliveryStore } from "./dev-delivery-warrant-store.mjs";
20
+ import { devDeliveryCliOptions } from "./dev-delivery-warrant-options.mjs";
21
+ export { GitHubDevDeliveryStore, devDeliveryCliOptions };
22
+
8
23
  const STATE_REF_PREFIX = "buildchain/dev-delivery-warrant/";
9
24
 
10
25
  function text(value = "") {
11
26
  return String(value ?? "").trim();
12
27
  }
13
-
14
28
  function bool(value, fallback = false) {
15
29
  if (value === undefined || value === null || value === "") return fallback;
16
30
  if (typeof value === "boolean") return value;
@@ -19,33 +33,41 @@ function bool(value, fallback = false) {
19
33
 
20
34
  function positiveInteger(value, label, fallback = 0) {
21
35
  const parsed = Number(value ?? fallback);
22
- if (!Number.isInteger(parsed) || parsed < 1) throw new Error(`${label} must be a positive integer`);
36
+ if (!Number.isInteger(parsed) || parsed < 1)
37
+ throw new Error(`${label} must be a positive integer`);
23
38
  return parsed;
24
39
  }
25
40
 
26
41
  function exactRoot(value, label) {
27
42
  const normalized = text(value).toLowerCase();
28
- if (!/^sha256:[0-9a-f]{64}$/.test(normalized)) throw new Error(`${label} must be a sha256 content root`);
43
+ if (!/^sha256:[0-9a-f]{64}$/.test(normalized))
44
+ throw new Error(`${label} must be a sha256 content root`);
29
45
  return normalized;
30
46
  }
31
47
 
32
48
  function exactSha(value, label) {
33
49
  const normalized = text(value).toLowerCase();
34
- if (!/^[0-9a-f]{40}$/.test(normalized)) throw new Error(`${label} must be a 40-character Git SHA`);
50
+ if (!/^[0-9a-f]{40}$/.test(normalized))
51
+ throw new Error(`${label} must be a 40-character Git SHA`);
35
52
  return normalized;
36
53
  }
37
54
 
38
55
  function normalizeRepository(value) {
39
56
  const normalized = text(value);
40
57
  const match = normalized.match(/^([^/\s]+)\/([^/\s]+)$/);
41
- if (!match) throw new Error(`repository must be owner/repo, got ${normalized || "<empty>"}`);
58
+ if (!match)
59
+ throw new Error(
60
+ `repository must be owner/repo, got ${normalized || "<empty>"}`,
61
+ );
42
62
  return { owner: match[1], repo: match[2], fullName: normalized };
43
63
  }
44
64
 
45
65
  function normalizeBranch(value) {
46
66
  const normalized = text(value).replace(/^refs\/heads\//, "");
47
67
  if (!/^dev\/v\d+\/v\d+\.\d+$/.test(normalized)) {
48
- throw new Error(`branch must be dev/vN/vN.M, got ${normalized || "<empty>"}`);
68
+ throw new Error(
69
+ `branch must be dev/vN/vN.M, got ${normalized || "<empty>"}`,
70
+ );
49
71
  }
50
72
  return normalized;
51
73
  }
@@ -56,142 +78,122 @@ export function defaultDevDeliveryStateRef(branch) {
56
78
  }
57
79
 
58
80
  function normalizeStateRef(value, branch) {
59
- const normalized = text(value || defaultDevDeliveryStateRef(branch)).replace(/^refs\/heads\//, "");
60
- if (!normalized.startsWith(STATE_REF_PREFIX) || normalized.includes("..") || normalized.endsWith("/")) {
81
+ const normalized = text(value || defaultDevDeliveryStateRef(branch)).replace(
82
+ /^refs\/heads\//,
83
+ "",
84
+ );
85
+ if (
86
+ !normalized.startsWith(STATE_REF_PREFIX) ||
87
+ normalized.includes("..") ||
88
+ normalized.endsWith("/")
89
+ ) {
61
90
  throw new Error(`state ref must remain under ${STATE_REF_PREFIX}`);
62
91
  }
63
92
  return normalized;
64
93
  }
65
94
 
66
- function encodeRef(ref) {
67
- return ref.split("/").map(encodeURIComponent).join("/");
95
+ function warrantIdentity(queue, options) {
96
+ const active = queue.activeWarrant;
97
+ if (!active) throw new Error("no active Delivery Warrant");
98
+ const fencingToken = exactRoot(options.fencingToken, "fencingToken");
99
+ const generation = positiveInteger(
100
+ options.leaseGeneration,
101
+ "leaseGeneration",
102
+ );
103
+ return { candidateId: active.candidateId, fencingToken, generation };
68
104
  }
69
105
 
70
- function decodeBlob(blob) {
71
- if (blob.encoding !== "base64") throw new Error(`unsupported Git blob encoding ${blob.encoding || "<empty>"}`);
72
- return Buffer.from(String(blob.content || "").replace(/\s+/g, ""), "base64").toString("utf8");
106
+ function jsonFile(file, label) {
107
+ if (!file) throw new Error(`${label} is required`);
108
+ return JSON.parse(fs.readFileSync(file, "utf8"));
73
109
  }
74
110
 
75
- export class GitHubDevDeliveryStore {
76
- constructor({ repository, token, apiUrl = "https://api.github.com", fetchImpl = globalThis.fetch } = {}) {
77
- this.repository = normalizeRepository(repository);
78
- if (!fetchImpl) throw new Error("fetch is required");
79
- if (!token) throw new Error("GITHUB_TOKEN is required for the GitHub dev delivery store");
80
- this.token = token;
81
- this.apiUrl = apiUrl.replace(/\/+$/, "");
82
- this.fetch = fetchImpl;
83
- }
84
-
85
- async request(method, requestPath, body) {
86
- const response = await this.fetch(`${this.apiUrl}${requestPath}`, {
87
- method,
88
- headers: {
89
- accept: "application/vnd.github+json",
90
- authorization: `Bearer ${this.token}`,
91
- "content-type": "application/json",
92
- "x-github-api-version": "2022-11-28",
93
- },
94
- body: body === undefined ? undefined : JSON.stringify(body),
95
- });
96
- const raw = await response.text();
97
- const data = raw ? JSON.parse(raw) : null;
98
- if (!response.ok) {
99
- const error = new Error(data?.message || raw || `${method} ${requestPath} failed`);
100
- error.status = response.status;
101
- error.data = data;
102
- throw error;
103
- }
104
- return data;
105
- }
106
-
107
- async read({ stateRef, protectedBase, now }) {
108
- let ref;
109
- try {
110
- ref = await this.request("GET", `/repos/${this.repository.fullName}/git/ref/heads/${encodeRef(stateRef)}`);
111
- } catch (error) {
112
- if (error.status !== 404) throw error;
113
- return {
114
- exists: false,
115
- commitSha: "",
116
- queue: createDevDeliveryQueue({
117
- repository: this.repository.fullName,
118
- protectedBase,
119
- now,
120
- }),
121
- };
122
- }
123
- const commitSha = exactSha(ref?.object?.sha, "state ref commit");
124
- const readback = await this.readCommit(commitSha);
125
- return { exists: true, ...readback };
111
+ function jsonList(value, label) {
112
+ let parsed;
113
+ try {
114
+ parsed = JSON.parse(value || "[]");
115
+ } catch (cause) {
116
+ throw new Error(`${label} must be a JSON array: ${cause.message}`);
126
117
  }
118
+ if (!Array.isArray(parsed)) throw new Error(`${label} must be a JSON array`);
119
+ return parsed;
120
+ }
127
121
 
128
- async readCommit(commitShaInput) {
129
- const commitSha = exactSha(commitShaInput, "state commit");
130
- const commit = await this.request("GET", `/repos/${this.repository.fullName}/git/commits/${commitSha}`);
131
- const tree = await this.request("GET", `/repos/${this.repository.fullName}/git/trees/${commit.tree?.sha}`);
132
- const entry = (tree.tree || []).find((item) => item.path === STATE_PATH && item.type === "blob");
133
- if (!entry?.sha) throw new Error(`${commitSha} does not contain ${STATE_PATH}`);
134
- const blob = await this.request("GET", `/repos/${this.repository.fullName}/git/blobs/${entry.sha}`);
135
- const queue = JSON.parse(decodeBlob(blob));
136
- return { commitSha, queue };
122
+ function jsonObject(value, label) {
123
+ if (!value) return null;
124
+ let parsed;
125
+ try {
126
+ parsed = typeof value === "string" ? JSON.parse(value) : value;
127
+ } catch (cause) {
128
+ throw new Error(`${label} must be a JSON object: ${cause.message}`);
137
129
  }
138
-
139
- async write({ stateRef, queue, expectedCommitSha, expectedStateRoot, receiptRoot }) {
140
- if (queue.stateRoot === expectedStateRoot) throw new Error("state transition did not advance the queue root");
141
- const blob = await this.request("POST", `/repos/${this.repository.fullName}/git/blobs`, {
142
- content: `${JSON.stringify(queue, null, 2)}\n`,
143
- encoding: "utf-8",
144
- });
145
- const tree = await this.request("POST", `/repos/${this.repository.fullName}/git/trees`, {
146
- tree: [{ path: STATE_PATH, mode: "100644", type: "blob", sha: blob.sha }],
147
- });
148
- const commit = await this.request("POST", `/repos/${this.repository.fullName}/git/commits`, {
149
- message: `chore(dev-delivery): advance Warrant queue ${receiptRoot.slice(0, 20)}`,
150
- tree: tree.sha,
151
- parents: expectedCommitSha ? [expectedCommitSha] : [],
152
- });
153
- if (expectedCommitSha) {
154
- await this.request("PATCH", `/repos/${this.repository.fullName}/git/refs/heads/${encodeRef(stateRef)}`, { sha: commit.sha, force: false });
155
- } else {
156
- await this.request("POST", `/repos/${this.repository.fullName}/git/refs`, {
157
- ref: `refs/heads/${stateRef}`,
158
- sha: commit.sha,
159
- });
160
- }
161
- const readback = await this.readCommit(commit.sha);
162
- if (readback.commitSha !== commit.sha || readback.queue.stateRoot !== queue.stateRoot) {
163
- throw new Error("dev delivery state commit readback mismatch after expected-old update");
164
- }
165
- return { commitSha: commit.sha, stateRoot: readback.queue.stateRoot };
130
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") {
131
+ throw new Error(`${label} must be a JSON object`);
166
132
  }
167
- }
168
-
169
- function warrantIdentity(queue, options) {
170
- const active = queue.activeWarrant;
171
- if (!active) throw new Error("no active Delivery Warrant");
172
- const fencingToken = exactRoot(options.fencingToken, "fencingToken");
173
- const generation = positiveInteger(options.leaseGeneration, "leaseGeneration");
174
- return { candidateId: active.candidateId, fencingToken, generation };
133
+ return parsed;
175
134
  }
176
135
 
177
136
  function transitionFor(command, queue, options) {
178
137
  if (command === "submit") {
138
+ const nativeCommandContract = options.environmentRoot
139
+ ? createNativeCommandContract(options.nativeCommand)
140
+ : null;
141
+ if (
142
+ options.nativeCommandRoot &&
143
+ nativeCommandContract?.commandRoot !==
144
+ exactRoot(options.nativeCommandRoot, "nativeCommandRoot")
145
+ ) {
146
+ throw new Error(
147
+ "native command contract root does not match native-command",
148
+ );
149
+ }
179
150
  return submitDevDeliveryCandidate(
180
151
  queue,
181
152
  {
182
- pullRequestNumber: positiveInteger(options.pullRequestNumber, "pullRequestNumber"),
153
+ pullRequestNumber: positiveInteger(
154
+ options.pullRequestNumber,
155
+ "pullRequestNumber",
156
+ ),
183
157
  sourceHead: exactSha(options.sourceHead, "sourceHead"),
184
158
  assignmentRoot: exactRoot(options.assignmentRoot, "assignmentRoot"),
185
159
  initiativeRoot: exactRoot(options.initiativeRoot, "initiativeRoot"),
186
- sourceIdentityRoot: exactRoot(options.sourceIdentityRoot, "sourceIdentityRoot"),
160
+ sourceIdentityRoot: exactRoot(
161
+ options.sourceIdentityRoot,
162
+ "sourceIdentityRoot",
163
+ ),
187
164
  sourcePatchRoot: exactRoot(options.sourcePatchRoot, "sourcePatchRoot"),
188
165
  sourceProofRoot: exactRoot(options.sourceProofRoot, "sourceProofRoot"),
189
166
  planRoot: exactRoot(options.planRoot, "planRoot"),
190
167
  closureRoot: exactRoot(options.closureRoot, "closureRoot"),
191
168
  dependencyRoot: exactRoot(options.dependencyRoot, "dependencyRoot"),
192
169
  toolchainRoot: exactRoot(options.toolchainRoot, "toolchainRoot"),
170
+ ...(options.environmentRoot
171
+ ? {
172
+ environmentRoot: exactRoot(
173
+ options.environmentRoot,
174
+ "environmentRoot",
175
+ ),
176
+ }
177
+ : {}),
178
+ ...(nativeCommandContract ? { nativeCommandContract } : {}),
179
+ affectedPaths: jsonList(options.affectedPaths, "affected paths"),
180
+ shardEvidenceRoots: jsonList(
181
+ options.shardEvidenceRoots,
182
+ "shard evidence roots",
183
+ ),
184
+ sourceWorkflowRunId: options.sourceWorkflowRunId
185
+ ? positiveInteger(options.sourceWorkflowRunId, "sourceWorkflowRunId")
186
+ : 0,
193
187
  deliveryClass: options.deliveryClass,
194
188
  priority: options.priority || "ordinary",
189
+ ...(options.releaseBlockerPriority
190
+ ? {
191
+ releaseBlockerPriority: jsonObject(
192
+ options.releaseBlockerPriority,
193
+ "release blocker priority",
194
+ ),
195
+ }
196
+ : {}),
195
197
  },
196
198
  { now: options.now },
197
199
  );
@@ -208,7 +210,25 @@ function transitionFor(command, queue, options) {
208
210
  leaseSeconds: options.leaseSeconds,
209
211
  });
210
212
  }
211
- if (command === "recover") return recoverExpiredDevDeliveryWarrant(queue, { now: options.now });
213
+ if (command === "qualify") {
214
+ return qualifyDevDeliveryWarrant(queue, warrantIdentity(queue, options), {
215
+ nativeProof: jsonFile(options.nativeProofPath, "native proof"),
216
+ reuseDecision: jsonFile(
217
+ options.nativeReuseDecisionPath,
218
+ "native reuse decision",
219
+ ),
220
+ current: {
221
+ currentBase: options.currentBase,
222
+ graphKnown: options.graphKnown,
223
+ attributionComplete: options.attributionComplete,
224
+ changedPaths: jsonList(options.changedPaths, "changed paths"),
225
+ renames: jsonList(options.renames, "renames"),
226
+ },
227
+ now: options.now,
228
+ });
229
+ }
230
+ if (command === "recover")
231
+ return recoverExpiredDevDeliveryWarrant(queue, { now: options.now });
212
232
  if (command === "close") {
213
233
  return closeDevDeliveryWarrant(queue, warrantIdentity(queue, options), {
214
234
  outcome: options.outcome,
@@ -221,14 +241,24 @@ function transitionFor(command, queue, options) {
221
241
  return settleDevDeliveryTerminalEvent(
222
242
  queue,
223
243
  {
224
- pullRequestNumber: positiveInteger(options.pullRequestNumber, "pullRequestNumber"),
225
- sourceHead: exactSha(options.expectedSourceHead || options.sourceHead, "sourceHead"),
244
+ pullRequestNumber: positiveInteger(
245
+ options.pullRequestNumber,
246
+ "pullRequestNumber",
247
+ ),
248
+ sourceHead: exactSha(
249
+ options.expectedSourceHead || options.sourceHead,
250
+ "sourceHead",
251
+ ),
226
252
  fencingToken: options.fencingToken,
227
253
  leaseGeneration: options.leaseGeneration,
228
254
  outcome: options.outcome,
229
255
  eventAction: options.eventAction,
230
256
  evidenceRoot: options.evidenceRoot,
231
257
  reason: options.reason,
258
+ transferRoot: options.transferRoot,
259
+ finalizerBoundaryRoot: options.finalizerBoundaryRoot,
260
+ nativeJobId: options.nativeJobId,
261
+ sealJobId: options.sealJobId,
232
262
  },
233
263
  { now: options.now },
234
264
  );
@@ -238,9 +268,18 @@ function transitionFor(command, queue, options) {
238
268
  queue,
239
269
  {
240
270
  candidateId: exactRoot(options.candidateId, "candidateId"),
241
- pullRequestNumber: positiveInteger(options.pullRequestNumber, "pullRequestNumber"),
242
- expectedSourceHead: exactSha(options.expectedSourceHead, "expectedSourceHead"),
243
- observedSourceHead: exactSha(options.observedSourceHead, "observedSourceHead"),
271
+ pullRequestNumber: positiveInteger(
272
+ options.pullRequestNumber,
273
+ "pullRequestNumber",
274
+ ),
275
+ expectedSourceHead: exactSha(
276
+ options.expectedSourceHead,
277
+ "expectedSourceHead",
278
+ ),
279
+ observedSourceHead: exactSha(
280
+ options.observedSourceHead,
281
+ "observedSourceHead",
282
+ ),
244
283
  eventAction: options.eventAction,
245
284
  outcome: options.outcome,
246
285
  evidenceRoot: exactRoot(options.evidenceRoot, "evidenceRoot"),
@@ -254,17 +293,29 @@ function transitionFor(command, queue, options) {
254
293
 
255
294
  async function observeQueue(loaded, options) {
256
295
  const readMode = text(options.readMode || "v3").toLowerCase();
257
- if (!["v3", "v4"].includes(readMode)) throw new Error("readMode must be v3 or v4");
258
- let observation = observeDevDeliveryQueue(loaded.queue, { now: options.now });
296
+ if (!["v3", "v4"].includes(readMode))
297
+ throw new Error("readMode must be v3 or v4");
298
+ let observation = observeDevDeliveryQueue(loaded.queue, {
299
+ now: options.now,
300
+ allowLegacyV3Readback: true,
301
+ });
259
302
  let readCandidate;
260
303
  if (readMode === "v4") {
261
- const qualification = options.readQualification || JSON.parse(fs.readFileSync(options.readQualificationPath, "utf8"));
304
+ const qualification =
305
+ options.readQualification ||
306
+ JSON.parse(fs.readFileSync(options.readQualificationPath, "utf8"));
262
307
  const retain =
263
308
  options.retainReadEvidence ||
264
309
  (async (evidence) => {
265
- if (!options.readEvidenceOutput) throw new Error("readEvidenceOutput is required");
266
- fs.mkdirSync(path.dirname(options.readEvidenceOutput), { recursive: true });
267
- fs.writeFileSync(options.readEvidenceOutput, `${JSON.stringify(evidence, null, 2)}\n`);
310
+ if (!options.readEvidenceOutput)
311
+ throw new Error("readEvidenceOutput is required");
312
+ fs.mkdirSync(path.dirname(options.readEvidenceOutput), {
313
+ recursive: true,
314
+ });
315
+ fs.writeFileSync(
316
+ options.readEvidenceOutput,
317
+ `${JSON.stringify(evidence, null, 2)}\n`,
318
+ );
268
319
  return { receiptRoot: evidence.evidenceRoot };
269
320
  });
270
321
  readCandidate = await runV4DeliveryWarrantReadCandidate(loaded.queue, {
@@ -305,20 +356,36 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
305
356
  now: new Date(optionsInput.now || Date.now()).toISOString(),
306
357
  execute: bool(optionsInput.execute, false),
307
358
  };
359
+ if (
360
+ options.command === "submit" &&
361
+ options.execute &&
362
+ options.deliveryClass !== "non-native-fast"
363
+ ) {
364
+ exactRoot(options.environmentRoot, "environmentRoot");
365
+ }
308
366
  const store =
309
367
  clientInput ||
310
368
  new GitHubDevDeliveryStore({
311
369
  repository: options.repository,
312
370
  token: options.token || process.env.GITHUB_TOKEN,
313
- apiUrl: options.apiUrl || process.env.GITHUB_API_URL || "https://api.github.com",
371
+ apiUrl:
372
+ options.apiUrl ||
373
+ process.env.GITHUB_API_URL ||
374
+ "https://api.github.com",
314
375
  });
315
376
  let loaded = await store.read({
316
377
  stateRef: options.stateRef,
317
378
  protectedBase: options.branch,
318
379
  now: options.now,
380
+ allowLegacyV3Readback: options.command === "observe",
319
381
  });
320
- if (options.expectedOldStateRoot && loaded.queue.stateRoot !== options.expectedOldStateRoot) {
321
- throw new Error(`expected-old state drift: ${loaded.queue.stateRoot} != ${options.expectedOldStateRoot}`);
382
+ if (
383
+ options.expectedOldStateRoot &&
384
+ loaded.queue.stateRoot !== options.expectedOldStateRoot
385
+ ) {
386
+ throw new Error(
387
+ `expected-old state drift: ${loaded.queue.stateRoot} != ${options.expectedOldStateRoot}`,
388
+ );
322
389
  }
323
390
  if (options.command === "observe") return observeQueue(loaded, options);
324
391
  const initialLoaded = loaded;
@@ -328,7 +395,9 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
328
395
  let concurrencyRecovery = null;
329
396
  if (options.execute && mutates) {
330
397
  if (changed.receipt.expectedOldStateRoot !== loaded.queue.stateRoot) {
331
- throw new Error("transition receipt expected-old root does not match the loaded authority");
398
+ throw new Error(
399
+ "transition receipt expected-old root does not match the loaded authority",
400
+ );
332
401
  }
333
402
  try {
334
403
  write = await store.write({
@@ -339,15 +408,21 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
339
408
  receiptRoot: changed.receiptRoot,
340
409
  });
341
410
  } catch (error) {
342
- if (options.command !== "settle" || options.expectedOldStateRoot) throw error;
411
+ if (options.command !== "settle" || options.expectedOldStateRoot)
412
+ throw error;
343
413
  const latest = await store.read({
344
414
  stateRef: options.stateRef,
345
415
  protectedBase: options.branch,
346
416
  now: options.now,
347
417
  });
348
418
  const reconciled = transitionFor(options.command, latest.queue, options);
349
- const reconciledMutates = reconciled.queue.stateRoot !== latest.queue.stateRoot;
350
- if (reconciledMutates || reconciled.receipt.action !== "duplicate-terminal-event-noop") throw error;
419
+ const reconciledMutates =
420
+ reconciled.queue.stateRoot !== latest.queue.stateRoot;
421
+ if (
422
+ reconciledMutates ||
423
+ reconciled.receipt.action !== "duplicate-terminal-event-noop"
424
+ )
425
+ throw error;
351
426
  loaded = latest;
352
427
  changed = reconciled;
353
428
  mutates = false;
@@ -381,83 +456,46 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
381
456
  };
382
457
  }
383
458
 
384
- function flag(args, name, fallback = "") {
385
- const index = args.indexOf(`--${name}`);
386
- return index === -1 ? fallback : args[index + 1] || "";
387
- }
388
-
389
- function hasFlag(args, name) {
390
- return args.includes(`--${name}`);
391
- }
392
-
393
- export function devDeliveryCliOptions(args = [], environment = process.env) {
394
- const [command = "", ...rest] = args;
395
- return {
396
- command,
397
- repository: flag(rest, "repository", environment.GITHUB_REPOSITORY),
398
- branch: flag(rest, "branch", environment.BUILDCHAIN_DEV_DELIVERY_BRANCH || environment.GITHUB_BASE_REF),
399
- stateRef: flag(rest, "state-ref", environment.BUILDCHAIN_DEV_DELIVERY_STATE_REF),
400
- expectedOldStateRoot: flag(rest, "expected-old", environment.BUILDCHAIN_DEV_DELIVERY_EXPECTED_OLD),
401
- pullRequestNumber: flag(rest, "pull-request", environment.BUILDCHAIN_DEV_DELIVERY_PR_NUMBER),
402
- candidateId: flag(rest, "candidate-id", environment.BUILDCHAIN_DEV_DELIVERY_CANDIDATE_ID),
403
- sourceHead: flag(rest, "source-head", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_HEAD),
404
- expectedSourceHead: flag(rest, "expected-source-head", environment.BUILDCHAIN_DEV_DELIVERY_EXPECTED_SOURCE_HEAD),
405
- observedSourceHead: flag(rest, "observed-source-head", environment.BUILDCHAIN_DEV_DELIVERY_OBSERVED_SOURCE_HEAD),
406
- assignmentRoot: flag(rest, "assignment-root", environment.BUILDCHAIN_DEV_DELIVERY_ASSIGNMENT_ROOT),
407
- initiativeRoot: flag(rest, "initiative-root", environment.BUILDCHAIN_DEV_DELIVERY_INITIATIVE_ROOT),
408
- sourceIdentityRoot: flag(rest, "source-identity-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_IDENTITY_ROOT),
409
- sourcePatchRoot: flag(rest, "source-patch-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_PATCH_ROOT),
410
- sourceProofRoot: flag(rest, "source-proof-root", environment.BUILDCHAIN_DEV_DELIVERY_SOURCE_PROOF_ROOT),
411
- planRoot: flag(rest, "plan-root", environment.BUILDCHAIN_DEV_DELIVERY_PLAN_ROOT),
412
- closureRoot: flag(rest, "closure-root", environment.BUILDCHAIN_DEV_DELIVERY_CLOSURE_ROOT),
413
- dependencyRoot: flag(rest, "dependency-root", environment.BUILDCHAIN_DEV_DELIVERY_DEPENDENCY_ROOT),
414
- toolchainRoot: flag(rest, "toolchain-root", environment.BUILDCHAIN_DEV_DELIVERY_TOOLCHAIN_ROOT),
415
- deliveryClass: flag(rest, "delivery-class", environment.BUILDCHAIN_DEV_DELIVERY_CLASS),
416
- priority: flag(rest, "priority", environment.BUILDCHAIN_DEV_DELIVERY_PRIORITY || "ordinary"),
417
- fencingToken: flag(rest, "fencing-token", environment.BUILDCHAIN_DEV_DELIVERY_FENCING_TOKEN),
418
- leaseGeneration: flag(rest, "lease-generation", environment.BUILDCHAIN_DEV_DELIVERY_LEASE_GENERATION),
419
- leaseSeconds: flag(rest, "lease-seconds", environment.BUILDCHAIN_DEV_DELIVERY_LEASE_SECONDS),
420
- outcome: flag(rest, "outcome", environment.BUILDCHAIN_DEV_DELIVERY_OUTCOME),
421
- eventAction: flag(rest, "event-action", environment.BUILDCHAIN_DEV_DELIVERY_EVENT_ACTION),
422
- evidenceRoot: flag(rest, "evidence-root", environment.BUILDCHAIN_DEV_DELIVERY_EVIDENCE_ROOT),
423
- reason: flag(rest, "reason", environment.BUILDCHAIN_DEV_DELIVERY_REASON),
424
- readMode: flag(rest, "read-mode", environment.BUILDCHAIN_V4_WARRANT_READ_MODE || "v3"),
425
- readQualificationPath: flag(rest, "read-qualification", environment.BUILDCHAIN_V4_WARRANT_READ_QUALIFICATION),
426
- readQualificationRoot: flag(rest, "read-qualification-root", environment.BUILDCHAIN_V4_WARRANT_READ_QUALIFICATION_ROOT),
427
- readTypescriptRevision: flag(rest, "read-typescript-revision", environment.BUILDCHAIN_V4_WARRANT_TYPESCRIPT_REVISION),
428
- readRustRevision: flag(rest, "read-rust-revision", environment.BUILDCHAIN_V4_WARRANT_RUST_REVISION),
429
- readValidatorVersion: flag(rest, "read-validator-version", environment.BUILDCHAIN_V4_WARRANT_VALIDATOR_VERSION),
430
- readTimeoutMs: flag(rest, "read-timeout-ms", environment.BUILDCHAIN_V4_WARRANT_READ_TIMEOUT_MS || "5000"),
431
- readEvidenceOutput: flag(rest, "read-evidence-output", environment.BUILDCHAIN_V4_WARRANT_READ_EVIDENCE || ".buildchain/dev-delivery/v4-read-evidence.json"),
432
- now: flag(rest, "now", environment.BUILDCHAIN_DEV_DELIVERY_NOW),
433
- outputPath: flag(rest, "output", environment.BUILDCHAIN_DEV_DELIVERY_OUTPUT || ".buildchain/dev-delivery/result.json"),
434
- execute: hasFlag(rest, "execute"),
435
- json: hasFlag(rest, "json"),
436
- };
437
- }
438
-
439
459
  function usage() {
440
- return "Usage:\n buildchain dev warrant <submit|select|heartbeat|recover|close|settle|cancel-queued|observe> --repository owner/repo --branch dev/vN/vN.M [--execute] [--output FILE] [--json]\n\nRead candidate:\n observe --read-mode v4 --read-qualification FILE --read-qualification-root sha256:... --read-typescript-revision SHA --read-rust-revision SHA --read-validator-version TOKEN [--read-evidence-output FILE]\n";
460
+ return "Usage:\n buildchain dev warrant <submit|select|heartbeat|qualify|recover|close|settle|cancel-queued|observe> --repository owner/repo --branch dev/vN/vN.M [--execute] [--output FILE] [--json]\n\nRead candidate:\n observe --read-mode v4 --read-qualification FILE --read-qualification-root sha256:... --read-typescript-revision SHA --read-rust-revision SHA --read-validator-version TOKEN [--read-evidence-output FILE]\n";
441
461
  }
442
462
 
443
463
  async function main() {
444
464
  const args = process.argv.slice(2);
445
- if (args.length === 0 || hasFlag(args, "help")) {
465
+ if (args.length === 0 || args.includes("--help")) {
446
466
  process.stdout.write(usage());
447
467
  return;
448
468
  }
449
469
  const options = devDeliveryCliOptions(args);
450
- if (!["submit", "select", "heartbeat", "recover", "close", "settle", "cancel-queued", "observe"].includes(options.command)) {
470
+ if (
471
+ ![
472
+ "submit",
473
+ "select",
474
+ "heartbeat",
475
+ "qualify",
476
+ "recover",
477
+ "close",
478
+ "settle",
479
+ "cancel-queued",
480
+ "observe",
481
+ ].includes(options.command)
482
+ ) {
451
483
  throw new Error(usage().trim());
452
484
  }
453
485
  const result = await runDevDeliveryCommand(options);
454
486
  fs.mkdirSync(path.dirname(options.outputPath), { recursive: true });
455
487
  fs.writeFileSync(options.outputPath, `${JSON.stringify(result, null, 2)}\n`);
456
- if (options.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
488
+ if (options.json)
489
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
457
490
  else {
458
- process.stdout.write(`Buildchain dev delivery ${options.command}: ${result.receipt?.reason || result.mode}\n`);
459
- process.stdout.write(`State root: ${result.after?.stateRoot || result.observation.stateRoot}\n`);
460
- if (result.receiptRoot) process.stdout.write(`Receipt root: ${result.receiptRoot}\n`);
491
+ process.stdout.write(
492
+ `Buildchain dev delivery ${options.command}: ${result.receipt?.reason || result.mode}\n`,
493
+ );
494
+ process.stdout.write(
495
+ `State root: ${result.after?.stateRoot || result.observation.stateRoot}\n`,
496
+ );
497
+ if (result.receiptRoot)
498
+ process.stdout.write(`Receipt root: ${result.receiptRoot}\n`);
461
499
  process.stdout.write(`Result: ${options.outputPath}\n`);
462
500
  }
463
501
  }
@@ -70,7 +70,7 @@ function normalizeOptions(options = {}) {
70
70
  allowedHeadPrefixes: splitList(options.allowedHeadPrefixes, DEFAULT_ALLOWED_HEAD_PREFIXES),
71
71
  requiredChecks: splitList(options.requiredChecks, DEFAULT_REQUIRED_CHECKS),
72
72
  queueAdmissionContext: String(options.queueAdmissionContext || "").trim(),
73
- activeLeaseContext: String(options.activeLeaseContext || "").trim(),
73
+ activeLeaseContext: String(options.activeLeaseContext || (choiceOption(options.warrantMode, VALID_WARRANT_MODES, "off", "delivery Warrant mode") === "required" ? "Queue family lease/exact" : "")).trim(),
74
74
  requireApproval: boolOption(options.requireApproval, true),
75
75
  sameRepositoryOnly: boolOption(options.sameRepositoryOnly, true),
76
76
  maxMerges: intOption(options.maxMerges, 1),
@@ -52,6 +52,11 @@ function exactWarrantBinding({ result, warrant, candidate, options, pullRequest
52
52
  requireMatch(Number.isInteger(Number(warrant.generation)) && Number(warrant.generation) >= 1, "delivery-warrant-generation-invalid");
53
53
  requireMatch(Number.isFinite(Date.parse(warrant.expiresAt)) && Date.parse(warrant.expiresAt) > Date.now(), "delivery-warrant-expired");
54
54
  requireMatch(ROOT_PATTERN.test(String(result.receiptRoot || "")), "delivery-warrant-receipt-root-missing");
55
+ requireMatch((warrant.phase || "qualified") === "qualified", "delivery-warrant-not-qualified");
56
+ if (warrant.phase === "qualified") {
57
+ requireMatch(ROOT_PATTERN.test(String(warrant.nativeProofRoot || "")), "delivery-warrant-native-proof-missing");
58
+ requireMatch(ROOT_PATTERN.test(String(warrant.nativeProofReuseRoot || "")), "delivery-warrant-native-reuse-missing");
59
+ }
55
60
  }
56
61
 
57
62
  export function createDevPrAdmissionReceipt({ options, pr = {}, state, reason, readiness, decision = {}, queue = null, warrant = null, labels = [], nextAction }) {
@@ -120,6 +125,8 @@ export function readDeliveryWarrantResult(options, pullRequest) {
120
125
  pullRequestNumber: warrant.pullRequestNumber,
121
126
  sourceHead: warrant.sourceHead,
122
127
  sourceProofRoot: warrant.sourceProofRoot,
128
+ nativeProofRoot: warrant.nativeProofRoot || "",
129
+ nativeProofReuseRoot: warrant.nativeProofReuseRoot || "",
123
130
  };
124
131
  }
125
132
 
@@ -136,6 +143,7 @@ export async function readCurrentDeliveryQueueState(client, repository, targetBr
136
143
  const { queue } = await store.read({
137
144
  stateRef: defaultDevDeliveryStateRef(targetBranch),
138
145
  protectedBase: targetBranch,
146
+ allowLegacyV3Readback: true,
139
147
  });
140
148
  return queue;
141
149
  }
@@ -162,6 +170,9 @@ export async function verifyCurrentDeliveryWarrant(client, options, pullRequest,
162
170
  requireMatch(Number(active?.pullRequestNumber) === Number(pullRequest.number), "delivery-warrant-current-pr-mismatch");
163
171
  requireMatch(String(active?.sourceHead || "").toLowerCase() === options.expectedHeadSha, "delivery-warrant-current-head-mismatch");
164
172
  requireMatch(active?.sourceProofRoot === warrant.sourceProofRoot, "delivery-warrant-current-source-proof-mismatch");
173
+ requireMatch((active?.phase || "qualified") === "qualified", "delivery-warrant-current-not-qualified");
174
+ requireMatch((active?.nativeProofRoot || "") === (warrant.nativeProofRoot || ""), "delivery-warrant-current-native-proof-mismatch");
175
+ requireMatch((active?.nativeProofReuseRoot || "") === (warrant.nativeProofReuseRoot || ""), "delivery-warrant-current-native-reuse-mismatch");
165
176
  requireMatch(Number.isFinite(Date.parse(active?.expiresAt)) && Date.parse(active.expiresAt) > Date.now(), "delivery-warrant-current-expired");
166
177
  requireMatch(["selected", "proving", "waiting", "blocked", "qualified"].includes(candidate?.status), "delivery-warrant-current-candidate-not-selected");
167
178
  requireMatch(candidate?.sourceHead === active.sourceHead, "delivery-warrant-current-candidate-head-mismatch");