@kungfu-tech/buildchain 2.1.1-alpha.0 → 2.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,6 +51,11 @@ future minor refs can each receive many patch releases such as `v2.0.1234`.
51
51
  `v2` points at the selected stable major line, while `v2.0` points at the
52
52
  latest production patch for that minor line.
53
53
 
54
+ Buildchain opens a new minor line when it adds a new compatible welded surface:
55
+ a reusable workflow contract, public CLI command family, published package
56
+ subpath, evidence protocol, or installable binary distribution shape. The local
57
+ decision log lives in [`docs/versioning.md`](docs/versioning.md).
58
+
54
59
  `publish-gate/major` replaces the old ABV `main` channel. It is deliberately
55
60
  not named `main` because it is not the active development trunk. Maintainers use
56
61
  the same PR flow as other channel promotions: merging
@@ -111,6 +116,9 @@ Buildchain v2 ships these active surfaces:
111
116
  - `actions/run-lifecycle` for callers that need the same lifecycle/manifest
112
117
  contract inside their own workflows;
113
118
  - governance-closed self-promotion through `Buildchain Ref Promotion`.
119
+ - `v2.2` release passport and binary distribution contracts for GitHub Release
120
+ assets, artifact evidence, impact ledgers, agent indexes, and standalone CLI
121
+ archives.
114
122
 
115
123
  Stable consumers should reference actions as:
116
124
 
@@ -13,13 +13,20 @@ import {
13
13
  createBuildchainLogger,
14
14
  defaultBuildchainLogPath,
15
15
  summarizeBuildchainLogEvents,
16
+ verifyBuildchainLogEvents,
16
17
  } from "../packages/core/logging.js";
17
18
  import {
18
19
  explainReleaseLineDryRun,
19
20
  formatReleaseLineDryRun,
20
21
  } from "../packages/core/release-line-dry-run.js";
22
+ import {
23
+ collectGitHubReleasePassport,
24
+ explainReleasePassport,
25
+ verifyReleasePassport,
26
+ } from "../packages/core/release-passport.js";
21
27
 
22
28
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
29
+ const embeddedPackageVersion = process.env.BUILDCHAIN_EMBEDDED_PACKAGE_VERSION || "";
23
30
 
24
31
  function usage() {
25
32
  return `Usage:
@@ -42,6 +49,16 @@ function usage() {
42
49
  [--tags <comma-list>] [--json]
43
50
  buildchain release <inspect|recover|finalize|abort> ...
44
51
  buildchain transaction inspect ...
52
+ buildchain collect github-release --tag <tag> [--repository <owner/repo>]
53
+ [--assets-dir <dir>] [--assets-json <json-or-path>]
54
+ [--release-json <json-or-path>] [--output-dir <dir>] [--json]
55
+ buildchain verify release-passport <file-or-url> [--json]
56
+ buildchain verify observability-log <jsonl> [--min-events <n>]
57
+ [--require-phase <csv>]
58
+ [--require-component <csv>]
59
+ [--require-event <csv>] [--allow-errors] [--json]
60
+ buildchain explain release --passport <file-or-url> [--for human|agent] [--json]
61
+ buildchain inspect release --passport <file-or-url> [--json]
45
62
  buildchain doctor [--cwd <dir>] [--json]
46
63
  buildchain log <info|warn|error> --event <name> [--phase <phase>]
47
64
  [--component <name>] [--source <name>] [--attribute key=value]...
@@ -62,6 +79,9 @@ Examples:
62
79
  buildchain npm dry-run --json
63
80
  buildchain release --dry-run --target-ref alpha/v2/v2.0
64
81
  buildchain span --event native.build -- cmake --build build
82
+ buildchain collect github-release --tag v2.2.0 --assets-dir dist --output-dir .buildchain/release-passport
83
+ buildchain verify release-passport .buildchain/release-passport/buildchain.release.json
84
+ buildchain verify observability-log .buildchain/logs/events.jsonl --min-events 4 --require-phase build
65
85
  `;
66
86
  }
