@kungfu-tech/buildchain 3.0.9-alpha.0 → 3.0.9-alpha.10

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 (117) hide show
  1. package/bin/buildchain.mjs +9 -1
  2. package/contracts/dev-delivery-authority-v2.schema.json +253 -0
  3. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +16 -0
  4. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +57 -0
  5. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
  6. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
  7. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
  8. package/contracts/next-development-request-v1.schema.json +66 -0
  9. package/contracts/next-development-transition-v1.schema.json +292 -0
  10. package/contracts/release-cut-v1.schema.json +1 -0
  11. package/contracts/release-train-transition-v1.schema.json +1 -0
  12. package/dist/site/agent-index.json +1 -0
  13. package/dist/site/artifact-schemas.json +2 -0
  14. package/dist/site/buildchain-contract.json +91 -32
  15. package/dist/site/buildchain-site.json +353 -45
  16. package/dist/site/capability-registry.json +7 -6
  17. package/dist/site/cli-registry.json +41 -2
  18. package/dist/site/controller-registry.json +8 -4
  19. package/dist/site/kfd-claims.json +193 -9
  20. package/dist/site/kfd-upstream-aggregate.json +9 -9
  21. package/dist/site/manual-registry.json +26 -10
  22. package/dist/site/node-api-registry.json +8012 -4574
  23. package/dist/site/page-registry.json +331 -31
  24. package/dist/site/public-surface-audit.json +353 -8
  25. package/dist/site/publication-registry.json +4 -4
  26. package/dist/site/release-passport-check-manifest.json +1 -0
  27. package/dist/site/release-provenance.json +12 -0
  28. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +442 -0
  29. package/dist/site/schemas/release-passport-v1.schema.json +3 -0
  30. package/dist/site/site-manifest.json +22 -14
  31. package/dist/site/workflow-registry.json +15 -7
  32. package/docs/MAP.md +5 -2
  33. package/docs/adopter-delivery-gate.md +106 -0
  34. package/docs/aws-us-elastic-runner-burst-plane.md +30 -3
  35. package/docs/cli-reference.md +200 -13
  36. package/docs/dev-alpha-candidate-patrol.md +8 -0
  37. package/docs/dev-delivery-qualification-landing-adr.md +137 -0
  38. package/docs/dev-delivery-warrant.md +164 -27
  39. package/docs/kfd-support.md +7 -2
  40. package/docs/next-development-transition.md +119 -0
  41. package/docs/node-api-reference.md +464 -277
  42. package/docs/release-governance.md +10 -7
  43. package/docs/release-passport.md +15 -2
  44. package/docs/release-train.md +46 -4
  45. package/docs/reusable-build-surface.md +26 -9
  46. package/docs/runtime-train-validation.md +6 -0
  47. package/docs/versioning.md +6 -2
  48. package/package.json +21 -6
  49. package/packages/core/README.md +25 -0
  50. package/packages/core/adopter-delivery-gate.js +576 -0
  51. package/packages/core/adopter-delivery-json.js +66 -0
  52. package/packages/core/adopter-delivery-passport.js +259 -0
  53. package/packages/core/adopter-delivery-vectors.js +158 -0
  54. package/packages/core/adopter-delivery-vectors.json +77 -0
  55. package/packages/core/artifact-verification-envelope.js +5 -5
  56. package/packages/core/buildchain-agent-manuals.js +1 -0
  57. package/packages/core/buildchain-channel-identity.js +118 -0
  58. package/packages/core/buildchain-compatibility-proof.js +54 -0
  59. package/packages/core/buildchain-contract.js +7 -0
  60. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  61. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  62. package/packages/core/channel-candidate.js +8 -0
  63. package/packages/core/channel-promotion-baseline.js +52 -0
  64. package/packages/core/controller-evidence.js +2 -1
  65. package/packages/core/dev-alpha-candidate-selection.js +10 -2
  66. package/packages/core/dev-delivery-authority.js +996 -0
  67. package/packages/core/dev-delivery-native-proof.js +418 -0
  68. package/packages/core/dev-delivery-warrant-settlement.js +67 -20
  69. package/packages/core/dev-delivery-warrant.js +141 -48
  70. package/packages/core/kfd-adopter-category-driver.js +167 -0
  71. package/packages/core/kfd-adopter-manifest.js +49 -50
  72. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  73. package/packages/core/next-development-candidate-reservation.js +186 -0
  74. package/packages/core/next-development-controller.js +726 -0
  75. package/packages/core/next-development-projection.js +288 -0
  76. package/packages/core/next-development-transition.js +738 -0
  77. package/packages/core/paper-agent-entry.js +7 -4
  78. package/packages/core/paper.js +14 -7
  79. package/packages/core/release-blocker-priority.js +192 -0
  80. package/packages/core/release-passport-contract.js +10 -6
  81. package/packages/core/release-passport.js +29 -12
  82. package/packages/core/release-train.js +220 -0
  83. package/scripts/aws-macos-jit-controller-core.mjs +239 -4
  84. package/scripts/aws-macos-jit-controller.mjs +198 -2
  85. package/scripts/aws-macos-jit-instance-rehydrate.mjs +260 -0
  86. package/scripts/aws-macos-jit-source-rebind.mjs +235 -14
  87. package/scripts/aws-macos-jit.mjs +12 -0
  88. package/scripts/buildchain-channel-router.mjs +18 -2
  89. package/scripts/buildchain-cli-help.mjs +9 -2
  90. package/scripts/buildchain-contract-lock.mjs +11 -1
  91. package/scripts/check-internal-architecture.mjs +1 -0
  92. package/scripts/check-inventory.mjs +14 -3
  93. package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
  94. package/scripts/dev-delivery-authority.mjs +368 -0
  95. package/scripts/dev-delivery-native-run.mjs +235 -0
  96. package/scripts/dev-delivery-proof.mjs +64 -2
  97. package/scripts/dev-delivery-two-phase-resume.mjs +90 -0
  98. package/scripts/dev-delivery-two-phase.mjs +577 -0
  99. package/scripts/dev-delivery-warrant.mjs +47 -5
  100. package/scripts/dev-pr-delivery-warrant.mjs +19 -14
  101. package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
  102. package/scripts/generate-channel-build-workflow.mjs +82 -31
  103. package/scripts/generate-next-development-guidance.mjs +49 -0
  104. package/scripts/generate-site-bundle.mjs +8 -3
  105. package/scripts/init-repo.mjs +135 -62
  106. package/scripts/next-development-self-dogfood-harness.mjs +409 -0
  107. package/scripts/next-development-self-dogfood.mjs +532 -0
  108. package/scripts/next-development-transition.mjs +47 -0
  109. package/scripts/promotion-channel-router.mjs +5 -3
  110. package/scripts/release-candidate-tail-reseal.mjs +426 -0
  111. package/scripts/release-train-self-dogfood.mjs +579 -0
  112. package/scripts/runtime-ref-core.mjs +4 -17
  113. package/scripts/seal-artifact-signing-requests.mjs +7 -4
  114. package/scripts/site-capability-metadata.mjs +23 -1
  115. package/scripts/stable-candidate-qualification.mjs +7 -7
  116. package/scripts/workflow-call-contract.mjs +1 -1
  117. package/templates/native-dev-delivery.yml +86 -0
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import { defaultDevDeliveryStateRef, GitHubDevDeliveryStore } from "./dev-delivery-warrant.mjs";
2
3
 
