@kungfu-tech/buildchain 3.0.1 → 3.0.2-alpha.0

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 (58) hide show
  1. package/README.md +5 -3
  2. package/actions/promote-buildchain-ref/README.md +7 -0
  3. package/bin/buildchain.mjs +173 -513
  4. package/bin/internal/cli-options.mjs +79 -0
  5. package/bin/internal/trust-release-cli.mjs +469 -0
  6. package/dist/site/agent-index.json +2 -0
  7. package/dist/site/artifact-schemas.json +5 -0
  8. package/dist/site/badge-endpoint-registry.json +18 -18
  9. package/dist/site/badges/v1/kfd-12/aligned.json +1 -1
  10. package/dist/site/badges/v1/kfd-12/declared.json +1 -1
  11. package/dist/site/badges/v1/kfd-12/downgraded.json +1 -1
  12. package/dist/site/badges/v1/kfd-12/draft.json +1 -1
  13. package/dist/site/badges/v1/kfd-12/failed.json +1 -1
  14. package/dist/site/badges/v1/kfd-12/missing.json +1 -1
  15. package/dist/site/badges/v1/kfd-12/passed.json +1 -1
  16. package/dist/site/badges/v1/kfd-12/planned.json +1 -1
  17. package/dist/site/badges/v1/kfd-13/aligned.json +1 -1
  18. package/dist/site/badges/v1/kfd-13/declared.json +1 -1
  19. package/dist/site/badges/v1/kfd-13/downgraded.json +1 -1
  20. package/dist/site/badges/v1/kfd-13/draft.json +1 -1
  21. package/dist/site/badges/v1/kfd-13/failed.json +1 -1
  22. package/dist/site/badges/v1/kfd-13/missing.json +1 -1
  23. package/dist/site/badges/v1/kfd-13/passed.json +1 -1
  24. package/dist/site/badges/v1/kfd-13/planned.json +1 -1
  25. package/dist/site/buildchain-contract.json +32 -22
  26. package/dist/site/buildchain-site.json +28 -18
  27. package/dist/site/capability-registry.json +2 -2
  28. package/dist/site/cli-registry.json +108 -0
  29. package/dist/site/controller-registry.json +10 -2
  30. package/dist/site/kfd-claims.json +136 -10
  31. package/dist/site/kfd-upstream-aggregate.json +20 -11
  32. package/dist/site/manual-registry.json +3 -3
  33. package/dist/site/node-api-registry.json +21 -8
  34. package/dist/site/page-registry.json +20 -10
  35. package/dist/site/public-surface-audit.json +114 -8
  36. package/dist/site/publication-registry.json +4 -4
  37. package/dist/site/release-passport-check-manifest.json +11 -0
  38. package/dist/site/release-provenance.json +3 -0
  39. package/dist/site/schemas/kfd-product-gate-input-v1.schema.json +164 -0
  40. package/dist/site/schemas/kfd-support-projection-v1.schema.json +106 -0
  41. package/dist/site/schemas/release-passport-v1.schema.json +3 -0
  42. package/dist/site/site-manifest.json +7 -7
  43. package/dist/site/workflow-registry.json +9 -3
  44. package/docs/cli.md +9 -2
  45. package/docs/kfd-support.md +43 -7
  46. package/docs/release-passport.md +15 -0
  47. package/package.json +6 -3
  48. package/packages/core/index.js +17 -0
  49. package/packages/core/kfd-product-gates.js +865 -0
  50. package/packages/core/kfd.js +16 -7
  51. package/packages/core/kfd3-surface-register.js +105 -1
  52. package/packages/core/public-surface-audit.js +6 -1
  53. package/packages/core/release-passport-contract.js +13 -0
  54. package/packages/core/release-passport.js +87 -3
  55. package/scripts/check-internal-architecture.mjs +171 -0
  56. package/scripts/check-inventory.mjs +4 -0
  57. package/scripts/generate-site-bundle.mjs +23 -0
  58. package/scripts/promotion-routing-evidence.mjs +73 -0
