P2: comments state the reason without quoting anyone or naming a role

Seventeen lines in 15 files. Six quoted short fragments from feedback; eleven used "design note" where a
person once stood ("design note selects 2+ rows", "a design note design call"). Each now states the
reason in neutral terms and keeps the technical content and dates.
All edits are comments, except one string: the safety_note text in
library/scripts/research/derive-city-candidates.mjs, a research script that is not bundled and whose
output is not committed. No logic changes.
This commit is contained in:
Doxa Maps 2026-10-02 08:45:40 +00:00
parent cf0146775b
commit 8b83c34a79
15 changed files with 21 additions and 21 deletions

View file

@ -148,7 +148,7 @@ function assetsBaseFor(depth) {
// depth is measured from the WEB-ROOT; the tree root is one level in, hence depth - 1.
return DEST_CONFIG.assetsBaseUrl || `${'../'.repeat(Math.max(0, depth - 1))}assets`
}
// design note 2026-07-11: the staging TOP must not mention deployment orgs (doxa/GO/...). The visible
// The staging TOP must not mention deployment orgs (doxa/GO/...). The visible
// title is the PRODUCT title (config displayTitle, default org-free), never the deployment TREE_NAME
// (TREE_NAME stays in URLs/paths where it is a folder name, not branding).
const DISPLAY_TITLE = (typeof DEST_CONFIG.displayTitle === 'string' && DEST_CONFIG.displayTitle.trim())

View file

@ -14,7 +14,7 @@
* a rect×rect overlap of the sheet against .mapboxgl-ctrl-bottom-left/-right
* (their VISIBLE children) in every sheet state.
*
* WHY: "it feels wrong on my phone" is not evidence a fix can be checked against. This
* WHY: a sense that the sheet feels wrong on a phone is not evidence a fix can be checked against. This
* probe turns the gesture into a per-step table (finger delta vs measured sheet height),
* the release into an expected/actual state, and the attribution into overlap px² —
* run it BEFORE a fix (baseline json) and AFTER (same json shape) and diff.

View file

@ -233,7 +233,7 @@ const packetOpen = ref(false)
const { scrimRef, scrimStyle } = useViewportScrim(exportMenuOpen)
// Feature flag: show the Export affordance. Gated here in the SHARED legend so
// one switch covers every bundle. It was false by an earlier coder decision;
// design note 2026-07-31 asked for the button back on the 100-list legend, and the
// the button came back on the 100-list legend (2026-07-31), and the
// export path is functional (the menu emits 'export' with 'poster' | 'packet',
// and a profile wires @export to a real download).
// A profile STILL has to opt in with :export-enabled="true" — this flag only

View file

