A9 leaf hints: key on the bundle's engine, not the leaf's isMap flag

rega's A9NET run found two production composite tabs — regions-dashboard/
regions-mfe and research-map-plus-grid — getting NO hints, because my first
cut gated on the leaf's isMap flag and both are isMap:false composites that
mount a map child and load the engine at runtime. Same trap the
check:leaf-engine-per-provider isMap gate fell into.

The gate now keys on whether the leaf's OWN page loads a map engine: a real
map leaf (isMap), OR a cap-4 nested-elements-mfe composite whose BUILT bundle
references an engine (builtBundleLoadsEngine). It excludes a cap-5
nested-mfe-spa shell (maps are iframe children), a genuinely map-less
composite (big-picture, stack-demo: 0 engine refs), and a plain non-map leaf
sharing a bundle with a map (connectivity-list). regions-mfe (33 engine refs)
is now hinted; research-map-plus-grid is not — its own bundle has 0 refs, the
engine loads in a lazily-fetched child (recorded gap; the deeper cause is the
upstream isMap classifier mislabelling these composites, which composes-by-tag
where the child .vue filename differs).

check:leaf-hints rebuilt to the same rule, read from the manifest (profile
type + isMap) and the built bundle, so gate and generator cannot drift. PASS
37 hinted / 27 bare; both new poisons bite (composite stripped of hints;
non-map leaf in a map bundle given a hint).
This commit is contained in:
doxa-maps 2026-09-29 08:53:48 +00:00 committed by Doxa Maps
parent fcebf2c589
commit 71f36b5079
3 changed files with 82 additions and 19 deletions

View file

