# Drift **Drift stops an AI agent from writing code that violates conventions your repo already follows.** It runs entirely on your machine. > **Beta install: build from source.** Nothing is published to npm yet — `npm install -g @drift/cli` > does not work, and the `driftdetect` package on npm is the unrelated v1 from January. Building > needs a Rust toolchain ([rustup](https://rustup.rs)) because the scan engine is Rust. ```bash git clone https://github.com/dadbodgeoff/drift.git && cd drift pnpm install --frozen-lockfile pnpm build && pnpm build:engine # There is no `drift` binary yet; the entry point is the built CLI. alias drift="node $PWD/packages/cli/dist/main.js" drift doctor --repo-root . # fails loudly if the toolchain is missing ``` Then, in the repository you want to protect: ```bash cd your-repo drift start --repo-root . --accept-defaults ``` `start` prints whether the convention it accepted will actually block, and the command to make it a gate if it will not. Now have an agent add a route that queries the database directly, the way a hundred other routes in your repo do not, and check the change: ```bash drift check --diff HEAD~1...HEAD --scope changed-hunks ``` It names the file, the line, and the convention that was broken. In `block` mode it exits `2`. > `--diff main...HEAD` only works once your branch has commits that `main` does not. On a freshly > cloned repo you are *on* `main`, so that range is empty and Drift refuses rather than reporting a > pass it cannot support — exit `3`. That refusal is correct; give it a range with changes in it. ## Scope — read this before adopting Drift enforces **one** convention family well, and says so rather than implying more: | | | |---|---| | **Languages** | TypeScript, JavaScript | | **Frameworks** | Next.js API routes (App Router and Pages) | | **Convention** | API routes must not import data-access clients directly | | **Status** | Beta — local CLI and a read-only MCP server | **It does not** review code generally, support other languages, modify your source, or sync anything to a server. The data layer is recognised when its import specifier contains `prisma`, `database`, `db` or `data-access`. A repo naming its data layer `store` or `supabase` must declare it with `--data-modules`. Drift bootstraps and enforces a *declared* layering contract — it does not learn conventions in general, and the claims manifest blocks that claim explicitly. Security heuristics exist behind `--experimental-security` and are **not** proofs. Their own audit is in [docs/internal/architecture/security-heuristic-audit.md](docs/internal/architecture/security-heuristic-audit.md). ## What it does on real repositories Evaluated on seven open-source Next.js repos on every change ([`pnpm eval:external`](scripts/external-eval.mjs)): | | | |---|---| | Onboards, learns the real data layer, catches an injected violation | **7 / 7** | | Correct `file:line` evidence | **7 / 7** | | A properly layered route falsely flagged | **0 / 7** | | False-positive rate (dub, 494 routes) | **3.1%** | | Evasion shapes caught, at the contract's mode ([`pnpm eval:evasion`](scripts/evasion-matrix.mjs)) | **66 / 66** testable cells | And, because the number that decides whether a first session is tolerable is not recall but how often Drift declines to answer — eight ordinary edits per repo, none of them a violation ([`pnpm eval:bench`](scripts/beta-bench.mjs)): | | | |---|---| | Ordinary edits refused rather than answered | **0 / 56** | | Local imports the resolver places | **96.1% – 99.9%** | Every check reports its own coverage, so a clean result is never mistaken for full coverage. Where Drift cannot resolve something it says so, with the offending specifiers — see [`drift doctor`](docs/reference/enforcement.md) and `summary.import_coverage`. ## Why your repo might only warn If findings appear but the check exits `0`, the convention is in **warn** mode, deliberately. Drift infers the convention from the violations themselves, so a repo where every route touches the database produces the same statement as one where a single route does. Enforcing both identically would reject new code written exactly like its neighbours. Instead the mode follows the evidence: a **minority** violating means new violations block; a **majority** violating means it is a refactor goal, and Drift warns until a human decides. formbricks (1 route of 83) blocks. dub (~323 of 494) warns. ## Documentation - [Quickstart](docs/quickstart.md) — install to first blocked change - [Concepts](docs/concepts.md) — facts → contract → baseline → check - [Agent integration](docs/agent-integration.md) — MCP, hooks, CI - [Enforcement reference](docs/reference/enforcement.md) — exit codes and JSON contract - [Claims](docs/internal/architecture/beta-claims.json) — every claim, and what is deliberately blocked - [History](docs/HISTORY.md) — why Drift has been rebuilt three times, and what changed each time ## First Five Minutes From the repo you want Drift to inspect: ```bash drift doctor --repo-root . drift start --repo-root . --accept-defaults drift doctor --repo-root . ``` The first `doctor` run is a zero-write readiness check. After `start`, run it again as the ongoing local health gate: it validates SQLite migration compatibility, repo registration, contract compatibility, scan freshness, audit-chain integrity, and tracked backup artifacts, then prints the next upkeep commands. `start --accept-defaults` is explicit onboarding confirmation. It accepts the deterministic default convention, materializes the repo contract, and baselines existing findings so legacy drift does not block the first check. Omit `--accept-defaults` when you want to review every candidate manually first. `drift start` prints the local SQLite path: ```bash export DRIFT_DB=/path/to/drift.sqlite ``` Then use the printed repo id with the review loop: ```bash drift baseline status --repo drift version --json drift capabilities --json drift conventions list --repo --status candidate --kind api_route_no_direct_data_access --capability deterministic_check --limit 20 --offset 0 --json drift conventions accepted --repo --kind api_route_no_direct_data_access --capability deterministic_check --limit 20 --offset 0 --json drift conventions show --repo --json drift ask "what should I know before changing this route?" --repo --path apps/web/app/api/users/route.ts --json drift prepare "add user search endpoint" --repo --path apps/web/app/api/users/route.ts --json drift repo map --repo --role api_route --json drift repo map --repo --limit 50 --offset 0 --json drift prepare "add user search endpoint" --repo --require-fresh --json drift checks list --repo --limit 20 --offset 0 --json drift checks run --repo --command "pnpm test" --timeout-ms 120000 --json drift policy check-context --repo --path apps/web/app/api/users/route.ts --surface cli-preflight --require-fresh --json drift check --diff main...HEAD --repo --scope changed-hunks drift findings list --repo drift findings list --repo --convention --json drift findings list --repo --path apps/web/app/api/users/route.ts --require-fresh --json drift findings list --repo --limit 25 --offset 0 --json drift findings show --repo --require-fresh --json drift backup create --repo --confirm drift backup list --repo --json drift backup list --repo --limit 20 --offset 0 --json drift backup list --repo --artifact-status missing --json drift audit list --repo drift audit verify --repo --json drift audit list --repo --target-id --limit 20 --offset 0 --json drift audit list --repo --since 2026-05-10T00:00:00.000Z --until 2026-05-11T00:00:00.000Z ``` Backup output prints the verify and restore dry-run commands. Backup list, verify, and restore JSON also include compact summaries so setup scripts can see artifact health, checksum status, restore intent, and rescan requirements without parsing prose: ```bash drift backup verify --repo --checksum drift backup verify --repo --checksum --require-checksum drift --db restore --repo --checksum --dry-run drift --db restore --repo --checksum --require-checksum --dry-run ``` For stricter restore identity checks, pass the expected repo fingerprint: ```bash drift backup verify --repo --expect-repo-fingerprint drift --db restore --repo --expect-repo-fingerprint --dry-run ``` Contract exports can stay on stdout or be written to one approved artifact: ```bash drift contract export --repo --format json --output ./repo-contract.json --confirm drift contract import ./repo-contract.json --dry-run drift contract import ./repo-contract.json --checksum --dry-run drift contract import ./repo-contract.json --checksum --require-checksum --dry-run ``` `contract show`, `contract validate`, `contract export`, `contract import --dry-run`, and MCP `get_repo_contract` expose a stable `contract_fingerprint` for compatibility checks. The fingerprint is computed from canonical contract content, so unordered governance lists do not create false drift. `scan status`, `prepare`, MCP `get_scan_status`, and MCP `get_task_preflight` expose a stable `scan_fingerprint` for the indexed graph plus no-approval governance metadata, summary counts, `indexed_file_count`, `source_change_count`, audit integrity, and next commands. Agents can compare the fingerprint before acting and after rescans without reading source code. `ask`, `prepare`, and MCP `get_task_preflight` can target a specific repo-relative `--path`, which pins that file into the preflight packet with a `requested path` reason. Agent-facing context commands also support `--require-fresh` / `require_fresh`; when set, Drift refuses stale scan context and tells the agent which `drift scan` command to run. `repo map` and MCP `get_repo_map` include per-file impact annotations for matching conventions, risky areas, and open findings, plus an `impact_summary`. `policy check-context` and MCP `get_allowed_context` are the path-level gate for agent context access. They return the policy decision, no-approval governance metadata, summary counts, next commands, freshness requirement, scan status, redaction caps, and indexed file impact metadata without reading or emitting source snippets. `findings list --path`, `findings list --convention`, `findings list --limit --offset`, `findings show`, and MCP `get_findings({ path, convention_id, limit, offset })` give agents and humans bounded review queues plus a drill-down view for one finding. Add `--require-fresh` / `require_fresh` when stale findings should be refused before an agent acts. `conventions list --kind --capability --limit --offset` keeps the human approval queue bounded and machine-checkable. Use it to separate deterministic blocking candidates from heuristic briefing candidates before accepting anything into the repo contract. `ask`, `repo map`, and MCP `get_repo_map` are deterministic local context surfaces. They answer from approved contracts, findings, file roles, imports, exports, calls, scan metadata, and policy state; they do not call an LLM, do not mutate Drift state, and do not include source snippets. `repo map` and MCP `get_repo_map` support limit/offset pagination so large repo maps stay scriptable. `audit verify --json` and MCP `get_audit_status` check the local audit hash chain and report summary counts, next commands, and the current head hash. Backup verification and restore dry-runs also validate the audit chain and expose summary fields for schema support, checksum checks, artifact size, write intent, and stale-graph rescan guidance. `audit list --target-id --limit --offset` keeps governance timelines scriptable as local state grows. Audit events are returned in deterministic created-time order, with total, filtered, returned, and next-offset metadata. `doctor --json` includes the same production-state signals in one automation-friendly payload: applied migrations, unsupported/missing migrations, contract schema/fingerprint, scan staleness, audit integrity, backup count, backup artifact problems, and `next_commands` for initialized repos. ## State Model Drift stores product state in SQLite, not a folder full of JSON files. The database owns: - repo identity - scan manifests - file snapshots and hashes - extracted facts - convention candidates and accepted conventions - repo contracts - findings - baselines - policies - audit events - backup manifests JSON is only an interface format for CLI automation, MCP responses, contract import/export, and tests. ## What Requires Approval Commands that do not require governance approval: ```bash drift doctor drift scan drift scan status drift prepare drift check drift findings list drift audit list ``` Some of these commands write local Drift state, such as `scan`, `start`, and `check`. The important boundary is that they do not mutate source code or approve governance decisions. Governance changes require explicit human intent: ```bash drift conventions accept --confirm drift conventions reject --reason "..." --confirm drift conventions edit --statement "..." --confirm drift conventions exception add --path --reason "..." --confirm drift findings mark-fixed --evidence --confirm drift findings mark-needs-review --reason "..." --confirm drift findings suppress --reason "..." --confirm drift findings accept-drift --reason "..." --confirm drift findings mark-false-positive --reason "..." --confirm drift baseline create --from main --confirm drift baseline clear --convention --confirm drift policy set-egress ... --confirm drift policy agent grant ... --confirm drift policy agent revoke ... --confirm drift contract export ... --confirm drift contract import --confirm drift contract waiver add ... --confirm drift contract waiver remove ... --confirm drift backup create --confirm drift restore --confirm ``` ## Architecture Rust owns the bounded-memory scanning and rule-critical engine pieces. TypeScript owns the CLI, MCP server, SQLite storage boundary, policy enforcement, onboarding, and packaging. The product boundary is intentionally CLI-first. The desktop UI comes after the CLI review loop is stable. ## Core Wedge Support Matrix | Surface | Status | | --- | --- | | TypeScript/JavaScript API route layering | Supported | | Local SQLite state | Supported | | CLI review loop | Supported | | Read-only MCP context | Supported | | Backup/restore of Drift state | Supported | | Python adapter | Deferred | | Desktop UI | Deferred | | Cloud sync | Deferred | | Duplicate helper detection | Deferred | Drift does not mutate source code. It stores repo intelligence locally, guides humans and agents with evidence, and requires explicit human confirmation for governance changes. Use `drift capabilities --json` or MCP `get_capabilities` to discover the supported command surface, no-approval agent tools, human-confirmed governance mutations, and deferred surfaces. ## Package Smoke The e2e suite packs and installs the workspace packages into a clean consumer project, then runs: - installed `drift doctor` - installed `drift scan` - installed `drift conventions list --kind --capability --limit --offset` - installed `drift conventions accepted --kind --capability --limit --offset` - installed `drift start --accept-defaults` - installed `drift scan status` - installed `drift ask` - installed `drift prepare` - installed `drift repo map` - installed `drift repo map --limit --offset` - installed `drift checks list --limit --offset` - installed `drift checks run` - installed `drift baseline status` - installed `drift contract show` - installed `drift check` - installed `drift findings list` - installed `drift findings list --convention` - installed `drift findings list --limit --offset` - installed `drift findings list --path --require-fresh` - installed `drift findings show` - installed `drift findings mark-needs-review --confirm` - installed `drift findings mark-fixed --confirm` - installed `drift audit list` - installed `drift audit list --target-id --limit --offset` - installed `drift audit verify` - installed `drift backup create --confirm` - installed `drift backup list` - installed `drift backup list --artifact-status --limit --offset` - installed `drift backup verify` - installed `drift restore --dry-run` - installed `drift restore --confirm` - installed `drift version --json` - installed `drift capabilities --json` - installed MCP `get_runtime_info` - installed MCP `get_capabilities` - installed MCP `get_audit_status` - installed MCP `get_scan_status` - installed MCP `get_repo_contract` - installed MCP `get_repo_map` - installed MCP `get_task_preflight` - installed MCP `get_conventions` - installed MCP `get_findings` - installed MCP `get_required_check_executions` - installed MCP `get_allowed_context` - installed `drift-mcp` - `pnpm beta:proof` Run the full gate: ```bash pnpm verify:full ``` `verify:full` is two gates. `pnpm verify:ci` is the half a hosted runner can execute and is what GitHub Actions runs on every PR. `pnpm verify:evals` is the external-repo battery — it requires the seven pinned evaluation repos cloned locally and is not run by CI. `pnpm verify:ci` includes `pnpm beta:proof`, which proves a fresh Rust scan, an accepted contract, a service-delegated route passing, a new direct data-access route blocking, evidence-complete findings, full schema-stable CLI/MCP parity, and audit hash-chain verification.