@kungfu-tech/buildchain 2.14.2 → 2.14.3-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.
Files changed (57) hide show
  1. package/dist/site/agent-index.json +2 -0
  2. package/dist/site/artifact-schemas.json +4 -0
  3. package/dist/site/badge-endpoint-registry.json +320 -0
  4. package/dist/site/badges/v1/kfd-5/aligned.json +14 -0
  5. package/dist/site/badges/v1/kfd-5/declared.json +14 -0
  6. package/dist/site/badges/v1/kfd-5/downgraded.json +14 -0
  7. package/dist/site/badges/v1/kfd-5/draft.json +14 -0
  8. package/dist/site/badges/v1/kfd-5/failed.json +14 -0
  9. package/dist/site/badges/v1/kfd-5/missing.json +14 -0
  10. package/dist/site/badges/v1/kfd-5/passed.json +14 -0
  11. package/dist/site/badges/v1/kfd-5/planned.json +14 -0
  12. package/dist/site/badges/v1/kfd-6/aligned.json +14 -0
  13. package/dist/site/badges/v1/kfd-6/declared.json +14 -0
  14. package/dist/site/badges/v1/kfd-6/downgraded.json +14 -0
  15. package/dist/site/badges/v1/kfd-6/draft.json +14 -0
  16. package/dist/site/badges/v1/kfd-6/failed.json +14 -0
  17. package/dist/site/badges/v1/kfd-6/missing.json +14 -0
  18. package/dist/site/badges/v1/kfd-6/passed.json +14 -0
  19. package/dist/site/badges/v1/kfd-6/planned.json +14 -0
  20. package/dist/site/buildchain-contract.json +149 -28
  21. package/dist/site/buildchain-site.json +25 -23
  22. package/dist/site/capability-registry.json +2 -2
  23. package/dist/site/controller-registry.json +61 -5
  24. package/dist/site/kfd-claims.json +129 -11
  25. package/dist/site/kfd-upstream-aggregate.json +12 -12
  26. package/dist/site/manual-registry.json +6 -6
  27. package/dist/site/node-api-registry.json +20 -7
  28. package/dist/site/page-registry.json +12 -12
  29. package/dist/site/public-surface-audit.json +134 -8
  30. package/dist/site/publication-authority-registry.json +26 -1
  31. package/dist/site/publication-registry.json +4 -4
  32. package/dist/site/release-model.json +3 -1
  33. package/dist/site/release-passport-check-manifest.json +167 -0
  34. package/dist/site/release-provenance.json +3 -0
  35. package/dist/site/schemas/release-passport-v1.schema.json +198 -0
  36. package/dist/site/site-manifest.json +10 -10
  37. package/dist/site/workflow-registry.json +128 -4
  38. package/docs/migration-inventory.md +8 -0
  39. package/docs/release-candidate.md +3 -0
  40. package/docs/release-governance.md +5 -4
  41. package/docs/release-passport.md +19 -0
  42. package/docs/release-propagation.md +19 -7
  43. package/docs/reusable-build-surface.md +26 -1
  44. package/package.json +5 -2
  45. package/packages/core/buildchain-contract.js +41 -0
  46. package/packages/core/buildchain-kfd-claims.js +4 -0
  47. package/packages/core/buildchain-publication-authority.js +1 -0
  48. package/packages/core/controller-evidence.js +1 -1
  49. package/packages/core/index.js +9 -0
  50. package/packages/core/package-manager.js +32 -0
  51. package/packages/core/release-passport-contract.js +249 -0
  52. package/packages/core/release-passport.js +39 -0
  53. package/scripts/check-inventory.mjs +37 -3
  54. package/scripts/generate-channel-promotion-workflow.mjs +331 -0
  55. package/scripts/generate-site-bundle.mjs +15 -0
  56. package/scripts/promotion-channel-router.mjs +132 -0
  57. package/scripts/validate-package-manager-contract.mjs +24 -0
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-site-bundle",
4
- "generatedAt": "2026-07-18T01:00:00.995Z",
5
- "publishedAt": "2026-07-18T01:00:00.995Z",
4
+ "generatedAt": "2026-07-19T00:00:09.045Z",
5
+ "publishedAt": "2026-07-19T00:00:09.045Z",
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": "ae16e640bf2f21c8f981ffa8b3523c8aae9200b2",
22
+ "sourceRevision": "cb4ea966786d561d5f6e9c53d55bba5e951bdb24",
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.2",
40
+ "version": "2.14.3-alpha.1",
41
41
  "versionSource": "package.json#version"
42
42
  },
43
43
  "source": {
@@ -918,7 +918,7 @@
918
918
  ],
919
919
  "maturity": "stable",
920
920
  "sourcePath": "docs/migration-inventory.md",
921
- "digest": "sha256:80018cf78785431e67bbc5278d82dda80d0baa320cdb5e4abda26a0b05e5c0bc",
921
+ "digest": "sha256:08daa7d0b42543c1d1dacf1f56f461f0e7a7ca9e25d0859e39599ceb97054ee0",
922
922
  "headings": [
923
923
  {
924
924
  "level": 1,
@@ -966,7 +966,7 @@
966
966
  "anchor": "verification-gates"
967
967
  }
968
968
  ],
969
- "markdown": "# Buildchain v2 Migration Inventory\n\nThis inventory records the buildchain v2 action surface. Buildchain v2 is the\nmonorepo source of truth for active Kungfu repository workflows, the active\nreusable build workflow, and the three GitHub Actions that are still part of\nBuildchain's reusable contract.\nStandalone `workflows` and `action-*` repositories are historical rollback\nanchors, not active Buildchain migration targets.\n\n## Workflow Sources\n\n| Source repository | Previous branch | Buildchain v2 disposition |\n| --- | --- | --- |\n| `workflows` | `dev/v2/v2.0` | root `.github/workflows` sources migrated; reusable workflows linted by actionlint |\n\n## Reusable Workflow Boundaries\n\nThe active Buildchain-native reusable surface is:\n\n| Workflow | Disposition |\n| --- | --- |\n| `.github/workflows/.build.yml` | active reusable build contract: runner presets, trusted event gate, publish source lock, lifecycle commands, deterministic artifacts, aggregate summary, and release manifest outputs |\n\nHidden reusable workflow files from the old `workflows` repository are retained\nonly when they still have an active compatibility or migration boundary. Retired\nPR orchestration paths are not kept in `.github/workflows`; they remain listed\nin the inventory as excluded legacy surfaces. In particular,\n`.batch-pull-request.yml` is removed with the retired batch PR action family,\nand the legacy `.release-new-version.yml` path is not the modern publish\nsurface. New publish integrations should use `.build.yml`, `buildchain.toml`,\n`lifecycle.publish`, and publish transaction evidence.\n\nRelease templates that previously performed direct publishing or deployment are\nnow fail-closed when retained for compatibility discovery. They do not call\nlegacy publish actions, `npm publish`, or deploy providers directly. Callers must\nmigrate to `release-candidate-promote.yml@v2` or a project-owned\n`lifecycle.publish` command behind a publish-gate source lock, so floating\n`@v2` consumers cannot bypass source-lock drift protection.\n\n## Migrated Actions\n\nNo standalone `action-*` repository is shipped as a Buildchain action anymore.\nOld product, operations, PR-helper, page-generation, dependency-sync, and\nversion-bump actions have either been retired or absorbed into Buildchain's\nnative lifecycle, reusable workflow, and promotion scripts.\n\n## Retired Actions Excluded From v2\n\nThese legacy action repositories are intentionally not shipped as buildchain v2\nactions because the corresponding workflows now reject the retired mechanism or\nbecause the action is part of that retired path.\n\n| Previous repository | Reason |\n| --- | --- |\n| `action-approve` | retired GitHub issue/PR helper; use repository-native GitHub automation |\n| `action-batch-pull-request` | retired PR orchestration helper; not part of the Buildchain reusable contract |\n| `action-bump-version` | replaced by Buildchain release-line scripts and `actions/promote-buildchain-ref` |\n| `action-check-format` | replaced by project-owned `lifecycle.verify` commands |\n| `action-find-dependencies` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-generate-download-page` | retired product page generator; model as project-owned lifecycle/deploy work if needed |\n| `action-generate-release-page` | retired product page generator; model as project-owned lifecycle/deploy work if needed |\n| `action-merge-close-issue` | retired GitHub issue/PR helper; use repository-native GitHub automation |\n| `action-package-dependency` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-publish-prebuilt` | retired S3 prebuilt publisher; use lifecycle publish plus publish transaction evidence |\n| `action-purge-artifacts` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-qa-automated` | retired external QA trigger; model as project-owned lifecycle or workflow logic |\n| `action-release-note` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-rollback-release` | replaced by Buildchain publish transaction recover/finalize/repair semantics |\n| `action-set-collaborators` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-sync-airtable` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-sync-extensions-version` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-sync-pr` | retired PR synchronization helper; not part of the Buildchain reusable contract |\n| `action-update-dependencies-version` | retired dependency-version helper; use package-manager adapters and lifecycle commands |\n\n## Retired Workflows Excluded From v2\n\nThese legacy workflow entrypoints are intentionally not shipped from the root\n`.github/workflows` directory.\n\n| Previous workflow | Reason |\n| --- | --- |\n| `.batch-pull-request.yml` | retired PR orchestration helper; v2.5 dev integration governance will use a new protected-dev PR protocol instead |\n| `.release-new-version.yml` | retained as a fail-closed compatibility stub; direct publish model replaced by source-locked release-candidate promotion |\n| `.release-elastic-beanstalk.yml` | retained as a fail-closed compatibility stub; deploy side effects must move behind project lifecycle publish and publish-gate source locks |\n| `.sam-release.yml` | retained as a fail-closed compatibility stub; deploy side effects must move behind project lifecycle publish and publish-gate source locks |\n| `.wheel-release.yml` | retained as a fail-closed compatibility stub; package publish side effects must move behind project lifecycle publish and publish-gate source locks |\n\n## Buildchain-Native Actions\n\nThese actions are new Buildchain v2 surfaces rather than migrations from an\nolder standalone action repository.\n\n| Buildchain path | Purpose |\n| --- | --- |\n| `actions/promote-buildchain-ref` | governance-closed Buildchain release ref promotion |\n| `actions/run-lifecycle` | lifecycle command execution and deterministic artifact manifest generation |\n| `actions/validate-config` | `buildchain.toml` version-state and lifecycle preflight without executing lifecycle commands |\n\n## Stable v2 Refs\n\n- Actions: `kungfu-systems/buildchain/actions/<name>@v2`\n- Reusable workflows: `kungfu-systems/buildchain/.github/workflows/<workflow>.yml@v2`\n\n## Verification Gates\n\n- `pnpm install --frozen-lockfile`\n- `pnpm run check`\n- GitHub-hosted `Verify` workflow\n- Manual `Self-hosted Runner Smoke` workflow for trusted self-hosted runner validation"
969
+ "markdown": "# Buildchain v2 Migration Inventory\n\nThis inventory records the buildchain v2 action surface. Buildchain v2 is the\nmonorepo source of truth for active Kungfu repository workflows, the active\nreusable build workflow, and the three GitHub Actions that are still part of\nBuildchain's reusable contract.\nStandalone `workflows` and `action-*` repositories are historical rollback\nanchors, not active Buildchain migration targets.\n\n## Workflow Sources\n\n| Source repository | Previous branch | Buildchain v2 disposition |\n| --- | --- | --- |\n| `workflows` | `dev/v2/v2.0` | root `.github/workflows` sources migrated; reusable workflows linted by actionlint |\n\n## Reusable Workflow Boundaries\n\nThe active Buildchain-native reusable surface is:\n\n| Workflow | Disposition |\n| --- | --- |\n| `.github/workflows/.build.yml` | active reusable build contract: runner presets, trusted event gate, publish source lock, lifecycle commands, deterministic artifacts, aggregate summary, and release manifest outputs |\n\nHidden reusable workflow files from the old `workflows` repository are retained\nonly when they still have an active compatibility or migration boundary. Retired\nPR orchestration paths are not kept in `.github/workflows`; they remain listed\nin the inventory as excluded legacy surfaces. In particular,\n`.batch-pull-request.yml` is removed with the retired batch PR action family,\nand the legacy `.release-new-version.yml` path is not the modern publish\nsurface. New publish integrations should use `.build.yml`, `buildchain.toml`,\n`lifecycle.publish`, and publish transaction evidence.\n\nRelease templates that previously performed direct publishing or deployment are\nnow fail-closed when retained for compatibility discovery. They do not call\nlegacy publish actions, `npm publish`, or deploy providers directly. Callers must\nmigrate to `release-candidate-promote.yml@v2` or a project-owned\n`lifecycle.publish` command behind a publish-gate source lock, so floating\n`@v2` consumers cannot bypass source-lock drift protection.\n\nThe public promotion workflow is now a generated dual-channel router. Existing\ncallers remain source-compatible, while callers that want alpha workflow-shell\nfixes before stable promotion should add `buildchain-channel: auto` plus\n`buildchain-alpha-contract-lock-path` and\n`buildchain-stable-contract-lock-path`. Keep one common promotion declaration;\ndo not duplicate alpha and stable jobs or call\n`.release-candidate-promote.yml` directly.\n\n## Migrated Actions\n\nNo standalone `action-*` repository is shipped as a Buildchain action anymore.\nOld product, operations, PR-helper, page-generation, dependency-sync, and\nversion-bump actions have either been retired or absorbed into Buildchain's\nnative lifecycle, reusable workflow, and promotion scripts.\n\n## Retired Actions Excluded From v2\n\nThese legacy action repositories are intentionally not shipped as buildchain v2\nactions because the corresponding workflows now reject the retired mechanism or\nbecause the action is part of that retired path.\n\n| Previous repository | Reason |\n| --- | --- |\n| `action-approve` | retired GitHub issue/PR helper; use repository-native GitHub automation |\n| `action-batch-pull-request` | retired PR orchestration helper; not part of the Buildchain reusable contract |\n| `action-bump-version` | replaced by Buildchain release-line scripts and `actions/promote-buildchain-ref` |\n| `action-check-format` | replaced by project-owned `lifecycle.verify` commands |\n| `action-find-dependencies` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-generate-download-page` | retired product page generator; model as project-owned lifecycle/deploy work if needed |\n| `action-generate-release-page` | retired product page generator; model as project-owned lifecycle/deploy work if needed |\n| `action-merge-close-issue` | retired GitHub issue/PR helper; use repository-native GitHub automation |\n| `action-package-dependency` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-publish-prebuilt` | retired S3 prebuilt publisher; use lifecycle publish plus publish transaction evidence |\n| `action-purge-artifacts` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-qa-automated` | retired external QA trigger; model as project-owned lifecycle or workflow logic |\n| `action-release-note` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-rollback-release` | replaced by Buildchain publish transaction recover/finalize/repair semantics |\n| `action-set-collaborators` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-sync-airtable` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-sync-extensions-version` | retired in workflows v2 or backed by retired Airtable/dependency/collaborator/purge mechanism |\n| `action-sync-pr` | retired PR synchronization helper; not part of the Buildchain reusable contract |\n| `action-update-dependencies-version` | retired dependency-version helper; use package-manager adapters and lifecycle commands |\n\n## Retired Workflows Excluded From v2\n\nThese legacy workflow entrypoints are intentionally not shipped from the root\n`.github/workflows` directory.\n\n| Previous workflow | Reason |\n| --- | --- |\n| `.batch-pull-request.yml` | retired PR orchestration helper; v2.5 dev integration governance will use a new protected-dev PR protocol instead |\n| `.release-new-version.yml` | retained as a fail-closed compatibility stub; direct publish model replaced by source-locked release-candidate promotion |\n| `.release-elastic-beanstalk.yml` | retained as a fail-closed compatibility stub; deploy side effects must move behind project lifecycle publish and publish-gate source locks |\n| `.sam-release.yml` | retained as a fail-closed compatibility stub; deploy side effects must move behind project lifecycle publish and publish-gate source locks |\n| `.wheel-release.yml` | retained as a fail-closed compatibility stub; package publish side effects must move behind project lifecycle publish and publish-gate source locks |\n\n## Buildchain-Native Actions\n\nThese actions are new Buildchain v2 surfaces rather than migrations from an\nolder standalone action repository.\n\n| Buildchain path | Purpose |\n| --- | --- |\n| `actions/promote-buildchain-ref` | governance-closed Buildchain release ref promotion |\n| `actions/run-lifecycle` | lifecycle command execution and deterministic artifact manifest generation |\n| `actions/validate-config` | `buildchain.toml` version-state and lifecycle preflight without executing lifecycle commands |\n\n## Stable v2 Refs\n\n- Actions: `kungfu-systems/buildchain/actions/<name>@v2`\n- Reusable workflows: `kungfu-systems/buildchain/.github/workflows/<workflow>.yml@v2`\n\n## Verification Gates\n\n- `pnpm install --frozen-lockfile`\n- `pnpm run check`\n- GitHub-hosted `Verify` workflow\n- Manual `Self-hosted Runner Smoke` workflow for trusted self-hosted runner validation"
970
970
  },
971
971
  {
972
972
  "id": "manual:ownership",
@@ -1294,7 +1294,7 @@
1294
1294
  ],
1295
1295
  "maturity": "stable",
1296
1296
  "sourcePath": "docs/release-candidate.md",
1297
- "digest": "sha256:1fc581cf1b4e4a202464c8d6671b3bff12d0b349760246a951e88ef524f99e6b",
1297
+ "digest": "sha256:81463a74dfe3d337768797b5b47f8cb3a22389312a917447f9b0e6096ab4f90a",
1298
1298
  "headings": [
1299
1299
  {
1300
1300
  "level": 1,
@@ -1302,7 +1302,7 @@
1302
1302
  "anchor": "release-candidate-passport"
1303
1303
  }
1304
1304
  ],
1305
- "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 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."
1305
+ "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."
1306
1306
  },
1307
1307
  {
1308
1308
  "id": "manual:release-flow",
@@ -1388,7 +1388,7 @@
1388
1388
  ],
1389
1389
  "maturity": "stable",
1390
1390
  "sourcePath": "docs/release-governance.md",
1391
- "digest": "sha256:e64ffd6183eaa7b73f8c0c133a645ced22212bca70c9609ad5f667cf3830aaae",
1391
+ "digest": "sha256:d25a2ceb6cdc1a1db321e7d2a6aa8a454fb4d7a10560177604f8ecb5801a6e66",
1392
1392
  "headings": [
1393
1393
  {
1394
1394
  "level": 1,
@@ -1476,7 +1476,7 @@
1476
1476
  "anchor": "operational-reading-order"
1477
1477
  }
1478
1478
  ],
