Three Zeros
Semantic versioning has three slots: major, minor, patch. The promise lives in the major slot. While major is zero, anything goes. While major is one or more, breaking changes mean a new major.
That works. But “anything goes” is too coarse. There is a real difference between I am building the first usable thing and I have the first usable thing and am iterating on it. Most projects do not distinguish, and users feel the difference anyway — they just have to read the changelog to find out.
I am building Muxon right now and I want the version number to carry the distinction. Three zeros, three promises given up. Each transition adds one back.
0.0.0 — nothing
The repository exists. The crates compile. The plan is written. There is no implementation. You could clone it and cargo build would succeed; you could not use it for anything.
The promise that is missing: code that does what the project is for.
0.0.x — building the first usable thing
Patch versions while major and minor are both zero are pre-MVP. Each patch lands one piece of the road to a usable workflow. Daemon handshake. Durable state. Directory binding. The Mux trait. The pieces appear; the workflow does not, yet.
A user running 0.0.4 cannot do anything end to end. They can poke at parts and watch them work, but the parts have not met. The code exists. The product does not.
The promise that is missing: a usable end-to-end workflow.
0.1.0 — the first usable thing
The minor bumps from zero to one when there is something to use. For Muxon, that is muxon --dir . opening a directory-bound workspace and reopening it restoring the layout. A user can do something real.
This is the gate everything before was building toward. The MVP, in the honest sense of the word.
0.x.0 — iterating on the usable thing
Once the MVP exists, minor bumps add capability. Stateful capture. The first aigent. The capability handshake. Editors. Remote. Each bump lands a meaningful slice. Compatibility may still break — the surfaces are absorbing real use, finding their right shape.
The promise that is missing: stable surfaces.
1.0.0 — the freeze
The major bumps from zero to one when the surfaces have settled. 1.0.0 ships nothing new on top of the last 0.x.0. It changes only the policy: from now on, no breaks.
The promise that is gained: compatibility.
Why three zeros
Each zero, in this scheme, represents a missing promise. Each transition adds one back.
- 0.0.0 → 0.0.1: code now exists.
- 0.0.x → 0.1.0: a usable workflow now exists.
- 0.x.0 → 1.0.0: stable surfaces now exist.
Standard semver collapses the first two transitions into one. Anything below 1.0 is “early”. That is enough information for a published library where users can wait for 1.0 before adopting. It is not enough for a project shipping in public, where the difference between still bootstrapping and usable but evolving is the difference between do not bother yet and try it, but pin your version.
Three numbers, three audiences:
- 0.0.x reads as wait.
- 0.x.0 reads as try it, but pin.
- 1.x.0 reads as use freely; we will not break you.
Rust did this implicitly
Pre-1.0 Rust shipped usable software. Each release broke things. The distinction between early-bootstrapping Rust (you could not write much yet) and late-pre-1.0 Rust (you could write real code, but the stdlib churned) was real, and it shaped which kind of user belonged on each version.
The version number did not carry that information. The community knew it from context.
The extension makes the context explicit.
Zero is a thing, not a placeholder
Zero is not a default. It is not “nothing yet, ignore”. It is a specific thing that does not exist yet, and naming it three different times tells the user exactly what is missing.
Code. Workflow. Compatibility.
Three zeros. Three promises. One number that means what it says.