---
status: active
period: ongoing
theme: buildchain-ref-promotion-action
doc_type: technical-reference
source_level: local-files
confidence: high
sensitivity: public
evidence_grade: A
review_state: unreviewed
last_reviewed: 2026-07-31
ai_provenance:
  model_family: GPT-5
  product: Codex
  generated_at: 2026-07-31
  invisible_context: not asserted
---

# promote-buildchain-ref

Internal buildchain action for promoting verified buildchain release-line and
compatibility refs from buildchain release channels:

- `alpha/v3/v3.0` creates or reuses the next exact prerelease tag such as
  `v3.0.3-alpha.0`, writes that version into package version state, points the
  alpha and dev channel branches at the version commit, then promotes
  `v3.0-alpha` and, when this is the highest published alpha minor, `v3-alpha`;
- `release/v3/v3.0` creates or reuses the next exact release tag such as
  `v3.0.2`, writes that version into package version state, points the release
  channel branch and release tags at the release commit, then prepares a second
  source commit for the next exact prerelease tag such as `v3.0.3-alpha.0` and
  points the alpha/dev channel branches plus `v3.0-alpha` at that prerelease
  commit, and moves `v3-alpha` only if no higher v3 minor has published an alpha;
- `publish-gate/major` accepts a reviewed PR from a production release line such
  as `release/v3/v3.0`, writes the next major production version such as
  `v4.0.0`, points `publish-gate/major`, `release/v4/v4.0`, `v4.0`, and `v4`
  at that release commit, then prepares `v4.0.1-alpha.0` for
  `alpha/v4/v4.0`, `dev/v4/v4.0`, `v4.0-alpha`, and `v4-alpha`. The older `major-gate`
  branch name is a compatibility alias only.

The release branch name defines the minor line. For example,
`release/v3/v3.1` creates `v3.1.N`, promotes `v3.1`, and promotes `v3` only
when the next minor tag such as `v3.2` does not already exist.

The action updates version state in `lerna.json`, root `package.json`, and
workspace package manifests discovered from package manager metadata
(`package.json` workspaces, `lerna.json` packages, or `pnpm-workspace.yaml`).
Package manager detection is adaptive (`pnpm`, `npm`, or `yarn`) and is recorded
in logs.

Repositories can also provide `buildchain.toml` to declare version-state files
and `lifecycle.verify`. TOML-configured version files take precedence over
package-manager discovery and can target JSON, TOML, or regex-based files. The
version commit itself is written through the GitHub Git Data API so the ref
graph is the durable source of truth. Repositories without any supported version
state degrade to ref-only promotion only when strict version state is disabled.

JSON and TOML version entries whose declared key already matches the requested
version are treated as semantic no-ops. TOML changes use a parser-verified
lossless key edit, so repository formatting is preserved and formatter-only
release-preparation commits are not created. If a unique lossless edit cannot
be proven, promotion fails closed instead of rewriting the full TOML document.

## Dry Run

Use `dry-run: "true"` or the CLI `buildchain release --dry-run` before merging a
channel PR when you need to understand what Buildchain would do. This dry-run is
at the Buildchain release-line level. It explains:

- the legal source branch for the target channel;
- exact release or alpha tags that would be created or reused;
- floating tags and channel branches that would move;
- version-state files and verification lifecycle that would apply;
- branch protection, PR lineage, and release-from-alpha checks;
- publish transaction behavior when `lifecycle.publish` or
  `publish-transaction` is enabled.

It does not move refs, move tags, write package files, run publish commands, or
publish npm packages. The GitHub action dry-run still calls GitHub APIs to
resolve the current target SHA and concrete pending ref updates, but every
write is reported as a dry-run update.

When the requested version already matches every declared version-state file,
dry-run planning still runs `lifecycle.version-state` and any explicit
`verification-command` so it can discover declared derived material. It does
not fall back to the repository-wide `lifecycle.verify` in that no-op case;
full product verification can require candidate artifacts that are deliberately
not present until the later admission phase. Non-dry-run version-state writes
continue to require the configured verification lifecycle before any ref moves.