@ -21,7 +21,7 @@
* either edge can be summoned where you reach for it. A HOVERED strip is
* pinned: it never auto-hides (even while you scroll) until you leave it.
*
* RIGHT is the primary side on EVERY form factor (design note, 2026-07-30): people
* RIGHT is the primary side on EVERY form factor (since 2026-07-30): people
* expect a scrollbar on the right, and a scroll hint that moves between edges
* depending on device is the one thing it must not do. An earlier version flipped
* to LEFT on mobile for thumb reach; consistency won. Both strips stay summonable
@ -71,7 +71,7 @@ export function useEdgeScrollAffordance(wrapper, opts = {}) {
const show = (e) => { e.style.opacity = '1' }
const hide = (e) => { e.style.opacity = '0' }
// Primary side: RIGHT on BOTH desktop and mobile (design note, 2026-07-30).
// Primary side: RIGHT on BOTH desktop and mobile (since 2026-07-30).
// It used to flip to LEFT on mobile on a thumb-reach argument, but that made the
// affordance appear on a different edge depending on device — the one place a
// scroll hint must be predictable. People expect a scrollbar on the right, and

View file

@ -699,7 +699,7 @@ function drawSeg(){
const target = isMobile() ? 'mobile' : 'desktop'
const tree = navForTarget(NAV, target)
const open = new Map(tree.filter(n=>n.kind==='folder').map(n=>[n.id, !!n.open]))
// The profile shown on first load (design note: the prayer map is the first map they see).
// The profile shown on first load (the prayer map is the first map a reader sees).
// Declared HERE, above its first use — a `const` further down the module would be in
// the temporal dead zone at this point and throw.
const DEFAULT_PROFILE = 'prayer-progress'

View file

@ -30,7 +30,7 @@
// No church, believer, meeting-point or fellowship data of any kind. This maps
// PUBLISHED RESOURCE AVAILABILITY per language — "does a translation exist" — which
// is public bibliographic fact. It must never gain a field describing where
// Christians are. (design note boundary, 2026-08-01.)
// Christians are. (A standing boundary since 2026-08-01.)
//
// HONESTY RULE: a language absent from eBible is recorded `has_scripture:false`
// with `checked_pools:['ebible']`. That means "not found in the pools we checked",

View file

@ -122,7 +122,7 @@ const stats = {
internet_researched: clusters.filter((c) => c.internet_access !== 'unknown').length,
generated: '2026-08-01',
scope_note: 'Scope is the full 2,106-record DX people-group corpus. No separate "2000 list" artefact exists on disk — verified by search — so this corpus IS the scope.',
safety_note: 'Contains NO gospel-resource, church or believer field, and must not gain one. That join is held pending design note architecture (separate store, never joined to UPG locations in a public artifact).',
safety_note: 'Contains NO gospel-resource, church or believer field, and must not gain one. That join is held pending a separate architecture (separate store, never joined to UPG locations in a public artifact).',
};
if (!existsSync(OUT)) mkdirSync(OUT, { recursive: true });

View file

@ -35,7 +35,7 @@
// Root cause: a one-element dfrac sweep + leaders still drawn beside
// the numbered badges + a DENSE_AT threshold that exists only as a
// comment. Fixing it changes the look of 41 posters in the SHARED
// template, which is a design note design call.
// template, which is a product design call.
// ALSO expect ONE "no crossing count" failure for country-chn: it is
// the 3x4 tier, built by a SEPARATE run, so the 173-country manifest
// does not cover it. That is the guard refusing to call an unmeasured

View file

@ -1446,8 +1446,8 @@ function switchTab(tabId) {
// search query doesn't leak across tabs (qa-buildinng-round-1 Bug 11).
// clearGeocoderProgrammatic uses its internal _geoBeingCleared to skip the legend-
// deselect side effect — pure text reset. Flip the FEATURES flag to keep
// search queries across tabs (the user said "could go either way" — sticking
// with clear-on-switch as the default per A4).
// search queries across tabs (either behaviour is acceptable; clear-on-switch
// stays the default per A4).
if (!FEATURES.geocoderPersistAcrossTabs) clearGeocoderProgrammatic()
const tab = tabs.value.find(t => t.id === tabId)
if (!tab || !map.value) return

View file

@ -1802,8 +1802,8 @@ function switchTab(tabId) {
// search query doesn't leak across tabs (qa-buildinng-round-1 Bug 11).
// clearGeocoderProgrammatic uses its internal _geoBeingCleared to skip the legend-
// deselect side effect — pure text reset. Flip the FEATURES flag to keep
// search queries across tabs (the user said "could go either way" — sticking
// with clear-on-switch as the default per A4).
// search queries across tabs (either behaviour is acceptable; clear-on-switch
// stays the default per A4).
if (!FEATURES.geocoderPersistAcrossTabs) clearGeocoderProgrammatic()
const tab = tabs.value.find(t => t.id === tabId)
if (!tab || !map.value) return

View file

@ -242,7 +242,7 @@ const selCount = computed(() => selectedTerritories.value.length)
const copyFlash = ref('') // transient "Copied!" / "Copy failed" label
let countryNameMap = null // iso3→label, built from live records
// ── R1.2 PIVOT (design note 2026-06-11): MOBILE-FRIENDLY "Batch Select" mode ──────
// ── R1.2 PIVOT (2026-06-11): MOBILE-FRIENDLY "Batch Select" mode ──────
// The selection INPUT GESTURE is a visible toggle button + plain taps — NO
// shift/ctrl/meta (touch devices have no modifier keys; D-8 removes them). When
// batchMode is ON a plain tap on a map `tapestry-fill` feature OR a legend row
@ -670,7 +670,7 @@ useShadowStyles(
'.map-toolbar .mcb.upg-batch-on.dark{border-color:#34d399!important}' +
// ── R1.3 (j): legend MULTI-SELECT marker (batch mode). The SHARED legend only
// paints ONE .selected row (its internal single selectedId). In batch mode the
// design note selects 2+ rows, so the profile marks EVERY legend row that maps to a
// the reader selects 2+ rows, so the profile marks EVERY legend row that maps to a
// selected zone with .upg-batch-row (applied profile-side by markLegendBatchRows
// — we never edit the shared component). Emerald left-rail + tint so multiple
// rows clearly read as selected at once; the dot gets an emerald ring.
@ -3343,7 +3343,7 @@ onBeforeUnmount(() => {
/>
</div>
</div>
<!-- R1.4 (L4 — Brief: single button + progressive disclosure): the land-
<!-- R1.4 (L4 — single button + progressive disclosure): the land-
division HUD renders ONLY while batch mode is ON. The map-rail
.upg-rail-batch (above, in the MapToolbar) is the SINGLE batch entry
point — the old always-visible HUD .upg-batch-btn is gone. Inside batch

View file

@ -125,7 +125,7 @@ const spec = computed(() => ({
{ id: 'map', label: t('tabs.map'), at: [1, 0], config: { profile: 'upg-100-list' } },
],
// The View switcher lives in this element's own header, so the layout's
// "… below ↓" pill would be a second, redundant control (shell design note 2026-08-01).
// "… below ↓" pill would be a second, redundant control (the shell's rule since 2026-08-01).
paneHint: false,
}))

View file

@ -1349,8 +1349,8 @@ onMounted(() => {
border-radius: 12px; overflow: hidden; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
/* SQUARE, NOT A LETTERBOX — the image is the point of the card.
Reported 2026-09-22: the images need to be larger, and more like squares than
rectangles, so the people image is easier to see. The old rule was a fixed
The images are larger, and closer to squares than to wide rectangles, so the
people image is easier to see. The old rule was a fixed
height:130px against a column that is at least 220px wide (see .grid.uupgs-list above), i.e. a
220x130 letterbox — roughly 1.7:1 — which crops a standing person to a band and renders faces small.

View file

@ -213,7 +213,7 @@ const preFiltered = computed(() => rows.value.filter((p) => {
// and `people_praying` is NOT in the field list they request — they fetch
// people_committed. So the field is always undefined and the filter matches nothing.
// MEASURED on their live /regions/india: 855 rows before, 0 after clicking Engaged.
// The scope ruling is "it exists on DX-life and must MATCH", so this reproduces their
// The scope rule is that what exists on DX Life must match it here, so this reproduces their
// result rather than quietly correcting it. Reading people_committed instead would make
// us DIFFER. Raised upstream as a product question: the fix is one word, in their file.
if (k === 'engaged') { if (!((p.people_praying ?? 0) > 0)) return false; continue }

View file

@ -225,7 +225,7 @@ useShadowStyles(`
tabs share ONE radius variable and can never drift. This bar only sizes them to its own control scale
and restores the tap target — the same treatment the Share button already had, now covering both so
the ⓘ matches it exactly. Scoped to .rd-topbar, so the map toolbars keep their 40px round buttons.
Reported 2026-09-28 ("too large"): ShareButton is a 40px round map control + a 2px rainbow ring, 44px
At map size they were too large for this bar: ShareButton is a 40px round map control + a 2px rainbow ring, 44px
in a 38px bar. Here both take 28px (+ Share's ring = 32px, the search box's height), icon 16px. */
.rd-topbar .rd-header-btns{ display:inline-flex; align-items:center; gap:8px; flex:0 0 auto; }
.rd-topbar .rd-header-btns .mcb{ --map-btn-size:28px; position:relative; }