@kungfu-tech/buildchain 4.0.2-alpha.9 → 4.0.3-alpha.0

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 (281) hide show
  1. package/AGENTS.md +14 -5
  2. package/CONTRIBUTING.md +58 -0
  3. package/README.md +3 -3
  4. package/actions/promote-buildchain-ref/README.md +1 -1
  5. package/actions/release-candidate-promote/README.md +31 -0
  6. package/architecture/agent-change-map.md +190 -126
  7. package/architecture/{v4-architecture-constitution.md → architecture-constitution.md} +33 -30
  8. package/architecture/{v3-core-mechanism-inventory.md → baseline-core-mechanism-inventory.md} +1 -1
  9. package/architecture/{v4-bootstrap-authority.json → bootstrap-authority.json} +3 -3
  10. package/architecture/{v3-v4-capability-closure.md → capability-parity-closure.md} +3 -3
  11. package/architecture/{v3-v4-live-capability-inventory.json → capability-parity-live-capability-inventory.json} +4 -4
  12. package/architecture/{v4-capability-state-machine-manifest.json → capability-state-machine-manifest.json} +2 -2
  13. package/architecture/{v4-capability-state-machine-manifest.schema.json → capability-state-machine-manifest.schema.json} +2 -2
  14. package/architecture/ci-lane-change-budget.json +384 -0
  15. package/architecture/{v4-cross-platform-adopter-qualification.json → cross-platform-adopter-qualification.json} +4 -4
  16. package/architecture/{v4-delivery-authority-parity.json → delivery-authority-parity.json} +17 -17
  17. package/architecture/{v4-delivery-warrant-shadow-bootstrap-plan.json → delivery-warrant-shadow-bootstrap-plan.json} +4 -4
  18. package/architecture/{v4-delivery-warrant-shadow-bootstrap-plan.md → delivery-warrant-shadow-bootstrap-plan.md} +7 -7
  19. package/architecture/{v4-floating-consumer-policy.json → floating-consumer-policy.json} +2 -2
  20. package/architecture/{v4-floating-consumer-policy.md → floating-consumer-policy.md} +1 -1
  21. package/architecture/implementation-naming.json +1273 -0
  22. package/architecture/internal-capabilities.json +201 -154
  23. package/architecture/maintainability-debt.json +205 -146
  24. package/architecture/maintainability-policy.json +37 -33
  25. package/architecture/{v4-next-development-parity.json → next-development-parity.json} +12 -12
  26. package/architecture/{v4-partial-mutation-recovery-qualification.json → partial-mutation-recovery-qualification.json} +8 -5
  27. package/architecture/{v4-platform-stage-checkpoints.json → platform-stage-checkpoints.json} +3 -3
  28. package/architecture/{v4-provider-operation-journal-contract.json → provider-operation-journal-contract.json} +10 -7
  29. package/architecture/publication-facade-patches.json +64 -0
  30. package/architecture/{v4-publication-rehearsal-parity.json → publication-rehearsal-parity.json} +4 -4
  31. package/architecture/{v4-release-activation-shadow-domain.json → release-activation-shadow-domain.json} +9 -6
  32. package/architecture/release-tail-contract-inventory.json +6 -6
  33. package/architecture/{v4-release-topology.json → release-topology.json} +163 -72
  34. package/architecture/{v4-release-train-parity.json → release-train-parity.json} +2 -2
  35. package/architecture/{v4-runtime-ref-resume-authority.json → runtime-ref-resume-authority.json} +1 -1
  36. package/architecture/{v4-runtime-semantic-closure.json → runtime-semantic-closure.json} +2 -2
  37. package/architecture/{v4-rust-libnode-bridge-evaluation.json → rust-libnode-bridge-evaluation.json} +3 -3
  38. package/architecture/{v4-rust-libnode-bridge-spike.md → rust-libnode-bridge-spike.md} +6 -6
  39. package/architecture/rust-wasm-production-authority.json +58 -0
  40. package/architecture/{v4-stable-publication-fence.json → stable-publication-fence.json} +7 -4
  41. package/architecture/{v4-stage-capsule-contract.json → stage-capsule-contract.json} +1 -1
  42. package/architecture/{v4-stage-capsule-qualification.json → stage-capsule-qualification.json} +8 -8
  43. package/architecture/{v4-stage-capsule-resume-planner.json → stage-capsule-resume-planner.json} +2 -2
  44. package/architecture/{v4-stage-capsule-store-contract.json → stage-capsule-store-contract.json} +4 -4
  45. package/architecture/{v4-tail-reseal-parity.json → tail-reseal-parity.json} +35 -33
  46. package/architecture/universal-workflow-bootstrap.json +176 -0
  47. package/architecture/universal-workflow-fault-campaign.json +72 -0
  48. package/architecture/universal-workflow-train-admission.json +40 -0
  49. package/architecture/workflow-taxonomy.json +1462 -0
  50. package/bin/buildchain.mjs +10 -10
  51. package/bin/internal/adopter-delivery-cli.mjs +9 -9
  52. package/contracts/buildchain-v2-residuals-v1.json +57 -48
  53. package/contracts/dev-delivery-authority-v2.schema.json +16 -2
  54. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  55. package/contracts/v4-canonical-contracts-v1.schema.json +1 -0
  56. package/crates/{buildchain-v4-bridge → buildchain-host-bridge}/Cargo.lock +1 -1
  57. package/crates/{buildchain-v4-bridge → buildchain-host-bridge}/Cargo.toml +1 -1
  58. package/crates/{buildchain-v4-bridge → buildchain-host-bridge}/src/main.rs +3 -3
  59. package/dist/site/buildchain-contract.json +98 -53
  60. package/dist/site/buildchain-site.json +351 -102
  61. package/dist/site/capability-registry.json +7 -7
  62. package/dist/site/controller-registry.json +50 -13
  63. package/dist/site/kfd-claims.json +2773 -1052
  64. package/dist/site/kfd-upstream-aggregate.json +1 -1
  65. package/dist/site/manual-registry.json +14 -14
  66. package/dist/site/node-api-registry.json +13176 -10733
  67. package/dist/site/page-registry.json +332 -83
  68. package/dist/site/public-surface-audit.json +3227 -1222
  69. package/dist/site/publication-authority-registry.json +1135 -363
  70. package/dist/site/publication-registry.json +4 -4
  71. package/dist/site/release-provenance.json +18 -9
  72. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +16 -2
  73. package/dist/site/site-manifest.json +18 -18
  74. package/dist/site/workflow-registry.json +5367 -1931
  75. package/docs/MAP.md +5 -3
  76. package/docs/auditable-demo.md +1 -1
  77. package/docs/cli.md +21 -8
  78. package/docs/dev-delivery-warrant.md +1 -1
  79. package/docs/engineering-housekeeper.md +3 -3
  80. package/docs/kfd-support.md +26 -2
  81. package/docs/next-development-transition.md +26 -3
  82. package/docs/node-api-reference.md +618 -358
  83. package/docs/oci-publication.md +173 -0
  84. package/docs/publication-authority.md +2 -2
  85. package/docs/publish-transaction.md +36 -3
  86. package/docs/release-candidate.md +3 -3
  87. package/docs/release-flow.md +67 -19
  88. package/docs/release-governance.md +8 -8
  89. package/docs/release-passport.md +27 -2
  90. package/docs/reusable-build-surface.md +17 -1
  91. package/docs/v4-adopter-delivery.md +4 -4
  92. package/docs/v4-canonical-contracts.md +7 -7
  93. package/docs/v4-delivery-warrant-read-candidate.md +3 -3
  94. package/docs/v4-delivery-warrant-semantic-diff.md +3 -3
  95. package/docs/v4-production-release.md +77 -8
  96. package/docs/v4-publication-rehearsal.md +3 -3
  97. package/docs/v4-runtime-ref-resume-authority.md +2 -2
  98. package/docs/v4-rust-wasm-production-authority.md +58 -0
  99. package/docs/v4-stage-capsule.md +9 -9
  100. package/docs/v4-tail-reseal.md +2 -2
  101. package/docs/workflow-catalog.md +155 -0
  102. package/docs/workflow-path-migration.md +109 -0
  103. package/package.json +39 -24
  104. package/packages/compatibility/adopter-delivery.js +16 -0
  105. package/packages/compatibility/cross-platform-adopter-qualification.js +12 -0
  106. package/packages/compatibility/floating-consumer-policy.js +14 -0
  107. package/packages/compatibility/publication-qualification.js +11 -0
  108. package/packages/compatibility/publication-rehearsal.js +18 -0
  109. package/packages/compatibility/runtime-ref-resume-authority.js +13 -0
  110. package/packages/compatibility/tail-reseal-receipt.js +7 -0
  111. package/packages/compatibility/tail-reseal.js +11 -0
  112. package/packages/core/{v4-adopter-delivery-parity.js → adopter-delivery-parity.js} +12 -12
  113. package/packages/core/{v4-adopter-delivery.js → adopter-delivery.js} +24 -24
  114. package/packages/core/artifact-verification-envelope.js +5 -5
  115. package/packages/core/buildchain-contract.js +6 -6
  116. package/packages/core/buildchain-domain.wasm +0 -0
  117. package/packages/core/buildchain-kfd-claims.js +2 -2
  118. package/packages/core/buildchain-publication-authority.js +43 -26
  119. package/packages/core/{v4-canonical-contracts.js → canonical-contracts.js} +39 -103
  120. package/packages/core/ci-lane-change-budget.js +14 -1
  121. package/packages/core/controller-evidence.js +4 -1
  122. package/packages/core/{v4-cross-platform-adopter-qualification.js → cross-platform-adopter-qualification.js} +19 -19
  123. package/packages/core/{v4-delivery-warrant-fixture-runner.js → delivery-warrant-fixture-runner.js} +36 -32
  124. package/packages/core/{v4-delivery-warrant-read-candidate.js → delivery-warrant-read-candidate.js} +40 -40
  125. package/packages/core/{v4-delivery-warrant-semantic-diff-gate.js → delivery-warrant-semantic-diff-gate.js} +58 -59
  126. package/packages/core/{v4-delivery-warrant-shadow-adapter.js → delivery-warrant-shadow-adapter.js} +15 -15
  127. package/packages/core/dev-delivery-candidate-identity.js +40 -6
  128. package/packages/core/dev-delivery-execution-transfer.js +6 -6
  129. package/packages/core/dev-delivery-native-proof.js +12 -8
  130. package/packages/core/dev-delivery-warrant-legacy-recovery.js +1 -1
  131. package/packages/core/dev-delivery-warrant-native-compatibility.js +4 -4
  132. package/packages/core/dev-delivery-warrant-state.js +14 -15
  133. package/packages/core/dev-delivery-warrant.js +15 -5
  134. package/packages/core/domain-wasm-artifact.js +7 -0
  135. package/packages/core/domain-wasm.js +216 -0
  136. package/packages/core/{v4-floating-consumer-evidence.js → floating-consumer-evidence.js} +14 -14
  137. package/packages/core/{v4-floating-consumer-policy.js → floating-consumer-policy.js} +28 -30
  138. package/packages/core/{v4-floating-consumer-release-passport.js → floating-consumer-release-passport.js} +25 -25
  139. package/packages/core/github-governance-authority.js +4 -4
  140. package/packages/core/index.js +4 -8
  141. package/packages/core/kfd-adopter-manifest.js +46 -47
  142. package/packages/core/next-development-projection.js +26 -3
  143. package/packages/core/next-development-transition.js +5 -6
  144. package/packages/core/oci-compose-qualification.js +143 -0
  145. package/packages/core/oci-publication-bundle.js +281 -0
  146. package/packages/core/oci-publication-graph.js +206 -0
  147. package/packages/core/paper-agent-entry.js +30 -4
  148. package/packages/core/paper-fleet.js +1 -1
  149. package/packages/core/paper-runtime-channels.js +348 -0
  150. package/packages/core/paper-scaffold-content.js +10 -0
  151. package/packages/core/paper.js +37 -71
  152. package/packages/core/partial-mutation-recovery-qualification.js +18 -0
  153. package/packages/core/{v4-platform-stage-checkpoints.js → platform-stage-checkpoints.js} +37 -37
  154. package/packages/core/product-publication.js +59 -0
  155. package/packages/core/{v4-protected-publication-source.js → protected-publication-source.js} +22 -24
  156. package/packages/core/provider-operation-journal.js +62 -0
  157. package/packages/core/{v4-provider-readback-idempotency.js → provider-readback-idempotency.js} +40 -125
  158. package/packages/core/public-export-compatibility.js +33 -0
  159. package/packages/core/publication-development.js +72 -0
  160. package/packages/core/{v4-publication-qualification.js → publication-qualification.js} +17 -17
  161. package/packages/core/{v4-publication-rehearsal-capsule.js → publication-rehearsal-capsule.js} +23 -23
  162. package/packages/core/publication-rehearsal-projection.js +2 -2
  163. package/packages/core/{v4-publication-rehearsal-provider-bindings.js → publication-rehearsal-provider-bindings.js} +4 -4
  164. package/packages/core/publication-rehearsal-runtime.js +21 -21
  165. package/packages/core/{v4-publication-rehearsal.js → publication-rehearsal.js} +33 -33
  166. package/packages/core/publication-sealed-bundle.js +78 -13
  167. package/packages/core/release-activation-shadow.js +32 -0
  168. package/packages/core/release-candidate.js +6 -6
  169. package/packages/core/release-invocation.js +64 -0
  170. package/packages/core/release-line-dry-run.js +1 -1
  171. package/packages/core/release-passport-contract.js +5 -5
  172. package/packages/core/release-passport.js +41 -40
  173. package/packages/core/release-tail-product-capabilities.js +9 -0
  174. package/packages/core/release-tail-provider-plane.js +68 -1048
  175. package/packages/core/{v4-runtime-ref-resume-authority.js → runtime-ref-resume-authority.js} +36 -40
  176. package/packages/core/{v4-runtime-selector-persistence.js → runtime-selector-persistence.js} +7 -7
  177. package/packages/core/stable-publication-fence.js +28 -0
  178. package/packages/core/{v4-stage-capsule-local-store.js → stage-capsule-local-store.js} +56 -54
  179. package/packages/core/{v4-stage-capsule-qualification-campaign.js → stage-capsule-qualification-campaign.js} +37 -38
  180. package/packages/core/{v4-stage-capsule-qualification.js → stage-capsule-qualification.js} +28 -29
  181. package/packages/core/{v4-stage-capsule-resume-planner.js → stage-capsule-resume-planner.js} +29 -29
  182. package/packages/core/{v4-stage-capsule-store.js → stage-capsule-store.js} +59 -56
  183. package/packages/core/{v4-stage-capsule.js → stage-capsule.js} +45 -45
  184. package/packages/core/{v4-tail-reseal-contract.js → tail-reseal-contract.js} +6 -6
  185. package/packages/core/{v4-tail-reseal-github.js → tail-reseal-github.js} +12 -12
  186. package/packages/core/{v4-tail-reseal-receipt.js → tail-reseal-receipt.js} +22 -22
  187. package/packages/core/{v4-tail-reseal.js → tail-reseal.js} +29 -29
  188. package/packages/core/universal-workflow-bootstrap.js +586 -0
  189. package/scripts/{v4-architecture.mjs → architecture.mjs} +6 -6
  190. package/scripts/binary-publication-evidence.mjs +135 -0
  191. package/scripts/{v4-bridge-bootstrap.mjs → bridge-bootstrap.mjs} +14 -18
  192. package/scripts/{v4-bridge-evidence.mjs → bridge-evidence.mjs} +8 -8
  193. package/scripts/build-contract-core.mjs +1 -14
  194. package/scripts/build-domain-wasm.mjs +177 -0
  195. package/scripts/{v3-v4-capability-catalog.mjs → capability-parity-catalog.mjs} +19 -19
  196. package/scripts/check-action-bundles.mjs +12 -2
  197. package/scripts/{check-v3-v4-capability-inventory.mjs → check-capability-parity-inventory.mjs} +42 -42
  198. package/scripts/check-ci-lane-change-budget.mjs +4 -1
  199. package/scripts/check-core-mechanism-inventory.mjs +8 -4
  200. package/scripts/{check-v4-floating-consumer-policy-contract.mjs → check-floating-consumer-policy-contract.mjs} +10 -10
  201. package/scripts/check-implementation-naming.mjs +135 -0
  202. package/scripts/check-inventory.mjs +39 -47
  203. package/scripts/check-maintainability.mjs +25 -115
  204. package/scripts/{check-v4-public-dogfood-contract.mjs → check-public-dogfood-contract.mjs} +37 -24
  205. package/scripts/{check-v4-release-topology.mjs → check-release-topology.mjs} +131 -52
  206. package/scripts/{check-v4-runtime-semantic-closure.mjs → check-runtime-semantic-closure.mjs} +23 -17
  207. package/scripts/check-universal-workflow-bootstrap.mjs +266 -0
  208. package/scripts/check-workflow-taxonomy.mjs +6 -0
  209. package/scripts/{v4-consumer-policy.mjs → consumer-policy.mjs} +15 -15
  210. package/scripts/copy-domain-wasm.mjs +14 -0
  211. package/scripts/{v4-cross-platform-adopter-qualification.mjs → cross-platform-adopter-qualification.mjs} +9 -9
  212. package/scripts/{v4-declarative-promotion-admission.mjs → declarative-promotion-admission.mjs} +4 -4
  213. package/scripts/dev-delivery-authority-command-adapters.mjs +1 -1
  214. package/scripts/dev-delivery-authority.mjs +8 -8
  215. package/scripts/dev-delivery-source-proof-reuse.mjs +2 -1
  216. package/scripts/dev-delivery-warrant-options.mjs +15 -10
  217. package/scripts/dev-delivery-warrant-store.mjs +98 -13
  218. package/scripts/dev-delivery-warrant-transition.mjs +109 -0
  219. package/scripts/dev-delivery-warrant.mjs +80 -103
  220. package/scripts/dev-pr-auto-merge.mjs +15 -15
  221. package/scripts/dev-pr-delivery-warrant.mjs +1 -1
  222. package/scripts/generate-channel-build-workflow.mjs +7 -2
  223. package/scripts/generate-channel-promotion-workflow.mjs +19 -34
  224. package/scripts/generate-release-candidate-passport.mjs +23 -23
  225. package/scripts/generate-site-bundle.mjs +28 -28
  226. package/scripts/generate-universal-workflow-facades.mjs +498 -0
  227. package/scripts/generate-workflow-taxonomy.mjs +25 -0
  228. package/scripts/github-output.mjs +31 -0
  229. package/scripts/{v4-host-adapter.mjs → host-adapter.mjs} +1 -1
  230. package/scripts/implementation-references.mjs +25 -0
  231. package/scripts/init-repo.mjs +1 -1
  232. package/scripts/maintainability-metrics.mjs +16 -5
  233. package/scripts/maintainability-public-surface.mjs +115 -0
  234. package/scripts/next-development-review.mjs +313 -0
  235. package/scripts/oci-compose-preview.mjs +261 -0
  236. package/scripts/paper-work-fleet-cli.mjs +2 -0
  237. package/scripts/{v4-platform-stage-checkpoint-rehearsal.mjs → platform-stage-checkpoint-rehearsal.mjs} +17 -17
  238. package/scripts/{v4-product-publication-intent.mjs → product-publication-intent.mjs} +28 -13
  239. package/scripts/publication-candidate-kind.mjs +93 -0
  240. package/scripts/publication-candidate-sealer.mjs +16 -1
  241. package/scripts/{v4-publication-rehearsal-fixture.mjs → publication-rehearsal-fixture.mjs} +5 -5
  242. package/scripts/publication-settlement.mjs +211 -0
  243. package/scripts/release-asset-client.mjs +91 -0
  244. package/scripts/{v4-release-candidate-adapter.mjs → release-candidate-adapter.mjs} +4 -4
  245. package/scripts/release-candidate-resolver.mjs +24 -35
  246. package/scripts/release-tail.mjs +5 -5
  247. package/scripts/resume-from-candidate-run.mjs +35 -42
  248. package/scripts/site-capability-metadata.mjs +9 -0
  249. package/scripts/source-verification-evidence.mjs +344 -0
  250. package/scripts/stable-candidate-qualification.mjs +1 -1
  251. package/scripts/{v4-stage-capsule-qualification.mjs → stage-capsule-qualification.mjs} +62 -62
  252. package/scripts/{v4-stage-capsule-resume-rehearsal.mjs → stage-capsule-resume-rehearsal.mjs} +6 -6
  253. package/scripts/{v4-tail-reseal-macos-rehearsal.mjs → tail-reseal-macos-rehearsal.mjs} +9 -9
  254. package/scripts/{v4-tail-reseal.mjs → tail-reseal.mjs} +19 -19
  255. package/scripts/universal-facade-maintainability.mjs +91 -0
  256. package/scripts/universal-workflow-backflow.mjs +212 -0
  257. package/scripts/universal-workflow-engine.mjs +620 -0
  258. package/scripts/universal-workflow-self-dogfood.mjs +205 -0
  259. package/scripts/verify-version-state-delta.mjs +175 -0
  260. package/scripts/{v4-warrant-shadow-plan.mjs → warrant-shadow-plan.mjs} +6 -6
  261. package/scripts/workflow-taxonomy.mjs +507 -0
  262. package/templates/native-dev-delivery.yml +4 -7
  263. package/templates/universal-buildchain-bootstrap-recovery.yml +282 -0
  264. package/templates/universal-buildchain-bootstrap.yml +28 -0
  265. package/actions/v4-release-candidate-promote/README.md +0 -3
  266. package/packages/core/v4-partial-mutation-recovery-qualification.js +0 -395
  267. package/packages/core/v4-product-publication.js +0 -387
  268. package/packages/core/v4-provider-operation-journal.js +0 -531
  269. package/packages/core/v4-release-activation-shadow.js +0 -519
  270. package/packages/core/v4-release-invocation.js +0 -425
  271. package/packages/core/v4-stable-publication-fence.js +0 -357
  272. /package/architecture/{v4-adopter-delivery-parity.json → adopter-delivery-parity.json} +0 -0
  273. /package/architecture/{v3-core-mechanism-inventory.json → baseline-core-mechanism-inventory.json} +0 -0
  274. /package/architecture/{v4-canonical-contract-fixtures.json → canonical-contract-fixtures.json} +0 -0
  275. /package/architecture/{v4-compatibility-facts-parity.json → compatibility-facts-parity.json} +0 -0
  276. /package/architecture/{v4-delivery-warrant-shadow-bootstrap-plan.schema.json → delivery-warrant-shadow-bootstrap-plan.schema.json} +0 -0
  277. /package/architecture/{v4-delivery-warrant-shadow-fixtures.json → delivery-warrant-shadow-fixtures.json} +0 -0
  278. /package/architecture/{v4-delivery-warrant-shadow-fixtures.schema.json → delivery-warrant-shadow-fixtures.schema.json} +0 -0
  279. /package/architecture/{v4-exception-ledger.json → exception-ledger.json} +0 -0
  280. /package/architecture/{v4-exception-ledger.schema.json → exception-ledger.schema.json} +0 -0
  281. /package/architecture/{v4-release-invocation-fixtures.json → release-invocation-fixtures.json} +0 -0
