@kungfu-tech/buildchain 4.0.0 → 4.0.1-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/AGENTS.md +13 -5
  2. package/actions/promote-buildchain-ref/README.md +11 -6
  3. package/actions/release-tail/README.md +8 -0
  4. package/architecture/ci-lane-change-budget.json +187 -0
  5. package/architecture/decisions/0002-next-development-transition.md +101 -0
  6. package/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
  7. package/architecture/internal-capabilities.json +219 -7
  8. package/architecture/maintainability-policy.json +246 -29
  9. package/architecture/release-tail-contract-inventory.json +5 -2
  10. package/architecture/v3-core-mechanism-inventory.json +91 -0
  11. package/architecture/v4-adopter-delivery-parity.json +32 -5
  12. package/architecture/v4-capability-state-machine-manifest.json +73 -0
  13. package/architecture/v4-compatibility-facts-parity.json +77 -0
  14. package/architecture/v4-delivery-authority-parity.json +250 -0
  15. package/architecture/v4-delivery-warrant-shadow-fixtures.json +41 -9
  16. package/architecture/v4-floating-consumer-policy.json +81 -0
  17. package/architecture/v4-floating-consumer-policy.md +32 -0
  18. package/architecture/v4-next-development-parity.json +192 -0
  19. package/architecture/v4-publication-rehearsal-parity.json +84 -0
  20. package/architecture/v4-release-train-parity.json +1 -0
  21. package/architecture/v4-runtime-ref-resume-authority.json +64 -0
  22. package/architecture/v4-stage-capsule-qualification.json +2 -2
  23. package/architecture/v4-tail-reseal-parity.json +150 -0
  24. package/bin/buildchain.mjs +32 -3
  25. package/bin/internal/adopter-delivery-cli.mjs +70 -0
  26. package/bin/internal/command-registry.mjs +3 -0
  27. package/bin/internal/compatibility-facts-cli.mjs +101 -0
  28. package/bin/internal/trust-release-release-handlers.mjs +5 -0
  29. package/contracts/dev-delivery-authority-v2.schema.json +662 -0
  30. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
  31. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
  32. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
  33. package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
  34. package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
  35. package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
  36. package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
  37. package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
  38. package/contracts/fixtures/v4-compatibility-facts-v1/shared.json +1129 -0
  39. package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +59 -0
  40. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
  41. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
  42. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
  43. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
  44. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
  45. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
  46. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
  47. package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
  48. package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
  49. package/contracts/fixtures/v4-runtime-ref-resume-authority-v1/scenario.json +15 -0
  50. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +229 -0
  51. package/contracts/next-development-request-v1.schema.json +66 -0
  52. package/contracts/next-development-transition-v1.schema.json +292 -0
  53. package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
  54. package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
  55. package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +107 -0
  56. package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  57. package/contracts/v4-runtime-ref-resume-authority-v1.schema.json +235 -0
  58. package/contracts/v4-tail-reseal-v1.schema.json +276 -0
  59. package/dist/site/agent-index.json +5 -0
  60. package/dist/site/artifact-schemas.json +10 -0
  61. package/dist/site/buildchain-contract.json +1562 -44
  62. package/dist/site/buildchain-site.json +684 -42
  63. package/dist/site/capability-registry.json +17 -14
  64. package/dist/site/cli-registry.json +205 -3
  65. package/dist/site/controller-registry.json +62 -6
  66. package/dist/site/kfd-claims.json +551 -22
  67. package/dist/site/kfd-upstream-aggregate.json +1 -1
  68. package/dist/site/manual-registry.json +55 -9
  69. package/dist/site/node-api-registry.json +15247 -8690
  70. package/dist/site/page-registry.json +647 -29
  71. package/dist/site/public-surface-audit.json +876 -55
  72. package/dist/site/publication-authority-registry.json +44 -8
  73. package/dist/site/publication-registry.json +4 -4
  74. package/dist/site/release-provenance.json +26 -0
  75. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -0
  76. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
  77. package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
  78. package/dist/site/schemas/v4-compatibility-facts-v1.schema.json +230 -0
  79. package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  80. package/dist/site/site-manifest.json +37 -13
  81. package/dist/site/workflow-registry.json +200 -22
  82. package/docs/MAP.md +10 -2
  83. package/docs/cli-reference.md +404 -13
  84. package/docs/cli.md +8 -0
  85. package/docs/dev-alpha-candidate-patrol.md +8 -0
  86. package/docs/dev-delivery-qualification-landing-adr.md +251 -0
  87. package/docs/dev-delivery-warrant.md +361 -30
  88. package/docs/lifecycle-protocol.md +41 -0
  89. package/docs/next-development-transition.md +118 -0
  90. package/docs/node-api-reference.md +748 -322
  91. package/docs/release-tail-provider-plane.md +30 -0
  92. package/docs/reusable-build-surface.md +41 -0
  93. package/docs/runtime-train-validation.md +10 -0
  94. package/docs/v4-adopter-delivery.md +133 -0
  95. package/docs/v4-compatibility-facts.md +91 -0
  96. package/docs/v4-publication-rehearsal.md +130 -0
  97. package/docs/v4-runtime-ref-resume-authority.md +69 -0
  98. package/docs/v4-tail-reseal.md +73 -0
  99. package/docs/versioning.md +4 -2
  100. package/package.json +36 -6
  101. package/packages/core/adopter-delivery-passport.js +259 -0
  102. package/packages/core/adopter-delivery-vectors.js +158 -0
  103. package/packages/core/adopter-delivery-vectors.json +77 -0
  104. package/packages/core/artifact-signing.js +61 -0
  105. package/packages/core/buildchain-agent-manuals.js +3 -0
  106. package/packages/core/buildchain-compatibility-authority.js +336 -0
  107. package/packages/core/buildchain-compatibility-fact.js +394 -0
  108. package/packages/core/buildchain-compatibility-facts.json +217 -0
  109. package/packages/core/buildchain-compatibility-proof.js +566 -0
  110. package/packages/core/buildchain-config.js +150 -1
  111. package/packages/core/buildchain-contract.js +96 -143
  112. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  113. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  114. package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
  115. package/packages/core/buildchain-publication-authority.js +1 -0
  116. package/packages/core/channel-candidate.js +8 -0
  117. package/packages/core/channel-promotion-baseline.js +55 -0
  118. package/packages/core/ci-lane-change-budget.js +247 -0
  119. package/packages/core/dev-alpha-candidate-selection.js +10 -2
  120. package/packages/core/dev-delivery-authority-candidate.js +270 -0
  121. package/packages/core/dev-delivery-authority-evidence.js +146 -0
  122. package/packages/core/dev-delivery-authority-landing.js +461 -0
  123. package/packages/core/dev-delivery-authority-observation.js +48 -0
  124. package/packages/core/dev-delivery-authority-qualification.js +591 -0
  125. package/packages/core/dev-delivery-authority-settlement.js +213 -0
  126. package/packages/core/dev-delivery-authority-state.js +583 -0
  127. package/packages/core/dev-delivery-candidate-identity.js +13 -0
  128. package/packages/core/dev-delivery-contract-surface.js +76 -0
  129. package/packages/core/dev-delivery-execution-failure.js +133 -0
  130. package/packages/core/dev-delivery-execution-transfer.js +592 -0
  131. package/packages/core/dev-delivery-landing-admission-core.js +119 -0
  132. package/packages/core/dev-delivery-landing-readback.js +598 -0
  133. package/packages/core/dev-delivery-landing-terminal-evidence.js +271 -0
  134. package/packages/core/dev-delivery-landing-testing-port.js +6 -0
  135. package/packages/core/dev-delivery-native-execution.js +110 -0
  136. package/packages/core/dev-delivery-native-proof.js +562 -0
  137. package/packages/core/dev-delivery-process-boundary.js +563 -0
  138. package/packages/core/dev-delivery-proof.js +37 -3
  139. package/packages/core/dev-delivery-provider-attempt.js +127 -0
  140. package/packages/core/dev-delivery-provider-heartbeat.js +382 -0
  141. package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
  142. package/packages/core/dev-delivery-warrant-qualification.js +145 -0
  143. package/packages/core/dev-delivery-warrant-settlement.js +237 -36
  144. package/packages/core/dev-delivery-warrant-state.js +587 -0
  145. package/packages/core/dev-delivery-warrant.js +328 -370
  146. package/packages/core/github-governance-authority.js +1 -0
  147. package/packages/core/index.js +7 -0
  148. package/packages/core/kungfu-temporal-fact.js +557 -0
  149. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  150. package/packages/core/next-development-candidate-reservation.js +186 -0
  151. package/packages/core/next-development-controller.js +728 -0
  152. package/packages/core/next-development-projection.js +287 -0
  153. package/packages/core/next-development-transition.js +738 -0
  154. package/packages/core/paper-agent-entry.js +11 -5
  155. package/packages/core/paper.js +16 -5
  156. package/packages/core/publication-authority.js +5 -5
  157. package/packages/core/published-delivery-authority.js +266 -0
  158. package/packages/core/release-candidate-recovery.js +66 -11
  159. package/packages/core/release-candidate.js +79 -19
  160. package/packages/core/release-passport.js +271 -38
  161. package/packages/core/v4-adopter-delivery-parity.js +3 -3
  162. package/packages/core/v4-adopter-delivery.js +359 -0
  163. package/packages/core/v4-canonical-contracts.js +5 -0
  164. package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
  165. package/packages/core/v4-floating-consumer-evidence.js +338 -0
  166. package/packages/core/v4-floating-consumer-policy.js +569 -0
  167. package/packages/core/v4-floating-consumer-release-passport.js +153 -0
  168. package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
  169. package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
  170. package/packages/core/v4-publication-rehearsal.js +481 -0
  171. package/packages/core/v4-runtime-ref-resume-authority.js +625 -0
  172. package/packages/core/v4-runtime-selector-persistence.js +228 -0
  173. package/packages/core/v4-tail-reseal-contract.js +26 -0
  174. package/packages/core/v4-tail-reseal-github.js +178 -0
  175. package/packages/core/v4-tail-reseal-receipt.js +225 -0
  176. package/packages/core/v4-tail-reseal.js +594 -0
  177. package/packages/core/workflow-yaml-contract.js +72 -2
  178. package/scripts/audit-publication-control-plane.mjs +31 -31
  179. package/scripts/buildchain-cli-help.mjs +41 -2
  180. package/scripts/check-ci-lane-change-budget.mjs +100 -0
  181. package/scripts/check-inventory.mjs +14 -3
  182. package/scripts/check-v4-floating-consumer-policy-contract.mjs +217 -0
  183. package/scripts/check-v4-public-dogfood-contract.mjs +6 -11
  184. package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
  185. package/scripts/dev-delivery-authority-command-adapters.mjs +206 -0
  186. package/scripts/dev-delivery-authority-provider.mjs +28 -0
  187. package/scripts/dev-delivery-authority.mjs +490 -0
  188. package/scripts/dev-delivery-native-run.mjs +177 -0
  189. package/scripts/dev-delivery-process-boundary.mjs +260 -0
  190. package/scripts/dev-delivery-proof.mjs +67 -2
  191. package/scripts/dev-delivery-provider-heartbeat.mjs +215 -0
  192. package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
  193. package/scripts/dev-delivery-source-proof-reuse.mjs +547 -0
  194. package/scripts/dev-delivery-two-phase-resume.mjs +345 -0
  195. package/scripts/dev-delivery-two-phase.mjs +573 -0
  196. package/scripts/dev-delivery-warrant-options.mjs +266 -0
  197. package/scripts/dev-delivery-warrant-store.mjs +231 -0
  198. package/scripts/dev-delivery-warrant.mjs +232 -194
  199. package/scripts/dev-pr-auto-merge.mjs +37 -48
  200. package/scripts/dev-pr-delivery-warrant.mjs +216 -2
  201. package/scripts/dev-pr-prequeue-guard.mjs +399 -0
  202. package/scripts/dev-qualification-patrol.mjs +9 -9
  203. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  204. package/scripts/ensure-github-release.mjs +3 -3
  205. package/scripts/generate-channel-build-workflow.mjs +3 -0
  206. package/scripts/generate-channel-promotion-workflow.mjs +114 -14
  207. package/scripts/generate-next-development-guidance.mjs +49 -0
  208. package/scripts/generate-release-candidate-passport.mjs +192 -32
  209. package/scripts/generate-site-bundle.mjs +69 -4
  210. package/scripts/init-repo.mjs +26 -4
  211. package/scripts/inspect-artifact-signing-requests.mjs +6 -0
  212. package/scripts/next-development-self-dogfood-harness.mjs +460 -0
  213. package/scripts/next-development-self-dogfood.mjs +597 -0
  214. package/scripts/next-development-transition.mjs +47 -0
  215. package/scripts/npm-publish-transaction.mjs +103 -91
  216. package/scripts/promotion-channel-router.mjs +4 -4
  217. package/scripts/release-candidate-anchor-provenance.mjs +99 -0
  218. package/scripts/release-tail.mjs +51 -3
  219. package/scripts/release-train-self-dogfood.mjs +94 -92
  220. package/scripts/resume-from-candidate-run.mjs +565 -41
  221. package/scripts/seal-artifact-signing-requests.mjs +6 -0
  222. package/scripts/site-capability-metadata.mjs +43 -0
  223. package/scripts/stable-candidate-qualification.mjs +9 -10
  224. package/scripts/v4-consumer-policy.mjs +231 -0
  225. package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
  226. package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
  227. package/scripts/v4-tail-reseal.mjs +416 -0
  228. package/templates/native-dev-delivery.yml +143 -0
