Drift

// evidence, not guesswork

Your dependency bot says 47 updates.
Drift says which three break your code.

Drift works out what changed upstream using computed API surfaces where available and citable release evidence everywhere else, then searches your repository for the exact lines that use it. Every claim carries a citation you can open, and anything it could not check is reported as a gap rather than softened into a pass.

TypeScript · Python · Ruby · Go · Rust · Java · C++ · Elixir · C# · PHP · Dart · Swift · OCaml

16

recorded runs

16

ecosystems

765

packages checked

679

linked code sites

Not a mock-up. A recording.

Drift clones repositories, calls registries and runs package managers, so it cannot run in a browser. Instead each of these is a real run against a real project, captured with every progress event and the timestamp it happened at — replayed here at its original cadence. The commit is linked; go and check it.

supabase/supabase-js The official Supabase client for JavaScript and TypeScript.

drift outdated — supabase/supabase-js
    586dee38d·real run took 12.7s, replayed at 1×

    // sixteen ecosystems, graded

    How far Drift gets, per ecosystem

    Every one of these has a recording above, including the ones where Drift can do less. The grade is computed from the same table the CLI consults before it runs a stage — nobody types a colour in here, so an ecosystem that loses a capability loses its badge on the next build. Three things decide it: whether Drift computes the API diff from what was actually published, whether the module names it searches for come from the package rather than from a guess, and whether it can run the ecosystem’s own build and tests over its work.

    DeepStrongWorkingLimited full partial none

    Tap or hover a cell for the exact sentence.

    EcosystemDetectEvidenceAPI surfaceStatic analysisFind upgradesInstall a versionVerifyFixAutomated PR

    JavaScript / TypeScript

    Python

    Go

    Rust

    Java / Kotlin / Scala

    .NET

    Elixir / Erlang

    Dart / Flutter

    Ruby

    PHP

    Swift

    C / C++ (Conan)

    C / C++ (vcpkg)

    Arduino / PlatformIO

    CocoaPods

    OCaml

    Deep Drift computes the API diff from the published artefact, joins it to your code against module names the package itself declares, and can run the ecosystem's own build and tests over the result.
    Strong Two of the three hold. Drift has a real answer here; one of the diff, the name resolution, or the verification is weaker than the others.
    Working One of the three holds. Drift will tell you what moved and research it properly, and leaves more of the judgement with you.
    Limited Detected, versioned, and researched. Whether it breaks your code is a question Drift cannot answer in this ecosystem.

    The full matrix — seven stages per ecosystem, each with the sentence saying exactly what it does and does not cover — is in docs/support.md, generated from the same source.

    The number that matters is the small one

    Drift separates upstream noise from code you actually own. The report is allowed to say three different things: safe here, affected here, or not verified.

    Safe here722
    Affects you40
    Not verified3

    722

    safe here

    Upstream changed; your repository does not call the changed surface.

    40

    need attention

    679 exact call sites linked to files and lines.

    3

    not verified

    Missing evidence is shown as a gap, not softened into a pass.

    How a finding gets made

    Five stages, each one independently checkable — followed here by the artefact it produces. The example is a real one: w3lib 1.17.0 → 2.4.1 in Scrapy, from the recording above. And the rules that keep Drift quiet are written out below the pipeline, because those are the ones you cannot verify by reading a finding.

    1. 1

      Read what moved

      Manifests and lockfiles, not guesses.

      Drift parses the manifest with the same parser for every surface, then consults the lockfile to sharpen the installed version — a range says what is permitted, not what is on disk. Workspace members are read as separate packages, because a bump in one is a fact about that one.

      package.jsongo.modGemfile.lockpyproject.tomlCargo.tomlpom.xmlDirectory.Packages.propsconanfile.txtvcpkg.jsonplatformio.ini
      pyproject.toml1 changed dependency
      @@ pyproject.toml — [project.dependencies] @@
      41 "cssselect>=0.9.1",
      42 "itemloaders>=1.0.1",
      43- "w3lib>=1.17.0",
      43+ "w3lib>=2.4.1",
      44 "parsel>=1.5.0",
    2. 2

      Gather evidence

      Three independent sources, each with a link you can open.

      Every record carries a URL or a local locator. This is the difference between Drift and asking a model what it remembers about a library.

      Registry metadata

      The package's own index entry

      Deprecation notices, yanked releases, publish dates, the maintainer's own 'latest' tag, and known advisories from OSV for both the old version and the new one.

      GET pypi.org/pypi/w3lib/json

      GET api.osv.dev/v1/query ← both versions

      Release notes & changelog

      What the maintainers said changed

      Every GitHub release between the two versions, the repository's CHANGELOG, and any migration guide it points to. Read as prose, matched by explicit rules rather than by a model's recollection.

      GET api.github.com/repos/scrapy/w3lib/releases

      GET raw…/w3lib/master/CHANGELOG.rst

      Computed API surface

      What actually changed, whatever they said

      Both versions are fetched and their public surfaces compared symbol by symbol — nothing is installed and no build script of theirs is ever run. This is the strongest signal Drift has, and the only one that catches a break nobody wrote down.

      .d.ts · Go AST · rustdoc JSON

      japicmp · ECMA-335 · C headers · stubs

      Sources are independent on purpose. A maintainer who forgets to write a changelog entry is caught by the surface diff; a behaviour change with no signature change is caught by the changelog. Agreement between two raises confidence, and disagreement is reported rather than resolved by picking a favourite.

    3. 3

      Decide what breaks

      A finding without a citation is not a finding.

      Computed diffs already know which symbol changed and how, so the analyser reads structure rather than re-parsing its own prose. Changelog lines go through explicit rules that recognise a removal or a rename and name the symbol. Every breaking change points back at the evidence that justified it, and anything that could not be established is recorded as a gap.

      drift / analyzew3lib · 13 breaking changes
      signature-changeconfidence: highcites: computed surface diff

      The signature of url.safe_url_string changed.

      Update every call site to match the new signature exactly. Pay attention to argument order, argument count, and whether an options object replaced positional arguments.

      @@ w3lib.url — public surface · defaults 2 → 3 @@
      -def safe_url_string(url, encoding, path_encoding)
      +def safe_url_string(url, encoding, path_encoding, quote_path)
    4. 4

      Find it in your code

      Only the files that import it are searched.

      The import graph is the precision lever: a file that never imports w3lib cannot be broken by a w3lib change, however often the word url appears in it. Within those files Drift matches the symbols the evidence named — skipping comments and string literals, so a word in a docstring is never mistaken for a call. Each site gets its own confidence, highest when the file provably bound that symbol from that import.

      import graph#include graphAST-aligned indexper-site confidence
      symbol: safe_url_string25 results · 10 files

      scrapy/downloadermiddlewares/redirect.py

      224 location = safe_url_string(response.headers["Location"])

      scrapy/http/request/__init__.py

      269 self._url = safe_url_string(url, self.encoding)

      scrapy/linkextractors/lxmlhtml.py

      138 url = safe_url_string(url, encoding=response_encoding)

      high confidence · 1 of 25 sites

      This file binds safe_url_string from an import of w3lib, so the name here is provably that function — not a local one that shares its spelling.

      tests/test_spider_crawl.py

      196 safe_url_string("http://EXAMPLE.ORG/SOMEPAGE/ITEM/12.HTML"),
    5. 5

      Plan, then fix

      One commit per concern, in dependency order.

      Drift never produces a single 'upgrade everything' commit — a reviewer has to be able to read, approve or revert one piece at a time, and git bisect has to stay meaningful. Each unit is resolved by a deterministic codemod where one exists, then a version-pinned community recipe, then an AI agent. Never silently, and always in that order.

      drift/upgrade-w3lib3 commits

      deps: w3lib 1.17.0 → 2.4.1

      The manifest and the lockfile, alone, so the version move is revertible on its own.

      codemod

      fix(w3lib): pass quote_path explicitly at 25 call sites

      A version-pinned community recipe for the signature change, applied deterministically.

      recipe

      fix(w3lib): canonicalize_url no longer lowercases userinfo

      A behaviour change has no mechanical fix, so it is dispatched last and reviewed as its own commit.

      agent

    When a source cannot be reached

    It is recorded as a gap and the package is reported as not verified — never as clean. Zero findings from a complete check and zero findings from a check that never happened are different facts, and a developer needs to be told which one they have. This is the whole reason the third verdict exists.

    not verified

    Drift found nothing it could check this version against.

    What Drift refuses to report

    Six lines that match a changed symbol and get no comment. Every rule below exists because a real run produced the wrong answer without it.

    scrapy/core/downloader/tls.pyno comment

    from twisted.internet.ssl import Certificate

    A symbol this file got from somewhere else. Certificate here is Twisted's, whatever cryptography did to a class of the same name. A name has one binding in a scope, and the import says whose it is.

    components/ui/button.tsxno comment

    const Comp = asChild ? Slot : "button";

    A mention, when the change is a change of signature. A changed argument list is a fact about calls. Storing the name, re-exporting it, or asking for its type passes no arguments and has nothing to update.

    scrapy/utils/conf.pyno comment

    """To define format set a colon at the end of the o…"""

    A word inside a comment, a docstring, or a string. An English sentence containing the word define is not a call to define. Comments are stripped and string contents blanked before any identifier is matched.

    w3lib/url.pyno comment

    + def add_or_replace_parameters(url, new_parameters)

    Anything additive. A new export, a widened parameter, a new optional field. It cannot break a caller, so it appears in the reasons to upgrade and never in the risks.

    openapi.yamlno comment

    + responses.200.content.schema.properties.region

    Changes that break the server, not you. In an API diff only the consumer-breaking direction is reported. A response gaining a field is not your problem, and a report you have to filter is a report nobody reads twice.

    zlib.hno comment

    #define ZLIB_VERSION "1.3.1"

    A version constant that moves every release. ZLIB_VERSION changes on every single tag. A guaranteed finding at the top of every upgrade teaches people to skim past the ones that matter.

    What the confidence on a finding means

    Two separate questions, never merged into one number. Did this change happen upstream? is answered by the evidence — a computed surface diff starts high, prose starts medium, and two independent sources agreeing promotes it. Does it land here? is answered per line.

    High

    The file bound that exact symbol from an import of this dependency. There is an edge from the declaration to this line.

    Medium

    The file imports the dependency and the symbol appears in it. Also the ceiling for C and C++, where #include binds no names and only a compiler could say more.

    Low

    The symbol appears but no import link could be established — a dynamic import, a re-export barrel, or a package whose module name differs from its own.

    A finding produced by the optional AI pass is capped at medium by construction, so it can never on its own clear the bar to open a pull request. It assists recall; it does not get a vote.

    // on every dependency change

    The same analysis, running without you

    The Action is the only surface where Drift acts while nobody is watching, so what it is allowed to do is worth knowing before you enable it. Out of the box it opens no pull request: it analyses, posts a check, files an issue with the plan, and waits for someone to say yes. Autonomy is a setting you turn on per repository, and every guardrail downgrades an automatic run to an approval request rather than dropping it.

    It is the same pipeline, which is not quite the same as the same result: Drift’s strongest evidence in some ecosystems needs that ecosystem’s toolchain on the runner. Where it is missing, Drift falls back to release notes and says so in the report rather than quietly reporting less. The shipped workflow sets up Go and carries commented-out steps for the rest.

    Analyse dependency changes01 / 06

    main.github/workflows/drift.yml·push · pyproject.toml

    1. Set up job

      2s

      The workflow triggers on manifests and lockfiles only, so it does not start on every push and do nothing.

      1on: push

      2paths: **/package.json, **/go.mod, **/vcpkg.json, …

      3permissions: contents: write · issues: write · pull-requests: write · checks: write

    2. Run trydrift/drift@v0

    3. Post the check run

    4. Ask before acting

    5. Verify the approval

    6. Open the pull request

    .github/workflows/drift.yml1 line
    18 - uses: trydrift/drift@v0

    what the default run leaves behind

    drift / analyze — affected

    A check run on the commit, next to your tests.

    #482 Upgrade plan: w3lib 1.17.0 → 2.4.1

    The plan, the evidence, and a comment box. Nothing is pushed until someone writes /drift apply.

    Copilot is only needed for fixes Drift can’t resolve itself. The built-in GITHUB_TOKEN covers everything else. Without a Copilot token, Drift still analyzes the change and applies deterministic remediation; if anything remains that needs an agent, it stops and asks instead of guessing. Add copilot-token only if you want agent fallback for the fixes Drift can’t resolve itself.

    Not ready for Drift to make changes? dry-run: true produces the full report without creating branches, issues, pull requests, or agent tasks.

    Three ways in

    VS Code

    Affected lines inline, with the upstream change that caused each one. Nothing to configure: open a repository and it starts.

    Install from the Marketplace

    CLI

    Full report against your working tree. analyze writes nothing and needs no token — it is the safe first command.

    Read the CLI docs

    GitHub Action

    Checks first, approval next, pull request only after you allow it. See the run. Or build your own drift.yml.

    Copy the example workflow
    
    

    drift analyzeThe full pipeline against your working tree. Writes nothing, needs no token.