Repository navigation
fix(react): report RefObject<T> declarations that need | null (UPT-87) - #201
Merged
lucasbraum merged 1 commit intoOct 17, 2026
Conversation
Fixes UPT-87. The committed verification.json (69%, 89 TP / 40 FN) was a stale tally. A fresh `pack test react` on main measures 107 TP / 22 FN (83%), and so does CI's `pack react` job on 4c3dc23: the environment is not the cause. #198 left the react tally as it was, and the scorer changed in #143/#155. Reach, then rules, for the 22 breaking misses: | cause | misses | |---|---| | reached, reported at the use site; truth marks the `RefObject<T>` declaration (`ref-object-nullable`) | 16 | | global-jsx-namespace, no finding in the file | 4 | | `generic` in dev-docs (tsconfig "not used in compilation") | 2 | Most of the 16 declarations use `React.RefObject` without importing react, so `detect` never read them. `ref-object-nullable` gets a syntax-tree detector (element type or a type parameter bounded by one, no `null`, not a cast) and the pack reads every project file that contains `RefObject<`. Casts are a near-miss: one would be a breaking false positive at TldrawUiContextualToolbar.tsx:56, so :55 stays missed. Breaking recall 83% -> 94.6% (122/129), 0 breaking FP, unverified FP unchanged (8), still verified. ref-object-nullable 38 -> 53 TP. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
lucasbraum
deleted the
lucasbraum/upt-87-react-pack-raise-breaking-recall-from-69-using-ground-truth
branch
October 17, 2026 18:40
Merged
lucasbraum
pushed a commit
that referenced
this pull request
Oct 17, 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.15.0 ### Minor Changes - [#203](#203) [`19dd649`](19dd649) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `fix` estimates what the agent's sites will cost and prints the estimate before its first call. When the estimate is above the limit (`--max-cost`, default $1), it stops before spending anything and exits 2: `estimated $3.00 for 50 sites; limit $1.00. Run again with --max-cost 3.00`. Action and App runs never stop on the estimate: they work within `maxCost`, and the summary and the pull request name the sites left and the `maxCost` in `uptide.yml` that finishes them. When the agent stops at the limit, the summary and the pull request name the estimate and the limit that continues. The summary (`Phases`), the pull request's run details and `report.json` (`phases`) give wall time and agent spend per phase. - [#207](#207) [`4a90e9f`](4a90e9f) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `fix` names the first major of a peer blocker that accepts the target (`no 1.x accepts react 19; 2.2.0 does`) and suggests adding it to the command instead of `--allow-peer`. When a verified pack covers that major, the blocker moves as a companion and its pack runs in the same fix: its rules, its detect-based findings and its inventory, each at the companion's own versions. When no pack covers it, the PR says the major needs its own migration and the risk is High. `--allow-peer` stays the suggestion only when no release accepts the target. A group member that the target's own range already accepts (`@casl/ability` under `@casl/react` 5) is no longer reported as a conflict. ### Patch Changes - [#208](#208) [`5fdb60e`](5fdb60e) Thanks [@lucasbraum](https://github.com/lucasbraum)! - The express pack reports the `const { id } = req.params` line as a site when the compiler reports a `string | string[]` error on one of its names, because that line is where the guide types the values once. - [#211](#211) [`213e1a5`](213e1a5) Thanks [@lucasbraum](https://github.com/lucasbraum)! - fix runs prisma generate / graphql-codegen in its verification clone before the baseline and after the upgrade; check names the missing generator step - [#204](#204) [`65b7edb`](65b7edb) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `check` and `fix` now measure how many of the files that use the upgraded package the repository actually type-checks (TypeScript, plus JavaScript under `checkJs` or `// @ts-check`). When that is under half, the summaries say `types cover N of M affected files` and no longer present a clean compile as proof: `fix` passes only when a test run or a build passed, and says the verdict rests on them. - [#205](#205) [`c0332eb`](c0332eb) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `fix` no longer fails on npm repositories whose lockfile carries optional platform packages such as sharp's `@emnapi/*`. npm 11.6 drops them on the first lock-only install and writes a lockfile its own `npm ci` rejects; `fix` now runs the lock-only install twice, at the upgrade and at the parent commit alike, so the lockfile it validates is one `npm ci` accepts. - [#201](#201) [`4550da5`](4550da5) Thanks [@lucasbraum](https://github.com/lucasbraum)! - The react pack now reports `RefObject<HTMLElement>` annotations that need `| null` at the declaration, including in files that type a prop with the global `React.RefObject` and never import react. The compiler reports these where a ref is passed in, so the declaration to edit was missed. - [#216](#216) [`95a3bf5`](95a3bf5) Thanks [@lucasbraum](https://github.com/lucasbraum)! - setup PRs pin Action v0.14.3 - [#209](#209) [`48df030`](48df030) Thanks [@lucasbraum](https://github.com/lucasbraum)! - The zod pack reports `.refine(check, { message })`, the type names zod 4 marks deprecated (`ZodTypeAny`, `ZodIssue`, `ZodSchema`, `ZodErrorMap` and the others, including `import('zod').ZodTypeAny`) and `errorMap` options with `ctx.defaultError`, as deprecated findings. - [#200](#200) [`4e4cc3e`](4e4cc3e) Thanks [@lucasbraum](https://github.com/lucasbraum)! - The zod pack reports names zod 3 exported from its root and zod 4 exports only from `zod/v3` (`SafeParseReturnType`, `ZodTypeDef`, `ZodEffects`, the `Zod*Def` interfaces), which the surface diff counted as unchanged, and `fix` now rewrites `z.string().uuid()` to `z.guid()` instead of `z.uuid()`, so ids that zod 3 accepted still validate. 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.
Fixes UPT-87.
Reconciliation first
The committed
verification.jsonsaid 69% (89 TP / 40 FN). A fresh localpack test react --offlinemeasures 107 TP / 22 FN (83%), and CI'spack reactjob on 4c3dc23 measures the same numbers (artifactpack-test-reactof run 39009240360). So it is a stale record, not the environment: #198 left the react tally as it was (its message says so), and the scorer changed in #143 and #155 after the record was written.Reach, then rules (22 breaking misses)
RefObject<T>declaration (ref-object-nullable)global-jsx-namespacewith no finding in the filegenericin dev-docsMost of the 16 declarations use the global
React.RefObjectwith no react import, sodetectnever read them.Change
ref-object-nullablegets a syntax-treedetect:RefObject<T>/React.RefObject<T>whereTis an element type or a type parameter bounded by one, has nonull, and is not a cast.RefObject<(same pattern as the ai pack, UPT-33).| null,RefObject<number>, a cast. A cast is excluded because it would be a breaking false positive atTldrawUiContextualToolbar.tsx:56(truth lists :55, not :56); :55 stays missed.verification.json, README anddocs/compatibility.mdregenerated.Before / after (same pinned ground truth, not touched)
ref-object-nullablePack stays verified; thresholds untouched.
Remaining 7 misses
dev-docs/src/pages/index.tsx:1,dev-docs/src/components/Homepage/index.tsx:1(excalidraw): its tsconfig says it is not used in compilation.examples/excalidraw/with-nextjs/src/app/page.tsx:19: a separate example package, outside the analyzed program.apps/dotcom/client/tsconfig.json:15(tldraw): a config file; truth marks the cause, no diagnostic lands there.ButtonIconCycle.tsx:23,apps/examples/src/ExampleWrapper.tsx:111: global-JSX sites with no finding in the file; not investigated beyond that.TldrawUiContextualToolbar.tsx:55: the cast, see above.Suspect truth entries to review (not changed): :55 vs :56 above are the same cast, one listed and one not.
Core tests pass (
pnpm testinpackages/core).🤖 Generated with Claude Code