@kungfu-tech/buildchain 4.0.2-alpha.8 → 4.0.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 (157) hide show
  1. package/AGENTS.md +10 -1
  2. package/CONTRIBUTING.md +24 -0
  3. package/README.md +2 -2
  4. package/actions/promote-buildchain-ref/README.md +1 -1
  5. package/actions/v4-release-candidate-promote/README.md +28 -0
  6. package/architecture/agent-change-map.md +64 -7
  7. package/architecture/ci-lane-change-budget.json +366 -0
  8. package/architecture/internal-capabilities.json +53 -8
  9. package/architecture/maintainability-debt.json +147 -93
  10. package/architecture/maintainability-policy.json +32 -28
  11. package/architecture/release-tail-contract-inventory.json +6 -6
  12. package/architecture/v4-architecture-constitution.md +23 -20
  13. package/architecture/v4-cross-platform-adopter-qualification.json +1 -1
  14. package/architecture/v4-delivery-authority-parity.json +2 -2
  15. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +1 -1
  16. package/architecture/v4-floating-consumer-policy.json +2 -2
  17. package/architecture/v4-next-development-parity.json +5 -5
  18. package/architecture/v4-partial-mutation-recovery-qualification.json +4 -1
  19. package/architecture/v4-platform-stage-checkpoints.json +1 -1
  20. package/architecture/v4-provider-operation-journal-contract.json +5 -2
  21. package/architecture/v4-publication-facade-patches.json +64 -0
  22. package/architecture/v4-release-activation-shadow-domain.json +5 -2
  23. package/architecture/v4-release-topology.json +74 -39
  24. package/architecture/v4-rust-wasm-production-authority.json +58 -0
  25. package/architecture/v4-stable-publication-fence.json +5 -2
  26. package/architecture/v4-stage-capsule-qualification.json +2 -2
  27. package/architecture/v4-tail-reseal-parity.json +8 -6
  28. package/architecture/v4-universal-workflow-bootstrap.json +171 -0
  29. package/architecture/v4-universal-workflow-fault-campaign.json +72 -0
  30. package/architecture/v4-universal-workflow-train-admission.json +40 -0
  31. package/architecture/workflow-taxonomy.json +1451 -0
  32. package/bin/buildchain.mjs +7 -7
  33. package/contracts/buildchain-v2-residuals-v1.json +57 -48
  34. package/contracts/dev-delivery-authority-v2.schema.json +16 -2
  35. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  36. package/contracts/v4-canonical-contracts-v1.schema.json +1 -0
  37. package/dist/site/buildchain-contract.json +93 -48
  38. package/dist/site/buildchain-site.json +222 -69
  39. package/dist/site/capability-registry.json +4 -4
  40. package/dist/site/controller-registry.json +50 -13
  41. package/dist/site/kfd-claims.json +2585 -1000
  42. package/dist/site/kfd-upstream-aggregate.json +1 -1
  43. package/dist/site/manual-registry.json +13 -13
  44. package/dist/site/node-api-registry.json +120 -115
  45. package/dist/site/page-registry.json +204 -51
  46. package/dist/site/public-surface-audit.json +3205 -1219
  47. package/dist/site/publication-authority-registry.json +1124 -362
  48. package/dist/site/publication-registry.json +4 -4
  49. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +16 -2
  50. package/dist/site/site-manifest.json +17 -17
  51. package/dist/site/workflow-registry.json +5061 -1659
  52. package/docs/MAP.md +1 -0
  53. package/docs/auditable-demo.md +1 -1
  54. package/docs/cli.md +21 -8
  55. package/docs/dev-delivery-warrant.md +1 -1
  56. package/docs/engineering-housekeeper.md +3 -3
  57. package/docs/kfd-support.md +26 -2
  58. package/docs/next-development-transition.md +2 -2
  59. package/docs/node-api-reference.md +190 -190
  60. package/docs/publication-authority.md +2 -2
  61. package/docs/release-candidate.md +3 -3
  62. package/docs/release-flow.md +67 -19
  63. package/docs/release-governance.md +8 -8
  64. package/docs/release-passport.md +27 -2
  65. package/docs/reusable-build-surface.md +16 -0
  66. package/docs/v4-adopter-delivery.md +2 -2
  67. package/docs/v4-production-release.md +71 -8
  68. package/docs/v4-rust-wasm-production-authority.md +58 -0
  69. package/docs/v4-stage-capsule.md +2 -2
  70. package/docs/v4-tail-reseal.md +1 -1
  71. package/docs/workflow-catalog.md +154 -0
  72. package/docs/workflow-path-migration.md +109 -0
  73. package/package.json +10 -6
  74. package/packages/core/artifact-verification-envelope.js +5 -5
  75. package/packages/core/buildchain-contract.js +1 -1
  76. package/packages/core/buildchain-kfd-claims.js +2 -2
  77. package/packages/core/buildchain-publication-authority.js +42 -26
  78. package/packages/core/buildchain-v4-domain.wasm +0 -0
  79. package/packages/core/ci-lane-change-budget.js +14 -1
  80. package/packages/core/controller-evidence.js +4 -1
  81. package/packages/core/dev-delivery-candidate-identity.js +40 -6
  82. package/packages/core/dev-delivery-warrant-state.js +3 -4
  83. package/packages/core/dev-delivery-warrant.js +12 -2
  84. package/packages/core/github-governance-authority.js +4 -4
  85. package/packages/core/kfd-adopter-manifest.js +46 -47
  86. package/packages/core/next-development-projection.js +2 -2
  87. package/packages/core/paper-agent-entry.js +30 -4
  88. package/packages/core/paper-fleet.js +1 -1
  89. package/packages/core/paper-runtime-channels.js +348 -0
  90. package/packages/core/paper-scaffold-content.js +10 -0
  91. package/packages/core/paper.js +37 -71
  92. package/packages/core/release-line-dry-run.js +1 -1
  93. package/packages/core/release-passport-contract.js +5 -5
  94. package/packages/core/release-passport.js +5 -4
  95. package/packages/core/release-tail-provider-plane.js +68 -1048
  96. package/packages/core/v4-canonical-contracts.js +10 -75
  97. package/packages/core/v4-domain-wasm-artifact.js +7 -0
  98. package/packages/core/v4-domain-wasm.js +216 -0
  99. package/packages/core/v4-floating-consumer-evidence.js +1 -1
  100. package/packages/core/v4-floating-consumer-policy.js +1 -1
  101. package/packages/core/v4-partial-mutation-recovery-qualification.js +9 -386
  102. package/packages/core/v4-product-publication.js +19 -349
  103. package/packages/core/v4-protected-publication-source.js +19 -21
  104. package/packages/core/v4-provider-operation-journal.js +18 -487
  105. package/packages/core/v4-provider-readback-idempotency.js +12 -97
  106. package/packages/core/v4-release-activation-shadow.js +13 -500
  107. package/packages/core/v4-release-invocation.js +28 -389
  108. package/packages/core/v4-stable-publication-fence.js +13 -342
  109. package/packages/core/v4-universal-workflow-bootstrap.js +586 -0
  110. package/scripts/binary-publication-evidence.mjs +135 -0
  111. package/scripts/build-contract-core.mjs +1 -14
  112. package/scripts/build-v4-domain-wasm.mjs +189 -0
  113. package/scripts/check-action-bundles.mjs +12 -2
  114. package/scripts/check-ci-lane-change-budget.mjs +4 -1
  115. package/scripts/check-core-mechanism-inventory.mjs +4 -3
  116. package/scripts/check-inventory.mjs +35 -44
  117. package/scripts/check-maintainability.mjs +16 -113
  118. package/scripts/check-v4-floating-consumer-policy-contract.mjs +2 -2
  119. package/scripts/check-v4-public-dogfood-contract.mjs +22 -9
  120. package/scripts/check-v4-release-topology.mjs +90 -33
  121. package/scripts/check-v4-runtime-semantic-closure.mjs +4 -1
  122. package/scripts/check-v4-universal-workflow-bootstrap.mjs +266 -0
  123. package/scripts/check-workflow-taxonomy.mjs +6 -0
  124. package/scripts/copy-v4-domain-wasm.mjs +14 -0
  125. package/scripts/dev-delivery-authority-command-adapters.mjs +1 -1
  126. package/scripts/dev-delivery-authority.mjs +8 -8
  127. package/scripts/dev-delivery-source-proof-reuse.mjs +2 -1
  128. package/scripts/dev-delivery-warrant-options.mjs +14 -9
  129. package/scripts/dev-delivery-warrant-store.mjs +73 -7
  130. package/scripts/dev-delivery-warrant-transition.mjs +109 -0
  131. package/scripts/dev-delivery-warrant.mjs +76 -99
  132. package/scripts/dev-pr-auto-merge.mjs +15 -15
  133. package/scripts/generate-channel-build-workflow.mjs +7 -2
  134. package/scripts/generate-channel-promotion-workflow.mjs +7 -5
  135. package/scripts/generate-site-bundle.mjs +11 -11
  136. package/scripts/generate-v4-universal-workflow-facades.mjs +458 -0
  137. package/scripts/generate-workflow-taxonomy.mjs +25 -0
  138. package/scripts/github-output.mjs +31 -0
  139. package/scripts/maintainability-metrics.mjs +16 -5
  140. package/scripts/maintainability-public-surface.mjs +115 -0
  141. package/scripts/next-development-review.mjs +307 -0
  142. package/scripts/paper-work-fleet-cli.mjs +2 -0
  143. package/scripts/release-asset-client.mjs +91 -0
  144. package/scripts/release-candidate-resolver.mjs +9 -9
  145. package/scripts/source-verification-evidence.mjs +344 -0
  146. package/scripts/stable-candidate-qualification.mjs +1 -1
  147. package/scripts/universal-facade-maintainability.mjs +82 -0
  148. package/scripts/v4-product-publication-intent.mjs +13 -6
  149. package/scripts/v4-publication-settlement.mjs +211 -0
  150. package/scripts/v4-universal-workflow-backflow.mjs +212 -0
  151. package/scripts/v4-universal-workflow-engine.mjs +618 -0
  152. package/scripts/v4-universal-workflow-self-dogfood.mjs +205 -0
  153. package/scripts/verify-version-state-delta.mjs +173 -0
  154. package/scripts/workflow-taxonomy.mjs +507 -0
  155. package/templates/native-dev-delivery.yml +4 -7
  156. package/templates/universal-buildchain-bootstrap-recovery.yml +282 -0
  157. package/templates/universal-buildchain-bootstrap.yml +28 -0
