ruflo has shipped thousands of releases. The number is
3.25.4right now. If you were handed that string cold — no changelog, no README — what could you tell me about it? "It's newer than 3.25.3." That's it.Meanwhile,
3.25.4shipped 40 new MCP tools, a new agent type, a memory-schema tweak, and re-signed the witness manifest — all invisible in the number. That's not a versioning discipline problem. It's a versioning scheme problem.This gist explains Agent-Native Versioning (ANV) — a small, backward-compatible change to how ruflo versions itself so the string carries actual information instead of ordering alone.
Semver's contract is: MAJOR = you broke something, MINOR = you added something backward-compatible, PATCH = you fixed a bug. It works if — and only if — every maintainer bumps correctly and every change fits one of those three shapes.
A 2023 arXiv paper analyzed the entire npm registry and found that ~33% of releases introduce a breaking change regardless of whether they're marked MAJOR, MINOR, or PATCH (Pinckney et al., arXiv:2304.00394). Semver adherence "has only marginally increased over time." So the number-only-encodes-ordering problem isn't a ruflo failing — it's the empirical general condition of npm-labeled software.
For an ordinary library, "did the CLI signature change" is a reasonable proxy for "did anything important change." For an agent framework, it isn't:
- We add 40 new MCP tools. CLI didn't change. Semver says MINOR at most. Reality: enormous capability expansion.
- We swap a routing model. CLI didn't change. Semver says PATCH. Reality: every downstream agent's behavior may shift.
- We update the skill catalog. CLI didn't change. Semver says nothing. Reality: the agent's knowledge got replaced.
The interesting signals in an agent framework — skill catalog, tool surface, memory schema, benchmark tier, model routing policy — are exactly the ones semver was never designed to carry.
The proposal, in one sentence: keep semver exactly as it is, and attach a structured suffix that carries the agent-native signals.
We call the two eras BC and AD:
- BC — "Before Catalog" — every release up to and including
ruflo@3.25.4. Plain semver, no suffix, no lineage information carried in the string. - AD — "Agent Descent" — every release from
4.0.0onward. Same semver core, samenpm install, plus a structured suffix.
Between them is a bridge: 3.26.x through 3.x.x will emit the AD suffix but not enforce it. This lets tooling learn to read the new format before it's mandatory. It's the same pattern Python used with six and __future__ imports before the 2→3 cutover.
4.0.0 is the epoch boundary because ruflo's own bump rules say a required-file addition and a change in default runtime behavior are MAJOR events. AD makes catalog-manifest.json a required shipped file, and old tooling that can't read the suffix will misinterpret catalog-generation events. So the transition to AD is the MAJOR bump — not something we tack onto one.
Here is a full AD-era version identifier:
3.26.0+ad.1.g8f3a91c.cat42.hal74
Decoded segment by segment:
| Piece | What it means | Why it's here |
|---|---|---|
3.26.0 |
Ordinary semver. npm i ruflo@^3.25.0 still resolves this. ~3.26.x still works. Nothing changes. |
Semver's ordering + range resolution is the part that works. Don't fix what isn't broken. |
+ad |
Epoch marker. Its presence says "this is an AD-era release." Absence = BC. | Everything after + in a semver string is called build metadata — semver.org §10 defines it as precedence-inert. Tools ignore it for ordering. It's the legal free-form slot the spec already gave us. |
.1 |
Generation counter. This is the 1st generational release since the AD epoch was declared. | Answers "how far into the era are we." Ruflo's cadence isn't calendar-driven, it's learning-driven — generation is the right unit. |
.g8f3a91c |
Git SHA of the tree that produced this release's agent catalog. Answers "which commit taught this release its skills." | HuggingFace's revision parameter, LangChain Hub's commit-pinned prompt lineage. Both are working precedents for content-hash-versioned catalogs in AI tooling. |
.cat42 |
Catalog generation number. Bumped whenever the agent-type roster, MCP tool surface, skill library, or memory schema changes in a way integrators need to know about — independent of whether the CLI API changed. | This is the direct answer to "we added 40 MCP tools and no one could tell from the version." HuggingFace's own 2024 paper (arXiv:2409.10472) flagged this as an unsolved gap in ML-release versioning. We solve it for our catalog rather than waiting on a standard. |
.hal74 |
Bucketed GAIA/HAL benchmark tier as of last verified run. hal74 means "in the 74–75% band." |
GAIA/HAL is the one benchmark ruflo already has first-party, verifiable submit tooling for. Bucketed (not decimal) to avoid implying false precision and to keep the string stable across noisy re-runs. |
Nothing about this string changes how npm behaves. npm install ruflo@latest, npm install ruflo@^3.26.0, npm install ruflo@3.26.0 all resolve exactly the same way they resolved for 3.25.4. The suffix is legal semver-2.0 syntax that every conformant tool ignores for precedence purposes.
The design goal was: a senior engineer looking at a version string should be able to answer, without a changelog:
- What generation of the framework is this? (
ad.1— first AD-era generation.) - What agent catalog did it inherit? (
g8f3a91c— that git tree.) - How different is its capability surface from the previous release? (
cat42vs. what came before.) - Is it benchmark-verified, and roughly where does it sit? (
hal74— 74th percentile band.)
That's four independent signals, not one arbitrary integer. And each maps to a real, verifiable thing in the repo:
- The git SHA is the actual commit.
git show g8f3a91c:catalog-manifest.jsonshows you the exact catalog. - The catalog counter is checked against
catalog-manifest.jsonat publish time — you can't lie about it. - The benchmark tier requires an Ed25519-signed submission via the existing
gaia-submittooling — no unverified claims allowed. - The generation counter is auto-bumped based on whether the catalog counter moved.
So the string carries a lineage — every AD release is traceable to (a) a git commit that produced it, (b) a catalog manifest that was signed at that commit, (c) a benchmark result that was verifiably submitted. The number stops being arbitrary and becomes a fingerprint.
Nothing changes. ruflo@latest still resolves. ruflo@^3.25.0 still resolves. ruflo@3.26.0 still resolves. If you never look at the suffix, you never have to know it exists.
$ ruflo version --explain
Installed: ruflo@3.26.0+ad.1.g8f3a91c.cat42.hal74
Era: AD (Agent Descent) — 1st generation
Lineage: git commit 8f3a91c (view: gh browse -c 8f3a91c)
Catalog: generation 42
agents: 61 types (+1 since cat41)
tools: 318 MCP tools (+4 since cat41)
skills: 47 skills (0 new)
memory: schema v3 (unchanged)
Benchmark: GAIA tier 74 (74–75% band, verified 2026-07-06)
signed by: <ed25519 pubkey fingerprint>
submission: hal-leaderboard.io/submissions/abc123Everything printed here comes from files shipped with the package. No API call, no GCP credential, no leaderboard fetch required at read-time. The signing checks happen at publish-time.
$ ruflo version --require-catalog-gte 40
OK (installed catalog is 42)
$ ruflo version --require-hal-gte 75
FAIL (installed is hal74; need hal75+)Cheaper than a runtime probe. Composable with npm scripts. This is the "capability negotiation" precedent from TLS suite versioning and gRPC-Web, adapted to a CLI framework.
$ ruflo lineage 3.26.0 3.25.4
BC → AD transition span
3.25.4 (BC-era, no catalog manifest)
↓ diff (37 commits, git log 3.25.4..3.26.0)
↓ ADR-184 declared AD epoch
↓ catalog-manifest.json added (catalog gen 42, first shipped)
3.26.0+ad.1.g8f3a91c.cat42.hal74The tool doesn't have to guess what changed. It reads the manifest.
Roughly, from the ADR's migration plan:
Phase 1 — Instrumentation (weeks, 3.26.0)
- Add
catalog-manifest.jsonschema and generator script - Wire it into
prepublishOnlyalongside the existingsign-helpers.mjs+witness/regen.mjs ruflo version --explainreads the manifest and prints- Suffix is emitted, but tooling doesn't require it yet
- Old scripts consuming raw
3.26.0keep working; nothing enforces the suffix - ANV suffix is advisory — a discipline the maintainer opts into
Phase 2 — Adoption (months, 3.27.x–3.x.x)
- Downstream tools (dream-cycle coordinator, business pods, federation trust) learn to read
+ad.*.cat*.hal* ruflo doctorstarts warning if suffix is missing- Benchmark verification hook (Ed25519-signed HAL submission required to claim a tier)
- Federation handshake starts negotiating on catalog generation, not raw semver
Phase 3 — Epoch declaration (4.0.0)
- Suffix becomes mandatory in publish
catalog-manifest.jsonbecomes a required file (this is what makes it a MAJOR bump under ruflo's existing rules)- CI enforces "if catalog counter didn't move, catN can't advance"
- Old tools that can't read the suffix will misread a catalog-generation event — that's a real behavior change, hence MAJOR
Phase 4 — Deprecation of BC assumptions (4.x+)
- Anything that treats a raw semver string as complete signal is a bug
- Federation refuses handshakes with peers that don't advertise
+ad - BC-era releases stay installable forever (npm never forgets), but tooling stops assuming BC-shape strings
Phase 5 — Lineage-hash upgrade (post-4.0.0, tied to ADR-170)
- The
g<sha>segment is upgradable to a content-addressed CID (Merkle-DAG) once ADR-170's agenticow memory-substrate matures - Grammar reserves this slot as
g<sha> | c<cid>— either resolves to a tree - No breakage on the transition — the tool just knows how to fetch both
Every phase is additive. Nothing removes an option. Users on 3.25.x are undisturbed.
The research report graded 21 versioning approaches. Several sound attractive; here's why they were rejected:
- Pure CalVer (
2026.07.08) — good for calendar-cadence software (Ubuntu, HA), useless when the interesting event is a benchmark regression or a catalog change, not a date. Rejected as primary; used only as timestamp metadata in the manifest. - Pure content-hash (
ruflo@sha256:abc…) — Nix's model. Great lineage, breaks npm range resolution completely. Existing^3.25.0installs would stop resolving. Non-negotiable requirement was that they keep resolving. Rejected as identifier; kept as internal lineage segment. - A prose changelog in the version string — tried by no one for a reason. Rejected before it was drafted.
- New package manager — solves the versioning problem by breaking the install surface for millions of users. Rejected on requirement grounds.
- Bake a live benchmark score into the primary version — creates a gaming incentive and makes the string change on every re-run. The bucketed tier segment (
hal74) with a verification gate is the survived compromise. - Multi-parent lineage (federated merges — several parents at once) — grammar reserves space (
.g<sha1>+<sha2>), but no surveyed precedent has a working UX for this in a package-manager-distributed tool. Explicitly out of scope for v1; revisited when federation-merge use cases become real.
Three concrete risks the ADR names and doesn't handwave:
- The catalog-generation ledger needs new schema work, not a copy of ADR-103's witness-manifest. Witness tracks fix presence; catalog tracks capability surface. Reuse the signing machinery, do not reuse the schema.
- A benchmark tier in the version string creates gaming incentive. Every prior scheme surveyed either (a) didn't include benchmarks at all or (b) included them advisorily. The ADR's hard verification gate (Ed25519-signed HAL submission required to claim a tier) is a new requirement, not a copy of prior art. If it's not enforced rigorously, this becomes a lie-detector problem.
- Multi-parent lineage has zero UX precedent in a package-manager-distributed tool. The grammar reserves the slot; the ADR is explicit that v1 defers it. This is a landmine when federation matures — better to know it's there than to trip on it.
Two reasons:
- The cost of doing it later scales with time. Every ruflo release from now until the transition is another release where "what actually changed" has to be dug out of a changelog. Every downstream tool built now bakes in the assumption that
3.x.ycarries no lineage signal. Rewriting those tools in year 3 is more painful than adding a suffix in year 0. 4.0.0is coming anyway. Ruflo's roadmap has multiple MAJOR-shape events queued (deep integration with agentic-flow, federation MVP, business autopilot GA). Declaring AD at the same time avoids stacking two "you have to relearn" moments into two adjacent releases.
- Not a claim that semver is broken. Semver's ordering + range resolution work fine and we're keeping them.
- Not a new package manager.
npmandpnpmcontinue to be the install path. - Not a breaking change for existing users. Everyone on
3.25.xstays on3.25.x, forever, if they want. - Not a "trust us, we invented something clever." Every element of the scheme is cited to a real precedent — semver.org's own spec, HuggingFace's
revision, LangChain Hub commits, OCI's tag+digest split, Python's__future__, MCP's date-anchored capability negotiation. - Not a mandate the day it's published. Phase 1 is instrumentation only. If it's not carrying its weight by the time
4.0.0is due, it doesn't ship as mandatory.
- ADR-184 —
v3/docs/adr/ADR-184-agent-native-versioning-scheme.md— the formal decision, 498 lines with an EBNF grammar, a worked decode example, migration phases, rejected alternatives, risks, FAQ, and glossary. - Research report —
v3/docs/reviews/versioning-sota-research-2026-07-08.md— 353 lines, 21 schemes graded A/B/C across 9 research surfaces, with per-source citations. Every element of ADR-184 is traceable back to a specific graded finding here.
- Is
4.0.0the right epoch boundary, or should we letcatalog-manifest.jsonland in3.26.xand defer AD until a natural MAJOR event downstream? halas the benchmark bucket assumes GAIA/HAL is the durable benchmark. If a better one appears (Terminal-Bench, EvoAgentBench), does the segment become<bench-name><tier>(e.g.evo74) with the benchmark name inline, or do we pick one canonical bench and stick with it?- Do we ship
ruflo version --explainas a subcommand ofruflo, or as a standaloneruflo-versionbinary so it can be shelled out to from any language? - The
g<sha>→c<cid>upgrade is Phase 5 and gated on ADR-170. Is there value in reservingcgrammar space earlier so tooling can distinguish "will be a CID one day" from "is a CID today"?
Written 2026-07-08. Corresponds to ADR-184 (Draft). This gist is the human-readable brief; the ADR is the authoritative decision document.