Skip to content

fix(downgrader): drop a repeated null branch in an anyOf, as Effect writes a nullish union - #237

Merged
dinwwwh merged 1 commit into
mainfrom
claude/project-thread-nxiytd
Oct 31, 2026
Merged

dinwwwh merged 1 commit into
mainfrom
claude/project-thread-nxiytd

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Oct 31, 2026

Copy link
Copy Markdown
Member

Requested by dinwwwh · project thread

Effect writes a nullish union, or an optional nullable one, as anyOf [A, B, { type: "null" }, { type: "null" }]. For example, Schema.NullishOr(Schema.TaggedUnion(...)) through oRPC's Effect converter does this. The 3.0 output kept both null branches, because each { type: "null" } becomes its own enum: [null] array and isSameBranch compares keyword values by identity. The same JSON already lost the duplicate when both branches were one in-memory object, so the output depended on object identity.

isSameBranch now counts two enum: [null] arrays as the same value, so dropEqualBranches keeps null once. A lone anyOf [null, null], which is how Effect writes an optional Schema.Null, now becomes the null schema instead of a oneOf with one branch. Null branches with different annotations still both stay, and so do other enum copies.

  • Tests: new cases in the equal-branches test. Without the fix they fail.
  • README: the equal-branches row now names Effect's nullish union as an example.

Checked on 1,721 generated and real docs (Effect, TypeBox, Yup, Vine, Zod, Valibot, ArkType through oRPC beta.47, plus GitHub, OpenAI, Discord, Stripe and X). Every changed output only drops a repeated null branch.

  • Effect pairwise docs: 3.0 positions with null twice go from 2,837 to 211. The 211 left have the copies apart, such as [string, null, "k", null], and only neighbors are compared.
  • Value check: 1,769,403 values on the Effect docs, 0 lost.

Generated by Claude Code

…rites a nullish union

Effect writes a nullish union, or an optional nullable one, as `anyOf [A, B, { type: "null" }, { type: "null" }]`. 3.0 kept both null branches, since each `{ type: "null" }` becomes its own `enum: [null]` array and `isSameBranch` compares keyword values by identity. Two `enum: [null]` arrays now count as the same value, so `dropEqualBranches` keeps null once, and a lone `anyOf [null, null]` (Effect's optional null) becomes the null schema instead of a `oneOf` with one branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VVaw7QJuqf3F5ycJ5Qq1rT
@dinwwwh dinwwwh self-assigned this Oct 31, 2026
@codecov

codecov Bot commented Oct 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Oct 31, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

1 warning

⚠️ 1 benchmark is dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

✅ 29 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing claude/project-thread-nxiytd (cb5c525) with main (a9ca2fc)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • isSameBranch treats two enum: [null] values as equal — packages/downgrader/src/v3.1-to-v3.0.ts:899-906. A repeated { type: "null" } branch in an anyOf now collapses even when the two branches are distinct objects (each carrying its own enum: [null] array), removing the prior object-identity dependence. The exception is scoped to name === 'enum' with isOnlyNull on both sides; key sets must still match exactly and every other keyword still compares by identity.
  • Tests — packages/downgrader/tests/v3.1-to-v3.0.test.ts:1938-1957. Adjacent duplicate null branches collapse to one; null branches with different description stay; two null branches separated by another branch stay (neighbor-only compare); duplicated ['a', null] enums still stay.
  • README — packages/downgrader/README.md:196. The equal-branches row now names Effect's nullish union as an example.

I traced the single consumer path (isSameBranch → dropEqualBranches, called only for anyOf at v3.1-to-v3.0.ts:1557): dropping a byte-identical adjacent null branch cannot change accepted values, and the other mergers already filtered null branches, so the only output change is the removed duplicate. Verified locally on cb5c525: the test suite passes, and reverting the source hunk makes the new assertion fail, confirming it is a live regression guard.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@dinwwwh
dinwwwh merged commit e102941 into main Oct 31, 2026
11 checks passed
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.

2 participants