0020. Semantic versions, one version source, a changelog and tagged releases
- Status: accepted
- Date: 2026-10-08
Context
Section titled “Context”Applications, plugins and teaching material build on shuffle_engine, and an
AI coding agent writes code against whatever the documentation says today.
Both need to know which version they have, what a new version breaks and
how to move. Until now the version appeared in three places (CMake,
CITATION.cff, nowhere in the code), there was no changelog and no release
procedure. The framework is young: most modules will still change shape
before their API settles, and a promise that cannot be kept is worse than
none.
Decision
Section titled “Decision”Version numbers
Section titled “Version numbers”- Semantic Versioning 2.0.0:
MAJOR.MINOR.PATCH, with a pre-release label for candidates (1.0.0-rc.1) and for the work between releases (0.3.0-dev, never released). - Before 1.0.0 (
0.y.z): a minor version (0.y) may break the public API; every break is listed inCHANGELOG.mdunder Changed or Removed with what to do instead. A patch version (0.y.z) never breaks it: fixes, documentation, new tests, nothing removed or renamed. - From 1.0.0: a break needs a new major version; a minor version adds; a patch version fixes.
- No binary compatibility is promised. The libraries are static and built from source with the application; rebuild everything on every update.
One source of truth
Section titled “One source of truth”- The version is declared once, in the top-level
CMakeLists.txt:project (ShuffleEngine VERSION x.y.z),SHUFFLE_ENGINE_VERSION_PRERELEASE(""on a release,"dev"between releases,"rc.N"for a candidate) andSHUFFLE_ENGINE_API_LEVEL. - CMake generates
shuffle/base/Version.h(kVersionMajor,kVersionMinor,kVersionPatch,kVersionPrerelease,kVersionString,kApiLevel, andSHUFFLE_ENGINE_*macros for the preprocessor) and the package version filefind_package (ShuffleEngine 0.2)checks. - The API level is an integer that grows by one with every minor or major
version and never otherwise, so code can select an API with
#if SHUFFLE_ENGINE_API_LEVEL >= 3without comparing version triples. tools/bump_version.pychanges the version,CITATION.cffand the changelog heading in one step;tools/check_repo.py(rule 7) fails when they disagree; the release workflow refuses a tag that is not the declared version.find_packagecompatibility:SameMinorVersionbefore 1.0, thenSameMajorVersion.
What the public API is
Section titled “What the public API is”The version number speaks for these, and only these:
| Surface | Includes |
|---|---|
| C++ headers | everything under core/<module>/include/shuffle/<module>/ and the include directories of adapters/; the fakes and contract suites under testing/ and contracts/ (an application’s tests use them) |
| Ports and their contracts | the interfaces and their documented rules: threads, SHUFFLE_RT marks, ordering, ownership, error values. A stricter rule for implementations (a new contract test an adapter may fail) is a break for adapter authors |
| Commands | dotted names, parameters (names, types, ranges, units), risk levels, results and snapshot schemas (specs/commands.md) |
| Protocol surfaces | the generated OSCQuery, OpenAPI, Companion, MCP and GBNF outputs, the ui_bridge protocol and its TypeScript client, the open tempo-sync protocol. Wire protocols also carry their own version field and negotiate it; their changes are still listed in the changelog |
| CMake | the package ShuffleEngine and its components, the imported targets shuffle::<module>, shuffle_engine_add_app, shuffle_engine_use, the project generator’s variables, the SHUFFLE_ENGINE_WITH_* options |
| Persisted data | show documents and settings keys the framework writes: a version reads what earlier versions of the same major wrote |
Not public: src/ directories, anything in a detail namespace, internal
CMake (cmake/ShuffleEngine.cmake), presets, tests, tools, benchmark
figures, log and error message wording (error codes are public).
Stability per module
Section titled “Stability per module”Each module is stable or experimental, derived from its spec:
- stable: the spec’s status is implemented (every requirement done). Before 1.0 a stable module breaks only in a minor version, and keeps a forwarding alias for one minor version wherever one is cheap (a type alias, a forwarding header, an inline wrapper).
- experimental: the spec is draft, reviewed or implemented in part, or the module is an adapter whose backend contract is still settling. It may change in any minor version, with a changelog entry and no deprecation period. After 1.0 it stays outside the SemVer promise until promoted.
baseandresamplehave no spec of their own; their contracts are those of the specs that use them.baseis stable;resamplefollows the modules that use it.- A module is promoted by the change that completes its spec; the module
README’s summary block and
docs/roadmap.mdsay which level it is at.
At 0.1.0 (amended 2026-10-09 by the maintainer: a smaller stable set for
the first tag, widened as modules are used in practice): stable base,
time, timecode, commands, midi, dsp; experimental every other
module (osc, resample, loudness, routing, setlist, cues,
showcontrol, netaudio, redundancy, synth, formats, engine,
ui_model, ui_theme, ui_bridge) and every adapter. A spec implemented
in full makes a module eligible for stable; the promotion is a changelog
line. The level is recorded in each module README’s summary block
(Stability) and in modules.md.
Deprecation
Section titled “Deprecation”- From 1.0.0: a public name is deprecated in a minor version, at least
one minor version before the major version that removes it. It carries
[[deprecated ("use <replacement>")]]naming the replacement (a CMake function prints aDEPRECATIONmessage the same way), and the changelog lists it under Deprecated. - Before 1.0.0: the same marks where they are cheap; otherwise the Changed or Removed entry is the deprecation, with the migration in one line.
- A deprecated name keeps working, with the same behaviour, until it is removed.
Platforms, compilers and pinned backends per release
Section titled “Platforms, compilers and pinned backends per release”- A release supports the five platforms of decision 0006 and WebAssembly, built by the CI of the tagged commit: Windows (MSVC, Visual Studio 2026), macOS and iOS (Apple Clang of the Xcode on the CI image, macOS 11 and later), Linux (GCC of the CI image; Clang 20 for the real-time and sanitizer jobs), Android (NDK, API 26, arm64), Emscripten. CMake 3.22 or later (presets need 3.25).
- The release notes record the compilers the release workflow actually used
and the pinned third-party versions from
NOTICES.md(JUCE, the Tracktion commit, Catch2, CPM, the codec libraries, miniaudio and the other fetched libraries; their hashes are incmake/ShuffleEngineDeps.cmake, the JUCE tag and Tracktion commit also in.github/workflows/ci.yml). - Changing a pin is a changelog entry under Changed. A patch release changes a pin only for a security or crash fix in that dependency.
Changelog and releases
Section titled “Changelog and releases”CHANGELOG.mdfollows Keep a Changelog 1.1.0. A pull request that changes a public surface adds its line under Unreleased;tools/check_repo.pywarns in CI when public headers, specs or the application CMake functions change without one (a warning, so small fixes are not blocked).- A release is an annotated tag
vX.Y.Zon a commit where CI is green;.github/workflows/release.ymlverifies it, builds and tests the cores on three operating systems, and publishes the source archives, the SPDX bill of materials, the documentation site and their SHA-256 sums. The procedure is docs/releasing.md. - Patch releases come from a
release/X.Ybranch cut from the tag, with fixes that landed onmainfirst.
Consequences
Section titled “Consequences”- An application can state
find_package (ShuffleEngine 0.2 CONFIG REQUIRED)and testkApiLevel; an agent readsCHANGELOG.mdto learn what moved. - Stable and experimental are visible per module, so early adopters know which parts may still change shape.
- Every public change costs one changelog line in the same pull request.
- No prebuilt binaries yet: releases are source archives. Prebuilt core libraries per platform and vcpkg / Conan ports are future work and will follow the same version.
© 2026 Umberto Sorbo. The software is licensed under the Apache License, Version 2.0, and provided “as is”, without warranties or conditions of any kind; see the disclaimer.
SHUFFLE Framework and the SHUFFLE Framework logo are trademarks of Umberto Sorbo; the licence grants no rights to them (trademarks). Other names belong to their owners. Fonts: IBM Plex Sans and JetBrains Mono, SIL Open Font License 1.1.
Contact: shuffleframework@gmail.com · Instagram @shuffleframework. Security problems: write to the same address with “Security” in the subject, not in a public issue.