PROCITY/web/assets/towns
m3ultra a7289d0550 Lane E R22 (v4.0): MODELBEAST notes refresh + provenance v4-final + G2a atlas review
Ledger #3 (E's G item), #6 (provenance prep), + the C/E contract review.

- MODELBEAST notes (#3): pipeline/README.md's toolchain row claimed sf3d/trellis_mac were 'HF-gated
  (setup needed)' -- ultra was right, that was stale (weights cached; trellis_mac 15/15 on thriftgod
  Tier-1; the :8777 queue also runs bg_remove_local/mflux_image_edit/seedvr2_upscale). Corrected.
  Ultra's finish-farm post-mortem folded into the props section VERBATIM + credited, with Fable's
  Ruling 1 amendment (NO-DRACO: drop --compress draco, rest of the command stands). Noted that our
  bake_lowpoly.py / decimate_props.py are NOT the finish farm, so nobody conflates them.
- Provenance v4-FINAL (#6): the frozen table carries the pack (22 towns, all 8 states/territories,
  1192 shops + roads[], ODbL, raw snapshots) + a row for Lane G's newtown_godverse attributed to G.
  The licence spine is complete -- nothing shipped is unaccounted.
- BUG: index.json still listed 22 -- generated before the dd4489c merge, so newtown_godverse was
  invisible to B's selector while R22 verifies at 23-town scale. Regenerated -> 23 towns (selfcheck
  ALL GREEN 161300/161300). Index is now self-describing: state reads from the cache first, then my
  config, so any lane's town rosters itself.
- G2a atlas contract review (E's half; C answers interior ceilings in LANE_C_PUB): Fable's priors all
  hold. Atlas-per-shop is an UPGRADE if the loader disposes on room exit -- today stockpack.js never
  disposes (R9: 181MB resident, accreting). Without dispose, per-shop strands ~16MB/shop (~190MB per
  town visited, unbounded across 23 towns). With dispose: ~4MB resident at 1024², ~45x better than
  today. Also: size the atlas to the shop (<=1024², not 2048² for a 6-item shop); reuse my index
  schema verbatim so stockpack.js/fail-soft-to-parody/?noassets/validate_pack all work unchanged;
  the R8 parody law (parodied metadata, real cover art) is unchanged.

-> Lane G: add "state":"NSW" to newtown_godverse.json and the index picks it up automatically.
-> Lane C: the R9 dispose-on-exit cache-cap is now load-bearing for G2a (my VRAM numbers above).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:54:49 +10:00
..
_raw Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
adelaide_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
ballarat_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
bendigo_real.json Lane E R20 (v4.0-beta): the density widening — widened fetch, 5 caches re-emitted, towns index 2026-07-16 19:30:50 +10:00
bowral_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
braddon_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
brunswick_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
castlemaine_real.json Lane E R20 (v4.0-beta): the density widening — widened fetch, 5 caches re-emitted, towns index 2026-07-16 19:30:50 +10:00
darwin_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
daylesford_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
fitzroy_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
fremantle_real.json Lane E R20 (v4.0-beta): the density widening — widened fetch, 5 caches re-emitted, towns index 2026-07-16 19:30:50 +10:00
geelong_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
glebe_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
hobart_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
index.json Lane E R22 (v4.0): MODELBEAST notes refresh + provenance v4-final + G2a atlas review 2026-07-16 21:54:49 +10:00
katoomba_real.json Lane E R20 (v4.0-beta): the density widening — widened fetch, 5 caches re-emitted, towns index 2026-07-16 19:30:50 +10:00
launceston_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
marrickville_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
newcastle_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
newtown_godverse.json Lane G (GODVERSE, ultra) R1: real-shop census adapter — 18 real Newtown shops on the real road graph 2026-07-16 20:19:14 +10:00
newtown_real.json Lane E R20 (v4.0-beta): the density widening — widened fetch, 5 caches re-emitted, towns index 2026-07-16 19:30:50 +10:00
northbridge_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
README.md Lane A round 18: town-cache schema v2 (roads[]) — published FIRST (v4.0-alpha REAL ROADS) 2026-07-16 15:54:19 +10:00
SOURCES.md Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
toowoomba_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00
westend_real.json Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating) 2026-07-16 21:06:46 +10:00

Real town caches — the home (web/assets/towns/)

