The output tree's name has always been configurable (vite.doxa-maps-build-config.json "name"),
but 61 tooling files ignored it and hardcoded 'doxa-maps' (181 literals), so a rename would have
moved the build while the gates kept reading the old folder.
- internal/_tree-name.mjs reads it once (validated as one URL-safe path segment; falls back to the
historical name) and exports TREE_NAME / TREE_NAME_RE; `node internal/_tree-name.mjs` prints it
for shell scripts.
- A codemod rewrote 171 literals in internal/ to TREE_NAME; the regex literals, messages with
backticks, atomic-public-swap.sh (fails loudly if the name cannot be read), build-lock.sh and
vite.config.js (its duplicate readMfeName is gone) were done by hand.
- The name is unchanged: old and new tooling produce all 1408 files under public/ byte for byte.
Code lines still naming the tree in internal/ + vite.config.js: 2, both intended (the helper's
fallback, the private-repo-name pattern in public-hygiene-check).
This renames nothing visible; the eventual rename becomes a one-line config change.
The mobile pair reported "composite did not mount (map=false, cards=24)" on a
tree verified SETTLED both before and after the run, so it read as a real
defect. It was not. A direct browser probe of the same leaf found the composite
completely healthy: both children mounted, a 1440x856 canvas PAINTED, 0 console
errors, 0 failed requests, body 3168px.
Two causes, and the asymmetry with the desktop pair is what hid them.
SHADOW DOM DOES NOT PIERCE. The gate used document.querySelector
("doxa-research-map"), a single-level query. The desktop gate's
document.querySelector("dashboard-plus-grid") works only because that element IS
the top-level custom element on its own leaf. Here <doxa-research-map> is nested
inside the composite's shadow root, where a top-level selector cannot reach it.
The harness already carries __walk(), a recursive shadowRoot walker, and simply
was not using it on that line.
THREE SECONDS IS NOT LONG ENOUGH. The direct probe needed ~22s before the nested
map had mounted and painted. A composite that lazily injects a child bundle's
script, waits for customElements, then initialises a GL map does not finish in
3s here. Now it polls up to 30s for both halves.
The failure message also reports the custom tags it DID find when it gives up,
because "map=false" alone cannot distinguish "never mounted" from "mounted
somewhere this query cannot reach" -- which is exactly the distinction that cost
a tick.
A gate that cries wolf on a healthy surface gets ignored, and then it cannot
report the real thing.
Verified: verify:interop now PASSES 4/4. Mobile grid search "India" -> Total:
941; map mounts independently. Desktop unchanged -- facet -> "Showing 1,309",
and row selection still moves the camera 28.000,18.000@1.45 ->
73.666,13.162@2.48. check:mjs-parse passes.
THE RAMP. The Driver reported that 60-149 reads as the same red as the 150+
heartbeat bin. The old ramp was mathematically even and he was still right --
that tension was the finding, not a contradiction. Measured in plain sRGB
distance the top step (60.0) looked like the LARGEST in the ramp; that
measurement was lying, because RGB distance is not perceptual. Re-measured as
deltaE the old steps were 15.5 / 15.8 / 16.7 / 17.7 -- nearly flat, while chroma
collapses at the dark end so hue and saturation stop carrying information there.
The new ramp deliberately ACCELERATES toward the dark end: 14.1 / 17.9 / 21.4 /
24.9. Top-bin separation 17.67 -> 24.94, a 41% improvement on exactly the pair
he named, and total lightness range widens from 37.2 to 49.0 L*.
A named greyscale ramp ships alongside it, grey to black, as he asked ("just in
case i need to swap from red to black scale"). Its steps measure 22.8 / 22.4 /
22.4 / 22.8 -- near-perfectly even, every step well above the ~10 threshold for
clear distinguishability. It is an OPTION, selected by name; the red ramp
remains the default, so no prior decision is silently reversed.
Bin EDGES are untouched -- check:heartbeat passes, which replays the bin
assignment and fails if a default moves.
THE INTEROP GATE. verify:interop drives the actual compiled cap-4 composites in
a real browser against live data, closing the gap verify:gate1 leaves open. On
its first run it PASSED both desktop directions: a facet-rail click narrowed the
grid to "Showing 1,309", and selecting three rows moved the camera from
28.000,18.000@1.45 to 73.666,13.162@2.48 -- read back off the live map, not
inferred. It asserts the grid's live count line rather than card count, because
card count is a proven false-negative (always 24).
It ships with verify:interop:self-test, a two-mode negative test -- grid-prop
freeze and camera freeze -- each required to fire its own combination-named
failure. A harness that has only ever reported PASS is unproven.
Gates: sfc-parse, template-literal, one-palette, synced-css, mjs-parse,
heartbeat, doc-scripts all pass.