3
4
  const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
4
5
  const SHA_PATTERN = /^[0-9a-f]{40}$/;
@@ -34,6 +35,11 @@ function exactActiveReadback(result) {
34
35
  const warrant = result.observation.activeWarrant;
35
36
  const candidate = result.observation.activeCandidate;
36
37
  requireMatch(warrant?.schema === "kungfu.buildchain.dev-delivery-warrant/v1", "delivery-warrant-missing");
38
+ requireMatch((warrant.phase || "qualified") === "qualified", "delivery-warrant-not-qualified");
39
+ if (warrant.phase === "qualified") {
40
+ requireMatch(ROOT_PATTERN.test(String(warrant.nativeProofRoot || "")), "delivery-warrant-native-proof-missing");
41
+ requireMatch(ROOT_PATTERN.test(String(warrant.nativeProofReuseRoot || "")), "delivery-warrant-native-reuse-missing");
42
+ }
37
43
  requireMatch(candidate?.candidateId === warrant.candidateId, "delivery-warrant-candidate-readback-missing");
38
44
  requireMatch(!result.warrant || JSON.stringify(result.warrant) === JSON.stringify(warrant), "delivery-warrant-readback-mismatch");
39
45
  return { warrant, candidate };
@@ -117,19 +123,18 @@ export function readDeliveryWarrantResult(options, pullRequest) {
117
123
  }
118
124
 
119
125
  export async function readCurrentDeliveryQueueState(client, repository, targetBranch) {
120
- if (typeof client.getDevDeliveryQueueState === "function") {
121
- return client.getDevDeliveryQueueState(targetBranch);
122
- }
123
- const stateRef = `buildchain/dev-delivery-warrant/${targetBranch.replaceAll("/", "-")}`;
124
- const query = new URLSearchParams({ ref: stateRef });
125
- const { data } = await client.request(
126
- "GET",
127
- `/repos/${repository.owner}/${repository.repo}/contents/queue.json?${query}`,
128
- );
129
- if (data?.type !== "file" || data?.encoding !== "base64" || !data?.content) {
130
- mismatch("delivery-warrant-current-readback-invalid");
131
- }
132
- return JSON.parse(Buffer.from(String(data.content).replace(/\s+/g, ""), "base64").toString("utf8"));
126
+ if (typeof client.getDevDeliveryQueueState === "function") return client.getDevDeliveryQueueState(targetBranch);
127
+ const store = new GitHubDevDeliveryStore({
128
+ repository: `${repository.owner}/${repository.repo}`,
129
+ token: client.token,
130
+ apiUrl: client.apiUrl,
131
+ fetchImpl: client.fetch,
132
+ });
133
+ const { queue } = await store.read({
134
+ stateRef: defaultDevDeliveryStateRef(targetBranch),
135
+ protectedBase: targetBranch,
136
+ });
137
+ return queue;
133
138
  }
134
139
 
135
140
  export async function verifyCurrentDeliveryWarrant(client, options, pullRequest, warrant) {
@@ -147,7 +152,7 @@ export async function verifyCurrentDeliveryWarrant(client, options, pullRequest,
147
152
  requireMatch(Number(active?.generation) === Number(warrant.generation), "delivery-warrant-current-generation-mismatch");
148
153
  requireMatch(Number(active?.pullRequestNumber) === Number(pullRequest.number), "delivery-warrant-current-pr-mismatch");
149
154
  requireMatch(String(active?.sourceHead || "").toLowerCase() === options.expectedHeadSha, "delivery-warrant-current-head-mismatch");
150
- requireMatch(["selected", "proving", "waiting", "blocked"].includes(candidate?.status), "delivery-warrant-current-candidate-not-selected");
155
+ requireMatch(["selected", "proving", "waiting", "blocked", "qualified"].includes(candidate?.status), "delivery-warrant-current-candidate-not-selected");
151
156
  requireMatch(candidate?.sourceHead === active.sourceHead, "delivery-warrant-current-candidate-head-mismatch");
152
157
  }
153
158
 
@@ -1,121 +1,115 @@
1
1
  #!/usr/bin/env node
2
+ import crypto from "node:crypto";
2
3
  import fs from "node:fs";
3
- import path from "node:path";
4
4
  import { execFileSync } from "node:child_process";
5
+ import { createRequire } from "node:module";
6
+ import path from "node:path";
5
7
  import { pathToFileURL } from "node:url";
6
- import {
7
- createBuildchainKfd1Witness,
8
- createBuildchainKfd2Claims,
9
- createBuildchainKfd3ArtifactWitness,
10
- createBuildchainKfd3PrebuildWitness,
11
- } from "../packages/core/buildchain-kfd-claims.js";
12
- import {
13
- BUILDCHAIN_KFD1_CONTRACT_WORLD_WITNESS_PATH,
14
- BUILDCHAIN_KFD2_CLAIMS_DIR,
15
- BUILDCHAIN_KFD3_ARTIFACT_WITNESS_PATH,
16
- BUILDCHAIN_KFD3_PREBUILD_WITNESS_PATH,
17
- } from "../packages/core/buildchain-layout.js";
8
+ import { addAdopterWitness, initAdopterManifest } from "@kungfu-tech/kfd/adopter-conformance/toolchain";
9
+ import kfdStandards from "@kungfu-tech/kfd/standards.json" with { type: "json" };
10
+ import { installedKfdPackageArtifactRoot } from "../packages/core/artifact-verification-envelope.js";
11
+ import { createKfdAdopterManifestGate, createKfdLegacySupportMatrixProjection, validateKfdAdopterManifestGate, validateKfdLegacySupportMatrixProjection } from "../packages/core/kfd-adopter-manifest.js";
12
+ import { KFD_PRODUCT_GATE_INPUT_CONTRACT, evaluateKfdProductGate, kfdProductGateDigest, validateKfdProductGateResult } from "../packages/core/kfd-product-gates.js";
13
+ import { createBuildchainKfd1Witness, createBuildchainKfd2Claims, createBuildchainKfd3ArtifactWitness, createBuildchainKfd3PrebuildWitness } from "../packages/core/buildchain-kfd-claims.js";
14
+ import { BUILDCHAIN_KFD1_CONTRACT_WORLD_WITNESS_PATH, BUILDCHAIN_KFD2_CLAIMS_DIR, BUILDCHAIN_KFD3_ARTIFACT_WITNESS_PATH, BUILDCHAIN_KFD3_PREBUILD_WITNESS_PATH } from "../packages/core/buildchain-layout.js";
18
15
  import { writeGitHubOutputs } from "./build-contract-core.mjs";
19
-
16
+ const REPOSITORY = "kungfu-systems/buildchain", require = createRequire(import.meta.url), kfdRoot = path.dirname(require.resolve("@kungfu-tech/kfd/package.json"));
17
+ const evidenceSources = { "KFD-1": "dist/site/release-passport-check-manifest.json", "KFD-2": "dist/site/kfd-claims.json", "KFD-3": "dist/site/public-surface-audit.json", "KFD-4": "packages/core/kfd-product-gates.js", "KFD-5": "packages/core/kfd-product-gates.js", "KFD-7": "packages/core/kfd-product-gates.js", "KFD-10": "packages/core/dev-delivery-warrant.js" };
20
18
  function parseArgs(argv = process.argv.slice(2)) {
21
- const args = {
22
- cwd: process.cwd(),
23
- outputDir: ".buildchain/kfd",
24
- sourceSha: process.env.BUILDCHAIN_SOURCE_SHA || "",
25
- };
19
+ const args = { cwd: process.cwd(), outputDir: ".buildchain/kfd", sourceSha: process.env.BUILDCHAIN_SOURCE_SHA || "" };
26
20
  for (let index = 0; index < argv.length; index += 1) {
27
21
  const arg = argv[index];
28
- if (arg === "--cwd") {
29
- args.cwd = argv[++index] || args.cwd;
30
- } else if (arg === "--output-dir") {
31
- args.outputDir = argv[++index] || args.outputDir;
32
- } else if (arg === "--source-sha") {
33
- args.sourceSha = argv[++index] || args.sourceSha;
34
- } else if (arg === "--help") {
35
- args.help = true;
36
- } else {
37
- throw new Error(`unknown argument: ${arg}`);
38
- }
22
+ if (arg === "--cwd") args.cwd = argv[++index] || args.cwd;
23
+ else if (arg === "--output-dir") args.outputDir = argv[++index] || args.outputDir;
24
+ else if (arg === "--source-sha") args.sourceSha = argv[++index] || args.sourceSha;
25
+ else if (arg === "--help") args.help = true;
26
+ else throw new Error(`unknown argument: ${arg}`);
39
27
  }
40
28
  return args;
41
29
  }
42
-
43
30
  function gitSha(cwd) {
44
- try {
45
- return execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }).trim();
46
- } catch {
47
- return "";
48
- }
31
+ try { return execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }).trim(); } catch { return ""; }
49
32
  }
