ADR 0003 — Versioning policy: strict-semver, conservative 1.0
- Status: Accepted
- Date: 2026-05-18
Context
SchemaBrain ships on PyPI asschemabrain. Downstream agent
frameworks, MCP host configs, and tooling will pin version constraints
that survive across releases. The MCP tool signatures, CLI argument
shapes, and the Store Protocol (ADR 0002) are the public API surface.
Two version numbers are easy to conflate:
- The roadmap milestone. Internal planning uses
v0/v1/v2/v3as platform milestones: v0 was preview, v1 is the semantic layer end-state, v2 adds theexecuteline, v3 is the hosted commercial line. - The semver number. PyPI shows
0.1.0a1,0.2.0a1,0.3.0, etc. — a release identifier with strict meaning under semver and PEP 440.
1.0.0 when the v1 roadmap milestone
lands. That collapses the two numbers and signals an API guarantee we
cannot honor — we have not yet had external users stress-test the
surface in production, and a tool rename in 2.0.0 three months
later is a worse signal than 0.3.0 → 0.4.0 would have been.
This ADR locks the rule so future contributors and reviewers don’t
relitigate it.
Decision
1. Stay on 0.x until the API is battle-tested
1.0.0 ships ONLY after BOTH:
- External users have used the MCP tool signatures, CLI args, and Store Protocol for at least several weeks without a forced break.
- Entity / metric / audit / join shapes have not had to change in response to real-user feedback.
0.x. The asymmetric cost
sets the bias: premature 1.0 → loud breaking change later (bad);
late 1.0 → “still on 0.x” looks slightly unfinished (cheap, easily
addressed by a release post).
2. Bump rules inside 0.x
| Trigger | Bump | Example |
|---|---|---|
| Bug-fix-only churn, no new public surface | alpha within minor | 0.2.0a1 → 0.2.0a2 |
| New layer / new MCP tool / new CLI command shipped | new minor | 0.2.0a1 → 0.3.0 |
| Alpha settled, no churn for a release cycle | stable minor | 0.3.0a* → 0.3.0 |
| Backwards-incompatible change to MCP / CLI / Store Protocol | new major | 1.0.0 → 2.0.0 (post-1.0 only) |
0.x do NOT need an alpha cycle first — the a* suffix is for
pre-stable churn within a minor, not for guarding every release.
3. The roadmap → semver mapping
| Roadmap milestone | Likely semver when shipped | Rationale |
|---|---|---|
| v1 (semantic layer end-state) | 0.3.0 (this release) | New tools, but API not battle-tested yet |
| v1 + 4–8 weeks of real usage with no breaks | 1.0.0rc1 → 1.0.0 | Earned stability |
| v2 (execute layer + fingerprint moat) | 1.x or 2.0.0 if v1 API breaks | Depends on whether execute can be added without breaking entity/metric tools |
| v3 (hosted commercial L6) | Independent versioning | Hosted product, not the OSS package |
4. Honest about specs vs convention
This policy stacks two specs plus one convention. Surface the layering so future contributors don’t argue past each other. What is strict semver:0.xsemantics: “API not stable, anything MAY change.”1.0.0semantics: “this defines the public API.”- Post-1.0 MAJOR / MINOR / PATCH meanings.
- Treating
0.MINORbumps as “new feature shipped” and0.PATCHas “fix only.” Semver itself says nothing about bumping inside0.x; technically every0.xchange could be a patch bump. We follow Cargo because it communicates intent.
0.2.0a1(no separator beforea). Pure semver writes0.2.0-alpha.1. PyPI requires PEP 440, so we follow it.
1.0 sooner. We deliberately
choose “battle-tested” over “in production” as the 1.0 trigger.
That is an interpretive choice, not a spec mandate. The asymmetric
cost of breaking an agent-facing API justifies the conservative
reading.
5. When to revisit
This policy is settled. Re-litigate ONLY if:- External users explicitly request a stable
1.0for pinning. - A framework integration requires a
1.xline (e.g. an MCP host policy refuses pre-1.0packages). - Substantive feedback from real deployments confirms the entity / metric / audit / join shapes are stable.
Consequences
What becomes possible:- Honest release notes: “v1 — the semantic layer is here” can ship
as
0.3.0without lying about API stability. - Breaking changes inside
0.xare cheap. A futureentities applyrename that surfaces in user feedback at0.3.0lands cleanly in0.4.0without breaking the semver contract. - Downstream pinning becomes a deliberate choice. Users pinning
>=0.3.0,<0.4.0get an explicit signal that minor bumps may break.
- Every PR that adds public surface (CLI command, MCP tool, Store Protocol method) is a minor bump. PRs that are bug-fix-only stay on the current minor and bump the alpha suffix.
- Release commits carry a single change to
pyproject.tomlplus a matching CHANGELOG entry. The Development Status classifier moves with the version:Pre-Alphafor0.x.0a*,Alphafor0.x.0past the first stable minor,Betaafter the first six months of no-break stable releases,Production/Stableafter1.0.
- The exact
1.0.0trigger criteria — “several weeks” is intentionally vague; the call is made when the evidence is in. - Whether v3’s hosted line uses the same package or a separate one
(e.g.
schemabrain-cloud) is a v3 decision, not a versioning decision.
References
- Semantic Versioning 2.0.0.
- PEP 440 — Version Identification and Dependency Specification.
- Cargo’s “Why does Cargo use SemVer?”
— the source of the
0.MINOR“new feature” convention.