Release Propagation
Release propagation lets a finalized upstream release open a downstream update PR using the upstream release passport as the audit source. It is for product chains such as:
kfd -> site-libkungfu-dev
A -> B -> C
The downstream repository receives an exact lock, not a floating dist-tag. A site or app can then consume the upstream package, site bundle, or release passport as its single source of truth without hand-copying release facts.
Contract
The propagation graph is declarative JSON:
{
"schemaVersion": 1,
"contract": "kungfu-buildchain-release-propagation-graph",
"nodes": [
{
"id": "kfd",
"repository": "kungfu-systems/kfd",
"package": "@kungfu-tech/kfd"
},
{
"id": "site-libkungfu-dev",
"repository": "kungfu-systems/site-libkungfu-dev",
"lockPath": "buildchain.upstreams/kfd.release.json",
"baseRef": "dev/v2/v2.7"
}
],
"edges": [
{
"id": "kfd-to-site",
"from": "kfd",
"to": "site-libkungfu-dev",
"channelPolicy": "preserve"
}
]
}
channelPolicy: "preserve" is the default and maps:
alpha -> alpha
release -> release
Cross-channel mapping is allowed only when an edge declares
channelPolicy: "explicit" and a channelMap. Buildchain rejects cycles so a
chain can fan out or continue as A -> B -> C, but cannot loop back into an
already visited release line.
Upstream Release Envelope
The upstream release envelope is the post-finalization fact set:
{
"repository": "kungfu-systems/kfd",
"channel": "alpha",
"tag": "v1.4.0-alpha.3",
"sourceSha": "1111111111111111111111111111111111111111",
"package": {
"name": "@kungfu-tech/kfd",
"version": "1.4.0-alpha.3",
"integrity": "sha512-..."
},
"releasePassport": {
"url": "https://github.com/kungfu-systems/kfd/releases/download/v1.4.0-alpha.3/buildchain.release.json",
"sha256": "2222222222222222222222222222222222222222222222222222222222222222"
},
"siteBundle": {
"manifestSha256": "3333333333333333333333333333333333333333333333333333333333333333"
}
}
The package version and integrity must be exact. Downstream build logic should
install that version directly, not resolve alpha or latest again.
Publication repositories can propagate immutable publication archive evidence
without npm package facts. The upstream envelope then includes
publicationArtifact:
{
"repository": "kungfu-systems/paper-observer-declared-timelines",
"channel": "alpha",
"tag": "v0.1.0-alpha.1",
"sourceSha": "4444444444444444444444444444444444444444",
"releasePassport": {
"url": "https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/buildchain.release.json",
"sha256": "5555555555555555555555555555555555555555555555555555555555555555"
},
"publicationArtifact": {
"id": "observer-declared-timelines",
"kind": "paper",
"version": "0.1.0-alpha.1",
"canonicalUrl": "https://papers.libkungfu.dev/observer-declared-timelines/",
"latestUrl": "https://papers.libkungfu.dev/observer-declared-timelines/latest/",
"latestEvidenceUrl": "https://papers.libkungfu.dev/observer-declared-timelines/latest/buildchain.release.json",
"immutableVersionUrl": "https://papers.libkungfu.dev/archive/observer-declared-timelines/v0.1.0-alpha.1/",
"registry": {
"url": "https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/publication-registry.json",
"sha256": "6666666666666666666666666666666666666666666666666666666666666666"
},
"manifest": {
"url": "https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/publication-artifact.json",
"sha256": "7777777777777777777777777777777777777777777777777777777777777777"
},
"passport": {
"url": "https://github.com/kungfu-systems/paper-observer-declared-timelines/releases/download/v0.1.0-alpha.1/publication-artifact-passport.json",
"sha256": "8888888888888888888888888888888888888888888888888888888888888888"
},
"primaryArtifact": {
"path": "_build/main.pdf",
"url": "https://papers.libkungfu.dev/archive/observer-declared-timelines/v0.1.0-alpha.1/main.pdf",
"sha256": "9999999999999999999999999999999999999999999999999999999999999999"
}
}
}
This lets a site repository render the latest reader page and historical version index from release facts while keeping old PDFs, source bundles, manifests, and passports immutable.
CLI
Generate a propagation plan:
buildchain release-propagation plan \
--graph buildchain.release-propagation.json \
--upstream-release .buildchain/upstream-release.json \
--output .buildchain/release-propagation-plan.json \
--json
Write the downstream lock:
buildchain release-propagation write-lock \
--plan .buildchain/release-propagation-plan.json \
--target site-libkungfu-dev \
--cwd downstream-checkout \
--json
The written lock has contract
kungfu-buildchain-release-propagation-lock and records:
- upstream repository, channel, exact tag, source SHA;
- optional npm package name, exact version, and sha512 integrity;
- optional publication artifact canonical/latest/immutable URLs, registry, manifest, passport, source bundle, and primary artifact digests;
- release passport URL and SHA-256;
- optional site bundle manifest SHA-256;
- downstream repository, channel, base ref, lock path;
- edge id and channel policy.
Reusable Workflow
Upstream repositories can call
.github/workflows/release-propagation.yml@v3 after release finalization:
jobs:
propagate-site:
uses: kungfu-systems/buildchain/.github/workflows/release-propagation.yml@v3
with:
buildchain-ref: v3
graph-json: ${{ needs.release.outputs.propagation-graph-json }}
upstream-release-json: ${{ needs.release.outputs.upstream-release-json }}
downstream-target: site-libkungfu-dev
downstream-repository: kungfu-systems/site-libkungfu-dev
downstream-base-ref: main
downstream-prepare-command: pnpm install --frozen-lockfile --ignore-scripts
downstream-verify-command: pnpm run check
dry-run: false
secrets:
propagation-token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
The downstream branch name may be reused across upstream releases. Before
replacing an existing managed branch, the workflow reads its exact remote SHA
and pushes with an explicit --force-with-lease=<ref>:<sha>. A surviving branch
from a merged PR is therefore reconciled without manual deletion, while a
concurrent writer makes the lease fail closed. The controller receipt includes a
propagation-branch-reconciliation evidence file recording the branch, observed
remote SHA, pushed SHA, lease mode, and the deterministically created or updated
open PR.
The workflow checks out the Buildchain runtime selected by
buildchain-repository and buildchain-ref into .buildchain/runtime, invokes
that runtime for the propagation plan and lock write, then checks out the
downstream repository and writes the exact lock. A consumer that must pin the
exact upstream package or regenerate deterministic files declares
downstream-prepare-command. The command receives
BUILDCHAIN_UPSTREAM_PACKAGE_NAME, BUILDCHAIN_UPSTREAM_PACKAGE_VERSION, and
BUILDCHAIN_UPSTREAM_RELEASE_LOCK. After preparation, Buildchain refreshes an
existing <!-- buildchain:badges:start --> README block by default. Consumers
can disable that step with refresh-managed-readme-badges: false.
downstream-verify-command runs against the final tree before any commit or
push, so consumers can use the same check as their PR workflow. Preparation,
badge refresh, and verification failures all fail closed. The workflow stages
the complete deterministic result, signs the propagation commit with DCO, and
then opens or updates the PR. It does not publish the downstream release
directly. The downstream repository keeps its normal Buildchain governance: the
PR updates source-of-truth facts, then downstream alpha or release publication
runs through its own protected channel. A byte-identical rerun is an explicit
successful no-op.
For unreleased runtime validation, keep the caller’s reusable workflow reference
on @v3 and pass a temporary train ref through buildchain-ref.
kfd to site-libkungfu-dev
For kfd -> site-libkungfu-dev, the graph should preserve channels:
- a
kfdalpha release produces a downstream alpha lock and downstream alpha publication consumes the exact@kungfu-tech/kfd@...-alpha.Npackage; - a
kfdstable release produces a downstream release lock and downstream stable publication consumes the exact stable package.
This keeps the site synchronized to the package truth without allowing the site to drift onto a floating npm dist-tag.