plugin-designs: neutral comments in shared/ownerless maps

Rewrite code comments across doxa-research-map, upg-neighbor,
research-map-plus-grid, rudder-voice, people-groups-grid, big-picture,
prayer-walk-planner and all-people-groups to drop internal role names
from measurement and provenance notes, keeping each technical fact.
Comments only; behaviour unchanged.
This commit is contained in:
Doxa Maps 2026-10-01 00:21:17 +00:00
parent 35c68048e8
commit 06a48a9b42
17 changed files with 67 additions and 67 deletions

View file

@ -194,7 +194,7 @@ const mapData = useMapData({
// all 12,379 features, 3.7 MB of GeoJSON built and re-tiled TWICE (first paint, then the DOXA recolour);
// slim, it is 1.25 MB, built once. The popup reads the row by id. The DOXA recolour no longer rebuilds
// the 12,379 with all their fields: DOXA pins are a small source of their own drawn on top, and the base
// is re-sent once, slim, WITHOUT them (an invisible twin per DOXA group still cost a draw: rega measured).
// is re-sent once, slim, WITHOUT them (an invisible twin per DOXA group still cost a draw: measured).
let rows = [] // the decoded rows; a feature's id is its index here
let doxaIds = new Set() // row indexes in the DOXA set (empty until the prayer API answers)
let doxaBySlugRop = new Map() // rop3 → { slug, name } for the pray link

View file

@ -146,7 +146,7 @@ watch(textDir, reflectDir)
// The host's people-group LIST (library/lists/), handed to both children as the only key of their
// profile-config, so nothing else about them changes. Absent = the default list.
// ★ THE LOCALE TRAVELS WITH THE LIST (rega, row 47: big-picture rendered 31 Arabic text nodes
// ★ THE LOCALE TRAVELS WITH THE LIST (measured: big-picture rendered 31 Arabic text nodes
// through profile-config against 79 through ?lang=, and check:host-locale-contract measured 402
// against 888). This handed each child ONLY the list, so the one documented channel a partner has
// for asking a nested element to speak Arabic — its profile-config — arrived empty of any locale.
@ -155,7 +155,7 @@ watch(textDir, reflectDir)
// Threading it costs nothing and is what ProfileLoader would have done had one existed beneath this
// composite, which is exactly the (C) failure mode: a composite that BAKES its children in has no
// ProfileLoader under it, so it must do that job itself.
// This is ONE identified gap, not a claim to have closed the whole 402/888 difference; rega
// This is ONE identified gap, not a claim to have closed the whole 402/888 difference; a later pass
// re-measures.
const listConfig = computed(() => {
const cfg = {}

View file

@ -529,7 +529,7 @@ const legendTopVar = computed(() => {
// Languages tabs it reserved 56px for a toolbar it never draws, and only those two tabs, because
// they are exactly the set showsFlyTools() returns true for. Every other tab took the 14px branch
// and looked right, which is why it was reported as two tabs misbehaving.
// MEASURED by rega at 1440x900 on main e1d0bc20: the band from the map's top edge (y 44) to the
// MEASURED at 1440x900 on main e1d0bc20: the band from the map's top edge (y 44) to the
// legend card (y 100) is 56px of EMPTY map on both tabs, with no LegendTools anywhere in the strip.
// So the gap was dead space, not a toolbar the reader had not registered.
const toolsShown = showLegendTools.value && showsFlyTools(activeLegendType?.value ?? activeLegendType)
@ -698,7 +698,7 @@ watch(() => activeTabId?.value, async (newTabId, oldTabId) => {
// ★ SCROLL THE TAB BAR, NOT THE PAGE. This was
// btn.scrollIntoView({ inline: 'center', block: 'nearest', behavior: 'smooth' })
// and 'nearest' does not mean "do not scroll the page" — it means "scroll every scrollable
// ancestor the least amount needed", and the HOST PAGE is an ancestor. MEASURED by rega's embed
// ancestor the least amount needed", and the HOST PAGE is an ancestor. MEASURED in the embed
// sweep: a partner page carrying this map below the fold jumps to y≈640 the moment it mounts, on
// a phone, where Simple Research is the default tab. The reader asked for nothing; the page moved
// under them.
@ -788,10 +788,10 @@ const mapData = useMapData({
// then force it visible (country search stays on the current tab — d7e741b — so
// the polygon layers, normally hidden off the Regions tab, must be shown here).
// ★ EVERY region paint write goes through this, or the pre-warm above is undone the first time a
// highlight is cleared. Found by cross-reading exp-shepherd's country-posters port (73438b43b), which
// highlight is cleared. Found by cross-reading the country-posters port (73438b43b), which
// wraps all eighteen of its clears while mine still wrote nine RAW constants — a raw value makes the
// property constant again, so the next highlight recompiles the shader variant the pre-warm exists to
// have already compiled. rega measured 0 ms on a fresh tab; a reader who clears a selection first was
// have already compiled. Measurement showed 0 ms on a fresh tab; a reader who clears a selection first was
// paying it again, which no run so far has exercised.
// regionIdle now comes from the library (useLegendMapBridge), which already called it — this file
// was defining a copy and then passing it in. See check:region-paint-single-source.
@ -932,7 +932,7 @@ const legend = useLegendData({
// ★ REMOVED: provide('legendRows', legend.rows). useLegendData returns `items`, never `rows`, so
// this provided undefined from the day it was written, and nothing in the repo injects 'legendRows'
// — LegendDesktop and LegendMobile read the `legend` key instead, which is what the comment above
// used to claim was happening. exp-shepherd found the same line in countries-map, where it was not
// used to claim was happening. the same line was found in countries-map, where it was not
// merely dead: it made Export poster throw (NE-17, their d6916e05e). A provide that is undefined and
// a comment that describes a contract nobody implements are worse together than either alone, so
// both go. If a consumer ever needs these rows, it should provide legend.items and say so then.
@ -1117,14 +1117,14 @@ provide('getActivePinColor', (properties) => {
// already (fam:/lang:/dial: + region:/block:/country: + bloc:/cluster:/pg:/pgic:),
// so this returns null for them and zoomToLegendRow falls back to node.filter.
// ─── ONE WRITER FOR THE REGION POLYGONS ──────────────────────────────────────
// ★ MEASURED (rega, main 373b7979b): a single WAGF row click produced NINE regions-* paint writes —
// ★ MEASURED (main 373b7979b): a single WAGF row click produced NINE regions-* paint writes —
// fill-opacity, line-color and line-width, three times each inside ~110 ms — because two paths painted
// the same highlight from DIFFERENT sources: onSemanticTreeSelect from node.isoCodes, applyDimFilter
// from mapLayers.regionIsoMap, each triggering the other through mapStore. Two sources for one visual
// is not just wasted work; it is how they drift, and it already cost a debugging cycle when we had to
// establish WHICH of them had cleared the highlight.
//
// ★ AND THE IDLE STATE IS DATA-DRIVEN ON PURPOSE. rega measured the row click compiling a new shader
// ★ AND THE IDLE STATE IS DATA-DRIVEN ON PURPOSE. Measurement showed the row click compiling a new shader
// variant: 5.5 s on MapLibre, 11.1 s on Mapbox (SwiftShader), because that click was the first moment
// these three properties stopped being constants. Same defect as the pins, same fix: the idle value is
// ['case', ['has', 'iso_3166_1_alpha_3'], x, x] — both branches identical, so not one pixel changes —
@ -1143,7 +1143,7 @@ function prewarmRegionPaint(m) {
// Was a THIRD local copy of the idle expression, under a different name — byte-identical body.
// The library's regionIdle replaces it exactly.
const wrap = regionIdle
// ★ SKIP WHAT IS ALREADY DATA-DRIVEN, and note the test is not "is it an array". exp-shepherd wrote
// ★ SKIP WHAT IS ALREADY DATA-DRIVEN, and note the test is not "is it an array". The country-posters port wrote
// their copy to skip any array, reasoning that an expression is already data-driven so there is no
// variant to warm. That is right for this layer — line-color arrives as a match on a feature property
// — but it is right by accident: a ZOOM expression like ['interpolate',['linear'],['zoom'],…] is also
@ -1163,10 +1163,10 @@ function prewarmRegionPaint(m) {
// exactly that reverse. Mapbox then classifies the property as composite and reaches for an
// interpolation factor the expression cannot supply — the class of
// this.expression.interpolationFactor is not a function
// rega saw once on Mapbox at L54, on the clear → other-row path. A missed pre-warm costs a frame; an
// a run saw it once on Mapbox at L54, on the clear → other-row path. A missed pre-warm costs a frame; an
// illegal paint costs the layer, so when the two rules disagree this one wins.
//
// exp-shepherd's port skips ANY array and is immune to this by accident; the difference between us
// the country-posters port skips ANY array and is immune to this by accident; the difference between us
// was never about zoom expressions being data-driven, it was about which mistake is recoverable.
const readsZoom = (e) => Array.isArray(e) && (e[0] === 'zoom' || e.some(readsZoom))
const warm = (layer, prop) => {
@ -1185,9 +1185,9 @@ function prewarmRegionPaint(m) {
function paintRegionScope(m, isoCodes) {
if (!m || !m.getLayer('regions-fill')) return
// ★ CANONICAL FIRST, OR THE COMPARE BELOW IS A LIE (rega, L43: still 9 writes per click, not 3).
// ★ CANONICAL FIRST, OR THE COMPARE BELOW IS A LIE (measured at L43: still 9 writes per click, not 3).
// The two sources build the same region from different lists — node.isoCodes and
// mapLayers.regionIsoMap[slug] — and they do not agree on ORDER. rega had already told me that in
// mapLayers.regionIsoMap[slug] — and they do not agree on ORDER. The measurement had already shown that in
// the very first stack trace ("a case over ['IND',…]" then "a case over ['MYS',…], a different
// literal order") and I wrote a JSON-string compare anyway, which can never match two orderings of
// the same set. Sorting and de-duplicating makes the expression a function of the SET, which is what
@ -1755,7 +1755,7 @@ async function ensureRegionsLoaded() {
if (regionsData && map.value) {
mapLayers.addRegionsLayer?.(regionsData, 'doxa-regions')
// ★ PRE-WARM HERE, which is the only moment that helps. These layers are added on first visit
// to the Regions tab, and rega measured the first ROW click paying 5.5 s (MapLibre) / 11.1 s
// to the Regions tab, and measurement showed the first ROW click paying 5.5 s (MapLibre) / 11.1 s
// (Mapbox) of shader compile because that click was when their paint first became data-driven.
// Making the paint data-driven at layer-add moves that compile here, where a 6.4 MB GeoJSON is
// already loading, instead of into the middle of a gesture.
@ -1904,7 +1904,7 @@ function pinBaseAlpha() {
// matchExpr: an expression to dim by, null to clear dimming, undefined to re-apply what is current
// (which is what the slider's watcher needs — it changes the scale, not the selection).
// ★ THE UNDIMMED VALUE IS DATA-DRIVEN TOO, AND THAT IS THE WHOLE POINT (reported bug: "the research map
// isn't connecting with the legend"; rega's CPU profile, 2026-09-28). A legend click on a big affinity
// isn't connecting with the legend"; a CPU profile). A legend click on a big affinity
// row appeared to do nothing for ~2 s. It was not the echo guard, not the legend's DOM (34 rows before,
// 34 after) and not expression evaluation (the JS filter scan over 2,106 features against a 700-id list
// measures 14 ms). It was ONE task of 3.2-3.5 s whose self time was getShaderParameter + getProgram-
@ -1918,7 +1918,7 @@ function pinBaseAlpha() {
// and the program compiles during boot alongside every other one. A selection then only re-uploads
// attribute values, which is the cheap path.
//
// The trade, stated: boot compiles one more program (tens of ms on a real GPU; rega measured seconds
// The trade, stated: boot compiles one more program (tens of ms on a real GPU; measurement showed seconds
// only because that box rasterises on the CPU via SwiftShader). Paying it at load beats freezing the
// map mid-gesture, and the variant was going to be compiled on first interaction anyway.
function applyPinOpacity(m, matchExpr) {
@ -1928,7 +1928,7 @@ function applyPinOpacity(m, matchExpr) {
// ['has','uniqueId'] reads a feature property, which is what makes it data-driven; both outcomes are
// `a`, so a pin without the property renders exactly as before.
const value = _pinMatchExpr ? ['case', _pinMatchExpr, a, 0] : ['case', ['has', 'uniqueId'], a, a]
// ★ ONE WRITE PER DISTINCT VALUE. rega counted circle-opacity rewritten 4-8 times for a single click
// ★ ONE WRITE PER DISTINCT VALUE. Measurement counted circle-opacity rewritten 4-8 times for a single click
// (+1.52, +1.57, +1.94, +5.17 s): the selection handler, the glow sync and the slider watcher all end
// up here, and each write re-uploads the per-feature attribute. Every writer already routes through
// this function, so the collapse belongs here and nowhere else. Compared as text because the value is
@ -2325,7 +2325,7 @@ watch(() => profileConfig?.value?.externalFilters, (raw) => {
try {
// ★ AN EMPTY VALUE THE HOST DID NOT SET MUST NOT CLEAR WHAT THIS PAYLOAD JUST LIT. This is the
// The report "the research map isn't connecting with the legend" on the WAGF Regions tab, found by
// rega's stack trace (l40): the highlight painted at +51 ms and was wiped at +438 ms by
// a stack trace (l40): the highlight painted at +51 ms and was wiped at +438 ms by
// clearAllHighlights, reached from applyDimFilter({kind:null}) through the selectedRegion watcher —
// so selectedRegion had gone FALSY while externalRegion still read "asia".
//
@ -2426,7 +2426,7 @@ watch(() => profileConfig?.value?.externalFilters, (raw) => {
// ★ EACH HOOK IS ALSO HELD LOCALLY, so the unmount path can release ONLY WHAT IS STILL OURS.
// Two of these maps can share a page (research-map-plus-grid composes one beside a grid, and the
// gallery can show another): B mounts and overwrites A's hooks, then A unmounts. An unconditional
// delete would strip the hooks belonging to the map still on screen, and rega would report a verify
// delete would strip the hooks belonging to the map still on screen, and it would report a verify
// hook missing on a map that is perfectly healthy — a false failure costing more to chase than this
// costs to write. Declared out here rather than inside the block below because a const inside it is
// invisible to onBeforeUnmount. Same pattern gospel used in d3597f3d1.
@ -2534,7 +2534,7 @@ function emitFilterState() {
//
// The NAME still goes out because this map's own legend selects by name; the CODE goes out for
// anyone matching rows. Both halves get the form they can actually use.
// ★ THE LOOKUP HAS TO MATCH THE WAY THE LEGEND BUILT THE LABEL (rega, 2026-09-26, main 49992a5d:
// ★ THE LOOKUP HAS TO MATCH THE WAY THE LEGEND BUILT THE LABEL (measured on main 49992a5d:
// MapLibre, BOTH locales, Languages > Language > "Arabic" -> languageCode:"" -> grid 2,107 -> 0).
//
// WHAT I GOT WRONG. This found a row whose language LABEL equals the selected name with `===`.
@ -2543,7 +2543,7 @@ function emitFilterState() {
// splitting the API label at the comma. MEASURED against the live source (2,107 rows, all 2,107
// carrying primary_language as {value,label}): there is NO row whose label is "Arabic". What
// exists is "Arabic, Sudanese", "Arabic, Levantine", "Arabic, Gulf", ... So the exact match could
// never hit, in any locale — which is why rega saw it fail identically in en and ar, and why the
// never hit, in any locale — which is why it failed identically in en and ar, and why the
// locale fix that prompted this code was right about the DISEASE and wrong about this line.
//
// This is the derived-value trap, one tier down from where I last wrote it up: a value that
@ -2586,7 +2586,7 @@ function emitFilterState() {
const detail = {
region: mapStore?.selectedRegion || '',
religion: religionName,
// ★ A DIALECT'S NAME COMES FROM ITS LABELS, NOT ITS KEY (rega, row 43 case 3: this reported
// ★ A DIALECT'S NAME COMES FROM ITS LABELS, NOT ITS KEY (a measured case: this reported
// ", Afro-Asiatic__Arabic__Sudanese"). `selectedDialect.key` is the TREE NODE ID —
// family__baseLang__dialect — and the watcher fifteen lines above says so in as many words;
// I read that comment and then used the key as a display name anyway. Worse, selectedLanguage
@ -2605,7 +2605,7 @@ function emitFilterState() {
languageCodes: langCodes,
}
// ★ AN EMPTY REPORT AFTER A NON-EMPTY ONE IS AMBIGUOUS, SO IT IS NOT SENT IMMEDIATELY.
// rega, row 43 case 3 at 52eeb0db: coalescing worked — one event per click — and in 3 of 4 runs
// measured at 52eeb0db: coalescing worked — one event per click — and in 3 of 4 runs
// that one event was EMPTY, so the grid stayed at 2,107. The cascade behind a dialect click
// passes through a fully-cleared state: mapStore.selectLanguage() sets selectedDialect = null
// BEFORE it sets the language, and the clear was the last write my watcher saw. The settle timer
@ -2642,7 +2642,7 @@ function emitFilterState() {
host.dispatchEvent(new CustomEvent('map-filtered', { detail, bubbles: true, composed: true }))
}
// ★ ONE EVENT PER SETTLED SELECTION (rega, row 43 case 3: a dialect click narrowed the grid in
// ★ ONE EVENT PER SETTLED SELECTION (a measured case: a dialect click narrowed the grid in
// only ONE of four runs, with a correct languageCode in all four).
//
// THE CAUSE IS A BURST MEETING A GUARD. Clicking a dialect does not change one store value; it
@ -2665,7 +2665,7 @@ const EMIT_SETTLE_MS = 60
// of delay on a clear — about a fifth of a second, well under what a reader notices, and far below
// the composite's own 1.2s push window so it cannot collide with that.
// ★ A CEILING ON BELIEVING A CLEAR — NOT A GUESS AT HOW LONG THE CASCADE TAKES.
// rega, row 43 case 3 at 835ec1ac: the retry budget catches the transient on MapLibre and NOT
// measured at 835ec1ac: the retry budget catches the transient on MapLibre and NOT
// reliably on Mapbox (and not at all in ar/mapbox), because 3 x 60ms is still a duration I picked
// without being able to measure the thing it has to outlast. That is the THIRD time I have tuned a
// number on one machine for this one defect, so the number's JOB changes rather than its value.
@ -2682,7 +2682,7 @@ let _emptyWaitStartedAt = 0
let _lastEmitWasFilled = false
function emitFilterStateSettled() {
// ★ THE ECHO GUARD MUST BE READ WHEN THE CHANGE HAPPENS, NOT WHEN THE TIMER FIRES.
// rega, row 50: a region row click emitted {region:"asia"} and then an EMPTY event, on both
// measured: a region row click emitted {region:"asia"} and then an EMPTY event, on both
// engines. It passed only because the composite's 1.2s push window happened to swallow the
// second one — a pass that depends on a swallow is not a pass.
//
@ -2809,7 +2809,7 @@ function applyFitSlugs(slugs) {
: null
// ★ REDUCED MOTION DRIVES THE MAP DIRECTLY, and that is not belt-and-braces.
// MEASURED by rega with the browser's prefers-reduced-motion: reduce set: the framing move still
// MEASURED with the browser's prefers-reduced-motion: reduce set: the framing move still
// animated for ~5s on MapLibre and ~6.7s on Mapbox — essentially the same as without it, where a
// jump is ~0ms. So the preference was being honoured in THIS file and thrown away downstream:
// useMapFly's flyToCoords(coords, zoom) takes NO options argument at all, so the `{ duration: 0 }`
@ -2840,7 +2840,7 @@ function applyFitSlugs(slugs) {
//
// The checkbox force-set 'mst' "regardless of what's in the store", and the picker wrote to a
// property mapStore never declared — so choosing Native drew nothing, and had it worked, one tick
// of the checkbox would have snapped it back to MST anyway. MEASURED as inert by exp-shepherd; the
// of the checkbox would have snapped it back to MST anyway. MEASURED as inert; the
// second half is what makes fixing only the picker the wrong fix, because the control would have
// worked until the reader touched the other one.
//
@ -2916,7 +2916,7 @@ async function onMapReady(normalizedPeopleGroups) {
// added with a CONSTANT "circle-opacity": 1 (see addLanguageFamilyLayer), and applyPinOpacity is
// otherwise first reached from a legend click — so without this line the expression shape below is
// only ever the CLEARED state and the first selection is still the moment the variant is compiled,
// which is the 3.2-3.5s freeze rega profiled. Found by reading the built bundle rather than the
// which is the 3.2-3.5s freeze seen in profiling. Found by reading the built bundle rather than the
// source: the addLayer constant is right there in the shipped JS.
// matchExpr is left UNDEFINED on purpose — "re-apply what is current" — so a selection already
// restored from a share-link hash is preserved instead of being cleared at boot.
@ -3022,7 +3022,7 @@ const reannounceInfoPending = () => { if (!appReady.value && !mapError.value) an
watch(mapError, (e) => { if (e) announceInfoPending(false) })
// ★ THE PRAYER LINE'S TABLE IS FETCHED AFTER THE MAP MOUNTS, when the browser is idle, not when a print starts: the
// print waits at most 3 s for it (openPoster), and on a slow phone network a fetch begun at the tap missed that window
// (rega: the line dropped in 1 of 2 runs). loadPacketStrings caches the promise, so the print reads it at once; it
// (the line dropped in 1 of 2 runs). loadPacketStrings caches the promise, so the print reads it at once; it
// never rejects, and a failed fetch is retried by the print. 48 KB gzipped, once per page.
onMounted(() => {
const prefetch = () => import('@map/packet/packetStrings.js').then(({ loadPacketStrings }) => loadPacketStrings(resolveAssetsBase(profileConfig?.value))).catch(() => {})
@ -3135,7 +3135,7 @@ onMounted(async () => {
window.addEventListener('resize', _resyncScrollZoom)
})
// ★ THE MAP IS TORN DOWN NO MATTER WHAT ELSE FAILS. rega's heap snapshots (leak-722ac7789) found
// ★ THE MAP IS TORN DOWN NO MATTER WHAT ELSE FAILS. heap snapshots (leak-722ac7789) found
// every unmounted research map still alive, held by window.__doxaLogoOwner -> Map -> div.rm-map-canvas
// -> ShadowRoot -> app, and that global is released only when the map's 'remove' event fires. So a
// single throw anywhere in this hook stranded the entire application in memory, one copy per mount.
@ -3158,7 +3158,7 @@ onBeforeUnmount(() => {
step(() => selectedPin.cleanup?.())
step(() => mapStore.unregisterMap?.(mapId))
// Every verify hook this component plants is an arrow closing over it, so each one left behind
// keeps the component, its shadow root and the whole app alive (rega, leak-722ac7789). They are
// keeps the component, its shadow root and the whole app alive (leak-722ac7789). They are
// released as a set: they are installed as a set, and releasing three of four leaks just as much
// as releasing none. Each is checked before deleting so a later mount's hook is never removed.
// Written out one per line, not looped over an array of names, SO THE CHECK CAN READ IT.

View file

@ -4,7 +4,7 @@
* different. A control that does nothing is worse than a control that is absent: it tells the
* reader the map cannot do what it can.
*
* THE DEFECT, MEASURED BY exp-shepherd AND CONFIRMED HERE. The side menu offers four clustering
* THE DEFECT, MEASURED AND CONFIRMED HERE. The side menu offers four clustering
* options; choosing "Native" changed nothing on the map. Two separate reasons, and only fixing both
* makes the control real:
*

View file

@ -2,7 +2,7 @@
/**
* check-language-code-link.mjs — the map's language selection must reach the grid, or narrow nothing.
*
* THE BUG (rega, 2026-09-26, main 49992a5d, MapLibre, BOTH locales). Languages > Language >
* THE BUG (measured on main 49992a5d, MapLibre, BOTH locales). Languages > Language >
* "Arabic": the map emitted `map-filtered` with {language:"Arabic", languageCode:""}, the composite
* handed the grid an empty code, and the grid went 2,107 -> 0. An empty grid reads as "no results",
* which is the worst shape a wiring bug can take — the reader believes the data.
@ -48,11 +48,11 @@
* E. no tier may produce a value that selects zero rows. That is the user-visible invariant and
* it is asserted directly, not inferred.
* F. a DIALECT reports a NAME and not a tree node id, and the emitter sends ONE event per
* settled selection rather than a burst. Both from rega's row 43 case 3.
* settled selection rather than a burst. Both from a measured case.
* G. that ONE event is not the cascade's transient EMPTY state. Same case, second measurement:
* the coalescing worked and the single event was EMPTY in 3 of 4 runs.
*
* NO NETWORK, NO BROWSER (Playwright is rega's alone). The fixture is committed and was taken from
* NO NETWORK, NO BROWSER (Playwright is the browser-QA lane's alone). The fixture is committed and was taken from
* the live source; refresh it with --refresh-fixture when the source's language set changes.
*
* NEGATIVE-TESTED 2026-09-26, each FAILS:
@ -221,7 +221,7 @@ if (matchesLanguage) {
}
// ── E. the dialect tier: a name, and one event ───────────────────────────────────────────────
// rega, row 43 case 3, on BOTH engines in BOTH locales: clicking "Arabic, Sudanese" emitted the
// Measured on BOTH engines in BOTH locales: clicking "Arabic, Sudanese" emitted the
// right languageCode and narrowed the grid in only ONE of four runs, while the `language` slot read
// ", Afro-Asiatic__Arabic__Sudanese".
// THE NAME: selectedDialect.key is the TREE NODE ID (family__baseLang__dialect) and
@ -258,7 +258,7 @@ if (matchesLanguage) {
}
// ── G. an empty report after a filled one is re-taken, and the re-take is BOUNDED ────────────
// rega, row 43 case 3 at 52eeb0db: coalescing landed — one event per click — and in 3 of 4 runs
// measured at 52eeb0db: coalescing landed — one event per click — and in 3 of 4 runs
// that one event was EMPTY, so the grid stayed at 2,107. The cascade behind a dialect click passes
// through a fully-cleared store state (mapStore.selectLanguage sets selectedDialect = null BEFORE
// setting the language), and the settle timer fired inside it.
@ -272,7 +272,7 @@ if (matchesLanguage) {
if (!/_lastEmitWasFilled/.test(src) || !/const isEmpty/.test(src)) {
fails.push('G research-map.vue no longer guards an all-empty report. A dialect click passes through a fully-cleared store state, so the settled read can be taken mid-cascade and ship {language:"",languageCode:""} — which the composite correctly reads as "the reader cleared everything".')
} else if (!/EMPTY_SETTLE_CEILING_MS/.test(src)) {
// ★ THE BOUND'S SHAPE CHANGED AND THE INVARIANT DID NOT. It used to be a retry COUNT; rega
// ★ THE BOUND'S SHAPE CHANGED AND THE INVARIANT DID NOT. It used to be a retry COUNT; it was
// measured that 3 x 60ms catches the cascade on MapLibre and not on Mapbox, so the count became
// a TIME CEILING — the difference being what the number is FOR. A count is a guess at how long
// the cascade takes, which cannot be known from here; a ceiling is how long before a clear is
@ -285,7 +285,7 @@ if (matchesLanguage) {
}
// ── H. the echo guard survives the coalescing ────────────────────────────────────────────────
// rega, row 50: a region click emitted the selection and then an EMPTY event, passing only because
// Measured: a region click emitted the selection and then an EMPTY event, passing only because
// the composite's 1.2s push window swallowed the second. emitFilterState has always opened with
// `if (applyingExternal) return` — a change pushed DOWN by the host is not a change to report back
// up — and that worked while the emit ran synchronously inside the watcher. Debouncing moved the

View file

@ -5,7 +5,7 @@
* ★ A READER OF THIS MAP IS PHOTOSENSITIVE. This is not a polish item: a camera that sweeps the globe every
* time a list is filtered is the strobe class this repo has already paid for once.
*
* THE DEFECT, measured by rega in a real browser with the preference set: the framing move still
* THE DEFECT, measured in a real browser with the preference set: the framing move still
* animated for ~5.0s on MapLibre and ~6.7s on Mapbox — indistinguishable from no preference at all,
* where a jump is ~0ms. The preference WAS being read correctly in research-map.vue and then thrown
* away one layer down: useMapFly's `flyToCoords(coords, zoom)` takes NO options argument, so the
@ -13,13 +13,13 @@
* options)` does accept one — two functions in one file, different signatures, and nothing at the
* call site to show it.
*
* ★ WHY THIS RUNS WITHOUT A BROWSER, WHICH IS THE POINT. rega found it by watching movestart →
* moveend for six seconds; only rega can do that, and only occasionally. What can be checked any
* ★ WHY THIS RUNS WITHOUT A BROWSER, WHICH IS THE POINT. it was found by watching movestart →
* moveend for six seconds; only a real browser can do that, and only occasionally. What can be checked any
* time, in milliseconds, is the DECISION: given the preference, which camera call does the code
* make? So this lifts the real camera block out of the SFC, runs it against a mocked matchMedia and
* a recording map, and asserts the call that comes out.
*
* It cannot prove the engine honours duration 0 — that is rega's half. It proves we ASK for a jump,
* It cannot prove the engine honours duration 0 — that is the browser half. It proves we ASK for a jump,
* which is the half that was wrong.
*
* ASSERTIONS:

View file

@ -257,7 +257,7 @@ const filtersBackdrop = computed(() => {
})
const prayBaseUrl = computed(() => props.prayBaseUrl || cfg.value.prayBaseUrl || 'https://pray.doxa.life')
// ★ THE URL IS A SOURCE OF TRUTH FOR THE DATA TOO, not just for the chrome.
// MEASURED by rega at ?lang=ar and ?lang=es: this grid's CHROME translated and its DATA stayed
// MEASURED at ?lang=ar and ?lang=es: this grid's CHROME translated and its DATA stayed
// English — country names, affinity names, religion labels — because the `&lang=` on the fetch was
// built from props and profile-config ONLY. A reader who asked for Arabic got Arabic buttons around
// English content, which reads as a half-finished translation rather than the wiring bug it is.
@ -420,7 +420,7 @@ const t = computed(() => {
const EAGER_IMAGES = 5
const truthy = (v) => !(v === false || v === 'false' || v === '0' || v === 0)
// ★ THE PRESET'S KEYS WERE NEVER REACHING THESE (rega RQ20). The "driven" preset asks for
// ★ THE PRESET'S KEYS WERE NEVER REACHING THESE (measured, RQ20). The "driven" preset asks for
// show-search:false and show-filters:false and the page rendered both anyway, with 0 console
// errors — because nothing was failing. ProfileLoader PROVIDES profile-config; it does not bind
// its keys as props. So props.showSearch stayed at its default of true and the preset was inert.
@ -1153,7 +1153,7 @@ onMounted(() => {
<div id="results" class="grid uupgs-list" :style="gridStyle">
<template v-for="(uupg, ui) in displayedUUPGs" :key="uupg.slug">
<div class="card uupg__card">
<!-- ★ THE PHOTOS WERE COMPETING WITH THE MAP FOR THE FIRST THREE SECONDS. rega measured a
<!-- ★ THE PHOTOS WERE COMPETING WITH THE MAP FOR THE FIRST THREE SECONDS. Measurement showed a
cold load of Simple Research pulling 24 people-group photos, 5.3 MB, DURING BOOT —
alongside the map engine and the API data, on the same connection. Nothing was wrong
with any one image; there was simply no instruction about WHEN to fetch them.

View file

@ -713,7 +713,7 @@ useShadowStyles(
{{ tr('finish', { n: drawPoints.length }) }}
</button>
<!-- PHONE: these three collapse behind "More" (they ran past x=390 in a sideways row with no
scroll cue, rega ka3). Desktop: the toggle is hidden and they sit inline as before. -->
scroll cue, measured ka3). Desktop: the toggle is hidden and they sit inline as before. -->
<button type="button" class="pwp-btn pwp-more-btn" :aria-expanded="moreOpen ? 'true' : 'false'" @click="moreOpen = !moreOpen">⋯ {{ tr('more') }}</button>
<div class="pwp-listops" :class="{ 'is-open': moreOpen }" @click="moreOpen = false">
<button type="button" class="pwp-btn is-danger" :disabled="selectedCount === 0"

View file

@ -421,7 +421,7 @@ function onMapFiltered(ev) {
})
}
// ★ AN AXIS THIS GRID CANNOT EXPRESS MUST NOT NARROW IT TO NOTHING (rega, 2026-09-26: Languages >
// ★ AN AXIS THIS GRID CANNOT EXPRESS MUST NOT NARROW IT TO NOTHING (measured: Languages >
// Language > "Arabic" took the grid 2,107 -> 0 in BOTH locales, reading as "no results").
//
// This line used to be `linked.language.value || linked.language.label`, and the fallback was the

View file

@ -120,7 +120,7 @@ for (const p of catalogues) {
const files = listFiles((p) => /\.(vue|js|mjs)$/.test(p) && !p.includes('/scripts/') && !p.includes('/locales/'))
// ── ★ A TRANSLATOR CAN ARRIVE FROM A COMPOSABLE, NOT ONLY FROM A LOCAL WRAPPER ───────────────
// go-int planted a poison key behind `const T = useGiT()` and this gate PASSED. Three failures of
// a poison key was planted behind `const T = useGiT()` and this gate PASSED. Three failures of
// the same shape stacked up: (1) the file never writes the word tStatic, so the precondition below
// skipped it outright; (2) `useGiT` is not `tStatic`, so aliasesIn() found no alias at all; and
// (3) the alias is PREFIXED — useOptionalT carries a namespace — while its call sites pass BARE
@ -267,7 +267,7 @@ function aliasesIn(src) {
}
// ── ★ SELF-TEST, BECAUSE THIS GATE ALREADY PASSED A PLANTED POISON KEY ────────────────────────
// go-int planted `T('poison_key')` behind `const T = useGiT()` and this gate reported the tree
// a poison key `T('poison_key')` was planted behind `const T = useGiT()` and this gate reported the tree
// clean. That is the failure a check cannot be forgiven for: a red gate gets fixed, a green one that
// should be red gets BELIEVED, and this one guards goal (d). Measured before and after, with the
// same poison on disk: the version on main PASSES it, this version FAILS it as

View file

@ -101,7 +101,7 @@ try {
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } })
// ★ LISTEN BEFORE THE PAGE RUNS. The grid reports its total on its FIRST filter pass, which can happen
// before an evaluate() after goto attaches a listener; the gate then read "the grid never reported a
// total" while the grid had reported one (rega, ded4b3c1). Every total is recorded from the start.
// total" while the grid had reported one (measured, ded4b3c1). Every total is recorded from the start.
await page.addInitScript(() => {
window.__lvTotals = []
document.addEventListener('groups-filtered', (e) => { window.__lvTotals.push(e.detail?.total ?? null) }, true)

View file

@ -16,7 +16,7 @@
*
* So this reads what the compiler reads and asks the one question those gates cannot: is every name
* this file USES actually BOUND somewhere — imported, declared, a parameter, or a known global?
* No browser, no mount, milliseconds per file. It is the cheap half of the pair; exp-shepherd's
* No browser, no mount, milliseconds per file. It is the cheap half of the pair; the browser lane's
* check:composites-mount is the expensive half that proves the thing actually runs.
*
* SCOPE: every .vue under plugin-designs/ and library/ — fleet-wide, because the mistake was a

View file

@ -534,7 +534,7 @@ function connect() {
clearTimeout(retry)
try { es?.close() } catch {}
// No EventSource (a headless mount, an old WebView): show the engine-not-running state instead of throwing
// in the mounted hook, which failed check:profiles-mount for every lane (go-int, END-DXS-ESRC).
// in the mounted hook, which failed check:profiles-mount for every lane.
if (typeof window === 'undefined' || typeof window.EventSource !== 'function') {
engineError.value = 'The desktop engine is not running.'
return
@ -665,7 +665,7 @@ function releaseMic() {
media = null
actx = null
}
// Stop: the mic goes off and the widget is idle at once (rega: it read "Stop" for 12 s+ while a loaded
// Stop: the mic goes off and the widget is idle at once (it read "Stop" for 12 s+ while a loaded
// server caught up). The socket stays open so the last words still land in the box; the engine closes the
// session with "recording false". Record again meanwhile simply lets go of that tail.
function stopBrowser() {
@ -790,7 +790,7 @@ function onKey(ev) {
if (ev.ctrlKey && ev.shiftKey && ev.code === 'Space') { ev.preventDefault(); toggle() }
}
// The status also by plain request: a proxy that buffers the event stream (rega's harness did) must not
// The status also by plain request: a proxy that buffers the event stream (as a test harness did) must not
// leave the page guessing whether this server is meant to have an engine.
async function fetchStatus() {
try { const r = await fetch(apiBase.value + '/status'); if (r.ok) applyStatus(await r.json()) } catch {}
@ -1160,7 +1160,7 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 1
.compact .notice, .compact .hint { order: 1; font-size: 12.5px; padding: 8px 10px; }
/* ★ AND THE NOTICE YIELDS. Ordering it below the draft fixes the reading order but NOT the height:
.notice is flex:0 1 auto and .compact .compose is flex:1 1 0, so the draft still gives up whatever
the notice's content asks for. rega measured the result at 375x450 — a 20px draft whose wheel moves
the notice's content asks for. Measurement showed the result at 375x450 — a 20px draft whose wheel moves
nothing. The engine-offline notice is the long one (apiBase, Go 1.22, the setup command), ~132px at
375 and ~179px at 390, and it is REFERENCE text: a reader needs it once, not a third of a short
panel forever. Capping it and letting it scroll gives the draft 58px at 375x450 and 124px at

View file

@ -94,7 +94,7 @@ const fc = (features) => ({ type: 'FeatureCollection', features })
// Hover label: after a language is picked, its believers (green) and the unreached they reach (red)
// were anonymous until clicked, and a green dot could not be clicked at all. Same label as Mobilize.
const hover = ref(null) // { x, y, text }
// The tip opens toward the side with more room, so near an edge it is never cut off (rega saw "Ko…"
// The tip opens toward the side with more room, so near an edge it is never cut off (a run saw "Ko…"
// at the right edge in ar); max-width keeps it inside half the map on a phone. A tip is stale as soon as
// the map moves (a tap, then the fly to the pick), so moving clears it.
const hoverStyle = computed(() => {
@ -110,7 +110,7 @@ function repaint() {
const m = map.value
if (!m) return
// isStyleLoaded() is also false while TILES load (MapLibre), so a data load that lands then used to
// be dropped for good: the start view showed no dots at all (rega, upgn6). Try again once idle.
// be dropped for good: the start view showed no dots at all (measured, upgn6). Try again once idle.
if (!m.isStyleLoaded?.()) { if (!repaintPending) { repaintPending = true; m.once('idle', () => { repaintPending = false; repaint() }) } return }
const b = active.value
setSrc(m, SRC_B, bridgeFeatures())

View file

@ -1,5 +1,5 @@
// useNeighborT — My UPG Neighbors' UI text: library/i18n/locales/<lc>/neighbor.json (11 locales).
// Chrome only, by decision (dx-shepherd, 2026-09-26): research terms, provenance lines and proper
// Chrome only, by decision: research terms, provenance lines and proper
// nouns (SEND, doxa-2000, Bounded set, JP, PPLR…) stay in English as named sources.
// i18n is OPTIONAL: useI18n() throws on a host that never installed it, so the catalogue is the
// fallback, never a blank or a raw key. Call it inside setup().

View file

@ -86,7 +86,7 @@ const believersNear = computed(() => neighbors.value.reduce((a, n) => a + n.g.be
const peopleReachable = computed(() => neighbors.value.reduce((a, n) => a + n.g.pop, 0))
// A pick raises the phone sheet first, so the frame below is computed for the sheet that will show
// the pick's detail (rega, 390: a pick with the list lowered showed only a tooltip).
// the pick's detail (measured at 390: a pick with the list lowered showed only a tooltip).
function select(g) { if (g) { nbState.sheetDown = false; nbState.pgid = g.pgid; query.value = '' } }
// The neighbor in focus: a dot click picks its card, a card click flies to its dot. Both ways, so the
@ -95,7 +95,7 @@ const activeId = ref(null)
// Hover label: 7,000 red dots are anonymous until clicked, so name the one under the pointer.
const hover = ref(null) // { x, y, text }
function hoverAt(ev, text) { hover.value = text ? { x: ev.point.x, y: ev.point.y, text } : null }
// The tip opens toward the side with more room, so near an edge it is never cut off (rega saw "Ko…"
// The tip opens toward the side with more room, so near an edge it is never cut off (a run saw "Ko…"
// at the right edge in ar); max-width keeps it inside half the map on a phone. A tip is stale as soon as
// the map moves (a tap, then the fly to the pick), so moving clears it.
const hoverStyle = computed(() => {
@ -332,7 +332,7 @@ function repaint() {
const m = map.value
if (!m) return
// isStyleLoaded() is also false while TILES load (MapLibre), so a data load that lands then used to
// be dropped for good: the start view showed no dots at all (rega, upgn6). Try again once idle.
// be dropped for good: the start view showed no dots at all (measured, upgn6). Try again once idle.
if (!m.isStyleLoaded?.()) { if (!repaintPending) { repaintPending = true; m.once('idle', () => { repaintPending = false; repaint() }) } return }
ensureLayers(m)
}
@ -357,7 +357,7 @@ function framePadding() {
const rtl = !!el && getComputedStyle(el).direction === 'rtl'
return w > 760 ? { top: 60, bottom: 60, left: rtl ? 60 : 400, right: rtl ? 400 : 60 } : { top: 40, bottom: (nbState.sheetDown ? 56 : Math.round(h * 0.55)) + 20, left: 30, right: 30 }
}
// PHONE: the list covered 54% of the map with no way to put it down (rega, 390). A handle on the sheet
// PHONE: the list covered 54% of the map with no way to put it down (measured at 390). A handle on the sheet
// lowers it to a bar and raises it again; the choice is shared with Campaigns.
function toggleSheet() { nbState.sheetDown = !nbState.sheetDown; nextTick(() => frame()) }

View file

@ -3,7 +3,7 @@
*
* MapLibre throws "Style is not done loading" from setProjection before that, and setProjectionOn
* swallows the throw, so the lenses that set 'mercator' right after initializeMap() never got it on
* MapLibre (rega, C2: lang-cluster, send-map, the bloom cockpit's MapCell). Mapbox accepts it early,
* MapLibre (measured, C2: lang-cluster, send-map, the bloom cockpit's MapCell). Mapbox accepts it early,
* which is why this only showed on one engine.
*/
export function whenStyleReady(map, fn) {