@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
@@ -21,7 +21,7 @@ export const KFD_LEGACY_SUPPORT_MATRIX_CONTRACT = "kungfu-kfd-support-matrix";
21
21
 
22
22
  const BUILDCHAIN_ADOPTER_ID = "kungfu-systems/buildchain";
23
23
  const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
24
- const SHA_PATTERN = /^[0-9a-f]{40}$/;
24
+ const SHA_PATTERN = /^[0-9a-f]{40}$/, SOURCE_REPOSITORY_PATTERN = /^[^@\s]+$/;
25
25
  const REQUIRED_DECISIONS = Object.freeze(["KFD-1", "KFD-2", "KFD-3", "KFD-4", "KFD-5", "KFD-7"]);
26
26
  const PRODUCT_GATE_STANDARDS = Object.freeze(["kfd-4", "kfd-5", "kfd-7"]);
27
27
  const PRODUCT_GATE_SET = new Set(PRODUCT_GATE_STANDARDS);
@@ -77,29 +77,27 @@ function verifyPublishedManifest(manifest, packageOptions, issues) {
77
77
  }
78
78
  }
79
79
 
80
- function manifestSourceSha(manifest) {
81
- const match = /^kungfu-systems\/buildchain@([0-9a-f]{40})$/.exec(text(manifest?.adopter?.artifact?.coordinate));
82
- return match?.[1] || "";
83
- }
80
+ function adopterExpectation({ expectedAdopterId = BUILDCHAIN_ADOPTER_ID, expectedSourceRepository = "" } = {}) { const adopterId = text(expectedAdopterId) || BUILDCHAIN_ADOPTER_ID; return { adopterId, sourceRepository: text(expectedSourceRepository) || adopterId }; }
84
81
 