Repositories whose package version is anchored to an explicitly selected
upstream release can opt into manual next-anchor behavior:

```toml
[version]
required = true
strategy = "anchored"
next = "manual"
manifest = "libnode.release.json"
```

In this mode, the action validates the configured version files and anchor
manifest through the repository's verify lifecycle, but it does not rewrite the
package version to match the Buildchain release tag. After a production
release, it sets `next-anchor-required=true` and does not auto-create the next
alpha branch or tag. The repository must create the next upstream anchor line
explicitly, then run the normal channel promotion flow for that line.

When branch protection requires pull requests, generated version-state commits
still run through promotion automation first. The action updates
Buildchain-managed channel protection before generated bookkeeping, admits only
the exact `github-actions` App to the target-bound bypass allowlist, creates
every configured required check on the exact generated version-state commit, then
tries to apply that commit directly. If GitHub still rejects release
finalization bookkeeping, Buildchain creates or reuses a same-repository
`buildchain/version-state/*` PR based on the current target channel head and
returns `finalization-needed=true`; a later idempotent promotion run can resume
from the durable transaction state. Strict alpha uses the same protected
version-state PR recovery for its target and dev bookkeeping, and does not move
tags until those provider-enforced transactions land. Reusable wrapper callers
should allow `checks: write` so the generated checks are owned by GitHub Actions
and match the managed branch protection rule.

Stable promotion also protects concurrent development work. The reusable
wrapper checks out the exact current `dev/vN/vN.M` head as a reconciliation
workspace. If next-alpha bookkeeping cannot fast-forward that branch, the
action reruns the declared version-state generation and verification from that
dev tree, creates a two-parent reconciliation commit from the regenerated
files, and fails closed if the checkout moved before the mutation boundary.
This prevents generated projections from an older release tree from replacing
capabilities that reached dev while the release was in progress.

For Buildchain-owned automation, callers may pass
`branch-protection-bypass-apps: github-actions`. The action rejects every other
App slug and all user or team bypass actors. It configures managed
`dev/vN/vN.M`, `alpha/vN/vN.M`, and `release/vN/vN.M` branches with one
required approving review, Code Owner review, stale-review dismissal,
latest-push approval, exact App-bound GitHub Actions checks, admin enforcement,
conversation resolution, no force pushes, and no deletions; the bypass
allowance only lets the named automation identity apply generated version-state
or channel bookkeeping without a second human review after the reviewed channel
PR has already merged.

GitHub may return either `403` or a deliberately opaque `404` when a
non-administrator token reads the full branch-protection endpoint. In that
case, promotion reads the provider's branch summary and accepts only an
already-protected branch that enforces the exact required check for everyone.
It does not interpret the opaque response as missing protection or try to
rewrite policy with a developer token. The independent publication-authority
audit remains responsible for the complete read-only governance proof.

## Publish Transactions

Promotion can also own external publish side effects. Enable this only from a
trusted channel workflow:

```yaml
- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v3
  with:
    token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
    generated-ref-update-token: ${{ github.token }}
    sha: ${{ github.sha }}
    target-ref: release/v3/v3.0
    publish-transaction: "true"
    publish-mode: publish-final-version
    publish-auth: trusted-publishing
    publish-required-artifacts-json: >-
      [
        {"kind":"npm","name":"@kungfu-tech/buildchain","ref":"3.0.0","digest":"sha256:..."}
      ]
```

For anchored/manual package repositories that build through the reusable
workflow, keep the publish entrypoint on the `publish-gate/*` source-lock
contract:

```yaml
- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v3
  with:
    token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
    sha: ${{ needs.build.outputs.publish-source-sha }}
    target-ref: release/v22/v22.22
    require-publish-source-lock: "true"
    publish-source-ref: ${{ needs.build.outputs.publish-source-ref }}
    publish-source-sha: ${{ needs.build.outputs.publish-source-sha }}
    publish-source-locked: ${{ needs.build.outputs.publish-source-locked }}
    publish-transaction: "true"
    publish-mode: publish-final-version
    publish-auth: trusted-publishing
```

