Skip to content

feat(packs): candidate express 4 to 5 pack (UPT-55) - #149

Merged
lucasbraum merged 3 commits into
mainfrom
lucasbraum/upt-55-pack-express-4-5
Oct 15, 2026
Merged

lucasbraum merged 3 commits into
mainfrom
lucasbraum/upt-55-pack-express-4-5

Conversation

@lucasbraum

@lucasbraum lucasbraum commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Pack

express >=4 <5 → >=5 <6 (default target 5.3.0). Linear: UPT-55. Candidate by choice. The branch is rebased on UPT-48 (#155) and UPT-63 (#157). pack test passes and measures verified. The record stays candidate until a person has reviewed it.

Sources

All pinned and read 2026-10-14 (ledger in packages/core/src/packs/express/RESEARCH.md):

  • Migration guide, expressjs.com 4721193
  • History.md at v5.3.0 (cf6722b)
  • path-to-regexp Readme at v8.4.2
  • body-parser HISTORY at 2.3.0
  • @types/express 4.17.25 / 5.0.6 and @types/express-serve-static-core 4.19.9 / 5.1.3 declarations
  • expressjs/codemod at 2f7f1e5. These were read, never run. @expressjs/codemod 0.0.5 is deprecated on npm.

Rules and behavior notes

No rule rewrites. Every codemod rewrite is either unsafe whatever the surrounding code, or never met a real site:

  • del and send don't check the receiver.
  • param drops values that came from the body or query.
  • dotfiles: 'allow' serves dotfiles Express 4 hid.
  • Route paths are the owner's choice, and handlers' req.params[0] changes with them.

Rules:

  • route-syntax (detect): path-to-regexp 8 route strings on an app or router the file binds. Covers express(), Router(), typed parameters and properties, this.app, a const list, and for…of over one. One site per registration.
  • params-string-array (compiler message): req.params values are string | string[]. The guide types the params and never casts.
  • req-param-removed, sendfile-renamed, static-mime-removed, query-middleware-removed: compiler messages.
  • redirect-order, param-callback-removed: detected, because their compiler messages are too generic to claim.

Behavior notes:

  • Findings: redirect-back and send-status-number, detected on Express responses.
  • Decisions: async-errors, req-body-undefined (also a test follow-up), query-parser, urlencoded-extended, static-dotfiles, node-minimum (only when engines.node allows below 18), and runtime-defaults.

Engine change: with the pack in use, check skips its curated, text-matched express 5 runtime findings for the package, because the pack owns those sites. The generic tier is unchanged.

Ground-truth repositories

The ground truth was committed in ab94e71 before the first score and has not been changed since. 88 sites, 86 of them breaking.

Screened out: snapshot-labs/stamp#711 (engines node >=24.15, while CI runs Node 22) and others. The list is in RESEARCH.md.

uptide pack test output

Run once after the rebase, with per-pack caches. The ground truth is unchanged since ab94e71.

uptide pack test express · verified, recorded candidate
fixtures      2 cases, 17 sites · pass
ground truth
  netlify/cli@63b6f1cbca31                     express 4.22.1 → 5.2.1  predicted 5, expected 10, false positives 0, false negatives 5
    with @types/express 4.17.25 → 5.0.6
    · verdaccio 6.2.4 keeps its own express 4.21.2; no release accepts express 5.2.1
    · http-proxy-middleware: the upgrade moved it to 3.0.5, check leaves it (peer conflict: http-proxy-middleware 2.0.9 declares @types/express ^4.17.13)
  bcgov/tenant-management-system@6fd01755043a  express 4.22.2 → 5.2.1  predicted 53, expected 54, false positives 0, false negatives 1
    with @types/express 4.17.21 → 5.0.6
  amiantos/lurker@f7a0f3dac775                 express 4.22.2 → 5.2.1  predicted 10, expected 18, false positives 0, false negatives 8
    with @types/express 4.17.25 → 5.0.6
  openhab/openhab-cloud@5a1866efe1c0           express 4.22.1 → 5.2.1  predicted 5, expected 6, false positives 0, false negatives 1
    with @types/express 4.17.25 → 5.0.6
    · body-parser: the upgrade moved it to 2.2.2, the repository's choice (it does not depend on the package)
    · serve-static: the upgrade moved it to 2.2.1, the repository's choice (it does not depend on the package)

  rule                         precision  recall    tp   fp   fn
  generic                           100%     50%     1    0    1
  params-string-array               100%     87%    62    0    9
  req-body-undefined                 n/a      0%     0    0    2
  route-syntax                      100%     77%    10    0    3
  overall (sites)                   100%     83%    73    0   15
  behavior notes (sites)             n/a      0%     0    0    2
  breaking (sites)                  100%     85%    73    0   13

gate          86 breaking sites, 2 behavior-note sites in public ground truth · verified
recorded candidate by choice; measures verified
PASS express: verified

The moves are now explained rather than failing:

  • verdaccio keeps a nested copy of express 4.
  • http-proxy-middleware is a peer conflict, which check names and leaves in place.
  • body-parser and serve-static are the repository's own choice.

Checklist

  • Every rule and note comes from a source listed in meta.sources
  • Every rule that rewrites or detects has a fixture case, with a site it must leave alone (keep)
  • 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
  • At least two public repositories for verified; otherwise the pack ships as a candidate
  • pnpm uptide pack test express passes and measures verified. verification.json was written with --write, then set to candidate by choice.
  • pnpm lint and pnpm typecheck pass. pnpm test passes.
  • Changeset added
  • No private code, names or paths anywhere: fixtures are synthetic, ground truth is public

🤖 Generated with Claude Code

@lucasbraum
lucasbraum marked this pull request as ready for review October 15, 2026 16:06
lucasbraum added a commit that referenced this pull request Oct 15, 2026
…a blocker; pack test explains blocked and unrelated moves (UPT-63) (#157)

Fixes UPT-63.

**Engine (check).** A direct dependency whose plain dependency range
rejects the lead's target no longer blocks: npm nests a copy, as the
real upgrades did. It is now a stay that names the new major accepting
the target, if there is one:
- prompty: `@azure/ai-projects 2.4.0 keeps its own openai ^6.16.0; no
release accepts openai 7.10.0`
- netlify: `verdaccio 6.2.4 keeps its own express 4.21.2; …` (an exact
pin, same path)
- ioredis/dokunc: `@hocuspocus/extension-redis 4.7.0 keeps its own
ioredis ~5.6.1; …` (the same false blocker, found by the re-score)

Within-line moves of a dependent are unchanged. Peers still block.
Nothing moves across a major.

**pack test.** A `with` move that check leaves behind is now:
- *explained* when check says why it stays: a blocker, a stay that names
a new major (guardrails' `@openai/agents` and `zod`), or a peer conflict
(netlify's `http-proxy-middleware 2.0.9 declares @types/express
^4.17.13`);
- *the repository's choice* when its installed copy doesn't depend on
the lead (openhab's `body-parser`, `serve-static`).

Neither fails the gate. Anything else still does.

Tests first: `required.test.ts` (prompty, verdaccio, guardrails) and
`pack-test.test.ts` (explained, peer conflict, repo's choice).

### Re-score (local, main 922c5ab vs this branch)

| Pack | main | this branch | Diff |
| --- | --- | --- | --- |
| express (#149 truth ab94e71) | fail: verdaccio blocker;
http-proxy-middleware, body-parser, serve-static left behind | **pass**,
P 100% R 85% (same) | moves explained / repo's choice |
| openai (#130 truth 47b4307) | fail: guardrails `@openai/agents`, zod
left behind; prompty blocker | **pass**, P 100% R 15% (same) | blocker →
stay |
| ioredis | pass, candidate | pass, candidate | dokunc blocker → stay |
| ai, joi, lucide-react, next, react, react-router, redis,
sentry-browser, sentry-nextjs, stripe, vitest, zod | pass | pass |
identical |
| sentry-react | fail | fail | identical (local npm install of
owine/youth-activity-scheduler fails, MODULE_NOT_FOUND) |

express and openai were scored in throwaway local merges of #149 and
#130 onto each side, never pushed. No ground truth was changed.

🤖 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>
@lucasbraum
lucasbraum force-pushed the lucasbraum/upt-55-pack-express-4-5 branch from 0811327 to b7bc3b1 Compare October 15, 2026 18:35
@lucasbraum

Copy link
Copy Markdown
Collaborator Author

Rebased on main (#155 UPT-48, #157 UPT-63, #156). Re-scored once with the ground truth unchanged: pack test now passes and measures verified (86 breaking sites; 73 TP, 0 FP, 85% recall; no unexercised rewrites). The record stays candidate by choice. The netlify and openhab package moves are now explained instead of failing. README and registry were regenerated with the repo tooling. I also fixed a fixture that tripped the shared-state test.

@lucasbraum

Copy link
Copy Markdown
Collaborator Author

The check.ts change stays where it is. A pack-contract hook (a pack declaring which curated generic findings it supersedes) can't replace it, for two reasons:

  • It wouldn't narrow the Pack test. Curated findings come from check/runtime-changes.ts, a pipeline separate from packs, and no existing pack hook sees or filters them. Any hook still needs check.ts to consult it, plus a new field in contract.ts/types.ts, and both paths trigger the full run.
  • It would be a contract field with one user. Express is the only entry in CURATED.

Dropping the skip would report express route sites twice: once as the pack's route-syntax, once as a curated unverified finding. So it waits for the UPT-60 matrix.

lucasbraum and others added 3 commits October 15, 2026 13:57
…PT-55)

Fixed before the first score: netlify/cli#7927, bcgov/tenant-management-system#699,
amiantos/lurker#147, openhab/openhab-cloud#550. Drafted with packs:truth and
reviewed by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
Route strings path-to-regexp 8 rejects are detected on calls of an Express
app or router this file binds, including const lists and for...of loops;
req.params widened to string | string[], req.param, res.sendfile,
express.static.mime and express.query are claimed from the compiler;
redirect(url, status), app.param(fn), redirect('back') and send(<number>)
are detected in the source; runtime defaults go to decisions. No rule
rewrites: the codemods' rewrites are unsafe or unexercised (RESEARCH.md).

Scored against four public upgrades (netlify/cli, bcgov, lurker, openhab):
breaking precision 100%, recall 85% (73 TP, 13 FN). Candidate: pack test
fails because two upgrades moved packages check leaves in place
(http-proxy-middleware, body-parser, serve-static; a verdaccio blocker).

With the pack in use, check no longer adds its text-matched express 5
runtime findings for the package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
…PT-55)

Rebased on main. pack test passes and measures verified (86 breaking
sites, 73 TP, 0 FP, 85% recall, no unexercised rewrite); the record stays
candidate by choice. The netlify and openhab moves are now explained
(nested verdaccio copy, a peer conflict, the repository's own choice).
Ground truth unchanged. README table regenerated with docs:packs.

The route-syntax fixture declares its non-Express Map instead of creating
one at module level, which the shared-state scan rejects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
@lucasbraum
lucasbraum force-pushed the lucasbraum/upt-55-pack-express-4-5 branch from b7bc3b1 to 392b38a Compare October 15, 2026 20:58
@lucasbraum

Copy link
Copy Markdown
Collaborator Author

Rebased on main (#159 Pack test matrix, #130 openai), with no conflicts. The registry (renderRegistry) and README (pnpm docs:packs) regenerate unchanged, so the only diff against main is the express lines. Typecheck passes. No re-score: the pack and its ground truth are unchanged.

@lucasbraum
lucasbraum merged commit 3303118 into main Oct 15, 2026
28 checks passed
@lucasbraum
lucasbraum deleted the lucasbraum/upt-55-pack-express-4-5 branch October 15, 2026 21:25
@uptide-release uptide-release Bot mentioned this pull request Oct 15, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant