Repository navigation
Version Packages - #133
Merged
Merged
Version Packages#133
Conversation
uptide-release
Bot
force-pushed
the
changeset-release/main
branch
12 times, most recently
from
October 15, 2026 03:29
03426c3 to
c910995
Compare
7 of 8 tasks
uptide-release
Bot
force-pushed
the
changeset-release/main
branch
3 times, most recently
from
October 15, 2026 05:00
e5ee105 to
9a5e659
Compare
lucasbraum
added a commit
that referenced
this pull request
Oct 15, 2026
…8) (#151) ## Pack `redis` (node-redis), `>=4 <6` → `>=5 <7`, for [UPT-58](https://linear.app/uptide/issue/UPT-58/pack-redis-node-redis-4-5-and-5-6). ioredis is a separate pack (#129). **One pack, two rule sets.** 4 → 5 and 5 → 6 are separate rule sets, and an upgrade gets the sets of the majors it crosses: 4 → 6 gets both, 5 → 6 never sees a 4 → 5 site. It is one pack and not two because pack lookup is one pack per package, and because 4.7 → 6.x in one step is the most common real upgrade (Dependabot offers it to every 4.x user; 3 of the 6 upgrades found did it). The gate is `appliesTo` in `index.ts`: detection, `ruleOf` and notes check the crossed majors, and `transform` refuses a 4 → 5 rewrite on code already on 5. **Status: candidate.** `verification.json` records candidate; since #154 pack test accepts a record that claims less than the run measures. ## Sources Pinned at node-redis `b64bacb`, read 2026-10-14: `docs/v4-to-v5.md`, `docs/v5-to-v6.md`, `docs/RESP.md`; release notes `redis@5.0.0` and `redis@6.0.0`; published declarations of @redis/client 1.6.1 / 5.12.1 / 6.3.0, @redis/search and @redis/time-series. Release group: the five `@redis/*` packages `redis` pins at its own version. `@redis/graph` is gone in 5. ## Rules and behavior notes **4 → 5** - `legacy-mode`: compiler + detect; assisted (`client.legacy()`). - `command-options`: `commandOptions()`, `isolated`, `executeIsolated`; compiler + detect; assisted (`withCommandOptions`, `createClientPool`). - `scan-iterators`: detect only; **rewrite** to a nested loop, declined when the body `break`s/`continue`s the loop. - `keep-alive`: compiler + detect; **rewrite** `keepAlive: N` → `keepAlive: true, keepAliveInitialDelay: N`. - `scan-cursor`: string cursors; compiler + detect (`cursor !== 0` never ends in JS); assisted. - `number-replies`: boolean → number replies; compiler; assisted. - `renamed-exports`: 17 enums → constants, `SearchOptions` → `FtSearchOptions`; **rewrite**: import under the old local name, declined when the file uses a member whose name or value changed (compared member by member in the declarations) or uses the name as a type. - `deep-imports`: `@redis/*/dist` internals; compiler only; assisted. - `graph-removed`: compiler + detect; assisted, a decision for the owner. - `quit-disconnect` (deprecated): **rewrite** to `close()`/`destroy()`, only where the result is unused. - `set-expiration` (deprecated): `EX`/`PX`/… → `expiration`; assisted. Not in the guide; from the declarations. - Notes: `search-dialect-2` (finding at query calls without `DIALECT`), `pipeline-discards-unwritten`, `node-18` (decisions). **5 → 6** - `client-type`: `RedisClientType` is invariant in RESP and clients default to RESP3, so a client typed `RedisClientType` / `ReturnType<typeof createClient>` breaks. **Not in the guide**; seen in all four real upgrades that reached 6; assisted. - `unstable-resp3`: compiler + detect; **rewrite**: delete the option. - `sentinel-node-command-options`: compiler; assisted. - Notes: `client-defaults` (finding + decision at each inline-options client that leaves RESP, command timeout, keep-alive delay or maintNotifications to the 6 default; the instructions pin the 5 defaults per client, as the ioredis pack does), `node-20`, `plain-object-replies`. ## Ground-truth repositories Drafted with `pnpm packs:truth` (each repository's own compiler and merged upgrade), reviewed by hand, and **committed in ffb4f90 before the first score (71a2172 before the rebases on main)**. | Repository | Hop | Pinned | Upgrade | Sites | |---|---|---|---|---| | deanmoses/tacocat-gallery-sam (`app`) | 4.6.12 → 5.10.0 | `34e1176` | PR #83 | 4 | | swoofer/mcp-coordinator | 5.12.1 → 6.2.0 | `8b337f0` | PR #251 | 2 | | deanmoses/tacocat-gallery-sam (`app`) | 5.12.1 → 6.2.1 | `30724e2` | PR #133 | 2 | | UNN-Devotek/FCM-Fallout-Chat-Mod (`backend`) | 4.7.1 → 6.0.0 | `2801904` | PR #125 | 9 | | tearingItUp786/blog | 4.7.0 → 6.0.0 | `bb88f3a` | #223 + `092ea34` | 5 | **One change after the first score:** FalkorDB/falkordb-ts (4.7.1 → 5.10.0, 20 sites, the richest 4 → 5 upgrade found) was in the committed truth, but the first run could not score it at all. Its `package.json` declares `workspaces: ["./packages/*"]` matching no directory, and `check` refuses the repository. It is removed (437e129), and none of its sites was ever scored. Also screened out: stamp (needs Node ≥ 24.15), semaphore-chat (lockfile outside the project directory), about 30 bump-only upgrades. Details in RESEARCH.md. ## `uptide pack test` output ``` uptide pack test redis · verified, recorded candidate fixtures 12 cases, 27 sites · pass ground truth deanmoses/tacocat-gallery-sam@34e11760f6e7 redis 4.6.12 → 5.10.0 predicted 4, expected 4, false positives 0, false negatives 0 swoofer/mcp-coordinator@8b337f0a081d redis 5.12.1 → 6.2.0 predicted 3, expected 2, false positives 1, false negatives 0 deanmoses/tacocat-gallery-sam@30724e21efd6 redis 5.12.1 → 6.2.1 predicted 2, expected 2, false positives 0, false negatives 0 UNN-Devotek/FCM-Fallout-Chat-Mod@280190489fac redis 4.7.1 → 6.0.0 predicted 5, expected 9, false positives 0, false negatives 4 tearingItUp786/blog@bb88f3afc40d redis 4.7.0 → 6.0.0 predicted 1, expected 5, false positives 1, false negatives 5 rule precision recall tp fp fn client-defaults 0% 0% 0 4 2 client-type 100% 33% 2 0 4 generic 50% 100% 2 2 0 renamed-exports 100% 100% 2 0 0 scan-cursor 100% 30% 3 0 7 overall (sites) 87% 59% 13 2 9 behavior notes (sites) 50% 50% 2 2 2 breaking (sites) 100% 61% 11 0 7 false positives (2) swoofer/mcp-coordinator@8b337f0a081d src/infra/redis.ts:50 client-defaults (deprecated) tearingItUp786/blog@bb88f3afc40d app/utils/redis.server.ts:15 client-defaults (deprecated) ✗ verification.json does not match this run: `uptide pack test redis --write` records it FAIL redis ``` **Why the record says candidate.** Today's gate (≥ 2 repos, 0 breaking false positives) computes `verified`, and the stricter UPT-48 gate is not on main yet. The evidence is thin exactly where it matters most. The core 4 → 5 API changes (`command-options`, `legacy-mode`, `scan-iterators`, `number-replies`, `keep-alive`) have **no real site** in the ground truth. The 4 → 5 rule set rests on one 4 → 5 repository (4 sites) plus the scan-cursor sites of the two 4 → 6 repositories. Three rewrites (`scan-iterators`, `keep-alive`, `unstable-resp3`) never met a real site, and under UPT-48 they would run as assisted. So I have not run `--write`, and CI's Pack test will flag the record as stale until this is decided: rebase on UPT-48 and re-score, or find a scorable 4 → 5 upgrade with those changes. **The 7 breaking false negatives are all outside the rules' reach:** - blog (5): its files reach the client through a local module and a local `RedisScanClient` type, so `check` reports no compile error there. - FCM and tacocat 5 → 6 (4 client-type): the repositories' own `tsc` reports TS2322, `check` doesn't. Those lines already had a different TS2322 at the installed version, so they were most likely subtracted as pre-existing. Not confirmed in the engine. **Behavior:** the two `client-defaults` false positives are clients whose repositories kept the RESP3 default without pinning it. ## Engine follow-ups found here (not in this PR) - `check` refuses a repository whose `workspaces` patterns match nothing (FalkorDB). - Compile errors in files that use the client without importing the package go unreported (blog). - Pre-existing errors seem to be subtracted by location, which hides a new error at the same line (FCM, tacocat). ## Checklist - [x] Every rule and note comes from a source listed in `meta.sources`. `client-type` and `set-expiration` come from the published declarations, which are listed. - [x] Every rule that rewrites or detects has a fixture case, with a site it must leave alone (`keep`) - [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`; otherwise the pack ships as a candidate. Five scored; kept as a candidate on purpose, see above. - [ ] `pnpm uptide pack test redis` passes and its output is pasted above; `verification.json` written with `--write`. Intentionally not written yet. - [x] `pnpm lint && pnpm typecheck` pass; `vitest run src/packs` in core (259 tests) passes. The full `pnpm test` was not run locally. - [x] Changeset added - [x] No private code, names or paths anywhere: fixtures are synthetic, ground truth is public 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Lucas Braum <lucasbraum@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
uptide-release
Bot
force-pushed
the
changeset-release/main
branch
10 times, most recently
from
October 15, 2026 20:58
62e0e5c to
cd33c65
Compare
uptide-release
Bot
force-pushed
the
changeset-release/main
branch
from
October 15, 2026 21:26
cd33c65 to
45a9fbb
Compare
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.
This PR was opened by the Changesets release 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. 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
b795705Thanks @lucasbraum! - Packs can name alternate leads: packages a repository declares instead of the pack's own (react-router-domforreact-router).checkandfixasked 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, andfixdeclares it beside the alternate at the target before upgrading both.#149
3303118Thanks @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.paramsvalues widened tostring | string[],req.param,res.sendfile,express.static.mimeandexpress.queryare claimed from the compiler;res.redirect(url, status),app.param(fn),res.redirect('back')andres.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,checkno longer adds its text-matched express 5 runtime findings.#148
5143c46Thanks @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 inemail(),domain()anduri()are reported as decisions. Scored against two public repositories, one of them a negative control.#131
413d3daThanks @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; deepdist/imports, untyped icon lookups, theabsoluteStrokeWidthandIconNodedeprecations, and thearia-hiddendefault 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
107c107Thanks @lucasbraum! - Add a verified React Router 6 → 7 migration pack, which also runs for applications that declare onlyreact-router-dom. It movesreact-router-domimports toreact-router. It reports, with a guide for the agent, the rest of the upgrade: the removedfutureprop and router option,react-router-dom/server, lowercaseformMethodcomparisons andjson/defercalls (found by syntax where the compiler is blind), the other removed exports, andgeneratePathcalls that passnullfor 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
9c57934Thanks @lucasbraum! -checkandfixplan 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 incheck, the PR body andpack test. The npm install scope guard no longer lets a declared root dependency change because a moved package peers on it.#128
06f11abThanks @lucasbraum! - Add verified Sentry JavaScript SDK 10 to 11 migration packs for@sentry/nextjsand@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 movewithSentryConfigto@sentry/nextjs/configand the removed build options underwebpack, removeenableLogs/enableMetrics: true, and rename exports, integration names andhttpIntegrationoptions; privacy, sampling, profiling and OpenTelemetry choices stay assisted and are reported for the owner. Fixuptide pack newdropping registry entries long enough to be wrapped.#155
922c5abThanks @lucasbraum! -uptide pack testapplies 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, andcheckandfixrun their sites as assisted.@sentry/browserand@sentry/reactbecome 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
0b88170Thanks @lucasbraum! -checkandfixremove a deprecated@typesstub 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,typingsor atypesexport), the plan removes it: "Removed: @types/ioredis, ioredis 5 ships its own types", incheckand in the PR body.checknow diffs such a package against its own target types instead of failing on a stub with no declarations, andfixremoves the entry in the same install, with the install scope guard allowing exactly that removal.#150
4c3212bThanks @lucasbraum! -check typescriptjudges 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
21894d7Thanks @lucasbraum! - Addverify.buildto uptide.yml: the package.json script a migration pack's required build runs. Unset, Uptide runsbuild:ci, thenbuild-ci, thenbuild. When a build fails before the framework's build starts because it needs an environment variable or a database (prisma db pushwithoutDATABASE_URL), the pull request and the report say so ("build needs DATABASE_URL; set verify.build") instead of a bare "build failed".uptide initwrites the setting as a comment.Patch Changes
#141
9debb6dThanks @lucasbraum! -checkno 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
e796e4cThanks @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 aconnectlistener 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
9ba005aThanks @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
f3af6a1Thanks @lucasbraum! -checkno longer reports a blocker for a direct dependency whose plain dependency range rejects the target (@azure/ai-projectsonopenai ^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 testcounts awithmove thatcheckleaves 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
5ced450Thanks @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,fileFromPathand the other removals get agent guides;APIError.headers, WebResponsebodies and double-encoded path ids are detected even in code typedany; 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
578a042Thanks @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
883e587Thanks @lucasbraum! -uptide pack testscores 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 inverification.json(behavior, with the counts behind each rate), false negatives show their severity, and a rate with nothing to measure printsn/ainstead of 100%, inpack testand in the README's packs table.#154
7aa3d58Thanks @lucasbraum! -uptide pack testfails a pack'sverification.jsononly when it claims more than the run measures (recorded verified, measured candidate). A pack recorded as candidate on purpose passes, withrecorded candidate by choice; measures verified, and a record whose numbers differ gets a note instead of a failure. Agenericground-truth site now counts as a true positive for the rule that claims it.#152
ca31aeeThanks @lucasbraum! -uptide pack testtakes several packs:uptide pack test zod ai.#159
1e157fdThanks @lucasbraum! -uptide pack test --jsontimes each ground-truth repository:timinggives the fetch, install and check wall time and the process's peak memory.#144
0881185Thanks @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:truthdrafts these anchors from insert-only hunks for review, and docs/packs.md and CONTRIBUTING describe the rule.#156
9039e3cThanks @lucasbraum! - Public ground truth now exercises three rules that ran as assisted: zoderror-params, stripeapi-version-unpinnedand nextstable-cache-exports. Their rewrites run mechanically again. All three packs stay verified.#138
0db5640Thanks @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
9ddaaffThanks @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 coverslegacyMode,commandOptionsand isolation, scan iterators that yield batches, string SCAN cursors, boolean replies that became numbers, enums that became constants,keepAlive,@redis/graphand internaldistimports; 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
42a3b54Thanks @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-plugin5.x,@sentry/cli, the old@sentry/utils8.x) are reported as "stays at X" incheck, the pull request andpack testinstead of makingfixrefuse the whole upgrade. Registers the verified@sentry/react10 to 11 pack.#146
09b8682Thanks @lucasbraum! - Report Stripe clients pinned to different API versions at the top ofcheckand of the PR body, as a decision for the owner: everynew 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
bc428d1Thanks @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
3c8902dThanks @lucasbraum! - The zod pack migratesZodError#errorsto.issuesby rule: reads (error.errors[0].message,error?.errors) and destructuring (const { errors } = errorbecomesconst { issues: errors } = error) where the type checker resolves the receiver toZodError, and reads off an untyped catch binding of a zod parse. Writes are left to the agent.#127
c4ead4fThanks @lucasbraum! - zod:checknow reportsZodError#formErrorsand#errorsread off an untyped catch binding of a zodparse/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.fixrewrites<err>.formErrors.fieldErrorsand<err>.formErrors.formErrorstoz.flattenError(<err>), importingzwhere it is missing; every other read goes to the agent.