Skip to content

Configuration

Create a drift.yaml in your project root to customize detection behavior.

Minimal Example

include:
  - "**/*.py"
exclude:
  - "**/node_modules/**"
  - "**/venv/**"
fail_on: none

Start with fail_on: none so the first rollout teaches the team how to read findings before CI starts blocking merges.

JSON Schema for drift.yaml

Drift publishes an authoritative JSON Schema at repository root:

  • drift.schema.json

This schema is generated from the runtime DriftConfig model and can be refreshed with:

drift config schema --output drift.schema.json

Editor autocomplete (YAML language server)

Add this header comment to drift.yaml:

# yaml-language-server: $schema=https://raw.githubusercontent.com/mick-gsk/drift/main/drift.schema.json

This enables field autocomplete and static validation in editors that support the YAML language server (for example VS Code).

CI/static validation

You can validate drift.yaml against the same schema in CI:

python -m pip install check-jsonschema
check-jsonschema --schemafile drift.schema.json drift.yaml

Full Configuration Reference

# File patterns
include:
  - "**/*.py"
exclude:
  - "**/node_modules/**"
  - "**/__pycache__/**"
  - "**/venv/**"

# Signal weights (normalized internally)
weights:
  pattern_fragmentation: 0.16
  architecture_violation: 0.16
  mutant_duplicate: 0.13
  explainability_deficit: 0.09
  temporal_volatility: 0.0   # report-only
  system_misalignment: 0.08
  doc_impl_drift: 0.04
  broad_exception_monoculture: 0.04
  test_polarity_deficit: 0.04
  guard_clause_deficit: 0.03
  cohesion_deficit: 0.01
  naming_contract_violation: 0.04
  bypass_accumulation: 0.03
  exception_contract_drift: 0.03
  co_change_coupling: 0.005
  fan_out_explosion: 0.005
  hardcoded_secret: 0.01
  phantom_reference: 0.02
  missing_authorization: 0.02
  insecure_default: 0.01
  ts_architecture: 0.0        # report-only
  cognitive_complexity: 0.0   # report-only
  circular_import: 0.0        # report-only
  dead_code_accumulation: 0.0 # report-only

# Detection thresholds
thresholds:
  high_complexity: 10
  medium_complexity: 5
  min_function_loc: 10
  similarity_threshold: 0.80
  recency_days: 14
  volatility_z_threshold: 1.5

# Architecture boundaries
policies:
  layer_boundaries:
    - name: "No DB imports in API layer"
      from: "api/**"
      deny_import: ["db.*", "models.*"]
    - name: "No API imports in DB layer"
      from: "db/**"
      deny_import: ["api.*", "routes.*"]

# CI severity gate
fail_on: none

Tip: Run drift config show --repo . to inspect your effective configuration (including calibration-adjusted values).

Signal Weights

Weights control the relative importance of each signal in the composite score. They are normalized internally — they don't need to sum to 1.0.

Default weights are calibrated via ablation study (see Scoring Model).

Architecture Policies

Layer boundaries define which imports are allowed between modules. This is the most impactful configuration for the Architecture Violation Signal (AVS).

Finding Context Policy

Drift classifies each finding into a machine-readable finding_context bucket. Default contexts are:

  • production
  • fixture
  • generated
  • migration
  • docs

By default, non-operational contexts (fixture, generated, migration, docs) remain visible in findings, but are excluded from prioritization queues (fix_first, fix_plan) unless explicitly enabled.

Example override with glob rules and precedence:

finding_context:
  default_context: production
  non_operational_contexts:
    - fixture
    - generated
    - migration
    - docs
  rules:
    - pattern: "**/benchmarks/**"
      context: fixture
      precedence: 40
    - pattern: "**/generated/**"
      context: generated
      precedence: 35
    - pattern: "src/generated/safe/**"
      context: production
      precedence: 50

Trade-off: this reduces remediation noise in mixed repositories, but teams with generated-code ownership should opt in to include non-operational contexts in prioritization for those workflows.

Calibration (Learning Model)

Drift includes a Bayesian learning model that adjusts signal weights based on your feedback. See the Feedback & Calibration Guide for the full workflow.

calibration:
  enabled: true
  min_samples: 20              # Min TP+FP per signal for full confidence
  correlation_window_days: 30  # Days to look for defect-fix commits
  decay_days: 90               # Profile considered stale after this
  weak_fp_window_days: 60      # No defect-fix → counts as weak FP
  fn_boost_factor: 0.1         # Boost weight for high-FN signals (0.0–1.0)
  auto_recalibrate: false      # Auto-calibrate after each analyze run
  github_token: null            # Or set DRIFT_GITHUB_TOKEN env var
  bug_labels:
    - bug
    - regression
    - defect
  feedback_path: ".drift/feedback.jsonl"
  history_dir: ".drift/history"
  max_snapshots: 20
  threshold_adaptation_enabled: false  # Experimental
Field Default Description
enabled false Master switch
min_samples 20 Min observations per signal for full calibration confidence
correlation_window_days 30 Days after scan to look for defect-fix commits (TP evidence)
decay_days 90 Stale profile threshold
weak_fp_window_days 60 No defect-fix in this window → weak FP evidence
fn_boost_factor 0.1 Weight boost for high-FN signals (max 1.0, 0.0 disables)
auto_recalibrate false Recompute weights on every drift analyze
github_token null GitHub token for issue/PR correlation
bug_labels ["bug", "regression", "defect"] Issue labels for defect correlation
feedback_path ".drift/feedback.jsonl" JSONL file for TP/FP/FN verdicts
history_dir ".drift/history" Directory for scan history snapshots
max_snapshots 20 Max retained snapshots before pruning
threshold_adaptation_enabled false Experimental adaptive thresholds

