BUG-BROWSER-GATES-CAN-MEASURE-ANOTHER-WORKTREES-TREE, the remainder. A
static scan of every internal/ script that drives a browser at a local
target found eight with no identity guard (a9550858 and 59f497eb
guarded the rest):
built tree check-spa-tab-order -> requireThisTree
dev server long-frames-probe, mobile-sheet-probe, theme-flash-probe,
plugin-gallery-smoke, share-roundtrip-smoke (--plugin-gallery),
smoke-studio, legend-columns-probe (only when aimed at
/plugin-designs/; its default target is the DX-life site
it compares against on purpose)
A dev server has no built manifest, so the new
requireDevServesThisCheckout checks the source it serves. It fetches
three library files with ?raw and compares them byte for byte with
this checkout. Another tree's dev server answers differently or 403s,
and the gate exits 75 (NOT MEASURED). Vite answers a plain fetch with
the file's text and an import-style request with `export default "…"`;
both shapes are accepted, since the first version parsed only the
second and refused this checkout's own server.
Proven: against a dev server running from 319064a1's parent and a
built tree whose manifest differs by one byte, all eight exit 75 and
name the differing file (useMapInstance.js). Against this checkout,
check-spa-tab-order and smoke-studio PASS, and plugin-gallery-smoke
gets past the guard. A re-scan finds no unguarded script left.
The card recorded a hypothesis and said not to assume it: that Firefox's deepFind resolved
only the outermost shadow root. Measured and disproved — both engines find exactly one
shadow host at depth 0, so shadow traversal is identical. The real difference is WebGL.
Chromium reports webglSupported true with 157 elements in the shadow root and the toolbar
present; Firefox reports false with 31 elements, no toolbar, and "[doxa-simple-map] Map
failed to initialize".
Confirmed as an environment property, not a page one: Firefox returns null from getContext
on a blank page here, and firefoxUserPrefs for software WebGL do not change it. Chromium
only works because launch() passes SwiftShader args. So the Share button is never rendered
because the map never initialises, and the suite was blaming the product for that.
Rather than quietly defaulting away from the Driver's house rule, the suite now aborts with
the measurement and the exact working command, and exits 2 so a caller can tell "could not
test" from "test failed". Restating the rule stays the Driver's call.
It also had no token wiring, the third gate found missing it. My first attempt set only
window.MAP_TOKEN, assuming a page global would survive into the reopened page. Measured:
?tk= only mounts, global only does NOT mount, both mount. The global is read by nested
children of a composite, not by a top-level element. So the host URL carries ?tk= and the
share URL is navigated verbatim, because appending a token there would fake the very thing
under test.
The remaining failure is now honest and in the right place: the host page renders, and the
share URL cannot, because a pasteable link deliberately strips query params. Only the token
endpoint fixes that.
- internal/playground → internal/plugin-gallery; playground-smoke → plugin-gallery-smoke;
generate-spa → generate-gallery (scripts: gallery, smoke:gallery; smoke:playground stays as alias)
- generate-gallery exports renderGallery(model, { mode }) — the dev plugin serves the same page
at / from the discovered registry (dev mode imports the dev entries, ladder popovers on);
the build writes public/index.html from the manifest (byte-identical to before)
- Docs and probes use the gallery vocabulary
- ShareButton resolves its link from where the bundle is hosted (profile-config.shareUrl ->
embedBaseUrl -> window.MAP_EMBED_BASE_URL -> the bundle's own script URL); profiles pass
tree-relative embed pages, so links work in the playground, on doxa.life and on any CDN
- Generated leaf pages try token endpoints in order (override -> configured absolute URL ->
same-origin) and only then offer the paste-once box; build config points at the canonical
https://doxa.life/api/maps/token
- Dev playground extracted from vite.config.js into internal/playground/{discover,render,dev-plugin}.mjs;
serves /assets/* like the built site so data-driven maps initialise; passes assetsBase +
shareUrl to every element; header ladder with a Start-here primer and the five kinds of
mounting files (single-element, preset-element, multi-view-element, composite-element, app-shell)
- New gates: smoke:playground (every element boots, with status text + data diagnostics),
smoke:share (Share URL resolves and reopens the same map), sync:dropin (mirror public/ into a
host checkout, never credentials), deploy:dev (build + sync)
- MapToolbar + ShareButton on every map profile; UPG-100 composite (map + prayer lists) on the
pane layout; shared --map-radius token consumed by the legend components
- docs/reference.md: profile types table, profile-config assetsBase/shareUrl/embedBaseUrl, gates