declaresResourceIDAbove (compilers/openapi/internal/schema/schema.go) reads $id off each node on the path from the document root down to a position, through nodeview. When a node's own merge chain is longer than MergeDepthLimit (64), View.expand returns no pairs at all, so ChildByToken(n, "\$id") finds nothing and the walk reports no boundary.
That is the direction the function's own comment says it must not err in: a false boundary costs an expansion that would have been safe, a missed one mints a reference the IR cannot express.
Reproducer
openapi: 3.1.0
info: {title: Deep, version: "1"}
paths: {}
x-l0: &l0 {$id: 'https://example.com/deep'}
x-l1: &l1 {<<: *l0}
# ... 70 links total
components:
schemas:
Node: {$dynamicAnchor: T, type: string}
A: {<<: *l70, $dynamicRef: '#T'}
A's $dynamicRef is expanded to t/openapi/components/schemas/Node, crossing the $id. Shortening the chain to two links degrades correctly.
Why it is not fixed in passing
View.Exhausted() is set when the bound is hit, but the boundary walk builds its own view and never consults it, and there is no channel for "this verdict rests on an expansion that was truncated". The anchor index has the same situation and reports it as openapi/cycle-scan-failed; this walk needs an equivalent, which is a design decision rather than a one-line guard. Noted in the code comment on declaresResourceIDAbove.
Predates the empty-pointer-segment fix in #362; found while probing it.
declaresResourceIDAbove(compilers/openapi/internal/schema/schema.go) reads$idoff each node on the path from the document root down to a position, throughnodeview. When a node's own merge chain is longer thanMergeDepthLimit(64),View.expandreturns no pairs at all, soChildByToken(n, "\$id")finds nothing and the walk reports no boundary.That is the direction the function's own comment says it must not err in: a false boundary costs an expansion that would have been safe, a missed one mints a reference the IR cannot express.
Reproducer
A's$dynamicRefis expanded tot/openapi/components/schemas/Node, crossing the$id. Shortening the chain to two links degrades correctly.Why it is not fixed in passing
View.Exhausted()is set when the bound is hit, but the boundary walk builds its own view and never consults it, and there is no channel for "this verdict rests on an expansion that was truncated". The anchor index has the same situation and reports it asopenapi/cycle-scan-failed; this walk needs an equivalent, which is a design decision rather than a one-line guard. Noted in the code comment ondeclaresResourceIDAbove.Predates the empty-pointer-segment fix in #362; found while probing it.