#74 landed the inlining of a same-document <use> and stopped a sprite host from
becoming a blank asset. Five ceilings were accepted knowingly on the way, and two
of them are defects rather than tradeoffs. All of it is latent: no committed board
draws with <use> today (1512 root svgs, 33 with defs/symbol, 0 <use>), so
this is the list to clear before a board actually ships a sprite.
1. seen===1 counts nested resolutions, so a composite symbol loses viewBox adoption.
One top-level <use> into a <symbol> that itself holds a resolvable <use> runs
seen to 2, the root declines the symbol's viewBox, and the size falls back to the
root's width/height attributes. A root sized only by a class or an inline style
has neither — both are stripped from the copy — so the row reads 0x0 and the standalone
copy draws in the browser's default viewport instead of the symbol's coordinate system.
Fix: count top-level resolutions separately from the budget. (Codex P2 on 9beabad.)
2. A sprite-hosted icon never matches its assets/icons file signature, so it never
gets the file's name. Not a regression — it was true before #74 too, for a different
reason. svgSignature scans every drawing element anywhere in the markup, <symbol>
contents included. Before: the symbol's geometry once, plus a use row the file has
not. After: the symbol's geometry twice, because the original <defs> stays in the copy
beside the inlined result. Either way the key differs from the file's and the inspector
falls back to an inferred label. Fix: after expansion, drop the <symbol>s that were
expanded away and a <defs> left empty — nothing references them once the geometry is
inlined, and the remaining signature is then exactly the icon file's. Gradients,
clipPaths and anything reached by url(#id) must stay. This also shrinks the data:
URI by the duplicated geometry. Keep a dangling <use>'s target if the budget ran out.
3. Wrappers are invisible to the signature, so a nested symbol collides with a flat one.
svgSignature does not sign the inner <svg> wrappers the expansion introduces, so one
glyph reached through two hops and the same glyph inlined flat produce the same key and
share one asset row. Harmless while the geometry really is identical; wrong the moment a
wrapper carries a transform or an offset that changes what is drawn.
4. The regression check is a string canary, not a behaviour test. inspectorAgent.test.ts
asserts substrings of AGENT (document.getElementById(h.slice(1)), :not(defs *,symbol *),
left=64) because the vitest env is node with no DOM. Every real verification for #73 and
its amendment ran out of band, through injectAgent into headless Chrome the way
tools/refkit.py shoots boards. A rename keeps the tests green while the behaviour breaks.
Fix: one headless-Chrome check in the toolkit's test path, driven by the same fixture, so it
runs in CI on a mac runner or is skipped explicitly on Linux.
5. The "defines only" guard names defs and symbol only. Geometry that exists purely
inside a <mask>, <clipPath>, <pattern> or <marker> also draws nothing by itself, so a
root holding only those still becomes an asset row. Nit: extend the :not() list.
Fix order that matches the payoff: 1 and 2 together (both live in addSvg, both decide what
the copy's size and name are), then 4, then 3 and 5 when a board makes them observable.
#74 landed the inlining of a same-document
<use>and stopped a sprite host frombecoming a blank asset. Five ceilings were accepted knowingly on the way, and two
of them are defects rather than tradeoffs. All of it is latent: no committed board
draws with
<use>today (1512 root svgs, 33 withdefs/symbol, 0<use>), sothis is the list to clear before a board actually ships a sprite.
1.
seen===1counts nested resolutions, so a composite symbol loses viewBox adoption.One top-level
<use>into a<symbol>that itself holds a resolvable<use>runsseento 2, the root declines the symbol'sviewBox, and the size falls back to theroot's
width/heightattributes. A root sized only by a class or an inlinestylehas neither — both are stripped from the copy — so the row reads 0x0 and the standalone
copy draws in the browser's default viewport instead of the symbol's coordinate system.
Fix: count top-level resolutions separately from the budget. (Codex P2 on 9beabad.)
2. A sprite-hosted icon never matches its
assets/iconsfile signature, so it nevergets the file's name. Not a regression — it was true before #74 too, for a different
reason.
svgSignaturescans every drawing element anywhere in the markup,<symbol>contents included. Before: the symbol's geometry once, plus a
userow the file hasnot. After: the symbol's geometry twice, because the original
<defs>stays in the copybeside the inlined result. Either way the key differs from the file's and the inspector
falls back to an inferred label. Fix: after expansion, drop the
<symbol>s that wereexpanded away and a
<defs>left empty — nothing references them once the geometry isinlined, and the remaining signature is then exactly the icon file's. Gradients,
clipPaths and anything reached byurl(#id)must stay. This also shrinks thedata:URI by the duplicated geometry. Keep a dangling
<use>'s target if the budget ran out.3. Wrappers are invisible to the signature, so a nested symbol collides with a flat one.
svgSignaturedoes not sign the inner<svg>wrappers the expansion introduces, so oneglyph reached through two hops and the same glyph inlined flat produce the same key and
share one asset row. Harmless while the geometry really is identical; wrong the moment a
wrapper carries a
transformor an offset that changes what is drawn.4. The regression check is a string canary, not a behaviour test.
inspectorAgent.test.tsasserts substrings of
AGENT(document.getElementById(h.slice(1)),:not(defs *,symbol *),left=64) because the vitest env is node with no DOM. Every real verification for #73 andits amendment ran out of band, through
injectAgentinto headless Chrome the waytools/refkit.pyshoots boards. A rename keeps the tests green while the behaviour breaks.Fix: one headless-Chrome check in the toolkit's test path, driven by the same fixture, so it
runs in CI on a mac runner or is skipped explicitly on Linux.
5. The "defines only" guard names
defsandsymbolonly. Geometry that exists purelyinside a
<mask>,<clipPath>,<pattern>or<marker>also draws nothing by itself, so aroot holding only those still becomes an asset row. Nit: extend the
:not()list.Fix order that matches the payoff: 1 and 2 together (both live in
addSvg, both decide whatthe copy's size and name are), then 4, then 3 and 5 when a board makes them observable.