diff --git a/plugin-designs/research-map-plus-grid/scripts/check-linked-vocabulary.mjs b/plugin-designs/research-map-plus-grid/scripts/check-linked-vocabulary.mjs index 1d6143c42..a39c2ad4d 100644 --- a/plugin-designs/research-map-plus-grid/scripts/check-linked-vocabulary.mjs +++ b/plugin-designs/research-map-plus-grid/scripts/check-linked-vocabulary.mjs @@ -82,9 +82,18 @@ await requireThisTree(TREE, ROOT, TAG) const browser = await chromium.launch({ args: ['--enable-unsafe-swiftshader', '--use-gl=angle', '--use-angle=swiftshader'] }) const fails = [] const lines = [] +// ★ THE PROVIDER MATRIX. The same assertions, run once per engine, because interoperability that +// holds on one provider and not the other is not interoperability +// (BUILD-INTEROP-CONTRACT-GRID-UNDER-MAP-BOTH-PROVIDERS). It is not a formality: the map half of +// this link resolves a religion family through its own legend, and the legend is drawn by the map +// engine — a MapLibre-only paint or layout mistake drops a whole layer silently, which would show +// up here as a link that delivers nothing while the Mapbox run stays green. +const PROVIDERS = ['mapbox', 'maplibre'] +const results = {} try { + for (const engine of PROVIDERS) { const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }) - await page.goto(LEAF, { waitUntil: 'domcontentloaded', timeout: 60000 }) + await page.goto(`${LEAF}?provider=${engine}`, { waitUntil: 'domcontentloaded', timeout: 60000 }) // Everything below runs in ONE evaluate: the totals have to be read from the same live page, and // a `groups-filtered` listener installed from Node would miss events fired between round trips. @@ -186,19 +195,38 @@ try { return { unfiltered, family, letter, direct, linked, mutant, mapVocab, gridVocab: gridVocab.slice(0, 12) } }) - if (out.skip) { await browser.close(); cleanup(); skip(out.skip) } - if (out.fail) fails.push(out.fail) - else { - lines.push(` · vocabulary — the map names ${out.mapVocab.length} religion families, the grid names ${out.gridVocab.length}+; '${out.family}' is shared`) - lines.push(` · direct — religion-family="${out.family}" set on the grid itself: ${out.direct} of ${out.unfiltered}`) - lines.push(` · linked — the same family over the composite's wire: ${out.linked}`) - lines.push(` · mutant — the legend KEY '${out.letter}' over the same wire: ${out.mutant}`) + await page.close() + if (out.skip) { await browser.close(); cleanup(); skip(`${out.skip} (under ${engine})`) } + results[engine] = out + if (out.fail) { fails.push(`[${engine}] ${out.fail}`); continue } - if (out.mutant === out.direct) { - fails.push(`NO TEETH — sending the legend key '${out.letter}' narrowed the grid to the same ${out.direct} rows as the family name '${out.family}'. This gate cannot tell a right mapping from a wrong one, so its pass would mean nothing.`) + lines.push(` ── ${engine}`) + lines.push(` vocabulary — the map names ${out.mapVocab.length} religion families, the grid names ${out.gridVocab.length}+; '${out.family}' is shared`) + lines.push(` direct — religion-family="${out.family}" set on the grid itself: ${out.direct} of ${out.unfiltered}`) + lines.push(` linked — the same family over the composite's wire: ${out.linked}`) + lines.push(` mutant — the legend KEY '${out.letter}' over the same wire: ${out.mutant}`) + + if (out.mutant === out.direct) { + fails.push(`[${engine}] NO TEETH — sending the legend key '${out.letter}' narrowed the grid to the same ${out.direct} rows as the family name '${out.family}'. This gate cannot tell a right mapping from a wrong one, so its pass would mean nothing.`) + } + if (out.linked !== out.direct) { + fails.push(`[${engine}] the link does not deliver the facet — '${out.family}' set on the grid gives ${out.direct} rows, the same family over the composite's wire gives ${out.linked}.`) + } + } + + // ★ AND THE TWO ENGINES MUST AGREE. Each run above only proves the link works within one engine; + // identical COUNTS is what proves the contract is the same product on both. A per-engine pass with + // different numbers would mean the two builds narrow to different peoples for the same choice. + const [a, b] = PROVIDERS + if (results[a] && results[b] && !results[a].fail && !results[b].fail) { + if (results[a].unfiltered !== results[b].unfiltered) { + fails.push(`the two engines start from different data — ${a} lists ${results[a].unfiltered} groups, ${b} lists ${results[b].unfiltered}.`) } - if (out.linked !== out.direct) { - fails.push(`the link does not deliver the facet — '${out.family}' set on the grid gives ${out.direct} rows, the same family over the composite's wire gives ${out.linked}.`) + if (results[a].family === results[b].family && results[a].direct !== results[b].direct) { + fails.push(`'${results[a].family}' narrows to ${results[a].direct} rows under ${a} and ${results[b].direct} under ${b} — the same choice gives different peoples on the two engines.`) + } + if (results[a].linked !== results[b].linked) { + fails.push(`the link delivers ${results[a].linked} rows under ${a} and ${results[b].linked} under ${b} — interoperability that holds on one provider and not the other is not interoperability.`) } } } catch (err) { @@ -217,4 +245,4 @@ if (fails.length) { console.log(' NAME per row. Both halves must speak the NAME on the wire.') process.exit(1) } -console.log(`${TAG}: PASS — the composite's religion link delivers exactly the facet the grid delivers on its own, and a wrong mapping does not.`) +console.log(`${TAG}: PASS — on BOTH providers the composite's religion link delivers exactly the facet the grid delivers on its own, a wrong mapping does not, and the two engines agree to the row.`)