Why cargo-release-plan?
The cargo-release-plan toolkit automates release preparation and publication for
Rust workspaces. It includes the cargo-release-plan command-line application,
the cargo-release-plan-action reusable GitHub workflows, and the
increment-versions agent skill. They are designed to be used together and are
documented together in this book.
The goals are:
- Every merged pull request immediately publishes its changed packages to crates.io.
- Fully automated version-number increments that obey Semantic Versioning.
- Reusable GitHub workflows for easy integration.
- No stored secrets required for publishing.
- Workspaces containing any number of library or binary packages.
cargo-binstallsupport for fast installation of binary packages.- Advanced workspace structures, including published packages with private APIs.
The skill prepares version changes as part of the pull request. Review approves the code and versions together; merging starts publication without another version-selection step. The normal workflow uses squash merges so the PR’s final content and versions enter release history together.
For version changes without publishing integration, use standalone planning. That scenario ends with the version-increment PR being reviewed and merged; publishing is not part of it.
Who this fits
The supported publication path is a Cargo workspace releasing to crates.io and
GitHub. Libraries receive package tags; packages with an installable binary also
receive GitHub releases and native ZIP archives usable by
cargo-binstall.
Binary publication supports one executable per package, built with default features on supported native targets. Alternative registries or forges, cross-compilation, changelog generation and arbitrary release-policy hooks are outside this process.
Responsibilities
| Participant | Responsibility |
|---|---|
| Author or authoring agent | Understand consumer promises and choose breaking, nonbreaking or patch semantic impacts. |
cargo-release-plan | Collect release evidence, enforce version relationships, resolve and apply plans, and reconcile publication. |
API compatibility checker (cargo-semver-checks) | Detect supported Rust API changes; its result is evidence, not a complete behavioral assessment. |
increment-versions skill | Guide an agent through the tool’s planning operations and explain its decisions. |
| Reviewer and merge policy | Approve the source and version changes together and require appropriate checks. |
| Cargo | Resolve dependencies when requested, construct and verify package archives, and order registry uploads. |
| GitHub Action and workflows | Install pinned tools, supply permissions, run jobs and transport evidence and outcomes. |
Completing a plan or running the skill grants neither merge authority nor permission to publish. Publication starts from the reviewed, merged source under the repository’s release policy.
From change to delivery
flowchart TD
A["Develop changes"] --> B["Execute increment-versions skill"]
B --> C["Review the pull request, including its versions"]
C --> D["Pull request is squash-merged"]
D --> E["Reusable workflows publish the release"]
E --> F["Packages appear on crates.io"]
F --> G["Binary packages gain cargo-binstall archives"]
The chapters first explain the model, then walk through repository setup and local/GitHub integration. The operations chapters cover ordinary releases, verification and recovery. The reference distinguishes literal configuration and decision formats from conceptual examples and opaque tool-generated evidence.
This book is published at folo-rs.github.io/folo/cargo-release-plan/. For an older pinned installation, use the matching documentation and skill revision, not an assumption that the current website describes every older executable.
Which release are we changing?
Before choosing a new version, the skill needs to know which package version and content this pull request follows. The answer can differ between packages, and between an ordinary PR and a PR stacked on an unmerged parent.
Package anchors retain unfinished release work
The release branch is the branch whose merged versions are published. Release history is that branch up to the commit selected for an assessment.
A package’s anchor supplies the preceding version and source for its comparison. In actual release history, it is the latest first-parent commit that changed the package’s version. The anchor version means exactly the version declared by that package in the anchor commit.
For example, widget reached 1.4.0 in commit B:
release history
A ---- B ---- C ---- D
| |
| +-- unrelated documentation change
+-- widget 1.4.0
The documentation change does not create a new widget release. Its anchor is
still B. If commit C instead changed widget without increasing its version,
that work must still be included in the next release. Comparing only with D
would hide it.
This is why the tool retains anchors rather than simply comparing every package with the release branch’s latest tree: it can catch up after migration or manual changes that omitted version planning.
A stacked PR follows its parent’s final result
The merge target is the commit a PR intends to merge into. In a stack, that can be an unmerged parent PR.
Suppose the parent changes widget to 1.5.0, then receives another fix before
review is complete. All of the parent’s final content belongs to its 1.5.0
release. The child must compare with that final snapshot, not the parent’s
intermediate version-edit commit.
release history: widget 1.4.0
|
+-- parent PR: widget 1.5.0, including all final fixes
|
+-- child PR: further changes need a version above 1.5.0
For a new or incremented parent package, the parent’s final commit acts as the child’s anticipated anchor. Packages still at their release-history version keep their historical anchors and any catch-up work.
The managed workflow uses squash merges so each PR’s final version and content enter release history in one commit. The anticipated parent then becomes the actual preceding release. The child does not depend on a merge queue combining the PRs, and it cannot reuse the parent’s version merely because the parent has not merged yet.
What the package status means
The report uses the package’s anchor version and content to distinguish:
| Status | Meaning |
|---|---|
needs-increment | Released content changed, but the version did not advance above the anchor version. |
pending-release | This PR already has a higher version, or introduces the package. |
unchanged | The package still matches its anchor’s content and version. |
A pending increment is kept when it covers the full change. If later edits make the impact stronger, the skill raises it. Merely rerunning the skill is not a reason for another increment.
Packages with publish = false are not publication requests, but their versions
can still participate in version groups.
History movement changes the question
If another PR merges a version that this PR planned to use, that version now belongs to different content. Likewise, a changed parent can alter what a child must be compared with. The skill records one history/target pair for the assessment and refreshes it before applying the plan.
When either input moves, the skill reassesses instead of attaching old results to the new history. Recovery describes that process.
Recorded versions do not prove delivery
After widget 1.5.0 merges, its source can be unchanged against its new anchor
even if its registry upload failed. Package status describes version readiness,
not whether crates.io, a GitHub tag or a binary archive exists.
Publication checks those destinations separately and completes missing work. That distinction makes a failed upload recoverable without inventing another version solely to retry it.
Released content
Released content is the content relevant to consumers of a Cargo package. The assessment follows the files Cargo would package and the dependency resolution an installable binary would deliver. It is not a filter over “production-looking” directory names.
Packaged files
Git-tracked files participate according to each package’s include and exclude
rules. A path selected at either the anchor or the work tree counts, so deleted
files and files removed from an allow-list remain visible. A package is identified
by its name even when its directory moves.
Uncommitted changes to tracked files are assessed. Untracked files are reported as advisory evidence; add intended release inputs to Git before relying on their assessment. A nested package boundary keeps its files out of the enclosing package’s content.
The executable bit is released content as well as file bytes. Git’s file-mode handling allows the comparison to remain meaningful on filesystems without Unix executable permissions.
Cargo.toml is packaged content. Even a formatting-only change to a package
manifest requires assessment; the tool does not maintain a second policy that
decides which lines of that file are important.
Choose archive contents deliberately
An include allow-list makes the consumer build explicit and keeps new
development-only files from entering the archive automatically. For example:
[package]
include = ["src/**/*"]
This is a manifest fragment, not a complete package. Add any build script and
other files required by the consumer build. If source embeds a file outside
src at compile time, the archive must contain it, including when that code is
behind a published feature.
An inherited include value replaces rather than extends the workspace value.
A package with additional compile-time inputs needs a complete list of its own.
Tests, benchmarks and books can remain repository-only when consumers do not
need them. Cargo’s actual rules, not this organizational recommendation, determine
what the tool assesses.
Changing an allow-list changes the package manifest and can change the archive; it is itself a released-content change.
Inputs Cargo adds
Cargo packages its manifest and a generated lockfile. It also includes declared
README and license files outside ordinary path rules, including inherited files
or resources outside the package directory. Without an explicit README setting,
Cargo detects a default README; readme = false opts out.
These inputs can affect a package even when its include list names only src.
A package-root target directory is not packaged.
To audit the model against Cargo, use the explicit
--verify-packaging probe. It is not part
of the normal offline check.
Inherited workspace values
A package’s published manifest contains the values it inherits from
[workspace.package] and [workspace.dependencies]. For example, changing an
inherited Rust version or dependency requirement affects every package that
inherits that value, without needing an edit under each package directory.
Attribution is per package, not a blanket release of the whole workspace. Workspace lint configuration does not participate in this released-content model. Path-only development dependencies are omitted by Cargo when packaging; adding a version requirement changes that boundary.
Binary locked dependencies
Library consumers resolve dependencies in their own graph. A library-only package therefore does not gain a release reason merely because Cargo puts a lockfile in its archive.
An installable binary is different: cargo install --locked uses its packaged
resolution. A package containing a binary, including a mixed library/binary
package, releases its locked dependency closure: the dependencies reachable
for installation, identified by name, version and source.
The tool compares a package-rooted closure from the workspace lockfile, not raw workspace lockfile bytes. It is conservative: registry dependencies retain their workspace-unified locked edges, so packaging can prune optional edges that this assessment includes. It does not perform separate package/feature resolution during read-only classification. Normal and build dependencies across target platforms participate; development-only workspace edges do not. Examples, benchmarks, tests and build scripts do not turn a library into an installable binary.
Consequently:
- An unrelated lockfile change need not release every binary.
- A transitive binary dependency change can require a release with no source diff in that binary package.
- Libraries still need consistent lockfiles for locked workspace commands even when lockfile changes are not release reasons.
Source identity matters: equal dependency names and versions do not make different registries or Git sources interchangeable. Unresolvable identity is an assessment error, not evidence of unchanged content.
During publication, Cargo can normalize the archive lockfile and prune inactive dependency branches. It must not select a dependency identity outside the installation closure that was assessed before merge.
Choosing versions together
A release report describes what changed. The skill assesses the change’s semantic impact on users, then produces a plan of the versions and workspace edits required to release it. This separation keeps a text diff from pretending to decide whether a consumer promise was broken.
Semantic impact and version bumps
The skill records each package’s semantic impact:
| Impact | Use |
|---|---|
breaking | A change breaks a consumer promise. |
nonbreaking | A compatible addition extends the consumer’s promises. |
patch | A correction or other change needs no stronger semantic impact. |
Consumer promises include documented behavior, CLI arguments, data formats, feature availability and build requirements, not just Rust function signatures.
The resulting numeric version bump is patch, minor or major.
The decisions document calls the semantic field impact; the proposal calls
the numeric field bump. An exact version can be supplied instead of a bump.
| Anchor version | Semantic impact | Example resulting version | Version bump |
|---|---|---|---|
1.4.0 | nonbreaking | 1.5.0 | minor |
1.4.0 | breaking | 2.0.0 | major |
0.4.2 | nonbreaking | 0.4.3 | patch |
0.4.2 | breaking | 0.5.0 | minor |
These examples start without an adequate pending increment or other group effects. Proposal generation retains an already sufficient version rather than incrementing it again.
The translation follows Cargo compatibility: the leftmost nonzero version
component defines the incompatible boundary. A 0.0.z package has no compatible
next version. Explicit target versions are also supported when numeric increments
do not express the intended release.
External API evidence is a floor
cargo-semver-checks detects
supported Rust API incompatibilities. Establish that the checker can run before
using its result: a rustdoc-format failure is not a compatibility pass.
Compare public library contracts with all features, but review feature changes manually too. Moving an existing item behind a new feature can break callers using a subset even when both all-features APIs contain the item. Removing a feature, changing observable behavior or breaking a CLI can also require a stronger decision than the checker reports.
Raise the decision when the contract requires it. Do not lower it below a valid compatibility finding.
The checker has a cross-crate limitation: re-exported dependency definitions are not fully available in its input. A public facade is the package consumers use while another package supplies its implementation. Review the definitions and behavior it exposes, including public methods on re-exported types, even when the facade’s own source is unchanged. A passing facade comparison does not replace this assessment.
Procedural macros transform consumer input into Rust code during compilation.
Their Cargo targets are unsupported for direct comparison, regardless of whether
they offer a public API. Review accepted invocations, attribute arguments, generated
APIs and trait implementations, consumer requirements, and promised behavior.
Explicit unavailable evidence supplies no minimum impact, not a default patch.
Consumer tests and source review support the decision; missing evidence is not
proof of compatibility.
Version groups
An exact local dependency such as version = "=1.4.0" joins its Git-tracked
workspace endpoints into a version group. Connected exact relationships,
in either dependency direction, make one group. There is no separate group
configuration.
Groups include normal, build and development dependencies, optional and target-specific declarations, and inherited workspace dependencies. A registry dependency, an outside-workspace path, a versionless path or an unused workspace entry does not create a group.
Use a single exact =major.minor.patch requirement. Partial exact versions,
prerelease/build suffixes and compound exact requirements do not declare valid
workspace groups.
Every member shares the resolved version. A non-publishable package
has publish = false. Its version can move for
group alignment, but its source changes receive
no semantic impact and it is never uploaded. The highest declared member
version participates in choosing the result, so alignment does not lower a member.
The running example is:
widget public library 1.4.0
exact dependency on widget_impl
widget_impl private implementation 1.4.0
widget-fixtures publish = false helper 1.4.0
exact dependency on widget_impl
widget-cli separate binary package 2.0.0
compatible dependency on widget
executable name: widget
The library, implementation and helper form one group. widget-cli does not:
compatible dependency requirements do not make exact-version groups.
Private APIs and public dependencies
A published implementation package can declare:
[package.metadata.release-plan]
private-api = true
That means its library surface serves another package rather than consumers
directly. It is not the same as publish = false. It still has released content,
version-group obligations and dependency effects.
When widget_impl changes, compatibility selection reaches the public widget
contract through their group. Re-exported items are compared where consumers use
them only to the extent supported by the checker; the author assesses their full
contract there rather than demanding compatibility for every private implementation item.
Unsupported procedural-macro members are excluded from direct comparison after group
selection, so a macro implementation change can still select the public facade.
All publishable packages remain in dependency-first semantic assessment.
A public origin is a workspace package defining types exposed by another package’s public API, directly or through another exposed owner’s API. Its incompatible version movement requires an incompatible release of the exposing package too. This is based on the version relationship, not a guess that the particular exposed types were unaffected.
Exposure comes from each package’s externally verified
allowed_external_types declarations. Defining crate names can differ from the
direct dependency through which a type is re-exported. The declarations are
followed transitively through the defining packages, reachable through normal
workspace dependencies. Sharing a third package’s origin does not make a
dependency’s own types part of the consumer’s API.
Private implementation packages do not interrupt breaking-change propagation. A re-exported type can expose another dependency through its methods without the outer library’s allow-list enumerating every such type. Following the defining implementation package’s own allow-list preserves that consumer contract. Using its whole allow-list can propagate changes from unrelated items, and exact-version groups can move an unaffected public member.
Inference or method-call ambiguity introduced by coexisting dependency versions’ trait implementations is intentionally considered nonbreaking. Implementation contributors are not tracked solely to prevent that ambiguity. Explicitly exposed trait and type identities, bounds and associated types still participate in propagation; other behavioral and API changes still require semantic assessment.
A compatible dependency-version movement imposes no type-identity break. It does not
cap the facade’s own impact at patch: a compatible new method on a re-exported type
is a nonbreaking facade capability. Conversely, changing an unexported implementation
item is not a public API change merely because it is pub inside that crate.
Assess its observable effects and let group alignment follow the semantic decisions.
This model requires a real
external-type validation gate.
Ordinary check does not verify those declarations against compiled APIs.
Requirement rewrites also matter
Workspace requirements must name the version their target declares, not merely
admit it within a broad range. An ordinary requirement such as 1.4.0 and an
exact requirement such as =1.4.0 have different grouping effects, but both need
updating when their target moves.
A dependent manifest rewrite is released content. Preview accounts for that rewrite and any binary lockfile effects, retaining sufficient pending increments. The complete release set is therefore larger than the packages whose source the author initially edited.
From changes to a version plan
The skill must answer two different questions: what changed, and what versions those changes require. A diff can answer the first question, but it cannot determine whether a behavioral change breaks a promise to users.
Facts first, then semantic impact
Keep command inputs stable
Keep assessed source, configuration and history unchanged while a command runs, apart from its explicit editing or dependency-resolution steps. Tools invoked for read-only work must follow the same requirement. Build output outside assessed inputs is permitted, as are Git object additions and index stat refreshes that leave semantic source and staged identities unchanged.
Each command acquires current inputs. It can reuse those observations during read-only work, but acquires new ones after its own writes or resolution and for new or relocated workspaces. Run source-mutating commands against the same workspace separately. This requirement does not freeze remote registry or publication state.
Assess the acquired facts
A release report lists each package’s changed released content, declared version and workspace dependencies. File patches explain source changes; structured entries explain inherited manifest values and binary dependency changes. Together with API comparison results, these are the facts used to choose versions.
A semantic impact describes what a package’s change means to its users:
breaking, nonbreaking or patch. The skill chooses it from the report,
API comparisons and the package’s behavioral contracts. Interpreting those facts
and choosing the impacts is the release assessment.
For example, removing a public method is a breaking impact. Fixing a computation without withdrawing a promise is usually a patch impact. A Rust API checker can detect the removed method, but deciding whether the computation is a correction still requires understanding the promised behavior.
Preview makes the complete release visible
The skill turns the selected impacts into a version proposal. A proposal starts with the changed packages, but a workspace release can affect more than those packages:
- Members of a version group must move together.
- Dependencies must name the new versions.
- A binary can need a release because its locked dependencies changed.
Preview computes these effects and produces a resolved plan: the complete package versions and exact manifest/lockfile edits to apply. Group expansion is part of that operation, not a separate user step.
Consider a compatible API addition in widget:
| Package | Starting version | Result | Why |
|---|---|---|---|
widget | 1.4.0 | 1.5.0 | A compatible API addition. |
widget_impl | 1.4.0 | 1.5.0 | It shares widget’s version group. |
widget-cli | 2.0.0 | 2.0.1 | Its dependency and locked binary inputs change. |
The proposal begins with the API decision. Preview makes the complete set visible so the skill can assess the dependent changes too. A required dependent release is a minimum obligation, not a claim that every such change is harmless.
Apply the result that was reviewed
Suppose preview records widget at 1.5.0 and a particular dependency resolution.
Application must install those exact edits, not run resolution again and silently
choose a newer dependency. The resolved plan therefore also records the original
inputs that those edits may replace.
If a manifest or source file changes after preview, the skill prepares a fresh plan. Editing the old generated plan by hand would hide which inputs were assessed. Applying a valid plan twice is a no-op the second time; a partially edited tree is not automatically treated as that completed result.
The same source binding matters for API checks. Comparisons before application use the preview’s retained workspace, where the proposed versions and lockfile are already present. Comparing an unrelated checkout would say nothing about the release described by the plan.
Planning files are not publishing instructions
The skill preserves its reports, comparisons and plan while preparing the PR. After merge, the publisher reads the approved versions from the merged source; it does not need the agent’s local working files.
Publication has a separate request that survives retries. That is the purpose of the publication manifest.
Deliver the release that was requested
A merged version change is only the start of delivery. The crate upload might succeed while a tag or one platform’s binary archive fails. Publication must be able to finish that release later, even if another PR has already merged.
That is why the publisher separates what was requested from what completed.
One request survives every retry
A publication manifest records the merged source commit, destination and exact package versions to deliver. It does not choose versions or contain mutable completed flags. It is the stable request shared by the publishing phases.
For example, a run requests widget-cli 2.0.1. Its crate reaches crates.io, but a
binary build fails. While the prerequisite is repaired, 2.0.2 merges. Retrying
the original manifest still completes 2.0.1; reading the current branch instead
would silently change the request.
flowchart LR
A["Merged source: widget-cli 2.0.1"] --> B["Publication manifest"]
B --> C["Upload crate"]
C --> D["Create package tag and release"]
D --> E["Deliver binary archives"]
E --> F["Complete release"]
E -. "Retry missing work with the same manifest" .-> B
Each phase observes the actual destination and skips work already complete. Existing registry versions and tags are not rewritten to make a retry pass. Changing the requested source or versions requires a new manifest.
Outcomes explain partial completion
A publication outcome records what happened during one phase attempt. It is kept separately from the manifest so a later attempt does not erase the original request or earlier diagnostics.
The final report combines these outcomes with the current workflow job results. An old successful artifact cannot hide a newly failed job, and an unreadable destination is not treated as empty. This gives the operator a specific missing step to repair rather than a misleading overall success.
Tags fix the source of binary builds
The publication source is the merged commit that requested the release. A package tag identifies the source used to build that package’s binary archives. It may name a later release-branch commit only when the package’s version and released content are unchanged there.
Tags use {package}-v{version} and are never moved. Binaries build from the commit
the actual tag resolves to, not whichever checkout happens to be current.
This matters when repairing an old release: rebuilding from today’s branch could
put different code inside an archive still labelled 2.0.1.
Platform batches organize missing binary work
A platform batch lists the incomplete binary releases for one native target, with their executable names and tag commits. It lets a runner reuse compatible Cargo artifacts while building each package separately with its own default feature selection.
For example, a release may already have its Windows archive but still lack Linux assets. Reconciliation creates only the required remaining work. A target is complete when both its ZIP and checksum are present; an incomplete pair is replaced together so a new checksum cannot accidentally describe an old archive.
Package and executable names can differ:
package: widget-cli 2.0.1
executable: widget
tag: widget-cli-v2.0.1
ZIP member: widget.exe on Windows
The artifact reference describes the transported manifest, batch and outcome fields. Consumers use tool-produced artifacts rather than constructing them manually.
A failed phase is not a rollback
Successful uploads remain valid when a later step fails. Cargo verifies package archives and orders missing uploads; per-upload OIDC credentials avoid relying on a stored publishing secret.
If another version reaches the release branch before an older request can create its tag, that request may need an operator to create the exact missing tag at its recorded source. Independent releases can continue, but the incomplete workflow remains failed. Recovery explains how to finish the original request without relabelling newer content.
Install and inspect
Adoption starts with a known tool interface and a repository whose release history is understood. You do not need to clone the tool’s source repository.
Select a published version
Choose a tested published action revision whose tool supports the schemas documented in this book and copied skill. Check the selected action release and the crate’s published versions. Confirm that the exact package and its promised native archives are published before adoption. Source-mode tests are not evidence of published availability, and a missing release is not a reason to substitute an older incompatible tool.
The command examples target the tested PowerShell 7.6 support baseline, independently
of the application’s CLI portability. This is a support policy, not a claim that
every example uses a newly introduced language feature. Multi-step procedures explicitly
enable terminating native-command errors; the PowerShell version alone does not
enable that behavior. Join-Path keeps filesystem arguments native on Windows,
Linux and macOS. Use one installation method:
$CrpVersion = "<published-version>"
cargo binstall "cargo-release-plan@$CrpVersion" --locked
Or install the published source with its lockfile:
$CrpVersion = "<published-version>"
cargo install cargo-release-plan --version "=$CrpVersion" --locked
Replace <published-version> with the exact tool version selected by that action.
Binstall uses a prebuilt executable where available and can fall back to source.
A successful fallback is useful installation behavior, but is not proof that a
promised release archive exists.
Verify the executable you will actually invoke:
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
cargo release-plan version
cargo release-plan --help
version prints the application’s version and supported schema revisions as JSON,
without a Cargo workspace or Git repository. The short --version identity remains
available for installation probes.
The matching skill uses report/plan/prepared schema 8, semantic-decision schema 2,
compatibility schema 3, and release-context schema 2. Exact skill/tool package
version synchronization is unnecessary; check those schemas. If unexpected CLI
errors suggest a mismatch, consider upgrading both tool and skill.
Separate the toolchains
Installing the application from source requires a compiler supported by that
application release. Your consumer repository may use a different compiler.
Select the installation toolchain explicitly when a local rust-toolchain.toml
would otherwise select an older one.
Source installation also needs a native C/C++ build toolchain and CMake for the application’s dependencies. Installing a matching prebuilt binary avoids that compilation requirement.
Registry upload operations require Cargo 1.95 or later for the workspace publication behavior used by the tool. This runtime requirement is distinct from the compiler needed to build the application. Source verification and tagged binary builds must also satisfy the source’s own toolchain and native build requirements.
The published action pins the application and API compatibility checker versions and prepares their installation separately from consumer builds. Binary sources continue to supply their own toolchain, Cargo configuration and lockfile.
Native binary publication uses Git, Cargo, rustup and the GitHub CLI. ZIP creation and SHA-256 hashing are included in the application; no separate archiver needs installation on the runner or a direct CLI user’s machine.
Inspect your workspace before editing it
Run from the Cargo workspace root with Git and Cargo available:
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
git status --short
cargo metadata --no-deps --format-version 1
cargo release-plan --version
Establish:
- Which branch actually releases, and whether full first-parent history exists.
- Which tracked members are publishable, private implementation packages or non-publishable packages.
- Which exact dependencies intentionally form version groups.
- Which packages contain an installable binary and what each executable is named.
- Whether the committed lockfile and toolchain describe a reproducible source build.
- Whether current registry versions and existing tags correspond to known publication source.
Confirming registry versions and tags correspond to known publication source needs an existing-repository adoption audit, not just matching version strings.
Use an ignored, workspace-local directory for evidence, such as
.release-plan-work. Keep each assessment’s inputs and outputs separate. Do not
commit generated plans as an alternative source of version truth.
Continue with repository configuration.
Configure your repository
This walkthrough uses example/widgets, a placeholder GitHub repository.
Replace it with your repository and select the branch that actually releases.
The TOML fragments below belong in existing manifests; they are not complete
standalone Cargo projects.
Workspace inputs
Track the workspace and package manifests, publication configuration, lockfile and build-toolchain selection. Prepare a consistent lockfile before collecting release evidence. Publication verifies the committed resolution rather than repairing it.
Choose package inclusion rules using the released-content model. For example:
# Workspace Cargo.toml
[workspace.package]
include = ["src/**/*"]
# packages/widget/Cargo.toml, within [package]
include.workspace = true
Packages needing additional compile-time inputs declare a complete allow-list of their own.
Declare groups and consumer contracts
The library uses its exact-version implementation:
# packages/widget/Cargo.toml
[dependencies]
widget_impl = { path = "../widget_impl", version = "=1.4.0" }
[package.metadata.cargo_check_external_types]
allowed_external_types = ["widget_impl::*"]
The implementation is published so Cargo can resolve it, but does not promise an independently supported API:
# packages/widget_impl/Cargo.toml
[package.metadata.release-plan]
private-api = true
A helper can share alignment without being published:
# packages/widget-fixtures/Cargo.toml
[package]
name = "widget-fixtures"
version = "1.4.0"
publish = false
[dependencies]
widget_impl = { path = "../widget_impl", version = "=1.4.0" }
The exact edges join all these members. Do not add a separate workspace group
table. An ordinary version = "1.4.0" requirement names the same declared
version but does not create an exact-version group.
Verify external-type exposure
Every library’s allow-list describes types outside that crate which its public API may expose. Use defining crate paths, including implementation paths reached through re-exports. A crate-level glob is useful for your own implementation partition; list other external types narrowly so accidental exposure remains visible.
An omitted allow-list declares no external exposure; it does not mean exposure
is unknown. cargo-release-plan check reads declarations but does not compile
and verify them. Adopt
cargo-check-external-types
as a required API-validation gate.
Pin the checker and its matching rustdoc nightly together. The revision below
provides the workspace and multi-target checking required by this gate. Select
a published release only when it supports those operations; take the matching nightly from its
rust-toolchain.toml, independently of your application’s compiler.
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
$ExternalTypesRevision = "705a0941997ebeb3bfb1a7f14070ba342f79d879"
$ExternalTypesNightly = "nightly-2026-03-20"
rustup toolchain install $ExternalTypesNightly --profile minimal
cargo install cargo-check-external-types --locked --force `
--git https://github.com/awslabs/cargo-check-external-types `
--rev $ExternalTypesRevision
cargo "+$ExternalTypesNightly" check-external-types --all-features --skip-unsupported `
--manifest-path Cargo.toml
The explicit reinstall establishes the selected revision even when another build has the same package version; it costs a rebuild rather than relying on version-only reuse. An automated installer can avoid that cost by verifying Cargo’s recorded source revision before deciding whether to reinstall.
The workspace invocation checks each eligible library using its own allow-list, including implementation packages. A dynamically discovered package subset is an optional CI optimization, not a manually maintained library list. Cover the relevant feature and platform surfaces; an all-features run on Linux cannot prove a Windows-only signature. Review unused entries in context because some are needed only on another target. Avoid overlapping patterns.
Feed these checks into your required status alongside tests, builds and release checks. A successful version check cannot substitute for this prerequisite.
Configure publication
Commit .cargo/release_plan.toml at the selected workspace root:
schema-version = 1
repository = "example/widgets"
release-branch = "main"
targets = [
"x86_64-unknown-linux-gnu",
"x86_64-pc-windows-msvc",
]
This example selects native Linux and Windows x64 binaries. A library-only
workspace can use targets = []. The full supported target set and precedence
are in the configuration reference.
--config is workspace-relative, even when invoking from elsewhere. For a
nested workspace:
$WorkspaceManifest = Join-Path "components" "widgets" "Cargo.toml"
$Configuration = Join-Path ".cargo" "release_plan.toml"
cargo release-plan check --manifest-path $WorkspaceManifest --config $Configuration
This reads components\widgets\.cargo\release_plan.toml. Artifact paths inside
publication files are instead repository-relative so different jobs can transport
them without sharing absolute checkout paths.
Describe the binary archive
widget-cli is the package; widget is its executable:
# packages/widget-cli/Cargo.toml
[package]
name = "widget-cli"
version = "2.0.0"
repository = "https://github.com/example/widgets"
[[bin]]
name = "widget"
path = "src/main.rs"
[dependencies]
widget = { path = "../widget", version = "1.4.0" }
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/{ name }-v{ version }/{ name }-v{ version }-{ target }.zip"
bin-dir = "{ bin }{ binary-ext }"
pkg-fmt = "zip"
Binary packages must declare one executable that builds with Cargo’s default
features. Check that selection in your own build validation; the offline release
check does not resolve features or compile. Publication uses the explicit native
Cargo build to check feature requirements and buildability.
Repository and binstall layout must match publication configuration.
Package release-targets can narrow the workspace selection:
[package.metadata.release-plan]
release-targets = ["x86_64-pc-windows-msvc"]
Use this only for a genuinely restricted package. Binary packages must retain at least one selected target.
Validate the inputs without querying a registry:
cargo release-plan check --config (Join-Path ".cargo" "release_plan.toml")
Configuration changes are often released manifest changes too. Continue with local planning rather than fixing a failing version check by hand-incrementing isolated packages.
Plan versions with an agent
The supported version-planning interface is the increment-versions skill.
The CLI supplies the operations that the skill executes; manually assembling a
different command sequence is not a supported planning workflow.
The skill prepares a PR containing the source changes, version changes and an explanation of the complete release. Review approves that contribution together. There is no separate prompt to approve each version choice.
Install the complete skill
Copy the complete
skill directory
from a known toolkit revision into .github/skills/increment-versions in your
repository. Include its command reference, decision guide and license.
Record the source revision in your repository’s adoption notes. The skill checks the schemas reported by the installed tool before editing source; it does not require matching package version numbers. On a mismatch, update the skill from the canonical repository. When developing that canonical skill, report the mismatch rather than overwriting it.
The agent needs Git, the selected Cargo/Rust toolchain, cargo-release-plan and
cargo-semver-checks. Tool installation and upgrades follow your authorization
policy. A private configured repository also needs authenticated history access.
Tell the agent what to assess
For configured publication, record the release branch and required checks in the
repository’s agent instructions and provide .cargo/release_plan.toml. Then ask,
for example:
Run increment-versions for this PR, assess the complete release impact, apply the resulting plan and update the PR’s Version/release plan.
For a stacked PR, identify the actual parent branch or PR. The skill must preserve the distinction between actual release history and the parent’s anticipated final release, rather than silently using the release branch as the child’s target.
Tracked files may have staged or unstaged edits. The agent does not need a clean checkout or a prior commit to assess them. Newly created intended release inputs must be tracked before the assessment.
For one-off planning without publishing configuration, explicitly request standalone mode.
What the skill produces
The skill reads the complete release report and API comparisons, chooses each package’s semantic impact, previews group and dependency effects, and applies the resolved plan. It retains reports and supporting files outside published source so it can verify that the applied result is the one it assessed.
The PR explains:
- Which package versions change and why.
- Group alignment and dependent-package changes, not only directly edited code.
- Existing pending increments retained because they are sufficient.
- Any unavailable API comparison or other unresolved prerequisite.
The API checker establishes only a minimum impact from the contracts it can see. The agent also considers behavior, CLI arguments, data formats and feature availability. Human review remains necessary.
Keep the result tied to the reviewed source
If source, release history or the parent changes, have the skill reassess. Do not edit generated plans or reuse a report from a different checkout as permission to apply changes. Repeating the skill retains an adequate pending increment; it does not increase versions solely because it ran again.
Configured mode includes publication-readiness checks and the first-publication handoff when required. Standalone mode stops at its version-increment PR. Neither invocation grants merge or publication authority by itself.
Add GitHub checks
The standard reusable check workflow is
folo-rs/cargo-release-plan-action/.github/workflows/check.yml.
Select a tested published release from the
action repository and
replace ACTION_REVISION below with its verified immutable commit. Use that same
commit for every action/workflow example in this book; ACTION_REVISION is a
placeholder, not an existing release or a GitHub Actions variable.
Add a caller such as .github/workflows/release-checks.yml:
name: Release checks
on:
pull_request:
merge_group:
permissions:
contents: read
actions: read
jobs:
release-check:
uses: folo-rs/cargo-release-plan-action/.github/workflows/check.yml@ACTION_REVISION
with:
working-directory: .
config: .cargo/release_plan.toml
install-method: binstall
source-path: .
These inputs show their defaults. working-directory selects the consumer
workspace; config is relative to it. source-path selects tool source only
with install-method: path, not the consumer workspace for released
installation. The reusable workflow and its internal composite use the same
immutable action revision.
For a custom graph, the root composite provides individual operations. Its installation and configuration inputs are described under custom jobs. A version-readiness operation is deliberately narrower than the complete PR gate.
Required check coverage
A complete release gate combines:
- Workspace-wide version readiness, including packages not directly edited.
- Publication-input validation using committed configuration.
- External compatibility evidence for the selected public library contracts.
- Consumer-owned external-type exposure checks and ordinary build/test checks.
Do not scope offline version readiness to a changed-package list. It must find unversioned released content anywhere in the workspace, including inherited inputs and effects carried from an earlier contribution.
The core offline invocation uses the pair returned by release-context:
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
$AssessmentArguments = @("--release-history", $Context.release_history)
if ($Context.merge_target) {
$AssessmentArguments += @("--merge-target", $Context.merge_target)
}
cargo release-plan check @AssessmentArguments `
--config (Join-Path ".cargo" "release_plan.toml") --format github
This command alone is not a replacement for external compatibility checking.
--format github adds annotations; it does not change the release rules.
The equivalent compatibility gate uses fresh bound evidence and rejects insufficient increments:
cargo release-plan check-compatibility @AssessmentArguments `
--output (Join-Path ".release-plan-work" "ci-compatibility") --deny-findings
Use a new output directory. check-compatibility acquires a read-only report
from source checked at command entry. Callers and tools invoked for read-only
work must keep assessed source, configuration and history unchanged during
comparison. It does not accept a detached report as permission to check another
checkout.
The compatibility stage must distinguish a valid empty selection from a broken checker or unavailable comparison. Run the checker on the assessed source with the supported feature selection, and retain diagnostics. All-features checking does not remove the author’s feature-subset and behavioral review obligations.
Fetch and select the correct history
Use a full-history checkout. Resolve actual release history separately from any anticipated target:
| Event | History and target |
|---|---|
| PR | Fetch actual configured release history; pass pull_request.base.sha as the proposed merge target. |
| Merge queue | Keep actual release history separate; the supplied integration base is a proposed target, not independently established history. |
| Pinned release-source check | Explicitly select that known release-branch commit as history; no anticipated target. |
| Other push, scheduled or manual check | Let context acquire configured release history rather than assuming the tested HEAD is already released. |
Fetch the required base-repository history for fork PRs too. A similarly named branch in the fork is not a substitute.
For a custom graph, release-context --config <path> fetches the configured
release branch and returns release_history plus nullable merge_target.
Supply --merge-target <target-commit> for a PR or integration target. Context
normalizes a target already in release history to null. Pass the returned pair
unchanged to check and compatibility. Use --release-history <commit> only when
the caller already knows the actual release-history boundary.
A repository can keep a deliberately narrow queue gate using the lower version-readiness operation and the candidate’s history/target pair. That does not remove the full compatibility and publication-input checks from ordinary PR validation.
Protect the branch
Configure squash merging and branch protection for the chosen release branch. Each PR’s target-aware checks establish its own version decisions; merge queue grouping does not replace those checks. Require a stable status that represents all merge-blocking work.
A final aggregation job is useful for dynamic matrices: it succeeds only when required prerequisites succeeded or were deliberately not applicable. Failure, cancellation, missing results and an unexpectedly skipped unconditional gate do not count as success.
Require the status on both PR and queue events. A queue check whose workflow never runs cannot establish readiness. Choose your own stable check name; no particular Folo job name is required.
Keep checks read-only
PR checking needs source/history access, not publication credentials, write permission or OIDC authority. Follow the repository’s normal approval policy for fork workflow runs. An unapproved or policy-disallowed check is not an executed pass.
Do not use privileged pull_request_target execution of contributor source as
an adoption shortcut. Mutation privileges belong only in the separately gated
publication workflow.
Before making the check required, exercise a passing change, an intentionally missing increment, a group/dependency violation and the queue event. Confirm the external-type gate covers your supported public API surfaces and that API compatibility checker execution failures remain failures in the final status.
Connect publication
Publication starts from clean merged source under your repository’s release policy. Its preparation step is automatic execution, not another human version approval gate.
The standard reusable flow is
folo-rs/cargo-release-plan-action/.github/workflows/release.yml.
Select a tested published action release and replace ACTION_REVISION with the
same verified immutable commit used for release checks.
Keep registry, GitHub reconciliation, platform batches and reporting in the same
workflow run. Tag and release events created using the workflow’s GITHUB_TOKEN
do not trigger downstream workflows.
Add the release caller
For the running example, use .github/workflows/release.yml:
name: Release
on:
push:
branches: [main]
workflow_dispatch:
inputs:
source:
description: Optional full original publication-source commit for recovery
type: string
default: ''
verify-publishing-identity:
description: Verify OIDC exchange and revocation without publication
type: boolean
default: false
permissions: {}
jobs:
release:
if: github.repository == 'example/widgets' && !inputs.verify-publishing-identity
permissions:
contents: write
actions: read
id-token: write
issues: write
uses: folo-rs/cargo-release-plan-action/.github/workflows/release.yml@ACTION_REVISION
with:
source: ${{ inputs.source }}
identity:
if: github.repository == 'example/widgets' && github.event_name == 'workflow_dispatch' && inputs.verify-publishing-identity
permissions:
contents: read
actions: read
id-token: write
uses: folo-rs/cargo-release-plan-action/.github/workflows/identity-probe.yml@ACTION_REVISION
Replace example/widgets and the example main branch with the configuration
for your repository. The shared flow also validates the destination and release
branch before writes. The caller grants the scopes the flow needs; individual
jobs narrow them.
Omitted inputs use the shared workflow defaults. For a protected environment, add the same setting to both jobs and register that environment with crates.io:
with:
publishing-environment: release
For path installation, add install-method: path and select the tool checkout
with source-path. Released installation does not use source-path.
An empty source uses the invocation’s commit. For explicit recovery, supply the
full original source SHA; the tool still verifies its release-branch membership.
This is a new invocation with new evidence, never an automatic fallback when an
old artifact is missing. The source override selects the publication source only.
The controller is the cargo-release-plan executable run by the workflow.
In source-install mode, source-path selects its source from the invocation
checkout, so recovering old publication source does not rebuild an old controller.
Establish publishing identity
Every package must already exist on crates.io before automatic publication. Complete first publication before a new package’s first merge.
Register each package’s crates.io Trusted Publisher with:
- Your GitHub repository owner and repository name.
- Your calling entry workflow filename, including when it calls a reusable workflow from the action repository.
- The publishing environment, if you use a protected GitHub environment.
The called action repository is not the publisher identity. The job exchanging OIDC credentials must use the environment registered for that caller. See GitHub’s reusable-workflow OIDC documentation.
Use phase-specific permissions. Registry publication needs source read access and
id-token: write; GitHub tag, release and asset operations need repository
content writes; failure-issue reporting needs issue writes. Grant only the scopes
required by each job, including any read access the selected workflow needs for
artifact transport and action-revision identification.
Do not provision a registry PAT or stored-token fallback. The application acquires short-lived credentials through OIDC for Cargo’s initial credential preflight and independently for each upload after verification. Preflight credentials are not reused for uploads. Cleanup attempts to revoke every issued credential and reports failures. Package verification shares the publication job’s trust boundary, including its job-level OIDC identity. Run only reviewed repository code and trusted build dependencies in that job; credential handling is not a sandbox against code running under the same account.
Verify identity without publishing
The permanent caller above includes a mutually exclusive identity-only mode. Dispatch it through the same registered entry workflow filename:
gh workflow run release.yml --repo example/widgets --ref main `
--field verify-publishing-identity=true
Run this before enabling routine publication and whenever the registered caller
or environment changes. The reusable probe checks OIDC exchange and revocation
without a Cargo workspace or publication; it has no working-directory or
config input. It uses the same action commit and environment as publication.
Do not move the probe to a differently named caller or replace the production file
to run it.
Its CLI operation is:
cargo release-plan check-publishing-identity
Run this in the authorized GitHub Actions OIDC context. It exchanges and immediately revokes a temporary credential, uploading nothing. Success exercises the caller identity path, not every package’s Trusted Publisher grant; verify package administration separately.
Capture the merged source
These commands demonstrate the CLI boundaries independently of workflow filenames. Run preparation from a clean checkout of the selected merged commit:
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
$Source = (git rev-parse HEAD).Trim()
$Work = ".release-plan-work"
$Publication = Join-Path $Work "publication.json"
$Outcomes = Join-Path $Work "outcomes"
cargo release-plan prepare-publish --source $Source `
--config (Join-Path ".cargo" "release_plan.toml") --output $Publication
$Source must be the full commit ID. Preparation verifies HEAD, clean source,
tracked Cargo inputs and first-parent membership in the
configured release branch. It fetches that branch to establish eligibility; it
does not replace the selected source with the fetched tip.
Preparation preserves the recorded lockfile without resolving dependencies.
Cargo’s publication and native build commands use --locked.
Keep output in an ignored artifact directory or outside tracked source. Preparation writes no registry package, tag or release. Repeating identical intent may reuse the manifest; different intent needs a different destination.
In CI, prepare this manifest once and retain it for every subsequent phase and retry. A failed-job rerun downloads original intent rather than regenerating it from the current release-branch tip.
Inspect registry work without uploading
cargo release-plan publish registry --publication $Publication `
--output (Join-Path $Work "registry-dry-run-1" "outcome.json") --dry-run
The dry run queries exact-version availability without exchanging publication credentials or uploading. Its outcome is never marked complete, even if every requested version already exists.
The following invocation performs live registry work and belongs only in the authorized publishing job, not tests or the local version-planning skill:
cargo release-plan publish registry --publication $Publication `
--output (Join-Path $Outcomes "registry-attempt-1" "outcome.json")
Use a new outcome path on every attempt. Naming each live phase file
outcome.json inside its own artifact subdirectory lets the final reporter
discover it. Keep local dry-run and nonpublishing verification output separate
from those publication outcomes. When the workspace is nested or the source
checkout moved between jobs, --manifest-path must select the original
publication-source workspace. Do not substitute a prospective planning workspace
or later source commit.
The Cargo runtime requirement supports locked package verification and dependency-ordered workspace uploads. Cargo owns normalization and hashing of the package bytes it uploads. Neither a failed upload command nor an old successful outcome replaces a fresh exact-version observation.
Continue through GitHub and native jobs
After successful registry reconciliation, inspect GitHub work without writes:
cargo release-plan publish github --publication $Publication `
--output (Join-Path $Work "github-dry-run-1" "outcome.json") `
--batches (Join-Path $Work "batches-dry-run-1") --dry-run
A dry run is not completed delivery and cannot establish missing tags for native builds. The authorized GitHub job performs live reconciliation:
$Batches = Join-Path $Work "batches-attempt-1"
$GithubOutcome = Join-Path $Outcomes "github-attempt-1" "outcome.json"
cargo release-plan publish github --publication $Publication `
--output $GithubOutcome --batches $Batches
The outcome supplies relative paths and batch_id values for frozen platform
batches. Both the outcome path and batch directory must be new. For a nested
workspace, pass --manifest-path for the original publication source, just as
in registry publication.
Do not derive binary work from “packages uploaded in this attempt.” A registry no-op can still require a missing tag, release or checksum.
A failure to tag one release does not suppress valid work for another. Valid manifest-linked batches can continue after registry prerequisites succeed, while the reconciliation failure remains a workflow failure. Missing batch artifacts, invalid identities or cancellation do not authorize downstream work.
On the matching native runner, select the emitted batch for its target. This example assumes the outcome includes Windows x64 work:
Choose an absolute shared Cargo build-cache location outside the clean source,
or beneath an ignored directory. This preserves source cleanliness in repositories
that do not ignore Cargo’s default target directory:
$env:CARGO_TARGET_DIR = [IO.Path]::GetFullPath((Join-Path $Work "build-cache"))
$Routing = Get-Content $GithubOutcome -Raw | ConvertFrom-Json
$Selected = $Routing.batches | Where-Object target -eq "x86_64-pc-windows-msvc"
if ($null -eq $Selected) {
throw "This outcome contains no Windows x64 batch."
}
$Batch = Join-Path $Batches $Selected.path
cargo release-plan publish binaries --publication $Publication --batch $Batch `
--output (Join-Path $Outcomes "windows-attempt-1" "outcome.json") `
--artifacts (Join-Path $Work "windows-artifacts-attempt-1")
The tool rechecks tag identities and assets, then builds each executable
separately from its actual peeled tag commit. To stage the frozen batch without
tag/release/asset queries or uploads, use --no-upload with separate destinations:
cargo release-plan publish binaries --publication $Publication --batch $Batch `
--output (Join-Path $Work "windows-verification-1" "outcome.json") `
--artifacts (Join-Path $Work "windows-verification-artifacts-1") --no-upload
This mode still verifies source and archives. It may fetch an unavailable source commit from the configured repository, including using repository-read authentication. It does not discover a batch, select substitute source or produce a completed publication outcome.
Report every attempt
The final reporting job downloads each phase’s artifact subdirectory, retaining
its outcome.json, and supplies current job results as jobs.json. The fixed
keys are prepare, registry, github and binaries; values come from GitHub’s
job results, including the binary matrix’s aggregate result:
{
"prepare": "success",
"registry": "success",
"github": "failure",
"binaries": "skipped"
}
This is a format example, not values to hardcode into a real report. Run the reporter even after failed phases:
cargo release-plan publish report --repository example/widgets `
--publication $Publication --outcomes $Outcomes `
--jobs (Join-Path $Work "jobs.json") `
--output (Join-Path $Work "publication-report-attempt-1.md")
The command requires the GitHub run context, writes the Markdown report even for
incomplete delivery, and exits nonzero when work remains. It creates or updates
the run-qualified failure issue with exact operator recovery instructions.
--no-issue disables issue writes but does not remove the run-context
requirement or turn an incomplete report into success.
If the publication manifest is unavailable, omit --publication. The reporter
still writes a report and failure issue; missing intent is always incomplete,
never a successful empty release.
Outcomes carry optional github: {run_id, run_attempt} metadata when produced in
GitHub Actions. Preserve it so the reporter can enforce
outcome identity and freshness.
Prepare native build environments
Configure the subset your packages support from the supported native targets. The action revision owns the corresponding runner mapping.
Provide native libraries or other source-build prerequisites through the fixed
optional .github/actions/release-plan-setup/action.yml. It prepares the
environment in Cargo verification and binary-build jobs, not GitHub-only
reconciliation jobs, and must not edit captured source.
The action’s controller installation, the invocation checkout and each tagged binary source are distinct. A historical tag need not contain the current controller or current publication configuration.
Queue and retain, without hiding failures
Serialize publication runs for the same repository, release branch and workspace
with non-cancelling queued concurrency (queue: max). Disable matrix fail-fast
so independent native targets can finish. Queue capacity and platform
cancellation still need operational attention.
Choose artifact retention long enough for operator recovery. Preserve the manifest, frozen batches, every attempt outcome and diagnostics, including on failure. Use run/attempt-qualified artifact names from the selected action interface rather than overwriting intent.
Queuing publication does not prevent a newer version from merging. For superseded-version tag failures, follow the original-source manual-tag recovery.
An ordinary release
Once the packages, Trusted Publishers and shared workflows are configured, an ordinary release is part of the PR process. For a new package, complete first publication before its first merge.
Make the change and invoke the skill
Develop the change with its tests and public documentation, then ask the agent:
Run increment-versions for this PR and update its Version/release plan.
The installed skill handles compatibility assessment, version selection, dependent-package changes and application of the plan. You do not need to choose tool commands, edit generated planning files or assemble the release table yourself.
Review the proposed release
Review the source changes and the skill’s generated Version/release plan
together. For a compatible operation added to widget, the table might contain:
| Package or group | Previous version | Proposed version | Reason |
|---|---|---|---|
widget, widget_impl, widget-fixtures | 1.4.0 | 1.5.0 | Compatible public operation; implementation and fixture helper align with the group. The helper is not published. |
widget-cli | 2.0.0 | 2.0.1 | Dependency update without a stronger CLI change; its existing pending patch increment is sufficient. |
The review question is whether those reasons accurately describe the change. For example, removing or changing an existing operation’s promised behavior may require a breaking release instead. A dependent package’s movement should have a clear explanation rather than an invented user-facing feature.
If the table’s reasoning does not match the source, ask the agent to correct the assessment and rerun the skill. If source or release history changes during review, have it refresh the plan rather than editing version numbers or generated artifacts by hand.
Merge under normal repository policy
Review and the required checks approve the source and version changes together. Invoking the skill is not itself merge authorization.
After an authorized merge, the shared workflow publishes the requested packages, tags, releases and native archives automatically. Successful publication needs no separate manual delivery audit.
If something goes wrong, the release workflow fails and its reporter creates or updates a failure issue. Follow failure recovery for the safe retry or repair action instead of starting another version increment by default.
Recover a failed release
You do not need to inspect tags, archives or registry entries after every successful release. The shared workflow owns those checks. Use this chapter when GitHub reports a failure and you need to decide what to retry or repair.
Find out what failed
Before merge, a failed PR check appears on the pull request. Fix the reported problem before merging; these checks do not start publication.
After merge, an incomplete release marks its workflow and affected commit checks as failed. The final reporter creates or updates an issue titled Release failed: workflow run …, linking the run and describing incomplete packages and any required operator action. A failed retry updates that same issue rather than creating a new issue for each attempt.
Start with the issue’s instructions and the failed job’s logs. If there is no issue, open the failed or cancelled run in the repository’s Actions tab. Cancellation, startup failure or missing reporting permissions can prevent an issue from being created; its absence does not mean the release succeeded.
Choose the recovery action
| Reported problem | What to do |
|---|---|
| Temporary network, registry or runner failure | Wait for the service to recover, then rerun the original failed jobs. |
| Publishing permission or Trusted Publisher registration is wrong | Have a maintainer correct the setting, then retry. If the fix changes workflow code, use a new recovery run as described below. |
| A native build prerequisite is unavailable | Repair the build environment. Retry the original run if it can use that repair; use a new recovery run if updated automation is required. |
| A ZIP or checksum upload is incomplete | Rerun the original failed jobs. The publisher completes missing work and repairs an incomplete pair together. |
| The failure issue names a missing tag after another version merged | Create that exact tag at the recorded source, then retry the original failed jobs. |
| Required artifacts expired, or the publisher needs corrected automation | Start a new recovery run for the original source. Do not reconstruct or edit the old artifacts. |
| The released package’s source is defective | Correct it in a new PR, run the skill and release a new version. Retrying unchanged source will not fix it. |
| An existing tag or uploaded version refers to unexpected content | Stop automatic retries and investigate. Do not move the tag or overwrite the version to make the run green. |
Retry the original run when its request is still correct
In Actions, open the original failed release run and choose Re-run failed jobs after correcting the cause. Rerunning the whole original workflow is also safe when that is necessary: it still requests the original source and versions.
Already published versions, established tags and complete archive pairs are preserved. Retrying an upload failure therefore does not normally need a new package version. Independent packages may have completed even though the overall run failed.
A retry does not update the run to newer source or newer workflow code. If that is what the repair requires, choose the appropriate path below rather than repeatedly rerunning the same failing inputs.
When a retry succeeds, close its failure issue with a link to the successful attempt. Successful retries do not automatically close those issues.
Missing tag after a newer version merges
For example, the run for widget-cli 2.0.1 can finish its crate upload after
2.0.2 has already merged. The old run still needs the 2.0.1 tag, but cannot
automatically tag the newer source as that version.
The failure issue identifies the exact tag and original source commit. An authorized maintainer must create that tag at the recorded commit, not at the current branch tip. Do not move an existing tag.
These commands are for that specific manual repair. Replace the example values with those from the failure issue:
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
$Repository = "example/widgets"
$Tag = "widget-cli-v2.0.1"
$Source = "<original-source-SHA-from-the-failure-issue>"
$RepositoryUrl = "https://github.com/$Repository.git"
# Inspect the recorded source and check whether the tag already exists.
git fetch $RepositoryUrl $Source
git show --no-patch --format=fuller $Source
git ls-remote $RepositoryUrl "refs/tags/$Tag" "refs/tags/$Tag^{}"
Only after confirming the intended source and that the tag is absent, create it:
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
git tag --annotate $Tag $Source --message "Release $Tag"
git push $RepositoryUrl "refs/tags/$Tag"
Then use Re-run failed jobs on the original run. It recognizes the created tag and completes the remaining work from that source. If the tag already exists but identifies different content, investigate instead of force-updating it.
Start a new recovery run when retrying is insufficient
If the original artifacts are unavailable or a fix requires newer publishing
automation, open the release workflow in Actions and choose Run workflow.
Select the release branch and set its source input to the full original
publication-source commit from the failure issue or failed run.
This uses the release branch’s current automation to create a new publication request for that original source and its configuration. It observes existing uploads and completes what is still missing. It does not recreate the old outcomes or turn the earlier failed run into a successful attempt. If the original source cannot be restored, do not substitute different content under the same versions; prepare a new release instead.
The caller needs the recovery input shown in Connect publication. If your caller does not expose it, have a maintainer update the caller rather than assembling publication commands or editing artifacts manually.
If the original publication configuration itself was wrong, selecting the same source also selects that old configuration. Correct the configuration through a reviewed PR and let its normal release run issue a new request instead. Do not edit an existing publication manifest to change its targets or destination.
After a successful replacement run, close the original failure issue with a link and explain that the new run supersedes it. The original failed run remains an accurate record of the request it could not complete.
When a new version is necessary
Create a forward release when the repair changes released package content or
when an occupied version cannot describe the intended code. Ask the agent to
make the correction and run increment-versions; review its proposed versions
normally.
A transient service failure, missing archive, permission repair or automation-only correction does not by itself require new package versions. The skill determines whether a proposed source/configuration correction actually changes released content. A newer successful release does not retroactively repair an older defective one; record that the old failure is superseded rather than retrying it indefinitely.
Release-branch movement
If a PR’s version check fails because release history or its parent advanced,
ask the agent to refresh its branch and rerun increment-versions. It preserves
your source changes, reassesses against the new history and updates the release
table. Do not compensate by manually increasing stale proposed versions.
First publication and adoption
The normal automatic path assumes established crates.io packages and a release history that corresponds to their content. Introducing a new package and adopting an existing repository need explicit maintainer setup.
First publication happens before the first merge
crates.io Trusted Publishing cannot publish a package that does not yet exist, because its publisher registration is configured on that existing package. A maintainer performs the first publication from the feature branch before the package’s first merge.
- Review and validate the new package and its publication dependencies.
- Publish the related bootstrap packages manually, letting Cargo order their dependencies.
- Configure its crates.io Trusted Publisher for the consuming repository’s calling workflow and optional environment.
- Prepare a strictly higher version for the first merge, including exact-group alignment, dependent requirements and consistent lockfiles.
- Update the PR’s release explanation with both versions and the completed maintainer setup.
- Merge under the repository’s normal review policy. This performs the second publication: the first automated release.
For example, if introducing the running example as new packages:
| Package | Manual bootstrap | First merged automated version |
|---|---|---|
widget_impl, widget | 1.4.0 | 1.5.0 |
widget-cli | 2.0.0 | 2.0.1 |
widget-fixtures | Not published | Align to 1.5.0 |
These numbers illustrate the ordering requirement; choose versions appropriate to the actual new packages. The helper needs no registry account or publisher.
The manual operation is ordinary Cargo publication, for example:
cargo publish --package widget_impl --package widget --package widget-cli --registry crates-io --locked
Use the supported Cargo publication runtime and the reviewed, current lockfile. Select the complete related package set; separate invocations are needed only when packages must be handled independently.
It belongs to an authorized maintainer, not tests, the local skill or an unprivileged workflow. Follow crates.io’s supported maintainer authentication procedure; do not add persistent publication credentials to the release automation.
Do not merge the bootstrap version unchanged. Do not rerun a release-branch workflow expecting it to publish source that exists only on a feature branch. For binary packages, the first automated merged version receives its GitHub release and native assets.
Registry preflight is not publisher verification
check-published without a plan discovers missing and unknown packages across
the workspace as an advisory. Before applying a resolved plan,
check-published --plan <resolved-plan.json> validates its targets and requires
an established registry package for every publishable target. Missing or unknown
registry results block that gate; non-publishable packages make no query.
These observations do not verify Trusted Publisher registration. Complete that
setup explicitly before resuming. The separate check-publishing-identity
OIDC exchange/revocation probe tests the calling workflow identity without
uploading; it does not establish every package’s grant either. A package can lack
a package anchor while its manually published bootstrap version already
exists in crates.io.
Adopting an already-published workspace
Before enabling publication, establish correspondence between current versions, their source and existing remote releases:
- Identify the source used to publish each current package version.
- Compare relevant package content and normalized publication inputs where needed, including binary dependency resolution.
- Verify repository ownership of package identities and existing package tags.
- Check that full first-parent history supports the intended anchors.
- Inspect existing binary releases against the configured naming and target contract.
- Run publication-input checks and read-only reconciliation before enabling writes.
Matching version strings or newly imported Git history are not proof of matching released content. Resolve discrepancies through reviewed forward versions rather than rewriting existing registry versions or tags.
When replacing another publisher, keep only one mutating release path active. Preserve the caller workflow filename where existing Trusted Publisher registrations depend on it. Drain or explicitly account for old queued runs; changing a concurrency identity does not coordinate with those old runs.
Source-mode tests, dry runs and a successfully built book do not prove that the published action/tool combination can perform the live path. Complete an authorized acceptance release using the exact pinned combination before relying on it for routine delivery.
Emergency manual publication
When the automated registry path is unavailable, an authorized maintainer can publish the exact reviewed merged versions with Cargo in dependency order. Preserve their source identity and locked-resolution requirements.
Then resume reconciliation for that original source so tags, releases and binary assets complete. Already uploaded versions are skipped. This does not require another version increment solely because automation needs retrying.
Keep emergency access out of the normal automation. The automatic path retains OIDC and its phase-specific permissions.
Upgrade tools, actions and skills
The application, GitHub integration and copied skill have related interfaces but different distribution lifecycles. Upgrade them as a tested combination rather than assuming equal version numbers.
The walkthrough and copied skill use report/plan/prepared schema 8,
semantic-decision schema 2, compatibility schema 3, and release-context schema 2.
cargo release-plan version reports these revisions as JSON.
ACTION_REVISION in workflow examples must be replaced with the verified
immutable commit of a tested published action release selecting that interface.
Use the same commit for the root composite, check, release and identity-probe
workflows. The placeholder is not a claim that an action tag already exists.
Keep an adoption record
Record:
- The immutable action revision.
- Its exact
cargo-release-planand API compatibility checker (cargo-semver-checks) pins. - The immutable source revision of the copied skill directory.
- The source revision of the book used for that combination.
- Consumer-owned external-type checker/nightly pins, libraries, feature selections and platform targets covered by those checks.
The action’s release manifest is authoritative for its installed tools. A published action revision does not accept a silently substituted application version underneath it. Source mode is an explicit, different selection.
The website follows current documentation. To read an older combination, use
GitHub’s tag or commit selector in the
book source directory
and read the Markdown at the recorded immutable revision. Use the same method
for .github/skills/increment-versions. Do not copy the latest skill while
retaining an older unsupported command interface.
Upgrade checklist
- Read the chosen release’s command, configuration and artifact compatibility notes.
- Confirm the exact application package and promised native archives exist.
- Verify the installed executable and schemas with
cargo release-plan version. - Exercise the selected action revision in read-only checks on a representative consumer workspace.
- If the skill’s supported schemas differ, update its complete directory from the canonical repository and record its revision. In the canonical repository, report a mismatch instead of overwriting the skill being developed.
- Verify OIDC exchange/revocation with the identity probe when the caller or publishing environment changes, and confirm package-specific grants separately. Use the permanent release caller’s identity-only mode.
- Regenerate stale local preparation/report/preview evidence rather than editing schema numbers.
- Inspect retained publication runs before changing the tool used for retries.
- Review and merge the coordinated configuration and pin changes.
A moving action major reference is convenient but can resolve to different source on a later invocation. An immutable commit or release tag gives a stable selection, including for full workflow reruns. The reusable workflow’s internal composite also uses that same immutable revision.
Artifact compatibility
Reports and version plans have a schema lifecycle separate from publication manifests and outcomes. Neither an action version nor a package version is an artifact schema number.
Unsupported local-planning formats require new prepare and preview evidence.
Captured file content and input identity are not fields to repair by hand.
For an incomplete publication, prefer the original compatible tool and retained manifest. If that combination cannot be restored, explicitly prepare new evidence from the retained original source under the recovery procedure. Do not replace original intent with current branch content or pretend that a new manifest is an old outcome.
Verify distribution, not just source
An action release must be usable with its exact published pins. Source-mode tests do not establish that a crates.io package exists or that each supported archive installs without fallback.
Test source installation and promised native binary installation separately.
Verify API compatibility checker installation independently; the release action
does not manufacture that tool’s archives. Update the external-type checker
(cargo-check-external-types) and its rustdoc nightly together.
Adding a target requires published tool support, native runner provisioning, archive verification and an assessed action/tool release. It is not a consumer configuration shortcut around the supported target set.
One-off version increments
Use the increment-versions skill in standalone mode when you want a
version-increment PR without adopting publishing integration. The user supplies
the planning inputs instead of release_plan.toml; no publishing configuration,
GitHub remote or workflow installation is required.
The scenario ends with the reviewed version-increment PR being merged under the repository’s normal authorization policy. Publishing after that merge is outside standalone mode. The shared workflow remains the toolkit’s canonical supported publishing path.
Provide inputs, not a publishing configuration
Tell the agent which Cargo workspace to assess, the local ref or commit delimiting release history, and the parent target ref for a stacked PR.
For example:
Run increment-versions in standalone mode for this workspace. Use my local
origin/mainref as release history and no unmerged parent. Prepare the version-increment PR; do not configure or run publication.
Those ref names are your choices, not defaults inferred from a publishing setup. A repository without remotes can use a local branch or commit. If you want a remote-tracking ref refreshed first, say so.
The agent prerequisites still apply. Tracked files may have staged or unstaged changes; newly created intended release inputs must be tracked before assessment.
The same version model, without publication setup
The skill uses the same reports, semantic impacts, version groups, dependency effects and captured-plan checks as configured planning. It supplies your selected history and target through the existing tool inputs; it does not create a dummy publishing configuration.
It does not require registry identities, Trusted Publisher bootstrap or binary archive metadata merely to edit versions. API comparison remains separate supporting information and can still read published crates. An unavailable comparison is not proof of compatibility.
Standalone mode is explicit, not a fallback for bypassing a failed configured check. Existing repository CI and merge-triggered automation remain in force; choosing this planning mode does not disable them.
Review and merge the version PR
Review the code and complete version explanation together, including dependent packages and retained pending versions. If the source or selected refs move, have the skill reassess rather than editing its generated files.
The skill does not grant merge permission. Once the version-increment PR is merged with normal authorization, the standalone workflow is complete.
Custom jobs and source identity
Prefer the reusable workflows for the standard process. Use the root composite when your repository needs a different job arrangement or a supported target on a different native runner. Neither option changes release policy.
Root composite
The root action lives at
folo-rs/cargo-release-plan-action.
It selects an operation with command; the operation determines its additional
inputs. Consult the exact selected revision’s input contract.
The shared inputs are:
| Input | Meaning |
|---|---|
command | Select the supported operation, not arbitrary shell or Cargo arguments. |
working-directory | Select the consumer Cargo workspace. |
config | Select publication configuration relative to that workspace. |
install-method | Choose binstall, install or explicit source mode path. |
source-path | Select the tool source checkout for path installation. |
The root operations include version, version-readiness, check,
prepare-publish and publish-registry. version-readiness is the narrow
offline gate; do not treat it as full compatibility and publication-input
validation.
This step illustrates executable identity checking. Replace ACTION_REVISION
with the same verified immutable commit selected from a tested published action
release for every caller:
- uses: folo-rs/cargo-release-plan-action@ACTION_REVISION
with:
command: version
install-method: binstall
The action version is independent of the application version printed by this
operation. A released revision pins exact tools; binstall permits source
fallback, install builds the exact published source with its lockfile, and
path builds the selected tool source. Path mode must not restore a released
executable cache in place of that source.
Consumers of released tools do not need a Folo checkout. Only deliberate tool development/source-mode testing supplies one.
Shared workflow inputs
The public reusable workflows are .github/workflows/check.yml,
.github/workflows/release.yml and .github/workflows/identity-probe.yml
in the action repository:
| Input | Default | Workflows |
|---|---|---|
working-directory | . | Check and release |
config | .cargo/release_plan.toml | Check and release |
install-method | binstall | All |
source-path | . | All |
publishing-environment | Empty | Release and identity probe |
The identity probe needs neither a Cargo workspace nor configuration. It tests OIDC exchange/revocation in the calling workflow’s identity. Use the registered entry workflow filename and publishing environment, not another caller and an assumption that the identity is equivalent.
The check caller and publication callers show complete examples. Do not add arbitrary per-field release-policy inputs; committed configuration remains the policy source.
Preserve source identities
| Identity | Role |
|---|---|
| Release-history commit | Select actual publishing-branch history for assessment. |
| Merge-target commit | Supply an unmerged parent’s final state as an anticipated squash release. |
| Per-package anchor | Supply that package’s comparison content and version. |
| Prepared source and prospective workspace | Bind semantic evidence and the exact plan application. |
| Publication source | Fix the merged source and declared versions to deliver. |
| Peeled tag commit | Fix the source actually used to build one binary release. |
| Action revision | Fix the reusable workflow implementation and its released tool pins. |
| Controller source in path installation | Select the application built from the invocation checkout. |
Do not substitute one for another. In particular, a source checkout’s
rust-toolchain.toml must not accidentally select an incompatible compiler for
installing the controller. Conversely, installing a newer controller does not
authorize building a historical binary with today’s source and lockfile.
Tagged builds run with the tag’s source as their working directory, not merely
a different --manifest-path while inheriting another checkout’s Cargo
configuration. Standard builds use release mode, locked dependencies, the
selected native target and default features.
Own the orchestration, not another release model
A custom graph must preserve these boundaries:
- Prepare immutable intent once; pass it unchanged to later phases.
- Require registry completion and fresh prerequisite validation before GitHub writes.
- Admit native work only from valid manifest-linked batches with established tag identities.
- Continue independent valid releases after a per-release failure while keeping the aggregate run failed.
- Keep credentials phase-local and out of native source-build subprocesses. Registry verification shares the trusted publication job’s identity.
- Retain input artifacts and attempt-specific diagnostics on failure.
- Treat missing artifacts as errors and retry original intent.
Do not hand-maintain package lists, derive upload order from semantic assessment batches or synthesize platform-batch JSON. The tool owns selection and artifact validation; Cargo owns registry ordering.
Use the fixed optional .github/actions/release-plan-setup/action.yml only to
prepare native build requirements. It is not an arbitrary release-policy hook
and cannot modify captured source.
For nested workspaces, keep working-directory and config consistent with
their workspace-relative rules. Give independent
release contexts distinct concurrency and artifact identities, while preserving
one context across retries.
release-context supplies the configured repository, release_branch,
release_history, nullable merge_target and stable concurrency_group.
With an explicit --release-history, it uses that commit without fetching history.
It does not replace publication preparation or require a clean checkout.
Use the exact phase commands
for custom jobs. Download outcomes into distinct artifact subdirectories named
per run and attempt, preserving outcome.json and batch linkage. The final
reporter takes the fixed prepare, registry, github and binaries job results,
regardless of the display names in your graph.
Troubleshoot at the boundary that failed
Use --verbose to see selection inputs and reasons without mixing diagnostics
into machine-readable output. Identify whether a failure occurred in offline
assessment, explicit resolution, API compatibility checker execution, source validation,
registry observation or GitHub publication.
Do not fix an offline classification issue by adding registry lookups, a API compatibility checker execution failure by suppressing its result, or an artifact mismatch by overwriting the artifact. Resolve the relevant input or tool combination and regenerate evidence where required.
Configuration reference
Publication uses committed TOML and package metadata. Source commits, output locations and attempt identities are invocation inputs, not persistent policy.
Workspace publication configuration
The default file is .cargo/release_plan.toml relative to the selected Cargo
workspace. Relative --config overrides resolve from that workspace, not the
shell’s invocation directory. Absolute overrides are also accepted. Publication
requires tracked configuration inside the source repository; it need not be
inside a nested Cargo workspace.
This is a complete example of the configuration format:
schema-version = 1
repository = "example/widgets"
release-branch = "main"
targets = [
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
"x86_64-pc-windows-msvc",
"aarch64-pc-windows-msvc",
"aarch64-apple-darwin",
]
Replace the example repository and release branch; no particular release-branch name is required.
| Key | Meaning |
|---|---|
schema-version | Configuration schema, currently 1. This spelling uses a hyphen, unlike JSON artifact fields. |
repository | GitHub destination in owner/repository form. |
release-branch | Branch whose merged source is eligible for publication. |
targets | Selected supported native targets for binary archives. |
For a library-only workspace, an empty targets = [] is valid. For a binary
package, the effective target set must be nonempty.
The supported native target set is:
| Target | Platform |
|---|---|
x86_64-unknown-linux-gnu | Linux x64 |
aarch64-unknown-linux-gnu | Linux ARM64 |
x86_64-pc-windows-msvc | Windows x64 |
aarch64-pc-windows-msvc | Windows ARM64 |
aarch64-apple-darwin | macOS ARM64 |
The action revision selects matching native runners. Intel macOS is not in this support set.
check --config validates configuration and binary publication inputs offline.
Ordinary check without that option retains its version-readiness scope.
Publication reads configuration by default. None of these settings silently
changes the history default for version assessment; automation supplies
--release-history and any --merge-target explicitly.
Package metadata
These entries are optional:
[package.metadata.release-plan]
private-api = true
release-targets = ["x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc"]
private-api declares that a library does not offer an independently supported
consumer API. It defaults to public when absent; binary-only packages have no
library contract to compare. The declaration does not disable publication or
dependency propagation.
Checker capability is separate. Procedural-macro libraries can offer a supported consumer API without declaring it private. Their Cargo target metadata excludes them from direct checker invocation and records the unsupported comparison explicitly. They remain subject to semantic assessment, release planning and publication. A private implementation’s re-exported items are assessed through the public package; the private declaration does not make that exposed contract private.
A CLI package with an internal library target must also declare private-api = true
when it offers no supported Rust library API. This does not waive compatibility
requirements for its command line or documented artifact formats.
release-targets narrows the workspace’s binary target selection. Omit it for
the whole workspace selection. Invalid targets or an empty effective binary
selection are errors.
Cargo’s publish = false is different: it excludes the package from publication
while retaining tracked version-group alignment.
Version groups come from exact local workspace dependencies, not metadata.
The obsolete [workspace.metadata.release-plan.groups] table is not accepted.
External types
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"widget_impl::*",
"other_library::PublicType",
]
Entries describe defining crate paths, including re-exported types. Verify them
with cargo-check-external-types for supported features and platforms. An absent
list allows no external exposure. See the
adoption prerequisite.
Binary metadata and naming
Each publishable binary package declares one executable that builds with Cargo’s
default features. Authors are responsible for checking that build selection.
The offline metadata check validates binary selection and archive declarations,
not feature eligibility or buildability; Cargo checks those during the native build.
Its Cargo repository is exactly https://github.com/<owner>/<repository> using
the configured destination, without a trailing slash or .git suffix, and
its binstall metadata describes the standard ZIP layout:
[package]
repository = "https://github.com/example/widgets"
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/{ name }-v{ version }/{ name }-v{ version }-{ target }.zip"
bin-dir = "{ bin }{ binary-ext }"
pkg-fmt = "zip"
The brace expressions are literal cargo-binstall template syntax, not placeholders to replace manually.
| Artifact | Naming contract |
|---|---|
| Package tag and binary release tag | {package}-v{version} |
| Archive | {package}-v{version}-{target}.zip |
| Sidecar | {package}-v{version}-{target}.sha256 |
| ZIP member | Executable name at archive root, with .exe on Windows |
The sidecar does not append .sha256 to the ZIP filename. Package and
executable names can differ. Unix ZIP members retain executable permissions.
Both assets must be uploaded for the pair to be complete.
Publication does not silently enable additional features to reach a gated binary, accept arbitrary Cargo build arguments or cross-compile an archive.
Command tasks and prerequisites
This reference describes the tool operations used by the skill and workflows.
Use increment-versions for version planning rather than constructing a manual
sequence from these commands.
Use cargo release-plan version to read JSON identifying the executable and its
supported artifact schemas, and cargo release-plan --help for its command
interface. The short --version output remains available for installation checks.
The copied skill checks schema compatibility rather than requiring an exact
package version.
Angle-bracket values below describe arguments to supply, not literal shell input. The integration walkthrough provides concrete PowerShell command sequences.
Input acquisition
Each command acquires current inputs and may reuse those observations after the required input checks during read-only work. Keep inputs stable during read-only command stages. Commands acquire new inputs after their own edits or resolution and for new or relocated workspaces. No workflow-managed cache identifier or observation-storage configuration is needed. Ordinary Cargo/compiler caches and checker build-directory configuration remain independent of release evidence.
Keep evidence outside Cargo’s target directory if it must survive cargo clean.
Release context and identity
cargo release-plan release-context [--manifest-path <path>] [--config <path>]
[--release-history <rev>] [--merge-target <rev>] [--verbose]
cargo release-plan check-publishing-identity [--verbose]
release-context reads the selected working-tree configuration and prints
the release-context artifact. It fetches the configured release branch
unless --release-history supplies the known history commit explicitly.
--merge-target identifies the intended PR target; a target already in release
history produces a null target in the output. It accepts dirty source
and does not require the configuration to match a committed file. In contrast,
prepare-publish requires tracked committed inputs.
check-publishing-identity requires GitHub Actions OIDC context but no workspace.
It exchanges and revokes a short-lived crates.io credential without uploading.
Success verifies the caller identity path, not each package’s publisher grant.
Do not pass workspace configuration to this command.
Assessment
cargo release-plan report --out-dir <dir> [--release-history <rev>] [--merge-target <rev>]
[--manifest-path <path>] [--verbose]
cargo release-plan check [--release-history <rev>] [--merge-target <rev>]
[--manifest-path <path>] [--config <path>]
[--format text|github] [--verify-packaging] [--verbose]
report writes report.json and patches for released file changes. Enumerate
the report’s statuses, not the patch directory, to find every affected package.
check fails on missing increments, inconsistent groups, malformed/stale
workspace requirements and required public-dependency breaking propagation.
With --config, it also validates publication inputs offline.
Ordinary assessment uses Git and cargo metadata --no-deps: it does not fetch,
contact crates.io, resolve dependencies or compile.
report, prepare, preview, apply and unconfigured check do not require
release_plan.toml or workflow integration. The
standalone workflow supplies history/target inputs
explicitly and stops at the version-increment PR.
--verify-packaging is an explicit audit against cargo package --list.
Divergences are warnings rather than changes to the check result. This probe performs
Cargo package preparation and resolution, and allows dirty trees; investigate
clean-tree divergence rather than treating it as proof that the ordinary check
performed the same work.
Prepare, choose and preview
cargo release-plan prepare --output <dir> [--release-history <rev>] [--merge-target <rev>]
[--manifest-path <path>] [--verbose]
cargo release-plan analysis-order --report <file-or-dir> [--verbose]
cargo release-plan semver-targets --report <file-or-dir> [--verbose]
cargo release-plan propose --report <file-or-dir> --decisions <decisions.json>
--out <plan.json> [--verbose]
cargo release-plan preview --prepared <prepared.json> --plan <plan.json> --output <dir>
[--manifest-path <path>] [--verbose]
prepare supplies a consistent lockfile and evidence for that source, using
cargo update --offline --workspace; it can modify the lockfile. preview
produces the complete version and dependency changes in a retained workspace
before recording the exact result to apply.
analysis-order, semver-targets and propose operate only on supplied
artifacts. A report argument can be a JSON file or a directory containing
report.json. They invoke neither Git nor Cargo. analysis-order and
semver-targets print JSON to stdout; propose writes its plan to --out and
prints a readable summary.
Compatibility and publication preflight
cargo release-plan check-compatibility --output <new-directory> [--manifest-path <path>]
[--prepared <prepared.json> | --plan <resolved-plan.json>]
[--release-history <rev>] [--merge-target <rev>]
[--deny-findings] [--verbose]
cargo release-plan check-published [--plan <resolved-plan.json>]
[--manifest-path <path>] [--verbose]
check-compatibility collects external API evidence without choosing semantic
decisions. Select exactly one source mode:
--preparedchecks the captured original prepared inputs.--planchecks a resolved preview’s retained workspace.- Without either, a fresh read-only report uses
--release-historyand optional--merge-target. These inputs cannot be combined with already-bound evidence.
It checks the selected inputs and regenerates a report from those same observations.
Assessed source, configuration and history must remain unchanged during checking.
An assessed workspace with selected contracts must have a lockfile accepted by Cargo’s
default metadata resolution without changes. It never accepts detached --report evidence.
The regenerated report uses report/plan schema 8.
For a resolved preview, input checks at command entry cover both the original workspace and the retained candidate. The original workspace must match the captured initial state or the completely applied plan; the retained candidate must match the captured final state.
The new output directory contains compatibility.json, semver-checks.log and
the read-only report. Evidence records the checker identity, comparison versions
and sources, completed comparisons and semantic floors. An
identical-source canary verifies the checker. When no supported public targets are
selected, neither checker execution nor registry access is required. Procedural-macro
targets are excluded before either, independently of private-api, and receive explicit
unsupported-proc-macro records. Their public version-group members remain eligible.
Operational failures do not become compatibility passes. Completed findings are
planning evidence by default; --deny-findings makes an insufficient increment
fail the command as well. Read completed, compared and required_impact,
not merely the exit code. completed: true can include unavailable comparisons;
read their not_compared_reason and retain semantic assessment of every affected package.
check-published --plan validates the resolved target set and fails on a
never-published or indeterminate publishable target. Non-publishable packages are
excluded. Without a plan, workspace-wide missing/unknown-package discovery is
advisory. Neither mode uploads a first version or verifies Trusted Publisher
administration.
Inspect and apply
cargo release-plan inspect-plan --plan <expanded.json> [--require-resolved]
[--manifest-path <path>] [--verbose]
cargo release-plan verify-preview --plan <plan.json> --manifest-path <prospective-manifest>
[--verbose]
cargo release-plan apply --plan <plan.json> [--dry-run] [--manifest-path <path>] [--verbose]
inspect-plan --require-resolved validates captured inputs and the retained
compatibility workspace, then prints publication target names and its manifest
path. Non-publishable packages are not publication targets.
verify-preview checks that external analysis left the prospective workspace
unchanged. It does not resolve or compile.
apply accepts only a resolved preview and installs its exact manifest and
lockfile edits. --dry-run validates those same inputs without writing.
Prepare and publish exact versions
cargo release-plan prepare-publish --source <full-commit-SHA> --output <manifest.json>
[--manifest-path <path>] [--config <path>] [--verbose]
cargo release-plan publish registry --publication <manifest.json> --output <new-outcome.json>
[--manifest-path <original-source-Cargo.toml>] [--dry-run] [--verbose]
cargo release-plan publish github --publication <manifest.json> --output <new-outcome.json>
--batches <new-directory> [--manifest-path <original-source-Cargo.toml>]
[--dry-run] [--verbose]
cargo release-plan publish binaries --publication <manifest.json> --batch <batch.json>
--output <new-outcome.json> --artifacts <new-directory>
[--manifest-path <original-source-Cargo.toml>] [--no-upload]
Preparation requires clean source with HEAD matching the full supplied SHA, tracked configuration and Cargo inputs, a consistent lockfile, and first-parent membership in the configured release branch. It fetches that branch but makes no remote publication writes.
Registry publication requires the original captured source. It queries exact
availability and delegates verification and ordered uploads to Cargo. Automatic
uploads require Cargo 1.95 or later and the calling workflow’s crates.io Trusted
Publisher registration with GitHub OIDC.
The Cargo floor supports stable multi-package workspace publication: one
cargo publish invocation accepts repeated --package arguments and orders
dependent uploads. It is separate from the application’s compiler requirement;
revalidate that capability when changing the floor. See
Cargo’s publish reference.
Registry --dry-run reads availability without exchanging credentials or
uploading. GitHub --dry-run observes tags, releases and assets without writes.
Neither dry run is a completed publication outcome.
publish github verifies registry availability for the complete manifest before
reconciling package tags and binary releases. It writes an outcome and frozen
platform batches with relative routing paths and stable batch_id values.
Missing-tag failures retain original-source recovery instructions and do not
suppress independent valid work.
publish binaries consumes one frozen batch and its parent manifest. It
rechecks tag identity and current assets, builds each executable from the
recorded tag commit, and completes missing ZIP/checksum pairs. --no-upload
stages that frozen work without tag/release/asset queries or uploads, retaining
source and archive verification. Missing source objects may still be fetched
from the configured repository, requiring repository-read access.
The binary-outcome reference defines its per-item
states and completion semantics.
Every attempt needs new outcome and batch/artifact destinations. Name phase
outcomes outcome.json inside separate artifact subdirectories when handing
them to the final reporter. The
publication walkthrough shows the complete
sequence without caller-authored batch JSON.
Final publication reporting
cargo release-plan publish report --repository <owner/repo>
--outcomes <download-directory> --jobs <jobs.json> --output <new-report.md>
[--publication <manifest.json>] [--no-issue]
Run this in the original GitHub workflow context after downloading phase
artifacts. It reads retained outcome.json files and the fixed current job
results, then writes Markdown and creates or updates a run-qualified failure
issue when publication is incomplete.
--repository identifies the GitHub repository used by the report and must match
the repository captured in a supplied valid publication manifest.
The manifest can be omitted when unavailable; the report still runs but cannot
claim complete delivery. Incomplete publication exits nonzero even when Markdown
and issue creation succeed. --no-issue suppresses GitHub writes only.
Retain run/attempt attribution so the reporter can enforce outcome identity and freshness.
Paths and output
--manifest-path defaults to Cargo.toml in the current directory. For planning,
--release-history defaults to the branch advertised by origin, then origin/main.
Automation supplies a commit explicitly.
release-context is the
explicit configuration-aware acquisition operation; unlike ordinary assessment,
it fetches the configured branch when no explicit --release-history is supplied.
Ordinary artifact arguments resolve from the invocation directory. Publication
relative --config paths resolve from the selected workspace; absolute overrides
are also accepted. Publication requires the selected configuration to be tracked
inside the repository. Paths recorded in transported
publication artifacts are repository-relative, not host-absolute paths.
Requested machine output stays separate from diagnostics. --verbose writes
explanatory selection and validation information to stderr.
Runtime boundaries
| Work | Prerequisites and effects |
|---|---|
| Identity/help | Installed executable; no workspace needed. |
| Release context | Configuration, Git and release-history access unless the caller supplies an explicit history commit; no clean-source requirement. |
| Publishing identity probe | GitHub Actions OIDC identity and crates.io exchange/revocation access; no Cargo workspace or upload. |
| Artifact-only planning | Supported report/decision artifacts; no checkout or network. |
| Ordinary assessment | Git history, work tree and Cargo metadata; offline and no resolution. |
| Preparation/preview | Cargo and available offline resolution inputs; can change the relevant lockfile. |
| External API checking | Compatible checker/compiler and comparison inputs; can compile and retrieve registry sources. |
| Publication preflight | Registry reads; plan-scoped checks fail closed, workspace discovery is advisory. |
| Publication preparation | Clean merged source, Git/Cargo, configuration and release-branch access; private repositories require GitHub authentication. |
| Live registry work | crates.io access, supported Cargo, registered caller OIDC identity and package verification prerequisites. |
| Native binaries | Native runner and source toolchain/build dependencies; ZIP creation is built in, and upload authority is separate from compilation. |
| Final report | Retained outcomes, current job results and GitHub run context; issue writes unless --no-issue. |
Do not treat the compiler used to install the controller, Cargo’s publication runtime floor and a tagged source’s toolchain as one interchangeable requirement.
Artifact reference
Local planning and post-merge publication serve different purposes and have separate schema lifecycles. Use tool-produced evidence where specified; do not fabricate captured inputs, identities or publication outcomes.
You choose output destinations and are responsible for their placement relative to source, configuration, existing evidence and other data. Commands write to the supplied locations without checking for input collisions. Each writer manages only its documented artifacts and owned workspace directories.
Preview validates captured inputs before invalidating its previous completion marker. Missing or malformed preparation and stale captured inputs leave that marker untouched. An existing completion marker does not prove that the captured inputs are still valid; consumers still check those inputs.
Captured source inputs
Preparation and preview capture declared inputs, Git-tracked files and source-directory
contents. Source directories include src and dedicated custom target or build-script
directories below each package root, including path dependencies. A target at the package
root or outside it contributes its declared file without recursively capturing the
package root or its ancestors.
Supporting modules and other build inputs outside those source directories must be
Git-tracked. Use git add; a commit is not required. The tool does not interpret Rust
modules or invoke a compiler to discover additional inputs.
Local decisions and plans
A decisions document is caller-authored literal JSON:
{
"schema_version": 2,
"changes": [
{ "name": "widget", "impact": "nonbreaking" },
{ "name": "widget_impl", "impact": "patch" }
]
}
changes[].impact is semantic: breaking, nonbreaking or patch.
Non-publishable packages have no semantic impacts.
The proposed-plan format uses report/plan schema revision 8. This is a literal
example of that format:
{
"schema_version": 8,
"increments": [
{ "name": "widget", "bump": "minor" },
{ "name": "widget-cli", "version": "2.0.1" }
]
}
increments[].bump is numeric: major, minor or patch. Supply exactly one
of bump and version per entry. Names select tracked workspace members;
group members expand together. Explicit targets cannot lower declared versions,
and group targets use plain major.minor.patch versions.
Preview produces an expanded plan with explicit package versions and the
resolved source/files needed for application, including
resolved file contents, original inputs and evidence_manifest_path.
Treat prepared.json and the resolved object as opaque. Preserve them intact;
regenerate unsupported or stale evidence with prepare and preview.
Reports
The revision-8 report’s top level contains:
| Field | Content |
|---|---|
schema_version | Report/plan format revision. |
head | Source commit associated with the assessment. |
release_history | Selected actual release-history commit. |
merge_target | Optional final parent commit supplying anticipated releases. |
packages | Publishable package assessments. |
non_publishable_packages | Tracked alignment targets without release assessments. |
groups | Complete group membership across both package arrays. |
Each publishable entry includes name, declared_version, status, changed,
stat, dependencies, public_origins, dependents, consumer_contract and proc_macro. Optional evidence
includes its group, anchor, patch path and advisory untracked files. An anchor can
identify a release-history version change or the final snapshot of an anticipated
parent version; the report’s history/target fields identify that context.
Changed entries distinguish:
source | Evidence |
|---|---|
package | Packaged file path and change kind. |
inherited | Changed inherited workspace field. |
lockfile | Changed binary installation dependency identity. |
Dependencies record name, req and exact_pin. public_origins is the sorted
set of defining workspace package names exposed directly or through another
exposed owner’s API. It is independent of the dependency paths supplying the
types and is required even when empty. consumer_contract is true for a library
offering a supported API for direct consumers, rather than declaring private-api = true.
proc_macro independently records Cargo’s procedural-macro library target kind.
Both booleans are required. A public macro can have both true while remaining unsupported
for direct comparison. Neither flag removes semantic, dependency or publication obligations.
Non-publishable packages carry name, declared_version and an optional group,
not a status or semantic impact. Group records include complete sorted
members and their highest declared version.
Patch paths are relative to the report directory. Patches are zero-context unified file diffs, not the complete report. An inherited-only or lockfile-only change can have no patch.
A report’s HEAD alone does not prove that an arbitrary dirty checkout matches
the evidence. check-compatibility regenerates a bound read-only report from
prepared inputs, a resolved preview or a fresh source assessment. It does not
accept a detached report.
Release context
release-context prints JSON with these fields:
| Field | Meaning |
|---|---|
schema_version | Release-context format revision, currently 2. |
repository | Configured GitHub owner/repository. |
release_branch | Configured release branch. |
release_history | Commit delimiting actual release-branch history. |
merge_target | Anticipated unmerged parent commit, or null when no additional target is needed. |
head | Assessed source HEAD. |
workspace_manifest | Repository-relative workspace manifest location. |
config_path | Repository-relative configuration location. |
concurrency_group | Stable workspace-scoped release concurrency identity. |
This is acquired context, not publication intent. Save it for the local planning run and reuse its history/target pair. Refresh both before application; do not replace either beneath prepared evidence.
Compatibility evidence
Each check-compatibility invocation uses a new output directory and writes
compatibility.json, semver-checks.log and a regenerated read-only report.
The evidence retains checker identity and exact comparison versions. Its linked
report identifies any anticipated-parent anchor supplying the comparison source.
Compatibility evidence uses schema 3:
| Field | Meaning |
|---|---|
schema_version | Compatibility-evidence format revision. |
checker | Identified checker version, or an explanation when no identity was established. |
report | Location of the source-bound report generated for this comparison. |
completed | Whether required input checks and supported comparisons completed, with exclusions recorded. |
findings | Whether completed comparisons found an insufficient increment. |
packages | Comparison records containing the fields below. |
Each comparison record contains name, compared, nullable baseline_version,
and nullable required_impact. A compared package uses a published version or its
anticipated parent’s version and final source. An unavailable comparison has
compared: false and no comparison version or required impact.
Unavailable records also contain not_compared_reason: unsupported-proc-macro
for a target the checker cannot compare directly, or no-baseline for a supported
target without a comparison version. Compared records omit this field.
Affected macro records include private packages and macro members of affected version
groups; unrelated unchanged packages need no record.
An operational failure can leave only earlier completed records; inspect the
overall completion flag and retained diagnostics rather than treating absent
records as passes.
Require completed: true before using the result. A package’s compared: false
means no comparison was available, not proof of compatibility.
required_impact is a semantic breaking or nonbreaking floor; null
establishes no minimum. The author still judges behavioral, CLI, format and
feature-subset effects.
This includes APIs and behavior supplied through re-exports, which are not fully covered
by the checker, and macro invocation syntax and generated code. See
semantic assessment.
Do not confuse a completed comparison with a passing merge gate:
--deny-findings additionally rejects insufficient increments. Source inputs are checked
at command entry. Callers and tools invoked for read-only work must keep assessed
source, configuration and history unchanged during comparison; operational errors
never become semantic passes.
Artifact-only command output
analysis-order emits a dependency-first array:
[
{ "order": 1, "packages": ["widget_impl"], "cyclic": false },
{ "order": 2, "packages": ["widget"], "cyclic": false },
{ "order": 3, "packages": ["widget-cli"], "cyclic": false }
]
This example assumes those are the publishable packages. Every publishable member appears once, including unchanged ones; non-publishable packages do not. Cycle batches represent actual dependencies, not group alignment.
semver-targets emits sorted directly checkable public package names, or []
when none are selected. Procedural macros are excluded regardless of consumer policy;
their public version-group members remain eligible. inspect-plan emits:
{
"publication_targets": ["widget", "widget-cli", "widget_impl"],
"evidence_manifest_path": null
}
This example represents structural inspection without retained preview evidence.
For a resolved artifact, the manifest path identifies the verified prospective
workspace. Request --require-resolved before relying on that workspace for the
complete local release flow.
Publication manifest
prepare-publish creates this envelope:
{
id,
publication: {
schema_version: 1,
tool_version,
source,
workspace_manifest,
config_path,
configuration,
packages: [
{ name, version, manifest, binary: null | { name, targets } }
]
}
}
This is a field-shape sketch, not JSON input. source is the full publication
commit. Workspace, package and configuration paths are repository-relative.
configuration captures the effective committed configuration. A binary’s
name is the executable name, not necessarily its package name.
The package array includes every publishable exact version at that source, including unchanged packages, and excludes non-publishable packages. The artifact records producer identity and schema independently of local-plan schemas.
id links subsequent work to the captured payload. It is an integrity and
linkage check, not a signature or independent authorization. A fetched branch
tip is not part of the captured intent digest. Do not change the payload,
recompute its identity by hand or add mutable publication flags.
Registry outcome
Registry outcomes use schema 1 and record:
| Field | Meaning |
|---|---|
schema_version | Outcome format revision. |
publication_id | Original publication manifest identity. |
phase | registry. |
dry_run | Whether the attempt only observed intended work. |
complete | Whether this live registry attempt established completion. |
packages | Entries with exact name, version and state. |
errors, notes | Failure diagnostics and explanatory observations. |
github | Optional {run_id, run_attempt} linkage captured in GitHub Actions. |
The exact state spellings are:
| State | Interpretation |
|---|---|
already_present | The exact version was observed without needing this attempt to publish it. |
published | Publication completed and the exact version was observed. |
would_publish | Dry-run work for a missing version. |
missing | The requested version remains absent. |
unknown | Availability could not be established. |
A dry run never sets complete to true. Use the outcome and command diagnostics
together; unknown queries and partial failure are not reduced to successful
skips. Each attempt writes a new outcome path.
GitHub outcome
GitHub reconciliation outcomes use schema 1:
| Field | Meaning |
|---|---|
schema_version | Outcome format revision. |
publication_id | Original publication manifest identity. |
phase | github. |
dry_run | Whether the attempt only observed intended work. |
complete | Whether live GitHub reconciliation completed; binary delivery can still remain. |
packages | Package/version/tag identities with the state and source evidence described below. |
batches | Routing entries containing target, path and batch_id for separate platform-batch files. |
planned_targets | Native targets selected for potential binary work, not batch payloads or delivery evidence. |
errors | Reconciliation diagnostics. |
github | Optional {run_id, run_attempt} linkage captured in GitHub Actions. |
Each package record contains name, version, tag and state, with these
state spellings:
| State | Interpretation |
|---|---|
pending | This record does not establish completed reconciliation. |
complete | The required tag and, for a binary package, GitHub release were reconciled. |
would_create_tag | A dry run identified a missing tag. |
would_create_release | A dry run identified a missing GitHub release. |
failed | Reconciliation for the package failed. |
The nullable source field retains an observed commit identity. A failure can
retain an unverified observation; this field alone does not authorize a build.
The nullable recovery_source is the original publication source retained for
missing-tag recovery, while observed_version is the candidate version when
available. Failures need not have missing-tag recovery evidence.
A dry run never sets the outcome’s complete field to true. A live complete
GitHub outcome does not imply that its binary archives are uploaded.
Each batch path is relative to the separately emitted batch directory; retain
those files and the parent publication manifest alongside the outcome.
Platform batch
publish github produces a separate schema 1 platform-batch file for each
selected native target. Its shape is:
{
schema_version: 1, publication_id, repository, target, batch_id,
binaries: [{name, bin, version, tag, source_sha}]
}
This is a field-shape sketch, not caller-authored JSON. name identifies the Cargo
package and bin its executable. source_sha is the frozen peeled package-tag
commit. publication_id identifies the parent manifest; batch_id identifies the
batch’s exact work, including those source commits. repository and target
retain their configured identities.
Binary outcome
publish binaries produces a schema 1 outcome:
{
schema_version: 1, publication_id, phase: "binaries",
target, no_upload, complete, batch_id,
github?: {run_id, run_attempt},
items: [
{
binary: {name, bin, version, tag, source_sha},
status, stage, diagnostic, cleanup_error
}
]
}
github is omitted outside GitHub Actions. Item diagnostic and cleanup_error
values are nullable strings. The supported status/stage combinations are:
| Status | Stage | Execution mode |
|---|---|---|
published | upload | Upload |
skipped-complete | refresh | Upload |
staged-only | package | No upload |
failed | refresh or upload | Upload |
failed | source, build, package or cancelled | Either |
unattempted | cancelled | Either |
A failed item includes a diagnostic. Cleanup failure is recorded separately
without replacing the item’s operation status. complete requires successful
upload-mode results for every requested binary and no cleanup failures.
no_upload staging never establishes delivery completion.
Retain the original manifest and platform batch with these outcomes. Consume the tool-produced artifacts rather than synthesizing them; their identities and attempt ordering are required evidence for reporting.
Derived batches and later outcomes
GitHub reconciliation emits new platform batches linked to the original
manifest. A batch fixes the native target, package versions, executable names,
tags and actual peeled tag commits. Its stable batch_id binds that content;
each outcome routing entry supplies target, relative path and batch_id.
Those observations do not get written back into the original manifest.
Native execution refreshes remote completeness before building. Later
reconciliation can emit another batch for remaining work without mutating an
older batch. Binary outcomes link both the publication and batch identities.
Phase outcomes include optional github: {run_id, run_attempt} metadata, which
the workflow preserves across artifact transport.
An older successful binary outcome cannot satisfy newer GitHub evidence of
missing assets. This remains true when the new batch contains exactly the same
requests and therefore has the same batch_id. Its outcome must be at least
as new as the selected GitHub reconciliation.
Consume these artifacts through the matching tool/action interface rather than writing them manually. Missing or incompatible artifacts require explicit recovery; they are not a valid empty work set.
Reporter job results
publish report --jobs reads a JSON object with fixed keys. Each value is one of
success, failure, cancelled or skipped, taken from GitHub’s workflow job-result
context rather than the broader check-run conclusion vocabulary:
{
"prepare": "success",
"registry": "success",
"github": "success",
"binaries": "success"
}
This demonstrates the format, not a default successful state. Supply observed
results from the current workflow; binaries is the binary matrix job result.
Skipped required phases are incomplete. A skipped binary matrix is acceptable
only when the selected GitHub outcome requires no platform batches.
Do not omit failed/skipped jobs, rename keys after your caller’s display names,
or synthesize success from existing assets.
The reporter recursively reads outcome.json files beneath --outcomes,
retaining their artifact subdirectories and run/attempt linkage. It compares
the latest applicable outcomes with current job results and the original
manifest. Old successes cannot hide failed jobs or newer missing-asset evidence.
--output names the new Markdown report. The command can report and file a
failure issue without the publication manifest, but always marks that case
incomplete. --no-issue suppresses issue writes, not GitHub-context validation
or incomplete-delivery failure.