Know what breaks before you upgrade.
Point Rift at a package.json or requirements.txt. Every dependency is researched against its own changelogs, release notes, and migration guides — and every breaking change comes back with the sentence it was found in.
- Sources per package
- 6
- Assertion threshold
- 0.75
- Ecosystems
- npm · PyPI
- Retrieval
- Bright Data
Deterministic end to end. No model writes a finding, so every claim traces back to a sentence someone actually published.
$ rift migrate next --from 13.4.19 --to 14.2.0
indexno coverage for this window, researching
registrynpm · resolved 14.2.0
fetchnextjs.org/docs/…/upgrading403→bright dataunlocked
sources6 planned · 6 read · 2 unlocked · 2 from cache
next13.4.19 → 14.2.0HIGH82/1004 breaking
HIGHDEPENDENCY_CONFLICTMinimum Node.js version is now 18.17
“The minimum Node.js version has been bumped from 16.14 to 18.17.”
nextjs.org/docs/app/building-your-application/upgrading·official_docs
MEDIUMDEFAULT_BEHAVIORfetch() is no longer cached by default
“Fetch requests are no longer cached by default.”
nextjs.org/blog/next-14·official_release
wrote.rift/next/14.2.0/migration.md
exit 2 · risk HIGH reached the --fail-on threshold
$
- sources read per package
- 0sources read per package
- confidence to assert
- 0.00confidence to assert
- tests over the engine
- 0tests over the engine
Every claim carries its quote.
Most upgrade tooling summarises. Summaries are where the detail you needed goes to die. Here a finding is stored with the verbatim sentence that produced it, the URL it came from, and how much authority that source has.
Read by authority
Sources are planned before they are fetched: registry metadata, then official releases, then changelogs, migration guides, and finally issues. A blog post never outranks a release note.
Normalize, then extract
HTML and Markdown are flattened into a section tree — the last place tags exist. Extraction turns that tree into knowledge objects, each pinned to the verbatim sentence that produced it.
Attest or stay quiet
Every object keeps its quote, its URL, its authority tier, and a confidence score. A finding that cannot cite a source is not rendered — and “nothing found” always says how many sources were actually read.
Source · official_docs
Upgrading from 13 to 14
Next.js 14 is a focused release with no breaking changes for most apps.
The minimum Node.js version has been bumped from 16.14 to 18.17.
Run npx @next/codemod@latest upgrade to migrate automatically.
nextjs.org/docs · upgrading
Knowledge object
- category
- DEPENDENCY_CONFLICT
- version_scope
- >=14.0.0
- symbol
- engines.node
- confidence
- 0.88
- quote
- “The minimum Node.js version has been bumped from 16.14 to 18.17.”
One sentence in the source becomes one object in the index
Authority is decided before anything is fetched.
Sources are planned as a ladder and read top-down until the document budget runs out. The weight a tier carries is the weight it contributes to confidence — which is why a forum thread can corroborate a finding but can never assert one on its own.
A tier is only worth its weight if the page can be read at all. Hosts that refuse automated requests are fetched through Bright Data, so a 403 costs a transport hop rather than a citation.
- 01Migration guideofficial_migration1.00
- 02Release notesofficial_release0.90
- 03Changelogofficial_changelog0.85
- 04Documentationofficial_docs0.75
- 05Registry metadataregistry0.60
- 06Issues & discussionscommunity0.35
Markdown is an output. The knowledge is the artefact.
Documents are normalized once, into a section tree, and never read back as prose. What downstream sees is a typed knowledge object with a version scope, a category, a confidence score, and a citation.
Eight stages — research only runs when the index cannot answer
Before the upgrade, and after it goes wrong.
The same index answers both. One asks what a version bump will do to you; the other asks which version bump already did.
Upgrade mode
Start from a manifest or a single package. Get back what changed, what breaks, and which of your files touch it.
$ rift repo . --fail-on HIGH --markdown > migration.md- package.json, requirements.txt, pyproject.toml
- Lockfile overlay for the versions actually installed
- Delta classified as major, minor, or patch — then risk-scored
- Findings correlated to the files that import the symbol
Error mode
Paste the stack trace. The error is fingerprinted, matched against indexed knowledge, and researched only if that is not enough.
$ rift error --package prisma --version 6.0.0 \
--error "PrismaClientInitializationError: ..."- Application frames stripped before fingerprinting
- Correlated against breaking changes for that exact version
- Falls through to research when coverage is missing
- Returns a fix plan, or says plainly that it has none
TypeError: Cannot read properties of undefined (reading 'params')
at Page (app/products/[id]/page.tsx:14:22)
at renderWithHooks (node_modules/react-dom/cjs/react-dom.js:16305)
at beginWork (node_modules/react-dom/cjs/react-dom.js:19073)
at Object.invokeGuardedCallback (app/lib/render.ts:8:3)
typeerror:undefined-read:params“params is now a Promise and must be awaited before its properties are read.”
Error mode — application frames are stripped before the trace is fingerprinted
One manifest in. Every upgrade scored and sourced.
Risk is not a star rating. It is a count of the changes that were actually documented between the version you have and the version you would move to — each one clickable, down to the sentence.
- 01Direct1
the package you bumped
- 02Transitive3
packages that depend on it
- 03Your source4
files that import a changed symbol
Hover a ring to trace how far one version bump actually reaches
BaseModel.dict() replaced by model_dump()
“The .dict() method is deprecated and will be removed in a future version — use .model_dump() instead.”
Affected symbols
- BaseModel.dict
- BaseModel.json
- parse_obj
Sample output — select a dependency to read its citation
One engine. Six ways to reach it.
The website is the demo. The real consumers are a terminal, a CI job, and a coding agent that has to decide what to change and then say whether it worked. All six call the same functions — nothing keeps its own copy of the logic, so an answer cannot be right in one place and wrong in another.
Website
Paste a manifest, read the answer. The analyzer runs the same pipeline the CLI does and streams each package in as it lands, rather than making you wait for the slowest one.
- Dashboard — what breaks, and how badly
- Sources — every page, and how it was fetched
- Report — Markdown, ready to paste into a PR
Terminal
Fifteen commands, every one of them with --json, because the first consumer of this output is a coding agent and the second is CI. Everything else is built on this surface.
- package · migrate · error · repo
- search · index · graph · sources
- backfill · prune · reindex · report
- mcp · stats · install-skill
HTTP API
Twelve routes over the same engine, plus three that lend the deployment's Bright Data and Voyage keys to a caller with none. /api/search only ever answers from the index; research is a separate, explicit call.
- /api/parse · /api/analyze
- /api/errors/analyze · /api/graph
- /api/agent/resolve · report
- Served from the deployment, not the web server
MCP
The best door for an agent: six typed tools over stdio, no shell and no parsing. A failure comes back as data rather than as an exit code it has to guess at.
- search_knowledge — what do we already know?
- analyze_error — why did this break?
- research_upgrade — what will this bump break?
- correlate_repository — which of my files use it?
- package_graph — which version fixes this?
- report_fix — that worked, remember it
Agent skill
Not a new door — instructions that teach an agent when to open one, and how to read what comes back. A Claude Code skill, and a harness-agnostic AGENT.md for everything else.
- skills/upgrade-intelligence
- skills/generic/AGENT.md
- Research, apply, then report whether it worked
CI gate
One flag turns the engine into a pass/fail check, because a build needs a number rather than a table. Start at CRITICAL so day one is quiet, then tighten it.
- exit 0 — fine
- exit 1 — something went wrong, reason on stderr
- exit 2 — risk hit your threshold
Three ways in.
One install, and the door you open depends on who is reading the answer — you, a build, or an agent. All three run the same engine against the same index.
- [ 01 ]
Terminal & CI
Install once and the command is rift — the npm name is riftcli, because rift on npm belongs to someone else.
rift repo . --fail-on HIGHexit 2 when something reaches HIGH - [ 02 ]
Any MCP agent
The same engine over stdio. Arguments are typed, and a tool failure comes back as a result rather than as something to parse out of stderr.
- [ 03 ]
Claude Code
Installs the skill, and the agent reaches for rift on its own. Other harnesses want skills/generic/AGENT.md — the same engine, documented over HTTP.
No keys needed. The vendor calls a key would pay for are borrowed from the deployment — Bright Data for the pages that refuse a plain request, Voyage for semantic search — while the cache, the extraction, and the index stay on the machine that ran the command. Nothing about the repository is sent anywhere, and RIFT_RELAY_URL=off refuses even that.
What it holds to — and what it admits it cannot do.
A tool that tells you what breaks is only useful if you can tell when it is guessing. So it does not guess, and the places it falls short are written down next to the places it does not.
Rules it holds
- 01Every claim carries its quoteExtraction attaches the verbatim source sentence to each object. A finding that cannot cite cannot be rendered.
- 02Version is a hard filter, not a hintKnowledge scoped to >=16.3.0 is excluded from a 15.0.0 query outright, however well the text matches.
- 03Absence of evidence is reported as such“No breaking changes found” is always qualified with how many sources were actually read.
- 04Agent-written knowledge is capped0.6 confidence until validated, 0.85 when a repository verified it. Passing tests prove a change works in one place, not that it is the migration the maintainers intended.
- 05The index is consulted before the networkResearch runs only when coverage is missing or retrieval comes back insufficient.
Limits it admits
- Semantic retrieval needs a keyWith one, an error phrased nothing like the changelog still matches. Without one, ranking falls back to BM25 and the wording has to line up.
- Extraction is deterministicIt classifies by heading, commit prefix, and prose pattern. It will not summarise or infer a migration the source never states.
- Not every usage site is parsedA site marked parsed came from the module graph and can be trusted. One marked textual is a lead — open the file before you edit it.
The parts people ask about twice.
Those tell you a newer version exists. They cannot tell you that the version bump removes a method you call on line 40 of a file you forgot about. Rift reads what the maintainers wrote about the change and correlates it against your source.
From the document that was actually fetched. Sources are ranked by authority — registry metadata, official releases, changelogs, migration guides, then issues — and the quote stored with a finding is the sentence it was extracted from, not a paraphrase.
It gets read anyway. Registries and GitHub answer plain requests, but a lot of documentation and changelog hosts return 403 to anything automated — and a source that cannot be fetched is a claim that cannot be cited. Those go through Bright Data's Web Unlocker, and the trace records which transport each source arrived on, so you can see exactly which findings depended on it. Search discovery uses the same account's SERP zone.
Not for extraction. The pipeline is deterministic end to end, which is why a finding can always be traced back to a sentence. There is a documented seam for a model pass that may improve wording, but it may not introduce claims.
Yes, and that is the point. What gets indexed is knowledge about a package, not about a repository, so it lives in ~/.upgrade-intel and every repo on the machine reads the same index. Solve a chalk error once and the next project already knows. A project that wants its own index opts in by creating a local .upgrade-intel directory.
It stops short of telling you what to do. Below 0.75 confidence a finding is a hypothesis, so you get the diagnosis, the evidence, and a caveat — not a migration to apply. An agent reading the JSON sees the same threshold and is instructed to treat it the same way.
Network, mostly. Sources are cached with per-type TTLs and revalidated with ETag or Last-Modified, and the index is consulted before anything is fetched, so a repeated run over the same manifest usually touches nothing.
npm and PyPI — package.json with a lockfile overlay, requirements.txt, and pyproject.toml. GitHub releases, issues, and in-repo files are read for both.
That is what --fail-on is for. rift repo . --fail-on HIGH exits 2 the moment a dependency reaches that risk level, and --json gives the build something to attach to the PR.
Read the changelog before the incident report.
Run it on a manifest you already have. It will tell you what changed, cite where it read that, and say plainly how much it could not find.