67
87
 
@@ -176,6 +196,9 @@ function printJson(value) {
176
196
  }
177
197
 
178
198
  function packageVersion() {
199
+ if (embeddedPackageVersion) {
200
+ return embeddedPackageVersion;
201
+ }
179
202
  const packageJson = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
180
203
  return packageJson.version;
181
204
  }
@@ -396,6 +419,129 @@ async function main(argv = process.argv.slice(2)) {
396
419
  return;
397
420
  }
398
421
 
422
+ if (command === "collect") {
423
+ const [subcommand = "", ...collectArgs] = args;
424
+ if (subcommand !== "github-release") {
425
+ throw new Error("usage: buildchain collect github-release --tag <tag>");
426
+ }
427
+ const workflow = {
428
+ name: process.env.GITHUB_WORKFLOW || "",
429
+ runId: process.env.GITHUB_RUN_ID || "",
430
+ runAttempt: process.env.GITHUB_RUN_ATTEMPT || "",
431
+ url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID
432
+ ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
433
+ : "",
434
+ runnerKind: process.env.BUILDCHAIN_RUNNER_KIND || "github-hosted",
435
+ runnerOs: process.env.RUNNER_OS || process.platform,
436
+ runnerArch: process.env.RUNNER_ARCH || process.arch,
437
+ runnerImage: process.env.ImageOS || "",
438
+ };
439
+ const result = collectGitHubReleasePassport({
440
+ cwd: readFlag(collectArgs, "cwd", process.cwd()),
441
+ tag: readFlag(collectArgs, "tag", ""),
442
+ repository: readFlag(collectArgs, "repository", process.env.GITHUB_REPOSITORY || ""),
443
+ sourceSha: readFlag(collectArgs, "source-sha", process.env.GITHUB_SHA || ""),
444
+ line: readFlag(collectArgs, "line", ""),
445
+ outputDir: readFlag(collectArgs, "output-dir", ".buildchain/release-passport"),
446
+ assetsDir: readFlag(collectArgs, "assets-dir", ""),
447
+ assetsJson: readFlag(collectArgs, "assets-json", ""),
448
+ releaseJson: readFlag(collectArgs, "release-json", ""),
449
+ packageName: readFlag(collectArgs, "package-name", "@kungfu-tech/buildchain"),
450
+ packageVersion: readFlag(collectArgs, "package-version", packageVersion()),
451
+ workflow,
452
+ });
453
+ if (readBooleanFlag(collectArgs, "json")) {
454
+ printJson(result);
455
+ } else {
456
+ process.stdout.write(`release passport collected: ${path.relative(process.cwd(), result.outputDir)}\n`);
457
+ process.stdout.write(`artifacts: ${result.artifactEvidence.artifacts.length}\n`);
458
+ }
459
+ return;
460
+ }
461
+
462
+ if (command === "verify") {
463
+ const [subcommand = "", location = "", ...verifyArgs] = args;
464
+ if (subcommand === "observability-log") {
465
+ if (!location) {
466
+ throw new Error("usage: buildchain verify observability-log <jsonl>");
467
+ }
468
+ const report = verifyBuildchainLogEvents({
469
+ path: location,
470
+ minEvents: Number(readFlag(verifyArgs, "min-events", "1")),
471
+ allowErrors: readBooleanFlag(verifyArgs, "allow-errors"),
472
+ requirePhases: readRepeatedFlag(verifyArgs, "require-phase"),
473
+ requireComponents: readRepeatedFlag(verifyArgs, "require-component"),
474
+ requireEvents: readRepeatedFlag(verifyArgs, "require-event"),
475
+ });
476
+ if (readBooleanFlag(verifyArgs, "json")) {
477
+ printJson(report);
478
+ } else {
479
+ process.stdout.write(`observability log: ${report.ok ? "ok" : "failed"}\n`);
480
+ process.stdout.write(`events: ${report.summary.eventCount}\n`);
481
+ for (const entry of report.issues) {
482
+ process.stdout.write(`- ${entry.level}: ${entry.code}: ${entry.message}\n`);
483
+ }
484
+ }
485
+ process.exitCode = report.ok ? 0 : 1;
486
+ return;
487
+ }
488
+ if (subcommand !== "release-passport" || !location) {
489
+ throw new Error("usage: buildchain verify release-passport <file-or-url>");
490
+ }
491
+ const report = await verifyReleasePassport({ passportLocation: location });
492
+ if (readBooleanFlag(verifyArgs, "json")) {
493
+ printJson(report);
494
+ } else {
495
+ process.stdout.write(`release passport: ${report.ok ? "ok" : "failed"}\n`);
496
+ process.stdout.write(`artifacts: ${report.completeness.artifactCount}\n`);
497
+ for (const entry of report.issues) {
498
+ process.stdout.write(`- ${entry.level}: ${entry.code}: ${entry.message}\n`);
499
+ }
500
+ }
501
+ process.exitCode = report.ok ? 0 : 1;
502
+ return;
503
+ }
504
+
505
+ if (command === "explain") {
506
+ const [subcommand = "", ...explainArgs] = args;
507
+ if (subcommand !== "release") {
508
+ throw new Error("usage: buildchain explain release --passport <file-or-url>");
509
+ }
510
+ const passport = readFlag(explainArgs, "passport", "");
511
+ if (!passport) {
512
+ throw new Error("buildchain explain release requires --passport <file-or-url>");
513
+ }
514
+ const explanation = await explainReleasePassport({
515
+ passportLocation: passport,
516
+ forAudience: readFlag(explainArgs, "for", "human"),
517
+ });
518
+ if (readBooleanFlag(explainArgs, "json")) {
519
+ printJson(explanation);
520
+ } else {
521
+ process.stdout.write(`release: ${explanation.release?.tag || "unknown"}\n`);
522
+ process.stdout.write(`trust: ${explanation.trust}\n`);
523
+ process.stdout.write(`next action: ${explanation.nextAction}\n`);
524
+ }
525
+ return;
526
+ }
527
+
528
+ if (command === "inspect") {
529
+ const [subcommand = "", ...inspectArgs] = args;
530
+ if (subcommand !== "release") {
531
+ throw new Error("usage: buildchain inspect release --passport <file-or-url>");
532
+ }
533
+ const passport = readFlag(inspectArgs, "passport", "");
534
+ if (!passport) {
535
+ throw new Error("buildchain inspect release requires --passport <file-or-url>");
536
+ }
537
+ const explanation = await explainReleasePassport({
538
+ passportLocation: passport,
539
+ forAudience: readFlag(inspectArgs, "for", "human"),
540
+ });
541
+ printJson(explanation);
542
+ return;
543
+ }
544
+
399
545
  if (command === "web-surface") {
400
546
  runScript("web-surface.mjs", args);
401
547
  return;
package/docs/MAP.md CHANGED
@@ -21,11 +21,13 @@ running artifact), *use* (consume / extend) - and a **status**:
21
21
  | How do I install or run the `buildchain` CLI? | [`cli.md`](cli.md) | use | stable |
