@kungfu-tech/buildchain 4.0.1 → 4.0.2-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 (65) hide show
  1. package/architecture/agent-change-map.md +15 -6
  2. package/architecture/ci-lane-change-budget.json +76 -8
  3. package/architecture/internal-capabilities.json +9 -2
  4. package/architecture/maintainability-debt.json +66 -136
  5. package/architecture/maintainability-policy.json +17 -12
  6. package/architecture/release-tail-contract-inventory.json +4 -4
  7. package/architecture/v3-core-mechanism-inventory.json +3 -0
  8. package/architecture/v3-v4-live-capability-inventory.json +17 -17
  9. package/architecture/v4-delivery-warrant-shadow-fixtures.json +5 -5
  10. package/architecture/v4-release-invocation-fixtures.json +139 -0
  11. package/architecture/v4-release-topology.json +316 -0
  12. package/architecture/v4-runtime-semantic-closure.json +4 -1
  13. package/contracts/buildchain-v2-residuals-v1.json +0 -9
  14. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  15. package/contracts/v4-release-invocation-v1.schema.json +134 -0
  16. package/dist/site/buildchain-contract.json +9 -9
  17. package/dist/site/buildchain-site.json +9 -9
  18. package/dist/site/kfd-claims.json +28 -7
  19. package/dist/site/kfd-upstream-aggregate.json +1 -1
  20. package/dist/site/manual-registry.json +1 -1
  21. package/dist/site/node-api-registry.json +92 -17
  22. package/dist/site/page-registry.json +4 -4
  23. package/dist/site/public-surface-audit.json +42 -10
  24. package/dist/site/publication-authority-registry.json +2 -3
  25. package/dist/site/publication-registry.json +4 -4
  26. package/dist/site/site-manifest.json +5 -5
  27. package/dist/site/workflow-registry.json +38 -11
  28. package/docs/dev-delivery-warrant.md +21 -0
  29. package/docs/node-api-reference.md +19 -16
  30. package/package.json +2 -2
  31. package/packages/core/dev-delivery-candidate-identity.js +45 -17
  32. package/packages/core/dev-delivery-execution-transfer.js +6 -7
  33. package/packages/core/dev-delivery-provider-heartbeat.js +22 -6
  34. package/packages/core/dev-delivery-warrant-legacy-recovery.js +248 -0
  35. package/packages/core/dev-delivery-warrant-native-compatibility.js +33 -0
  36. package/packages/core/dev-delivery-warrant-state.js +54 -54
  37. package/packages/core/dev-delivery-warrant.js +8 -0
  38. package/packages/core/dev-delivery-writer-protocol-transition.js +72 -0
  39. package/packages/core/release-tail-product-capabilities.js +29 -0
  40. package/packages/core/release-tail-provider-plane.js +2 -2
  41. package/packages/core/v4-canonical-contracts.js +14 -0
  42. package/packages/core/v4-floating-consumer-policy.js +4 -1
  43. package/packages/core/v4-product-publication.js +387 -0
  44. package/packages/core/v4-protected-publication-source.js +93 -0
  45. package/packages/core/v4-publication-qualification.js +1 -0
  46. package/packages/core/v4-release-invocation.js +425 -0
  47. package/scripts/audit-publication-control-plane.mjs +0 -1
  48. package/scripts/check-inventory.mjs +27 -59
  49. package/scripts/check-maintainability.mjs +13 -5
  50. package/scripts/check-v3-v4-capability-inventory.mjs +3 -61
  51. package/scripts/check-v4-floating-consumer-policy-contract.mjs +10 -20
  52. package/scripts/check-v4-release-topology.mjs +490 -0
  53. package/scripts/dev-delivery-warrant-options.mjs +15 -4
  54. package/scripts/dev-delivery-warrant.mjs +41 -7
  55. package/scripts/generate-channel-promotion-workflow.mjs +14 -55
  56. package/scripts/release-candidate-resolver.mjs +23 -24
  57. package/scripts/resume-from-candidate-run.mjs +16 -17
  58. package/scripts/v3-v4-capability-catalog.mjs +107 -0
  59. package/scripts/v4-declarative-promotion-admission.mjs +4 -1
  60. package/scripts/v4-product-publication-intent.mjs +114 -0
  61. package/scripts/v4-release-candidate-adapter.mjs +41 -0
  62. package/scripts/capture-package-release-propagation.mjs +0 -263
  63. package/scripts/publication-commit-evidence.mjs +0 -444
  64. package/scripts/publish-github-artifact-attestation-evidence.mjs +0 -201
  65. package/scripts/stage-github-artifact-attestation-inputs.mjs +0 -65
