@kungfu-tech/buildchain 4.0.8 → 4.0.9-alpha.1

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 (57) hide show
  1. package/AGENTS.md +12 -19
  2. package/CONTRIBUTING.md +6 -9
  3. package/README.md +11 -1
  4. package/architecture/agent-change-map.md +11 -4
  5. package/architecture/build-environments.json +71 -0
  6. package/architecture/build-orchestration.json +18 -0
  7. package/architecture/ci-lane-change-budget.json +51 -2
  8. package/architecture/internal-capabilities.json +17 -4
  9. package/architecture/maintainability-debt.json +37 -126
  10. package/architecture/maintainability-policy.json +19 -210
  11. package/architecture/release-topology.json +9 -7
  12. package/architecture/universal-workflow-bootstrap.json +5 -4
  13. package/architecture/universal-workflow-train-admission.json +2 -2
  14. package/architecture/workflow-taxonomy.json +13 -0
  15. package/bin/buildchain.mjs +1 -1
  16. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  17. package/dist/site/buildchain-contract.json +41 -1131
  18. package/dist/site/buildchain-site.json +48 -153
  19. package/dist/site/capability-registry.json +2 -2
  20. package/dist/site/cli-registry.json +1 -1
  21. package/dist/site/controller-registry.json +30 -868
  22. package/dist/site/kfd-claims.json +148 -396
  23. package/dist/site/kfd-upstream-aggregate.json +1 -1
  24. package/dist/site/manual-registry.json +7 -7
  25. package/dist/site/node-api-registry.json +37 -37
  26. package/dist/site/page-registry.json +36 -141
  27. package/dist/site/public-surface-audit.json +93 -402
  28. package/dist/site/publication-authority-registry.json +22 -1
  29. package/dist/site/publication-registry.json +4 -4
  30. package/dist/site/site-manifest.json +11 -11
  31. package/dist/site/workflow-registry.json +165 -391
  32. package/docs/MAP.md +5 -4
  33. package/docs/cli-reference.md +1 -1
  34. package/docs/getting-started.md +8 -7
  35. package/docs/node-api-reference.md +85 -85
  36. package/docs/release-governance.md +19 -18
  37. package/docs/reusable-build-surface.md +160 -1430
  38. package/docs/runtime-train-validation.md +45 -172
  39. package/docs/workflow-catalog.md +1 -0
  40. package/package.json +1 -1
  41. package/packages/core/build-configuration.js +117 -0
  42. package/packages/core/buildchain-config.js +2 -0
  43. package/packages/core/buildchain-contract.js +14 -44
  44. package/packages/core/buildchain-publication-authority.js +1 -0
  45. package/packages/core/controller-evidence.js +2 -9
  46. package/scripts/buildchain-cli-help.mjs +1 -1
  47. package/scripts/check-floating-consumer-policy-contract.mjs +4 -7
  48. package/scripts/check-inventory.mjs +91 -77
  49. package/scripts/check-maintainability.mjs +23 -8
  50. package/scripts/check-universal-workflow-bootstrap.mjs +12 -2
  51. package/scripts/generate-channel-build-workflow.mjs +50 -410
  52. package/scripts/generate-site-bundle.mjs +2 -1
  53. package/scripts/generate-universal-workflow-facades.mjs +1 -0
  54. package/scripts/init-repo.mjs +25 -26
  55. package/scripts/resolve-build-configuration.mjs +105 -0
  56. package/scripts/universal-workflow-self-dogfood.mjs +8 -1
  57. package/scripts/verify-golden-path.mjs +3 -3
@@ -1,13 +1,13 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { parseWorkflowCallJobs } from "../packages/core/workflow-yaml-contract.js";
4
+ import { execFileSync } from "node:child_process";
3
5
  import {
4
6
  assertPublicSurfaceReverseAudit,
5
7
  collectPublicSurfaceReverseAudit,
6
8
  } from "../packages/core/public-surface-audit.js";
7
- import { evaluateBuildchainContractLock } from "../packages/core/buildchain-contract.js";
9
+ import { createBuildchainContractLock, evaluateBuildchainContractLock, finalizeBuildchainContractWorld } from "../packages/core/buildchain-contract.js";
8
10
  import {
9
- canAdmitSelfDogfoodLockEvaluation,
10
- contractForSelfDogfoodEvaluation,
11
11
  hasQualifiedSelfDogfoodBootstrapAuthority,
12
12
  resolveSelfDogfoodMajor,
13
13
  } from "../packages/core/self-dogfood-version.js";