50
-
51
- function writeJson(filePath, value) {
33
+ function digest(bytes) { return `sha256:${crypto.createHash("sha256").update(bytes).digest("hex")}`; }
34
+ function fileDigest(filePath) { return digest(fs.readFileSync(filePath)); }
35
+ function relative(root, filePath) { return path.relative(root, filePath).replace(/\\/g, "/"); }
36
+ function writeJson(root, relativePath, value) {
37
+ const filePath = path.join(root, relativePath);
52
38
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
53
39
  fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
40
+ return { path: relativePath, sha256: fileDigest(filePath), filePath };
54
41
  }
55
-
56
- function toRepoRelative(cwd, filePath) {
57
- return path.relative(cwd, filePath).replace(/\\/g, "/");
58
- }
59
-
60
- export function generateBuildchainKfdWitnesses({
61
- cwd = process.cwd(),
62
- outputDir = ".buildchain/kfd",
63
- sourceSha = "",
64
- emitOutputs = true,
65
- } = {}) {
66
- const root = path.resolve(cwd);
67
- const outDir = path.resolve(root, outputDir);
68
- const resolvedSourceSha = sourceSha || gitSha(root);
42
+ function readJson(filePath, sourceSha = "") { return JSON.parse(fs.readFileSync(filePath, "utf8").replaceAll("{{SOURCE_SHA}}", sourceSha)); }
43
+ export function generateBuildchainKfdWitnesses({ cwd = process.cwd(), outputDir = ".buildchain/kfd", sourceSha = "", emitOutputs = true } = {}) {
44
+ const root = path.resolve(cwd), outDir = path.resolve(root, outputDir), resolvedSourceSha = sourceSha || gitSha(root);
69
45
  const outputPath = (canonicalPath) => path.join(outDir, path.relative(".buildchain/kfd", canonicalPath));
70
- const paths = {
71
- kfd1Witness: outputPath(BUILDCHAIN_KFD1_CONTRACT_WORLD_WITNESS_PATH),
72
- kfd3PrebuildWitness: outputPath(BUILDCHAIN_KFD3_PREBUILD_WITNESS_PATH),
73
- kfd3ArtifactWitness: outputPath(BUILDCHAIN_KFD3_ARTIFACT_WITNESS_PATH),
74
- kfd2ClaimsDir: outputPath(BUILDCHAIN_KFD2_CLAIMS_DIR),
75
- };
76
- writeJson(paths.kfd1Witness, createBuildchainKfd1Witness({ root, sourceSha: resolvedSourceSha }));
77
- writeJson(paths.kfd3PrebuildWitness, createBuildchainKfd3PrebuildWitness({ root, sourceSha: resolvedSourceSha }));
78
- writeJson(paths.kfd3ArtifactWitness, createBuildchainKfd3ArtifactWitness({ root, sourceSha: resolvedSourceSha }));
79
- const witnessFiles = {
80
- "kfd-1-witness": toRepoRelative(root, paths.kfd1Witness),
81
- "kfd-3-prebuild-witness": toRepoRelative(root, paths.kfd3PrebuildWitness),
82
- "kfd-3-artifact-witness": toRepoRelative(root, paths.kfd3ArtifactWitness),
83
- };
46
+ const paths = { kfd1Witness: outputPath(BUILDCHAIN_KFD1_CONTRACT_WORLD_WITNESS_PATH), kfd3PrebuildWitness: outputPath(BUILDCHAIN_KFD3_PREBUILD_WITNESS_PATH), kfd3ArtifactWitness: outputPath(BUILDCHAIN_KFD3_ARTIFACT_WITNESS_PATH), kfd2ClaimsDir: outputPath(BUILDCHAIN_KFD2_CLAIMS_DIR) };
47
+ writeJson(path.dirname(paths.kfd1Witness), path.basename(paths.kfd1Witness), createBuildchainKfd1Witness({ root, sourceSha: resolvedSourceSha }));
48
+ writeJson(path.dirname(paths.kfd3PrebuildWitness), path.basename(paths.kfd3PrebuildWitness), createBuildchainKfd3PrebuildWitness({ root, sourceSha: resolvedSourceSha }));
49
+ writeJson(path.dirname(paths.kfd3ArtifactWitness), path.basename(paths.kfd3ArtifactWitness), createBuildchainKfd3ArtifactWitness({ root, sourceSha: resolvedSourceSha }));
50
+ const witnessFiles = { "kfd-1-witness": relative(root, paths.kfd1Witness), "kfd-3-prebuild-witness": relative(root, paths.kfd3PrebuildWitness), "kfd-3-artifact-witness": relative(root, paths.kfd3ArtifactWitness) };
84
51
  const kfd2ClaimPaths = createBuildchainKfd2Claims({ root, witnessFiles }).map((claim) => {
85
- const slug = String(claim.id || "claim")
86
- .replace(/^claim:/, "")
87
- .replace(/[^0-9A-Za-z._-]+/g, "-")
88
- .replace(/^-+|-+$/g, "") || "claim";
89
- const claimPath = path.join(paths.kfd2ClaimsDir, `${slug}.json`);
90
- writeJson(claimPath, claim);
91
- return claimPath;
52
+ const slug = String(claim.id || "claim").replace(/^claim:/, "").replace(/[^0-9A-Za-z._-]+/g, "-").replace(/^-+|-+$/g, "") || "claim";
53
+ return writeJson(paths.kfd2ClaimsDir, `${slug}.json`, claim).filePath;
92
54
  });
93
- const outputs = {
94
- "kfd-1-witness-jsons": toRepoRelative(root, paths.kfd1Witness),
95
- "kfd-2-claim-jsons": kfd2ClaimPaths.map((claimPath) => toRepoRelative(root, claimPath)).join(","),
96
- "kfd-3-prebuild-witness-jsons": toRepoRelative(root, paths.kfd3PrebuildWitness),
97
- "kfd-3-artifact-witness-jsons": toRepoRelative(root, paths.kfd3ArtifactWitness),
98
- "source-sha": resolvedSourceSha,
99
- "output-dir": toRepoRelative(root, outDir),
100
- };
101
- if (emitOutputs) {
102
- writeGitHubOutputs(outputs);
55
+ const outputs = { "kfd-1-witness-jsons": relative(root, paths.kfd1Witness), "kfd-2-claim-jsons": kfd2ClaimPaths.map((filePath) => relative(root, filePath)).join(","), "kfd-3-prebuild-witness-jsons": relative(root, paths.kfd3PrebuildWitness), "kfd-3-artifact-witness-jsons": relative(root, paths.kfd3ArtifactWitness), "source-sha": resolvedSourceSha, "output-dir": relative(root, outDir) };
56
+ if (emitOutputs) writeGitHubOutputs(outputs);
57
+ return { schemaVersion: 1, contract: "kungfu-buildchain-self-kfd-witness-generation", outputs };
58
+ }
59
+ function productRecords(root, standard, sourceSha) {
60
+ if (standard === "kfd-4") return [
61
+ ["observer-perspective", readJson(path.join(root, "contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json"), sourceSha)],
62
+ ["perspective-replay", readJson(path.join(root, "contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json"), sourceSha)],
63
+ ];
64
+ if (standard === "kfd-5") return [["primitive-discovery", readJson(path.join(kfdRoot, "cases/live/software-work-perspective-settlement/cuts/0001-assignment.json"))]];
65
+ const profile = readJson(path.join(kfdRoot, "verifier/fixtures/kfd-7/valid-domain-profile.json"));
66
+ profile.evidenceObligations = profile.evidenceObligations.map((entry) => ({ ...entry, status: "passed", artifactRefs: ["qualification-proof"], residualRisk: "Bound to the retained Buildchain product gate cut; independent certification remains external." }));
67
+ profile.activation = { decision: "activate", evidenceCut: `git://${REPOSITORY}@${sourceSha}`, independentReview: "review://protected-release-review", productWitnesses: ["qualification-proof"], residualRisk: "The product gate is non-qualifying and non-self-certifying." };
68
+ profile.domainProfile = { ...profile.domainProfile, product: "Buildchain", implementation: `git+https://github.com/${REPOSITORY}@${sourceSha}`, qualificationStatus: "qualified" };
69
+ return [["domain-profile", profile]];
70
+ }
71
+ async function generateProductGate(root, outDir, standard, sourceSha, checkedAt) {
72
+ const records = productRecords(root, standard, sourceSha).map(([role, value], index) => ({ role, ...writeJson(outDir, `records/${standard}-${index}.json`, value) }));
73
+ const kinds = standard === "kfd-4" ? [["projection-fsck", "projection-fsck"], ["negative", "negative-fixture"]] : standard === "kfd-5" ? [["negative", "negative-fixture"]] : [["qualification-proof", "qualification-proof"], ["independent-review", "independent-review"], ["negative", "negative-fixture"]];
74
+ const evidence = kinds.map(([id, kind]) => ({ id, kind, ...writeJson(outDir, `evidence/${standard}-${id}.json`, { schemaVersion: 1, contract: "kungfu-buildchain-kfd-adopter-evidence-reference", id, kind, source: { repository: REPOSITORY, sha: sourceSha }, observedAt: checkedAt, status: "retained", nonClaim: "This product-owned reference does not independently qualify or certify KFD adoption." }) }));
75
+ const input = { schemaVersion: 1, contract: KFD_PRODUCT_GATE_INPUT_CONTRACT, standard, standardRevision: kfdStandards.standards[standard].revision, source: { repository: REPOSITORY, sha: sourceSha }, evidenceCut: { generatedAt: checkedAt, expiresAt: new Date(Date.parse(checkedAt) + 86400000).toISOString() }, records: records.map(({ role, path, sha256 }) => ({ role, path, sha256 })), evidence: evidence.map(({ id, kind, path, sha256 }) => ({ id, kind, path, sha256 })), responsibility: { owner: REPOSITORY, evidenceOwner: "Buildchain maintainers", proofOwner: "Buildchain release gate" }, nonClaims: ["A passed product gate does not qualify, certify, activate, or independently approve KFD adoption."] };
76
+ const gate = await evaluateKfdProductGate({ cwd: outDir, input, expectedSourceSha: sourceSha, checkedAt });
77
+ if (gate.status !== "passed" || !validateKfdProductGateResult(gate, { expectedSourceSha: sourceSha, checkedAt }).valid) throw new Error(`${standard} product gate failed: ${JSON.stringify(gate.issues)}`);
78
+ writeJson(outDir, `${standard}/product-gate.json`, gate);
79
+ return gate;
80
+ }
81
+ export async function generateBuildchainKfdAdopterRelease({ cwd = process.cwd(), outputDir = ".buildchain/kfd/adopter-release", sourceSha = "", checkedAt = new Date().toISOString(), emitOutputs = true } = {}) {
82
+ const root = path.resolve(cwd), outDir = path.resolve(root, outputDir);
83
+ if (!/^[0-9a-f]{40}$/.test(sourceSha) || !Number.isFinite(Date.parse(checkedAt))) throw new Error("source SHA and checked-at must be exact");
84
+ for (const sourcePath of Object.values(evidenceSources)) if (!fs.statSync(path.join(root, sourcePath)).isFile()) throw new Error(`required Buildchain evidence source is missing: ${sourcePath}`);
85
+ const packageArtifactRoot = installedKfdPackageArtifactRoot(), gates = [];
86
+ for (const standard of ["kfd-4", "kfd-5", "kfd-7"]) gates.push(await generateProductGate(root, outDir, standard, sourceSha, checkedAt));
87
+ const sourceRoot = kfdProductGateDigest({ repository: REPOSITORY, sourceSha, files: [...new Set(Object.values(evidenceSources))].sort().map((sourcePath) => ({ path: sourcePath, sha256: fileDigest(path.join(root, sourcePath)) })) });
88
+ let manifest = initAdopterManifest({ manifestId: "buildchain-v3-full-cut", adopterId: REPOSITORY, artifactKind: "git-commit", artifactCoordinate: `${REPOSITORY}@${sourceSha}`, artifactRoot: sourceRoot, scope: "Buildchain v3 release and protected delivery authority", packageArtifactRoot, verifiedAt: checkedAt, maxAgeSeconds: 86400, packageRoot: kfdRoot });
89
+ const gateById = new Map(gates.map((gate) => [gate.standard.toUpperCase(), gate]));
90
+ for (const id of ["KFD-1", "KFD-2", "KFD-3", "KFD-4", "KFD-5", "KFD-7"]) {
91
+ const row = manifest.decisions.find((entry) => entry.id === id);
92
+ const sourcePath = evidenceSources[id];
93
+ const sourceEvidence = (kind, rootValue = fileDigest(path.join(root, sourcePath))) => ({ kind, coordinate: `git+https://github.com/${REPOSITORY}@${sourceSha}#${sourcePath}`, root: rootValue, observedAt: checkedAt, kfdPackageRoot: packageArtifactRoot });
94
+ row.state = "candidate"; row.usage = "used"; row.implementationEvidence = [sourceEvidence("implementation")]; row.verificationEvidence = [sourceEvidence("verification", gateById.get(id)?.gateRoot)]; row.gaps = ["Independent decision-specific assessment and certification remain external."];
103
95
  }
104
- return {
105
- schemaVersion: 1,
106
- contract: "kungfu-buildchain-self-kfd-witness-generation",
107
- outputs,
108
- };
96
+ const kfd6 = manifest.decisions.find((entry) => entry.id === "KFD-6");
97
+ kfd6.state = "unsupported"; kfd6.usage = "unused"; kfd6.gaps = ["Buildchain does not claim KFD-6 support in this cut."];
98
+ manifest = addAdopterWitness(manifest, { decisionId: "KFD-10", profileId: "kfd-warrant-evidence", witnessCoordinate: `git+https://github.com/${REPOSITORY}@${sourceSha}#${evidenceSources["KFD-10"]}`, witnessRoot: fileDigest(path.join(root, evidenceSources["KFD-10"])), packageArtifactRoot, verifiedAt: checkedAt, maxAgeSeconds: 86400, packageRoot: kfdRoot });
99
+ const manifestPath = writeJson(outDir, "kfd-adopter-manifest.json", manifest).filePath, manifestGate = createKfdAdopterManifestGate({ manifest, packageArtifactRoot, gateResults: gates, authorityPath: "kfd-adopter-manifest.json", expectedSourceSha: sourceSha, checkedAt });
100
+ if (!validateKfdAdopterManifestGate(manifestGate, { expectedSourceSha: sourceSha, checkedAt }).valid) throw new Error(`generated adopter manifest gate failed: ${JSON.stringify(manifestGate.issues)}`);
101
+ const gatePath = writeJson(outDir, "kfd-adopter-manifest-gate.json", manifestGate).filePath, support = createKfdLegacySupportMatrixProjection({ manifest, manifestGate });
102
+ if (!validateKfdLegacySupportMatrixProjection(support, { manifest, manifestGate }).valid) throw new Error("generated KFD support projection failed");
103
+ const supportPath = writeJson(outDir, "kfd-support.json", support).filePath;
104
+ const outputs = { "kfd-adopter-manifest-json": relative(root, manifestPath), "kfd-adopter-manifest-gate-json": relative(root, gatePath), "kfd-support-matrix-json": relative(root, supportPath), "kfd-product-gate-jsons": gates.map((gate) => relative(root, path.join(outDir, gate.standard, "product-gate.json"))).join(","), "kfd-adopter-manifest-root": manifestGate.authority.manifestRoot, "kfd-adopter-gate-root": manifestGate.gateRoot, "kfd-package-artifact-root": packageArtifactRoot, "source-sha": sourceSha };
105
+ if (emitOutputs) writeGitHubOutputs(outputs);
106
+ return { schemaVersion: 1, contract: "kungfu-buildchain-kfd-adopter-release-generation", status: "passed", qualifying: false, selfCertified: false, outputs };
109
107
  }
110
-
111
108
  if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
112
109
  try {
113
110
  const args = parseArgs();
114
- if (args.help) {
115
- process.stdout.write("Usage: generate-buildchain-kfd-witnesses [--cwd <repo>] [--output-dir <dir>] [--source-sha <sha>]\n");
116
- process.exit(0);
117
- }
118
- process.stdout.write(`${JSON.stringify(generateBuildchainKfdWitnesses(args), null, 2)}\n`);
111
+ if (args.help) process.stdout.write("Usage: generate-buildchain-kfd-witnesses [--cwd <repo>] [--output-dir <dir>] [--source-sha <sha>]\n");
112
+ else process.stdout.write(`${JSON.stringify(generateBuildchainKfdWitnesses(args), null, 2)}\n`);
119
113
  } catch (error) {
120
114
  console.error(`buildchain self KFD witnesses: ${error.message}`);
121
115
  process.exitCode = 1;
@@ -46,26 +46,30 @@ function routerInputs(inputBlock) {
46
46
 
47
47
  function routerOutputs(outputBlock) {
48
48
  const forwarded = outputBlock
49
- .replace(/^ value: \$\{\{ jobs\.[^.]+\.outputs\.([^ }]+) \}\}$/gm, " value: ${{ jobs.build.outputs.$1 }}")
49
+ .replace(
50
+ /^ value: \$\{\{ jobs\.[^.]+\.outputs\.([a-z0-9-]+) \|\| jobs\.[^.]+\.outputs\.\1 \}\}$/gm,
51
+ " value: ${{ jobs.alpha.outputs.$1 || jobs.stable.outputs.$1 }}",
52
+ )
53
+ .replace(/^ value: \$\{\{ jobs\.[^.]+\.outputs\.([^ }]+) \}\}$/gm, " value: ${{ jobs.alpha.outputs.$1 || jobs.stable.outputs.$1 }}")
50
54
  .replaceAll("build-lifecycle", "build-channel-router")
51
- .replace("value: ${{ jobs.build.outputs.controller-plan-artifact }}", "value: ${{ jobs.controller-plan.outputs.controller-plan-artifact }}")
52
- .replace("value: ${{ jobs.build.outputs.controller-plan-json }}", "value: ${{ jobs.controller-plan.outputs.controller-plan-json }}")
53
- .replace("value: ${{ jobs.build.outputs.controller-plan-digest }}", "value: ${{ jobs.controller-plan.outputs.controller-plan-digest }}")
54
- .replace("value: ${{ jobs.build.outputs.controller-receipt-artifact }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-artifact }}")
55
- .replace("value: ${{ jobs.build.outputs.controller-receipt-json }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-json }}")
56
- .replace("value: ${{ jobs.build.outputs.controller-receipt-digest }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-digest }}")
57
- .replace("value: ${{ jobs.build.outputs.controller-receipt-status }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-status }}")
55
+ .replace("value: ${{ jobs.alpha.outputs.controller-plan-artifact || jobs.stable.outputs.controller-plan-artifact }}", "value: ${{ jobs.controller-plan.outputs.controller-plan-artifact }}")
56
+ .replace("value: ${{ jobs.alpha.outputs.controller-plan-json || jobs.stable.outputs.controller-plan-json }}", "value: ${{ jobs.controller-plan.outputs.controller-plan-json }}")
57
+ .replace("value: ${{ jobs.alpha.outputs.controller-plan-digest || jobs.stable.outputs.controller-plan-digest }}", "value: ${{ jobs.controller-plan.outputs.controller-plan-digest }}")
58
+ .replace("value: ${{ jobs.alpha.outputs.controller-receipt-artifact || jobs.stable.outputs.controller-receipt-artifact }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-artifact }}")
59
+ .replace("value: ${{ jobs.alpha.outputs.controller-receipt-json || jobs.stable.outputs.controller-receipt-json }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-json }}")
60
+ .replace("value: ${{ jobs.alpha.outputs.controller-receipt-digest || jobs.stable.outputs.controller-receipt-digest }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-digest }}")
61
+ .replace("value: ${{ jobs.alpha.outputs.controller-receipt-status || jobs.stable.outputs.controller-receipt-status }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-status }}")
58
62
  .replace(
59
- /( credential-island-macos-artifact:\n(?: .*\n)*? value:) \$\{\{ jobs\.build\.outputs\.artifact-name \}\}/,
60
- "$1 ${{ jobs.build.outputs.credential-island-macos-artifact }}",
63
+ /( credential-island-macos-artifact:\n(?: .*\n)*? value:) \$\{\{ jobs\.alpha\.outputs\.artifact-name \|\| jobs\.stable\.outputs\.artifact-name \}\}/,
64
+ "$1 ${{ jobs.alpha.outputs.credential-island-macos-artifact || jobs.stable.outputs.credential-island-macos-artifact }}",
61
65
  )
62
66
  .replace(
63
- /( credential-island-macos-manifest-artifact:\n(?: .*\n)*? value:) \$\{\{ jobs\.build\.outputs\.manifest-artifact-name \}\}/,
64
- "$1 ${{ jobs.build.outputs.credential-island-macos-manifest-artifact }}",
67
+ /( credential-island-macos-manifest-artifact:\n(?: .*\n)*? value:) \$\{\{ jobs\.alpha\.outputs\.manifest-artifact-name \|\| jobs\.stable\.outputs\.manifest-artifact-name \}\}/,
68
+ "$1 ${{ jobs.alpha.outputs.credential-island-macos-manifest-artifact || jobs.stable.outputs.credential-island-macos-manifest-artifact }}",
65
69
  );
66
70
  return [
67
71
  " buildchain-channel:",
68
- ' description: "Resolved Buildchain channel: alpha, stable, or override"',
72
+ ' description: "Resolved Buildchain channel: alpha or stable"',
69
73
  " value: ${{ jobs.resolve-channel.outputs.channel }}",
70
74
  " buildchain-channel-selection-source:",
71
75
  ' description: "Evidence source used to select the Buildchain channel"',
@@ -154,7 +158,8 @@ function routerControllerReceiptJob() {
154
158
  needs:
155
159
  - resolve-channel
156
160
  - controller-plan
157
- - build
161
+ - alpha
162
+ - stable
158
163
  if: \${{ always() && needs.controller-plan.result == 'success' }}
159
164
  runs-on: \${{ fromJSON(inputs.control-runner-json) }}
160
165
  outputs:
@@ -183,9 +188,9 @@ function routerControllerReceiptJob() {
183
188
  env:
184
189
  BUILDCHAIN_CONTROLLER_PLAN_PATH: .buildchain/controller/plan.json
185
190
  BUILDCHAIN_CONTROLLER_STAGES_JSON: >-
186
- [{"id":"resolve-channel","status":"\${{ needs.resolve-channel.result }}"},{"id":"build","status":"\${{ needs.build.result }}"},{"id":"aggregate","status":"\${{ needs.build.result }}"}]
187
- BUILDCHAIN_CONTROLLER_EVIDENCE_JSON: \${{ needs.build.outputs.controller-receipt-digest != '' && format('[{{"kind":"nested-controller-receipt","digest":"{0}"}}]', needs.build.outputs.controller-receipt-digest) || '[]' }}
188
- BUILDCHAIN_CONTROLLER_REASON_CODE: \${{ needs.build.result == 'success' && '' || 'nested-build-incomplete' }}
191
+ [{"id":"resolve-channel","status":"\${{ needs.resolve-channel.result }}"},{"id":"alpha","status":"\${{ needs.alpha.result }}"},{"id":"stable","status":"\${{ needs.stable.result }}"},{"id":"aggregate","status":"\${{ needs.resolve-channel.outputs.channel == 'alpha' && needs.alpha.result || needs.stable.result }}"}]
192
+ BUILDCHAIN_CONTROLLER_EVIDENCE_JSON: \${{ (needs.alpha.outputs.controller-receipt-digest != '' || needs.stable.outputs.controller-receipt-digest != '') && format('[{{"kind":"nested-controller-receipt","digest":"{0}"}}]', needs.alpha.outputs.controller-receipt-digest || needs.stable.outputs.controller-receipt-digest) || '[]' }}
193
+ BUILDCHAIN_CONTROLLER_REASON_CODE: \${{ ((needs.resolve-channel.outputs.channel == 'alpha' && needs.alpha.result == 'success') || (needs.resolve-channel.outputs.channel == 'stable' && needs.stable.result == 'success')) && '' || 'nested-build-incomplete' }}
189
194
  BUILDCHAIN_CONTROLLER_REASON_SUMMARY: Nested build controller did not complete successfully
190
195
  BUILDCHAIN_CONTROLLER_RECEIPT_ARTIFACT: buildchain-channel-controller-receipt-\${{ github.sha }}
191
196
  BUILDCHAIN_CONTROLLER_RECEIPT_PATH: .buildchain/controller/receipt.json
@@ -215,7 +220,9 @@ function routerAggregateJob() {
215
220
  return ` summarize:
216
221
  name: Summarize build contract
217
222
  needs:
218
- - build
223
+ - resolve-channel
224
+ - alpha
225
+ - stable
219
226
  - controller-receipt
220
227
  if: \${{ always() }}
221
228
  runs-on: \${{ fromJSON(inputs.control-runner-json) }}
@@ -223,12 +230,16 @@ function routerAggregateJob() {
223
230
  - name: Enforce public channel router aggregate
224
231
  shell: bash
225
232
  env:
226
- BUILD_RESULT: \${{ needs.build.result }}
233
+ CHANNEL: \${{ needs.resolve-channel.outputs.channel }}
234
+ ALPHA_RESULT: \${{ needs.alpha.result }}
235
+ STABLE_RESULT: \${{ needs.stable.result }}
227
236
  CONTROLLER_RECEIPT_RESULT: \${{ needs.controller-receipt.result }}
228
237
  run: |
229
238
  set -euo pipefail
230
- if [[ "\${BUILD_RESULT}" != "success" || "\${CONTROLLER_RECEIPT_RESULT}" != "success" ]]; then
231
- echo "::error::Buildchain channel router did not qualify: build=\${BUILD_RESULT} controller-receipt=\${CONTROLLER_RECEIPT_RESULT}"
239
+ selected_result="\${STABLE_RESULT}"
240
+ if [[ "\${CHANNEL}" = "alpha" ]]; then selected_result="\${ALPHA_RESULT}"; fi
241
+ if [[ "\${selected_result}" != "success" || "\${CONTROLLER_RECEIPT_RESULT}" != "success" ]]; then
242
+ echo "::error::Buildchain channel router did not qualify: channel=\${CHANNEL} selected=\${selected_result} controller-receipt=\${CONTROLLER_RECEIPT_RESULT}"
232
243
  exit 1
233
244
  fi
234
245
  echo "Buildchain channel router aggregate passed."`;
@@ -245,6 +256,47 @@ function generateChannelBuildWorkflowBase(source) {
245
256
  return `# Generated by scripts/generate-channel-build-workflow.mjs. Do not edit directly.\nname: Buildchain Channel Build\n\non:\n workflow_call:\n inputs:\n${routerInputs(inputs)}\n secrets:\n${secrets.trimEnd()}\n outputs:\n${routerOutputs(outputs)}\n\npermissions:\n contents: read\n issues: write\n id-token: write\n\njobs:\n resolve-channel:\n name: Resolve Buildchain channel\n runs-on: ubuntu-24.04\n outputs:\n channel: \${{ steps.channel.outputs.channel }}\n buildchain-ref: \${{ steps.channel.outputs.buildchain-ref }}\n contract-lock-path: \${{ steps.lock.outputs.path }}\n selection-source: \${{ steps.channel.outputs.selection-source }}\n reason: \${{ steps.channel.outputs.reason }}\n steps:\n - name: Resolve router source\n id: router\n shell: bash\n env:\n BUILDCHAIN_ROUTER_WORKFLOW_REF: \${{ job.workflow_ref }}\n run: |\n set -euo pipefail\n workflow_ref=\"\${BUILDCHAIN_ROUTER_WORKFLOW_REF}\"\n repository=\"\${workflow_ref%%/.github/workflows/*}\"\n ref=\"\${workflow_ref##*@}\"\n ref=\"\${ref#refs/heads/}\"\n ref=\"\${ref#refs/tags/}\"\n if [[ -z \"\${repository}\" || \"\${repository}\" = \"\${workflow_ref}\" || -z \"\${ref}\" ]]; then\n echo \"Unable to resolve Buildchain router source from job.workflow_ref=\${workflow_ref}\" >&2\n exit 1\n fi\n {\n echo \"repository=\${repository}\"\n echo \"ref=\${ref}\"\n } >> \"\${GITHUB_OUTPUT}\"\n\n - name: Checkout Buildchain router\n uses: actions/checkout@v7.0.0\n with:\n repository: \${{ steps.router.outputs.repository }}\n ref: \${{ steps.router.outputs.ref }}\n path: .buildchain/router\n persist-credentials: false\n\n - name: Resolve channel\n id: channel\n shell: bash\n env:\n BUILDCHAIN_CHANNEL: \${{ inputs.buildchain-channel }}\n BUILDCHAIN_REQUESTED_REF: \${{ inputs.buildchain-ref }}\n BUILDCHAIN_PUBLISH_CHANNEL: \${{ inputs.publish-channel }}\n BUILDCHAIN_EVENT_NAME: \${{ github.event_name }}\n BUILDCHAIN_GIT_REF: \${{ github.ref }}\n BUILDCHAIN_RELEASE_PRERELEASE: \${{ github.event.release.prerelease }}\n BUILDCHAIN_ROUTER_REF: \${{ steps.router.outputs.ref }}\n run: |\n node .buildchain/router/scripts/buildchain-channel-router.mjs \\\n --cwd .buildchain/router \\\n --channel \"\${BUILDCHAIN_CHANNEL}\" \\\n --buildchain-ref \"\${BUILDCHAIN_REQUESTED_REF}\" \\\n --publish-channel \"\${BUILDCHAIN_PUBLISH_CHANNEL}\" \\\n --event-name \"\${BUILDCHAIN_EVENT_NAME}\" \\\n --ref \"\${BUILDCHAIN_GIT_REF}\" \\\n --release-prerelease \"\${BUILDCHAIN_RELEASE_PRERELEASE}\" \\\n --router-ref \"\${BUILDCHAIN_ROUTER_REF}\"\n\n - name: Select consumer contract lock\n id: lock\n shell: bash\n env:\n EXPLICIT_PATH: \${{ inputs.buildchain-contract-lock-path }}\n ALPHA_PATH: \${{ inputs.buildchain-alpha-contract-lock-path }}\n STABLE_PATH: \${{ inputs.buildchain-stable-contract-lock-path }}\n CHANNEL: \${{ steps.channel.outputs.channel }}\n run: |\n set -euo pipefail\n path=\"\${EXPLICIT_PATH}\"\n if [[ -z \"\${path}\" ]]; then\n if [[ \"\${CHANNEL}\" = \"alpha\" ]]; then\n path=\"\${ALPHA_PATH}\"\n else\n path=\"\${STABLE_PATH}\"\n fi\n fi\n echo \"path=\${path}\" >> \"\${GITHUB_OUTPUT}\"\n\n build:\n name: Build with resolved channel\n needs: resolve-channel\n uses: ./.github/workflows/.build.yml\n permissions:\n contents: read\n issues: write\n id-token: write\n with:\n${forwardedInputs(names)}\n secrets: inherit\n`;
246
257
  }
247
258
 
259
+ function splitBuildJobsByChannel(workflow, names, major) {
260
+ const marker = "\n build:\n";
261
+ const index = workflow.indexOf(marker);
262
+ if (index < 0) throw new Error("generated workflow is missing the build job");
263
+ const inputs = forwardedInputs(names);
264
+ const jobs = `
265
+ alpha:
266
+ name: Build alpha channel
267
+ needs:
268
+ - resolve-channel
269
+ - controller-plan
270
+ if: \${{ needs.resolve-channel.outputs.channel == 'alpha' }}
271
+ uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v${major}-alpha
272
+ permissions:
273
+ actions: read
274
+ contents: read
275
+ issues: write
276
+ id-token: write
277
+ with:
278
+ ${inputs}
279
+ secrets: inherit
280
+
281
+ stable:
282
+ name: Build stable channel
283
+ needs:
284
+ - resolve-channel
285
+ - controller-plan
286
+ if: \${{ needs.resolve-channel.outputs.channel == 'stable' }}
287
+ uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v${major}
288
+ permissions:
289
+ actions: read
290
+ contents: read
291
+ issues: write
292
+ id-token: write
293
+ with:
294
+ ${inputs}
295
+ secrets: inherit
296
+ `;
297
+ return `${workflow.slice(0, index)}${jobs}`;
298
+ }
299
+
248
300
  function bindRouterCheckoutToWorkflowSha(workflow) {
249
301
  return workflow
250
302
  .replace(
@@ -294,7 +346,14 @@ function bindRouterCheckoutToWorkflowSha(workflow) {
294
346
  }
295
347
 
296
348
  export function generateChannelBuildWorkflow(source) {
297
- const generated = bindRouterCheckoutToWorkflowSha(generateChannelBuildWorkflowBase(source))
349
+ const inputBlock = blockBetween(source, " inputs:\n", " secrets:\n");
350
+ const names = inputNames(inputBlock);
351
+ const packageVersion = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")).version || "";
352
+ const major = Number(String(packageVersion).split(".")[0]);
353
+ if (!Number.isInteger(major) || major < 1) throw new Error("package.json version must declare a positive major");
354
+ const generated = bindRouterCheckoutToWorkflowSha(
355
+ splitBuildJobsByChannel(generateChannelBuildWorkflowBase(source), names, major),
356
+ )
298
357
  .replaceAll("runs-on: ubuntu-24.04", "runs-on: ${{ fromJSON(inputs.control-runner-json) }}")
299
358
  .replace(
300
359
  "\npermissions:\n contents: read\n",
@@ -310,15 +369,7 @@ export function generateChannelBuildWorkflow(source) {
310
369
  "",
311
370
  ].join("\n"),
312
371
  )
313
- .replace("\n build:\n", `\n${routerControllerPlanJob()}\n\n build:\n`)
314
- .replace(
315
- " needs: resolve-channel\n uses: ./.github/workflows/.build.yml",
316
- " needs:\n - resolve-channel\n - controller-plan\n uses: ./.github/workflows/.build.yml",
317
- )
318
- .replace(
319
- " uses: ./.github/workflows/.build.yml\n permissions:\n contents: read\n",
320
- " uses: ./.github/workflows/.build.yml\n permissions:\n actions: read\n contents: read\n",
321
- );
372
+ .replace("\n alpha:\n", `\n${routerControllerPlanJob()}\n\n alpha:\n`);
322
373
  return `${generated.trimEnd()}\n\n${routerControllerReceiptJob()}\n\n${routerAggregateJob()}\n`;
323
374
  }
324
375
 
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+
7
+ import { nextDevelopmentManual } from "../packages/core/next-development-projection.js";
8
+
9
+ export const NEXT_DEVELOPMENT_MANUAL_PATH =
10
+ "docs/next-development-transition.md";
11
+
12
+ export function generateNextDevelopmentGuidance({
13
+ cwd = process.cwd(),
14
+ check = false,
15
+ } = {}) {
16
+ const target = path.resolve(cwd, NEXT_DEVELOPMENT_MANUAL_PATH);
17
+ const expected = nextDevelopmentManual();
18
+ const current = fs.existsSync(target) ? fs.readFileSync(target, "utf8") : "";
19
+ if (check) {
20
+ if (current !== expected) {
21
+ throw new Error(
22
+ `${NEXT_DEVELOPMENT_MANUAL_PATH} drifted from its contract source`,
23
+ );
24
+ }
25
+ return { ok: true, changed: false, path: NEXT_DEVELOPMENT_MANUAL_PATH };
26
+ }
27
+ fs.mkdirSync(path.dirname(target), { recursive: true });
28
+ if (current !== expected) fs.writeFileSync(target, expected);
29
+ return {
30
+ ok: true,
31
+ changed: current !== expected,
32
+ path: NEXT_DEVELOPMENT_MANUAL_PATH,
33
+ };
34
+ }
35
+
36
+ if (
37
+ process.argv[1] &&
38
+ import.meta.url === pathToFileURL(process.argv[1]).href
39
+ ) {
40
+ try {
41
+ const result = generateNextDevelopmentGuidance({
42
+ check: process.argv.includes("--check"),
43
+ });
44
+ process.stdout.write(`${JSON.stringify(result)}\n`);
45
+ } catch (error) {
46
+ process.stderr.write(`next-development guidance: ${error.message}\n`);
47
+ process.exitCode = 1;
48
+ }
49
+ }
@@ -368,6 +368,7 @@ const manualMetaById = new Map(Object.entries({
368
368
  "release-activation-transaction": { capabilityGroup: "release-passport-trust", audience: ["release-operator", "agent"], maturity: "preview", order: 125 },
369
369
  "release-candidate": { capabilityGroup: "reusable-build", audience: ["release-operator", "consumer"], maturity: "stable", order: 130 },
370
370
  "release-train": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 132 },
371
+ "next-development-transition": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 133 },
371
372
  "stable-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer"], maturity: "preview", order: 135 },
372
373
  "dev-qualification-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 136 },
373
374
  "dev-alpha-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 137 },
@@ -733,6 +734,7 @@ function buildSiteBundle() {
733
734
  "docs/reusable-build-surface.md",
734
735
  "docs/release-candidate.md",
735
736
  "docs/release-train.md",
737
+ "docs/next-development-transition.md",
736
738
  "docs/stable-candidate-patrol.md",
737
739
  "docs/dev-qualification-patrol.md",
738
740
  "docs/dev-alpha-candidate-patrol.md",
@@ -929,7 +931,7 @@ function buildSiteBundle() {
929
931
  "schemas/release-tail-capabilities-v1.schema.json",
930
932
  "schemas/release-tail-provider-bindings-v1.schema.json",
931
933
  "schemas/kfd-agent-hub-adoption.schema.json",
932
- "schemas/kfd-product-gate-input-v1.schema.json",
934
+ "schemas/kfd-product-gate-input-v1.schema.json", "schemas/dev-delivery-authority-v2.schema.json",
933
935
  "kfd-support.json",
934
936
  "artifact-evidence.json",
935
937
  "product-mechanism.json",
@@ -966,7 +968,7 @@ function buildSiteBundle() {
966
968
  "schemas/release-tail-capabilities-v1.schema.json",
967
969
  "schemas/release-tail-provider-bindings-v1.schema.json",
968
970
  "schemas/kfd-agent-hub-adoption.schema.json",
969
- "schemas/kfd-product-gate-input-v1.schema.json",
971
+ "schemas/kfd-product-gate-input-v1.schema.json", "schemas/dev-delivery-authority-v2.schema.json",
970
972
  "buildchain-contract.json",
971
973
  "kfd-claims.json",
972
974
  "product-mechanism.json",
@@ -1099,7 +1101,7 @@ function buildSiteBundle() {
1099
1101
  "schemas/release-tail-capabilities-v1.schema.json",
1100
1102
  "schemas/release-tail-provider-bindings-v1.schema.json",
1101
1103
  "schemas/kfd-agent-hub-adoption.schema.json",
1102
- "schemas/kfd-product-gate-input-v1.schema.json",
1104
+ "schemas/kfd-product-gate-input-v1.schema.json", "schemas/dev-delivery-authority-v2.schema.json",
1103
1105
  "buildchain-contract.json",
1104
1106
  "kfd-upstream-aggregate.json",
1105
1107
  "kfd-claims.json",
@@ -1283,6 +1285,9 @@ function buildSiteBundle() {
1283
1285
  ),
1284
1286
  "schemas/kfd-agent-hub-adoption.schema.json": KFD_AGENT_HUB_ADOPTION_SCHEMA,
1285
1287
  "schemas/kfd-product-gate-input-v1.schema.json": KFD_PRODUCT_GATE_INPUT_SCHEMA,
1288
+ "schemas/dev-delivery-authority-v2.schema.json": readJson(
1289
+ "contracts/dev-delivery-authority-v2.schema.json",
1290
+ ),
1286
1291
  "badge-endpoint-registry.json": badgeEndpointRegistry,
1287
1292
  "publication-registry.json": publicationRegistry,
1288
1293
  "buildchain-contract.json": createBuildchainContractWorld({ root, controllerRegistry }),