@ -36,13 +36,37 @@ const ENGINE_ORIGINS = [ORIGIN(CDN.mapboxGlJs.url), ORIGIN(CDN.maplibreGlJs.url)
const STYLE_ORIGINS = ['https://tiles.openfreemap.org', ORIGIN(CDN.mapboxGlJs.url)]
const MAP_ORIGINS = new Set([...ENGINE_ORIGINS, ...STYLE_ORIGINS].filter(Boolean))
// Markers generate-tree writes into a leaf's <head> (mapPageHtml peerBlock):
const NON_MAP = 'NON-MAP leaf' // grid/card/chart-table/app-shell/composite → no peer
const MAPLIBRE_PEER = 'MapLibre peer' // provider:'maplibre' static engine
const AUTO_NOTE = 'NO STATIC ENGINE TAGS' // the 'auto' leaf: engine chosen at runtime
const HINTS = 'A9 hints'
const ENGINE_RE = /useMapInstance|useMapProvider|mapbox-gl|maplibre-gl|window\.(?:mapboxgl|maplibregl)/
// Every leaf index.html under the tree (a profile or instance page).
// The rule the generator applies (mapPageHtml → drawsMapInPage): a leaf gets A9 hints iff its OWN page
// loads a map engine — a real map leaf (`isMap`), OR a cap-4 `nested-elements-mfe` composite whose bundle
// mounts a map inline (`bundleLoadsEngine`) — and it is not a cap-5 `nested-mfe-spa` shell (its maps are
// iframe children). A plain non-map leaf that merely SHARES a bundle with a map leaf gets none. The gate
// reads the same authoritative facts: profile `type`/`isMap` from the manifest, and the built bundle. So
// the gate and the generator cannot drift.
const manifest = JSON.parse(readFileSync(join(TREE, 'manifest.json'), 'utf8'))
const engineBundles = new Map()
function bundleLoadsEngine(bundle) {
if (!engineBundles.has(bundle)) {
let hit = false
try { hit = ENGINE_RE.test(readFileSync(join(TREE, bundle, `${bundle}.js`), 'utf8')) } catch { hit = false }
engineBundles.set(bundle, hit)
}
return engineBundles.get(bundle)
}
// Expected hint state per profile, and the leaf dirs it renders to (profile page + any instance pages).
const expect = new Map() // "<bundle>/<profile>" -> boolean shouldHint
for (const b of manifest.bundles || []) {
for (const p of b.profiles || []) {
const shell = p.type === 'nested-mfe-spa'
const composite = p.type === 'nested-elements-mfe'
const shouldHint = (!!p.isMap || (composite && bundleLoadsEngine(b.bundle))) && !shell
expect.set(`${b.bundle}/${p.profile}`, shouldHint)
}
}
// Every leaf index.html under the tree, mapped back to its "<bundle>/<profile>".
function leaves(dir, out = []) {
for (const e of readdirSync(dir)) {
const p = join(dir, e)
@ -51,32 +75,36 @@ function leaves(dir, out = []) {
}
return out
}
// rel is "<bundle>/<profile>[/<instance>]"; the profile key is the first two segments.
const keyOf = (rel) => rel.split('/').slice(0, 2).join('/')
const fails = []
let maps = 0
let nonMaps = 0
let hinted = 0
let bare = 0
let unclassified = 0
for (const file of leaves(TREE)) {
const html = readFileSync(file, 'utf8')
const rel = file.slice(TREE.length + 1)
const isMapLeaf = html.includes(MAPLIBRE_PEER) || html.includes(AUTO_NOTE)
const isNonMap = html.includes(NON_MAP)
// Engine/style-origin preconnects and engine preloads present in this page.
const key = keyOf(rel)
if (!expect.has(key)) { unclassified += 1; continue } // a non-profile page (e.g. tree root) — not our subject
const shouldHint = expect.get(key)
const hasHints = html.includes(HINTS)
const preconnects = [...html.matchAll(/<link rel="preconnect" href="([^"]+)"/g)].map((m) => m[1])
const enginePreload = /<link rel="preload" as="script" href="([^"]+maplibre-gl[^"]*|[^"]+mapbox-gl[^"]*)"/.test(html)
const mapHint = preconnects.some((o) => MAP_ORIGINS.has(o)) || enginePreload
if (isMapLeaf) {
maps += 1
if (!html.includes(HINTS)) fails.push(`${rel}: MAP leaf with no A9 hints (expected preconnect + preload)`)
// Every preconnect on a map leaf must name an origin the code uses (no stale host).
if (shouldHint) {
if (!hasHints) fails.push(`${rel}: loads a map engine in its own page but has NO A9 hints`)
else hinted += 1
for (const o of preconnects) {
if (!MAP_ORIGINS.has(o) && !/^https:\/\/[a-z0-9.-]+$/i.test(o)) fails.push(`${rel}: malformed preconnect origin ${o}`)
}
} else if (isNonMap) {
nonMaps += 1
if (mapHint) fails.push(`${rel}: NON-MAP leaf carries a map-engine hint (a non-map leaf loads no peer)`)
} else {
if (mapHint || hasHints) fails.push(`${rel}: does not load a map engine in its own page, yet carries a map-engine hint`)
else bare += 1
}
}
const maps = hinted, nonMaps = bare
if (maps === 0) fails.push('no map leaves found in the tree: the classifier or the tree is wrong')

View file

@ -881,8 +881,17 @@ function resourceHints({ isMapLibre, config, apiBaseUrl, tokenUrlExpr, bakedToke
return lines.length ? ` <!-- A9 hints: the map's origins, so DNS/TLS + the engine start with the bundle, not after it (dx-base/a9-leaf-hints; engine derived from the token ladder). -->\n${lines.join('\n')}\n` : ''
}
// Does a bundle's BUILT .js reference a map engine? The directly verifiable signal for A9 leaf hints
// (below), read from public/<tree>/<bundle>/<bundle>.js with the same regex the source classifier uses.
// This catches a composite whose own bundle mounts a map inline even when its profile is isMap:false;
// it does not catch one whose engine loads only in a lazily-fetched child bundle (see the hint comment).
function builtBundleLoadsEngine(bundleName) {
try { return MAP_ENGINE_RE.test(readFileSync(join(DEFAULT_ROOT, bundleName, `${bundleName}.js`), 'utf8')) }
catch { return false }
}
function mapPageHtml(opts) {
const { title, tag, config, jsSrc, apiBaseUrl, tokenUrlExpr, bakedToken, integrity, shellTokenFree, isMap, scrollPage, isComposite } = opts
const { title, tag, config, jsSrc, apiBaseUrl, tokenUrlExpr, bakedToken, integrity, shellTokenFree, isMap, scrollPage, isComposite, bundleLoadsEngine } = opts
const apiScript = apiBaseUrl
? `\n <script>window.MAP_APP_API_URL = ${JSON.stringify(apiBaseUrl)};</script>`
: ''
@ -924,7 +933,24 @@ function mapPageHtml(opts) {
// hardcoded: preconnect the token-implied engine + the style host + the data origin, and — only
// where the leaf emits no engine <script> today (the 'auto' leaf) — preload the engine, its URL,
// integrity and crossorigin from library/constants/cdn.js so it cannot drift from the runtime tag.
const hintBlock = shellOnly ? '' : resourceHints({ isMapLibre, config, apiBaseUrl, tokenUrlExpr, bakedToken })
// WHO GETS HINTS: any page whose OWN bundle loads a map engine — the directly verifiable signal
// (`bundleLoadsEngine`, an engine reference in the built .js), NOT the leaf's `isMap` flag. rega's A9
// net run found regions-mfe (isMap:false, but 33 engine refs in its bundle) opening cdn.jsdelivr.net +
// tiles.openfreemap.org from the top page and getting no hints — my first cut keyed on isMap and missed
// it, the same trap check:leaf-engine-per-provider's isMap gate fell into. Keying on the built bundle
// hints regions-mfe and excludes the genuinely map-less composites (big-picture, stack-demo: 0 engine
// refs), with no false positives. A cap-5 nested-mfe-spa shell (shellTokenFree) is excluded even if it
// somehow references an engine: its nested maps render in their OWN iframe leaves, which carry hints.
// KNOWN GAP: a composite whose engine loads only inside a LAZILY-fetched child bundle (research-map-plus-grid
// composes doxa-research-map; its own bundle has 0 engine refs) is not hinted — the top page's own bytes
// don't name the engine. Recorded in the A9 report; the deeper fix is the upstream isMap classifier,
// which mislabels these composites isMap:false (composes-by-tag where the child .vue filename differs).
// A plain non-map leaf (a list/card) can SHARE a bundle with a map leaf (connectivity-list rides the
// cities-internet bundle, which loads an engine for connectivity-map) — so the bundle signal alone
// over-hints it. The engine only mounts in-page for a real map leaf (drawsMap) or a cap-4 composite
// (isComposite) that stacks a map child; a non-map, non-composite leaf never opens the engine itself.
const drawsMapInPage = (drawsMap || (isComposite && bundleLoadsEngine)) && !shellTokenFree
const hintBlock = drawsMapInPage ? resourceHints({ isMapLibre, config, apiBaseUrl, tokenUrlExpr, bakedToken }) : ''
const peerBlock = shellOnly
? `<!-- NON-MAP leaf (app-shell / composite / grid / card / chart-table): NO map peer.
This element draws no map tiles, so it needs no Mapbox/MapLibre engine and MUST NOT
@ -1231,6 +1257,8 @@ function profileHtml(b, profile, tokenUrlExpr, bakedToken) {
// Whether this leaf actually draws map tiles (computed in scanBundles → isMapLeaf). A
// non-map leaf renders token-free with no map peer; only a real map runs the token ladder.
isMap: profile.isMap,
// Whether this bundle's OWN built .js loads a map engine — the A9-leaf-hint signal (mapPageHtml).
bundleLoadsEngine: builtBundleLoadsEngine(b.bundle),
// A cap-4 composite (nested-elements-mfe) is a StackLayout SCROLL-STACK: its leaf must let the
// DOCUMENT own the scroll (height:auto, no position:fixed) so the page grows and a reader scrolls
// PAST one section to the next. A fixed-viewport leaf traps a 100vh dashboard and nothing scrolls
@ -1265,6 +1293,8 @@ function instanceHtml(b, profile, inst, tokenUrlExpr, bakedToken) {
// Instance pages inherit the profile's map-ness: a preset of a non-map leaf is still a
// non-map (token-free, no peer); a preset of a real map still runs the token ladder.
isMap: profile.isMap,
// Same bundle as the profile page, so the same engine signal (A9-leaf-hint, mapPageHtml).
bundleLoadsEngine: builtBundleLoadsEngine(b.bundle),
// A preset of a cap-4 composite scroll-stack is still a scroll page (see profileHtml).
scrollPage: profile.type === 'nested-elements-mfe',
})

View file

@ -11,6 +11,11 @@
<script>window.MAP_OWNS_PAGE = true;</script>
<title>regions-mfe · regions-dashboard</title>
<link rel="icon" href="data:,">
<!-- A9 hints: the map's origins, so DNS/TLS + the engine start with the bundle, not after it (dx-base/a9-leaf-hints; engine derived from the token ladder). -->
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link rel="preconnect" href="https://tiles.openfreemap.org" crossorigin />
<link rel="preconnect" href="https://pray.doxa.life" crossorigin />
<link rel="preload" as="script" href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js" integrity="sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp" crossorigin="anonymous" />
<!-- NON-MAP leaf (app-shell / composite / grid / card / chart-table): NO map peer.
This element draws no map tiles, so it needs no Mapbox/MapLibre engine and MUST NOT
gate on a token. Any nested MAP (in a composite/shell) renders in its own iframe leaf,