@@ -161,6 +161,7 @@ const requiredPaths = [
161
161
  ".github/workflows/self-ops-patrol-weekly.yml",
162
162
  ".github/workflows/self-ops-patrol-monthly.yml",
163
163
  ".github/workflows/self-build-alpha-dogfood.yml",
164
+ ".github/workflows/self-build-stable-dogfood.yml",
164
165
  ".github/workflows/release-candidate-promote.yml",
165
166
  ".github/workflows/.release-candidate-promote.yml",
166
167
  ".github/workflows/release-propagation.yml",
@@ -196,16 +197,9 @@ if (rootPackage.name !== "@kungfu-tech/buildchain") {
196
197
  if (rootPackage.private !== false) {
197
198
  throw new Error("root package must be publishable with private=false");
198
199
  }
199
- const selfDogfoodWorkflow = fs.readFileSync(
200
- path.join(root, ".github/workflows/self-build-alpha-dogfood.yml"),
201
- "utf8",
202
- );
203
200
  const selfDogfoodAlphaLock = JSON.parse(
204
201
  fs.readFileSync(path.join(root, ".buildchain/alpha-contract-lock.json"), "utf8"),
205
202
  );
206
- const currentBuildchainContract = JSON.parse(
207
- fs.readFileSync(path.join(root, "dist/site/buildchain-contract.json"), "utf8"),
208
- );
209
203
  const selfDogfoodBootstrapAuthority = JSON.parse(
210
204
  fs.readFileSync(path.join(root, "architecture/bootstrap-authority.json"), "utf8"),
211
205
  );
@@ -227,34 +221,66 @@ if (!/^[0-9a-f]{40}$/.test(selfDogfoodAlphaLock.buildchain?.resolvedSha || ""))
227
221
  if (selfDogfoodAlphaLock.buildchain?.compatibilityPolicy !== "major-compatible") {
228
222
  throw new Error("Buildchain self-dogfood alpha lock must enforce major-compatible policy");
229
223
  }
230
- const selfDogfoodAlphaEvaluation = evaluateBuildchainContractLock({
231
- lock: selfDogfoodAlphaLock,
232
- current: contractForSelfDogfoodEvaluation({
233
- currentContract: currentBuildchainContract,
234
- majorResolution: selfDogfoodMajorResolution,
235
- }),
236
- runtimeRef: `v${selfDogfoodMajor}-alpha`,
237
- runtimeSha: "current-development-contract",
238
- runtimeClass: "alpha", workflowShellRef: `v${selfDogfoodMajor}-alpha`,
239
- });
240
- if (
241
- !canAdmitSelfDogfoodLockEvaluation({
242
- evaluation: selfDogfoodAlphaEvaluation,
243
- majorResolution: selfDogfoodMajorResolution,
244
- })
245
- ) {
246
- throw new Error("Buildchain self-dogfood alpha lock requires review after a breaking contract change");
247
- }
248
- for (const requiredSnippet of [
249
- `/.github/workflows/build.yml@v${selfDogfoodMajor}-alpha`,
250
- "buildchain-channel: auto",
251
- "buildchain-channel: stable",
252
- `const alphaRef = "v${selfDogfoodMajor}-alpha"`,
253
- `const stableRef = "v${selfDogfoodMajor}"`,
254
- ]) {
255
- if (!selfDogfoodWorkflow.includes(requiredSnippet)) {
256
- throw new Error(`Buildchain self-dogfood workflow missing current-major snippet: ${requiredSnippet}`);
257
- }
224
+ // Consumer locks describe accepted published runtimes, not this unpublished producer.
225
+ // Validate their immutable source worlds here; hosted admission checks floating drift.
226
+ for (const channel of ["alpha-contract-lock.json", "contract-lock.json"]) {
227
+ const lock = JSON.parse(
228
+ fs.readFileSync(path.join(root, ".buildchain", channel), "utf8"),
229
+ );
230
+ const sha = lock.buildchain?.resolvedSha;
231
+ if (!/^[0-9a-f]{40}$/u.test(sha || ""))
232
+ throw new Error(`${channel}: invalid accepted SHA`);
233
+ let world;
234
+ try {
235
+ world = JSON.parse(
236
+ execFileSync(
237
+ "git",
238
+ ["show", `${sha}:dist/site/buildchain-contract.json`],
239
+ { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] },
240
+ ),
241
+ );
242
+ } catch {
243
+ const response = await fetch(
244
+ `https://raw.githubusercontent.com/kungfu-systems/buildchain/${sha}/dist/site/buildchain-contract.json`,
245
+ { signal: AbortSignal.timeout(30000) },
246
+ );
247
+ if (!response.ok)
248
+ throw new Error(
249
+ `${channel}: accepted source contract unavailable (${response.status})`,
250
+ );
251
+ world = await response.json();
252
+ }
253
+ const verified = finalizeBuildchainContractWorld(world);
254
+ for (const field of ["contractDigest", "compatibilityDigest"]) {
255
+ if (
256
+ world[field] !== verified[field] ||
257
+ lock.buildchain[field] !== world[field]
258
+ )
259
+ throw new Error(`${channel}: accepted ${field} mismatch`);
260
+ }
261
+ const accepted = createBuildchainContractLock({ contractWorld: verified }).buildchain;
262
+ if (JSON.stringify(lock.buildchain.surfaces) !== JSON.stringify(accepted.surfaces) || lock.buildchain.majorLine !== verified.majorLine || lock.buildchain.compatibilityPolicy !== "major-compatible") throw new Error(`${channel}: accepted surface set or policy mismatch`);
263
+ const ref = channel.startsWith("alpha")
264
+ ? `v${selfDogfoodMajor}-alpha`
265
+ : `v${selfDogfoodMajor}`;
266
+ const evaluation = evaluateBuildchainContractLock({
267
+ lock,
268
+ current: verified,
269
+ runtimeRef: ref,
270
+ runtimeSha: sha,
271
+ runtimeClass: channel.startsWith("alpha") ? "alpha" : "stable",
272
+ workflowShellRef: ref,
273
+ });
274
+ if (!evaluation.ok || lock.buildchain.ref !== ref)
275
+ throw new Error(`${channel}: invalid accepted runtime contract`);
276
+ }
277
+ for (const [channel, ref] of [["alpha", `v${selfDogfoodMajor}-alpha`], ["stable", `v${selfDogfoodMajor}`]]) {
278
+ const workflow = fs.readFileSync(path.join(root, `.github/workflows/self-build-${channel}-dogfood.yml`), "utf8");
279
+ if (!workflow.includes(`/.github/workflows/build.yml@${ref}`) || /steps:|buildchain-channel:|runner-preset:|working-directory:/u.test(workflow)) throw new Error(`${channel} self-dogfood must remain a thin public TOML build caller`);
280
+ if (!workflow.includes(`group: buildchain-${channel}-self-dogfood-`) || !workflow.includes("cancel-in-progress: false")) throw new Error(`${channel} self-dogfood must serialize its own runs`);
281
+ const expectedInputs = channel === "alpha" ? [] : ["config-path"];
282
+ const call = parseWorkflowCallJobs(workflow).find((job) => job.id === `${channel}-consumer`);
283
+ if (JSON.stringify(Object.keys(call.with || {})) !== JSON.stringify(expectedInputs)) throw new Error(`${channel} self-dogfood input contract drift`);
258
284
  }
259
285
  const reusableBuildWorkflow = fs.readFileSync(
260
286
  path.join(root, ".github/workflows/.build.yml"),
@@ -262,8 +288,8 @@ const reusableBuildWorkflow = fs.readFileSync(
262
288
  );
263
289
  for (const requiredSnippet of [
264
290
  "BUILDCHAIN_WORKFLOW_REF: ${{ job.workflow_ref }}",
265
- "process.env.BUILDCHAIN_WORKFLOW_REF || process.env.GITHUB_WORKFLOW_REF",
266
- 'replace(/^refs\\/(?:heads|tags)\\//, "")',
291
+ "BUILDCHAIN_WORKFLOW_SHA: ${{ job.workflow_sha }}",
292
+ "Resolve rooted build plan from TOML",
267
293
  ]) {
268
294
  if (!reusableBuildWorkflow.includes(requiredSnippet)) {
269
295
  throw new Error(`reusable build workflow missing called-workflow identity: ${requiredSnippet}`);
@@ -343,31 +369,13 @@ const promotionOverrideAuthorization = fs.readFileSync(
343
369
  if (!promotionOverrideAuthorization.includes("promotion runtime override is only allowed for trusted workflow_dispatch runs")) {
344
370
  throw new Error("promotion runtime override authorization must remain fail closed");
345
371
  }
346
- for (const requiredSnippet of [
347
- "buildchain-channel:",
348
- "uses: ./.github/workflows/.build.yml",
349
- "needs.resolve-channel.outputs.runtime-override != 'true' && needs.resolve-channel.outputs.channel == 'alpha'",
350
- "needs.resolve-channel.outputs.runtime-override != 'true' && needs.resolve-channel.outputs.channel == 'stable'",
351
- "needs.resolve-channel.outputs.buildchain-ref",
352
- "needs.resolve-channel.outputs.contract-lock-path",
353
- ]) {
354
- if (!channelBuildWorkflow.includes(requiredSnippet)) {
355
- throw new Error(`channel build workflow missing routing contract: ${requiredSnippet}`);
356
- }
357
- }
358
- if ((channelBuildWorkflow.match(/uses: \.\/\.github\/workflows\/\.build\.yml/g) || []).length !== 3) {
359
- throw new Error("channel build workflow must bind override, alpha, and stable to the exact caller workflow shell");
360
- }
361
- if (channelBuildWorkflow.includes("uses: kungfu-systems/buildchain/.github/workflows/.build.yml@")) {
362
- throw new Error("channel build workflow must not statically fetch another channel shell before its ref exists");
372
+ for (const workflow of [channelBuildWorkflow, reusableBuildWorkflow]) {
373
+ const block = workflow.split(" inputs:\n")[1].split(" secrets:\n")[0];
374
+ const names = [...block.matchAll(/^ ([a-z0-9-]+):$/gm)].map((match) => match[1]);
375
+ if (JSON.stringify(names) !== '["config-path"]') throw new Error("ordinary build inputs must contain only config-path");
363
376
  }
364
- for (const requiredSnippet of [
365
- "group: buildchain-release-promotion-${{ github.repository }}",
366
- "cancel-in-progress: false",
367
- ]) {
368
- if (!selfDogfoodWorkflow.includes(requiredSnippet)) {
369
- throw new Error(`Buildchain self-dogfood workflow missing promotion serialization: ${requiredSnippet}`);
370
- }
377
+ if ((channelBuildWorkflow.match(/uses: \.\/\.github\/workflows\/\.build\.yml/g) || []).length !== 1) {
378
+ throw new Error("public build facade must invoke its exact backbone once");
371
379
  }
372
380
  const actionlintConfig = fs.readFileSync(
373
381
  path.join(root, ".github/actionlint.yaml"),
@@ -780,8 +788,9 @@ const registeredActionIds = (workflowRegistry.actions || []).map((entry) => entr
780
788
  const readmeActionIndex = fs.readFileSync(path.join(root, "README.md"), "utf8");
781
789
  const mapActionIndex = fs.readFileSync(path.join(root, "docs/MAP.md"), "utf8");
782
790
  const retrospectiveActionIndex = fs.readFileSync(path.join(root, ".github/retrospectives/2026-07-10-buildchain-consolidation.md"), "utf8");
783
- if (registeredActionIds.length !== 8) {
784
- throw new Error(`workflow-registry.json must expose the eight current action entries, got ${registeredActionIds.length}`);
791
+ const buildOwners = JSON.parse(fs.readFileSync(path.join(root, "architecture/build-orchestration.json"), "utf8")).owners;
792
+ if (registeredActionIds.length !== 8 + Object.keys(buildOwners).length) {
793
+ throw new Error(`workflow-registry.json must expose the eight public actions and owned build composites, got ${registeredActionIds.length}`);
785
794
  }
786
795
  for (const actionId of registeredActionIds) {
787
796
  if (!readmeActionIndex.includes(`actions/${actionId}`) || !mapActionIndex.includes(`actions/${actionId}`)) {
@@ -925,16 +934,16 @@ for (const requiredSnippet of [
925
934
  }
926
935
  const reusableBuildSurfaceDoc = fs.readFileSync(path.join(root, "docs/reusable-build-surface.md"), "utf8");
927
936
  for (const requiredSnippet of [
928
- "Floating Ref Contract Lock",
929
- "dist/site/buildchain-contract.json",
930
- "buildchain-contract-drift-issue-mode",
931
- "compatible drift",
932
- "Locked Source Checkout Cache",
933
- "checkout-cache-mode",
934
- "BUILDCHAIN_CHECKOUT_CACHE_MIRROR_URL_TEMPLATE",
935
- "sourceCheckout",
936
- "Shifu Cache Profile Passthrough",
937
- "opaque reference and digest",
937
+ "config-path",
938
+ "buildchain.toml",
939
+ ".buildchain/contract-lock.json",
940
+ ".buildchain/alpha-contract-lock.json",
941
+ "architecture/build-environments.json",
942
+ "build.contract",
943
+ "cache roots",
944
+ "exact consumer source",
945
+ "build-lifecycle-stage",
946
+ "build-artifact-transfer",
938
947
  ]) {
939
948
  if (!reusableBuildSurfaceDoc.includes(requiredSnippet)) {
940
949
  throw new Error(`reusable build surface doc missing contract lock snippet: ${requiredSnippet}`);
@@ -1319,7 +1328,12 @@ if (!Array.isArray(inventory.migratedActions) || inventory.migratedActions.lengt
1319
1328
  throw new Error("migratedActions must be empty; buildchain v2 only ships native actions");
1320
1329
  }
1321
1330
  const shippedActions = internalActions;
1322
- const shippedActionNames = shippedActions.map((action) => action.path.replace(/^actions\//, "")).sort();
1331
+ if (inventory.compositeActionOwnership !== "architecture/build-orchestration.json#owners") throw new Error("composite action ownership must use the build orchestration contract");
1332
+ const compositeNames = Object.keys(buildOwners).map((file) => {
1333
+ if (!/^actions\/[^/]+\/action\.yml$/.test(file) || !/using:\s*composite/.test(fs.readFileSync(path.join(root, file), "utf8"))) throw new Error(`invalid owned composite action: ${file}`);
1334
+ return path.posix.basename(path.posix.dirname(file));
1335
+ });
1336
+ const shippedActionNames = [...shippedActions.map((action) => action.path.replace(/^actions\//, "")), ...compositeNames].sort();
1323
1337
 
1324
1338
  if (JSON.stringify(actualActions) !== JSON.stringify(shippedActionNames)) {
1325
1339
  throw new Error(
@@ -82,15 +82,30 @@ function revisionAvailable(root, revision) {
82
82
  }
83
83
  }
84
84
 
85
- function ensureRevisionAvailable(root, revision) {
85
+ function ensureRevisionAvailable(
86
+ root,
87
+ revision,
88
+ {
89
+ fetchRevision = () =>
90
+ gitOutput(root, ["fetch", "--no-tags", "--depth=1", "origin", revision]),
91
+ } = {},
92
+ ) {
86
93
  if (revisionAvailable(root, revision)) return false;
87
- try {
88
- gitOutput(root, ["fetch", "--no-tags", "--depth=1", "origin", revision]);
89
- } catch (error) {
90
- const detail = String(error?.stderr || error?.message || error).trim();
91
- throw new Error(
92
- `maintainability revision ${revision} is unavailable and could not be fetched from origin${detail ? `: ${detail}` : ""}`,
93
- );
94
+ for (let attempt = 0; attempt < 3; attempt += 1) {
95
+ try {
96
+ fetchRevision();
97
+ break;
98
+ } catch (error) {
99
+ const detail = String(error?.stderr || error?.message || error).trim();
100
+ if (
101
+ attempt < 2 &&
102
+ /shallow file has changed since we read it/u.test(detail)
103
+ )
104
+ continue;
105
+ throw new Error(
106
+ `maintainability revision ${revision} is unavailable and could not be fetched from origin${detail ? `: ${detail}` : ""}`,
107
+ );
108
+ }
94
109
  }
95
110
  if (!revisionAvailable(root, revision)) {
96
111
  throw new Error(
@@ -75,13 +75,23 @@ for (const relative of contract.retiredWorkflowSurfaces) {
75
75
  const activeWorkflows = contract.inventoryWorkflows.filter(
76
76
  (relative) =>
77
77
  !contract.retiredWorkflowSurfaces.includes(relative) &&
78
+ !contract.configurationGovernedWorkflows.includes(relative) &&
78
79
  relative !== contract.bootstrap.consumerRecoveryWorkflow,
79
80
  );
80
81
  assert.deepEqual(
81
82
  contract.bootstrapGovernedWorkflows,
82
83
  activeWorkflows,
83
- "every active public reusable workflow must be Bootstrap-governed",
84
- );
84
+ "every active reusable workflow must have its declared configuration or Bootstrap authority",
85
+ );
86
+ assert.deepEqual(contract.configurationGovernedWorkflows, [
87
+ ".github/workflows/.build.yml", ".github/workflows/build.yml",
88
+ ]);
89
+ for (const relative of contract.configurationGovernedWorkflows) {
90
+ const source = fs.readFileSync(path.join(root, relative), "utf8");
91
+ const inputBlock = source.split(" inputs:\n")[1].split(" secrets:\n")[0];
92
+ assert.deepEqual([...inputBlock.matchAll(/^ ([a-z0-9-]+):$/gmu)].map((match) => match[1]), ["config-path"]);
93
+ assert.doesNotMatch(source, /universal-request-json/u);
94
+ }
85
95
  for (const relative of contract.bootstrapGovernedWorkflows) {
86
96
  assert.ok(
87
97
  contract.inventoryWorkflows.includes(relative),