Skip to content

feat(zod): report root exports that moved to zod/v3, uuid() becomes guid() (UPT-86) - #200

Merged
lucasbraum merged 2 commits into
mainfrom
lucasbraum/upt-86-zod-pack-reconcile-verificationjson-66-vs-44-tp-and-raise
Oct 17, 2026
Merged

lucasbraum merged 2 commits into
mainfrom
lucasbraum/upt-86-zod-pack-reconcile-verificationjson-66-vs-44-tp-and-raise

Conversation

@lucasbraum

Copy link
Copy Markdown
Collaborator

UPT-86. Breaking recall 54% → 65% (44 → 55 TP, 38 → 30 FN), 0 breaking FP. Ground truth and thresholds unchanged.

Part 1: 66 vs 44

44 / 0 / 38 is the number. A clean local run (packs:fetch, pack test zod) and CI's pack zod job on main (4c3dc23, run 39009240360) agree exactly. The recorded 66 / 0 / 37 does not reproduce, not even at its own commit: 9039e3c (#156) scores 38 / 0 / 37 here.

What I could establish: breaking TP counts only sites check reports at breaking. For vzdc-artcc/website, 0 of its breaking sites are TP: its findings come back unverified with "57 of 95 files cannot resolve their imports; compile signal skipped". The files import @/generated/prisma/*, which prisma generate writes in a postinstall, and ground-truth installs run scripts off. tsc on the cached checkout confirms 234 TS2307 on those paths. #156 predicted this ("probably caused by the Prisma client"). I did not run prisma generate, so I could not show that a generated client yields 66; I believe that is the difference, but it is not demonstrated. The unverified findings cost recall, not precision.

verification.json is regenerated from the run (--write); docs:packs updated README and compatibility. The README's 64% → 65% looks like a one-point gain; against what CI measures, the gain is 54% → 65%.

Part 2: recall

Reach first: of the 38 misses, 33 are in files that import zod directly (the scan reads them); 5 are not (import("zod").X only, LessonForm.tsx reading error.errors with no zod import). The misses are rule gaps, not unread files.

group misses cause
root exports now only in zod/v3 (SafeParseReturnType, ZodTypeDef, ZodEffects) 15 (7 FN here; 8 already seen as types) fixed: the diff counts them as unchanged because exportedFrom overlaps on ./v3
.refine(fn, { message }) → error 6 not done: deprecated, still compiles; needs a detect rule on the second argument
compiler-only TS2322/TS2345 flowing across files (PATH post-runs, resume-run; band server, sse, stdio) 10 not done: no zod API at the line
import("zod").ZodTypeAny and ZodRawShape/_def/ZodOptional name checks 8 not done: deprecated names, _def reads
errorMap / ctx.defaultError 2 not done
rule before TP/FP/FN after
removed-exports (new) n/a 7 / 8 / 0 (all 15 on truth lines; 8 labelled types there, so counted as a wrong rule)
generic 82 / 534 / 26 82 / 527 / 19
error-params, error-issues, string-format, record-key unchanged unchanged
breaking 44 / 0 / 38 55 / 0 / 30

uuid() → guid() (the calmcp case): string-format wrote z.uuid(), which in zod 4 also checks the RFC version and variant; zod 3 accepted any 8-4-4-4-12 hex id. It now writes z.guid(). Decision for you: the public upgrades in the truth mostly use z.uuid(); I chose behaviour preservation. Fixture and guide updated; line-level scores unchanged.

Checks: biome and tsc clean; pack unit tests pass except scaffold.test.ts and pack-test.test.ts "each companion", which fail on a clean main here too.

🤖 Generated with Claude Code

lucasbraum and others added 2 commits October 17, 2026 11:14
…uid() (UPT-86)

Breaking recall 54% -> 65% (44 -> 55 of 82 -> 85 sites), 0 breaking FP.
The recorded 66 / 0 / 37 did not reproduce; CI and a clean local run
measure 44 / 0 / 38 on main (see the PR for why).

- removed-exports: names zod 3 exported from its root that zod 4 exports
  only from zod/v3 (SafeParseReturnType, ZodTypeDef, ZodEffects, ...).
  The surface diff counts them as unchanged because the entry point
  overlaps. All 15 sites land on ground-truth lines.
- string-format: uuid() rewrites to z.guid(), not z.uuid(), which also
  checks the RFC version and variant (the calmcp case).

Ground truth and thresholds unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
…usion (UPT-86)

zod's override now also runs every detect rule over the visited files, so
the error-members fixtures are reported through runtimeFindings like any
pack's. runtime-findings.test.ts no longer excludes zod. Ground truth
unchanged: 55 / 0 / 30 breaking.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
@lucasbraum
lucasbraum force-pushed the lucasbraum/upt-86-zod-pack-reconcile-verificationjson-66-vs-44-tp-and-raise branch from 34a4d28 to e49dbcd Compare October 17, 2026 18:16
@lucasbraum
lucasbraum merged commit 4e4cc3e into main Oct 17, 2026
28 checks passed
@lucasbraum
lucasbraum deleted the lucasbraum/upt-86-zod-pack-reconcile-verificationjson-66-vs-44-tp-and-raise branch October 17, 2026 18:40
@uptide-release uptide-release Bot mentioned this pull request Oct 17, 2026
lucasbraum added a commit that referenced this pull request Oct 17, 2026
…209)

UPT-92, follow-up to #200. Three source rules, all `deprecated`
severity, with fixtures (incl. `keep` cases): `refine-message`
(`.refine(fn, { message })`), `deprecated-names` (`ZodTypeAny`,
`ZodIssue`, `ZodSchema`, `ZodErrorMap`, … also `import('zod').X`),
`error-map` (`errorMap` option, `ctx.defaultError`).
`removed-exports.ts` is generalised so the new rules reuse its walker.

**Same pinned ground truth, no truth or threshold changes.** Baseline is
the CI `pack-test-zod` artifact of run 39057357503 (the pre-squash code
of #200; `packs/zod` is unchanged on main since). After is a local `pack
test zod --write` on the warm cache.

| | before | after |
|---|---|---|
| breaking FN | 30 | 17 |
| breaking TP / FP | 55 / 0 | 55 / 0 |
| breaking recall | 65% | 76% |
| gate | verified | verified |

Breaking TP stays 55 because the scorer counts a `deprecated`-severity
hit on a breaking site as a removed miss, not a TP, so the recall gain
is partly that.

**Unverified FP change:** +28 `deprecated` false positives
(refine-message 13, deprecated-names 15, error-map 2 minus 1 matched).
They are real deprecated uses the truth does not list, not wrong hits.

**Dropped:** a `typename-literals` rule (`name === "ZodArray"`) found 3
truth sites but also a breaking FP at PATH `wire-step-plugins.ts:90` (a
comparison the upgrade left alone), so it was removed, not relabelled.
`error-map` is `deprecated` for the same reason: AwesomeIntune
`tool-submission.ts:100` is a real site the truth anchors one line
earlier.

**Compiler-only group (10):** not attributable from syntax; reasons and
the other leftovers are in `packages/core/src/packs/zod/RESEARCH.md`.
`prompt/index.ts:41` (single-arg `z.record`) was not investigated.

`verification.json`, README and `docs/compatibility.md` regenerated. Not
run: full CI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
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>
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