// 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.
// 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.
Tap or hover a cell for the exact sentence.
| Ecosystem | Detect | Evidence | API surface | Static analysis | Find upgrades | Install a version | Verify | Fix | Automated 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.
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
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.inipyproject.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
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
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 changessignature-changeconfidence: highcites: computed surface diffThe signature of
url.safe_url_stringchanged.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
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 confidencesymbol: safe_url_string25 results · 10 filesscrapy/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_stringfrom 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
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 commitscodemoddeps: w3lib 1.17.0 → 2.4.1
The manifest and the lockfile, alone, so the version move is revertible on its own.
recipefix(w3lib): pass quote_path explicitly at 25 call sites
A version-pinned community recipe for the signature change, applied deterministically.
agentfix(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.
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.
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 CertificateA 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.regionChanges 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.
main.github/workflows/drift.yml·push · pyproject.toml
Set up job
2sThe 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
Run trydrift/drift@v0
—Post the check run
—Ask before acting
—Verify the approval
—Open the pull request
—
| 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 MarketplaceCLI
Full report against your working tree. analyze writes nothing and needs no token — it is the safe first command.
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 workflowdrift analyze — The full pipeline against your working tree. Writes nothing, needs no token.