@kungfu-tech/buildchain 2.14.7-alpha.0 → 2.14.8-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.
@@ -4,7 +4,7 @@
4
4
  "product": {
5
5
  "name": "Buildchain",
6
6
  "package": "@kungfu-tech/buildchain",
7
- "version": "2.14.7-alpha.0",
7
+ "version": "2.14.8-alpha.0",
8
8
  "repository": "https://github.com/kungfu-systems/buildchain"
9
9
  },
10
10
  "majorLine": "v2",
@@ -170,7 +170,7 @@
170
170
  "GitHub Release passport and evidence publication is delegated to promote-buildchain-ref after the semver release transaction completes"
171
171
  ],
172
172
  "breakingDigest": "sha256:1f476fb9a4854b362c4b24cef89a3c7675d7d63baf3bc828d7b8ae7d2787cead",
173
- "auditDigest": "sha256:d95a2804b117b1aae3b9ff415c1288a654caeac147c731d8f639484c625f6089"
173
+ "auditDigest": "sha256:37e44f90dd433606cd01cf4c0b2911a7d2b4f694898c1629dcd905e974ad0c53"
174
174
  },
175
175
  {
176
176
  "contractVersion": 1,
@@ -212,7 +212,7 @@
212
212
  "promotion reuses PR-stage release-candidate artifacts and does not run the heavy native build matrix"
213
213
  ],
214
214
  "breakingDigest": "sha256:aa30f22e3af0a89841310bdbdc900844dd95a66974db173fa140a71bbd7e82c0",
215
- "auditDigest": "sha256:0588d4762f5e8f4d73ed15bd19c5d5fb03302942ce6f1f6c2e82f341618e4afc"
215
+ "auditDigest": "sha256:9f4d8d0d8837ef2d06352113c1262f2611e6460faea95d969142385e964306e2"
216
216
  },
217
217
  {
218
218
  "contractVersion": 1,
@@ -582,7 +582,7 @@
582
582
  "manual entries carry source file digests so downstream sites and agents can detect stale hand-written documentation"
583
583
  ],
584
584
  "breakingDigest": "sha256:7d0d2819e3a3e72989d9c57b5efe9d0bc0a79bc0f2c82a0c7b9d6c5a211a91f2",
585
- "auditDigest": "sha256:d0c39e3083979c259cf2a90a872ae397231fa3d0d798990ccf9b80eeade82673"
585
+ "auditDigest": "sha256:af5c2479bf66f24b75a8625ccbbc60830e83fc59d516a27ae3c6c84525489792"
586
586
  },
587
587
  {
588
588
  "contractVersion": 1,
@@ -2700,7 +2700,7 @@
2700
2700
  "missing receipts are non-qualifying and must not be represented as a successful controller run"
2701
2701
  ],
2702
2702
  "breakingDigest": "sha256:015d8de793adbd4c539416be7d7512e18cc92097e629b0203fa06d2c183e98bd",
2703
- "auditDigest": "sha256:0588d4762f5e8f4d73ed15bd19c5d5fb03302942ce6f1f6c2e82f341618e4afc"
2703
+ "auditDigest": "sha256:9f4d8d0d8837ef2d06352113c1262f2611e6460faea95d969142385e964306e2"
2704
2704
  },
2705
2705
  {
2706
2706
  "contractVersion": 1,
@@ -2844,5 +2844,5 @@
2844
2844
  }
2845
2845
  ],
2846
2846
  "compatibilityDigest": "sha256:edc3653e5cb34efd97065a6941db16140bbf375a565bbcf329d9d795bb07676f",
2847
- "contractDigest": "sha256:b9359a159a5cdc984973615f560800528534c425c8572449446aaf3f1d57457a"
2847
+ "contractDigest": "sha256:b82f5788b3deb02712569d80859a0c38eee8023a84f9e6d252f6e87862c1bfe1"
2848
2848
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-site-bundle",
4
- "generatedAt": "2026-07-21T08:15:55.468Z",
5
- "publishedAt": "2026-07-21T08:15:55.468Z",
4
+ "generatedAt": "2026-07-21T14:45:28.549Z",
5
+ "publishedAt": "2026-07-21T14:45:28.549Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "85d51ae82b43195d5c7c7d92d6ecb63f054a4bf2",
22
+ "sourceRevision": "0d9c71d965ec0825af481f5789c5a138c7dad5dc",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "package": {
39
39
  "name": "@kungfu-tech/buildchain",
40
- "version": "2.14.7-alpha.0",
40
+ "version": "2.14.8-alpha.0",
41
41
  "versionSource": "package.json#version"
42
42
  },
43
43
  "source": {
@@ -1330,7 +1330,7 @@
1330
1330
  ],
1331
1331
  "maturity": "stable",
1332
1332
  "sourcePath": "docs/release-candidate.md",
1333
- "digest": "sha256:81463a74dfe3d337768797b5b47f8cb3a22389312a917447f9b0e6096ab4f90a",
1333
+ "digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99",
1334
1334
  "headings": [
1335
1335
  {
1336
1336
  "level": 1,
@@ -1338,7 +1338,7 @@
1338
1338
  "anchor": "release-candidate-passport"
1339
1339
  }
1340
1340
  ],
