Repository navigation
fix(check): an error the upgrade only rewords is not new - #141
Merged
Merged
Conversation
The compile signal subtracts baseline errors matched on file, code and message. A pre-existing error whose message prints a type the upgrade changes no longer matched, and was reported as a breaking site the upgrade made. OpenMetadata's sixteen `Property 'backHandler' does not exist` errors did so once React Router 7's navigate turned `() => void` into `() => void | Promise<void>` in the elaborated type. Both programs check the same source text, so an error at the same file, code and position whose chain of messages is the same once the type expressions it prints are masked is the same error, and now counts as pre-existing too. Quoted plain names stay, so a chain that names another member is still reported: an assignment that already failed on property `a` and now fails first on `b` ends on the same last message, and is new. A different reason at the place (one more missing property) is reported too. Moved lines still match on the full message, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EV6fvvLupC2Hdbwcg7iovY Signed-off-by: Claude <noreply@anthropic.com>
Every registered pack's ground truth installed at once is about 24 GB, measured per pack while re-scoring them all; a standard hosted runner documents 14 GB of disk. Scored one pack at a time with that pack's repositories evicted before the next, the cache stays under 5 GB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EV6fvvLupC2Hdbwcg7iovY Signed-off-by: Claude <noreply@anthropic.com>
8 tasks done
lucasbraum
added a commit
that referenced
this pull request
Oct 15, 2026
Stacked on #141 (the compile-signal fix); its base is that branch until #141 merges. ## Pack `react-router` `>=6 <7` → `>=7 <8` (default target 7.18.4), queue entry `react-router`. `react-router-dom` is a `releaseGroup` member (published in lockstep, pinned exactly). It is also the pack's **alternate lead** (#134), so the pack runs for the usual v6 setup that declares only `react-router-dom`: `check react-router` and `check react-router-dom` both run it, and `fix` declares `react-router` at the target beside it. The queue asked for 6 → 8. The evidence says that is two upgrades: - The v6 guide exists only in the v7 docs. - Each guide assumes the previous major's future flags were adopted on that major. - `react-router-dom` has no 8.x. So this PR ships 6 → 7. `packs/queue.json` retargets the entry to `>=7 <8` (verified) and adds a 7 → 8 entry (`todo`). Also here: one commit fixes `pack new`'s registry renderer, which ordered `./react-router/` before `./react/` against biome. It comes with a regression test, on top of #128's wrapping fix. ## Sources All pinned to release commits and read 2026-10-14. Each change is classified in `packages/core/src/packs/react-router/RESEARCH.md`. - [Upgrading from v6 @ 7.18.4](https://github.com/remix-run/react-router/blob/1b1e0b0e79b21692ce907933475233babdd16e3e/docs/upgrading/v6.md) - [v6 future flags @ 6.30.6](https://github.com/remix-run/react-router/blob/dead4962d6c33e962f266af910b7a09e3ece30cf/docs/upgrading/future.md) - [react-router CHANGELOG 7.0.0–7.18.4](https://github.com/remix-run/react-router/blob/1b1e0b0e79b21692ce907933475233babdd16e3e/packages/react-router/CHANGELOG.md) - [react-router-dom CHANGELOG](https://github.com/remix-run/react-router/blob/1b1e0b0e79b21692ce907933475233babdd16e3e/packages/react-router-dom/CHANGELOG.md) and its [7.0.0 entry point](https://github.com/remix-run/react-router/blob/e8e3b004d94a949372e77ce97a543bff5bc6fb89/packages/react-router-dom/index.ts) - [Root CHANGELOG](https://github.com/remix-run/react-router/blob/1b1e0b0e79b21692ce907933475233babdd16e3e/CHANGELOG.md) - For the split: [Upgrading from v7 @ 8.4.0](https://github.com/remix-run/react-router/blob/7ccdcecdd944e15be0cd0dcb702b86c9af1214a8/docs/upgrading/v7.md) ## Rules and behavior notes **Mechanical** (a rewrite at the reported site), exercised by ground truth: - `dom-imports` (deprecated, detect + rewrite): moves a `react-router-dom` import or re-export to `react-router` when every name it takes is there. It declines when a test mocks `react-router-dom` by name. Phoenix exercises it with 29 sites. **Assisted, rewrite ready** (detection runs; the edit is unit-tested but not wired until a ground-truth repository exercises it; RESEARCH.md lists them): - `future-prop` (breaking, TS2322): removes the `future` prop of a router (aliases resolved through the import), inline `v7_*` literals only. The only inline-literal upgrades seen, wire-webapp and OpenMetadata, are excluded by the parent rule below. Lago's sites pass a variable. - `dom-server-entry` (breaking, TS2307 + detect): `react-router-dom/server` becomes `react-router`. - `future-option` (deprecated, detect): the stale `future` option of a router factory, which v7 types `{}`. - `form-method-case` (breaking, TS2367/2678/2820 + detect): a lowercase method compared with `.formMethod`. This is the syntactic detect for comparisons the compiler is blind to through `any`/`string`. **Assisted** (a guide for the agent; guide options go to "Decisions for you"): - `dom-entry-imports` (deprecated, detect): `RouterProvider`/`HydratedRouter` from `react-router/dom`, which needs `moduleResolution` bundler/node16/nodenext. - `future-prop-untyped` (deprecated, detect): a `future` prop passed through untyped `createElement` (jest mocks); v7 ignores it. - `removed-json` and `removed-defer` (breaking, compiler + binding-aware detect). - `removed-exports`, `fallback-element`, `stabilized-options` (breaking). - `generate-path-params` (breaking): `generatePath` rejects `null` for a required param since 7.14.2. Lago has 8 such sites. **Behavior notes** (`decision`, each with `applies()` evidence from the inventory). A flag note is omitted when the repository already enabled that flag: - `relative-splat-path`, `start-transition`, `fetcher-persist`, `normalize-form-method`, `partial-hydration`, `skip-action-error-revalidation` - `data-strategy`, `navigation-promises`, `platform-minimums`, `remix-router-package`, `dom-package`, `also-review` `requiresBuild` is not set. `@react-router/*` has no 6.x release, so it is not declared. No framework mode and no route-structure change. ## Ground-truth repositories **Final truth: Arize-ai/phoenix and getlago/lago-front.** Expectations were drafted with `pnpm packs:truth` and the upgrade diff, then reviewed by hand before scoring, and none changed afterwards. Installs ran with scripts off. Nothing was pushed, commented or opened on these repositories. - [Arize-ai/phoenix](Arize-ai/phoenix#6896) - `app/`, pnpm. Parent `4d6c1dfb01e9406cf769efab14f0350ea2d0a0f8`, upgrade `3e9dc43bcc686e8ff3d57a028a9fcc15f3935490`, 6.25.1 → 7.4.0. - 30 expected: 29 `dom-imports`, 1 `dom-entry-imports`. - [getlago/lago-front](getlago/lago-front@6055a5a) - pnpm, declares only `react-router-dom`. Parent `84aa05d535edc326a4d104923e4e7293d1be6646`, upgrade `6055a5a3c8076522235fe2da452a209e99af8541`, 6.30.6 → 7.18.3. - 18 expected: 6 `future-prop`, 8 `generate-path-params`, 1 `removed-exports`, 3 `generic`. **Parents that do not type-check** (one rule, applied to all four drafted repositories, RESEARCH.md "Parents that do not type-check"): - The rule: run the parent's own project compile (its type-check script and tsconfig). A parent that fails is excluded, unless every error comes from an unbuilt workspace package the upgrade does not touch, shown by building it. - **Phoenix**: `tsc --noEmit` in `app/` reports 0 errors. Kept. - **Lago**: `scripts/check-types.js` reports 188 errors, all from `lago-design-system` before it is built. With its declarations emitted as its own build does, the parent compiles clean and so does the upgrade commit. The parent's code against the upgrade's dependencies then reports exactly the truth's 14 compiler sites. Kept, confined. - **wireapp/wire-webapp** (#22299): 1979 errors, including a gitignored generated icon module and 5 in `testUtil.tsx`, which the upgrade edits. Excluded. - **open-metadata/OpenMetadata** (#31641): 1481 errors, including ones unrelated to any build and one in `AppRoot.tsx`, which the upgrade edits. Excluded. Screened out (see RESEARCH.md): feishin (out-of-sync lockfile, and git-hosted dependencies this environment cannot reach), medusa (a 79-file mixed commit), and others with few compiler-visible sites. ## `uptide pack test` output On this branch, rebased on main `9ba005a` with #141 under it: ``` uptide pack test react-router · verified fixtures 9 cases, 29 sites · pass ground truth Arize-ai/phoenix@4d6c1dfb01e9 react-router 6.25.1 → 7.4.0 predicted 31, expected 30, false positives 1, false negatives 0 with react-router-dom 6.25.1 → 7.4.0 getlago/lago-front@84aa05d535ed react-router 6.30.6 → 7.18.3 predicted 351, expected 18, false positives 335, false negatives 2 with react-router-dom 6.30.6 → 7.18.3 rule precision recall tp fp fn dom-entry-imports 100% 100% 1 0 0 dom-imports 8% 100% 29 336 0 future-prop 100% 67% 4 0 2 future-prop-untyped 0% 100% 0 2 0 generate-path-params 100% 100% 8 0 0 generic 100% 33% 1 0 2 removed-exports 100% 100% 1 0 0 overall (sites) 12% 96% 46 336 2 breaking (sites) 100% 88% 14 0 2 false negatives (2) getlago/lago-front@84aa05d535ed src/components/activityLogs/__tests__/utils.test.tsx:348 generic getlago/lago-front@84aa05d535ed src/core/router/__tests__/Link.test.tsx:94 generic reported under another rule (2) getlago/lago-front@84aa05d535ed src/test-utils/routerFutureMock.ts:12 future-prop-untyped, expected future-prop getlago/lago-front@84aa05d535ed src/test-utils/routerFutureMock.ts:14 future-prop-untyped, expected future-prop PASS react-router: verified ``` - **Breaking: 100% precision, 88% recall (14 TP, 0 FP, 2 FN).** - **The 336 false positives are all `deprecated` `dom-imports` moves the upgrades did not make.** Lago kept all 335 of its `react-router-dom` imports, which v7 still serves; Phoenix kept one. `fix` applies them only with `--include-deprecated`. - **Misses.** The two breaking misses are Lago's deleted jest mock (`routerFutureMock.ts`). The pack finds it but files it as `future-prop-untyped` (deprecated), because the prop goes through untyped `createElement` and v7 ignores it. The two `generic` misses are test assertions whose v7 behavior changed (generatePath escaping, Link href normalization). No compiler site and no syntax match exists for those. - **#141's effect here:** with #141's change reverted, the same final truth gives identical findings. ### What the engine could not express 1. A workspace whose lockfile sits at the monorepo root cannot be ground truth with `directory` (`UNSUPPORTED_PACKAGE_MANAGER`); wire-webapp hit this. 2. A package the upgrade removed (Phoenix's `react-router-dom`) cannot be stated in the truth, so it is linked at the target. `packs:truth` also omits the lines of deleted files. 3. Fixture `keep` means neither detected nor rewritten, so a detected-but-declined site needs a rule of its own. 4. `applies()` reads the inventory as text, so one router's flag silences its note for the whole repository. 5. Behavior only tests observe has no finding to attach to. 6. A parent whose own compile needs a build step is scored unbuilt (scripts off). For Lago, the built-world check shows this hides nothing. ## Checklist - [x] Every rule and note comes from a source listed in `meta.sources` - [x] Every rule that rewrites or detects has a fixture case, with a site it must leave alone (`keep`); the rewrite-ready edits are unit-tested - [x] Ground truth: public repositories at the commit before their upgrade; expected findings read from their own upgrade commit and the compiler, never copied from Uptide's output - [x] At least two public repositories for `verified` - [x] `pnpm uptide pack test react-router` passes and its output is pasted above; `verification.json` written with `--write` - [x] `pnpm lint && pnpm typecheck && pnpm test` pass: full suite on an idle machine with default timeouts gives 2,120 tests, none failing. The lint warning and info are pre-existing on main. - [x] Changeset added (`.changeset/react-router-seven.md`) - [x] No private code, names or paths anywhere: fixtures are synthetic, ground truth is public 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01EV6fvvLupC2Hdbwcg7iovY --------- Signed-off-by: Claude <noreply@anthropic.com> Co-authored-by: Claude <noreply@anthropic.com>
Merged
lucasbraum
pushed a commit
that referenced
this pull request
Oct 15, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## uptide@0.13.0 ### Minor Changes - [#134](#134) [`b795705`](b795705) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Packs can name alternate leads: packages a repository declares instead of the pack's own (`react-router-dom` for `react-router`). `check` and `fix` asked for either name use the pack; where only the alternate is declared, the lead is analyzed at the version installed through it, pnpm stores included, and `fix` declares it beside the alternate at the target before upgrading both. - [#149](#149) [`3303118`](3303118) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Add a candidate express 4 to 5 migration pack. Route strings that path-to-regexp 8 rejects or reads differently are found on calls of an Express app or router (including lists registered in a loop); `req.params` values widened to `string | string[]`, `req.param`, `res.sendfile`, `express.static.mime` and `express.query` are claimed from the compiler; `res.redirect(url, status)`, `app.param(fn)`, `res.redirect('back')` and `res.send(<number>)` are found in the source; runtime defaults (async errors, `req.body`, the query parser, `urlencoded`, dotfiles, Node 18) go to decisions. No rule rewrites. With the pack in use, `check` no longer adds its text-matched express 5 runtime findings. - [#148](#148) [`5143c46`](5143c46) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Add a candidate joi 17 to 18 migration pack. `new ValidationError(message, {}, original)`, which stops compiling for code written against joi 17.0–17.6, is rewritten to pass `[]`; the Node.js 20 requirement and the top-level domains joi 18 no longer accepts in `email()`, `domain()` and `uri()` are reported as decisions. Scored against two public repositories, one of them a negative control. - [#131](#131) [`413d3da`](413d3da) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Add a verified lucide-react 0.x to 1.x migration pack. Brand icons removed in 1.x are found at their imports and every use, with a guide whose replacement choice goes to the owner; icon names dropped during 0.x are renamed at the import or member; deep `dist/` imports, untyped icon lookups, the `absoluteStrokeWidth` and `IconNode` deprecations, and the `aria-hidden` default for repositories before 0.489.0 are reported. Lucide's lockstep packages move together and the consumer's build is required. Verified against three public repositories, one of them a negative control. - [#132](#132) [`107c107`](107c107) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Add a verified React Router 6 → 7 migration pack, which also runs for applications that declare only `react-router-dom`. It moves `react-router-dom` imports to `react-router`. It reports, with a guide for the agent, the rest of the upgrade: the removed `future` prop and router option, `react-router-dom/server`, lowercase `formMethod` comparisons and `json`/`defer` calls (found by syntax where the compiler is blind), the other removed exports, and `generatePath` calls that pass `null` for a required parameter. The six v6 future flags that become defaults are reported as decisions where the repository's router setup has not enabled them. React Router 7 → 8 is a separate upgrade. - [#142](#142) [`9c57934`](9c57934) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `check` and `fix` plan what the target version requires: a package the target, or anything moving with it, declares as a peer or dependency at a range the installed copy rejects moves to the lowest release of its major that satisfies it (openai 7 moves zod 3.24.2 to 3.25.0), shown apart from release-group companions ("moved because openai 7.4.0 requires zod ^3.25 || ^4.0"). A move only a new major makes is never made: it is reported as a blocker with the exact range in `check`, the PR body and `pack test`. The npm install scope guard no longer lets a declared root dependency change because a moved package peers on it. - [#128](#128) [`06f11ab`](06f11ab) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Add verified Sentry JavaScript SDK 10 to 11 migration packs for `@sentry/nextjs` and `@sentry/browser`, with the SDK's lockstep release group, a required build for Next.js, and inventory-scoped decisions for data collection, span streaming, sampling and platform minimums. Mechanical rules move `withSentryConfig` to `@sentry/nextjs/config` and the removed build options under `webpack`, remove `enableLogs`/`enableMetrics: true`, and rename exports, integration names and `httpIntegration` options; privacy, sampling, profiling and OpenTelemetry choices stay assisted and are reported for the owner. Fix `uptide pack new` dropping registry entries long enough to be wrapped. - [#155](#155) [`922c5ab`](922c5ab) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `uptide pack test` applies a stricter verified gate: at least five expected breaking sites across the public ground-truth repositories, besides two repositories and no breaking false positive. Upgrades the compiler cannot see are gated on their behavior notes instead. Mechanical rules no real site exercises are recorded as unexercised, and `check` and `fix` run their sites as assisted. `@sentry/browser` and `@sentry/react` become candidates (fewer than five real breaking sites). A pack can lower the minimum only with a written reason in its RESEARCH.md. The README packs table shows each pack's breaking-site count. The output shows the gate's class, site counts and every reason a pack is a candidate. - [#147](#147) [`0b88170`](0b88170) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `check` and `fix` remove a deprecated `@types` stub instead of upgrading to it. When the release the upgrade would move `@types/<pkg>` to is deprecated on npm, and `<pkg>`'s target declares its own types (`types`, `typings` or a `types` export), the plan removes it: "Removed: @types/ioredis, ioredis 5 ships its own types", in `check` and in the PR body. `check` now diffs such a package against its own target types instead of failing on a stub with no declarations, and `fix` removes the entry in the same install, with the install scope guard allowing exactly that removal. - [#150](#150) [`4c3212b`](4c3212b) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `check typescript` judges a TypeScript 5 to 6 upgrade with both compilers: every project of the workspace is compiled with the installed TypeScript and with the target's, and what is new at the target is a finding; the bundled compiler never stands in, and a target without a JavaScript API (TypeScript 7) is reported as not compiled. A candidate typescript 5 to 6 pack reports the tsconfig options 6.0 deprecates where they are set, the rootDir default, module-keyword namespaces and import assertions. A peer that rejects the target (typescript-eslint on TypeScript 6) is now reported as a peer conflict even when it is not grouped with the package. - [#137](#137) [`21894d7`](21894d7) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Add `verify.build` to uptide.yml: the package.json script a migration pack's required build runs. Unset, Uptide runs `build:ci`, then `build-ci`, then `build`. When a build fails before the framework's build starts because it needs an environment variable or a database (`prisma db push` without `DATABASE_URL`), the pull request and the report say so ("build needs DATABASE_URL; set verify.build") instead of a bare "build failed". `uptide init` writes the setting as a comment. ### Patch Changes - [#141](#141) [`9debb6d`](9debb6d) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `check` no longer reports a pre-existing type error as a breaking site when the upgrade only changes its wording, such as a type the message prints in full. An error at the same place with a different reason is still reported. - [#129](#129) [`e796e4c`](e796e4c) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Add a candidate ioredis 5 to 6 migration pack: client constructions that inherit the RESP3, reconnect and keep-alive defaults ioredis 6 changed, compiler-blind sites (`mode === 'subscriber'`, commands in a `connect` listener with the offline queue off, AUTH recognized by command name), dependents that bundle ioredis 5, instrumentation that stops patching ioredis 6, and Node 20. It is scored against five public upgrades and stays a candidate: none of them has a breaking site to verify it with. - [#139](#139) [`9ba005a`](9ba005a) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Allow local check and fix smoke tests of registered candidate packs with --allow-candidate, with an explicit unverified banner. Reject candidate opt-in in the Action and with publication flags, and prevent stored candidate runs from being published. Label pack-claimed assisted sites by rule id even with --no-llm. - [#157](#157) [`f3af6a1`](f3af6a1) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `check` no longer reports a blocker for a direct dependency whose plain dependency range rejects the target (`@azure/ai-projects` on `openai ^6`, verdaccio pinning express 4): the package manager gives it its own nested copy, so it is listed as staying, with the new major that would accept the target when there is one. `pack test` counts a `with` move that `check` leaves and explains (a blocker, a new-major stay or a peer conflict) as explained, and one of a package that does not depend on the lead as the repository's choice; neither fails the run. - [#130](#130) [`5ced450`](5ced450) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Add a candidate openai 4 to 7 migration pack: named path parameters, `del` → `delete`, promoted beta chat helpers, renamed runner events, removed entry points and request-option overloads are rewritten where unambiguous; tool-call unions, `httpAgent`, `fileFromPath` and the other removals get agent guides; `APIError.headers`, Web `Response` bodies and double-encoded path ids are detected even in code typed `any`; Node 22, zod, fetch and client-subclass decisions carry repository evidence. It stays a candidate (the dependency is still checked and fixed as generic) until two engine gaps its ground truth exposed are closed. - [#136](#136) [`578a042`](578a042) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Fold implicit-any errors (TS7006, TS7031, TS7019) inside an option the target removed into the pack finding on that option, as downstream evidence: the callbacks lost the types the option gave them, and the same edit resolves them. They no longer stand as separate breaking sites. - [#143](#143) [`883e587`](883e587) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `uptide pack test` scores each expected site at the severity of the rule or behavior note it belongs to, so a missed note that only needs a look, or a decision, no longer counts as a missed break. Behavior-note sites get their own precision and recall in the output and in `verification.json` (`behavior`, with the counts behind each rate), false negatives show their severity, and a rate with nothing to measure prints `n/a` instead of 100%, in `pack test` and in the README's packs table. - [#154](#154) [`7aa3d58`](7aa3d58) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `uptide pack test` fails a pack's `verification.json` only when it claims more than the run measures (recorded verified, measured candidate). A pack recorded as candidate on purpose passes, with `recorded candidate by choice; measures verified`, and a record whose numbers differ gets a note instead of a failure. A `generic` ground-truth site now counts as a true positive for the rule that claims it. - [#152](#152) [`ca31aee`](ca31aee) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `uptide pack test` takes several packs: `uptide pack test zod ai`. - [#159](#159) [`1e157fd`](1e157fd) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `uptide pack test --json` times each ground-truth repository: `timing` gives the fetch, install and check wall time and the process's peak memory. - [#144](#144) [`0881185`](0881185) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Ground truth can name a site for an upgrade that only adds lines inside an existing call, such as an option added to a constructor: the site is the call's start line, so a pack finding there scores as a true positive instead of a false positive. `pnpm packs:truth` drafts these anchors from insert-only hunks for review, and docs/packs.md and CONTRIBUTING describe the rule. - [#156](#156) [`9039e3c`](9039e3c) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Public ground truth now exercises three rules that ran as assisted: zod `error-params`, stripe `api-version-unpinned` and next `stable-cache-exports`. Their rewrites run mechanically again. All three packs stay verified. - [#138](#138) [`0db5640`](0db5640) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Respect repository tsconfig file selection, recursive project references, and workspace project options when checking dependency upgrades. Compiler diagnostics outside every TypeScript project no longer produce breaking findings; pack rules and build-configuration findings retain their own scope. Repositories without a tsconfig retain importer-based compilation, with that fallback stated in the report. Anchor missing members supplied by dependency augmentations at their setup import, retaining affected calls as evidence instead of counting each call as a separate breaking edit. - [#151](#151) [`9ddaaff`](9ddaaff) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Add a candidate redis (node-redis) 4 to 5 and 5 to 6 migration pack, one rule set per major: an upgrade gets the rules of the majors it crosses, so 4 to 6 gets both. 4 to 5 covers `legacyMode`, `commandOptions` and isolation, scan iterators that yield batches, string SCAN cursors, boolean replies that became numbers, enums that became constants, `keepAlive`, `@redis/graph` and internal `dist` imports; 5 to 6 covers the RESP3 client type, `unstableResp3`, and the client defaults 6 changes (RESP3, command timeout, keep-alive delay, maintenance notifications) with a decision per client. Scored against five public upgrades; it stays a candidate until the stricter verified gate lands, since the core 4 to 5 API changes have no real site yet. - [#135](#135) [`42a3b54`](42a3b54) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Move only packages released with the upgraded one. A pack's declared release group now bounds the lead's npm scope, and without one a same-scope package moves only when a pin, a peer or a release at the target relates it to the group. Packages of the scope that are versioned on their own (`@sentry/vite-plugin` 5.x, `@sentry/cli`, the old `@sentry/utils` 8.x) are reported as "stays at X" in `check`, the pull request and `pack test` instead of making `fix` refuse the whole upgrade. Registers the verified `@sentry/react` 10 to 11 pack. - [#146](#146) [`09b8682`](09b8682) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Report Stripe clients pinned to different API versions at the top of `check` and of the PR body, as a decision for the owner: every `new Stripe(...)` in TypeScript and JavaScript sources, scripts included, with the version it speaks after the upgrade (an unpinned client speaks the SDK default). The PR's decisions list, for each client behind the target, the changelog entries it would cross for the resources it calls. No pin is rewritten. - [#140](#140) [`bc428d1`](bc428d1) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Keep migration PR bodies within their size budget while preserving summaries, representative diffs, decisions, review warnings and verification ahead of changelog and transitive detail. Keep every evidenced Stripe change visible and uncapped; group and cap only other changes to used resources, link the full changelog, and identify omitted text in the retained report. - [#145](#145) [`3c8902d`](3c8902d) Thanks [@lucasbraum](https://github.com/lucasbraum)! - The zod pack migrates `ZodError#errors` to `.issues` by rule: reads (`error.errors[0].message`, `error?.errors`) and destructuring (`const { errors } = error` becomes `const { issues: errors } = error`) where the type checker resolves the receiver to `ZodError`, and reads off an untyped catch binding of a zod parse. Writes are left to the agent. - [#127](#127) [`c4ead4f`](c4ead4f) Thanks [@lucasbraum](https://github.com/lucasbraum)! - zod: `check` now reports `ZodError#formErrors` and `#errors` read off an untyped catch binding of a zod `parse`/`parseAsync` (`catch (error: any) { return error.formErrors.fieldErrors }`), which zod 4 removed and the compiler cannot see. Files that only import a schema from the repository's own module (`@/lib/zod`) are covered too. `fix` rewrites `<err>.formErrors.fieldErrors` and `<err>.formErrors.formErrors` to `z.flattenError(<err>)`, importing `z` where it is missing; every other read goes to the agent. Co-authored-by: uptide-release[bot] <338861287+uptide-release[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Based on main. #132 (the react-router pack) stacks on this one.
What and why
Signal B (
compile.ts) subtracts the baseline's errors from the target's, matched on file, code and full message. A pre-existing error whose message prints a type the upgrade changes no longer matched. It was then reported as a breaking site the upgrade made. Real case: OpenMetadata's sixteenProperty 'backHandler' does not exist on type 'IntrinsicAttributes & object & …'inEntityVersionPage.component.tsx. All sixteen exist at the parent: a real scripts-off install ofreact-router-dom@7.18.1and the repository's owntscshow them. Only their wording changes, because React Router 7's navigate turns() => voidinto() => void | Promise<void>in the elaborated type.check react-routerreported one of them as a breaking false positive.The fix: an error at the same file, code and position whose chain of messages is the same once the type expressions it prints are masked is pre-existing. Both programs check the same source text, so a position names one construct.
'{ backHandler: () => void; … }','IntrinsicAttributes & object') with'…'.'Opts') is kept. So the chain still says which member fails.Can it hide a real new error? (review item 3) Not when the chain says something different. Two cases are tested, each at the same file, code and position as a baseline error:
const opts: Opts = valuealready fails on propertya; the target also breaksband checks it first. tsc reports one error per assignment, so the chain differs only inTypes of property 'b' are incompatible. Its last message (Type 'string' is not assignable to type 'number') is the same. A first version of this fix matched on the last message alone and hid it: that is the third test, and it fails on that version. The full masked chain reports it.{}misseda, band now missesa, b, c. The reason differs, so it is reported.The remaining blind spot is inherent to tsc and was already there with the full-message key. If the upgrade adds a second incompatibility after the first one tsc reports, tsc prints the same error and says nothing about the second.
How it was verified
Failing first, each test against the code without its part of the fix:
compile.test.ts)(The second test fails against a position-only key, which was the version before the reason was added.)
Full suite (
pnpm test) on an idle machine with default timeouts, on #132's branch (which contains this one): 2,120 tests pass (CLI 452; core 1,668, 32 skipped), none fail.Every pack, re-scored
Every pack registered on main, including
@sentry/react(#135):pnpm uptide pack test <pack> --jsonon main578a042(before) and on this PR (after). Both sides ran on the same freshly fetched repositories (scripts off, the global pnpm, corepack for Yarn Berry only), and each pack's repositories were evicted before the next. Status, then TP/FP/FN for breaking and overall.Main moved by #139 after the re-score (not analysis-related: an opt-in candidate flag and its warning label;
pack testpasses its own packs tocheck, so scoring is unchanged). This branch is rebased on it; CI's Pack test re-checks at merge.No pack changes. The fix only matches an error that is already in the baseline at the same place with the same masked chain. No registered pack's ground truth has such a pair; the case came from OpenMetadata, a react-router candidate repository later excluded (#132's RESEARCH.md). vitest's 18938 and react's 6 overall false positives are non-breaking findings (both have 0 breaking false positives), identical on both sides. On #132's branch, react-router scores the same with and without this fix on its final truth.
Cache size (review item 4, now in docs/packs.md): measured per pack while re-scoring, the largest are react and @sentry/nextjs at 4.3 GB each. All packs at once is about 24 GB, against the 14 GB of disk a standard hosted runner documents. Evicting per pack keeps it under 5 GB.
Checklist
pnpm lint && pnpm typecheck && pnpm testpasspnpm changeset)🤖 Generated with Claude Code
https://claude.ai/code/session_01EV6fvvLupC2Hdbwcg7iovY
Generated by Claude Code