Repository navigation
feat(check): one finding when call sites trace to a member declared in a repository augmentation - #60
Merged
Merged
Conversation
…n a repository augmentation A member the target no longer sees (TS2339, TS2551) that the repository declared itself in an augmentation, such as a custom matcher on the global jest.Matchers in a test setup file, was a breaking finding at every call site beside the declaration the pack already reported. The tracer handled parameters only (#51). - cause.ts: augmentationCause resolves the missing member in the baseline program, where it still exists, and when its declaration sits in a repository file anchors the diagnostic there: a `declare global` block at the block, a `declare module "x"` augmentation at the interface inside it, any other repository declaration at itself. One site is enough, since the edit is never at the call; the anchor is also the diagnostic's root, so sites in other workspaces fold with it after the merge. - compile.ts: the augmentation cause is tried after the compiler-option cause and before the parameter cause. - fixtures/deps/matchers-v1 and -v2: an assertion that reads the global `checks.Matchers` at v1 and its own `Matchers<R, T>` at v2; cause.test.ts folds three call sites in two files into one anchor at the setup file's `declare global` line, and leaves a member the package itself dropped at the site. - The README's line budget (readme.test.ts) no longer counts the generated packs table, which grows one row per pack. 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>
…of its interface vxsuite's `expect(x).toMatchImageSnapshot()` fails at vitest 5 because the global jest.Matchers is no longer read, but the member itself is declared by jest-image-snapshot's types, not by the setup file; the setup file augments the same interface. The tracer now anchors such a member at the repository's own augmentation of the interface it sits on (this workspace's first), with the package named in the reason. fixtures/deps/matchers-extra declares a matcher on the fixture's global `checks.Matchers`; the new test folds its call site into the setup file's `declare global` block. 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 10, 2026 05:31
Merged
lucasbraum
added a commit
that referenced
this pull request
Oct 10, 2026
Supersedes #57. ## Pack `vitest`, `>=4 <5` → `>=5 <6`. Status: **verified** (2 public repos, 100% precision on breaking findings, recall 50%). The only breaking false positive in #57 (`libs/image-utils/src/jest_pdf_snapshot.ts:42`) is gone since #60 folds missing-member diagnostics (TS2339/TS2551) into one finding at the repository's own declaration. Rules, expectations, ground truth and companions are unchanged from #57. Changes over #57: - The README edit that joined two lines is undone (the line limit no longer counts the packs table). - `verification.json`, `packs/queue.json` and the changeset say verified; the README packs table is regenerated (`pnpm docs:packs`; `--check` and `pnpm docs:links` pass). - Tests that used vitest as the example of a package without a pack (`packages/cli/src/cli.test.ts`, `packages/core/src/fix/isolate.test.ts`) use `lodash` instead, since vitest now has a verified pack. Only the example package changed. ## Sources - Migrating to Vitest 5.0: https://vitest.dev/guide/migration (read as `docs/guide/migration/index.md` of vitest-dev/vitest at 69e6369) - Vitest 5.0.0 release notes: https://github.com/vitest-dev/vitest/releases/tag/v5.0.0 - Benchmarking guide: https://vitest.dev/guide/benchmarking - Extending matchers: https://vitest.dev/guide/extending-matchers No official codemod exists for this major. ## Rules and behavior notes Rules: `matcher-types`, `jest-dom-matchers`, `bench-api`, `removed-entrypoints` (rewrites four entry points), `removed-entrypoints-manual`, `sequential-removed`. Behavior notes: `coverage-patterns` (also a finding), `clear-mocks-default`, `test-name-pattern`, `inline-projects-extend`, `hoisted-calls-top-level`, `mock-behavior`, `assertion-timing`, `pretty-format-output`, `fake-timers-temporal`, `artifact-locations`, `config-resolution`, `browser-mode`, `environment-requirements`, `node-api`. ## Ground-truth repositories - votingworks/vxsuite @ `5cb3c7d9c1abc891e7b12de57c0278790dd6160a`, upgrade commit bb04c5fb01aa9bcd285170d2277df04ad5d7e09c (vitest 4.1.11 → 5.0.2, with `@vitest/coverage-istanbul`): 30 expected findings. - vitorvasc/opentelemetry-ecosystem-explorer @ `46d5b37d00bb` (directory `ecosystem-explorer`), upgrade PR head 2b0497b35d0e49dbeeb0d6174ad09aed03d46697 (4.1.11 → 5.0.0): 2 expected findings. Dropped candidates are listed in #57 (siemens/element cannot install from a public registry; the others are version-only bumps). ## `uptide pack test` output ``` uptide pack test vitest · verified, recorded candidate (before --write; now recorded verified) fixtures 5 cases, 17 sites · pass ground truth votingworks/vxsuite@5cb3c7d9c1ab vitest 4.1.11 → 5.0.2 predicted 15333, expected 30, false positives 15319, false negatives 16 with @vitest/coverage-istanbul 4.1.11 → 5.0.2 vitorvasc/opentelemetry-ecosystem-explorer@46d5b37d00bb vitest 4.1.11 → 5.0.0 predicted 3621, expected 2, false positives 3619, false negatives 0 rule precision recall tp fp fn bench-api 15% 67% 2 11 1 coverage-patterns 100% 0% 0 0 13 generic 0% 100% 0 18927 0 jest-dom-matchers 100% 85% 11 0 2 matcher-types 100% 100% 3 0 0 overall (sites) 0% 50% 16 18938 16 breaking (sites) 100% 50% 16 0 16 PASS vitest: verified ``` (The ~18,900 `generic (unverified)` lines are omitted; the 11 `bench-api` false positives are unverified inferred sites, not breaking.) Existing packs, rerun, all unchanged: ``` zod verified overall 100% / 84%, breaking 100% / 71% (PASS) stripe verified overall 100% / 90%, breaking 100% / 90% (PASS) ai verified overall 100% / 77%, breaking 100% / 36% (PASS) react verified breaking 100% / 69% (89 sites, 0 false) (PASS) ``` Environment note: tldraw (react) pins Yarn Berry and the sandbox `yarn` is classic, so the react run used a corepack shim on PATH (sandbox only). With that shim on PATH, stripe's nextjs/saas-starter failed to install (`MODULE_NOT_FOUND`); stripe passes without the shim. Environment issues, not regressions. `pnpm lint`, `pnpm typecheck` and `pnpm test` pass. ## What is not covered - Pack `detect` runs only on files that import the package, so these expected sites are missed: 13 bare-directory `coverage.include/exclude` entries in configs that import a shared `defineConfig` wrapper, and jest-dom imports in two setup files that import nothing from `vitest`. Breaking recall is 50%. - `check` could not resolve imports in 69 of 71 vxsuite files (workspace packages are not built), so only a few of the 169 compiler diagnostics are visible there. - `bench-api` misses the describe wrapper at `render_ballot.bench.ts:16`. - Rules and notes with no evidence in either repo, resting on fixtures and the guide only: `removed-entrypoints*`, `sequential-removed`, nested `vi.mock`, and all behavior notes except `coverage-patterns`. - The two repos are one monorepo and one small app; a third that really migrated would make "verified" stronger. ## Tooling friction - `pnpm uptide check` from source fails inside its worker (`Cannot find module .../packages/core/src/errors.js`); `pnpm build` and the dist CLI work. `pack test` is unaffected. - `pnpm packs:truth` requires full 40-character SHAs and prints the new path with the old line number for moved files. - A corepack shim on PATH fixes Yarn Berry repos but breaks the pnpm install of nextjs/saas-starter in the same run; the packs had to be run with and without it. - vitest.dev is not reachable from the sandbox. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01NwSXH7Dy3r4KjJhwKYBuxT --- _Generated by [Claude Code](https://claude.ai/code/session_01NwSXH7Dy3r4KjJhwKYBuxT)_ --------- Signed-off-by: Claude <noreply@anthropic.com> Co-authored-by: Claude <noreply@anthropic.com>
lucasbraum
pushed a commit
that referenced
this pull request
Oct 10, 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.7.0 ### Minor Changes - [#60](#60) [`7ec02e0`](7ec02e0) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `check` reports one breaking finding when a member the target no longer sees was declared by your own code in an augmentation: a custom matcher on the global `jest.Matchers` in a test setup file, or an interface augmented in a `declare module "x"` block. The finding is anchored at that declaration (the `declare global` block, or the augmented interface), where the one edit is, and the call sites that fail (TS2339, TS2551) are listed under it as evidence, in the terminal, the HTML report and the JSON report. Before, each call site was a breaking finding of its own next to the declaration. The README's line budget no longer counts the generated "Verified packs" table, which grows one row per pack. - [#64](#64) [`86b24d2`](86b24d2) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Plan peer blockers before cloning or installing, suggest explicit compatible peer upgrades, and add repeatable `--allow-peer` with visible package-manager overrides and PR risks. Reject npm lockfiles that disagree with committed manifests before starting a migration. Collect peer blockers across the complete upgrade group and print one complete retry command. Include explicitly allowed peer packages in the old/new lockfile subtree union while retaining strict rejection of unrelated resolution changes. Accept same-content lockfile deduplication and descriptive metadata updates only when outside resolutions stay identical, and disclose accepted housekeeping in the fix report and PR description. - [#63](#63) [`8dcf5fb`](8dcf5fb) Thanks [@lucasbraum](https://github.com/lucasbraum)! - A verified migration pack for Vitest 4 → 5, scored against votingworks/vxsuite and vitorvasc/opentelemetry-ecosystem-explorer at the commit before their own upgrade. It is verified with two public repositories and no false positive among breaking findings (recall 50%). Sites it finds: custom matchers declared on the global `jest.Matchers` or on a one-parameter `Assertion<T>`, `import '@testing-library/jest-dom/vitest'` registrations whose types stop reaching `expect`, the removed `bench` export, bare-directory `coverage.include` and `coverage.exclude` entries, and the removed `vitest/*` entry points (`vitest/coverage`, `vitest/reporters`, `vitest/environments` and `vitest/snapshot` are rewritten). What the compiler cannot see (mock history cleared by default, `testNamePattern` joined with " > ", hoisted `vi.mock` calls, un-awaited assertions, timers, report locations, browser mode) is listed for review. ### Patch Changes - [#62](#62) [`c0f8a70`](c0f8a70) Thanks [@lucasbraum](https://github.com/lucasbraum)! - Preserve partial version ranges such as `^18`, `~18.2`, and `18.x` when fixing packages and their companions, including workspace manifests and pnpm catalogs. Reject unsupported complex ranges before cloning or spending on assisted fixes, with the declaration location and an installed-version replacement suggestion. 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.
What and why
1. Shared root through a setup or declaration file. In the vitest pack test (#57, ground truth votingworks/vxsuite),
checkreported a breaking finding atlibs/image-utils/src/jest_pdf_snapshot.ts:42(TS2551,toMatchImageSnapshotmissing onAssertion) beside the declaration atlibs/image-utils/src/vitest_setup.ts:10the pack already reports. The tracer did not link them: #51 handles parameters only, and this is a global augmentation. Two things turned out to be true on inspection:toMatchImageSnapshotcomes fromjest-image-snapshot's own types, which declare it on the globaljest.Matchers. The setup file augments that same interface (declare global { namespace jest { interface Matchers<R> { toMatchImage…; toMatchPdfSnapshot… } } }) and registers all three matchers withexpect.extend. Vitest 5 stops reading the global namespace, so every matcher on it has to move toMatchers<R, T>of "vitest" together, and the repository's augmentation is where that edit goes.The cause tracer (
adapters/typescript/cause.ts,augmentationCause) now handles missing-member diagnostics (TS2339, TS2551): the member is resolved in the baseline program, where it still exists, anddeclare globalblock at the block, adeclare module "x"augmentation at the interface inside it, any other repository declaration at itself;root, so sites in other workspaces fold with it after the merge (feat(check): one finding when call sites in several workspaces trace to one declaration #51's model, same report fields:anchorOnly,downstream,root, "N call sites").It is tried after the compiler-option cause and before the parameter cause. On vxsuite the finding is now one site at
vitest_setup.ts:10, with thejest_pdf_snapshot.tscall as evidence.2. README line limit.
readme.test.tscapped the file at 150 lines and every pack row pushed against it. The test now excludes the generated block between<!-- packs:start -->and<!-- packs:end -->from the count. #57's two joined documentation lines are not onmain(that change lives on #57's branch), so there is nothing to restore here; once this merges, #57 can drop that edit.No pack's rules, expectations or ground truth were edited.
How it was verified
Unit tests in
cause.test.tson a new fixture pair,fixtures/deps/matchers-v1(an assertion that reads the globalchecks.Matchers) andmatchers-v2(its ownMatchers<R, T>, the global no longer read), plusmatchers-extra-v1(a package declaring a matcher on that global):declare globalline, each site carrying the cause and the root;pnpm lint,pnpm typecheck,pnpm docs:links,pnpm docs:packs --check, the private-material guard, core (930 tests) and CLI (350 tests) pass.Pack tests, before (main at 45677eb) and after (this branch), breaking findings; vitest run in a scratch worktree with #57's pack copied in uncommitted:
jest_pdf_snapshot.ts:42)vitest per rule after:
matcher-types100% / 100% (3 of 3), was 75% / 100%.pack test viteststill exits non-zero on this branch only because #57'sverification.jsonrecords 94% / 1 false positive and no longer matches; that file is #57's to rewrite with--write. Its 16 false negatives and the 18,900-oddgeneric (unverified)sites (not breaking) are unchanged.Checklist
pnpm lint && pnpm typecheck && pnpm testpassdocs/architecture.md,docs/concepts.md)pnpm changeset): minor🤖 Generated with Claude Code
https://claude.ai/code/session_018u9b3Pp5H68DdYZGp5Qg1U
Generated by Claude Code