@@ -16,12 +16,21 @@ function flag(name, fallback = "") {
16
16
  return index === -1 ? fallback : String(process.argv[index + 1] || "");
17
17
  }
18
18
 
19
- function commandJson(command, args, label) {
20
- const result = spawnSyncCommand(command, args, {
19
+ function commandJson(command, args, label, { publicReadFallback = false } = {}) {
20
+ const options = {
21
21
  encoding: "utf8",
22
22
  timeout: 60_000,
23
23
  maxBuffer: GITHUB_JSON_MAX_BUFFER,
24
- });
24
+ };
25
+ let result = spawnSyncCommand(command, args, options);
26
+ const fallbackToken = String(process.env.BUILDCHAIN_GITHUB_PUBLIC_READ_TOKEN || "");
27
+ const primaryToken = String(process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "");
28
+ if (result.status !== 0 && publicReadFallback && fallbackToken && fallbackToken !== primaryToken) {
29
+ result = spawnSyncCommand(command, args, {
30
+ ...options,
31
+ env: { ...process.env, GH_TOKEN: fallbackToken, GITHUB_TOKEN: fallbackToken },
32
+ });
33
+ }
25
34
  if (result.status !== 0) {
26
35
  const category = /401|E401|unauthorized/i.test(result.stderr) ? "unauthorized" : "unavailable";
27
36
  throw new Error(`${label} is ${category}; publication control-plane audit fails closed`);
@@ -33,18 +42,22 @@ function commandJson(command, args, label) {
33
42
  }
34
43
  }
35
44
 
36
- function githubJson(apiPath, label) {
37
- return commandJson("gh", ["api", apiPath, "-H", "Accept: application/vnd.github+json"], label);
45
+ function githubJson(apiPath, label, { publicReadFallback = false } = {}) {
46
+ return commandJson("gh", ["api", apiPath, "-H", "Accept: application/vnd.github+json"], label, { publicReadFallback });
47
+ }
48
+
49
+ function githubPublicJson(apiPath, label) {
50
+ return githubJson(apiPath, label, { publicReadFallback: true });
38
51
  }
39
52
 
40
- function githubJsonOptional(apiPath, label, fallback) {
53
+ function githubJsonOptional(apiPath, label, fallback, fallbackPattern = /404|not found/i) {
41
54
  const result = spawnSyncCommand("gh", ["api", apiPath, "-H", "Accept: application/vnd.github+json"], {
42
55
  encoding: "utf8",
43
56
  timeout: 60_000,
44
57
  maxBuffer: GITHUB_JSON_MAX_BUFFER,
45
58
  });
46
59
  if (result.status !== 0) {
47
- if (/404|not found/i.test(`${result.stdout}\n${result.stderr}`)) return fallback;
60
+ if (fallbackPattern.test(`${result.stdout}\n${result.stderr}`)) return fallback;
48
61
  const category = /401|403|unauthorized|forbidden/i.test(`${result.stdout}\n${result.stderr}`) ? "unauthorized" : "unavailable";
49
62
  throw new Error(`${label} is ${category}; publication control-plane audit fails closed`);
50
63
  }
@@ -56,20 +69,7 @@ function githubJsonOptional(apiPath, label, fallback) {
56
69
  }
57
70
 
58
71
  function githubJsonReadLimited(apiPath, label, fallback) {
59
- const result = spawnSyncCommand("gh", ["api", apiPath, "-H", "Accept: application/vnd.github+json"], {
60
- encoding: "utf8",
61
- timeout: 60_000,
62
- maxBuffer: GITHUB_JSON_MAX_BUFFER,
63
- });
64
- if (result.status !== 0) {
65
- if (/401|403|404|unauthorized|forbidden|not found/i.test(`${result.stdout}\n${result.stderr}`)) return fallback;
66
- throw new Error(`${label} is unavailable; publication control-plane audit fails closed`);
67
- }
68
- try {
69
- return JSON.parse(result.stdout);
70
- } catch {
71
- throw new Error(`${label} did not return JSON; publication control-plane audit fails closed`);
72
- }
72
+ return githubJsonOptional(apiPath, label, fallback, /401|403|404|unauthorized|forbidden|not found/i);
73
73
  }
74
74
 
75
75
  function rulesetIncludesBranch(ruleset, branch, defaultBranch) {
@@ -218,7 +218,7 @@ function main() {
218
218
  if (!repository || !branch) throw new Error("--repository and --branch are required");
219
219
 
220
220
  const encodedWorkflow = workflowPath.split("/").map(encodeURIComponent).join("/");
221
- const workflowFile = githubJson(
221
+ const workflowFile = githubPublicJson(
222
222
  `repos/${workflowRepository}/contents/${encodedWorkflow}${workflowRef ? `?ref=${encodeURIComponent(workflowRef)}` : ""}`,
223
223
  "publication workflow source",
224
224
  );
@@ -231,8 +231,8 @@ function main() {
231
231
  !/^\s*[a-z-]+:\s*write\s*$/m.test(workflowHeader) &&
232
232
  !/permissions\s*:\s*write-all/i.test(workflowHeader);
233
233
 
234
- const repositoryState = githubJson(`repos/${repository}`, "repository metadata");
235
- const branchState = githubJson(`repos/${repository}/branches/${encodeURIComponent(branch)}`, "branch summary");
234
+ const repositoryState = githubPublicJson(`repos/${repository}`, "repository metadata");
235
+ const branchState = githubPublicJson(`repos/${repository}/branches/${encodeURIComponent(branch)}`, "branch summary");
236
236
  const exactTransactionSource = /^[0-9a-f]{40}$/.test(sourceSha);
237
237
  const protection = exactTransactionSource
238
238
  ? null
@@ -290,7 +290,7 @@ function main() {
290
290
  if (!/^[0-9a-f]{40}$/.test(sourceSha)) {
291
291
  throw new Error("--source-sha is required when detailed branch policy is not readable");
292
292
  }
293
- const sourceCommit = githubJson(`repos/${repository}/commits/${sourceSha}`, "source commit");
293
+ const sourceCommit = githubPublicJson(`repos/${repository}/commits/${sourceSha}`, "source commit");
294
294
  const sourceParents = Array.isArray(sourceCommit.parents) ? sourceCommit.parents.map((entry) => String(entry?.sha || "").toLowerCase()) : [];
295
295
  const sourceChangedPaths = Array.isArray(sourceCommit.files) ? sourceCommit.files.map((entry) => String(entry?.filename || "")) : [];
296
296
  const sourceMessage = String(sourceCommit.commit?.message || "").split("\n", 1)[0];
@@ -305,7 +305,7 @@ function main() {
305
305
  const branchHeadSha = String(branchState.commit?.sha || "").toLowerCase();
306
306
  const sourceComparison = sourceSha === branchHeadSha
307
307
  ? null
308
- : githubJson(
308
+ : githubPublicJson(
309
309
  `repos/${repository}/compare/${sourceSha}...${branchHeadSha}`,
310
310
  "source protected-branch lineage",
311
311
  );
@@ -313,7 +313,7 @@ function main() {
313
313
  sourceComparison?.status === "ahead" &&
314
314
  String(sourceComparison?.merge_base_commit?.sha || "").toLowerCase() === sourceSha
315
315
  );
316
- let pullRequests = githubJson(`repos/${repository}/commits/${authorizationSha}/pulls`, "source pull-request lineage");
316
+ let pullRequests = githubPublicJson(`repos/${repository}/commits/${authorizationSha}/pulls`, "source pull-request lineage");
317
317
  let mergedPullRequest = (Array.isArray(pullRequests) ? pullRequests : []).find((entry) =>
318
318
  entry?.merged_at &&
319
319
  (
@@ -324,7 +324,7 @@ function main() {
324
324
  entry.head?.repo?.full_name === repository
325
325
  );
326
326
  if (!mergedPullRequest && allowReleaseReconciliation) {
327
- const packageFile = githubJson(
327
+ const packageFile = githubPublicJson(
328
328
  `repos/${repository}/contents/package.json?ref=${encodeURIComponent(sourceSha)}`,
329
329
  "release reconciliation package metadata",
330
330
  );
@@ -340,7 +340,7 @@ function main() {
340
340
  });
341
341
  if (releaseReconciliation.qualifying) {
342
342
  authorizationSha = parentSha;
343
- pullRequests = githubJson(`repos/${repository}/commits/${authorizationSha}/pulls`, "release parent pull-request lineage");
343
+ pullRequests = githubPublicJson(`repos/${repository}/commits/${authorizationSha}/pulls`, "release parent pull-request lineage");
344
344
  mergedPullRequest = (Array.isArray(pullRequests) ? pullRequests : []).find((entry) =>
345
345
  entry?.merged_at &&
346
346
  entry.merge_commit_sha === authorizationSha &&
@@ -350,7 +350,7 @@ function main() {
350
350
  }
351
351
  }
352
352
  const reviews = mergedPullRequest
353
- ? githubJson(`repos/${repository}/pulls/${mergedPullRequest.number}/reviews?per_page=100`, "source pull-request reviews")
353
+ ? githubPublicJson(`repos/${repository}/pulls/${mergedPullRequest.number}/reviews?per_page=100`, "source pull-request reviews")
354
354
  : [];
355
355
  const latestReviews = new Map();
356
356
  for (const review of Array.isArray(reviews) ? reviews : []) {
@@ -378,7 +378,7 @@ function main() {
378
378
  (prefixedRequiredStatusChecks.length === 1 ? prefixedRequiredStatusChecks[0] : requiredStatusCheck);
379
379
  const requiredStatusCheckMatchCount = exactRequiredStatusCheck ? 1 : prefixedRequiredStatusChecks.length;
380
380
  const checkRuns = /^[0-9a-f]{40}$/.test(pullRequestHeadSha)
381
- ? githubJson(`repos/${repository}/commits/${pullRequestHeadSha}/check-runs?check_name=${encodeURIComponent(resolvedRequiredStatusCheck)}&filter=latest&per_page=100`, "merged pull-request required check runs")
381
+ ? githubPublicJson(`repos/${repository}/commits/${pullRequestHeadSha}/check-runs?check_name=${encodeURIComponent(resolvedRequiredStatusCheck)}&filter=latest&per_page=100`, "merged pull-request required check runs")
382
382
  : { check_runs: [] };
383
383
  const requiredCheckSource = (requiredStatusCheckPolicy.checks || []).find((entry) =>
384
384
  entry.context === resolvedRequiredStatusCheck
@@ -8,6 +8,11 @@ export const BUILDCHAIN_USAGE = `Usage:
8
8
  buildchain architecture qualify --authority-revision <git-revision>
9
9
  [--candidate-revision <git-revision>]
10
10
  [--cwd <dir>] [--json]
11
+ buildchain adopter-delivery run --input <json-or-path> [--output <path>]
12
+ buildchain adopter-delivery verify --input <json-or-path>
13
+ --readback <json-or-path> [--output <path>]
14
+ buildchain adopter-delivery bootstrap --input <json-or-path> [--output <path>]
15
+ buildchain adopter-delivery archive --input <json-or-path> [--output <path>]
11
16
  buildchain portable-cache plan --manifest <file-or-json> [--output <file>]
12
17
  [--github-output <file>] [--json]
13
18
  buildchain portable-cache receipt --plan <file-or-json> [--matched-key <key>]
@@ -44,6 +49,23 @@ export const BUILDCHAIN_USAGE = `Usage:
44
49
  buildchain release-tail init --declaration <json-or-path> [--state <path>]
45
50
  buildchain release-tail <status|verify> [--state <path>] [--output <path>]
46
51
  buildchain release-tail compat --hooks-json <json-or-path> [--output <path>]
52
+ buildchain release-tail rehearse --capsule <absolute-path>
53
+ --candidate-root <absolute-path>
54
+ --mode <simulate|replay>
55
+ --state <absolute-path>
56
+ --evidence <absolute-path>
57
+ buildchain tail-reseal plan --request <request.json> [--output <plan.json>]
58
+ buildchain tail-reseal admit --request <request.json> [--output <admission.json>]
59
+ buildchain tail-reseal verify-platform --request <request.json> --platform <id>
60
+ [--artifact-root <dir>] [--mode retained|resealed]
61
+ [--provider-readback-root <sha256:...>]
62
+ [--output <readback.json>]
63
+ buildchain tail-reseal seal --request <request.json> --plan <plan.json>
64
+ --readbacks <dir> --passport <passport.json>
65
+ --protected-readback-root <sha256:...>
66
+ [--output <receipt.json>]
67
+ buildchain next-development materialize --input <request.json> [--cwd <dir>]
68
+ [--write] [--output <path>] [--json]
47
69
  buildchain github-governance <plan|apply|rollback|protection-policy-plan|ruleset-policy-plan> ...
48
70
  buildchain release <inspect|recover|finalize|abort> ...
49
71
  buildchain transaction inspect ...
@@ -127,11 +149,20 @@ export const BUILDCHAIN_USAGE = `Usage:
127
149
  [--from-config | --workflow <required-workflow.yml>...] [--cwd <dir>]
128
150
  [--check-response-timeout-minutes <n>]
129
151
  [--max-entries-to-build <n>] [--apply]
130
- buildchain dev warrant <submit|select|heartbeat|recover|close|cancel-queued|observe>
152
+ buildchain dev warrant <submit|select|heartbeat|qualify|recover|close|settle|cancel-queued|observe>
131
153
  --repository <owner/repo> --branch <dev/vN/vN.M>
132
154
  [--execute] [--output <file>] [--json]
133
- buildchain dev proof <source|verify-source|classify|replay|integration|verify-integration>
155
+ buildchain dev authority <migrate|submit|lease-qualification|heartbeat-qualification|complete-qualification|lease-landing|heartbeat-landing|recover|admit-merge-group|settle|observe>
156
+ --repository <owner/repo> --branch <dev/vN/vN.M>
157
+ [--environment-root <root>] [--qualification-domains <json>]
158
+ [--provider-attempt <admitted-attempt.json>]
159
+ [--execute] [--output <file>] [--json]
160
+ buildchain dev proof <source|verify-source|classify|native|verify-native|classify-native|verify-native-reuse|replay|integration|verify-integration>
134
161
  [--output <file>] [--json]
162
+ buildchain dev two-phase --repository <owner/repo> --branch <dev/vN/vN.M>
163
+ --pull-request <n> --expected-head <sha>
164
+ --warrant-result <file> --native-command <command>
165
+ [--native-proof <file>] [--output <file>] [--json]
135
166
  buildchain log <info|warn|error> --event <name> [--phase <phase>]
136
167
  [--component <name>] [--source <name>] [--attribute key=value]...
137
168
  [--path <jsonl>] [--json]
@@ -145,6 +176,14 @@ export const BUILDCHAIN_USAGE = `Usage:
145
176
  [--module-fact <file>]... [--artifact <path>]...
146
177
  [--output <file>] [--json]
147
178
  buildchain facts verify [--cwd <dir>] --fact <file> [--json]
179
+ buildchain facts compatibility project [--cwd <dir>] [--registry <json-or-path>]
180
+ [--output <file>] [--json]
181
+ buildchain facts compatibility verify [--cwd <dir>] [--registry <json-or-path>] [--json]
182
+ buildchain facts compatibility query --query <json-or-path>
183
+ [--registry <json-or-path>] [--cwd <dir>] [--json]
184
+ buildchain facts compatibility query-template --fact-root <sha256:...>
185
+ [--query-id <id>] [--registry <json-or-path>]
186
+ [--cwd <dir>] [--json]
148
187
  buildchain kfd ...
149
188
  buildchain kfd hub <init|inspect|test|explain> [--cwd <dir>]
150
189
  [--declaration <path>] [--output-dir <path>]
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import process from "node:process";
6
+ import { execFileSync } from "node:child_process";
7
+ import { evaluateCiLaneChangeBudget } from "../packages/core/ci-lane-change-budget.js";
8
+
9
+ const root = process.cwd();
10
+ const workflowRoot = path.join(root, ".github", "workflows");
11
+ const policyPath = path.join(
12
+ root,
13
+ "architecture",
14
+ "ci-lane-change-budget.json",
15
+ );
16
+
17
+ const workflows = fs
18
+ .readdirSync(workflowRoot, { withFileTypes: true })
19
+ .filter((entry) => entry.isFile() && /\.(?:yaml|yml)$/u.test(entry.name))
20
+ .map((entry) => {
21
+ const workflowPath = path.posix.join(".github/workflows", entry.name);
22
+ return {
23
+ path: workflowPath,
24
+ text: fs.readFileSync(path.join(workflowRoot, entry.name), "utf8"),
25
+ };
26
+ })
27
+ .sort((left, right) => left.path.localeCompare(right.path));
28
+
29
+ const policy = JSON.parse(fs.readFileSync(policyPath, "utf8"));
30
+ const baselineRevision = String(policy.baseline?.gitRevision || "").trim();
31
+ if (!/^[0-9a-f]{40}$/u.test(baselineRevision)) {
32
+ throw new Error("baseline.gitRevision must be an exact Git commit");
33
+ }
34
+ const embeddedBaselineLanes = policy.baseline?.lanes;
35
+ if (
36
+ !Array.isArray(embeddedBaselineLanes) ||
37
+ embeddedBaselineLanes.length === 0
38
+ ) {
39
+ throw new Error("baseline.lanes must contain the exact offline lane set");
40
+ }
41
+ const embeddedLaneSetRoot = `sha256:${crypto
42
+ .createHash("sha256")
43
+ .update(`${JSON.stringify(embeddedBaselineLanes)}\n`)
44
+ .digest("hex")}`;
45
+ if (embeddedLaneSetRoot !== policy.baseline?.laneSetRoot) {
46
+ throw new Error("baseline laneSetRoot does not match baseline.lanes");
47
+ }
48
+
49
+ let baselineWorkflows = null;
50
+ try {
51
+ execFileSync("git", ["cat-file", "-e", `${baselineRevision}^{commit}`], {
52
+ cwd: root,
53
+ stdio: "ignore",
54
+ });
55
+ const baselinePaths = execFileSync(
56
+ "git",
57
+ [
58
+ "ls-tree",
59
+ "-r",
60
+ "--name-only",
61
+ baselineRevision,
62
+ "--",
63
+ ".github/workflows",
64
+ ],
65
+ { cwd: root, encoding: "utf8" },
66
+ )
67
+ .split(/\r?\n/u)
68
+ .filter((value) => /\.(?:yaml|yml)$/u.test(value))
69
+ .sort();
70
+ baselineWorkflows = baselinePaths.map((workflowPath) => ({
71
+ path: workflowPath,
72
+ text: execFileSync("git", ["show", `${baselineRevision}:${workflowPath}`], {
73
+ cwd: root,
74
+ encoding: "utf8",
75
+ }),
76
+ }));
77
+ } catch {
78
+ // Shallow and exported source cuts may not contain the pinned commit object.
79
+ // The content-rooted embedded lane set remains the exact offline authority.
80
+ }
81
+
82
+ const embeddedEvaluation = evaluateCiLaneChangeBudget({
83
+ policy,
84
+ workflows,
85
+ });
86
+ const evaluation = baselineWorkflows
87
+ ? evaluateCiLaneChangeBudget({ policy, workflows, baselineWorkflows })
88
+ : embeddedEvaluation;
89
+ if (
90
+ baselineWorkflows &&
91
+ (evaluation.baselineLaneCount !== embeddedEvaluation.baselineLaneCount ||
92
+ JSON.stringify(evaluation.newLanes) !==
93
+ JSON.stringify(embeddedEvaluation.newLanes))
94
+ ) {
95
+ throw new Error(
96
+ "embedded baseline lanes do not match the pinned baseline revision",
97
+ );
98
+ }
99
+ process.stdout.write(`${JSON.stringify(evaluation, null, 2)}\n`);
100
+ if (!evaluation.ok) process.exitCode = 1;
@@ -54,6 +54,10 @@ const requiredPaths = [
54
54
  "packages/core/homebrew.js",
55
55
  "packages/core/artifact-verification-envelope.js",
56
56
  "packages/core/anchored-version-material.js",
57
+ "packages/core/next-development-transition.js",
58
+ "packages/core/next-development-controller.js",
59
+ "packages/core/next-development-candidate-reservation.js",
60
+ "packages/core/next-development-projection.js",
57
61
  "packages/core/build-facts.js",
58
62
  "packages/core/publication-package.js",
59
63
  "packages/core/publication-reproducibility.js",
@@ -76,6 +80,8 @@ const requiredPaths = [
76
80
  "docs/auditable-demo.md",
77
81
  "contracts/auditable-demo-scenario-v1.schema.json",
78
82
  "contracts/release-candidate-recovery-v1.schema.json",
83
+ "contracts/next-development-request-v1.schema.json",
84
+ "contracts/next-development-transition-v1.schema.json",
79
85
  "contracts/auditable-demo-media-profiles-v1.json",
80
86
  "contracts/evidence/auditable-demo-web-delivery-v1.json",
81
87
  "contracts/evidence/auditable-demo-responsive-web-delivery-v1.json",
@@ -87,10 +93,15 @@ const requiredPaths = [
87
93
  "contracts/fixtures/auditable-demo-responsive-web-delivery-v1/public-projection.json",
88
94
  "contracts/fixtures/auditable-demo-responsive-web-delivery-v1/scene.json",
89
95
  "docs/release-propagation.md",
96
+ "docs/next-development-transition.md",
90
97
  "docs/site-bundle-contract.md",
91
98
  "docs/toolkit-observability.md",
92
99
  "docs/versioning.md",
93
100
  "scripts/release-line-dry-run.mjs",
101
+ "scripts/next-development-transition.mjs",
102
+ "scripts/next-development-self-dogfood.mjs",
103
+ "scripts/next-development-self-dogfood-harness.mjs",
104
+ "scripts/generate-next-development-guidance.mjs",
94
105
  "scripts/reconcile-release-governance.mjs",
95
106
  "scripts/buildchain-channel-router.mjs",
96
107
  "scripts/generate-channel-build-workflow.mjs",
@@ -1010,16 +1021,16 @@ for (const forbiddenSnippet of [
1010
1021
  for (const requiredSnippet of [
1011
1022
  "id-token: write",
1012
1023
  "actions: write",
1013
- "uses: ./.github/workflows/.release-candidate-promote.yml",
1024
+ "uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v4-alpha",
1014
1025
  "github.event.workflow_run.event == 'push'",
1015
1026
  "!startsWith(github.event.workflow_run.display_title, 'chore(release): prepare v')",
1016
1027
  "!startsWith(github.event.workflow_run.display_title, 'chore(release): release v')",
1017
1028
  "resume-candidate-run-id:",
1018
1029
  "resume-expected-source-tree:",
1019
1030
  "resume-buildchain-runtime-sha:",
1020
- "github-release: true",
1031
+ "uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v4",
1021
1032
  "release-passport-buildchain-self-kfd: true",
1022
- 'artifact-patterns: "buildchain-package-*"',
1033
+ "buildchain-channel: alpha",
1023
1034
  "release-passport-impact-json: .buildchain/release-impact.json",
1024
1035
  ]) {
1025
1036
  if (!buildchainRefPromotionWorkflow.includes(requiredSnippet)) {
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { parseYamlUses } from "../packages/core/workflow-yaml-contract.js";
6
+
7
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
+ const read = (relative) => fs.readFileSync(path.join(root, relative), "utf8");
9
+ function fail(message) {
10
+ throw new Error(`v4-floating-consumer-policy-contract: ${message}`);
11
+ }
12
+
13
+ export function workflowJobBlock(source, job) {
14
+ return (
15
+ source.match(
16
+ new RegExp(
17
+ `^ ${job}:\\n([\\s\\S]*?)(?=^ [a-z0-9_-]+:|(?![\\s\\S]))`,
18
+ "mu",
19
+ ),
20
+ )?.[1] || ""
21
+ );
22
+ }
23
+
24
+ export function assertTrustGatedJobs(source, jobs) {
25
+ for (const job of jobs) {
26
+ const block = workflowJobBlock(source, job);
27
+ if (!block.includes("- trust-gate")) {
28
+ fail(`.build.yml job ${job} is not directly gated by trust-gate`);
29
+ }
30
+ }
31
+ }
32
+
33
+ function jobDependsOn(source, job, dependency) {
34
+ const block = workflowJobBlock(source, job);
35
+ return new RegExp(
36
+ `^ needs:\\s*(?:${dependency}|\\[[^\\]]*\\b${dependency}\\b[^\\]]*\\])\\s*$`,
37
+ "mu",
38
+ ).test(block);
39
+ }
40
+
41
+ function assertOrdered(relative, markers) {
42
+ const source = read(relative);
43
+ let cursor = -1;
44
+ for (const marker of markers) {
45
+ const next = source.indexOf(marker, cursor + 1);
46
+ if (next < 0) fail(`${relative} is missing ${marker}`);
47
+ if (next <= cursor)
48
+ fail(`${relative} does not enforce ${markers.join(" -> ")}`);
49
+ cursor = next;
50
+ }
51
+ }
52
+
53
+ export function assertPromotionCertificationWiring(source) {
54
+ for (const marker of [
55
+ "path: .buildchain/runtime/candidate-policy-runtime",
56
+ "BUILDCHAIN_EXPECTED_RUNTIME_SHA: ${{ steps.v4-policy-caller.outputs.runtime-sha }}",
57
+ "policy_runtime=.buildchain/runtime",
58
+ 'node "${policy_runtime}/scripts/v4-consumer-policy.mjs" certify',
59
+ "passport.consumerPolicy?.receipt?.caller?.sourceSha",
60
+ "path: .buildchain/runtime/policy-caller",
61
+ '--caller-root "${{ github.workspace }}/.buildchain/runtime/policy-caller"',
62
+ '--stable-lock "${{ inputs.buildchain-stable-contract-lock-path }}"',
63
+ '--alpha-lock "${{ inputs.buildchain-alpha-contract-lock-path }}"',
64
+ "release-passport-v4-consumer-policy-certification-root: ${{ steps.v4-policy-certification.outputs.v4-consumer-policy-certification-root }}",
65
+ ]) {
66
+ if (!source.includes(marker)) {
67
+ fail(`promotion certification is missing ${marker}`);
68
+ }
69
+ }
70
+ }
71
+
72
+ function assertPersistedSelectors() {
73
+ const offenders = [];
74
+ const workflows = fs
75
+ .readdirSync(path.join(root, ".github/workflows"))
76
+ .filter((name) => /\.ya?ml$/u.test(name))
77
+ .sort();
78
+ for (const name of workflows) {
79
+ const relative = `.github/workflows/${name}`;
80
+ for (const node of parseYamlUses(read(relative))) {
81
+ const match = node.value.match(
82
+ /^kungfu-systems\/buildchain\/(.+)@(.+)$/u,
83
+ );
84
+ if (!match) continue;
85
+ const selector = match[2];
86
+ const protectedBootstrap =
87
+ relative ===
88
+ ".github/workflows/buildchain-ref-promotion-recovery.yml" &&
89
+ match[1] === ".github/workflows/.release-candidate-promote.yml" &&
90
+ selector === "alpha/v4/v4.0";
91
+ const isV4 =
92
+ selector === "v4" ||
93
+ selector === "v4-alpha" ||
94
+ /^v4(?:[./-]|$)/u.test(selector) ||
95
+ /^[0-9a-f]{40}$/iu.test(selector) ||
96
+ selector.includes("${{");
97
+ if (
98
+ (isV4 || protectedBootstrap) &&
99
+ !["v4", "v4-alpha"].includes(selector) &&
100
+ !protectedBootstrap
101
+ ) {
102
+ offenders.push(`${relative}:${node.line} @${selector}`);
103
+ }
104
+ }
105
+ }
106
+ if (offenders.length)
107
+ fail(
108
+ `persisted v4 selectors must be v4 or v4-alpha: ${offenders.join(", ")}`,
109
+ );
110
+ }
111
+
112
+ export function checkV4FloatingConsumerPolicyContract() {
113
+ const policy = JSON.parse(
114
+ read("architecture/v4-floating-consumer-policy.json"),
115
+ );
116
+ if (policy.contract !== "kungfu-buildchain-v4-floating-consumer-policy/v1") {
117
+ fail("architecture policy contract is missing");
118
+ }
119
+ if (
120
+ policy.contractLocks?.selectedLockMustBindResolvedWorkflowShell !== true
121
+ ) {
122
+ fail("contract lock must bind the visible workflow shell");
123
+ }
124
+ assertPersistedSelectors();
125
+ assertOrdered(".github/workflows/.build.yml", [
126
+ "Enforce v4 floating consumer policy",
127
+ "Validate consumer package manager contract",
128
+ ]);
129
+ if (
130
+ !read(".github/workflows/.build.yml").includes(
131
+ "BUILDCHAIN_EXPECTED_INVOCATION_CHANNEL: ${{ inputs.buildchain-expected-channel }}",
132
+ )
133
+ )
134
+ fail("channel builds must disambiguate dual-channel caller invocations");
135
+ assertTrustGatedJobs(read(".github/workflows/.build.yml"), [
136
+ "resolve-source",
137
+ "resolve-contract",
138
+ "controller-plan",
139
+ "artifact-transfer",
140
+ "build-native",
141
+ "build-linux-container",
142
+ "summarize",
143
+ ]);
144
+ assertOrdered(".github/workflows/publication-artifact.yml", [
145
+ "Enforce v4 floating consumer policy",
146
+ "Resolve controller identities",
147
+ "Install Buildchain runtime dependencies",
148
+ ]);
149
+ const stageCanary = read(".github/workflows/v4-stage-capsule-canary.yml");
150
+ if (
151
+ !stageCanary.includes("consumer-admission:") ||
152
+ !stageCanary.includes("needs: consumer-admission")
153
+ ) {
154
+ fail(
155
+ "Stage Capsule qualification is not transitively gated by consumer admission",
156
+ );
157
+ }
158
+ const promotion = read(".github/workflows/release-candidate-promote.yml");
159
+ if (
160
+ !promotion.includes("consumer-admission:") ||
161
+ !jobDependsOn(promotion, "alpha", "consumer-admission") ||
162
+ !jobDependsOn(promotion, "stable", "consumer-admission")
163
+ ) {
164
+ fail(
165
+ "release candidate promotion is not transitively gated by consumer admission",
166
+ );
167
+ }
168
+ assertPromotionCertificationWiring(
169
+ read(".github/workflows/.release-candidate-promote.yml"),
170
+ );
171
+ for (const [relative, marker] of [
172
+ ["packages/core/release-candidate.js", "consumerPolicy"],
173
+ ["packages/core/release-passport.js", "v4ConsumerPolicy"],
174
+ [
175
+ "actions/promote-buildchain-ref/action.yml",
176
+ "release-passport-v4-consumer-policy-certification-json",
177
+ ],
178
+ [
179
+ "actions/promote-buildchain-ref/action.yml",
180
+ "release-passport-v4-consumer-policy-certification-root",
181
+ ],
182
+ [
183
+ ".github/workflows/.release-candidate-promote.yml",
184
+ "v4-policy-certification",
185
+ ],
186
+ ]) {
187
+ if (!read(relative).includes(marker)) {
188
+ fail(
189
+ `${relative} does not bind v4 consumer policy evidence marker ${marker}`,
190
+ );
191
+ }
192
+ }
193
+ const agents = read("AGENTS.md");
194
+ for (const invariant of [
195
+ "source-persisted exact commit SHA",
196
+ "matching stable and alpha contract locks",
197
+ "trusted non-persistent runtime input",
198
+ ]) {
199
+ if (!agents.includes(invariant))
200
+ fail(`AGENTS.md is missing invariant: ${invariant}`);
201
+ }
202
+ return { ok: true, entrypoints: policy.scope.publicWorkflowEntrypoints };
203
+ }
204
+
205
+ if (
206
+ process.argv[1] &&
207
+ path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
208
+ ) {
209
+ try {
210
+ process.stdout.write(
211
+ `${JSON.stringify(checkV4FloatingConsumerPolicyContract(), null, 2)}\n`,
212
+ );
213
+ } catch (error) {
214
+ console.error(error.message);
215
+ process.exitCode = 1;
216
+ }
217
+ }
@@ -10,9 +10,7 @@ const DEFAULT_ROOT = path.resolve(
10
10
  );
11
11
  const CALLER_PATH = ".github/workflows/v4-public-consumer-dogfood.yml";
12
12
  const REUSABLE_PATH = ".github/workflows/v4-stage-capsule-canary.yml";
13
- export const V4_PUBLIC_DOGFOOD_TRAIN_REF =
14
- "train/v4/v4.0/public-consumer-self-dogfood";
15
- const EXACT_SHA = /^[0-9a-f]{40}$/u;
13
+ export const V4_PUBLIC_DOGFOOD_ALPHA_REF = "v4-alpha";
16
14
  const PRIVATE_CONSUMER = ["buildchain", "self", "dogfood"].join("-");
17
15
  const PRIVATE_SHADOW = ["kungfu", "shadow"].join("-");
18
16
 
@@ -139,13 +137,8 @@ function assertArchitecture(root) {
139
137
  );
140
138
  const dogfood = architecture.publicConsumerDogfood;
141
139
  const validationRef = dogfood?.validationRef;
142
- if (
143
- validationRef !== V4_PUBLIC_DOGFOOD_TRAIN_REF &&
144
- !EXACT_SHA.test(String(validationRef))
145
- )
146
- fail(
147
- "architecture validationRef must be the exact public train or a protected commit SHA",
148
- );
140
+ if (validationRef !== V4_PUBLIC_DOGFOOD_ALPHA_REF)
141
+ fail("architecture validationRef must use the floating v4-alpha channel");
149
142
  const caller = read(root, CALLER_PATH);
150
143
  if (caller !== expectedV4PublicDogfoodWorkflow(validationRef))
151
144
  fail(`${CALLER_PATH} must remain the exact thin public consumer caller`);
@@ -168,7 +161,7 @@ function assertArchitecture(root) {
168
161
  dogfood.relativeOrSelfInvocationAllowed !== false ||
169
162
  dogfood.directQualificationInvocationAllowed !== false ||
170
163
  dogfood.candidateBranchOverrideAllowed !== false ||
171
- dogfood.recursionRecovery !== "public-train-ref-only"
164
+ dogfood.recursionRecovery !== "floating-selector-with-trusted-runtime-input"
172
165
  )
173
166
  fail(
174
167
  "architecture publicConsumerDogfood contract is incomplete or widened",
@@ -226,6 +219,8 @@ function assertPolicySources(root) {
226
219
  "No agent may add or restore a relative/self reusable-workflow call",
227
220
  "never solve recursion with an internal exception",
228
221
  "scripts/check-v4-public-dogfood-contract.mjs",
222
+ "source-persisted exact commit SHA",
223
+ "v4-alpha",
229
224
  ])
230
225
  if (!agents.includes(invariant))
231
226
  fail(`AGENTS.md is missing invariant: ${invariant}`);