check:poster-browser-equiv is chained in gates:pre and exited 75 NOT MEASURED
every run. Its input, out/_records.json, is gitignored and written only by
generate.mjs against a live API, so on CI and on any box without credentials the
gate never looked at anything. The runner reported that honestly rather than as
a pass, but an identical NOT MEASURED every run is indistinguishable from an
unwired gate -- and this is the gate that proves a sheet generated in the browser
is byte-identical to the one Node writes.
An equivalence check needs the SAME input on both sides, not fresh input, so a
committed fixture loses nothing it was measuring. The fixture is the 124 public
records the two arms actually consume: Nepal's 34 for the country arm, and the
90 slugs the Gujarat zone selects for the zone arm. Same fields the served maps
show; no other data.
Verified equivalent to the full 2,106-record cache: identical zone pins in the
same order, identical Nepal set, count 90, pop 3,164,770, series {2209, 28, 1}.
Record ORDER is load-bearing -- it drives draw and label order -- so the fixture
keeps the cache's order. A first cut sorted it: every aggregate matched and the
pin SET matched, while the order differed. Sorting a fixture would have quietly
changed the sheet it produces.
generate.mjs gains RECORDS_FILE so a caller can name the offline input instead of
only the API-written cache. It still fails closed without an API when no offline
file is given. The gate passes it the very file the browser arm read, so both
sides are fed the same records by construction.
The gate now says which input it measured, because the full cache widens coverage
to all 175 countries where it exists and the fixture is the floor everywhere else.
Two-sided, on this tree: with the fixture it PASSES byte-identical on both arms
(834,174 and 921,522 bytes). Dropping the province boundaries at
posterSheets.js:206 -- the defect this gate's header records, where a country
beside India or China printed without them -- makes the country arm 251,576
against Node's 921,522 and the gate exits 1. Restored, it passes again.
|
||
|---|---|---|
| .. | ||
| data | ||
| fixtures | ||
| snapshots | ||
| build-gallery.mjs | ||
| build-hd-gallery.mjs | ||
| emit-countries.mjs | ||
| export-public.mjs | ||
| generate.mjs | ||
| make-previews.py | ||
| osm-basemap.mjs | ||
| packet-strings.json | ||
| packet.mjs | ||
| pin-poster-render.mjs | ||
| poster-regions.mjs | ||
| publish-html.mjs | ||
| README.md | ||
| series.json | ||
| serve-persist.sh | ||
| serve.sh | ||
| shoot.mjs | ||
| verify-posters.mjs | ||
library/scripts/posters/ — UPG-100 poster generator
Renders one 3ft × 3ft print poster per zone (28 today) for the my-upg-100-list profile, from the profile's own data. It ships inside the repo alongside that data on purpose — reaching for the data via an env var once silently rendered stale posters (Nepal, Bangladesh, Bhutan vanished).
bun run posters -> posters:shoot -> (WebP previews) -> bun run build
HTML per zone PNG @3000² assets/previews/ app shows them
generate.mjsis the template;shoot.mjsscreenshots to PNG (Firefox only — privacy-first policy).- A template change is not visible in the app until previews are regenerated and the app rebuilt.
- Exported PNGs go to a sibling folder (
../../../upg-100-posters/), neverpublic/— Capacitor packagespublic/wholesale. snapshots/holds frozen template versions (v5 is the approved current); each has its own README.
Full reference — files, env vars, gotchas, measurement discipline: ../../../documentation/architecture/poster-generation.md.