`target-ref` remains the channel promotion target that must point at `sha`.
`publish-source-ref` is the reviewed source-lock branch that authorized this
specific package publication. Direct `alpha/*` or `release/*` channel refs are
not valid publish source locks when `require-publish-source-lock` is enabled,
and a mismatched `publish-source-sha` fails before any promotion or publish side effects begin.

Consumers that opt in to a product-owned final predicate set
`require-publication-qualification: "true"` and pass the exact sealed
`publication-capability-json`, complete `publication-gate-aggregate-json`, and
`publication-qualification-receipt-json`. The action validates their canonical
digests, predicate identity, freshness, nonce, source, version, channel, target,
and evidence bindings both before provider access and immediately before the
publish transaction. Missing or drifted receipts fail before mutation. The
reusable `release-candidate-promote.yml` workflow creates these values in a
separate credentialless consumer job; direct callers must preserve the same
contract and may pass previously consumed nonces through
`publication-used-qualification-nonces-json`.

The reusable promote workflow serializes non-dry-run promotion intents per
repository and re-reads `target-ref` before checkout, dependency installation,
release-candidate resolution, or publish-gate writes. If a queued intent asks
for an older SHA after the protected channel has advanced, the workflow records
the requested/current SHA pair, verifies that the current target is ahead of
the requested commit, and exits successfully as a superseded no-op. Diverged,
behind, or unreadable comparisons still fail closed.
After queued-intent revalidation, the reusable workflow runs the canonical
release-candidate resolver in metadata-only mode before installing candidate
dependencies or starting the full promotion job. The full job resolves and
downloads the evidence again before any publish-gate or publication mutation.
This preserves the final exact-evidence trust check while making missing or
stale PR-stage evidence fail early.
The action repeats that check at its mutation boundary for governed promotion
calls, closing the race between workflow preflight and action start. Direct
non-governed calls and dry-runs keep the strict target mismatch error so local
diagnostics cannot silently reinterpret a stale request.
Expected manual dry-run failures remain visible in the workflow result and do
not create automated workflow-friction issues; issue reporting is reserved for
non-dry-run promotion failures.
The reusable build workflow performs the cheaper channel-ref preflight earlier:
after source-lock resolution and before the build matrix, it requires the target
channel ref such as `alpha/v22/v22.22` or `release/v22/v22.22` to already point
at the locked `publish-source-sha`. If not, maintainers should merge the source
commit through the channel PR first.

For promote-only release candidates, attach the PR-stage reusable build evidence
and fail before publish-gate side effects if it no longer matches:

```yaml
- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v3
  with:
    token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
    sha: ${{ needs.build.outputs.publish-source-sha }}
    target-ref: alpha/v22/v22.22
    promote-only-release-candidate: "true"
    release-candidate-passport-path: .buildchain/artifacts/release-candidate-passport.json
    release-candidate-build-summary-path: .buildchain/artifacts/build-summary.json
    release-candidate-family-evidence-required: "true"
    release-candidate-family-evidence-root: sha256:<initiative-family-root>
    release-candidate-family-initiative-id: 2026-07-30-example-initiative
    release-candidate-family-assignment-id: 2026-07-30-example-release
```

The action validates repository, channel, source identity, platform matrix, and
the aggregate build-summary hash before it writes version state, opens
release-state, runs publish transaction logic, or moves tags/branches. Source
identity accepts the exact source SHA, the PR merge ref SHA, or the promoted
channel HEAD's Git tree SHA matching the passport tree hash. If validation
fails, run or attach the verified channel PR build first instead of promoting a
stale or unproven artifact set.

The four family-evidence inputs are optional. When enabled, the action requires
the candidate passport to carry the exact
`kungfu-buildchain-initiative-family-release-evidence/v1` envelope and checks
its family root, Initiative id, and Assignment id before any promotion
mutation. Buildchain only transports and validates this adapter-edge release
evidence; Kungfu Work Control remains authoritative for native Family State v1
and its additive v2 typed envelope.

