Skip to content

fix(compilers/openapi): serve a merge memo only where it fits - #453

Open
fuad-daoud wants to merge 1 commit into
mainfrom
fix/nodeview-memo-depth
Open

fix(compilers/openapi): serve a merge memo only where it fits#453
fuad-daoud wants to merge 1 commit into
mainfrom
fix/nodeview-memo-depth

Conversation

@fuad-daoud

Copy link
Copy Markdown
Collaborator

Summary

nodeview.View memoized a mapping's << merge expansion under the node alone and served it at any depth. But the expansion is a function of the node and the depth it is reached at: MergeDepthLimit truncates from the entry point down, so a chain that fits inside the bound from one node may not from a node above it. A memo filled by a shallow read therefore answered a later, deeper read that a fresh view would have truncated, and what a view reported depended on what it had read before (#404).

Each memo entry now records the height of the merge chain beneath it. expand serves a complete entry only where depth + height ≤ MergeDepthLimit; an incomplete entry, which is kept only as an entry point, is served only to another entry point. A refused read expands afresh and truncates exactly where a fresh view would, so a shared view answers as one built per read. Every depth-0 read — every pointer walk and every key-index read — was already servable under that rule, so the fast path is unchanged.

This closes #402 at the same time, because it is the same mechanism at a different site: the cycle pre-scan shares one view across its walk, and whether it reported openapi/cycle-scan-failed depended on which schema it reached first. With the fix, mergeBoundOrderSpec reports the bound in both declaration orders.

Two follow-ups this leaves open, deliberately:

  • dynamicAnchors folds its own walk bounds and view.Exhausted() into one bool, so when the view's 64-level merge bound is what stopped the $dynamicAnchor index, sites reports the wrong bound ("512 levels, 1048576 nodes"). Pre-existing and reachable by any deep-first document; the fixture here now reaches it in both orders. Filed separately rather than widened into this change.
  • openapi: keyword and pointer lookups still rescan the nodes the index already walked #338 (sharing the view in declaresResourceIDAbove) is unblocked; the comment there now says so.

Test plan

  • TestNodeView_SharedViewAnswersAsAFreshOne (new): red before the fix, green after. Three planted mutations — serves always true, the bound one short (<), one long (<= +1) — each redden exactly the subtest written to catch it. The one-short direction is invisible to an answer comparison (it recomputes the same pairs), so the edge case pins memo use via cachedPairs as well.
  • TestDynamicRef_ResourceBoundaryVerdictIsOrderInvariant now compares the diagnostics of the two orders as a set, the way the harness's order-invariance oracle does, and asserts the bound report is present — so the openapi: the cycle pre-scan's merge-bound warning depends on declaration order #402 site is guarded by the same fixture. Confirmed it reddens with the defect planted.
  • make gate passes: lint 0 issues, coverage at 100%, fuzz, bench-smoke.

Closes #404
Closes #402

🤖 Generated with Claude Code

https://claude.ai/code/session_01TJZJ8HaugGZTjzVueafLAT

nodeview memoized a mapping's `<<` expansion under the node alone and
served it at any depth, but the expansion is a function of the node and
the depth it is reached at: MergeDepthLimit truncates from the entry
point down, so a chain that fits from one node may not from a node above
it. A memo filled by a shallow read therefore answered a later, deeper
read that a fresh view would have truncated, and what a view reported
depended on what it had read before (GitHub #404).

Each memo entry now records the height of the chain beneath it, and
expand serves a complete entry only where depth+height still fits under
the bound; an incomplete entry, kept only as an entry point, is served
only to another entry point. A refused read expands afresh and truncates
exactly where a fresh view would, so a shared view answers as one built
per read.

This closes GitHub #402 at the same time: the cycle pre-scan shares one
view across its walk, and whether it reported its bound depended on which
schema it reached first. The resource-boundary two-order test now holds
the diagnostics to the same standard as the registry, so both sites are
guarded by one fixture.

Closes #404
Closes #402

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

1 participant