Skip to main content
Suchi releases follow the “boring underneath, easy to get around” principle at the ops layer too. No custom versioning scheme, no hand-crafted release notes — just conventional commits + Keep-a- Changelog + SemVer tags.

Commit convention

Every commit message MUST follow Conventional Commits:
Types (all lowercase): Scopes are packages (authz, automations, ui, api, db, refile, mcp, deploy, docs) — greppable, not fancy. Breaking change — append ! after the scope AND include a BREAKING CHANGE: footer:
Concrete examples from the log:

Changelog

Every user-visible change lands in CHANGELOG.md. Format is Keep a Changelog. Rules:
  • Every PR that touches user-visible behavior updates the [Unreleased] section as part of the diff. Not a follow-up chore — same PR, same review.
  • Sections: Added, Changed, Removed, Fixed, Security. Migration notes go inline as callouts.
  • Write for the operator, not the maintainer. “URL rename” beats “refactor endpoint” every time.
At release time, the [Unreleased] contents shift under the new version header. Fresh [Unreleased] opens.

SemVer

Suchi follows SemVer 2.0:
  • MAJOR — breaking changes to any documented public interface. URLs, request/response shapes, config knob names, CLI flags, DB schema in ways that block downgrade.
  • MINOR — new features, additive-only changes. Adding an endpoint is minor; renaming an existing one is major.
  • PATCH — bug fixes, docs, internal changes with zero operator-visible effect.
Pre-1.0 (which is where suchi is today): both minor and patch bumps can carry breaking changes, but every breaking change still gets a BREAKING CHANGE: footer + a ⚠ Migration callout in the changelog. 0.x → 0.(x+1) is the “we changed something you’ll notice” signal.

Cutting a release

Once the [Unreleased] block has enough shipped to justify a tag (usually 2–4 weeks of merged changes, driven by feel — no calendar):
  1. Freeze main. Merge nothing but doc-only PRs while cutting.
  2. Verify green CI. .github/workflows/ci.yml and .github/workflows/smoke.yml must both be passing on the tip of main.
  3. Walk docs-claims.md. Every row’s grep must confirm. Any drift is a hard blocker for the tag — fix the code or the doc, then re-run. See docs-claims.md in the repo root; it’s the source of truth for “the docs still describe reality.”
  4. Roll the changelog. Move [Unreleased] contents under the new version header:
    Reset [Unreleased] to an empty stub. Commit as chore: release 0.5.0 (no ! — the tag is the artifact, the commit isn’t breaking).
  5. Tag. Signed and annotated:
  6. Publish the release notes. Copy the new CHANGELOG section into a GitHub release attached to the tag. Attach:
    • The two Docker images (suchi:0.5.0 and suchi:0.5.0-full — built and pushed by the release workflow)
    • Linux amd64 + arm64 binaries (from the release workflow)
    • macOS + Windows binaries
    • sbom.spdx.json + sbom.cyclonedx.json for each artifact
    • Cosign signatures alongside every image tag
  7. Announce. Once we’re public: a short GitHub Discussions post with the changelog highlights. Nothing more.

Automation

  • .github/workflows/ci.yml — per-module test/vet/gofmt gate on every push and PR.
  • .github/workflows/smoke.yml — full-image ingest smoke on every push and PR.
  • .github/workflows/release.yml (arrives with the repo flip) — triggered on v* tag: builds both Docker images, pushes to ghcr, builds cross-platform binaries, generates SBOM + cosign signatures, attaches everything to the GitHub release.

Deprecation policy

  • Deprecated features stay for at least one MINOR release with a warning in logs + docs.
  • Removals only in MAJOR releases (pre-1.0: any MINOR, but always called out).
  • Config-key renames ship both names for at least one MINOR; the old name logs a deprecation warning per-boot.

Support policy

Pre-1.0: support is the current tag only. Once we hit 1.0, we commit to at least the current + one previous major with security-only backports. Regular patch releases target only the current major.

Hotfixes

For a security fix on the current release:
  1. Branch from the tag: git switch -c hotfix/0.5.1 v0.5.0
  2. Land the fix (usually a single fix commit).
  3. Update CHANGELOG: [0.5.1] - 2026-08-11 under Security.
  4. Tag + release as usual.
  5. Merge the hotfix back into main via a PR so the fix isn’t lost.

Not covered here

  • Automated changelog generation (git-cliff / release-please) is an option but not wired yet. Manual authorship keeps the quality up and the reader’s-first framing consistent. If it becomes a bottleneck, revisit.
  • Signed commits are encouraged but not enforced. DCO sign-off (git commit -s) is required on all merged PRs from the repo- public flip forward.
Related: