The gap
When allOf branches declare the same field with incompatible types, compilers/openapi/internal/merge/merge.go:98,251-264 keeps the first declaration and emits openapi/conflicting-redeclaration naming both pointers.
The losing type is not written to Unmodeled. Verified in source: the merge path (merge.go:80-109) merges docs, default, constraints, deprecation, XML, examples and unmodeled — but not the discarded type.
Why this one stands out
Every other degradation in this compiler keeps the thing it could not model. That is the whole discipline: an Unmodeled entry with a Reason, so nothing reaches the IR in no form. This path is the exception, and it is silent to any consumer that reads the document rather than the diagnostic stream.
Example and scale
102 occurrences in GitHub's published spec, e.g. webhook-fork/properties/forkee/allOf/1/properties/clone_url: t/prim/url in one branch, t/prim/string in the other, first wins.
A consumer diffing two versions of that document sees "no change" across a release where the losing branch's type moved.
Suggested fix
Keep the losing declaration verbatim under Unmodeled with ReasonDegradedLowering, the way the co-declared-keyword paths already do.
Found by a study of dexpace/spaceapi adopting morphic IR as its input format (spaceapi#56), 2026-09-05. Related: #418 (non-emitter consumer), #419 (cross-revision identity).
The gap
When
allOfbranches declare the same field with incompatible types,compilers/openapi/internal/merge/merge.go:98,251-264keeps the first declaration and emitsopenapi/conflicting-redeclarationnaming both pointers.The losing type is not written to
Unmodeled. Verified in source: the merge path (merge.go:80-109) merges docs, default, constraints, deprecation, XML, examples and unmodeled — but not the discarded type.Why this one stands out
Every other degradation in this compiler keeps the thing it could not model. That is the whole discipline: an
Unmodeledentry with aReason, so nothing reaches the IR in no form. This path is the exception, and it is silent to any consumer that reads the document rather than the diagnostic stream.Example and scale
102 occurrences in GitHub's published spec, e.g.
webhook-fork/properties/forkee/allOf/1/properties/clone_url:t/prim/urlin one branch,t/prim/stringin the other, first wins.A consumer diffing two versions of that document sees "no change" across a release where the losing branch's type moved.
Suggested fix
Keep the losing declaration verbatim under
UnmodeledwithReasonDegradedLowering, the way the co-declared-keyword paths already do.Found by a study of
dexpace/spaceapiadopting morphic IR as its input format (spaceapi#56), 2026-09-05. Related: #418 (non-emitter consumer), #419 (cross-revision identity).