P assembly: the widened census, stated neutrally

The case-insensitive census with every plugin agent's name found 30 more lines: library (ProfileLoader,
useMapProvider, ApproxPinNote, listSelection, colocation), the gallery's embed-contract card id, the gospel
plugins' source notes and build scripts, a regions script note and one gate description. Comments and notes
only, except the Gospel x Internet CSV export's provenance line, which no longer names who screened the links.
This commit is contained in:
Doxa Maps 2026-10-01 05:42:24 +00:00
parent 59afad5a33
commit 802d7b5b8a
16 changed files with 30 additions and 30 deletions

View file

@ -185,7 +185,7 @@ const showPinHint = computed(() => config.value?.pinHint !== false)
const lang = computed(() => {
// ?lang= / ?locale= on the page is the last rung: an embed opened with ?lang=ar already got Arabic TEXT
// (createAppI18n reads the URL) but this component's lang stayed '' so its dir and data language did not
// follow (upg100-plugin). profile-config still wins.
// follow. profile-config still wins.
const raw = (config.value?.locale || config.value?.lang || localeFromUrl() || '').split('-')[0].toLowerCase()
return SUPPORTED_LOCALES.includes(raw) ? raw : ''
})

View file

@ -5,7 +5,7 @@
when the pin's point is SHARED, naming the other groups (from library/utils/colocation.js, built from the
roster the map draws), because "approximate" alone hides that some dots are the same dot.
Never shows the raw coordinate: six decimal places beside a caveat that says "approximate" reads as a
contradiction. Wording: gospel-plugin's measured proposal (P5), in library/i18n/locales/<lc>/pins.json.
contradiction. Wording: a measured proposal (P5), in library/i18n/locales/<lc>/pins.json.
-->
<script setup>
import { computed, unref } from 'vue'

View file

@ -78,7 +78,7 @@ export function withUnassigned(nodes, allZoneIds, label = 'Unassigned') {
* error, no empty state. check:list-selection FAILS ON PURPOSE at that moment.
*
* This component cannot fix it: the CONSUMER builds the tree, so only the consumer knows which rows
* are regions. Found by gospel-plugin while wiring, who is namespacing their region rows; written
* are regions. Found while wiring a plugin that namespaces its region rows; written
* here because the next consumer to build a tree from the same zone data inherits the same collision.
*/
export function selectedId(node) {

View file

@ -200,7 +200,7 @@ export function engineFor(map) {
* and hold a map built by the other (a fallback, a second map, an auto-engine decision), and the
* object-vs-string question is a fact about the INSTANCE.
*
* BOTH RETURN SOMETHING OBSERVABLE, on upg100-plugin's argument: a precondition that bails without
* BOTH RETURN SOMETHING OBSERVABLE, on a plugin owner's argument: a precondition that bails without
* recording that it bailed is indistinguishable, at the rendered layer, from the request never
* arriving. setProjectionOn returns whether it applied; projectionOf returns null rather than
* throwing. Callers that do not care may ignore it, and all 12 existing sites do.

View file

@ -2,7 +2,7 @@
* colocation.js — which people groups share ONE coordinate on a map (APPROXPIN, 2026-09-26).
*
* A people-group pin is a representative point, not an address, and some pins ARE THE SAME POINT:
* measured on the Doxa roster (gospel-plugin): 2,107 groups on 2,048 distinct coordinates, 108 groups on
* measured on the Doxa roster: 2,107 groups on 2,048 distinct coordinates, 108 groups on
* a point shared with another (41 pairs, 6 triples, 2 quadruples). Saying "approximate" hides that; naming
* the co-located groups is the only version a reader can check. The index is built from THE ROSTER THE MAP
* DRAWS (every map's roster differs), never hardcoded.

View file

@ -528,7 +528,7 @@
"check:map-paints": "The map must put PIXELS on screen, not just features. Exists because injecting mapbox-gl.css collapsed this profile's canvas from 1440x900 to 1440x300 - a white rectangle - and EVERY assertion went green over it: verify-gospel-map counted 2,107 of 2,107 pins rendered, leaves-mount found a full shadow root, the i18n gates found every label translated and nothing clipped. queryRenderedFeatures does not care whether anything is visible. This screenshots the element and counts distinct colours (a basemap has hundreds, a blank rectangle one or two) and checks the canvas fills its container. --poison hides the canvas and requires a red verdict.",
"check:map-props-unit": "the engine and token props decided in the library, measured in Node with no browser: auto picks the engine from the token ladder, an explicit provider wins, Clear clears the page-wide token, and tokenUrl is fetched and honoured (26 cases, one per defect U1/U3/U7).",
"check:map-unmount-mid-init": "end-to-end, deterministic: holds mapbox-gl.js, opens a map plugin in the studio, switches away (destroying the pane) mid-initialise, then releases the script. Fails on any page error. REQUIRES --url. Exit 2 if the race was not staged.",
"check:map-unmount-race": "browser-free, sub-second: runs the shipped ensureEngineCSS with a null element (what an unmounted pane hands it) against a copy with its guard stripped; the guarded arm must resolve and the stripped arm must throw \"reading getRootNode\". By gospel-plugin, adopted by studio.",
"check:map-unmount-race": "browser-free, sub-second: runs the shipped ensureEngineCSS with a null element (what an unmounted pane hands it) against a copy with its guard stripped; the guarded arm must resolve and the stripped arm must throw \"reading getRootNode\".",
"check:mapbox-drew": "the Mapbox engine actually DREW, on every curated production map tab. The hole it fills: check-leaf-engine-per-provider proves MapLibre purity, and check-provider-switch loads ?provider=mapbox with a FAKE token — so the engine LOADING was measured and a tile RENDERING never was. Reads the engine off the canvas that drew (.mapboxgl-canvas vs .maplibregl-canvas), because a silent fallback to MapLibre looks identical to success; asserts queryRenderedFeatures() > 0, because a canvas at the right size is not a map. THREE exit states: 0 drew, 1 a real failure, 75 NOT MEASURED when there is no real token or only the known sandbox entitlement 403s (/v4/ composite, country-boundaries) blocked it — counting those as failures would manufacture work, counting them as passes would be false. Carries a negative arm: ?provider=maplibre must draw with MapLibre, else the engine check cannot tell the engines apart and every pass is unfalsifiable. Browser seat.",
"check:mobile": "deploy-2 CONTRACT check: needs NO JDK/SDK/device. cap sync is a file copy + plugin wiring, so it runs anywhere; cap:verify then asserts assets/public/index.html exists. Guards the SOFT FAILURE capacitor.config.json warns about: a wrong webDir yields an EMPTY assets/public and a BLANK WHITE SCREEN at launch, not an error.",
"check:mst-clustering": "the MST clustering mode draws a true minimum spanning tree/forest (same edge count and length as a reference Kruskal) and 2,107 pins take under 3 s on the main thread. Headless.",

View file

@ -805,7 +805,7 @@ export default defineComponent({
// NOTE: no `locale` PROP here on purpose — `locale` is a data() field (the manual switcher's
// state); a same-named prop+data collides in Vue. The alias lives in profile-config instead.
language: { type: String, default: '' },
// ── THE EMBED CONTRACT (card DELEGATE-NEW-TAB-REGA-RESEARCH-GALLERY-CAP5-EMBEDDABILITY) ──
// ── THE EMBED CONTRACT (card DELEGATE-NEW-TAB-RESEARCH-GALLERY-CAP5-EMBEDDABILITY) ──
// A host that drops <doxa-maps-spa> onto its own page (the DX-life research page) supplies NONE
// of the page globals a generated leaf page bakes — window.MAP_APP_API_URL, window.MAP_TOKEN, a
// correct assets path. Measured 2026-09-24 from a second origin: the shell mounted and 8 of 13

View file

@ -5,23 +5,23 @@
* Indian state) is online.
*
* No new source. Every column is read from a table another owner already builds:
* data/tables/doxa-gospel-access.json (gospel-plugin) access ladder, resources, ways in
* data/tables/doxa-people-groups.json (gospel-plugin) slug, lat/lng, pray link
* data/tables/doxa-gospel-access.json access ladder, resources, ways in
* data/tables/doxa-people-groups.json slug, lat/lng, pray link
* data/tables/country-connectivity.json (World Bank) internet %, year, mobile per 100
* data/tables/internet-by-country.json (cities-internet) top platform
* data/tables/internet-by-adm1.json (cities-internet) India by state, NFHS-5
* data/tables/messaging-channels.json (cities-internet) top free apps per country (Apple, iOS only)
* data/tables/social-platforms.json (cities-internet) social referral share per country (StatCounter)
* data/tables/gospel-by-province-groups.json (gospel-plugin) each IND/CHN group's province (approximate)
* data/tables/gospel-by-province.json (gospel-plugin) the aggregates that per-group file must sum to
* data/tables/gospel-by-province-groups.json each IND/CHN group's province (approximate)
* data/tables/gospel-by-province.json the aggregates that per-group file must sum to
*
* ★ IT FAILS, IT NEVER DROPS. Every join is counted, and the build exits non-zero if the output has
* fewer rows than the access table, if a key is duplicated, or if a group's country has no
* connectivity row. A board that quietly lost 40 groups would say "nothing here" about real people.
*
* ★ THE PROVINCE IS READ, NEVER RE-DERIVED. gospel-plugin publishes each placed group's province
* ★ THE PROVINCE IS READ, NEVER RE-DERIVED. The Gospel Resources build publishes each placed group's province
* (gospel-by-province-groups.json, reconciled with its aggregates before either is written). This build
* keeps ONE cheap assertion, the one gospel-plugin recommended: sum(aggregate groups) == per-group rows,
* keeps ONE cheap assertion, the one recommended: sum(aggregate groups) == per-group rows,
* which catches a stale copy of one file beside a fresh copy of the other. Every IND/CHN group must be
* either placed there or listed in the aggregate's `unassigned`, so none is silently province-less.
* (It replaces this builder's own point-in-polygon, which agreed with theirs group for group.)
@ -92,7 +92,7 @@ for (const r of adm1.rows) {
adm1By.set(k, a)
}
// ── province: gospel-plugin's per-group assignment, keyed like join 1 (name + iso3) ─────────────────
// ── province: the per-group assignment, keyed like join 1 (name + iso3) ─────────────────
const PROVINCE_COUNTRIES = new Set(['IND', 'CHN'])
const provinceBy = new Map()
for (const g of byGroup.rows || []) {
@ -240,7 +240,7 @@ if (failures.length) {
coverage,
band_rule: bandingText,
internet_note: 'internet_pct is the COUNTRY\'s figure (World Bank), never the group\'s. state_internet_* is India\'s state figure from NFHS-5 (2019-21): the share of women and of men aged 15-49 who have EVER used the internet, kept apart, never averaged, never on a scale with subscriptions per 100.',
province_note: 'province is read from gospel-by-province-groups.json (gospel-plugin): placed by point-in-polygon on the group\'s coordinate, which marks where a PEOPLE is found, not a town. It is approximate, and near a border it can fall on the wrong side; it is never a fact about connectivity in that state.',
province_note: 'province is read from gospel-by-province-groups.json: placed by point-in-polygon on the group\'s coordinate, which marks where a PEOPLE is found, not a town. It is approximate, and near a border it can fall on the wrong side; it is never a fact about connectivity in that state.',
access_note: 'access_detail says what exists in the group\'s OWN language in the sources checked; "none" means nothing was found there, never that nothing exists. access_level: own = something in their language; bridge = nothing in their language but a bridge, candidate or Deaf way in; zero = neither.',
platforms: {
messaging_source: msg.meta?.source?.name || '', messaging_accessed: msg.meta?.source?.accessed || '',

View file

@ -14,7 +14,7 @@
* g a New Testament or full Bible found: eBible (nt/full flags), a link whose scope is New
* Testament, Full Bible, or 27/66 parts (FCBH, Scripture Earth, PNG.Bible, Door43), or FCBH's own
* verse scope (people-group-pins codes[iso].au.vsc) of 27 or 66 parts
* y something found in their own language (gospel-plugin's access ladder), but no New Testament
* y something found in their own language (the access ladder), but no New Testament
* r nothing found in their own language in the sources checked
* A DIALECT is never coloured on its own evidence: every source catalogues by language, never by dialect.
* It carries its language's colour with `inh:1`, and the board draws it lighter and says so.
@ -53,9 +53,9 @@ const NT_SCOPE = /^(New Testament|Full Bible)$|^(27|66) parts/
for (const r of links) if (NT_SCOPE.test(r.scope || '')) ntFound.add(r.iso639_3)
// Faith Comes By Hearing's own verse scope for its fileset (people-group-pins codes[iso].au.vsc, shaped
// "N parts, FIRST to LAST"): the same 27|66-parts rule. FCBH's published claim, recorded as cited, not
// counted book by book (gospel-plugin, 2026-09-27: 4 full Bibles + 5 NTs this adds).
// counted book by book (2026-09-27: 4 full Bibles + 5 NTs this adds).
for (const [iso, c] of Object.entries(T('people-group-pins.json').codes || {})) if (/^(27|66) parts/.test((c && c.au && c.au.vsc) || '')) ntFound.add(iso)
const langAccess = new Map() // iso -> 'some' | 'none' (from the rows: gospel-plugin's ladder)
const langAccess = new Map() // iso -> 'some' | 'none' (from the rows: the access ladder)
const groupsBy = new Map() // iso -> groups on the roster
for (const r of gi.rows) {
const some = (r.access_detail || 'none') !== 'none'
@ -243,7 +243,7 @@ const treeDoc = {
nodes,
}
const resDoc = {
meta: { title: 'Gospel resources for the languages on the board', source: 'gospel-resource-links.json (gospel-plugin): every link already screened', rows: resources.length },
meta: { title: 'Gospel resources for the languages on the board', source: 'gospel-resource-links.json: every link already screened', rows: resources.length },
rows: resources,
}

View file

@ -11,7 +11,7 @@ function setGlobal(name, value) {
* groups in scope. Pick a family on the tree, a country on the map or a facet in the rail, and this lists
* what already exists for those languages: type, scope, source, and the link.
*
* The rows are gospel-plugin's screened links (gospel-resource-links.json, cut to the board's languages at
* The rows are the screened links (gospel-resource-links.json, cut to the board's languages at
* build). Scope is the board's: a language is listed when at least one group in scope speaks it, and the
* "groups here" column counts those groups. CSV exports exactly what is listed.
*/
@ -87,7 +87,7 @@ function exportCsv() {
{ key: 'resource', header: 'resource' }, { key: 'scope', header: 'scope' }, { key: 'source', header: 'source' },
{ key: 'url', header: 'url' }, { key: 'groups', header: 'groups_in_scope' }]
const filters = Object.entries(board.bus.filterSet).map(([k, v]) => `${k}=${Array.isArray(v) ? v.join('|') : v}`).join('; ') || 'none'
const csv = buildCsv(rows.value, cols, { provenance: `# Gospel x Internet resources export — list: ${board.listId.value}; filters: ${filters}${board.treeNode.value ? '; tree node: ' + board.treeNode.value : ''}; rows: ${rows.value.length}; source: gospel-resource-links (gospel-plugin, every link screened)` })
const csv = buildCsv(rows.value, cols, { provenance: `# Gospel x Internet resources export — list: ${board.listId.value}; filters: ${filters}${board.treeNode.value ? '; tree node: ' + board.treeNode.value : ''}; rows: ${rows.value.length}; source: gospel-resource-links (every link screened)` })
try { navigator.clipboard && navigator.clipboard.writeText(csv).catch(() => {}) } catch (_) {}
try {
const url = URL.createObjectURL(new Blob([csv], { type: 'text/csv;charset=utf-8' }))

View file

@ -33,7 +33,7 @@ export const LEVEL_COLOR = { own: '#3b463d', bridge: '#7d3b30', zero: '#1f1f1f'
// ── the English for every CODED label (the catalogue gospelInternet.<family>.<code> holds each locale).
// Used as the call-site fallback when a key is built from a code, so a missing key reads as English, not
// as "read_watch". Wording rules (gospel-plugin): "own" is a resource in their OWN language, not that
// as "read_watch". Wording rules: "own" is a resource in their OWN language, not that
// anyone has heard it; a bridge is a way IN, never coverage, and never called access without "bridge".
export const LABELS_EN = {
level: { own: 'Something in own language', bridge: 'Nothing in own language · bridge only', zero: 'Nothing in own language · no way in yet' },

View file

@ -63,8 +63,8 @@ import { useShadowStyles } from '@map/composables/useShadowStyles.js'
English sentence the fallback exists to protect. */
// C4: the guard is library/composables/useOptionalT.js now. enLookup STAYS as the fallback, passed
// in, so a bare host renders the same ENGLISH it always did -- routing this to the catalogue default
// would make a bare host honour ?lang=ar, which is gospel-plugin's call and not this migration's.
// C4 wave 3a, gospel-plugin's call (2026-09-26): take the LOCALE-HONOURING fallback rather than
// would make a bare host honour ?lang=ar, which is the plugin owner's call and not this migration's.
// C4 wave 3a, the plugin owner's call (2026-09-26): take the LOCALE-HONOURING fallback rather than
// enLookup's English. A partner host that never installed vue-i18n now gets the reader's language,
// with English as the last resort. tStatic resolves 'gospel.<key>' against the same catalogue.
const tr = useOptionalT('gospel')

View file

@ -39,8 +39,8 @@ const assetsBase = computed(() => resolveAssetsBase(profileConfig.value))
every other useI18n() call in this bundle: the host may never have installed i18n. */
// C4: the guard is library/composables/useOptionalT.js now. enLookup STAYS as the fallback, passed
// in, so a bare host renders the same ENGLISH it always did -- routing this to the catalogue default
// would make a bare host honour ?lang=ar, which is gospel-plugin's call and not this migration's.
// C4 wave 3a, gospel-plugin's call (2026-09-26): take the LOCALE-HONOURING fallback rather than
// would make a bare host honour ?lang=ar, which is the plugin owner's call and not this migration's.
// C4 wave 3a, the plugin owner's call (2026-09-26): take the LOCALE-HONOURING fallback rather than
// enLookup's English. A partner host that never installed vue-i18n now gets the reader's language,
// with English as the last resort. tStatic resolves 'gospel.<key>' against the same catalogue.
const tr = useOptionalT('gospel')

View file

@ -45,8 +45,8 @@ import { resolveAssetsBase } from '@map/utils/embedBase.js'
and the shipped English catalogue is the fallback rather than a broken key. */
// C4: the guard is library/composables/useOptionalT.js now. enLookup STAYS as the fallback, passed
// in, so a bare host renders the same ENGLISH it always did -- routing this to the catalogue default
// would make a bare host honour ?lang=ar, which is gospel-plugin's call and not this migration's.
// C4 wave 3a, gospel-plugin's call (2026-09-26): take the LOCALE-HONOURING fallback rather than
// would make a bare host honour ?lang=ar, which is the plugin owner's call and not this migration's.
// C4 wave 3a, the plugin owner's call (2026-09-26): take the LOCALE-HONOURING fallback rather than
// enLookup's English. A partner host that never installed vue-i18n now gets the reader's language,
// with English as the last resort. tStatic resolves 'gospel.<key>' against the same catalogue.
const tr = useOptionalT('gospel')

View file

@ -1003,7 +1003,7 @@ const fill = (str, params) => (params
// pins.approximate into gospel.pins.approximate on the translated path and sent the fallback looking
// under EN_MESSAGES.gospel, where pins.* does not exist. studio caught it as a SEMANTIC conflict
// before it landed; passing '' keeps every key exactly as the caller wrote it.
// C4 wave 3a, gospel-plugin's call (2026-09-26): take the LOCALE-HONOURING fallback. Their reason is
// C4 wave 3a, the plugin owner's call (2026-09-26): take the LOCALE-HONOURING fallback. Their reason is
// the bundle's purpose -- most partners drop an element or an iframe and never install vue-i18n, so
// those hosts got enLookup's ENGLISH while the bundle already shipped all 11 catalogues. "That is a
// bug, not a decision." tStatic honours the runtime locale and keeps English as the LAST resort.

View file

@ -36,7 +36,7 @@
// including a commented-out hit makes a check STRICTER (a false red, cheap). Under-including a real
// hit makes it claim completeness it does not have (a false green, expensive). Only use this stripper
// when a comment mentioning the pattern would be a FALSE POSITIVE you cannot tolerate.
// gospel-plugin's generalisation, worth keeping: "any <!-- --> in a template that quotes an identifier
// A generalisation worth keeping: "any <!-- --> in a template that quotes an identifier
// will keep doing that to you". THE RULE: the stripper must match the LANGUAGE of the text being read.
// For a .vue file, strip HTML comments first, then call this for the script block.