@kungfu-tech/buildchain 4.1.0-alpha.0 → 4.1.0-alpha.2

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 (295) hide show
  1. package/AGENTS.md +30 -41
  2. package/CONTRIBUTING.md +6 -7
  3. package/architecture/action-taxonomy.json +5 -21
  4. package/architecture/agent-change-map.md +74 -56
  5. package/architecture/build-orchestration.json +8 -1
  6. package/architecture/ci-lane-change-budget.json +179 -69
  7. package/architecture/floating-consumer-policy.json +7 -17
  8. package/architecture/internal-capabilities.json +58 -72
  9. package/architecture/maintainability-debt.json +138 -52
  10. package/architecture/maintainability-policy.json +6 -15
  11. package/architecture/promotion-job-topology.json +39 -20
  12. package/architecture/release-topology.json +26 -37
  13. package/architecture/runtime-entry.json +26 -0
  14. package/architecture/runtime-resume-lineage.json +23 -0
  15. package/architecture/stage-capsule-qualification.json +2 -2
  16. package/architecture/universal-workflow-bootstrap.json +10 -25
  17. package/architecture/universal-workflow-capability-policy.json +61 -0
  18. package/architecture/universal-workflow-fault-campaign.json +18 -24
  19. package/architecture/workflow-taxonomy.json +0 -11
  20. package/contracts/fixtures/{v4-runtime-ref-resume-authority-v1 → runtime-resume-lineage-v2}/scenario.json +2 -3
  21. package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +2 -2
  22. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  23. package/contracts/promotion-invocation-v1.schema.json +0 -44
  24. package/contracts/promotion-request-v1.schema.json +0 -39
  25. package/contracts/runtime-resume-lineage-v2.schema.json +182 -0
  26. package/dist/site/buildchain-contract.json +472 -361
  27. package/dist/site/buildchain-site.json +99 -99
  28. package/dist/site/capability-registry.json +3 -3
  29. package/dist/site/cli-registry.json +4 -4
  30. package/dist/site/controller-registry.json +94 -78
  31. package/dist/site/kfd-claims.json +224 -343
  32. package/dist/site/kfd-upstream-aggregate.json +1 -1
  33. package/dist/site/manual-registry.json +15 -15
  34. package/dist/site/node-api-registry.json +239 -354
  35. package/dist/site/page-registry.json +80 -80
  36. package/dist/site/public-surface-audit.json +203 -254
  37. package/dist/site/publication-authority-registry.json +1 -24
  38. package/dist/site/publication-registry.json +4 -4
  39. package/dist/site/release-provenance.json +1 -1
  40. package/dist/site/site-manifest.json +19 -19
  41. package/dist/site/workflow-registry.json +197 -357
  42. package/docs/MAP.md +3 -3
  43. package/docs/auditable-demo.md +4 -4
  44. package/docs/cli-reference.md +2 -2
  45. package/docs/code-organization.md +5 -4
  46. package/docs/controller-evidence.md +2 -2
  47. package/docs/dev-alpha-candidate-patrol.md +5 -5
  48. package/docs/dev-delivery-warrant.md +14 -24
  49. package/docs/engineering-housekeeper.md +3 -3
  50. package/docs/github-artifact-attestation.md +9 -12
  51. package/docs/github-governance-authority.md +4 -4
  52. package/docs/node-api-reference.md +153 -167
  53. package/docs/publication-artifacts.md +3 -4
  54. package/docs/publication-authority.md +2 -2
  55. package/docs/release-candidate.md +21 -27
  56. package/docs/release-governance.md +10 -10
  57. package/docs/release-promotion-request.md +11 -18
  58. package/docs/release-propagation.md +5 -6
  59. package/docs/release-tail-provider-plane.md +2 -3
  60. package/docs/reusable-build-surface.md +13 -7
  61. package/docs/runtime-entry.md +122 -0
  62. package/docs/runtime-train-validation.md +30 -41
  63. package/docs/v4-publication-rehearsal.md +5 -5
  64. package/docs/v4-stage-capsule.md +9 -10
  65. package/docs/web-surface-deployments.md +16 -20
  66. package/docs/workflow-catalog.md +0 -1
  67. package/package.json +6 -6
  68. package/packages/core/adoption/commands/init-repo.mjs +2 -2
  69. package/packages/core/adoption/cross-platform-adopter-qualification.js +2 -13
  70. package/packages/core/adoption/qualification/actions.js +3 -5
  71. package/packages/core/adoption/qualification/admission.js +2 -3
  72. package/packages/core/build/artifact/attestation.js +0 -1
  73. package/packages/core/build/artifact/contracts.js +1 -4
  74. package/packages/core/build/artifact/store.js +6 -5
  75. package/packages/core/build/artifact-signing.js +4 -10
  76. package/packages/core/build/binary/actions.js +15 -9
  77. package/packages/core/build/binary-publication-dispatch.js +2 -6
  78. package/packages/core/build/demo/action-context.js +2 -11
  79. package/packages/core/build/demo/binary-action.js +8 -10
  80. package/packages/core/build/gate/controller.js +0 -2
  81. package/packages/core/build/github-attestation/action.js +2 -7
  82. package/packages/core/build/github-attestation/admission.js +0 -8
  83. package/packages/core/build/github-attestation/transaction.js +1 -2
  84. package/packages/core/build/lifecycle/stage-action.js +2 -0
  85. package/packages/core/build/lifecycle/stage.js +6 -3
  86. package/packages/core/build/macos-credential-island/dmg-assembly.js +0 -2
  87. package/packages/core/build/macos-credential-island/lib.js +2 -6
  88. package/packages/core/build/plan/action-context.js +0 -7
  89. package/packages/core/build/plan/action.js +7 -11
  90. package/packages/core/build/plan/admission.js +6 -73
  91. package/packages/core/build/plan/configuration.js +1 -1
  92. package/packages/core/build/plan/environment.js +1 -1
  93. package/packages/core/build/plan/identity.js +1 -5
  94. package/packages/core/build/plan/resolve.js +7 -1
  95. package/packages/core/build/recovery/checkpoint.js +158 -0
  96. package/packages/core/build/recovery/transaction.js +51 -0
  97. package/packages/core/build/signing/authority-actions.js +2 -11
  98. package/packages/core/build/signing/control.js +0 -5
  99. package/packages/core/build/signing/controller-transaction.js +0 -1
  100. package/packages/core/build/signing/delegation.js +2 -4
  101. package/packages/core/build/signing/dispatch.js +10 -56
  102. package/packages/core/build/signing/intake.js +0 -4
  103. package/packages/core/build/signing/prepare.js +1 -1
  104. package/packages/core/build/signing/request.js +3 -8
  105. package/packages/core/build/source/checkout-action.js +6 -10
  106. package/packages/core/build/source/controller.js +1 -4
  107. package/packages/core/build/source/lifecycle.js +3 -13
  108. package/packages/core/build/stage-capsule/actions.js +3 -13
  109. package/packages/core/build/stage-capsule/campaign/resume.js +10 -5
  110. package/packages/core/build/stage-capsule/rehearsal/verify.js +3 -4
  111. package/packages/core/build/stage-capsule-resume-planner.js +0 -1
  112. package/packages/core/build/summary/aggregation.js +5 -5
  113. package/packages/core/build/summary/finalization.js +1 -1
  114. package/packages/core/build/verification/actions.js +3 -12
  115. package/packages/core/build/verification/runner.js +22 -20
  116. package/packages/core/consumer/floating-consumer-evidence.js +3 -21
  117. package/packages/core/consumer/floating-consumer-policy.js +2 -93
  118. package/packages/core/consumer/floating-consumer-release-passport.js +1 -34
  119. package/packages/core/consumer/policy-admission.js +1 -3
  120. package/packages/core/contracts/buildchain-contract.js +12 -146
  121. package/packages/core/contracts/workflow-yaml-contract.js +11 -1
  122. package/packages/core/dev-delivery/candidate/admission.js +3 -4
  123. package/packages/core/dev-delivery/candidate/coordinates.js +0 -8
  124. package/packages/core/dev-delivery/commands/dev-delivery-request.sh +5 -13
  125. package/packages/core/dev-delivery/commands/dev-delivery-source-proof-reuse.mjs +0 -3
  126. package/packages/core/dev-delivery/dev-delivery-contract-surface.js +4 -52
  127. package/packages/core/dev-delivery/dev-delivery-execution-transfer.js +2 -15
  128. package/packages/core/dev-delivery/native/action-context.js +4 -32
  129. package/packages/core/dev-delivery/native/provider-boundary.js +0 -2
  130. package/packages/core/dev-delivery/native/transactions.js +1 -2
  131. package/packages/core/dev-delivery/queue/reconciliation-action.js +6 -10
  132. package/packages/core/dev-delivery/source-proof/predicates.js +0 -6
  133. package/packages/core/dev-delivery/source-proof/reuse.js +0 -2
  134. package/packages/core/dev-delivery/source-proof/seal.js +0 -2
  135. package/packages/core/governance/alpha-candidate/action.js +1 -10
  136. package/packages/core/governance/audit/actions.js +1 -3
  137. package/packages/core/governance/audit/collection.js +1 -3
  138. package/packages/core/governance/audit/identity.js +3 -31
  139. package/packages/core/governance/audit/transactions.js +2 -4
  140. package/packages/core/governance/buildchain-publication-authority.js +0 -1
  141. package/packages/core/governance/commands/audit-github-governance.mjs +3 -2
  142. package/packages/core/governance/github-governance-authority.js +0 -5
  143. package/packages/core/governance/receipt-admission.js +0 -15
  144. package/packages/core/index.js +1 -1
  145. package/packages/core/observability/controller/identity.js +4 -5
  146. package/packages/core/observability/controller-evidence.js +5 -10
  147. package/packages/core/paper/commands/paper.mjs +6 -6
  148. package/packages/core/paper/operations/plans.js +2 -2
  149. package/packages/core/paper/operations/provisioning-validation.js +1 -1
  150. package/packages/core/paper/operations/scaffold-workflows.js +29 -12
  151. package/packages/core/paper/operations/scaffold.js +97 -69
  152. package/packages/core/paper/paper-agent-entry.js +0 -11
  153. package/packages/core/paper/paper-runtime-channels.js +21 -66
  154. package/packages/core/paper/paper-scaffold-content.js +1 -1
  155. package/packages/core/paper/paper.js +9 -5
  156. package/packages/core/paper/publication/candidate.js +0 -9
  157. package/packages/core/paper/publication-controller.js +2 -4
  158. package/packages/core/providers/github/artifact-attestation.js +1 -1
  159. package/packages/core/publication/authority/actions.js +9 -8
  160. package/packages/core/publication/authority/admission.js +0 -11
  161. package/packages/core/publication/authority/assembly.js +2 -7
  162. package/packages/core/publication/authority/control-plane.js +2 -2
  163. package/packages/core/publication/authority/governance.js +2 -9
  164. package/packages/core/publication/authority/request.js +7 -2
  165. package/packages/core/publication/authority/verification.js +3 -18
  166. package/packages/core/publication/binary/action.js +5 -7
  167. package/packages/core/publication/candidate/qualification-action.js +1 -6
  168. package/packages/core/publication/candidate/stage-capsules.js +1 -1
  169. package/packages/core/publication/npm/preview-action.js +9 -15
  170. package/packages/core/publication/oci/preview-actions.js +1 -10
  171. package/packages/core/publication/publication-artifact-candidate.js +2 -2
  172. package/packages/core/publication/publication-authority.js +2 -2
  173. package/packages/core/publication/publication-rehearsal-projection.js +3 -3
  174. package/packages/core/publication/settlement/actions.js +2 -11
  175. package/packages/core/release/binary/coordinates-action.js +0 -13
  176. package/packages/core/release/binary/coordinates.js +2 -19
  177. package/packages/core/release/commands/promotion-channel-router.mjs +12 -56
  178. package/packages/core/release/commands/resume-from-candidate-run.mjs +2 -3
  179. package/packages/core/release/dev-alpha-active-release-train.js +0 -5
  180. package/packages/core/release/line/action.js +2 -13
  181. package/packages/core/release/next-development/actions.js +2 -5
  182. package/packages/core/release/passport/consumer-validation.js +1 -28
  183. package/packages/core/release/promote-candidate/development-source.js +7 -8
  184. package/packages/core/release/promotion/actions.js +7 -38
  185. package/packages/core/release/promotion/admission.js +3 -50
  186. package/packages/core/release/promotion/candidate.js +2 -4
  187. package/packages/core/release/promotion/channel.js +13 -101
  188. package/packages/core/release/promotion/qualification-action.js +2 -13
  189. package/packages/core/release/promotion/routing.js +16 -81
  190. package/packages/core/release/promotion/selection.js +1 -9
  191. package/packages/core/release/promotion-request.js +1 -40
  192. package/packages/core/release/qualification/actions.js +0 -8
  193. package/packages/core/release/qualification/public-build.js +1 -1
  194. package/packages/core/release/recovery/candidate.js +15 -21
  195. package/packages/core/release/recovery/capsules.js +2 -10
  196. package/packages/core/release/recovery/lineage.js +305 -0
  197. package/packages/core/release/recovery/provenance.js +1 -2
  198. package/packages/core/release/recovery/publication.js +4 -14
  199. package/packages/core/release/recovery/readback.js +19 -32
  200. package/packages/core/release/recovery/runtime.js +4 -95
  201. package/packages/core/release/release-candidate-recovery.js +4 -9
  202. package/packages/core/release/release-candidate.js +2 -5
  203. package/packages/core/release/reseal/actions.js +3 -22
  204. package/packages/core/release/reseal/admission.js +0 -2
  205. package/packages/core/release/reseal/finalize-platform.js +0 -2
  206. package/packages/core/release/tail-reseal-github.js +1 -2
  207. package/packages/core/release/tail-reseal-receipt.js +0 -1
  208. package/packages/core/release/tail-reseal.js +2 -20
  209. package/packages/core/release/version-state.js +16 -13
  210. package/packages/core/runtime/buildchain-domain.wasm +0 -0
  211. package/packages/core/runtime/checkout-identity.js +0 -13
  212. package/packages/core/runtime/domain-wasm-artifact.js +2 -2
  213. package/packages/core/runtime/entry/actions.js +121 -0
  214. package/packages/core/runtime/entry/activation.js +86 -0
  215. package/packages/core/runtime/entry/github.js +55 -0
  216. package/packages/core/runtime/entry/recovery.js +37 -0
  217. package/packages/core/runtime/entry/selection.js +110 -0
  218. package/packages/core/web/controller.js +1 -1
  219. package/packages/core/web/publication-admission-action.js +1 -8
  220. package/packages/core/web/web-surface-publication-candidate.js +2 -5
  221. package/packages/core/workflow/admission/actions.js +6 -26
  222. package/packages/core/workflow/admission/recovery.js +3 -43
  223. package/packages/core/workflow/admission/transactions.js +8 -24
  224. package/packages/core/workflow/commands/buildchain-cli-help.mjs +1 -1
  225. package/packages/core/workflow/commands/universal-workflow-engine.mjs +2 -7
  226. package/packages/core/workflow/dogfood/actions.js +0 -17
  227. package/packages/core/workflow/engine/action.js +0 -5
  228. package/packages/core/workflow/engine/conformance.js +1 -1
  229. package/packages/core/workflow/engine/execution.js +2 -6
  230. package/packages/core/workflow/engine/identity.js +1 -6
  231. package/packages/core/workflow/engine/release-environment.js +11 -35
  232. package/packages/core/workflow/engine/release-promotion.js +2 -2
  233. package/packages/core/workflow/settlement/actions.js +0 -12
  234. package/packages/core/workflow/universal-self-dogfood.js +2 -16
  235. package/packages/core/workflow/universal-workflow-bootstrap.js +16 -241
  236. package/packages/core/workflow/workflow-taxonomy.mjs +2 -2
  237. package/scripts/check-dev-delivery-actions.mjs +17 -8
  238. package/scripts/check-floating-consumer-policy-contract.mjs +6 -6
  239. package/scripts/check-inventory.mjs +1 -10
  240. package/scripts/check-public-dogfood-contract.mjs +34 -21
  241. package/scripts/check-release-topology.mjs +2 -6
  242. package/scripts/check-runtime-entry.mjs +133 -0
  243. package/scripts/check-universal-workflow-bootstrap.mjs +45 -281
  244. package/scripts/check-workflows.sh +0 -7
  245. package/scripts/generate-bootstrap-admission.mjs +1 -1
  246. package/scripts/generate-channel-promotion-workflow.mjs +4 -4
  247. package/scripts/inventory/binary.mjs +4 -4
  248. package/scripts/inventory/promotion.mjs +5 -3
  249. package/scripts/runtime-source-boundaries.mjs +70 -0
  250. package/scripts/site-capability-metadata.mjs +2 -2
  251. package/scripts/workflow-action-graph.mjs +5 -18
  252. package/scripts/workflow-self-reference.mjs +20 -0
  253. package/templates/native-dev-delivery.yml +4 -4
  254. package/templates/universal-buildchain-bootstrap.yml +12 -2
  255. package/architecture/runtime-ref-resume-authority.json +0 -64
  256. package/architecture/universal-workflow-train-admission.json +0 -76
  257. package/contracts/v4-runtime-ref-resume-authority-v1.schema.json +0 -235
  258. package/docs/v4-runtime-ref-resume-authority.md +0 -69
  259. package/packages/core/consumer/channel-selection.js +0 -211
  260. package/packages/core/consumer/commands/buildchain-channel-router.mjs +0 -81
  261. package/packages/core/consumer/invocation-selector.js +0 -4
  262. package/packages/core/consumer/runtime-ref-resume-authority.js +0 -621
  263. package/packages/core/consumer/workflow-definition-authority.js +0 -64
  264. package/packages/core/contracts/runtime-contract-action.js +0 -48
  265. package/packages/core/release/commands/promotion-identity-resolver.mjs +0 -77
  266. package/packages/core/release/commands/verify-promotion-router-binding.sh +0 -36
  267. package/packages/core/release/promotion/identities.js +0 -56
  268. package/packages/core/release/runtime-override-authorization.js +0 -242
  269. package/packages/core/runtime/commands/runtime-ref-core.mjs +0 -171
  270. package/packages/core/runtime/provider-closure.js +0 -60
  271. package/packages/core/runtime/runtime-selection-action.js +0 -34
  272. package/packages/core/runtime/runtime-selection.js +0 -136
  273. package/packages/core/runtime/workflow-runtime.js +0 -11
  274. package/packages/core/workflow/admission/review.js +0 -188
  275. package/packages/core/workflow/commands/universal-workflow-backflow.mjs +0 -26
  276. package/packages/core/workflow/installation.js +0 -33
  277. package/packages/core/workflow/settlement/backflow.js +0 -187
  278. package/scripts/generate-bootstrap-recovery.mjs +0 -94
  279. package/templates/bootstrap-recovery/actions/build/source/admit/action.yml +0 -17
  280. package/templates/bootstrap-recovery/actions/build/source/admit/dist/index.js +0 -63
  281. package/templates/bootstrap-recovery/actions/runtime/dependencies/install/action.yml +0 -18
  282. package/templates/bootstrap-recovery/actions/runtime/dependencies/install/dist/index.js +0 -63
  283. package/templates/bootstrap-recovery/actions/runtime/environment/prepare/action.yml +0 -21
  284. package/templates/bootstrap-recovery/actions/workflow/admission/inspect/action.yml +0 -27
  285. package/templates/bootstrap-recovery/actions/workflow/admission/inspect/dist/index.js +0 -64
  286. package/templates/bootstrap-recovery/actions/workflow/admission/review/action.yml +0 -15
  287. package/templates/bootstrap-recovery/actions/workflow/admission/review/dist/index.js +0 -65
  288. package/templates/bootstrap-recovery/actions/workflow/recovery/admit/action.yml +0 -47
  289. package/templates/bootstrap-recovery/actions/workflow/recovery/execute/action.yml +0 -48
  290. package/templates/bootstrap-recovery/actions/workflow/recovery/settle/action.yml +0 -54
  291. package/templates/bootstrap-recovery/actions/workflow/settlement/seal/action.yml +0 -17
  292. package/templates/bootstrap-recovery/actions/workflow/settlement/seal/dist/index.js +0 -65
  293. package/templates/bootstrap-recovery/manifest.json +0 -68
  294. package/templates/bootstrap-recovery/package.json +0 -9
  295. package/templates/universal-buildchain-bootstrap-recovery.yml +0 -115