@@ -0,0 +1,135 @@
1
+ import fs from "node:fs";
2
+ import crypto from "node:crypto";
3
+ import path from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import {
6
+ SETTLEMENT_ASSET,
7
+ verifyPublicationSettlement,
8
+ } from "./v4-publication-settlement.mjs";
9
+ import { releaseAssetClient } from "./release-asset-client.mjs";
10
+
11
+ export async function readBinaryPublicationEvidence({
12
+ client,
13
+ repository,
14
+ tag,
15
+ sourceSha,
16
+ attempts = 40,
17
+ wait = () => new Promise((resolve) => setTimeout(resolve, 15000)),
18
+ }) {
19
+ for (let attempt = 0; attempt < attempts; attempt++) {
20
+ let release;
21
+ try {
22
+ release = client.release(tag);
23
+ } catch {
24
+ /* Tag creation can precede Release creation. */
25
+ }
26
+ const assets = release?.assets || [];
27
+ const settlements = assets.filter(({ name }) => name === SETTLEMENT_ASSET);
28
+ const passports = assets.filter(
29
+ ({ name }) => name === "buildchain.release.json",
30
+ );
31
+ if (settlements.length > 1 || passports.length > 1)
32
+ throw new Error("ambiguous immutable publication evidence");
33
+ if (settlements.length === 1 && passports.length === 1) {
34
+ const settlement = JSON.parse(client.assetBytes(settlements[0]));
35
+ const publicPassport = JSON.parse(client.assetBytes(passports[0]));
36
+ if (
37
+ settlement.contract !== "buildchain-v4-publication-settlement/v1" ||
38
+ settlement.release.sourceSha !== sourceSha ||
39
+ settlement.release.tag !== tag
40
+ )
41
+ throw new Error("binary publication settlement identity mismatch");
42
+ const tagSha = client.json(
43
+ `repos/${repository}/commits/${encodeURIComponent(tag)}`,
44
+ ).sha;
45
+ if (tagSha !== sourceSha)
46
+ throw new Error("binary source does not match the exact release tag");
47
+ verifyPublicationSettlement(settlement.documents, {
48
+ repository,
49
+ tag,
50
+ sourceSha,
51
+ publicPassport,
52
+ });
53
+ return settlement;
54
+ }
55
+ if (attempt + 1 < attempts) await wait();
56
+ }
57
+ throw new Error(
58
+ "completed v4 publication evidence is not available within the bounded wait",
59
+ );
60
+ }
61
+
62
+ export function prepareBinaryAssetPaths({
63
+ binaryDir,
64
+ passportDir,
65
+ outputDir,
66
+ capabilityPath,
67
+ }) {
68
+ fs.mkdirSync(outputDir, { recursive: true });
69
+ const files = [];
70
+ for (const directory of [binaryDir, passportDir]) {
71
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
72
+ if (!entry.isFile()) continue;
73
+ const source = path.join(directory, entry.name);
74
+ if (
75
+ directory === passportDir &&
76
+ entry.name === "buildchain.release.json"
77
+ ) {
78
+ const renamed = path.join(outputDir, "buildchain.binary.release.json");
79
+ fs.copyFileSync(source, renamed);
80
+ files.push(renamed);
81
+ } else files.push(source);
82
+ }
83
+ }
84
+ if (capabilityPath) {
85
+ const bytes = fs.readFileSync(capabilityPath);
86
+ const digest = crypto.createHash("sha256").update(bytes).digest("hex");
87
+ const retained = path.join(
88
+ outputDir,
89
+ `buildchain.binary.capability-${digest}.json`,
90
+ );
91
+ fs.writeFileSync(retained, bytes);
92
+ files.push(retained);
93
+ }
94
+ return files;
95
+ }
96
+
97
+ async function main(mode) {
98
+ const repository = process.env.GITHUB_REPOSITORY;
99
+ const tag = process.env.RELEASE_TAG;
100
+ const client = releaseAssetClient(repository);
101
+ if (mode === "read") {
102
+ const settlement = await readBinaryPublicationEvidence({
103
+ client,
104
+ repository,
105
+ tag,
106
+ sourceSha: process.env.SOURCE_SHA,
107
+ });
108
+ const directory = ".buildchain/publication-evidence";
109
+ client.write(`${directory}/${SETTLEMENT_ASSET}`, settlement);
110
+ client.write(`${directory}/release.json`, {
111
+ channel: settlement.release.channel,
112
+ });
113
+ } else if (mode === "publish") {
114
+ const release = client.release(tag);
115
+ const files = prepareBinaryAssetPaths({
116
+ binaryDir: "dist/binary",
117
+ passportDir: ".buildchain/release-passport",
118
+ outputDir: ".buildchain/binary-publication",
119
+ capabilityPath: ".buildchain/publication-authority/capability.json",
120
+ });
121
+ const results = await client.publish(release, files);
122
+ client.write(".buildchain/binary-publication/readback.json", {
123
+ repository,
124
+ tag,
125
+ assets: results,
126
+ });
127
+ console.log(JSON.stringify({ published: results.length, repository, tag }));
128
+ } else throw new Error(`unknown binary publication mode: ${mode}`);
129
+ }
130
+
131
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
132
+ main(process.argv[2]).catch((error) => {
133
+ console.error(error.message);
134
+ process.exitCode = 1;
135
+ });
@@ -3,6 +3,7 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { macosJitRunnerLabel } from "./aws-macos-jit-core.mjs";
5
5
  import { windowsJitRunnerLabel } from "./aws-windows-jit-core.mjs";
