Skip to content
Apothem
Governance

Release-engineering policy

Five canonical policies for versioning, release tagging, deprecation cycles, breaking changes, and rollback procedures.

Five policies govern how apothem releases.

1. Semantic versioning

The project ships under Semantic Versioning 2.0. Version numbers take the form MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD].

BumpTriggers
MAJORBreaking changes to artifact schemas, mandate identifiers (CM-N / TM-N / CP-N), or hook contracts
MINORNew rules, delegated workers, commands, skills, hook events, or non-breaking schema extensions
PATCHClarifications, anti-pattern additions, doc fixes, edits that do not change behavior

Candidate tags use the format vX.Y.Z-rc.N (release candidate) or vX.Y.Z-alpha.N / vX.Y.Z-beta.N (early-access).

2. Release tags

Every release lands a signed git tag of the form vMAJOR.MINOR.PATCH on main. The tag triggers .github/workflows/release.yml which builds, signs, generates SBOM and SLSA-3 provenance, and publishes the GitHub Release page.

Tag conventions:

  • Format — vMAJOR.MINOR.PATCH exactly. No release- prefix, no -final suffix.
  • Annotation — every tag is an annotated tag (git tag -a -s) carrying the release-notes summary in the tag message.
  • Signature — every tag is GPG-signed. The release workflow additionally produces a Sigstore keyless cosign signature on each artifact and a SLSA-3 provenance record, both attached to the GitHub Release page.

Release model: additive

Releases are additive. Each release adds one commit on main and one tag; main is never force-pushed, and no published tag, GitHub Release or npm version is deleted, moved or reused. Every release keeps its tag, its GitHub Release and its npm version, and all of them stay published. npm versions are immutable: a published name@version can never be replaced, and an unpublished number can never be used again. Pinned installs and reproducible builds depend on every published tag. The release-cycle and release-recovery runbooks both follow this model.

The repository's release-facade rule (the Release Facade section of the contributor instructions) states this model. Its current-version-only requirement, mirrored in the freshness-facade rule, covers what public surfaces say. It does not limit which releases stay published.

3. Deprecation policy

The current public support line is the latest released minor. When the project has multiple public minors, the SECURITY.md supported-versions matrix is the source of truth for any additional critical-fix-only support windows.

Deprecation cadence:

  • Announcement — a deprecated symbol or behavior is announced in the CHANGELOG.md ### Deprecated section of the release in which the deprecation lands. The release notes mirror the deprecation announcement.
  • Grace window — deprecated symbols stay available for a minimum of one full minor cycle before removal.
  • Removal — removal lands in a MAJOR bump. Each removed symbol carries a CHANGELOG.md ### Removed line and a migration guide entry under How-to-guides.

Runtime floors

Apothem declares two runtime floors: requires-python = ">=3.10" in pyproject.toml and "engines": {"node": ">=18"} in package.json.

  • Every declared floor is tested. The Python floor runs in the CI quality matrix and the CI Matrix workflow. The npm Shim workflow runs the npm shim on the Node floor and on every later LTS major: it checks the missing-prerequisite error, node bin/apothem.mjs --version, a dry-run install in an isolated home directory, and the packed tarball through npx. A runtime that is not tested is not declared.
  • End of life is disclosed. When a runtime that is still a declared floor reaches upstream end of life, the installation guide says so and names the versions to prefer. Python 3.10 reached end of life on 2026-10-01; Node.js 18 and 20 on 2025-04-30 and 2026-04-30. All three remain supported floors.
  • Raising a floor is a deprecation. It follows the cadence above: an announcement under ### Deprecated, at least one full minor cycle in which the old floor stays tested, then the release that raises it records the change. requires-python, engines.node, the installers' version probes, and the CI matrices change together in that release.

4. Breaking-change policy

Breaking changes require:

  1. An RFC issue — opened against the rfcs issue label describing the breaking change, the migration path, and the rollback strategy.
  2. A 30-day comment window — the RFC stays open for at least 30 days before merge. Maintainers may extend the window when feedback warrants.
  3. A MAJOR bump — the release that lands the breaking change bumps the MAJOR component.
  4. A migration guide — the change ships with a How-to-guides entry walking the operator through the migration.

5. Rollback policy

If a release exhibits a regression that blocks adoption:

  1. Revert — the offending commit(s) are reverted on main.
  2. Fix-forward release — a follow-up release lands as vMAJOR.MINOR.PATCH+1 carrying the revert plus any incremental fixes. The previous bad release is not deleted from GitHub Releases, and its npm version stays published (npm versions are immutable) and is marked with npm deprecate. A ### Yanked annotation in CHANGELOG.md documents the situation so reproducible builds remain anchored.
  3. Communication — a security-advisories notice (when security-relevant) or a GitHub issue pinned for the incident (when reliability-relevant) accompanies the rollback.

See also

On this page