22
22
  | How do I initialize a new repository? | [`cli.md`](cli.md) + [`lifecycle-protocol.md`](lifecycle-protocol.md) | use | stable |
23
23
  | Why does Buildchain use branch-driven release governance? | [`release-governance.md`](release-governance.md) | why | stable |
24
+ | How does Buildchain decide patch, minor, and major release lines? | [`versioning.md`](versioning.md) | why | stable |
24
25
  | What exact branch/tag state machine runs on alpha, release, and major gate? | [`release-flow.md`](release-flow.md) | verify | stable |
25
26
  | What did Buildchain migrate or retire from old action repositories? | [`migration-inventory.md`](migration-inventory.md) | verify | stable |
26
27
  | What is the active action and workflow source of truth? | [`ownership.md`](ownership.md) | verify | stable |
27
28
  | How do I declare version files and custom lifecycle commands? | [`lifecycle-protocol.md`](lifecycle-protocol.md) | use | stable |
28
29
  | How does publish evidence, recovery, and finalization work? | [`publish-transaction.md`](publish-transaction.md) | verify | stable |
30
+ | How do I publish or verify binary release passport artifacts? | [`release-passport.md`](release-passport.md) | use | stable |
29
31
  | How do I call the reusable build workflow? | [`reusable-build-surface.md`](reusable-build-surface.md) | use | stable |