1341
- "markdown": "# Release Candidate Passport\n\nThe release-candidate passport is the pre-promotion evidence contract produced\nafter a reusable build matrix succeeds and before any publish-gate side effects\nrun. It is different from the release passport:\n\n- `release-candidate-passport.json` proves which source SHA, channel, runtime,\n workflow run, and platform artifacts were verified before promotion.\n- `buildchain.release.json` is generated after publish finalization and remains\n the durable audit entrypoint for the published release.\n\nEnable it on the reusable build workflow:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n artifact-name: libnode\n release-candidate: true\n publish-channel: alpha\n publish-source-ref: publish-gate/alpha/v22/v22.22/22.22.3-kf.3-alpha.7\n```\n\nWhen the platform matrix and aggregate summaries complete, Buildchain uploads:\n\n```text\n<artifact-name>-release-candidate-<publish-source-sha>\n```\n\nThe passport contract is `kungfu-buildchain-release-candidate-passport`. It\ncontains:\n\n- repository and pull request context;\n- target channel, target ref, and product version or a non-publish\n `source-<shortSha>` candidate label;\n- source head SHA, merge ref SHA, and the Git `HEAD^{tree}` SHA for PR merge\n equivalence after the channel PR lands;\n- Buildchain runtime ref/SHA and workflow shell ref;\n- workflow run id/attempt/url;\n- normalized platform matrix and artifact summaries;\n- the hash of the aggregate `build-summary.json`.\n\nPromotion workflows that should not rebuild artifacts can enable:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: alpha/v22/v22.22\n promote-only-release-candidate: \"true\"\n release-candidate-passport-path: .buildchain/artifacts/release-candidate-passport.json\n release-candidate-build-summary-path: .buildchain/artifacts/build-summary.json\n```\n\nWith `promote-only-release-candidate: \"true\"`, promotion fails before\nversion-state, publish transaction, tag, or branch side effects when the\npassport does not match the repository, channel, source identity, platform\nmatrix, or build-summary hash. Source identity accepts the exact PR source SHA,\nthe PR merge ref SHA, or an exact Git tree match with the promoted channel HEAD;\nthis keeps post-merge channel commits strict without forcing a rebuild. The\nBuildchain-owned promotion workflow resolves the matching same-repository\nmerged channel PR and downloads its PR-stage RC passport automatically before\npromotion starts. The consumer wrapper defaults to a PR-stage workflow file\nnamed `build.yml` with display name `Build`, and filters the RC passport and\nbuild summary by the configured `artifact-name` before promotion. It also\ndownloads payload artifacts from the same PR-stage run, validates the required\npayload count, passes downloaded platform manifests into the release passport,\nand either forwards an explicit `publish-required-artifacts-json` value or\ngenerates one before calling `promote-buildchain-ref`. Before that call, the\nwrapper creates or updates `publish-gate/{alpha,release,major}` to the\npromotion channel commit and passes that ref, target SHA, and `locked=true` to\nthe promote action with `require-publish-source-lock: \"true\"`. Consumers using\nfloating `@v2` therefore get publish-side source-lock drift protection without\ncopying resolver or promote YAML. The default npm path\ngenerates that requirement list from the downloaded `.tgz` payloads themselves:\nBuildchain reads `package/package.json` inside each tarball for the real scoped\npackage name and version, computes npm-style `sha512-...` integrity over the\ntarball bytes, marks `publish-package-main` as `role: main`, and marks every\nother package as `role: platform`. Consumer workflows therefore stay\ndeclarative and do not need their own artifact download or publish-evidence\ngeneration scripts.\n\nBecause a channel merge can trigger promotion before its PR-stage matrix has\nfinished uploading evidence, the resolver waits up to ten minutes for the exact\nmerged PR's successful workflow run and paired artifacts. Polling remains bound\nto the PR/head identity; timeout or a sibling run still fails closed.\n\nBy default, the wrapper forwards GitHub Release publication to the underlying\n`promote-buildchain-ref` semver model. Once the release transaction is complete,\nthe action creates or updates the public GitHub Release, applies\nprerelease/latest metadata from the semver tag, and uploads the publish evidence\nfile together with the generated release passport assets. This keeps\nnpm/registry publication, Buildchain release passport persistence, and\n`release.published` propagation in one declarative reusable workflow. Consumers\nthat do not publish GitHub Releases can opt out with `github-release: false`.\nFor anchored/manual package releases, the public GitHub Release tag defaults to\n`v<publishedVersion>` while the internal transaction exact tag remains recorded\nin the release passport.\n\nProducts that publish KFD release trust evidence can keep that path declarative\ntoo. Pass KFD-1 self contract witnesses, KFD-2 public claim files, and KFD-3\npre-build/artifact evidence into the wrapper:\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n with:\n buildchain-channel: auto\n buildchain-alpha-contract-lock-path: .buildchain/alpha-contract-lock.json\n buildchain-stable-contract-lock-path: .buildchain/contract-lock.json\n channel: alpha\n artifact-name: libnode\n release-passport-kfd-1-witness-jsons: .buildchain/kfd/kfd-1/standard-contract.witness.json\n release-passport-kfd-2-claim-jsons: .buildchain/kfd/kfd-2/release-claims.json\n release-passport-kfd-3-prebuild-witness-jsons: .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n release-passport-kfd-3-artifact-verify-command: kungfu agent verify --json\n```\n\nBuildchain forwards those declarations into `promote-buildchain-ref`, verifies\nKFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and\ncompares KFD-3 declared shipped public surfaces with artifact-exposed public\nsurfaces. The release passport records the results under `kfd-1`, `kfd-2`, and\nthe KFD-provided `kfd-3` section.\n\nManaged consumers may also ask the promotion wrapper to assemble sealed\npublication evidence from the exact release candidate instead of producing\nshort-lived admission JSON in repository-specific workflow code:\n\n```yaml\n publication-auto-admission: true\n publication-auto-no-gate: true\n publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml\n publication-product: Example Product\n publication-target: npm:@example/product\n publication-package-name: \"@example/product\"\n```\n\n`publication-auto-no-gate` is an explicit consumer decision, not a default. A\nconsumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`\ninstead. Buildchain still requires caller-owned RC evidence, an exact authority\nruntime and source SHA, a repository-local publisher workflow, matching npm\ntarget/package identity, and a qualifying control-plane audit.\n\nA consumer that owns additional product qualification semantics can opt in to\nthe sealed handoff without teaching Buildchain those semantics:\n\n```yaml\n publication-consumer-predicate-id: kungfu.release-admission/v1\n publication-consumer-qualification-command: node scripts/qualify-release.mjs\n```\n\nThe command reads `BUILDCHAIN_PUBLICATION_CAPABILITY_PATH` and\n`BUILDCHAIN_PUBLICATION_GATE_AGGREGATE_PATH`, evaluates the complete aggregate,\nand writes a decision JSON document to\n`BUILDCHAIN_PUBLICATION_QUALIFICATION_RESULT_PATH`. It also receives\n`BUILDCHAIN_PUBLICATION_PREDICATE_ID` and\n`BUILDCHAIN_PUBLICATION_PREDICATE_DIGEST`. The separate qualification job has\nno write or OIDC permission and does not inherit publication secrets. A\nsuccessful deterministic receipt is rechecked by the provider action\nimmediately before mutation. Omitting both inputs preserves the existing\nconsumer contract; supplying only one fails closed."
1341
+ "markdown": "# Release Candidate Passport\n\nThe release-candidate passport is the pre-promotion evidence contract produced\nafter a reusable build matrix succeeds and before any publish-gate side effects\nrun. It is different from the release passport:\n\n- `release-candidate-passport.json` proves which source SHA, channel, runtime,\n workflow run, and platform artifacts were verified before promotion.\n- `buildchain.release.json` is generated after publish finalization and remains\n the durable audit entrypoint for the published release.\n\nEnable it on the reusable build workflow:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n artifact-name: libnode\n release-candidate: true\n publish-channel: alpha\n publish-source-ref: publish-gate/alpha/v22/v22.22/22.22.3-kf.3-alpha.7\n```\n\nWhen the platform matrix and aggregate summaries complete, Buildchain uploads:\n\n```text\n<artifact-name>-release-candidate-<publish-source-sha>\n```\n\nThe passport contract is `kungfu-buildchain-release-candidate-passport`. It\ncontains:\n\n- repository and pull request context;\n- target channel, target ref, and product version or a non-publish\n `source-<shortSha>` candidate label;\n- source head SHA, merge ref SHA, and the Git `HEAD^{tree}` SHA for PR merge\n equivalence after the channel PR lands;\n- Buildchain runtime ref/SHA and workflow shell ref;\n- workflow run id/attempt/url;\n- normalized platform matrix and artifact summaries;\n- the hash of the aggregate `build-summary.json`.\n\nPromotion workflows that should not rebuild artifacts can enable:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: alpha/v22/v22.22\n promote-only-release-candidate: \"true\"\n release-candidate-passport-path: .buildchain/artifacts/release-candidate-passport.json\n release-candidate-build-summary-path: .buildchain/artifacts/build-summary.json\n```\n\nWith `promote-only-release-candidate: \"true\"`, promotion fails before\nversion-state, publish transaction, tag, or branch side effects when the\npassport does not match the repository, channel, source identity, platform\nmatrix, or build-summary hash. Source identity accepts the exact PR source SHA,\nthe PR merge ref SHA, or an exact Git tree match with the promoted channel HEAD;\nthis keeps post-merge channel commits strict without forcing a rebuild. The\nBuildchain-owned promotion workflow resolves the matching same-repository\nmerged channel PR and downloads its PR-stage RC passport automatically before\npromotion starts. The consumer wrapper defaults to a PR-stage workflow file\nnamed `build.yml` with display name `Build`, and filters the RC passport and\nbuild summary by the configured `artifact-name` before promotion. It also\ndownloads payload artifacts from the same PR-stage run, validates the required\npayload count, passes downloaded platform manifests into the release passport,\nand either forwards an explicit `publish-required-artifacts-json` value or\ngenerates one before calling `promote-buildchain-ref`. Before that call, the\nwrapper creates or updates `publish-gate/{alpha,release,major}` to the\npromotion channel commit and passes that ref, target SHA, and `locked=true` to\nthe promote action with `require-publish-source-lock: \"true\"`. Consumers using\nfloating `@v2` therefore get publish-side source-lock drift protection without\ncopying resolver or promote YAML. The default npm path\ngenerates that requirement list from the downloaded `.tgz` payloads themselves:\nBuildchain reads `package/package.json` inside each tarball for the real scoped\npackage name and version, computes npm-style `sha512-...` integrity over the\ntarball bytes, marks `publish-package-main` as `role: main`, and marks every\nother package as `role: platform`. Consumer workflows therefore stay\ndeclarative and do not need their own artifact download or publish-evidence\ngeneration scripts.\n\nBecause a channel merge can trigger promotion before its PR-stage matrix has\nfinished uploading evidence, the resolver waits up to ten minutes for the exact\nmerged PR's successful workflow run and paired artifacts. Polling remains bound\nto the PR/head identity; timeout or a sibling run still fails closed.\n\nThe public promotion router preserves the requested `vN` or `vN-alpha` ref as\naudit metadata, but binds the router to GitHub's selected reusable-workflow SHA\nand resolves each remaining floating shell/runtime ref exactly once. Every\nlater checkout and delegated promotion receives those immutable SHAs, so a\nchannel tag moving during the run cannot mix two Buildchain revisions.\n\nIf a promote-only run failed after its consumer commit was already merged and\nits PR-stage artifacts were built, rerun the complete failed workflow after the\nfixed Buildchain channel is published:\n\n```bash\ngh run rerun <run-id> --repo <owner>/<consumer>\n```\n\nUse a complete rerun, not `--failed`, so GitHub resolves the reusable workflow\nagain. The release-candidate resolver reuses the existing candidate artifacts,\nand the durable publication transaction makes the recovery idempotent; do not\nopen a replacement consumer PR or rebuild the native matrix solely for this\nrouter failure.\n\nBy default, the wrapper forwards GitHub Release publication to the underlying\n`promote-buildchain-ref` semver model. Once the release transaction is complete,\nthe action creates or updates the public GitHub Release, applies\nprerelease/latest metadata from the semver tag, and uploads the publish evidence\nfile together with the generated release passport assets. This keeps\nnpm/registry publication, Buildchain release passport persistence, and\n`release.published` propagation in one declarative reusable workflow. Consumers\nthat do not publish GitHub Releases can opt out with `github-release: false`.\nFor anchored/manual package releases, the public GitHub Release tag defaults to\n`v<publishedVersion>` while the internal transaction exact tag remains recorded\nin the release passport.\n\nProducts that publish KFD release trust evidence can keep that path declarative\ntoo. Pass KFD-1 self contract witnesses, KFD-2 public claim files, and KFD-3\npre-build/artifact evidence into the wrapper:\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n with:\n buildchain-channel: auto\n buildchain-alpha-contract-lock-path: .buildchain/alpha-contract-lock.json\n buildchain-stable-contract-lock-path: .buildchain/contract-lock.json\n channel: alpha\n artifact-name: libnode\n release-passport-kfd-1-witness-jsons: .buildchain/kfd/kfd-1/standard-contract.witness.json\n release-passport-kfd-2-claim-jsons: .buildchain/kfd/kfd-2/release-claims.json\n release-passport-kfd-3-prebuild-witness-jsons: .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n release-passport-kfd-3-artifact-verify-command: kungfu agent verify --json\n```\n\nBuildchain forwards those declarations into `promote-buildchain-ref`, verifies\nKFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and\ncompares KFD-3 declared shipped public surfaces with artifact-exposed public\nsurfaces. The release passport records the results under `kfd-1`, `kfd-2`, and\nthe KFD-provided `kfd-3` section.\n\nManaged consumers may also ask the promotion wrapper to assemble sealed\npublication evidence from the exact release candidate instead of producing\nshort-lived admission JSON in repository-specific workflow code:\n\n```yaml\n publication-auto-admission: true\n publication-auto-no-gate: true\n publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml\n publication-product: Example Product\n publication-target: npm:@example/product\n publication-package-name: \"@example/product\"\n```\n\n`publication-auto-no-gate` is an explicit consumer decision, not a default. A\nconsumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`\ninstead. Buildchain still requires caller-owned RC evidence, an exact authority\nruntime and source SHA, a repository-local publisher workflow, matching npm\ntarget/package identity, and a qualifying control-plane audit.\n\nA consumer that owns additional product qualification semantics can opt in to\nthe sealed handoff without teaching Buildchain those semantics:\n\n```yaml\n publication-consumer-predicate-id: kungfu.release-admission/v1\n publication-consumer-qualification-command: node scripts/qualify-release.mjs\n```\n\nThe command reads `BUILDCHAIN_PUBLICATION_CAPABILITY_PATH` and\n`BUILDCHAIN_PUBLICATION_GATE_AGGREGATE_PATH`, evaluates the complete aggregate,\nand writes a decision JSON document to\n`BUILDCHAIN_PUBLICATION_QUALIFICATION_RESULT_PATH`. It also receives\n`BUILDCHAIN_PUBLICATION_PREDICATE_ID` and\n`BUILDCHAIN_PUBLICATION_PREDICATE_DIGEST`. The separate qualification job has\nno write or OIDC permission and does not inherit publication secrets. A\nsuccessful deterministic receipt is rechecked by the provider action\nimmediately before mutation. Omitting both inputs preserves the existing\nconsumer contract; supplying only one fails closed."
1342
1342
  },