Attribution (Git Blame)

Enrich findings with git-blame provenance — shows which commit introduced the drifting code.

attribution:
  enabled: true
  cache_enabled: true
  timeout_per_file_seconds: 3.0
  max_parallel_workers: 4
  include_branch_hint: true

When enabled, each finding gains attribution: {commit_hash, author, email, date, branch_hint}. Blame results are cached in .drift-cache/blame_cache.db.

Plugins

Selectively disable plugins discovered via Python entry points.

plugins:
  disabled:
    - my-noisy-plugin
    - experimental-signal

Plugin names must match entry-point names exactly. Invalid names are silently ignored.

Agent Objective

Declare the agent's current task so drift provides targeted feedback and tracks effectiveness.

agent:
  goal: "Migrate payment module to Stripe API"
  strict_guardrails: false
  out_of_scope:
    - "legacy/"
    - "tests/fixtures/"
  success_criteria:
    - "No new AVS findings in src/billing/"
    - "All PFS findings resolved"
  effectiveness_thresholds:
    low_effect_resolved_per_changed_file: 0.25
    low_effect_resolved_per_100_loc_changed: 0.5
    high_churn_min_changed_files: 5
    high_churn_min_loc_changed: 200
Field Default Description
goal "" Natural-language task description
strict_guardrails false Enforce MCP tool ordering (brief→scan→fix-plan→nudge→diff)
out_of_scope [] Paths the agent should not touch
success_criteria [] Human-readable completion conditions
effectiveness_thresholds (see above) Deterministic low-effect/high-churn warning thresholds

Path Overrides

Per-path weight overrides, signal exclusions, and severity gates.

path_overrides:
  "tests/**":
    weights:
      pattern_fragmentation: 0.0
      architecture_violation: 0.05
    exclude_signals:
      - temporal_volatility
  "legacy/**":
    severity_gate: "critical"
    weights:
      pattern_fragmentation: 0.05
      architecture_violation: 0.05

Most specific (longest) glob match wins. exclude_signals takes precedence over weights for the same signal.

Deferred Areas

Mark known technical debt — analyzed but tagged as deferred=true (unlike exclude, which skips analysis entirely).

deferred:
  - pattern: "legacy/**"
    reason: "Scheduled Q3 rewrite"
    review_by: "2026-09-01"
  - pattern: "vendor/**"
    reason: "Third-party code"

Deferred findings are excluded from fix-plan by default (opt in via --include-deferred).

Brief Scope Aliases

Keyword → path mapping for convenient drift brief --scope resolution.

brief:
  scope_aliases:
    payment: src/billing/
    auth: src/auth/
    api: src/api/
    legacy: legacy/

Usage: drift brief --scope payment resolves to src/billing/. Exact paths still work.

Monorepo Configuration Examples

Drift works with any Python repository layout, including monorepos. Two complementary approaches are available — often used together.

When to use --path vs include/exclude

Approach When to use
--path packages/my_service One-off scan of a single package from the command line; no config file changes needed.
include/exclude in drift.yaml Permanent, reviewable configuration committed next to the code; required for CI and multi-package setups.

Use --path for quick ad-hoc analysis. Use include/exclude when the scope should be reproducible and versioned.

Example 1 — Scanning a single package

Place a drift.yaml inside the package directory (or at the repo root and pass --path on the command line):

# packages/payment_service/drift.yaml
include:
  - "**/*.py"
exclude:
  - "**/tests/**"
  - "**/migrations/**"
  - "**/__pycache__/**"
fail_on: medium

Run with:

drift analyze --repo . --path packages/payment_service

Drift restricts file discovery and Git-history analysis to packages/payment_service/ so findings from other packages do not appear.

Example 2 — Scanning multiple packages with shared config

Keep one drift.yaml at the repo root that covers all packages but excludes infrastructure, tooling, and generated code:

# drift.yaml (repo root)
include:
  - "packages/**/*.py"
  - "libs/**/*.py"
exclude:
  - "**/tests/**"
  - "**/migrations/**"
  - "**/generated/**"
  - "**/node_modules/**"
  - "**/__pycache__/**"
  - "infra/**"
  - "scripts/**"
fail_on: none

Run a single analysis that covers the entire monorepo:

drift analyze --repo .

Or scan individual packages in CI per job:

drift analyze --repo . --path packages/auth_service
drift analyze --repo . --path packages/payment_service

Example 3 — Per-package drift.yaml with package-local overrides

For packages that need stricter or looser thresholds than the default, add a drift.yaml directly inside that package and pass --path:

# packages/core_lib/drift.yaml
include:
  - "**/*.py"
exclude:
  - "**/tests/**"
thresholds:
  high_complexity: 8       # stricter than default (10)
  similarity_threshold: 0.75
fail_on: high
drift analyze --repo . --path packages/core_lib

The local drift.yaml is resolved relative to the --path argument, so each package can carry its own policy independent of the repo root.