@@ -1,12 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
  export function exactRuntime(admission) {
3
3
  const runtime = admission?.runtime;
4
- if (
5
- admission?.status !== "admitted" ||
6
- runtime?.repository !== "kungfu-systems/buildchain" ||
7
- !/^[0-9a-f]{40}$/u.test(runtime?.sha || "")
8
- )
9
- fail("an exact admitted Buildchain runtime is required");
4
+ if (admission?.status !== "admitted") fail("Capability execution requires an admitted request");
10
5
  return runtime;
11
6
  }
12
7
  export function contentRoot(domain, value) {
@@ -9,7 +9,7 @@ export function prepareTrustedPublishingNpm() {
9
9
  !process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN
10
10
  )
11
11
  fail("trusted publishing requires GitHub Actions OIDC authority");
12
- const binDirectory = path.resolve(".buildchain/runtime/bin");
12
+ const binDirectory = path.resolve(".buildchain/tooling/trusted-publishing/bin");
13
13
  const npmShim = path.join(binDirectory, "npm");
14
14
  fs.mkdirSync(binDirectory, { recursive: true });
15
15
  fs.writeFileSync(
@@ -20,38 +20,14 @@ export function prepareTrustedPublishingNpm() {
20
20
  fs.chmodSync(npmShim, 0o755);
21
21
  process.env.PATH = `${binDirectory}${path.delimiter}${process.env.PATH || ""}`;
22
22
  }
23
- export function prepareReleasePromotionConsumerDependencies(repository) {
24
- if (repository !== "kungfu-systems/buildchain") return;
25
- const manifest = JSON.parse(fs.readFileSync("package.json", "utf8"));
26
- if (
27
- manifest.packageManager !== "pnpm@11.7.0" ||
28
- !fs.existsSync("pnpm-lock.yaml")
29
- )
30
- fail("Buildchain release consumer dependency lock is unavailable");
31
- const consumerModules = path.resolve("node_modules"),
32
- runtimeModules = path.resolve(".buildchain/runtime/node_modules");
33
- if (!fs.existsSync(consumerModules) && fs.existsSync(runtimeModules))
34
- fs.symlinkSync(
35
- path.relative(path.dirname(consumerModules), runtimeModules),
36
- consumerModules,
37
- "dir",
38
- );
39
- if (!fs.existsSync("node_modules/@kungfu-tech/kfd/package.json"))
40
- execFileSync(
41
- "corepack",
42
- "pnpm@11.7.0 install --frozen-lockfile --ignore-scripts".split(" "),
43
- { stdio: ["ignore", 2, 2] },
44
- );
45
- if (fs.existsSync(runtimeModules)) {
46
- if (fs.realpathSync(consumerModules) !== fs.realpathSync(runtimeModules))
47
- fail("Buildchain release consumer runtime bridge conflicts");
48
- return;
49
- }
50
- fs.mkdirSync(path.dirname(runtimeModules), { recursive: true });
51
- fs.renameSync(consumerModules, runtimeModules);
52
- fs.symlinkSync(
53
- path.relative(path.dirname(consumerModules), runtimeModules),
54
- consumerModules,
55
- "dir",
56
- );
23
+ export function prepareReleaseConsumerDependencies(directory = process.cwd(), execute = execFileSync) {
24
+ const file = path.join(directory, "package.json");
25
+ if (!fs.existsSync(file)) return;
26
+ const manifest = JSON.parse(fs.readFileSync(file, "utf8"));
27
+ const manager = String(manifest.packageManager || "");
28
+ if (!/^(?:npm|pnpm|yarn)@\d+\.\d+\.\d+(?:\+sha\d+\.[a-zA-Z0-9]+)?$/u.test(manager))
29
+ fail("Release consumer dependencies require a pinned packageManager");
30
+ const kind = manager.split("@")[0];
31
+ const args = kind === "npm" ? ["ci", "--ignore-scripts"] : kind === "pnpm" ? ["install", "--frozen-lockfile", "--ignore-scripts"] : /^yarn@1\./u.test(manager) ? ["install", "--frozen-lockfile", "--ignore-scripts"] : ["install", "--immutable", "--mode=skip-builds"];
32
+ execute("corepack", [manager, ...args], { cwd: directory, stdio: ["ignore", 2, 2] });
57
33
  }
@@ -16,7 +16,7 @@ import {
16
16
  } from "./release-observation.js";
17
17
  import {
18
18
  prepareTrustedPublishingNpm,
19
- prepareReleasePromotionConsumerDependencies,
19
+ prepareReleaseConsumerDependencies,
20
20
  } from "./release-environment.js";
21
21
  import { verifiedReleaseDocuments } from "./release-evidence.js";
22
22
  async function materializeProductPublicationIntent(
@@ -125,7 +125,7 @@ export async function executeReleasePromotion(request, admission, context) {
125
125
  fail(`release route blocked: ${route.reason}`);
126
126
  if (route.decision === "NoOp" || payload.inputs["dry-run"] === true)
127
127
  return { route, dryRun: payload.inputs["dry-run"] === true };
128
- prepareReleasePromotionConsumerDependencies(repository);
128
+ prepareReleaseConsumerDependencies();
129
129
  if (payload.inputs["trusted-publishing"] === true)
130
130
  prepareTrustedPublishingNpm();
131
131
  const candidate = await resolveReleaseCandidateArtifacts({
@@ -1,7 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { recoveryTerminalReceipt } from "../admission/recovery.js";
4
- import { upsertUniversalBackflow } from "./backflow.js";
5
4
  export function sealUniversalReceiptAction(core, env) {
6
5
  const admission = JSON.parse(
7
6
  core.getInput("admission-json", { required: true }),
@@ -21,14 +20,3 @@ export function sealUniversalReceiptAction(core, env) {
21
20
  "Candidate workflow failed after sealing its terminal receipt",
22
21
  );
23
22
  }
24
- export async function reconcileUniversalBackflowAction(core, env) {
25
- const result = await upsertUniversalBackflow({
26
- request: JSON.parse(core.getInput("request-json", { required: true })),
27
- receipt: JSON.parse(core.getInput("receipt-json", { required: true })),
28
- token: core.getInput("token", { required: true }),
29
- });
30
- const file = path.join(env.GITHUB_WORKSPACE, ".buildchain/backflow.json");
31
- fs.mkdirSync(path.dirname(file), { recursive: true });
32
- fs.writeFileSync(file, JSON.stringify(result, null, 2) + "\n");
33
- core.setOutput("backflow-root", result.backflowRoot || "");
34
- }
@@ -33,7 +33,7 @@ function contentRoot(domain, value) {
33
33
  return `sha256:${hash.digest("hex")}`;
34
34
  }
35
35
 
36
- function defaultPolicy() { return JSON.parse(fs.readFileSync(path.join(installationRoot(import.meta.url), "architecture/universal-workflow-train-admission.json"), "utf8")); }
36
+ function defaultPolicy() { return JSON.parse(fs.readFileSync(path.join(installationRoot(import.meta.url), "architecture/universal-workflow-capability-policy.json"), "utf8")); }
37
37
 
38
38
  function exactSha(value, label) {
39
39
  const normalized = String(value || "").toLowerCase();
@@ -42,16 +42,11 @@ function exactSha(value, label) {
42
42
  }
43
43
 
44
44
  export function createUniversalSelfDogfoodRequest({
45
- candidateSha,
46
45
  consumerSha,
47
- pullRequest,
48
46
  channel,
49
47
  policy = defaultPolicy(),
50
48
  }) {
51
- const expectedSha = exactSha(candidateSha, "candidateSha");
52
49
  const sourceSha = exactSha(consumerSha, "consumerSha");
53
- if (!Number.isSafeInteger(pullRequest) || pullRequest < 1)
54
- fail("pullRequest must be a positive integer");
55
50
  if (!["alpha", "stable", "conformance"].includes(channel))
56
51
  fail("channel must be alpha, stable, or conformance");
57
52
  const capability =
@@ -85,14 +80,6 @@ export function createUniversalSelfDogfoodRequest({
85
80
  };
86
81
  return validateUniversalWorkflowRequest({
87
82
  schema: UNIVERSAL_WORKFLOW_REQUEST,
88
- mode: "exact",
89
- candidate: {
90
- repository: "kungfu-systems/buildchain",
91
- discoveryRef: expectedSha,
92
- expectedSha,
93
- admissionRoot: universalWorkflowAdmissionRoot(policy),
94
- reviewPullRequest: pullRequest,
95
- },
96
83
  consumer: {
97
84
  repository: "kungfu-systems/buildchain",
98
85
  workflow: ".github/workflows/self-ops-bootstrap-dogfood.yml",
@@ -111,8 +98,7 @@ function exactResult(value, label, expectedSha, expectedChannel) {
111
98
  if (
112
99
  value?.schema !== "kungfu-buildchain-v4-universal-workflow-result/v1" ||
113
100
  value.status !== "succeeded" ||
114
- value.runtime?.repository !== "kungfu-systems/buildchain" ||
115
- value.runtime?.sha !== expectedSha
101
+ value.runtime?.repository !== "kungfu-systems/buildchain"
116
102
  )
117
103
  fail(`${label} is not a successful exact-candidate result`);
118
104
  if (expectedChannel === "conformance") {
@@ -1,13 +1,13 @@
1
1
  import crypto from "node:crypto";
2
2
 
3
3
  export const UNIVERSAL_WORKFLOW_REQUEST =
4
- "kungfu-buildchain-v4-universal-workflow-request/v1";
4
+ "buildchain.universal-workflow-request/v2";
5
5
  export const UNIVERSAL_WORKFLOW_ADMISSION_POLICY =
6
- "kungfu-buildchain-v4-universal-workflow-admission-policy/v1";
6
+ "buildchain.universal-workflow-admission-policy/v2";
7
7
  export const UNIVERSAL_WORKFLOW_ADMISSION =
8
- "kungfu-buildchain-v4-universal-workflow-admission/v1";
8
+ "buildchain.universal-workflow-admission/v2";
9
9
  export const UNIVERSAL_WORKFLOW_TERMINAL_RECEIPT =
10
- "kungfu-buildchain-v4-universal-workflow-terminal-receipt/v1";
10
+ "buildchain.universal-workflow-terminal-receipt/v2";
11
11
  const EXACT_SHA = /^[0-9a-f]{40}$/u;
12
12
  const ROOT = /^sha256:[0-9a-f]{64}$/u;
13
13
  const REPOSITORY = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
@@ -169,45 +169,11 @@ function timestamp(value, label) {
169
169
  export function validateUniversalWorkflowRequest(value) {
170
170
  exactKeys(
171
171
  value,
172
- ["schema", "mode", "candidate", "consumer", "capability", "payload"],
172
+ ["schema", "consumer", "capability", "payload"],
173
173
  "request",
174
174
  );
175
175
  if (value.schema !== UNIVERSAL_WORKFLOW_REQUEST)
176
176
  fail("unsupported-request-schema", "request schema is unsupported");
177
- if (!MODES.has(value.mode))
178
- fail("unsupported-mode", "request mode is unsupported");
179
- exactKeys(
180
- value.candidate,
181
- [
182
- "repository",
183
- "discoveryRef",
184
- "expectedSha",
185
- "admissionRoot",
186
- "reviewPullRequest",
187
- ],
188
- "request.candidate",
189
- );
190
- const candidateRepository = repository(
191
- value.candidate.repository,
192
- "request.candidate.repository",
193
- );
194
- if (candidateRepository !== "kungfu-systems/buildchain")
195
- fail(
196
- "untrusted-candidate-repository",
197
- "candidate repository must be kungfu-systems/buildchain",
198
- );
199
- const discoveryRef = nonEmpty(
200
- value.candidate.discoveryRef,
201
- "request.candidate.discoveryRef",
202
- ).replace(/^refs\/(?:heads|tags)\//u, "");
203
- if (value.mode === "train" && !TRAIN.test(discoveryRef))
204
- fail("invalid-train-ref", "train mode requires a governed v4 Train ref");
205
- if (value.mode === "exact" && !EXACT_SHA.test(discoveryRef.toLowerCase()))
206
- fail("invalid-exact-ref", "exact mode requires an exact Git SHA selector");
207
- if (value.mode === "alpha" && discoveryRef !== "v4-alpha")
208
- fail("invalid-alpha-ref", "alpha mode requires v4-alpha discovery");
209
- if (value.mode === "stable" && discoveryRef !== "v4")
210
- fail("invalid-stable-ref", "stable mode requires v4 discovery");
211
177
  exactKeys(
212
178
  value.consumer,
213
179
  ["repository", "workflow", "sourceSha"],
@@ -224,23 +190,6 @@ export function validateUniversalWorkflowRequest(value) {
224
190
  canonical(value.payload);
225
191
  return {
226
192
  schema: value.schema,
227
- mode: value.mode,
228
- candidate: {
229
- repository: candidateRepository,
230
- discoveryRef,
231
- expectedSha: exactSha(
232
- value.candidate.expectedSha,
233
- "request.candidate.expectedSha",
234
- ),
235
- admissionRoot: root(
236
- value.candidate.admissionRoot,
237
- "request.candidate.admissionRoot",
238
- ),
239
- reviewPullRequest: positiveInteger(
240
- value.candidate.reviewPullRequest,
241
- "request.candidate.reviewPullRequest",
242
- ),
243
- },
244
193
  consumer: {
245
194
  repository: repository(
246
195
  value.consumer.repository,
@@ -290,10 +239,6 @@ function validatePolicy(value) {
290
239
  "allowedCapabilities",
291
240
  "permissionCeiling",
292
241
  "contractRoots",
293
- "targetRef",
294
- "allowedReviewers",
295
- "minimumApprovals",
296
- "requiredChecks",
297
242
  "validFrom",
298
243
  "expiresAt",
299
244
  ],
@@ -323,19 +268,6 @@ function validatePolicy(value) {
323
268
  "policy.permissionCeiling",
324
269
  ),
325
270
  contractRoots: sortedRoots(value.contractRoots, "policy.contractRoots"),
326
- targetRef: nonEmpty(value.targetRef, "policy.targetRef"),
327
- allowedReviewers: sortedTokens(
328
- value.allowedReviewers,
329
- "policy.allowedReviewers",
330
- ),
331
- minimumApprovals: positiveInteger(
332
- value.minimumApprovals,
333
- "policy.minimumApprovals",
334
- ),
335
- requiredChecks: sortedStrings(
336
- value.requiredChecks,
337
- "policy.requiredChecks",
338
- ),
339
271
  validFrom: timestamp(value.validFrom, "policy.validFrom"),
340
272
  expiresAt: timestamp(value.expiresAt, "policy.expiresAt"),
341
273
  };
@@ -344,56 +276,6 @@ function validatePolicy(value) {
344
276
  return policy;
345
277
  }
346
278
 
347
- function validateReviewEvidence(value) {
348
- const hasBinding = Object.hasOwn(value || {}, "runtimeBinding");
349
- exactKeys(value, ["repository", "pullRequest", "headSha", "baseRef", "approvals", "checks", "observedAt", ...(hasBinding ? ["runtimeBinding"] : [])], "reviewEvidence");
350
- const approvals = value.approvals.map((approval, index) => {
351
- exactKeys(approval, ["reviewer", "commitSha", "submittedAt"], `reviewEvidence.approvals[${index}]`);
352
- return {
353
- reviewer: nonEmpty(approval.reviewer, `reviewEvidence.approvals[${index}].reviewer`),
354
- commitSha: exactSha(approval.commitSha, `reviewEvidence.approvals[${index}].commitSha`),
355
- submittedAt: timestamp(approval.submittedAt, `reviewEvidence.approvals[${index}].submittedAt`),
356
- };
357
- });
358
- const checks = value.checks.map((check, index) => {
359
- const hasCommit = Object.hasOwn(check || {}, "commitSha");
360
- exactKeys(check, ["name", "status", "conclusion", ...(hasCommit ? ["commitSha"] : [])], `reviewEvidence.checks[${index}]`);
361
- return {
362
- name: nonEmpty(check.name, `reviewEvidence.checks[${index}].name`),
363
- status: nonEmpty(check.status, `reviewEvidence.checks[${index}].status`),
364
- conclusion: nonEmpty(check.conclusion, `reviewEvidence.checks[${index}].conclusion`),
365
- ...(hasCommit ? { commitSha: exactSha(check.commitSha, `reviewEvidence.checks[${index}].commitSha`) } : {}),
366
- };
367
- });
368
- let runtimeBinding;
369
- if (hasBinding) {
370
- exactKeys(value.runtimeBinding, ["kind", "runtimeSha", "parentShas", "mergedAt"], "reviewEvidence.runtimeBinding");
371
- const kind = nonEmpty(value.runtimeBinding.kind, "reviewEvidence.runtimeBinding.kind");
372
- if (!new Set(["reviewed-head", "protected-alpha-merge"]).has(kind)) fail("invalid-runtime-binding", "review evidence runtime binding kind is unsupported");
373
- const parentShas = value.runtimeBinding.parentShas.map((parent, index) => exactSha(parent, `reviewEvidence.runtimeBinding.parentShas[${index}]`));
374
- if (new Set(parentShas).size !== parentShas.length) fail("invalid-runtime-binding", "review evidence runtime parents must be duplicate-free");
375
- runtimeBinding = { kind, runtimeSha: exactSha(value.runtimeBinding.runtimeSha, "reviewEvidence.runtimeBinding.runtimeSha"), parentShas, mergedAt: value.runtimeBinding.mergedAt === null ? null : timestamp(value.runtimeBinding.mergedAt, "reviewEvidence.runtimeBinding.mergedAt") };
376
- }
377
- return {
378
- repository: repository(value.repository, "reviewEvidence.repository"),
379
- pullRequest: positiveInteger(value.pullRequest, "reviewEvidence.pullRequest"),
380
- headSha: exactSha(value.headSha, "reviewEvidence.headSha"),
381
- baseRef: nonEmpty(value.baseRef, "reviewEvidence.baseRef"),
382
- approvals, checks,
383
- observedAt: timestamp(value.observedAt, "reviewEvidence.observedAt"),
384
- ...(runtimeBinding ? { runtimeBinding } : {}),
385
- };
386
- }
387
-
388
- function reviewedRuntimeSha(request, policy, evidence, runtimeSha) {
389
- const binding = evidence.runtimeBinding || { kind: "reviewed-head", runtimeSha: evidence.headSha, parentShas: [], mergedAt: null };
390
- if (binding.runtimeSha !== runtimeSha) fail("review-runtime-mismatch", "review evidence runtime binding does not match the candidate");
391
- const direct = binding.kind === "reviewed-head" && evidence.headSha === runtimeSha && evidence.baseRef === policy.targetRef && binding.mergedAt === null;
392
- const alphaMerge = binding.kind === "protected-alpha-merge" && request.mode === "alpha" && evidence.baseRef === policy.targetRef.replace(/^dev\//u, "alpha/") && binding.parentShas.length === 2 && binding.parentShas.includes(evidence.headSha) && binding.mergedAt !== null && Date.parse(binding.mergedAt) <= Date.parse(evidence.observedAt);
393
- if (!direct && !alphaMerge) fail("review-identity-mismatch", "review evidence does not bind the candidate runtime");
394
- return evidence.headSha;
395
- }
396
-
397
279
  export function universalWorkflowAdmissionRoot(value) {
398
280
  return documentRoot("universal-workflow-admission", validatePolicy(value));
399
281
  }
@@ -409,128 +291,25 @@ function assertPermissionCeiling(requested, ceiling) {
409
291
  }
410
292
  }
411
293
 
412
- export function admitUniversalWorkflow({
413
- request: requestValue,
414
- policy: policyValue,
415
- observedRefSha,
416
- observedConsumerRepository,
417
- observedConsumerSha,
418
- observedConsumerWorkflowRef,
419
- reviewEvidence: reviewEvidenceValue,
420
- now,
421
- } = {}) {
294
+ export function admitUniversalWorkflow({ request: requestValue, policy: policyValue, runtime, observedConsumerRepository, observedConsumerSha, observedConsumerWorkflowRef, now } = {}) {
422
295
  const request = validateUniversalWorkflowRequest(requestValue);
423
296
  const policy = validatePolicy(policyValue);
424
- const admissionRoot = universalWorkflowAdmissionRoot(policy);
425
- if (request.candidate.admissionRoot !== admissionRoot)
426
- fail("stale-admission", "request is not bound to the current admission");
427
- if (policy.sourceRepository !== request.candidate.repository)
428
- fail("source-repository-mismatch", "admission source repository mismatch");
429
297
  const workflowRef = nonEmpty(observedConsumerWorkflowRef, "workflow ref");
430
- const expectedWorkflowPrefix = `${request.consumer.repository}/${request.consumer.workflow}@`;
431
- if (
432
- request.consumer.repository !== observedConsumerRepository ||
433
- request.consumer.sourceSha !==
434
- exactSha(observedConsumerSha, "observedConsumerSha") ||
435
- !workflowRef.startsWith(expectedWorkflowPrefix) ||
436
- workflowRef.length === expectedWorkflowPrefix.length
437
- )
298
+ const prefix = `${request.consumer.repository}/${request.consumer.workflow}@`;
299
+ if (request.consumer.repository !== observedConsumerRepository || request.consumer.sourceSha !== exactSha(observedConsumerSha, "observedConsumerSha") || !workflowRef.startsWith(prefix) || workflowRef.length === prefix.length)
438
300
  fail("consumer-identity-mismatch", "caller identity mismatch");
439
- if (!policy.allowedCapabilities.includes(request.capability.id))
440
- fail("capability-not-admitted", "capability is not admitted");
441
- if (
442
- request.capability.contractRoots.length !== policy.contractRoots.length ||
443
- request.capability.contractRoots.some(
444
- (value, index) => value !== policy.contractRoots[index],
445
- )
446
- )
447
- fail("contract-root-mismatch", "candidate contract roots are not admitted");
448
- assertPermissionCeiling(
449
- request.capability.permissions,
450
- policy.permissionCeiling,
451
- );
301
+ if (!policy.allowedCapabilities.includes(request.capability.id)) fail("capability-not-admitted", "capability is not admitted");
302
+ assertPermissionCeiling(request.capability.permissions, policy.permissionCeiling);
452
303
  const observedAt = timestamp(now, "now");
453
- if (
454
- Date.parse(observedAt) < Date.parse(policy.validFrom) ||
455
- Date.parse(observedAt) >= Date.parse(policy.expiresAt)
456
- )
457
- fail("stale-admission", "admission is outside its validity window");
458
- const runtimeSha = exactSha(observedRefSha, "observedRefSha");
459
- if (runtimeSha !== request.candidate.expectedSha)
460
- fail(
461
- "candidate-ref-moved",
462
- "candidate ref readback does not match expected SHA",
463
- );
464
- const reviewEvidence = validateReviewEvidence(reviewEvidenceValue);
465
- if (
466
- reviewEvidence.repository !== request.candidate.repository ||
467
- reviewEvidence.pullRequest !== request.candidate.reviewPullRequest
468
- )
469
- fail(
470
- "review-identity-mismatch",
471
- "review evidence does not bind the candidate",
472
- );
473
- const reviewedSha = reviewedRuntimeSha(request, policy, reviewEvidence, runtimeSha);
474
- const approvedReviewers = new Set(
475
- reviewEvidence.approvals
476
- .filter((approval) => approval.commitSha === reviewedSha)
477
- .map((approval) => approval.reviewer),
478
- );
479
- const admittedApprovalCount = policy.allowedReviewers.filter((reviewer) =>
480
- approvedReviewers.has(reviewer),
481
- ).length;
482
- if (admittedApprovalCount < policy.minimumApprovals)
483
- fail(
484
- "independent-review-missing",
485
- "candidate lacks exact-head independent review",
486
- );
487
- const checksByName = new Map();
488
- for (const check of reviewEvidence.checks) {
489
- const entries = checksByName.get(check.name) || [];
490
- entries.push(check);
491
- checksByName.set(check.name, entries);
492
- }
493
- if (
494
- policy.requiredChecks.some((name) => {
495
- const checks = checksByName.get(name) || [];
496
- return (
497
- checks.length === 0 ||
498
- checks.some(
499
- (check) =>
500
- check.status !== "completed" || check.conclusion !== "success" ||
501
- (check.commitSha !== undefined && check.commitSha !== runtimeSha),
502
- )
503
- );
504
- })
505
- )
506
- fail(
507
- "exact-head-checks-incomplete",
508
- "candidate exact-head checks are not successful",
509
- );
510
- const requestRoot = universalWorkflowRequestRoot(request);
511
- const discovery = {
512
- repository: request.candidate.repository,
513
- ref: request.candidate.discoveryRef,
514
- expectedSha: request.candidate.expectedSha,
515
- observedSha: runtimeSha,
516
- observedAt,
517
- };
304
+ if (Date.parse(observedAt) < Date.parse(policy.validFrom) || Date.parse(observedAt) >= Date.parse(policy.expiresAt)) fail("stale-admission", "capability policy is outside its validity window");
518
305
  return {
519
306
  schema: UNIVERSAL_WORKFLOW_ADMISSION,
520
307
  status: "admitted",
521
- requestRoot,
522
- admissionRoot,
523
- discoveryRoot: documentRoot("universal-workflow-discovery", discovery),
524
- reviewRoot: documentRoot("universal-workflow-review", reviewEvidence),
308
+ requestRoot: universalWorkflowRequestRoot(request),
309
+ admissionRoot: universalWorkflowAdmissionRoot(policy),
525
310
  consumerRoot: documentRoot("universal-workflow-consumer", request.consumer),
526
- capabilityRoot: documentRoot(
527
- "universal-workflow-capability",
528
- request.capability,
529
- ),
530
- runtime: {
531
- repository: request.candidate.repository,
532
- sha: runtimeSha,
533
- },
311
+ capabilityRoot: documentRoot("universal-workflow-capability", request.capability),
312
+ runtime,
534
313
  permissions: request.capability.permissions,
535
314
  contractRoots: request.capability.contractRoots,
536
315
  };
@@ -544,8 +323,6 @@ export function completeUniversalWorkflow({ admission, resultRoot, status }) {
544
323
  "status",
545
324
  "requestRoot",
546
325
  "admissionRoot",
547
- "discoveryRoot",
548
- "reviewRoot",
549
326
  "consumerRoot",
550
327
  "capabilityRoot",
551
328
  "runtime",
@@ -566,8 +343,6 @@ export function completeUniversalWorkflow({ admission, resultRoot, status }) {
566
343
  status,
567
344
  requestRoot: root(admission.requestRoot, "admission.requestRoot"),
568
345
  admissionRoot: root(admission.admissionRoot, "admission.admissionRoot"),
569
- discoveryRoot: root(admission.discoveryRoot, "admission.discoveryRoot"),
570
- reviewRoot: root(admission.reviewRoot, "admission.reviewRoot"),
571
346
  consumerRoot: root(admission.consumerRoot, "admission.consumerRoot"),
572
347
  capabilityRoot: root(admission.capabilityRoot, "admission.capabilityRoot"),
573
348
  runtime: {
@@ -575,7 +350,7 @@ export function completeUniversalWorkflow({ admission, resultRoot, status }) {
575
350
  admission.runtime?.repository,
576
351
  "admission.runtime.repository",
577
352
  ),
578
- sha: exactSha(admission.runtime?.sha, "admission.runtime.sha"),
353
+ sha: admission.runtime?.sha,
579
354
  },
580
355
  resultRoot: root(resultRoot, "resultRoot"),
581
356
  };
@@ -208,13 +208,13 @@ function validateGateIntegration(root, errors) {
208
208
  errors.push(`required Verify workflow lacks ${trigger}`);
209
209
  }
210
210
  const checkJob = verify.match(/^ check:\n([\s\S]*?)(?=^ [\w-]+:|(?![\s\S]))/mu)?.[1] || "";
211
- const verifyNode = "./.buildchain/workflow-shell/actions/build/verification/repository";
211
+ const verifyNode = "./.buildchain/runtime/actions/build/verification/repository";
212
212
  const verifyAction = read("actions/build/verification/repository/action.yml");
213
213
  const sourceAction = read("actions/build/verification/qualify-source/action.yml");
214
214
  const sourceVerification = read("packages/core/build/verification/source.js");
215
215
  const sourceLifecycle = read("packages/core/build/source/lifecycle.js");
216
216
  if (!parseYamlUses(checkJob).some(call => call.value === verifyNode) ||
217
- !parseYamlUses(verifyAction).some(call => call.value === "./actions/build/verification/qualify-source") ||
217
+ !parseYamlUses(verifyAction).some(call => call.value === "./.buildchain/runtime/actions/build/verification/qualify-source") ||
218
218
  !/using: node24/u.test(sourceAction) ||
219
219
  !/qualify = qualifySourceLifecycle/u.test(sourceVerification) ||
220
220
  !/mode: "verify"/u.test(sourceVerification) ||
@@ -5,11 +5,17 @@ import path from "node:path";
5
5
  import assert from "node:assert/strict";
6
6
  import { spawnSync } from "node:child_process";
7
7
  import YAML from "yaml";
8
+ import { lowerSelfReferencesForLint } from "./workflow-self-reference.mjs";
8
9
  const root = process.cwd();
9
10
  const contract = JSON.parse(fs.readFileSync(path.join(root, "architecture/dev-delivery-orchestration.json"), "utf8"));
10
- const temp = fs.mkdtempSync(path.join(os.tmpdir(), "buildchain-delivery-actionlint-"));
11
+ const temp = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), "buildchain-delivery-actionlint-")));
11
12
  try {
12
- const files = [];
13
+ const workflows = path.join(temp, ".github/workflows");
14
+ fs.cpSync(path.join(root, ".github"), path.join(temp, ".github"), { recursive: true });
15
+ fs.mkdirSync(path.join(temp, ".git")); // actionlint's isolated project boundary, never a runtime checkout.
16
+ fs.mkdirSync(path.join(temp, ".buildchain"));
17
+ fs.symlinkSync(path.join(root, "actions"), path.join(temp, "actions"), "junction");
18
+ const files = fs.readdirSync(workflows).filter(file => /\.ya?ml$/u.test(file)).map(file => path.join(workflows, file));
13
19
  for (const node of contract.nodes) for (const implementation of node.implementations) {
14
20
  const action = YAML.parse(fs.readFileSync(path.join(root, implementation.action), "utf8"));
15
21
  assert.deepEqual(Object.keys(action.inputs || {}), implementation.inputs, `${implementation.action}: input contract drift`);
@@ -21,15 +27,18 @@ try {
21
27
  }
22
28
  const inputs = Object.fromEntries(Object.entries(action.inputs || {}).map(([name, input]) => [name, { ...input, type: "string" }]));
23
29
  const outputs = Object.fromEntries(Object.entries(action.outputs || {}).map(([name, output]) => [name, output.value]));
24
- const file = path.join(temp, `${node.id}-${files.length}.yml`);
30
+ const file = path.join(workflows, `lint-composite-${node.id}-${files.length}.yml`);
25
31
  fs.writeFileSync(file, YAML.stringify({ name: `Validate ${node.id} composite`, on: { workflow_call: { inputs } }, jobs: { node: { "runs-on": "ubuntu-24.04", ...(Object.keys(outputs).length ? { outputs } : {}), steps: action.runs.steps } } }));
26
32
  files.push(file);
27
33
  }
28
- const probe = spawnSync("actionlint", ["-version"], { stdio: "ignore" });
29
- const command = probe.error?.code === "ENOENT" ? "go" : "actionlint";
30
- const prefix = command === "go" ? ["run", "github.com/rhysd/actionlint/cmd/actionlint@v1.7.12"] : [];
31
- const result = spawnSync(command, [...prefix, "-color=false", ...files], { stdio: "inherit" });
34
+ const aliases = new Set(files.flatMap(file => [...fs.readFileSync(file, "utf8").matchAll(/\.\/\.buildchain\/([a-z0-9][a-z0-9-]*)\/actions\//gu)].map(match => match[1])));
35
+ for (const alias of aliases) fs.symlinkSync(root, path.join(temp, ".buildchain", alias), "junction");
36
+ for (const file of files) fs.writeFileSync(file, lowerSelfReferencesForLint(fs.readFileSync(file, "utf8")));
37
+ const version = "1.7.12", probe = spawnSync("actionlint", ["-version"], { encoding: "utf8" });
38
+ const command = probe.status === 0 && probe.stdout.trim().split(/\s/u)[0].replace(/^v/u, "") === version ? "actionlint" : "go";
39
+ const prefix = command === "go" ? ["run", `github.com/rhysd/actionlint/cmd/actionlint@v${version}`] : [];
40
+ const result = spawnSync(command, [...prefix, "-color=false", ...files], { cwd: temp, stdio: "inherit" });
32
41
  if (result.error) throw result.error;
33
42
  if (result.signal || result.status !== 0) process.exitCode = result.status || 1;
34
- else console.log(`Validated ${files.length} delivery composite bodies and interfaces.`);
43
+ else console.log(`Validated ${files.length} source workflows and delivery composite interfaces.`);
35
44
  } finally { fs.rmSync(temp, { recursive: true, force: true }); }
@@ -52,7 +52,7 @@ function assertOrdered(relative, markers) {
52
52
  export function assertPromotionCertificationWiring(source) {
53
53
  const workflow = YAML.parse(source);
54
54
  const node = workflow?.jobs?.qualify?.steps?.find((step) => step.id === "node");
55
- if (node?.uses !== "./.buildchain/workflow-shell/actions/release/promotion/qualify" ||
55
+ if (node?.uses !== "./.buildchain/runtime/actions/release/promotion/qualify" ||
56
56
  node.with?.["request-json"] !== "${{ inputs.request-json }}" ||
57
57
  node.with?.["job-workflow-sha"] !== "${{ toJSON(job.workflow_sha) }}")
58
58
  fail("promotion certification is missing its exact owned node and typed request binding");
@@ -101,12 +101,12 @@ export function checkFloatingConsumerPolicyContract() {
101
101
  fail("architecture policy contract is missing");
102
102
  }
103
103
  if (
104
- policy.contractLocks?.selectedLockMustBindResolvedWorkflowShell !== true
104
+ policy.contractLocks?.runtimeSelectionOwner !== "actions/runtime/selection/resolve"
105
105
  ) {
106
- fail("contract lock must bind the visible workflow shell");
106
+ fail("contract lock selection must belong to the unified entry");
107
107
  }
108
108
  assertPersistedSelectors();
109
- assertOrdered("packages/core/build/plan/admission.js", ["const policy = scanConsumerPolicy(", "validatePackageManagerContract({", "const lock = inspectRuntimeContract({", "assertRuntimeContractAccepted(lock)"]);
109
+ assertOrdered("packages/core/build/plan/admission.js", ["const policy = scanConsumerPolicy(", "validatePackageManagerContract({"]);
110
110
  const buildWorkflow = read(".github/workflows/.build.yml");
111
111
  const planner = read("packages/core/build/plan/admission.js");
112
112
  if (!planner.includes("expectedInvocationChannel: plan.identity.channel") ||
@@ -114,7 +114,7 @@ export function checkFloatingConsumerPolicyContract() {
114
114
  !buildWorkflow.includes("actions/build/lifecycle/plan")) fail("build planning must bind called workflow and channel admission");
115
115
  assertTrustGatedJobs(buildWorkflow, ["build-native", "build-container", "sign", "attest", "deliver"]);
116
116
  assertOrdered("actions/publication/candidate/plan/action.yml", [
117
- "ref: ${{ inputs.runtime-sha }}", "Prepare exact publication runtime", "Enforce v4 floating consumer policy", "Resolve controller identities",
117
+ "Admit source checkout", "Enforce v4 floating consumer policy", "Resolve controller identities",
118
118
  ]);
119
119
  const publication = YAML.parse(read(".github/workflows/public-build-publication.yml"));
120
120
  if (!Object.values(publication.jobs).some((job) => job.steps?.some((step) => step.uses?.endsWith("/actions/publication/candidate/plan"))))
@@ -122,7 +122,7 @@ export function checkFloatingConsumerPolicyContract() {
122
122
  const stageCanary = read(".github/workflows/public-build-stage-capsule-canary.yml");
123
123
  if (
124
124
  !stageCanary.includes("consumer-admission:") ||
125
- !stageCanary.includes("needs: consumer-admission")
125
+ !jobDependsOn(stageCanary, "qualify", "consumer-admission")
126
126
  ) {
127
127
  fail(
128
128
  "Stage Capsule qualification is not transitively gated by consumer admission",
@@ -106,9 +106,7 @@ const requiredPaths = [
106
106
  "scripts/next-development-self-dogfood-harness.mjs",
107
107
  "scripts/generate-next-development-guidance.mjs",
108
108
  "packages/core/governance/commands/reconcile-release-governance.mjs",
109
- "packages/core/consumer/commands/buildchain-channel-router.mjs",
110
109
  "packages/core/release/commands/promotion-channel-router.mjs",
111
- "packages/core/release/commands/promotion-identity-resolver.mjs",
112
110
  "scripts/generate-channel-promotion-workflow.mjs",
113
111
  "scripts/build-standalone-binary.mjs",
114
112
  "packages/core/publication/commands/create-release-bundle.mjs",
@@ -317,17 +315,10 @@ const boundedAlphaRecoveryWorkflow = fs.readFileSync(
317
315
  "utf8",
318
316
  );
319
317
  assertPromotionInventory(root);
320
- const promotionOverrideAuthorization = fs.readFileSync(
321
- path.join(root, "packages/core/release/runtime-override-authorization.js"),
322
- "utf8",
323
- );
324
- if (!promotionOverrideAuthorization.includes("promotion runtime override is only allowed for trusted workflow_dispatch runs")) {
325
- throw new Error("promotion runtime override authorization must remain fail closed");
326
- }
327
318
  for (const workflow of [channelBuildWorkflow, reusableBuildWorkflow]) {
328
319
  const block = workflow.split(" inputs:\n")[1].split(" secrets:\n")[0];
329
320
  const names = [...block.matchAll(/^ ([a-z0-9-]+):$/gm)].map((match) => match[1]);
330
- if (JSON.stringify(names) !== '["config-path"]') throw new Error("ordinary build inputs must contain only config-path");
321
+ if (JSON.stringify(names.sort()) !== JSON.stringify(['config-path','contract-lock','resume-run-id','runtime-ref','runtime-selection'])) throw new Error('build API must expose only the locator and unified runtime entry inputs');
331
322
  }
332
323
  if ((channelBuildWorkflow.match(/uses: \.\/\.github\/workflows\/\.build\.yml/g) || []).length !== 1) {
333
324
  throw new Error("public build facade must invoke its exact backbone once");