@@ -0,0 +1,115 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { execFileSync } from "node:child_process";
4
+ import {
5
+ isGeneratedFacadePublicTransition,
6
+ loadUniversalFacadeMigration,
7
+ } from "./universal-facade-maintainability.mjs";
8
+
9
+ function readJson(root, file) {
10
+ return JSON.parse(fs.readFileSync(path.join(root, file), "utf8"));
11
+ }
12
+
13
+ function readJsonAtRevision(root, revision, file) {
14
+ return JSON.parse(
15
+ execFileSync("git", ["show", `${revision}:${file}`], {
16
+ cwd: root,
17
+ encoding: "utf8",
18
+ }),
19
+ );
20
+ }
21
+
22
+ function publicSurfaceContract(entry, kind) {
23
+ if (kind === "cli") return { id: entry.id, usage: entry.usage };
24
+ if (kind === "node")
25
+ return {
26
+ export: entry.export,
27
+ specifier: entry.specifier,
28
+ target: entry.target,
29
+ };
30
+ return {
31
+ id: entry.id,
32
+ path: entry.path,
33
+ reusable: entry.reusable,
34
+ inputs: entry.inputs || [],
35
+ secrets: entry.secrets || [],
36
+ outputs: entry.outputs || [],
37
+ };
38
+ }
39
+
40
+ function evaluatePublicSurface({
41
+ root,
42
+ revision,
43
+ policy,
44
+ migration = loadUniversalFacadeMigration(root),
45
+ }) {
46
+ const issues = [];
47
+ const capabilityGroups = new Set(
48
+ readJson(root, "dist/site/capability-registry.json").groups.map(
49
+ (entry) => entry.id,
50
+ ),
51
+ );
52
+ const definitions = [
53
+ ["dist/site/cli-registry.json", "commands", "cli", "id"],
54
+ ["dist/site/node-api-registry.json", "exports", "node", "export"],
55
+ ["dist/site/workflow-registry.json", "workflows", "workflow", "id"],
56
+ ["dist/site/workflow-registry.json", "actions", "action", "id"],
57
+ ];
58
+ for (const [file, collection, kind, key] of definitions) {
59
+ const current = readJson(root, file)[collection] || [];
60
+ const baseline = readJsonAtRevision(root, revision, file)[collection] || [];
61
+ const previousByKey = new Map(baseline.map((entry) => [entry[key], entry]));
62
+ for (const entry of current) {
63
+ const label = `${kind}:${entry[key]}`;
64
+ for (const field of policy.publicSurfacePolicy.requiredLifecycleFields) {
65
+ if (
66
+ !Object.prototype.hasOwnProperty.call(entry, field) ||
67
+ typeof entry[field] !== "string"
68
+ )
69
+ issues.push(`${label}: lifecycle field ${field} is missing`);
70
+ }
71
+ if (!capabilityGroups.has(entry.capabilityGroup))
72
+ issues.push(
73
+ `${label}: capability group ${entry.capabilityGroup || "<empty>"} is not registered`,
74
+ );
75
+ const previous = previousByKey.get(entry[key]);
76
+ const currentContract = publicSurfaceContract(entry, kind);
77
+ if (
78
+ previous &&
79
+ JSON.stringify(currentContract) !==
80
+ JSON.stringify(publicSurfaceContract(previous, kind))
81
+ ) {
82
+ const approval = policy.approvedPublicSurfaceTransitions?.[label];
83
+ const approvedContract =
84
+ approval?.fromRevision === revision &&
85
+ String(approval?.rationale || "").trim()
86
+ ? approval.contract
87
+ : null;
88
+ const generated = isGeneratedFacadePublicTransition({
89
+ entry,
90
+ kind,
91
+ migration,
92
+ baseContract:
93
+ approvedContract || publicSurfaceContract(previous, kind),
94
+ currentContract,
95
+ });
96
+ if (
97
+ !generated &&
98
+ (!approvedContract ||
99
+ JSON.stringify(approvedContract) !==
100
+ JSON.stringify(currentContract))
101
+ )
102
+ issues.push(
103
+ `${label}: existing public contract drifted from ${revision}`,
104
+ );
105
+ }
106
+ if (!previous && !entry.nonDuplicationRationale)
107
+ issues.push(
108
+ `${label}: new public surface requires a non-duplication rationale`,
109
+ );
110
+ }
111
+ }
112
+ return issues;
113
+ }
114
+
115
+ export { evaluatePublicSurface, publicSurfaceContract };
@@ -0,0 +1,313 @@
1
+ import { nextPatchDevelopmentVersion } from "../packages/core/publication-development.js";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { execFileSync } from "node:child_process";
5
+ import { pathToFileURL } from "node:url";
6
+ import { verifyVersionStateDelta } from "./verify-version-state-delta.mjs";
7
+ import { readBinaryPublicationEvidence } from "./binary-publication-evidence.mjs";
8
+ import { releaseAssetClient } from "./release-asset-client.mjs";
9
+ import { enqueueNextDevelopmentPullRequest } from "../actions/release-candidate-promote/next-development-queue.js";
10
+
11
+ const REPOSITORY = "kungfu-systems/buildchain";
12
+ const WORKFLOW = ".github/workflows/self-build-verify.yml";
13
+ const REVIEWER = "kungfu-origin";
14
+ const SHA = /^[a-f0-9]{40}$/u;
15
+ const BRANCH =
16
+ /^chore\/next-development\/((?:0|[1-9]\d*)\.\d+\.\d+-alpha\.\d+)-[a-f0-9]{16}$/u;
17
+
18
+ export function assertReviewRun(run, { repository, runId, headSha } = {}) {
19
+ if (
20
+ repository !== REPOSITORY ||
21
+ run.repository?.full_name !== repository ||
22
+ String(run.id) !== String(runId) ||
23
+ run.path !== WORKFLOW ||
24
+ run.name !== "Verify" ||
25
+ run.event !== "pull_request" ||
26
+ run.status !== "completed" ||
27
+ run.conclusion !== "success" ||
28
+ !SHA.test(run.head_sha || "") ||
29
+ !BRANCH.test(run.head_branch || "") ||
30
+ (headSha && run.head_sha !== headSha)
31
+ )
32
+ throw new Error("unqualified next-development verification run");
33
+ }
34
+
35
+ export function assertReviewPull(
36
+ pull,
37
+ { repository, headSha, baseSha, branch },
38
+ ) {
39
+ if (
40
+ repository !== REPOSITORY ||
41
+ pull.head?.repo?.full_name !== repository ||
42
+ pull.base?.repo?.full_name !== repository ||
43
+ pull.head?.sha !== headSha ||
44
+ pull.base?.sha !== baseSha ||
45
+ pull.head?.ref !== branch ||
46
+ !BRANCH.test(branch || "") ||
47
+ pull.base?.ref !== `dev/v${BRANCH.exec(branch)?.[1].split(".")[0]}/v${BRANCH.exec(branch)?.[1].split(".").slice(0, 2).join(".")}` ||
48
+ pull.state !== "open" ||
49
+ pull.draft ||
50
+ pull.merged_at ||
51
+ !SHA.test(baseSha || "") ||
52
+ !SHA.test(headSha || "") ||
53
+ pull.user?.login === REVIEWER
54
+ )
55
+ throw new Error(
56
+ "next-development review identity or protected base changed",
57
+ );
58
+ }
59
+
60
+ function observe(client, repository, runId, expected = {}) {
61
+ const prefix = `repos/${repository}`;
62
+ const run = client.json(`${prefix}/actions/runs/${runId}`);
63
+ assertReviewRun(run, { repository, runId, headSha: expected.headSha });
64
+ const associated = client.pages(`${prefix}/commits/${run.head_sha}/pulls`);
65
+ const matches = associated.filter(
66
+ (pull) =>
67
+ pull.head?.sha === run.head_sha &&
68
+ pull.head?.ref === run.head_branch &&
69
+ pull.head?.repo?.full_name === repository &&
70
+ pull.state === "open",
71
+ );
72
+ if (matches.length !== 1) throw new Error("ambiguous next-development PR");
73
+ const pull = client.json(`${prefix}/pulls/${matches[0].number}`);
74
+ const baseSha = client.json(`${prefix}/git/ref/heads/${pull.base.ref}`).object
75
+ .sha;
76
+ assertReviewPull(pull, {
77
+ repository,
78
+ headSha: run.head_sha,
79
+ baseSha,
80
+ branch: run.head_branch,
81
+ });
82
+ if (
83
+ expected.baseSha &&
84
+ (baseSha !== expected.baseSha || pull.number !== expected.number)
85
+ )
86
+ throw new Error("next-development review plan became stale");
87
+ const checks = client.pages(
88
+ `${prefix}/actions/runs/${runId}/jobs?filter=latest`,
89
+ "jobs",
90
+ );
91
+ const check = checks.filter((job) => job.name === "check");
92
+ if (
93
+ check.length !== 1 ||
94
+ check[0].status !== "completed" ||
95
+ check[0].conclusion !== "success"
96
+ )
97
+ throw new Error("exact Verify check did not succeed");
98
+ return { run, pull, baseSha };
99
+ }
100
+
101
+ export async function verifyNextDevelopmentReview({
102
+ client,
103
+ repository,
104
+ runId,
105
+ git,
106
+ verifyDelta,
107
+ publication,
108
+ }) {
109
+ const { run, pull, baseSha } = observe(client, repository, runId);
110
+ if (git("rev-parse", "HEAD") !== baseSha)
111
+ throw new Error(
112
+ "review runtime is not the exact protected development base",
113
+ );
114
+ git("fetch", "--no-tags", "origin", run.head_sha);
115
+ const parents = git("show", "-s", "--format=%P", run.head_sha).split(" ");
116
+ if (parents.length !== 1 || parents[0] !== baseSha)
117
+ throw new Error("next-development must have one exact protected parent");
118
+ const version = JSON.parse(git("show", `${baseSha}:package.json`)).version;
119
+ const targetVersion = BRANCH.exec(run.head_branch)[1];
120
+ const stable = targetVersion.endsWith("-alpha.0");
121
+ const completedStableVersion = version.split("-alpha.")[0];
122
+ if (stable && nextPatchDevelopmentVersion(completedStableVersion) !== targetVersion)
123
+ throw new Error("development target is not the completed stable patch successor");
124
+ const tag = `v${stable ? completedStableVersion : version}`;
125
+ const sourceSha = client.json(`repos/${repository}/commits/${tag}`).sha;
126
+ const settlement = await publication({ repository, tag, sourceSha });
127
+ const publishedTree = settlement.documents.passport.source.treeHash;
128
+ if (!stable && publishedTree !== git("rev-parse", `${baseSha}^{tree}`))
129
+ throw new Error("development base is not the completed alpha source tree");
130
+ if (stable && (settlement.documents.passport.release?.version !== completedStableVersion || settlement.documents.invocation?.target?.channel !== "stable"))
131
+ throw new Error("stable publication evidence differs from the completed version");
132
+ const projection = verifyDelta({ baseSha, headSha: run.head_sha, ...(stable ? { completedStableVersion } : {}) });
133
+ if (targetVersion !== projection.version)
134
+ throw new Error("next-development branch version differs from regenerated version");
135
+ observe(client, repository, runId, {
136
+ headSha: run.head_sha,
137
+ baseSha,
138
+ number: pull.number,
139
+ });
140
+ return {
141
+ schema: "buildchain.next-development-review/v1",
142
+ repository,
143
+ runId,
144
+ number: pull.number,
145
+ headSha: run.head_sha,
146
+ baseSha,
147
+ branch: run.head_branch,
148
+ publicationReceiptRoot: settlement.documents.receipt.receiptRoot,
149
+ projection,
150
+ };
151
+ }
152
+
153
+ export async function approveNextDevelopment({ client, reviewer, plan }) {
154
+ if (plan.schema !== "buildchain.next-development-review/v1")
155
+ throw new Error("missing verified next-development plan");
156
+ const { pull } = observe(client, plan.repository, plan.runId, plan);
157
+ const identity = reviewer.json("user");
158
+ if (identity.login !== REVIEWER || identity.login === pull.user?.login)
159
+ throw new Error(
160
+ "next-development requires the independent CODEOWNER identity",
161
+ );
162
+ const endpoint = `repos/${plan.repository}/pulls/${plan.number}/reviews`;
163
+ const approved = (review) =>
164
+ review.user?.login === REVIEWER &&
165
+ review.commit_id === plan.headSha &&
166
+ review.state === "APPROVED";
167
+ const reviews = client.pages(endpoint);
168
+ const latest = reviews
169
+ .filter(
170
+ (review) =>
171
+ review.user?.login === REVIEWER &&
172
+ ["APPROVED", "CHANGES_REQUESTED", "DISMISSED"].includes(review.state),
173
+ )
174
+ .at(-1);
175
+ if (latest?.state === "CHANGES_REQUESTED")
176
+ throw new Error(
177
+ "independent reviewer requested changes; automation cannot override it",
178
+ );
179
+ if (!latest || !approved(latest)) {
180
+ observe(client, plan.repository, plan.runId, plan);
181
+ await reviewer.post(endpoint, {
182
+ event: "APPROVE",
183
+ commit_id: plan.headSha,
184
+ body: `Verified the exact version-only transition by regenerating all tracked bytes from protected base ${plan.baseSha}. Completed publication receipt: ${plan.publicationReceiptRoot}. Verification run: https://github.com/${plan.repository}/actions/runs/${plan.runId}.`,
185
+ });
186
+ }
187
+ observe(client, plan.repository, plan.runId, plan);
188
+ const readback = client.pages(endpoint).filter(approved).at(-1);
189
+ if (!readback)
190
+ throw new Error("independent exact-head approval readback failed");
191
+ return { ...plan, reviewId: readback.id, reviewUrl: readback.html_url };
192
+ }
193
+
194
+ function api(token) {
195
+ const call = (endpoint, args = [], input) =>
196
+ JSON.parse(
197
+ execFileSync("gh", ["api", endpoint, ...args], {
198
+ encoding: "utf8",
199
+ input,
200
+ env: { ...process.env, GH_TOKEN: token },
201
+ maxBuffer: 8 * 1024 * 1024,
202
+ stdio: ["pipe", "pipe", "pipe"],
203
+ }),
204
+ );
205
+ return {
206
+ json: (endpoint) => call(endpoint),
207
+ pages: (endpoint, field) =>
208
+ call(endpoint, ["--paginate", "--slurp"]).flatMap((page) =>
209
+ field ? page[field] : page,
210
+ ),
211
+ post: (endpoint, body) =>
212
+ call(
213
+ endpoint,
214
+ ["--method", "POST", "--input", "-"],
215
+ JSON.stringify(body),
216
+ ),
217
+ };
218
+ }
219
+
220
+ async function main(mode) {
221
+ const repository = process.env.GITHUB_REPOSITORY;
222
+ const runId = process.env.VERIFY_RUN_ID;
223
+ if (!/^\d+$/u.test(runId || "") || repository !== REPOSITORY)
224
+ throw new Error("exact repository and verification run required");
225
+ const client = api(process.env.GH_TOKEN);
226
+ const file = ".buildchain/next-development-review.json";
227
+ if (mode === "verify") {
228
+ if (process.env.BUILDCHAIN_APPROVAL_TOKEN)
229
+ throw new Error("verification must not receive the approval credential");
230
+ const git = (...args) =>
231
+ execFileSync("git", args, {
232
+ encoding: "utf8",
233
+ maxBuffer: 8 * 1024 * 1024,
234
+ stdio: ["ignore", "pipe", "pipe"],
235
+ }).trim();
236
+ const plan = await verifyNextDevelopmentReview({
237
+ client,
238
+ repository,
239
+ runId,
240
+ git,
241
+ verifyDelta: verifyVersionStateDelta,
242
+ publication: (args) =>
243
+ readBinaryPublicationEvidence({
244
+ ...args,
245
+ client: releaseAssetClient(repository),
246
+ attempts: 1,
247
+ }),
248
+ });
249
+ fs.mkdirSync(path.dirname(file), { recursive: true });
250
+ fs.writeFileSync(file, `${JSON.stringify(plan, null, 2)}\n`);
251
+ } else if (mode === "approve") {
252
+ if (!process.env.BUILDCHAIN_APPROVAL_TOKEN)
253
+ throw new Error("independent approval credential missing");
254
+ const plan = JSON.parse(fs.readFileSync(file));
255
+ if (plan.repository !== repository || String(plan.runId) !== runId)
256
+ throw new Error("review plan is from another invocation");
257
+ const result = await approveNextDevelopment({
258
+ client,
259
+ plan,
260
+ reviewer: api(process.env.BUILDCHAIN_APPROVAL_TOKEN),
261
+ });
262
+ fs.writeFileSync(file, `${JSON.stringify(result, null, 2)}\n`);
263
+ console.log(
264
+ JSON.stringify({ reviewId: result.reviewId, headSha: result.headSha }),
265
+ );
266
+ } else if (mode === "enqueue") {
267
+ const plan = JSON.parse(fs.readFileSync(file));
268
+ if (
269
+ plan.repository !== repository ||
270
+ String(plan.runId) !== runId ||
271
+ !plan.reviewId
272
+ )
273
+ throw new Error(
274
+ "exact independent review readback required before enqueue",
275
+ );
276
+ const { pull } = observe(client, repository, runId, plan);
277
+ const reviews = client.pages(
278
+ `repos/${repository}/pulls/${plan.number}/reviews`,
279
+ );
280
+ if (
281
+ !reviews.some(
282
+ (review) =>
283
+ review.id === plan.reviewId &&
284
+ review.user?.login === REVIEWER &&
285
+ review.commit_id === plan.headSha &&
286
+ review.state === "APPROVED",
287
+ )
288
+ )
289
+ throw new Error("independent approval no longer qualifies enqueue");
290
+ await enqueueNextDevelopmentPullRequest({
291
+ pull,
292
+ headSha: plan.headSha,
293
+ mutationOctokit: {
294
+ graphql: async (query, variables) => {
295
+ const result = client.post("graphql", { query, variables });
296
+ if (result.errors?.length)
297
+ throw new Error(
298
+ result.errors.map((error) => error.message).join("; "),
299
+ );
300
+ return result.data;
301
+ },
302
+ },
303
+ wait: (delay) => new Promise((resolve) => setTimeout(resolve, delay)),
304
+ maxPolls: 20,
305
+ });
306
+ } else throw new Error("expected verify, approve or enqueue");
307
+ }
308
+
309
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
310
+ main(process.argv[2]).catch((error) => {
311
+ console.error(error.message);
312
+ process.exitCode = 1;
313
+ });
@@ -0,0 +1,261 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { execFileSync } from "node:child_process";
5
+ import { pathToFileURL } from "node:url";
6
+ import { verifyPublicationSettlement } from "./publication-settlement.mjs";
7
+ import {
8
+ verifyComposePublication,
9
+ verifyComposeQualification,
10
+ } from "../packages/core/oci-compose-qualification.js";
11
+ import { releaseTailRoot } from "../packages/core/release-tail-provider-plane.js";
12
+ import { createRegistryClient } from "../actions/release-candidate-promote/oci-registry-client.js";
13
+ import { ociPublicationTag } from "../packages/core/oci-publication-graph.js";
14
+
15
+ const digest = (bytes) =>
16
+ `sha256:${crypto.createHash("sha256").update(bytes).digest("hex")}`;
17
+ function check(condition, message) {
18
+ if (!condition) throw new Error(`Compose preview: ${message}`);
19
+ }
20
+ function gh(args) {
21
+ return execFileSync("gh", args, {
22
+ encoding: "utf8",
23
+ stdio: ["ignore", "pipe", "pipe"],
24
+ });
25
+ }
26
+ const api = (route) => JSON.parse(gh(["api", route]));
27
+ const read = (file) => JSON.parse(fs.readFileSync(file));
28
+ export function createComposePreviewRegistry(environment, fetchImpl = fetch) {
29
+ const token = environment.BUILDCHAIN_REGISTRY_TOKEN;
30
+ const actor = environment.GITHUB_ACTOR;
31
+ check(
32
+ typeof token === "string" &&
33
+ token.length > 0 &&
34
+ typeof actor === "string" &&
35
+ actor.length > 0,
36
+ "explicit scoped registry identity required",
37
+ );
38
+ return createRegistryClient(token, fetchImpl, actor);
39
+ }
40
+ function output(name, value) {
41
+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`);
42
+ }
43
+
44
+ export async function promoteComposePreview({
45
+ context,
46
+ registry,
47
+ receipt,
48
+ fetchManifest,
49
+ }) {
50
+ const { application, policy } = context;
51
+ const exact = await fetchManifest(
52
+ ociPublicationTag(application, context.tag.slice(1)),
53
+ );
54
+ check(
55
+ exact.digest === application.digest,
56
+ "immutable application digest changed",
57
+ );
58
+ const current = await fetchManifest(policy.alias, true);
59
+ check(
60
+ current.digest === policy.previousDigest ||
61
+ current.digest === application.digest,
62
+ "preview changed since qualification",
63
+ );
64
+ const plan = {
65
+ schema: "kungfu-buildchain-compose-preview-plan/v1",
66
+ ...context,
67
+ qualificationRoot: releaseTailRoot(receipt),
68
+ expectedOld: policy.previousDigest,
69
+ targetDigest: application.digest,
70
+ operation: "digest-preserving-preview-promotion",
71
+ };
72
+ if (current.digest !== application.digest) {
73
+ const result = await registry(application, `manifests/${policy.alias}`, {
74
+ write: true,
75
+ method: "PUT",
76
+ body: exact.bytes,
77
+ headers: {
78
+ "content-type": exact.mediaType,
79
+ "content-length": String(exact.bytes.length),
80
+ },
81
+ });
82
+ check(
83
+ result.status === 201,
84
+ `preview write uncertain (HTTP ${result.status}); retain the original qualification for readback`,
85
+ );
86
+ }
87
+ const observed = await fetchManifest(policy.alias);
88
+ check(observed.digest === application.digest, "preview readback mismatch");
89
+ const body = {
90
+ schema: "kungfu-buildchain-compose-preview-receipt/v1",
91
+ repository: context.repository,
92
+ tag: context.tag,
93
+ sourceSha: context.sourceSha,
94
+ publicationReceiptRoot: context.publicationReceiptRoot,
95
+ planRoot: releaseTailRoot(plan),
96
+ qualificationRoot: plan.qualificationRoot,
97
+ previousDigest: policy.previousDigest,
98
+ application: `${application.repository}@${application.digest}`,
99
+ alias: `${application.repository}:${policy.alias}`,
100
+ observedDigest: observed.digest,
101
+ outcome: "complete",
102
+ };
103
+ return { ...body, receiptRoot: releaseTailRoot(body) };
104
+ }
105
+
106
+ async function run(mode) {
107
+ const repository = process.env.GITHUB_REPOSITORY,
108
+ event = read(process.env.GITHUB_EVENT_PATH);
109
+ check(
110
+ process.env.GITHUB_EVENT_NAME === "workflow_run" && event.workflow_run?.id,
111
+ "requires a completed qualification workflow event",
112
+ );
113
+ check(
114
+ /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/u.test(repository),
115
+ "invalid repository",
116
+ );
117
+ const id = String(event.workflow_run.id);
118
+ check(/^\d+$/u.test(id), "invalid qualification run");
119
+ const run = api(`repos/${repository}/actions/runs/${id}`),
120
+ tag = run.head_branch;
121
+ check(
122
+ /^v\d+\.\d+\.\d+-alpha\.\d+$/u.test(tag),
123
+ "qualification must run on an exact alpha tag",
124
+ );
125
+ const release = api(`repos/${repository}/releases/tags/${tag}`);
126
+ check(release.prerelease && !release.draft, "public alpha release required");
127
+ const root = path.resolve(".buildchain/compose-preview");
128
+ fs.mkdirSync(path.join(root, "public"), { recursive: true });
129
+ const names = [
130
+ "oci-family.json",
131
+ "oci-publication-readback.json",
132
+ "buildchain-publication-settlement.json",
133
+ "buildchain.release.json",
134
+ ];
135
+ for (const name of names) {
136
+ check(
137
+ release.assets.filter((asset) => asset.name === name).length === 1,
138
+ `missing exact public asset ${name}`,
139
+ );
140
+ gh([
141
+ "release",
142
+ "download",
143
+ tag,
144
+ "--repo",
145
+ repository,
146
+ "--pattern",
147
+ name,
148
+ "--dir",
149
+ path.join(root, "public"),
150
+ "--clobber",
151
+ ]);
152
+ }
153
+ const [family, readback, settlement, publicPassport] = names.map((name) =>
154
+ read(path.join(root, "public", name)),
155
+ );
156
+ const documents = settlement.documents;
157
+ const tagged = api(`repos/${repository}/commits/${tag}`).sha;
158
+ verifyPublicationSettlement(documents, {
159
+ repository,
160
+ tag,
161
+ sourceSha: tagged,
162
+ publicPassport,
163
+ });
164
+ const context = verifyComposePublication({
165
+ family,
166
+ readback,
167
+ documents,
168
+ repository,
169
+ tag,
170
+ });
171
+ verifyComposeQualification(context, run);
172
+ const artifactName = `oci-compose-qualification-${id}-${run.run_attempt}`;
173
+ const artifacts = api(
174
+ `repos/${repository}/actions/runs/${id}/artifacts?per_page=100`,
175
+ ).artifacts.filter(
176
+ (artifact) => artifact.name === artifactName && !artifact.expired,
177
+ );
178
+ check(
179
+ artifacts.length === 1,
180
+ "qualification artifact is missing or ambiguous",
181
+ );
182
+ if (mode === "prepare") {
183
+ output("artifact-id", artifacts[0].id);
184
+ output("run-id", id);
185
+ output("source-sha", tagged);
186
+ return;
187
+ }
188
+ check(mode === "apply", "unsupported mode");
189
+ const evidenceRoot = path.join(root, "qualification");
190
+ const receipt = read(path.join(evidenceRoot, "qualification.json"));
191
+ verifyComposeQualification(context, run, receipt);
192
+ for (const item of receipt.evidence) {
193
+ const file = path.join(evidenceRoot, item.path);
194
+ check(
195
+ !fs.lstatSync(file).isSymbolicLink() &&
196
+ fs.statSync(file).size < 2_000_000 &&
197
+ digest(fs.readFileSync(file)) === item.sha256,
198
+ "qualification evidence bytes mismatch",
199
+ );
200
+ }
201
+ const { registry } = createComposePreviewRegistry(process.env);
202
+ async function fetchManifest(ref, allowAbsent = false) {
203
+ const response = await registry(context.application, `manifests/${ref}`, {
204
+ headers: {
205
+ accept:
206
+ "application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json",
207
+ },
208
+ });
209
+ if (allowAbsent && response.status === 404) return { digest: "none" };
210
+ check(response.ok, "public registry readback unavailable");
211
+ const bytes = Buffer.from(await response.arrayBuffer()),
212
+ value = digest(bytes);
213
+ check(
214
+ !response.headers.get("docker-content-digest") ||
215
+ response.headers.get("docker-content-digest") === value,
216
+ "registry digest mismatch",
217
+ );
218
+ return { bytes, digest: value, mediaType: JSON.parse(bytes).mediaType };
219
+ }
220
+ const result = await promoteComposePreview({
221
+ context,
222
+ registry,
223
+ receipt,
224
+ fetchManifest,
225
+ });
226
+ const file = path.join(
227
+ root,
228
+ `buildchain-compose-preview-${id}-${run.run_attempt}.json`,
229
+ );
230
+ fs.writeFileSync(file, JSON.stringify(result, null, 2) + "\n");
231
+ const existing = release.assets.find(
232
+ (asset) => asset.name === path.basename(file),
233
+ );
234
+ if (existing) {
235
+ const previous = path.join(root, "previous");
236
+ fs.mkdirSync(previous, { recursive: true });
237
+ gh([
238
+ "release",
239
+ "download",
240
+ tag,
241
+ "--repo",
242
+ repository,
243
+ "--pattern",
244
+ path.basename(file),
245
+ "--dir",
246
+ previous,
247
+ "--clobber",
248
+ ]);
249
+ check(
250
+ fs.readFileSync(path.join(previous, path.basename(file)), "utf8") ===
251
+ fs.readFileSync(file, "utf8"),
252
+ "existing preview receipt conflict",
253
+ );
254
+ } else gh(["release", "upload", tag, file, "--repo", repository]);
255
+ process.stdout.write(`Verified ${result.alias}@${result.observedDigest}\n`);
256
+ }
257
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
258
+ run(process.argv[2]).catch((error) => {
259
+ process.stderr.write(`${error.message}\n`);
260
+ process.exitCode = 1;
261
+ });
@@ -418,6 +418,8 @@ function runMigration(options) {
418
418
  options.buildchainVersion,
419
419
  ),
420
420
  buildchainSha: options.buildchainSha,
421
+ stableBuildchainRoot: readFlag(options.args, "stable-buildchain-root", ""),
422
+ alphaBuildchainRoot: readFlag(options.args, "alpha-buildchain-root", ""),
421
423
  });
422
424
  return options.args.some((entry) => ["--write", "--execute"].includes(entry))
423
425
  ? writePaperMigration(plan)