Commit graph

1055 commits

Author SHA1 Message Date
Doxa Maps
cdbda65a06 People-group names: the threshold pinned, no trust without Latin evidence, raw indexing flagged
Folds in the review's four points:
(a) check:pg-name-gate plants a row with EXACTLY two far Latin locales, which must
    show English, so a rule that needs three fails.
(b) A row with fewer than two Latin names cannot be checked (117 rows), so it now
    shows English too. The whole table: 8,172 of 21,419 (38.2%).
(c) The one-reader scan also flags a file that imports the helper but still
    indexes rows itself (`.names[`), with negative controls both ways.
(d) The helper documents that callers always pass `english`.
Seven mutants of the helper each fail the gate.
2026-10-01 06:04:41 +00:00
Doxa Maps
6e80da212c People-group names: one helper decides when a localized name can be shown
library/utils/peopleGroupName.js (pure) returns a group's name in the reader's
language only when the row's names are transliterations. When two or more of its
Latin-script names (fr pt es tl id de) are far from the English one (bigram Dice
< 0.5), the row was machine-translated as words (Balantak → "Balance",
Balo → "Sac à dos"), and every locale of that group shows English. That is
8,055 of 21,419 rows (37.6%). it and ro have no column and show English; zh
reads zh_CN. A fallback shows the caller's own English name.

check:pg-name-gate (gates:pre) proves it on the real table: the 4 known
translated rows show English in 7 locales, Balangao Bontoc keeps its names,
planted rows are two-sided, the whole-table fallback stays between 30% and 45%,
and no file outside the helper reads pg-names rows (pplr-data-maps is the
exception; the scanner is negative-controlled). Five mutants of the helper each
fail it. No consumer is wired yet.
2026-10-01 05:52:31 +00:00
Doxa Maps
b6777218e3 Merge remote-tracking branch 'origin/development' into poster-design-plugin/zero
# Conflicts:
#	public/dx-maps/doxa-maps-spa/manifest.json
#	public/dx-maps/manifest.json
#	public/dx-maps/my-upg-100-list/my-upg-100-list.js
#	public/dx-maps/my-upg-100-list/poster-wall/index.html
#	public/dx-maps/my-upg-100-list/upg-100-list/index.html
#	public/dx-maps/my-upg-100-list/upg-100-mfe/index.html
2026-10-01 05:18:28 +00:00
Doxa Maps
289d31e3f4 Merge remote-tracking branch 'origin/development' into upg100/batch-tap
# Conflicts:
#	public/dx-maps/doxa-maps-spa/manifest.json
#	public/dx-maps/manifest.json
#	public/dx-maps/my-upg-100-list/my-upg-100-list.js
#	public/dx-maps/my-upg-100-list/poster-wall/index.html
#	public/dx-maps/my-upg-100-list/upg-100-list/index.html
#	public/dx-maps/my-upg-100-list/upg-100-mfe/index.html
2026-10-01 05:12:05 +00:00
Doxa Maps
f3851da57c PosterWall: sectionItemCountKey, so one section can be counted in its own words
The Country Posters India section headed "855 people groups · 37
countries", which is untrue: its cards are India's own and one per state
and union territory. sectionItemCountKey (default null: every section
counted with item-count-key, as before) lets a wall answer, per section,
an i18n plural key, or { key, n } to count n rather than the section's
card count, or null to keep the default.