1479
- "markdown": "# Release Governance\n\nBuildchain v2 preserves the release semantics of the older ABV workflow while\nmoving the implementation into one modern repository.\n\nThe central idea is simple: a reviewed merge into a release channel is the\nrelease intent. Automation must then create the version-state commit, exact tag,\nfloating tag, and next alpha state that make that intent true in Git.\n\n## Design Problem\n\nKungfu release automation has to keep four facts aligned:\n\n1. The source tree that was reviewed.\n2. The package version recorded in manifests such as `package.json` or\n `lerna.json`.\n3. The exact immutable release or prerelease tag.\n4. The floating channel refs that consumers actually use.\n\nIf any one of these facts is updated by hand, the system can split:\n\n- a consumer can fetch `v2.0` and receive a tree whose package version still\n says the previous release;\n- a maintainer can move `v2` without producing an exact `v2.0.N` audit tag;\n- an alpha can be promoted to production even though the release tree is not the\n same tree that was tested;\n- a protected branch merge can succeed while the follow-up version commit is\n missing, or a flow-internal generated `dev`/`alpha`/`release` ref update can\n fail after publish because the automation identity was not declared in the\n branch-protection review bypass allowance.\n\nThe older ABV workflow addressed this by letting GitHub PRs drive release\nstate. Buildchain keeps that choice because it makes release intent reviewable,\nobservable, and recoverable from Git history.\n\n## What ABV Contributed\n\nThe old ABV model was not just \"bump a version number.\" It encoded a governance\nloop:\n\n- release branches are named as channels: `dev`, `alpha`, `release`, and the\n administrative `publish-gate/major`;\n- a PR from one channel to the next is the release request;\n- verify jobs check that the branch pair is valid before merge;\n- a maintainer review is required before the branch moves;\n- after merge, automation writes the version change and moves tags;\n- exact tags and floating refs are aligned with the resulting commit;\n- the next development channel is prepared automatically.\n\nABV also kept the version-state mutation in the repository. For JavaScript\nrepositories that usually meant changing `lerna.json` and/or `package.json`.\nThat commit is important because the tag alone is not enough evidence: the\nsource tree should also declare the version that the tag advertises.\n\nBuildchain v2 treats that as a hard semantic requirement for its own release\nline.\n\n## Buildchain Implementation\n\nBuildchain implements the same governance loop with:\n\n- `.github/workflows/release-verify.yml` for PR verification;\n- `.github/workflows/buildchain-ref-promotion.yml` for post-verify ref\n promotion; this workflow dogfoods the declarative\n `release-candidate-promote.yml` wrapper and does not hand-wire resolver,\n artifact download, publish-gate, or promote action steps;\n- Buildchain self promotion enables `release-passport-buildchain-self-kfd`, so\n the promote action generates KFD-1 witnesses, KFD-2 public claim JSON, and\n KFD-3 collaboration-interface witnesses from the final version-state workspace\n before release passport finalization. The witness hashes therefore bind to the\n exact published package and site facts from\n `packages/core/buildchain-kfd-claims.js` instead of relying on prose release\n notes;\n- `actions/promote-buildchain-ref` for branch, tag, version-state, and\n governance checks;\n- package-manager adapters that can update version state for pnpm, npm, and\n yarn style repositories;\n- `buildchain.toml` lifecycle configuration for repositories whose version\n state or verification commands are not Node package-manager defaults.\n\nThe implementation is intentionally stricter than a local release script:\n\n- manual workflow dispatch can only do dry-run promotion;\n- non-dry-run promotion must be driven by a completed `Verify` workflow;\n- target branch protection details must be readable, and branch protection must\n apply to administrators as well as regular contributors;\n- `alpha/vX/vX.Y` and `release/vX/vX.Y` branch protection must require both the\n general `check` context and the Release Verify aggregate `verify` context, so\n an invalid channel pair cannot merge even when repository checks pass;\n- release targets keep those checks non-strict with respect to source-branch\n ancestry: generated channel bookkeeping intentionally makes the source and\n target histories diverge, while the pair-specific `verify` context validates\n the legal channel transition;\n- alpha promotion must come from a merged same-repository PR from\n `dev/vX/vX.Y` to `alpha/vX/vX.Y`;\n- release promotion must come from a merged same-repository PR from\n `alpha/vX/vX.Y` to `release/vX/vX.Y`;\n- major promotion must come from a merged same-repository PR from\n `release/vX/vX.Y` to `publish-gate/major`;\n- release promotion requires an existing same-patch alpha tag and checks the\n release source tree against that tested alpha tree;\n- generated version-state commits are verified before refs move.\n\nExact publication planning installs the checked-out promotion source's declared\ndependencies before version-state verification. This keeps the pre-authority\nversion plan on the same package-manager boundary as the later promotion job,\nincluding repositories whose verification commands import production packages.\nThe planning pass may materialize and verify declared derived files locally,\nbut dry-run never creates Git blobs, trees, commits, refs, or tags.\n\nPromotion intents are serialized globally per caller repository with\n`cancel-in-progress: false`. A queued intent re-reads its protected target ref\nbefore checkout, dependency installation, release-candidate resolution, or any\npublish-gate/ref mutation. If the ref already points at a newer SHA, that older\nintent is no longer release authority: the workflow records the requested and\ncurrent SHAs, proves that the current target is ahead of the requested commit,\nand completes as a `target-ref-advanced` superseded no-op. Diverged or behind\ncomparisons are not superseded transactions and still fail closed.\nMissing refs, unreadable repository state, invalid channels, governance\nfailures, and artifact mismatches still fail closed. The promote action repeats\nthe target check at the mutation boundary, so a ref that advances after the\nworkflow preflight cannot receive a second set of publication side effects.\n\n## Version Lines\n\nKungfu uses Python-like version lines where a minor line can represent a\nlong-lived product train. A line such as `v2.0` can produce many production\npatch releases:\n\n```text\nv2.0.0\nv2.0.1\nv2.0.2\n...\nv2.0.1234\n```\n\nThis is why Buildchain maintains both exact and floating refs:\n\n- `v2.0.2` is immutable release evidence;\n- `v2.0` is the latest production release on the `2.0` line;\n- `v2` is the selected stable major-line entrypoint;\n- `v2.0.3-alpha.0` is immutable alpha evidence;\n- `v2.0-alpha` is the latest test channel for the `2.0` line.\n- `v2-alpha` is the latest test channel on the highest published alpha minor in major `2`.\n\nA release does not mean \"minor is complete.\" It means \"this patch on this minor\nline is now production.\"\n\nGitHub repository rules must preserve that distinction. Exact tags such as\n`v2.0.2` and `v2.0.3-alpha.0` should be immutable. Floating channel tags such as\n`v2`, `v2.0`, `v2.0-alpha`, and `v2-alpha` must remain movable by the Buildchain promotion\ntoken after governance checks and publish evidence pass. A tag ruleset that\nprotects every `refs/tags/v*` ref is too broad because it also locks the\nfloating channel tags that Buildchain is required to update. Prefer exact-tag\npatterns such as `refs/tags/v*.*.*` for immutable release evidence, while\nleaving floating channel tags under Buildchain automation control.\n\n## Alpha Semantics\n\nAn alpha merge is:\n\n```text\ndev/vX/vX.Y -> alpha/vX/vX.Y\n```\n\nBuildchain then:\n\n1. Computes the next prerelease for the minor line.\n2. Writes version state such as `vX.Y.Z-alpha.N`.\n3. Verifies the generated version-state tree.\n4. Creates or reuses the exact alpha tag.\n5. Moves `alpha/vX/vX.Y` to the generated alpha commit.\n6. Moves `dev/vX/vX.Y` to the same generated alpha commit when this is a\n fast-forward update.\n7. Moves `vX.Y-alpha` to the same generated alpha commit.\n8. Moves `vX-alpha` only when `X.Y` is the highest minor in major `X` with a published alpha; older minor alpha work records a skip and cannot move the major channel backwards.\n\nThe npm channel follows the same ownership rule. The highest alpha minor publishes\nwith dist-tag `alpha`; maintenance alphas on an older minor publish with the\nline-specific dist-tag `vX.Y-alpha` so they cannot roll the global `alpha`\nchannel backward. Exact prerelease versions remain installable directly.\n\nThis keeps the test channel self-describing. If a consumer checks out\n`v2.0-alpha` or `v2-alpha`, the manifests and exact alpha tag agree. The major\nalpha ref removes routine consumer edits when Buildchain opens a newer minor,\nwhile exact tags and SHAs remain the reproducible audit choice.\n\n### Buildchain Alpha Self-Dogfood\n\nBuildchain continuously consumes its own current major alpha through\n`.github/workflows/buildchain-alpha-self-dogfood.yml`. Both lanes call the\nreleased channel router at `build.yml@v2-alpha`. The auto lane must resolve\n`v2-alpha`; the explicit stable lane must resolve `v2`. Both execute the same\ndeclared install, build, and verify fixture, proving that a single consumer\nsurface routes to distinct released runtimes without duplicating lifecycle\nconfiguration in the consumer.\n\nThe reusable build trust gate reads `job.workflow_ref`, which identifies the\ncalled workflow and its selected ref. It does not infer the runtime from\n`github.workflow_ref`, because GitHub defines that context as the caller\nworkflow identity during a reusable call.\n\nThe router selects `.buildchain/alpha-contract-lock.json` for alpha and\n`.buildchain/contract-lock.json` for stable. The alpha lock records the exact\nreviewed alpha SHA and compatibility digest; it does not replace the stable\nconsumer lock. A later alpha with only compatible additive drift continues,\nwhile a changed breaking digest fails until the new alpha contract is reviewed.\n\nThe evidence job resolves `v2-alpha` and `v2` through the GitHub refs API,\ncompares those immutable SHAs with the reusable workflow outputs, verifies the\n`alpha` and `stable` classifications, and uploads a JSON evidence artifact.\nThe canary runs after successful Buildchain ref promotion, on a daily fallback\nschedule, and on manual dispatch. It shares the release-promotion concurrency\ngroup, so another promotion cannot move the floating refs between runtime\nresolution and evidence comparison.\n\nThis is a post-publication consumer canary, not a release bootstrap. Source\nverification still runs the current commit, and\n`buildchain-ref-promotion.yml` still passes the verified exact SHA into the\npromotion workflow. Patrol, dev-merge, repair, and promotion defaults remain on\nstable or exact refs so a broken alpha cannot prevent Buildchain from publishing\nits fix. When Buildchain opens a new major, inventory validation requires this\nworkflow's fixed GitHub `uses` refs to move from `vN`/`vN-alpha` to the new\nmajor; GitHub does not allow expressions in a reusable-workflow `uses` ref.\n\nIf `dev/vX/vX.Y` has already advanced before generated alpha version-state\nbookkeeping can sync back, Buildchain records `skipped-non-fast-forward` for the\ndev sync and still completes the exact and floating alpha tags for the reviewed\nalpha commit. Later dev changes must go through their own dev-to-alpha\npromotion instead of rewinding dev. The normal path is direct: after alpha\nmerges, Buildchain applies the generated version-state commit to alpha and then\nfast-forwards dev to the same commit without a human version-state PR.\n\nIf alpha finalization is resumed after generated version-state bookkeeping was\npartially applied, Buildchain accepts the current alpha head as the generated\ncommit, or as a historical merge commit that contains the recorded release\nmaterial. An already-created exact alpha tag may point at the transaction\nrelease/material SHA or at the finalized alpha head; missing floating alpha\ntags are retried before the transaction becomes `complete`.\n\n## Release Semantics\n\nA release merge is:\n\n```text\nalpha/vX/vX.Y -> release/vX/vX.Y\n```\n\nBuildchain then:\n\n1. Finds the same-patch alpha tag that was tested.\n2. Checks that the release source tree matches that alpha tag tree, excluding\n only generated version-state differences.\n3. Writes final release version state such as `vX.Y.Z`.\n4. Verifies the generated release tree.\n5. Creates or reuses the exact release tag `vX.Y.Z`.\n6. Moves `release/vX/vX.Y` to the exact release commit.\n7. Moves `vX.Y` to the exact release commit.\n8. Moves `vX` when this minor line should be the stable major entrypoint.\n9. Prepares the next alpha version-state commit, such as\n `vX.Y.(Z+1)-alpha.0`.\n10. Moves `alpha/vX/vX.Y`, `dev/vX/vX.Y`, and `vX.Y-alpha` to that next alpha\n commit.\n11. Moves `vX-alpha` to that next alpha only when this remains the highest published alpha minor.\n\nThe production channel and the test channel therefore intentionally diverge\nafter release: production stays on the release commit, while alpha/dev continue\nat the next prerelease commit.\n\n### Stable Release Throttle And Canary Gate\n\nBuildchain's own stable channel has an additional pre-publication gate. It is\nevaluated after the PR-stage release candidate has been resolved and before the\npublish-gate ref, package registry, exact stable tag, or floating stable refs\nare mutated. Train refs and alpha promotion do not execute this gate.\n\nAll alpha and release promotions first run a metadata-only release-candidate\npreflight after queued-intent revalidation. The preflight uses the same resolver\nand exact target SHA as the publication job, but does not download payloads or\ninstall the candidate repository dependencies. Missing channel PRs, stale\nworkflow runs, expired passport pairs, and insufficient payload artifact sets\ntherefore fail before the full promotion job starts. The publication job still\ndownloads and validates the exact evidence again at the trust boundary; the\npreflight moves failure earlier without weakening the final check.\n\nThe policy is versioned in `.buildchain/stable-release-policy.json`. A stable\ncandidate is allowed only when all of these facts are true:\n\n- the candidate resolves to an immutable exact alpha tag and its GitHub\n prerelease timestamp;\n- at least 24 hours have passed since the preceding stable patch on the same\n minor line;\n- the preceding stable-to-alpha comparison contains a product or public\n contract path, not only version, test, evidence, or retrospective changes;\n- the version-bound impact record has a non-empty summary and at least one\n surface impact;\n- the `Build Surface Fixture` release-candidate run succeeded;\n- `site-libkungfu-dev` completed its no-apply `Buildchain Stable Canary` and an allowed\n maintainer attested that successful run on the exact alpha SHA through the\n `buildchain-canary/site-libkungfu-dev` commit-status context;\n- the canary runtime input is exactly the candidate alpha tag or the 40-character\n commit SHA resolved from that tag; a successful status pointing at another\n workflow, repository, tag, floating ref, or SHA is rejected as mismatched;\n- at least one hour has elapsed after the last required canary completed.\n\nThe machine report is written to\n`.buildchain/release-passport/stable-release-gate.json`. A passing report is\nuploaded with the stable release passport. A blocked run writes the same report\nbefore failing, so the missing or stale condition is inspectable without\nopening a publication transaction.\n\nGitHub's Actions run REST object does not expose `workflow_dispatch` inputs.\nBuildchain therefore verifies the run's `workflow_id` against the authoritative\nworkflow metadata, then reads the exact runtime from the workflow-owned\n`<workflow name> / <runtime ref>` run-name when no input field is available.\nAn explicit API input, when present, remains authoritative and cannot be\noverridden by the display name.\n\nThe cooldown is a minimum interval, not an instruction to release every day.\nCompatible work should still be batched until a stable release has a concrete\nconsumer need. Changing the interval, canary set, attestors, product path\nboundary, or soak time is a reviewed policy change.\n\nRepositories that want a predictable scheduled stable window can use\n[`Stable Candidate Patrol`](stable-candidate-patrol.md). It persists each exact\nalpha independently, qualifies it after repository-declared checks and soak,\nand selects the newest qualified non-revoked candidate. A newer soaking alpha\ndoes not invalidate an older qualified candidate. The selected tree enters the\nexisting strict `publish-gate/release/<line>/<version> -> release/<line>` PR\npath, so scheduled selection changes release intent timing without weakening\nsource locks, review, verification, publish transactions, or passports.\n\nIf release finalization is resumed after generated version-state bookkeeping was\npartially applied, Buildchain applies the same recovery rule: the current\nrelease head may be the generated commit, or a historical merge commit that\ncontains the recorded release material, existing exact tags and alpha/dev refs\nare accepted when they match the transaction, and missing floating `vX.Y` or\n`vX` tags are retried idempotently before completion.\n\nOnce the durable release transaction is `complete` and the exact/floating\nstable refs have moved, next-alpha preparation is post-release bookkeeping. A\nfailure there records `deferred-post-release-bookkeeping` and\n`next-anchor-required` instead of retroactively reporting the stable release as\nfailed. Generated next-alpha merges use the current dev tree as their base and\noverlay only declared version-state paths, preserving concurrent dev changes.\n\n## Major Gate Semantics\n\nA major gate merge is:\n\n```text\nrelease/vX/vX.Y -> publish-gate/major\n```\n\n`publish-gate/major` is the explicit replacement for the older ABV `main`\nchannel. The name is intentionally operational: it is a gate for a rare\nadministrator decision, not the active trunk. Keeping this decision in the same\nPR UI as alpha and release promotion keeps the human workflow simple while\navoiding the misleading meaning of `main`. The older `major-gate` branch name is\na compatibility alias only.\n\nBuildchain then:\n\n1. Verifies the source is a merged same-repository PR from a protected release\n line into `publish-gate/major`.\n2. Writes the next major production version state, such as `v(X+1).0.0`.\n3. Creates or reuses the exact release tag `v(X+1).0.0`.\n4. Moves `publish-gate/major` and `release/v(X+1)/v(X+1).0` to that release commit.\n5. Moves `v(X+1).0` and `v(X+1)` to that release commit.\n6. Prepares the next alpha version-state commit, such as\n `v(X+1).0.1-alpha.0`.\n7. Moves `alpha/v(X+1)/v(X+1).0`, `dev/v(X+1)/v(X+1).0`, and\n `v(X+1).0-alpha` to that next alpha commit.\n\nChecking out `publish-gate/major` should therefore look like a frozen release\nstate, not like a branch where day-to-day source work continues. Day-to-day\nsource work continues on `dev/vX/vX.Y`.\n\n## Protected Dev Branches\n\n`dev/vX/vX.Y` is a protected development channel, not a scratch branch. Normal\nsource changes should be made on work branches such as `feature/*`, `fix/*`,\n`chore/*`, `docs/*`, `ci/*`, or `refactor/*`, then reviewed through a pull\nrequest into the target dev line.\n\nThis keeps the earliest development channel audit-friendly:\n\n- the version line being changed is visible in the PR base branch;\n- CI and required checks run before the channel moves;\n- branch protection can prevent direct pushes and stale merges;\n- later `dev -> alpha -> release` promotion inherits a reviewable source\n lineage instead of trying to reconstruct how the dev branch changed.\n\nWhen required checks take longer than the normal dev-channel commit interval,\nclassic strict up-to-date protection can become a non-converging retry loop:\neach base update invalidates a completed check set and rebasing restarts the\nsame slow checks. Buildchain supports GitHub merge queues for that channel\nshape. The queue validates the projected merged result and serializes the final\nref update, so concurrent channel movement no longer invalidates the candidate.\n\nEvery required workflow must handle both `pull_request` and `merge_group`\nbefore the queue is enabled. Queue runs do not provide\n`github.event.pull_request`; required workflows must use the checked-out\n`github.sha` or event-neutral source facts. The governance command is dry-run by\ndefault and refuses to enable a queue when a declared required workflow lacks\neither trigger or still reads the pull-request-only payload directly:\n\n```bash\nbuildchain dev merge-queue \\\n --repository owner/repository \\\n --branch dev/v4/v4.0 \\\n --workflow .github/workflows/source-acceptance.yml \\\n --workflow .github/workflows/affected-native-pr.yml \\\n --bypass-app dedicated-release-app\n```\n\nAfter reviewing the plan, repeat with `--apply`. Buildchain creates or updates\nan exact-branch `merge_queue` ruleset first, then changes only the classic\nrequired-status-check policy from strict to loose. Reviews, administrator\nenforcement, conversation resolution, required check identities, force-push\nprotection, and deletion protection remain owned by the existing branch\nprotection. The ruleset uses the first merge method that the repository itself\nallows, and fails closed when the repository has no enabled merge method.\nRe-running the command is idempotent.\n\nThe repository policy can be declared once instead of repeated as CLI flags:\n\n```toml\n[governance.dev.merge_queue]\nmode = \"inherit\"\nrequired_workflows = [\".github/workflows/verify.yml\"]\n```\n\n`enabled` explicitly requires Buildchain to create or update an exact-branch\nqueue; `inherit` copies queue parameters and bypass actors from the repository's\ncurrent default dev branch; `disabled` prevents automatic queue creation. An\nabsent declaration behaves as `inherit` during release-line bootstrap so a new\nmajor or minor line does not silently lose governance already active on the\nprevious line. Required status-check identities still come from the new\nbranch's own classic protection rather than being copied from the old branch.\n\nMerge-queue rules also reject generated post-publish version-state ref updates.\nWhen the sealed promotion workflow uses a dedicated GitHub App, user, or team\nalready declared by release governance, repeat `--bypass-app`, `--bypass-user`,\nor `--bypass-team` to project that exact actor into the ruleset. Bypass actors\nare never inferred and broad repository or organization roles are not accepted.\nThis keeps ordinary feature PRs on the predecessor-aligned queue path while the\nsealed publication authority can finish its machine-verified bookkeeping. The\ndry-run receipt exposes the exact actor IDs before `--apply` changes GitHub.\n\nBuildchain provides the reusable\n`.github/workflows/dev-pr-auto-merge.yml` workflow for repositories that want a\nscheduled or manual \"merge ready dev PRs\" pass. The consumer repository owns\nthe trigger schedule, but the merge decision is declared through workflow\ninputs: target dev branch, required status/check names, ready and block labels,\nallowed work-branch prefixes, review requirements, maximum merges per run,\nmerge method, and dry-run mode.\n\nThe workflow defaults are conservative. A PR is skipped unless it targets the\nconfigured dev line, is not a draft, has the ready label, has no block label,\ncomes from the same repository, uses an allowed work-branch prefix, has a\ncurrent approval, is mergeable, and has the configured required checks passing.\n`landing-mode: auto` reads the target branch's native merge-queue state. When a\nqueue exists, Buildchain never calls the direct merge endpoint: it admits at\nmost one PR against the observed target-branch SHA and immutable PR head, then\ncalls GraphQL `enqueuePullRequest` with `expectedHeadOid`. GitHub's\n`merge_group` checks remain the final authority for the projected merge.\n\nThe admission receipt records the expected and observed base/head SHAs, policy\nchecks, decision, reason, and active predecessor. Buildchain re-reads the base,\nhead, mergeability, and native queue immediately before enqueueing. Base or\nhead drift fails closed, an active queue entry blocks admission, and a rejected\nready predecessor leaves its PR open while later PRs receive\n`blocked-by-predecessor`. Workflow concurrency serializes Buildchain-owned\nadmission runs; GitHub still owns the atomic queue and protected-ref update.\nRepositories may explicitly select `landing-mode: direct` only when the target\nbranch has no native queue. Queue presence always disables the direct path.\n\nThe canonical consumer required check context is `check / check`, matching the\nreusable workflow call plus its `check` job. Buildchain's own `Verify` workflow\nemits the repository-local context `check`, so Buildchain self-promotion,\nrelease-line bootstrap, and dogfood patrol wrappers pass `check` explicitly.\nRelease governance preserves the exact context emitted by the repository and\nrecords branch-protection policy before/after facts; it does not rewrite one\ncontext shape into the other. Consumer repositories can keep their context\nstable while changing the actual verification command declaratively in\n`buildchain.toml`:\n\n```toml\n[lifecycle.install]\ncommand = \"cargo fetch --locked\"\n\n[lifecycle.verify]\ncommand = \"cargo test --workspace --locked\"\n```\n\nConsumers that want Buildchain to own the check wrapper can call\n`.github/workflows/check.yml@v2`. The wrapper runs the declared\n`lifecycle.install` and `lifecycle.verify` stages and fails the `check` job when\neither declaration is missing or the command exits non-zero.\n\nDevelopment pull requests that need source acceptance without product build or\nartifact verification can opt into `mode: source`. That mode runs only\n`lifecycle.install` and `lifecycle.check` on GitHub-hosted `ubuntu-24.04` while\npreserving the stable `check / check` required-check context. Existing callers\nremain on `mode: verify` by default, and callers may set\n`upload-artifacts: false` without weakening the job conclusion.\n\nTypical consumer wrapper:\n\n```yaml\nname: Merge Ready Dev PRs\n\non:\n schedule:\n - cron: \"17 * * * *\"\n workflow_dispatch:\n inputs:\n dry-run:\n type: boolean\n default: true\n\njobs:\n merge-dev:\n uses: kungfu-systems/buildchain/.github/workflows/dev-pr-auto-merge.yml@v2\n permissions:\n contents: write\n pull-requests: write\n checks: read\n statuses: read\n with:\n target-branch: dev/v2/v2.6\n required-status-checks: check / check\n ready-label: ready\n block-labels: blocked,do-not-merge\n max-merges: 1\n landing-mode: auto\n dry-run: ${{ inputs.dry-run || false }}\n```\n\n## Buildchain Patrol\n\n`dev-pr-auto-merge.yml` remains the focused merge primitive. For repositories\nthat want a stable day-to-day operations contract, Buildchain also exposes a\npatrol workflow family:\n\n| Workflow | Intended cadence | Default intent |\n| --- | --- | --- |\n| `.github/workflows/patrol-daily.yml` | daily | lightweight inspection plus ready dev PR maintenance |\n| `.github/workflows/patrol-weekly.yml` | weekly | release-state, passport, gate, and stale-state health checks as they are added |\n| `.github/workflows/patrol-monthly.yml` | monthly | governance, permission, branch-protection, and workflow drift checks as they are added |\n| `.github/workflows/stable-candidate-patrol.yml` | repository-selected release window | qualify immutable alpha candidates and open the exact source-lock stable PR |\n\nThe cadence names describe patrol intensity, not release cadence:\n\n- daily patrol can run every day without implying a daily release;\n- weekly patrol is for medium-cost maintenance and audit checks;\n- monthly patrol is for structural drift checks that should not block ordinary\n development velocity.\n\nStable Candidate Patrol is separate from those maintenance cadences because its\ncaller-owned cron is a release-intent window. Its candidate ledger and selection\nremain generic; registry-specific side effects still run through the normal\nrepository `lifecycle.publish` transaction. See\n[`stable-candidate-patrol.md`](stable-candidate-patrol.md).\n\nConsumers should schedule thin callers and keep their YAML declarative. For\nexample:\n\n```yaml\nname: Buildchain Daily Patrol\n\non:\n schedule:\n - cron: \"17 2 * * *\"\n workflow_dispatch:\n\njobs:\n patrol:\n uses: kungfu-systems/buildchain/.github/workflows/patrol-daily.yml@v2\n with:\n dry-run: false\n max-actions: 1\n```\n\nWeekly and monthly callers use the matching wrapper:\n\n```yaml\njobs:\n patrol:\n uses: kungfu-systems/buildchain/.github/workflows/patrol-weekly.yml@v2\n with:\n dry-run: true\n```\n\nAll three wrappers default to the `v2` floating Buildchain runtime. When\n`target-branch` is omitted, the caller's current/default branch selects the\nactive semver dev line, so consumers do not pin patrol to a stale minor branch.\nThe separate workflow names keep consumer schedules readable and stable while\nBuildchain adds new checks behind the cadence wrappers.\n\n## Package-Manager Adapters\n\nOld ABV assumed JavaScript repositories with root version state and often\nLerna. Buildchain keeps the version-state contract but does not assume every\nrepository is yarn/Lerna.\n\nThe promotion action discovers and updates:\n\n- root `package.json`;\n- `lerna.json`;\n- package manifests from `package.json` workspaces;\n- package manifests from `lerna.json` packages;\n- package manifests from `pnpm-workspace.yaml`.\n\nIt then runs the repository's detected package manager semantics where needed:\n\n- pnpm repositories use pnpm-oriented workspace discovery;\n- npm repositories use npm/package-lock semantics where present;\n- yarn repositories use yarn-style metadata where present.\n\nFor Buildchain itself, version state is required. For a consumer repository that\nhas no package manifest, the same action can degrade to ref-only behavior only\nwhen that is explicitly allowed by the caller.\n\n## Lifecycle Configuration\n\n`buildchain.toml` is the v2 user configuration format. It lets a repository\ndeclare version-state files and lifecycle commands without pretending every\nproject is a Node workspace. Supported version files include JSON, TOML, and\nregex-based files such as `CMakeLists.txt` or `conanfile.py`.\n\nThe promotion action currently consumes `version.files` and `lifecycle.verify`.\nThe verify stage runs after generated version-state changes are applied locally\nand before any release refs move. If `verification-command` is passed directly\nto the action, that explicit command overrides `lifecycle.verify`.\n\nProtected release-line branches keep their normal human review gate. Managed\n`dev/vN/vN.M`, `alpha/vN/vN.M`, and `release/vN/vN.M` branches are configured\nwith one required approving review, required GitHub Actions checks, administrator\nenforcement, conversation resolution, no force pushes, and no deletions. Dev\nkeeps its repository-selected strict or merge-queue policy. Alpha and release\nrequire both `check` and the pair-specific `verify` aggregate without requiring\nthe source branch to contain the target's generated bookkeeping history. The\nreusable `release-candidate-promote.yml` wrapper defaults\n`branch-protection-bypass-apps` to `github-actions`, which lets the workflow's\nautomation identity apply generated version-state or post-publish channel\nbookkeeping after the reviewed channel PR has merged. Direct\n`promote-buildchain-ref` callers must opt into the same controlled bypass with\n`branch-protection-bypass-apps`, `branch-protection-bypass-users`, or\n`branch-protection-bypass-teams`; the action also adds the current promotion\ntoken's authenticated user or app to the managed bypass allowlist. Before\npatching a protected generated bookkeeping ref, the action creates the\nfull configured required-check set on the exact generated version-state commit, so strict\nstatus checks are satisfied by machine-verifiable Buildchain evidence rather\nthan a human PR. The protected ref PATCH itself uses the generated ref update\ntoken; the reusable wrapper defaults it to\n`secrets.BUILDCHAIN_PROMOTION_TOKEN || github.token`. If direct generated\nrelease finalization bookkeeping is still rejected, Buildchain creates or\nreuses a same-repository `buildchain/version-state/*` PR based on the current\ntarget channel head and records `finalization-needed=true` in the durable\ntransaction output. Strict alpha bookkeeping still fails with a\ntoken/protection diagnostic instead of creating a post-publish PR.\n\nFor a stable release, the wrapper also checks out the exact current development\nchannel into `.buildchain/reconciliation/dev`. When the prepared next-alpha\ncommit cannot fast-forward dev because reviewed work landed concurrently, the\npromotion action applies the next version to that checkout, regenerates every\ndeclared derived version-state file, reruns the verification lifecycle, and\nonly then creates the two-parent reconciliation commit. A checkout/current-ref\nSHA mismatch blocks reconciliation instead of committing stale projections.\nBuildchain's own promotion workflow reads `BUILDCHAIN_PROMOTION_BYPASS_APPS`,\n`BUILDCHAIN_PROMOTION_BYPASS_USERS`, and\n`BUILDCHAIN_PROMOTION_BYPASS_TEAMS` repository variables so the declared bypass\nidentity can match the actual `BUILDCHAIN_PROMOTION_TOKEN` actor, but consumers\ndo not need to duplicate that actor manually when the token identity is\ndiscoverable. Buildchain's release-line bootstrap applies the same declared\nallowances when it creates new `dev`, `alpha`, and `release` branch protection,\nand fails closed before changing protection when no promotion authority is\ndeclared. This keeps a new minor line publishable without weakening its normal\nhuman review gate.\n\n## What This Guarantees\n\nWhen the loop succeeds, maintainers and consumers can rely on these facts:\n\n- every production release has an exact tag such as `v2.0.2`;\n- every production minor line has a floating tag such as `v2.0`;\n- every selected stable major has a floating tag such as `v2`;\n- every next-major release is driven by a reviewed `release -> publish-gate/major` PR,\n not a hidden manual button;\n- every test channel has an exact alpha tag such as `v2.0.3-alpha.0`;\n- every alpha minor line has a floating tag such as `v2.0-alpha`;\n- every major with a published alpha has a cross-minor floating tag such as `v2-alpha`, owned by its highest published alpha minor;\n- version manifests match the tag visible from the same commit;\n- production releases are derived from the alpha tree that was tested;\n- manual non-dry-run promotion cannot bypass PR review and verification;\n- flow-internal automation bypasses apply only to declared GitHub Apps, users,\n or teams on Buildchain-managed channel branch protection, while one-review\n protection remains enforced for humans;\n- admin users cannot make a channel promotion valid by temporarily bypassing\n branch protection.\n\nThis is the practical meaning of \"governance closed loop\" in Buildchain: the\ndecision, code, version state, and Git refs close over the same evidence chain.\n\n## What This Does Not Do\n\nBuildchain release promotion does not embed registry clients or product-specific\npublish logic. When publish transactions are enabled, `promote-buildchain-ref`\ncan run the consumer's `lifecycle.publish` command and own the transaction,\nevidence validation, durable recovery state, and ref finalization order. The\nconsumer repository still owns registry truth: npm, PyPI, OCI, S3, Conan, CMake\npackaging, download pages, dist-tags, and similar side effects must be\nimplemented by project lifecycle commands that emit Buildchain publish evidence.\n\nEvery Buildchain publish model that can run registry side effects must bind the\npublish entrypoint to an immutable `publish-gate/*` source lock. The reusable\n`release-candidate-promote.yml@v2` wrapper creates or updates that gate ref and\npasses `require-publish-source-lock`, `publish-source-ref`,\n`publish-source-sha`, and `publish-source-locked` to\n`promote-buildchain-ref`. Direct action callers must pass the same four inputs\nfrom the reusable build outputs. Workflows that only collect passports or run\ndry-run package checks do not move publish refs and are not publish-gate\npublication models. A dry-run of `release-candidate-promote.yml` computes and\nreports the exact `publish-gate/*` source lock that a real promotion would use,\nbut does not read, create, or move that ref.\n\nSemver GitHub Release publication is owned by `promote-buildchain-ref`, not by\nconsumer shell glue. Consumers normally use the `release-candidate-promote.yml`\nwrapper, where GitHub Release publication is enabled by default and can be\ndisabled with `github-release: false`; the wrapper passes that declaration to\nthe action. After the publish transaction reaches `complete`, Buildchain creates\nor updates the public GitHub Release and uploads the generated\n`buildchain.release.json`, release-passport assets, and publish evidence. Semver\nprerelease tags are marked `prerelease=true` and `make_latest=false`; stable\nsemver tags are marked latest. For anchored/manual package releases, the public\nrelease tag defaults to `v<publishedVersion>` while the internal exact\ntransaction tag remains in the release passport and release-state ref. This is\nthe supported path for downstream\n`release.published` propagation across semver, major, and promote-only release\ncandidate publication models.\n\nPublished GitHub Release assets are immutable evidence. A repeated promotion\npreserves an existing asset when its SHA-256 digest matches the regenerated\nbytes, uploads only missing assets, and fails with an immutable-release\ncollision when a same-name asset has different bytes. It never deletes and\nreplaces an existing asset during retry or duplicate workflow delivery.\n\nBuildchain also does not maintain bare exact tags such as `1.0.0`. The supported\nexact release and alpha refs are v-prefixed:\n\n```text\nv2.0.0\nv2.0.1-alpha.0\n```\n\n## Operational Reading Order\n\nWhen debugging or extending release behavior, read in this order:\n\n1. `docs/release-flow.md`\n2. `.github/workflows/release-verify.yml`\n3. `.github/workflows/buildchain-ref-promotion.yml`\n4. `.github/workflows/release-candidate-promote.yml`\n5. `actions/promote-buildchain-ref/README.md`\n6. `actions/promote-buildchain-ref/src/`\n7. `docs/migration-inventory.md`\n\nThat path gives the policy first, the workflow trigger second, and the action\nimplementation last."
1479
+ "markdown": "# Release Governance\n\nBuildchain v2 preserves the release semantics of the older ABV workflow while\nmoving the implementation into one modern repository.\n\nThe central idea is simple: a reviewed merge into a release channel is the\nrelease intent. Automation must then create the version-state commit, exact tag,\nfloating tag, and next alpha state that make that intent true in Git.\n\n## Design Problem\n\nKungfu release automation has to keep four facts aligned:\n\n1. The source tree that was reviewed.\n2. The package version recorded in manifests such as `package.json` or\n `lerna.json`.\n3. The exact immutable release or prerelease tag.\n4. The floating channel refs that consumers actually use.\n\nIf any one of these facts is updated by hand, the system can split:\n\n- a consumer can fetch `v2.0` and receive a tree whose package version still\n says the previous release;\n- a maintainer can move `v2` without producing an exact `v2.0.N` audit tag;\n- an alpha can be promoted to production even though the release tree is not the\n same tree that was tested;\n- a protected branch merge can succeed while the follow-up version commit is\n missing, or a flow-internal generated `dev`/`alpha`/`release` ref update can\n fail after publish because the automation identity was not declared in the\n branch-protection review bypass allowance.\n\nThe older ABV workflow addressed this by letting GitHub PRs drive release\nstate. Buildchain keeps that choice because it makes release intent reviewable,\nobservable, and recoverable from Git history.\n\n## What ABV Contributed\n\nThe old ABV model was not just \"bump a version number.\" It encoded a governance\nloop:\n\n- release branches are named as channels: `dev`, `alpha`, `release`, and the\n administrative `publish-gate/major`;\n- a PR from one channel to the next is the release request;\n- verify jobs check that the branch pair is valid before merge;\n- a maintainer review is required before the branch moves;\n- after merge, automation writes the version change and moves tags;\n- exact tags and floating refs are aligned with the resulting commit;\n- the next development channel is prepared automatically.\n\nABV also kept the version-state mutation in the repository. For JavaScript\nrepositories that usually meant changing `lerna.json` and/or `package.json`.\nThat commit is important because the tag alone is not enough evidence: the\nsource tree should also declare the version that the tag advertises.\n\nBuildchain v2 treats that as a hard semantic requirement for its own release\nline.\n\n## Buildchain Implementation\n\nBuildchain implements the same governance loop with:\n\n- `.github/workflows/release-verify.yml` for PR verification;\n- `.github/workflows/buildchain-ref-promotion.yml` for post-verify ref\n promotion; this workflow dogfoods the declarative\n `release-candidate-promote.yml` wrapper and does not hand-wire resolver,\n artifact download, publish-gate, or promote action steps;\n- Buildchain self promotion enables `release-passport-buildchain-self-kfd`, so\n the promote action generates KFD-1 witnesses, KFD-2 public claim JSON, and\n KFD-3 collaboration-interface witnesses from the final version-state workspace\n before release passport finalization. The witness hashes therefore bind to the\n exact published package and site facts from\n `packages/core/buildchain-kfd-claims.js` instead of relying on prose release\n notes;\n- `actions/promote-buildchain-ref` for branch, tag, version-state, and\n governance checks;\n- package-manager adapters that can update version state for pnpm, npm, and\n yarn style repositories;\n- `buildchain.toml` lifecycle configuration for repositories whose version\n state or verification commands are not Node package-manager defaults.\n\nThe implementation is intentionally stricter than a local release script:\n\n- manual workflow dispatch can only do dry-run promotion;\n- non-dry-run promotion must be driven by a completed `Verify` workflow;\n- target branch protection details must be readable, and branch protection must\n apply to administrators as well as regular contributors;\n- `alpha/vX/vX.Y` and `release/vX/vX.Y` branch protection must require both the\n general `check` context and the Release Verify aggregate `verify` context, so\n an invalid channel pair cannot merge even when repository checks pass;\n- release targets keep those checks non-strict with respect to source-branch\n ancestry: generated channel bookkeeping intentionally makes the source and\n target histories diverge, while the pair-specific `verify` context validates\n the legal channel transition;\n- alpha promotion must come from a merged same-repository PR from\n `dev/vX/vX.Y` to `alpha/vX/vX.Y`;\n- release promotion must come from a merged same-repository PR from\n `alpha/vX/vX.Y` to `release/vX/vX.Y`;\n- major promotion must come from a merged same-repository PR from\n `release/vX/vX.Y` to `publish-gate/major`;\n- release promotion requires an existing same-patch alpha tag and checks the\n release source tree against that tested alpha tree;\n- generated version-state commits are verified before refs move.\n\nExact publication planning installs the checked-out promotion source's declared\ndependencies before version-state verification. This keeps the pre-authority\nversion plan on the same package-manager boundary as the later promotion job,\nincluding repositories whose verification commands import production packages.\nThe planning pass may materialize and verify declared derived files locally,\nbut dry-run never creates Git blobs, trees, commits, refs, or tags.\n\nPromotion intents are serialized globally per caller repository with\n`cancel-in-progress: false`. A queued intent re-reads its protected target ref\nbefore checkout, dependency installation, release-candidate resolution, or any\npublish-gate/ref mutation. If the ref already points at a newer SHA, that older\nintent is no longer release authority: the workflow records the requested and\ncurrent SHAs, proves that the current target is ahead of the requested commit,\nand completes as a `target-ref-advanced` superseded no-op. Diverged or behind\ncomparisons are not superseded transactions and still fail closed.\nMissing refs, unreadable repository state, invalid channels, governance\nfailures, and artifact mismatches still fail closed. The promote action repeats\nthe target check at the mutation boundary, so a ref that advances after the\nworkflow preflight cannot receive a second set of publication side effects.\n\n## Version Lines\n\nKungfu uses Python-like version lines where a minor line can represent a\nlong-lived product train. A line such as `v2.0` can produce many production\npatch releases:\n\n```text\nv2.0.0\nv2.0.1\nv2.0.2\n...\nv2.0.1234\n```\n\nThis is why Buildchain maintains both exact and floating refs:\n\n- `v2.0.2` is immutable release evidence;\n- `v2.0` is the latest production release on the `2.0` line;\n- `v2` is the selected stable major-line entrypoint;\n- `v2.0.3-alpha.0` is immutable alpha evidence;\n- `v2.0-alpha` is the latest test channel for the `2.0` line.\n- `v2-alpha` is the latest test channel on the highest published alpha minor in major `2`.\n\nA release does not mean \"minor is complete.\" It means \"this patch on this minor\nline is now production.\"\n\nGitHub repository rules must preserve that distinction. Exact tags such as\n`v2.0.2` and `v2.0.3-alpha.0` should be immutable. Floating channel tags such as\n`v2`, `v2.0`, `v2.0-alpha`, and `v2-alpha` must remain movable by the Buildchain promotion\ntoken after governance checks and publish evidence pass. A tag ruleset that\nprotects every `refs/tags/v*` ref is too broad because it also locks the\nfloating channel tags that Buildchain is required to update. Prefer exact-tag\npatterns such as `refs/tags/v*.*.*` for immutable release evidence, while\nleaving floating channel tags under Buildchain automation control.\n\n## Alpha Semantics\n\nAn alpha merge is:\n\n```text\ndev/vX/vX.Y -> alpha/vX/vX.Y\n```\n\nBuildchain then:\n\n1. Computes the next prerelease for the minor line.\n2. Writes version state such as `vX.Y.Z-alpha.N`.\n3. Verifies the generated version-state tree.\n4. Creates or reuses the exact alpha tag.\n5. Moves `alpha/vX/vX.Y` to the generated alpha commit.\n6. Moves `dev/vX/vX.Y` to the same generated alpha commit when this is a\n fast-forward update.\n7. Moves `vX.Y-alpha` to the same generated alpha commit.\n8. Moves `vX-alpha` only when `X.Y` is the highest minor in major `X` with a published alpha; older minor alpha work records a skip and cannot move the major channel backwards.\n\nThe npm channel follows the same ownership rule. The highest alpha minor publishes\nwith dist-tag `alpha`; maintenance alphas on an older minor publish with the\nline-specific dist-tag `vX.Y-alpha` so they cannot roll the global `alpha`\nchannel backward. Exact prerelease versions remain installable directly.\n\nThis keeps the test channel self-describing. If a consumer checks out\n`v2.0-alpha` or `v2-alpha`, the manifests and exact alpha tag agree. The major\nalpha ref removes routine consumer edits when Buildchain opens a newer minor,\nwhile exact tags and SHAs remain the reproducible audit choice.\n\n### Buildchain Alpha Self-Dogfood\n\nBuildchain continuously consumes its own current major alpha through\n`.github/workflows/buildchain-alpha-self-dogfood.yml`. Both lanes call the\nreleased channel router at `build.yml@v2-alpha`. The auto lane must resolve\n`v2-alpha`; the explicit stable lane must resolve `v2`. Both execute the same\ndeclared install, build, and verify fixture, proving that a single consumer\nsurface routes to distinct released runtimes without duplicating lifecycle\nconfiguration in the consumer.\n\nThe reusable build trust gate reads `job.workflow_ref`, which identifies the\ncalled workflow and its selected ref. It does not infer the runtime from\n`github.workflow_ref`, because GitHub defines that context as the caller\nworkflow identity during a reusable call.\n\nThe router selects `.buildchain/alpha-contract-lock.json` for alpha and\n`.buildchain/contract-lock.json` for stable. The alpha lock records the exact\nreviewed alpha SHA and compatibility digest; it does not replace the stable\nconsumer lock. A later alpha with only compatible additive drift continues,\nwhile a changed breaking digest fails until the new alpha contract is reviewed.\n\nThe evidence job resolves `v2-alpha` and `v2` through the GitHub refs API,\ncompares those immutable SHAs with the reusable workflow outputs, verifies the\n`alpha` and `stable` classifications, and uploads a JSON evidence artifact.\nThe canary runs after successful Buildchain ref promotion, on a daily fallback\nschedule, and on manual dispatch. It shares the release-promotion concurrency\ngroup, so another promotion cannot move the floating refs between runtime\nresolution and evidence comparison.\n\nThis is a post-publication consumer canary, not a release bootstrap. Source\nverification still runs the current commit, and\n`buildchain-ref-promotion.yml` still passes the verified exact SHA into the\npromotion workflow. Patrol, dev-merge, repair, and promotion defaults remain on\nstable or exact refs so a broken alpha cannot prevent Buildchain from publishing\nits fix. When Buildchain opens a new major, inventory validation requires this\nworkflow's fixed GitHub `uses` refs to move from `vN`/`vN-alpha` to the new\nmajor; GitHub does not allow expressions in a reusable-workflow `uses` ref.\n\nIf `dev/vX/vX.Y` has already advanced before generated alpha version-state\nbookkeeping can sync back, Buildchain records `skipped-non-fast-forward` for the\ndev sync and still completes the exact and floating alpha tags for the reviewed\nalpha commit. Later dev changes must go through their own dev-to-alpha\npromotion instead of rewinding dev. The normal path is direct: after alpha\nmerges, Buildchain applies the generated version-state commit to alpha and then\nfast-forwards dev to the same commit without a human version-state PR.\n\nIf alpha finalization is resumed after generated version-state bookkeeping was\npartially applied, Buildchain accepts the current alpha head as the generated\ncommit, or as a historical merge commit that contains the recorded release\nmaterial. An already-created exact alpha tag may point at the transaction\nrelease/material SHA or at the finalized alpha head; missing floating alpha\ntags are retried before the transaction becomes `complete`.\n\n## Release Semantics\n\nA release merge is:\n\n```text\nalpha/vX/vX.Y -> release/vX/vX.Y\n```\n\nBuildchain then:\n\n1. Finds the same-patch alpha tag that was tested.\n2. Checks that the release source tree matches that alpha tag tree, excluding\n only generated version-state differences.\n3. Writes final release version state such as `vX.Y.Z`.\n4. Verifies the generated release tree.\n5. Creates or reuses the exact release tag `vX.Y.Z`.\n6. Moves `release/vX/vX.Y` to the exact release commit.\n7. Moves `vX.Y` to the exact release commit.\n8. Moves `vX` when this minor line should be the stable major entrypoint.\n9. Prepares the next alpha version-state commit, such as\n `vX.Y.(Z+1)-alpha.0`.\n10. Moves `alpha/vX/vX.Y`, `dev/vX/vX.Y`, and `vX.Y-alpha` to that next alpha\n commit.\n11. Moves `vX-alpha` to that next alpha only when this remains the highest published alpha minor.\n\nThe production channel and the test channel therefore intentionally diverge\nafter release: production stays on the release commit, while alpha/dev continue\nat the next prerelease commit.\n\n### Stable Release Throttle And Canary Gate\n\nBuildchain's own stable channel has an additional pre-publication gate. It is\nevaluated after the PR-stage release candidate has been resolved and before the\npublish-gate ref, package registry, exact stable tag, or floating stable refs\nare mutated. Train refs and alpha promotion do not execute this gate.\n\nAll alpha and release promotions first run a metadata-only release-candidate\npreflight after queued-intent revalidation. The preflight uses the same resolver\nand exact target SHA as the publication job, but does not download payloads or\ninstall the candidate repository dependencies. Missing channel PRs, stale\nworkflow runs, expired passport pairs, and insufficient payload artifact sets\ntherefore fail before the full promotion job starts. The publication job still\ndownloads and validates the exact evidence again at the trust boundary; the\npreflight moves failure earlier without weakening the final check.\n\nThe policy is versioned in `.buildchain/stable-release-policy.json`. A stable\ncandidate is allowed only when all of these facts are true:\n\n- the candidate resolves to an immutable exact alpha tag and its GitHub\n prerelease timestamp;\n- at least 24 hours have passed since the preceding stable patch on the same\n minor line;\n- the preceding stable-to-alpha comparison contains a product or public\n contract path, not only version, test, evidence, or retrospective changes;\n- the version-bound impact record has a non-empty summary and at least one\n surface impact;\n- the `Build Surface Fixture` release-candidate run succeeded;\n- `site-libkungfu-dev` completed its no-apply `Buildchain Stable Canary` and an allowed\n maintainer attested that successful run on the exact alpha SHA through the\n `buildchain-canary/site-libkungfu-dev` commit-status context;\n- the canary runtime input is exactly the candidate alpha tag or the 40-character\n commit SHA resolved from that tag; a successful status pointing at another\n workflow, repository, tag, floating ref, or SHA is rejected as mismatched;\n- at least one hour has elapsed after the last required canary completed.\n\nThe machine report is written to\n`.buildchain/release-passport/stable-release-gate.json`. A passing report is\nuploaded with the stable release passport. A blocked run writes the same report\nbefore failing, so the missing or stale condition is inspectable without\nopening a publication transaction.\n\nGitHub's Actions run REST object does not expose `workflow_dispatch` inputs.\nBuildchain therefore verifies the run's `workflow_id` against the authoritative\nworkflow metadata, then reads the exact runtime from the workflow-owned\n`<workflow name> / <runtime ref>` run-name when no input field is available.\nAn explicit API input, when present, remains authoritative and cannot be\noverridden by the display name.\n\nThe cooldown is a minimum interval, not an instruction to release every day.\nCompatible work should still be batched until a stable release has a concrete\nconsumer need. Changing the interval, canary set, attestors, product path\nboundary, or soak time is a reviewed policy change.\n\nRepositories that want a predictable scheduled stable window can use\n[`Stable Candidate Patrol`](stable-candidate-patrol.md). It persists each exact\nalpha independently, qualifies it after repository-declared checks and soak,\nand selects the newest qualified non-revoked candidate. A newer soaking alpha\ndoes not invalidate an older qualified candidate. The selected tree enters the\nexisting strict `publish-gate/release/<line>/<version> -> release/<line>` PR\npath, so scheduled selection changes release intent timing without weakening\nsource locks, review, verification, publish transactions, or passports.\n\nIf release finalization is resumed after generated version-state bookkeeping was\npartially applied, Buildchain applies the same recovery rule: the current\nrelease head may be the generated commit, or a historical merge commit that\ncontains the recorded release material, existing exact tags and alpha/dev refs\nare accepted when they match the transaction, and missing floating `vX.Y` or\n`vX` tags are retried idempotently before completion.\n\nOnce the durable release transaction is `complete` and the exact/floating\nstable refs have moved, next-alpha preparation is post-release bookkeeping. A\nfailure there records `deferred-post-release-bookkeeping` and\n`next-anchor-required` instead of retroactively reporting the stable release as\nfailed. Generated next-alpha merges use the current dev tree as their base and\noverlay only declared version-state paths, preserving concurrent dev changes.\n\n## Major Gate Semantics\n\nA major gate merge is:\n\n```text\nrelease/vX/vX.Y -> publish-gate/major\n```\n\n`publish-gate/major` is the explicit replacement for the older ABV `main`\nchannel. The name is intentionally operational: it is a gate for a rare\nadministrator decision, not the active trunk. Keeping this decision in the same\nPR UI as alpha and release promotion keeps the human workflow simple while\navoiding the misleading meaning of `main`. The older `major-gate` branch name is\na compatibility alias only.\n\nBuildchain then:\n\n1. Verifies the source is a merged same-repository PR from a protected release\n line into `publish-gate/major`.\n2. Writes the next major production version state, such as `v(X+1).0.0`.\n3. Creates or reuses the exact release tag `v(X+1).0.0`.\n4. Moves `publish-gate/major` and `release/v(X+1)/v(X+1).0` to that release commit.\n5. Moves `v(X+1).0` and `v(X+1)` to that release commit.\n6. Prepares the next alpha version-state commit, such as\n `v(X+1).0.1-alpha.0`.\n7. Moves `alpha/v(X+1)/v(X+1).0`, `dev/v(X+1)/v(X+1).0`, and\n `v(X+1).0-alpha` to that next alpha commit.\n\nChecking out `publish-gate/major` should therefore look like a frozen release\nstate, not like a branch where day-to-day source work continues. Day-to-day\nsource work continues on `dev/vX/vX.Y`.\n\n## Protected Dev Branches\n\n`dev/vX/vX.Y` is a protected development channel, not a scratch branch. Normal\nsource changes should be made on work branches such as `feature/*`, `fix/*`,\n`chore/*`, `docs/*`, `ci/*`, or `refactor/*`, then reviewed through a pull\nrequest into the target dev line.\n\nThis keeps the earliest development channel audit-friendly:\n\n- the version line being changed is visible in the PR base branch;\n- CI and required checks run before the channel moves;\n- branch protection can prevent direct pushes and stale merges;\n- later `dev -> alpha -> release` promotion inherits a reviewable source\n lineage instead of trying to reconstruct how the dev branch changed.\n\nWhen required checks take longer than the normal dev-channel commit interval,\nclassic strict up-to-date protection can become a non-converging retry loop:\neach base update invalidates a completed check set and rebasing restarts the\nsame slow checks. Buildchain supports GitHub merge queues for that channel\nshape. The queue validates the projected merged result and serializes the final\nref update, so concurrent channel movement no longer invalidates the candidate.\n\nEvery required workflow must handle both `pull_request` and `merge_group`\nbefore the queue is enabled. Queue runs do not provide\n`github.event.pull_request`; required workflows must use the checked-out\n`github.sha` or event-neutral source facts. The governance command is dry-run by\ndefault and refuses to enable a queue when a declared required workflow lacks\neither trigger or still reads the pull-request-only payload directly:\n\n```bash\nbuildchain dev merge-queue \\\n --repository owner/repository \\\n --branch dev/v4/v4.0 \\\n --workflow .github/workflows/source-acceptance.yml \\\n --workflow .github/workflows/affected-native-pr.yml \\\n --bypass-app dedicated-release-app\n```\n\nAfter reviewing the plan, repeat with `--apply`. Buildchain creates or updates\nan exact-branch `merge_queue` ruleset first, then changes only the classic\nrequired-status-check policy from strict to loose. Reviews, administrator\nenforcement, conversation resolution, required check identities, force-push\nprotection, and deletion protection remain owned by the existing branch\nprotection. The ruleset uses the first merge method that the repository itself\nallows, and fails closed when the repository has no enabled merge method.\nRe-running the command is idempotent.\n\nThe repository policy can be declared once instead of repeated as CLI flags:\n\n```toml\n[governance.dev.merge_queue]\nmode = \"inherit\"\nrequired_workflows = [\".github/workflows/verify.yml\"]\n```\n\n`enabled` explicitly requires Buildchain to create or update an exact-branch\nqueue; `inherit` copies queue parameters and bypass actors from the repository's\ncurrent default dev branch; `disabled` prevents automatic queue creation. An\nabsent declaration behaves as `inherit` during release-line bootstrap so a new\nmajor or minor line does not silently lose governance already active on the\nprevious line. Required status-check identities still come from the new\nbranch's own classic protection rather than being copied from the old branch.\n\nMerge-queue rules also reject generated post-publish version-state ref updates.\nWhen the sealed promotion workflow uses a dedicated GitHub App, user, or team\nalready declared by release governance, repeat `--bypass-app`, `--bypass-user`,\nor `--bypass-team` to project that exact actor into the ruleset. Bypass actors\nare never inferred and broad repository or organization roles are not accepted.\nThis keeps ordinary feature PRs on the predecessor-aligned queue path while the\nsealed publication authority can finish its machine-verified bookkeeping. The\ndry-run receipt exposes the exact actor IDs before `--apply` changes GitHub.\n\nBuildchain provides the reusable\n`.github/workflows/dev-pr-auto-merge.yml` workflow for repositories that want a\nscheduled or manual \"merge ready dev PRs\" pass. The consumer repository owns\nthe trigger schedule, but the merge decision is declared through workflow\ninputs: target dev branch, required status/check names, ready and block labels,\nallowed work-branch prefixes, review requirements, maximum merges per run,\nmerge method, and dry-run mode.\n\nThe workflow defaults are conservative. A PR is skipped unless it targets the\nconfigured dev line, is not a draft, has the ready label, has no block label,\ncomes from the same repository, uses an allowed work-branch prefix, has a\ncurrent approval, is mergeable, and has the configured required checks passing.\n`landing-mode: auto` reads the target branch's native merge-queue state. When a\nqueue exists, Buildchain never calls the direct merge endpoint: it admits at\nmost one PR against the observed target-branch SHA and immutable PR head, then\ncalls GraphQL `enqueuePullRequest` with `expectedHeadOid`. GitHub's\n`merge_group` checks remain the final authority for the projected merge.\n\nThe admission receipt records the expected and observed base/head SHAs, policy\nchecks, decision, reason, and active predecessor. Buildchain re-reads the base,\nhead, mergeability, and native queue immediately before enqueueing. Base or\nhead drift fails closed, an active queue entry blocks admission, and a rejected\nready predecessor leaves its PR open while later PRs receive\n`blocked-by-predecessor`. Workflow concurrency serializes Buildchain-owned\nadmission runs; GitHub still owns the atomic queue and protected-ref update.\nRepositories may explicitly select `landing-mode: direct` only when the target\nbranch has no native queue. Queue presence always disables the direct path.\n\nThe canonical consumer required check context is `check / check`, matching the\nreusable workflow call plus its `check` job. Buildchain's own `Verify` workflow\nemits the repository-local context `check`, so Buildchain self-promotion,\nrelease-line bootstrap, and dogfood patrol wrappers pass `check` explicitly.\nRelease governance preserves the exact context emitted by the repository and\nrecords branch-protection policy before/after facts; it does not rewrite one\ncontext shape into the other. Consumer repositories can keep their context\nstable while changing the actual verification command declaratively in\n`buildchain.toml`:\n\n```toml\n[lifecycle.install]\ncommand = \"cargo fetch --locked\"\n\n[lifecycle.verify]\ncommand = \"cargo test --workspace --locked\"\n```\n\nConsumers that want Buildchain to own the check wrapper can call\n`.github/workflows/check.yml@v2`. The wrapper runs the declared\n`lifecycle.install` and `lifecycle.verify` stages and fails the `check` job when\neither declaration is missing or the command exits non-zero.\n\nDevelopment pull requests that need source acceptance without product build or\nartifact verification can opt into `mode: source`. That mode runs only\n`lifecycle.install` and `lifecycle.check` on GitHub-hosted `ubuntu-24.04` while\npreserving the stable `check / check` required-check context. Existing callers\nremain on `mode: verify` by default, and callers may set\n`upload-artifacts: false` without weakening the job conclusion.\n\nTypical consumer wrapper:\n\n```yaml\nname: Merge Ready Dev PRs\n\non:\n schedule:\n - cron: \"17 * * * *\"\n workflow_dispatch:\n inputs:\n dry-run:\n type: boolean\n default: true\n\njobs:\n merge-dev:\n uses: kungfu-systems/buildchain/.github/workflows/dev-pr-auto-merge.yml@v2\n permissions:\n contents: write\n pull-requests: write\n checks: read\n statuses: read\n with:\n target-branch: dev/v2/v2.6\n required-status-checks: check / check\n ready-label: ready\n block-labels: blocked,do-not-merge\n max-merges: 1\n landing-mode: auto\n dry-run: ${{ inputs.dry-run || false }}\n```\n\n## Buildchain Patrol\n\n`dev-pr-auto-merge.yml` remains the focused merge primitive. For repositories\nthat want a stable day-to-day operations contract, Buildchain also exposes a\npatrol workflow family:\n\n| Workflow | Intended cadence | Default intent |\n| --- | --- | --- |\n| `.github/workflows/patrol-daily.yml` | daily | lightweight inspection plus ready dev PR maintenance |\n| `.github/workflows/patrol-weekly.yml` | weekly | release-state, passport, gate, and stale-state health checks as they are added |\n| `.github/workflows/patrol-monthly.yml` | monthly | governance, permission, branch-protection, and workflow drift checks as they are added |\n| `.github/workflows/stable-candidate-patrol.yml` | repository-selected release window | qualify immutable alpha candidates and open the exact source-lock stable PR |\n\nThe cadence names describe patrol intensity, not release cadence:\n\n- daily patrol can run every day without implying a daily release;\n- weekly patrol is for medium-cost maintenance and audit checks;\n- monthly patrol is for structural drift checks that should not block ordinary\n development velocity.\n\nStable Candidate Patrol is separate from those maintenance cadences because its\ncaller-owned cron is a release-intent window. Its candidate ledger and selection\nremain generic; registry-specific side effects still run through the normal\nrepository `lifecycle.publish` transaction. See\n[`stable-candidate-patrol.md`](stable-candidate-patrol.md).\n\nConsumers should schedule thin callers and keep their YAML declarative. For\nexample:\n\n```yaml\nname: Buildchain Daily Patrol\n\non:\n schedule:\n - cron: \"17 2 * * *\"\n workflow_dispatch:\n\njobs:\n patrol:\n uses: kungfu-systems/buildchain/.github/workflows/patrol-daily.yml@v2\n with:\n dry-run: false\n max-actions: 1\n```\n\nWeekly and monthly callers use the matching wrapper:\n\n```yaml\njobs:\n patrol:\n uses: kungfu-systems/buildchain/.github/workflows/patrol-weekly.yml@v2\n with:\n dry-run: true\n```\n\nAll three wrappers default to the `v2` floating Buildchain runtime. When\n`target-branch` is omitted, the caller's current/default branch selects the\nactive semver dev line, so consumers do not pin patrol to a stale minor branch.\nThe separate workflow names keep consumer schedules readable and stable while\nBuildchain adds new checks behind the cadence wrappers.\n\n## Package-Manager Adapters\n\nOld ABV assumed JavaScript repositories with root version state and often\nLerna. Buildchain keeps the version-state contract but does not assume every\nrepository is yarn/Lerna.\n\nThe promotion action discovers and updates:\n\n- root `package.json`;\n- `lerna.json`;\n- package manifests from `package.json` workspaces;\n- package manifests from `lerna.json` packages;\n- package manifests from `pnpm-workspace.yaml`.\n\nIt then runs the repository's detected package manager semantics where needed:\n\n- pnpm repositories use pnpm-oriented workspace discovery;\n- npm repositories use npm/package-lock semantics where present;\n- yarn repositories use yarn-style metadata where present.\n\nFor Buildchain itself, version state is required. For a consumer repository that\nhas no package manifest, the same action can degrade to ref-only behavior only\nwhen that is explicitly allowed by the caller.\n\n## Lifecycle Configuration\n\n`buildchain.toml` is the v2 user configuration format. It lets a repository\ndeclare version-state files and lifecycle commands without pretending every\nproject is a Node workspace. Supported version files include JSON, TOML, and\nregex-based files such as `CMakeLists.txt` or `conanfile.py`.\n\nThe promotion action currently consumes `version.files` and `lifecycle.verify`.\nThe verify stage runs after generated version-state changes are applied locally\nand before any release refs move. If `verification-command` is passed directly\nto the action, that explicit command overrides `lifecycle.verify`.\n\nProtected release-line branches keep their normal human review gate. Managed\n`dev/vN/vN.M`, `alpha/vN/vN.M`, and `release/vN/vN.M` branches are configured\nwith one required approving review, required GitHub Actions checks, administrator\nenforcement, conversation resolution, no force pushes, and no deletions. Dev\nkeeps its repository-selected strict or merge-queue policy. Alpha and release\nrequire both `check` and the pair-specific `verify` aggregate without requiring\nthe source branch to contain the target's generated bookkeeping history. The\nreusable `release-candidate-promote.yml` wrapper defaults\n`branch-protection-bypass-apps` to `github-actions`, which lets the workflow's\nautomation identity apply generated version-state or post-publish channel\nbookkeeping after the reviewed channel PR has merged. Direct\n`promote-buildchain-ref` callers must opt into the same controlled bypass with\n`branch-protection-bypass-apps`, `branch-protection-bypass-users`, or\n`branch-protection-bypass-teams`; the action also adds the current promotion\ntoken's authenticated user or app to the managed bypass allowlist. Before\npatching a protected generated bookkeeping ref, the action creates the\nfull configured required-check set on the exact generated version-state commit, so strict\nstatus checks are satisfied by machine-verifiable Buildchain evidence rather\nthan a human PR. The protected ref PATCH itself uses the generated ref update\ntoken; the reusable wrapper defaults it to\n`secrets.BUILDCHAIN_PROMOTION_TOKEN || github.token`. If direct generated\nrelease finalization bookkeeping is still rejected, Buildchain creates or\nreuses a same-repository `buildchain/version-state/*` PR based on the current\ntarget channel head and records `finalization-needed=true` in the durable\ntransaction output. Strict alpha bookkeeping still fails with a\ntoken/protection diagnostic instead of creating a post-publish PR.\n\nFor a stable release, the wrapper also checks out the exact current development\nchannel into `.buildchain/reconciliation/dev`. When the prepared next-alpha\ncommit cannot fast-forward dev because reviewed work landed concurrently, the\npromotion action applies the next version to that checkout, regenerates every\ndeclared derived version-state file, reruns the verification lifecycle, and\nonly then creates the two-parent reconciliation commit. A checkout/current-ref\nSHA mismatch blocks reconciliation instead of committing stale projections.\nBuildchain's own promotion workflow reads `BUILDCHAIN_PROMOTION_BYPASS_APPS`,\n`BUILDCHAIN_PROMOTION_BYPASS_USERS`, and\n`BUILDCHAIN_PROMOTION_BYPASS_TEAMS` repository variables so the declared bypass\nidentity can match the actual `BUILDCHAIN_PROMOTION_TOKEN` actor, but consumers\ndo not need to duplicate that actor manually when the token identity is\ndiscoverable. Buildchain's release-line bootstrap applies the same declared\nallowances when it creates new `dev`, `alpha`, and `release` branch protection,\nand fails closed before changing protection when no promotion authority is\ndeclared. This keeps a new minor line publishable without weakening its normal\nhuman review gate.\n\n## What This Guarantees\n\nWhen the loop succeeds, maintainers and consumers can rely on these facts:\n\n- every production release has an exact tag such as `v2.0.2`;\n- every production minor line has a floating tag such as `v2.0`;\n- every selected stable major has a floating tag such as `v2`;\n- every next-major release is driven by a reviewed `release -> publish-gate/major` PR,\n not a hidden manual button;\n- every test channel has an exact alpha tag such as `v2.0.3-alpha.0`;\n- every alpha minor line has a floating tag such as `v2.0-alpha`;\n- every major with a published alpha has a cross-minor floating tag such as `v2-alpha`, owned by its highest published alpha minor;\n- version manifests match the tag visible from the same commit;\n- production releases are derived from the alpha tree that was tested;\n- manual non-dry-run promotion cannot bypass PR review and verification;\n- flow-internal automation bypasses apply only to declared GitHub Apps, users,\n or teams on Buildchain-managed channel branch protection, while one-review\n protection remains enforced for humans;\n- admin users cannot make a channel promotion valid by temporarily bypassing\n branch protection.\n\nThis is the practical meaning of \"governance closed loop\" in Buildchain: the\ndecision, code, version state, and Git refs close over the same evidence chain.\n\n## What This Does Not Do\n\nBuildchain release promotion does not embed registry clients or product-specific\npublish logic. When publish transactions are enabled, `promote-buildchain-ref`\ncan run the consumer's `lifecycle.publish` command and own the transaction,\nevidence validation, durable recovery state, and ref finalization order. The\nconsumer repository still owns registry truth: npm, PyPI, OCI, S3, Conan, CMake\npackaging, download pages, dist-tags, and similar side effects must be\nimplemented by project lifecycle commands that emit Buildchain publish evidence.\n\nEvery Buildchain publish model that can run registry side effects must bind the\npublish entrypoint to an immutable `publish-gate/*` source lock. The reusable\n`release-candidate-promote.yml@v2` wrapper creates or updates that gate ref and\npasses `require-publish-source-lock`, `publish-source-ref`,\n`publish-source-sha`, and `publish-source-locked` to\n`promote-buildchain-ref`. Direct action callers must pass the same four inputs\nfrom the reusable build outputs. Workflows that only collect passports or run\ndry-run package checks do not move publish refs and are not publish-gate\npublication models. A dry-run of `release-candidate-promote.yml` computes and\nreports the exact `publish-gate/*` source lock that a real promotion would use,\nbut does not read, create, or move that ref.\n\nSemver GitHub Release publication is owned by `promote-buildchain-ref`, not by\nconsumer shell glue. Consumers normally use the `release-candidate-promote.yml`\ngenerated channel router, where GitHub Release publication is enabled by default and can be\ndisabled with `github-release: false`; the wrapper passes that declaration to\nthe action. After the publish transaction reaches `complete`, Buildchain creates\nor updates the public GitHub Release and uploads the generated\n`buildchain.release.json`, release-passport assets, and publish evidence. Semver\nprerelease tags are marked `prerelease=true` and `make_latest=false`; stable\nsemver tags are marked latest. For anchored/manual package releases, the public\nrelease tag defaults to `v<publishedVersion>` while the internal exact\ntransaction tag remains in the release passport and release-state ref. This is\nthe supported path for downstream\n`release.published` propagation across semver, major, and promote-only release\ncandidate publication models.\n\nPublished GitHub Release assets are immutable evidence. A repeated promotion\npreserves an existing asset when its SHA-256 digest matches the regenerated\nbytes, uploads only missing assets, and fails with an immutable-release\ncollision when a same-name asset has different bytes. It never deletes and\nreplaces an existing asset during retry or duplicate workflow delivery.\n\nBuildchain also does not maintain bare exact tags such as `1.0.0`. The supported\nexact release and alpha refs are v-prefixed:\n\n```text\nv2.0.0\nv2.0.1-alpha.0\n```\n\n## Operational Reading Order\n\nWhen debugging or extending release behavior, read in this order:\n\n1. `docs/release-flow.md`\n2. `.github/workflows/release-verify.yml`\n3. `.github/workflows/buildchain-ref-promotion.yml`\n4. `.github/workflows/release-candidate-promote.yml`\n5. `.github/workflows/.release-candidate-promote.yml`\n6. `actions/promote-buildchain-ref/README.md`\n7. `actions/promote-buildchain-ref/src/`\n8. `docs/migration-inventory.md`\n\nThat path gives the policy first, the workflow trigger second, and the action\nimplementation last."
1480
1480
  },