85
- function validateManifestIdentity(manifest, packageArtifactRoot, expectedSourceSha, issues) {
86
- if (manifest?.adopter?.id !== BUILDCHAIN_ADOPTER_ID) {
87
- issues.push(issue("adopter-identity", "manifest.adopter.id", `manifest adopter must be ${BUILDCHAIN_ADOPTER_ID}`));
88
- }
82
+ function manifestSource(manifest) { const coordinate = text(manifest?.adopter?.artifact?.coordinate), match = /^([^@\s]+)@([0-9a-f]{40})$/.exec(coordinate); return { coordinate, repository: match?.[1] || "", sha: match?.[2] || "" }; }
83
+
84
+ function validateManifestIdentity(manifest, packageArtifactRoot, expectation, expectedSourceSha, issues) {
85
+ if (manifest?.adopter?.id !== expectation.adopterId) issues.push(issue("adopter-identity", "manifest.adopter.id", `manifest adopter must be ${expectation.adopterId}`));
89
86
  const pinned = manifest?.kfdCut?.package;
90
87
  if (pinned?.name !== kfdPackageJson.name
91
88
  || pinned?.version !== kfdPackageJson.version
92
89
  || pinned?.artifactRoot !== packageArtifactRoot) {
93
90
  issues.push(issue("adopter-package-cut", "manifest.kfdCut.package", "manifest must bind the exact installed KFD package cut"));
94
91
  }
95
- const sourceSha = manifestSourceSha(manifest);
96
- if (manifest?.adopter?.artifact?.kind !== "git-commit" || !sourceSha
97
- || !ROOT_PATTERN.test(text(manifest?.adopter?.artifact?.root))) {
98
- issues.push(issue("adopter-source", "manifest.adopter.artifact", "Buildchain adopter authority must bind one exact git commit and artifact root"));
99
- } else if (expectedSourceSha && sourceSha !== expectedSourceSha) {
92
+ const source = manifestSource(manifest);
93
+ if (!SOURCE_REPOSITORY_PATTERN.test(expectation.sourceRepository) || manifest?.adopter?.artifact?.kind !== "git-commit" || !source.sha || !ROOT_PATTERN.test(text(manifest?.adopter?.artifact?.root))) {
94
+ issues.push(issue("adopter-source", "manifest.adopter.artifact", "adopter authority must bind one exact repository, git commit, and artifact root"));
95
+ } else if (source.repository !== expectation.sourceRepository) {
96
+ issues.push(issue("adopter-source", "manifest.adopter.artifact.coordinate", `manifest source repository must be ${expectation.sourceRepository}`));
97
+ } else if (expectedSourceSha && source.sha !== expectedSourceSha) {
100
98
  issues.push(issue("adopter-source", "manifest.adopter.artifact.coordinate", "manifest source does not match the requested release source"));
101
99
  }
102
- return sourceSha;
100
+ return source;
103
101
  }
104
102
 
105
103
  function validateRequiredDecisions(decisions, issues) {
@@ -131,7 +129,7 @@ function collectProductGates(gateResults, decisions, verificationCut, issues) {
131
129
  for (const [index, gate] of gateResults.entries()) {
132
130
  const validation = validateKfdProductGateResult(gate, verificationCut);
133
131
  if (!validation.valid) {
134
- issues.push(issue("adopter-gate-invalid", `gateResults[${index}]`, "Buildchain product gate is invalid", {
132
+ issues.push(issue("adopter-gate-invalid", `gateResults[${index}]`, "adopter product gate is invalid", {
135
133
  gateIssues: validation.issues,
136
134
  }));
137
135
  }
@@ -141,15 +139,16 @@ function collectProductGates(gateResults, decisions, verificationCut, issues) {
141
139
  if (gates.has(gate?.standard)) {
142
140
  issues.push(issue("adopter-gate-duplicate", `gateResults[${index}].standard`, `${gate.standard} gate is duplicated`));
143
141
  }
142
+ if (gate?.source?.repository !== verificationCut.expectedSourceRepository) issues.push(issue("adopter-gate-source", `gateResults[${index}].source.repository`, "product gate repository must match the adopter source repository"));
144
143
  gates.set(gate?.standard, gate);
145
144
  }
146
145
  for (const standard of PRODUCT_GATE_STANDARDS) {
147
146
  const gate = gates.get(standard);
148
147
  const id = standard.toUpperCase();
149
148
  if (!gate) {
150
- issues.push(issue("adopter-gate-missing", `gateResults.${standard}`, `${id} requires its existing Buildchain product gate`));
149
+ issues.push(issue("adopter-gate-missing", `gateResults.${standard}`, `${id} requires its exact adopter product gate`));
151
150
  } else if (!(decisions.get(id)?.verificationEvidence || []).some((entry) => entry?.root === gate.gateRoot)) {
152
- issues.push(issue("adopter-gate-unbound", `manifest.decisions.${id}.verificationEvidence`, `${id} must bind the exact Buildchain gate root`));
151
+ issues.push(issue("adopter-gate-unbound", `manifest.decisions.${id}.verificationEvidence`, `${id} must bind the exact adopter gate root`));
153
152
  }
154
153
  }
155
154
  return gates;
@@ -159,12 +158,12 @@ function projectedProductGates(gates) {
159
158
  return PRODUCT_GATE_STANDARDS.map((standard) => {
160
159
  const gate = gates.get(standard);
161
160
  return gate
162
- ? { standard, sourceSha: gate.source.sha, gateRoot: gate.gateRoot, status: gate.status }
163
- : { standard, sourceSha: "", gateRoot: "", status: "missing" };
161
+ ? { standard, sourceRepository: gate.source.repository, sourceSha: gate.source.sha, gateRoot: gate.gateRoot, status: gate.status }
162
+ : { standard, sourceRepository: "", sourceSha: "", gateRoot: "", status: "missing" };
164
163
  });
165
164
  }
166
165
 
167
- function createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, sourceSha, checkedAt, maxAgeSeconds, issues }) {
166
+ function createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, source, checkedAt, maxAgeSeconds, issues }) {
168
167
  const gate = {
169
168
  schemaVersion: 1,
170
169
  contract: KFD_ADOPTER_MANIFEST_GATE_CONTRACT,
@@ -174,10 +173,8 @@ function createGateDocument({ manifest, bundle, gates, authorityPath, packageArt
174
173
  contract: text(manifest?.contract),
175
174
  manifestRoot: bundle?.roots?.manifestRoot || "",
176
175
  },
177
- source: {
178
- sha: sourceSha,
179
- artifactRoot: text(manifest?.adopter?.artifact?.root),
180
- },
176
+ adopter: { id: text(manifest?.adopter?.id) },
177
+ source: { repository: source.repository, coordinate: source.coordinate, sha: source.sha, artifactRoot: text(manifest?.adopter?.artifact?.root) },
181
178
  verificationCut: { checkedAt, maxAgeSeconds },
182
179
  standardPackage: {
183
180
  name: kfdPackageJson.name,
@@ -211,28 +208,28 @@ export function createKfdAdopterManifestGate({
211
208
  packageArtifactRoot = "",
212
209
  gateResults = [],
213
210
  authorityPath = ".buildchain/kfd/adopter-manifest.json",
214
- expectedSourceSha = "",
211
+ expectedAdopterId = BUILDCHAIN_ADOPTER_ID, expectedSourceRepository = "", expectedSourceSha = "",
215
212
  checkedAt = new Date().toISOString(),
216
213
  maxAgeSeconds = 86400,
217
214
  } = {}) {
218
- const issues = [];
215
+ const issues = [], expectation = adopterExpectation({ expectedAdopterId, expectedSourceRepository });
219
216
  const bundle = verifyPublishedManifest(manifest, {
220
217
  packageArtifactRoot,
221
218
  verifiedAt: checkedAt,
222
219
  maxAgeSeconds,
223
220
  }, issues);
224
221
  const decisions = new Map((manifest?.decisions || []).map((row) => [row.id, row]));
225
- let sourceSha = manifestSourceSha(manifest);
222
+ let source = manifestSource(manifest);
226
223
  if (bundle) {
227
- sourceSha = validateManifestIdentity(manifest, packageArtifactRoot, expectedSourceSha, issues);
224
+ source = validateManifestIdentity(manifest, packageArtifactRoot, expectation, expectedSourceSha, issues);
228
225
  validateRequiredDecisions(decisions, issues);
229
226
  validateWarrantWitness(decisions, issues);
230
227
  }
231
- const gates = collectProductGates(gateResults, decisions, { expectedSourceSha: sourceSha || expectedSourceSha, checkedAt }, issues);
232
- return createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, sourceSha, checkedAt, maxAgeSeconds, issues });
228
+ const gates = collectProductGates(gateResults, decisions, { expectedSourceRepository: expectation.sourceRepository, expectedSourceSha: source.sha || expectedSourceSha, checkedAt }, issues);
229
+ return createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, source, checkedAt, maxAgeSeconds, issues });
233
230
  }
234
231
 
235
- function validateGateDocument(gate, issues) {
232
+ function validateGateDocument(gate, expectation, issues) {
236
233
  const copy = structuredClone(gate);
237
234
  const root = copy.gateRoot;
238
235
  delete copy.gateRoot;
@@ -244,12 +241,13 @@ function validateGateDocument(gate, issues) {
244
241
  || !ROOT_PATTERN.test(text(gate?.authority?.manifestRoot))) {
245
242
  issues.push(issue("adopter-gate-authority", "authority", "adopter gate must bind the standard manifest contract and root"));
246
243
  }
244
+ if (gate?.adopter?.id !== expectation.adopterId) issues.push(issue("adopter-gate-identity", "adopter.id", `adopter gate must bind ${expectation.adopterId}`));
247
245
  if (gate?.standardPackage?.name !== kfdPackageJson.name || gate?.standardPackage?.version !== kfdPackageJson.version
248
246
  || ![gate?.standardPackage?.artifactRoot, gate?.standardPackage?.registryRoot, gate?.standardPackage?.verifierSetRoot].every((value) => ROOT_PATTERN.test(text(value)))) {
249
247
  issues.push(issue("adopter-gate-package", "standardPackage", "adopter gate uses stale KFD package metadata"));
250
248
  }
251
- if (!SHA_PATTERN.test(text(gate?.source?.sha)) || !ROOT_PATTERN.test(text(gate?.source?.artifactRoot))) {
252
- issues.push(issue("adopter-gate-source", "source", "adopter gate must bind one exact Buildchain source commit and artifact root"));
249
+ if (gate?.source?.repository !== expectation.sourceRepository || gate?.source?.coordinate !== `${expectation.sourceRepository}@${gate?.source?.sha || ""}` || !SHA_PATTERN.test(text(gate?.source?.sha)) || !ROOT_PATTERN.test(text(gate?.source?.artifactRoot))) {
250
+ issues.push(issue("adopter-gate-source", "source", "adopter gate must bind the expected repository, exact source commit, coordinate, and artifact root"));
253
251
  }
254
252
  if (gate?.decisionWitness?.rootAlgorithm !== "sha256-buildchain-stable-json-v1"
255
253
  || !ROOT_PATTERN.test(text(gate?.decisionWitness?.root))
@@ -259,7 +257,7 @@ function validateGateDocument(gate, issues) {
259
257
  }
260
258
  }
261
259
 
262
- function validateProjectedGates(gate, expectedSourceSha, issues) {
260
+ function validateProjectedGates(gate, expectation, expectedSourceSha, issues) {
263
261
  const seenStandards = new Set();
264
262
  for (const [index, productGate] of (gate.gateResults || []).entries()) {
265
263
  if (!PRODUCT_GATE_SET.has(productGate?.standard) || productGate?.status !== "passed" || !ROOT_PATTERN.test(text(productGate?.gateRoot))) {
@@ -267,7 +265,7 @@ function validateProjectedGates(gate, expectedSourceSha, issues) {
267
265
  }
268
266
  if (seenStandards.has(productGate?.standard)) issues.push(issue("adopter-gate-result-set", `gateResults[${index}]`, "product gate standards must be unique"));
269
267
  seenStandards.add(productGate?.standard);
270
- if (productGate?.sourceSha !== gate?.source?.sha || (expectedSourceSha && productGate?.sourceSha !== expectedSourceSha)) {
268
+ if (productGate?.sourceRepository !== gate?.source?.repository || productGate?.sourceRepository !== expectation.sourceRepository || productGate?.sourceSha !== gate?.source?.sha || (expectedSourceSha && productGate?.sourceSha !== expectedSourceSha)) {
271
269
  issues.push(issue("adopter-gate-source", `gateResults[${index}].sourceSha`, "product gate source must match the release source"));
272
270
  }
273
271
  }
@@ -277,15 +275,15 @@ function validateProjectedGates(gate, expectedSourceSha, issues) {
277
275
  }
278
276
 
279
277
  export function validateKfdAdopterManifestGate(gate, {
280
- expectedSourceSha = "",
278
+ expectedAdopterId = BUILDCHAIN_ADOPTER_ID, expectedSourceRepository = "", expectedSourceSha = "",
281
279
  checkedAt = gate?.checkedAt || new Date().toISOString(),
282
280
  } = {}) {
283
- const issues = [];
281
+ const issues = [], expectation = adopterExpectation({ expectedAdopterId, expectedSourceRepository });
284
282
  if (!gate || gate.schemaVersion !== 1 || gate.contract !== KFD_ADOPTER_MANIFEST_GATE_CONTRACT) {
285
283
  return { valid: false, issues: [issue("adopter-gate-contract", "", `gate must use ${KFD_ADOPTER_MANIFEST_GATE_CONTRACT} v1`)] };
286
284
  }
287
- validateGateDocument(gate, issues);
288
- validateProjectedGates(gate, expectedSourceSha, issues);
285
+ validateGateDocument(gate, expectation, issues);
286
+ validateProjectedGates(gate, expectation, expectedSourceSha, issues);
289
287
  if (gate.checkedAt !== checkedAt || gate?.verificationCut?.checkedAt !== checkedAt || !Number.isFinite(Date.parse(checkedAt))
290
288
  || !Number.isSafeInteger(gate?.verificationCut?.maxAgeSeconds) || gate.verificationCut.maxAgeSeconds < 0) {
291
289
  issues.push(issue("adopter-gate-time", "verificationCut", "adopter gate verification cut does not match the requested cut"));
@@ -300,7 +298,7 @@ function legacyStatus(row) {
300
298
  return row.state;
301
299
  }
302
300
 
303
- function legacyRow(row, gates) {
301
+ function legacyRow(row, gates, adopterId) {
304
302
  const key = row.id.toLowerCase();
305
303
  const productGate = gates.get(key);
306
304
  return {
@@ -318,7 +316,7 @@ function legacyRow(row, gates) {
318
316
  releaseQualification: { shippedSupport: false },
319
317
  claimClass: "standard-adopter-manifest-projection",
320
318
  knownLimitations: [...row.gaps],
321
- owner: BUILDCHAIN_ADOPTER_ID,
319
+ owner: adopterId,
322
320
  nextGate: "Release Passport artifact binding and independent release decision",
323
321
  declaration: { state: row.state, usage: row.usage, root: kfdProductGateDigest(decisionWitnessRow(row)) },
324
322
  };
@@ -326,7 +324,7 @@ function legacyRow(row, gates) {
326
324
 
327
325
  export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate } = {}) {
328
326
  const validation = validateKfdAdopterManifestGate(manifestGate, {
329
- expectedSourceSha: manifestGate?.source?.sha,
327
+ expectedAdopterId: manifestGate?.adopter?.id, expectedSourceRepository: manifestGate?.source?.repository, expectedSourceSha: manifestGate?.source?.sha,
330
328
  checkedAt: manifestGate?.checkedAt,
331
329
  });
332
330
  if (!validation.valid || manifest?.contract !== "kfd.adopter-conformance-manifest/v1") {
@@ -338,9 +336,9 @@ export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate
338
336
  verifiedAt: manifestGate.verificationCut.checkedAt,
339
337
  maxAgeSeconds: manifestGate.verificationCut.maxAgeSeconds,
340
338
  }, authorityIssues);
341
- const sourceSha = validateManifestIdentity(manifest, manifestGate.standardPackage.artifactRoot, manifestGate.source.sha, authorityIssues);
339
+ const source = validateManifestIdentity(manifest, manifestGate.standardPackage.artifactRoot, { adopterId: manifestGate.adopter.id, sourceRepository: manifestGate.source.repository }, manifestGate.source.sha, authorityIssues);
342
340
  const decisionRoot = kfdProductGateDigest(manifest.decisions.map(decisionWitnessRow));
343
- if (!bundle || sourceSha !== manifestGate.source.sha || manifest.adopter.artifact.root !== manifestGate.source.artifactRoot
341
+ if (!bundle || source.sha !== manifestGate.source.sha || source.repository !== manifestGate.source.repository || source.coordinate !== manifestGate.source.coordinate || manifest.adopter.artifact.root !== manifestGate.source.artifactRoot
344
342
  || bundle.roots.manifestRoot !== manifestGate.authority.manifestRoot
345
343
  || bundle.roots.verificationReportRoot !== manifestGate.manifestVerificationReportRoot
346
344
  || bundle.bundleRoot !== manifestGate.manifestBundleRoot
@@ -364,7 +362,8 @@ export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate
364
362
  path: manifestGate.authority.path,
365
363
  contract: manifest.contract,
366
364
  root: manifestGate.authority.manifestRoot,
367
- gateRoot: manifestGate.gateRoot,
365
+ gateRoot: manifestGate.gateRoot, adopterId: manifestGate.adopter.id,
366
+ sourceRepository: manifestGate.source.repository, sourceSha: manifestGate.source.sha,
368
367
  },
369
368
  upstream: {
370
369
  package: manifestGate.standardPackage.name,
@@ -374,7 +373,7 @@ export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate
374
373
  verifierSetRoot: manifestGate.standardPackage.verifierSetRoot,
375
374
  standardsSha256: standardsFileDigest(),
376
375
  },
377
- rows: manifest.decisions.map((row) => legacyRow(row, gates)),
376
+ rows: manifest.decisions.map((row) => legacyRow(row, gates, manifestGate.adopter.id)),
378
377
  };
379
378
  }
380
379
 
@@ -175,7 +175,13 @@ confidence: high
175
175
  sensitivity: public
176
176
  evidence_grade: A
177
177
  review_state: self-reviewed
178
- last_reviewed: 2026-08-11
178
+ last_reviewed: 2026-09-07
179
+ ai_provenance:
180
+ model_family: GPT-6
181
+ product: Codex
182
+ generated_at: 2026-09-07
183
+ visible_context: Release completion, protected development provider, and independent review sources.
184
+ invisible_context_boundary: No private credentials or external unpublished release state.
179
185
  ---
180
186
 
181
187
  # Next-development Transition
@@ -229,6 +235,23 @@ can settle, Patrol reads every prepared version path at the candidate SHA and
229
235
  requires the exact reserved blob identities; missing or stale state blocks
230
236
  before a Release Cut or heavy candidate build.
231
237
 
238
+ ## Stable publication and the next patch
239
+
240
+ A completed stable publication with \`semver/auto\` schedules the next patch at
241
+ Alpha zero: \`4.0.2\` prepares \`4.0.3-alpha.0\`. Publication settlement is
242
+ retained before the development PR starts. Preparation reads the exact current
243
+ protected Dev commit and regenerates only its declared version files, preserving
244
+ code that arrived after the stable candidate. The independent reviewer verifies
245
+ the completed stable publication and reconstructs every tracked byte from the
246
+ protected parent; a PR body alone grants no authority.
247
+
248
+ A replay returns \`already-current\` or \`already-advanced\` when Dev already
249
+ contains the target or a later version. A late Alpha completion cannot regress
250
+ Dev to an older patch. A moved protected base fails before creating the version
251
+ commit and requires a fresh preparation attempt. Pending review leaves the
252
+ completed publication intact. Anchored version models still require a reviewed
253
+ manual anchor.
254
+
232
255
  ## Version models
233
256
 
234
257
  \`semver/auto\` increments the Alpha sequence on the same semantic patch. For
@@ -243,8 +266,8 @@ invalid.
243
266
 
244
267
  ## Public self-dogfood and adoption
245
268
 
246
- \`.github/workflows/v4-public-consumer-dogfood.yml\` remains the exact thin
247
- consumer of the public \`v4-stage-capsule-canary.yml@v4-alpha\` contract. Its
269
+ \`.github/workflows/self-build-public-consumer-dogfood.yml\` remains the exact thin
270
+ consumer of the public \`public-build-stage-capsule-canary.yml@v4-alpha\` contract. Its
248
271
  declared \`lifecycle.verify\` runs \`pnpm run check\`, so the same public path
249
272
  executes pure recovery tests that inject a transient durable-state write
250
273
  failure, restore the adapter operation on a fresh runner, supersede stale work
@@ -159,15 +159,14 @@ function normalizeModel(value = {}) {
159
159
  return model;
160
160
  }
161
161
 
162
- function normalizeCompletedAlpha(value = {}) {
162
+ function normalizeCompletedAlpha(value = {}, model) {
163
163
  const version = String(value.version || "").trim();
164
- if (!ALPHA.test(version))
164
+ const expression = model?.strategy === "anchored" ? /-alpha\.(0|[1-9]\d*)$/u : ALPHA;
165
+ if (!SEMVER.test(version) || !expression.test(version))
165
166
  throw new Error("completedAlpha.version must be an alpha semantic version");
166
167
  const exactTag = String(value.exactTag || "").trim();
167
168
  if (exactTag !== `v${version}`)
168
- throw new Error(
169
- "completedAlpha.exactTag must match completedAlpha.version",
170
- );
169
+ throw new Error("completedAlpha.exactTag must match completedAlpha.version");
171
170
  const normalized = {
172
171
  outcome: String(value.outcome || "").trim(),
173
172
  version,
@@ -251,8 +250,8 @@ function initialStateRoot(status, idempotencyKey) {
251
250
  }
252
251
 
253
252
  export function createNextDevelopmentTransition(input = {}) {
254
- const completedAlpha = normalizeCompletedAlpha(input.completedAlpha);
255
253
  const model = normalizeModel(input.model);
254
+ const completedAlpha = normalizeCompletedAlpha(input.completedAlpha, model);
256
255
  const adapter = normalizeAdapter(input);
257
256
  const declaredPaths = [
258
257
  ...adapter.sourcePaths,
@@ -0,0 +1,143 @@
1
+ import { domainContentRoot } from "./canonical-contracts.js";
2
+ import { releaseTailRoot } from "./release-tail-provider-plane.js";
3
+ import { ociPublicationTag } from "./oci-publication-graph.js";
4
+
5
+ function check(condition, message) {
6
+ if (!condition) throw new Error(`Compose preview: ${message}`);
7
+ }
8
+
9
+ export function verifyComposePublication({
10
+ family,
11
+ readback,
12
+ documents,
13
+ repository,
14
+ tag,
15
+ }) {
16
+ const { root, ...body } = family;
17
+ const version = tag.replace(/^v/u, "");
18
+ const operation = documents.productState.operations.find(
19
+ (entry) => entry.capabilityId === "product.oci.publish",
20
+ );
21
+ check(
22
+ family.schema === "kungfu-buildchain-oci-family/v2" &&
23
+ root === domainContentRoot("oci-publication-family", body) &&
24
+ family.repository === repository &&
25
+ family.version === version &&
26
+ family.sourceSha === documents.passport.source.builtSourceSha &&
27
+ documents.passport.source.builtSourceTreeSha ===
28
+ documents.invocation.candidate.tree &&
29
+ readback.sourceSha === documents.invocation.candidate.commit &&
30
+ readback.familyRoot === root &&
31
+ readback.version === version &&
32
+ readback.candidateSourceSha === family.sourceSha &&
33
+ operation?.receipt.evidenceRoots.includes(releaseTailRoot(readback)),
34
+ "unbound public OCI family",
35
+ );
36
+ const applications = family.images.filter(
37
+ (entry) => entry.kind === "compose" && entry.preview,
38
+ );
39
+ check(applications.length === 1, "expected one declared Compose preview");
40
+ const application = applications[0],
41
+ policy = application.preview;
42
+ check(
43
+ policy.alias === "compose-preview" &&
44
+ /^\.github\/workflows\/[a-z0-9-]+\.yml$/u.test(
45
+ policy.qualificationWorkflow,
46
+ ) &&
47
+ /^(none|sha256:[0-9a-f]{64})$/u.test(policy.previousDigest),
48
+ "invalid preview policy",
49
+ );
50
+ const image = family.images.find(
51
+ (entry) =>
52
+ entry.name === application.targetImage && entry.kind !== "compose",
53
+ );
54
+ check(
55
+ image &&
56
+ application.repository === image.repository &&
57
+ image.repository === `ghcr.io/${repository}/${image.name}` &&
58
+ /^[a-z0-9][a-z0-9._-]*$/u.test(image.name),
59
+ "foreign application destination",
60
+ );
61
+ for (const entry of [image, application])
62
+ check(
63
+ readback.images.some(
64
+ (observed) =>
65
+ observed.name === entry.name &&
66
+ observed.repository === entry.repository &&
67
+ observed.digest === entry.digest &&
68
+ observed.ref === ociPublicationTag(entry, version) &&
69
+ observed.anonymous === true,
70
+ ),
71
+ "missing public artifact readback",
72
+ );
73
+ return {
74
+ application,
75
+ image,
76
+ policy,
77
+ familyRoot: root,
78
+ repository,
79
+ tag,
80
+ sourceSha: documents.product.publication.releaseSha,
81
+ publicationReceiptRoot: documents.receipt.receiptRoot,
82
+ };
83
+ }
84
+
85
+ export function verifyComposeQualification(context, run, receipt) {
86
+ const { application, image, policy, repository, tag, sourceSha, familyRoot } =
87
+ context;
88
+ check(
89
+ run.repository?.full_name === repository &&
90
+ run.head_repository?.full_name === repository &&
91
+ run.event === "workflow_dispatch" &&
92
+ run.status === "completed" &&
93
+ run.conclusion === "success" &&
94
+ run.head_sha === sourceSha &&
95
+ run.path.split("@")[0] === policy.qualificationWorkflow,
96
+ "qualification run is not the exact published source",
97
+ );
98
+ if (!receipt) return;
99
+ check(
100
+ receipt.schema === "kungfu-buildchain-compose-qualification/v1" &&
101
+ receipt.repository === repository &&
102
+ receipt.tag === tag &&
103
+ receipt.sourceSha === sourceSha &&
104
+ receipt.familyRoot === familyRoot &&
105
+ String(receipt.runId) === String(run.id) &&
106
+ Number(receipt.runAttempt) === run.run_attempt &&
107
+ receipt.image === `${image.repository}@${image.digest}` &&
108
+ receipt.application ===
109
+ `${application.repository}@${application.digest}` &&
110
+ receipt.previousDigest === policy.previousDigest &&
111
+ receipt.passed === true,
112
+ "qualification receipt identity mismatch",
113
+ );
114
+ for (const name of [
115
+ "freshInstall",
116
+ "restartPersistence",
117
+ "upgradePersistence",
118
+ "rollbackPersistence",
119
+ "accountIsolation",
120
+ "hardenedRuntime",
121
+ ])
122
+ check(receipt.checks?.[name] === true, `qualification lacks ${name}`);
123
+ check(
124
+ Array.isArray(image.platforms) &&
125
+ image.platforms.length === 2 &&
126
+ JSON.stringify([...image.platforms].sort()) ===
127
+ JSON.stringify(["linux/amd64", "linux/arm64"]) &&
128
+ image.platforms.every(
129
+ (platform) => receipt.platforms?.[platform]?.passed === true,
130
+ ),
131
+ "qualification lacks both platforms",
132
+ );
133
+ check(
134
+ Array.isArray(receipt.evidence) &&
135
+ receipt.evidence.length > 0 &&
136
+ receipt.evidence.every(
137
+ (entry) =>
138
+ /^[a-zA-Z0-9][a-zA-Z0-9._-]*\.json$/u.test(entry.path) &&
139
+ /^sha256:[0-9a-f]{64}$/u.test(entry.sha256),
140
+ ),
141
+ "missing qualification evidence bindings",
142
+ );
143
+ }