@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,281 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { domainContentRoot } from "./canonical-contracts.js";
5
+ import {
6
+ verifyOciGraph,
7
+ verifyOciDestination,
8
+ } from "./oci-publication-graph.js";
9
+
10
+ export const OCI_FAMILY_SCHEMA = "kungfu-buildchain-oci-family/v1";
11
+ const digestPattern = /^sha256:[0-9a-f]{64}$/u;
12
+ const namePattern = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u;
13
+
14
+ export function sealOciPublicationBundle({ bundleRoot, body }) {
15
+ const manifest = {
16
+ ...body,
17
+ root: domainContentRoot("oci-publication-family", body),
18
+ };
19
+ verifyOciPublicationBundle({
20
+ bundleRoot,
21
+ manifest,
22
+ repository: body.repository,
23
+ sourceSha: body.sourceSha,
24
+ version: body.version,
25
+ });
26
+ return manifest;
27
+ }
28
+
29
+ function requireValue(condition, message) {
30
+ if (!condition) throw new Error(`OCI bundle: ${message}`);
31
+ }
32
+
33
+ export function ociBundleFile(root, relative) {
34
+ requireValue(
35
+ typeof relative === "string" &&
36
+ relative.length > 0 &&
37
+ !relative.includes("\\") &&
38
+ !path.isAbsolute(relative) &&
39
+ !relative.split(/[\\/]/u).some((p) => p === ".." || p === "." || !p),
40
+ "unsafe file path",
41
+ );
42
+ const base = fs.realpathSync(root);
43
+ let current = base;
44
+ for (const segment of relative.split("/")) {
45
+ current = path.join(current, segment);
46
+ requireValue(
47
+ !fs.lstatSync(current).isSymbolicLink(),
48
+ "symlinks are forbidden",
49
+ );
50
+ }
51
+ requireValue(
52
+ fs.statSync(current).isFile() &&
53
+ fs.realpathSync(current).startsWith(`${base}${path.sep}`),
54
+ "file escapes bundle",
55
+ );
56
+ return current;
57
+ }
58
+
59
+ function fileDigest(file) {
60
+ const hash = crypto.createHash("sha256"),
61
+ buffer = Buffer.alloc(1024 * 1024);
62
+ const fd = fs.openSync(file, "r");
63
+ try {
64
+ for (;;) {
65
+ const size = fs.readSync(fd, buffer);
66
+ if (!size) break;
67
+ hash.update(buffer.subarray(0, size));
68
+ }
69
+ } finally {
70
+ fs.closeSync(fd);
71
+ }
72
+ return `sha256:${hash.digest("hex")}`;
73
+ }
74
+
75
+ function verifyImageProvenance(image, config, sourceSha, version) {
76
+ const labels = config.config?.Labels || {};
77
+ requireValue(
78
+ image.content &&
79
+ /^[0-9a-f]{40}$/u.test(image.content.sourceSha) &&
80
+ typeof image.content.version === "string" &&
81
+ image.content.version.length > 0,
82
+ "missing image content provenance",
83
+ );
84
+ requireValue(
85
+ labels["org.opencontainers.image.revision"] === image.content.sourceSha &&
86
+ labels["org.opencontainers.image.version"] === image.content.version,
87
+ "image content provenance mismatch",
88
+ );
89
+ if (image.action === "built")
90
+ requireValue(
91
+ image.content.sourceSha === sourceSha &&
92
+ image.content.version === version,
93
+ "built image is not from the candidate source",
94
+ );
95
+ if (image.contractMajor !== undefined)
96
+ requireValue(
97
+ Number.isInteger(image.contractMajor) &&
98
+ image.contractMajor > 0 &&
99
+ labels["io.kungfu.image.contract-major"] ===
100
+ String(image.contractMajor),
101
+ "image contract major mismatch",
102
+ );
103
+ if (image.parentDigest)
104
+ requireValue(
105
+ digestPattern.test(image.parentDigest) &&
106
+ labels["io.kungfu.image.parent-digest"] === image.parentDigest,
107
+ "image parent digest mismatch",
108
+ );
109
+ if (image.content.materialSha)
110
+ requireValue(
111
+ /^[0-9a-f]{40}$/u.test(image.content.materialSha) &&
112
+ labels["io.kungfu.buildchain.release-material-sha"] ===
113
+ image.content.materialSha,
114
+ "image material provenance mismatch",
115
+ );
116
+ }
117
+
118
+ export function verifyOciPublicationBundle({
119
+ bundleRoot,
120
+ manifest,
121
+ repository,
122
+ sourceSha,
123
+ version,
124
+ }) {
125
+ requireValue(
126
+ [OCI_FAMILY_SCHEMA, "kungfu-buildchain-oci-family/v2"].includes(
127
+ manifest?.schema,
128
+ ),
129
+ "unsupported manifest schema",
130
+ );
131
+ const { root, ...body } = manifest;
132
+ const computed = domainContentRoot("oci-publication-family", body);
133
+ requireValue(root === computed, "manifest root mismatch");
134
+ requireValue(
135
+ manifest.repository === repository &&
136
+ manifest.sourceSha === sourceSha &&
137
+ manifest.version === version,
138
+ "candidate identity mismatch",
139
+ );
140
+ requireValue(
141
+ /^[a-z0-9-]+\/[a-z0-9._-]+$/u.test(repository),
142
+ "invalid repository",
143
+ );
144
+ requireValue(
145
+ Array.isArray(manifest.images) && manifest.images.length > 0,
146
+ "empty image family",
147
+ );
148
+ const expected = manifest.expectedImages;
149
+ requireValue(
150
+ Array.isArray(expected) &&
151
+ expected.length === new Set(expected).size &&
152
+ expected.every((n) => namePattern.test(n)),
153
+ "invalid expected image family",
154
+ );
155
+ const actual = manifest.images.map((i) => i.name).sort();
156
+ requireValue(
157
+ JSON.stringify(actual) === JSON.stringify([...expected].sort()),
158
+ "incomplete or duplicate image family",
159
+ );
160
+ const files = new Map();
161
+ const graphs = new Map();
162
+ for (const image of manifest.images) {
163
+ verifyOciDestination(image, manifest, requireValue);
164
+ requireValue(
165
+ digestPattern.test(image.digest) &&
166
+ ["built", "reused"].includes(image.action),
167
+ "invalid image identity",
168
+ );
169
+ requireValue(
170
+ /^linux\/(amd64|arm64)$/u.test(image.platform) ||
171
+ (manifest.schema.endsWith("/v2") &&
172
+ ["multi-platform", "compose"].includes(image.platform)),
173
+ "unsupported image platform",
174
+ );
175
+ const prefix = image.layout;
176
+ const descriptorFile = (descriptor) => {
177
+ requireValue(
178
+ digestPattern.test(descriptor.digest) &&
179
+ Number.isSafeInteger(descriptor.size) &&
180
+ descriptor.size >= 0 &&
181
+ !descriptor.urls,
182
+ "invalid OCI descriptor",
183
+ );
184
+ const relative = `${prefix}/blobs/sha256/${descriptor.digest.slice(7)}`;
185
+ const file = ociBundleFile(bundleRoot, relative);
186
+ requireValue(
187
+ fs.statSync(file).size === descriptor.size,
188
+ "OCI blob size mismatch",
189
+ );
190
+ if (!files.has(file))
191
+ requireValue(
192
+ fileDigest(file) === descriptor.digest,
193
+ "OCI blob digest mismatch",
194
+ );
195
+ if (Object.hasOwn(descriptor, "data")) {
196
+ requireValue(
197
+ descriptor.size <= 1024 * 1024 &&
198
+ descriptor.data === fs.readFileSync(file).toString("base64"),
199
+ "embedded OCI content mismatch",
200
+ );
201
+ }
202
+ files.set(file, descriptor);
203
+ return file;
204
+ };
205
+ const layout = JSON.parse(
206
+ fs.readFileSync(ociBundleFile(bundleRoot, `${prefix}/oci-layout`)),
207
+ );
208
+ requireValue(
209
+ layout.imageLayoutVersion === "1.0.0",
210
+ "unsupported OCI layout",
211
+ );
212
+ const index = JSON.parse(
213
+ fs.readFileSync(ociBundleFile(bundleRoot, `${prefix}/index.json`)),
214
+ );
215
+ const selected = (index.manifests || []).filter(
216
+ (descriptor) =>
217
+ descriptor.digest === image.digest &&
218
+ (descriptor.annotations?.["org.opencontainers.image.ref.name"] ===
219
+ image.name ||
220
+ index.manifests.length === 1),
221
+ );
222
+ requireValue(
223
+ index.schemaVersion === 2 && selected.length === 1,
224
+ "OCI index must select one exact image manifest",
225
+ );
226
+ if (manifest.schema.endsWith("/v2")) {
227
+ graphs.set(
228
+ image.name,
229
+ verifyOciGraph({
230
+ image,
231
+ manifest,
232
+ descriptor: selected[0],
233
+ descriptorFile,
234
+ requireValue,
235
+ verifyProvenance: (config) =>
236
+ verifyImageProvenance(image, config, sourceSha, version),
237
+ }),
238
+ );
239
+ } else {
240
+ const document = JSON.parse(fs.readFileSync(descriptorFile(selected[0])));
241
+ requireValue(
242
+ document.schemaVersion === 2 &&
243
+ [
244
+ "application/vnd.oci.image.manifest.v1+json",
245
+ "application/vnd.docker.distribution.manifest.v2+json",
246
+ ].includes(document.mediaType) &&
247
+ Array.isArray(document.layers),
248
+ "unsupported OCI image manifest",
249
+ );
250
+ const config = JSON.parse(
251
+ fs.readFileSync(descriptorFile(document.config)),
252
+ );
253
+ requireValue(
254
+ `${config.os}/${config.architecture}` === image.platform,
255
+ "OCI platform mismatch",
256
+ );
257
+ verifyImageProvenance(image, config, sourceSha, version);
258
+ for (const layer of document.layers) descriptorFile(layer);
259
+ graphs.set(image.name, {
260
+ manifests: [selected[0]],
261
+ blobs: [document.config, ...document.layers],
262
+ });
263
+ }
264
+ const smokeFile = ociBundleFile(bundleRoot, image.smoke.path);
265
+ requireValue(
266
+ fileDigest(smokeFile) === image.smoke.sha256,
267
+ "smoke evidence digest mismatch",
268
+ );
269
+ const smoke = JSON.parse(fs.readFileSync(smokeFile));
270
+ requireValue(
271
+ smoke.passed === true && smoke.image === image.name,
272
+ "smoke qualification failed",
273
+ );
274
+ }
275
+ return {
276
+ root: computed,
277
+ manifest,
278
+ graphs,
279
+ bundleRoot: fs.realpathSync(bundleRoot),
280
+ };
281
+ }
@@ -0,0 +1,206 @@
1
+ import fs from "node:fs";
2
+
3
+ const imageTypes = [
4
+ "application/vnd.oci.image.manifest.v1+json",
5
+ "application/vnd.docker.distribution.manifest.v2+json",
6
+ ];
7
+ const indexTypes = [
8
+ "application/vnd.oci.image.index.v1+json",
9
+ "application/vnd.docker.distribution.manifest.list.v2+json",
10
+ ];
11
+ const composeType = "application/vnd.docker.compose.project";
12
+
13
+ export function ociPublicationTag(image, version) {
14
+ return `${image.kind === "compose" ? "compose-" : ""}v${version}`;
15
+ }
16
+
17
+ export function verifyOciDestination(image, manifest, requireValue) {
18
+ const compose = manifest.schema.endsWith("/v2") && image.kind === "compose";
19
+ requireValue(
20
+ manifest.schema.endsWith("/v2") || !image.kind,
21
+ "artifact kinds require family v2",
22
+ );
23
+ const target =
24
+ compose &&
25
+ manifest.images.find(
26
+ (entry) => entry.name === image.targetImage && entry.kind !== "compose",
27
+ );
28
+ requireValue(
29
+ !image.kind || ["image", "compose"].includes(image.kind),
30
+ "unsupported artifact kind",
31
+ );
32
+ requireValue(!compose || target, "Compose target image is absent");
33
+ requireValue(
34
+ image.repository ===
35
+ `ghcr.io/${manifest.repository}/${compose ? target.name : image.name}`,
36
+ "destination must belong to the consumer repository",
37
+ );
38
+ requireValue(
39
+ manifest.images.filter(
40
+ (entry) =>
41
+ entry.repository === image.repository &&
42
+ ociPublicationTag(entry, manifest.version) ===
43
+ ociPublicationTag(image, manifest.version),
44
+ ).length === 1,
45
+ "duplicate publication destination",
46
+ );
47
+ }
48
+
49
+ export function verifyOciGraph({
50
+ image,
51
+ manifest,
52
+ descriptor,
53
+ descriptorFile,
54
+ requireValue: check,
55
+ verifyProvenance,
56
+ }) {
57
+ const manifests = [],
58
+ blobs = new Map(),
59
+ platforms = [],
60
+ attestations = [];
61
+ const visited = new Set();
62
+ const read = (entry) => JSON.parse(fs.readFileSync(descriptorFile(entry)));
63
+ function blob(entry) {
64
+ descriptorFile(entry);
65
+ blobs.set(entry.digest, entry);
66
+ }
67
+ function visit(entry, depth = 0) {
68
+ check(
69
+ depth < 4 && visited.size < 256 && !visited.has(entry.digest),
70
+ "duplicate or excessive OCI graph",
71
+ );
72
+ visited.add(entry.digest);
73
+ const document = read(entry);
74
+ check(
75
+ document.schemaVersion === 2 && document.mediaType === entry.mediaType,
76
+ "OCI descriptor media type mismatch",
77
+ );
78
+ if (indexTypes.includes(document.mediaType)) {
79
+ check(
80
+ image.kind !== "compose" &&
81
+ Array.isArray(document.manifests) &&
82
+ document.manifests.length > 0,
83
+ "invalid OCI index",
84
+ );
85
+ for (const child of document.manifests) visit(child, depth + 1);
86
+ } else {
87
+ check(
88
+ imageTypes.includes(document.mediaType) &&
89
+ Array.isArray(document.layers),
90
+ "unsupported OCI manifest",
91
+ );
92
+ const config = read(document.config);
93
+ blob(document.config);
94
+ for (const layer of document.layers) blob(layer);
95
+ if (image.kind === "compose") {
96
+ verifyCompose(document, image, manifest, read, check);
97
+ } else if (
98
+ entry.annotations?.["vnd.docker.reference.type"] ===
99
+ "attestation-manifest"
100
+ ) {
101
+ check(
102
+ entry.platform?.os === "unknown" &&
103
+ entry.platform?.architecture === "unknown" &&
104
+ document.layers.length > 0 &&
105
+ document.layers.every(
106
+ (layer) => layer.mediaType === "application/vnd.in-toto+json",
107
+ ),
108
+ "invalid image attestation",
109
+ );
110
+ attestations.push({
111
+ target: entry.annotations["vnd.docker.reference.digest"],
112
+ statements: document.layers.map(read),
113
+ });
114
+ } else {
115
+ const platform = `${config.os}/${config.architecture}`;
116
+ check(
117
+ /^linux\/(amd64|arm64)$/u.test(platform) &&
118
+ (!entry.platform ||
119
+ platform ===
120
+ `${entry.platform.os}/${entry.platform.architecture}`),
121
+ "OCI platform mismatch",
122
+ );
123
+ check(
124
+ !platforms.some((found) => found.platform === platform),
125
+ "duplicate image platform",
126
+ );
127
+ platforms.push({ platform, digest: entry.digest });
128
+ verifyProvenance(config);
129
+ }
130
+ }
131
+ manifests.push(entry);
132
+ }
133
+ visit(descriptor);
134
+ if (image.kind !== "compose") {
135
+ const expected =
136
+ image.platform === "multi-platform" ? image.platforms : [image.platform];
137
+ check(
138
+ Array.isArray(expected) &&
139
+ expected.length > 0 &&
140
+ new Set(expected).size === expected.length &&
141
+ JSON.stringify([...expected].sort()) ===
142
+ JSON.stringify(platforms.map((entry) => entry.platform).sort()),
143
+ "incomplete image platform family",
144
+ );
145
+ for (const attestation of attestations) {
146
+ check(
147
+ platforms.some((entry) => entry.digest === attestation.target) &&
148
+ attestation.statements.every((statement) =>
149
+ statement.subject?.some(
150
+ (subject) =>
151
+ `sha256:${subject.digest?.sha256}` === attestation.target,
152
+ ),
153
+ ),
154
+ "image attestation subject mismatch",
155
+ );
156
+ }
157
+ }
158
+ return { manifests, blobs: [...blobs.values()], platforms };
159
+ }
160
+
161
+ function verifyCompose(document, image, manifest, read, check) {
162
+ if (image.preview)
163
+ check(
164
+ image.preview.alias === "compose-preview" &&
165
+ /^\.github\/workflows\/[a-z0-9-]+\.yml$/u.test(
166
+ image.preview.qualificationWorkflow,
167
+ ) &&
168
+ /^(none|sha256:[0-9a-f]{64})$/u.test(image.preview.previousDigest),
169
+ "invalid Compose preview policy",
170
+ );
171
+ check(
172
+ image.platform === "compose" &&
173
+ document.artifactType === composeType &&
174
+ document.config.mediaType === "application/vnd.oci.empty.v1+json" &&
175
+ document.layers.length === 1 &&
176
+ document.layers[0].mediaType ===
177
+ "application/vnd.docker.compose.file+yaml",
178
+ "unsupported Compose artifact",
179
+ );
180
+ // JSON is a YAML subset; this bounded public form needs no executable YAML loader.
181
+ const project = read(document.layers[0]);
182
+ check(
183
+ project.services && Object.keys(project.services).length > 0,
184
+ "empty Compose application",
185
+ );
186
+ const target = manifest.images.find(
187
+ (entry) => entry.name === image.targetImage,
188
+ );
189
+ const references = Object.values(project.services).map(
190
+ (service) => service.image,
191
+ );
192
+ check(
193
+ references.every(
194
+ (ref) =>
195
+ typeof ref === "string" &&
196
+ /^[a-z0-9./:_-]+@sha256:[0-9a-f]{64}$/u.test(ref),
197
+ ) && references.includes(`${target.repository}@${target.digest}`),
198
+ "Compose images must bind exact digests",
199
+ );
200
+ check(
201
+ image.content?.sourceSha === manifest.sourceSha &&
202
+ image.content?.version === manifest.version &&
203
+ image.action === "built",
204
+ "Compose candidate provenance mismatch",
205
+ );
206
+ }
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { paperRuntimeSourceMatches } from "./paper-runtime-channels.js";
3
4
 