1481
1481
  {
1482
1482
  "id": "manual:release-passport",
@@ -1490,7 +1490,7 @@
1490
1490
  ],
1491
1491
  "maturity": "stable",
1492
1492
  "sourcePath": "docs/release-passport.md",
1493
- "digest": "sha256:2421e3e9f2dd892971bea5deb605492dfec38f3a83e539229442b55380bebe25",
1493
+ "digest": "sha256:0029014ffab7d7228a12d8b92326b78cf6757a41f481b661c5997e2cef06d3cc",
1494
1494
  "headings": [
1495
1495
  {
1496
1496
  "level": 1,
@@ -1538,7 +1538,7 @@
1538
1538
  "anchor": "binary-distribution"
1539
1539
  }
1540
1540
  ],
1541
- "markdown": "# Release Passport\n\nBuildchain Release Passport is the core product mechanism: a mature product\nrelease record for artifacts that users or agents depend on.\n\nThe protocol is GitHub-native because it uses protected refs, reviewed\npromotion PRs, exact tags, GitHub Releases, npm Trusted Publishing, and\nmachine-readable evidence. A project can keep its existing build system and use\nBuildchain to make the release record auditable.\n\nRelease-candidate and final passports may also include compact\n`controllerReceipts[]` references. These bind a real reusable-workflow receipt\nto its plan digest, consumer source SHA, and exact Buildchain runtime SHA; they\ndo not infer execution evidence from a green job. See\n[`controller-evidence.md`](controller-evidence.md).\n\n## Contract\n\nThe release passport surface is a welded contract. Additive fields are allowed;\nbreaking semantic changes require a new major line.\n\nP0 protocol artifacts:\n\n- `product-mechanism.json`\n- `buildchain.release.json`\n- `artifact-evidence.json`\n- `impact.json`\n- `agent-index.json`\n- `check-report.json`\n- `llms.txt`\n- `buildchain-release-bundle.json`\n- `buildchain-release-bundle.tar.gz`\n\n`buildchain.release.json` is the first file an agent should read. It points to\nartifact evidence, impact, recovery, product mechanism, and agent index facts.\nIt is also the unified release responsibility summary: when publish\ntransactions are used, the same passport records the package set, npm dist-tags,\nrelease source refs, release-state ref, anchor manifest, registry artifact\ndigests, trusted publishing evidence, and transaction result.\n\nAdditive passport sections:\n\n- `release`: public release tag, internal transaction tag, line, channel,\n source SHA, target ref, release SHA, release material SHA, publish tooling\n SHA, and durable release-state ref. Anchored/manual package releases use the\n published package version for the public tag while preserving the internal\n exact transaction tag for Buildchain recovery.\n- `versionImpact`: final patch/minor/major classification, source, and\n rationale.\n- `surfaceImpacts`: per registered surface classification. The final impact is\n the highest entry in this list.\n- `packageSet`: main package, platform packages, package-set order, registry,\n versions, dist-tags, and package digests.\n- `anchorManifest`: anchored/manual version manifest path, digest, and fields.\n- `trustedPublishing`: provider, auth mode, workflow run evidence, and whether\n trusted publishing was enabled.\n- `transaction`: durable Buildchain release transaction id, state, exact tag,\n release SHA, state ref, and state SHA.\n- `surfaceTimestampPolicy`: the common Buildchain surface manifest timestamp\n policy. It records real CI/release generation and publication times,\n reproducibility inputs, source revision or source-date-epoch, and whether\n timestamp fields participate in the release artifact digest.\n- `buildFacts`: module/product build facts that bind Git source digests,\n version sources, lifecycle invocations, platforms, outputs, product\n artifacts, and verification results to the release.\n- `artifacts`: release assets and registry artifacts in one list, each pointing\n back to the evidence file that proves its digest. Registry artifacts retain\n optional built/reused action, content and current-release coordinates,\n platform/contract/parent metadata, and fail-closed verification evidence.\n\nBuildchain's own binary lane also publishes observability artifacts generated by\nthe Buildchain logging API and CLI:\n\n- `buildchain-log-events-<platform>.jsonl`\n- `buildchain-log-summary-<platform>.json`\n- `buildchain-log-events-passport.jsonl`\n- `buildchain-log-summary-passport.json`\n\n`buildchain-release-bundle.tar.gz` is the single evidence bundle for consumers\nthat want one file for offline inspection, mirroring, or site ingestion.\n`buildchain-release-bundle.json` records its digest and the digest of every\nincluded file.\n\n## Runner Policy\n\nProduction binary distribution should use GitHub-hosted runners by default:\n\n- `ubuntu-24.04`\n- `macos-latest`\n- `windows-2022`\n\nThis keeps the public release path easy for other projects to reproduce.\nSelf-hosted runners remain compatibility fixtures: they prove that the protocol\ndoes not depend on GitHub-hosted images, but they are not the default public\ndistribution lane.\n\nThe protocol records runner facts in `artifact-evidence.json`; it does not\nrequire a specific runner class.\n\n## CLI\n\nGenerate a local release passport bundle from release assets:\n\n```bash\nbuildchain collect github-release \\\n --tag v2.2.0 \\\n --repository kungfu-systems/buildchain \\\n --assets-dir dist \\\n --publish-evidence-json .buildchain/release-evidence/v2.2.0/evidence.json \\\n --transaction-json .buildchain/release-state/v2.2.0/state.json \\\n --package-set-json package-set.json \\\n --anchor-manifest-json libnode.release.json \\\n --build-summary-json .buildchain/artifacts/build-summary.json \\\n --build-facts-json .buildchain/facts/native-core.json \\\n --build-facts-json .buildchain/facts/product.json \\\n --platform-manifest-json .buildchain/artifacts/linux-x64/manifest.json \\\n --platform-manifest-json .buildchain/artifacts/darwin-arm64/manifest.json \\\n --platform-manifest-json .buildchain/artifacts/win32-x64/manifest.json \\\n --dist-tag-evidence-json .buildchain/release-evidence/v2.2.0/dist-tag-evidence.json \\\n --kfd-1-witness-json .buildchain/kfd/kfd-1/contract-world.witness.json \\\n --kfd-2-claim-json .buildchain/kfd/kfd-2/release-claims.json \\\n --output-dir .buildchain/release-passport\n```\n\n`packageSet` records the ordered main-plus-platform package set.\n`publish.packages[]` summarizes each published npm package with its version,\ndist-tag, registry, role, platform, and digest, so agents do not need to stitch\nnpm facts back together from the lower-level evidence files.\n`buildSummary`, `platformArtifactManifests`, and `distTagPromotion` preserve the\nbuild and npm dist-tag evidence chain in the same passport. `buildFacts[]`\nrecords first-class module/product build facts, while\n`evidence.buildFacts[]` gives agents compact paths, SHA-256 hashes, contracts,\nids, and digests for quick audit traversal. See\n[`build-facts.md`](build-facts.md) for the fact collection and verification\ncontract.\n\n### KFD-1 contract-world release gate\n\nBuildchain can gate release artifacts with KFD-1 contract-world witnesses. This\nis a structured evidence protocol, not a request for consumers to shell out to\nthe Kungfu SDK. The authority chain is:\n\n1. KFD owns the standard metadata and schema ids in `@kungfu-tech/kfd`.\n2. Buildchain imports that metadata, owns the JSON formatting policy, freezes\n the pre-build witness, and independently verifies post-build artifact bytes.\n3. Consumers only pass declarative witness JSON plus the artifact payloads their\n build already produced.\n\nThis gives agents a concrete answer to \"what changed and can I trust it?\" A\nrelease can include both normal release passport evidence and KFD-1 evidence:\nthe passport proves the release transaction and artifacts are complete, while\nKFD-1 proves selected contract-world surfaces inside those artifacts are the\nbyte-for-byte surfaces the release intended to ship.\n\nThe witness JSON names the contract world, the canonical serialization policy,\nand the release surfaces that must be byte-for-byte verified:\n\n```json\n{\n \"id\": \"kungfu-config\",\n \"standard\": \"kfd-1\",\n \"source\": \"kfd\",\n \"contractWorld\": {\n \"id\": \"kungfu-config\",\n \"kind\": \"schema\",\n \"name\": \"Kungfu config schema\"\n },\n \"canonicalPolicy\": {\n \"format\": \"json\",\n \"encoding\": \"utf-8\",\n \"indent\": 2,\n \"trailingNewline\": true\n },\n \"surfaces\": [\n {\n \"id\": \"kungfu-config-schema\",\n \"artifactPath\": \"Contents/Resources/core/config.schema.json\",\n \"expectedSha256\": \"...\"\n }\n ]\n}\n```\n\n`collect github-release` writes the result under the KFD-provided top-level key\ncurrently named `kfd-1`. Each contract world records the frozen witness digest,\nthe KFD package version, KFD schema ids, the Buildchain formatting policy, and\nthe actual artifact digest observed after the build. Verification fails closed\nwhen the witness is missing required facts, an artifact cannot be found, or a\npost-build digest does not match the frozen witness.\n\nFor the KFD repository itself, the KFD-1 witness can be a self-hosted standard\ncontract witness. In that mode KFD owns the standard-contract facts and\nBuildchain verifies declared source standard metadata, schemas, package\nexports, and site-consumption entrypoints against the packaged artifact. The\npassport records source and artifact hash summaries, schema ids, the\nself-hosting boundary, result, residual risk, and responsibility state for\nsource ownership, artifact verification, and release-passport proof ownership.\n\nGood KFD-1 witnesses should point at release payload surfaces, not at private\nbuild-machine state. For Buildchain itself, the natural witness set is the\nrelease passport schema and implementation, KFD-1 gate implementation,\n`dist/site/buildchain-contract.json`, and the npm package payload files that\nexpose the public CLI, reusable workflow/action contracts, and site facts.\nThe final `buildchain.release.json` file should not be used as an ordinary\nbyte-for-byte KFD surface because it contains KFD evidence; instead, the\npassport is audited through release-state SHA, `check-report.json`, and the\ncontract files that generate and verify it.\n\n### KFD-2 release trust passport audit\n\nBuildchain can write a KFD-2 release trust passport audit under the top-level\n`kfd-2` section. The section is generated automatically from KFD-1 and KFD-3\nrelease-gate evidence, and callers may add explicit public release claims with\n`--kfd-2-claim-json`.\n\nEvery public claim must bind:\n\n- declared sources;\n- machine-readable evidence;\n- source/evidence/artifact hashes;\n- artifact coordinates;\n- verification results;\n- audit boundary;\n- responsibility state;\n- residual risk, even when the array is empty.\n\nUnbound public claims fail release passport verification. Claims that are\nmachine-bound but only supported by prose downgrade the KFD-2 audit and produce\na warning, so agents can distinguish \"verified\", \"needs review\", and \"not\nbound to evidence\" without reading release notes.\n\nFor Buildchain's own releases, public release claims are not authored in prose\ninside the workflow. The source registry is\n`packages/core/buildchain-kfd-claims.js`, published as\n`dist/site/kfd-claims.json` and exported as\n`@kungfu-tech/buildchain/buildchain-kfd-claims`. That registry is the\nversion-invariant source of public claims and collaboration surfaces: it does\nnot store the exact release version, promotion SHA, or exact runtime contract\ndigest. Those run-specific facts belong in the release passport and generated\nwitnesses. During Buildchain promotion,\n`scripts/generate-buildchain-kfd-witnesses.mjs` binds the source registry to the\ncurrent source/artifact hashes and generates:\n\n- a KFD-1 self contract-world witness for the packaged docs, schemas, workflows,\n actions, Node exports, and site-consumption facts;\n- one KFD-2 claim JSON per public Buildchain release claim;\n- KFD-3 pre-build and artifact witnesses for the same public collaboration\n surfaces.\n\nThe generated claim set covers Buildchain's KFD release passport support,\nagent-first single source of truth, floating `@v2` contract drift protection,\nsemver GitHub Release evidence publication, channel-preserving release\npropagation, and npm publish evidence/finalization. Buildchain self promotion\npasses those files into `promote-buildchain-ref`; `verifyReleasePassport()` then\nfails closed if any claim is missing source bindings, machine evidence, hashes,\nartifact coordinates, verification result, audit boundary, responsibility, or\nresidual risk.\n\n### KFD-3 collaboration-interface release gate\n\nKFD-3 asks a different release question than KFD-1. KFD-1 proves that named\npayload bytes match one contract world. KFD-3 proves that a product's shipped\nparticipant-facing collaboration/control surface is closed over its declared\ninterface.\n\nFor Buildchain itself, the declared interface starts in\n`packages/core/buildchain-kfd-claims.js`, not in this Markdown file. The\nregistry enumerates public human/agent surfaces across manuals, schema and\nstandard metadata, package exports, site-consumption contracts, workflows, and\nactions. `dist/site/kfd-claims.json` is the packaged machine-readable form used\nby downstream sites and by Buildchain's own release passport. Exact release\nversion/SHA binding is deliberately deferred to the promotion witness, so the\nsource registry can remain stable across semver version-state bumps.\n\nBuildchain also performs a reverse audit before that witness is used. The\ngenerated `dist/site/public-surface-audit.json` enumerates real CLI commands\nfrom `bin/buildchain.mjs`, workflow inputs, action inputs, site pages, and docs\ncommand references, then compares them with the generated registries. Buildchain\nself KFD witnesses include that audit result and classify the collaboration\ninterface as closed-world only when the reverse audit passes. If a public\ncommand, workflow input, action input, or site page is exposed without a\nregistry entry, `pnpm run check` fails before release promotion can produce a\npassport.\n\nThe product remains the fact source. Before build/publish, the product writes a\npre-build witness:\n\n```bash\nkungfu sdk collaboration-interface witness --json \\\n > .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n```\n\nThat witness must contain, or point to, the product-owned KFD-3 collaboration\ninterface, registry digest, participants, and declared public shipped surfaces.\nKFD repository self-verification can declare the same facts as grouped machine\nsurfaces: docs, schemas, standards metadata, package exports, and\nsite-consumption contracts.\nAfter the artifact is built, the product also provides artifact-side evidence,\neither as a JSON file or a command:\n\n```bash\nbuildchain collect github-release \\\n --kfd-3-prebuild-witness-json .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json \\\n --kfd-3-artifact-verify-cmd \"kungfu agent verify --json\"\n```\n\nBuildchain imports the KFD-3 metadata from `@kungfu-tech/kfd`, freezes the\npre-build witness digest, ingests the artifact witness, and compares the two\nsets:\n\n- every declared `shipped` public participant-facing surface must appear in the\n artifact witness;\n- every artifact-exposed public participant-facing surface must be declared by\n the pre-build witness;\n- if both witnesses record `collaborationInterface.digest`, the digests must\n match;\n- contradictory, missing, stale, or schema-incomplete evidence fails closed.\n\nThe generated release passport records the result under the KFD-provided\ntop-level key currently named `kfd-3`. The section includes the KFD package\nversion, schema ids/paths, pre-build witness digest, artifact witness digest,\ndeclared/exposed surface counts, missing declared shipped surfaces, and\nunclassified artifact public surfaces. Buildchain also projects the same\ncollaboration-interface evidence into the top-level `kfd-2` audit as a\nmachine-readable `trustProof` object on the generated `kfd-3:*` public claim.\nThat proof carries `releaseStatus`, witness file hashes and canonical hashes,\ndeclared capability verification, reverse audit result and boundary, residual\nrisk, and responsibility state.\n\nThe trust proof makes the strongest claim only when the witnesses justify it:\n`No unclassified reachable surface within the declared audit boundary.` If the\nproduct declares non-exhaustive surfaces, Buildchain keeps the passport\nverifiable but marks the interface `audited` instead of `enforced` and records\nthe residual risk explicitly. Draft or partial KFD-3 declarations are\ndowngraded; missing declared capabilities, undeclared public artifact surfaces,\nor stale collaboration-interface digests fail the proof.\n\nThis makes KFD-3 support usable by readers and agents immediately: they can\ninspect `buildchain.release.json` and know whether the released package\nactually exposes no more and no less than the declared collaboration interface,\ninstead of trusting docs or release notes.\n\n### Floating Buildchain contract lock\n\nKFD-1 protects release payload surfaces. Floating ref contract locks protect the\nconsumer's relationship to Buildchain itself. A consumer can keep\n`.buildchain/contract-lock.json` with the Buildchain floating ref it accepted,\nthe resolved SHA, the contract digest, and the compatibility policy. Each\nBuildchain run reads the actual contract from the checked-out Buildchain ref\nand compares it before heavy build or publish work begins.\n\nCompatible drift, such as optional inputs or extra diagnostics, continues and\ncreates a consumer-local issue for review. Breaking drift fails fast. This means\nconsumers can use `@v2` without silently accepting incompatible changes, while\nBuildchain maintainers can still ship compatible improvements under the same\nmajor floating tag.\n\n`impact.json` can be supplied with `--impact-json`. Production release\npassports (`release/*`) and major publish-gate passports require\n`surfaceImpacts[]`; alpha, local, and legacy passport contexts keep the field\noptional. When `surfaceImpacts[]` is required or supplied, verification fails\nclosed unless each entry has an id, impact, and rationale, and\n`versionImpact.final` matches the highest surface impact. For example, KFD-2\ncontent can remain patch while an additive `registry.kind` field on the\nmachine-consumed KFD registry schema records a minor `kfd-registry-schema`\nsurface impact.\n\nVerify a release passport:\n\n```bash\nbuildchain verify release-passport .buildchain/release-passport/buildchain.release.json\n```\n\nVerify a specific artifact by discovering its detached passport:\n\n```bash\nbuildchain verify artifact ./Kungfu-2.8.0-windows-x64.exe\n```\n\nArtifact verification is subject-centric. Buildchain identifies the subject,\ncomputes or obtains its digest, discovers a detached `buildchain.release.json`,\nverifies that release passport and its evidence, then proves the subject digest\nappears in the passport's artifacts, package set, publish evidence, or artifact\nevidence. The command returns `pass`, `fail`, or `unverifiable`; missing\npassports and digest mismatches fail closed.\n\nFor npm subjects, Buildchain treats the registry as the package digest source:\n`npm:<name>@<version>` resolves `dist.integrity` and matches it against\n`packageSet.main.digest`, `packageSet.platforms[].digest`, and publish evidence.\nUse `--npm-registry <url>` when the package comes from a non-default registry.\n\nDiscovery is ordered and auditable:\n\n1. explicit `--passport`;\n2. sidecar pointer;\n3. embedded/package pointer;\n4. local config or org index;\n5. GitHub Release default from artifact naming/repository/tag hints;\n6. custom locator;\n7. unverifiable with retry guidance.\n\nFor Buildchain-managed GitHub Release lanes, release passport files are\npublished as release assets by default when the upload backend is enabled, so a\nGitHub Release asset URL can discover the sibling `buildchain.release.json`\nwithout a consumer copying YAML resolver logic.\n\nExplain a release to an agent:\n\n```bash\nbuildchain explain release \\\n --passport .buildchain/release-passport/buildchain.release.json \\\n --for agent \\\n --json\n```\n\nThe verifier fails closed when a passport omits artifacts, omits evidence, has\ndigest mismatches, or misses required protocol files.\n\n## Binary Distribution\n\nInitial binary distribution stays lightweight:\n\n- GitHub Release assets.\n- `checksums.txt`.\n- release passport artifacts.\n- a single release evidence bundle.\n- install scripts and Homebrew tap fixtures after the passport path is reliable.\n\nBuildchain publishes platform-specific archives, not loose top-level\nexecutables:\n\n- `buildchain-x86_64-unknown-linux-gnu.tar.gz`\n- `buildchain-aarch64-apple-darwin.tar.gz`\n- `buildchain-x86_64-pc-windows-msvc.zip`\n\nThe executable name inside each archive stays natural for the platform\n(`buildchain` or `buildchain.exe`). Top-level loose executable assets are not\nuploaded, because Linux and macOS would otherwise collide when GitHub Actions\nmatrix artifacts are merged.\n\nHeavy package manager channels such as apt, yum, winget, choco, Scoop, mise, or\nasdf are out of the P0/P1 scope until there is real external demand.\n\nStandalone binaries are a distribution shape, not a second implementation. The\nsource of truth remains the Node/ESM CLI and core library.\n\nThe standalone binary builder imports `@kungfu-tech/buildchain/logging` directly\nand records setup, SEA blob generation, injection, signing, archiving, manifest,\nand evidence phases. The GitHub workflow wraps the same build and passport\nsteps with `buildchain mark`, `buildchain span`,\n`buildchain verify observability-log`, and `buildchain log summary`. Logging is\na hard release gate: missing events, error events, or missing required phases\nfail the job before assets are uploaded. The verified logs are release assets\nand are covered by the release passport digest checks.\n\nSee also [`binary-distribution.md`](binary-distribution.md) for asset naming and\nbundle details, and [`install.md`](install.md) for consumer commands."
1541
+ "markdown": "# Release Passport\n\nBuildchain Release Passport is the core product mechanism: a mature product\nrelease record for artifacts that users or agents depend on.\n\nThe protocol is GitHub-native because it uses protected refs, reviewed\npromotion PRs, exact tags, GitHub Releases, npm Trusted Publishing, and\nmachine-readable evidence. A project can keep its existing build system and use\nBuildchain to make the release record auditable.\n\nRelease-candidate and final passports may also include compact\n`controllerReceipts[]` references. These bind a real reusable-workflow receipt\nto its plan digest, consumer source SHA, and exact Buildchain runtime SHA; they\ndo not infer execution evidence from a green job. See\n[`controller-evidence.md`](controller-evidence.md).\n\n## Contract\n\nThe release passport surface is a welded contract. Additive fields are allowed;\nbreaking semantic changes require a new major line.\n\nIndependent verifiers can pin two package-owned, standalone files without\nscraping this implementation:\n\n- `dist/site/schemas/release-passport-v1.schema.json` is the self-contained\n JSON Schema for the Buildchain-owned envelope;\n- `dist/site/release-passport-check-manifest.json` names the normative checker,\n required and conditional sibling evidence, local-resolution rules,\n Buildchain aggregation fields, canonical KFD subsection schema authorities,\n and forward-compatibility policy.\n\nThe same files are exported as\n`@kungfu-tech/buildchain/site/schemas/release-passport-v1.schema.json` and\n`@kungfu-tech/buildchain/site/release-passport-check-manifest.json`. The schema\nchecks the envelope shape. `buildchain verify release-passport` remains the\nnormative semantic checker: it resolves sibling evidence relative to the\npassport and fails closed on missing evidence, digest drift, or inconsistent\nrelease facts. Buildchain owns envelope aggregation; KFD retains schema and\ncompatibility authority for the `kfd-1`, `kfd-2`, and `kfd-3` subsections.\n\nP0 protocol artifacts:\n\n- `product-mechanism.json`\n- `buildchain.release.json`\n- `artifact-evidence.json`\n- `impact.json`\n- `agent-index.json`\n- `check-report.json`\n- `llms.txt`\n- `buildchain-release-bundle.json`\n- `buildchain-release-bundle.tar.gz`\n\n`buildchain.release.json` is the first file an agent should read. It points to\nartifact evidence, impact, recovery, product mechanism, and agent index facts.\nIt is also the unified release responsibility summary: when publish\ntransactions are used, the same passport records the package set, npm dist-tags,\nrelease source refs, release-state ref, anchor manifest, registry artifact\ndigests, trusted publishing evidence, and transaction result.\n\nAdditive passport sections:\n\n- `release`: public release tag, internal transaction tag, line, channel,\n source SHA, target ref, release SHA, release material SHA, publish tooling\n SHA, and durable release-state ref. Anchored/manual package releases use the\n published package version for the public tag while preserving the internal\n exact transaction tag for Buildchain recovery.\n- `versionImpact`: final patch/minor/major classification, source, and\n rationale.\n- `surfaceImpacts`: per registered surface classification. The final impact is\n the highest entry in this list.\n- `packageSet`: main package, platform packages, package-set order, registry,\n versions, dist-tags, and package digests.\n- `anchorManifest`: anchored/manual version manifest path, digest, and fields.\n- `trustedPublishing`: provider, auth mode, workflow run evidence, and whether\n trusted publishing was enabled.\n- `transaction`: durable Buildchain release transaction id, state, exact tag,\n release SHA, state ref, and state SHA.\n- `surfaceTimestampPolicy`: the common Buildchain surface manifest timestamp\n policy. It records real CI/release generation and publication times,\n reproducibility inputs, source revision or source-date-epoch, and whether\n timestamp fields participate in the release artifact digest.\n- `buildFacts`: module/product build facts that bind Git source digests,\n version sources, lifecycle invocations, platforms, outputs, product\n artifacts, and verification results to the release.\n- `artifacts`: release assets and registry artifacts in one list, each pointing\n back to the evidence file that proves its digest. Registry artifacts retain\n optional built/reused action, content and current-release coordinates,\n platform/contract/parent metadata, and fail-closed verification evidence.\n\nBuildchain's own binary lane also publishes observability artifacts generated by\nthe Buildchain logging API and CLI:\n\n- `buildchain-log-events-<platform>.jsonl`\n- `buildchain-log-summary-<platform>.json`\n- `buildchain-log-events-passport.jsonl`\n- `buildchain-log-summary-passport.json`\n\n`buildchain-release-bundle.tar.gz` is the single evidence bundle for consumers\nthat want one file for offline inspection, mirroring, or site ingestion.\n`buildchain-release-bundle.json` records its digest and the digest of every\nincluded file.\n\n## Runner Policy\n\nProduction binary distribution should use GitHub-hosted runners by default:\n\n- `ubuntu-24.04`\n- `macos-latest`\n- `windows-2022`\n\nThis keeps the public release path easy for other projects to reproduce.\nSelf-hosted runners remain compatibility fixtures: they prove that the protocol\ndoes not depend on GitHub-hosted images, but they are not the default public\ndistribution lane.\n\nThe protocol records runner facts in `artifact-evidence.json`; it does not\nrequire a specific runner class.\n\n## CLI\n\nGenerate a local release passport bundle from release assets:\n\n```bash\nbuildchain collect github-release \\\n --tag v2.2.0 \\\n --repository kungfu-systems/buildchain \\\n --assets-dir dist \\\n --publish-evidence-json .buildchain/release-evidence/v2.2.0/evidence.json \\\n --transaction-json .buildchain/release-state/v2.2.0/state.json \\\n --package-set-json package-set.json \\\n --anchor-manifest-json libnode.release.json \\\n --build-summary-json .buildchain/artifacts/build-summary.json \\\n --build-facts-json .buildchain/facts/native-core.json \\\n --build-facts-json .buildchain/facts/product.json \\\n --platform-manifest-json .buildchain/artifacts/linux-x64/manifest.json \\\n --platform-manifest-json .buildchain/artifacts/darwin-arm64/manifest.json \\\n --platform-manifest-json .buildchain/artifacts/win32-x64/manifest.json \\\n --dist-tag-evidence-json .buildchain/release-evidence/v2.2.0/dist-tag-evidence.json \\\n --kfd-1-witness-json .buildchain/kfd/kfd-1/contract-world.witness.json \\\n --kfd-2-claim-json .buildchain/kfd/kfd-2/release-claims.json \\\n --output-dir .buildchain/release-passport\n```\n\n`packageSet` records the ordered main-plus-platform package set.\n`publish.packages[]` summarizes each published npm package with its version,\ndist-tag, registry, role, platform, and digest, so agents do not need to stitch\nnpm facts back together from the lower-level evidence files.\n`buildSummary`, `platformArtifactManifests`, and `distTagPromotion` preserve the\nbuild and npm dist-tag evidence chain in the same passport. `buildFacts[]`\nrecords first-class module/product build facts, while\n`evidence.buildFacts[]` gives agents compact paths, SHA-256 hashes, contracts,\nids, and digests for quick audit traversal. See\n[`build-facts.md`](build-facts.md) for the fact collection and verification\ncontract.\n\n### KFD-1 contract-world release gate\n\nBuildchain can gate release artifacts with KFD-1 contract-world witnesses. This\nis a structured evidence protocol, not a request for consumers to shell out to\nthe Kungfu SDK. The authority chain is:\n\n1. KFD owns the standard metadata and schema ids in `@kungfu-tech/kfd`.\n2. Buildchain imports that metadata, owns the JSON formatting policy, freezes\n the pre-build witness, and independently verifies post-build artifact bytes.\n3. Consumers only pass declarative witness JSON plus the artifact payloads their\n build already produced.\n\nThis gives agents a concrete answer to \"what changed and can I trust it?\" A\nrelease can include both normal release passport evidence and KFD-1 evidence:\nthe passport proves the release transaction and artifacts are complete, while\nKFD-1 proves selected contract-world surfaces inside those artifacts are the\nbyte-for-byte surfaces the release intended to ship.\n\nThe witness JSON names the contract world, the canonical serialization policy,\nand the release surfaces that must be byte-for-byte verified:\n\n```json\n{\n \"id\": \"kungfu-config\",\n \"standard\": \"kfd-1\",\n \"source\": \"kfd\",\n \"contractWorld\": {\n \"id\": \"kungfu-config\",\n \"kind\": \"schema\",\n \"name\": \"Kungfu config schema\"\n },\n \"canonicalPolicy\": {\n \"format\": \"json\",\n \"encoding\": \"utf-8\",\n \"indent\": 2,\n \"trailingNewline\": true\n },\n \"surfaces\": [\n {\n \"id\": \"kungfu-config-schema\",\n \"artifactPath\": \"Contents/Resources/core/config.schema.json\",\n \"expectedSha256\": \"...\"\n }\n ]\n}\n```\n\n`collect github-release` writes the result under the KFD-provided top-level key\ncurrently named `kfd-1`. Each contract world records the frozen witness digest,\nthe KFD package version, KFD schema ids, the Buildchain formatting policy, and\nthe actual artifact digest observed after the build. Verification fails closed\nwhen the witness is missing required facts, an artifact cannot be found, or a\npost-build digest does not match the frozen witness.\n\nFor the KFD repository itself, the KFD-1 witness can be a self-hosted standard\ncontract witness. In that mode KFD owns the standard-contract facts and\nBuildchain verifies declared source standard metadata, schemas, package\nexports, and site-consumption entrypoints against the packaged artifact. The\npassport records source and artifact hash summaries, schema ids, the\nself-hosting boundary, result, residual risk, and responsibility state for\nsource ownership, artifact verification, and release-passport proof ownership.\n\nGood KFD-1 witnesses should point at release payload surfaces, not at private\nbuild-machine state. For Buildchain itself, the natural witness set is the\nrelease passport schema and implementation, KFD-1 gate implementation,\n`dist/site/buildchain-contract.json`, and the npm package payload files that\nexpose the public CLI, reusable workflow/action contracts, and site facts.\nThe final `buildchain.release.json` file should not be used as an ordinary\nbyte-for-byte KFD surface because it contains KFD evidence; instead, the\npassport is audited through release-state SHA, `check-report.json`, and the\ncontract files that generate and verify it.\n\n### KFD-2 release trust passport audit\n\nBuildchain can write a KFD-2 release trust passport audit under the top-level\n`kfd-2` section. The section is generated automatically from KFD-1 and KFD-3\nrelease-gate evidence, and callers may add explicit public release claims with\n`--kfd-2-claim-json`.\n\nEvery public claim must bind:\n\n- declared sources;\n- machine-readable evidence;\n- source/evidence/artifact hashes;\n- artifact coordinates;\n- verification results;\n- audit boundary;\n- responsibility state;\n- residual risk, even when the array is empty.\n\nUnbound public claims fail release passport verification. Claims that are\nmachine-bound but only supported by prose downgrade the KFD-2 audit and produce\na warning, so agents can distinguish \"verified\", \"needs review\", and \"not\nbound to evidence\" without reading release notes.\n\nFor Buildchain's own releases, public release claims are not authored in prose\ninside the workflow. The source registry is\n`packages/core/buildchain-kfd-claims.js`, published as\n`dist/site/kfd-claims.json` and exported as\n`@kungfu-tech/buildchain/buildchain-kfd-claims`. That registry is the\nversion-invariant source of public claims and collaboration surfaces: it does\nnot store the exact release version, promotion SHA, or exact runtime contract\ndigest. Those run-specific facts belong in the release passport and generated\nwitnesses. During Buildchain promotion,\n`scripts/generate-buildchain-kfd-witnesses.mjs` binds the source registry to the\ncurrent source/artifact hashes and generates:\n\n- a KFD-1 self contract-world witness for the packaged docs, schemas, workflows,\n actions, Node exports, and site-consumption facts;\n- one KFD-2 claim JSON per public Buildchain release claim;\n- KFD-3 pre-build and artifact witnesses for the same public collaboration\n surfaces.\n\nThe generated claim set covers Buildchain's KFD release passport support,\nagent-first single source of truth, floating `@v2` contract drift protection,\nsemver GitHub Release evidence publication, channel-preserving release\npropagation, and npm publish evidence/finalization. Buildchain self promotion\npasses those files into `promote-buildchain-ref`; `verifyReleasePassport()` then\nfails closed if any claim is missing source bindings, machine evidence, hashes,\nartifact coordinates, verification result, audit boundary, responsibility, or\nresidual risk.\n\n### KFD-3 collaboration-interface release gate\n\nKFD-3 asks a different release question than KFD-1. KFD-1 proves that named\npayload bytes match one contract world. KFD-3 proves that a product's shipped\nparticipant-facing collaboration/control surface is closed over its declared\ninterface.\n\nFor Buildchain itself, the declared interface starts in\n`packages/core/buildchain-kfd-claims.js`, not in this Markdown file. The\nregistry enumerates public human/agent surfaces across manuals, schema and\nstandard metadata, package exports, site-consumption contracts, workflows, and\nactions. `dist/site/kfd-claims.json` is the packaged machine-readable form used\nby downstream sites and by Buildchain's own release passport. Exact release\nversion/SHA binding is deliberately deferred to the promotion witness, so the\nsource registry can remain stable across semver version-state bumps.\n\nBuildchain also performs a reverse audit before that witness is used. The\ngenerated `dist/site/public-surface-audit.json` enumerates real CLI commands\nfrom `bin/buildchain.mjs`, workflow inputs, action inputs, site pages, and docs\ncommand references, then compares them with the generated registries. Buildchain\nself KFD witnesses include that audit result and classify the collaboration\ninterface as closed-world only when the reverse audit passes. If a public\ncommand, workflow input, action input, or site page is exposed without a\nregistry entry, `pnpm run check` fails before release promotion can produce a\npassport.\n\nThe product remains the fact source. Before build/publish, the product writes a\npre-build witness:\n\n```bash\nkungfu sdk collaboration-interface witness --json \\\n > .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n```\n\nThat witness must contain, or point to, the product-owned KFD-3 collaboration\ninterface, registry digest, participants, and declared public shipped surfaces.\nKFD repository self-verification can declare the same facts as grouped machine\nsurfaces: docs, schemas, standards metadata, package exports, and\nsite-consumption contracts.\nAfter the artifact is built, the product also provides artifact-side evidence,\neither as a JSON file or a command:\n\n```bash\nbuildchain collect github-release \\\n --kfd-3-prebuild-witness-json .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json \\\n --kfd-3-artifact-verify-cmd \"kungfu agent verify --json\"\n```\n\nBuildchain imports the KFD-3 metadata from `@kungfu-tech/kfd`, freezes the\npre-build witness digest, ingests the artifact witness, and compares the two\nsets:\n\n- every declared `shipped` public participant-facing surface must appear in the\n artifact witness;\n- every artifact-exposed public participant-facing surface must be declared by\n the pre-build witness;\n- if both witnesses record `collaborationInterface.digest`, the digests must\n match;\n- contradictory, missing, stale, or schema-incomplete evidence fails closed.\n\nThe generated release passport records the result under the KFD-provided\ntop-level key currently named `kfd-3`. The section includes the KFD package\nversion, schema ids/paths, pre-build witness digest, artifact witness digest,\ndeclared/exposed surface counts, missing declared shipped surfaces, and\nunclassified artifact public surfaces. Buildchain also projects the same\ncollaboration-interface evidence into the top-level `kfd-2` audit as a\nmachine-readable `trustProof` object on the generated `kfd-3:*` public claim.\nThat proof carries `releaseStatus`, witness file hashes and canonical hashes,\ndeclared capability verification, reverse audit result and boundary, residual\nrisk, and responsibility state.\n\nThe trust proof makes the strongest claim only when the witnesses justify it:\n`No unclassified reachable surface within the declared audit boundary.` If the\nproduct declares non-exhaustive surfaces, Buildchain keeps the passport\nverifiable but marks the interface `audited` instead of `enforced` and records\nthe residual risk explicitly. Draft or partial KFD-3 declarations are\ndowngraded; missing declared capabilities, undeclared public artifact surfaces,\nor stale collaboration-interface digests fail the proof.\n\nThis makes KFD-3 support usable by readers and agents immediately: they can\ninspect `buildchain.release.json` and know whether the released package\nactually exposes no more and no less than the declared collaboration interface,\ninstead of trusting docs or release notes.\n\n### Floating Buildchain contract lock\n\nKFD-1 protects release payload surfaces. Floating ref contract locks protect the\nconsumer's relationship to Buildchain itself. A consumer can keep\n`.buildchain/contract-lock.json` with the Buildchain floating ref it accepted,\nthe resolved SHA, the contract digest, and the compatibility policy. Each\nBuildchain run reads the actual contract from the checked-out Buildchain ref\nand compares it before heavy build or publish work begins.\n\nCompatible drift, such as optional inputs or extra diagnostics, continues and\ncreates a consumer-local issue for review. Breaking drift fails fast. This means\nconsumers can use `@v2` without silently accepting incompatible changes, while\nBuildchain maintainers can still ship compatible improvements under the same\nmajor floating tag.\n\n`impact.json` can be supplied with `--impact-json`. Production release\npassports (`release/*`) and major publish-gate passports require\n`surfaceImpacts[]`; alpha, local, and legacy passport contexts keep the field\noptional. When `surfaceImpacts[]` is required or supplied, verification fails\nclosed unless each entry has an id, impact, and rationale, and\n`versionImpact.final` matches the highest surface impact. For example, KFD-2\ncontent can remain patch while an additive `registry.kind` field on the\nmachine-consumed KFD registry schema records a minor `kfd-registry-schema`\nsurface impact.\n\nVerify a release passport:\n\n```bash\nbuildchain verify release-passport .buildchain/release-passport/buildchain.release.json\n```\n\nVerify a specific artifact by discovering its detached passport:\n\n```bash\nbuildchain verify artifact ./Kungfu-2.8.0-windows-x64.exe\n```\n\nArtifact verification is subject-centric. Buildchain identifies the subject,\ncomputes or obtains its digest, discovers a detached `buildchain.release.json`,\nverifies that release passport and its evidence, then proves the subject digest\nappears in the passport's artifacts, package set, publish evidence, or artifact\nevidence. The command returns `pass`, `fail`, or `unverifiable`; missing\npassports and digest mismatches fail closed.\n\nFor npm subjects, Buildchain treats the registry as the package digest source:\n`npm:<name>@<version>` resolves `dist.integrity` and matches it against\n`packageSet.main.digest`, `packageSet.platforms[].digest`, and publish evidence.\nUse `--npm-registry <url>` when the package comes from a non-default registry.\n\nDiscovery is ordered and auditable:\n\n1. explicit `--passport`;\n2. sidecar pointer;\n3. embedded/package pointer;\n4. local config or org index;\n5. GitHub Release default from artifact naming/repository/tag hints;\n6. custom locator;\n7. unverifiable with retry guidance.\n\nFor Buildchain-managed GitHub Release lanes, release passport files are\npublished as release assets by default when the upload backend is enabled, so a\nGitHub Release asset URL can discover the sibling `buildchain.release.json`\nwithout a consumer copying YAML resolver logic.\n\nExplain a release to an agent:\n\n```bash\nbuildchain explain release \\\n --passport .buildchain/release-passport/buildchain.release.json \\\n --for agent \\\n --json\n```\n\nThe verifier fails closed when a passport omits artifacts, omits evidence, has\ndigest mismatches, or misses required protocol files.\n\n## Binary Distribution\n\nInitial binary distribution stays lightweight:\n\n- GitHub Release assets.\n- `checksums.txt`.\n- release passport artifacts.\n- a single release evidence bundle.\n- install scripts and Homebrew tap fixtures after the passport path is reliable.\n\nBuildchain publishes platform-specific archives, not loose top-level\nexecutables:\n\n- `buildchain-x86_64-unknown-linux-gnu.tar.gz`\n- `buildchain-aarch64-apple-darwin.tar.gz`\n- `buildchain-x86_64-pc-windows-msvc.zip`\n\nThe executable name inside each archive stays natural for the platform\n(`buildchain` or `buildchain.exe`). Top-level loose executable assets are not\nuploaded, because Linux and macOS would otherwise collide when GitHub Actions\nmatrix artifacts are merged.\n\nHeavy package manager channels such as apt, yum, winget, choco, Scoop, mise, or\nasdf are out of the P0/P1 scope until there is real external demand.\n\nStandalone binaries are a distribution shape, not a second implementation. The\nsource of truth remains the Node/ESM CLI and core library.\n\nThe standalone binary builder imports `@kungfu-tech/buildchain/logging` directly\nand records setup, SEA blob generation, injection, signing, archiving, manifest,\nand evidence phases. The GitHub workflow wraps the same build and passport\nsteps with `buildchain mark`, `buildchain span`,\n`buildchain verify observability-log`, and `buildchain log summary`. Logging is\na hard release gate: missing events, error events, or missing required phases\nfail the job before assets are uploaded. The verified logs are release assets\nand are covered by the release passport digest checks.\n\nSee also [`binary-distribution.md`](binary-distribution.md) for asset naming and\nbundle details, and [`install.md`](install.md) for consumer commands."
1542
1542
  },
