Closes the gap Fable caught (hud.js:11 still hardcoded REAL_TOWNS). The real-town list now comes from E's
web/assets/towns/index.json (procity-towns-index/1), so it grows with the town pack instead of being edited
in hud.js. Verified fresh Chromium. Nothing else touched -- tram/ground work is done and verified.
Fail-soft by construction: createHUD is synchronous, so the picker is built immediately from the fallback
literal and UPGRADED IN PLACE when the index resolves -- if the index never arrives, a working list is
already on screen.
- index present -> real names/states/shop counts: "Katoomba, NSW · 80 shops", "Fremantle, WA · 80 shops",
"Newtown, NSW · 72 shops", "Bendigo, VIC · 36 shops", "Castlemaine, VIC · 24 shops" (roads:false -> "· marched").
- ?noassets -> the fetch is SKIPPED entirely (asset law; the shell doesn't load town caches there either).
Measured: 0 index fetches, fallback list, 0 errors.
- anything wrong with the index -> the fallback simply stands. Exercised the exact chain: 404, network error,
malformed JSON, towns:[], towns-not-an-array, junk entries (no key) -> none throws, none rebuilds from junk;
only a good index rebuilds.
REAL_TOWNS_FALLBACK is retained ONLY as that fallback (comment says so), no longer the source of truth.
Verified live: booting katoomba_real shows "Katoomba, NSW · 80 shops" pre-selected; fits (193px of 230px max);
0 page errors; navigation unchanged (onchange is set once on the <select>, so rebuilding options never clears it).
-> F: unchanged from R19 -- NO shell seam (selector is entirely hud.js, already built by createHUD) and it
lives inside #pc-hud so setVisible(false) hides it for the beta money shot. The gating call is still yours.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
godverse_town.py marries a donor OSM cache's roads[] with thriftgod's DealGod-scraped
shop census (2,928 real AU secondhand shops) -> <town>_godverse.json, schema v2 unchanged.
First artifact newtown_godverse boots clean (?plansrc=osm&town=newtown_godverse).
Lane brief + asks (Draco ruling, per-town bookmarks, enterShop selector) in
docs/LANES/LANE_G_GODVERSE.md + G-progress.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-ran everything against A's absorbed graph (5d29863, selfcheck 51866/51866).
Katoomba barely moved from my pre-absorb read (null 77.2->78.1, visits 64->62,
hero share 30->29%), so the earlier conclusion held.
SIM VERDICT: zero changes needed at 4x density. All five towns: 0 NaN, chunk rosters
stream (15-24), identity sigs accumulate (262-483), patronage/loiter/bench-sit all fire,
up to 401 active peds, no degradation. Determinism unchanged (identity is graph+seed
keyed; shops don't touch it). D->F: GREEN.
THE FINDING (for E/C/Fable): the widening MOVED the main street off the secondhand strip.
Hero visit-share falling (29%->0%) is NOT a behaviour bug — patronage is type-agnostic, so
visits track the local mix exactly. The mix is the story. Pre-widening the densest cluster
WAS the secondhand strip (katoomba: 9 of 9 main-street shops were heroes). Post-widening
the centre of gravity relocated 308m (katoomba) / 434m (castlemaine) onto the general-
retail cluster, where heroes are a 0-19% minority. Castlemaine's main street now has ZERO
secondhand shops (0 of 4 heroes within 160m; median hero 463m away). The heroes weren't
drowned in place — they were demoted: the liveliest street is no longer theirs.
The good half stands: heroes kept absolute footfall (katoomba 17->18 visits) and the
street is 3.8x busier. The widening works; it overshot the dial. This is exactly the case
John's directive pre-authorised ("E may seed-subsample the texture classes"). My read
(measurement, not a ruling — E/C/Fable own it): subsample texture until the secondhand
strip is again the densest cluster. Sparse towns need it most (bendigo 7%, castlemaine 0%).
ALSO FILED (A/F): venues land off the retail hub at density — newtown's three are all
2190-2433m out with 0-1 neighbours; katoomba's pub still 1007m/1 (A's "pub facade bias"
fixed frontage, not position). Quality call, not a break; "venue placement still lands"
is A's ledger + F's district gate. Castlemaine shows the good case (band_room on the hub).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
E's five caches landed at 3-6x shops (292 total: katoomba 20→80, fremantle 21→80, newtown 18→72,
bendigo 9→36, castlemaine 6→24). All five absorb cleanly and still place 3 venues; the R20 poster cap
auto-scaled off the live shop count exactly as designed (katoomba 111 posters at 80 shops).
- CAPACITY WIDENED (the R18 overflow fence, load-tested). At 4x the dominant drop cause was OVERFLOW —
an edge-side holding more shops than fit (katoomba 10 of 11 drops). Tightened the run: NODE_CLEAR 6→3
and a ROADS-ONLY RGAP 2→0.5 (the shared marched GAP is untouched ⇒ marched fixtures stay frozen).
Drops 7.9% → 5.5% (16/292). 0.5m also reads truer — a real AU main street is continuous terrace
shopfronts, not detached boxes. Stopped there deliberately: the next lever (spilling overflow onto a
DIFFERENT street) buys ~1% by putting a shop on a road it isn't on — dropping a crowded shop is the
honest trade. Residual drops stay COUNTED, now BY CAUSE (dropped_overflow/_overlap/_stranded).
- VENUE FACADE BIAS NOW COVERS pub. R16 exempted pub ('spine-fronted, +Z always open') — true on the
synthetic/marched towns, but on a REAL graph at density the pub landed where its frontage poster
overhung a cross street (newtown, -0.91m). The POSTER_CLEAR filter now covers every venue kind; still a
NO-OP on synthetic (every pub candidate already clears) ⇒ gig golden 0xb1d48ea1 frozen.
- Goldens re-pinned ONCE (base + gig, all five towns). classic 0x3fa36874 / gig 0xb1d48ea1 / marched
fixtures FROZEN. selfcheck ALL GREEN 51866/51866; scaffold + consistency GREEN; cross-process
deterministic.
- Fixed: my real-cache loader globbed *.json and tried to validate E's new index.json as a town cache —
it now skips it (that's B's selector index, not a cache).
Handshake → all: densities absorbed, goldens pinned. → F: re-measure venue_night + gate the five towns.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified fresh Chromium across 9 town configs (synthetic, 3 fixtures, 5 real).
TRAM RULING (ledger #3): spinePolyline now walks from each main-subgraph dead-end, greedily taking the
unused main edge fronting the most shops (lot.frontEdge), keeping the best-scoring path (ties -> longer).
SYNTHETIC + FIXTURES BYTE-IDENTICAL by construction AND measurement (a synthetic main node has <=2 mains so
there's never a choice; its two dead-ends tie exactly and strict > keeps the pre-R20 start). Old vs new
logic on the same plan = identical polylines (Boolarra: 7 pts, 6 edges, 810m, 172 shops, 2 stops).
The alpha tram fronted 0 shops on 4 of 5 real towns (it was a highway bus). New: katoomba 0->3, newtown
0->8, fremantle 0->2, bendigo 0->1, castlemaine 1->6. Where the best main chain still fronts <5 shops the
tram is FENCED per-town (Fable's fallback). RUNS: synthetic, all fixtures, newtown (8 shops/1924m/16 stops),
castlemaine (6/2184m/10 stops). FENCED: katoomba (3), fremantle (2), bendigo (1) -- highway, not high street.
The fence keys off the REAL-ROADS signal (edges>200), NEVER shop count alone: the marched fixtures front 0
shops on their mains (bare spine, avenues carry shops) so a naive fence would have killed their v2 tram.
routeInfo{fenced,reason,shopsFronted,routeMetres} exposed for F's smoke.
BUG FOUND+FIXED: the stop projection accepted every shelter in the town with no distance gate -- harmless on
synthetic (2 shelters on the spine), nonsense on real graphs: Newtown made 149 phantom stops (~9 min of dwell
at points the tram never passes). Added a 30m gate (a shelter sits halfRoad+1.7 <=~16m off its centreline, so
genuine stops survive): newtown 149->16, castlemaine 37->10; synthetic 2 / fixtures 3 unchanged.
GROUND POLISH (ledger #4): measured, NO fix needed. Real Katoomba has 325 deg-3+ junctions, 28 under 30 deg,
worst 4.3 deg (near-parallel). At grazing eye level the worst reads clean -- no z-fight, no gaps. Layers are
already y-separated (road 0.0/foot 0.02/kerb 0.06) and road-on-road overlap is invisible by design (OVERLAP=1.5
overruns nodes on purpose; one skin, one y, one merged draw on uniform asphalt). Cosmetic only: footpath/kerb
double up on the sliver between <10deg near-parallel roads -- v4.0 candidate, not a defect.
Selector polish (ledger #5): BLOCKED -- E's towns/index.json not landed (build_towns.py WIP); REAL_TOWNS
hardcode stands, one-line swap when E lands.
-> A (finding, measured): only a slice of shops front a MAIN edge on real towns -- katoomba 7/69, fremantle
4/79, bendigo 7/35, newtown 32/64, castlemaine 11/22. The real high street classifies as 'side', which is
WHY three towns need the fence. If the main/side classifier promoted shop-dense ways to main, the fence
would lift. Note onMain also gates gigs.js pickVenues pub placement.
-> F: tram verdict above; selector gating (HUD vs ?dbg) is your call, one line either way.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SELF-CORRECTION first. My part-1 note called the 95-98% patronage null rate
"sparsity-throttled" and made it the headline metric. Both halves were wrong:
(1) it was measured at 21:00, when only 4 of 69 shops are open — retail is shut BY
DESIGN, so a high night null is correct behaviour, not sparsity; (2) it sampled each
town's first venue, and katoomba's is the pub — 1000m from the retail cluster with 1
neighbour. I had parked the camera on the one isolated venue in town. Same town, same
seed: at the pub 99.4% null / 0 visits; on the actual main street 77.2% / 64 visits.
The gig-crowd table stands (it measures the mechanism — 0 NaN, rosters, venue-win —
which is location-independent); only my sparsity reading was bad.
THE REAL A/B (main street, 14:00, same seed, pre-cache from git vs E's widened):
hub density 8 -> 25 shops within 160m; null 90.2% -> 77.2%; total visits 17 -> 64
(3.8x); hero visits 17 -> 19; texture visits 0 -> 45; hero share 100% -> 30% of visits
from 26% of shops.
THE DIRECTIVE LANDS, measured from the crowd side: the secondhand heroes did NOT lose
footfall (17 -> 19). The 45 texture visits are added ON TOP, not taken from them. The
street is 3.8x busier while the heroes stay over-indexed. "More alive, not a different
game."
FILED: (a) venue placement at density — katoomba's rsl/band_room sit in the strip
(13/10 neighbours) but the pub is 1000m out with 1; a gig there has no passing retail
crowd. Quality observation, A/F's lane. (b) GIG_RANGE is a 34m radius but
_nearestOpenShop searches only the ped's own 64m chunk — 8.7% of in-range peds are
chunk-blind. Real but small; v4.x candidate, not a mid-beta hot-path change.
POST numbers are vs E's committed caches but BEFORE A's absorb (11/80 still overflow-
drop). Final re-run + the other four towns once A lands.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ledger #2 (E's half), built to C's LANE_C_PUB §6 table under John's SUBTLE directive.
- Widened fetch: C's tasteful staple list (shop=* + amenity=cafe per C's dagger note), mapped through
C's copy-paste table onto EXISTING registry types (no new archetypes). Guardrails honoured: no
vehicle/fuel/funeral/storage/industrial, no pure services. New raw snapshot per town (the fetcher
auto-refetches when the class list changes -- query recorded in each snapshot), committed like the first.
- SUBTLE by construction: secondhand HEROES never subsampled; texture capped at 3x heroes (top of C's
~2-3x band), over-cap towns seed-subsampled deterministically (sha256(townKey:osmId)) with every drop
counted in counts.textureDropped. Result 3.0x in all five: katoomba 20+60 (-4), newtown 18+54 (-156),
fremantle 20+60 (-81), bendigo 9+27 (-81), castlemaine 6+18 (-43). Density 6-21 -> 24-80 shops (3-4x).
- web/assets/towns/index.json (new, E-owned): {schema, generated, license, attribution, towns[{key, town,
state, shops, roads}]} -- B's selector can drop the REAL_TOWNS hardcode (hud.js:11).
- Deterministic (byte-identical re-run, 0 re-fetch). ODbL: SOURCES.md + per-cache license/attribution.
-> Lane A (absorb pass): (1) 10 expected golden re-pins -- the widening moves every real town's base+gig
golden (designed E->A handoff); synthetic 0x3fa36874 UNMOVED, fixtures frozen. (2) SEAM: index.json
trips selfcheck's town glob (selfcheck.js:467 takes every top-level towns/*.json as a cache) -- one-line
fix in your file: .filter(f => f.endsWith('.json') && f !== 'index.json'). Didn't touch selfcheck.js
(yours, you're mid-round in it); Fable spec'd the index at this path so the path stays.
-> Lane C: kept video_games -> video (absent from your §6 table; was already shipping as video pre-widening,
so the fallback would have regressed it to opshop). Same family, same target -- flagging for your table.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The unblocked half of "citizens at density": the gig crowd's BEHAVIOUR on all five
real towns' venues, measured BEFORE E's widening so the after-table is a real A/B.
Density re-audit follows once E's caches + A's absorb land.
BEHAVIOUR IS CORRECT ON ALL FIVE: 0 NaN everywhere; tonightRoster populates (2-7),
so R14 identity continuity holds on real bones; every town generates 3 venues
(pub/band_room/rsl) on night hours; and the surge works — whenever a patronage check
finds any shop, the gig venue beats ordinary shopfronts (venue-win 100% on newtown/
fremantle/bendigo, 74% katoomba). Castlemaine's 40% is the 6-shop floor, not a leak:
3 of its 6 shops ARE venues, so "other" hits are mostly other venues.
FINDING (quantifies Fable's ledger #2 "real towns are sparse"): the crowd is
SPARSITY-throttled — 94.9-97.7% of patronage checks return null on every town, because
peds spend nearly all their time in shopless chunks (katoomba: 19 shops in ~5 door-chunks
of a ~78x78-chunk town). The mechanism is fine; the town has no shops per chunk. The
density widening attacks exactly this — null% is my headline before/after metric.
FILED (not fixed): GIG_RANGE is a 34m radius but _nearestOpenShop searches only the
ped's own 64m chunk, so in-range peds across a chunk edge are blind to the gig — 8.7%
of in-range ped-samples (852/9740) on katoomba. Small vs the 97% sparsity throttle;
not worth a hot-path change mid-beta. Revisit if the widening leaves null% high.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The density widening's C half: real towns fetch general retail, not just secondhand.
Published the OSM shop-class -> registry-archetype mapping in LANE_C_PUB.md §6 (new
v4.0-beta amendment; frozen venue contract §0-§5 untouched). C->E handshake: E builds to it.
- No new archetypes. General retail wears existing recipes: food/grocer/newsagent/chemist
-> milkbar; clothes/hardware/big-retail -> dept; produce/florist -> stall.
- Secondhand stays the hero (charity->opshop, books->book, music->record, video->video,
pawn->pawn, toys->toy); never subsampled. Unknown/unlisted -> opshop (R18-proven).
- Subtle per John: texture not takeover. Copy-paste JSON map + tasteful fetch list (skip
vehicle/funeral/industrial/pure-services) + guardrail for E to subsample if retail drowns
the secondhand strip (count in norm.dropped).
- Mechanic: plan_osm keeps registry types, so E bakes a registry type per the map into each
cache's shop.type -> no C code change, budgets unchanged.
Pre-validated before E builds to it: milkbar/dept/stall/opshop all build valid at a real
katoomba lot (8.5x14, 0 carves, <=143 draws); dept reads as a clean dressed-retail floor
(shelves+racks+counter, escalator does not dominate the 7.9x11.7 hall) -> clothes/hardware
-> dept confirmed. Full five-town door-soak (ledger #6) follows E's re-emitted caches.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes my own R19 finding + F's one open budget edge. A sparse REAL town has few shops but a big edge
graph, so the 'two posters per main edge' pole run reads busy (katoomba 438) and drove venue_night to
200,965 tris (+0.5% over the 200k soft ceiling).
- gigs.js buildPosters caps the spine pole run at POSTER_PER_SHOP × shops (1.5/shop, floor 12) — a seeded
subsample. SCALED BY SHOP COUNT, not edge count (the whole point). katoomba 438→31 posters (~2x the
synthetic feel), fremantle 32, castlemaine 14. NO-OP on the shop-dense synthetic town (493 shops ⇒ cap
~740 ≫ its ~15 posters), so the synthetic gig golden 0xb1d48ea1 stays FROZEN.
- Posters are gig-layer (withGigs), so the real-town BASE goldens are unchanged (correctly green). Added
REAL_TOWN_GIG_GOLDENS (the full district+capped-poster plan per town: katoomba 0xbd332e83, …) to
regression-guard the capped output byte-exact, not just via districtInvariants. classic/gig/marched
fixtures frozen; selfcheck ALL GREEN 51675/51675; scaffold + consistency GREEN; cross-process deterministic.
→ F: re-measure venue_night, the +965 edge should close. Density absorb (ledger #2) pends E's widened
caches — the poster cap already reads the live shop count, so it auto-scales to the widened towns.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A resolved the R18 findings (fragmentation cull/join, poster clamp, goldens pinned) + D verified; F gated.
- town_matrix.py: a `roads` dimension — a schema-v2 cache (roads[]) must build the REAL street graph
(norm.mode==='roads', read via the plan_osm opts.report sink); synthetic/fixtures stay marched/synthetic.
10 towns x 6 gates: real roads green everywhere; classic/default proven untouched (roads ride the cache
schema, not a flag — 0x3fa36874 frozen).
- docs/shots/v4_alpha/katoomba_mainstreet.png: the first real-street money shot — Katoomba's actual main
street (asphalt + lane markings, footpaths, a pedestrian crossing, a walking crowd, gig posters, B's town
selector in-frame), PROCITY's look on real bones, 121k tris midday.
- docs: LANE_F_NOTES §18 (why F correctly HELD in R18 — the gate would have failed by design) + §19 (the
close), README real-roads note, F-progress.
qa.sh --strict --soak GREEN 7/7. One filed NON-blocking finding: katoomba_real venue_night 200,965 tris
(+965 / 0.5% over the soft ceiling, poster-count-scales-with-real-edges driven; midday play is 121k) — a
v4.0-beta hardening item (A caps posters per real town), on the opt-in --matrix warn-gate, not a play view.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Post-fix re-measure of my R18 finding against A's committed connectivity pass
(6fc4b40). Same harness, same seed.
VERDICT: fragmentation fully resolved, sim untouched. Real Katoomba:
components 105 -> 1, street-metres in main 58.2% -> 100%, shops stranded 2 -> 0
(1 bridged, 1 dropped past SEAT_MAX AND counted in norm.dropped), % near-crowd on
fragments ~10% -> 0%. selfcheck 51670/51670 green, katoomba golden pinned.
Mechanism check: re-ran R18's dead-straight-through-junction geometry — R18 dropped
the junction (deg 2, disconnected); R19 protects it (deg-4 crossroads, 1 component).
DP-collinear drop fixed at the source.
Sim confirm on the fixed bridged graph: 0 NaN over 1,303,200 samples, footpath-perfect,
loiter/bench-sit fire, patronage rose 24->41 (peds reach shops across the connected
graph), 0 fragment-stranded peds. Zero sim changes, as R18 predicted.
D->F: fragmentation verdict post-fix is GREEN — tag away. FYI B/F: edges rose 799->966
(junction protection + bridges), flagged for the tris budget.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Shipped the town picker in hud.js (which already owns its DOM overlay -> NO index.html seam). A compact
<select> in the HUD (top-left, pointer-events:auto), 9 options: synthetic + 3 OSM fixtures (osmTownKeys
minus _real) + 5 real-road caches (Katoomba/Newtown/Fremantle/Bendigo/Castlemaine, hardcoded REAL_TOWNS --
no manifest yet, beta derives it). Selecting navigates: synthetic clears plansrc/town; a town sets
?plansrc=osm&town=<key>, preserving all other params. Current pre-selected; unknown-current still shown.
Verified end-to-end: picked katoomba_real through the dropdown -> booted real Katoomba (966 edges/323
intersections/3 venues), 0 errors. Lives inside #pc-hud so hud.setVisible(false) hides it for money shots.
-> F (B->F seam): NONE -- self-contained in hud.js (already constructed by createHUD). Gate it behind ?dbg
with one setVisible call if you prefer; left always-in-HUD as a modest debug-grade feature.
Corner-strips spot-check (R18 failure-item #3): ground fills real intersections OK -- degree-4 junction
(58 exist) shows road strips overlap-filling the junction, lane markings + zebra crossing render, no gross
gap. Acceptable for alpha; finer acute-angle pass = beta.
Budget note -> F (measured vs A's UNCOMMITTED WIP -- caveat, not a defect): F's BIG_CITY one-liner is live
(chunks 50->26). But venue block still ~237k tris at night against the current tree, which carries A's
uncommitted R19 finalization (plan_osm modified; graph grew 799->966 edges, junction-joining in but the
far-island CULL not yet). Fable's ruling: the cull is "half of B's tris problem" -> the <=200k gate needs
F's fix AND A's committed cull. Re-measure the busiest real block after A pins (the deferred tris-post-cull
re-measure genuinely waits on A's commit; the dev server serves other lanes' uncommitted edits).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A's finalization (ledger #1), the round's spine. Every filed R18/R19 finding resolved; the tag is F's.
- Fragmentation (D's finding; Fable ruled, A owns details). The naive real graph fragmented (katoomba 105
components, 58% street-metres on islands). plan_osm.buildRealRoads now: (a) JUNCTION-PROTECTED
Douglas-Peucker — a point shared by >=2 ways is a junction; simplify only BETWEEN junctions, never
through one (the collinear tee-off drop, the root cause) → 94-100% main-net alone; (b) cull/bridge —
main component + near-fragments joined (JOIN_TOL 12m; shop-island bridge <=200m), far SHOPLESS islands
culled. Result: 1 connected component on all 5 towns (katoomba 100% main-net, 2 islands culled). Every
dropped shop COUNTED in norm.dropped (ruling): katoomba 19/20 (1 crowd drop — R18 overflow prediction
measured), the other four kept all.
- E's poster-clearance finding fixed (gigs.js): a spine poster overhanging a real crossing carriageway is
skipped, measured exactly as the selfcheck kerb floor — a NO-OP on synthetic (gig golden frozen, pr()
consumed before the check). 23 real-town clearance failures → 0.
- Goldens pinned (A->E->A finalization): all 5 real towns on their real-roads output (katoomba 0xb7ffbca2,
newtown 0x6f984c81, fremantle 0xfb197000, bendigo 0x21179b3b, castlemaine 0x7c47f639). synthetic
0x3fa36874 / gig 0xb1d48ea1 / marched fixtures / classic / default FROZEN — the alpha changed nothing
outside the cache-schema path. selfcheck ALL GREEN 51670/51670; scaffold + consistency GREEN. Cross-
process deterministic.
- New finding filed (beta, non-blocking): spine-poster density scales with edges (katoomba 438 vs ~14
synthetic; draws low via instancing) → a beta posters-per-town cap.
Handshake fired → B/C/D/F: katoomba_real boots real roads, golden pinned, findings resolved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
E's roads landed (katoomba_real now schema v2, 484 real roads), so re-ran the door soak
against the real street graph (closes the R18 "re-run when roads land" caveat).
Honest caveat: the dev server also served Lane A's UNCOMMITTED R19 fragmentation WIP
(plan_osm.js: JOIN_TOL/BRIDGE_MAX, junction-protected DP, island cull/bridge), so this
exercised A's in-progress finalization, not a frozen state.
Robust result (holds whatever A's WIP does): every shop the plan produced builds a valid
interior -- 0 throws/carves/path-fails/over-budget, worst 147 draws; 3 gig venues (district
placement holds on the real graph); plan deterministic; lot dims unchanged (7.5-8.5 x 14 ->
interiors dim-identical to R18, road-agnostic). Shop count came out 19 here, but that reflects
A's in-flight cull/bridge logic (expected per Fable's fragmentation ruling), NOT a defect --
not filed as a finding; interiors build clean whichever count A pins.
Final C spot-confirm pending A's committed finalization + golden pin (C confirms after A).
Docs-only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Real towns can have FEW shops but a HUGE road graph (real Katoomba: 20 shops / 799 edges / 5 km), so the
shop-count-only heuristic left them on small-town streaming and blew the tri budget (280k > the 200k gate).
Extend the trigger to the graph: BIG_CITY = shops>120 || edges>200. B proved the effect; landed EARLY so
D/B's post-fix re-measures see it (per the R19 order).
Verified: katoomba_real 799 edges -> BIG_CITY -> 61 draws / ~12k tris (was 280k), 0 errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audited the citizen sim on real irregular street topology. E landed katoomba_real's
real roads (484 ways -> 872 nodes/799 edges) mid-round, so this is measured on the
real Katoomba graph plus a crafted irregular proxy.
VERDICT: the CityPlan graph contract IS the contract — zero sim changes. On real
Katoomba (7.2deg junctions, 1303m edge, 105 components): 0 NaN over 1,095,500 samples,
peds footpath-perfect (0 off-footpath), loiter/bench-sit/patronage all fire, chunk
streaming works over non-grid, determinism byte-identical (1167 identity sigs, R14
chunk-local law holds). No source touched.
FILED FOR A: katoomba_real's graph is 105 components — main holds 58% of street-metres
+ 18/20 shops, but 42% of street-metres + 2 shops strand (~10% of near-main crowd
presence lands on tiny fragments). Primary cause is expected: median fragment 71.5m
from main = peripheral/bbox-clipped roads. Secondary (corroborated with Lane B on A's
selfcheck fixture): per-way Douglas-Peucker drops collinear junction points + no
mid-edge split — bites only the 9/85 fragments <10m from main. Reconciles with B:
B verified 126 intersections form (main component); I add the global measurement
(still 105 components). A fidelity knob (charter risk #4), not a blocker; sim degrades
gracefully (no crash). A owns the connect-tolerance decision.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audited katoomba_real on E's real roads (schema v2, 484 OSM ways -> 799-edge/872-node graph, 126 real
intersections, edges 3.5m-1303m incl 28 short <12m, ~4x5km span). E's roads landed mid-audit; verified
fresh Chromium, cache-busted. Docs-only (no code change: streetscape is road-agnostic).
VERDICT: Lane B streetscape is ROAD-AGNOSTIC — boots real Katoomba 0-error, every B system builds and
renders on the irregular graph (ground, furniture 39 chunks/148 trees, town-wide pools, tram, chunk
streaming, buildings, wind). Real main street reads well (road+footpath+lamps+working gig poster+citizens).
Like C's interiors, needed no code change to eat real geometry.
FINDING -> F (gate-blocking, PROVEN one-line fix): the BIG_CITY adaptive heuristic misfires on real roads.
index.html BIG_CITY = plan.shops.length>120 -> Katoomba has 20 shops(<120) but 799 edges/5km, so it stays
small-town mode (radius 3 + shadows ON), streams 50-52 chunks -> busiest venue block ~280k tris (clean,
settled) = BREACHES 200k gate (draws 150 <300). Proven fix: ?r=2&shadows=0 (BIG_CITY mode) -> 84k day /
104k night, 20 chunks, 72 draws, well under. Recommend: BIG_CITY = shops>120 || edges>200 (catches
Katoomba's 799, leaves synthetic Boolarra's 22). B's R3 adaptive-streaming concept, edge-count not shop-count.
Tram (Fable's flagged spine risk): degrades gracefully, no crash. spinePolyline runs one main chain =
3527m/49 edges = 21% of 232 mains (longest walkable chain). Arbitrary-but-functional for alpha; smarter
route or hard fence = beta (no plan.mode marker exposed to fence off; didn't add untested fencing).
Furniture/pools scale: town-wide pools ~4989 instances = still ~1 draw; 28 short edges get 0 lamps
(graceful). Correction: the disconnected graph I first saw was a synthetic-fixture DP artifact (junction
vertices dropped on near-collinear ways) — real Katoomba forms 126 intersections correctly; flagged to A.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Newtown/Fremantle/Bendigo/Castlemaine now schema v2 with roads[] (1086/622/1552/397 real streets),
same fidelity cut (street tier only). Data-only: only katoomba gates the round; A hardens against these.
build_towns.py: added Overpass retry/backoff (public API 504s under load — hit one mid-run).
The poster-clearance finding is SYSTEMATIC: it reproduces on all 5 real-road towns, every seed (23
checks, -0.9 to -4.7m), confirming it's plan-level poster placement (A's pickVenues/POSTER_CLEAR), not
town data. Scoping holds: only the real/* v2 path fails; synthetic + fixtures + classic + default boots
stay green. A's one fix generalizes to all five. ODbL: raw roads committed, SOURCES.md current.
-> Lane A: all 5 towns' roads live; the systematic poster finding is yours to resolve + re-pin goldens.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend build_towns.py: bounded way[highway](bbox) out geom per town -> raw cached in-repo -> roads[]
in A's v2 contract ({id,kind,pts:[[lat,lon]]}, kind=raw OSM highway class). katoomba_real is now
schema v2 with 484 real streets (Bathurst Rd, Cliff Drive, ...). Fidelity cut (charter risk #4, flagged
to A): fetch every ROAD_KIND tier -> raw; EMIT the town street tier only, excluding service/track/
footway/path/steps/cycleway (1461 of 2506 Katoomba ways = bushwalking tracks/sidewalks/stairs/driveways).
A's lift builds a real town from it (verified): 484 streets -> 872 nodes/799 edges (main 232 side 531
arcade 36) -> 12 blocks, 20 lots, 3 venues (pub/band_room/rsl), 12 gigs. pickVenues lands venues on the
real graph (ledger #2 met).
FINDING for A (alpha failure-list, charter risk #3): on the real graph, spine posters overhang crossing
carriageways -- selfcheck kerb-clearance fails on 4 seeds (-1.6 to -2.5m; e.g. seed 20261990 poster 50 vs
edge 58 = -2.42m). Same class as A's R15/R16 corner-poster fix, now on real cross-street geometry. Owner:
A (pickVenues/POSTER_CLEAR on the real graph, the 'A again' harden step) -- my roads data is VALID (cache
validates; failures are plan-level poster placement). Golden settles after A's fix (pre-fix 0x6014a8e6).
ODbL: raw roads committed, SOURCES.md updated.
-> Lane A: katoomba roads live; harden the lift + resolve the poster finding + re-pin the golden.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entered every katoomba_real shop the way the shell does (registerTownCache ->
generatePlanFor osm, gigs on = default boot -> buildInterior per shop, lot dims
from plan.lots). All 20 doors PASS: 0 throws / 0 carves / 0 path-fails / 0
over-budget, worst 153 draws (<=350); 7 types incl. the 3 gig venues (pub/rsl/
band_room) all built valid.
Degenerate-lot stress (preempts A's real-road re-seating): 32 extreme-lot x type
combos (incl. 0x0, 1x1, thin slivers) -> 0 throws/carves/path-fails; every room
floors at >=4x4 m. The interior contract absorbs any real frontage.
Honest caveat: current katoomba_real.json is schema v1 (roads:none) -> marched
fallback (E's roads cache not committed yet). Interiors are road-agnostic (lot dims
+ type + seed only), so the conclusion holds once roads seat shops on real edges;
one-line re-run if lot dims shift. Interior lane: 0 failures for the alpha list.
Docs-only (verification of shipped behaviour).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The epoch headline (ledger #2). A v2 cache's roads[] now builds the CityPlan street graph FROM real
OSM geometry instead of marching shops onto synthetic avenues. SAME CityPlan schema out — B/C/D/F consume
it unchanged by contract. roads absent ⇒ the v1 marched fallback, so classic/gig/osm goldens are FROZEN.
plan_osm.buildRealRoads: project ways+shops to the local metre frame → simplify each way (Douglas-Peucker
eps~6m, the fidelity knob) → snap coincident points (<=3m) to shared nodes = real intersections →
straight-segment edges → classify main by OSM highway class + shop density → seat each shop on its NEAREST
real edge (real order, real side), marched with regularised spacing + a deterministic overlap-resolve pass.
Fidelity (charter risk #4): real edge/order/side + regularised spacing — NOT pixel positions (real shops
overlap; the no-overlap invariant is load-bearing). Alpha failure list in LANE_A_NOTES §R18: overflow drop
on short edges; intersections = shared-coordinates only (a D sim-graph risk); per-edge frontage-strip
blocks not planar faces (a B ground-render item); crowded-intersection overlap-resolve.
Proven: a committed synthetic irregular graph (main + two cross streets + a real intersection + an acute
angle + a dead-end lane + a curve) passes the FULL structuralSuite + gig district + byte-identical
determinism; mutation-tested non-vacuous (flip the facade facing → 'faces its frontEdge' fails). Robust on
messy inputs (single road, coincident dups, disconnected ways, all-roads-too-short → marched). All osm
goldens frozen; E's 4 trailing caches pinned on v1; katoomba_real UNPINNED (its real-roads golden pins when
E lands roads — the A->E->A finalization). selfcheck ALL GREEN 16810/16810; scaffold + consistency GREEN.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The half-day handshake so E's build_towns.py can fetch road geometry (ledger #1). Optional roads[]
on the town cache carries real OSM way geometry; plan_osm's graph lift (consuming it) follows in the
next commit. v1 caches (roads absent) stay valid — the marched fallback — so nothing shipped regresses.
- validateTownCache accepts schema procity-town-cache/1 AND /2; when roads[] is present each road must be
{ kind:string, pts:[[lat,lon],…] } with >=2 finite points (else rejected); <2 pts dropped, unmapped
highway kind → 'side' (warnings). ROAD_KIND maps OSM highway=* → CityPlan edge kind (main/side/lane/arcade).
- Full v2 contract in web/assets/towns/README.md (the doc E builds to): shape, road validity, the kind map,
the fidelity knob (charter risk #4), and the bbox-bound-the-roads rule.
- E's 5 existing v1 caches re-validate clean; selfcheck ALL GREEN.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
F wired the last mile of the real-map scout and gated the whole matrix.
- index.js barrel re-exports registerTownCache/validateTownCache; the shell fetches + registers
assets/towns/<key>.json before generatePlanFor when the town isn't a checked-in fixture — so
?plansrc=osm&town=bendigo_real boots a REAL AU town in the engine (the gap: E's caches were on disk +
in the node selfcheck, but the shell never loaded them → silent Melbourne fallback). Fail-soft
(404 / invalid / ?noassets -> fixture fallback); opt-in, never on the default or ?classic path.
- tools/qa/town_matrix.py (opt-in qa.sh --matrix): boot / determinism / budget / district / ?noassets
across 10 towns (2 synthetic hero seeds + 3 osm fixtures + E's 5 real AU caches). ALL 5x10 GREEN —
Castlemaine at the 6-shop floor still lands a 3-venue district. The existing CityPlan contract eats real
Australian geography: the evidence John charters v4 = THE REAL MAP on.
- docs: LANE_F_NOTES §17 (the town x gate table), README real-town row, F-progress R17.
qa.sh --strict --soak GREEN 7/7. Verified: ?plansrc=osm&town=bendigo_real -> Bendigo, 9 shops, 3 venues, 0 errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Street peds occasionally sit during a window-shop loiter (verandah/footpath
edge). makeActor gains an opt-in setSitting with a SCALE-AWARE re-plant (the
R16 spawnRig re-plant was scale-naive; the sim scales these rigs per-citizen),
played only on sit so walkers/placeholders/?classic are byte-identical. Seeded
via a dedicated benchsit rng stream (no shift to turn/loit/patron; identity
signature unchanged). Classic inert at the source (roll gated on sitClip).
In-shell (default, seed 20261990): head bone 1.466->1.070, bone span
1.667->1.299 (in seated gate [0.9,1.5]), feet re-plant to y=0 and hold through
sit-idle, stand-up restores inner.y 0.929 byte-for-byte, procitySeated tagged,
innerRotY:pi throughout. 9 distinct peds sat naturally per soak. Classic:
fleetSitClip:false, everSat:0. Plan golden 0x3fa36874 unchanged. Draws 35/tris 6k.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ledger #4 (warm-up): publish.py --verify no longer false-flags non-procity_ published assets
(sit.glb, vintage-cash-register). It compares the _published.json name record against the depot's
full file list (fetch_depot_files) instead of a procity_ prefix filter; only OUR namespace flags as
unrecorded. --verify now: all 39 recorded assets live.
Ledger #6 (the scout): new pipeline/build_towns.py ports thriftgod build_city()'s Overpass route to
per-town caches in A's procity-town-cache/1 contract. 5 real compact AU towns under web/assets/towns/
(katoomba/newtown/fremantle/bendigo/castlemaine, 6-21 shops, spans <2.4km): bounded Overpass query ->
raw cached in-repo (re-runs never re-fetch) -> dedupe/suburb-fill/parody (trademarked chains only;
indie shops keep real OSM names)/type-map. Deterministic (byte-identical re-run, no RNG). ODbL:
SOURCES.md licence table + license/attribution on every cache. All 5 boot through the full
structural+gig suites (selfcheck 16596/16596 green; synthetic golden 0x3fa36874 unmoved). Overpass
fetch John-authorized (Standing #2).
-> Lane A: caches live under web/assets/towns/; pin the per-town goldens selfcheck printed (katoomba
0x61760542 newtown 0x95978b45 fremantle 0xee9fd09a bendigo 0x5e3e76a6 castlemaine 0xf1a628a1) +
feed through plan_osm. E->A handshake.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The R16 seated drummer sat bolt-upright (the sit clip can't carry a pelvic tilt
— Hips.quaternion is unusable on these rigs). Fix: a small constant forward torso
lean applied POST-MIX. spawnRig({seated}) caches the mid-spine bone (seatBone);
seatedLean(spine) rotates it forward ~0.34 rad each frame after the mixer resets
it (constant offset, not accumulation); band.js calls it for m.seated members.
The drummer leans into the kit — head 0.28m forward of the hips, reads as a real
drummer from the side.
Byte-identical (my signature): standing rigs get no seatBone (found only in the
seated block) and seatedLean runs for seated figs only. Verified with the lean
live — walkers 188/188 face travel, keeper standing rig (stature 1.718,
hasSeatBone:false, innerRotY pi). No existing consumer moves.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The real-map scout (ledger #6), A's half. Scope fence held: NO new plan features, NO golden moved
(classic 0x3fa36874, gig 0xb1d48ea1, all three osm fixtures frozen). The scout proves the EXISTING
?plansrc=osm contract eats real geography.
- The town-cache contract (published FIRST — E builds build_towns.py to it). plan_osm.js gains
validateTownCache() (authoritative {ok,errors,warnings}), registerTownCache(), a generatePlanOSM(..,
{cache}) seam, and TOWN_CACHE_SCHEMA/MIN_TOWN_SHOPS(6)/MAX_TOWN_SPAN_M(5km). Shape: {schema,key,town,
source,license,attribution,center{lat,lon},shops[{id,name,type,lat,lon,suburb?}]}. Home declared:
web/assets/towns/<key>.json (+ README.md full spec). Documented in CITY_SPEC v3.2 + LANE_A_NOTES R17.
- plan_osm hardened: blank name -> registry label; the validator gates finite center/coords + a >=6-shop
floor and warns on unknown-type(->opshop)/dup-id/span>5km. Proven vs thriftgod's real 2,828-shop
city_source.json: boots a valid plan, 0 bad coords, 0 'undefined' names.
- selfcheck: a committed synthetic-adversarial contract suite (valid-messy caches boot + pass structural/
gig; invalid reject; wide-spread warns; registry seam) + an auto-loader for web/assets/towns/*.json
(validate -> suites -> pin per-town golden), empty and green until E lands caches. ALL GREEN 15277/15277.
- v4 charter risk list (LANE_A_NOTES R17): the MEGA-STRIP (unbounded bbox -> 11.6 km strip; town selection
is E's real lever, warned >5km); sparse compact towns (2 km inner-Canberra held 6 shops); osm facades
clear (rows 54 m apart) so R16's bias is a no-op on real towns.
E->A handshake: E drops a cache in web/assets/towns/, the selfcheck prints its golden, I pin it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verdict: shipped, not killed. The R7 park reason ("touches shared materials") is a non-blocker under
R16's measurement-over-brief standard: the gum-tree billboard material is B-owned (furniture.js), so
this is entirely Lane B -- no C/E material seam. Verified fresh Chromium seed 20261990.
- New web/js/world/wind.js: shared uniforms (uWindTime/uWindAmp) + applyWind(material) (onBeforeCompile
vertex patch: top-weighted horizontal sway, per-instance world-position phase, USE_INSTANCING-guarded)
+ updateWind(dt,intensity). furniture.js applies it to the tree material; weather.js drives it from
update(dt) (base breeze 0.05 clear -> 0.05+intensity*0.30 ~0.35 rain gusts).
- Trees only. Awnings excluded by MEASUREMENT: rigid posted-verandah box slabs don't flutter (fabric
canopy ripple = separate v3.3, shares the rigid awning InstancedMesh today).
- Byte-identical when weather off (the covenant): updateWind runs only inside weather.update, and the
weather system isn't constructed under ?weather=0 / ?classic=1 -> uWindAmp stays 0 -> sin(...)*0.0==0.0
-> identical vertices. Proven: amp=0 two times = 0/180000 px differ; ?weather=0 boot = 0 tree-motion px;
?classic=1 = amp 0, trees render, 0 errors. No plan/fetch/rng change -> F's classic gate untouched.
- Sway works: rain amp 0.305 moves ~14100/180000 region px over 0.8s (exaggerated 2.5 -> 101k); trees
render clean, no glitch. ~0 draws (ALU ops in the existing tree InstancedMesh vertex program), 0 errors.
-> Lane F (B->F handshake): SWAY SHIPPED. In old frames under the default boot; ?classic=1/?weather=0
sway-free + byte-identical. Procedural (no asset -> E has nothing to do).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
D's R16 wiring (ab7a8d0) reads stage.backline[0] with an immediate primitive ampPrim()
fallback + GLB upgrade. Verified fresh (seed 20261990), 3 archetypes x 2 modes:
- amp plants at backline[0] exactly (offset 0.00); 1 amp per stage (double-amp gone).
- no interpenetration: amp-to-nearest-band AABB edge gaps all positive — pub 0.40/0.44,
band_room 0.27/0.29, rsl 0.54/0.57 m (GLB/primitive). Rendered band_room confirms the read.
On-call (for A/E real-map scout): C interior type fallback eats real OSM shop types — 18 real
shop= values (+ ''/null/undefined/aliases) all build 0 throws, all pathOK, resolve to opshop.
No C code change (docs-only; verification of shipped behaviour).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The closing half of F's bookend. All gates green on the flipped tree (qa.sh --strict --soak: classic
regression + default-boot gate + smokes + no-giants incl. the seated drummer + the full-week soak).
- tour_shots.py -> docs/shots/release_v3_1/: the money shots on the DEFAULT boot (no per-shot flag juggling
now the flip covers it) + a ?classic=1 before/after ("the town before v3"). 7 shots, settled, 0 page errors.
- week_soak.py: re-pointed to the default boot; the leak check now treats an at/below-baseline OR non-climbing
trail as leak-free (a decreasing tex trail is GC/atlas-settle, not a leak — it was false-failing at warn-level).
- docs: README v3.1 flags table (defaults on + ?classic=1), CITY_SPEC v3.1 amendment (the rewritten prime
law + classic covenant, A's deferred edit), LANE_F_NOTES §16, F-progress R16.
Numbers (seed 20261990): default @ venue block 124 draws / 57k tris (<=300/200k); ?classic 161 draws / 24k
tris (the v2 baseline); seated drummer 1.32 m stature; week soak 42 cycles leak-free (net +0/-6).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The town shows its best by default. gigs / weather / winmap / tram flip ON (each keeps a ?flag=0 opt-out;
?classic=1 forces the exact v2 boot). The prime-law covenant MOVED from "flags off" to "?classic=1" —
byte-identical forever (synthetic fingerprint 0x3fa36874). The default boot is now the full experience,
deterministic to the gig golden 0xb1d48ea1 (A's R16 corner-poster re-pin).
- index.html: CLASSIC + flagOn(); gigs/weather/tram default-on; loadPedFleet({sit:!CLASSIC}) for the seated
drummer (?classic keeps the zero-fetch-delta covenant); a window.PROCITY.flags intent surface for the gates
- buildings.js: winmap default-on + classic-aware (the one cross-lane flip line the flag placement requires)
- flags_check.py: flags_off_regression -> classic_regression (?classic=1 byte-identical); a new default_boot_gate
(all four flags on, gig layer present, gig golden, budget); every gig smoke re-pointed at the DEFAULT boot
- seated-stature gate (ledger #3, published for D): interior_scale_check.py + flags_check.py accept
fig.userData.procitySeated at stature [0.9,1.5] m (the drummer); standing figs stay [1.4,2.0]
- qa.sh flags-harness gate name updated (classic regression + default-boot gate)
Verified: ?classic byte-identical (0x3fa36874, gig layer absent); default boot = the full town (14 gigs, 15
posters, tram, gig golden green, 112 draws @ the venue block); the seated drummer passes at 1.32 m stature;
district (3 venues / 3 genres), continuity, and cover all green on the default boot.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ledger #2 — drummer sits. E's sit.glb is wired into the shared rig stack:
- FINDING: Fable's prescribed _rotWithHips (keep Hips.quaternion) MANGLES the
pose in-shell (head folds to hip height / laid flat) — the fleet rigs' bind
Hips orientation is incompatible with the Mixamo clip's, exactly what _rotOnly
documents. So the sit clip rides _rotOnly (drops Hips.quaternion) like every
clip; _rotWithHips removed. The seated read comes from the leg bends + a new
spawnRig({seated}) foot-replant (opt-in; standing spawns skip it).
- Drummer uses fleet.sitClip when present (retires SEAT_DROP), tags
fig.userData.procitySeated=true (F's gate). Stature ~1.1-1.3m in [0.9,1.5];
reads seated from the crowd POV (kit hides the legs). Graceful SEAT_DROP
fallback when sitClip absent (?noassets / before F wires {sit}).
- BYTE-IDENTICAL proof (load-bearing): with sitClip null AND loaded, keepers are
standing rigs (stature 1.718, innerRotY pi, replanted:false) and walkers 188/188
face travel. Parallel/opt-in path; _rotOnly + makeActor untouched.
- F seam: loadPedFleet(base, {sit}) — pass sit:<gig active> (false under
?classic → no sit.glb fetch, classic covenant intact). Default off, graceful.
Ledger #5 — backline unification. _addBackline plants E's guitar_amp at C's
stage.backline[0] (== D's R15 pose, 0.75m bass clearance) instead of a hardcoded
pose. C dropped its primitive ampStacks, so D owns the backline: placeholder-
persists (primitive amp → GLB swap) with the primitive as the ?noassets fallback.
Verified: GLB swaps, primitive under noassets, no crash, 0 fetch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ledger #6 fix + the round's one sanctioned gig-golden re-pin, and ledger #7. Classic/synthetic
golden 0x3fa36874 frozen (classic boot byte-identical across all seeds); base plan untouched.
- Corner poster: the R15 finding (frontage poster near a crossing kerb) is NOT fixable by the brief's
'nudge along the facade' — proved the crossing edge is PARALLEL and spans the facade (0/187 slides
help); band_room/rsl land on through-lots on narrow warehouse blocks whose +Z wall (B's door side)
overlaps the block's other street by up to ~3 m. Surfaced to Fable; ruling: bias pickVenues. It now
prefers a band_room/rsl candidate whose +Z facade clears every carriageway by >= POSTER_CLEAR (0.5 m),
broadening off-spine if the fringe is uniformly narrow. Result: 0/851 frontage posters below floor
across 400 seeds (worst 0.51 m); pub untouched (hero shot unmoved).
- Gig golden re-pinned 0x4f4a549d -> 0xb1d48ea1 (posters moved). selfcheck's R15 blanket frontage
exemption becomes a real floor: frontage >= POSTER_CLEAR, spine >= 0 (POSTER_CLEAR exported from the
barrel). Mutation-tested non-vacuous.
- No-spine osm fixture 'silverton' (single-row town, 0 main edges) proves pickVenues' no-spine branch +
buildPosters' no-main fallback, closing the R14 sweep gap. Base golden pinned 0xd4b351c9; sweep asserts
0 main edges + graceful 2-4-venue placement. selfcheck ALL GREEN 14769/14769; scaffold + consistency GREEN.
- CITY_SPEC v3 amended (golden, poster clearance-bias prose, no-spine note, recorded amendment). Left the
prime-flag-law blockquote for F (the v3.1 classic-law + flags-table is F's amendment). For F:
default-boot golden = 0xb1d48ea1; ?classic=1 target = 0x3fa36874.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ledger #4. Verified fresh Chromium seed 20261990. No new rng, goldens untouched.
- Town-wide pools (venue.js): extended the R13 instanced-pool pass from venue frontages to EVERY
streetlight + bus shelter. New townPoolSpecs(plan) replicates furniture.js's lamp cadence off the
plan street graph (lamp every 18m from s=8, alternating sides, off=halfRoad+0.7) and reuses the
exported busShelterStops(plan) -- read-only, furniture.js untouched (imported one function). All
pools ride the ONE existing InstancedMesh -> still ~1 draw: 381 instances / 1 draw / ~760 tris.
Night-driven; fog:true now so distant pools fade into night fog (pools aren't chunk-streamed) instead
of floating as dots. Gig-gated -> ?classic=1 is pool-free = v2 parity by construction (no always-on
furniture change, the v3.0 blocker). Deterministic cadence, no seeded draws (release law).
- Default-boot re-baseline: measured the default content (gigs + weather=rain + winmap + tram all
live), settled. Busiest venue block (pub, NIGHT, rain) ~157 scene / ~170 total draws, 66k tris;
venue_night 122, queue_night 121, district_posters/street_noon 185 (worst tour shot), market_square
fresh 97/52k. ALL <=300 draws / <=200k tris. Flip adds ~3 draws (rain 1 + tram 2; winmap 0), pools
add ~0 (instanced). Daytime dense-citizen ceiling (~275, Lane D, live-walk) unchanged (pools night-only).
- Bookmark spot-check: all 19 resolve, un-letterboxed, 0 errors under the default boot; rain+tram+winmap
compose cleanly in old frames (street_noon rainy 177/114k, nothing blocked). No pose fixes.
Ran before F's flip landed (out of the stated order): pools are gig-gated so they work under ?gigs now;
re-baseline uses explicit all-flags URLs = post-flip default content. F re-confirms at close.
-> Lane F (B->F handshake): default-boot busiest block ~170 total draws / 66k tris; worst street view
185 / 131k; densest daytime 97 / 52k. All under budget. Town-wide pools ~1 draw. Pool sprite is
procedural (E has nothing to do).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- README: a v3.0 'venues & gigs, THE DISTRICT' section — what shipped, the flags table, the release tour
- LANE_F_NOTES §14 (R14: F never ran, brief carried) + §15 (the R15 release-close runbook, numbers, gotcha ledger)
- F-progress: R15 release-close summary at the top
- confirmed all five freezes committed (A a3911a4/35da83c, B eee60e0/a0b5784, C 62e38c5/2446ae3, D 9a65f51/66aec0f, E 55f6698)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tools/qa/tour_shots.py: one deterministic run capturing the release set into docs/shots/release_v3/
(+ contact.html), grouped by the flags each shot needs, all seed 20261990:
- venue_night, district_posters, queue_night (John's HERO — now with A's flipped frontage poster VISIBLE
by the door), gig_interior (the 4-piece + real instruments + a roster crowd — 2 punters carried through),
rain_verandah, tram_stop
- settled discipline (ledger #5): every shot measured after the async instrument GLBs / poster textures /
chunk stream land (shoot-twice), from a clean per-group load; the queue is driven LIVE (explicit admits,
not rAF auto-drain which automation throttles)
The hero confirms ledger #1: the frontage poster reads from the street for the first time. Interior settles
35k->182k tris (draws 56 <= 350 gate). All street shots <=300 draws / <=200k tris. 0 page errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tools/qa/week_soak.py: rolls the day-clock through all 7 nights in one context, entering every playing
venue each night. Asserts latch state per plan.gigs (dark venues stay quiet), the cover STAMP per venue
per night (debited once, re-entry free, due again next night), tonight's roster clears per night (no
accumulation), and geo/tex return to a WARMED baseline across >=20 enter/exit cycles — leak proven by a
plateau (a real leak climbs every cycle; warm-up cache residue plateaus). Opt-in --soak warn-gate in
qa.sh (never blocks the tag; the core 6 stay a fast strict run).
Verified seed 20261990: 2 playing (Exchange Hotel + RSL) + 1 dark, 28 cycles, $60 covers, geo/tex flat
across the back half (115/86 -> 115/84), 0 console errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ped you followed into a venue now turns up in its crowd. D published the seam in R14; F wires the three touches:
- interior_mode.js: crew.spawn({ roster: rosterOf(shop.id) }) — the front crowd slots become tonight's roster
- index.html: rosterOf=(id)=>citizens.tonightRoster(id) into interior_mode; onAdmit=(e)=>citizens.recordVenueEntry(id,e)
on the queue (each admit relays into the roster); the clear is automatic on the existing setGig(id,false)
- flags_check.py: smoke_continuity — asserts the seam (crowd includes roster-cap, pedIndex in tonightRoster),
empty-roster injects nobody (shell side of D's byte-identical law), + ledger #5 settled-tris honesty
Verified: 2 queue admits carry into the crowd; empty roster 0-injected; settled interior 71k->182k tris
(C's finding — interior gate is draws <=54, no violation). 0 console errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>