When enabled, the action creates or resumes a release transaction keyed by
repository, version, source SHA, and target ref. It persists that transaction to
a machine-managed branch under `buildchain/release-state/<version>`, with
`state.json` and, once available, `evidence.json`. Fresh GitHub runners read
that durable ref before running publish, so reruns do not depend on a previous
runner's local `.buildchain` directory.

Consumers whose publish lifecycle only assembles local release assets or
Passport inputs may set `publish-rematerialize-on-resume: true`. After
Buildchain restores and validates durable publish evidence, it replays that
consumer-owned lifecycle with the original transaction environment before
Passport collection. This is explicit opt-in because registry publication and
other provider mutations must not be replayed blindly; the option rejects
`promote-existing-version`.

Build-once callers additionally pass `publish-sealed-bundle-root` and
`publish-sealed-bundle-manifest`. The action verifies the typed manifest and
persists every declared file below
`sealed-bundle/<candidate-root>/files/` on the same durable ref before it starts
the publish command. A fresh runner can omit both inputs: the action restores
the exact binary bundle into `.buildchain/recovered-publication/<version>/`,
re-verifies it, and exports the recovered npm tarball through
`BUILDCHAIN_SEALED_NPM_TARBALL` with its exact integrity and SHA-256. This path
never repacks the npm tarball.

The action runs `lifecycle.publish` from `buildchain.toml` or the explicit
`publish-command` input, then validates publish evidence before exact tags and
floating refs move. If durable state persistence fails, the action fails closed
before publish or public ref finalization.

`publish-mode` defaults to `publish-final-version`, the token-free path for
normal npm Trusted Publishing. Same-version alpha-to-latest recovery must be
declared as `publish-mode: promote-existing-version` and
`publish-auth: npm-token`; Buildchain runs `npm whoami` before it creates
release-state or moves any `npm dist-tag`. The Trusted Publishing mode is not
allowed to perform `npm dist-tag add`.

Buildchain itself uses this path for npm. Its `lifecycle.publish` runs
`node scripts/npm-publish-transaction.mjs`, which publishes
`@kungfu-tech/buildchain` through npm Trusted Publishing and writes npm
artifact evidence into the transaction before release refs move. The separate
`.github/workflows/npm-publish.yml` workflow is dry-run only.

Publish lifecycle environment:

```text
BUILDCHAIN_VERSION
BUILDCHAIN_CHANNEL
BUILDCHAIN_SOURCE_SHA
BUILDCHAIN_TARGET_REF
BUILDCHAIN_RELEASE_STATE
BUILDCHAIN_EVIDENCE_DIR
BUILDCHAIN_RELEASE_SHA
BUILDCHAIN_RELEASE_MATERIAL_SHA
BUILDCHAIN_PUBLISH_TOOLING_SHA
BUILDCHAIN_PUBLISH_EVIDENCE
BUILDCHAIN_SEALED_BUNDLE_ROOT
BUILDCHAIN_SEALED_NPM_TARBALL
BUILDCHAIN_SEALED_NPM_INTEGRITY
BUILDCHAIN_SEALED_NPM_SHA256
BUILDCHAIN_REQUIRED_ARTIFACTS
```

`BUILDCHAIN_REQUIRED_ARTIFACTS` is the normalized requirement array after the
action resolves a missing artifact `ref` to `BUILDCHAIN_VERSION`, or expands an
optional `ref_template` containing exactly one `{version}`, and binds any
declared provenance to the current release coordinate. Template expansion
happens after exact version selection; ambiguous or unsupported templates fail
before `lifecycle.publish`. Requirement descriptors may omit `digest`; final
publish evidence may not.