1543
1543
  {
1544
1544
  "id": "manual:release-propagation",
@@ -1552,7 +1552,7 @@
1552
1552
  ],
1553
1553
  "maturity": "preview",
1554
1554
  "sourcePath": "docs/release-propagation.md",
1555
- "digest": "sha256:d5877075eb41a3d41e531549da113756d29d6f34c91e3b68b8ce650753d5ca1d",
1555
+ "digest": "sha256:fc2b743e3d76c4f90cdd8c6560cf9d619ad25d74c15c074be96db7bcb6cc2a29",
1556
1556
  "headings": [
1557
1557
  {
1558
1558
  "level": 1,
@@ -1585,7 +1585,7 @@
1585
1585
  "anchor": "kfd-to-site-libkungfu-dev"
1586
1586
  }
1587
1587
  ],
1588
- "markdown": "# Release Propagation\n\nRelease propagation lets a finalized upstream release open a downstream update\nPR using the upstream release passport as the audit source. It is for product\nchains such as:\n\n```text\nkfd -> site-libkungfu-dev\nA -> B -> C\n```\n\nThe downstream repository receives an exact lock, not a floating dist-tag. A\nsite or app can then consume the upstream package, site bundle, or release\npassport as its single source of truth without hand-copying release facts.\n\n## Contract\n\nThe propagation graph is declarative JSON:\n\n```json\n{\n \"schemaVersion\": 1,\n \"contract\": \"kungfu-buildchain-release-propagation-graph\",\n \"nodes\": [\n {\n \"id\": \"kfd\",\n \"repository\": \"kungfu-systems/kfd\",\n \"package\": \"@kungfu-tech/kfd\"\n },\n {\n \"id\": \"site-libkungfu-dev\",\n \"repository\": \"kungfu-systems/site-libkungfu-dev\",\n \"lockPath\": \"buildchain.upstreams/kfd.release.json\",\n \"baseRef\": \"dev/v2/v2.7\"\n }\n ],\n \"edges\": [\n {\n \"id\": \"kfd-to-site\",\n \"from\": \"kfd\",\n \"to\": \"site-libkungfu-dev\",\n \"channelPolicy\": \"preserve\"\n }\n ]\n}\n```\n\n`channelPolicy: \"preserve\"` is the default and maps:\n\n```text\nalpha -> alpha\nrelease -> release\n```\n\nCross-channel mapping is allowed only when an edge declares\n`channelPolicy: \"explicit\"` and a `channelMap`. Buildchain rejects cycles so a\nchain can fan out or continue as `A -> B -> C`, but cannot loop back into an\nalready visited release line.\n\n## Upstream Release Envelope\n\nThe upstream release envelope is the post-finalization fact set:\n\n```json\n{\n \"repository\": \"kungfu-systems/kfd\",\n \"channel\": \"alpha\",\n \"tag\": \"v1.4.0-alpha.3\",\n \"sourceSha\": \"1111111111111111111111111111111111111111\",\n \"package\": {\n \"name\": \"@kungfu-tech/kfd\",\n \"version\": \"1.4.0-alpha.3\",\n \"integrity\": \"sha512-...\"\n },\n \"releasePassport\": {\n \"url\": \"https://github.com/kungfu-systems/kfd/releases/download/v1.4.0-alpha.3/buildchain.release.json\",\n \"sha256\": \"2222222222222222222222222222222222222222222222222222222222222222\"\n },\n \"siteBundle\": {\n \"manifestSha256\": \"3333333333333333333333333333333333333333333333333333333333333333\"\n }\n}\n```\n\nThe package version and integrity must be exact. Downstream build logic should\ninstall that version directly, not resolve `alpha` or `latest` again.\n\nPublication repositories can propagate immutable publication archive evidence\nwithout npm package facts. The upstream envelope then includes\n`publicationArtifact`:\n\n```json\n{\n \"repository\": \"kungfu-systems/paper-observer-declared-timelines\",\n \"channel\": \"alpha\",\n \"tag\": \"v0.1.0-alpha.1\",\n \"sourceSha\": \"4444444444444444444444444444444444444444\",\n \"releasePassport\": {\n \"url\": \"https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/buildchain.release.json\",\n \"sha256\": \"5555555555555555555555555555555555555555555555555555555555555555\"\n },\n \"publicationArtifact\": {\n \"id\": \"observer-declared-timelines\",\n \"kind\": \"paper\",\n \"version\": \"0.1.0-alpha.1\",\n \"canonicalUrl\": \"https://papers.libkungfu.dev/observer-declared-timelines/\",\n \"latestUrl\": \"https://papers.libkungfu.dev/observer-declared-timelines/latest/\",\n \"latestEvidenceUrl\": \"https://papers.libkungfu.dev/observer-declared-timelines/latest/buildchain.release.json\",\n \"immutableVersionUrl\": \"https://papers.libkungfu.dev/archive/observer-declared-timelines/v0.1.0-alpha.1/\",\n \"registry\": {\n \"url\": \"https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/publication-registry.json\",\n \"sha256\": \"6666666666666666666666666666666666666666666666666666666666666666\"\n },\n \"manifest\": {\n \"url\": \"https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/publication-artifact.json\",\n \"sha256\": \"7777777777777777777777777777777777777777777777777777777777777777\"\n },\n \"passport\": {\n \"url\": \"https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/publication-artifact-passport.json\",\n \"sha256\": \"8888888888888888888888888888888888888888888888888888888888888888\"\n },\n \"primaryArtifact\": {\n \"path\": \"_build/main.pdf\",\n \"url\": \"https://papers.libkungfu.dev/archive/observer-declared-timelines/v0.1.0-alpha.1/main.pdf\",\n \"sha256\": \"9999999999999999999999999999999999999999999999999999999999999999\"\n }\n }\n}\n```\n\nThis lets a site repository render the latest reader page and historical\nversion index from release facts while keeping old PDFs, source bundles,\nmanifests, and passports immutable.\n\n## CLI\n\nGenerate a propagation plan:\n\n```bash\nbuildchain release-propagation plan \\\n --graph buildchain.release-propagation.json \\\n --upstream-release .buildchain/upstream-release.json \\\n --output .buildchain/release-propagation-plan.json \\\n --json\n```\n\nWrite the downstream lock:\n\n```bash\nbuildchain release-propagation write-lock \\\n --plan .buildchain/release-propagation-plan.json \\\n --target site-libkungfu-dev \\\n --cwd downstream-checkout \\\n --json\n```\n\nThe written lock has contract\n`kungfu-buildchain-release-propagation-lock` and records:\n\n- upstream repository, channel, exact tag, source SHA;\n- optional npm package name, exact version, and sha512 integrity;\n- optional publication artifact canonical/latest/immutable URLs, registry,\n manifest, passport, source bundle, and primary artifact digests;\n- release passport URL and SHA-256;\n- optional site bundle manifest SHA-256;\n- downstream repository, channel, base ref, lock path;\n- edge id and channel policy.\n\n## Reusable Workflow\n\nUpstream repositories can call\n`.github/workflows/release-propagation.yml@v2` after release finalization:\n\n```yaml\njobs:\n propagate-site:\n uses: kungfu-systems/buildchain/.github/workflows/release-propagation.yml@v2\n with:\n buildchain-ref: v2\n graph-json: ${{ needs.release.outputs.propagation-graph-json }}\n upstream-release-json: ${{ needs.release.outputs.upstream-release-json }}\n downstream-target: site-libkungfu-dev\n downstream-repository: kungfu-systems/site-libkungfu-dev\n downstream-base-ref: dev/v2/v2.7\n dry-run: false\n secrets:\n propagation-token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n```\n\nThe downstream branch name may be reused across upstream releases. Before\nreplacing an existing managed branch, the workflow reads its exact remote SHA\nand pushes with an explicit `--force-with-lease=<ref>:<sha>`. A surviving branch\nfrom a merged PR is therefore reconciled without manual deletion, while a\nconcurrent writer makes the lease fail closed. The controller receipt includes a\n`propagation-branch-reconciliation` evidence file recording the branch, observed\nremote SHA, pushed SHA, lease mode, and the deterministically created or updated\nopen PR.\n\nThe workflow checks out the Buildchain runtime selected by\n`buildchain-repository` and `buildchain-ref` into `.buildchain/runtime`, invokes\nthat runtime for the propagation plan and lock write, then checks out the\ndownstream repository, writes the exact lock, and opens or updates a PR. It does\nnot publish the downstream release directly. The downstream repository keeps its\nnormal Buildchain governance: the PR updates source-of-truth facts, then\ndownstream alpha or release publication runs through its own protected channel.\nThe workflow stages the declared lock path before checking the index, so the\nfirst propagation creates a PR even when the lock did not previously exist; a\nbyte-identical rerun is an explicit successful no-op.\nFor unreleased runtime validation, keep the caller's reusable workflow reference\non `@v2` and pass a temporary train ref through `buildchain-ref`.\n\n## kfd to site-libkungfu-dev\n\nFor `kfd -> site-libkungfu-dev`, the graph should preserve channels:\n\n- a `kfd` alpha release produces a downstream alpha lock and downstream alpha\n publication consumes the exact `@kungfu-tech/kfd@...-alpha.N` package;\n- a `kfd` stable release produces a downstream release lock and downstream\n stable publication consumes the exact stable package.\n\nThis keeps the site synchronized to the package truth without allowing the site\nto drift onto a floating npm dist-tag."
1588
+ "markdown": "# Release Propagation\n\nRelease propagation lets a finalized upstream release open a downstream update\nPR using the upstream release passport as the audit source. It is for product\nchains such as:\n\n```text\nkfd -> site-libkungfu-dev\nA -> B -> C\n```\n\nThe downstream repository receives an exact lock, not a floating dist-tag. A\nsite or app can then consume the upstream package, site bundle, or release\npassport as its single source of truth without hand-copying release facts.\n\n## Contract\n\nThe propagation graph is declarative JSON:\n\n```json\n{\n \"schemaVersion\": 1,\n \"contract\": \"kungfu-buildchain-release-propagation-graph\",\n \"nodes\": [\n {\n \"id\": \"kfd\",\n \"repository\": \"kungfu-systems/kfd\",\n \"package\": \"@kungfu-tech/kfd\"\n },\n {\n \"id\": \"site-libkungfu-dev\",\n \"repository\": \"kungfu-systems/site-libkungfu-dev\",\n \"lockPath\": \"buildchain.upstreams/kfd.release.json\",\n \"baseRef\": \"dev/v2/v2.7\"\n }\n ],\n \"edges\": [\n {\n \"id\": \"kfd-to-site\",\n \"from\": \"kfd\",\n \"to\": \"site-libkungfu-dev\",\n \"channelPolicy\": \"preserve\"\n }\n ]\n}\n```\n\n`channelPolicy: \"preserve\"` is the default and maps:\n\n```text\nalpha -> alpha\nrelease -> release\n```\n\nCross-channel mapping is allowed only when an edge declares\n`channelPolicy: \"explicit\"` and a `channelMap`. Buildchain rejects cycles so a\nchain can fan out or continue as `A -> B -> C`, but cannot loop back into an\nalready visited release line.\n\n## Upstream Release Envelope\n\nThe upstream release envelope is the post-finalization fact set:\n\n```json\n{\n \"repository\": \"kungfu-systems/kfd\",\n \"channel\": \"alpha\",\n \"tag\": \"v1.4.0-alpha.3\",\n \"sourceSha\": \"1111111111111111111111111111111111111111\",\n \"package\": {\n \"name\": \"@kungfu-tech/kfd\",\n \"version\": \"1.4.0-alpha.3\",\n \"integrity\": \"sha512-...\"\n },\n \"releasePassport\": {\n \"url\": \"https://github.com/kungfu-systems/kfd/releases/download/v1.4.0-alpha.3/buildchain.release.json\",\n \"sha256\": \"2222222222222222222222222222222222222222222222222222222222222222\"\n },\n \"siteBundle\": {\n \"manifestSha256\": \"3333333333333333333333333333333333333333333333333333333333333333\"\n }\n}\n```\n\nThe package version and integrity must be exact. Downstream build logic should\ninstall that version directly, not resolve `alpha` or `latest` again.\n\nPublication repositories can propagate immutable publication archive evidence\nwithout npm package facts. The upstream envelope then includes\n`publicationArtifact`:\n\n```json\n{\n \"repository\": \"kungfu-systems/paper-observer-declared-timelines\",\n \"channel\": \"alpha\",\n \"tag\": \"v0.1.0-alpha.1\",\n \"sourceSha\": \"4444444444444444444444444444444444444444\",\n \"releasePassport\": {\n \"url\": \"https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/buildchain.release.json\",\n \"sha256\": \"5555555555555555555555555555555555555555555555555555555555555555\"\n },\n \"publicationArtifact\": {\n \"id\": \"observer-declared-timelines\",\n \"kind\": \"paper\",\n \"version\": \"0.1.0-alpha.1\",\n \"canonicalUrl\": \"https://papers.libkungfu.dev/observer-declared-timelines/\",\n \"latestUrl\": \"https://papers.libkungfu.dev/observer-declared-timelines/latest/\",\n \"latestEvidenceUrl\": \"https://papers.libkungfu.dev/observer-declared-timelines/latest/buildchain.release.json\",\n \"immutableVersionUrl\": \"https://papers.libkungfu.dev/archive/observer-declared-timelines/v0.1.0-alpha.1/\",\n \"registry\": {\n \"url\": \"https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/publication-registry.json\",\n \"sha256\": \"6666666666666666666666666666666666666666666666666666666666666666\"\n },\n \"manifest\": {\n \"url\": \"https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/publication-artifact.json\",\n \"sha256\": \"7777777777777777777777777777777777777777777777777777777777777777\"\n },\n \"passport\": {\n \"url\": \"https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/publication-artifact-passport.json\",\n \"sha256\": \"8888888888888888888888888888888888888888888888888888888888888888\"\n },\n \"primaryArtifact\": {\n \"path\": \"_build/main.pdf\",\n \"url\": \"https://papers.libkungfu.dev/archive/observer-declared-timelines/v0.1.0-alpha.1/main.pdf\",\n \"sha256\": \"9999999999999999999999999999999999999999999999999999999999999999\"\n }\n }\n}\n```\n\nThis lets a site repository render the latest reader page and historical\nversion index from release facts while keeping old PDFs, source bundles,\nmanifests, and passports immutable.\n\n## CLI\n\nGenerate a propagation plan:\n\n```bash\nbuildchain release-propagation plan \\\n --graph buildchain.release-propagation.json \\\n --upstream-release .buildchain/upstream-release.json \\\n --output .buildchain/release-propagation-plan.json \\\n --json\n```\n\nWrite the downstream lock:\n\n```bash\nbuildchain release-propagation write-lock \\\n --plan .buildchain/release-propagation-plan.json \\\n --target site-libkungfu-dev \\\n --cwd downstream-checkout \\\n --json\n```\n\nThe written lock has contract\n`kungfu-buildchain-release-propagation-lock` and records:\n\n- upstream repository, channel, exact tag, source SHA;\n- optional npm package name, exact version, and sha512 integrity;\n- optional publication artifact canonical/latest/immutable URLs, registry,\n manifest, passport, source bundle, and primary artifact digests;\n- release passport URL and SHA-256;\n- optional site bundle manifest SHA-256;\n- downstream repository, channel, base ref, lock path;\n- edge id and channel policy.\n\n## Reusable Workflow\n\nUpstream repositories can call\n`.github/workflows/release-propagation.yml@v2` after release finalization:\n\n```yaml\njobs:\n propagate-site:\n uses: kungfu-systems/buildchain/.github/workflows/release-propagation.yml@v2\n with:\n buildchain-ref: v2\n graph-json: ${{ needs.release.outputs.propagation-graph-json }}\n upstream-release-json: ${{ needs.release.outputs.upstream-release-json }}\n downstream-target: site-libkungfu-dev\n downstream-repository: kungfu-systems/site-libkungfu-dev\n downstream-base-ref: dev/v2/v2.7\n downstream-prepare-command: pnpm install --frozen-lockfile --ignore-scripts\n downstream-verify-command: pnpm run check\n dry-run: false\n secrets:\n propagation-token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n```\n\nThe downstream branch name may be reused across upstream releases. Before\nreplacing an existing managed branch, the workflow reads its exact remote SHA\nand pushes with an explicit `--force-with-lease=<ref>:<sha>`. A surviving branch\nfrom a merged PR is therefore reconciled without manual deletion, while a\nconcurrent writer makes the lease fail closed. The controller receipt includes a\n`propagation-branch-reconciliation` evidence file recording the branch, observed\nremote SHA, pushed SHA, lease mode, and the deterministically created or updated\nopen PR.\n\nThe workflow checks out the Buildchain runtime selected by\n`buildchain-repository` and `buildchain-ref` into `.buildchain/runtime`, invokes\nthat runtime for the propagation plan and lock write, then checks out the\ndownstream repository and writes the exact lock. A consumer that must pin the\nexact upstream package or regenerate deterministic files declares\n`downstream-prepare-command`. The command receives\n`BUILDCHAIN_UPSTREAM_PACKAGE_NAME`, `BUILDCHAIN_UPSTREAM_PACKAGE_VERSION`, and\n`BUILDCHAIN_UPSTREAM_RELEASE_LOCK`. After preparation, Buildchain refreshes an\nexisting `<!-- buildchain:badges:start -->` README block by default. Consumers\ncan disable that step with `refresh-managed-readme-badges: false`.\n\n`downstream-verify-command` runs against the final tree before any commit or\npush, so consumers can use the same check as their PR workflow. Preparation,\nbadge refresh, and verification failures all fail closed. The workflow stages\nthe complete deterministic result, signs the propagation commit with DCO, and\nthen opens or updates the PR. It does not publish the downstream release\ndirectly. The downstream repository keeps its normal Buildchain governance: the\nPR updates source-of-truth facts, then downstream alpha or release publication\nruns through its own protected channel. A byte-identical rerun is an explicit\nsuccessful no-op.\nFor unreleased runtime validation, keep the caller's reusable workflow reference\non `@v2` and pass a temporary train ref through `buildchain-ref`.\n\n## kfd to site-libkungfu-dev\n\nFor `kfd -> site-libkungfu-dev`, the graph should preserve channels:\n\n- a `kfd` alpha release produces a downstream alpha lock and downstream alpha\n publication consumes the exact `@kungfu-tech/kfd@...-alpha.N` package;\n- a `kfd` stable release produces a downstream release lock and downstream\n stable publication consumes the exact stable package.\n\nThis keeps the site synchronized to the package truth without allowing the site\nto drift onto a floating npm dist-tag."
1589
1589
  },