@@ -0,0 +1,114 @@
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 { spawnSyncCommand } from "../packages/core/spawn-command.js";
8
+ import { selectV4ProductPublicationIntent } from "../packages/core/v4-product-publication.js";
9
+ import { v4ContentRoot } from "../packages/core/v4-canonical-contracts.js";
10
+
11
+ function env(name, required = false) {
12
+ const value = String(process.env[name] || "").trim();
13
+ if (required && !value) throw new Error(`${name} is required`);
14
+ return value;
15
+ }
16
+
17
+ function readJson(file) {
18
+ return JSON.parse(fs.readFileSync(path.resolve(file), "utf8"));
19
+ }
20
+
21
+ function packageName() {
22
+ const declared = env("BUILDCHAIN_PUBLISH_PACKAGE_MAIN");
23
+ if (declared) return declared;
24
+ const manifestPath = env("BUILDCHAIN_SEALED_BUNDLE_MANIFEST", true);
25
+ const manifest = readJson(manifestPath);
26
+ const selected = String(manifest?.npm?.name || "").trim();
27
+ if (!selected)
28
+ throw new Error("sealed bundle manifest does not declare npm.name");
29
+ return selected;
30
+ }
31
+
32
+ function observedVersions(name) {
33
+ const result = spawnSyncCommand(
34
+ "npm",
35
+ [
36
+ "view",
37
+ name,
38
+ "versions",
39
+ "--json",
40
+ "--registry=https://registry.npmjs.org/",
41
+ ],
42
+ { encoding: "utf8" },
43
+ );
44
+ if (result.error) throw result.error;
45
+ if (result.status !== 0) {
46
+ const output = `${result.stdout || ""}\n${result.stderr || ""}`;
47
+ if (/\bE404\b|404 Not Found|is not in this registry/iu.test(output))
48
+ return [];
49
+ throw new Error(`npm version discovery failed: ${output.trim()}`);
50
+ }
51
+ const parsed = JSON.parse(String(result.stdout || "[]"));
52
+ return Array.isArray(parsed) ? parsed : [parsed].filter(Boolean);
53
+ }
54
+
55
+ function writeOutput(name, value) {
56
+ const output = env("GITHUB_OUTPUT");
57
+ if (!output) return;
58
+ fs.appendFileSync(output, `${name}=${String(value)}\n`);
59
+ }
60
+
61
+ export function resolveV4ProductPublicationIntent() {
62
+ const name = packageName();
63
+ const sourceSha = env("BUILDCHAIN_SOURCE_SHA", true);
64
+ const manifest = readJson(env("BUILDCHAIN_SEALED_BUNDLE_MANIFEST", true));
65
+ const requiredArtifacts = readJson(
66
+ env("BUILDCHAIN_REQUIRED_ARTIFACTS_PATH", true),
67
+ );
68
+ const channel = env("BUILDCHAIN_CHANNEL", true);
69
+ const intent = selectV4ProductPublicationIntent({
70
+ channel,
71
+ targetRef: env("BUILDCHAIN_TARGET_REF", true),
72
+ sourceSha,
73
+ sourceTimestamp: env("BUILDCHAIN_SOURCE_TIMESTAMP", true),
74
+ repository: env("BUILDCHAIN_REPOSITORY", true),
75
+ packageName: name,
76
+ distTag:
77
+ env("BUILDCHAIN_PUBLISH_DIST_TAG") ||
78
+ (channel === "alpha" ? "alpha" : "latest"),
79
+ sealedBundleRoot: manifest.root,
80
+ requiredArtifactsRoot: v4ContentRoot(
81
+ "v4-product-required-artifacts",
82
+ requiredArtifacts,
83
+ ),
84
+ candidateVersion: env("BUILDCHAIN_CANDIDATE_VERSION", true),
85
+ recoveredVersion: env("BUILDCHAIN_RECOVERED_PUBLICATION_VERSION"),
86
+ observedVersions: observedVersions(name),
87
+ });
88
+ const outputPath = path.resolve(
89
+ env("BUILDCHAIN_PRODUCT_PUBLICATION_INTENT_PATH") ||
90
+ ".buildchain/release-candidate/v4-product-publication-intent.json",
91
+ );
92
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
93
+ fs.writeFileSync(outputPath, `${JSON.stringify(intent, null, 2)}\n`);
94
+ writeOutput("version", intent.version);
95
+ writeOutput("exact-tag", intent.exactTag);
96
+ writeOutput("intent-path", outputPath);
97
+ writeOutput("intent-root", intent.intentRoot);
98
+ return { name, intent, outputPath };
99
+ }
100
+
101
+ if (
102
+ process.argv[1] &&
103
+ import.meta.url === pathToFileURL(process.argv[1]).href
104
+ ) {
105
+ try {
106
+ const result = resolveV4ProductPublicationIntent();
107
+ process.stdout.write(
108
+ `v4 product publication intent: ${result.name}@${result.intent.version} (${result.intent.mode})\n`,
109
+ );
110
+ } catch (error) {
111
+ console.error(`v4-product-publication-intent: ${error.message}`);
112
+ process.exitCode = 1;
113
+ }
114
+ }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execFileSync } from "node:child_process";
4
+ import path from "node:path";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
+
7
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
+
9
+ export function resolveV4ReleaseCandidateAdapter({
10
+ resumeCandidateRunId = "",
11
+ } = {}) {
12
+ const mode = String(resumeCandidateRunId || "").trim() ? "recovery" : "fresh";
13
+ return Object.freeze({
14
+ mode,
15
+ script:
16
+ mode === "recovery"
17
+ ? "scripts/resume-from-candidate-run.mjs"
18
+ : "scripts/release-candidate-resolver.mjs",
19
+ });
20
+ }
21
+
22
+ export function runV4ReleaseCandidateAdapter({
23
+ env = process.env,
24
+ exec = execFileSync,
25
+ } = {}) {
26
+ const route = resolveV4ReleaseCandidateAdapter({
27
+ resumeCandidateRunId: env.BUILDCHAIN_RESUME_CANDIDATE_RUN_ID,
28
+ });
29
+ exec(process.execPath, [path.join(root, route.script)], {
30
+ env,
31
+ stdio: "inherit",
32
+ });
33
+ return route;
34
+ }
35
+
36
+ if (
37
+ process.argv[1] &&
38
+ import.meta.url === pathToFileURL(process.argv[1]).href
39
+ ) {
40
+ runV4ReleaseCandidateAdapter();
41
+ }
@@ -1,263 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import crypto from "node:crypto";
4
- import fs from "node:fs";
5
- import os from "node:os";
6
- import path from "node:path";
7
- import { execFileSync } from "node:child_process";
8
- import { fileURLToPath } from "node:url";
9
- import {
10
- createPackageReleasePropagationCapture,
11
- normalizePackageReleasePropagationConfig,
12
- } from "../packages/core/release-propagation-capture.js";
13
- import { stableJson } from "../packages/core/release-propagation-common.js";
14
-
15
- function parseArgs(argv) {
16
- const args = {};
17
- for (let index = 0; index < argv.length; index += 1) {
18
- const token = argv[index];
19
- if (!token.startsWith("--")) throw new Error(`unexpected argument: ${token}`);
20
- const key = token.slice(2);
21
- const value = argv[index + 1];
22
- if (!value || value.startsWith("--")) throw new Error(`${token} requires a value`);
23
- args[key] = value;
24
- index += 1;
25
- }
26
- return args;
27
- }
28
-
29
- function required(args, key) {
30
- const value = String(args[key] || "").trim();
31
- if (!value) throw new Error(`--${key} is required`);
32
- return value;
33
- }
34
-
35
- function sha256(bytes) {
36
- return crypto.createHash("sha256").update(bytes).digest("hex");
37
- }
38
-
39
- function commandJson(command, args, options = {}) {
40
- const text = execFileSync(command, args, { encoding: "utf8", ...options });
41
- return JSON.parse(text);
42
- }
43
-
44
- function assertSourcePath(value) {
45
- const sourcePath = String(value || "").trim();
46
- if (!sourcePath || path.isAbsolute(sourcePath) || sourcePath.split("/").includes("..") || /[\r\n]/.test(sourcePath)) {
47
- throw new Error("release propagation config path must be a safe repository-relative path");
48
- }
49
- return sourcePath;
50
- }
51
-
52
- function hasCommit(sourceSha, cwd) {
53
- try {
54
- execFileSync("git", ["cat-file", "-e", `${sourceSha}^{commit}`], {
55
- cwd,
56
- stdio: "ignore",
57
- });
58
- return true;
59
- } catch {
60
- return false;
61
- }
62
- }
63
-
64
- export function readConfigAtSource(sourceSha, configPath, cwd) {
65
- if (!hasCommit(sourceSha, cwd)) {
66
- try {
67
- execFileSync("git", ["fetch", "--no-tags", "--depth=1", "origin", sourceSha], {
68
- cwd,
69
- stdio: ["ignore", "pipe", "pipe"],
70
- });
71
- } catch (error) {
72
- const detail = String(error.stderr || error.message || "unknown git fetch failure").trim();
73
- throw new Error(`exact release source ${sourceSha} is unavailable from origin: ${detail}`);
74
- }
75
- }
76
- const bytes = execFileSync("git", ["show", `${sourceSha}:${configPath}`], {
77
- cwd,
78
- encoding: "utf8",
79
- });
80
- const parsed = JSON.parse(bytes);
81
- return { bytes, parsed, normalized: normalizePackageReleasePropagationConfig(parsed) };
82
- }
83
-
84
- function resolveTagTarget(repository, tag) {
85
- let object = commandJson("gh", ["api", `repos/${repository}/git/ref/tags/${tag}`]).object;
86
- for (let depth = 0; depth < 8 && object?.type === "tag"; depth += 1) {
87
- object = commandJson("gh", ["api", `repos/${repository}/git/tags/${object.sha}`]).object;
88
- }
89
- if (object?.type !== "commit" || !/^[0-9a-f]{40}$/i.test(object.sha || "")) {
90
- throw new Error(`release tag ${tag} does not resolve to one exact commit`);
91
- }
92
- return object.sha.toLowerCase();
93
- }
94
-
95
- function resolvePackageFact(packageName, version) {
96
- const fact = commandJson("npm", [
97
- "view",
98
- `${packageName}@${version}`,
99
- "version",
100
- "dist.integrity",
101
- "gitHead",
102
- "--json",
103
- "--registry=https://registry.npmjs.org/",
104
- ]);
105
- return {
106
- name: packageName,
107
- version: String(fact.version || ""),
108
- integrity: String(fact.dist?.integrity || fact["dist.integrity"] || ""),
109
- gitHead: String(fact.gitHead || "").toLowerCase(),
110
- };
111
- }
112
-
113
- function verifyPublicReleasePassport({ repository, tag, localPath }) {
114
- const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "buildchain-release-passport-"));
115
- try {
116
- execFileSync("gh", [
117
- "release", "download", tag,
118
- "--repo", repository,
119
- "--pattern", "buildchain.release.json",
120
- "--dir", temporary,
121
- ], { stdio: ["ignore", "pipe", "pipe"] });
122
- const localBytes = fs.readFileSync(localPath);
123
- const remotePath = path.join(temporary, "buildchain.release.json");
124
- const remoteBytes = fs.readFileSync(remotePath);
125
- const localDigest = sha256(localBytes);
126
- const remoteDigest = sha256(remoteBytes);
127
- if (localDigest !== remoteDigest) {
128
- throw new Error("public release passport bytes disagree with finalized local passport");
129
- }
130
- return {
131
- url: `https://github.com/${repository}/releases/download/${tag}/buildchain.release.json`,
132
- sha256: remoteDigest,
133
- };
134
- } finally {
135
- fs.rmSync(temporary, { recursive: true, force: true });
136
- }
137
- }
138
-
139
- function resolveBaseShas(config) {
140
- return Object.fromEntries(config.targets.map((targetId) => {
141
- const node = config.graph.nodes.find((entry) => entry.id === targetId);
142
- if (!node?.baseRef) throw new Error(`configured propagation target ${targetId} has no baseRef`);
143
- const response = commandJson("gh", ["api", `repos/${node.repository}/commits/${node.baseRef}`]);
144
- const sha = String(response.sha || "").toLowerCase();
145
- if (!/^[0-9a-f]{40}$/.test(sha)) {
146
- throw new Error(`configured propagation target ${targetId} baseRef did not resolve exactly`);
147
- }
148
- return [targetId, sha];
149
- }));
150
- }
151
-
152
- function writeOutput(name, value) {
153
- if (!process.env.GITHUB_OUTPUT) return;
154
- fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`);
155
- }
156
-
157
- export function capturePackageReleasePropagation({
158
- config,
159
- upstreamRelease,
160
- expectedBaseShas,
161
- outputDir,
162
- configBytes = "",
163
- } = {}) {
164
- const captured = createPackageReleasePropagationCapture({
165
- config,
166
- upstreamRelease,
167
- expectedBaseShas,
168
- });
169
- fs.mkdirSync(outputDir, { recursive: true });
170
- fs.writeFileSync(
171
- path.join(outputDir, "config.json"),
172
- configBytes || stableJson(captured.config),
173
- );
174
- fs.writeFileSync(path.join(outputDir, "upstream-release.json"), stableJson(captured.upstreamRelease));
175
- fs.writeFileSync(path.join(outputDir, "plan.json"), stableJson(captured.plan));
176
- for (const item of captured.works) {
177
- const targetDir = path.join(outputDir, "work", item.propagationKey);
178
- fs.mkdirSync(targetDir, { recursive: true });
179
- fs.writeFileSync(path.join(targetDir, "work.json"), stableJson(item.work));
180
- fs.writeFileSync(path.join(targetDir, "status.json"), stableJson(item.status));
181
- }
182
- return captured;
183
- }
184
-
185
- export function main(argv = process.argv.slice(2)) {
186
- const args = parseArgs(argv);
187
- const repository = required(args, "repository");
188
- const channel = required(args, "channel");
189
- const sourceSha = required(args, "source-sha").toLowerCase();
190
- const tag = required(args, "tag");
191
- const configPath = assertSourcePath(required(args, "config-path"));
192
- const releasePassportPath = path.resolve(required(args, "release-passport-path"));
193
- const outputDir = path.resolve(required(args, "output-dir"));
194
- if (!/^[0-9a-f]{40}$/.test(sourceSha)) throw new Error("--source-sha must be an exact commit SHA");
195
- if (tag !== `v${tag.replace(/^v/, "")}`) throw new Error("--tag must be an exact v-prefixed release tag");
196
- const version = tag.slice(1);
197
- const configSource = readConfigAtSource(sourceSha, configPath, process.cwd());
198
- const sourceNode = configSource.normalized.graph.nodes.find(
199
- (node) => node.id === configSource.normalized.sourceNode,
200
- );
201
- const packageFact = resolvePackageFact(sourceNode.package, version);
202
- const tagTargetSha = resolveTagTarget(repository, tag);
203
- const releasePassport = verifyPublicReleasePassport({
204
- repository,
205
- tag,
206
- localPath: releasePassportPath,
207
- });
208
- const upstreamRelease = {
209
- repository,
210
- channel,
211
- tag,
212
- sourceSha,
213
- tagTargetSha,
214
- package: packageFact,
215
- releasePassport,
216
- };
217
- const expectedBaseShas = resolveBaseShas(configSource.normalized);
218
- const captured = capturePackageReleasePropagation({
219
- config: configSource.parsed,
220
- configBytes: configSource.bytes,
221
- upstreamRelease,
222
- expectedBaseShas,
223
- outputDir,
224
- });
225
- const artifactName = `package-propagation-work-${version}-${sourceSha}`;
226
- const workRoots = captured.works.map((item) => item.work.contentRoot);
227
- writeOutput("configured", "true");
228
- writeOutput("artifact-name", artifactName);
229
- writeOutput("work-roots-json", JSON.stringify(workRoots));
230
- process.stdout.write(stableJson({
231
- schemaVersion: 1,
232
- contract: "kungfu-buildchain-package-release-propagation-capture-result",
233
- artifactName,
234
- release: {
235
- repository,
236
- channel,
237
- tag,
238
- sourceSha,
239
- tagTargetSha,
240
- package: packageFact,
241
- releasePassport,
242
- },
243
- workCount: captured.works.length,
244
- works: captured.works.map((item) => ({
245
- target: item.target,
246
- repository: item.repository,
247
- propagationKey: item.propagationKey,
248
- workId: item.work.workId,
249
- workRoot: item.work.contentRoot,
250
- nextAction: item.status.nextAction,
251
- })),
252
- }));
253
- }
254
-
255
- const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
256
- if (isMain) {
257
- try {
258
- main();
259
- } catch (error) {
260
- console.error(error.message);
261
- process.exitCode = 1;
262
- }
263
- }