Skip to main content

Deterministic architectural drift detection for AI-accelerated Python repositories through cross-file coherence analysis

Project description

Drift

Deterministic cross-file coherence analysis for Python codebases

CI PyPI Python versions codecov License

docs · benchmarking · full study · trust & limitations · FAQ

Drift detects structural erosion that accumulates across files: the same error handling done four different ways, database imports leaking into the API layer, AST-level near-duplicate helpers across modules. These problems pass existing tests but make the codebase progressively harder to change.

The analysis is deterministic (no LLM in the pipeline) and produces findings with file locations, severity, and a suggested next step. Precision upper-bound estimate: 77 % strict / 95 % lenient on the historical v0.5.0 ground-truth baseline (score-weighted sample of 286 findings, 5 repos, single-rater — not yet independently replicated). See Trust and limitations for full caveats.

pip install drift-analyzer        # requires Python 3.11+
drift analyze --repo .
╭─ drift analyze  myproject/ ──────────────────────────────────────────────────╮
│  DRIFT SCORE  0.52  Δ -0.031 ↓ improving  │  87 files  │  AI: 34%  │  2.1s │
╰──────────────────────────────────────────────────────────────────────────────╯

  Module                  Score  Bar                   Findings  Top Signal
  src/api/routes/          0.71  ██████████████░░░░░░       12   PFS 0.85
  src/services/auth/       0.58  ███████████░░░░░░░░░        7   AVS 0.72
  src/db/models/           0.41  ████████░░░░░░░░░░░░        4   MDS 0.61

  ◉ PFS  0.85  Error handling split 4 ways
               → src/api/routes.py:42
               → Next: consolidate into shared error handler

  ◉ AVS  0.72  DB import in API layer
               → src/api/auth.py:18
               → Next: move DB access behind service interface

Example output — actual findings depend on repository structure.

drift analyze terminal demo

If you want the shortest path from headline claim to raw evidence, start with Benchmarking and Trust, then use the Full Study for methodology and benchmark_results/README.md for checked-in artifacts.


How drift differs from adjacent tools

Linters, type checkers, and security scanners operate on individual files or single-flow paths. Drift operates across files: it detects when the same concern is solved inconsistently across modules, when layer boundaries erode through imports, and when near-duplicate code accumulates structurally.

Category Primary analysis target Drift's additional scope
Linters / formatters (Ruff, Black) Style, single-file correctness Cross-module coherence
Type checkers (mypy, Pyright) Type safety per expression Architectural consistency across modules
Security scanners (Semgrep, CodeQL) Risky flows, policy violations Structural fragmentation patterns
Maintainability dashboards (SonarQube) Broad quality heuristics Per-signal drift scores, deterministic and reproducible
Clone detectors (jscpd, CPD) Text-level duplication AST-level near-duplicates across modules

Drift is designed to run alongside linters and security scanners, not replace them. Recommended stack: linter (style) + type checker (types) + drift (coherence) + security scanner (SAST).

Capability comparison table
Capability drift SonarQube pylint / mypy jscpd / CPD
Pattern Fragmentation across modules
Near-Duplicate Detection (AST-level) Partial (text) ✔ (text)
Architecture Violation signals Partial
Temporal / change-history signals
GitHub Code Scanning via SARIF
Zero server setup Partial
TypeScript support Experimental ¹

✔ = within primary design scope · — = not a primary design target (may be partially available via configuration or plugins) · Partial = limited coverage

¹ Via drift-analyzer[typescript]. Python is the primary analysis target.

Comparison reflects primary design scope per STUDY.md §9.

Quickstart

drift analyze --repo .          # see your top findings
drift explain PFS               # learn what a signal means
drift fix-plan --repo .         # get actionable repair tasks

Add to CI (start report-only):

- uses: mick-gsk/drift@v1
  with:
    fail-on: none               # report findings without blocking
    upload-sarif: "true"        # findings appear as PR annotations

Once the team trusts the output, tighten: fail-on: high.

More: Quick Start · Example Findings · Team Rollout

Installation