@@ -0,0 +1,79 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ function readFlag(args, name, fallback = "") {
5
+ const index = args.indexOf(`--${name}`);
6
+ if (index === -1) {
7
+ return fallback;
8
+ }
9
+ return args[index + 1] || "";
10
+ }
11
+
12
+ function readBooleanFlag(args, name) {
13
+ return args.includes(`--${name}`);
14
+ }
15
+
16
+ function readRepeatedFlag(args, name) {
17
+ const values = [];
18
+ for (let index = 0; index < args.length; index += 1) {
19
+ if (args[index] === `--${name}` && args[index + 1]) {
20
+ values.push(args[index + 1]);
21
+ index += 1;
22
+ }
23
+ }
24
+ return values;
25
+ }
26
+
27
+ function artifactEnvelopeOptions(args) {
28
+ const assessmentTime = readFlag(args, "assessment-time", "");
29
+ return {
30
+ ...(assessmentTime ? { assessmentTime: Number(assessmentTime) } : {}),
31
+ expectedEnvelopeRoot: readFlag(args, "expected-root", ""),
32
+ expectedIssuer: readFlag(args, "expected-issuer", ""),
33
+ expectedPublisher: readFlag(args, "expected-publisher", ""),
34
+ expectedContractVersion: readFlag(args, "expected-contract", ""),
35
+ };
36
+ }
37
+
38
+ function printJson(value) {
39
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
40
+ }
41
+
42
+ function writeJsonFile(filePath, value) {
43
+ if (!filePath) {
44
+ return "";
45
+ }
46
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
47
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
48
+ return filePath;
49
+ }
50
+
51
+ function readJsonInput(value, { cwd = process.cwd(), label = "json" } = {}) {
52
+ const input = String(value || "").trim();
53
+ if (!input) {
54
+ throw new Error(`${label} is required`);
55
+ }
56
+ const filePath = path.isAbsolute(input) ? input : path.join(cwd, input);
57
+ if (fs.existsSync(filePath)) {
58
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
59
+ }
60
+ return JSON.parse(input);
61
+ }
62
+
63
+ function readRepeatedJsonInputs(args, name, { cwd = process.cwd(), label = name } = {}) {
64
+ return readRepeatedFlag(args, name).map((value, index) => readJsonInput(value, {
65
+ cwd,
66
+ label: `${label}[${index}]`,
67
+ }));
68
+ }
69
+
70
+ export {
71
+ artifactEnvelopeOptions,
72
+ printJson,
73
+ readBooleanFlag,
74
+ readFlag,
75
+ readJsonInput,
76
+ readRepeatedFlag,
77
+ readRepeatedJsonInputs,
78
+ writeJsonFile,
79
+ };
@@ -0,0 +1,469 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { verifyInfraContractEvidenceBundle } from "../../scripts/infra-contract-core.mjs";
4
+ import { verifyBuildchainLogEvents } from "../../packages/core/logging.js";
5
+ import {
6
+ explainReleaseLineDryRun,
7
+ formatReleaseLineDryRun,
8
+ } from "../../packages/core/release-line-dry-run.js";
9
+ import {
10
+ planReleaseLineBootstrap,
11
+ writeReleaseLineBootstrapVersionState,
12
+ } from "../../packages/core/release-line-bootstrap.js";
13
+ import {
14
+ collectGitHubReleasePassport,
15
+ explainReleasePassport,
16
+ verifyReleasePassport,
17
+ } from "../../packages/core/release-passport.js";
18
+ import {
19
+ createPublicationAdmission,
20
+ createRunnerProvenance,
21
+ verifyPublicationAdmission,
22
+ } from "../../packages/core/publication-authority.js";
23
+ import {
24
+ explainArtifactPassport,
25
+ verifyArtifactPassport,
26
+ } from "../../packages/core/artifact-passport.js";
27
+ import {
28
+ projectArtifactVerificationEnvelopeToKfx,
29
+ verifyArtifactVerificationEnvelope,
30
+ } from "../../packages/core/artifact-verification-envelope.js";
31
+ import {
32
+ artifactEnvelopeOptions,
33
+ printJson,
34
+ readBooleanFlag,
35
+ readFlag,
36
+ readJsonInput,
37
+ readRepeatedFlag,
38
+ writeJsonFile,
39
+ } from "./cli-options.mjs";
40
+
41
+ const TRUST_RELEASE_COMMANDS = new Set([
42
+ "audit",
43
+ "collect",
44
+ "create",
45
+ "explain",
46
+ "inspect",
47
+ "project",
48
+ "release",
49
+ "transaction",
50
+ "verify",
51
+ ]);
52
+
53
+ async function dispatchTrustReleaseCommand({ command, args, runScript, packageVersion }) {
54
+ if (command === "release") {
55
+ if (args[0] === "line" && args[1] === "open") {
56
+ const lineArgs = args.slice(2);
57
+ const options = {
58
+ cwd: readFlag(lineArgs, "cwd", process.cwd()),
59
+ major: readFlag(lineArgs, "major", ""),
60
+ minor: readFlag(lineArgs, "minor", ""),
61
+ sourceRef: readFlag(lineArgs, "source-ref", ""),
62
+ initialVersion: readFlag(lineArgs, "initial-version", ""),
63
+ };
64
+ const result = readBooleanFlag(lineArgs, "write")
65
+ ? writeReleaseLineBootstrapVersionState({
66
+ ...options,
67
+ runVersionStateLifecycle: !readBooleanFlag(lineArgs, "skip-version-state-lifecycle"),
68
+ generatedAt: readFlag(lineArgs, "generated-at", ""),
69
+ })
70
+ : planReleaseLineBootstrap({
71
+ ...options,
72
+ requiredStatusCheck: readFlag(lineArgs, "required-status-check", "check"),
73
+ setDefault: !readBooleanFlag(lineArgs, "no-set-default"),
74
+ createAlphaPr: !readBooleanFlag(lineArgs, "no-alpha-pr"),
75
+ approvalCount: Number(readFlag(lineArgs, "approval-count", "1")),
76
+ bootstrapBranch: readFlag(lineArgs, "bootstrap-branch", ""),
77
+ });
78
+ if (readBooleanFlag(lineArgs, "json")) {
79
+ printJson(result);
80
+ } else {
81
+ process.stdout.write(`Buildchain release line bootstrap ${result.line}\n`);
82
+ process.stdout.write(`- source: ${result.source.ref}${result.source.sha ? ` (${result.source.sha})` : ""}\n`);
83
+ process.stdout.write(`- initial version: ${result.initialVersion}\n`);
84
+ process.stdout.write(`- dev: ${result.refs.dev}\n`);
85
+ process.stdout.write(`- alpha: ${result.refs.alpha}\n`);
86
+ process.stdout.write(`- release: ${result.refs.release}\n`);
87
+ process.stdout.write(`- version files: ${result.versionState.files.join(", ") || "none"}\n`);
88
+ if (result.changedFiles) {
89
+ process.stdout.write(`- changed files: ${result.changedFiles.join(", ") || "none"}\n`);
90
+ }
91
+ process.stdout.write(result.dryRun ? "No refs, branches, PRs, or files were modified.\n" : "Version-state files were updated in the working tree.\n");
92
+ }
93
+ return;
94
+ }
95
+ const explainMode = args[0] === "dry-run" || args[0] === "explain";
96
+ const releaseArgs = explainMode ? args.slice(1) : args;
97
+ if (explainMode || readBooleanFlag(args, "dry-run")) {
98
+ const plan = explainReleaseLineDryRun({
99
+ cwd: readFlag(releaseArgs, "cwd", process.cwd()),
100
+ targetRef: readFlag(releaseArgs, "target-ref", ""),
101
+ sourceRef: readFlag(releaseArgs, "source-ref", ""),
102
+ sha: readFlag(releaseArgs, "sha", ""),
103
+ tags: readFlag(releaseArgs, "tags", ""),
104
+ publishTransaction: readBooleanFlag(releaseArgs, "publish-transaction"),
105
+ publishCommand: readFlag(releaseArgs, "publish-command", ""),
106
+ });
107
+ if (readBooleanFlag(releaseArgs, "json")) {
108
+ printJson(plan);
109
+ } else {
110
+ process.stdout.write(formatReleaseLineDryRun(plan));
111
+ }
112
+ return;
113
+ }
114
+ runScript("release-transaction.mjs", args);
115
+ return;
116
+ }
117
+
118
+ if (command === "transaction") {
119
+ const [subcommand = "inspect", ...transactionArgs] = args;
120
+ if (subcommand !== "inspect") {
121
+ throw new Error("usage: buildchain transaction inspect ...");
122
+ }
123
+ runScript("release-transaction.mjs", ["inspect", ...transactionArgs]);
124
+ return;
125
+ }
126
+
127
+ if (command === "create") {
128
+ const [subcommand = "", ...createArgs] = args;
129
+ const inputValue = readFlag(createArgs, "input-json", "");
130
+ if (!inputValue || !["publication-admission", "runner-provenance"].includes(subcommand)) {
131
+ throw new Error("usage: buildchain create <publication-admission|runner-provenance> --input-json <file-or-json> [--output <file>]");
132
+ }
133
+ const input = readJsonInput(inputValue, { label: "input-json" });
134
+ const value = subcommand === "publication-admission"
135
+ ? createPublicationAdmission(input)
136
+ : createRunnerProvenance(input);
137
+ const output = readFlag(createArgs, "output", "");
138
+ if (output) writeJsonFile(path.resolve(output), value);
139
+ if (!output || readBooleanFlag(createArgs, "json")) printJson(value);
140
+ else process.stdout.write(`${subcommand}: ${output}\n`);
141
+ return;
142
+ }
143
+
144
+ if (command === "collect") {
145
+ const [subcommand = "", ...collectArgs] = args;
146
+ if (subcommand !== "github-release") {
147
+ throw new Error("usage: buildchain collect github-release --tag <tag>");
148
+ }
149
+ const workflow = {
150
+ name: process.env.GITHUB_WORKFLOW || "",
151
+ runId: process.env.GITHUB_RUN_ID || "",
152
+ runAttempt: process.env.GITHUB_RUN_ATTEMPT || "",
153
+ url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID
154
+ ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
155
+ : "",
156
+ runnerKind: process.env.BUILDCHAIN_RUNNER_KIND || "github-hosted",
157
+ runnerOs: process.env.RUNNER_OS || process.platform,
158
+ runnerArch: process.env.RUNNER_ARCH || process.arch,
159
+ runnerImage: process.env.ImageOS || "",
160
+ };
161
+ const result = collectGitHubReleasePassport({
162
+ cwd: readFlag(collectArgs, "cwd", process.cwd()),
163
+ tag: readFlag(collectArgs, "tag", ""),
164
+ repository: readFlag(collectArgs, "repository", process.env.GITHUB_REPOSITORY || ""),
165
+ sourceSha: readFlag(collectArgs, "source-sha", process.env.GITHUB_SHA || ""),
166
+ line: readFlag(collectArgs, "line", ""),
167
+ outputDir: readFlag(collectArgs, "output-dir", ".buildchain/release-passport"),
168
+ assetsDir: readFlag(collectArgs, "assets-dir", ""),
169
+ assetsJson: readFlag(collectArgs, "assets-json", ""),
170
+ releaseJson: readFlag(collectArgs, "release-json", ""),
171
+ productName: readFlag(collectArgs, "product-name", "Buildchain"),
172
+ packageName: readFlag(collectArgs, "package-name", "@kungfu-tech/buildchain"),
173
+ packageVersion: readFlag(collectArgs, "package-version", packageVersion()),
174
+ packageSetJson: readFlag(collectArgs, "package-set-json", ""),
175
+ publishEvidenceJson: readFlag(collectArgs, "publish-evidence-json", ""),
176
+ trustedPublishingJson: readFlag(collectArgs, "trusted-publishing-json", ""),
177
+ transactionJson: readFlag(collectArgs, "transaction-json", ""),
178
+ anchorManifestJson: readFlag(collectArgs, "anchor-manifest-json", ""),
179
+ impactJson: readFlag(collectArgs, "impact-json", ""),
180
+ buildSummaryJson: readFlag(collectArgs, "build-summary-json", ""),
181
+ buildFactsJsons: readRepeatedFlag(collectArgs, "build-facts-json"),
182
+ platformManifestJsons: readRepeatedFlag(collectArgs, "platform-manifest-json"),
183
+ distTagEvidenceJson: readFlag(collectArgs, "dist-tag-evidence-json", ""),
184
+ kfd1WitnessJsons: readRepeatedFlag(collectArgs, "kfd-1-witness-json"),
185
+ kfd2ClaimJsons: readRepeatedFlag(collectArgs, "kfd-2-claim-json"),
186
+ kfd3PrebuildWitnessJsons: readRepeatedFlag(collectArgs, "kfd-3-prebuild-witness-json"),
187
+ kfd3ArtifactWitnessJsons: readRepeatedFlag(collectArgs, "kfd-3-artifact-witness-json"),
188
+ kfd3ArtifactVerifyCommand: readFlag(collectArgs, "kfd-3-artifact-verify-cmd", ""),
189
+ kfdSupportMatrixJson: readFlag(collectArgs, "kfd-support-matrix-json", ""),
190
+ kfdProductGateJsons: readRepeatedFlag(collectArgs, "kfd-product-gate-json"),
191
+ invariantPassportJsons: readRepeatedFlag(collectArgs, "invariant-passport-json"),
192
+ invariantPassportCommand: readFlag(collectArgs, "invariant-passport-cmd", ""),
193
+ kfdAgentHubEvidenceJson: readFlag(collectArgs, "kfd-agent-hub-evidence-json", ""),
194
+ basePassportJson: readFlag(collectArgs, "base-passport-json", ""),
195
+ requireBaseKfd: readBooleanFlag(collectArgs, "require-base-kfd"),
196
+ releaseJsonExtra: readFlag(collectArgs, "release-extra-json", ""),
197
+ publishJson: readFlag(collectArgs, "publish-json", ""),
198
+ workflow,
199
+ });
200
+ if (readBooleanFlag(collectArgs, "json")) {
201
+ printJson(result);
202
+ } else {
203
+ process.stdout.write(`release passport collected: ${path.relative(process.cwd(), result.outputDir)}\n`);
204
+ process.stdout.write(`artifacts: ${result.artifactEvidence.artifacts.length}\n`);
205
+ }
206
+ return;
207
+ }
208
+
209
+ if (command === "project") {
210
+ const [subcommand = "", location = "", ...projectArgs] = args;
211
+ if (subcommand !== "kfx-admission" || !location) {
212
+ throw new Error("usage: buildchain project kfx-admission <file-or-json>");
213
+ }
214
+ const projection = projectArtifactVerificationEnvelopeToKfx({
215
+ envelope: readJsonInput(location, { label: "artifact verification envelope" }),
216
+ ...artifactEnvelopeOptions(projectArgs),
217
+ });
218
+ if (readBooleanFlag(projectArgs, "json")) {
219
+ printJson(projection);
220
+ } else {
221
+ process.stdout.write(`KFX admission envelope: ${projection.envelopeRoot}\n`);
222
+ }
223
+ return;
224
+ }
225
+
226
+ if (command === "verify") {
227
+ const [subcommand = "", location = "", ...verifyArgs] = args;
228
+ if (subcommand === "publication-admission") {
229
+ if (!location) {
230
+ throw new Error("usage: buildchain verify publication-admission <file-or-json> --registry-json <file-or-json> --runner-json <file-or-json> --control-plane-audit-json <file-or-json> --publication-evidence-json <file-or-json>");
231
+ }
232
+ const requiredInput = (name) => {
233
+ const value = readFlag(verifyArgs, name, "");
234
+ if (!value) throw new Error(`buildchain verify publication-admission requires --${name} <file-or-json>`);
235
+ return readJsonInput(value, { label: name });
236
+ };
237
+ const expectedInput = readFlag(verifyArgs, "expected-json", "");
238
+ const capability = verifyPublicationAdmission({
239
+ admission: readJsonInput(location, { label: "publication admission" }),
240
+ registry: requiredInput("registry-json"),
241
+ runnerProvenance: requiredInput("runner-json"),
242
+ controlPlaneAudit: requiredInput("control-plane-audit-json"),
243
+ publicationEvidence: requiredInput("publication-evidence-json"),
244
+ expected: expectedInput ? readJsonInput(expectedInput, { label: "expected-json" }) : {},
245
+ usedNonces: readRepeatedFlag(verifyArgs, "used-nonce"),
246
+ });
247
+ if (readBooleanFlag(verifyArgs, "json")) {
248
+ printJson(capability);
249
+ } else {
250
+ process.stdout.write(`publication admission: ${capability.decision}\n`);
251
+ process.stdout.write(`capability digest: ${capability.capabilityDigest}\n`);
252
+ process.stdout.write(`expires at: ${capability.expiresAt}\n`);
253
+ }
254
+ return;
255
+ }
256
+ if (subcommand === "artifact") {
257
+ if (!location) {
258
+ throw new Error("usage: buildchain verify artifact <subject>");
259
+ }
260
+ const report = await verifyArtifactPassport({
261
+ subject: location,
262
+ cwd: process.cwd(),
263
+ passportLocation: readFlag(verifyArgs, "passport", ""),
264
+ locatorConfig: readFlag(verifyArgs, "locator-config", ""),
265
+ repository: readFlag(verifyArgs, "repository", ""),
266
+ tag: readFlag(verifyArgs, "tag", ""),
267
+ githubReleaseBaseUrl: readFlag(verifyArgs, "github-release-base-url", ""),
268
+ subjectDigest: readFlag(verifyArgs, "subject-digest", ""),
269
+ subjectKind: readFlag(verifyArgs, "subject-kind", ""),
270
+ npmRegistryBaseUrl: readFlag(verifyArgs, "npm-registry", ""),
271
+ });
272
+ if (readBooleanFlag(verifyArgs, "json")) {
273
+ printJson(report);
274
+ } else {
275
+ process.stdout.write(`artifact: ${report.outcome}\n`);
276
+ process.stdout.write(`subject: ${report.subject?.name || location}\n`);
277
+ process.stdout.write(`passport: ${report.passport?.location || report.discovery?.passportLocation || "unresolved"}\n`);
278
+ for (const entry of report.issues) {
279
+ process.stdout.write(`- ${entry.level}: ${entry.code}: ${entry.message}\n`);
280
+ }
281
+ }
282
+ process.exitCode = report.ok ? 0 : 1;
283
+ return;
284
+ }
285
+ if (subcommand === "artifact-envelope") {
286
+ if (!location) {
287
+ throw new Error("usage: buildchain verify artifact-envelope <file-or-json>");
288
+ }
289
+ const report = verifyArtifactVerificationEnvelope({
290
+ envelope: readJsonInput(location, { label: "artifact verification envelope" }),
291
+ ...artifactEnvelopeOptions(verifyArgs),
292
+ });
293
+ if (readBooleanFlag(verifyArgs, "json")) {
294
+ printJson(report);
295
+ } else {
296
+ process.stdout.write(`artifact verification envelope: ${report.outcome}\n`);
297
+ process.stdout.write(`root: ${report.envelopeRoot || "unresolved"}\n`);
298
+ for (const entry of report.issues) {
299
+ process.stdout.write(`- ${entry.level}: ${entry.code}: ${entry.message}\n`);
300
+ }
301
+ }
302
+ process.exitCode = report.ok ? 0 : 1;
303
+ return;
304
+ }
305
+ if (subcommand === "observability-log") {
306
+ if (!location) {
307
+ throw new Error("usage: buildchain verify observability-log <jsonl>");
308
+ }
309
+ const report = verifyBuildchainLogEvents({
310
+ path: location,
311
+ minEvents: Number(readFlag(verifyArgs, "min-events", "1")),
312
+ allowErrors: readBooleanFlag(verifyArgs, "allow-errors"),
313
+ requirePhases: readRepeatedFlag(verifyArgs, "require-phase"),
314
+ requireComponents: readRepeatedFlag(verifyArgs, "require-component"),
315
+ requireEvents: readRepeatedFlag(verifyArgs, "require-event"),
316
+ });
317
+ if (readBooleanFlag(verifyArgs, "json")) {
318
+ printJson(report);
319
+ } else {
320
+ process.stdout.write(`observability log: ${report.ok ? "ok" : "failed"}\n`);
321
+ process.stdout.write(`events: ${report.summary.eventCount}\n`);
322
+ for (const entry of report.issues) {
323
+ process.stdout.write(`- ${entry.level}: ${entry.code}: ${entry.message}\n`);
324
+ }
325
+ }
326
+ process.exitCode = report.ok ? 0 : 1;
327
+ return;
328
+ }
329
+ if (subcommand === "infra-contract-evidence-bundle") {
330
+ if (!location) {
331
+ throw new Error("usage: buildchain verify infra-contract-evidence-bundle <file>");
332
+ }
333
+ const bundle = JSON.parse(fs.readFileSync(path.resolve(location), "utf8"));
334
+ const report = verifyInfraContractEvidenceBundle(bundle);
335
+ if (readBooleanFlag(verifyArgs, "json")) {
336
+ printJson(report);
337
+ } else {
338
+ process.stdout.write(`infra contract evidence bundle: ${report.ok ? "ok" : "failed"}\n`);
339
+ process.stdout.write(`artifact: ${report.artifactHash || "unknown"}\n`);
340
+ for (const entry of report.issues) {
341
+ process.stdout.write(`- ${entry.level}: ${entry.code}: ${entry.message}\n`);
342
+ }
343
+ }
344
+ process.exitCode = report.ok ? 0 : 1;
345
+ return;
346
+ }
347
+ if (subcommand !== "release-passport" || !location) {
348
+ throw new Error("usage: buildchain verify release-passport <file-or-url>");
349
+ }
350
+ const report = await verifyReleasePassport({ passportLocation: location });
351
+ if (readBooleanFlag(verifyArgs, "json")) {
352
+ printJson(report);
353
+ } else {
354
+ process.stdout.write(`release passport: ${report.ok ? "ok" : "failed"}\n`);
355
+ process.stdout.write(`artifacts: ${report.completeness.artifactCount}\n`);
356
+ for (const entry of report.issues) {
357
+ process.stdout.write(`- ${entry.level}: ${entry.code}: ${entry.message}\n`);
358
+ }
359
+ }
360
+ process.exitCode = report.ok ? 0 : 1;
361
+ return;
362
+ }
363
+
364
+ if (command === "audit") {
365
+ const [subcommand = "", ...auditArgs] = args;
366
+ if (subcommand === "publication-control-plane") {
367
+ runScript("audit-publication-control-plane.mjs", auditArgs);
368
+ return;
369
+ }
370
+ if (subcommand === "github-governance") {
371
+ runScript("audit-github-governance.mjs", auditArgs);
372
+ return;
373
+ }
374
+ throw new Error("usage: buildchain audit <publication-control-plane|github-governance> ...");
375
+ }
376
+
377
+ if (command === "explain") {
378
+ const [subcommand = "", ...explainArgs] = args;
379
+ if (subcommand === "artifact") {
380
+ const subject = explainArgs[0] || "";
381
+ if (!subject) {
382
+ throw new Error("usage: buildchain explain artifact <subject>");
383
+ }
384
+ const explanation = await explainArtifactPassport({
385
+ subject,
386
+ cwd: process.cwd(),
387
+ passportLocation: readFlag(explainArgs, "passport", ""),
388
+ locatorConfig: readFlag(explainArgs, "locator-config", ""),
389
+ repository: readFlag(explainArgs, "repository", ""),
390
+ tag: readFlag(explainArgs, "tag", ""),
391
+ githubReleaseBaseUrl: readFlag(explainArgs, "github-release-base-url", ""),
392
+ subjectDigest: readFlag(explainArgs, "subject-digest", ""),
393
+ subjectKind: readFlag(explainArgs, "subject-kind", ""),
394
+ npmRegistryBaseUrl: readFlag(explainArgs, "npm-registry", ""),
395
+ forAudience: readFlag(explainArgs, "for", "human"),
396
+ });
397
+ if (readBooleanFlag(explainArgs, "json")) {
398
+ printJson(explanation);
399
+ } else {
400
+ process.stdout.write(`artifact: ${explanation.subject?.name || subject}\n`);
401
+ process.stdout.write(`trust: ${explanation.trust}\n`);
402
+ process.stdout.write(`next action: ${explanation.nextAction}\n`);
403
+ }
404
+ process.exitCode = explanation.trust === "pass" ? 0 : 1;
405
+ return;
406
+ }
407
+ if (subcommand !== "release") {
408
+ throw new Error("usage: buildchain explain release --passport <file-or-url>");
409
+ }
410
+ const passport = readFlag(explainArgs, "passport", "");
411
+ if (!passport) {
412
+ throw new Error("buildchain explain release requires --passport <file-or-url>");
413
+ }
414
+ const explanation = await explainReleasePassport({
415
+ passportLocation: passport,
416
+ forAudience: readFlag(explainArgs, "for", "human"),
417
+ });
418
+ if (readBooleanFlag(explainArgs, "json")) {
419
+ printJson(explanation);
420
+ } else {
421
+ process.stdout.write(`release: ${explanation.release?.tag || "unknown"}\n`);
422
+ process.stdout.write(`trust: ${explanation.trust}\n`);
423
+ process.stdout.write(`next action: ${explanation.nextAction}\n`);
424
+ }
425
+ return;
426
+ }
427
+
428
+ if (command === "inspect") {
429
+ const [subcommand = "", ...inspectArgs] = args;
430
+ if (subcommand === "artifact") {
431
+ const subject = inspectArgs[0] || "";
432
+ if (!subject) {
433
+ throw new Error("usage: buildchain inspect artifact <subject>");
434
+ }
435
+ const report = await verifyArtifactPassport({
436
+ subject,
437
+ cwd: process.cwd(),
438
+ passportLocation: readFlag(inspectArgs, "passport", ""),
439
+ locatorConfig: readFlag(inspectArgs, "locator-config", ""),
440
+ repository: readFlag(inspectArgs, "repository", ""),
441
+ tag: readFlag(inspectArgs, "tag", ""),
442
+ githubReleaseBaseUrl: readFlag(inspectArgs, "github-release-base-url", ""),
443
+ subjectDigest: readFlag(inspectArgs, "subject-digest", ""),
444
+ subjectKind: readFlag(inspectArgs, "subject-kind", ""),
445
+ npmRegistryBaseUrl: readFlag(inspectArgs, "npm-registry", ""),
446
+ });
447
+ printJson(report);
448
+ process.exitCode = report.ok ? 0 : 1;
449
+ return;
450
+ }
451
+ if (subcommand !== "release") {
452
+ throw new Error("usage: buildchain inspect release --passport <file-or-url>");
453
+ }
454
+ const passport = readFlag(inspectArgs, "passport", "");
455
+ if (!passport) {
456
+ throw new Error("buildchain inspect release requires --passport <file-or-url>");
457
+ }
458
+ const explanation = await explainReleasePassport({
459
+ passportLocation: passport,
460
+ forAudience: readFlag(inspectArgs, "for", "human"),
461
+ });
462
+ printJson(explanation);
463
+ return;
464
+ }
465
+
466
+ throw new Error(`unsupported trust or release command: ${command}`);
467
+ }
468
+
469
+ export { TRUST_RELEASE_COMMANDS, dispatchTrustReleaseCommand };
@@ -18,6 +18,8 @@
18
18
  "release-passport-check-manifest.json",
