Skip to main content
Version: MVP

Release Management

5 min readFor maintainersUpdated 2026-05-19

What you'll do

Cut, tag, document, build, and publish a Craik release. Every release must be installable, documented, and recoverable — 0.x.0 is not a license to skip discipline.

0.x.0 MVP, not 1.0.0.

The 0.x.0 series can still change contracts between minor releases, but each published release must pass the same packaging, docs, quality, and changelog gates as a stable line.

Release cadence

Release line
Use for
Notes
0.x.0
capability milestones
MVP capability increments. Publish when the roadmap gate completes, CI is green, and release notes are reviewed.
0.x.y
patches
Bug · docs · packaging · compatibility · security fixes.
1.0.0
deferred
Waits until compatibility promises, upgrade paths, and real-world security soak justify the stability signal.

Tag Policy

Release tags use vMAJOR.MINOR.PATCH — e.g. v0.1.0 — and must be signed annotated tags. The embedded tag signature is the release integrity source of truth; the GitHub Release also carries the public release signing key as an operator convenience for verification.

  1. Update pyproject.toml, src/craik/init.py, and docs/package.json.
  2. Move relevant CHANGELOG.md entries from Unreleased into the target version section.
  3. Run package, docs, quality, and version checks.
  4. Open a release PR that links the completed roadmap issue.
  5. Tag only the merge commit from the release PR with git tag -s vX.Y.Z -m "vX.Y.Z — Release name".
  6. Push the tag, confirm the GitHub Release is created, and upload the ASCII-armored public signing key as craik-release-signing-key.asc.
git tag -v vX.Y.Z
git push origin vX.Y.Z
gpg --armor --export KEY_FINGERPRINT > craik-release-signing-key.asc
gh release upload vX.Y.Z craik-release-signing-key.asc --repo eidetic-labs/craik --clobber
gh release view vX.Y.Z --repo eidetic-labs/craik --json assets --jq '.assets[].name'

craik-release-signing-key.asc is a public key export, not a detached signature. Maintainers must verify that its fingerprint matches the key reported by git tag -v vX.Y.Z before treating the release as complete. Local signing-key exports are ignored by the repository. Do not commit craik-release-signing-key.asc or any private/secret signing key export; upload the public key only as a GitHub Release asset.

Release Notes

Every release needs a GitHub release entry and a matching CHANGELOG.md section. The Publish workflow's create-github-release job extracts the ## X.Y.Z - YYYY-MM-DD block from CHANGELOG.md on tag push and creates the GitHub Release automatically — title Craik X.Y.Z, body verbatim from the CHANGELOG section, marked latest. The job fails fast if the CHANGELOG has no section for the tag version, so the CHANGELOG is the single source of truth for release notes. After the release entry appears, upload the public release signing key asset and verify the asset list before announcing the release.

User-facing additions and fixes

Migration notes and compatibility risks

Provider · policy · persistence · receipt changes

Known limitations remaining

Links to closing issues and release PR

Package verification

Package artifacts are built in CI by the Package workflow, which checks version consistency, validates release-process docs, builds sdist + wheel artifacts, runs twine check, smoke-installs the wheel, and uploads build artifacts.

Local equivalent:

python scripts/check_version_consistency.py
python scripts/check_release_readiness.py
python -m build
python -m twine check dist/*

PyPI publishing

Tag-driven, OIDC-published.

Publishing runs only from the immutable release tag (currently v0.1.0) after the workflow verifies tag, package version, and changelog all agree. Manual dispatch builds and validates artifacts only.

Publishing requires the pypi Protected Environment:

Reviewer approval

At least one maintainer.

Branch / tag restriction

Protected branches and release tags only.

Trusted publishing

Through GitHub OIDC — not stored PyPI tokens.

PyPI publisher config

Aligned with eidetic-labs/craik.

Rollback

PyPI releases are immutable from dependents' perspective. If a bad release ships:

  1. Publish a patch release with a clear changelog entry and GitHub release note.
  2. Yank only when installation of the bad artifact is actively harmful.

What's next