diff --git a/internal/check-shot-fidelity.mjs b/internal/check-shot-fidelity.mjs index 090e635e3..85bd306cc 100644 --- a/internal/check-shot-fidelity.mjs +++ b/internal/check-shot-fidelity.mjs @@ -110,9 +110,59 @@ function restrictedTilesetFiles() { return out } +/** + * Map bundles whose BASEMAP comes only from Mapbox tiles — i.e. that render a blank void when + * the token cannot fetch tilesets from this origin. + * + * ★ THE THIRD FIDELITY CLASS, and the widest (measured 2026-09-21 by looking at cap2-*.png). + * All three doxa-simple-map shots show data dots floating on an empty dark field: no coastlines, + * no country fills, no labels. `bun run shots` reported OK for every one — the page loads, the + * element mounts, the legend and controls paint, the data layer is correct. + * + * ★ WHY IT IS INVISIBLE TO EVERY OTHER SIGNAL: the Mapbox STYLE DOCUMENT returns 200 from every + * origin, so the map genuinely initialises. Only the TILESETS are restricted — + * mapbox-streets-v8 (the basemap itself), mapbox-bathymetry-v2, country-boundaries-v1 all return + * 403 off doxa.life and 200 with its referer. A booted map showing its own UI and its own data + * looks like a working map; only the GEOGRAPHY is absent. + * + * ★ AND IT EXPLAINS A NEAR-MISDIAGNOSIS worth keeping: cap4-upg100-list.png paints coastlines and + * country fills perfectly, which made the blank cap-2 maps look like a per-bundle bug. It is not — + * my-upg-100-list SHIPS ITS OWN world-adm0.geojson and draws geography from self-hosted data + * instead of tiles. Three bundles do that and survive; the other eight do not. + * + * NOTE the dark basemap in those shots is DELIBERATE, not a theme violation: useMapTheme's + * lookForStrategy() keeps prayer/adoption/engagement on the night preset by design ("light and + * darkness"), and only affinity/region/language go light. The missing TILES are the defect. + */ +function tileDependentMapBundles() { + const dir = join(ROOT, 'plugin-designs') + if (!existsSync(dir)) return [] + const out = [] + for (const e of readdirSync(dir, { withFileTypes: true })) { + if (!e.isDirectory() || /^[_.]/.test(e.name)) continue + const bdir = join(dir, e.name) + let usesMap = false, selfHosted = false + const scan = (d) => { + for (const f of readdirSync(d, { withFileTypes: true })) { + if (/^(node_modules|[_.])/.test(f.name)) continue + const p = join(d, f.name) + if (f.isDirectory()) { scan(p); continue } + if (!/\.(vue|js|mjs)$/.test(f.name)) continue + const src = readFileSync(p, 'utf8') + if (/useMapInstance|mapbox-gl|bootStyle/.test(src)) usesMap = true + if (/adm0/.test(src)) selfHosted = true + } + } + scan(bdir) + if (usesMap && !selfHosted) out.push(e.name) + } + return out +} + const bundles = emojiBundles() const fonts = emojiFontCount() const tilesetFiles = restrictedTilesetFiles() +const tileBundles = tileDependentMapBundles() const findings = [] // every condition reports; no early exit hides a later one console.log(`SHOT-FIDELITY: ${bundles.length} bundle(s) render emoji; emoji fonts on this box: ` + @@ -124,6 +174,23 @@ if (fonts === null) { console.log(' i fc-list not available — cannot verify glyph coverage. Screenshots UNASSESSED.') } +if (tileBundles.length) { + findings.push([ + '', + 'SHOT-FIDELITY: these map bundles draw their GEOGRAPHY from Mapbox tiles only, so they shoot', + 'as a blank void when the token cannot fetch tilesets from this origin:', + ` ${tileBundles.join(', ')}`, + '', + ' Measured: the STYLE document returns 200 everywhere (so the map boots, the legend and', + ' controls paint, the data layer is correct) while mapbox-streets-v8 / bathymetry-v2 /', + ' country-boundaries-v1 all return 403 off doxa.life. A booted map showing its own UI and', + ' its own data looks like a working map — only the coastlines and country fills are gone.', + '', + ' Bundles that ship their own world-adm0.geojson are UNAFFECTED, which is why one cap-4 shot', + ' looks perfect beside four blank ones. That contrast is a red herring, not a per-bundle bug.', + ].join('\n')) +} + if (tilesetFiles.length) { findings.push([ '', @@ -132,8 +199,8 @@ if (tilesetFiles.length) { '', ' The shared pk. token is URL-restricted. Measured against country-boundaries-v1:', ' Referer doxa.life -> 200 (3/3) · 127.0.0.1 -> 403 · no referer -> 403', - ' The STYLE endpoint returns 200 from every origin, so MOST maps paint here and only the', - ' tileset-backed choropleth blanks — which is why this looked like "that surface has no map".', + ' The STYLE endpoint returns 200 from every origin, so every map BOOTS and only the TILES are', + ' missing — which is why this first looked like "that surface has no map".', '', ' ★ It is NOT a missing tileset scope (re-tested with the doxa.life referer, which passes)', ' and NOT a code defect. Either shoot these panes from an allowed origin, or add the shot',