ADR-0007: Versioning and Release Model
Context
The project consists of multiple independent components, each in its own repository with its own development pace (ADR-0004). Components depend on each other through pkg-config and CMake (ADR-0005).
A versioning scheme must answer three questions:
- Compatibility: how does a consumer know whether a given version of a dependency is safe to use?
- Coordination: how do independently-versioned components stay coherent as a system?
- Maintenance: how are stable releases maintained while development continues?
Decision
Project-wide major version
All components share a single major version. Any two components at the same major version are guaranteed to be compatible.
The guarantee is forward-only: code built against an older minor version of a dependency will work with any newer minor version within the same major. The reverse is not true — code that uses APIs introduced in a newer minor version requires at least that version. Components declare their minimum dependency versions through pkg-config and CMake.
Semantic versioning
Each component follows Semantic Versioning:
- Major (
X.0.0): the project-wide major version, shared across all components. - Minor (
X.Y.0): incremented independently when backward-compatible functionality is added. - Patch (
X.Y.Z): incremented independently for backward-compatible bug fixes.
Branch model
Each component repository uses two types of branches:
main— development branch, targeting the next major version. Breaking changes are accepted.stable/X— stable branch for major version X. Only backward-compatible changes are accepted.
Releases are identified by tags: v1.0.0, v1.1.0, v1.1.1, etc. The build system derives version
numbers from these tags.
Release process
When the project lead decides to release a new major version:
- Freeze: feature freeze across all components. Only integration and regression fixes are accepted on
main. - Stabilize: components are built and tested together through the workspace until the system passes all acceptance criteria.
- Branch: a
stable/Xbranch is created in every component from the tip ofmain. - Tag: the initial release
vX.0.0is tagged on the branch point. - Advance:
mainmoves to the next major version (X+1).
There is no fixed release calendar. Major releases happen at the discretion of the project lead when the accumulated changes justify a new stable version.
Maintenance
The project supports the two most recent stable versions plus the development branch. Each stable version is in one of three phases:
- Active — the latest stable version. Receives new backward-compatible functionality (minor releases), security fixes, and bug fixes (patch releases).
- Maintenance — the previous stable version. Receives only security fixes and bug fixes (patch releases). No new functionality is added.
- End of life — all older versions. No updates.
When a new stable version is released, each existing version moves one phase forward: the active version enters maintenance, and the version that was in maintenance reaches end of life. Users have one full major cycle to migrate before losing support.
Bug fixes are applied on the branch where they are most appropriate and then backported or forward-ported as needed.
Alternatives Considered
Independent major versions per component. Each component increments its own major version independently. This maximizes autonomy but makes it impossible to know which versions are compatible without an external compatibility matrix.
Calendar-based versioning (CalVer). Versions like 2026.03 indicate when a release was made but say
nothing about compatibility. This works for end-user products but not for libraries that need machine-readable
compatibility guarantees.
Rolling release. Always ship from main with no stable branches. Simple, but downstream consumers need
stable APIs they can depend on for more than one development cycle.
Consequences
- Clear compatibility rule. Same major version means compatible. Simple to understand for consumers and packagers.
- Independent pacing. Components release minor and patch versions on their own schedule.
- Freeze cost. Major releases require a global freeze, blocking feature development across all components.
- Forward compatibility only. Old consumers work with new providers, but not the reverse. Minimum dependency versions are enforced by the build system.
- Graduated support. The active version receives features and fixes; the previous version receives only security and bug fixes; older versions are unsupported.