FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

flatten: recursive schemas under an allOf overlay serialize again by reuvenharrison · Pull Request #1233 · oasdiff/oasdiff · GitHub

flatten: recursive schemas under an allOf overlay serialize again - #1233

Merged
reuvenharrison merged 2 commits into
mainfrom
flatten-refless-cycle
Sep 12, 2026
Merged

flatten: recursive schemas under an allOf overlay serialize again#1233
reuvenharrison merged 2 commits into
mainfrom
flatten-refless-cycle

Conversation

Copy link
Copy Markdown
Collaborator

Repro

oasdiff flatten data/allof/circular-overlay.yaml

crashed with fatal error: stack overflow (found while verifying #1231, which fixed the same fixture's crash on the diff path; the flatten path crashes in SchemaRef.MarshalJSON instead).

Two compounding defects

1. MergeSpec's write-back breaks self-referential identity. *s.Value = *m copies the merged contents into the shared original object so every $ref sees the merge, but the merged subtree's self-references still point at the object Merge returned. A recursive schema's one-object cycle becomes a two-object cycle. Later attachment points then meet two distinct "originals" for one schema, and the per-attachment merge cache cannot unify them (confirmed by pointer tracing: two $ref: Node sites arrived at the items merge with different values). Fixed by redirectSchemaRefs: after the copy, walk the subtree and point references at the written-back object. The walk is reflection-driven, so a new subschema-bearing field is covered without being listed.

2. A subschema set that dedups to one schema was flattened anyway. resolveItems and friends only short-circuited on len == 1 by pointer, so two $ref: Node siblings (same value, distinct SchemaRef wrappers) went into flattenSchemas, whose in-flight cycle guard pointed the fresh ref-less result at an ancestor: a cycle with no $ref, which the marshaler follows until the stack overflows. The new mergeSubschemas dedups by value first and reuses the lone schema, $ref intact, at every subschema merge site (items, contains, propertyNames, additionalProperties, properties — the last had no single-schema shortcut at all).

Result

The merged reproducer now inlines the combined shape once, with its recursion expressed as $ref: Node — serializable, and visible to the diff's ref-based circular guard, so breaking --flatten-allof on this fixture no longer overflows even without #1231's in-flight guard.

#1231 remains necessary alongside this: an allOf over two distinct recursive components still merges to a ref-less cycle (the combined node has no component name a $ref could use), and only the in-flight diff guard keeps breaking --flatten-allof alive on that shape. Serializing that case in oasdiff flatten needs a synthetic hoisted component and is left to a follow-up issue.

One pinned identity moves deliberately: the #890 test asserted that merging allOf: [node, node] re-anchors the cycle onto the merged root. Merging a set that dedups to one schema now yields that schema itself, cycle intact (X ∧ X = X); the test asserts the preserved cycle instead.

Regression test: Test_MergeSpec_RecursiveOverlaySerializes (merged shape, preserved $ref, and a successful marshal of the whole spec). Full suite and lint pass.

Two defects compounded into a stack overflow in 'oasdiff flatten' on
allOf: [$ref Node, <overlay referencing Node>] where Node is recursive:

1. MergeSpec's write-back (*s.Value = *m) copies the merged contents
   into the shared original object, but the subtree's self-references
   still point at the object Merge returned. A recursive schema's
   one-object cycle becomes a two-object cycle, so later attachment
   points see two distinct originals for one schema and the merge cache
   cannot unify them. redirectSchemaRefs now repairs the references
   after the copy, type-driven so new subschema fields are covered
   without being listed.

2. Merging sibling subschemas that dedup to a single schema flattened
   them anyway, and the in-flight cycle guard then pointed the fresh
   ref-less result at an ancestor: a cycle with no $ref, which the
   marshaler follows until the stack overflows. mergeSubschemas now
   reuses the lone schema, $ref intact, at every subschema merge site
   (items, contains, propertyNames, additionalProperties, properties).

The merged output for the reproducer now inlines the combined shape
once with its recursion expressed as $ref Node, so it serializes and
the diff's ref-based circular guard sees the cycle. The #890 pin moves
with the second change: merging a set that dedups to one schema yields
that schema itself, cycle intact, rather than re-anchoring the cycle
onto the merged root.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QDvUFsg5nu1NBWQffErGDw

codecov-commenter commented Sep 9, 2026
edited
Loading

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.67442% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.26%. Comparing base (76d2b54) to head (d2a0940).

Files with missing lines Patch % Lines
flatten/allof/merge_allof.go 95.45% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1233      +/-   ##
==========================================
- Coverage   92.27%   92.26%   -0.02%     
==========================================
  Files         347      347              
  Lines       14312    14306       -6     
==========================================
- Hits        13207    13200       -7     
- Misses       1105     1106       +1     
Flag Coverage Δ
unittests 92.26% <97.67%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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


Back | FazBrowse Home | New Git URL