From c5c01deada04890bb31b2fe7f3b030383d5146b2 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Mon, 28 Sep 2026 23:25:51 +0000 Subject: [PATCH] Delete 7 unreachable library components, and gate the thing a census cannot see MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seven components with no importer, removed. The interesting part is not the deletion, it is that an import census was NOT sufficient to authorise it, and two other agents proved that rather than me. WHAT AN IMPORT CENSUS MISSED. internal/check-gospel-set-charts.mjs iterated ['BoundedSet.vue', 'CenteredSetTarget.vue'] as literal STRINGS and read their props off disk. No import, so no census and no build would have warned; deleting them would have turned a gate red on main with nothing to explain it. gospel found that in their own file after I asked, and retargeted the arm at the components that actually render (GiBoundedSetMap / GiCenteredSetMap, 2d2e853a7). Their retarget also fixed a quieter defect: the seed guarantee had been asserted against two files NOTHING renders, so it would have kept passing while the shipping component grew a seed prop. I did not take either agent's word for it. Verified gospel's retarget on their sha and ran their gate green before deleting; verified upg100's structural claim that packet-render.js cannot mount a component at all — 0 resolveComponent, 0 defineAsyncComponent, 0 dynamic import(), with the same probe returning a hit elsewhere in library so an empty result is a real absence. DELETED: charts/LanguageTreeChart.vue, components/charts/VisionFunnel.vue, components/charts/BoundedSet.vue, components/charts/CenteredSetTarget.vue, components/legends/ListSelectorLegend.vue, components/dashboards/DashboardBreadcrumb.vue, composables/useMapSelection.js. NOT DELETED, and worth stating because the names collide: the lowercase boundedSet.js / centeredSet.js HELPERS are load-bearing — packet-render.js and GiBoundedSetMap import them directly, and check-gospel-set-charts still does too. Also left alone: colors/resource.js (a colour STRATEGY resolved by MODE NAME through _registry.js, 'resource' appearing in mapStore.js and GiResources.vue) and layouts/app-wrapper-material/nav.js (115 mentions). Both look dead to an import census and would break a working map. check:no-dead-library scans for a filename as a STRING across internal/, library/ and plugin-designs/, and: · DISTINGUISHES A READ FROM A COMMENT (gospel's ask). check-packet-variants.mjs discusses CenteredSetTarget.vue in prose while asserting against JSON. A comment hit is reported, never fatal — a gate that cannot tell the two apart cries wolf once and is then ignored. It currently reports 4 such mentions. · IS TWO-SIDED (upg100's ask). It also asserts a KNOWN-PRESENT dynamic reference is found — LegendDesktop lazy-loads LegendMobile through import(). Mutation-tested: blinding the scanner makes it FAIL on that control instead of passing with "no references found", which is what a one-sided version would have done. · ignores a longer identifier that merely contains the name, so GiBoundedSet.vue is not read as a reference to BoundedSet.vue. Both arms mutation-tested and every mutated file restored byte-identical. Registered in all three places and verified by re-reading the parsed package.json — the suite insert silently hit the scripts KEY instead of the suite string on the first attempt, which verifying caught and trusting would not have. --- internal/check-no-dead-library.mjs | 88 ++++++++ library/charts/LanguageTreeChart.vue | 160 --------------- library/components/charts/BoundedSet.vue | 69 ------- .../components/charts/CenteredSetTarget.vue | 122 ----------- library/components/charts/VisionFunnel.vue | 145 ------------- .../dashboards/DashboardBreadcrumb.vue | 70 ------- .../components/legends/ListSelectorLegend.vue | 92 --------- library/composables/useMapSelection.js | 192 ------------------ package.json | 4 +- 9 files changed, 91 insertions(+), 851 deletions(-) create mode 100644 internal/check-no-dead-library.mjs delete mode 100644 library/charts/LanguageTreeChart.vue delete mode 100644 library/components/charts/BoundedSet.vue delete mode 100644 library/components/charts/CenteredSetTarget.vue delete mode 100644 library/components/charts/VisionFunnel.vue delete mode 100644 library/components/dashboards/DashboardBreadcrumb.vue delete mode 100644 library/components/legends/ListSelectorLegend.vue delete mode 100644 library/composables/useMapSelection.js diff --git a/internal/check-no-dead-library.mjs b/internal/check-no-dead-library.mjs new file mode 100644 index 000000000..62734d58c --- /dev/null +++ b/internal/check-no-dead-library.mjs @@ -0,0 +1,88 @@ +/** + * check-no-dead-library.mjs — a library module must be reachable, and the scanner must prove it works. + * + * ★ WHY A NAME-GREP IS NOT ENOUGH, and why this file exists in this shape. Seven library components + * were deleted as unreachable. Two of them were NOT reachable by import and still could not be + * deleted safely: internal/check-gospel-set-charts.mjs iterated ['BoundedSet.vue', + * 'CenteredSetTarget.vue'] AS STRINGS and read their props off disk. No import, no build error — + * deleting them would have turned a gate red on main with nothing to explain it. So this scans for a + * filename appearing as a STRING anywhere in internal/ and library/, not only in import statements. + * + * ★ IT DISTINGUISHES A READ FROM A MENTION (gospel's ask). check-packet-variants.mjs discusses + * CenteredSetTarget.vue in prose while asserting against a JSON file. A gate that cannot tell a read + * from a comment cries wolf once and is then ignored, so a comment-only hit is REPORTED, never fatal. + * + * ★ IT IS TWO-SIDED (upg100's ask, learned the same night). A gate that only asserts "no references + * found" passes identically when the scanner is broken and when the tree is clean. So it also + * asserts a KNOWN-PRESENT reference is found: LegendDesktop.vue lazy-loads LegendMobile.vue through + * a dynamic import(). If that positive control ever stops being found, the scanner is lying and the + * gate fails — green here means "the scanner works AND found nothing". + */ +import { readdirSync, readFileSync, existsSync } from 'node:fs' +import { join } from 'node:path' + +const ROOT = new URL('../', import.meta.url).pathname +const SCAN = ['internal', 'library', 'plugin-designs'] + +const files = [] +const walk = (d) => { + if (!existsSync(d)) return + for (const e of readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue + const p = join(d, e.name) + if (e.isDirectory()) walk(p) + else if (/\.(vue|js|mjs)$/.test(e.name)) files.push(p) + } +} +for (const d of SCAN) walk(join(ROOT, d)) +const text = new Map(files.map((p) => [p, readFileSync(p, 'utf8')])) + +/** Every hit of `name` outside its own file, split into reads and comment-only mentions. */ +function hitsFor(name, selfPath) { + const reads = [], mentions = [] + for (const [p, src] of text) { + if (p.endsWith(selfPath)) continue + // This gate NAMES every deleted module in its own DELETED list, so it would report itself + // as the reader of all seven. Excluding it is not a loophole: the positive control below + // is what proves the scanner still sees real references. + if (p.endsWith('internal/check-no-dead-library.mjs')) continue + for (const line of src.split('\n')) { + if (!line.includes(name)) continue + // A longer identifier that merely CONTAINS the name is not a reference to it + // (GiBoundedSet.vue contains BoundedSet.vue). + const idx = line.indexOf(name) + const before = idx > 0 ? line[idx - 1] : '' + if (/[A-Za-z0-9_]/.test(before)) continue + const trimmed = line.trim() + if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) mentions.push(`${p.split('/').slice(-2).join('/')}`) + else reads.push(`${p.split('/').slice(-2).join('/')}`) + } + } + return { reads, mentions } +} + +const fail = [] + +// ── THE POSITIVE CONTROL. A known dynamic reference the scanner must find. ───────────────────── +const control = hitsFor('LegendMobile.vue', 'components/legends/LegendMobile.vue') +if (control.reads.length === 0) { + fail.push('POSITIVE CONTROL FAILED: LegendMobile.vue is lazy-loaded by LegendDesktop.vue and the scanner did not find it — the scanner is broken, so any "no references" result below is meaningless') +} + +// ── THE DELETED SET must stay deleted and unreferenced. ─────────────────────────────────────── +const DELETED = ['LanguageTreeChart.vue', 'VisionFunnel.vue', 'useMapSelection.js', 'ListSelectorLegend.vue', + 'BoundedSet.vue', 'CenteredSetTarget.vue', 'DashboardBreadcrumb.vue'] +const notes = [] +for (const name of DELETED) { + const { reads, mentions } = hitsFor(name, `__deleted__/${name}`) + if (reads.length) fail.push(`${name}: deleted, but READ by ${[...new Set(reads)].join(', ')}`) + if (mentions.length) notes.push(`${name}: mentioned in a comment by ${[...new Set(mentions)].join(', ')} (harmless)`) +} + +if (fail.length) { + console.error('NO-DEAD-LIBRARY: FAIL') + for (const f of fail) console.error(` ✗ ${f}`) + process.exit(1) +} +console.log(`NO-DEAD-LIBRARY: PASS — positive control found (${control.reads.length} read of LegendMobile.vue), ${DELETED.length} deleted module(s) with no reads.`) +for (const n of notes) console.log(` · ${n}`) diff --git a/library/charts/LanguageTreeChart.vue b/library/charts/LanguageTreeChart.vue deleted file mode 100644 index 539117938..000000000 --- a/library/charts/LanguageTreeChart.vue +++ /dev/null @@ -1,160 +0,0 @@ - - - - - diff --git a/library/components/charts/BoundedSet.vue b/library/components/charts/BoundedSet.vue deleted file mode 100644 index cbcf2e8e4..000000000 --- a/library/components/charts/BoundedSet.vue +++ /dev/null @@ -1,69 +0,0 @@ - - - - - diff --git a/library/components/charts/CenteredSetTarget.vue b/library/components/charts/CenteredSetTarget.vue deleted file mode 100644 index 18052ca56..000000000 --- a/library/components/charts/CenteredSetTarget.vue +++ /dev/null @@ -1,122 +0,0 @@ - - - - - diff --git a/library/components/charts/VisionFunnel.vue b/library/components/charts/VisionFunnel.vue deleted file mode 100644 index 34ad5a807..000000000 --- a/library/components/charts/VisionFunnel.vue +++ /dev/null @@ -1,145 +0,0 @@ - - - - - diff --git a/library/components/dashboards/DashboardBreadcrumb.vue b/library/components/dashboards/DashboardBreadcrumb.vue deleted file mode 100644 index 32f990f31..000000000 --- a/library/components/dashboards/DashboardBreadcrumb.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - diff --git a/library/components/legends/ListSelectorLegend.vue b/library/components/legends/ListSelectorLegend.vue deleted file mode 100644 index 2a9d592dc..000000000 --- a/library/components/legends/ListSelectorLegend.vue +++ /dev/null @@ -1,92 +0,0 @@ - - - - - diff --git a/library/composables/useMapSelection.js b/library/composables/useMapSelection.js deleted file mode 100644 index d8c0fdd46..000000000 --- a/library/composables/useMapSelection.js +++ /dev/null @@ -1,192 +0,0 @@ -// useMapSelection.js - Map Selection Composable -// Handles region/family selection and visibility updates -// Vue 3 Composition API - ES Module with NPM packages - -// Import Vue functions from NPM package -import { ref, computed } from 'vue'; - -// Import zoom utilities -import { getCircleRadiusInterpolation } from '../constants/zoom.js'; - -/** - * useMapSelection composable - manages map selection state and visibility - * - * @param {Object} options - Configuration options - * @param {Function} options.getMap - Function that returns the map instance - * @param {string} options.mapId - Unique map identifier for logging - * @param {Object} options.mapStore - Pinia mapStore for shared state - * @param {Function} options.addFamilyConnectionLines - Function to add connection lines - * @param {Function} options.removeFamilyConnectionLines - Function to remove connection lines - * @param {Function} options.getRegionIsoMap - Returns region-slug → array of iso_3166_1_alpha_3 codes - * - * @returns {Object} Selection management functions and state - */ -export function useMapSelection(options = {}) { - const { - getMap = () => null, - mapId = 'unknown', - mapStore = null, - addFamilyConnectionLines = () => {}, - removeFamilyConnectionLines = () => {}, - // regionIsoMap is built in useMapLayers.addRegionsLayer (region slug → - // array of alpha-3 ISO codes). The regions-fill features come from the - // country-boundaries-v1 tileset which exposes iso_3166_1_alpha_3 (NOT a - // `region` property), so selection highlighting must match on ISO. - getRegionIsoMap = () => ({}) - } = options; - - // Local state for color scheme - const colorScheme = ref('doxa-regions'); - - // Computed from mapStore if available - const selectedRegion = computed(() => mapStore?.selectedRegion || null); - const selectedFamily = computed(() => mapStore?.selectedFamily || null); - - /** - * Update region layer visibility based on selection - * Highlights selected region, fades others - */ - function updateRegionVisibility() { - const map = getMap(); - if (!map || !map.getLayer('regions-fill')) { - return; - } - - const region = selectedRegion.value; - - if (region) { - // Resolve the selected region's ISO alpha-3 codes. selectedRegion may - // arrive as a display label ('Asia') while regionIsoMap is keyed by - // slug ('asia'); normalize (lowercase, strip non-alpha) for a robust - // match without re-keying the map. - const rMap = getRegionIsoMap() || {}; - const norm = (s) => String(s).toLowerCase().replace(/[^a-z]/g, ''); - let isos = rMap[region] || []; - if (!isos.length) { - const target = norm(region); - for (const [k, v] of Object.entries(rMap)) { - if (norm(k) === target) { isos = v; break; } - } - } - - // Highlight selected region (0.85, vibrant), fade others (0.15, dim). - // Match on iso_3166_1_alpha_3 — the country-boundaries-v1 tileset has - // no `region` property. - map.setPaintProperty('regions-fill', 'fill-opacity', [ - 'case', - ['in', ['get', 'iso_3166_1_alpha_3'], ['literal', isos]], - 0.85, - 0.15 - ]); - } else { - // Deselect → restore the vibrant base for all regions. - const opacity = colorScheme.value === 'none' ? 0.2 : 0.7; - map.setPaintProperty('regions-fill', 'fill-opacity', opacity); - } - } - - /** - * Update pin opacity based on region selection - * Dims pins outside selected region - */ - function updatePinOpacityByRegion() { - const map = getMap(); - if (!map || !map.getLayer('language-family-pins')) { - return; - } - - const region = selectedRegion.value; - - // If no region selected, show all pins at normal opacity - if (!region) { - map.setPaintProperty('language-family-pins', 'circle-opacity', 0.9); - return; - } - - // Dim pins based on PHYSICAL region location (not cluster membership) - map.setPaintProperty('language-family-pins', 'circle-opacity', [ - 'case', - ['==', ['get', 'doxaRegion'], region], - 0.9, // In selected region: bright - 0.2 // Outside selected region: dimmed - ]); - - } - - /** - * Update language family layer visibility based on selection - * Highlights selected family, fades others - * - * @param {Array} peopleGroups - People groups data (for connection lines) - */ - function updateLanguageFamilyVisibility(peopleGroups = []) { - const map = getMap(); - if (!map || !map.getLayer('language-family-pins')) { - return; - } - - const family = selectedFamily.value; - - if (family) { - // Highlight selected family, fade others - map.setPaintProperty('language-family-pins', 'circle-opacity', [ - 'case', - ['==', ['get', 'languageFamily'], family], - 1, - 0.15 - ]); - - // Keep standard radius (no size change on selection) - // NOTE: Cannot use zoom expressions inside case statements in Mapbox - - // Add connecting lines for selected family (tapestry effect) - addFamilyConnectionLines(family, peopleGroups); - - } else { - // Show all at normal opacity and size - map.setPaintProperty('language-family-pins', 'circle-opacity', 0.9); - map.setPaintProperty('language-family-pins', 'circle-radius', - getCircleRadiusInterpolation('standard') - ); - - // Remove connection lines - removeFamilyConnectionLines(); - - } - } - - /** - * Set the color scheme for region display - */ - function setColorScheme(scheme) { - colorScheme.value = scheme; - } - - /** - * Clear all selections via mapStore - */ - function clearSelections() { - if (mapStore) { - mapStore.selectRegion(null); - mapStore.selectFamily(null); - } - } - - return { - // State - selectedRegion, - selectedFamily, - colorScheme, - - // Visibility updates - updateRegionVisibility, - updatePinOpacityByRegion, - updateLanguageFamilyVisibility, - - // Configuration - setColorScheme, - clearSelections - }; -} - -// ES Module export diff --git a/package.json b/package.json index cdd7bfbca..c56d0aea2 100644 --- a/package.json +++ b/package.json @@ -185,6 +185,7 @@ "check:no-credentials-omit:self-test": "node internal/check-no-credentials-omit.mjs --self-test", "check:no-cross-plugin-imports": "node internal/check-no-cross-plugin-imports.mjs", "check:no-cross-plugin-imports:self-test": "node internal/check-no-cross-plugin-imports.mjs --self-test", + "check:no-dead-library": "node internal/check-no-dead-library.mjs", "check:no-eval": "node internal/check-no-eval.mjs", "check:no-free-identifiers": "node plugin-designs/research-map-plus-grid/scripts/check-no-free-identifiers.mjs", "check:no-orphan-gates": "node internal/check-no-orphan-gates.mjs", @@ -303,7 +304,7 @@ "dev": "bun x vite", "fix:sri": "node internal/fix-sri.mjs", "gallery": "node internal/generate-gallery.mjs", - "gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:poster-print-fallback check:country-names check:reduced-motion", + "gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:poster-print-fallback check:country-names check:reduced-motion check:no-dead-library", "gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin", "gen:iso3-to-alpha2": "node library/scripts/generate-iso3-to-alpha2.mjs", "internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs", @@ -464,6 +465,7 @@ "check:no-credentials-omit": "no data fetch in library/ or plugin-designs/ uses credentials:'omit': behind a login gate a same-origin request then drops the session and is refused (the Research Gallery's 'Could not load Big Picture'). 'same-origin' never sends credentials cross-origin. A deliberate omit says so on its line: // credentials-omit: .", "check:no-credentials-omit:self-test": "a planted omit is caught; prose and an annotated site are not.", "check:no-cross-plugin-imports": "no plugin imports from another plugin's folder (src/, utils, data) by a relative path; shared code goes through library/ (@map/\u2026). A composite may mount another bundle's PUBLIC profile .vue. --self-test plants a crossing import.", + "check:no-dead-library": "a deleted library module must stay unreferenced, and the scanner must prove it still works. Scans for a filename as a STRING across internal/, library/ and plugin-designs/ — not just imports — because check-gospel-set-charts.mjs read two components by filename off disk with no import anywhere, so a census and a build would both have missed it. Distinguishes a READ (fatal) from a comment MENTION (reported, harmless). TWO-SIDED: also asserts a known dynamic reference IS found (LegendDesktop lazy-loads LegendMobile), so green means the scanner works AND found nothing, rather than merely found nothing.", "check:no-eval": "no built bundle contains new Function or eval (a strict CSP without 'unsafe-eval' must work), and vue-i18n in JIT mode renders real catalogue messages in en and ar with eval blocked. Headless.", "check:no-free-identifiers": "every name a .vue script uses is imported, declared, a parameter or a known global (scope-aware, across plugin-designs and library). A free identifier compiles and then throws at runtime in exactly the path nobody clicked.", "check:no-silent-engine": "A profile must NEVER fall back to a different map engine than its provider asked for. `engineGlobal(provider) || window.mapboxgl` looks defensive and is not: engineGlobal already returns the right global for the requested provider (useMapInstance.js:115 falls back to mapbox's NAME only for an UNKNOWN provider, never a missing engine), so the || fires in exactly one case — the page asked for maplibre and maplibre failed to load — and hands back the TOKEN-GATED engine to a page that asked for the KEYLESS one. Worse, accessToken is gated on !isMaplibre, so the substitute arrives WITHOUT a token: 401s on every tile instead of a legible failure. Found by regions' C3 read 2026-09-26; 2 offenders remained (cities-internet, gospel-resources) after my-upg-100-list was fixed. NOT a duplicate of check:leaf-engine-per-provider / check:provider-switch / check:auto-engine — all three assert the HAPPY path (what loads) and this bug only appears when something does NOT load, so all three were green while it existed. A SOURCE check on purpose: reproducing it live means serving a leaf with MapLibre deliberately blocked, spending a browser slot on what a grep decides. Exits 75 when no file calls engineGlobal — 'found nothing' is not 'found nothing wrong'. Run :self-test to prove the pattern still matches.",