Path Command / Config Best for
PyPI pip install drift-analyzer Local use, scripts, CI
pipx / uvx pipx install drift-analyzer Isolated CLI (no venv)
Install script curl -fsSL .../install.sh | sh One-liner (auto-detects pipx, uv, pip)
Homebrew brew tap mick-gsk/drift && brew install drift-analyzer macOS / Linux devs
Docker docker run -v .:/src ghcr.io/mick-gsk/drift analyze --repo /src Container-based CI
GitHub Action uses: mick-gsk/drift@v1 GitHub CI/CD pipelines
pre-commit repo: https://github.com/mick-gsk/drift Git hooks

Full installation guide: Installation

Public evaluation path: Benchmarking and Trust · Full Study · Artifact Index

AI-assisted workflows

Drift provides an MCP server and agent-native commands for use inside AI coding sessions (Copilot, Cursor, Claude):

pip install drift-analyzer[mcp]
drift init --mcp --claude         # scaffold MCP configs for your editor
drift scan --repo . --max-findings 5   # session baseline for agents
drift diff --staged-only               # pre-commit structural check
drift fix-plan --repo .                # agent-friendly repair tasks

Full setup: Integrations · Vibe-Coding Guide · Demo walkthroughs

Signals

Drift runs multiple signal families against the codebase. Each signal detects a specific cross-file coherence problem. Findings include severity, file location, and a suggested next step.

drift explain <SIGNAL> shows what any signal detects and how to address it.

Signal Reference · Algorithm Deep Dive · Scoring Model

Use cases

Pattern fragmentation in a connector layer

Problem: A FastAPI service has 4 connectors, each implementing error handling differently — bare except, custom exceptions, retry decorators, and silent fallbacks.

drift analyze --repo . --sort-by impact --max-findings 5

Output: PFS finding (high score) — "26 error_handling variants in connectors/" — shows exactly which files diverge and suggests consolidation.

Architecture boundary violation in a monorepo

Problem: A database model file imports directly from the API layer, creating a circular dependency that breaks test isolation.

drift check --fail-on high

Output: AVS finding — "DB import in API layer at src/api/auth.py:18" — blocks the CI pipeline until the import direction is fixed.

Duplicate utility code from AI-generated scaffolding

Problem: AI code generation created 6 identical _run_async() helper functions across separate task files instead of finding the existing shared utility.

drift analyze --repo . --format json | jq '.findings[] | select(.signal=="MDS")'

Output: MDS findings listing all 6 locations with high similarity scores, enabling a single extract-to-shared-module refactoring.

Setup and CI integration

Full GitHub Action example (recommended: start report-only)
name: Drift

on: [push, pull_request]

jobs:
  drift:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: mick-gsk/drift@v1
        with:
          fail-on: none           # report findings without blocking CI
          upload-sarif: "true"    # findings appear as PR annotations

Once the team has reviewed findings for a few sprints, tighten the gate:

      - uses: mick-gsk/drift@v1
        with:
          fail-on: high           # block only high-severity findings
          upload-sarif: "true"
pre-commit hook configuration
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/mick-gsk/drift
    rev: vX.Y.Z                  # replace with the latest tag from https://github.com/mick-gsk/drift/releases
    hooks:
      - id: drift-check          # blocks on high-severity findings
      # - id: drift-report        # report-only alternative (start here)

Or use a local hook if you already have drift installed:

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: drift
        name: drift
        entry: drift check --fail-on high
        language: system
        pass_filenames: false
        always_run: true

CI gate (local):

drift check --fail-on none    # report-only
drift check --fail-on high    # block on high-severity findings

More: Configuration · Team Rollout

Is drift a good fit?

Drift is a strong fit for:

  • Python teams using AI coding tools in repositories where architecture matters
  • repositories with 20+ files and recurring refactors across modules
  • teams that want deterministic architectural feedback in local runs and CI

Wait or start more cautiously if:

  • the repository is tiny and a few findings would dominate the score
  • you need bug finding, security review, or type-safety enforcement rather than structural analysis
  • Python 3.11+ is not available in your local and CI execution path yet

The safest rollout path is progressive:

  1. Start with drift analyze locally and review the top findings.
  2. Add drift check --fail-on none in CI as report-only discipline.
  3. Gate only on high findings once the team understands the output.
  4. Ignore generated or vendor code and tune config only after reviewing real findings in your repo.

Trust and limitations

