Status: Active
Last reviewed: 2026-09-27
This document is normative for package versions, Git tags, GitHub Releases and the changelog.
ChunkShift follows Semantic Versioning 2.0.0 and is in its major-zero initial-development phase. The first public release is 0.1.0.
Until an explicit decision to release 1.0.0, normal releases use one monotonic patch train:
0.1.0 -> 0.1.1 -> 0.1.2 -> ...
- every normal release increments PATCH by one;
0.2.0is not used to signal a feature release;- features, fixes, performance changes and deliberate breaking pre-1.0 changes can all appear in the next
0.1.Zrelease; - the version number alone promises no backward compatibility before
1.0.0; - breaking changes are always explicit in the PR, the changelog and the release notes;
1.0.0requires a separate recorded compatibility decision.
These compatibility domains are versioned or identified independently from the NuGet package:
- the CSM format (FormatMajor);
- HashSuite identifiers;
- chunking profiles (
ChunkingProfileId/ProfileFingerprint).
A package release never silently reinterprets an existing persisted format, profile or HashSuite identifier. Changing those semantics requires a new identifier or format version, even during 0.1.Z.
When validation before a normal release is useful, use SemVer pre-release suffixes:
0.1.4-alpha.1 -> 0.1.4-beta.1 -> 0.1.4-rc.1 -> 0.1.4
alpha: incomplete or experimental;beta: feature-complete enough for broader testing;rc: expected to become the normal release unless a blocking defect appears.
CI builds of ordinary commits are not releases and never create tags.
Each published version has exactly one tag: a lowercase v followed by the exact package version, for example v0.1.0 or v0.1.3-rc.1.
- The release workflow creates the tag on the exact commit it built and published.
- Tags are immutable: never moved, force-updated, deleted or reused.
- There are no floating
v0orv0.1aliases.
CHANGELOG.md is written for users, not copied from the Git log. It follows Keep a Changelog (Added, Changed, Deprecated, Removed, Fixed, Security). The maintainer curates it in the release PR; contributors do not edit it in every PR.
Every release gets a dated changelog section, with explicit breaking-change and migration notes where they apply. The release workflow uses that exact section as the GitHub release notes and refuses a version that has none. GitHub-generated notes, grouped by the PR labels configured in .github/release.yml, are only an input when writing the changelog section.
- Release PR. Title
chore(release): prepare X.Y.Z. It setsVersionPrefixinsrc/ChunkShift/ChunkShift.csproj, moves thePublicAPI.Unshipped.txtentries intoPublicAPI.Shipped.txt(§8.1) and adds the dated## [X.Y.Z]changelog section, which becomes the release notes. Merge it after all intended changes are onmainand CI is green. - Dry run. Dispatch the
Releaseworkflow frommainwith the version andpublish_nuget: false. Inspect the uploaded.nupkg,.snupkg,SHA256SUMSandRELEASE_NOTES.md. A dry run creates no package, tag or release. - Publish. Dispatch the workflow again with
publish_nuget: true. Approve thereleaseenvironment deployment when prompted. - GitHub Release. After nuget.org accepts the package, the workflow creates
vX.Y.Zon the exact validated commit, creates a draft GitHub Release, attaches the validated.nupkg,.snupkgandSHA256SUMS, and publishes the release. With release immutability enabled, the published tag and assets cannot be replaced; a faulty release is corrected with a new package version. - Check. Install the published version into a clean project from nuget.org.
The workflow refuses a version whose X.Y.Z differs from VersionPrefix, that is not higher than every version already on nuget.org, or whose tag already exists. It builds, tests, packs, inspects and consumes the package (JIT and NativeAOT) once, then publishes those exact files; nothing is rebuilt after validation.
If a release turns out to be wrong, publish a new version. Never mutate or reuse an old one.
- Package published, symbols failed: re-push the
.snupkgfrom the run'schunkshift-<version>artifact withdotnet nuget push <file>.snupkg --skip-duplicate. Re-running the workflow is refused because the version is already published. - Package published,
github-releasefailed: re-run only the failed job (gh run rerun <run id> --failed). It reuses the original workflow artifact and the originalGITHUB_SHA: it keeps av<version>tag that already points to that commit, completes a draft left by the failed attempt, and publishes the release with the original.nupkg,.snupkgandSHA256SUMS. The artifacts are kept for 90 days, so recover within that window; packages downloaded from nuget.org carry its repository signature and no longer matchSHA256SUMS. Never rebuild the same version and never tag a different commit.
Publication uses nuget.org Trusted Publishing: the workflow exchanges a short-lived GitHub OIDC token for a temporary nuget.org API key. There is no long-lived NuGet API key.
Required configuration before the first publication:
- a GitHub environment named
release, limited to themainbranch, with required reviewers; - a nuget.org Trusted Publishing policy for owner
MrFr3di, repositoryChunkShift, workflow filerelease.ymland environmentrelease; - the repository or environment variable
NUGET_USERset to the nuget.org profile name that owns the policy; - repository rules protecting
mainandv*tags, and immutable releases enabled.
Only the publishing job receives id-token: write, and it runs no repository code: it downloads the validated artifacts, checks their SHA-256 sums, attests their provenance and pushes them. Tag and release creation run in a separate job that holds the only contents: write permission.
A breaking change is allowed during the 0.1.Z train only when it is deliberate. It needs:
!in the Conventional Commits PR title, or aBREAKING CHANGE:footer;- a compatibility and migration explanation in the PR;
- updated tests and vectors for the affected contracts;
- a prominent changelog and release-note entry.
Persisted-format and identity changes additionally follow §2.
The PublicAPI analyzer tracks the public surface:
PublicAPI.Unshipped.txtholds every public symbol not yet in a published version. Before the first publication the whole surface lives here.PublicAPI.Shipped.txtholds the symbols of the last published version.- Entries move from Unshipped to Shipped only in the release PR (§6 step 1), so
Shipped.txtalways describes something consumers could install. - Removing or changing a Shipped entry is a breaking change under §8.
From the release after 0.1.0 on, package validation also compares the package with the previous published version (PackageValidationBaselineVersion), which catches binary breaks that source compilation misses.
1.0.0 is a separate project decision, recorded in an issue, that the public API and compatibility policy are ready for SemVer stability. After it, PATCH is for compatible fixes, MINOR for compatible features and MAJOR for incompatible API changes.