Skip to content

fix(compilers/openapi): keep an anchor-valued path-item key - #458

Open
fuad-daoud wants to merge 2 commits into
mainfrom
fix/anchored-path-item-key
Open

fix(compilers/openapi): keep an anchor-valued path-item key#458
fuad-daoud wants to merge 2 commits into
mainfrom
fix/anchored-path-item-key

Conversation

@fuad-daoud

Copy link
Copy Markdown
Collaborator

Summary

A Path Item Object's undeclared keys are read off the operations map the library folds them into, which is what lets them be kept verbatim. The library skips a key whose value carries a YAML anchor before that fold (speakeasy-api/openapi v1.24.1, marshaller/unmarshaller.go), so such a key entered no map and no field: it reached the IR in no form at all — no Unmodeled entry, no diagnostic — while a plainly-valued key beside it was kept and reported. Two documents differing only in that key compiled to the same IR (#412).

The raw mapping is the only place the key is written, so the census now reads it too. annotation.RawMappingKeys lists a raw mapping's keys the way RawChildNode looks one up — the same non-expanding reader the keeper uses, so every name it returns is one the keeper can find a value for — and undeclaredPathItemKeys diffs them against what the folded map, the model's fields, the standard methods and the x- prefix account for. Each key found only there goes to the same keeper as the plain case, so both land under openapi:pathItem/<key> with one unknown-object-key warning apiece: no new key form, reason, code or converter.

nodeview.View was not used, deliberately: archtest forbids operation and annotation from importing it, and a merge-expanded key set would name keys the non-expanding keeper then cannot reach. What this reader therefore does not see — and says so in its GoDoc — is a key merged in through << whose value is anchored; that is #395's to close.

The model's field list is hand-written and held to core.PathItem's own key tags by a test, which answers the concern that set raw reading aside in #377: the vocabulary tracks the library, not the specification by hand.

Found by the mechanism sweep, not fixed here: the same library skip drops an anchored declared entry — a whole path item under paths, a response under responses, a callback expression, or get: &g {...} on a path item — with no diagnostic. That is a different loss (an entry to lower, not a key to preserve) and gets its own issue.

Test plan

  • New conformance case path-item-anchored-key, declared anchored-key-first (the order that was wrong): pins both keys kept under Unmodeled with their values and one unknown-object-key warning per key at the key's pointer. The harness's order-invariance oracle does permute this fixture (an anchor without an alias survives reversal), and reports ok.
  • TestPathItemFields_MatchTheLibraryModel holds the hand-written field list to core.PathItem; TestPathItemDeclares_AnchoredDeclaredKeysAreNotUndeclared is the false-positive control (every declared class anchored, only bogus reported); the every-route test gains an anchored twin on all three path-item parents.
  • Three planted defects each reddened the named assertion; restored.
  • make gate passes: lint 0 issues, coverage at 100%, fuzz, bench-smoke.

Closes #412

🤖 Generated with Claude Code

https://claude.ai/code/session_01TJZJ8HaugGZTjzVueafLAT

fuad-daoud and others added 2 commits September 11, 2026 13:45
A Path Item Object's undeclared keys are read off the operations map the
library folds them into, which is what lets them be kept verbatim. The
library skips a key whose value carries a YAML anchor before that fold
(speakeasy-api/openapi v1.24.1, marshaller/unmarshaller.go), so such a
key entered no map and no field: it reached the IR in no form at all —
no Unmodeled entry, no diagnostic — while a plainly-valued key beside it
was kept and reported. Two documents differing only in that key compiled
to the same IR.

The raw mapping is the only place the key is written, so the census now
reads it too: annotation.RawMappingKeys lists a raw mapping's keys the
way RawChildNode looks one up, and undeclaredPathItemKeys diffs them
against what the folded map, the model's fields, the standard methods
and the x- prefix account for. Each key found only there is handed to
the same keeper as the plain case, so both land under
openapi:pathItem/<key> with one unknown-object-key warning apiece.

The model's field list is hand-written and held to core.PathItem's own
key tags by a test, which answers the concern that set the raw reading
aside in #377: the vocabulary tracks the library rather than the
specification by hand, and #293 has since settled the method half.

Closes #412

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJZJ8HaugGZTjzVueafLAT
RawMappingKeys's GoDoc said the pairs a `<<` merge key brings in are what
the model holds. That is false for a merged-in value that is itself
anchored: the library's anchor skip drops it from the model too, so
neither the folded map nor the raw mapping presents it and only a
merge-expanded view could — which is #395's to close. The comment now
says so where the next reader lands, instead of asserting the opposite.
Its "nil for a node that is not a mapping" also covered an empty mapping
without saying so; the sentence and the test now name that case.

Beside it: the route fixture's comment records that a plain scalar under
an undeclared path-item key draws the library's type-mismatch error, and
now also that an anchored scalar bypasses the fold and is kept with the
warning alone — the lossless outcome, not a divergence to close by
rejecting the anchored form. And the field-list pin binds its cmp.Diff
before asserting on it, so a failure prints the diff rather than an
opaque value.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJZJ8HaugGZTjzVueafLAT
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.

openapi: an anchor-valued undeclared key on a path item reaches the IR in no form

1 participant