People-group pins say what they are: one shared note, and the groups that share a point are named
A people-group pin is a representative point, not an address, and some pins ARE the same point: on the Doxa roster 2,107 groups sit on 2,048 coordinates, 108 of them on a point shared with another (gospel- plugin's measurement). Every map with people-group pins must say so, in one wording. - library/components/legends/ApproxPinNote.vue: "The dot marks where this people is found, not a town or a boundary." always; when the selected pin's point is shared, "It sits on the same point as <names> — <n> groups share this coordinate…". Never prints the raw coordinate. Wording: gospel-plugin's P5 proposal, in a new pins catalogue (11 locales). - library/utils/colocation.js: the co-location index, built from the roster THE MAP draws (rows or GeoJSON features), never a hardcoded count. - LegendDesktop and LegendMobile (research map, simple map, country posters, cities, UPG-100) show the note under the legend, and under the detail card with the shared names. - check:approx-pin-note (headless, gates:post): the index, the two sentences, no coordinate, Arabic.
This commit is contained in:
parent
29feabb9a0
commit
1ac32fe3d0
17 changed files with 202 additions and 8 deletions
58
internal/check-approx-pin-note.mjs
Normal file
58
internal/check-approx-pin-note.mjs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* check-approx-pin-note — people-group pins say what they are, in one shared wording (APPROXPIN). Headless.
|
||||
*
|
||||
* ASSERTS (1) library/utils/colocation.js: groups on the same coordinate find each other (pairs, triples),
|
||||
* a lone point shares with nobody, rows without a position are ignored, and both roster rows and GeoJSON
|
||||
* features are read; (2) ApproxPinNote renders the first sentence always, the named second sentence only
|
||||
* for a shared point, never a raw coordinate, and in Arabic from the Arabic catalogue.
|
||||
* EXIT 0 pass · 1 fail
|
||||
*/
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join, resolve, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { buildColocationIndex, colocatedWith } from '../library/utils/colocation.js'
|
||||
|
||||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
let bad = 0
|
||||
const check = (name, ok, got) => { if (!ok) bad++; console.log(` ${ok ? 'ok ' : 'FAIL'} ${name}${ok ? '' : ' — ' + JSON.stringify(got)}`) }
|
||||
|
||||
const rows = [
|
||||
{ name: 'Godda', latitude: 12.843781, longitude: 75.247906 },
|
||||
{ name: 'Koraga', latitude: '12.843781', longitude: '75.247906' },
|
||||
{ type: 'Feature', properties: { name: 'Maila' }, geometry: { type: 'Point', coordinates: [75.247906, 12.843781] } },
|
||||
{ name: 'Alone', latitude: 10, longitude: 10 },
|
||||
{ name: 'NoPos', latitude: '', longitude: '' },
|
||||
]
|
||||
const idx = buildColocationIndex(rows)
|
||||
check('three groups on one point find each other', colocatedWith(idx, rows[0]).sort().join() === 'Koraga,Maila', colocatedWith(idx, rows[0]))
|
||||
check('a GeoJSON feature is matched to rows on the same point', colocatedWith(idx, rows[2]).sort().join() === 'Godda,Koraga', colocatedWith(idx, rows[2]))
|
||||
check('a lone point shares with nobody', colocatedWith(idx, rows[3]).length === 0, colocatedWith(idx, rows[3]))
|
||||
check('a row without a position is ignored', colocatedWith(idx, rows[4]).length === 0, colocatedWith(idx, rows[4]))
|
||||
|
||||
const { createServer } = await import('vite')
|
||||
const { default: vue } = await import('@vitejs/plugin-vue')
|
||||
const server = await createServer({ root: ROOT, configFile: false, logLevel: 'error', appType: 'custom',
|
||||
server: { middlewareMode: true, hmr: false, ws: false, watch: null }, plugins: [vue()], resolve: { alias: { '@map': join(ROOT, 'library') } } })
|
||||
try {
|
||||
globalThis.__INTLIFY_JIT_COMPILATION__ = true
|
||||
const Note = (await server.ssrLoadModule('/library/components/legends/ApproxPinNote.vue')).default
|
||||
const { createSSRApp, h } = await import('vue')
|
||||
const { createI18n } = await import('vue-i18n')
|
||||
const { renderToString } = await import('vue/server-renderer')
|
||||
const render = async (lc, props) => {
|
||||
const pins = JSON.parse(readFileSync(join(ROOT, 'library/i18n/locales', lc, 'pins.json'), 'utf8'))
|
||||
const app = createSSRApp({ render: () => h(Note, props) })
|
||||
app.use(createI18n({ legacy: false, locale: lc, messages: { [lc]: { pins } } }))
|
||||
return (await renderToString(app)).replace(/<[^>]+>/g, '').replace(/<!--.*?-->/g, '').trim()
|
||||
}
|
||||
const one = await render('en', {})
|
||||
check('first sentence always', /not a town or a boundary/.test(one) && !/same point/.test(one), one)
|
||||
const two = await render('en', { shared: ['Koraga', 'Maila'] })
|
||||
check('shared point names the others and counts all', /Koraga, Maila/.test(two) && /3 groups share/.test(two), two)
|
||||
check('never prints a raw coordinate', !/\d+\.\d{3,}/.test(one + two), one + two)
|
||||
const ar = await render('ar', { shared: ['Koraga'] })
|
||||
check('Arabic renders from the Arabic catalogue', /[-ۿ]/.test(ar) && /Koraga/.test(ar), ar)
|
||||
} finally { await server.close() }
|
||||
if (bad) { console.log(`APPROX-PIN-NOTE: FAIL — ${bad} case(s).`); process.exit(1) }
|
||||
console.log('APPROX-PIN-NOTE: PASS — pins say they are localities, and name the groups that share a point.')
|
||||
29
library/components/legends/ApproxPinNote.vue
Normal file
29
library/components/legends/ApproxPinNote.vue
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
ApproxPinNote.vue — the ONE wording every map with people-group pins uses (APPROXPIN, 2026-09-26).
|
||||
|
||||
The first sentence always: a dot is where a people is found, not a town or a boundary. The second only
|
||||
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.
|
||||
-->
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = defineProps({
|
||||
/** Names of the OTHER groups on this pin's coordinate; empty = not shared. */
|
||||
shared: { type: Array, default: () => [] },
|
||||
dark: { type: Boolean, default: false },
|
||||
})
|
||||
const { t } = useI18n()
|
||||
const second = computed(() => props.shared.length
|
||||
? t('pins.sharedPoint', { names: props.shared.join(', '), n: props.shared.length + 1 })
|
||||
: '')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p class="approx-pin-note" :style="{ margin: '6px 10px 4px', font: '400 11px/1.4 system-ui, -apple-system, Segoe UI, Roboto, sans-serif', color: dark ? 'rgba(243,243,241,0.7)' : '#5b6470' }">
|
||||
{{ t('pins.approximate') }}<template v-if="second"> {{ second }}</template>
|
||||
</p>
|
||||
</template>
|
||||
|
|
@ -12,6 +12,8 @@ import { useLegendData } from '../../composables/legends/useLegendData.js'
|
|||
import { retryableAsyncComponent } from '../../composables/retryableAsyncComponent.js'
|
||||
import LegendRows from './LegendRows.vue'
|
||||
import PeopleGroupDetail from '../PeopleGroupDetail.vue'
|
||||
import ApproxPinNote from './ApproxPinNote.vue'
|
||||
import { buildColocationIndex, colocatedWith } from '../../utils/colocation.js'
|
||||
// Async-import the heavier branches so the boot bundle stays lean.
|
||||
const LegendMobile = retryableAsyncComponent(() => import('./LegendMobile.vue'))
|
||||
// Language-family tab is now handled by <SemanticTreeLegend> mounted directly
|
||||
|
|
@ -253,6 +255,9 @@ const isMobile = computed(() => uiStore.isMobile)
|
|||
// Legend mode (prayer progress ↔ people group detail)
|
||||
const legendMode = computed(() => uiStore.legendMode)
|
||||
const selectedPeopleGroup = computed(() => uiStore.selectedPeopleGroup)
|
||||
// APPROXPIN: the other groups on the selected pin's coordinate, from the roster THIS map draws.
|
||||
const colocation = computed(() => buildColocationIndex(peopleGroupsForFamilyTree.value))
|
||||
const selectedShared = computed(() => (selectedPeopleGroup.value ? colocatedWith(colocation.value, selectedPeopleGroup.value) : []))
|
||||
|
||||
// Reactive data
|
||||
const isCollapsed = ref(props.initiallyCollapsed)
|
||||
|
|
@ -398,13 +403,15 @@ onBeforeUnmount(() => {
|
|||
</button>
|
||||
|
||||
<div class="legend-content" :class="{ 'no-padding': legendColumns.length > 0 }">
|
||||
<PeopleGroupDetail
|
||||
v-if="legendMode === 'detail' && selectedPeopleGroup"
|
||||
:peopleGroup="selectedPeopleGroup"
|
||||
:hideHeader="true"
|
||||
:dark="isDark"
|
||||
:action="popupAction"
|
||||
/>
|
||||
<template v-if="legendMode === 'detail' && selectedPeopleGroup">
|
||||
<PeopleGroupDetail
|
||||
:peopleGroup="selectedPeopleGroup"
|
||||
:hideHeader="true"
|
||||
:dark="isDark"
|
||||
:action="popupAction"
|
||||
/>
|
||||
<ApproxPinNote :shared="selectedShared" :dark="isDark" />
|
||||
</template>
|
||||
<template v-else-if="legendType === 'language-family'"></template>
|
||||
<LegendRows
|
||||
v-else
|
||||
|
|
@ -432,6 +439,7 @@ onBeforeUnmount(() => {
|
|||
</button>
|
||||
</template>
|
||||
</LegendRows>
|
||||
<ApproxPinNote v-if="legendMode !== 'detail'" :dark="isDark" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import { useLanguageFamilyLegendData } from '../../composables/legends/useLangua
|
|||
import { useAffinityBlockLegendData } from '../../composables/legends/useAffinityBlockLegendData.js'
|
||||
import { useWagfRegionsLegendData } from '../../composables/legends/useWagfRegionsLegendData.js'
|
||||
import PeopleGroupDetail from '../PeopleGroupDetail.vue'
|
||||
import ApproxPinNote from './ApproxPinNote.vue'
|
||||
import { buildColocationIndex, colocatedWith } from '../../utils/colocation.js'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -521,6 +523,13 @@ const caretRotation = computed(() => uiStore.caretRotation)
|
|||
const showPullTab = computed(() => uiStore.showPullTab)
|
||||
const legendMode = computed(() => uiStore.legendMode)
|
||||
const selectedPeopleGroup = computed(() => uiStore.selectedPeopleGroup)
|
||||
// APPROXPIN: the other groups on the selected pin's coordinate, from the roster THIS map draws.
|
||||
const colocation = computed(() => {
|
||||
const rows = []
|
||||
for (const id in (dataStore?.sources || {})) { const src = dataStore.sources[id]; if (src?.features?.length) rows.push(...src.features) }
|
||||
return buildColocationIndex(rows)
|
||||
})
|
||||
const selectedShared = computed(() => (selectedPeopleGroup.value ? colocatedWith(colocation.value, selectedPeopleGroup.value) : []))
|
||||
|
||||
// Legend title — swaps based on legend state and selection
|
||||
const legendTitle = computed(() => {
|
||||
|
|
@ -804,6 +813,7 @@ onBeforeUnmount(() => {
|
|||
:dark="isDark"
|
||||
:action="popupAction"
|
||||
/>
|
||||
<ApproxPinNote v-if="legendState !== 'collapsed'" :shared="selectedShared" :dark="isDark" />
|
||||
</template>
|
||||
|
||||
<!-- Semantic-tree tabs (Languages, People Groups, WAGF Regions):
|
||||
|
|
@ -865,6 +875,7 @@ onBeforeUnmount(() => {
|
|||
</button>
|
||||
</template>
|
||||
</LegendRows>
|
||||
<ApproxPinNote v-if="legendMode !== 'detail' && legendState !== 'collapsed'" :dark="isDark" />
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.legend-sheet-root -->
|
||||
|
|
|
|||
4
library/i18n/locales/ar/pins.json
Normal file
4
library/i18n/locales/ar/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "تشير النقطة إلى المكان الذي يوجد فيه هذا الشعب، وليست بلدة أو حدودًا.",
|
||||
"sharedPoint": "تقع في النقطة نفسها مع {names} — {n} مجموعات تتشارك هذا الإحداثي، لذا يدل الموقع على المنطقة لا على مكان كل مجموعة."
|
||||
}
|
||||
4
library/i18n/locales/de/pins.json
Normal file
4
library/i18n/locales/de/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "Der Punkt zeigt, wo dieses Volk lebt – nicht eine Stadt oder eine Grenze.",
|
||||
"sharedPoint": "Er liegt auf demselben Punkt wie {names} — {n} Gruppen teilen sich diese Koordinate; die Position nennt also die Gegend, nicht wo welche Gruppe lebt."
|
||||
}
|
||||
4
library/i18n/locales/en/pins.json
Normal file
4
library/i18n/locales/en/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "The dot marks where this people is found, not a town or a boundary.",
|
||||
"sharedPoint": "It sits on the same point as {names} — {n} groups share this coordinate, so the position tells you the locality and not which group is where."
|
||||
}
|
||||
4
library/i18n/locales/es/pins.json
Normal file
4
library/i18n/locales/es/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "El punto indica dónde se encuentra este pueblo, no una ciudad ni una frontera.",
|
||||
"sharedPoint": "Está en el mismo punto que {names}: {n} grupos comparten esta coordenada, así que la posición indica la zona y no dónde está cada grupo."
|
||||
}
|
||||
4
library/i18n/locales/fr/pins.json
Normal file
4
library/i18n/locales/fr/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "Le point indique où vit ce peuple, pas une ville ni une frontière.",
|
||||
"sharedPoint": "Il se trouve au même point que {names} — {n} groupes partagent cette coordonnée : la position indique la région, pas où se trouve chaque groupe."
|
||||
}
|
||||
4
library/i18n/locales/hi/pins.json
Normal file
4
library/i18n/locales/hi/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "बिंदु दिखाता है कि यह लोग कहाँ पाए जाते हैं, कोई शहर या सीमा नहीं।",
|
||||
"sharedPoint": "यह {names} के साथ एक ही बिंदु पर है — {n} समूह यह निर्देशांक साझा करते हैं, इसलिए स्थान क्षेत्र बताता है, यह नहीं कि कौन-सा समूह कहाँ है।"
|
||||
}
|
||||
4
library/i18n/locales/it/pins.json
Normal file
4
library/i18n/locales/it/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "Il punto indica dove si trova questo popolo, non una città né un confine.",
|
||||
"sharedPoint": "Si trova nello stesso punto di {names} — {n} gruppi condividono questa coordinata, quindi la posizione indica la zona e non dove si trova ciascun gruppo."
|
||||
}
|
||||
4
library/i18n/locales/pt/pins.json
Normal file
4
library/i18n/locales/pt/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "O ponto indica onde este povo se encontra, não uma cidade nem uma fronteira.",
|
||||
"sharedPoint": "Está no mesmo ponto que {names} — {n} grupos partilham esta coordenada, por isso a posição indica a região e não onde está cada grupo."
|
||||
}
|
||||
4
library/i18n/locales/ro/pins.json
Normal file
4
library/i18n/locales/ro/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "Punctul arată unde se află acest popor, nu un oraș sau o graniță.",
|
||||
"sharedPoint": "Se află în același punct cu {names} — {n} grupuri împart această coordonată, deci poziția arată zona, nu unde se află fiecare grup."
|
||||
}
|
||||
4
library/i18n/locales/ru/pins.json
Normal file
4
library/i18n/locales/ru/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "Точка показывает, где живёт этот народ, а не город и не границу.",
|
||||
"sharedPoint": "Она совпадает с точкой {names} — {n} групп делят эту координату, поэтому положение указывает местность, а не где какая группа."
|
||||
}
|
||||
4
library/i18n/locales/zh/pins.json
Normal file
4
library/i18n/locales/zh/pins.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"approximate": "此点标示该民族所在的地区,而非某个城镇或边界。",
|
||||
"sharedPoint": "它与 {names} 位于同一点 — {n} 个群体共用此坐标,因此位置只表示所在地区,而非各群体的具体位置。"
|
||||
}
|
||||
42
library/utils/colocation.js
Normal file
42
library/utils/colocation.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
const KEY = (lat, lng) => `${Number(lat).toFixed(6)},${Number(lng).toFixed(6)}`
|
||||
|
||||
/** Read [lat, lng] from a roster row or a GeoJSON feature; null when it has no usable position. */
|
||||
export function pointOf(r) {
|
||||
if (!r) return null
|
||||
const c = r.geometry && r.geometry.coordinates
|
||||
const p = r.properties || r
|
||||
const lat = c ? c[1] : (p.latitude ?? p.lat), lng = c ? c[0] : (p.longitude ?? p.lng ?? p.lon)
|
||||
return Number.isFinite(Number(lat)) && Number.isFinite(Number(lng)) && lat !== '' && lng !== '' ? [Number(lat), Number(lng)] : null
|
||||
}
|
||||
const nameOf = (r) => { const p = (r && (r.properties || r)) || {}; return p.name || p.peopleName || p.PeopNameInCountry || '' }
|
||||
|
||||
/** Build once per roster: Map<coordinate key, names[]>. */
|
||||
export function buildColocationIndex(rows) {
|
||||
const idx = new Map()
|
||||
for (const r of rows || []) {
|
||||
const pt = pointOf(r); const n = nameOf(r)
|
||||
if (!pt || !n) continue
|
||||
const k = KEY(pt[0], pt[1])
|
||||
if (!idx.has(k)) idx.set(k, [])
|
||||
idx.get(k).push(n)
|
||||
}
|
||||
return idx
|
||||
}
|
||||
|
||||
/** The OTHER groups on this row's point ([] when the point is not shared). */
|
||||
export function colocatedWith(idx, row) {
|
||||
const pt = pointOf(row); if (!idx || !pt) return []
|
||||
const me = nameOf(row)
|
||||
const all = idx.get(KEY(pt[0], pt[1])) || []
|
||||
const others = all.filter((n) => n !== me)
|
||||
return all.length > 1 ? [...new Set(others)] : []
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
"capability-exemplars": "node internal/generate-capability-exemplars.mjs",
|
||||
"check:a11y-button-names": "node internal/check-a11y-button-names.mjs",
|
||||
"check:app-start-page": "node internal/set-app-start-page.mjs --check",
|
||||
"check:approx-pin-note": "node internal/check-approx-pin-note.mjs",
|
||||
"check:auto-engine": "node internal/check-auto-engine.mjs",
|
||||
"check:backlog-refs": "node internal/check-backlog-refs.mjs",
|
||||
"check:backlog-status": "node internal/check-backlog-status-enum.mjs",
|
||||
|
|
@ -207,7 +208,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: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: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",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- 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: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",
|
||||
"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",
|
||||
"internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs",
|
||||
"library-index": "node internal/generate-library-index.mjs",
|
||||
|
|
@ -259,6 +260,7 @@
|
|||
"cap:sync": "NOT `bunx` — bunx is not on PATH in this environment (command not found, exit 127), which broke cap:sync -> apk:debug -> phone at step one. The CLI IS installed locally (@capacitor/cli 7.6.9); invoke the local bin directly. The rest of package.json already uses the spaced `bun x` form.",
|
||||
"capability-exemplars": "regenerate documentation/reference-capability-exemplars.md — the DERIVED answer to \"which bundle is the capability-N example?\", a question answered WRONGLY in three separate artifacts (a gate, the README, the /plugin-designer skill).",
|
||||
"check:a11y-button-names": "every VISIBLE button must have an accessible name (aria-label / aria-labelledby / visible text — a `title` is a TOOLTIP and does NOT count). Measures the RENDERED DOM through shadow roots: a source grep cannot answer this (132 `<button>` hits, most with visible text). Needs a browser, so NOT in build:unlocked and NOT in CI. ALL=1 sweeps every profile.",
|
||||
"check:approx-pin-note": "people-group pins say they are localities, not towns or boundaries, in ONE shared wording (library/components/legends/ApproxPinNote.vue): the colocation index finds groups sharing a coordinate, the note names them only for a shared point, never prints a raw coordinate, and renders from each locale's catalogue. Headless.",
|
||||
"check:auto-engine": "BROWSER. The default engine is auto: on one map leaf, no token draws with MapLibre and makes zero api.mapbox.com requests, a token draws with Mapbox, and an explicit provider wins. Under the fleet browser policy it is a rega run, not part of gates:post.",
|
||||
"check:backlog-refs": "a card may not depend on a card that does not exist. A dangling depends_on can never be done, so the card can never become ready and nothing says so — it reads as ordinary blocked work forever. FAILS only for depends_on on an OPEN card; a dangling parent or a stale link on a done card is REPORTED, because failing on filing debt teaches people to delete the reference rather than restore the card. Reads all three phase backlogs — references legitimately cross phases. SKIPs when _qa-cycle is absent.",
|
||||
"check:basemap-labels": "the BASEMAP's place names follow the reader's language on BOTH engines (library/maps/localize-labels.js): reads the RENDERED country labels at ?lang=en and ?lang=ru — Mapbox via the Standard basemap's place-labels featureset, MapLibre via each symbol's evaluated text — and requires them to flip. Serves this checkout on an OS-assigned port; --dir <tree> for a negative control. Negative-tested: a bundle built before 319064a1 FAILS on both engines.",
|
||||
|
|
|
|||
Loading…
Reference in a new issue