onevcs
Version control and its remote host behind one host-neutral vocabulary, for agent workflows.
The review unit is a change request — GitHub maps it to a pull request, and a
later host maps it to whatever it calls the same thing. Vcs owns the repository
side (identities, sessions over an isolated worktree, preserved work);
RemoteHost owns the host side (opening a change, reading its checks, merging
it); a rules file decides, per repository, how a change publishes and what
verifies it. Everything a run does is emitted as an NDJSON event stream.
The public surface is the approved contract — docs/contract.md
— compiled, and it is implemented: the registry with its lazy migration, the rules
engine, sessions over borrowing clones, bounded git, the FIFO merge queue, both
publication strategies, recovery and provenance, the merge train, and the event
stream.
What one change looks like
onevcs register ~/projects/widgets # once per checkout
onevcs rules check widgets # which policy, and why
token=$(onevcs session open widgets --branch feature/thing | jq -r .token)
# …work in the worktree the session printed…
onevcs publish "$token" # verify, then land it
onevcs events "$token" # everything it did, as NDJSON
A branch that outlived the session that cut it is landed by name instead, under
that same rules-resolved policy: onevcs publish-branch feature/thing --repo ~/projects/widgets for work that finished, and onevcs recover for a step that
stopped half way, which publishes it with the attestation that a green gate
cleared it. Whichever of the three refuses a branch names the one that takes it.
onevcs status REF answers what became of a piece of work, asked by whichever
name you hold — a change request's URL, a session token, a branch, or a commit. It
reports the identity's resolved policy, the session, every checkout and per-run
clone holding the branch, whether the change landed, the host's checks, the
last gate verdict, and the command that advances it. A change that squash-merged
reads as landed rather than as unpublished, and a host that cannot be reached
leaves its section unavailable instead of failing the command.
onevcs import BRANCH --repo PATH [--from SOURCE] [--as NAME] makes a branch
reachable from an identity's registered checkouts, so a later run's clone can see
work a stopped run left in its own. It writes refs and nothing else — no checkout,
no working tree — and refuses a non-fast-forward overwrite by naming the commits
it would lose.
onevcs sweep [--dry-run] [--min-age-hours HOURS] reclaims the workspaces those
landings leave behind. Every branch published by name cuts a run root — a clone, a
worktree, and the gate's preserved logs — under the state root, and until this verb
existed nothing removed one; thirty-one of them filled a host's disk twice in a
single run. It removes a workspace only where this tool can prove it is finished:
its gate recorded a verdict, no live session holds its occupancy lease, it was last
written outside the age floor --min-age-hours sets, and removing it is something
this host can do at all. Everything else is retained and reported with the reason —
a workspace somebody is still publishing in is never removed and never terminated,
and neither is one belonging to another manager on a shared state root — and the
per-run lifecycle clones a session keeps as recovery history are outside the verb
entirely.
Everything durable lives under one state root — ONEVCS_HOME, otherwise
~/.onevcs.
GitHub is reached through gh, so whatever gh auth status reports is the
credential. A fine-grained personal access token needs Actions: Read on the
repository beside its contents and pull-requests access: GitHub will not let that
credential class resolve a check run at all — there is no Checks permission to
grant one — so a change request's checks are read from its workflow runs, and
change_checks says so in the sources it answers with. Anything a third-party
integration posted as a check run or a commit status is invisible to such a token,
and a credential that can read neither source is refused rather than reported as
having no checks.
Install
cargo install onevcs # crates.io
pip install onevcs-cli # a prebuilt wheel, no Rust toolchain
npm install -g onevcs-cli # a prebuilt binary, no Rust toolchain
All three install the same onevcs binary. Prebuilt binaries exist for Linux
(x64, arm64), macOS (x64, arm64), and Windows (x64); every release also attaches
the archives and their .sha256 checksums for a direct download.
Use
onevcs --help
--help is the command surface, and publish reserves its own exit codes for a
gate that failed, a request that was invalid, and a base that moved under it.
Embed it
A command answers a process: an exit code and a line of prose. A caller embedding the crate wants the decision, so the same operations answer values.
let published = onevcs::publish(&providers, &token, &PublishRequest::default())?;
match published.outcome {
PublishOutcome::Merged(sha) => journal.landed(sha),
PublishOutcome::ChangeOpen(url) | PublishOutcome::Queued(url) => journal.awaiting(url),
PublishOutcome::NothingToPublish => journal.nothing(),
PublishOutcome::Failed { kind, reason, retained } => journal.failed(kind, reason, retained),
}
close_session and session are the same for the rest of a session's life, and
EventStream::open(&token) reads its events as Envelopes, each attributed to
the session that wrote it — so a caller following several publications at once can
tell them apart. The command line is a rendering of these rather than a second
path through them.
A consumer that wants some of what a session writes reads it through the filter
grammar the three producing libraries share — EventStream::open_filtered(&token, filter), or onevcs events TOKEN --filter SPEC with the spec inline as JSON or in
a file. An envelope passes when it matches any include matcher (or include is
absent) and no exclude matcher, matching source by family, kind by glob
(change-*), and the reserved label keys exactly:
include:
- {source: vcs, kind: "gate-*"}
exclude:
- {kind: lock-wait}
Before a caller has a token, session_holders(repo) answers who is in a
repository: one SessionHolder per recorded session, carrying the token the calls
above take, the branch and worktree it holds, and whether its owner is still
running (Liveness::Live) or the session is the remains of a run that stopped.
Test against it, without a real GitHub
Embedding the crate, run_with(&cli, providers) takes the two implementations a
run reaches Vcs and RemoteHost through; run is that with Git and GitHub,
and every entry point above goes through the same seam.
onevcs-testing ships in-memory and file-backed
implementations of both, so a consumer's suite drives a real onevcs through a
real journey against a host it seeded:
cargo add --dev onevcs-testing
They emit the events the real implementations emit — a claim this repository's own suite checks by running one publication journey on both backends and holding the two event streams to each other.
Develop
just bootstrap # from a clean clone
just check # the deterministic gate: format, clippy, tests, coverage, docs
just gate # check, plus the diff-scoped LLM-judge tier — the pre-push bar
just --list is the full index.
License
MIT. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file onevcs_cli-0.8.0-py3-none-win_amd64.whl.
File metadata
- Download URL: onevcs_cli-0.8.0-py3-none-win_amd64.whl
- Upload date:
- Size: 1.2 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
613e41ea25b7279d982daf7b2540cad2fed609fca90ccab745cfcc28d5f14d60
|
|
| MD5 |
8966c1f4c79eca9982473754abb0baa0
|
|
| BLAKE2b-256 |
7f21602f8b6be2b0e502b9886a190d42db41cf7ced90721073d7000da044205a
|
File details
Details for the file onevcs_cli-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: onevcs_cli-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.2 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
47722e622151c7b9faff5241dd74559dc484c00cd8a9e5b6727c61760d740108
|
|
| MD5 |
5db431ab66b191b5dff1376ef7f17376
|
|
| BLAKE2b-256 |
ce8a597450f585c1457645e18f42423e16e05bd333c7413eff0090c81f221c23
|
File details
Details for the file onevcs_cli-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: onevcs_cli-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 1.1 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
02ec63d24f8e3d310e8822f784ca19a03c9e1938ed5f9c1d68634f599af32c27
|
|
| MD5 |
3604dabae6b02b8824e701a3e47ae1df
|
|
| BLAKE2b-256 |
321019c7898ce3c56b763fc17710c2a1d75e662f3b0af7a68494278899b02917
|
File details
Details for the file onevcs_cli-0.8.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: onevcs_cli-0.8.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.1 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
281781eb3fe159285cb554667e38808c6552c00f5cce32956c6dce06e0760817
|
|
| MD5 |
d6ab0a146bb1e638c02e1fee37f0e954
|
|
| BLAKE2b-256 |
11935479882abbb7ba811bfbfe01e69a8e669d04303fb2bdcee699a093eed98c
|
File details
Details for the file onevcs_cli-0.8.0-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: onevcs_cli-0.8.0-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 1.2 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d0da238f7bc9e3bb87cfde56cc1dfaaaed969643f29c616315cb52fa7f78d34
|
|
| MD5 |
9d388605cc09eb2b4379f61f0e80fbfa
|
|
| BLAKE2b-256 |
9d6ed42ac4a3fcd08329355a675220e0dea0bc7fc331994dcda5a404929ff476
|