19
19
  "schemas/release-passport-v1.schema.json",
20
20
  "schemas/kfd-agent-hub-adoption.schema.json",
21
+ "schemas/kfd-product-gate-input-v1.schema.json",
22
+ "schemas/kfd-support-projection-v1.schema.json",
21
23
  "buildchain-contract.json",
22
24
  "kfd-upstream-aggregate.json",
23
25
  "kfd-claims.json",
@@ -6,6 +6,9 @@
6
6
  "release-passport-check-manifest.json",
7
7
  "schemas/release-passport-v1.schema.json",
8
8
  "schemas/kfd-agent-hub-adoption.schema.json",
9
+ "schemas/kfd-product-gate-input-v1.schema.json",
10
+ "schemas/kfd-support-projection-v1.schema.json",
11
+ "kfd-support.json",
9
12
  "artifact-evidence.json",
10
13
  "product-mechanism.json",
11
14
  "impact.json",
@@ -34,6 +37,8 @@
34
37
  "release-passport-check-manifest.json",
35
38
  "schemas/release-passport-v1.schema.json",
36
39
  "schemas/kfd-agent-hub-adoption.schema.json",
40
+ "schemas/kfd-product-gate-input-v1.schema.json",
41
+ "schemas/kfd-support-projection-v1.schema.json",
37
42
  "buildchain-contract.json",
38
43
  "kfd-claims.json",
39
44
  "product-mechanism.json",