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

110 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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).