4
5
  import {
5
6
  PAPER_PATHS,
@@ -12,7 +13,13 @@ import {
12
13
  stableJson,
13
14
  workCheck,
14
15
  } from "./paper-repository.js";
15
- import { mergeNextDevelopmentAgentInstructions } from "./next-development-projection.js";
16
+ import {
17
+ mergeNextDevelopmentAgentInstructions,
18
+ NEXT_DEVELOPMENT_AGENT_SECTION_START,
19
+ NEXT_DEVELOPMENT_AGENT_SECTION_END,
20
+ NEXT_DEVELOPMENT_LOCAL_COMMAND,
21
+ } from "./next-development-projection.js";
22
+ import { NEXT_DEVELOPMENT_ADR } from "./next-development-transition.js";
16
23
 
17
24
  export const PAPER_AGENT_ENTRY_CONTRACT = "kungfu-buildchain-paper-agent-entry";
18
25
  export const PAPER_AGENT_ENTRY_SCHEMA_VERSION = 1;
@@ -148,6 +155,24 @@ export function createPaperAgentEntry({
148
155
  };
149
156
  }
150
157
 
158
+ function mergePaperNextDevelopmentInstructions(current) {
159
+ const merged = mergeNextDevelopmentAgentInstructions(current);
160
+ const start = merged.indexOf(NEXT_DEVELOPMENT_AGENT_SECTION_START);
161
+ const end = merged.indexOf(NEXT_DEVELOPMENT_AGENT_SECTION_END);
162
+ const section = merged.slice(start, end)
163
+ .replace(
164
+ `\`${NEXT_DEVELOPMENT_ADR}\``,
165
+ `[Buildchain next-development ADR](https://github.com/kungfu-systems/buildchain/blob/v4/${NEXT_DEVELOPMENT_ADR})`,
166
+ )
167
+ .replace(
168
+ NEXT_DEVELOPMENT_LOCAL_COMMAND,
169
+ NEXT_DEVELOPMENT_LOCAL_COMMAND.replace(
170
+ "node scripts/", "node node_modules/@kungfu-tech/buildchain/scripts/",
171
+ ),
172
+ );
173
+ return `${merged.slice(0, start)}${section}${merged.slice(end)}`;
174
+ }
175
+
151
176
  export function paperAgentEntryFiles({
152
177
  cwd,
153
178
  buildchainVersion,
@@ -168,7 +193,7 @@ export function paperAgentEntryFiles({
168
193
  [PAPER_PATHS.agentEntry, jsonText(entry)],
169
194
  [
170
195
  PAPER_PATHS.agentInstructions,
171
- mergeNextDevelopmentAgentInstructions(
196
+ mergePaperNextDevelopmentInstructions(
172
197
  mergePaperAgentEntryInstructions(currentAgents, {
173
198
  developmentRef: resolvedDevelopmentRef,
174
199
  }),
@@ -256,6 +281,7 @@ export function collectPaperAgentEntry({
256
281
  buildchainSha = "",
257
282
  mode = "contract",
258
283
  env = process.env,
284
+ runtimeAdmission,
259
285
  } = {}) {
260
286
  const resolvedCwd = path.resolve(cwd);
261
287
  const developmentRef = paperDevelopmentRef(resolvedCwd);
@@ -311,8 +337,8 @@ export function collectPaperAgentEntry({
311
337
  workCheck(
312
338
  "agent-entry.runtime-source",
313
339
  GIT_SHA_PATTERN.test(effectiveBuildchainSha) &&
314
- entry.runtime?.sourceSha === effectiveBuildchainSha,
315
- "The agent-entry contract is bound to the exact executing Buildchain source SHA.",
340
+ paperRuntimeSourceMatches(entry.runtime, effectiveBuildchainSha, mode, runtimeAdmission),
341
+ "The executing Buildchain source is pinned locally or admitted by its v4 channel contract in CI.",
316
342
  "buildchain paper migrate --write --json",
317
343
  ),
318
344
  workCheck(
@@ -128,7 +128,7 @@ function paperFleetEntry({
128
128
  workCheck(
129
129
  "managed-surfaces.current",
130
130
  !expected.error && managed.every((entry) => entry.status === "current"),
131
- "Every Buildchain-owned paper control surface matches v3.",
131
+ "Every Buildchain-owned paper control surface matches the selected runtime.",
132
132
  "buildchain paper migrate --write --json",
133
133
  ),
134
134
  workCheck(