Drift's analysis pipeline is deterministic and its benchmark artifacts are published in the repository, so claims can be inspected rather than trusted on assertion.

  • Deterministic pipeline: no LLMs in detection — same input produces the same output.
  • Benchmarked: precision upper-bound estimate on a historical v0.5.0 ground-truth baseline (77 % strict / 95 % lenient, score-weighted sample of 286 findings, 5 repos). 88 % mutation recall on a controlled benchmark (15/17 patterns, 10 signal types). These numbers apply to historical benchmark models and have not been revalidated for the current signal set.
  • Single-rater caveat: ground-truth classification is not yet independently replicated.
  • Small-repo noise: repositories with few files can produce noisy scores. Auto-calibration mitigates but does not eliminate this.
  • Temporal signals depend on clone depth and git history quality.
  • The composite score is orientation, not a verdict. Interpret deltas via drift trend, not isolated snapshots.
Interpreting the score

The drift score measures loss of structural coherence, not code quality.

  • Interpret deltas, not snapshots. Use drift trend to track changes over time. A single score in isolation has limited meaning.
  • Temporary increases are expected during migrations. Two coexisting patterns (old and new) will raise PFS/MDS signals. This is the migration happening, not a problem.
  • Deliberate polymorphism is not erosion. Strategy, Adapter, and Plugin patterns produce structural similarity that MDS flags as duplication. Findings include a deliberate_pattern_risk hint — verify intent before acting.
  • The score rewards reduction, not correctness. Deleting code lowers the score just like refactoring does. Do not optimize for a low score — optimize for understood, intentional structure.

Without layer_boundaries in drift.yaml, drift detects emergent drift — structural patterns that diverge without explicit prohibition. With configured layer_boundaries, drift additionally performs conformance checking against a defined architecture. Both modes are complementary.

See STUDY.md §14 for epistemological boundaries.

Release status

The PyPI classifier is Development Status :: 4 - Beta.

  • core Python analysis: stable
  • CI and SARIF workflow: stable
  • TypeScript support: experimental
  • embeddings-based parts: optional / experimental
  • benchmark methodology: evolving

Full rationale: Stability and Release Status

Further reading: Benchmarking and Trust · Full Study · Case Studies

Contributing

Drift's biggest blind spots are found by people running it on codebases the maintainers have never seen. A well-documented false positive can be more valuable than a new feature.

I want to… Go here
Ask a usage question Discussions
Report a false positive / false negative FP/FN template
Report a bug Bug report
Suggest a feature Feature request
Propose a contribution before coding Contribution proposal
Report a security vulnerability SECURITY.md — not a public issue
git clone https://github.com/mick-gsk/drift.git && cd drift && make install
make test-fast    # confirm everything passes, then start

See CONTRIBUTING.md for the full guide and ROADMAP.md for current priorities.

Documentation

Topic Link
Getting Started Quick Start
Algorithms How It Works
Evidence Benchmarking and Trust
Strategy Product Strategy
Contributing Contributor Guide
Development Developer Guide

License

MIT. See LICENSE.

Project details


Release history Release notifications | RSS feed

This version

2.6.2

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

drift_analyzer-2.6.2.tar.gz (1.2 MB view details)

Uploaded Source

Built Distribution

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

drift_analyzer-2.6.2-py3-none-any.whl (361.8 kB view details)

Uploaded Python 3

File details

Details for the file drift_analyzer-2.6.2.tar.gz.

File metadata

  • Download URL: drift_analyzer-2.6.2.tar.gz
  • Upload date:
  • Size: 1.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for drift_analyzer-2.6.2.tar.gz
Algorithm Hash digest
SHA256 0c2a8223d749e5aa6d1d042ab512596fa8fd8a3d5945d14c8ab861517db2f983
MD5 5a3f95672b55c94d87c17724b6df1cb5
BLAKE2b-256 cc08273d3ee2c65a80afeee7de64c2e810ecee2d5d4fdcf31c6e10367f5fc932

See more details on using hashes here.

File details

Details for the file drift_analyzer-2.6.2-py3-none-any.whl.

File metadata

  • Download URL: drift_analyzer-2.6.2-py3-none-any.whl
  • Upload date:
  • Size: 361.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for drift_analyzer-2.6.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e8c0106448c77bc3c217d3b0f48e1665129c0c1687cceb3ec4316894c96e1241
MD5 389f622da3a6f9aeb607ed0bce29c39e
BLAKE2b-256 3030482cd58f214a4f317eff5179988cd7f32886185fd9c70d685ff1a0f40bde

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page