Skip to main content

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
  • init validates and saves roots without scanning.
  • scan inspects all discovered repositories and records local decision events.
  • inbox reports repository states that are new, changed, or resolved since its previous snapshot.
  • history reads 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 by scan --check, an unhealthy doctor, 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 (partial or failed), 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

version_drift-1.0.0.tar.gz (55.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

version_drift-1.0.0-py3-none-any.whl (27.7 kB view details)

Uploaded Python 3

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

Hashes for version_drift-1.0.0.tar.gz
Algorithm Hash digest
SHA256 3abed0ed79bcbcf0f0666966e8e2fff54f820ccf5b633c14049deec2591dbb6d
MD5 8cf4b183f456e171f853255338cd5250
BLAKE2b-256 4f2a04236d81b93bd292e64ebc3301756a99aa5a5ffa8427945273ce2b087297

See more details on using hashes here.

Provenance

The following attestation bundles were made for version_drift-1.0.0.tar.gz:

Publisher: release.yml on seojoonkim/version-drift

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for version_drift-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 13b483363db4761e6362bcfa4afd5bc40f06314c558b457f86c84669252ed23e
MD5 41bc2c509ea7d2ee03ce992f93eeccdc
BLAKE2b-256 e34a5dcbc432f44f08c95a52a1ca11dfc0f301a69a465ae2fc2ecc3bb2311d23

See more details on using hashes here.

Provenance

The following attestation bundles were made for version_drift-1.0.0-py3-none-any.whl:

Publisher: release.yml on seojoonkim/version-drift

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.1.1

2 files

1.1.0

2 files

This release

1.0.0 This release

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page