Skip to content

fix(downgrader): count every 3.1 annotation beside record key enums and nullable targets - #205

Merged
dinwwwh merged 1 commit into
mainfrom
claude/project-thread-egtoix
Oct 29, 2026
Merged

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

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Oct 29, 2026

Copy link
Copy Markdown
Member

Requested by dinwwwh · project thread

Three spots in the 3.1 to 3.0 converter check 3.1 input keys for annotations, and two of them used a shorter list than "every annotation":

  1. Records keyed by an enum (enumNames, from feat(downgrader): turn a propertyNames enum into one property per name in 3.1 to 3.0 #100) accepted only enum, const, title, description, and type: "string" in the key schema. Any other annotation, such as example, examples, default, deprecated, $comment or an x- key, made it bail, so the 3.0 record allowed any key.
    • Meilisearch 1.15.2 (utoipa): BatchStats.status and BatchStats.types are keyed by an enum with example: "processing". openapi-typescript gave [key: string]: number. With this change it gives enqueued?: number; processing?: number; ....
    • ElevenLabs (Pydantic): ConversationInitiationSource has default: "unknown" beside its 27-value enum. The records keyed by it now get 27 properties, and the discriminated oneOf responses that hold them keep their oneOf instead of becoming anyOf.
    • oRPC with Zod: z.record(E, V) where E has .meta({ id, examples }) or .meta({ id, deprecated: true }) now gets one property per name, as a described E already did.
  2. A nullable $ref to a nullable anyOf (admitsNull, from fix(downgrader): keep only the $ref of a nullable reference to a nullable anyOf #203) allowed only 3.0 annotations beside the target's anyOf, so the examples that Zod's .meta writes there kept the anyOf. NSwag reads that as an empty class.

isAnnotatedRef (#204) already had the longer list. A new isInputAnnotation helper (examples, $comment, or a 3.0 annotation) now serves all three.

The README row for enum-keyed records now says that annotations beside the names are dropped. The nullable-reference row already said "only annotations", so it is unchanged.

Evidence from the issue hunt: on a 19,775-doc corpus the enumNames change touches 7 docs at 64 sites, with no throws and no values lost per the value oracle. The admitsNull change touches none of those docs, but it fixes the oRPC repro.

Tests

  • pnpm run check and pnpm test pass (364 tests). Downgrader coverage stays at 100%.
  • The new assertions fail on main: the enum-key test, the $ref-to-enum test, and the nullable anyOf target test.

Generated by Claude Code

…nd nullable targets

A record keyed by an enum with an `example`, `examples`, `default`,
`deprecated`, `$comment` or `x-` key beside the names lost its names in
3.0, so it allowed any key (Meilisearch's utoipa `example`, ElevenLabs'
Pydantic `default`, oRPC `.meta({ examples })`). A nullable `$ref` to a
nullable `anyOf` with `examples` or `$comment` beside it kept the `anyOf`.

One helper, `isInputAnnotation`, now checks 3.1 input keys for
annotations in `enumNames`, `admitsNull` and `isAnnotatedRef`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VVaw7SWqGqen9de9vgJcko
@dinwwwh dinwwwh self-assigned this Oct 29, 2026
@codecov

codecov Bot commented Oct 29, 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 29, 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-egtoix (4e65f5a) with main (e92cd8f)

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

  • isInputAnnotation helper — new helper (examples/$comment/isAnnotation) centralizes the 3.1 annotation set; isAnnotatedRef now delegates to it as a pure equivalent refactor.
  • enumNames widening — both the $ref sibling check and the resolved-target keyword allow-list now accept any input annotation instead of only title/description, so records keyed by an enum carrying example/examples/default/deprecated/$comment/x- convert to fixed properties + additionalProperties: false.
  • admitsNull widening — accepts examples/$comment beside an anyOf with a null branch, or alone; its only caller (nullable $ref to a null-admitting target) now drops the anyOf.
  • Docs & tests — README row updated; new assertions cover the enum-key, $ref-to-enum, and nullable-anyOf cases.

The added keywords are all non-restricting by definition, so the widening carries no value-loss risk. Verified pnpm run check clean and pnpm test 364 passing on 4e65f5a.

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

@dinwwwh
dinwwwh merged commit 8ed8c16 into main Oct 29, 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