The action outputs `transaction-id`, `transaction-state`,
`transaction-publication-state`, `transaction-sealed-bundle-root`,
`transaction-resume-command`,
`transaction-exact-tag`, `public-release-tag`, `transaction-release-sha`,
`transaction-state-ref`, `transaction-state-sha`, `transaction-state-path`,
`publish-evidence-path`, and `release-passport-path`, `release-passport-output-dir`,
`release-passport-state-sha`, and `finalization-needed`.
`transaction-state-ref` is the durable recovery location.
`transaction-publication-state` provides the stable
`prepared`, `sealed`, `package-published`, `alpha-complete`, or
`release-complete` operator view. `transaction-resume-command` is the exact
consumer-facing resume entrypoint bound into the sealed manifest.
`release-passport-state-sha` is the durable ref commit after the generated
`release-passport/*` files have been uploaded into that recovery ref.
`finalization-needed=true` means publish evidence is valid, but protected branch
or ref finalization needs a later idempotent promotion run. For release
finalization, Buildchain may create a same-repository generated version-state
PR when GitHub rejects the direct protected ref update; that PR is Buildchain
bookkeeping, not a consumer-authored release change.
Set `github-release: "true"` when the semver promotion should also publish the
public GitHub Release. After the release transaction reaches `complete` and
`finalization-needed` is false, the action creates or updates the GitHub Release
for `public-release-tag`, applies deterministic metadata from the authoritative
publication channel (`alpha` is a prerelease and never latest; `release`,
`stable`, and `major` are stable and latest), and uploads the publish evidence
file plus generated release passport assets. Tag syntax remains the fallback for
ordinary callers that do not supply publication intent. For anchored/manual
package releases, `public-release-tag` is derived
from the published package version, while `transaction-exact-tag` remains the
internal Buildchain transaction ref for recovery and audit. If the transaction is
not complete yet, the action defers GitHub Release publication to the next
idempotent promotion run. When sealed release assets are present, those restored
files replace caller-supplied artifact paths. After upload succeeds, the action
writes the `github_release` milestone back to the durable transaction; an
interruption before that write is safe to retry because release creation and
asset replacement are idempotent.

After a publish transaction reaches `complete`, the action generates the unified
`buildchain-release-passport` in `.buildchain/release-passport` by default and
persists those files under `release-passport/` in the durable release-state ref.
Set `release-passport-product-name` to record the consumer product name, for
example `Libnode`, instead of the default `Buildchain`.
Set `release-passport-kfd-1-witness-jsons` to newline-separated KFD-1
contract-world witness JSON paths when released artifacts must prove
byte-for-byte KFD contract surfaces. Buildchain imports the KFD metadata from
`@kungfu-tech/kfd`, freezes the witness, verifies artifact bytes, and writes the
result under the KFD-provided `kfd-1` passport section.
Set `release-passport-kfd-2-claim-jsons` to newline-separated KFD-2 public
release trust claim JSON paths when a release makes additional human/agent
visible claims. Buildchain requires each public claim to bind declared sources,
machine-readable evidence, hashes, artifact coordinates, verification results,
audit boundary, responsibility state, and residual risk. Unbound claims fail
passport verification; prose-only claims downgrade the KFD-2 audit.
Set `release-passport-kfd-3-prebuild-witness-jsons` to newline-separated KFD-3
collaboration-interface pre-build witness paths, then provide artifact-side
evidence with `release-passport-kfd-3-artifact-witness-jsons` or a
product-owned `release-passport-kfd-3-artifact-verify-command` such as
`kungfu agent verify --json`. Buildchain compares declared shipped public
surfaces with artifact-exposed public surfaces and writes the result under the
KFD-provided `kfd-3` passport section.
Set `release-passport-kfd-adopter-manifest-json` to the standard full-cut
adopter manifest and `release-passport-kfd-product-gate-jsons` to the
newline-separated KFD-4, KFD-5, and KFD-7 gate results. Buildchain binds the
exact published KFD package, registry, verifier set, source, decision witness,
and product-gate roots into both Passport and artifact evidence. The optional
`release-passport-kfd-support-matrix-json` is comparison-only; it must exactly
match the derived `kfd-support.json` compatibility projection and cannot widen
candidate, unsupported, draft, or non-shipped states.
Set `release-passport-invariant-passport-jsons` to one or more product-owned
invariant Passport paths, or set `release-passport-invariant-passport-command`
to a command that emits one canonical Passport JSON document. Buildchain does
not reinterpret product invariants: it verifies the declared semantic root,
requires a `verified` verdict, complete platform coverage, a clean exact source
revision, and then binds the result into `buildchain.release.json`. Missing,
stale, falsified, incomplete, dirty, or tampered Passport evidence fails the
release transaction closed.
Set `release-passport-evidence-jsons` to newline-separated product-owned release
evidence attachment indexes. Each JSON document must declare `schemaVersion`, a
stable `id`, a product contract, and the exact release source SHA, tag, and
channel. Buildchain copies and hashes the documents, verifies their coordinates
against the final Passport, and retains them in the release evidence bundle
without interpreting product-specific or legal claims.

