declaresResourceIDAbove (compilers/openapi/internal/schema/schema.go) walks a
pointer from the document root looking for a $id above the position it names, and
skips every empty segment on the way:
for seg := range strings.SplitSeq(pointer, "/") {
...
if seg != "" { // every pointer starts with the empty segment
cur = nodeview.Deref(view.ChildByToken(cur, ids.UnescapeSegment(seg)))
}
}
Only the leading empty segment is an artifact of the split. Every later one is a
real RFC 6901 reference token naming the key "", so a pointer that ends in one
stops the walk at the parent and the position's own $id is never read.
A component schema named "" is addressed at /components/schemas/, which is
exactly that shape. Probed directly against the lowerer:
const src = `openapi: 3.1.0
info: {title: t, version: '1'}
paths: {}
components:
schemas:
"":
$id: https://example.com/empty
type: object
`
l, _ := loweredFor(t, src)
declaresResourceIDAbove(l.ctx, "/components/schemas/") // => false, want true
Consequence is a missed resource boundary rather than a hang: dynamicChainVerdict
lets a $dynamicRef expansion cross into a schema resource it should have degraded
at, which the function's own comment calls the worse of the two directions to err in
("a missed one mints a reference the IR cannot express").
Found while fixing the same laundering in nodeview.PointerPath, whose consequence
was a deadlock and is fixed separately. The two sites need different fixes: this walk
reads $id before descending, so its first iteration must still skip the leading
empty segment while every later one becomes a real ChildByToken step.
declaresResourceIDAbove(compilers/openapi/internal/schema/schema.go) walks apointer from the document root looking for a
$idabove the position it names, andskips every empty segment on the way:
Only the leading empty segment is an artifact of the split. Every later one is a
real RFC 6901 reference token naming the key
"", so a pointer that ends in onestops the walk at the parent and the position's own
$idis never read.A component schema named
""is addressed at/components/schemas/, which isexactly that shape. Probed directly against the lowerer:
Consequence is a missed resource boundary rather than a hang:
dynamicChainVerdictlets a
$dynamicRefexpansion cross into a schema resource it should have degradedat, which the function's own comment calls the worse of the two directions to err in
("a missed one mints a reference the IR cannot express").
Found while fixing the same laundering in
nodeview.PointerPath, whose consequencewas a deadlock and is fixed separately. The two sites need different fixes: this walk
reads
$idbefore descending, so its first iteration must still skip the leadingempty segment while every later one becomes a real
ChildByTokenstep.