Expand description
§Stability, versioning & the path to 1.0
How far each crate has settled, what a version number promises, the MSRV
contract, and the gate this workspace holds itself to before tagging a 1.0.
§Versioning
Every crate is versioned and published independently and adheres to
SemVer; each has its own CHANGELOG.md
(Keep a Changelog) and is tagged
<crate>-v<version>. There is no workspace-wide version.
While a crate is pre-1.0 (0.x), a SemVer minor bump (0.x → 0.(x+1))
may carry breaking changes and a patch (0.x.y → 0.x.(y+1)) is
non-breaking — the standard Cargo ^0.x interpretation. Breaking changes are
allowed at 0.x and called out in the changelog’s ### Changed/### Removed.
After a crate reaches 1.0, it switches to strict SemVer: breaking changes
require a major bump.
Because the crates depend on each other (vcs-core → vcs-git/vcs-jj; the
wrappers → the foundational vcs-diff/vcs-cli-support), each intra-workspace
dependency carries a ^MAJOR.MINOR requirement that must stay in range when a
dependency crosses a boundary — see the release process in
AGENTS.md and the publish ordering in release.yml.
§Stability tiers
All crates are pre-1.0 today — the API may still change. Relative maturity:
| Crate | Version | Tier |
|---|---|---|
vcs-diff | 0.3 | settling |
vcs-cli-support | 0.3 | settling |
vcs-testkit | 0.3 | stable-ish (dev-only) |
vcs-git | 0.7 | maturing |
vcs-jj | 0.7 | maturing |
vcs-github | 0.7 | maturing |
vcs-core | 0.5 | evolving |
vcs-gitlab | 0.3 | evolving |
vcs-gitea | 0.3 | evolving |
vcs-forge | 0.3 | evolving |
vcs-watch | 0.3 | evolving |
vcs-mcp | 0.3 | evolving |
What the tiers mean — and the per-crate caveats:
- settling — close to its 1.0 shape; small and pure.
vcs-diff(diff model + parser,Version) andvcs-cli-support(the argv guard, fetch policy, error classifiers) are narrow surfaces unlikely to change much. - stable-ish (dev-only) —
vcs-testkitis a dev-dependency only, so its churn never reaches a release build. - maturing — broad, consumer-validated surfaces that still grow additively:
vcs-git,vcs-jj(tracks jj, whose CLI/template surface churns — see the CI version matrix), andvcs-github(theghPR/issue/run/release surface). - evolving — the surfaces with the most movement among the released crates; still
growing (often additively), with the empirically-validated CLI argv/JSON occasionally
shifting.
vcs-core’s common facade grows as cross-backend needs surface (e.g.snapshot).vcs-gitlab/vcs-giteaare lean MR/PR lifecycles, mostly fixture-pinned (vcs-giteais the narrower of the two — see its capability notes);vcs-forge’s unified DTOs grow with the wrappers;vcs-watchis the workspace’s first runtime-tokio + streaming API (its event set may still shift);vcs-mcpis the MCP server (a lib + binary), whose tool catalogue, names, and JSON shapes will grow.
§MSRV policy
The minimum supported Rust version is 1.88 (edition 2024 needs 1.85, but the
wrappers use let-chains, stabilised in 1.88). It is declared once in
[workspace.package] as rust-version = "1.88" and inherited by every crate, so
cargo build on an older toolchain fails early with a clear message — the
contract is machine-checked, not just documented.
An MSRV bump is treated as a minor-version change (a 0.x minor today; a
major after 1.0 if a consumer pins an older toolchain) and is called out in the
changelog. We bump the MSRV only when a dependency or a genuinely useful language
feature requires it — not casually.
§Public-API review checklist (the 1.0 gate)
Before any crate is tagged 1.0, its public surface is reviewed against the
invariants this workspace already holds (most are enforced today; 1.0 makes them
a promise):
- Trait object-safety & mockability. Every public trait stays object-safe
(no generic methods, no nested-reference lifetimes — owned
&[String]/Option<String>, not&[&str]/Option<&str>), so&dyn Apiandasync-traitkeep working. The wrapper traits (GitApi/JjApi/GitHubApi/GitLabApi/GiteaApi) are additionallymockall-friendly: themockfeature generates aMock*Api. The facade traits (VcsRepo,ForgeApi) deliberately carry nomockfeature — they’re generated by amacro_rules!whose:tysignaturesmockall::automockcan’t parse — so the facades are tested over a fake runner (Repo::from_git/Forge::from_github), which is their documented test path anyway. #[non_exhaustive]on returned types. Every struct/enum a consumer reads (DTOs, parsed results) is#[non_exhaustive]so a new field/variant isn’t a breaking change — except deliberate value types (Version) that callers legitimately construct.- Structured errors. Failures surface as
processkit::Errorvariants (Exit/Timeout/Spawn/Parse), never a stringly-typed blob; the facade adds only repo-detection variants. Classifiers (is_merge_conflict, …) give intent without matching on internals. - Injection-safe by default. Caller strings in bare positional argv slots are
guarded (
reject_flag_like); flag-value slots are documented as exempt. - No leaked internals. Re-exports are explicit (no glob leaks); private
parsers/helpers stay private; the
cli_client!seam isn’t part of the surface. - Docs + tests. Every public method has a doc comment and at least a hermetic
test pinning its argv/parse; the pure parsers are property-tested for
panic-freedom; real-binary behaviour is covered by the
#[ignore]integration suites (CI version-matrixes jj).
§See also
- AGENTS.md — the release process, changelog curation, and dependency conventions.
- Process model & errors — the error model the API review references.
- ROADMAP.md —
6.12and the remaining path-to-1.0 work.