1590
1590
  {
1591
1591
  "id": "manual:reusable-build-surface",
@@ -1599,7 +1599,7 @@
1599
1599
  ],
1600
1600
  "maturity": "stable",
1601
1601
  "sourcePath": "docs/reusable-build-surface.md",
1602
- "digest": "sha256:a2092c8064332cec9a4be852b24d94f90625cb45f885c4c3208f7068a643a3f0",
1602
+ "digest": "sha256:7b745037fedafa1bc2f8ce65f32bb7e887988df2bfe2439694da418d482f6929",
1603
1603
  "headings": [
1604
1604
  {
1605
1605
  "level": 1,
@@ -1697,7 +1697,7 @@
1697
1697
  "anchor": "fixture"
1698
1698
  }
1699
1699
  ],
1700
- "markdown": "# Reusable Build Surface\n\nBuildchain v2 provides a reusable build workflow for repositories that need\nBuildchain's release semantics but cannot be described as a simple Node package.\nThe first target shape is `libnode`: expensive native builds, multiple operating\nsystems, self-hosted runner labels, and release artifacts that must be auditable.\n\n## Automatic Channel Router\n\nThe preferred consumer surface is one reusable workflow call. After v2.12\nreaches the stable major ref, consumers keep this configuration for both alpha\ndevelopment and stable release work:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/build.yml@v2\n permissions:\n contents: read\n issues: write\n id-token: write\n with:\n working-directory: .\n artifact-name: libnode\n runner-preset: kungfu-v4-self-hosted\n publish-channel: none\n secrets: inherit\n```\n\n`buildchain-channel` defaults to `auto`. Selection uses this precedence:\n\n1. an explicit `buildchain-ref` train, SHA, or official channel;\n2. an explicit `buildchain-channel: alpha|stable`;\n3. `publish-channel: alpha|release|major`;\n4. GitHub release prerelease metadata;\n5. a canonical semver tag;\n6. non-release PR, push, dispatch, schedule, and workflow-run events default to\n alpha.\n\nThe resolved runtime is `vN-alpha` for development and prerelease intent and\n`vN` for stable release intent. Unknown custom publish channels, malformed\nrelease events, and non-semver release-like tags fail before the build matrix;\nthey never guess alpha for a stable release.\n\nThe router automatically selects `.buildchain/alpha-contract-lock.json` for\nalpha and `.buildchain/contract-lock.json` for stable. A repository can override\nthe common path with `buildchain-contract-lock-path`, or override one channel\nwith `buildchain-alpha-contract-lock-path` /\n`buildchain-stable-contract-lock-path`.\n\nOnly repositories changing the default policy need extra routing input:\n\n```yaml\nwith:\n buildchain-channel: stable\n```\n\nDuring the v2.12 prerelease evaluation window, canaries use\n`build.yml@v2-alpha`. The same router then selects `v2-alpha` or stable `v2` as\nthe runtime. Production consumers should adopt `build.yml@v2` after the router\nhas reached stable; this keeps the routing shell itself on a stable ref.\n\nThe router is generated from `.build.yml`'s input/output surface. Run\n`node scripts/generate-channel-build-workflow.mjs` after changing the advanced\nbuild workflow; inventory and unit tests reject a stale generated router.\n\n## Advanced Workflow\n\nConsumers that need direct workflow-shell or runtime control call the advanced\nsurface:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n working-directory: .\n artifact-name: libnode\n runner-preset: kungfu-v4-self-hosted\n linux-container-preset: kungfu-verify\n artifact-name-template: \"{artifact}-{platform}-{sha}\"\n artifact-paths: |\n dist\n build/stage\n expected-artifacts-json: >-\n {\"minFiles\":2,\"requiredPaths\":[\"dist/libnode.tar.gz\",\"dist/checksums.txt\"]}\n process-summary-path: .buildchain/diagnostics/process-summary.json\n release-candidate: true\n publish-channel: release\n publish-source-ref: publish-gate/release/v22/v22.22/22.22.3-kf.0\n```\n\n`runner-preset` is the stable first-class surface for known runner fleets:\n\n| Preset | Platforms |\n| ----------------------- | ------------------------------------------------------------------------ |\n| `github-hosted` | `ubuntu-24.04`, `macos-latest`, `windows-2022` |\n| `kungfu-v4-self-hosted` | Kungfu Linux x64, macOS ARM64, and Windows x64 self-hosted runner labels |\n| `custom` | Requires `platforms-json` |\n\nCallers can still provide a custom matrix with `platforms-json`. Each platform\nobject has:\n\n| Field | Meaning |\n| -------- | ------------------------------------------------- |\n| `id` | Stable artifact/platform key, such as `linux-x64` |\n| `name` | Human-readable job name |\n| `runner` | JSON string passed to `runs-on` after `fromJSON` |\n\nThe runner field is intentionally a JSON string so callers can pass either\nGitHub-hosted runners or multi-label self-hosted runners without Buildchain\nguessing the labels.\n\nOnly include platforms that should run. GitHub schedules matrix jobs before\nsteps execute, so a disabled entry with unavailable runner labels can still\nblock the workflow queue.\n\n## Linux Job Containers\n\nLinux build platforms can run inside a digest-pinned job container while macOS\nand Windows keep using native runners. This is the recommended way to remove\nmoving Linux runner prerequisites from Buildchain consumers: the Linux host only\nneeds a GitHub Actions runner, Docker, and network access; common verification\ntools come from the image contract.\n\nUse the Kungfu verification image for lifecycle stages that need Git, jq,\nPython, uv, and fnm, but do not need native compilation:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n runner-preset: kungfu-v4-self-hosted\n linux-container-preset: kungfu-verify\n```\n\n`kungfu-verify` resolves to:\n\n```text\nghcr.io/kungfu-systems/build-images/kungfu-verify@sha256:11f0ba64267ce88174a4f73a9bf833ff4e9c59cd16ec3d08a6432a06c2be6fb1\n```\n\nCallers that own a different Linux image can pass it explicitly:\n\n```yaml\nwith:\n linux-container-preset: custom\n linux-container-image: ghcr.io/example/project-build@sha256:<digest>\n```\n\n`linux-container-image` should be pinned by digest. A floating tag makes the\nrunner surface mutable and weakens Buildchain's release evidence.\n\nThe workflow splits the matrix into two build jobs:\n\n- Linux platforms go to `build-linux-container` when a Linux container is\n configured.\n- All other platforms go to `build-native`.\n\nArtifact names, manifest paths, expected artifact checks, publish-source locks,\nand aggregate summaries are the same in both jobs. The split is an execution\ndetail, not a different artifact contract.\n\n## Native Rust Toolchains\n\nNative lifecycle jobs can request an isolated Rust installation instead of\ndepending on a self-hosted runner user's PATH:\n\n```yaml\nwith:\n setup-rust: true\n rust-toolchain: \"1.96.0\"\n rustup-dist-server: \"https://rsproxy.cn\"\n rustup-update-root: \"https://rsproxy.cn/rustup\"\n cargo-registry-index: ${{ vars.BUILDCHAIN_CARGO_REGISTRY_INDEX }}\n```\n\n`setup-rust` defaults to `false`, so existing consumers are unchanged. When it\nis enabled, Buildchain installs `rust-toolchain` before the install, build, and\nverify lifecycle stages on every native matrix platform. Windows uses the\nofficial rustup bootstrap through `cmd.exe` and `curl.exe` into runner-temporary\nCargo and rustup homes, so it works under a restrictive PowerShell execution\npolicy and the service account does not depend on another user's PATH or mutate\nhost toolchain state. Pin an exact toolchain for release builds. Linux container jobs continue\nto obtain Rust from their digest-pinned image contract; Buildchain does not\nmutate that container surface.\n\nThe rustup server inputs are optional and default to Rust's official servers.\nConsumers behind a slow cross-border link may select a trusted transport mirror;\nrustup still verifies the selected toolchain's distribution metadata and\ncomponent checksums.\n\n`cargo-registry-index` is also optional. When set, Buildchain exposes it to\nCargo as `CARGO_REGISTRIES_CRATES_IO_INDEX` for every native lifecycle stage,\nso a self-hosted runner can use a repository or organization variable without\ncommitting private LAN topology to public workflow YAML. The endpoint must be a\ncrates.io-compatible index whose `config.json` download contract serves the\nmatching checksum-verified crate archives. An empty value preserves Cargo's\nnormal crates.io behavior.\n\nThe container image provides `fnm` but does not preinstall Node. Buildchain uses\n`fnm` inside the container to install the requested `node-version` before it\nruns Buildchain runtime scripts or lifecycle actions.\n\nDo not use `kungfu-verify` for stages that need CMake, Ninja, ccache, Conan, or\nDocker image publishing. Those should use a heavier native-build image or remain\non a host runner until their image contract is explicit.\n\n## Buildchain Runtime Override\n\nStable consumers should keep the reusable workflow pinned to stable refs such as\n`@v2`. The optional `buildchain-ref` input is empty by default; empty means\nBuildchain resolves and executes the stable runtime selected by the workflow\nshell. The full train validation protocol is documented in\n[`runtime-train-validation.md`](runtime-train-validation.md).\n\nFor one-off manual validation, a trusted maintainer can run the caller workflow\nwith a temporary runtime override:\n\n```yaml\non:\n workflow_dispatch:\n inputs:\n buildchain-ref:\n description: \"Temporary Buildchain runtime ref for trusted manual validation\"\n required: false\n default: \"\"\n\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n buildchain-ref: ${{ inputs.buildchain-ref || '' }}\n```\n\nAllowed override refs are deliberately narrow:\n\n| Ref form | Meaning |\n| --- | --- |\n| `train/v2/v2.3/<capability>` | Temporary capability train under the active minor line |\n| `refs/heads/train/v2/v2.3/<capability>` | Explicit branch ref for the same train |\n| `<40-character SHA>` | Exact immutable Buildchain runtime commit |\n\nOverride requests fail closed unless the event is `workflow_dispatch` and the\nactor has write, maintain, or admin permission on the caller repository.\nPull requests, including same-repository pull requests and fork-originated pull\nrequests, cannot use `buildchain-ref` override. This keeps automated PR builds on\nthe stable runtime surface.\n\nEvery run resolves the runtime ref to an immutable SHA before checkout. The job\nsummary and aggregate build summary record the workflow shell ref, requested\nruntime ref, resolved runtime ref, runtime SHA, stability class, trust decision,\nand rollback ref. Train refs are development validation refs: they do not move\n`v2`, `vX.Y`, `vX.Y-alpha`, npm dist-tags, or production release refs, and they\nmust not be pinned as long-term production dependencies.\n\nRuntime override validates Buildchain runtime scripts, CLI code, local actions,\nconfig parsing, and lifecycle behavior. It cannot validate changes that require\nthe outer reusable workflow YAML itself to change, such as new jobs,\npermissions, workflow outputs, or matrix topology. Those changes need a canary\nworkflow path or a temporary explicit workflow ref.\n\n## Floating Ref Contract Lock\n\nStable consumers should use floating major refs such as `@v2`, but a floating\nref is not blind trust. Each released Buildchain ref carries a package-owned\nruntime contract world in `dist/site/buildchain-contract.json`. Consumers may\nkeep a small lock file, `.buildchain/contract-lock.json`, recording the\nBuildchain ref, resolved SHA, contract digest, compatibility digest, accepted\nmajor line, and compatibility policy they reviewed.\n\nThe reusable build trust gate checks this lock before any heavy matrix job:\n\n1. resolve the Buildchain runtime ref, for example `v2`, to an immutable SHA;\n2. read `dist/site/buildchain-contract.json` from that checked-out Buildchain\n ref;\n3. read the consumer's `.buildchain/contract-lock.json`;\n4. compare the accepted contract with the current contract.\n\nSHA drift alone is not a failure. `v2` is expected to advance. Buildchain only\nfails fast when the accepted contract is no longer compatible, for example a\nrequired input is removed, a required output disappears, a protected behavior\npromise changes, or the major line changes. Additive changes such as optional\ninputs, optional outputs, diagnostics, or documentation updates continue under\nthe default `major-compatible` policy.\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n permissions:\n contents: read\n issues: write\n id-token: write\n with:\n buildchain-contract-lock-path: .buildchain/contract-lock.json\n buildchain-contract-compatibility-policy: major-compatible\n buildchain-contract-drift-issue-mode: compatible-and-breaking\n```\n\nWhen compatible drift is detected, the build continues and Buildchain opens or\nupdates a low-priority issue in the consumer repository. The issue records the\nold SHA/digest, new SHA/digest, compatibility result, workflow run, and the next\naction: review the Buildchain release notes and update the lock. When breaking\ndrift is detected, the same issue path is used, but the trust gate fails before\nmatrix build or publish work starts. If the workflow token cannot write issues,\nBuildchain writes a copyable issue body into the job summary.\n\nThe lock is intentionally small. It does not copy the full contract. The full\ncontract remains in the Buildchain ref and package; the consumer records only\nwhat it accepted and the policy used to compare future floating-ref movement.\n\nAdvanced alpha-channel consumers select the matching workflow shell:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2-alpha\n with:\n buildchain-contract-lock-path: .buildchain/contract-lock.json\n```\n\nThe runtime follows the called workflow through `job.workflow_ref`. Callers may\nalso pass `buildchain-ref: v2-alpha` explicitly; official floating refs are\nordinary channel selections and are allowed on pull requests and pushes. Train\nrefs and exact SHAs remain trusted manual overrides.\n\n## Shifu Cache Profile Passthrough\n\nBuildchain can carry one trusted Shifu cache-profile reference and its exact\ndigest into lifecycle execution. Its contract is an opaque reference and digest\nonly. This surface is deliberately opaque:\nBuildchain does not fetch the profile, parse JSON, select cache services,\nrewrite bindings, decide fallback, or emit Shifu resolution evidence. Those\nsemantics remain owned by the consumer's pinned Shifu implementation.\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/build.yml@v2\n with:\n shifu-cache-profile-ref: ${{ vars.SHIFU_CACHE_PROFILE_REF }}\n shifu-cache-profile-digest: ${{ vars.SHIFU_CACHE_PROFILE_DIGEST }}\n```\n\nThe reusable workflow passes the pair as `SHIFU_CACHE_PROFILE_REF` and\n`SHIFU_CACHE_PROFILE_DIGEST` to install, build, and verify lifecycle commands.\nThe consumer must invoke its Shifu cache-aware execution surface. An empty pair\npreserves existing behavior; a consumer Shifu should fail closed when exactly\none value is present or the resolved bytes do not match the expected digest.\n\nUse trusted repository or organization variables rather than PR-controlled\nfiles for private/LAN references. The variables must remain secret-free; any\ncredentials use a separate provider-approved secret surface and must not be\nembedded in the profile reference. This passthrough is separate from\nBuildchain's locked source checkout cache below: Buildchain owns checkout\ntransport and source identity, while Shifu owns post-checkout execution cache\nbindings and receipts.\n\n## Locked Source Checkout Cache\n\nSelf-hosted runners that build large repositories can opt into a locked checkout\ncache for both the consumer source and the Buildchain runtime. This changes only\nthe Git object transport. Buildchain still resolves `publish-source-sha` and the\nruntime SHA before any build runner starts, checks out those exact commits, and\nverifies each final `HEAD` plus the resolved consumer source tree SHA before\nlifecycle commands run.\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n runner-preset: kungfu-v4-self-hosted\n checkout-cache-mode: auto\n checkout-cache-mirror-url-template: ${{ vars.BUILDCHAIN_CHECKOUT_CACHE_MIRROR_URL_TEMPLATE }}\n checkout-cache-reference-repository-template: ${{ vars.BUILDCHAIN_CHECKOUT_CACHE_REFERENCE_REPOSITORY_TEMPLATE }}\n checkout-cache-fallback: github\n checkout-cache-timeout-seconds: 60\n checkout-cache-github-timeout-seconds: 600\n checkout-cache-fetch-attempts: 3\n```\n\n`checkout-cache-mode` accepts:\n\n| Mode | Behavior |\n| --- | --- |\n| `off` | Default. Buildchain fetches the locked commit from GitHub. |\n| `auto` | Try the trusted cache first; on miss, record the miss and fall back according to `checkout-cache-fallback`. |\n| `require` | Require the cache to provide the locked commit and fail before lifecycle work if unavailable. |\n\nThe cache can be a local/LAN mirror URL template or a runner-local bare\nreference repository template. Templates support `{owner}`, `{repo}`,\n`{repository}`, `{repositorySlug}`, and `{sha}`. The workflow also reads\nrepository or organization variables named\n`BUILDCHAIN_CHECKOUT_CACHE_MIRROR_URL_TEMPLATE` and\n`BUILDCHAIN_CHECKOUT_CACHE_REFERENCE_REPOSITORY_TEMPLATE`, so consumers can keep\nprivate LAN topology out of repository YAML.\n\nThe GitHub-hosted trust gate resolves the reusable workflow shell to an exact\ncommit and uploads that shell's small checkout bootstrap script. Native and\nLinux-container build jobs download the bootstrap, then use the same cache\npolicy to obtain both the selected Buildchain runtime and consumer source at\ntheir already resolved immutable SHAs. Keeping the bootstrap owned by the\nworkflow shell is important when `@vN-alpha` routes a stable release to an older\n`vN` runtime: the stable runtime does not need to already contain the newest\ncheckout transport implementation. This also prevents a large direct\n`actions/checkout` runtime clone from becoming a separate timeout path on\nconstrained self-hosted uplinks. The bootstrap artifact does not contain the\nruntime repository and cannot move either selected ref.\n\nDo not read cache URLs or reference paths from PR-controlled files such as\n`.buildchain/buildchain.toml`. These values are trusted workflow inputs or repo/org\nvariables. Buildchain does not pass GitHub credentials to cache mirrors or\nreference repositories. If it must fall back to GitHub, the workflow token is\nused only for the GitHub fetch path. Cache attempts use\n`checkout-cache-timeout-seconds`; the potentially larger GitHub fallback uses\nthe independent `checkout-cache-github-timeout-seconds` budget (600 seconds by\ndefault). Buildchain fetches the advertised source ref before trying an exact\nSHA, so a cache hit or stale-cache seed can contribute objects and the fallback\ndoes not first waste a full timeout on an unadvertised SHA. Retryable timeout\nand transient network failures use the bounded `checkout-cache-fetch-attempts`\nbudget; permanent failures stop immediately. Diagnostics record both timeout\nbudgets and the actual GitHub fetch attempts before exact HEAD/tree\nverification.\n\nEach platform diagnostics artifact includes `source-checkout.json` and embeds a\ncompact `sourceCheckout` summary in `diagnostics.json`: mode, transport,\nhit/miss, fallback reason, duration, final HEAD verification, and tree\nverification. Remote URLs are sanitized and local reference paths are represented\nby a short display name plus fingerprint, not by secret-bearing credentials.\nRuntime checkout evidence is uploaded separately as `runtime-checkout.json`,\nincluding cache transport, fallback attempts, and exact runtime `HEAD`\nverification, even when a later lifecycle step fails.\n\nWhen a Buildchain maintainer asks for downstream validation, the expected\nrequest is:\n\n```text\nBuildchain train ready: buildchain-ref=train/v2/v2.3/<capability>.\nKeep uses: ...@v2; run workflow_dispatch with that buildchain-ref and report the runtime evidence summary.\n```\n\nAfter validation succeeds, the Buildchain change should continue through the\nnormal mainline and release path. Do not treat the train as a pending merge\nitem; it is only a temporary fast-use, diagnostic, and rollback channel. It may\nremain for a retention window after release, with old trains handled by a\nseparate periodic cleanup task.\n\n## Workflow Outputs\n\nThe reusable workflow exposes the resolved contract:\n\n| Output | Meaning |\n| --------------------------------- | ------------------------------------------------------------------------------- |\n| `runner-preset` | Resolved preset, or `custom` when `platforms-json` was provided |\n| `platforms-json` | Exact matrix JSON used by the build job |\n| `platform-count` | Number of matrix platforms |\n| `linux-container-enabled` | `true` when Linux platforms are routed through a job container |\n| `linux-container-image` | Resolved digest-pinned Linux job container image |\n| `build-summary-artifact` | Uploaded aggregate summary artifact name |\n| `build-diagnostics-summary-artifact` | Uploaded aggregate diagnostics summary artifact name |\n| `release-candidate-passport-artifact` | Uploaded PR-stage release-candidate passport artifact name when `release-candidate` is enabled |\n| `release-candidate-passport-json` | Compact release-candidate passport JSON when `release-candidate` is enabled |\n| `build-summary-json` | Compact aggregate JSON with platform count, file count, and byte total |\n| `build-diagnostics-summary-json` | Compact aggregate diagnostics JSON with platform, lifecycle warning/error, diagnostics contract warning, and sidecar manifest warning totals |\n| `trusted-event` | `true` when the event is trusted enough to reach build runners |\n| `buildchain-runtime-ref` | Runtime ref selected after applying the empty-default or override policy |\n| `buildchain-runtime-sha` | Immutable Buildchain runtime commit used by all runtime checkouts |\n| `buildchain-runtime-class` | `stable`, `alpha`, `train`, `exact-sha`, or `development` |\n| `buildchain-runtime-override` | `true` when a train or exact-SHA `buildchain-ref` override was accepted |\n| `buildchain-runtime-trust-decision` | Runtime override trust decision |\n| `buildchain-contract-lock-status` | `unchanged`, `compatible-drift`, `breaking-drift`, `missing-lock`, `non-floating-runtime`, or first-release `runtime-contract-unavailable` |\n| `buildchain-contract-lock-drift` | `true` when the floating runtime SHA or contract digest changed |\n| `buildchain-contract-digest` | Current Buildchain runtime contract digest |\n| `publish-channel` | Resolved publish channel requested by the caller |\n| `publish-allowed` | `true` only when this event/ref may publish after verification |\n| `publish-reason` | Human-readable reason for the publish gate decision |\n| `publish-source-ref` | Gate source ref that was resolved before checkout |\n| `publish-source-sha` | Exact source commit used by checkout, build, verify, and artifacts |\n| `publish-source-locked` | `true` when a `publish-gate/*` source ref was explicitly locked |\n| `publish-source-channel` | `alpha`, `release`, `anchor`, or `major` parsed from the source ref |\n| `publish-source-line` | Product line parsed from source refs such as `v22/v22.22` |\n| `publish-source-consumer-version` | Consumer package version parsed from source refs |\n| `release-manifest-json` | Resolved release manifest including source lock, version state, and anchor data |\n\nThe aggregate summaries are intentionally artifacts as well as outputs. GitHub\nActions matrix outputs are not a reliable place to carry every platform's full\nmanifest, so Buildchain uploads each platform manifest and then emits one\naggregate build summary artifact after the matrix completes. Buildchain uploads\n`diagnostics-summary.json` as a separate aggregate diagnostics summary artifact,\na compact rollup of each platform's small diagnostics upload. The rollup keeps\nper-platform runner facts, checked tool versions/missing tools, package\nmanager/cache directory details, compiler-cache availability, lifecycle timing,\nprocess sampler context, and links back to the exact platform artifacts. Each\nplatform diagnostics upload includes `diagnostics.json`,\n`diagnostics-manifest.json`, the lifecycle `events.jsonl`, and process sampler\nsidecars when enabled, so slow-build diagnosis does not require downloading the\nbinary platform artifact or the aggregate build summary. The sidecar manifest\nrecords the uploaded diagnostics files with bytes and sha256 hashes. Each\n`diagnostics.json` also records the related binary artifact name, manifest\nartifact name, diagnostics artifact name, diagnostics sidecar manifest path, and\nplatform id in `links`, so a reviewer can navigate from the small diagnostics\nartifact back to the exact platform outputs when deeper inspection is needed.\nThe workflow output `build-diagnostics-summary-json` includes\n`diagnosticsContractWarningCount` and `diagnosticsManifestWarningCount` so\nrelease jobs can detect drifting diagnostics JSON contracts and missing or\ndrifting diagnostics sidecar manifests without downloading the per-platform\ndiagnostics artifacts first.\n\n## Artifact Transfer Relay\n\nBy default, platform jobs upload payloads, manifests, and diagnostics directly\nto GitHub artifacts:\n\n```yaml\nwith:\n artifact-transfer-mode: github-artifacts\n```\n\nLarge self-hosted native builds can opt into the first-class S3 relay path:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n runner-preset: kungfu-v4-self-hosted\n artifact-transfer-mode: s3-to-github-artifacts\n artifact-relay-s3-bucket: ${{ vars.BUILDCHAIN_ARTIFACT_RELAY_S3_BUCKET }}\n artifact-relay-s3-region: ${{ vars.BUILDCHAIN_ARTIFACT_RELAY_S3_REGION }}\n artifact-relay-s3-prefix: ${{ vars.BUILDCHAIN_ARTIFACT_RELAY_S3_PREFIX }}\n```\n\nIn relay mode, each self-hosted platform job uploads the heavy payload files to\nS3 and uploads only a small `relay-manifest.json` to GitHub. A GitHub-hosted\n`relay-artifacts` job then assumes the configured download role, downloads the\npayloads from S3, verifies every file by SHA256, and re-uploads the normal\nGitHub artifacts under the same artifact names that direct mode uses. Downstream\nsummary, release-candidate, and promote-only workflows therefore continue to\nconsume GitHub artifacts and do not need custom S3 logic.\nThe relay implementation uses Node.js plus the standard AWS environment\ncredentials from GitHub OIDC; runner images and build containers do not need the\nAWS CLI installed.\n\nAfter the GitHub artifact uploads succeed, Buildchain deletes the S3 objects\nlisted in the relay manifest for that platform. If any download, verification,\nor GitHub artifact upload fails, cleanup is skipped so maintainers can inspect\nthe retained S3 payload. Configure a short bucket lifecycle expiration as a\ncost and cleanup backstop.\n\nThe relay configuration is intentionally generic. Buildchain does not hard-code\norganization buckets, regions, or role ARNs. Callers may pass explicit inputs,\nor set repository/organization variables and secrets using these names:\n\n| Variable or secret | Meaning |\n| --- | --- |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_BUCKET` | Relay bucket name |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_REGION` | Relay bucket region |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_PREFIX` | Relay object prefix; defaults to `buildchain-artifacts` |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_ROLE_ARN` | Shared OIDC role ARN for upload and download |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_UPLOAD_ROLE_ARN` | Upload OIDC role ARN for self-hosted build jobs |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_DOWNLOAD_ROLE_ARN` | Download OIDC role ARN for the GitHub-hosted relay job |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_OIDC_AUDIENCE` | Optional OIDC audience override |\n\nFor AWS China regions, Buildchain defaults the OIDC audience to\n`sts.amazonaws.com.cn`; other regions default to `sts.amazonaws.com`. The caller\nworkflow must allow `id-token: write`, and the target role trust policy should\nrestrict GitHub OIDC claims to the expected organization, repository, workflow,\nand branch/ref. The S3 permissions should be scoped to the relay bucket/prefix\nused by the repository.\nUpload roles need write/delete access under the relay prefix; download roles\nneed read access plus delete access for successful cleanup.\n\nRelay mode is opt-in and does not affect forks or open-source users that do not\nconfigure S3. Missing bucket, region, upload role, or download role values fail\nbefore the heavy build matrix is scheduled. Buildchain treats S3 as a transport\ncache, not as the final release evidence store; the final audit entry remains\nthe GitHub artifact set plus the Buildchain build summary and release-candidate\npassport.\n\nSet `release-candidate: true` when the successful reusable build is meant to be\nthe artifact source promoted later. Buildchain then uploads\n`release-candidate-passport.json` under the\n`<artifact-name>-release-candidate-<publish-source-sha>` artifact name. Promotion\njobs can pass that passport to `promote-buildchain-ref` with\n`promote-only-release-candidate: \"true\"` so source, channel, platforms, and the\naggregate build-summary hash are checked before publish-gate side effects. The\npassport records the locked commit's Git tree SHA, so a post-merge channel HEAD\ncan be accepted only when it is tree-equivalent to the PR-stage build evidence.\n\n## Publish Gate\n\nBuildchain separates \"may build/verify\" from \"may publish.\" A same-repository\npull request may be trusted enough to run the build matrix, but it still must\nnot publish packages, S3 objects, release pages, or preview aliases. Publishing\nis allowed only when the caller explicitly requests a channel and the current\nevent/ref matches that channel.\n\nUse `publish-channel` to request a channel:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n publish-channel: release\n\n publish:\n needs: build\n if: ${{ needs.build.outputs.publish-allowed == 'true' }}\n runs-on: ubuntu-24.04\n steps:\n - run: ./scripts/publish.sh\n```\n\nDefault channels are:\n\n| Channel | Allowed refs |\n| --------- | ---------------------------------------------------------------------------------------------------- |\n| `none` | Never publishes; this is the default |\n| `alpha` | `alpha/vN/vN.M` branches or exact `vN.M.P-alpha.K` tags |\n| `release` | `release/vN/vN.M` branches or release tags such as `vN.M.P`, `vN.M`, `vN` |\n| `major` | `publish-gate/major`, legacy `major-gate`, or next-major release tags such as `vN.0.0`, `vN.0`, `vN` |\n\nPull request events always produce `publish-allowed=false`, even when the PR is\nfrom the same repository. Untrusted fork events also produce\n`publish-allowed=false`; with the default `untrusted-policy: fail`, the workflow\nthen fails before any build runner starts.\n\nProjects with their own channel names can pass `publish-refs-json`:\n\n```yaml\nwith:\n publish-channel: nightly\n publish-refs-json: >-\n {\"nightly\":[\"^refs/heads/nightly/v\\\\d+$\"]}\n```\n\nThe aggregate build summary includes the same publish gate decision under\n`publishGate`, so a downloaded artifact summary explains both what was built and\nwhy it was or was not eligible to publish.\n\n## Publish Source Lock\n\n`publish-channel` answers \"may this event publish?\" Source lock answers \"which\nsource tree is the publish decision about?\" A caller can pass `publish-source-ref`\nto bind a publish run to a reviewed gate branch before any checkout happens:\n\n| Ref | Meaning |\n| ------------------------------------------------ | --------------------------------------------------------------------------- |\n| `publish-gate/alpha/<line>/<consumer-version>` | Build and publish an alpha candidate for a consumer line |\n| `publish-gate/release/<line>/<consumer-version>` | Build and publish a production candidate for a consumer line |\n| `publish-gate/anchor` | Resolve an explicit anchor request; it does not publish artifacts by itself |\n| `publish-gate/major` | Gate the next major source state |\n| `major-gate` | Legacy compatibility alias for the major gate |\n\nFor alpha and release refs, `<line>` is intentionally allowed to contain `/`, so\nKungfu-style lines such as `v22/v22.22` stay readable. The final path segment is\nthe consumer-visible version, for example `22.22.3-kf.0`.\n\nThe reusable workflow resolves the branch tip to `publish-source-sha`, checks out\nthat SHA in every build job, and uses the same SHA in artifact names, manifests,\nand aggregate summaries. Reruns therefore rebuild the same source tree even if a\ngate branch moves later.\n\nBefore any heavy build matrix is scheduled, the workflow also verifies that the\ntarget channel ref implied by the source lock already points at\n`publish-source-sha` and that the target channel HEAD came from the required\nmerged same-repository channel PR. `publish-gate/alpha/<line>/<version>` must\nmatch `alpha/<line>` and have PR lineage `dev/<line> -> alpha/<line>`;\n`publish-gate/release/<line>/<version>` must match `release/<line>` and have PR\nlineage `alpha/<line> -> release/<line>`. If either check fails, the run fails\nfast with a diagnostic telling maintainers to merge the source commit through\nthe channel PR first. This keeps verify from spending runner time on a source\ntree that cannot legally enter the requested publish channel.\n\nThe resolved release manifest is uploaded as an artifact and emitted as\n`release-manifest-json`. It records:\n\n- source ref, source SHA, channel, line, and consumer version;\n- configured version strategy and configured version-state files;\n- each version file's value, with release gates failing closed if the configured\n files do not equal the consumer version;\n- anchor manifest summary for anchored/manual projects;\n- explicit anchor request JSON for `publish-gate/anchor`;\n- publish registry, dist-tag, and gate visibility metadata.\n\nPublish side-effect jobs should verify the lock immediately before publishing:\n\n```yaml\n- name: Verify publish gate did not move\n run: node .buildchain/runtime/scripts/verify-publish-source-lock.mjs\n env:\n BUILDCHAIN_PUBLISH_SOURCE_REF: ${{ needs.build.outputs.publish-source-ref }}\n BUILDCHAIN_PUBLISH_SOURCE_SHA: ${{ needs.build.outputs.publish-source-sha }}\n BUILDCHAIN_SOURCE_REPOSITORY: ${{ github.repository }}\n GITHUB_TOKEN: ${{ github.token }}\n```\n\nIf the branch tip no longer matches the manifest SHA, the publish job must fail\nclosed. Moving a gate branch creates a new publish decision and should produce a\nnew build run.\n\n## Release Candidate Promote-Only\n\nFor native package sets, the PR build is the only heavy build. When a PR targets\n`alpha/<line>` or `release/<line>`, `.build.yml` uploads a release-candidate\nbundle next to the platform artifacts. The bundle contains:\n\n- `release-candidate.passport.json`;\n- the aggregate `build-summary.json`;\n- copied platform manifest evidence for the built platforms.\n\nThe passport records two separate source identities:\n\n- `builtSourceSha` / `builtSourceTreeSha`: the PR-stage source that produced the\n artifacts, usually the PR merge ref;\n- `promotionChannelSha` / `promotionChannelTreeSha`: the post-merge channel\n commit used for publish authority.\n\nThe reusable promote wrapper resolves the merged PR, finds exactly one matching\nPR-stage release-candidate artifact, downloads it with the build summary and\npayload artifacts from the same PR-stage run, validates the payload count,\ncompares the built tree with the promotion channel tree, locks\n`publish-gate/{alpha,release,major}` to the promotion channel commit, and then\ncalls `actions/promote-buildchain-ref` with\n`promote-only-release-candidate: \"true\"` and\n`require-publish-source-lock: \"true\"`. The wrapper passes the created\n`publish-gate/*` ref, target SHA, and `locked=true` into the promote action, so\nfloating `@v2` consumers receive publish-side source-lock drift protection by\ndefault. It also defaults `branch-protection-bypass-apps` to `github-actions`\nso the workflow automation can apply generated version-state and channel\nbookkeeping on protected `dev`/`alpha`/`release` branches after the reviewed\nchannel PR has merged; consumers using a different promotion identity can pass\n`branch-protection-bypass-users`, `branch-protection-bypass-teams`, or a\ndifferent app slug declaratively. The wrapper uses\n`secrets.BUILDCHAIN_PROMOTION_TOKEN || github.token` as the generated ref update\ntoken for protected bookkeeping PATCH calls, so a bypass-capable promotion token\ncan sync dev immediately after alpha/release publish without a post-publish PR.\nIt does not call `.build.yml`, does not create a matrix, and must fail before\npublish if the RC evidence, payload set, or source-lock ref is missing or\nambiguous.\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n secrets:\n buildchain-issue-app-id: ${{ secrets.BUILDCHAIN_ISSUE_APP_ID }}\n buildchain-issue-app-private-key: ${{ secrets.BUILDCHAIN_ISSUE_APP_PRIVATE_KEY }}\n with:\n channel: alpha\n target-ref: alpha/v22/v22.22\n artifact-name: libnode\n # Defaults to build.yml / Build. Override only when the PR-stage build\n # workflow uses a different file or display name.\n release-candidate-workflow-file: build.yml\n release-candidate-workflow-name: Build\n package-manager: npm\n publish-target: npm\n runner-preset: github-hosted\n trusted-publishing: true\n github-release: true\n required-status-check: check / check\n required-artifact-count: 3\n publish-dist-tag: alpha\n publish-package-set-order: platforms-first-main-last\n publish-package-main: \"@kungfu-tech/libnode\"\n release-passport-product-name: Libnode\n buildchain-contract-lock-path: .buildchain/contract-lock.json\n buildchain-contract-drift-issue-mode: compatible-and-breaking\n```\n\n`buildchain-issue-app-id` and `buildchain-issue-app-private-key` are optional\nbut recommended for cross-repository consumers. They should identify a GitHub\nApp installation with `issues: write` on `kungfu-systems/buildchain`; the\nwrapper mints the installation token before calling\n`actions/report-buildchain-issue`. Consumers can also pass a pre-minted\n`buildchain-issue-token`. If both are omitted, the wrapper falls back to\n`BUILDCHAIN_ISSUE_TOKEN`, `BUILDCHAIN_PROMOTION_TOKEN`, and then the consumer\nworkflow's `github.token`; the last fallback can only report issues when it has\nwrite access to the target Buildchain repository.\n\n`publish-required-artifacts-json` can still be passed explicitly for custom\npublish targets. Custom OCI requirements may omit pre-publish refs and digests;\nthe action resolves the exact version ref and validates final digests and any\nbuilt/reused provenance after `lifecycle.publish`. For the default\n`publish-artifact-kind: npm` path, consumers do\nnot download artifacts or run repository scripts to build publish evidence. The\nwrapper downloads the PR-stage payload artifacts, finds the downloaded `.tgz`\npackages, reads each tarball's `package/package.json` for the real scoped\npackage name and version, computes the npm `sha512-...` integrity from the\ntarball bytes, marks the package matching `publish-package-main` as `role:\nmain`, marks the rest as `role: platform`, and passes the generated\n`publish-required-artifacts-json` to `promote-buildchain-ref` before any publish\nside effect. Downloaded platform manifests are still passed into the release\npassport unless `release-passport-platform-manifest-paths` is set explicitly.\nThe same Buildchain contract lock check runs before release-candidate\nresolution and before publish. A compatible `v2` drift leaves an issue in the\nconsumer repository but does not trigger a second heavy build; an incompatible\ndrift fails before publish side effects.\n\nThe wrapper publishes the public release tag as a GitHub Release by default.\nAfter `promote-buildchain-ref` reports a complete release transaction, the\nwrapper creates or updates the public release, marks semver prerelease tags\nsuch as `v1.2.3-alpha.0`, `v1.2.3-rc.1`, or `v22.22.3-kf.3-alpha.7` as\n`prerelease=true` and `make_latest=false`, marks stable semver tags as latest,\nand uploads the publish evidence file plus every file in the generated release\npassport directory, including `buildchain.release.json` and `check-report.json`.\nFor anchored/manual package releases, the public release tag is derived from the\npublished package version and the internal exact transaction tag remains\navailable in the release passport.\nConsumers do not need to hand-write `gh release` logic to trigger\n`release.published` propagation. Set `github-release: false` only for\nrepositories that intentionally do not maintain GitHub Releases.\nIf the transaction still needs protected-ref finalization, the wrapper defers\nGitHub Release creation until the later run that reaches `state=complete`.\n\nCustom publish jobs can also repeat the channel-ref preflight:\n\n```yaml\n- name: Verify publish channel ref still matches\n run: node .buildchain/runtime/scripts/verify-publish-channel-ref.mjs\n env:\n BUILDCHAIN_PUBLISH_SOURCE_REF: ${{ needs.build.outputs.publish-source-ref }}\n BUILDCHAIN_PUBLISH_SOURCE_SHA: ${{ needs.build.outputs.publish-source-sha }}\n BUILDCHAIN_SOURCE_REPOSITORY: ${{ github.repository }}\n GITHUB_TOKEN: ${{ github.token }}\n```\n\nAnchored/manual package release jobs should also make the Buildchain promotion\naction validate that publication is entering through the same\n`publish-gate/{alpha,release,major}` source-lock contract before any package\npublish side effect:\n\n```yaml\n- name: Promote release ref and publish npm package set\n uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: release/v22/v22.22\n require-publish-source-lock: 'true'\n publish-source-ref: ${{ needs.build.outputs.publish-source-ref }}\n publish-source-sha: ${{ needs.build.outputs.publish-source-sha }}\n publish-source-locked: ${{ needs.build.outputs.publish-source-locked }}\n```\n\n`target-ref` stays the Buildchain channel promotion target, such as\n`alpha/v22/v22.22`, `release/v22/v22.22`, or `publish-gate/major`.\n`publish-source-ref` is the reviewed source-lock branch that authorized this\nspecific package publication. For alpha and release package publications, the\nsource-lock branch must point at the exact channel-line commit that promotion is\nvalidating; it is not a replacement for `target-ref`.\n\nThis keeps the version bump commit, publish authorization, and auditable publish\nentrypoint on the Buildchain source-lock protocol. The CLI form\n`buildchain publish-source validate-anchored-release --json` is still useful for\ncustom publish scripts, but the preferred GitHub Actions gate is the promotion\naction input above. A publish job that still runs directly from `alpha/*` or\n`release/*` channel branches fails this check because those refs are channel\nstate, not publish-gate decisions.\n\n## Package-Set Publish Plan\n\nProjects that publish multiple packages should treat package publication as a\npackage-set operation. Buildchain's package-set planner uses these rules:\n\n- platform packages publish first;\n- the main package publishes last;\n- the dist-tag move happens only after the full package set is present;\n- reruns accept already-published packages only when package name, version, and\n integrity match;\n- an existing package with different integrity is a hard failure.\n\nThis keeps a consumer from observing a floating dist-tag that points to a main\npackage before all platform artifacts for the same source SHA are available.\n\n## Command Sources\n\nThe workflow runs `.buildchain/buildchain.toml` lifecycle stages by default:\n\n```toml\n[lifecycle.install]\ncommand = \"corepack yarn install --immutable\"\n\n[lifecycle.build]\ncommands = [\n \"corepack yarn make\",\n \"corepack yarn build\",\n]\n\n[lifecycle.verify]\ncommand = \"corepack yarn test\"\n```\n\nCallers can override any stage for one invocation:\n\n```yaml\nwith:\n build-command: cmake --build build --config Release\n verify-command: ctest --test-dir build --output-on-failure\n```\n\nEvery native and container matrix job is bounded by\n`lifecycle-timeout-minutes`, which defaults to 120 minutes. The same input is\nthe fallback deadline for each install, build, and verify action, so a hung\ncommand fails with the lifecycle name and matrix platform before it can occupy\na self-hosted runner indefinitely. A stage-level `timeout_minutes` in\n`buildchain.toml` remains the more specific override for that stage.\n\n```yaml\nwith:\n lifecycle-timeout-minutes: 90\n```\n\nThe reusable build workflow samples the build lifecycle by default and carries\nthe generated summary into the final verify diagnostics. Callers can override\nthe sidecar path or disable sampling:\n\n```yaml\nwith:\n sample-process-tree: true\n process-summary-path: .buildchain/diagnostics/process-summary.json\n process-sample-interval-ms: 15000\n requested-parallelism: 20\n```\n\nWhen `sample-process-tree` is true, Buildchain wraps either `build-command` or\nthe configured `lifecycle.build` stage with `buildchain sample process-tree`.\nThe path is relative to the checked-out workspace and is read again during the\nfinal verify lifecycle. Custom workflows can still write their own sampler\nsummary and pass `process-summary-path`; Buildchain reads the file after the\nlifecycle command finishes, so it may be produced during the same invocation.\nWhen the build stage is optional, the reusable workflow treats the default\nsampler path as optional during verify; an explicitly supplied\n`process-summary-path` remains required.\n\nFor custom workflows, use the action directly:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/run-lifecycle@v2\n with:\n stage: build\n required: \"true\"\n timeout-minutes: \"90\"\n artifact-name: libnode-linux-x64-${{ github.sha }}\n artifact-paths: |\n dist\n build/stage\n```\n\n## Artifact Contract\n\nEach platform upload uses `artifact-name-template`. The default is:\n\n```text\n{artifact}-{platform}-{sha}\n```\n\nSupported placeholders are `{artifact}`, `{artifactName}`, `{platform}`,\n`{platformId}`, `{platformName}`, `{sha}`, `{shortSha}`, `{ref}`, `{runId}`,\nand `{runAttempt}`. Invalid GitHub artifact name characters are normalized to\n`-`, so `{ref}` remains deterministic even for refs such as\n`refs/heads/dev/v2/v2.0`.\n\nEach platform also writes and uploads:\n\n```text\n.buildchain/artifacts/<platform-id>/manifest.json\n.buildchain/artifacts/<platform-id>/summary.json\n```\n\nThe manifest schema is:\n\n```json\n{\n \"schemaVersion\": 1,\n \"contract\": \"kungfu-buildchain-artifact\",\n \"artifactName\": \"libnode-linux-x64-<sha>\",\n \"platform\": {\n \"id\": \"linux-x64\",\n \"name\": \"Linux x64\",\n \"os\": \"Linux\",\n \"arch\": \"X64\"\n },\n \"git\": {\n \"repository\": \"kungfu-systems/libnode\",\n \"sha\": \"<sha>\",\n \"ref\": \"<ref>\",\n \"runId\": \"<run id>\",\n \"runAttempt\": \"<attempt>\"\n },\n \"lifecycle\": {\n \"stage\": \"verify\",\n \"commandSource\": \"buildchain.toml\",\n \"executed\": true\n },\n \"summary\": {\n \"contract\": \"kungfu-buildchain-artifact-summary\",\n \"artifactName\": \"libnode-linux-x64-<sha>\",\n \"fileCount\": 1,\n \"totalBytes\": 1234,\n \"digest\": \"<hex>\"\n },\n \"expectedArtifacts\": {\n \"ok\": true,\n \"source\": \"expected-artifacts-json\",\n \"checks\": []\n },\n \"files\": [\n {\n \"path\": \"dist/example.zip\",\n \"size\": 1234,\n \"sha256\": \"<hex>\"\n }\n ]\n}\n```\n\nArtifact names do not include actor names, timestamps, or retry counters. Reruns\nproduce a new GitHub Actions run but keep the same source SHA/platform contract.\n\n`expected-artifacts-json` fails the build before upload when the artifact does\nnot match the caller's declared contract. Supported checks are:\n\n| Field | Meaning |\n| --------------- | ------------------------------------ |\n| `minFiles` | Minimum number of manifest files |\n| `maxFiles` | Maximum number of manifest files |\n| `minTotalBytes` | Minimum total byte count |\n| `requiredPaths` | Exact manifest paths that must exist |\n\n## Trusted Event Gate\n\nThe workflow has an explicit `trust-gate` job. By default, pull requests from\nforks fail before any build job can reach self-hosted runners, secrets,\npublishing credentials, or heavyweight build commands. Same-repository PRs,\nworkflow dispatches, and protected branch events can proceed.\n\nIf a repository wants fork PRs to skip rather than fail, it can set:\n\n```yaml\nwith:\n untrusted-policy: skip\n```\n\nDo not set `require-trusted-event: false` for workflows that use self-hosted\nrunners or secrets.\n\n`require-trusted-event` controls access to build runners. It does not override\nthe publish gate: pull requests remain non-publishing events.\n\n## Fixture\n\n`fixtures/libnode-shaped` is the contract fixture. It has:\n\n- `package.json` version state;\n- `.buildchain/buildchain.toml` with `install`, `build`, `verify`, and `publish`;\n- cross-platform Node scripts that create small `dist/` outputs;\n- `Build Surface Fixture` workflow coverage.\n\nThe fixture proves the reusable surface without running the real libnode native\nbuild."
1700
+ "markdown": "# Reusable Build Surface\n\nBuildchain v2 provides a reusable build workflow for repositories that need\nBuildchain's release semantics but cannot be described as a simple Node package.\nThe first target shape is `libnode`: expensive native builds, multiple operating\nsystems, self-hosted runner labels, and release artifacts that must be auditable.\n\n## Automatic Channel Router\n\nThe preferred consumer surface is one reusable workflow call. After v2.12\nreaches the stable major ref, consumers keep this configuration for both alpha\ndevelopment and stable release work:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/build.yml@v2\n permissions:\n contents: read\n issues: write\n id-token: write\n with:\n working-directory: .\n artifact-name: libnode\n runner-preset: kungfu-v4-self-hosted\n publish-channel: none\n secrets: inherit\n```\n\n`buildchain-channel` defaults to `auto`. Selection uses this precedence:\n\n1. an explicit `buildchain-ref` train, SHA, or official channel;\n2. an explicit `buildchain-channel: alpha|stable`;\n3. `publish-channel: alpha|release|major`;\n4. GitHub release prerelease metadata;\n5. a canonical semver tag;\n6. non-release PR, push, dispatch, schedule, and workflow-run events default to\n alpha.\n\nThe resolved runtime is `vN-alpha` for development and prerelease intent and\n`vN` for stable release intent. Unknown custom publish channels, malformed\nrelease events, and non-semver release-like tags fail before the build matrix;\nthey never guess alpha for a stable release.\n\nThe router automatically selects `.buildchain/alpha-contract-lock.json` for\nalpha and `.buildchain/contract-lock.json` for stable. A repository can override\nthe common path with `buildchain-contract-lock-path`, or override one channel\nwith `buildchain-alpha-contract-lock-path` /\n`buildchain-stable-contract-lock-path`.\n\nOnly repositories changing the default policy need extra routing input:\n\n```yaml\nwith:\n buildchain-channel: stable\n```\n\nDuring the v2.12 prerelease evaluation window, canaries use\n`build.yml@v2-alpha`. The same router then selects `v2-alpha` or stable `v2` as\nthe runtime. Production consumers should adopt `build.yml@v2` after the router\nhas reached stable; this keeps the routing shell itself on a stable ref.\n\nThe router is generated from `.build.yml`'s input/output surface. Run\n`node scripts/generate-channel-build-workflow.mjs` after changing the advanced\nbuild workflow; inventory and unit tests reject a stale generated router.\n\n## Advanced Workflow\n\nConsumers that need direct workflow-shell or runtime control call the advanced\nsurface:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n working-directory: .\n artifact-name: libnode\n runner-preset: kungfu-v4-self-hosted\n linux-container-preset: kungfu-verify\n artifact-name-template: \"{artifact}-{platform}-{sha}\"\n artifact-paths: |\n dist\n build/stage\n expected-artifacts-json: >-\n {\"minFiles\":2,\"requiredPaths\":[\"dist/libnode.tar.gz\",\"dist/checksums.txt\"]}\n process-summary-path: .buildchain/diagnostics/process-summary.json\n release-candidate: true\n publish-channel: release\n publish-source-ref: publish-gate/release/v22/v22.22/22.22.3-kf.0\n```\n\n`runner-preset` is the stable first-class surface for known runner fleets:\n\n| Preset | Platforms |\n| ----------------------- | ------------------------------------------------------------------------ |\n| `github-hosted` | `ubuntu-24.04`, `macos-latest`, `windows-2022` |\n| `kungfu-v4-self-hosted` | Kungfu Linux x64, macOS ARM64, and Windows x64 self-hosted runner labels |\n| `custom` | Requires `platforms-json` |\n\nCallers can still provide a custom matrix with `platforms-json`. Each platform\nobject has:\n\n| Field | Meaning |\n| -------- | ------------------------------------------------- |\n| `id` | Stable artifact/platform key, such as `linux-x64` |\n| `name` | Human-readable job name |\n| `runner` | JSON string passed to `runs-on` after `fromJSON` |\n\nThe runner field is intentionally a JSON string so callers can pass either\nGitHub-hosted runners or multi-label self-hosted runners without Buildchain\nguessing the labels.\n\nOnly include platforms that should run. GitHub schedules matrix jobs before\nsteps execute, so a disabled entry with unavailable runner labels can still\nblock the workflow queue.\n\n## Linux Job Containers\n\nLinux build platforms can run inside a digest-pinned job container while macOS\nand Windows keep using native runners. This is the recommended way to remove\nmoving Linux runner prerequisites from Buildchain consumers: the Linux host only\nneeds a GitHub Actions runner, Docker, and network access; common verification\ntools come from the image contract.\n\nUse the Kungfu verification image for lifecycle stages that need Git, jq,\nPython, uv, and fnm, but do not need native compilation:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n runner-preset: kungfu-v4-self-hosted\n linux-container-preset: kungfu-verify\n```\n\n`kungfu-verify` resolves to:\n\n```text\nghcr.io/kungfu-systems/build-images/kungfu-verify@sha256:11f0ba64267ce88174a4f73a9bf833ff4e9c59cd16ec3d08a6432a06c2be6fb1\n```\n\nCallers that own a different Linux image can pass it explicitly:\n\n```yaml\nwith:\n linux-container-preset: custom\n linux-container-image: ghcr.io/example/project-build@sha256:<digest>\n```\n\n`linux-container-image` should be pinned by digest. A floating tag makes the\nrunner surface mutable and weakens Buildchain's release evidence.\n\nThe workflow splits the matrix into two build jobs:\n\n- Linux platforms go to `build-linux-container` when a Linux container is\n configured.\n- All other platforms go to `build-native`.\n\nArtifact names, manifest paths, expected artifact checks, publish-source locks,\nand aggregate summaries are the same in both jobs. The split is an execution\ndetail, not a different artifact contract.\n\n## Native Rust Toolchains\n\nNative lifecycle jobs can request an isolated Rust installation instead of\ndepending on a self-hosted runner user's PATH:\n\n```yaml\nwith:\n setup-rust: true\n rust-toolchain: \"1.96.0\"\n rustup-dist-server: \"https://rsproxy.cn\"\n rustup-update-root: \"https://rsproxy.cn/rustup\"\n cargo-registry-index: ${{ vars.BUILDCHAIN_CARGO_REGISTRY_INDEX }}\n```\n\n`setup-rust` defaults to `false`, so existing consumers are unchanged. When it\nis enabled, Buildchain installs `rust-toolchain` before the install, build, and\nverify lifecycle stages on every native matrix platform. Windows uses the\nofficial rustup bootstrap through `cmd.exe` and `curl.exe` into runner-temporary\nCargo and rustup homes, so it works under a restrictive PowerShell execution\npolicy and the service account does not depend on another user's PATH or mutate\nhost toolchain state. Pin an exact toolchain for release builds. Linux container jobs continue\nto obtain Rust from their digest-pinned image contract; Buildchain does not\nmutate that container surface.\n\nThe rustup server inputs are optional and default to Rust's official servers.\nConsumers behind a slow cross-border link may select a trusted transport mirror;\nrustup still verifies the selected toolchain's distribution metadata and\ncomponent checksums.\n\n`cargo-registry-index` is also optional. When set, Buildchain exposes it to\nCargo as `CARGO_REGISTRIES_CRATES_IO_INDEX` for every native lifecycle stage,\nso a self-hosted runner can use a repository or organization variable without\ncommitting private LAN topology to public workflow YAML. The endpoint must be a\ncrates.io-compatible index whose `config.json` download contract serves the\nmatching checksum-verified crate archives. An empty value preserves Cargo's\nnormal crates.io behavior.\n\nThe container image provides `fnm` but does not preinstall Node. Buildchain uses\n`fnm` inside the container to install the requested `node-version` before it\nruns Buildchain runtime scripts or lifecycle actions.\n\nDo not use `kungfu-verify` for stages that need CMake, Ninja, ccache, Conan, or\nDocker image publishing. Those should use a heavier native-build image or remain\non a host runner until their image contract is explicit.\n\n## Buildchain Runtime Override\n\nStable consumers should keep the reusable workflow pinned to stable refs such as\n`@v2`. The optional `buildchain-ref` input is empty by default; empty means\nBuildchain resolves and executes the stable runtime selected by the workflow\nshell. The full train validation protocol is documented in\n[`runtime-train-validation.md`](runtime-train-validation.md).\n\nFor one-off manual validation, a trusted maintainer can run the caller workflow\nwith a temporary runtime override:\n\n```yaml\non:\n workflow_dispatch:\n inputs:\n buildchain-ref:\n description: \"Temporary Buildchain runtime ref for trusted manual validation\"\n required: false\n default: \"\"\n\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n buildchain-ref: ${{ inputs.buildchain-ref || '' }}\n```\n\nAllowed override refs are deliberately narrow:\n\n| Ref form | Meaning |\n| --- | --- |\n| `train/v2/v2.3/<capability>` | Temporary capability train under the active minor line |\n| `refs/heads/train/v2/v2.3/<capability>` | Explicit branch ref for the same train |\n| `<40-character SHA>` | Exact immutable Buildchain runtime commit |\n\nOverride requests fail closed unless the event is `workflow_dispatch` and the\nactor has write, maintain, or admin permission on the caller repository.\nPull requests, including same-repository pull requests and fork-originated pull\nrequests, cannot use `buildchain-ref` override. This keeps automated PR builds on\nthe stable runtime surface.\n\nEvery run resolves the runtime ref to an immutable SHA before checkout. The job\nsummary and aggregate build summary record the workflow shell ref, requested\nruntime ref, resolved runtime ref, runtime SHA, stability class, trust decision,\nand rollback ref. Train refs are development validation refs: they do not move\n`v2`, `vX.Y`, `vX.Y-alpha`, npm dist-tags, or production release refs, and they\nmust not be pinned as long-term production dependencies.\n\nRuntime override validates Buildchain runtime scripts, CLI code, local actions,\nconfig parsing, and lifecycle behavior. It cannot validate changes that require\nthe outer reusable workflow YAML itself to change, such as new jobs,\npermissions, workflow outputs, or matrix topology. Those changes need a canary\nworkflow path or a temporary explicit workflow ref.\n\n## Floating Ref Contract Lock\n\nStable consumers should use floating major refs such as `@v2`, but a floating\nref is not blind trust. Each released Buildchain ref carries a package-owned\nruntime contract world in `dist/site/buildchain-contract.json`. Consumers may\nkeep a small lock file, `.buildchain/contract-lock.json`, recording the\nBuildchain ref, resolved SHA, contract digest, compatibility digest, accepted\nmajor line, and compatibility policy they reviewed.\n\nThe reusable build trust gate checks this lock before any heavy matrix job:\n\n1. resolve the Buildchain runtime ref, for example `v2`, to an immutable SHA;\n2. read `dist/site/buildchain-contract.json` from that checked-out Buildchain\n ref;\n3. read the consumer's `.buildchain/contract-lock.json`;\n4. compare the accepted contract with the current contract.\n\nSHA drift alone is not a failure. `v2` is expected to advance. Buildchain only\nfails fast when the accepted contract is no longer compatible, for example a\nrequired input is removed, a required output disappears, a protected behavior\npromise changes, or the major line changes. Additive changes such as optional\ninputs, optional outputs, diagnostics, or documentation updates continue under\nthe default `major-compatible` policy.\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n permissions:\n contents: read\n issues: write\n id-token: write\n with:\n buildchain-contract-lock-path: .buildchain/contract-lock.json\n buildchain-contract-compatibility-policy: major-compatible\n buildchain-contract-drift-issue-mode: compatible-and-breaking\n```\n\nWhen compatible drift is detected, the build continues and Buildchain opens or\nupdates a low-priority issue in the consumer repository. The issue records the\nold SHA/digest, new SHA/digest, compatibility result, workflow run, and the next\naction: review the Buildchain release notes and update the lock. When breaking\ndrift is detected, the same issue path is used, but the trust gate fails before\nmatrix build or publish work starts. If the workflow token cannot write issues,\nBuildchain writes a copyable issue body into the job summary.\n\nThe lock is intentionally small. It does not copy the full contract. The full\ncontract remains in the Buildchain ref and package; the consumer records only\nwhat it accepted and the policy used to compare future floating-ref movement.\n\nAdvanced alpha-channel consumers select the matching workflow shell:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2-alpha\n with:\n buildchain-contract-lock-path: .buildchain/contract-lock.json\n```\n\nThe runtime follows the called workflow through `job.workflow_ref`. Callers may\nalso pass `buildchain-ref: v2-alpha` explicitly; official floating refs are\nordinary channel selections and are allowed on pull requests and pushes. Train\nrefs and exact SHAs remain trusted manual overrides.\n\n## Shifu Cache Profile Passthrough\n\nBuildchain can carry one trusted Shifu cache-profile reference and its exact\ndigest into lifecycle execution. Its contract is an opaque reference and digest\nonly. This surface is deliberately opaque:\nBuildchain does not fetch the profile, parse JSON, select cache services,\nrewrite bindings, decide fallback, or emit Shifu resolution evidence. Those\nsemantics remain owned by the consumer's pinned Shifu implementation.\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/build.yml@v2\n with:\n shifu-cache-profile-ref: ${{ vars.SHIFU_CACHE_PROFILE_REF }}\n shifu-cache-profile-digest: ${{ vars.SHIFU_CACHE_PROFILE_DIGEST }}\n```\n\nThe reusable workflow passes the pair as `SHIFU_CACHE_PROFILE_REF` and\n`SHIFU_CACHE_PROFILE_DIGEST` to install, build, and verify lifecycle commands.\nThe consumer must invoke its Shifu cache-aware execution surface. An empty pair\npreserves existing behavior; a consumer Shifu should fail closed when exactly\none value is present or the resolved bytes do not match the expected digest.\n\nUse trusted repository or organization variables rather than PR-controlled\nfiles for private/LAN references. The variables must remain secret-free; any\ncredentials use a separate provider-approved secret surface and must not be\nembedded in the profile reference. This passthrough is separate from\nBuildchain's locked source checkout cache below: Buildchain owns checkout\ntransport and source identity, while Shifu owns post-checkout execution cache\nbindings and receipts.\n\n## Locked Source Checkout Cache\n\nSelf-hosted runners that build large repositories can opt into a locked checkout\ncache for both the consumer source and the Buildchain runtime. This changes only\nthe Git object transport. Buildchain still resolves `publish-source-sha` and the\nruntime SHA before any build runner starts, checks out those exact commits, and\nverifies each final `HEAD` plus the resolved consumer source tree SHA before\nlifecycle commands run.\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n runner-preset: kungfu-v4-self-hosted\n checkout-cache-mode: auto\n checkout-cache-mirror-url-template: ${{ vars.BUILDCHAIN_CHECKOUT_CACHE_MIRROR_URL_TEMPLATE }}\n checkout-cache-reference-repository-template: ${{ vars.BUILDCHAIN_CHECKOUT_CACHE_REFERENCE_REPOSITORY_TEMPLATE }}\n checkout-cache-fallback: github\n checkout-cache-timeout-seconds: 60\n checkout-cache-github-timeout-seconds: 600\n checkout-cache-fetch-attempts: 3\n```\n\n`checkout-cache-mode` accepts:\n\n| Mode | Behavior |\n| --- | --- |\n| `off` | Default. Buildchain fetches the locked commit from GitHub. |\n| `auto` | Try the trusted cache first; on miss, record the miss and fall back according to `checkout-cache-fallback`. |\n| `require` | Require the cache to provide the locked commit and fail before lifecycle work if unavailable. |\n\nThe cache can be a local/LAN mirror URL template or a runner-local bare\nreference repository template. Templates support `{owner}`, `{repo}`,\n`{repository}`, `{repositorySlug}`, and `{sha}`. The workflow also reads\nrepository or organization variables named\n`BUILDCHAIN_CHECKOUT_CACHE_MIRROR_URL_TEMPLATE` and\n`BUILDCHAIN_CHECKOUT_CACHE_REFERENCE_REPOSITORY_TEMPLATE`, so consumers can keep\nprivate LAN topology out of repository YAML.\n\nThe GitHub-hosted trust gate resolves the reusable workflow shell to an exact\ncommit and uploads that shell's small checkout bootstrap script. Native and\nLinux-container build jobs download the bootstrap, then use the same cache\npolicy to obtain both the selected Buildchain runtime and consumer source at\ntheir already resolved immutable SHAs. Keeping the bootstrap owned by the\nworkflow shell is important when `@vN-alpha` routes a stable release to an older\n`vN` runtime: the stable runtime does not need to already contain the newest\ncheckout transport implementation. This also prevents a large direct\n`actions/checkout` runtime clone from becoming a separate timeout path on\nconstrained self-hosted uplinks. The bootstrap artifact does not contain the\nruntime repository and cannot move either selected ref.\n\nDo not read cache URLs or reference paths from PR-controlled files such as\n`.buildchain/buildchain.toml`. These values are trusted workflow inputs or repo/org\nvariables. Buildchain does not pass GitHub credentials to cache mirrors or\nreference repositories. If it must fall back to GitHub, the workflow token is\nused only for the GitHub fetch path. Cache attempts use\n`checkout-cache-timeout-seconds`; the potentially larger GitHub fallback uses\nthe independent `checkout-cache-github-timeout-seconds` budget (600 seconds by\ndefault). Buildchain fetches the advertised source ref before trying an exact\nSHA, so a cache hit or stale-cache seed can contribute objects and the fallback\ndoes not first waste a full timeout on an unadvertised SHA. Retryable timeout\nand transient network failures use the bounded `checkout-cache-fetch-attempts`\nbudget; permanent failures stop immediately. Diagnostics record both timeout\nbudgets and the actual GitHub fetch attempts before exact HEAD/tree\nverification.\n\nEach platform diagnostics artifact includes `source-checkout.json` and embeds a\ncompact `sourceCheckout` summary in `diagnostics.json`: mode, transport,\nhit/miss, fallback reason, duration, final HEAD verification, and tree\nverification. Remote URLs are sanitized and local reference paths are represented\nby a short display name plus fingerprint, not by secret-bearing credentials.\nRuntime checkout evidence is uploaded separately as `runtime-checkout.json`,\nincluding cache transport, fallback attempts, and exact runtime `HEAD`\nverification, even when a later lifecycle step fails.\n\nWhen a Buildchain maintainer asks for downstream validation, the expected\nrequest is:\n\n```text\nBuildchain train ready: buildchain-ref=train/v2/v2.3/<capability>.\nKeep uses: ...@v2; run workflow_dispatch with that buildchain-ref and report the runtime evidence summary.\n```\n\nAfter validation succeeds, the Buildchain change should continue through the\nnormal mainline and release path. Do not treat the train as a pending merge\nitem; it is only a temporary fast-use, diagnostic, and rollback channel. It may\nremain for a retention window after release, with old trains handled by a\nseparate periodic cleanup task.\n\n## Workflow Outputs\n\nThe reusable workflow exposes the resolved contract:\n\n| Output | Meaning |\n| --------------------------------- | ------------------------------------------------------------------------------- |\n| `runner-preset` | Resolved preset, or `custom` when `platforms-json` was provided |\n| `platforms-json` | Exact matrix JSON used by the build job |\n| `platform-count` | Number of matrix platforms |\n| `linux-container-enabled` | `true` when Linux platforms are routed through a job container |\n| `linux-container-image` | Resolved digest-pinned Linux job container image |\n| `build-summary-artifact` | Uploaded aggregate summary artifact name |\n| `build-diagnostics-summary-artifact` | Uploaded aggregate diagnostics summary artifact name |\n| `release-candidate-passport-artifact` | Uploaded PR-stage release-candidate passport artifact name when `release-candidate` is enabled |\n| `release-candidate-passport-json` | Compact release-candidate passport JSON when `release-candidate` is enabled |\n| `build-summary-json` | Compact aggregate JSON with platform count, file count, and byte total |\n| `build-diagnostics-summary-json` | Compact aggregate diagnostics JSON with platform, lifecycle warning/error, diagnostics contract warning, and sidecar manifest warning totals |\n| `trusted-event` | `true` when the event is trusted enough to reach build runners |\n| `buildchain-runtime-ref` | Runtime ref selected after applying the empty-default or override policy |\n| `buildchain-runtime-sha` | Immutable Buildchain runtime commit used by all runtime checkouts |\n| `buildchain-runtime-class` | `stable`, `alpha`, `train`, `exact-sha`, or `development` |\n| `buildchain-runtime-override` | `true` when a train or exact-SHA `buildchain-ref` override was accepted |\n| `buildchain-runtime-trust-decision` | Runtime override trust decision |\n| `buildchain-contract-lock-status` | `unchanged`, `compatible-drift`, `breaking-drift`, `missing-lock`, `non-floating-runtime`, or first-release `runtime-contract-unavailable` |\n| `buildchain-contract-lock-drift` | `true` when the floating runtime SHA or contract digest changed |\n| `buildchain-contract-digest` | Current Buildchain runtime contract digest |\n| `publish-channel` | Resolved publish channel requested by the caller |\n| `publish-allowed` | `true` only when this event/ref may publish after verification |\n| `publish-reason` | Human-readable reason for the publish gate decision |\n| `publish-source-ref` | Gate source ref that was resolved before checkout |\n| `publish-source-sha` | Exact source commit used by checkout, build, verify, and artifacts |\n| `publish-source-locked` | `true` when a `publish-gate/*` source ref was explicitly locked |\n| `publish-source-channel` | `alpha`, `release`, `anchor`, or `major` parsed from the source ref |\n| `publish-source-line` | Product line parsed from source refs such as `v22/v22.22` |\n| `publish-source-consumer-version` | Consumer package version parsed from source refs |\n| `release-manifest-json` | Resolved release manifest including source lock, version state, and anchor data |\n\nThe aggregate summaries are intentionally artifacts as well as outputs. GitHub\nActions matrix outputs are not a reliable place to carry every platform's full\nmanifest, so Buildchain uploads each platform manifest and then emits one\naggregate build summary artifact after the matrix completes. Buildchain uploads\n`diagnostics-summary.json` as a separate aggregate diagnostics summary artifact,\na compact rollup of each platform's small diagnostics upload. The rollup keeps\nper-platform runner facts, checked tool versions/missing tools, package\nmanager/cache directory details, compiler-cache availability, lifecycle timing,\nprocess sampler context, and links back to the exact platform artifacts. Each\nplatform diagnostics upload includes `diagnostics.json`,\n`diagnostics-manifest.json`, the lifecycle `events.jsonl`, and process sampler\nsidecars when enabled, so slow-build diagnosis does not require downloading the\nbinary platform artifact or the aggregate build summary. The sidecar manifest\nrecords the uploaded diagnostics files with bytes and sha256 hashes. Each\n`diagnostics.json` also records the related binary artifact name, manifest\nartifact name, diagnostics artifact name, diagnostics sidecar manifest path, and\nplatform id in `links`, so a reviewer can navigate from the small diagnostics\nartifact back to the exact platform outputs when deeper inspection is needed.\nThe workflow output `build-diagnostics-summary-json` includes\n`diagnosticsContractWarningCount` and `diagnosticsManifestWarningCount` so\nrelease jobs can detect drifting diagnostics JSON contracts and missing or\ndrifting diagnostics sidecar manifests without downloading the per-platform\ndiagnostics artifacts first.\n\n## Artifact Transfer Relay\n\nBy default, platform jobs upload payloads, manifests, and diagnostics directly\nto GitHub artifacts:\n\n```yaml\nwith:\n artifact-transfer-mode: github-artifacts\n```\n\nLarge self-hosted native builds can opt into the first-class S3 relay path:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n runner-preset: kungfu-v4-self-hosted\n artifact-transfer-mode: s3-to-github-artifacts\n artifact-relay-s3-bucket: ${{ vars.BUILDCHAIN_ARTIFACT_RELAY_S3_BUCKET }}\n artifact-relay-s3-region: ${{ vars.BUILDCHAIN_ARTIFACT_RELAY_S3_REGION }}\n artifact-relay-s3-prefix: ${{ vars.BUILDCHAIN_ARTIFACT_RELAY_S3_PREFIX }}\n```\n\nIn relay mode, each self-hosted platform job uploads the heavy payload files to\nS3 and uploads only a small `relay-manifest.json` to GitHub. A GitHub-hosted\n`relay-artifacts` job then assumes the configured download role, downloads the\npayloads from S3, verifies every file by SHA256, and re-uploads the normal\nGitHub artifacts under the same artifact names that direct mode uses. Downstream\nsummary, release-candidate, and promote-only workflows therefore continue to\nconsume GitHub artifacts and do not need custom S3 logic.\nThe relay implementation uses Node.js plus the standard AWS environment\ncredentials from GitHub OIDC; runner images and build containers do not need the\nAWS CLI installed.\n\nAfter the GitHub artifact uploads succeed, Buildchain deletes the S3 objects\nlisted in the relay manifest for that platform. If any download, verification,\nor GitHub artifact upload fails, cleanup is skipped so maintainers can inspect\nthe retained S3 payload. Configure a short bucket lifecycle expiration as a\ncost and cleanup backstop.\n\nThe relay configuration is intentionally generic. Buildchain does not hard-code\norganization buckets, regions, or role ARNs. Callers may pass explicit inputs,\nor set repository/organization variables and secrets using these names:\n\n| Variable or secret | Meaning |\n| --- | --- |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_BUCKET` | Relay bucket name |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_REGION` | Relay bucket region |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_PREFIX` | Relay object prefix; defaults to `buildchain-artifacts` |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_ROLE_ARN` | Shared OIDC role ARN for upload and download |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_UPLOAD_ROLE_ARN` | Upload OIDC role ARN for self-hosted build jobs |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_DOWNLOAD_ROLE_ARN` | Download OIDC role ARN for the GitHub-hosted relay job |\n| `BUILDCHAIN_ARTIFACT_RELAY_S3_OIDC_AUDIENCE` | Optional OIDC audience override |\n\nFor AWS China regions, Buildchain defaults the OIDC audience to\n`sts.amazonaws.com.cn`; other regions default to `sts.amazonaws.com`. The caller\nworkflow must allow `id-token: write`, and the target role trust policy should\nrestrict GitHub OIDC claims to the expected organization, repository, workflow,\nand branch/ref. The S3 permissions should be scoped to the relay bucket/prefix\nused by the repository.\nUpload roles need write/delete access under the relay prefix; download roles\nneed read access plus delete access for successful cleanup.\n\nRelay mode is opt-in and does not affect forks or open-source users that do not\nconfigure S3. Missing bucket, region, upload role, or download role values fail\nbefore the heavy build matrix is scheduled. Buildchain treats S3 as a transport\ncache, not as the final release evidence store; the final audit entry remains\nthe GitHub artifact set plus the Buildchain build summary and release-candidate\npassport.\n\nSet `release-candidate: true` when the successful reusable build is meant to be\nthe artifact source promoted later. Buildchain then uploads\n`release-candidate-passport.json` under the\n`<artifact-name>-release-candidate-<publish-source-sha>` artifact name. Promotion\njobs can pass that passport to `promote-buildchain-ref` with\n`promote-only-release-candidate: \"true\"` so source, channel, platforms, and the\naggregate build-summary hash are checked before publish-gate side effects. The\npassport records the locked commit's Git tree SHA, so a post-merge channel HEAD\ncan be accepted only when it is tree-equivalent to the PR-stage build evidence.\n\n## Publish Gate\n\nBuildchain separates \"may build/verify\" from \"may publish.\" A same-repository\npull request may be trusted enough to run the build matrix, but it still must\nnot publish packages, S3 objects, release pages, or preview aliases. Publishing\nis allowed only when the caller explicitly requests a channel and the current\nevent/ref matches that channel.\n\nUse `publish-channel` to request a channel:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n publish-channel: release\n\n publish:\n needs: build\n if: ${{ needs.build.outputs.publish-allowed == 'true' }}\n runs-on: ubuntu-24.04\n steps:\n - run: ./scripts/publish.sh\n```\n\nDefault channels are:\n\n| Channel | Allowed refs |\n| --------- | ---------------------------------------------------------------------------------------------------- |\n| `none` | Never publishes; this is the default |\n| `alpha` | `alpha/vN/vN.M` branches or exact `vN.M.P-alpha.K` tags |\n| `release` | `release/vN/vN.M` branches or release tags such as `vN.M.P`, `vN.M`, `vN` |\n| `major` | `publish-gate/major`, legacy `major-gate`, or next-major release tags such as `vN.0.0`, `vN.0`, `vN` |\n\nPull request events always produce `publish-allowed=false`, even when the PR is\nfrom the same repository. Untrusted fork events also produce\n`publish-allowed=false`; with the default `untrusted-policy: fail`, the workflow\nthen fails before any build runner starts.\n\nProjects with their own channel names can pass `publish-refs-json`:\n\n```yaml\nwith:\n publish-channel: nightly\n publish-refs-json: >-\n {\"nightly\":[\"^refs/heads/nightly/v\\\\d+$\"]}\n```\n\nThe aggregate build summary includes the same publish gate decision under\n`publishGate`, so a downloaded artifact summary explains both what was built and\nwhy it was or was not eligible to publish.\n\n## Publish Source Lock\n\n`publish-channel` answers \"may this event publish?\" Source lock answers \"which\nsource tree is the publish decision about?\" A caller can pass `publish-source-ref`\nto bind a publish run to a reviewed gate branch before any checkout happens:\n\n| Ref | Meaning |\n| ------------------------------------------------ | --------------------------------------------------------------------------- |\n| `publish-gate/alpha/<line>/<consumer-version>` | Build and publish an alpha candidate for a consumer line |\n| `publish-gate/release/<line>/<consumer-version>` | Build and publish a production candidate for a consumer line |\n| `publish-gate/anchor` | Resolve an explicit anchor request; it does not publish artifacts by itself |\n| `publish-gate/major` | Gate the next major source state |\n| `major-gate` | Legacy compatibility alias for the major gate |\n\nFor alpha and release refs, `<line>` is intentionally allowed to contain `/`, so\nKungfu-style lines such as `v22/v22.22` stay readable. The final path segment is\nthe consumer-visible version, for example `22.22.3-kf.0`.\n\nThe reusable workflow resolves the branch tip to `publish-source-sha`, checks out\nthat SHA in every build job, and uses the same SHA in artifact names, manifests,\nand aggregate summaries. Reruns therefore rebuild the same source tree even if a\ngate branch moves later.\n\nBefore any heavy build matrix is scheduled, the workflow also verifies that the\ntarget channel ref implied by the source lock already points at\n`publish-source-sha` and that the target channel HEAD came from the required\nmerged same-repository channel PR. `publish-gate/alpha/<line>/<version>` must\nmatch `alpha/<line>` and have PR lineage `dev/<line> -> alpha/<line>`;\n`publish-gate/release/<line>/<version>` must match `release/<line>` and have PR\nlineage `alpha/<line> -> release/<line>`. If either check fails, the run fails\nfast with a diagnostic telling maintainers to merge the source commit through\nthe channel PR first. This keeps verify from spending runner time on a source\ntree that cannot legally enter the requested publish channel.\n\nThe resolved release manifest is uploaded as an artifact and emitted as\n`release-manifest-json`. It records:\n\n- source ref, source SHA, channel, line, and consumer version;\n- configured version strategy and configured version-state files;\n- each version file's value, with release gates failing closed if the configured\n files do not equal the consumer version;\n- anchor manifest summary for anchored/manual projects;\n- explicit anchor request JSON for `publish-gate/anchor`;\n- publish registry, dist-tag, and gate visibility metadata.\n\nPublish side-effect jobs should verify the lock immediately before publishing:\n\n```yaml\n- name: Verify publish gate did not move\n run: node .buildchain/runtime/scripts/verify-publish-source-lock.mjs\n env:\n BUILDCHAIN_PUBLISH_SOURCE_REF: ${{ needs.build.outputs.publish-source-ref }}\n BUILDCHAIN_PUBLISH_SOURCE_SHA: ${{ needs.build.outputs.publish-source-sha }}\n BUILDCHAIN_SOURCE_REPOSITORY: ${{ github.repository }}\n GITHUB_TOKEN: ${{ github.token }}\n```\n\nIf the branch tip no longer matches the manifest SHA, the publish job must fail\nclosed. Moving a gate branch creates a new publish decision and should produce a\nnew build run.\n\n## Release Candidate Promote-Only\n\nFor native package sets, the PR build is the only heavy build. When a PR targets\n`alpha/<line>` or `release/<line>`, `.build.yml` uploads a release-candidate\nbundle next to the platform artifacts. The bundle contains:\n\n- `release-candidate.passport.json`;\n- the aggregate `build-summary.json`;\n- copied platform manifest evidence for the built platforms.\n\nThe passport records two separate source identities:\n\n- `builtSourceSha` / `builtSourceTreeSha`: the PR-stage source that produced the\n artifacts, usually the PR merge ref;\n- `promotionChannelSha` / `promotionChannelTreeSha`: the post-merge channel\n commit used for publish authority.\n\nThe reusable promote wrapper resolves the merged PR, finds exactly one matching\nPR-stage release-candidate artifact, downloads it with the build summary and\npayload artifacts from the same PR-stage run, validates the payload count,\ncompares the built tree with the promotion channel tree, locks\n`publish-gate/{alpha,release,major}` to the promotion channel commit, and then\ncalls `actions/promote-buildchain-ref` with\n`promote-only-release-candidate: \"true\"` and\n`require-publish-source-lock: \"true\"`. The wrapper passes the created\n`publish-gate/*` ref, target SHA, and `locked=true` into the promote action, so\nfloating `@v2` consumers receive publish-side source-lock drift protection by\ndefault. It also defaults `branch-protection-bypass-apps` to `github-actions`\nso the workflow automation can apply generated version-state and channel\nbookkeeping on protected `dev`/`alpha`/`release` branches after the reviewed\nchannel PR has merged; consumers using a different promotion identity can pass\n`branch-protection-bypass-users`, `branch-protection-bypass-teams`, or a\ndifferent app slug declaratively. The wrapper uses\n`secrets.BUILDCHAIN_PROMOTION_TOKEN || github.token` as the generated ref update\ntoken for protected bookkeeping PATCH calls, so a bypass-capable promotion token\ncan sync dev immediately after alpha/release publish without a post-publish PR.\nIt does not call `.build.yml`, does not create a matrix, and must fail before\npublish if the RC evidence, payload set, or source-lock ref is missing or\nambiguous.\n\nThe public `release-candidate-promote.yml` is a generated channel router. It\nderives the publication lane from `target-ref`, then selects the matching\nadvanced workflow shell, runtime, and consumer lock before the advanced\npromotion starts:\n\n- alpha targets use `.release-candidate-promote.yml@vN-alpha`, runtime\n `vN-alpha`, and `buildchain-alpha-contract-lock-path`;\n- release and major targets use `.release-candidate-promote.yml@vN`, runtime\n `vN`, and `buildchain-stable-contract-lock-path`.\n\nThe router resolves immutable SHAs and the selected lock digest before candidate\ndownload. The advanced shell verifies the same router, shell, runtime, lock,\nchannel, and target binding again. Train and exact-SHA runtime overrides remain\nrestricted to trusted `workflow_dispatch` actors with write, maintain, or admin\npermission. Promotion controller evidence, the promotion copy of the release\ncandidate passport, and the final release passport record these identities.\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n secrets:\n buildchain-issue-app-id: ${{ secrets.BUILDCHAIN_ISSUE_APP_ID }}\n buildchain-issue-app-private-key: ${{ secrets.BUILDCHAIN_ISSUE_APP_PRIVATE_KEY }}\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 target-ref: alpha/v22/v22.22\n artifact-name: libnode\n # Defaults to build.yml / Build. Override only when the PR-stage build\n # workflow uses a different file or display name.\n release-candidate-workflow-file: build.yml\n release-candidate-workflow-name: Build\n package-manager: npm\n publish-target: npm\n runner-preset: github-hosted\n trusted-publishing: true\n github-release: true\n required-status-check: check / check\n required-artifact-count: 3\n publish-dist-tag: alpha\n publish-package-set-order: platforms-first-main-last\n publish-package-main: \"@kungfu-tech/libnode\"\n release-passport-product-name: Libnode\n buildchain-contract-drift-issue-mode: compatible-and-breaking\n```\n\nExisting callers may keep `buildchain-contract-lock-path`; a non-empty explicit\npath overrides channel-specific selection for compatibility. Migration only\nrequires adding the two channel lock inputs and may retain the remaining common\npromotion declaration unchanged. Consumers must not call the dot-prefixed\nadvanced workflow directly.\n\n`buildchain-issue-app-id` and `buildchain-issue-app-private-key` are optional\nbut recommended for cross-repository consumers. They should identify a GitHub\nApp installation with `issues: write` on `kungfu-systems/buildchain`; the\nwrapper mints the installation token before calling\n`actions/report-buildchain-issue`. Consumers can also pass a pre-minted\n`buildchain-issue-token`. If both are omitted, the wrapper falls back to\n`BUILDCHAIN_ISSUE_TOKEN`, `BUILDCHAIN_PROMOTION_TOKEN`, and then the consumer\nworkflow's `github.token`; the last fallback can only report issues when it has\nwrite access to the target Buildchain repository.\n\n`publish-required-artifacts-json` can still be passed explicitly for custom\npublish targets. Custom OCI requirements may omit pre-publish refs and digests;\nthe action resolves the exact version ref and validates final digests and any\nbuilt/reused provenance after `lifecycle.publish`. For the default\n`publish-artifact-kind: npm` path, consumers do\nnot download artifacts or run repository scripts to build publish evidence. The\nwrapper downloads the PR-stage payload artifacts, finds the downloaded `.tgz`\npackages, reads each tarball's `package/package.json` for the real scoped\npackage name and version, computes the npm `sha512-...` integrity from the\ntarball bytes, marks the package matching `publish-package-main` as `role:\nmain`, marks the rest as `role: platform`, and passes the generated\n`publish-required-artifacts-json` to `promote-buildchain-ref` before any publish\nside effect. Downloaded platform manifests are still passed into the release\npassport unless `release-passport-platform-manifest-paths` is set explicitly.\nThe same Buildchain contract lock check runs before release-candidate\nresolution and before publish. A compatible `v2` drift leaves an issue in the\nconsumer repository but does not trigger a second heavy build; an incompatible\ndrift fails before publish side effects.\n\nThe wrapper publishes the public release tag as a GitHub Release by default.\nAfter `promote-buildchain-ref` reports a complete release transaction, the\nwrapper creates or updates the public release, marks semver prerelease tags\nsuch as `v1.2.3-alpha.0`, `v1.2.3-rc.1`, or `v22.22.3-kf.3-alpha.7` as\n`prerelease=true` and `make_latest=false`, marks stable semver tags as latest,\nand uploads the publish evidence file plus every file in the generated release\npassport directory, including `buildchain.release.json` and `check-report.json`.\nFor anchored/manual package releases, the public release tag is derived from the\npublished package version and the internal exact transaction tag remains\navailable in the release passport.\nConsumers do not need to hand-write `gh release` logic to trigger\n`release.published` propagation. Set `github-release: false` only for\nrepositories that intentionally do not maintain GitHub Releases.\nIf the transaction still needs protected-ref finalization, the wrapper defers\nGitHub Release creation until the later run that reaches `state=complete`.\n\nCustom publish jobs can also repeat the channel-ref preflight:\n\n```yaml\n- name: Verify publish channel ref still matches\n run: node .buildchain/runtime/scripts/verify-publish-channel-ref.mjs\n env:\n BUILDCHAIN_PUBLISH_SOURCE_REF: ${{ needs.build.outputs.publish-source-ref }}\n BUILDCHAIN_PUBLISH_SOURCE_SHA: ${{ needs.build.outputs.publish-source-sha }}\n BUILDCHAIN_SOURCE_REPOSITORY: ${{ github.repository }}\n GITHUB_TOKEN: ${{ github.token }}\n```\n\nAnchored/manual package release jobs should also make the Buildchain promotion\naction validate that publication is entering through the same\n`publish-gate/{alpha,release,major}` source-lock contract before any package\npublish side effect:\n\n```yaml\n- name: Promote release ref and publish npm package set\n uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: release/v22/v22.22\n require-publish-source-lock: 'true'\n publish-source-ref: ${{ needs.build.outputs.publish-source-ref }}\n publish-source-sha: ${{ needs.build.outputs.publish-source-sha }}\n publish-source-locked: ${{ needs.build.outputs.publish-source-locked }}\n```\n\n`target-ref` stays the Buildchain channel promotion target, such as\n`alpha/v22/v22.22`, `release/v22/v22.22`, or `publish-gate/major`.\n`publish-source-ref` is the reviewed source-lock branch that authorized this\nspecific package publication. For alpha and release package publications, the\nsource-lock branch must point at the exact channel-line commit that promotion is\nvalidating; it is not a replacement for `target-ref`.\n\nThis keeps the version bump commit, publish authorization, and auditable publish\nentrypoint on the Buildchain source-lock protocol. The CLI form\n`buildchain publish-source validate-anchored-release --json` is still useful for\ncustom publish scripts, but the preferred GitHub Actions gate is the promotion\naction input above. A publish job that still runs directly from `alpha/*` or\n`release/*` channel branches fails this check because those refs are channel\nstate, not publish-gate decisions.\n\n## Package-Set Publish Plan\n\nProjects that publish multiple packages should treat package publication as a\npackage-set operation. Buildchain's package-set planner uses these rules:\n\n- platform packages publish first;\n- the main package publishes last;\n- the dist-tag move happens only after the full package set is present;\n- reruns accept already-published packages only when package name, version, and\n integrity match;\n- an existing package with different integrity is a hard failure.\n\nThis keeps a consumer from observing a floating dist-tag that points to a main\npackage before all platform artifacts for the same source SHA are available.\n\n## Command Sources\n\nThe workflow runs `.buildchain/buildchain.toml` lifecycle stages by default:\n\n```toml\n[lifecycle.install]\ncommand = \"corepack yarn install --immutable\"\n\n[lifecycle.build]\ncommands = [\n \"corepack yarn make\",\n \"corepack yarn build\",\n]\n\n[lifecycle.verify]\ncommand = \"corepack yarn test\"\n```\n\nCallers can override any stage for one invocation:\n\n```yaml\nwith:\n build-command: cmake --build build --config Release\n verify-command: ctest --test-dir build --output-on-failure\n```\n\nEvery native and container matrix job is bounded by\n`lifecycle-timeout-minutes`, which defaults to 120 minutes. The same input is\nthe fallback deadline for each install, build, and verify action, so a hung\ncommand fails with the lifecycle name and matrix platform before it can occupy\na self-hosted runner indefinitely. A stage-level `timeout_minutes` in\n`buildchain.toml` remains the more specific override for that stage.\n\n```yaml\nwith:\n lifecycle-timeout-minutes: 90\n```\n\nThe reusable build workflow samples the build lifecycle by default and carries\nthe generated summary into the final verify diagnostics. Callers can override\nthe sidecar path or disable sampling:\n\n```yaml\nwith:\n sample-process-tree: true\n process-summary-path: .buildchain/diagnostics/process-summary.json\n process-sample-interval-ms: 15000\n requested-parallelism: 20\n```\n\nWhen `sample-process-tree` is true, Buildchain wraps either `build-command` or\nthe configured `lifecycle.build` stage with `buildchain sample process-tree`.\nThe path is relative to the checked-out workspace and is read again during the\nfinal verify lifecycle. Custom workflows can still write their own sampler\nsummary and pass `process-summary-path`; Buildchain reads the file after the\nlifecycle command finishes, so it may be produced during the same invocation.\nWhen the build stage is optional, the reusable workflow treats the default\nsampler path as optional during verify; an explicitly supplied\n`process-summary-path` remains required.\n\nFor custom workflows, use the action directly:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/run-lifecycle@v2\n with:\n stage: build\n required: \"true\"\n timeout-minutes: \"90\"\n artifact-name: libnode-linux-x64-${{ github.sha }}\n artifact-paths: |\n dist\n build/stage\n```\n\n## Artifact Contract\n\nEach platform upload uses `artifact-name-template`. The default is:\n\n```text\n{artifact}-{platform}-{sha}\n```\n\nSupported placeholders are `{artifact}`, `{artifactName}`, `{platform}`,\n`{platformId}`, `{platformName}`, `{sha}`, `{shortSha}`, `{ref}`, `{runId}`,\nand `{runAttempt}`. Invalid GitHub artifact name characters are normalized to\n`-`, so `{ref}` remains deterministic even for refs such as\n`refs/heads/dev/v2/v2.0`.\n\nEach platform also writes and uploads:\n\n```text\n.buildchain/artifacts/<platform-id>/manifest.json\n.buildchain/artifacts/<platform-id>/summary.json\n```\n\nThe manifest schema is:\n\n```json\n{\n \"schemaVersion\": 1,\n \"contract\": \"kungfu-buildchain-artifact\",\n \"artifactName\": \"libnode-linux-x64-<sha>\",\n \"platform\": {\n \"id\": \"linux-x64\",\n \"name\": \"Linux x64\",\n \"os\": \"Linux\",\n \"arch\": \"X64\"\n },\n \"git\": {\n \"repository\": \"kungfu-systems/libnode\",\n \"sha\": \"<sha>\",\n \"ref\": \"<ref>\",\n \"runId\": \"<run id>\",\n \"runAttempt\": \"<attempt>\"\n },\n \"lifecycle\": {\n \"stage\": \"verify\",\n \"commandSource\": \"buildchain.toml\",\n \"executed\": true\n },\n \"summary\": {\n \"contract\": \"kungfu-buildchain-artifact-summary\",\n \"artifactName\": \"libnode-linux-x64-<sha>\",\n \"fileCount\": 1,\n \"totalBytes\": 1234,\n \"digest\": \"<hex>\"\n },\n \"expectedArtifacts\": {\n \"ok\": true,\n \"source\": \"expected-artifacts-json\",\n \"checks\": []\n },\n \"files\": [\n {\n \"path\": \"dist/example.zip\",\n \"size\": 1234,\n \"sha256\": \"<hex>\"\n }\n ]\n}\n```\n\nArtifact names do not include actor names, timestamps, or retry counters. Reruns\nproduce a new GitHub Actions run but keep the same source SHA/platform contract.\n\n`expected-artifacts-json` fails the build before upload when the artifact does\nnot match the caller's declared contract. Supported checks are:\n\n| Field | Meaning |\n| --------------- | ------------------------------------ |\n| `minFiles` | Minimum number of manifest files |\n| `maxFiles` | Maximum number of manifest files |\n| `minTotalBytes` | Minimum total byte count |\n| `requiredPaths` | Exact manifest paths that must exist |\n\n## Trusted Event Gate\n\nThe workflow has an explicit `trust-gate` job. By default, pull requests from\nforks fail before any build job can reach self-hosted runners, secrets,\npublishing credentials, or heavyweight build commands. Same-repository PRs,\nworkflow dispatches, and protected branch events can proceed.\n\nIf a repository wants fork PRs to skip rather than fail, it can set:\n\n```yaml\nwith:\n untrusted-policy: skip\n```\n\nDo not set `require-trusted-event: false` for workflows that use self-hosted\nrunners or secrets.\n\n`require-trusted-event` controls access to build runners. It does not override\nthe publish gate: pull requests remain non-publishing events.\n\n## Fixture\n\n`fixtures/libnode-shaped` is the contract fixture. It has:\n\n- `package.json` version state;\n- `.buildchain/buildchain.toml` with `install`, `build`, `verify`, and `publish`;\n- cross-platform Node scripts that create small `dist/` outputs;\n- `Build Surface Fixture` workflow coverage.\n\nThe fixture proves the reusable surface without running the real libnode native\nbuild."
1701
1701
  },
