VersionDrift
VersionDrift is a local-only, fail-closed safety gate for checking and selectively fast-forwarding Git repositories. It discovers repositories only below roots you provide, reports drift without changing working files, and blocks apply when repository state or topology is dirty, ambiguous, unsupported, or unknown.
VersionDrift does not provide general multi-repository command execution. Its only working-tree update is a verified clean fast-forward using exactly git pull --ff-only.
Requirements and installation
VersionDrift requires Python 3.9 or newer and Git on PATH.
uv tool install version-drift
# or
pipx install version-drift
For development:
git clone https://github.com/seojoonkim/version-drift.git
cd version-drift
python -m pip install -e .
Start with a local checkup
version-drift init ~/code ~/work
version-drift scan
version-drift inbox --fetch
initvalidates and saves roots without scanning.scaninspects all discovered repositories and records local decision events.inboxreports repository states that are new, changed, or resolved since its previous snapshot.historyreads the local JSONL decision trail newest-first.- Configuration precedence is command-line roots, saved configuration,
VERSION_DRIFT_ROOTS, then the current directory.
Without --fetch, observation uses existing local remote-tracking refs. With --fetch, scan, inbox, and inspect refresh refs before comparison. Sync fetches by default; select that behavior explicitly with --fetch or disable it with --no-fetch.
Safety contract
VersionDrift applies only a clean, attached, ordinary checkout that tracks an upstream and is behind-only. Unsupported or unknown topology is blocked, including detached HEAD, an in-progress Git operation, shallow repositories, linked worktrees, submodule checkouts, repositories containing tracked submodule metadata, and unreadable required Git metadata.
VersionDrift never automatically performs:
- reset, stash, or clean operations;
- merge or rebase operations;
- push or force operations;
- upstream guessing;
- arbitrary user-supplied Git commands.
sync --apply independently reinspects each repository immediately before pulling. A prior scan or plan is observation, not authorization. If the head, upstream, worktree fingerprint, eligibility, or required metadata no longer agrees, apply is blocked. The only pull command is git pull --ff-only; success is then reinspected and must be verified.
Commands
Scan
version-drift scan ~/code ~/work [--fetch] [--check] [--json]
--check exits 1 when drift is present. --max-depth N bounds discovery.
Inbox and configuration
version-drift init ~/code ~/work
version-drift inbox [--fetch] [--json]
Inbox snapshots are replaced atomically. A corrupt snapshot is preserved and causes a fail-closed error rather than silently replacing the baseline.
History
version-drift history
version-drift history ~/code/project --event scan --limit 20 --json
History is read-only: it never invokes Git, records events, updates snapshots, or creates missing state directories. Malformed JSONL lines are counted and skipped; an unreadable event file fails without being modified.
Inspect and explain
version-drift inspect ~/code/project [--fetch] [--json]
version-drift explain ~/code/project [--json]
explain converts inspection states into reasons and safe next actions. It does not fetch, change Git state, record an event, or update the inbox snapshot.
Plan or synchronize
version-drift sync ~/code --plan [--fetch | --no-fetch] [--json]
version-drift sync ~/code # dry-run preview
version-drift sync ~/code --apply [--fetch | --no-fetch] [--json]
sync --plan records structured decisions but changes no repository. A plan never authorizes a later apply. Dry-run sync also changes no repository. Apply takes a per-repository local lock, reinspects immediately before pull, and verifies afterward. Locks reduce duplicate local concurrency; they are not authorization.
Doctor
version-drift doctor [--json]
doctor performs read-only checks of the Python runtime, Git executable, configuration, event history, inbox snapshot, apply locks, and state directory. It reports issues but does not repair or delete files. See operations guidance.
JSON contracts and outcomes
The frozen v1 schemas are:
- inspection/event report:
version-drift/1 - scan envelope:
version-drift/scan/1 - sync envelope:
version-drift/sync/1 - plan envelope:
version-drift/plan/1 - doctor envelope:
version-drift/doctor/1
Other command schemas include version-drift/config/1, version-drift/inbox/1, version-drift/explain/1, and version-drift/history/1.
Scan, sync, and plan envelopes use complete, partial, or failed. These describe whether inspection or operations completed, not whether every repository was eligible. A policy-blocked repository can occur in a complete run. See COMPATIBILITY.md for the exact 1.x API and migration rules.
Exit codes
0: command completed under its command-specific policy.1: drift requested byscan --check, an unhealthydoctor, an unsuccessful inspection, a single-repository policy block, or another reported command failure.2: command-line usage or validation error, including argparse errors.3: sync operational failure (partialorfailed), distinct from an ordinary policy block.
JSON consumers should use the envelope outcome and per-repository reasons as well as the process exit code.
Local state and privacy
Default state paths:
macOS: ~/Library/Application Support/VersionDrift/events.jsonl
Linux: ${XDG_STATE_HOME:-~/.local/state}/version-drift/events.jsonl
inbox_snapshot.json and locks/ live beside events.jsonl. Configuration is at ~/Library/Application Support/VersionDrift/config.toml on macOS or ${XDG_CONFIG_HOME:-~/.config}/version-drift/config.toml on Linux. --base-dir and VERSION_DRIFT_DIR select an explicit state root; for compatibility, explicit roots use .version-drift/ beneath that root.
State contains local paths and Git metadata. VersionDrift sends no telemetry and does not upload repository paths, remotes, or results. Treat state files as private local operational data and do not put secrets in configuration. See THREAT_MODEL.md for boundaries and residual risks.
Compatibility
The VersionDrift 1.x public contract preserves legacy report fields and permits only additive, orthogonal fields and values that follow the documented compatibility rules. Removals or semantic breaks require a new major version. Safety invariants cannot be weakened for convenience.
Contributing and license
Bug reports and focused pull requests are welcome. See CONTRIBUTING.md. VersionDrift is licensed under the MIT License.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 version_drift-1.0.0.tar.gz.
File metadata
- Download URL: version_drift-1.0.0.tar.gz
- Upload date:
- Size: 55.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3abed0ed79bcbcf0f0666966e8e2fff54f820ccf5b633c14049deec2591dbb6d
|
|
| MD5 |
8cf4b183f456e171f853255338cd5250
|
|
| BLAKE2b-256 |
4f2a04236d81b93bd292e64ebc3301756a99aa5a5ffa8427945273ce2b087297
|
Provenance
The following attestation bundles were made for version_drift-1.0.0.tar.gz:
Publisher:
release.yml on seojoonkim/version-drift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
version_drift-1.0.0.tar.gz -
Subject digest:
3abed0ed79bcbcf0f0666966e8e2fff54f820ccf5b633c14049deec2591dbb6d - Sigstore transparency entry: 2552632162
- Sigstore integration time:
-
Permalink:
seojoonkim/version-drift@e3562cfdb3aebf55811885fb1cae9d9baac34b90 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/seojoonkim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e3562cfdb3aebf55811885fb1cae9d9baac34b90 -
Trigger Event:
push
-
Statement type:
File details
Details for the file version_drift-1.0.0-py3-none-any.whl.
File metadata
- Download URL: version_drift-1.0.0-py3-none-any.whl
- Upload date:
- Size: 27.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13b483363db4761e6362bcfa4afd5bc40f06314c558b457f86c84669252ed23e
|
|
| MD5 |
41bc2c509ea7d2ee03ce992f93eeccdc
|
|
| BLAKE2b-256 |
e34a5dcbc432f44f08c95a52a1ca11dfc0f301a69a465ae2fc2ecc3bb2311d23
|
Provenance
The following attestation bundles were made for version_drift-1.0.0-py3-none-any.whl:
Publisher:
release.yml on seojoonkim/version-drift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
version_drift-1.0.0-py3-none-any.whl -
Subject digest:
13b483363db4761e6362bcfa4afd5bc40f06314c558b457f86c84669252ed23e - Sigstore transparency entry: 2552632229
- Sigstore integration time:
-
Permalink:
seojoonkim/version-drift@e3562cfdb3aebf55811885fb1cae9d9baac34b90 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/seojoonkim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e3562cfdb3aebf55811885fb1cae9d9baac34b90 -
Trigger Event:
push
-
Statement type: