PROCITY/web/assets/towns/SOURCES.md
m3ultra bee7563a76 Lane E R27 (v5.0): provenance v5-final — and the roster gap the freeze caught (ledger #6)
THE BUG THE FREEZE FOUND: redhill_godverse was ON DISK but NOT IN THE ROSTER — for three rounds. The v5
provenance table needed real numbers, so I measured instead of carrying the v4 row forward. The count
didn't match: 23 caches on disk, 22 rostered. The missing one was redhill_godverse — THE TOWN THE ENTIRE
v5 EPOCH WALKS TO.

Mine, squarely. index.json is Lane E's file (write_index()). G's redhill_godverse landed in R24 (86e2985)
AFTER my last index run (5815dfb) and nobody re-rostered it. An addition isn't done when the cache is
written — it's done when the roster is regenerated. That is the exact mirror of my own R24 lesson ("a
retirement isn't done when the cache is deleted; it's done when the consumers are swept"): I wrote the
lesson for deletions and missed its reflection for additions.

WHY FOUR GREEN ROUNDS NEVER CAUGHT IT — a gate-shape lesson. Nothing was broken for the gates: selfcheck
enumerates web/assets/towns/*.json DIRECTLY (so it saw the town; A even pinned its goldens) and F's crate
smoke boots BY KEY (?town=redhill_godverse). The roster's only consumer is B's HUD selector (hud.js, my
R20 ledger #2 design). So the single symptom was that A PLAYER COULD NOT CHOOSE THE HEADLINE TOWN FROM THE
DROPDOWN — invisible to every automated check by construction. A gate that reads around the artifact under
test cannot test it: the vacuous-gate species, one layer out.
Fixed: re-ran write_index() -> 23 rostered, 23 on disk, 0 missing. Only index.json moved (every cache
byte-identical again). selfcheck ALL GREEN 156,212, golden 0x3fa36874 untouched. qa.sh --strict --matrix
7 passed / 0 failed / 0 warn.

-> F (#5, the tour): the money shot walks Musgrave Road into Monster Robot Party. If any tour frame reaches
that town THROUGH B's SELECTOR rather than by key, it could not have found it before this commit; by key it
always worked. Worth one glance at your framing path.
-> B: the dropdown now carries both godverse towns. Your `${t.state ? ', ' + t.state : ''}` handles their
empty state gracefully — nothing needed, and the defensive write is why this cost nothing cosmetically.
-> G: your godverse caches carry no `state`, so they roster as state:''. Harmless (B degrades cleanly). For
"Red Hill, QLD" in the picker, emit `state` — write_index() already prefers the cache's own field.

PROVENANCE v5-FINAL. The ⟨v5.0-FINAL⟩ rows are measured, not inherited — the town-pack row still claimed
v4's "22 towns / 1192 shops", stale since my R24 ballarat retirement and redhill build (now 21 real / 1196
shops / 299 heroes, + both godverse towns = 23 rostered). Added the two v5 art rows: the real-stock atlas
(1 crate, 120 items, Monster Robot's own POS, sku_ ids) and the mint atlases (14 crates, 224 items, seeded
from real dealgod listings, mint_ ids). These are the table's FIRST AMBER ROWS — everything before v5 was
green web-ok; real product photography is the first asset class this project ships that a human must clear
before public release.

I VERIFIED MY OWN PROSE AND IT CAUGHT ME: I first wrote the mint crates as "6 record · 6 book · 3 toy" —
that is the breakdown of all FIFTEEN crates, which includes Monster Robot's REAL record crate. The 14 mint
crates are 5 record · 6 book · 3 toy. A count attributed to the wrong population, one line from being
frozen into the release record. Every other claim in the table re-measured true (21/1196/299/8 states/23
rostered/roster==disk/72/37/120 items/224 items/both zones in-house-green).

THE LICENCE LINE'S STORY, recorded as a mechanism. The amber rows are guarded by exactly one printed line,
so the freeze says why it took two rounds to make honest: R23's glob matched 0 files and returned 0; R25's
permissive `or` fallback let the print go silent while the check still passed — the flag stopped reaching a
human on real product photos while the gate said OK. Now: one spelling, one lookup with one owner, and the
line is COUNTED (judged N => N lines, or FAIL). Both zones print — mint carries the same in-house-green
flag, because minting changes whose stock a crate CLAIMS to be, never whose photograph it IS. Frozen today
at: atlas-QA OK — 15 per-shop atlas(es) valid, 15 licence line(s) shown, 0 warning(s).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:59:49 +10:00

7.2 KiB
Raw Blame History

Town-cache data sources & licence (web/assets/towns/)

Lane E owns this file (ROUND17 ledger #6). Every real-town cache here is derived from OpenStreetMap data and ships under OSM's licence. This notice + the license/attribution fields on every <key>.json are the attribution that travels with the data.

Licence — OpenStreetMap, ODbL 1.0

© OpenStreetMap contributors. Data available under the Open Database License (ODbL) 1.0. https://www.openstreetmap.org/copyright · https://opendatacommons.org/licenses/odbl/1-0/

The caches are a produced work derived from OSM via the Overpass API (overpass-api.de), filtered to on-theme shop=* points, deduped, suburb-filled, and type-mapped by pipeline/build_towns.py. Attribution ships in-repo (this file) and in every cache ("license":"ODbL 1.0", "attribution":"© OpenStreetMap contributors"). If a rendered/public build surfaces this data, keep the "© OpenStreetMap contributors (ODbL)" credit visible.

Provenance table

index.json is the machine-readable roster (key/town/state/shops/roads) — read it rather than hardcoding a list. Every cache carries the same terms; per-town numbers live in each cache's counts.

The pack: 21 real towns · 1196 shops · 299 heroes (+ Lane G's newtown_godverse and redhill_godverse = 23 rostered in index.json), every one fetched from OSM Overpass with a bounded per-town bbox, ODbL 1.0, raw snapshots in _raw/ (shops + roads per town), fetched 2026-07-16 (redhill_real 2026-07-17).

⟨v5.0-FINAL⟩ roster fix (R27): redhill_godverse — the town the whole v5 epoch walks to — was on disk but missing from index.json for three rounds. G's cache landed after my last write_index() run and nobody re-rostered it. Every gate stayed green because selfcheck reads the directory directly and F's smoke boots the town by key; only B's HUD selector reads this roster, so the sole symptom was that a player could not choose it from the dropdown. Mine to own: the index is Lane E's, and an addition isn't done when the cache is written — it's done when the roster is regenerated. (The mirror of R24's lesson, where a retirement wasn't done until the consumers were swept.) See LANE_E_NOTES R27.

state towns
NSW (6) katoomba · newtown · glebe · marrickville · newcastle (Hamilton strip) · bowral
VIC (6) fitzroy · brunswick · geelong · bendigo · castlemaine · daylesford
QLD (2) westend · redhill (Monster Robot Party's real street — see below)
WA (2) fremantle · northbridge
TAS (2) hobart · launceston
SA (1) adelaide
ACT (1) braddon
NT (1) darwin

redhill_real — the v5 crate's real street (R24, ledger #3)

Red Hill, Brisbane QLD · centre -27.4553, 153.0064 · span 2.4 km · 36 shops (9 heroes + 27 texture, 29 subsampled) · 884 roads · median NN 73 m, hub 5/36. Built because v5's 120 real records come from a real shop — Monster Robot Party, 147 Musgrave Rd — and the shop had no street. Musgrave Road is in roads[] (41 ways, primary + residential), which was the point of the round. This cache is the DONOR Lane G runs godverse_town.py against → redhill_godverse.

The shop itself is not in OSM (no shop=music node in the bbox), so G injects it from the census — it cannot arrive via this fetch. See LANE_E_NOTES R24 for the seating hazard handed to G/A.

Retired (R23, counted): toowoomba_real — D's clustering audit measured it DEAD (0 patronage finds in 1417 checks); a re-bbox onto the Ruthven St heart fixed the spacing metric (median NN 396 → 89 m) but made the hub worse (2/12 within 160 m, the pack's worst), and its densest 300 m cluster is only 3 shops. Cache + raw snapshots removed; the config entry stays commented with the rationale. See LANE_E_NOTES R23.

Retired (R24, counted): ballarat_real — A's spacing warn caught it at median NN 255 m. Unlike Toowoomba, Ballarat is not dead: 103 POIs in the bbox, densest 400 m disc = 47 of them. It fails a different way — its live core and its heroes don't coincide. The dense disc is cafés/clothes/bakeries; the op-shops sit 854 m, 1042 m and 1709 m out, so no bbox holds both. Measured at every span (real builds): 1.0 km → 8 shops / NN 184 m / hub 4/8 but zero op-shops (a thrift town with no thrift shop); 2.0 km → 12 / 186 m / 3-12; 3.0 km (as shipped) → 20 / 255 m / 4-20. Every row is worse than any shipped town (darwin 68 m, glebe 60 m, redhill 73 m). The binding constraint is ours, not OSM's: with 2 heroes the 3× texture cap keeps 6 of 47 POIs and the subsample is spatially blind, so survivors scatter at any span. Cache + raw snapshots removed; config entry commented with the rationale. See LANE_E_NOTES R24.

The density widening (v4.0-beta, ROUND20 ledger #2)

The fetch covers the secondhand heroes plus the everyday main-street texture (bakery/cafe/deli/ newsagent/chemist/clothes/hardware/greengrocer/…), mapped to existing registry types per C's LANE_C_PUB §6 table. Tasteful staples only — no vehicle/fuel, funeral, storage, industrial, or pure services. cafe rides amenity=*; everything else is shop=*. Per John's subtle directive the heroes are never subsampled and texture is capped at 3× the hero count (deterministic sha256(townKey:osmId) rank); every drop is counted in each cache's counts.textureDropped.

index.json — the towns roster

{schema:"procity-towns-index/1", generated, license, attribution, towns:[{key, town, state, shops, roads}]}. Same ODbL terms as the caches. Consumers (B's selector) derive the town list from this rather than hardcoding.

Raw responses (_raw/)

The raw Overpass JSON is cached in-repo (wrapped with fetchedAt, endpoint, query, bbox): _raw/<key>.overpass.json (shops) and _raw/<key>.roads.overpass.json (OSM highway ways, schema v2). Re-runs reuse the raw cache and never re-hit Overpass — the fetch is outward-facing network (John-authorized scout, once per town). --refetch forces a deliberate re-fetch. selfcheck.js reads only top-level *.json, so _raw/ is never loaded as a cache.

Roads — schema v2 (ROUND18, v4.0-alpha REAL ROADS)

A v2 cache carries roads[] — the town's real OSM street geometry (highway=* ways), same ODbL licence and attribution as the shops. Fidelity cut (charter risk #4): the raw fetch grabs every ROAD_KIND tier, but the emitted roads[] is the town street graph only — drivable roads + pedestrian malls; service/track/footway/path/steps/cycleway are excluded (in Katoomba, 1461 of 2506 ways — bushwalking tracks, sidewalks, stairs, driveways). The excluded tier stays in the raw response if a tier is wanted back. katoomba_real is v2 (the alpha); the other towns' roads land as data-only.

Names — parody vs factual

Big trademarked charity/franchise chains are parodied in build_towns.py (Salvos→Slave-ohs, Vinnies→Sinnies, Anglicare→Tanglecare, Cash Converters→Cashtheists, …) for the game's comic tone. Independent shops keep their real OSM name — factual, publicly-mapped POI data under ODbL. No private-individual data is carried (name/type/coordinate/suburb only).