Repository navigation
feat(packs): react 18 → 19 verified pack - #53
Merged
Merged
Conversation
6 of 8 tasks
…ding) Scaffold only: the example rule is still in place and ground-truth.json is empty. The tldraw ground truth could not be drafted because Yarn 4 cannot be downloaded by corepack behind the sandbox proxy. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PiuU3en7qKGdWHBGbAZirY Signed-off-by: Claude <noreply@anthropic.com>
…ldraw Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PiuU3en7qKGdWHBGbAZirY Signed-off-by: Claude <noreply@anthropic.com>
… are one site, at the tsconfig that causes them
apps/dotcom/client reads JSX element types through the global JSX namespace
("jsx": "preserve", no "jsxImportSource"); after the upgrade its lockfile
keeps a second copy of @types/react (18.3.23, for a dependency pinned to
16 || 17 || 18) beside @types/react 19, and every element in every file
errors with one fix, the "jsxImportSource": "react" the upgrade added.
Those 1745 expected sites are one, at that line of the tsconfig, with the
cause in its note. verification.json records the run of uptide-dev/uptide
with the check engine fixes (breaking precision 88%, recall 64%).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018u9b3Pp5H68DdYZGp5Qg1U
Signed-off-by: Lucas Braum <lucas.braum@deskfy.io>
…reaking precision 96%, 4 false positives) The four breaking false positives left are engine or ground-truth-environment issues, not rules: dev-docs/Homepage (a workspace whose tsconfig extends a package that is not installed), two useTransform(ref) call sites in other workspaces than the declaration that is the one cause, and next-mdx-remote-client/rsc after a companion move the real upgrade never made. The pack stays a candidate. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AKRx4kuV6oi25MP5WguxW9 Signed-off-by: Claude <noreply@anthropic.com>
…g precision 100%) pack test react --write: excalidraw and tldraw, 0 false positives among breaking findings. queue.json: react verified. The two cli fix tests that used react as their example of a package without a pack now use vitest. Known: packages/core/src/fix/isolate.test.ts:600 still uses react as the no-pack package and fails. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CECy7agtzRrX4SJWSAUHfS Signed-off-by: Claude <noreply@anthropic.com>
…-pack example - The changeset is minor: a new verified pack. It opens with the two public repositories the pack is scored against and keeps the numbers (2 repositories, 100% precision on breaking findings, 89 sites, recall 69%). - README "Verified packs" regenerated with pnpm docs:packs: react joins ai, stripe and zod. - packages/core/src/fix/isolate.test.ts used react as its example of a package without a pack; it is vitest now, as the two cli tests already are. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018u9b3Pp5H68DdYZGp5Qg1U Signed-off-by: Lucas Braum <lucas.braum@deskfy.io>
lucasbraum
force-pushed
the
feat/react-pack
branch
from
October 9, 2026 19:13
81799f7 to
7d2eeb2
Compare
…n Berry install tldraw pins yarn@4.7.0 through packageManager; the runner's global Yarn is 1.22.22, so the install failed and the react pack scored one repository instead of two. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018u9b3Pp5H68DdYZGp5Qg1U Signed-off-by: Lucas Braum <lucas.braum@deskfy.io>
lucasbraum
marked this pull request as ready for review
October 9, 2026 19:41
Merged
lucasbraum
pushed a commit
that referenced
this pull request
Oct 9, 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.6.0 ### Minor Changes - [#46](#46) [`458aa14`](458aa14) Thanks [@lucasbraum](https://github.com/lucasbraum)! - What changed in `check`: - **Breaking means confirmed.** A type-surface change is reported as breaking only when the compiler rejects your code against the target at that site, the runtime probe saw the export go, or a migration pack found it, in the verified tier as in the generic one. What nothing confirmed is listed as "possible impact", apart from the breaking count. - **Coverage, under every package.** `compiled 355 of 356 files in 5 workspaces; skipped: <reason (count)>` says how much of the code that uses the package the compiler judged, and the verdict says "types partly verified" when not every file was. - **TypeScript-version-aware defaults.** A repository on TypeScript 5 is read with TypeScript 5's defaults for what its tsconfig leaves unset (`strict` off, automatic `@types`), not the bundled TypeScript 6's. Monorepos compile far more of their files: workspace packages declared `workspace:*` under Yarn and npm resolve to their source, scoped programs keep the tsconfig's ambient declaration files, and a workspace that only peer-depends on a package finds the hoisted copy. - **Companions.** `check` and `fix` move `@types/<pkg>` with `<pkg>`, and a package released in lockstep with it (react-dom with react): `check react` 18 → 19 moves react-dom, @types/react and @types/react-dom together. - **One finding for a root cause that is one edit.** A compiler option (the global `JSX` namespace `@types/react` 19 removes, read by `"jsx": "preserve"`) and a repository parameter several call sites trip are each one finding at the place to edit, with the compiler errors as evidence. - [#51](#51) [`a18bebd`](a18bebd) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `check` reports one breaking finding when call sites in different workspaces trace back to the same local declaration (a parameter or prop whose type names something from the upgraded package): the finding is anchored at the declaration, where the one edit is, and the call sites are listed under it as evidence with an `N call sites in M workspaces` line, in the terminal, the HTML report and the JSON report. Before, a site alone in its workspace stayed a finding of its own, so a hook typed `RefObject<HTMLElement>` and called from three workspaces was three findings plus the one that mattered. The JSON report gains `root` on call-site findings and `workspace` on evidence sites; nothing is renamed or removed. The pack test scorer counts the anchor as the predicted site and never the evidence under it. - [#49](#49) [`2e5c510`](2e5c510) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Two fixes to `check`: - **A project whose tsconfig cannot be resolved is not compiled.** A tsconfig that `extends` a package or file that is not installed describes options the repository never builds with, so what a compiler reports there is not a place to change. `check` skips it, never turns its diagnostics into findings, and says so in the coverage line (a file governed by such a nested project is dropped from the workspace that happens to reach it): `compiled 5 of 7 files in 2 workspaces; not compiled: docs/tsconfig.json (extends "@tsconfig/docusaurus/tsconfig.json" cannot be resolved)`. - **A package that only peers on the package is left in place when its peer range rejects the target.** What always moves with the package stays as before: the release group published at the target's own version (`react-dom`), `@types/*`, the exact pins of the target, and the packages a pack names in `companions` (each with the official page that says so), even when their installed range already accepts the target. Any other package whose installed peer range rejects the target is not moved and not compiled at another version; `check` lists it under possible impact, never as breaking: `? possible impact, peer conflict: next-mdx-remote-client 1.1.2 declares react >= 18.3.0 < 19.0.0`. - [#53](#53) [`d33bdd0`](d33bdd0) Thanks [@lucasbraum](https://github.com/lucasbraum)! - A verified migration pack for React 18 → 19, scored against excalidraw/excalidraw and tldraw/tldraw at the commit before their own upgrade. Rewrites by rule: `useRef<T>()` → `useRef<T>(undefined)`, `ref={(el) => (x = el)}` → a block body, and `React.MutableRefObject` → `React.RefObject`. `RefObject<T | null>`, the removed global `JSX` namespace, untyped `element.props`, the removed react-dom APIs and `PropsWithRef` go to the agent with the guide; what the compiler cannot see (errors no longer re-thrown, removed legacy APIs, `act` moved to "react", Strict Mode and Suspense changes) is listed for review. 2 public repositories, 100% precision on breaking findings (89 sites, none false), recall 69% on breaking sites; the sites it misses are listed in the pack test output. ### Patch Changes - [#48](#48) [`94611a9`](94611a9) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `check` compiles your code with the repository's own TypeScript (the `node_modules/typescript` its build uses, found the way `fix` has found it since verification), and falls back to the bundled compiler only when the repository installs none. Errors and their positions are the ones your `tsc` would print: a repository on TypeScript 4.9 is no longer judged by TypeScript 6's rules. The coverage line says which compiler judged: `compiled 355 of 356 files in 5 workspaces with the repo's TypeScript 4.9.5`. A workspace without a tsconfig of its own is now configured by the nearest one above it (a monorepo root whose `include` covers the workspace), for its options only; synthetic defaults apply only when no tsconfig exists anywhere. Co-authored-by: uptide-release[bot] <338861287+uptide-release[bot]@users.noreply.github.com>
7 tasks done
lucasbraum
added a commit
that referenced
this pull request
Oct 10, 2026
…ATH is classic (#54) ## What and why On a repository whose `package.json` pins `"packageManager": "yarn@4.x"` (any Yarn 2 or later) while the global `yarn` is classic 1.x, or there is none, `uptide fix` and `uptide verify` failed to install: Yarn stopped with "This project's package.json defines packageManager yarn@4.7.0. However the current global version of Yarn is 1.22.22". The same failure the pack-test job had on tldraw's ground truth (#53), fixed there with corepack in CI. The installer used by `fix` and `verify` (`packages/core/src/fix/managers/availability.ts`) now: - **Detects** a pinned Yarn 2+ that the `yarn` on PATH cannot run: classic 1.x, another version than the pin, or no yarn at all (`needsCorepack`). A Berry on PATH at the pinned version is used as before. - **Runs it through corepack** when corepack is available: the same install as `corepack yarn install --immutable --mode=skip-build`, with `COREPACK_ENABLE_DOWNLOAD_PROMPT=0` and `COREPACK_ENABLE_AUTO_PIN=0` (`throughCorepack`). Nothing on the user's machine changes outside corepack's own cache: no `corepack enable`, no shim, no rewrite of `packageManager`. Every later install in the run (`updateArgs`, the plain `install`) goes through the same route (`PackageManager.via`). - **Exits 2 before cloning** when corepack is missing (`PACKAGE_MANAGER_UNAVAILABLE`, raised by `run.ts` before any clone), with the exact command: ``` This repository pins yarn@4.7.0 (packageManager), but the yarn on PATH is 1.22.22 and corepack is not available to run the pinned version. Nothing was cloned or installed. Next: corepack enable ``` - **Respects `COREPACK_NPM_REGISTRY`** from the environment (it passes through to the corepack call as it is) and names it only when corepack could not fetch the pinned version, together with the manual activation command. `docs/troubleshooting.md` gains "Yarn 4 says the global Yarn is 1.x", and the "Package manager not available" section points to it. `packs/tooling/truth.ts` (ground-truth installs for `pack test`) is unchanged; CI handles that with `corepack enable` since #53. ## How it was verified Unit tests in `availability.test.ts`, against a new fixture `fixtures/repos/yarn-berry-pinned` (synthetic: pins `yarn@4.7.0`, Berry lockfile, node-modules linker, nothing installed): - detection: pinned Berry with no yarn, with classic 1.22.22, with Berry 4.6.0 → corepack; with Berry 4.7.0 → not; classic and pnpm repositories → never; - command selection: `corepack yarn install --immutable --mode=skip-build`, prompt off, `via: 'corepack'`, `updateArgs` prefixed with `yarn`; - the probe sequence (`yarn --version`, `corepack --version`, `corepack yarn --version`) and that `corepack enable` is never run; - corepack missing → exit-2 error with `Next: corepack enable`; corepack unable to fetch → message with `COREPACK_NPM_REGISTRY` and the activation command (and the registry hint absent when corepack is simply missing); a corepack yarn at the wrong version → `PACKAGE_MANAGER_VERSION`. `pnpm lint`, `pnpm typecheck`, `pnpm docs:links`, the private-material guard, core (928 tests) and CLI (350 tests) pass. One unrelated test (`check/progress.test.ts`, worker-event forwarding) failed once while the suite shared the machine with the build, and passed three times in isolation. ## Checklist - [x] Tests for the change (a bug fix starts with a failing test) - [x] `pnpm lint && pnpm typecheck && pnpm test` pass - [x] README and docs updated if behavior changed - [x] Changeset added if a user would notice (`pnpm changeset`): patch - [x] No new dependency, or it was agreed in an issue - [x] No private code, names or paths in fixtures, tests or docs - [x] The rules in CONTRIBUTING.md ("Rules that do not bend") still hold 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018u9b3Pp5H68DdYZGp5Qg1U --- _Generated by [Claude Code](https://claude.ai/code/session_018u9b3Pp5H68DdYZGp5Qg1U)_ Signed-off-by: Lucas Braum <lucas.braum@deskfy.io> Co-authored-by: Lucas Braum <lucas.braum@deskfy.io> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.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.
Pack
react,>=18 <19→>=19 <20. Companions move with it:react-dom,@types/reactand@types/react-dom, as the engine's companion rules decide (not listed by the pack). Supersedes #45: same pack and ground truth, brought onto current main (merge ofmainat 0de525c, no conflict), with the changeset, the README table and a test fixed up.Sources
Rules and behavior notes
Rewrites by rule:
use-ref-argument:useRef<T>()→useRef<T>(undefined).ref-callback-return:ref={(el) => (x = el)}→ a block body (sites fromdetect).mutable-ref-object:React.MutableRefObject→React.RefObject.To the agent, with a guide:
ref-object-nullable: a parameter or prop typedRefObject<T>onceuseRefreturnsRefObject<T | null>.global-jsx-namespace: the removed globalJSXnamespace (one finding at the tsconfig'sjsxline, the elements as evidence).element-props-unknown: untypedelement.props.removed-react-dom:ReactDOM.render,hydrate,unmountComponentAtNode,findDOMNode.props-with-ref.Behavior notes, listed for review (the compiler cannot see them):
errors-not-rethrown,removed-legacy-apis,act-moved-to-react,element-ref-deprecated,strict-mode-memoization,suspense-fallback-first,umd-removed,javascript-urls-blocked,use-reducer-types.Ground-truth repositories
Expected findings were drafted with
pnpm packs:truthfrom each repository's own TypeScript and its upgrade commit, and read by hand; tldraw's 1,745 JSX element diagnostics are one site at the tsconfig that causes them (#45). Rules, expectations, ground truth and companions are unchanged from #45's last state.uptide pack testoutputverification.jsonon this branch records the run on main after #51 (commit 8573cc0);pack test reactwas not rerun here, since the merge touched neitherpackages/core/src/checknor the pack. That run:Breaking findings: precision 100% (89 sites, none false), recall 69%, on 2 public repositories. The 7 non-breaking false positives are deprecated or unverified findings and do not count toward the verdict.
What is not covered
ref-object-nullable(34) andglobal-jsx-namespace(19 in the per-rule table, counted at the tsconfig anchor) sites the compiler does not reach, plus 4element-props-unknownand 1use-ref-argument. They are listed bypack test reactas false negatives.@types/reactcopy, as a real install can keep, is not modeled.This PR on top of #45
.changeset/react-pack.mdisminor(a new verified pack) and opens with the two repositories; numbers unchanged.pnpm docs:packs;pnpm docs:packs --checkandpnpm docs:linkspass.packages/core/src/fix/isolate.test.tsused react as its example of a package without a pack; it is vitest now, like the twocli.test.tscases. No other test did.pnpm lint,pnpm typecheck, core (919 tests) and CLI (350 tests) pass.Checklist
meta.sourceskeep)verified; otherwise the pack ships as a candidatepnpm uptide pack test <package>passes and its output is pasted above;verification.jsonwritten with--writepnpm lint && pnpm typecheck && pnpm testpasspnpm changeset): a new pack is something users noticeSupersedes #45.
🤖 Generated with Claude Code
https://claude.ai/code/session_018u9b3Pp5H68DdYZGp5Qg1U
Generated by Claude Code