When release coordinates are not known until promotion, set
`release-passport-attachment-command`. Buildchain supplies
`BUILDCHAIN_RELEASE_SOURCE_SHA`, `BUILDCHAIN_RELEASE_TAG`,
`BUILDCHAIN_RELEASE_CHANNEL`, `BUILDCHAIN_RELEASE_VERSION`,
`BUILDCHAIN_RELEASE_DEPLOYMENT_COORDINATE`, `BUILDCHAIN_RELEASE_TARGET_REF`, and
`BUILDCHAIN_RELEASE_PASSPORT_OUTPUT_DIR`; the command must emit a JSON array or
an object with a non-empty `files` array. Direct Action callers may still use
the v2 alias `release-passport-evidence-command`. Reusable v3 workflows reserve
that older name for the distinct post-activation released-evidence command.

Buildchain's own release workflow sets `release-passport-buildchain-self-kfd:
"true"`. In that mode the action generates Buildchain-owned KFD-1/2/3 witnesses
inside the final version-state workspace, after the release transaction has
materialized generated files such as `package.json` and `dist/site/*`. This
keeps self-hosted KFD witness hashes bound to the exact published package
instead of to a pre-promotion checkout.
Set `release-passport-github-artifact-attestation-policy-jsons` to one or more
newline-separated `buildchain.github-artifact-attestation-policy/v1` paths when
the Release Passport must require GitHub keyless provenance for Linux release
artifacts. The higher-level v3 promotion workflow exposes the single-artifact
`github-artifact-attestation-policy-json` input and owns staging, signing,
provider verification, immutable GitHub Release evidence upload, and read-back;
direct action callers own those post-Passport steps themselves.
When present, the passport includes the aggregate build summary, platform
artifact manifests, npm publish evidence, dist-tag promotion evidence, the
release-state ref, trusted publishing metadata, and the Buildchain transaction
result. After the first passport upload, Buildchain backfills the durable
release-state SHA into `buildchain.release.json` and persists the passport
again, so the consumer-side passport is a complete audit entrypoint. Set
`release-passport: "false"` only for a controlled recovery run that must skip
passport generation.

Finalization recovery is anchored to the durable transaction, not to a single
workflow run SHA. After generated version-state bookkeeping is applied, the
current channel head can be the generated version-state commit or a historical
merge commit that contains or corresponds to the recorded `release_material_sha`;
it does not have to equal the original `source_sha` or the transaction
`release_sha`. Reruns accept exact tags that already point at the transaction
release/material SHA or the finalized channel head, and continue moving any
missing floating tags or dev/alpha refs before marking the transaction
`complete`. An explicit recovery of an already `complete` transaction is also
accepted when the requested source SHA, version, exact tag, channel, and target
all match the durable record exactly. It returns the completed transaction
without republishing package bytes; a source accepted only through later branch
history is not sufficient for this terminal-state reuse.

Normal reruns accept already-published artifacts only when evidence matches.
Missing required artifacts can be published on the next run. Conflicting
refs, digests, or declared provenance put the transaction into
`repair_required`; `abandoned` and
`failed_permanently` also fail closed unless `publish-transaction-override` is
set for a controlled repair. The same override may replace a stale transaction
only when its version, exact tag, target ref, and channel are unchanged, the
transaction is not complete, and it contains no published artifacts or
evidence. This lets a newly admitted source retry a previously failed paper
publication without weakening already-published facts.

In strict buildchain promotion, ref movement is also gated by the old ABV
governance semantics:

- when detailed target branch protection is readable, it must enforce
  protection for administrators and require approving PR review plus the strict
  `check` job from the `Verify` workflow; when GitHub withholds that
  administration endpoint from the workflow token, the exact transaction must
  instead prove a protected current head, same-repository merged PR,
  independent approval, and the required successful `check` from its configured
  GitHub App;
- post-publish channel reconciliation reuses an already qualifying
  provider-enforced policy when the workflow token cannot read or rewrite the
  administrative protection document, and fails closed if the public branch
  summary no longer carries the required check for everyone;
- alpha promotion must come from a merged same-repository PR
  `dev/vN/vN.M -> alpha/vN/vN.M`, or from a strict same-line
  `publish-gate/alpha/vN/vN.M/<version> -> alpha/vN/vN.M` source-lock PR;
- release promotion must come from a merged same-repository PR
  `alpha/vN/vN.M -> release/vN/vN.M`, or from a strict same-line
  `publish-gate/release/vN/vN.M/<version> -> release/vN/vN.M` source-lock PR;
- major promotion must come from a merged same-repository PR
  `release/vN/vN.M -> publish-gate/major`;
- release promotion must have an exact alpha tag for the same patch line, and
  the release source tree must match that alpha tag tree, so release does not
  introduce new code after alpha;
- anchored/manual release promotion may differ from that alpha tree only in
  declared `version.files` and the configured anchor manifest, and only when the
  checked-out release material has passed `lifecycle.verify` or the explicit
  `verification-command`;
- generated release and next-alpha version-state trees can be verified locally
  with either the `verification-command` input or `buildchain.toml`
  `lifecycle.verify` before any tags or channel refs move.

The reusable promotion workflow keeps governance reads, generated status checks,
and generated ref updates on the run-scoped `github.token`. When branch
protection rejects generated bookkeeping, it supplies `BUILDCHAIN_PROMOTION_TOKEN`
only through `generated-pull-request-token` so the same-repository recovery PR can
be listed or created without broadening the governance client. Protected branch
review and check rules guard human channel merges, while the reusable build trust
gate checks the source-lock channel HEAD and merged same-repository PR lineage
before heavy build runners start. This action still independently rechecks PR
lineage, alpha/release tree equivalence, and generated version-state verification
before moving channel refs and tags.
The reusable `release-candidate-promote.yml` wrapper defaults
`branch-protection-bypass-apps` to `github-actions`, so flow-internal promotion
can complete generated `dev`/`alpha`/`release` bookkeeping while ordinary human
pushes and PR merges remain governed by the one-review branch protection rule.
The promotion action rejects alternate App slugs and every user or team bypass,
so the mutation path cannot widen the authority descriptor.

The tag names intentionally follow the old ABV release semantics:
exact release tags are `vX.Y.Z`, exact alpha tags are `vX.Y.Z-alpha.N`, floating
release tags are minor/major tags such as `v3.0` and `v3`, and floating alpha
tags are minor-line tags such as `v3.0-alpha` plus cross-minor major tags such
as `v3-alpha`. A major alpha tag only moves for the highest minor in that major
with a published alpha, so older-line maintenance cannot roll consumers back.
Bare tags such as `1.0.0` are not
maintained as buildchain release entrypoints.

Repository rulesets should protect exact tags, not every `v*` tag. A ruleset
such as `refs/tags/v*` also protects floating channel tags like `v3.0-alpha` and `v3-alpha`,
which Buildchain must update after exact tags and publish evidence are durable.
Use an exact-tag rule such as `refs/tags/v*.*.*` for immutable evidence tags and
leave floating channel tags mutable for the promotion token.