Walls that do not pass it render byte-identical trees before and after
(a differential render of an upg100-style and a country-posters-style
wall). check:poster-wall-host-seams arm K: a named section counts in its
own key and number, an unnamed one keeps the default; it fails with the
prop ignored.
2026-10-01 05:10:55 +00:00
Doxa Maps
24f3e4176a PosterWall: a section head names its people-group total only when there is one
A section head read "<n> people groups · <items>" always, so a wall whose
cards count no people (Poster Design's gallery of templates) showed
"0 people groups · 4 lists" on every head. The total and its " · " now
leave together when the total is 0, the rule the cards already follow with
v-if="item.count". The head is still one string in one interpolation, so a
section with a total renders exactly the text it always did, whitespace
included: a differential render of an upg100-style wall and a
country-posters-style wall, before and after, gives identical trees.

check:poster-wall-host-seams gains arm J (a head with people keeps its
total; one without reads its items alone); the old head line fails it.
2026-10-01 05:10:55 +00:00
Doxa Maps
c5687142ae Merge origin/development into studio/scrub 2026-10-01 00:46:07 +00:00
Doxa Maps
7b1730e8aa Comments name what measured a fact, not who: the tree generator, the Mapbox draw gate, the profile metadata and the admin proxy 2026-10-01 00:07:34 +00:00
Doxa Maps
51524b7897 A poster series for India's states and union territories; series-isolation sees shared wording and poster text correctly
series.json: a new `indiaState` series, modelled on `country`, so a state poster names itself honestly
("India State Maps", "What is a State Prayer Map?", one sheet for each state and union territory of India,
"which state God has put on their heart"). The country series' text would have called Gujarāt one of the
~170 Country Maps. Approved as the default wording; shown as editable.

check:series-isolation, two blind spots found by adding a third series:
- Shared wording was dropped from only the EARLIER series (sets were mutated while still being compared),
  so the later one kept it as a fingerprint and the earlier failed for carrying it. The shared set is now
  found on the original sets. Self-test: three series in any order; the old loop fails it.
- The contamination scan never read a series' `poster` block, where a country poster once printed the
  UPG-100 side box. It does now. The phrases that NAME country and indiaState are hand-listed, so a
  verbatim copy of one's framing into the other is caught even though symmetric dropping treats it as shared.
  Self-test 9/9, including both directions of a country/state copy.

countrySheet.js: the seriesFor comment no longer says "map 1 of 0" was printed; it was latent (the map-only
packet page prints no series line, and the India poster is gated), as a rendered check showed.
2026-09-30 23:33:34 +00:00
Doxa Maps
9f246e4710 UPG-100 batch-tap gate: the pin handler adds no second territory toggle (one pin tap in batch mode = exactly one toggle, from the tapestry listener); a double-toggle variant FAILS 2026-09-30 23:18:11 +00:00
Doxa Maps
2e6f6a59e6 UPG-100 batch mode: a tap on a people-group pin selects the territory under it and opens no detail sheet (the sheet covered the whole batch HUD on a phone, and in a dense state nearly every tap lands on a pin); outside batch mode pins behave as before. Gated; my-upg-100-list rebuilt with the tree and SRI 2026-09-30 23:17:20 +00:00
Doxa Maps
1f2dc22cbd composite-interop: Poster Design has no cross-pane links, by design
Its two panes, Styles and Sizes, are independent views of the poster
templates; neither drives the other, so it gets a NO_LINKS entry with
that reason rather than invented links.
2026-09-30 22:54:56 +00:00
Doxa Maps
28c638d7ec Comments name no internal hosts or teams; the A9 leaf-hint comment every page carries is neutral 2026-09-30 22:36:47 +00:00
Doxa Maps
c8d6d2cb48 UPG-100: an ADM1 territory always knows its country (the baked table split into India's 34 and China's 12, same data), so the regroup warning is right even for a territory whose feature did not say; gated 2026-09-30 22:35:31 +00:00
Doxa Maps
3060619d93 Merge origin/development into studio/fake-token 2026-09-30 22:29:31 +00:00
Doxa Maps
3e5646832d UPG-100 batch selection Copy: a selection apply-grouping cannot regroup in one go (a mix of states, provinces and countries, or a legend zone) still copies, and the flash now says its first line will not regroup. regroupable() is gated against apply-grouping's own dry-run verdict in six cases 2026-09-30 22:28:37 +00:00
Doxa Maps
1d2b29d6b5 Merge origin/development into studio/land-pd-seam 2026-09-30 22:23:58 +00:00
Doxa Maps
ca9d210d8c Universal-props gate: the planted token sample is assembled at runtime, not committed as a literal
The TOK-LITERAL self-test planted a string shaped like a real Mapbox token,
which is itself what the rule forbids and reads as a leaked credential to any
secret scan. The sample is now built at runtime from plainly fake parts
('example', 'not-a-token') in the same pk. + base64url + tail shape, so the
self-test still proves the rule fires, and no token-shaped literal is left in
the tree.
2026-09-30 22:22:09 +00:00
Doxa Maps
356e1063dd Merge PanelSheets (wall-of-people-groups-plugin/work, 3ee5ed9fd..f5ec4a180) with the PosterWall host seams, for one capture 2026-09-30 22:09:52 +00:00
Doxa Maps
332aa002f8 Wall of People Groups: the Bless poster takes its sheets, glue bands and marks from PanelSheets, byte for byte
blessPoster.js now plans its 5 x 8 ft sheets, finished size, screen layout, glue band, trim line and sheet
tags through library panelSheets.js instead of its own copy. The print HTML, the screen layout and the plan
are unchanged: hashed for 3 presets and the default in all 11 locales (44 cases), before and after, all
identical; a 0.001 in change to the glue label moves all 44.

check:panel-sheets (gates:pre, headless) pins both geometries (60x96 stacked, 66x66 side by side, one
sheet; glue and bleed):
- the sheets tile the finished poster;
- glue slices tile it, and bleed slices repeat exactly the join;
- a bleed join prints a guide, not a solid band, and a screen preview always shows the red band;
- the Bless 5x8ft takes its bands and marks from the module.
It is two-sided: 4 planted defects are caught, and the Bless arm fails on the old private copy.
2026-09-30 22:03:40 +00:00
Doxa Maps
47a856c144 Merge origin/development (939db5b6c: P0 landed, posterverse, csv-guard) into upg100/india-states; package.json keeps both sides' gates; rebuild my-upg-100-list from the merged source, and regenerate the tree and SRI 2026-09-30 21:49:47 +00:00
Doxa Maps
939db5b6c7 Merge origin/development (e9805a5c8: csv-guard, posterverse) into upg100/batch-select; package.json keeps both sides' gates; rebuild my-upg-100-list from the merged source, and regenerate the tree and SRI 2026-09-30 21:42:50 +00:00
Doxa Maps
ba8dfad628 UPG-100 batch selection: an India state or union territory now finds its people groups, from the served states table (fetched only once a state is selected, warmed on selection); China's provinces stay named as missing until they have a table. The gate asserts the wiring 2026-09-30 21:33:43 +00:00
Doxa Maps
39ded339fa Merge upg100/batch-select (P0 batch selection, 2bf2607fc) into upg100/india-states: P0b's India detection builds on P0's selectionPeople. package.json keeps both new gates 2026-09-30 21:33:24 +00:00
Doxa Maps
d9b237ac19 PosterWall seams: gate the second-base retry, observe the wall's scroller
check:poster-wall-host-seams gains arm H: a default wall's broken stored
webp retries the tree's own copy before the placeholder. Arm I: a generated
preview that fails shows the placeholder at once. The gate serves embedBase.js
as if from a built tree, because the second base is derived from that URL and
is empty from source, which let a mutant that skipped the retry pass.

previewSrc's observer now uses the wall's own scroller as its root (the
viewport when the wall is embedded and the page scrolls it). A pane wall
scrolls inside .wall, which clips its cards, so with the viewport as root the
200px margin never looked ahead. Arm E asserts the root.

PosterWall states the two limits of previewSrc: a timeout ends the wait, not
the render, and the page cache never revokes the URLs it holds.
2026-09-30 21:29:17 +00:00
Doxa Maps
7cc28b7aee PosterWall: a previewSrc seam so a host can supply each card's thumbnail
A card's thumbnail was always the stored previews/<id>.webp. A gallery of
poster templates drawn from the library has no stored file, and must show a
library change the moment it lands, so the thumbnail can now come from the
host: previewSrc: async (item) => an image URL | null.

Default null keeps the stored webp and creates no observer, so every current
wall is unchanged. When set, a card renders only once it is in view, one
render at a time, cached on the page by instanceId + item id. Until it answers
the card shows its label; null, a throw or no answer within 20 s shows the
existing "preview unavailable" placeholder.

The header-action gate becomes check:poster-wall-host-seams and covers both
seams: arms D to G for previewSrc (default webp and no observer; lazy, one at
a time and the null fallback; the timeout; the cache). Six mutants of the new
code each fail it.
2026-09-30 21:21:27 +00:00
Doxa Maps
307feab55c UPG-100 batch selection Copy: line 1 is a machine contract, so it carries the names apply-grouping resolves in every UI language (a country as its ISO3, a state as its shape name); the reader's names stay on line 2, in the HUD and the CSV. The gate runs apply-grouping's own dry run on an Arabic selection, with a negative control 2026-09-30 21:19:02 +00:00
Doxa Maps
bc3a5029a6 Tab-radius gate: find Collapse's rule inside a selector list
The home link joined Collapse's rule (.vtl-collapse, .vtl-home { … }) so the
two share one box, and the gate only recognised a rule that named
.vtl-collapse alone, so it found no reference and failed on development. It
now reads the reference from any rule whose selector list names
.vtl-collapse as a whole selector (not :hover, -tx or a descendant), and its
self-test gains a poison that moves only that rule's radius.
2026-09-30 21:17:42 +00:00
Doxa Maps
79b949cb24 WIP P0b: India's states and union territories table (derived from admin-membership, served as an asset, sha384-pinned in the same emitter run), its loader, the tree allowlist row, and check:india-states (not yet registered) 2026-09-30 21:17:28 +00:00
Doxa Maps
b3d6db3c59 PosterWall headerAction: a countless item's sticker reads the label alone
A template card (Poster Design) carries no people-group count, and the new
branch printed a bare ' UPG ·' before its label. The count and separator now
render only when item.count is set, in the headerAction branch only; the
default toggle is unchanged. check:poster-wall-header-action gains arm C for
it, which fails with the guard removed.
2026-09-30 21:11:33 +00:00
Doxa Maps
b138c876d5 CSV exports: a value starting with TAB or CR is neutralised too, and the gate asserts it
esc() prefixed a single quote to a value starting with = + - @, but OWASP's
formula triggers also include a leading TAB and CR, which a spreadsheet can
strip before reading the = behind them. Every map's CSV export shares this
escaper. The shape gate carried a formula probe but never checked it came
back neutralised; it now does, for =, TAB and CR, by slug.
2026-09-30 21:09:29 +00:00
Doxa Maps
58242987c8 PosterWall: a headerAction seam for the card's header sticker
The sticker on every poster card was hard-wired to "Show on map". A wall with
no map beside it (a gallery of poster templates) needs the same card with a
different action, and a fork of the card would drift from the wall.

headerAction null (the default) renders the existing toggle unchanged: the
same markup, aria-pressed, the is-last highlight and the window event, so
every current wall is untouched. { labelKey, ariaKey } renders a plain button
labelled from those keys that emits 'header-action' with the item and
dispatches no window event.

check:poster-wall-header-action (gates:pre) mounts the real PosterWall
headlessly on a recording renderer and calls the sticker's onClick in both
modes. Four mutants fail it: no seam, a new branch that also dispatches, a
default that emits, and aria-pressed on the new branch.
2026-09-30 21:07:37 +00:00
Doxa Maps
ee955bac5a UPG-100 batch selection CSV: also neutralise a leading TAB or CR (OWASP's full list of formula triggers), asserted by the gate 2026-09-30 21:04:51 +00:00
Doxa Maps
8c75220222 Legend-source gate: the baseline follows the live corpus again (2078 unengaged, 29 engaged)
One more people group moved from Unengaged to Engaged upstream; the total is
still 2107. The maps compute from the live rows and already show the new
split (read on Prayer Progress's engagement legend), no shipping code
hardcodes the totals, and the live endpoint was read independently, so only
this baseline lagged.
2026-09-30 20:54:22 +00:00
Doxa Maps
dd913f7269 UPG-100 batch selection: recolour, Copy and Export CSV are back. The colour swatches return; Copy puts the territory line apply-grouping parses first, then the selection's people groups as CSV; Export CSV downloads the same CSV. One detection feeds every action, prefetched when batch mode opens so a tap copies from cache. New gate check:batch-selection-export (gates:pre) 2026-09-30 20:53:19 +00:00
Doxa Maps
f963d90bc3 Packet: the lightweight worksheet shows every group's photo and short description too (a photo cell and a two-line description per row, 16 + 18 rows a page); the variant gate asserts both headless, the render gate counts the rows' photos and follows the new pagination 2026-09-30 12:04:20 +00:00
Doxa Maps
3211236014 Packet: point 4's reference names what prints, Mark 5:18–20, in all 11 locales (the Gerasene reading keeps 5:12–20), re-pinned; the passage gets a 700-char ceiling beside its 250 floor, in the render gate and, headless, in the structure gate for every locale 2026-09-30 11:55:51 +00:00
Doxa Maps
f5fa9e05b1 WIP packet: Mark 5:18-20 back on the points page (source) 2026-09-30 11:38:02 +00:00
Doxa Maps
2dbd567945 WIP packet 10:58 (the dictated points, point 4's statement + lead-in, the wheel turned with Start here; re-pinned; 5 bundles rebuilt): measured, reads pending 2026-09-30 11:17:25 +00:00
Doxa Maps
911f0878a6 Merge remote-tracking branch 'origin/development' into upg100/mark5 2026-09-30 10:35:51 +00:00
Doxa Maps
011afa6440 Merge remote-tracking branch 'origin/development' into repo/mobile-app-howto 2026-09-30 10:27:50 +00:00
Doxa Maps
b4d26d1940 Merge remote-tracking branch 'origin/development' into upg100/mark5
# Conflicts:
#	public/dx-maps/doxa-maps-spa/manifest.json
#	public/dx-maps/doxa-research-map/doxa-research-map.js
#	public/dx-maps/doxa-research-map/research-map/index.html
#	public/dx-maps/doxa-research-map/research-map/maplibre/index.html
#	public/dx-maps/manifest.json
2026-09-30 10:24:16 +00:00
Doxa Maps
8e6664539f Merge origin/development into studio/rudder-circle 2026-09-30 10:08:20 +00:00
Doxa Maps
f3fdf4bf37 Mobile apps from plugins: a how-to, with Deep Research as the worked example
documentation/how-to-build-mobile-apps-from-plugins/README.md explains why Deep Research
(doxa-maps-spa, its app preset) is the plugin to wrap first: the seven production maps in one
phone-ready shell, token-free on MapLibre. It then walks the whole path: tools, the wiring check,
one command, what each step does, another plugin as the app, making it your own, and fixes.

The one command needed two small changes; the default app is unchanged:
- internal/set-app-start-page.mjs takes APP_START=<plugin>/<screen>[/<preset>] and opens that page
  instead of the UPG-100 list. Unset, it behaves and marks the page exactly as before. A malformed
  value, a page missing from the build, or links that would not resolve are refused, as before.
- package.json: phone:from-tree is sync, start page, verify, assemble, install from the committed
  tree, with no full rebuild (bun run build refuses without BUILD_ALLOW=1).
  phone:deep-research is phone:from-tree with Deep Research as the start page.

how-to.md links the guide, and two of its lines are corrected: webDir is public/dx-maps/, and
apk:debug's first step is the full build, which asks for BUILD_ALLOW=1.

Checked, with no Android tools: the start page written for doxa-maps-spa/doxa-maps-spa/app, then
the app root served and loaded in the shared browser at 390 and 1280. It showed the gallery
element, 15 tabs (the seven maps and the experimental ones) and a drawn map, with 0 console errors
and 0 failed requests. --check passed for APP_START and failed for the default until it was
restored; the default run still writes the UPG-100 page with its original marker. The Gradle and
adb steps are those bun run phone already uses; they were not run here.
2026-09-30 10:04:34 +00:00
Doxa Maps
efcd08a40c Share policy: record the regions dashboard's one share button 2026-09-30 09:57:20 +00:00
Doxa Maps
86034174b0 WIP Mark 5 page (source) 2026-09-30 09:32:33 +00:00
Doxa Maps
fdb06b8b56 Phone pages: the app's height no longer changes as the address bar hides or shows
The host page of every full-page map (and the Plugin Studio) sized the
element to the viewport in a way that tracks the phone's address bar:
- the built fixed-viewport leaf used position:fixed with inset:0, which
  follows the DYNAMIC viewport, so the map and its legend sheet resized
  on every scroll that moved the bar;
- the Plugin Studio page and the dev server's leaf and studio pages used
  100vh, the LARGE viewport, taller than the view while the bar shows, so
  the page itself scrolled by the bar's height and the app seemed to
  resize.
All four now use height:100svh (100% first as the fallback where svh is
unknown), anchored top-left, and set --profile-min-h:0 so ProfileLoader's
100dvh floor cannot grow the element when the bar hides. On a desktop svh
equals the viewport, so nothing moves there. Scroll-page leaves (the
cap-4 stacks) are unchanged.

Regenerated the tree: 51 leaf pages and the Plugin Studio page change
only this rule and its comment; no bundle changes.
2026-09-30 09:31:46 +00:00
Doxa Maps
fa2a7fc0e7 Merge branch 'upg100/p6-card-row-dev' into upg100/land-pair
# Conflicts:
#	public/dx-maps/country-posters/countries-map/index.html
#	public/dx-maps/country-posters/country-poster-wall/index.html
#	public/dx-maps/country-posters/country-posters-mfe/index.html
#	public/dx-maps/country-posters/country-posters.js
#	public/dx-maps/doxa-maps-spa/manifest.json
#	public/dx-maps/manifest.json
#	public/dx-maps/my-upg-100-list/my-upg-100-list.js
#	public/dx-maps/my-upg-100-list/poster-wall/index.html
#	public/dx-maps/my-upg-100-list/upg-100-list/index.html
#	public/dx-maps/my-upg-100-list/upg-100-mfe/index.html
#	public/dx-maps/regions-dashboard/region-dashboards/index.html
#	public/dx-maps/regions-dashboard/regions-dashboard.js
#	public/dx-maps/regions-dashboard/regions-list/index.html
#	public/dx-maps/regions-dashboard/regions-mfe/index.html
2026-09-30 08:33:01 +00:00
Doxa Maps
8105b1e76a Prayer packet: point 4 prints a short reading list, and the 12 points are larger
Point 4 (READ THE WORD) printed Mark 5:1-20 in full at 14px, the longest
and smallest text in the packet. It now prints THE HEAVENLY FATHER'S
HEART: one heading, one framing line and seven short readings (Noah,
Lot, Rahab, the Gerasene, Cornelius, Lydia, the jailer), each a
household the Father saved through one person. The names come from each
locale's block; the addresses stay canonical English in the data and the
book is named in the packet's language, like every other reference
(Genesis and Joshua added to the 11 books tables).

That room goes to the twelve points: body 17 -> 22px, heading 19 -> 24px,
references 14 -> 17px, and 13 -> 16px between points. The page still fits
one sheet in all 11 locales (the tightest, de, ends 59px above the
bottom).

The gates that asserted the full passage (packet-render, packet-browser,
poster-prints) now assert the list: a .p4-heart block with its seven
readings. Rebuilt: my-upg-100-list, country-posters, regions-dashboard,
doxa-research-map, upg-neighbor; the tree and SRI regenerated.
2026-09-30 08:29:54 +00:00
Doxa Maps
d12380ce22 Poster walls: a card contains its print row when the row wraps
At two posters per row on a phone (a ~162px card) the print row wraps to
three lines, and because it is a sticker pinned over the banner's bottom
edge that grows downward, it hung about 45px below the card's white box.
Each card is now fitted to its own row: a ResizeObserver on every .pfs
writes --pfs-room on its card when the row hangs past the white box (the
part below the banner, plus 8px of daylight, that the 26px bottom padding
does not cover). A row that fits writes 0, so every card that already
contained its row is unchanged.

check:poster-card-fits asserts it: a print row may not end below its own
card (below-card N). Rebuilt: my-upg-100-list, country-posters,
regions-dashboard; the tree and SRI regenerated.
2026-09-30 07:55:03 +00:00