30
32
  | How do I deploy a site/app preview, staging, or production surface? | [`web-surface-deployments.md`](web-surface-deployments.md) | use | stable |
31
33
  | How do I use the active actions directly? | [`../actions/validate-config/README.md`](../actions/validate-config/README.md), [`../actions/run-lifecycle/README.md`](../actions/run-lifecycle/README.md), [`../actions/promote-buildchain-ref/README.md`](../actions/promote-buildchain-ref/README.md) | use | stable |
@@ -40,6 +42,8 @@ running artifact), *use* (consume / extend) - and a **status**:
40
42
  - **v2 / v2.0 / v2.0-alpha / exact tags / floating tags** ->
41
43
  [`release-governance.md`](release-governance.md) and
42
44
  [`release-flow.md`](release-flow.md).
45
+ - **v2.1 vs v2.2 / when to open a new minor line** ->
46
+ [`versioning.md`](versioning.md).
43
47
  - **dry-run / what would happen if this channel PR merges** -> [`cli.md`](cli.md)
44
48
  and [`release-flow.md`](release-flow.md).
45
49
  - **pnpm / npm / yarn / package-manager adapters** ->
@@ -51,6 +55,8 @@ running artifact), *use* (consume / extend) - and a **status**:
51
55
  [`../fixtures/libnode-shaped/README.md`](../fixtures/libnode-shaped/README.md).
52
56
  - **Trusted Publishing / npm / publish evidence / recovery** ->
53
57
  [`cli.md`](cli.md) and [`publish-transaction.md`](publish-transaction.md).
58
+ - **GitHub Release passport / binary assets / artifact evidence / agent release checks** ->
59
+ [`release-passport.md`](release-passport.md) and [`cli.md`](cli.md).
54
60
  - **sites / web previews / staging / production gates** ->
55
61
  [`web-surface-deployments.md`](web-surface-deployments.md).
56
62
 
package/docs/cli.md CHANGED
@@ -75,12 +75,16 @@ buildchain mark --event configure.ready --phase configure --attribute target=rel
75
75
  buildchain span --event native.build --phase build -- cmake --build build
76
76
  buildchain log warn --event cache.miss --component conan --attribute token=hidden
77
77
  buildchain log summary --json