Lane A owns the contract; Lane E fills this directory (ROUND17 ledger #6, the real-map scout).

Each real Australian town is one file, <key>.json, in the town-cache contract shape — the processed output of pipeline/build_towns.py (Overpass → dedupe/suburb/parody-name/seeded-hours). plan_osm.js marches it exactly like the checked-in fixtures in web/js/citygen/osm_fixture.js; the scout proves the existing CityPlan contract eats real data — no new plan fields, no new geometry.

The contract (authoritative: validateTownCache() in web/js/citygen/plan_osm.js)

{
  "schema": "procity-town-cache/1",
  "key": "katoomba_real",                    // matches the filename; distinct from the hand-made fixture key
  "town": "Katoomba",                        // display name
  "source": "osm",
  "license": "ODbL 1.0",                     // REQUIRED — OSM is ODbL
  "attribution": "© OpenStreetMap contributors",
  "generator": "pipeline/build_towns.py",    // optional provenance
  "fetchedAt": "2026-07-16",                 // optional
  "center": { "lat": -33.71, "lon": 150.31 },// REQUIRED — the equirectangular projection origin
  "bbox": { "minLat": …, "minLon": …, "maxLat": …, "maxLon": … },  // optional metadata
  "counts": { "raw": 512, "shops": 96 },     // optional metadata
  "shops": [
    { "id": 318855426, "name": "Canty's Bookshop", "type": "book", "lat": -35.32, "lon": 149.18, "suburb": "Fyshwick" }
    // …
  ]
}

Hard requirements (else the cache is rejected and does not boot):

  • center.{lat,lon} finite numbers.
  • shops an array of MIN_TOWN_SHOPS (6) entries (up to 4 venues + the one openLate landmark + a spare).
  • every shop has finite lat/lon.

Absorbed as warnings (still boots): a blank name defaults to the type label; an unknown type remaps to opshop; duplicate ids; and — importantly — a cache spanning > 5 km warns "likely more than one town". A cache MUST be ONE compact town: an over-broad Overpass bbox marches into an unusable multi-km strip (the R17 mega-strip risk — a whole region of 2,918 shops became an 11.6 km avenue). Bound the query.

type should be a registry SHOP_TYPE (record/opshop/toy/book/video/pawn/milkbar/dept/stall); build_towns.py maps OSM shop=* tags to these (unknowns land on opshop).

Schema v2 — roads[] (ROUND18, v4.0-alpha REAL ROADS)

A v2 cache adds an optional roads[] — the town's real OSM street geometry. When present, plan_osm builds the CityPlan street graph from the real roads and seats each shop on its nearest real edge (its real street, real order, real side) instead of marching shops onto synthetic parallel avenues. roads absent (or schema procity-town-cache/1) ⇒ the marched fallback — every shipped v1 cache stays valid.

{
  "schema": "procity-town-cache/2",
  // … all v1 fields (center, shops, license, …) …
  "roads": [
    { "id": 12345678,                 // optional (OSM way id)
      "kind": "primary",              // REQUIRED — the OSM highway=* class (see the map below)
      "name": "Katoomba Street",      // optional
      "pts": [ [-33.7175, 150.3110], [-33.7140, 150.3112], [-33.7100, 150.3115] ] }  // REQUIRED — ≥2 [lat,lon]
    // …
  ]
}

Road validity (a bad road is a hard error; a soft one is absorbed):

  • each road is { kind: string, pts: [[lat,lon], …] } with ≥ 2 finite [lat,lon] points — else rejected.
  • < 2 points → the road is dropped (warning); an unmapped kind falls to side (warning).

kind → CityPlan edge kind (ROAD_KIND in plan_osm.js; the lift then promotes a shop-dense side road to the main spine): motorway/trunk/primary/secondarymain; tertiary/unclassified/ residential/living_street/roadside; service/tracklane; pedestrian/footway/path/ steps/cyclewayarcade. Fidelity (charter risk #4, A's knob): ship the geometry roughly as OSM has it; plan_osm simplifies (DouglasPeucker) — don't pre-decimate below ~1 point per 5 m.

Bound the roads to the town (the bbox law): fetch highway=* within the same per-town bbox as the shops so the graph is the town's streets, not a region's.

Provenance (Lane E)

OSM data is ODbL. Ship attribution with the data: this README, a SOURCES.md (E's licence table), and the license/attribution fields on every cache. Cache the raw Overpass responses too so re-runs never re-fetch.

Wiring

  • plan_osm.generatePlanOSM(seed, key, { cache }) marches a cache directly; registerTownCache(key, cache) adds it to the runtime registry so osmTownKeys() and generatePlanFor(seed, 'osm', { town: key }) see it.
  • web/js/citygen/selfcheck.js auto-loads every *.json here: validates it, runs the full structural + gig suites across the hero seeds, and pins a per-town golden (unpinned → prints the value to paste).