1343
1343
  {
1344
1344
  "id": "manual:release-flow",
@@ -2569,7 +2569,7 @@
2569
2569
  "path": "docs/release-candidate.md",
2570
2570
  "plane": "verify",
2571
2571
  "exists": true,
2572
- "digest": "sha256:81463a74dfe3d337768797b5b47f8cb3a22389312a917447f9b0e6096ab4f90a"
2572
+ "digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99"
2573
2573
  },
2574
2574
  {
2575
2575
  "id": "release-propagation",
@@ -21,7 +21,7 @@
21
21
  "contract": "kungfu-buildchain-public-surface-reverse-audit",
22
22
  "path": "dist/site/public-surface-audit.json",
23
23
  "status": "passed",
24
- "sha256": "718d09d60b1d04d3c93b2e0021131e23a1462a2de080c968b03dfa0ec0a03a00",
24
+ "sha256": "74cb3ec8e051754901cbdcffd06fb7fa7579c4a1ba27488e5e5340abf5497952",
25
25
  "summary": {
26
26
  "cliCommandCount": 84,
27
27
  "workflowCount": 51,
@@ -228,7 +228,7 @@
228
228
  "contract": "kungfu-buildchain-public-surface-reverse-audit",
229
229
  "path": "dist/site/public-surface-audit.json",
230
230
  "status": "passed",
231
- "sha256": "718d09d60b1d04d3c93b2e0021131e23a1462a2de080c968b03dfa0ec0a03a00",
231
+ "sha256": "74cb3ec8e051754901cbdcffd06fb7fa7579c4a1ba27488e5e5340abf5497952",
232
232
  "summary": {
233
233
  "cliCommandCount": 84,
234
234
  "workflowCount": 51,
@@ -4,7 +4,7 @@
4
4
  "product": {
5
5
  "id": "kungfu-tech-buildchain",
6
6
  "name": "@kungfu-tech/buildchain",
7
- "version": "2.14.7-alpha.0",
7
+ "version": "2.14.8-alpha.0",
8
8
  "repository": "https://github.com/kungfu-systems/buildchain"
9
9
  },
10
10
  "source": {
@@ -79,7 +79,7 @@
79
79
  "title": "Release Candidate Passport",
80
80
  "path": "docs/release-candidate.md",
81
81
  "plane": "verify",
82
- "digest": "sha256:81463a74dfe3d337768797b5b47f8cb3a22389312a917447f9b0e6096ab4f90a",
82
+ "digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99",
83
83
  "capabilityGroup": "reusable-build",
84
84
  "audience": [
85
85
  "release-operator",
@@ -1264,7 +1264,7 @@
1264
1264
  ],
1265
1265
  "maturity": "stable",
1266
1266
  "sourcePath": "docs/release-candidate.md",
1267
- "digest": "sha256:81463a74dfe3d337768797b5b47f8cb3a22389312a917447f9b0e6096ab4f90a",
1267
+ "digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99",
1268
1268
  "headings": [
1269
1269
  {
1270
1270
  "level": 1,
@@ -1272,7 +1272,7 @@
1272
1272
  "anchor": "release-candidate-passport"
1273
1273
  }
1274
1274
  ],
1275
- "markdown": "# Release Candidate Passport\n\nThe release-candidate passport is the pre-promotion evidence contract produced\nafter a reusable build matrix succeeds and before any publish-gate side effects\nrun. It is different from the release passport:\n\n- `release-candidate-passport.json` proves which source SHA, channel, runtime,\n workflow run, and platform artifacts were verified before promotion.\n- `buildchain.release.json` is generated after publish finalization and remains\n the durable audit entrypoint for the published release.\n\nEnable it on the reusable build workflow:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n artifact-name: libnode\n release-candidate: true\n publish-channel: alpha\n publish-source-ref: publish-gate/alpha/v22/v22.22/22.22.3-kf.3-alpha.7\n```\n\nWhen the platform matrix and aggregate summaries complete, Buildchain uploads:\n\n```text\n<artifact-name>-release-candidate-<publish-source-sha>\n```\n\nThe passport contract is `kungfu-buildchain-release-candidate-passport`. It\ncontains:\n\n- repository and pull request context;\n- target channel, target ref, and product version or a non-publish\n `source-<shortSha>` candidate label;\n- source head SHA, merge ref SHA, and the Git `HEAD^{tree}` SHA for PR merge\n equivalence after the channel PR lands;\n- Buildchain runtime ref/SHA and workflow shell ref;\n- workflow run id/attempt/url;\n- normalized platform matrix and artifact summaries;\n- the hash of the aggregate `build-summary.json`.\n\nPromotion workflows that should not rebuild artifacts can enable:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: alpha/v22/v22.22\n promote-only-release-candidate: \"true\"\n release-candidate-passport-path: .buildchain/artifacts/release-candidate-passport.json\n release-candidate-build-summary-path: .buildchain/artifacts/build-summary.json\n```\n\nWith `promote-only-release-candidate: \"true\"`, promotion fails before\nversion-state, publish transaction, tag, or branch side effects when the\npassport does not match the repository, channel, source identity, platform\nmatrix, or build-summary hash. Source identity accepts the exact PR source SHA,\nthe PR merge ref SHA, or an exact Git tree match with the promoted channel HEAD;\nthis keeps post-merge channel commits strict without forcing a rebuild. The\nBuildchain-owned promotion workflow resolves the matching same-repository\nmerged channel PR and downloads its PR-stage RC passport automatically before\npromotion starts. The consumer wrapper defaults to a PR-stage workflow file\nnamed `build.yml` with display name `Build`, and filters the RC passport and\nbuild summary by the configured `artifact-name` before promotion. It also\ndownloads payload artifacts from the same PR-stage run, validates the required\npayload count, passes downloaded platform manifests into the release passport,\nand either forwards an explicit `publish-required-artifacts-json` value or\ngenerates one before calling `promote-buildchain-ref`. Before that call, the\nwrapper creates or updates `publish-gate/{alpha,release,major}` to the\npromotion channel commit and passes that ref, target SHA, and `locked=true` to\nthe promote action with `require-publish-source-lock: \"true\"`. Consumers using\nfloating `@v2` therefore get publish-side source-lock drift protection without\ncopying resolver or promote YAML. The default npm path\ngenerates that requirement list from the downloaded `.tgz` payloads themselves:\nBuildchain reads `package/package.json` inside each tarball for the real scoped\npackage name and version, computes npm-style `sha512-...` integrity over the\ntarball bytes, marks `publish-package-main` as `role: main`, and marks every\nother package as `role: platform`. Consumer workflows therefore stay\ndeclarative and do not need their own artifact download or publish-evidence\ngeneration scripts.\n\nBecause a channel merge can trigger promotion before its PR-stage matrix has\nfinished uploading evidence, the resolver waits up to ten minutes for the exact\nmerged PR's successful workflow run and paired artifacts. Polling remains bound\nto the PR/head identity; timeout or a sibling run still fails closed.\n\nBy default, the wrapper forwards GitHub Release publication to the underlying\n`promote-buildchain-ref` semver model. Once the release transaction is complete,\nthe action creates or updates the public GitHub Release, applies\nprerelease/latest metadata from the semver tag, and uploads the publish evidence\nfile together with the generated release passport assets. This keeps\nnpm/registry publication, Buildchain release passport persistence, and\n`release.published` propagation in one declarative reusable workflow. Consumers\nthat do not publish GitHub Releases can opt out with `github-release: false`.\nFor anchored/manual package releases, the public GitHub Release tag defaults to\n`v<publishedVersion>` while the internal transaction exact tag remains recorded\nin the release passport.\n\nProducts that publish KFD release trust evidence can keep that path declarative\ntoo. Pass KFD-1 self contract witnesses, KFD-2 public claim files, and KFD-3\npre-build/artifact evidence into the wrapper:\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n with:\n buildchain-channel: auto\n buildchain-alpha-contract-lock-path: .buildchain/alpha-contract-lock.json\n buildchain-stable-contract-lock-path: .buildchain/contract-lock.json\n channel: alpha\n artifact-name: libnode\n release-passport-kfd-1-witness-jsons: .buildchain/kfd/kfd-1/standard-contract.witness.json\n release-passport-kfd-2-claim-jsons: .buildchain/kfd/kfd-2/release-claims.json\n release-passport-kfd-3-prebuild-witness-jsons: .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n release-passport-kfd-3-artifact-verify-command: kungfu agent verify --json\n```\n\nBuildchain forwards those declarations into `promote-buildchain-ref`, verifies\nKFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and\ncompares KFD-3 declared shipped public surfaces with artifact-exposed public\nsurfaces. The release passport records the results under `kfd-1`, `kfd-2`, and\nthe KFD-provided `kfd-3` section.\n\nManaged consumers may also ask the promotion wrapper to assemble sealed\npublication evidence from the exact release candidate instead of producing\nshort-lived admission JSON in repository-specific workflow code:\n\n```yaml\n publication-auto-admission: true\n publication-auto-no-gate: true\n publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml\n publication-product: Example Product\n publication-target: npm:@example/product\n publication-package-name: \"@example/product\"\n```\n\n`publication-auto-no-gate` is an explicit consumer decision, not a default. A\nconsumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`\ninstead. Buildchain still requires caller-owned RC evidence, an exact authority\nruntime and source SHA, a repository-local publisher workflow, matching npm\ntarget/package identity, and a qualifying control-plane audit.\n\nA consumer that owns additional product qualification semantics can opt in to\nthe sealed handoff without teaching Buildchain those semantics:\n\n```yaml\n publication-consumer-predicate-id: kungfu.release-admission/v1\n publication-consumer-qualification-command: node scripts/qualify-release.mjs\n```\n\nThe command reads `BUILDCHAIN_PUBLICATION_CAPABILITY_PATH` and\n`BUILDCHAIN_PUBLICATION_GATE_AGGREGATE_PATH`, evaluates the complete aggregate,\nand writes a decision JSON document to\n`BUILDCHAIN_PUBLICATION_QUALIFICATION_RESULT_PATH`. It also receives\n`BUILDCHAIN_PUBLICATION_PREDICATE_ID` and\n`BUILDCHAIN_PUBLICATION_PREDICATE_DIGEST`. The separate qualification job has\nno write or OIDC permission and does not inherit publication secrets. A\nsuccessful deterministic receipt is rechecked by the provider action\nimmediately before mutation. Omitting both inputs preserves the existing\nconsumer contract; supplying only one fails closed."
1275
+ "markdown": "# Release Candidate Passport\n\nThe release-candidate passport is the pre-promotion evidence contract produced\nafter a reusable build matrix succeeds and before any publish-gate side effects\nrun. It is different from the release passport:\n\n- `release-candidate-passport.json` proves which source SHA, channel, runtime,\n workflow run, and platform artifacts were verified before promotion.\n- `buildchain.release.json` is generated after publish finalization and remains\n the durable audit entrypoint for the published release.\n\nEnable it on the reusable build workflow:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n artifact-name: libnode\n release-candidate: true\n publish-channel: alpha\n publish-source-ref: publish-gate/alpha/v22/v22.22/22.22.3-kf.3-alpha.7\n```\n\nWhen the platform matrix and aggregate summaries complete, Buildchain uploads:\n\n```text\n<artifact-name>-release-candidate-<publish-source-sha>\n```\n\nThe passport contract is `kungfu-buildchain-release-candidate-passport`. It\ncontains:\n\n- repository and pull request context;\n- target channel, target ref, and product version or a non-publish\n `source-<shortSha>` candidate label;\n- source head SHA, merge ref SHA, and the Git `HEAD^{tree}` SHA for PR merge\n equivalence after the channel PR lands;\n- Buildchain runtime ref/SHA and workflow shell ref;\n- workflow run id/attempt/url;\n- normalized platform matrix and artifact summaries;\n- the hash of the aggregate `build-summary.json`.\n\nPromotion workflows that should not rebuild artifacts can enable:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: alpha/v22/v22.22\n promote-only-release-candidate: \"true\"\n release-candidate-passport-path: .buildchain/artifacts/release-candidate-passport.json\n release-candidate-build-summary-path: .buildchain/artifacts/build-summary.json\n```\n\nWith `promote-only-release-candidate: \"true\"`, promotion fails before\nversion-state, publish transaction, tag, or branch side effects when the\npassport does not match the repository, channel, source identity, platform\nmatrix, or build-summary hash. Source identity accepts the exact PR source SHA,\nthe PR merge ref SHA, or an exact Git tree match with the promoted channel HEAD;\nthis keeps post-merge channel commits strict without forcing a rebuild. The\nBuildchain-owned promotion workflow resolves the matching same-repository\nmerged channel PR and downloads its PR-stage RC passport automatically before\npromotion starts. The consumer wrapper defaults to a PR-stage workflow file\nnamed `build.yml` with display name `Build`, and filters the RC passport and\nbuild summary by the configured `artifact-name` before promotion. It also\ndownloads payload artifacts from the same PR-stage run, validates the required\npayload count, passes downloaded platform manifests into the release passport,\nand either forwards an explicit `publish-required-artifacts-json` value or\ngenerates one before calling `promote-buildchain-ref`. Before that call, the\nwrapper creates or updates `publish-gate/{alpha,release,major}` to the\npromotion channel commit and passes that ref, target SHA, and `locked=true` to\nthe promote action with `require-publish-source-lock: \"true\"`. Consumers using\nfloating `@v2` therefore get publish-side source-lock drift protection without\ncopying resolver or promote YAML. The default npm path\ngenerates that requirement list from the downloaded `.tgz` payloads themselves:\nBuildchain reads `package/package.json` inside each tarball for the real scoped\npackage name and version, computes npm-style `sha512-...` integrity over the\ntarball bytes, marks `publish-package-main` as `role: main`, and marks every\nother package as `role: platform`. Consumer workflows therefore stay\ndeclarative and do not need their own artifact download or publish-evidence\ngeneration scripts.\n\nBecause a channel merge can trigger promotion before its PR-stage matrix has\nfinished uploading evidence, the resolver waits up to ten minutes for the exact\nmerged PR's successful workflow run and paired artifacts. Polling remains bound\nto the PR/head identity; timeout or a sibling run still fails closed.\n\nThe public promotion router preserves the requested `vN` or `vN-alpha` ref as\naudit metadata, but binds the router to GitHub's selected reusable-workflow SHA\nand resolves each remaining floating shell/runtime ref exactly once. Every\nlater checkout and delegated promotion receives those immutable SHAs, so a\nchannel tag moving during the run cannot mix two Buildchain revisions.\n\nIf a promote-only run failed after its consumer commit was already merged and\nits PR-stage artifacts were built, rerun the complete failed workflow after the\nfixed Buildchain channel is published:\n\n```bash\ngh run rerun <run-id> --repo <owner>/<consumer>\n```\n\nUse a complete rerun, not `--failed`, so GitHub resolves the reusable workflow\nagain. The release-candidate resolver reuses the existing candidate artifacts,\nand the durable publication transaction makes the recovery idempotent; do not\nopen a replacement consumer PR or rebuild the native matrix solely for this\nrouter failure.\n\nBy default, the wrapper forwards GitHub Release publication to the underlying\n`promote-buildchain-ref` semver model. Once the release transaction is complete,\nthe action creates or updates the public GitHub Release, applies\nprerelease/latest metadata from the semver tag, and uploads the publish evidence\nfile together with the generated release passport assets. This keeps\nnpm/registry publication, Buildchain release passport persistence, and\n`release.published` propagation in one declarative reusable workflow. Consumers\nthat do not publish GitHub Releases can opt out with `github-release: false`.\nFor anchored/manual package releases, the public GitHub Release tag defaults to\n`v<publishedVersion>` while the internal transaction exact tag remains recorded\nin the release passport.\n\nProducts that publish KFD release trust evidence can keep that path declarative\ntoo. Pass KFD-1 self contract witnesses, KFD-2 public claim files, and KFD-3\npre-build/artifact evidence into the wrapper:\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n with:\n buildchain-channel: auto\n buildchain-alpha-contract-lock-path: .buildchain/alpha-contract-lock.json\n buildchain-stable-contract-lock-path: .buildchain/contract-lock.json\n channel: alpha\n artifact-name: libnode\n release-passport-kfd-1-witness-jsons: .buildchain/kfd/kfd-1/standard-contract.witness.json\n release-passport-kfd-2-claim-jsons: .buildchain/kfd/kfd-2/release-claims.json\n release-passport-kfd-3-prebuild-witness-jsons: .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n release-passport-kfd-3-artifact-verify-command: kungfu agent verify --json\n```\n\nBuildchain forwards those declarations into `promote-buildchain-ref`, verifies\nKFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and\ncompares KFD-3 declared shipped public surfaces with artifact-exposed public\nsurfaces. The release passport records the results under `kfd-1`, `kfd-2`, and\nthe KFD-provided `kfd-3` section.\n\nManaged consumers may also ask the promotion wrapper to assemble sealed\npublication evidence from the exact release candidate instead of producing\nshort-lived admission JSON in repository-specific workflow code:\n\n```yaml\n publication-auto-admission: true\n publication-auto-no-gate: true\n publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml\n publication-product: Example Product\n publication-target: npm:@example/product\n publication-package-name: \"@example/product\"\n```\n\n`publication-auto-no-gate` is an explicit consumer decision, not a default. A\nconsumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`\ninstead. Buildchain still requires caller-owned RC evidence, an exact authority\nruntime and source SHA, a repository-local publisher workflow, matching npm\ntarget/package identity, and a qualifying control-plane audit.\n\nA consumer that owns additional product qualification semantics can opt in to\nthe sealed handoff without teaching Buildchain those semantics:\n\n```yaml\n publication-consumer-predicate-id: kungfu.release-admission/v1\n publication-consumer-qualification-command: node scripts/qualify-release.mjs\n```\n\nThe command reads `BUILDCHAIN_PUBLICATION_CAPABILITY_PATH` and\n`BUILDCHAIN_PUBLICATION_GATE_AGGREGATE_PATH`, evaluates the complete aggregate,\nand writes a decision JSON document to\n`BUILDCHAIN_PUBLICATION_QUALIFICATION_RESULT_PATH`. It also receives\n`BUILDCHAIN_PUBLICATION_PREDICATE_ID` and\n`BUILDCHAIN_PUBLICATION_PREDICATE_DIGEST`. The separate qualification job has\nno write or OIDC permission and does not inherit publication secrets. A\nsuccessful deterministic receipt is rechecked by the provider action\nimmediately before mutation. Omitting both inputs preserves the existing\nconsumer contract; supplying only one fails closed."
1276
1276
  },
1277
1277
  {
1278
1278
  "id": "manual:release-flow",
@@ -3690,7 +3690,7 @@
3690
3690
  "pageRegistryPath": "dist/site/page-registry.json",
3691
3691
  "cliRegistryDigest": "90c8650f870c350ca187c612c37f2459dcbfe265aa9b1d5b375ee5c4fb31fab6",
3692
3692
  "workflowRegistryDigest": "02950eef2769b1bd2d899099ef23c7b9ae716b18d602e25aef169d788bc6ece0",
3693
- "pageRegistryDigest": "813255a900666cfd7e061e709293a6f67b5e6945d2f0fd27183344ba8db36cc2"
3693
+ "pageRegistryDigest": "b5bdca0bc6ed05a2a03b6456785381a333af238c25bd33c64be1b4882fcccd9b"
3694
3694
  },
3695
3695
  "comparison": {
3696
3696
  "missingCliRegistry": [],
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-publication-release-registry",
4
- "generatedAt": "2026-07-21T08:15:55.468Z",
5
- "publishedAt": "2026-07-21T08:15:55.468Z",
4
+ "generatedAt": "2026-07-21T14:45:28.549Z",
5
+ "publishedAt": "2026-07-21T14:45:28.549Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "85d51ae82b43195d5c7c7d92d6ecb63f054a4bf2",
22
+ "sourceRevision": "0d9c71d965ec0825af481f5789c5a138c7dad5dc",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "package": {
34
34
  "name": "@kungfu-tech/buildchain",
35
- "version": "2.14.7-alpha.0",
35
+ "version": "2.14.8-alpha.0",
36
36
  "versionSource": "package.json#version"
37
37
  },
38
38
  "sourceKind": "package-site-bundle",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-site-manifest",
4
- "generatedAt": "2026-07-21T08:15:55.468Z",
5
- "publishedAt": "2026-07-21T08:15:55.468Z",
4
+ "generatedAt": "2026-07-21T14:45:28.549Z",
5
+ "publishedAt": "2026-07-21T14:45:28.549Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "85d51ae82b43195d5c7c7d92d6ecb63f054a4bf2",
22
+ "sourceRevision": "0d9c71d965ec0825af481f5789c5a138c7dad5dc",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "package": {
39
39
  "name": "@kungfu-tech/buildchain",
40
- "version": "2.14.7-alpha.0",
40
+ "version": "2.14.8-alpha.0",
41
41
  "versionSource": "package.json#version"
42
42
  },
43
43
  "entrypoint": "buildchain-site.json",
@@ -93,7 +93,7 @@
93
93
  "path": "docs/release-candidate.md",
94
94
  "plane": "verify",
95
95
  "exists": true,
96
- "digest": "sha256:81463a74dfe3d337768797b5b47f8cb3a22389312a917447f9b0e6096ab4f90a"
96
+ "digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99"
97
97
  },
98
98
  {
99
99
  "id": "release-propagation",
@@ -87,6 +87,26 @@ finished uploading evidence, the resolver waits up to ten minutes for the exact
87
87
  merged PR's successful workflow run and paired artifacts. Polling remains bound
88
88
  to the PR/head identity; timeout or a sibling run still fails closed.
89
89
 
90
+ The public promotion router preserves the requested `vN` or `vN-alpha` ref as
91
+ audit metadata, but binds the router to GitHub's selected reusable-workflow SHA
92
+ and resolves each remaining floating shell/runtime ref exactly once. Every
93
+ later checkout and delegated promotion receives those immutable SHAs, so a
94
+ channel tag moving during the run cannot mix two Buildchain revisions.
95
+
96
+ If a promote-only run failed after its consumer commit was already merged and
97
+ its PR-stage artifacts were built, rerun the complete failed workflow after the
98
+ fixed Buildchain channel is published:
99
+
100
+ ```bash
101
+ gh run rerun <run-id> --repo <owner>/<consumer>
102
+ ```
103
+
104
+ Use a complete rerun, not `--failed`, so GitHub resolves the reusable workflow
105
+ again. The release-candidate resolver reuses the existing candidate artifacts,
106
+ and the durable publication transaction makes the recovery idempotent; do not
107
+ open a replacement consumer PR or rebuild the native matrix solely for this
108
+ router failure.
109
+
90
110
  By default, the wrapper forwards GitHub Release publication to the underlying
91
111
  `promote-buildchain-ref` semver model. Once the release transaction is complete,
92
112
  the action creates or updates the public GitHub Release, applies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungfu-tech/buildchain",
3
- "version": "2.14.7-alpha.0",
3
+ "version": "2.14.8-alpha.0",
4
4
  "private": false,
5
5
  "description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
6
6
  "repository": "https://github.com/kungfu-systems/buildchain",
@@ -51,6 +51,7 @@ const requiredPaths = [
51
51
  "scripts/buildchain-channel-router.mjs",
52
52
  "scripts/generate-channel-build-workflow.mjs",
53
53
  "scripts/promotion-channel-router.mjs",
54
+ "scripts/promotion-identity-resolver.mjs",
54
55
  "scripts/generate-channel-promotion-workflow.mjs",
55
56
  "scripts/build-standalone-binary.mjs",
56
57
  "scripts/create-release-bundle.mjs",
@@ -246,6 +247,8 @@ for (const requiredSnippet of [
246
247
  ".github/workflows/.build.yml:",
247
248
  ".github/workflows/.gate-profile.yml:",
248
249
  'property "workflow_ref" is not defined in object type',
250
+ 'property "workflow_repository" is not defined in object type',
251
+ 'property "workflow_sha" is not defined in object type',
249
252
  ]) {
250
253
  if (!actionlintConfig.includes(requiredSnippet)) {
251
254
  throw new Error(`actionlint config missing scoped job.workflow_ref compatibility rule: ${requiredSnippet}`);
@@ -97,7 +97,7 @@ function forwardedInputs(inputNames, { includeInternal = true, unsupportedInputs
97
97
  .filter((name) => !unsupported.has(name))
98
98
  .map((name) => {
99
99
  const routed = {
100
- "buildchain-ref": "runtime-ref",
100
+ "buildchain-ref": "runtime-sha",
101
101
  "buildchain-contract-lock-path": "contract-lock-path",
102
102
  channel: "publication-channel",
103
103
  "target-ref": "target-ref",
@@ -221,7 +221,7 @@ jobs:
221
221
  publication-channel: \${{ steps.route.outputs.publication-channel }}
222
222
  target-ref: \${{ steps.route.outputs.target-ref }}
223
223
  router-ref: \${{ steps.router.outputs.ref }}
224
- router-sha: \${{ steps.identities.outputs.router-sha }}
224
+ router-sha: \${{ steps.router.outputs.sha }}
225
225
  shell-ref: \${{ steps.route.outputs.shell-ref }}
226
226
  shell-sha: \${{ steps.identities.outputs.shell-sha }}
227
227
  runtime-ref: \${{ steps.route.outputs.runtime-ref }}
@@ -237,25 +237,35 @@ jobs:
237
237
  shell: bash
238
238
  env:
239
239
  BUILDCHAIN_ROUTER_WORKFLOW_REF: \${{ job.workflow_ref }}
240
+ BUILDCHAIN_ROUTER_WORKFLOW_REPOSITORY: \${{ job.workflow_repository }}
241
+ BUILDCHAIN_ROUTER_WORKFLOW_SHA: \${{ job.workflow_sha }}
240
242
  run: |
241
243
  set -euo pipefail
242
244
  workflow_ref="\${BUILDCHAIN_ROUTER_WORKFLOW_REF}"
243
- repository="\${workflow_ref%%/.github/workflows/*}"
245
+ repository="\${BUILDCHAIN_ROUTER_WORKFLOW_REPOSITORY}"
244
246
  ref="\${workflow_ref##*@}"
245
247
  ref="\${ref#refs/heads/}"
246
248
  ref="\${ref#refs/tags/}"
247
- if [[ -z "\${repository}" || "\${repository}" = "\${workflow_ref}" || -z "\${ref}" ]]; then
249
+ parsed_repository="\${workflow_ref%%/.github/workflows/*}"
250
+ if [[ -z "\${repository}" || "\${repository}" != "\${parsed_repository}" || -z "\${ref}" ]]; then
248
251
  echo "::error::Unable to resolve promotion router source from job.workflow_ref=\${workflow_ref}"
249
252
  exit 1
250
253
  fi
251
- echo "repository=\${repository}" >> "\${GITHUB_OUTPUT}"
252
- echo "ref=\${ref}" >> "\${GITHUB_OUTPUT}"
254
+ if [[ ! "\${BUILDCHAIN_ROUTER_WORKFLOW_SHA}" =~ ^[0-9a-fA-F]{40}$ ]]; then
255
+ echo "::error::job.workflow_sha is not an exact commit SHA"
256
+ exit 1
257
+ fi
258
+ {
259
+ echo "repository=\${repository}"
260
+ echo "ref=\${ref}"
261
+ echo "sha=\${BUILDCHAIN_ROUTER_WORKFLOW_SHA,,}"
262
+ } >> "\${GITHUB_OUTPUT}"
253
263
 
254
264
  - name: Checkout promotion router
255
265
  uses: actions/checkout@v7.0.0
256
266
  with:
257
267
  repository: \${{ steps.router.outputs.repository }}
258
- ref: \${{ steps.router.outputs.ref }}
268
+ ref: \${{ steps.router.outputs.sha }}
259
269
  path: .buildchain/router
260
270
  persist-credentials: false
261
271
 
@@ -289,11 +299,43 @@ jobs:
289
299
  throw new Error(\`promotion runtime override requires write, maintain, or admin permission; actor has \${level}\`);
290
300
  }
291
301
 
302
+ - name: Resolve immutable promotion identities
303
+ id: identities
304
+ shell: bash
305
+ env:
306
+ GITHUB_TOKEN: \${{ github.token }}
307
+ CHANNEL: \${{ steps.route.outputs.channel }}
308
+ SELECTED_SHELL_REF: \${{ steps.route.outputs.shell-ref }}
309
+ ALPHA_SHELL_REF: ${alphaRoute.logicalRef}
310
+ ALPHA_SHELL_CALL_REF: ${alphaRoute.callRef}
311
+ STABLE_SHELL_REF: ${stableRoute.logicalRef}
312
+ STABLE_SHELL_CALL_REF: ${stableRoute.callRef}
313
+ run: |
314
+ set -euo pipefail
315
+ if [[ "\${CHANNEL}" = "alpha" ]]; then
316
+ expected_ref="\${ALPHA_SHELL_REF}"
317
+ call_ref="\${ALPHA_SHELL_CALL_REF}"
318
+ else
319
+ expected_ref="\${STABLE_SHELL_REF}"
320
+ call_ref="\${STABLE_SHELL_CALL_REF}"
321
+ fi
322
+ if [[ "\${SELECTED_SHELL_REF}" != "\${expected_ref}" ]]; then
323
+ echo "::error::Selected promotion shell ref \${SELECTED_SHELL_REF} does not match configured \${expected_ref}"
324
+ exit 1
325
+ fi
326
+ node .buildchain/router/scripts/promotion-identity-resolver.mjs \\
327
+ --repository "\${{ steps.router.outputs.repository }}" \\
328
+ --router-ref "\${{ steps.router.outputs.ref }}" \\
329
+ --router-sha "\${{ steps.router.outputs.sha }}" \\
330
+ --shell-ref "\${SELECTED_SHELL_REF}" \\
331
+ --shell-call-ref "\${call_ref}" \\
332
+ --runtime-ref "\${{ steps.route.outputs.runtime-ref }}"
333
+
292
334
  - name: Checkout selected promotion workflow shell
293
335
  uses: actions/checkout@v7.0.0
294
336
  with:
295
337
  repository: \${{ steps.router.outputs.repository }}
296
- ref: \${{ steps.route.outputs.shell-ref }}
338
+ ref: \${{ steps.identities.outputs.shell-sha }}
297
339
  path: .buildchain/shell
298
340
  persist-credentials: false
299
341
 
@@ -301,7 +343,7 @@ jobs:
301
343
  uses: actions/checkout@v7.0.0
302
344
  with:
303
345
  repository: \${{ steps.router.outputs.repository }}
304
- ref: \${{ steps.route.outputs.runtime-ref }}
346
+ ref: \${{ steps.identities.outputs.runtime-sha }}
305
347
  path: .buildchain/runtime
306
348
  persist-credentials: false
307
349
 
@@ -334,44 +376,26 @@ jobs:
334
376
  echo "path=\${path}" >> "\${GITHUB_OUTPUT}"
335
377
  echo "digest=\${digest}" >> "\${GITHUB_OUTPUT}"
336
378
 
337
- - name: Resolve immutable promotion identities
338
- id: identities
379
+ - name: Verify immutable promotion checkouts
339
380
  shell: bash
340
381
  env:
341
382
  CHANNEL: \${{ steps.route.outputs.channel }}
342
- SELECTED_SHELL_REF: \${{ steps.route.outputs.shell-ref }}
343
- ALPHA_SHELL_REF: ${alphaRoute.logicalRef}
344
- ALPHA_SHELL_CALL_REF: ${alphaRoute.callRef}
345
383
  ALPHA_SHELL_WORKFLOW_PATH: ${alphaRoute.workflowPath}
346
- STABLE_SHELL_REF: ${stableRoute.logicalRef}
347
- STABLE_SHELL_CALL_REF: ${stableRoute.callRef}
348
384
  STABLE_SHELL_WORKFLOW_PATH: ${stableRoute.workflowPath}
385
+ ROUTER_SHA: \${{ steps.router.outputs.sha }}
386
+ SHELL_SHA: \${{ steps.identities.outputs.shell-sha }}
387
+ RUNTIME_SHA: \${{ steps.identities.outputs.runtime-sha }}
349
388
  run: |
350
389
  set -euo pipefail
351
390
  if [[ "\${CHANNEL}" = "alpha" ]]; then
352
- expected_ref="\${ALPHA_SHELL_REF}"
353
- call_ref="\${ALPHA_SHELL_CALL_REF}"
354
391
  workflow_path="\${ALPHA_SHELL_WORKFLOW_PATH}"
355
392
  else
356
- expected_ref="\${STABLE_SHELL_REF}"
357
- call_ref="\${STABLE_SHELL_CALL_REF}"
358
393
  workflow_path="\${STABLE_SHELL_WORKFLOW_PATH}"
359
394
  fi
360
- if [[ "\${SELECTED_SHELL_REF}" != "\${expected_ref}" ]]; then
361
- echo "::error::Selected promotion shell ref \${SELECTED_SHELL_REF} does not match configured \${expected_ref}"
362
- exit 1
363
- fi
364
395
  test -f ".buildchain/shell/\${workflow_path}"
365
- shell_sha="$(git -C .buildchain/shell rev-parse HEAD)"
366
- if [[ "\${call_ref}" =~ ^[0-9a-f]{40}$ && "\${shell_sha}" != "\${call_ref}" ]]; then
367
- echo "::error::Pinned promotion shell \${call_ref} does not match selected \${shell_sha}"
368
- exit 1
369
- fi
370
- {
371
- echo "router-sha=$(git -C .buildchain/router rev-parse HEAD)"
372
- echo "shell-sha=\${shell_sha}"
373
- echo "runtime-sha=$(git -C .buildchain/runtime rev-parse HEAD)"
374
- } >> "\${GITHUB_OUTPUT}"
396
+ [[ "$(git -C .buildchain/router rev-parse HEAD)" = "\${ROUTER_SHA}" ]] || { echo "::error::Promotion router checkout moved"; exit 1; }
397
+ [[ "$(git -C .buildchain/shell rev-parse HEAD)" = "\${SHELL_SHA}" ]] || { echo "::error::Promotion shell checkout moved"; exit 1; }
398
+ [[ "$(git -C .buildchain/runtime rev-parse HEAD)" = "\${RUNTIME_SHA}" ]] || { echo "::error::Promotion runtime checkout moved"; exit 1; }
375
399
 
376
400
  alpha:
377
401
  name: Promote with alpha workflow shell
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const SHA_PATTERN = /^[0-9a-f]{40}$/i;
8
+
9
+ function normalized(value) {
10
+ return String(value ?? "").trim();
11
+ }
12
+
13
+ function requireSha(value, label) {
14
+ const sha = normalized(value).toLowerCase();
15
+ if (!SHA_PATTERN.test(sha)) throw new Error(`${label} must be an exact 40-character SHA`);
16
+ return sha;
17
+ }
18
+
19
+ export async function resolvePromotionIdentities({
20
+ routerRef,
21
+ routerSha,
22
+ shellRef,
23
+ shellCallRef,
24
+ runtimeRef,
25
+ resolveRef,
26
+ } = {}) {
27
+ const requested = {
28
+ router: normalized(routerRef),
29
+ shell: normalized(shellRef),
30
+ shellCall: normalized(shellCallRef),
31
+ runtime: normalized(runtimeRef),
32
+ };
33
+ if (Object.values(requested).some((value) => !value)) {
34
+ throw new Error("router, shell, shell call, and runtime refs are required");
35
+ }
36
+ if (typeof resolveRef !== "function") throw new Error("resolveRef must be a function");
37
+
38
+ const immutableRouterSha = requireSha(routerSha, "router SHA");
39
+ const resolved = new Map([[requested.router, immutableRouterSha]]);
40
+ const resolveOnce = async (ref) => {
41
+ if (SHA_PATTERN.test(ref)) return ref.toLowerCase();
42
+ if (!resolved.has(ref)) {
43
+ resolved.set(ref, requireSha(await resolveRef(ref), `resolved SHA for ${ref}`));
44
+ }
45
+ return resolved.get(ref);
46
+ };
47
+
48
+ return {
49
+ routerRef: requested.router,
50
+ routerSha: immutableRouterSha,
51
+ shellRef: requested.shell,
52
+ shellSha: await resolveOnce(requested.shellCall),
53
+ runtimeRef: requested.runtime,
54
+ runtimeSha: await resolveOnce(requested.runtime),
55
+ };
56
+ }
57
+
58
+ function parseArgs(argv) {
59
+ const values = {};
60
+ for (let index = 0; index < argv.length; index += 1) {
61
+ const token = argv[index];
62
+ if (!token.startsWith("--")) throw new Error(`unexpected argument: ${token}`);
63
+ const value = argv[index + 1];
64
+ if (value === undefined || value.startsWith("--")) throw new Error(`missing value for ${token}`);
65
+ values[token.slice(2)] = value;
66
+ index += 1;
67
+ }
68
+ return values;
69
+ }
70
+
71
+ function repositoryParts(value) {
72
+ const match = normalized(value).match(/^([^/]+)\/([^/]+)$/);
73
+ if (!match) throw new Error("repository must use owner/name form");
74
+ return { owner: match[1], repo: match[2] };
75
+ }
76
+
77
+ async function githubCommitResolver({ repository, token }) {
78
+ const { owner, repo } = repositoryParts(repository);
79
+ return async (ref) => {
80
+ const response = await fetch(
81
+ `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits/${encodeURIComponent(ref)}`,
82
+ {
83
+ headers: {
84
+ Accept: "application/vnd.github+json",
85
+ Authorization: `Bearer ${token}`,
86
+ "X-GitHub-Api-Version": "2022-11-28",
87
+ "User-Agent": "kungfu-buildchain-promotion-router",
88
+ },
89
+ },
90
+ );
91
+ if (!response.ok) throw new Error(`unable to resolve ${ref}: GitHub API returned ${response.status}`);
92
+ return (await response.json()).sha;
93
+ };
94
+ }
95
+
96
+ function writeOutputs(file, identities) {
97
+ const outputs = {
98
+ "router-sha": identities.routerSha,
99
+ "shell-sha": identities.shellSha,
100
+ "runtime-sha": identities.runtimeSha,
101
+ };
102
+ fs.appendFileSync(file, Object.entries(outputs).map(([key, value]) => `${key}=${value}\n`).join(""));
103
+ }
104
+
105
+ async function main() {
106
+ const args = parseArgs(process.argv.slice(2));
107
+ const token = normalized(process.env.GITHUB_TOKEN);
108
+ if (!token) throw new Error("GITHUB_TOKEN is required to resolve floating promotion refs");
109
+ const identities = await resolvePromotionIdentities({
110
+ routerRef: args["router-ref"],
111
+ routerSha: args["router-sha"],
112
+ shellRef: args["shell-ref"],
113
+ shellCallRef: args["shell-call-ref"],
114
+ runtimeRef: args["runtime-ref"],
115
+ resolveRef: await githubCommitResolver({ repository: args.repository, token }),
116
+ });
117
+ if (process.env.GITHUB_OUTPUT) writeOutputs(process.env.GITHUB_OUTPUT, identities);
118
+ process.stdout.write(`${JSON.stringify(identities, null, 2)}\n`);
119
+ }
120
+
121
+ const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
122
+ if (isMain) {
123
+ main().catch((error) => {
124
+ console.error(`promotion-identity-resolver: ${error.message}`);
125
+ process.exitCode = 1;
126
+ });
127
+ }