1702
1702
  {
1703
1703
  "id": "manual:runtime-train-validation",
@@ -2504,7 +2504,7 @@
2504
2504
  "path": "docs/release-passport.md",
2505
2505
  "plane": "verify",
2506
2506
  "exists": true,
2507
- "digest": "sha256:2421e3e9f2dd892971bea5deb605492dfec38f3a83e539229442b55380bebe25"
2507
+ "digest": "sha256:0029014ffab7d7228a12d8b92326b78cf6757a41f481b661c5997e2cef06d3cc"
2508
2508
  },
2509
2509
  {
2510
2510
  "id": "controller-evidence",
@@ -2528,7 +2528,7 @@
2528
2528
  "path": "docs/release-candidate.md",
2529
2529
  "plane": "verify",
2530
2530
  "exists": true,
2531
- "digest": "sha256:1fc581cf1b4e4a202464c8d6671b3bff12d0b349760246a951e88ef524f99e6b"
2531
+ "digest": "sha256:81463a74dfe3d337768797b5b47f8cb3a22389312a917447f9b0e6096ab4f90a"
2532
2532
  },
2533
2533
  {
2534
2534
  "id": "release-propagation",
@@ -2536,7 +2536,7 @@
2536
2536
  "path": "docs/release-propagation.md",
2537
2537
  "plane": "use",
2538
2538
  "exists": true,
2539
- "digest": "sha256:d5877075eb41a3d41e531549da113756d29d6f34c91e3b68b8ce650753d5ca1d"
2539
+ "digest": "sha256:fc2b743e3d76c4f90cdd8c6560cf9d619ad25d74c15c074be96db7bcb6cc2a29"
2540
2540
  },