78
+ buildchain verify observability-log .buildchain/logs/events.jsonl --min-events 4 --require-phase build
78
79
  ```
79
80
 
80
81
  During `buildchain lifecycle run`, child processes receive
81
82
  `BUILDCHAIN_LOG_PATH` and `BUILDCHAIN_LOG_RUN_ID`. A shell, Python, CMake, Conan,
82
83
  or JavaScript helper can call `buildchain mark` or `buildchain span` mid-build
83
84
  and have those events grouped into the same lifecycle summary.
85
+ `buildchain verify observability-log` is a release gate: it fails when the log
86
+ is missing, has too few events, contains error events, or does not include
87
+ required phases, components, or event names.
84
88
 
85
89
  The event protocol is JSONL and is also available from the SDK:
86
90
 
@@ -111,6 +115,40 @@ and `buildchain build-contract` route to the same scripts used by Buildchain's
111
115
  GitHub Actions workflows. This keeps local inspection and CI behavior on the
112
116
  same implementation path.
113
117
 
118
+ `buildchain collect github-release` creates a release passport bundle from
119
+ GitHub Release assets or a local asset directory:
120
+
121
+ ```bash
122
+ buildchain collect github-release \
123
+ --tag v2.2.0 \
124
+ --repository kungfu-systems/buildchain \
125
+ --assets-dir dist \
126
+ --output-dir .buildchain/release-passport
127
+ ```
128
+
129
+ The bundle includes `buildchain.release.json`, `artifact-evidence.json`,
130
+ `impact.json`, `agent-index.json`, `product-mechanism.json`, `check-report.json`,
131
+ and `llms.txt`. Production binary distribution defaults to GitHub-hosted
132
+ runners so other projects can reproduce the release lane; self-hosted runners
133
+ remain compatibility fixtures and are recorded as runner facts when used.
134
+
135
+ Buildchain dogfoods its observability toolkit in this lane. The standalone
136
+ builder writes API-generated events, while the workflow uses `buildchain mark`,
137
+ `buildchain span`, `buildchain verify observability-log`, and `buildchain log
138
+ summary`; the event logs and summaries are published as release passport assets.
139
+
140
+ Verify and explain release passports:
141
+
142
+ ```bash
143
+ buildchain verify release-passport .buildchain/release-passport/buildchain.release.json
144
+ buildchain explain release --passport .buildchain/release-passport/buildchain.release.json --for agent --json
145
+ buildchain inspect release --passport .buildchain/release-passport/buildchain.release.json
146
+ ```
147
+
148
+ The verifier fails closed when required protocol files are absent, artifacts are
149
+ not covered by evidence, or digests disagree. The explanation output is shaped
150
+ for agents: trust, completeness, impact, recovery route, and next action.
151
+
114
152
  `buildchain release --dry-run` explains the release-line state machine before a
115
153
  maintainer opens or merges a channel PR:
116
154
 
@@ -0,0 +1,99 @@
1
+ # Release Passport and Binary Distribution
2
+
3
+ Buildchain v2.2 adds a GitHub-native release passport protocol for binary and
4
+ multi-artifact products. The protocol is meant for projects that want release
5
+ evidence without migrating their whole CI/CD system to Buildchain.
6
+
7
+ ## Contract
8
+
9
+ The release passport surface is a welded contract. Additive fields are allowed;
10
+ breaking semantic changes require a new major line.
11
+
12
+ P0 protocol artifacts:
13
+
14
+ - `product-mechanism.json`
15
+ - `buildchain.release.json`
16
+ - `artifact-evidence.json`
17
+ - `impact.json`
18
+ - `agent-index.json`
19
+ - `llms.txt`
20
+
21
+ `buildchain.release.json` is the first file an agent should read. It points to
22
+ artifact evidence, impact, recovery, product mechanism, and agent index facts.
23
+ Buildchain's own binary lane also publishes observability artifacts generated by
24
+ the Buildchain logging API and CLI:
25
+
26
+ - `buildchain-log-events-<platform>.jsonl`
27
+ - `buildchain-log-summary-<platform>.json`
28
+ - `buildchain-log-events-passport.jsonl`
29
+ - `buildchain-log-summary-passport.json`
30
+
31
+ ## Runner Policy
32
+
33
+ Production binary distribution should use GitHub-hosted runners by default:
34
+
35
+ - `ubuntu-24.04`
36
+ - `macos-latest`
37
+ - `windows-2022`
38
+
39
+ This keeps the public release path easy for other projects to reproduce.
40
+ Self-hosted runners remain compatibility fixtures: they prove that the protocol
41
+ does not depend on GitHub-hosted images, but they are not the default public
42
+ distribution lane.
43
+
44
+ The protocol records runner facts in `artifact-evidence.json`; it does not
45
+ require a specific runner class.
46
+
47
+ ## CLI
48
+
49
+ Generate a local release passport bundle from release assets:
50
+
51
+ ```bash
52
+ buildchain collect github-release \
53
+ --tag v2.2.0 \
54
+ --repository kungfu-systems/buildchain \
55
+ --assets-dir dist \
56
+ --output-dir .buildchain/release-passport
57
+ ```
58
+
59
+ Verify a release passport:
60
+
61
+ ```bash
62
+ buildchain verify release-passport .buildchain/release-passport/buildchain.release.json
63
+ ```
64
+
65
+ Explain a release to an agent:
66
+
67
+ ```bash
68
+ buildchain explain release \
69
+ --passport .buildchain/release-passport/buildchain.release.json \
70
+ --for agent \
71
+ --json
72
+ ```
73
+
74
+ The verifier fails closed when a passport omits artifacts, omits evidence, has
75
+ digest mismatches, or misses required protocol files.
76
+
77
+ ## Binary Distribution
78
+
79
+ Initial binary distribution stays lightweight:
80
+
81
+ - GitHub Release assets.
82
+ - `checksums.txt`.
83
+ - release passport artifacts.
84
+ - install scripts and Homebrew tap fixtures after the passport is reliable.
85
+
86
+ Heavy package manager channels such as apt, yum, winget, choco, Scoop, mise, or
87
+ asdf are out of the P0/P1 scope until there is real external demand.
88
+
89
+ Standalone binaries are a distribution shape, not a second implementation. The
90
+ source of truth remains the Node/ESM CLI and core library.
91
+
92
+ The standalone binary builder imports `@kungfu-tech/buildchain/logging` directly
93
+ and records setup, SEA blob generation, injection, signing, archiving, manifest,
94
+ and evidence phases. The GitHub workflow wraps the same build and passport
95
+ steps with `buildchain mark`, `buildchain span`,
96
+ `buildchain verify observability-log`, and `buildchain log summary`. Logging is
97
+ a hard release gate: missing events, error events, or missing required phases
98
+ fail the job before assets are uploaded. The verified logs are release assets
99
+ and are covered by the release passport digest checks.
@@ -0,0 +1,57 @@
1
+ # Buildchain Versioning
2
+
3
+ Buildchain uses semantic version lines to describe public contracts, not only
4
+ code size. A release can be small in diff size and still open a new minor line
5
+ when it adds a durable surface that consumers, workflows, or agents can depend
6
+ on.
7
+
8
+ ## Lines
9
+
10
+ | Line | Meaning |
11
+ | --- | --- |
12
+ | Patch | Compatible fix, hardening, documentation correction, or implementation repair inside an existing surface. |
13
+ | Minor | New compatible welded surface: reusable workflow output, CLI command family, config protocol, published subpath, evidence file, runner contract, or agent-readable artifact. |
14
+ | Major | Breaking semantic change, removed stable surface, changed branch/tag governance, or incompatible protocol rewrite. |
15
+
16
+ Kungfu minor lines are long-lived trains. `v2.0`, `v2.1`, and `v2.2` can each
17
+ receive many patch releases. The major ref, such as `v2`, points at the
18
+ selected stable major entrypoint; the minor ref, such as `v2.2`, points at the
19
+ latest stable production patch for that minor line.
20
+
21
+ ## Welded Surfaces
22
+
23
+ These surfaces require at least a minor bump when first introduced:
24
+
25
+ - reusable workflow inputs, outputs, and artifact contracts;
26
+ - public CLI command families and their machine-readable JSON shapes;
27
+ - public npm exports such as `@kungfu-tech/buildchain/logging`;
28
+ - config protocols such as `buildchain.toml`;
29
+ - release governance state machines and protected ref semantics;
30
+ - release evidence contracts such as passport, artifact evidence, impact
31
+ ledger, and agent index files;
32
+ - binary distribution shapes that users can install or automate against.
33
+
34
+ Additive fields inside an existing welded surface can be patch releases when
35
+ old consumers continue to work and validation remains stricter, not looser.
36
+ Removing fields, changing meanings, weakening trust gates, or changing the
37
+ expected ref flow requires a major bump.
38
+
39
+ ## Decision Log
40
+
41
+ | Date | Decision | Line | Reason |
42
+ | --- | --- | --- | --- |
43
+ | 2026-07-02 | Buildchain toolkit observability is a minor surface. | `v2.1` | It adds the public logging SDK, CLI observability commands, and package subpaths that consumers can import. |
44
+ | 2026-07-02 | Release passport and binary distribution are a minor surface. | `v2.2` | They add agent-readable release passport files, artifact evidence, impact ledger, agent index, GitHub Release collection and verification commands, and standalone binary assets. |
45
+
46
+ ## Runner Policy
47
+
48
+ The `v2.2` binary distribution lane uses GitHub-hosted runners for production
49
+ assets because that is the easiest release path for external users to reproduce:
50
+
51
+ - `ubuntu-24.04`
52
+ - `macos-latest`
53
+ - `windows-2022`
54
+
55
+ Self-hosted runners remain compatibility fixtures. They prove Buildchain's
56
+ protocol does not depend on GitHub-hosted images, but they do not define the
57
+ public binary distribution path.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungfu-tech/buildchain",
3
- "version": "2.1.1-alpha.0",
3
+ "version": "2.2.0-alpha.1",
4
4
  "private": false,
5
5
  "description": "Kungfu Buildchain reusable workflows, release governance, and CLI tooling.",
6
6
  "repository": "https://github.com/kungfu-systems/buildchain",
@@ -14,6 +14,7 @@
14
14
  ".": "./packages/core/index.js",
15
15
  "./core": "./packages/core/index.js",
16
16
  "./logging": "./packages/core/logging.js",
17
+ "./release-passport": "./packages/core/release-passport.js",
17
18
  "./package.json": "./package.json"
18
19
  },
19
20
  "files": [
@@ -31,9 +32,11 @@
31
32
  "docs/migration-inventory.md",
32
33
  "docs/ownership.md",
33
34
  "docs/publish-transaction.md",
35
+ "docs/release-passport.md",
34
36
  "docs/release-flow.md",
35
37
  "docs/release-governance.md",
36
38
  "docs/reusable-build-surface.md",
39
+ "docs/versioning.md",
37
40
  "docs/web-surface-deployments.md",
38
41
  "actions/*/README.md",
39
42
  "fixtures/*/README.md"
@@ -50,4 +50,25 @@ export {
50
50
  readBuildchainLogEvents,
51
51
  redactBuildchainLogAttributes,
52
52
  summarizeBuildchainLogEvents,
53
+ verifyBuildchainLogEvents,
53
54
  } from "./logging.js";
55
+
56
+ export {
57
+ AGENT_INDEX_CONTRACT,
58
+ ARTIFACT_EVIDENCE_CONTRACT,
59
+ IMPACT_LEDGER_CONTRACT,
60
+ PRODUCT_MECHANISM_CONTRACT,
61
+ RELEASE_CHECK_REPORT_CONTRACT,
62
+ RELEASE_PASSPORT_CONTRACT,
63
+ collectGitHubReleasePassport,
64
+ createArtifactEvidence,
65
+ createReleaseCheckReport,
66
+ createReleasePassport,
67
+ explainReleasePassport,
68
+ makeReleasePassportFixtureAssets,
69
+ readJsonFromLocation,
70
+ sha256File,
71
+ sha256Text,
72
+ validateKnownReleasePassportContracts,
73
+ verifyReleasePassport,
74
+ } from "./release-passport.js";
@@ -111,6 +111,90 @@ export function summarizeBuildchainLogEvents(input = {}) {
111
111
  };
112
112
  }
113
113
 
114
+ function listOption(value) {
115
+ if (Array.isArray(value)) {
116
+ return value.flatMap((entry) => listOption(entry));
117
+ }
118
+ if (typeof value !== "string") {
119
+ return [];
120
+ }
121
+ return value.split(",").map((entry) => entry.trim()).filter(Boolean);
122
+ }
123
+
124
+ function issue(level, code, message) {
125
+ return { level, code, message };
126
+ }
127
+
128
+ export function verifyBuildchainLogEvents({
129
+ path: filePath = "",
130
+ events: inputEvents = undefined,
131
+ minEvents = 1,
132
+ allowErrors = false,
133
+ requirePhases = [],
134
+ requireComponents = [],
135
+ requireEvents = [],
136
+ } = {}) {
137
+ const issues = [];
138
+ let events = [];
139
+ try {
140
+ events = Array.isArray(inputEvents) ? inputEvents : readBuildchainLogEvents(filePath);
141
+ } catch (error) {
142
+ issues.push(issue("error", "log.read.failed", error.message));
143
+ }
144
+ if (!events.length) {
145
+ issues.push(issue("error", "log.events.empty", `no Buildchain log events found at ${filePath || "<memory>"}`));
146
+ }
147
+ for (const [index, event] of events.entries()) {
148
+ if (event?.contract !== BUILDCHAIN_LOG_EVENT_CONTRACT) {
149
+ issues.push(issue("error", "log.contract.invalid", `event ${index} has invalid contract`));
150
+ }
151
+ for (const key of ["timestamp", "level", "source", "component", "event"]) {
152
+ if (!event?.[key]) {
153
+ issues.push(issue("error", "log.field.missing", `event ${index} is missing ${key}`));
154
+ }
155
+ }
156
+ }
157
+ const summary = summarizeBuildchainLogEvents(events);
158
+ const requiredEventCount = Number(minEvents || 0);
159
+ if (summary.eventCount < requiredEventCount) {
160
+ issues.push(issue("error", "log.events.too_few", `expected at least ${requiredEventCount} events, found ${summary.eventCount}`));
161
+ }
162
+ if (!allowErrors && summary.errorCount > 0) {
163
+ issues.push(issue("error", "log.errors.present", `expected no error events, found ${summary.errorCount}`));
164
+ }
165
+ for (const phase of listOption(requirePhases)) {
166
+ if (!summary.phases[phase]) {
167
+ issues.push(issue("error", "log.phase.missing", `required phase missing: ${phase}`));
168
+ }
169
+ }
170
+ for (const component of listOption(requireComponents)) {
171
+ if (!summary.components[component]) {
172
+ issues.push(issue("error", "log.component.missing", `required component missing: ${component}`));
173
+ }
174
+ }
175
+ const eventNames = new Set(events.map((event) => event.event).filter(Boolean));
176
+ for (const eventName of listOption(requireEvents)) {
177
+ if (!eventNames.has(eventName)) {
178
+ issues.push(issue("error", "log.event.missing", `required event missing: ${eventName}`));
179
+ }
180
+ }
181
+ return {
182
+ schemaVersion: 1,
183
+ contract: "kungfu-buildchain-log-verification",
184
+ ok: !issues.some((entry) => entry.level === "error"),
185
+ path: filePath,
186
+ summary,
187
+ requirements: {
188
+ minEvents: requiredEventCount,
189
+ allowErrors,
190
+ phases: listOption(requirePhases),
191
+ components: listOption(requireComponents),
192
+ events: listOption(requireEvents),
193
+ },
194
+ issues,
195
+ };
196
+ }
197
+
114
198
  export function createBuildchainLogger(options = {}) {
115
199
  const cwd = path.resolve(options.cwd || process.cwd());
116
200
  const runningInActions = process.env.GITHUB_ACTIONS === "true";