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>
The compat alias delegating to the primary venue is gone — every read is now keyed by venueShopId.
B swept the cross-lane readers in R14; this migrates F's own tools + the spec, one atomic commit.
- gig_state.js: delete the alpha-shape alias block + the now-unused primaryId/P; update() just ticks all latches
- flags_check.py + gig_shot.py: .venueShopId->venueShopIds[0], .state->stateOf(id), .cover->coverOf(id), .bandName->bandNameOf(id)
- CITY_SPEC \S v3: delete the two alias sentences, add the omitted nightOf(id) to the accessor list
selfcheck 14264/14264 green (base 0x3fa36874, gig 0x4f4a549d); flags_off + smoke_gigs green, 0 warn.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
D moved the backline plant (66aec0f) to C's §3 corrected pose exactly
(band.js:236 = stage.x + stage.w*0.36, deckY, stage.z - stage.d*0.22).
Verified fresh (port-isolated context, seed 20261990, D's real GigCrew + GLB backline):
- matches §3 both venues; bass clearance 0.87 m (pub) / 0.92 m (rsl) — the R14
interpenetration (0.29 m) is resolved; on-deck; flanks C's primitive ampStack
(4.4 m pub / 3.5 m rsl) as a clean up-stage backline; no band/crowd clash.
- rendered pub gig confirms the read (amp up-stage behind the bassist).
LANE_C_PUB §3 updated RESOLVED; freeze otherwise stands. No code change (docs only).
Nothing else blocks on C — F runs the release (continuity wiring, alias delete, week
soak, tour, tag v3.0).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The R13 backline plant sat down-stage (stage.z + 0.15), 0.29 m from the bass rig
— it interpenetrated the bassist (C's R14 audit caught it). Move it up-stage-right
to C's blessed pose (stage.x + w*0.36, deckY, stage.z - d*0.22): 0.75 m clear of
the bass, behind the front line, flanking C's primitive ampStacks. Pose-only.
Verified: draw/tri delta 0 (same GLB moved — pub 50 draws / ~183k tris, <=350);
?noassets unaffected (amp is fleet-gated dressing → 0 props, band placeholders,
no crash). No other code — continuity + the ratified sit-clip deferral are closed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified A's ledger-#1 flip (35da83c): the pub's own-frontage poster moved from 15.9m behind the
building to 0.09m from the door on the +Z street face, facing the reader -- no queue_night pose
change needed (R14 prediction held). Gig golden 0x1f636349->0x4f4a549d consumed cleanly.
Fix (John-sanctioned exception to the R14 freeze): posters were unlit MeshStandardMaterial and
rendered pure black at NIGHT (measured frontage poster centre [183,71,59] day -> [4,0,1] night), so
queue_night + district_posters showed no readable bill even after A's flip. venue.js now gives posters
emissiveMap = the poster art, emissiveIntensity ramped in update() by night (0 day -> 0.55 night, same
lerp as glow/pools). Self-lights the bill after dark; daylight byte-identical (verified midday spine
poster pixel-unchanged). No new rng, no plan change, goldens frozen. ?noassets fallback self-lights too.
Verified fresh Chromium seed 20261990: queue_night (seg5, live queue) frames marquee + window glow +
streetlamp pools + a readable red poster by the door + queue punters (90 draws/29k tris);
district_posters night now shows a poster (was black, 180/138k); daylight unregressed.
-> Lane F (B->F handshake): HERO VERIFIED. Shoot queue_night at seg 5 (NIGHT) -- poster now reads there
and night is needed for pools+glow; drive the queue live (rAF throttled in automation). Poster
night-emissive rides the shell's per-frame venuePresentation.update(gigState.byVenue).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
R14 review found every venue's own-frontage poster on the building's −Z BACK since R13 (~16 m
behind the street face, B measured 15.9 m) — invisible from the street, why queue_night framed no
poster. Ledger #1, A-first (B's hero re-shoot + F's tour hang off this).
- gigs.js buildPosters item 1: flip to the +Z street facade (B's canon: buildings.js facade zf=+d/2
+ venue.js door/queue/camera are +Z) AND ry=lot.ry+π so the printed face points +Z at the queue-side
camera (else DoubleSide shows a mirrored band name). Proven: pub+rsl frontage posters now sit 0.06 m
from B's door, printed-face·(+Z street)=1.000. Spine posters untouched.
- Gig golden re-pinned 0x1f636349 → 0x4f4a549d (the round's ONE sanctioned move; posters moved).
Synthetic golden 0x3fa36874 frozen. §v3 FROZEN marker records the amendment (amendment law).
- selfcheck: the +Z facade is the block-interior side in plan coords, so corner venues (band_room/rsl
fronting one street with another behind — seed 1 lot#481, 1.58 m from a crossing centreline) seat the
frontage poster near that street. That's B's building geometry (the door is there too), not a mid-road
defect — so the no-bitumen assert now EXEMPTS building-bound frontage posters, keeps it for spine
posters. 400-seed sweep green again: 14264/14264.
- CITY_SPEC §v3 amended (poster prose → +Z facade + ry+π; golden line; frontage/spine carriageway split;
recorded amendment in the FROZEN marker). LANE_A_NOTES §R15 hands B/F the new golden + a corner-venue
eyeball note. Convention settled: visible venue face is +Z; selfcheck's −Z 'faces frontEdge' is the
sim/GLB convention (π-off the visual front, the R13 RY_FLIP ambiguity).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The last charter promise. A per-venue tonight roster (sim.js _venueRoster) of
who entered a gig venue tonight — surge occupants self-record in _enter, queue
admits relay via citizens.recordVenueEntry — is consumed by GigCrew.spawn: the
crowd's front slots BECOME the roster identities (same pedIndex, same height),
seeded strangers fill the rest.
- No golden move: both goldens are plan-gen hashes; each crowd slot is an
independently-keyed rng stream, so a roster override touches only that slot and
an EMPTY roster is byte-identical to R13 (verified). Roster heights 1.55-1.95m
stay inside the no-giants [1.4,2.0] gate. Pure bookkeeping: 0 rng, 0 GPU.
- Roster persists across interior exit/re-enter, bounded, auto-clears on the
setGig(id,false) transition (per-night). Deduped by stable key.
- queue.js: admitOne() returns {pedIndex,height,key} + fires an onAdmit callback
(auto-drain AND explicit) so F relays every walked-in punter to the roster;
_spawnAt stashes the identity. band.js: _make returns pedIndex; members carry
it for F's continuity smoke; crew.spawn is now null-gig-safe.
- Verified: surge 8/8 + queue 4/4 real identities land in the crowd; empty-roster
== R13; 7-night x 3-venue soak leak geo0/tex0, roster bounded, no identity
churn; interior 64 draws <=350 (0 new rigs); ?noassets placeholders, 0 fetch.
- F seam in LANE_D_NOTES: crew.spawn({roster}) + queue onAdmit relay + the
tonightRoster(venueShopId) accessor.
- Debt #2 (sit clip): investigated, deferred to v3.1 — the clip converts+binds
(tailnet Sitting_Idle.fbx -> 394KB sit.glb) but a real seated pose is ~1.2m
stature (fails F's no-giants floor) and needs a _rotWithHips loader + replant
(shared rig-stack) + an F smoke exemption; not a freeze-week change. SEAT_DROP
hack kept (reads seated from the money-shot front).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Release round: no new systems, no rng, goldens frozen (0x3fa36874 / gig 0x1f636349 both unmoved).
Files: audio.js, venue.js, dbg.js (+ LANE_B_NOTES). Verified fresh Chromium, seed 20261990.
- Alpha-alias readers dropped (debt #1, B first): audio.js venueGigState() loses the alias hop
(g.venueShopId===id -> g.state); byVenue + clock fallback only. venue.js stateGetter() loses the
bare-string arm (F passes gigState.byVenue since R13, confirmed index.html:379); seed = update(null).
Verified live: byVenue {116:on,404:on,490:quiet} at NIGHT drives the marquee with no string arm;
audio resolves gig-<genreKey> per venue with no alias hop. F is UNBLOCKED to delete the getters.
- Bookmark/tour audit under ?gigs=1: all 19 bookmarks resolve, frame un-letterboxed, 0 page errors,
<=300 draws. District venue-conversions hijacked none (night_neon->Video Regal, dig_real->Retro
Groove). No pose fixes needed. (Orphan-chunk teleport inflates tris across sequential shots -- fresh
counts <=153k; tram_stop's real ~293k is close-range Lane D GLB ped rigs, framing fine, not a venue block.)
- queue_night bookmark added (dbg.js): John's release hero -- close 3/4 on the pub door at seg 5
('on'), biased to the queue side; new poseForVenueQueue() recomputes venue.js frontage geometry.
Frames marquee + window glow + streetlamp pools + the queue (spawned live by the shell loop; F's
gig_shot.py drives it live -- verified by manually spawning D's VenueQueue: 2 punters at the door).
- Poster-repetition call (debt #4): FINE, no ask to E -- 2 gigs tonight => 2 distinct designs
(screenprint/Screaming Utes x7, xerox/The Dead Ringers x7); variety scales with venues playing.
- Town-wide streetlamp pools: v3.1 candidate per Fable ruling (venue-scoped for v3.0; no code).
-> Lane F: cross-lane alias sweep is CLEAN (no A/C/D/E reads it); only your own 20 tool lines in
flags_check.py + gig_shot.py remain -- migrate to *Of(id)/venueShopIds[0] when you delete the
gig_state.js:141-151 getters + the CITY_SPEC alias line (with A).
-> Lane A (now gates John's hero-shot poster): buildPosters item 1 still seats each venue's own
frontage poster on local -Z (back wall, 15.9m from the +Z street facade). Flip to +sin,+cos and it
seats by the door -- in the existing queue_night frame, no pose change. Per "don't touch world
code," B did not work around it. Re-flagged from R13.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>