2541
2541
  {
2542
2542
  "id": "readme-badges",
@@ -2600,7 +2600,7 @@
2600
2600
  "path": "docs/migration-inventory.md",
2601
2601
  "plane": "verify",
2602
2602
  "exists": true,
2603
- "digest": "sha256:80018cf78785431e67bbc5278d82dda80d0baa320cdb5e4abda26a0b05e5c0bc"
2603
+ "digest": "sha256:08daa7d0b42543c1d1dacf1f56f461f0e7a7ca9e25d0859e39599ceb97054ee0"
2604
2604
  },
2605
2605
  {
2606
2606
  "id": "ownership",
@@ -2656,7 +2656,7 @@
2656
2656
  "path": "docs/reusable-build-surface.md",
2657
2657
  "plane": "use",
2658
2658
  "exists": true,
2659
- "digest": "sha256:a2092c8064332cec9a4be852b24d94f90625cb45f885c4c3208f7068a643a3f0"
2659
+ "digest": "sha256:7b745037fedafa1bc2f8ce65f32bb7e887988df2bfe2439694da418d482f6929"
2660
2660
  },
2661
2661
  {
2662
2662
  "id": "publish-transaction",
@@ -2672,7 +2672,7 @@
2672
2672
  "path": "docs/release-governance.md",
2673
2673
  "plane": "why",
2674
2674
  "exists": true,
2675
- "digest": "sha256:e64ffd6183eaa7b73f8c0c133a645ced22212bca70c9609ad5f667cf3830aaae"
2675
+ "digest": "sha256:d25a2ceb6cdc1a1db321e7d2a6aa8a454fb4d7a10560177604f8ecb5801a6e66"
2676
2676
  },
2677
2677
  {
2678
2678
  "id": "release-flow",
@@ -2724,7 +2724,9 @@
2724
2724
  "releasePassport": {
2725
2725
  "entrypoint": "buildchain.release.json",
2726
2726
  "bundle": "buildchain-release-bundle.tar.gz",
2727
- "contract": "kungfu-buildchain-release-passport"
2727
+ "contract": "kungfu-buildchain-release-passport",
2728
+ "schema": "schemas/release-passport-v1.schema.json",
2729
+ "checkManifest": "release-passport-check-manifest.json"
2728
2730
  },
2729
2731
  "releasePropagation": {
2730
2732
  "graphContract": "kungfu-buildchain-release-propagation-graph",