6
+ export { writeGitHubOutputs } from "./github-output.mjs";
6
7
  export const RUNNER_PRESETS = Object.freeze({
7
8
  "github-hosted": [
8
9
  { id: "linux-x64", name: "Linux x64", platform: "linux", runner: '["ubuntu-24.04"]', capabilities: ["node"] },
@@ -1201,20 +1202,6 @@ export function validateExpectedArtifacts({ expected, files, summary }) {
1201
1202
  return { ok: true, source: "expected-artifacts-json", checks };
1202
1203
  }
1203
1204
 
1204
- export function writeGitHubOutputs(outputs) {
1205
- const outputPath = process.env.GITHUB_OUTPUT;
1206
- if (!outputPath) {
1207
- for (const [key, value] of Object.entries(outputs)) {
1208
- console.log(`${key}=${value}`);
1209
- }
1210
- return;
1211
- }
1212
- const lines = Object.entries(outputs).map(
1213
- ([key, value]) => `${key}=${value}`,
1214
- );
1215
- fs.appendFileSync(outputPath, `${lines.join("\n")}\n`);
1216
- }
1217
-
1218
1205
  export function findJsonFiles(root) {
1219
1206
  if (!fs.existsSync(root)) {
1220
1207
  return [];
@@ -0,0 +1,189 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ import { spawnSyncCommand } from "../packages/core/spawn-command.js";
8
+
9
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
10
+ const crate = path.join(root, "crates", "buildchain-v4-contracts");
11
+ const artifact = path.join(
12
+ root,
13
+ "packages",
14
+ "core",
15
+ "buildchain-v4-domain.wasm",
16
+ );
17
+ const metadata = path.join(
18
+ root,
19
+ "packages",
20
+ "core",
21
+ "v4-domain-wasm-artifact.js",
22
+ );
23
+ const check = process.argv.includes("--check");
24
+ const toolchain = process.env.BUILDCHAIN_RUST_WASM_TOOLCHAIN || "1.96.0";
25
+ const expectedRustc = "rustc 1.96.0 (ac68faa20 2026-05-25)";
26
+ const canonicalBuildHost =
27
+ process.platform === "linux" && process.arch === "x64";
28
+
29
+ const sha256 = (bytes) =>
30
+ crypto.createHash("sha256").update(bytes).digest("hex");
31
+ const normalize = (value) => value.split(path.sep).join("/");
32
+
33
+ function rustBuildEnvironment() {
34
+ const environment = {
35
+ ...process.env,
36
+ RUSTUP_TOOLCHAIN: toolchain,
37
+ CARGO_INCREMENTAL: "0",
38
+ SOURCE_DATE_EPOCH: "0",
39
+ };
40
+ delete environment.RUSTFLAGS;
41
+ delete environment.CARGO_ENCODED_RUSTFLAGS;
42
+
43
+ const metadata = spawnSyncCommand(
44
+ "cargo",
45
+ [
46
+ "metadata",
47
+ "--format-version",
48
+ "1",
49
+ "--locked",
50
+ "--manifest-path",
51
+ path.join(crate, "Cargo.toml"),
52
+ ],
53
+ { cwd: root, encoding: "utf8", env: environment },
54
+ );
55
+ if (metadata.error) throw metadata.error;
56
+ if (metadata.status !== 0) process.exit(metadata.status ?? 1);
57
+ const dependencyRemaps = JSON.parse(metadata.stdout)
58
+ .packages.filter((entry) => entry.source)
59
+ .map((entry) => [
60
+ path.dirname(entry.manifest_path),
61
+ `/buildchain/dependencies/${entry.name}-${entry.version}`,
62
+ ])
63
+ .sort(([left], [right]) => right.length - left.length);
64
+ const remaps = [[root, "/buildchain/source"], ...dependencyRemaps].map(
65
+ ([from, to]) => `--remap-path-prefix=${from}=${to}`,
66
+ );
67
+ environment.CARGO_ENCODED_RUSTFLAGS = remaps.join("\x1f");
68
+ return environment;
69
+ }
70
+
71
+ function sourceFiles(directory) {
72
+ return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
73
+ const absolute = path.join(directory, entry.name);
74
+ return entry.isDirectory() ? sourceFiles(absolute) : [absolute];
75
+ });
76
+ }
77
+
78
+ const rustc = spawnSyncCommand("rustc", ["--version"], {
79
+ cwd: root,
80
+ encoding: "utf8",
81
+ env: { ...process.env, RUSTUP_TOOLCHAIN: toolchain },
82
+ });
83
+ if (rustc.error) throw rustc.error;
84
+ if (rustc.status !== 0) process.exit(rustc.status ?? 1);
85
+ if (rustc.stdout.trim() !== expectedRustc) {
86
+ throw new Error(
87
+ `Rust/WASM authority requires ${expectedRustc}; observed ${rustc.stdout.trim()}`,
88
+ );
89
+ }
90
+
91
+ const sourceInputs = [
92
+ path.join(crate, "Cargo.toml"),
93
+ path.join(crate, "Cargo.lock"),
94
+ path.join(crate, "rust-toolchain.toml"),
95
+ ...sourceFiles(path.join(crate, "src")),
96
+ ].sort();
97
+ const sourceHash = crypto.createHash("sha256");
98
+ for (const file of sourceInputs) {
99
+ sourceHash.update(normalize(path.relative(root, file)));
100
+ sourceHash.update("\0");
101
+ sourceHash.update(fs.readFileSync(file));
102
+ sourceHash.update("\0");
103
+ }
104
+ const sourceSha256 = sourceHash.digest("hex");
105
+ const generatedMetadata = (artifactSha256) =>
106
+ `// Generated by scripts/build-v4-domain-wasm.mjs.\nexport const V4_DOMAIN_WASM_ABI_VERSION = 1;\nexport const V4_DOMAIN_WASM_RUSTC = ${JSON.stringify(expectedRustc)};\nexport const V4_DOMAIN_WASM_SOURCE_SHA256 =\n ${JSON.stringify(sourceSha256)};\nexport const V4_DOMAIN_WASM_SHA256 =\n ${JSON.stringify(artifactSha256)};\n`;
107
+
108
+ if (!canonicalBuildHost && !check) {
109
+ throw new Error(
110
+ "Buildchain v4 Rust/WASM generation requires the canonical linux-x64 host",
111
+ );
112
+ }
113
+
114
+ if (!canonicalBuildHost) {
115
+ if (!fs.existsSync(artifact)) {
116
+ throw new Error("tracked Buildchain v4 Rust/WASM artifact is missing");
117
+ }
118
+ const artifactSha256 = sha256(fs.readFileSync(artifact));
119
+ if (
120
+ !fs.existsSync(metadata) ||
121
+ fs.readFileSync(metadata, "utf8") !== generatedMetadata(artifactSha256)
122
+ ) {
123
+ throw new Error("tracked Buildchain v4 Rust/WASM metadata is stale");
124
+ }
125
+ console.log(
126
+ `Buildchain v4 Rust/WASM portable artifact verified: sha256:${artifactSha256}`,
127
+ );
128
+ } else {
129
+ const buildEnvironment = rustBuildEnvironment();
130
+
131
+ const temporary = fs.mkdtempSync(
132
+ path.join(os.tmpdir(), "buildchain-v4-wasm-"),
133
+ );
134
+ try {
135
+ const build = spawnSyncCommand(
136
+ "cargo",
137
+ [
138
+ "build",
139
+ "--locked",
140
+ "--release",
141
+ "--manifest-path",
142
+ path.join(crate, "Cargo.toml"),
143
+ "--target",
144
+ "wasm32-unknown-unknown",
145
+ "--target-dir",
146
+ temporary,
147
+ "--lib",
148
+ ],
149
+ {
150
+ cwd: root,
151
+ stdio: "inherit",
152
+ env: buildEnvironment,
153
+ },
154
+ );
155
+ if (build.error) throw build.error;
156
+ if (build.status !== 0) process.exit(build.status ?? 1);
157
+ const builtPath = path.join(
158
+ temporary,
159
+ "wasm32-unknown-unknown",
160
+ "release",
161
+ "buildchain_v4_contracts.wasm",
162
+ );
163
+ const built = fs.readFileSync(builtPath);
164
+ const artifactSha256 = sha256(built);
165
+ const generated = generatedMetadata(artifactSha256);
166
+ if (check) {
167
+ if (
168
+ !fs.existsSync(artifact) ||
169
+ !fs.readFileSync(artifact).equals(built)
170
+ ) {
171
+ throw new Error("tracked Buildchain v4 Rust/WASM artifact is stale");
172
+ }
173
+ if (
174
+ !fs.existsSync(metadata) ||
175
+ fs.readFileSync(metadata, "utf8") !== generated
176
+ ) {
177
+ throw new Error("tracked Buildchain v4 Rust/WASM metadata is stale");
178
+ }
179
+ } else {
180
+ fs.copyFileSync(builtPath, artifact);
181
+ fs.writeFileSync(metadata, generated);
182
+ }
183
+ console.log(
184
+ `Buildchain v4 Rust/WASM ${check ? "verified" : "built"}: sha256:${artifactSha256}`,
185
+ );
186
+ } finally {
187
+ fs.rmSync(temporary, { recursive: true, force: true });
188
+ }
189
+ }
@@ -5,7 +5,7 @@ import { spawnSyncCommand } from "../packages/core/spawn-command.js";
5
5
 
6
6
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
7
7
  const actionsRoot = path.join(root, "actions");
8
- const bundlePaths = readdirSync(actionsRoot, { withFileTypes: true })
8
+ const javascriptBundlePaths = readdirSync(actionsRoot, { withFileTypes: true })
9
9
  .filter((entry) => entry.isDirectory())
10
10
  .map((entry) => path.join(actionsRoot, entry.name, "dist", "index.js"))
11
11
  .filter((bundlePath) => {
@@ -17,6 +17,14 @@ const bundlePaths = readdirSync(actionsRoot, { withFileTypes: true })
17
17
  }
18
18
  })
19
19
  .sort();
20
+ const wasmBundlePaths = [
21
+ "promote-buildchain-ref",
22
+ "release-tail",
23
+ "v4-release-candidate-promote",
24
+ ].map((name) =>
25
+ path.join(actionsRoot, name, "dist", "buildchain-v4-domain.wasm"),
26
+ );
27
+ const bundlePaths = [...javascriptBundlePaths, ...wasmBundlePaths];
20
28
 
21
29
  const before = new Map(
22
30
  bundlePaths.map((bundlePath) => [bundlePath, readFileSync(bundlePath)]),
@@ -46,4 +54,6 @@ if (changed.length > 0) {
46
54
  process.exit(1);
47
55
  }
48
56
 
49
- console.log(`action bundle integrity check passed (${bundlePaths.length} bundles)`);
57
+ console.log(
58
+ `action bundle integrity check passed (${bundlePaths.length} bundles)`,
59
+ );
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import crypto from "node:crypto";
3
+ import { projectWorkflowIdentities } from "./workflow-taxonomy.mjs";
3
4
  import fs from "node:fs";
4
5
  import path from "node:path";
5
6
  import process from "node:process";
@@ -14,7 +15,7 @@ const policyPath = path.join(
14
15
  "ci-lane-change-budget.json",
15
16
  );
16
17
 
17
- const workflows = fs
18
+ const physicalWorkflows = fs
18
19
  .readdirSync(workflowRoot, { withFileTypes: true })
19
20
  .filter((entry) => entry.isFile() && /\.(?:yaml|yml)$/u.test(entry.name))
20
21
  .map((entry) => {
@@ -26,6 +27,8 @@ const workflows = fs
26
27
  })
27
28
  .sort((left, right) => left.path.localeCompare(right.path));
28
29
 
30
+ const workflows = projectWorkflowIdentities(root, physicalWorkflows);
31
+
29
32
  const policy = JSON.parse(fs.readFileSync(policyPath, "utf8"));
30
33
  const baselineRevision = String(policy.baseline?.gitRevision || "").trim();
31
34
  if (!/^[0-9a-f]{40}$/u.test(baselineRevision)) {
@@ -34,9 +34,10 @@ function registrySurfaceIds(root) {
34
34
  (entry) => `cli:${entry.id}`,
35
35
  );
36
36
  const workflows = loadJson(root, "dist/site/workflow-registry.json");
37
- const workflowIds = workflows.workflows.map(
38
- (entry) => `workflow:${entry.id}`,
39
- );
37
+ const workflowIds = workflows.workflows.flatMap((entry) => [
38
+ `workflow:${entry.id}`,
39
+ ...(entry.taxonomy ? [`workflow:${entry.taxonomy.id}`] : []),
40
+ ]);
40
41
  const actionIds = workflows.actions.map((entry) => `action:${entry.id}`);
41
42
  const exports = Object.keys(loadJson(root, "package.json").exports).map(
42
43
  (entry) => `export:${entry}`,
@@ -145,39 +145,39 @@ const requiredPaths = [
145
145
  ".buildchain/alpha-contract-lock.json",
146
146
  ".buildchain/release-impact.json",
147
147
  ".github/actionlint.yaml",
148
- ".github/workflows/self-hosted-runner-smoke.yml",
149
- ".github/workflows/buildchain-ref-promotion.yml",
150
- ".github/workflows/buildchain-candidate-recovery-dogfood-failure.yml",
151
- ".github/workflows/release-line-bootstrap.yml",
148
+ ".github/workflows/self-ops-runner-smoke.yml",
149
+ ".github/workflows/self-release-promote.yml",
150
+ ".github/workflows/self-ops-recovery-failure-dogfood.yml",
151
+ ".github/workflows/self-release-line-open.yml",
152
152
  ".github/workflows/release-governance-reconcile.yml",
153
153
  ".github/workflows/dev-pr-auto-merge.yml",
154
- ".github/workflows/buildchain-dev-delivery.yml",
154
+ ".github/workflows/self-ops-dev-delivery.yml",
155
155
  ".github/workflows/buildchain-patrol.yml",
156
156
  ".github/workflows/patrol-daily.yml",
157
157
  ".github/workflows/patrol-weekly.yml",
158
158
  ".github/workflows/patrol-monthly.yml",
159
159
  ".github/workflows/patrol-observed-evidence.yml",
160
- ".github/workflows/buildchain-patrol-daily.yml",
161
- ".github/workflows/buildchain-patrol-weekly.yml",
162
- ".github/workflows/buildchain-patrol-monthly.yml",
163
- ".github/workflows/buildchain-alpha-self-dogfood.yml",
160
+ ".github/workflows/self-ops-patrol-daily.yml",
161
+ ".github/workflows/self-ops-patrol-weekly.yml",
162
+ ".github/workflows/self-ops-patrol-monthly.yml",
163
+ ".github/workflows/self-build-alpha-dogfood.yml",
164
164
  ".github/workflows/release-candidate-promote.yml",
165
165
  ".github/workflows/.release-candidate-promote.yml",
166
166
  ".github/workflows/release-propagation.yml",
167
- ".github/workflows/npm-publish.yml",
167
+ ".github/workflows/self-release-npm-dry-run.yml",
168
168
  ".github/workflows/paper-release.yml",
169
- ".github/workflows/binary-distribution.yml",
169
+ ".github/workflows/self-build-binary-distribution.yml",
170
170
  ".github/workflows/.binary-release-assets.yml",
171
- ".github/workflows/binary-release-assets.yml",
172
- ".github/workflows/verify.yml",
171
+ ".github/workflows/self-release-binary-assets.yml",
172
+ ".github/workflows/self-build-verify.yml",
173
173
  ".github/workflows/.build.yml",
174
174
  ".github/workflows/.gate-profile.yml",
175
175
  ".github/workflows/.auditable-demo.yml",
176
176
  ".github/workflows/.declarative-auditable-demo.yml",
177
- ".github/workflows/auditable-demo.yml",
177
+ ".github/workflows/self-build-demo-dogfood.yml",
178
178
  ".github/workflows/build.yml",
179
- ".github/workflows/build-surface-fixture.yml",
180
- ".github/workflows/candidate-lab.yml",
179
+ ".github/workflows/self-build-fixture.yml",
180
+ ".github/workflows/self-build-candidate-lab.yml",
181
181
  "fixtures/libnode-shaped/buildchain.toml",
182
182
  "fixtures/libnode-shaped/.github/workflows/build.yml",
183
183
  "fixtures/libnode-shaped/package.json"
@@ -197,7 +197,7 @@ if (rootPackage.private !== false) {
197
197
  throw new Error("root package must be publishable with private=false");
198
198
  }
199
199
  const selfDogfoodWorkflow = fs.readFileSync(
200
- path.join(root, ".github/workflows/buildchain-alpha-self-dogfood.yml"),
200
+ path.join(root, ".github/workflows/self-build-alpha-dogfood.yml"),
201
201
  "utf8",
202
202
  );
203
203
  const selfDogfoodAlphaLock = JSON.parse(
@@ -285,7 +285,7 @@ const channelPromotionWorkflow = fs.readFileSync(
285
285
  "utf8",
286
286
  );
287
287
  const boundedAlphaRecoveryWorkflow = fs.readFileSync(
288
- path.join(root, ".github/workflows/buildchain-ref-promotion-recovery.yml"),
288
+ path.join(root, ".github/workflows/self-ops-promotion-recovery.yml"),
289
289
  "utf8",
290
290
  );
291
291
  const promotionShellRouting = parsePromotionShellRouting(
@@ -1000,11 +1000,11 @@ for (const requiredSnippet of [
1000
1000
  if (commonJsSourcePattern.test(standaloneBinaryScript)) {
1001
1001
  throw new Error("scripts/build-standalone-binary.mjs must use ESM syntax");
1002
1002
  }
1003
- const npmPublishWorkflow = fs.readFileSync(path.join(root, ".github/workflows/npm-publish.yml"), "utf8");
1004
- const buildchainRefPromotionWorkflow = fs.readFileSync(path.join(root, ".github/workflows/buildchain-ref-promotion.yml"), "utf8");
1005
- const binaryDistributionWorkflow = fs.readFileSync(path.join(root, ".github/workflows/binary-distribution.yml"), "utf8");
1003
+ const npmPublishWorkflow = fs.readFileSync(path.join(root, ".github/workflows/self-release-npm-dry-run.yml"), "utf8");
1004
+ const buildchainRefPromotionWorkflow = fs.readFileSync(path.join(root, ".github/workflows/self-release-promote.yml"), "utf8");
1005
+ const binaryDistributionWorkflow = fs.readFileSync(path.join(root, ".github/workflows/self-build-binary-distribution.yml"), "utf8");
1006
1006
  const binaryReleaseAssetsWorkflow = fs.readFileSync(path.join(root, ".github/workflows/.binary-release-assets.yml"), "utf8");
1007
- const selfHostedRunnerSmokeWorkflow = fs.readFileSync(path.join(root, ".github/workflows/self-hosted-runner-smoke.yml"), "utf8");
1007
+ const selfHostedRunnerSmokeWorkflow = fs.readFileSync(path.join(root, ".github/workflows/self-ops-runner-smoke.yml"), "utf8");
1008
1008
  const npmDryRunScript = fs.readFileSync(path.join(root, "scripts/npm-publish-dry-run.mjs"), "utf8");
1009
1009
  const npmPublishTransactionScript = fs.readFileSync(path.join(root, "scripts/npm-publish-transaction.mjs"), "utf8");
1010
1010
  const rootPackageJson = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
@@ -1043,8 +1043,8 @@ for (const requiredSnippet of [
1043
1043
  "actions: write",
1044
1044
  "uses: kungfu-systems/buildchain/.github/workflows/.release-candidate-promote.yml@v4-alpha",
1045
1045
  "github.event.workflow_run.event == 'push'",
1046
- "!startsWith(github.event.workflow_run.display_title, 'chore(release): prepare v')",
1047
- "!startsWith(github.event.workflow_run.display_title, 'chore(release): release v')",
1046
+ "name: Classify generated product-state finalization",
1047
+ "selectV4FinalizedProductPublicationVersion",
1048
1048
  "resume-candidate-run-id:",
1049
1049
  "resume-expected-source-tree:",
1050
1050
  "resume-buildchain-runtime-ref:",
@@ -1056,12 +1056,19 @@ for (const requiredSnippet of [
1056
1056
  throw new Error(`buildchain ref promotion workflow missing npm transaction snippet: ${requiredSnippet}`);
1057
1057
  }
1058
1058
  }
1059
+ if (buildchainRefPromotionWorkflow.includes("workflow_run.display_title")) {
1060
+ throw new Error(
1061
+ "buildchain ref promotion must classify finalization from rooted product state, not a display title",
1062
+ );
1063
+ }
1059
1064
  const releaseCandidatePromoteWorkflow = fs.readFileSync(path.join(root, ".github/workflows/.release-candidate-promote.yml"), "utf8");
1060
1065
  for (const requiredSnippet of [
1061
1066
  "name: QUALIFY canonical v4 release invocation inputs",
1062
1067
  "name: APPLY one rooted provider transaction",
1063
1068
  "name: SETTLE terminal ReleaseReceipt projection",
1064
1069
  "uses: ./.buildchain/runtime/actions/v4-release-candidate-promote",
1070
+ "name: Resolve one exact product publication recovery",
1071
+ "selectV4RecoveredProductPublicationVersion",
1065
1072
  "publisher-workflow-sha:",
1066
1073
  "runtime-commit:",
1067
1074
  "runtime-tree:",
@@ -1101,31 +1108,15 @@ for (const workflowFile of fs.readdirSync(workflowDir).filter((entry) => entry.e
1101
1108
  }
1102
1109
  }
1103
1110
  for (const retiredWorkflow of [
1111
+ ".release-docker.yml",
1104
1112
  ".release-new-version.yml",
1105
1113
  ".release-elastic-beanstalk.yml",
1106
1114
  ".sam-release.yml",
1107
1115
  ".wheel-release.yml",
1116
+ "schedule-purge-artifacts.yml",
1108
1117
  ]) {
1109
- const retiredSource = fs.readFileSync(path.join(workflowDir, retiredWorkflow), "utf8");
1110
- for (const requiredSnippet of [
1111
- "release path is retired",
1112
- "release-candidate-promote.yml@v3",
1113
- "publish-gate source-lock enforcement",
1114
- ]) {
1115
- if (!retiredSource.includes(requiredSnippet)) {
1116
- throw new Error(`${retiredWorkflow} must fail closed and point callers at the source-locked release-candidate-promote model: ${requiredSnippet}`);
1117
- }
1118
- }
1119
- for (const forbiddenSnippet of [
1120
- "npm publish --access=public",
1121
- "actions/publish-prebuilt@v2",
1122
- "actions/bump-version@v2",
1123
- "beanstalk-deploy@",
1124
- "sam deploy",
1125
- ]) {
1126
- if (retiredSource.includes(forbiddenSnippet)) {
1127
- throw new Error(`${retiredWorkflow} must not keep retired publish side effects: ${forbiddenSnippet}`);
1128
- }
1118
+ if (fs.existsSync(path.join(workflowDir, retiredWorkflow))) {
1119
+ throw new Error(`${retiredWorkflow} is fully retired and must remain deleted`);
1129
1120
  }
1130
1121
  }
1131
1122
  const promoteBuildchainRefAction = fs.readFileSync(path.join(root, "actions/promote-buildchain-ref/action.yml"), "utf8");