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.0

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.0.tar.gz (1.1 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.0-py3-none-any.whl (336.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: drift_analyzer-2.6.0.tar.gz
  • Upload date:
  • Size: 1.1 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.0.tar.gz
Algorithm Hash digest
SHA256 341b05e0f6d803e6a5dee89cf08bc91daa8690851fa048ef27821163fd5663e1
MD5 47e34fc5217d7c56fd2227d35fc384df
BLAKE2b-256 a658f141f8dacdf75d6f6d0b425eb187f0f40d6fe649a82b126ee144b5bd5a58

See more details on using hashes here.

File details

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

File metadata

  • Download URL: drift_analyzer-2.6.0-py3-none-any.whl
  • Upload date:
  • Size: 336.0 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 57c3ff918ab51fa162ae97e5328c4c4bca679719c0598d0eb6dd3421e3fca3d6
MD5 c20718a58d5c30c237fac4bdd618eb44
BLAKE2b-256 882ef671ab99ff5bfe70bfd89d061b4e49bc5f0b430c6c26cc95a136e51f9f6d

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