Commit Graph

216 Commits

Author SHA1 Message Date
m3ultra
8ac3b18b36 Lane F R22 (v4.0 — THE EPOCH CLOSE): the re-measure, the selector, the release tour, the tag
The blocking gate is CLOSED, and the epoch closes green.

- #1b THE BLOCKING RE-MEASURE: F's R21 re-measure made the tri diet blocking; B diagnosed layer-by-layer,
  the driver traced A -> E (A's own commit: "B's numbers say the fix is E's"), E fixed it, F re-measured:
  katoomba venue_night 221,935 (+11%) -> 108,365 (-51%); bendigo 201k -> 124,682; fremantle -> 88,397.
  Worst 124,682 — the 200k stress edge is closed. The measure-first loop worked end to end.
- #3 DBG.enterShop(selector) (Lane G's ask): id · registry type · exact name · name substring, resolved
  most-specific-first so an id-shaped string can't be shadowed. One-liner QA on any of 23 towns.
- #4 The release tour -> docs/shots/release_v4/: the Fremantle hero · the Fitzroy mecca · the Toowoomba thin
  tail · the synthetic control · the ?classic=1 covenant. Every real-town frame poses through F's R21
  cluster primitive; B's R22 bookmarks anchor on the same retail heart, so tour and bookmarks agree by
  construction. HUD hidden throughout (F's R21 ruling corollary).
  The primitive earned itself twice: the first cut posed via street_noon and the MECCA CAME OUT AS BARE
  BITUMEN — the same trap as R21. Re-posed through the cluster, Fitzroy reads.
- HONEST FINDING (filed v4.x): the brief asked for the thin tail as "proof the floor holds". It does not,
  and the frame says so — Toowoomba reads as an empty road. The floor is a RATIO, not a count: 0.008
  shops/edge vs the heroes' 0.06-0.08. Shipped labelled honestly rather than swapped for a flattering town.
- #6 Docs: README v4 section (the towns ARE the product), LANE_F_NOTES §22, F-progress.

qa.sh --strict --matrix GREEN 7/7; MATRIX GREEN — 10 towns × 7 gates (the budget gate green everywhere for
the first time); selfcheck 161,300/161,300 (A's 36 goldens + the 23-town pack).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:32:32 +10:00
m3ultra
b0e4ce103e Lane C R22 (v4.0): 23-town door-soak (#5) + the G2a atlas contract (#3)
Wave 3 — ran after A pinned the 36 goldens (3face97) and E's tri-diet fix (d81e499).

#5 — 23-TOWN DOOR-SOAK: five-town harness scaled (~10 doors/town; sample takes one of every
type present then stride-fills). 238 doors / 1,133 in-plan shops / 23 towns.
  23/23 PASS — 0 throws / 0 carves / 0 path-fails / 0 over-budget; worst 159 draws (fitzroy)
  <=350. All 12 archetypes covered town-set-wide (the whole registry). 23/23 towns landed 3
  venues — A's cluster bias + district placement hold from the 12-shop thin tail (darwin,
  toowoomba) to the 139-shop mecca (fitzroy, worst 159 = same band as everywhere: interiors
  don't care about town scale). Lane G's newtown_godverse passes (18 shops, worst 134).

#3 — G2a PER-SHOP ATLAS CONTRACT (C's half), published in LANE_C_PUB §7. All three Fable priors
hold; atlas-per-shop is strictly BETTER for draws than today's type-packs. The binding constraint
is VRAM, not draws — E's dispose ask GRANTED, it's my seam:
  - Draws: 1 atlas/shop = 1 draw (shared material + baked UV -> batch merges). Cap 2; 60+ item
    shops spill to the type-pack. Headroom huge (stock rooms 41-58 vs the <=350 law).
  - VRAM: E's 181MB audit is right, and the cause is my own design note (stockpack.js:11 caches
    atlases "NOT per-room, like depot GLBs") — correct for 3 bounded type-packs, wrong for
    unbounded per-shop. Ruling: <=2 resident atlases, <=8MB decoded, <=1024" typical; C lands the
    LRU cap + dispose in stockpack.js. Pre-existing + ?stock=real is opt-in (index.html:196) so
    it can't touch the default boot -> NOT a v4.0 blocker; C's first v4.x item, before G2a ships.
  - Schema: E's index confirmed verbatim, keyed per shop; UV top-left (C flips V); C reads uv not
    cell. Fail-soft/parody/?noassets unchanged. Ask to G: emit items[] in deterministic id order —
    C's seeded per-bin pick indexes into it.

C->F: 23-town interior verdict GREEN. Docs-only; both C items closed for v4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:12:00 +10:00
m3ultra
15ff53685f Lane D R22 (v4.0): citizens at pack scale — fitzroy stress PASS + the thin-tail clustering finding (ledger #5)
Against A's pack absorb (3face97, 23 towns / 36 goldens). selfcheck ALL GREEN
161300/161300, synthetic 0x3fa36874 frozen.

FITZROY STRESS: PASS — the sim doesn't notice 2x scale. 160 shops -> 139 seated (21
dropped, counted), 1859 nodes / 2408 edges (biggest of the epoch), 376 active peds,
0 NaN, 0.08 ms/frame, 22 chunk rosters, 454 identity sigs, 103 patronage visits.
A's R21 cluster bias survives the mecca (min 8 shops within 160m). Nothing needed
touching; the citizen field is not a budget risk at any scale this epoch reaches.

THIN TAIL — "does 12 shops still read alive?" ANSWER: only if they CLUSTER. I checked
both 12-shop towns because they disagree, and the disagreement is the finding:
- darwin_real ALIVE: 27m median nearest-neighbour spacing, 7 of 12 shops within 160m of
  the hub, 18 visits (13 hero).
- toowoomba_real DEAD: 388m median spacing (14x) across a 4.6km road network, only 3
  shops within 160m of its own densest point, 1417 patronage checks -> 0 finds, 0 visits.
  Not a shopping town — a road network with occasional shops.
Verified real, not an R20-style sampling error: 9 shops open at 14:00 in BOTH, camera
stood on the town's own densest point. 0 NaN / 0.05 ms/frame either way.

FILED for E/Fable (curation, not code): MIN_TOWN_SHOPS counts shops, but what makes a
town read alive is clustering. A cheap test that separates them: ">=5 shops within 160m
of the densest point" — darwin 7 PASS, toowoomba 3 FAIL. Toowoomba ships in the v4.0
pack today with no main street to walk. Cull or accept as a known thin-tail (it costs
nothing). E/Fable's call — A's venue bias correctly exempts it ("no better candidate").

D->F: citizens at pack scale GREEN, no D-side blocker for v4.0. Toowoomba is curation,
not a citizen defect — the tag isn't gated on it from me.
Carried to v4.x: GIG_RANGE 34m radius vs single-chunk lookup (8.7% chunk-blind).
Last flag before the retro: R20's main-street relocation half remains unowned (fitzroy
13 hero vs 90 texture visits); one call of this harness re-runs it if v4.x wants it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:09:39 +10:00
m3ultra
d81e499194 Lane E R22 (v4.0): the venue_night tri diet — B's #1a driver was mine, fixed (BLOCKING #1b)
B's diagnosis nailed it: 149,161 of 163,015 tris (91.5%) at katoomba venue_night were four of my GLB
street props. Buildings 348, posters 222 (A's cap worked, never the lever), pools 0. B's sharpest point:
it is NOT the density widening -- road density drives the bench/bin/shelter cadence (~100 props on a
966-edge real graph).

Fix (collapse-decimate via the R13 instrument tool; originals kept as _hi):
  bench       1942 -> 500   rendered 60,202 -> 15,500
  bus_shelter 7998 -> 2500  rendered 55,986 -> 17,500
  bin         3000 -> 800   rendered 18,000 ->  4,800
  food_cart   4991 -> 1000  rendered 14,973 ->  3,000
  furniture total 149,161 -> 40,800 (-108,361)
Projected view ~54.7k (B's 163,015 baseline) / ~113.6k (vs F's 221,935) -- PASSES the 200k gate either
way with 86-145k spare. F: these are projections off B's per-instance attribution; yours is the real
measure. -> F: RE-MEASURE.

Eyeballed every prop; two needed re-cutting: bus_shelter @1000 shredded (thin roof plate on a
voxel-remeshed shell) -> @2500 clean; bin @400 creased -> @800 clean. food_cart's black base slab is in
the SOURCE model (confirmed against the pre-diet thumb from git), not a decimation artifact.

Zero blast radius: dims/footprints/heights identical -> manifest.json byte-identical -> B's placement and
A's goldens untouched. Published 4 diet + 4 _hi archives to the depot (John re-authorized explicitly;
GLBs are git-ignored so the depot IS the archive).

-> Lane B (your 2nd note, cause found, NOT fixed by design): the MeshPhysicalMaterial on bench+food_cart
   is KHR_materials_specular in the glTF, and it survives a Blender re-export. Fix = neutralise specular
   pre-export. Deliberately deferred to v4.x: the gate is tris (cleared), and changing shipped shading in
   a release round without a visual re-verify is risk for no gate benefit. Same list: food_cart is 3.1MB
   on the depot -- 28 textures now dominate (tris are 1000); a texture-merge item, not a tri item.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:04:56 +10:00
m3ultra
d485b18ecc Lane A round 22: close the tri-diet loop — B's numbers say the fix is E's, not A's (ledger #1)
Docs-only handshake close; no code, no goldens. Filed so F isn't waiting on A for the re-measure.

My R22 notes left ledger #1's ownership open on purpose: "if B's layer-by-layer names plan geometry
(lot or poster counts) as the venue_night driver, I own the fix and will take it." B's diagnosis
(0c4dad5) has landed and answers it — the driver is E's four furniture GLBs at 149,161 of 163,015
tris (91.5%), which is ASSET TRIS, so LANE E owns the fix by the ledger's own rule. A IS CLEAR.

Plan geometry isn't close to the bill:
  posters   222 tris  <- B: A's cap "already worked and was never the lever"
  buildings 348 tris  <- the R3 atlas/instancing holding
  venue pools 0

Recording that B corrected MY expectation too, not just the brief's. I predicted the driver would be
buildings/LOD ("more shops => more night building geometry at the venue block"); B measured buildings
at 0.2%. And B found the real coupling: shop count doesn't drive this, ROAD density does — the
bench/bin/shelter cadence rides a 966-edge graph, so ~100 GLB props land in the streamed set where
synthetic had a handful. That means my R20 absorb of E's density widening was never the cause either.
The brief is a hypothesis and the measurement wins — mine included.

Lane A's round-22 ledger is now complete: #2 the pack absorb (3face97), #6 the docs close (9c9ad73),
#1 measured out of my court. Standing by for F's close.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:03:01 +10:00
m3ultra
af8a6db020 Lane B round 22 (v4.0): #3 per-town bookmarks — anchor on the retail heart (Lane G's filed bug) + F's primitive
ROOT CAUSE: every "nearest" resolver in dbg.js sorted by distance from the WORLD ORIGIN. That's the town
centre only on a synthetic plan -- a real OSM town is projected about its cache's bbox centre while its shops
cluster hundreds of metres away, so bookmarks teleported into arbitrary far-flung geometry, exactly as G
reported. Measured origin-pick distance from the retail heart: katoomba 408m, newtown 626m, fremantle 371m.

FIX: resolve bookmarks about the town's DENSEST SHOP CLUSTER (grid-bin shop lots at 120m; heaviest bin's
centroid = the retail heart) instead of the origin -- byDist, lotsByDist, streetViewPose, stopPose, and
crossroadsPose's tie-break.

GATED on the REAL-ROADS signal (edges > 200) -- the same primitive as the R20 tram fence -- so synthetic and
the marched fixtures keep their exact origin-centred behaviour and F's release-tour control shot cannot move.
The gate is load-bearing, not cosmetic: synthetic's anchor is only 117m out but it DOES re-pick (Anglibarn Op
Shop -> Dusty Pages), so an unconditional switch would have silently moved the control. Same reason the new
crossroadsPose tie-break is gated (a degree tie would otherwise resolve differently on synthetic).

Verified across the town set: synthetic(22 edges)/melbourne(15)/katoomba-fixture(15)/silverton(1) -> origin,
UNCHANGED; newtown_godverse(1626) -> anchor(98,-629); katoomba_real(993) -> anchor(-227,-625). G's own town
is fixed.

Result on katoomba: shopfront bookmarks went from ~408m adrift to <=42m of the retail heart (arcade 7m,
milkbar_dusk/patronage_door/shopfront_detail 24m, market_square 26m, dig_real 42m); strip views ~195m on the
high street; venue_night/queue_night ~345m (A's venue placement, not the bookmark). All 19 resolve, 0 errors;
synthetic 19/19 unchanged. warehouse_fringe sits 3,074m out -- correct by design (it's the far:true fringe
shot; on a 5km town the fringe genuinely is 3km away).

-> F (ledger #4, the shared posing primitive): published on the harness as DBG.townAnchor {x,z,n,anchored,ref}
   (the retail heart + whether it's in use) and DBG.clusterPose(kinds) (stand on the road at the densest
   cluster, look along it). Your cluster-pose finding as a dbg.js-consumable -- the tour and the bookmarks now
   resolve through the SAME primitive, so a tour shot and its bookmark can't drift apart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:01:41 +10:00
m3ultra
9c9ad730a3 Lane A round 22: CITY_SPEC v4 amendments final + charter risk-list retired-or-carried (ledger #6)
Docs-only; no output change, no golden moves (selfcheck 161,300/161,300 green before and after).

THE DRIFT THIS CLOSES. Three gig-layer behaviour changes had reached only my lane notes, never the
spec — the amendment law says the spec is where they live:
- ROUND20 ledger #2: the R16 facade-clearance bias WIDENED from band_room/rsl to every venue kind (line
  312 still claimed band_room/rsl). A real road graph at E's density can put a PUB's facade over a cross
  street; synthetic pubs always clear, so it stayed a no-op there.
- ROUND21 ledger #1: the venue cluster bias was never written down at all. Now recorded with the
  measurement (11 of 15 venues under the floor -> 0; Newtown's RSL 2,228m -> 29m from the hub) and the
  by-construction gate (, which synthetic never sets — measured, not assumed).
- ROUND22 ledger #2: the graded fallback (densest-available where the floor is unreachable).
Also documented the pick ORDER, which no lane could have inferred: cluster field -> kind flavour ->
clearance filter -> farthestFirst -> seeded pick, each degrading to the previous set.

CITY_SPEC v4 section marked FINAL at the tag: the alpha's five towns became the epoch's 23 (1,210
shops) and every ruling held with no new hardening. 'All five towns' now reads as the measurement of
record when the ruling was written; the ruling is pack-wide.

THE RISK LIST, line by line, judged against the full pack rather than the 5-town sample it was written
for (V4_REAL_MAP.md):
1. RETIRED — bbox law enforced with headroom: widest is geelong at 3,829m, 77% of the 5km ceiling.
2. RETIRED — the floor never bound: thinnest is darwin at 12 shops, 2x MIN_TOWN_SHOPS. (Whether 12
   shops READS alive is D's thin-tail check, not a generator risk — different question, still live.)
3. RETIRED plan-side; CARRIED for the runtime lanes. The alpha's real risk paid off exactly as
   intended (it surfaced fragmentation, capacity, stranding — each fixed-or-fenced). But a green plan
   is not a green town: C's soak, D's fitzroy audit and F's matrix are the verifies that close it.
4. RETIRED — the DP knob is at a measured setting: 1 component on all 23.
5. Unchanged, now on 23 towns not 5.
6. NEW, CARRIED — thin-graph seating capacity: 6.4% of shops don't seat (77/1,210), all counted by
   cause; daylesford 19% (32 shops on 171 roads), fitzroy 13%. Levers are spent at RGAP 0.5m /
   NODE_CLEAR 3m; the only one left puts a shop on a road it isn't on. R20 ruling stands: drop and
   count beats teleport. Carried into v4.x measured, not hidden.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:01:31 +10:00
m3ultra
3face97046 Lane A round 22: absorb the town pack — 23 towns, 36 goldens pinned in one commit (v4.0, ledger #2)
E's 17 new towns + Lane G's newtown_godverse fed through the lift. ONE batched golden commit as
briefed: REAL_TOWN_GOLDENS + REAL_TOWN_GIG_GOLDENS go 5 -> 23 towns each, zero unpinned.

THE HEADLINE: everything built this epoch held at 23-town scale, FIRST TRY. 1,210 shops -> 1,133 seated
(77 dropped, 6.4%, all counted by cause); 1 CONNECTED COMPONENT and 3 VENUES on every one of the 23 towns.
fitzroy — 160 shops, 2x anything absorbed before — landed 139 seated, 1 component, 3 venues, 212 posters,
all invariants green. The junction-protected DP, fragmentation ruling, cluster bias, poster cap and
capacity fence needed NO new hardening: the pack needed absorbing, not fixing.

- Graded cluster fallback (the one refinement). Three towns have no candidate at the floor at all
  (ballarat/toowoomba/newtown_godverse top out at 2 shops within 160m); R21's bias switched off entirely
  there, stranding venues at 0. It now falls back to the DENSEST AVAILABLE — ballarat 0,2,0 -> 2,2,2,
  godverse 0,1,1 -> 2,2,2, toowoomba 1,0,0 -> 1,2,1. Fires only where the floor is unreachable, so NO
  already-pinned town moved (verified). Acceptance at pack scale: 20 of 23 towns have every venue >=3;
  the three above sit at their town's genuine ceiling — the carved-out case, stated explicitly.
- Drop outliers, counted not hidden: daylesford 19% (32 shops on a 171-road graph, the thinnest in the
  pack, all overflow), fitzroy 13%. RGAP is already 0.5m and NODE_CLEAR 3m; the only lever left is
  spilling overflow onto a DIFFERENT street, which buys a few percent by putting a shop on a road it
  isn't on. Same ruling as R20: drop and count beats teleport.
- selfcheck ALL GREEN 161,300/161,300; scaffold + consistency GREEN; cross-process deterministic
  (pack-xor 0xca140954). classic 0x3fa36874 / gig 0xb1d48ea1 / marched fixtures FROZEN.

Handshake -> C + D + F: the pack is in, 23 towns pinned, start your verifies. Tri diet (ledger #1): if
B's layer-by-layer names plan geometry as the venue_night driver, I own the fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:55:05 +10:00
m3ultra
a7289d0550 Lane E R22 (v4.0): MODELBEAST notes refresh + provenance v4-final + G2a atlas review
Ledger #3 (E's G item), #6 (provenance prep), + the C/E contract review.

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:54:49 +10:00
m3ultra
0c4dad5acc Lane B round 22 (v4.0): #1a venue_night tri diagnosis — the driver is E's four furniture GLBs (91.5%)
BLOCKING-ITEM DIAGNOSIS, filed first so E/A/F aren't waiting. Method: katoomba venue_night, settled, then
hide-a-layer-and-re-measure (frustum-accurate: a layer's cost = the tris that vanish when hidden), then the
same drill inside furniture bucketed by geometry.

VERDICT: the four GLB street-furniture props are 149,161 of 163,015 tris = 91.5% of the stress view.
OWNER: LANE E (asset tris), per the ledger's own rule. Decimation clears the gate with ~120k to spare.

Layer breakdown (baseline 163,015 tris / 88 draws):
  chunk-furniture 151,341 (92.8%) | ground 9,932 | venue-presentation 770 | lighting 624 |
  chunk-buildings 348 (0.2%) | venue-posters 222 | citizens/weather/tram/venue-pools 0

Buildings are 348 tris (the R3 atlas/instancing holds). Posters are 222 -- A's cap already worked and was
never the lever. Pools cost 0.

Inside furniture -- the four GLBs are the whole bill:
  bench 1,942 tris/inst x64 = 60,202 (37%)      <- procity_street_bench_01
  bus_shelter 7,998 x10     = 55,986 (34%)      <- procity_street_bus_shelter_01
  bin 3,000 x19             = 18,000 (11%)      <- procity_street_bin_01
  food_cart 4,991 x8        = 14,973 (9%)       <- procity_street_food_cart_01
  pole/lamp/trees/novelty (primitives) x303 = 2,180 (1.3%)
Identity confirmed by DCC-authored material names (wire_143225087 = a Sketchfab scan, baked_mat,
"2 wood frame ", Material_0) -- E's GLBs, not our procedural materials. Two use MeshPhysicalMaterial.

WHY NOW (and why it is NOT the density widening): shop count doesn't drive this -- ROAD density does. Real
towns put the bench/bin/shelter cadence on a 966-edge graph, so ~100 GLB props land in the streamed set
where synthetic had a handful. A 1.2m bench at 1,942 tris is ~5x the entire building stock in frame. This is
B's own R6 note coming due ("street_bin 3.0k tris... a decimate to ~500 would give back tri headroom if the
combined-lane budget tightens").

-> E, suggested targets: bench 1942->~250, bus_shelter 7998->~600, bin 3000->~200, food_cart 4991->~400.
   149,161 -> ~29,000 (saves ~120k). Projected: my 163k -> ~43k; F's 221,935 -> ~102k. Both far under 200k.
   Cheap extra: drop the two MeshPhysicalMaterials to MeshStandardMaterial.
-> Fable: a B-side fallback exists (furniture.js already has a primitive fallback for every GLB prop via the
   R4 use-if-ready path), so a distance-gated LOD is a contained B change I can land fast. NOT doing it
   unilaterally -- per the ledger this is E's, and source decimation helps every town/view, not just this one.
-> F: my baseline is 163,015 vs your 221,935 on the same view -- my automation's rAF is throttled so the live
   gig crowd never spawns (citizens measured 0). Furniture is still ~68% of your number; decimation clears the
   gate on either baseline. Re-measure against the live crowd, not my figure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:52:43 +10:00
m3ultra
4590197259 Fable (integrator): round-22 instructions — v4.0 THE EPOCH CLOSE (tri diet first, pack absorb, release tour, G items)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 21:44:03 +10:00
m3ultra
dd4489c70e Fable (integrator): merge Lane G R1 at the v4.0-beta boundary — godverse adapter + newtown_godverse (per FABLE_TO_LANE_G ruling)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 21:42:04 +10:00
m3ultra
201a2bb868 Lane F R21 (v4.0-beta close): the beta gate — tram dimension, the re-measure, the beta shot
All five lanes in (A's venue cluster bias, B's selector-from-index, C's density door-soak, D's relocation
verify PASSES, E's 22-town pack non-gating); F gated and tagged.

- town_matrix.py: a `tram` dimension — B's R20 shop-adjacency ruling verified EXACTLY (newtown runs 16 stops
  after B's phantom-stop fix, castlemaine 10; katoomba/fremantle/bendigo FENCED at 3/2/1 shops fronted, all
  below the 5-shop floor). 10 towns x 7 gates: boot/determ/district/roads/tram/noassets green on all ten.
- index.html: expose window.PROCITY.tram — B built `routeInfo` FOR F's smoke but nothing published it; F owns
  the shell, so F wired the seam.
- RECORDED (ledger #6b — the instruction was "record the new number"): the venue_night re-measure. A's cap
  WORKED (posters 438->111) but E's density widening ate it — katoomba 200,965 (+965, R19) -> 221,935
  (+21,935, +11%); bendigo 201k (+0.5%); fremantle/newtown/castlemaine in budget. Draws fine everywhere
  (151/300 — half budget); tris-only, at the worst night stress view. Filed as R22's first item, where the
  tri-diet backlog (instrument LOD, tram tri diet) already lives. katoomba is the outlier: 966 edges, 72 shops.
- Ruling (ledger #6d): the town selector stays ALWAYS-IN-HUD, not ?dbg-gated — THE REAL MAP's product IS the
  towns. Corollary (F's own gap): money shots hide the HUD; the R19 alpha shot had the selector in-frame
  because F's harness screenshotted the full viewport.
- Beta money shot: Fremantle's real street at golden hour — a corridor of shopfronts, footpaths, a crossing,
  gum trees, and a genuinely busy crowd (the density widening, visible). 126d/167k tris, HUD hidden.
- docs: LANE_F_NOTES §20 (the honest why-R20-had-no-tag record) + §21, README beta paragraph, F-progress.

qa.sh --strict GREEN; selfcheck 154141/154141 (A's re-pinned goldens hold).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:37:04 +10:00
m3ultra
01c925816e Lane D R21 (v4.0-beta close): relocation verify — A's venue cluster bias PASSES (ledger #4)
Verified A's venue cluster-adjacency bias (110c828), assigned from my R20 relocation
finding. selfcheck ALL GREEN 154141/154141 against A's re-pinned goldens.

ACCEPTANCE PASSES ON ALL FIVE (no venue with <3 shops in its 160m radius):
katoomba min 9 | newtown min 5 | bendigo min 5 | castlemaine min 4 | fremantle min 3.
Castlemaine's floor did NOT need an exemption.

MY HEADLINE FINDING IS CLOSED: katoomba's pub — "a gig with no passing crowd", 1 shop
at 1007m — now sits at 466m with 9 shops. Newtown is the dramatic fix: all three venues
were 2.2-2.4km out with 0-1 shops; its rsl now sits 29m from the hub with 11.

THE CROWD FOLLOWED (katoomba pub, same density/seed/21:00): passing crowd 34->66 (2x),
patron checks 226->442 (2x), inside frames 1042->1424, roster 2->3, 0 NaN.

TWO READINGS THAT NEED CARE:
- fremantle is the weakest pass: pub/rsl clear the >=3 bar but moved FURTHER from the
  primary hub (969->1352m, 561->1292m) — A's bias optimises local cluster adjacency and
  they found a secondary cluster. Letter satisfied, crowd healthy (pub roster 6, inside
  2741), so not a miss — just named. A's call if they want it tighter.
- venue-win % DROPPING is not a regression (newtown rsl 100%->21%). It was only 100%
  because the venue was alone; post-bias it has neighbours competing for finds while
  total crowd rose. Isolation flatters that metric — ignore it post-bias.

D->F: relocation verify GREEN, no gate concern for v4.0-beta.
FYI F: selfcheck scans every towns/*.json on disk incl. E's UNTRACKED pack WIP — I saw
it transiently RED (newcastle_real, no open-late shop) then green minutes later. If you
gate while E is mid-fetch you may catch a false red; check if a failure is a pack town.
Still filed for v4.x: GIG_RANGE 34m radius vs single-chunk lookup (8.7% chunk-blind).
Flagged as unowned: R20's main-street relocation half (hero strip vs densest cluster).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:09:38 +10:00
m3ultra
8a383e494e Lane E R21 (v4.0): the town pack — 22 towns, 1192 shops (DATA-ONLY, non-gating)
Ledger #5 pull-forward. Nothing here touches a golden until A absorbs in R22; F tags v4.0-beta with
or without it. selfcheck ALL GREEN 154141/154141, synthetic 0x3fa36874 unmoved, all 22 byte-identical
on re-run.

- Curated 18 candidates on secondhand density first (heroes are the game), a walkable 2-3.4km centre,
  and a national spread; 17 survived -> 22 towns, 1192 shops, 298 heroes, all 8 states/territories.
  fitzroy is the mecca (40 heroes/160 shops, 2x katoomba); thin tail toowoomba/darwin (3 heroes) left
  in as honest data -- A/Fable may drop them in R22.
- FINDING: the 3x-hero cap is a CURATION FILTER, not just a density knob. Wollongong had ~62 real
  shops but 1 hero -> capped to 4 -> dropped by the >=6 floor. A town without a secondhand strip
  cannot inflate into a retail strip; John's SUBTLE directive does quality control for free.
- Newcastle: my centre was wrong, not the town (CBD held 2 heroes; the strip is Hamilton/Beaumont St).
  Re-centred: 2 -> 9 heroes, 8 -> 36 shops.
- BUG (mine, shipped R18, caught here): fetch_roads_raw had no query-comparison while fetch_raw did,
  so re-centring refetched shops but silently reused the OLD CBD roads -> Hamilton shops seated against
  a graph 2.5km away -> the town marched out with ZERO shops (surfacing as 32 bogus 'open-late
  invariant' failures; ten other no-video towns passing is what proved it wasn't the open-late rule).
  Fixed: the roads path now compares its query too -- the bbox lives in the query, so a re-centre must
  invalidate BOTH snapshots. Newcastle rebuilt: 34 shops seated, openLate=1.
- Discipline: bounded bboxes, raw snapshots committed (shops+roads), C's §6 mapping, SUBTLE guardrails
  (heroes never subsampled, texture 3x, 2392 drops counted), ODbL. Overpass rate-limits hard at pack
  scale (batch 1 died on a fatal 504 after 25 retries) -- courtesy sleep 4s, two polite batches.

-> Lane A (R22): 34 goldens to pin (17 new towns x base+gig); sizes 12->160 shops load-test your
   capacity/overflow fences. -> Lane B: index.json now lists 22 towns (your selector already reads it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:06:46 +10:00
m3ultra
110c82833b Lane A round 21: venue cluster-adjacency bias — D's relocation finding (ledger #1)
D found katoomba's pub 1,000m from the retail cluster — a gig with no passing crowd. Measuring all
five towns first showed it was SYSTEMIC: 11 of 15 venues sat with <3 shops within 160m (newtown's were
0/0/1 — every venue stranded). After the bias: 0 of 15. Katoomba's pub 1 → 9. Castlemaine cleared without
needing the floor exception the brief anticipated.

- pickVenues builds a CLUSTER-ADJACENT field first (>= CLUSTER_MIN 3 shops within CLUSTER_R 160m), then the
  kind flavors (pub corner / band_room fringe / rsl off-spine) bias WITHIN that field rather than overriding
  it (per the brief: biases, not absolutes) — a fringe band_room in the cluster beats one a kilometre from
  anyone. A town with no cluster at all falls back to the pool.
- SYNTHETIC FROZEN BY CONSTRUCTION, not by luck. The brief assumed a blanket filter would be a no-op because
  'the synthetic town is one dense cluster anyway'. Measurement says otherwise: 4 synthetic candidates PER
  SEED sit under the threshold (min cluster 0-1), so a blanket filter WOULD have moved the frozen golden.
  Gated on plan.source === 'osm' (undefined on synthetic) — impossible rather than unlikely. The marched
  fixtures are 'osm' but genuinely dense (min cluster 3/11/13) ⇒ verified no-op.
- Only the GIG goldens moved (venue selection is gig-layer; base town goldens untouched): katoomba
  0xbf586b77, newtown 0xacdb3eee, fremantle 0xcf3426fc, bendigo 0x646b2c23, castlemaine 0xf9713a69.
  classic 0x3fa36874 / gig 0xb1d48ea1 / marched fixtures FROZEN. selfcheck ALL GREEN 66419/66419;
  scaffold + consistency GREEN; cross-process deterministic.
- FYI: E's v4.0 pack (glebe/marrickville/newcastle) already boots clean through my loader (validates + full
  suites pass), listed UNPINNED — I pin the pack in R22; it does not gate the beta tag.

Handshake → C + D: cluster bias landed, goldens re-pinned, start your verifies (before/after table in
LANE_A_NOTES §R21).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 20:37:45 +10:00
m3ultra
4b0144989b Lane B round 21 (v4.0-beta): town selector reads E's towns index (ledger #2, the missed R20 #5)
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>
2026-07-16 20:35:47 +10:00
m3ultra
e3917dd35b Fable (integrator): response to Lane G — Draco ruling (no-Draco depot), density ruling, bug triage, G2/G3 sequence
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 20:34:30 +10:00
type-two
223fbcaf72 Lane G (GODVERSE, ultra) R1: real-shop census adapter — 18 real Newtown shops on the real road graph
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>
2026-07-16 20:19:14 +10:00
m3ultra
d678138ec1 Fable (integrator): round-21 instructions — the v4.0-beta close (venue cluster bias, selector index, density soak, the gate)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 20:14:39 +10:00
m3ultra
0d992803c5 Lane D R20 (v4.0-beta): citizens at density — five-town verdict + the main-street relocation finding (ledger #7)
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>
2026-07-16 20:10:03 +10:00
m3ultra
5d29863b78 Lane A round 20: absorb E's density widening — capacity, pub facade bias, goldens pinned (ledger #2)
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>
2026-07-16 20:05:11 +10:00
m3ultra
94e56ed5fa Lane B round 20 (v4.0-beta): the tram ruling — route by shop adjacency + per-town fence (ledger #3/#4)
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>
2026-07-16 19:35:56 +10:00
m3ultra
62cbb955b1 Lane D R20 (v4.0-beta): the density A/B + correcting my own sparsity misread (ledger #7, part 2)
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>
2026-07-16 19:34:56 +10:00
m3ultra
078245f7bd Lane E R20 (v4.0-beta): the density widening — widened fetch, 5 caches re-emitted, towns index
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>
2026-07-16 19:30:50 +10:00
m3ultra
85745d2017 Lane D R20 (v4.0-beta): gig crowd on the five real towns — pre-density baseline (ledger #7, part 1)
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>
2026-07-16 19:27:53 +10:00
m3ultra
c7ca39b4f7 Lane C R20 (v4.0-beta): publish OSM-class -> archetype mapping (ledger #2, C's half)
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>
2026-07-16 18:32:05 +10:00
m3ultra
955ec3497b Lane A round 20: spine-poster cap scaled by shop count (v4.0-beta, ledger #1)
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>
2026-07-16 18:27:29 +10:00
m3ultra
06fe64875a Fable: R20 kickoff ruling — density widening approved (John), directive: subtle, texture not takeover
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 18:14:53 +10:00
m3ultra
9029e4cbc5 Fable (integrator): round-20 instructions — v4.0-beta THE FIVE TOWNS (density widening, poster cap, tram ruling)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 18:13:05 +10:00
m3ultra
ddd1e6827e Lane F R19 (v4.0-alpha close): the alpha gate + roads dimension + the first real-street money shot
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>
2026-07-16 18:07:24 +10:00
m3ultra
96b3dea70f Lane D R19 (v4.0-alpha close): fragmentation re-measure — A's ruling verified (ledger #3)
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>
2026-07-16 17:41:59 +10:00
m3ultra
73694c97aa Lane B round 19 (v4.0-alpha): in-game town selector (ledger #4, beta pull-forward, non-blocking)
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>
2026-07-16 17:38:59 +10:00
m3ultra
6fc4b40632 Lane A round 19: the v4.0-alpha close — fragmentation ruling + findings hardened + goldens pinned
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>
2026-07-16 17:37:01 +10:00
m3ultra
55d5c634d9 Fable (integrator): round-19 instructions — the v4.0-alpha close (fragmentation ruling, BIG_CITY fix, the gate)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:06:18 +10:00
m3ultra
db33faa628 Lane D R18 (v4.0-alpha): citizens on real bones — audit + fragmentation finding (ledger #5)
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>
2026-07-16 16:42:06 +10:00
m3ultra
d4fcf68860 Lane B round 18 (v4.0-alpha REAL ROADS): irregular-geometry audit (ledger #4) — the failure list
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>
2026-07-16 16:35:55 +10:00
m3ultra
10db373e67 Lane E R18 (v4.0-alpha): the other 4 towns' road geometry (data-only) + Overpass retry
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>
2026-07-16 16:32:10 +10:00
m3ultra
dbb52c2390 Lane E R18 (v4.0-alpha): katoomba_real real road geometry (schema v2, ledger #3)
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>
2026-07-16 16:27:55 +10:00
m3ultra
a78dc76f84 Lane A round 18: the real-graph lift — OSM ways → CityPlan street graph (v4.0-alpha REAL ROADS)
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>
2026-07-16 16:10:14 +10:00
m3ultra
cf1334fc73 Fable (integrator): v4 REAL MAP charter + round-18 instructions — v4.0-alpha REAL ROADS (katoomba_real)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 15:29:10 +10:00
m3ultra
e4a49b5bc8 Lane F R17 (v3.2): real towns boot in the engine + the town-matrix scout gate (ledger #7)
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>
2026-07-16 15:23:15 +10:00
m3ultra
64ded4ea01 Lane D R17 (v3.2): bench-sit loiter (ledger #2) — the carried R16 stretch
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>
2026-07-16 14:53:22 +10:00
m3ultra
483a4b75c6 Lane E R17 (v3.2): the real-map scout — 5 AU town caches + publish --verify fix
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>
2026-07-16 14:49:40 +10:00
m3ultra
6671cf08f4 Lane D R17 (v3.2): the drummer's pelvic-lean (ledger #1)
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>
2026-07-16 14:32:58 +10:00
m3ultra
6fffd19043 Lane A round 17: v3.2 the town-cache contract (published first) + plan_osm hardened for real data
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>
2026-07-16 14:32:14 +10:00
m3ultra
ee16545941 Lane B round 17 (v3.2): wind sway SHIPS -- weather-driven gum-tree sway (ledger #5, parked since R7)
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>
2026-07-16 14:30:09 +10:00
m3ultra
b6404ff7a2 Fable: R17 kickoff rulings — v4 = THE REAL MAP (John), Overpass fetch authorized
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 14:05:50 +10:00
m3ultra
2ce1b9a640 Fable (integrator): round-17 instructions — v3.2 the tail + the real-map scout
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 14:01:37 +10:00
m3ultra
9277238462 Lane F R16 (v3.1 THE FLIP) — close: default-boot tour + week soak + docs
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>
2026-07-16 13:32:22 +10:00
m3ultra
ab7a8d0edf Lane D R16 (v3.1): the drummer sits + backline unification (ledger #2, #5)
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>
2026-07-16 12:49:44 +10:00
m3ultra
b322c998db Lane E R16 (v3.1): the drummer's sit clip — sit.glb produced + published
Ledger #2 asset half (E produces, D wires). Fetched Adobe Mixamo Sitting_Idle.fbx (533KB) by scp
from the tailnet source box, converted on-device via Blender (new pipeline/fbx_to_clip.py: import ->
skinned GLB export -> pure-python mesh-strip) to web/models/peds/sit.glb (109KB, mesh-free,
structurally identical to walk/idle: 66 nodes, 65 mixamorig bones, dangling skin, 195 channels).
Verified: three.js loads + 195/195 tracks bind to peds + 0 NaN; Blender confirms seated (hips
0.955->0.299m, stature 1.276->0.758m) + eyes-on bone-dot render (docs/shots/laneE/sit_clip.png).
Published to depot via tailnet under John's R13 auth (_published.json 38->39; benign non-procity_
drift note). Provenance: Adobe Mixamo royalty-free, same zone as the shipped walk/idle; LANE_E_NOTES
v3.1 amendment marker.

-> Lane D: sit.glb ready at web/models/peds/sit.glb (+ on depot); canon binds 195/195; seated confirmed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 12:37:27 +10:00
m3ultra
2b13dd4ad7 Lane A round 16: v3.1 corner-poster clearance bias (#6) + no-spine osm fixture (#7)
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>
2026-07-16 12:35:30 +10:00
m3ultra
54a030b38b Lane B round 16 (v3.1 THE FLIP): town-wide streetlamp pools + default-boot budget re-baseline
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>
2026-07-16 12:21:19 +10:00
m3ultra
42b11e717d Lane C R16 (v3.1): stage.backline[] amp unification, drop primitive ampStacks (ledger #5, C half)
C exposes room.stage.backline[] — 2 seeded up-stage amp slots {x,z,ry,y} (pure data) — and
drops its primitive ampStacks from placeStage. Lane D plants E's guitar_amp GLB at backline[0]
(D's primitive fallback under ?noassets): one unified backline, GLB when present.

- backline[0] = (stage.x + stage.w*0.36, deckY, stage.z - stage.d*0.22) == D's R15-verified amp
  pose exactly, so plumbing D's plant to backline[0] moves nothing on screen. backline[1] = spare
  stage-left mirror. ry=pi (audience-facing), y=deckY.
- Verified fresh (seed 20261990): slots up-stage, on-deck, clear of all 4 bandPoses (nearest-bass
  0.87 pub / 0.70 band_room / 0.92 rsl) and all watchPoints (>=1.95 m); determinism byte-identical;
  0 leftover ampStack groups; drawSweep venues 122/120/131 (was 126/124/135, -~4 draws each) <=350.
- Determinism/golden-safe: dropping the amp loop only drops draws on the 'stage' sub-stream (nothing
  downstream reads it); placement snapshot excludes ampStacks; interiors aren't hashed; ?classic has
  no venues. Disjoint from F's flip. LANE_C_PUB §3 amended (v3.1 marker).

C->D handshake: backline shape live. Verify with D once the GLB is wired at backline[0].

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 12:16:38 +10:00
m3ultra
b9e3f3a92d Fable (integrator): round-16 instructions — v3.1 THE FLIP (defaults on, classic covenant, drummer sits, town-wide pools)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 12:01:26 +10:00
m3ultra
0490bdd07e Lane F R15 (v3.0): docs assembly — README v3, LANE_F_NOTES §14/§15, F-progress, freezes confirmed
- 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>
2026-07-16 11:38:37 +10:00
m3ultra
6a35b29352 Lane F R15 (v3.0): the release money-shot tour (ledger #4) + settled measurements (ledger #5)
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>
2026-07-16 11:35:27 +10:00
m3ultra
146b5f3d2d Lane F R15 (v3.0): retire the R12 single-venue alias (ledger #2)
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>
2026-07-16 11:14:19 +10:00
m3ultra
2446ae3a5d Lane C R15 (v3.0): verify D's guitar_amp reposition (ledger #4)
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>
2026-07-16 10:49:20 +10:00
m3ultra
66aec0f83c Lane D R15 (v3.0): guitar_amp up-stage per C's §3 ruling (ledger #4)
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>
2026-07-16 10:25:08 +10:00
m3ultra
a0b578477c Lane B round 15: verify hero after A's poster flip + night poster readability (?gigs=1)
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>
2026-07-16 10:11:53 +10:00
m3ultra
35da83c347 Lane A round 15: frontage poster +Z street-face flip (ledger #1) + gig golden re-pin
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>
2026-07-16 09:48:17 +10:00
m3ultra
e466491300 Fable (integrator): round-15 instructions — close the v3.0 release (F's carried brief + poster-face fix + amp fix)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 09:21:39 +10:00
m3ultra
9a65f51a88 Lane D R14 (v3.0): identity continuity — the ped you followed is in the crowd
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>
2026-07-16 09:04:06 +10:00
m3ultra
eee60e05a5 Lane B (Streetscape) round 14: v3.0 release — alpha-alias retirement + tour audit + queue_night hero (?gigs=1)
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>
2026-07-16 08:43:22 +10:00
m3ultra
62e38c5df8 Lane C R14 (v3.0 release): venue audit + docs freeze + guitar_amp ruling
Light release round — C interior code unchanged since R13 (venue geometry stable by
construction); this is the audit, the amp ruling, and the v3.0-FROZEN docs.

- Re-verified vs the current tree (D's continuity + RY-fix + real instrument GLBs landed):
  contract byte-identical (caps 8/8/12, deckY/riserY, gigKeys, determinism), drawSweep <=350
  (worst 161), gig-night draws pub 60 / band_room 52 / rsl 71 <=350. Release-law confirmed:
  D's rng namespace is disjoint from C's ctx.stream; no golden can move.
- guitar_amp ruling (LANE_C_PUB §3): NOT blessed as-is. D's pose (stage.z+0.15, down-stage)
  interpenetrates the bass rig by 0.29 m; move it up-stage-right ~ (stage.w*0.36, deckY,
  stage.z - stage.d*0.22), clear of the bass + C's primitive ampStacks. Full amp unification
  = v3.1. Cosmetic + ?gigs opt-in -> not a tag blocker. C->D handshake.
- Docs freeze: LANE_C_PUB.md + LANE_C_AUDIO.md marked v3.0-FROZEN; RY §0 reframed past-tense
  (the +Z fleet was fixed at source in D's buildFigure, R13 - no per-consumer flips); budgets
  updated to R14; in-flight handoffs removed.
- Comment-only code (zero runtime/golden/rng change): fixed stale R12 inline return-shape
  comments (watchPoints 6..8 -> 8/8/12; stage bandPoses[3] -> [4] + riserY) in interiors.js +
  layout.js; documented the standalone gigKey hand-build. theme.js untouched.

Note for E/F: settled gig-night interior is ~182k tris (E instruments 14k each x5); F's smoke
reports ~34k measuring before the async GLBs load. Interior draw gate passes; v3.1 LOD candidate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:40:42 +10:00
m3ultra
a3911a4590 Lane A round 14: v3.0 release — 400-seed district invariant sweep + CITY_SPEC §v3 frozen
Release round: harden + freeze, no new systems. No golden moved (synthetic 0x3fa36874, gig
0x1f636349 both re-verified frozen); selfcheck.js is the only code touched, so no seeded generation
stream gained a draw (release-round law).

- selfcheck.js: factored the district checks out of gigSuite into districtInvariants(plan,label)
  (behaviour-preserving — hero-seed gigSuite runs byte-identical), then added a 400-seed synthetic
  sweep + the large edge seeds asserting the full district contract each (2-4 venues, >=1 pub, never
  the openLate shop, no band twice/night town-wide, per-venue 4-6 nights, every poster off the
  carriageway). Asserts the seeded count spans {2,3,4} across 400 seeds (146/121/133 — range is
  exercised, not pinned). osm graceful placement: katoomba (19 shops, no warehouse district) still
  lands 2-4 valid venues incl. a pub for every hero seed. Poster check kept at roadWidth/2 (a literal
  poleOffset compare would false-fail corner posters); added -1e-6 epsilon on the strict boundary.
  ALL GREEN 14264/14264 in ~0.7s.
- CITY_SPEC §v3: final wording pass, marked FROZEN at v3.0 (round 14) with a v2-style marker + an
  explicit carve-out for F's pending alias-line deletion. Fixed the roadWidth prose (full carriageway
  width, not 'half-corridor'). Genres final (pubrock/grunge/covers).

Coordination: the R12 single-venue alias line stays in §v3 for F to delete atomically with the
gig_state.js getters (debt #1) — deleting now would be doc-ahead-of-code (alias still live) and race F.
Flagged to F that the runtime block omits nightOf(id); to B that venue.js:228 is a 2nd bare-string
site. Verified: mutation-tested the sweep (non-vacuous — catches 5-venue/7-night/on-road violations)
+ adversarial pass (goldens frozen, freeze matches code, deterministic).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:36:31 +10:00
m3ultra
55f6698fb2 Lane E R14 (v3.0 release): provenance freeze + doc-consistency fixes
Provenance close-out for the v3.0 tag. Verified via a 6-agent inventory+audit workflow
(both adversarial audits PASS, 0 blockers) cross-checked against glb_stat/ffprobe/manifest.

- LANE_E_NOTES: v3.0-FROZEN provenance table + full audio(28)/GLB(38)/skins(96)/packs(3)
  inventories; by-design notes + v3.1 deferrals (street_bin decimation).
- AUDIT.md: add R13 gig-layer finalization section (14k decimation, _hi published, gigKey
  rename + 2 beds, depot publish done) — ledger was stale at R12.
- manifest glb_law string: state the documented <=8k-hero / <=14k-instrument tiers so the
  frozen contract is self-consistent (regenerated; 1-line diff, Gate 3 still 0/0).
- gen_audio: roomtone-video gain 0.3 -> 0.35 to match the shipped manifest.

Gate 3 GREEN; publish.py --verify clean; 0 quarantine assets in manifest; goldens unmoved
(synthetic 0x3fa36874, gig 0x1f636349). No new synthesis (genres final); no poster templates
(no ask from B).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 01:18:40 +10:00
m3ultra
f8ab837985 Fable: R14 kickoff rulings recorded (push done, flag stays opt-in, genres final) + gitignore local session files
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 00:59:42 +10:00
m3ultra
e4e16c34ed Fable (integrator): round-14 instructions — v3.0 release round
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 00:56:00 +10:00
m3ultra
a358c5d9ef Lane F R13 (v3.0-beta, the district): per-venue gig state + walla/applause + queue wiring; qa --strict 6/6
Wires A–E + D's district together and fires it. Seed 20261990 → "Boolarra Heads": 3 venues, 2 playing
tonight (The Exchange Hotel pub gig-pubrock + an RSL gig-covers, one dark), Screaming Utes $10 on the door.

- gig_state.js → PER-VENUE latches off the shared procity:segment spine (one per venueShopId); all three
  hard-won R12 laws kept. New runtime surface window.PROCITY.gigs.byVenue + onOf/openOf/gigOf/coverOf/
  bandNameOf/paidOf(id) + markPaidOf/venueShopIds; the R12 single-venue alias kept for one round.
- interior_mode.js: crowd-walla rides the interior tone layer under the live band bed while `on`; an
  applause sting fires once on the on->done edge. DELETE the GIG_BED bridge — debt #1, and it was a LIVE
  BUG: E renamed pubrock-live->gig-pubrock, so the bridge (gig-pubrock->pubrock-live) pointed at a dead
  key => the band would have mimed in silence. room.audio.gigKey (already the manifest key) feeds directly.
- index.html: per-venue cover charge (a night at two venues is two covers), concurrent per-venue setGig
  surges, and the outdoor VenueQueue lifecycle (D shipped the class + left the street-side seam to F:
  spawn at doors off B's queueZoneFor, drive each frame, dispose by done), exposed as queueCountOf(id).
- interior_scale_check.py: human-size assert now measures STATURE (crown-foot, platform-independent);
  world-crown ceiling-clip assert kept as its own line (debt #6).
- flags_check.py smoke_gigs -> THE DISTRICT: band->4, per-venue loop (every gigKey resolves across 3
  genres, crowd <= its own watchPoints, no-giants by stature), street budget at the busiest venue block,
  the queue assert; plus the R12 determinism/cover/noassets/flags-off arms.
- CITY_SPEC: pin the window.PROCITY.gigs runtime surface. gig_shot.py -> r13 interior + venue_night shots.

qa.sh --strict GREEN 6/6, 0 warn. Interior 63 draws/35k tris; street @ venue block 121 draws/51k tris;
band+crowd stature 1.61-1.81 m under a 4 m ceiling; flags-off fingerprint 0x3fa36874 unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 00:22:03 +10:00
m3ultra
fefbb4b806 Lane D R13 (v3.0-beta): 4-piece + instruments, outdoor queue, multi-venue surge, RY fix
- RY audit (debt #4): the ped GLB fleet's visual front is +Z but the whole
  system assumed -Z (sim heading math, C's pose ry, placeholder, impostor).
  Fixed at the source (rigs.js buildFigure normalises every rig to -Z front)
  and dropped band.js RY_FLIP. Corrects keepers/browsers (faced the back wall)
  and streamed walkers (moon-walked): 223/223 gig + 188/188 flags-off now face
  travel. Visual only, no golden move. keepers.js/placeholder.js/impostor.js
  unchanged (fixed for free). Canonical convention -> LANE_D_NOTES for C.
- Instruments (debt #2): E's manifest GLBs swap over the primitives async
  (placeholder-persists, fleet-gated -> 0 fetch under ?noassets), guitar/bass
  held, mic + kit planted, guitar_amp dressing.
- Drummer (debt #3): 4-piece, seated at C's drums pose (sink + kit occlusion;
  reads from the front/crowd POV - a sit clip is the polish).
- Outdoor queue (new queue.js): seeded 2-6 line at the frontage, drains on
  admitOne() + auto-drain, consumes B's queueZone or a frontage-hugging door
  fallback. F wiring seam documented in LANE_D_NOTES.
- Multi-venue surge (sim.js): setGig -> Set of concurrent venues.
- Interior gig 56 draws <=350; ?noassets+?gigs placeholders, silent-and-fine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 00:00:05 +10:00
m3ultra
5dac2cac5a Lane E R13: publish decimated instruments + _hi originals to depot
John OK'd direct publish. Pushed the 5 base instruments (14k) + 5 _hi hi-poly originals
via the passwordless tailnet ingress (this box is allowlisted). Public digalot.fyi now
serves the 14k bytes (amp 5.9MB->1.3MB) + the _hi originals; -70% decimation win is live
in production. _published.json 33->38; validate_manifest --depot GREEN. Gate-3 flag cleared.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:12:48 +10:00
m3ultra
46127c7e95 Lane B (Streetscape) round 13: v3.0-beta the district — N-venue frontage + night pass + posters + queue zone (?gigs=1)
Generalises the R12 one-pub slice to all 2-4 venues and lands the night-readability polish. All
gig-gated (flags-off byte-identical, verified in a fresh browser context). Files: venue.js, audio.js, dbg.js.

- venue.js -> N venues: per-venue lit marquee/frontage on B's +Z facade side + window-light spill;
  streetlamp pools on venue blocks (one instanced warm-pool mesh, night-driven -> the Friday-night
  walk reads); town-wide posters instanced per gig (14 posters = 2 draws / 2 textures, band-name
  overprint, flat printed-poster fallback). Town-wide pools deferred (would need always-on
  furniture.js -> breaks flags-off parity).
- Queue-zone marker per venue: venue.queueZone = {x,z,ry,len} for Lane D (head at the door, extends
  along the frontage; queuer faces -ry). queueZones{} + queueZoneFor(id).
- audio.js muffled spill per-genre (debt #1): import gigKeyFor; the nearest venue at doors/on within
  26 m spills its gig-<genreKey> through the 470 Hz lowpass, so pub/band_room/RSL crossfade genre.
  F: delete the GIG_BED bridge.
- update(gigStates) polymorphic: per-venue map {[venueShopId]:state} (F wires PROCITY.gigs.byVenue)
  or alpha-compat bare string (primary pub). Contract documented for F in LANE_B_NOTES.
- ?noassets fetch fix (latent R12 bug): gate the poster JPEG fetch behind !noassets -> 0 gig-layer
  network over the 22-JPEG facade/ground baseline.
- dbg.js: venue_night + district_posters bookmarks (seg 5) so tools/qa/gig_shot.py scripts the
  street money shot; poseForVenue resolves from the live plan.

Budget (fresh Chromium, gigs-on NIGHT, busiest pub block, all frontages lit): worst 154 scene /
~167 total draws, 64k tris -- <=300 / <=200k. 0 console errors (gigs / gigs+noassets / flags-off).
Verified 3 venues seed 20261990: pub->gig-pubrock, RSL->gig-covers, band_room->gig-grunge (all in
E's manifest).

Finding for Lane A: buildPosters item 1 seats each venue-frontage poster on the lot -Z (back wall,
measured 15.9 m from the +Z street facade) -- flip to +sin,+cos. Spine/pole posters render correctly
and face the footpath ("SCREAMING UTES" readable). Non-blocking (marquee + pools carry the shot).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:11:10 +10:00
m3ultra
82b711f962 Lane C R13 (v3.0-beta, the district): band_room + rsl archetypes, 4-piece band, RY audit (?gigs=1)
- theme.js: band_room (small/scuzzy tin shed) + rsl (carpet-and-flock club) venue recipes,
  reusing the pub machinery via a new per-kind venueSpec (deck size/height + crowd cap).
- layout.js: parametrized placeStage/placeWatchPoints; 4th `drums` pose seated on the riser
  (debt #3 — the band is now a 4-piece across all venues), stage.riserY + per-pose y.
- interiors.js: robust gig-<genreKey> resolution (gig.genreKey -> shop.genreKey -> kind
  default) matching E's renamed manifest beds (debt #1); API doc for the 4-piece + riserY.
- LANE_C_PUB.md: 3-kind venue contract + pinned ry convention and the empirically-verified
  keeper/browser back-to-front finding + source-normalise fix for Lane D (debt #4).

Verified fresh (seed 20261990 + 90-build soak): watch caps 8/8/12, gigKeys
gig-pubrock/gig-grunge/gig-covers, determinism 0, path/carve 0, gig-night worst RSL 237 draws
asset-free (16 rigs) / 77 GLB <=350, leak geo0/tex0. Shot: docs/shots/laneC/rsl_gig_r13.jpg.
Lane F: bump flags_check band==3 -> ==4 (tools/flags_check.py:674,794) — RED by design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:05:03 +10:00
m3ultra
bf98b41b16 Lane E R13 (v3.0-beta): gigKey rename + grunge/covers beds + instrument decimation
- debt #1: the manifest music key IS the gigKey, always gig-<genreKey>. Renamed
  pubrock-live -> gig-pubrock (files + gen_audio + build_manifest); old files deleted.
  B/F switch readers, F deletes the GIG_BED bridge (both fail soft to silence til then).
- two new gig beds: gig-grunge (band_room, quiet-loud dynamic) + gig-covers (RSL, mellow
  organ-led); procedural/seeded/loopable/$0. New organ() + grungechord() synth helpers.
- decimate the 5 instrument GLBs to 14k tris each (was 43-96k; fleet 236k->70k, -70%) via
  collapse (thin features survive); _hi hi-poly originals kept for depot; thumbs re-rendered.
- manifest regen (validates 0/0 incl --depot); app boots clean flags-off / ?gigs=1 (3-venue
  district, all 3 beds resolve) / ?noassets=1&gigs=1 (0 audio+GLB fetches).
- depot re-publish (14k bytes + _hi originals) gated on John's OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:03:14 +10:00
m3ultra
6fba79a1cb Lane A round 13: v3.0-beta the district (?gigs=1) — 2–4 venues, week schedule, town-wide posters, verge fix
Publishes the multi-venue plan.gigs interface C/D/B/F hang off (half-day handshake, A first).

- pickVenues: seeded 2–4 shops converted in place → pub (spine end) / band_room (warehouse
  fringe) / rsl (off-spine); 4th = 2nd pub at far end. Registry gains weight-0 band_room+rsl
  kinds + venueKind/genreKey/placement. Never the openLate landmark; osm-safe fallbacks.
- gigKey contract (debt #1): gigKeyFor(genreKey) => 'gig-'+genreKey, the single exported
  genre→bed mapping. No table anywhere; F deletes the GIG_BED bridge.
- plan.gigs goes district: 7 nights × every venue, each plays a seeded 4–6 (dark reads true);
  cover skewed by kind; no band plays two venues on one night; custom names spread night-major.
- plan.posters goes town-wide: tonight's playing venues each get frontage + seeded spine run.
- Verge fix (debt #5): new corridor law roadWidth/vergeBand/poleOffset(edge) in registry.js
  (road-vs-verge split of edge.width, published as functions ⇒ base goldens frozen). Posters
  seat on the verge, never bitumen; selfcheck asserts no poster on a carriageway.
- registry pub.interior 'band_room'→'pub' (unread field; avoids collision with band_room type).

Gig golden re-pinned 0xa6ae5a5e→0x1f636349 (multi-venue changed output; in-commit per amendment
law). selfcheck ALL GREEN 3273/3273; scaffold_check + consistency GREEN; flags-off byte-identical.
CITY_SPEC §v3.0-beta + LANE_A_NOTES §Round 13 updated in this commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 22:35:05 +10:00
jing
79852ca220 Fable (integrator): round-13 instructions — v3.0-beta, the district
v3.0-alpha re-verified green (qa.sh --strict 6/6, 0 warn) on the shipped tree. Round 13
opens the beta: 2-4 venues (pub/band_room/rsl), full week schedule, posters town-wide,
the outdoor queue, walla+applause, night-readability pass — all behind ?gigs=1.

All six R12 debts assigned: gigKey contract (manifest key IS the gigKey, gig-<genre>),
D wires E's instrument GLBs, the drummer (band goes 4-piece — John may veto), the
RY_FLIP facing audit, A's poster verge offset, F's stature-gate hardening.

Three items for John, only the depot-publish OK blocks anything.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:30:01 +10:00
jing
2498fdc892 Lane F R12: wire the v3.0-alpha gig layer (?gigs=1) — state machine, cover charge, band+crowd, gig audio
F last: five landed lanes → one Friday night. Seed 20261990 → The Thornbury Hotel,
tonight SCREAMING UTES, $10 on the door. qa.sh --strict GREEN 6/6.

- gig_state.js (new, F-owned): quiet → doors (DUSK) → on (NIGHT) → done (DAWN) → quiet.
  Rides lighting's procity:segment event (a poll only sees the segments it samples, and
  missed the night roll entirely); `state` is a getter because B's audio reads it from its
  own rAF while the player is inside a shop.
- ?gigs=1 seam: custom_bands.json fetched in the bootstrap (no fetch flag-off or ?noassets)
  → generatePlanFor(seed, src, {gigs:true, customBands}). 7 of John's 10 names fill the week.
- Cover charge on the R8 buy seam, zero new UI: paid debits once with a toast, re-entry the
  same night is free, broke → polite knockback, free nights walk straight in.
- interior_mode: opts.gig → C's gigKey; D's GigCrew (band 3 + crowd 8, 3 dancing) at C's
  stage/watch points; both disposed with the room (leak-free over 4 cycles).
- Cross-lane fix at the glue: C emits gigKey 'gig-pubrock', E shipped the bed as
  'pubrock-live' — unresolved the pub plays to a silent room and nothing errors. Bridged
  here; C/E/Fable owe CITY_SPEC a contract line. Details + 2 more findings in LANE_F_NOTES §12.
- smoke_gigs (16 gates): schedule determinism, band==3, crowd ≤ watchPoints, dance mix, bed
  resolves, ≤350 draws (47 rig / 171 placeholder), no-giants by STATURE (the band stands on a
  0.32m deck, so world-crown fails a correct band), cover ruling, ?noassets+?gigs.
  Flags-off regression gains a v3 arm: no state machine, no plan.gigs, no pub, no venue geo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 21:09:56 +10:00
m3ultra
f0a0a99207 Lane B (Streetscape): round-12 v3.0-alpha — venue frontage + posters + muffled-gig spill (?gigs=1)
- web/js/world/venue.js (new): createVenuePresentation — renders E's poster skins at plan.posters with band-name overprint (nameZone, seeded template, flat fallback if JPEG missing); lit pub marquee + bulb row over the door, update(gigState) ramps the glow. Constructed by the shell/F under ?gigs (like weather/tram).
- web/js/world/audio.js: muffled-gig spill — near the venue (<=26m) while a gig is at doors/on, pubrock-live plays through a 470Hz lowpass at low distance-gain. Reads window.PROCITY.gigs (F) with a plan.gigs+clock fallback.
- Prime flag law: inert/absent without the gig layer; goldens frozen (no citygen edits, selfcheck 3074/3074); byte-identical flags-off boot.
- Verified live on a gig plan: THE WOMBATTS posters over band-photo art, The Thornbury Hotel lit at night, spill gain ~0.14. F wiring documented in LANE_B_NOTES; streetlamp-pool stretch deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:15:40 +10:00
m3ultra
888335d633 Lane D round 12: gig band + crowd + patronage surge (?gigs=1)
v3.0-alpha — Friday night at the pub. New web/js/citizens/band.js (GigCrew):
- Band trio on C's stage.bandPoses (deckY), distinct fleet peds (seeded per gig), each with a
  primitive instrument on the chest (guitar/mic/bass; E's GLBs drop in via opts.instrumentFor).
- Crowd (<=8) at C's watchPoints on the gate-protected spawnRig path: dance:false = weight-shift
  idle, dance:true = bounce/sway/step with seeded phase (no metronome).
- Deterministic from citySeed; placeholder crew under ?noassets.

sim.js: setGig(venueShopId, on) gig-night surge — patronage pulls peds from further (GIG_RANGE)
and ducks in harder (GIG_SURGE) at the venue; occupants drain at close per A's closing-time ruling.
Guarded by _gigVenue==null so gig-off is byte-identical (prime flag law; goldens unmoved).

Verified standalone (A withGigs + C buildInterior + D): band 3, crowd 8, dancing 3, all 11 figures
human-sized (1.60-1.83m under a 4.0m ceiling; R10 no-giants holds), band faces audience + crowd
faces stage, deterministic, leak-free (8 cycles, 0 delta). Crew ~40 draws. Surge: venue occupancy
1 -> peak 5, identity stable. Shot: docs/shots/laneD/r12_gig_night.jpg.

Flag for C/F: band.js flips C's watchPoints/bandPoses ry by pi (RY_FLIP) — the fleet mesh's visual
front is local +Z, not the -Z the comments assume. Remove RY_FLIP if C re-fronts the gig poses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:49:07 +10:00
m3ultra
05e020e3b8 Lane C: round-12 pub venue — archetype + watchPoints + audio.gigKey (?gigs=1)
v3.0-alpha band room. New pub recipe (venue:true): stage + PA + amps at the back wall (new stage/paSpeaker/ampStack fittings, GLB slots stage/pa_speaker/amp for E), bar counter (keeper), sticky carpet, blocked back door = green room.

buildInterior returns for venues: room.watchPoints[] (6-8 seeded audience poses {x,z,ry,dance} facing the stage, dance ~1/3, counter.stand ry convention); room.stage {x,z,w,d,deckY,frontZ,bandPoses[3]} for Lane D's band; room.audio.gigKey ('gig-'+genreKey) set when Lane F passes opts.gig.

Flags-off byte-identical: venue code runs only for recipe.venue on its own seed sub-streams; non-pub rooms have watchPoints:[], stage:null, unchanged placement/audio.

Verified: 40-seed pub sweep (0 throws/carves/path-fails, 8 watchPoints, dance 0.38) + 60-room mixed soak (0 det/path fails, leak geo0/tex0, worst 19ms). Non-pub unchanged. Interface for D/F: docs/LANES/LANE_C_PUB.md. scaffold + manifest green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:59:48 +10:00
m3ultra
acf734a1e5 Lane A round 12: v3.0-alpha gig layer (?gigs=1) — venue, plan.gigs, band names, closing-time debt
Publishes the gig interface the whole round hangs off (A runs first). Everything is
behind ?gigs=1 as a POST-HOC augmentation (gigs.js withGigs, applied by the selector),
so generatePlan is untouched and all v2 goldens stay frozen (synthetic 0x3fa36874, osm
melbourne 0x34cfdec0, katoomba 0x0f652510). Source-agnostic (synthetic + osm).

- Venue: one 'pub' per town — a spine-end plain shop converted in place (keeps lot
  id/geometry, enterable), shop.venue=true, never the openLate landmark. Registry gains
  a weight-0 'pub' type (interior:'band_room' for Lane C) → never auto-placed → base
  plan byte-identical.
- plan.gigs: 7 nightly {gigId,venueShopId,bandName,genreKey:'pubrock',night,startSeg:5,
  endSeg:5,cover}; cover ~half free / half $2-$10 (F charges at the door).
- Band names: bandName(seed) generator (90s pub-rock) + OPTIONAL web/assets/custom_bands.json
  drop-in (priority into the pool; absent → pure generator, no fetch under ?noassets).
- plan.posters: ~6 seeded {id,gigId,x,z,ry} for Lane B.
- Closing-time debt paid: isOpen(shopOrHours,hour) exported (half-open law as code, D consumes).
- map.html?gigs=1: venue ring + poster pins + gig panel line. Flags-off unchanged.

Self-check ALL GREEN 3074/3074 (gigSuite ×2 sources, flags-off≡base, gig golden
0xa6ae5a5e). qa.sh --strict GREEN 6/6. Verified in-browser both modes. CITY_SPEC v3
section + LANE_A_NOTES handshake for C/D/B/F. Atomic pathspec commit (shared-tree race).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:33:53 +10:00
m3ultra
22a0741ad1 Fable (integrator): round-12 instructions — v3.0-alpha, one pub end-to-end
R11 reviewed: clean audio round, v2.1 tagged+pushed by F, E's pack fully procedural
(numpy synthesis, 23 assets, 8.7MB, $0). R12 opens the venues epoch behind ?gigs=1
with John's three charter rulings encoded: crowd = stand-and-watch + seeded ~1/3
dancing; events = mixed free/paid (seeded cover $0 or $2-10, wallet door charge,
knockback on skint, no re-charge same night); band names = generator + optional
web/assets/custom_bands.json drop-in (asset-law compliant, deterministic given
seed+file). A publishes plan.gigs first; C watchPoints; D band trio + crowd;
E instruments/posters/pub-rock bed; B lit frontage + muffled spill; F gig state
machine + door charge + smokes + v3.0-alpha tag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:32:34 +10:00
m3ultra
f7dd44f55a Lane B (Streetscape): round-11 audio — WebAudio engine (street side)
- web/js/world/audio.js: one AudioContext unlocked on first gesture; day/night ambience crossfade, rain (weather.intensity), footsteps, shop-door music spill (nearest open music-shop, distance-gain), tram rumble+bell, enterShop SFX. Consumes Lane E manifest.audio (3x retry). Self-ticks off window.PROCITY.
- House audio law verified live: silent pre-gesture (0 fetches), ?mute=1 silent surface (0 fetches), ?noassets=1 zero audio/manifest fetches, lazy-load (8.7MB<=25MB), no per-frame alloc.
- window.PROCITY.audio for Lane F: playInterior({musicKey,toneKey})/stopInterior (street beds duck while inside), playSfx, setMasterGain, mute, state diag. Confirms Lane C room.audio={musicKey,toneKey} contract.
- Small guarded [Lane B R11 audio] loader in index.html.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 16:00:31 +10:00
m3ultra
89f278b603 Lane C: round-11 audio contract — room.audio on buildInterior
buildInterior returns room.audio = { musicKey, toneKey }, seeded per shop: keys into Lane E's manifest.audio (interior music bed + room-tone). Lane F's interior_mode plays it, B's audio.js resolves keys->files. Keys not files: buildInterior stays synchronous + asset-free; silence on missing/mute/noassets (house audio law).

Maps mirror the manifest per-type types arrays: record/milkbar/video/dept/arcade always play their bed; other types room-tone-only except a seeded ~1-in-3 general radio (the 'general'-tagged milk-bar bed).

Verified 9 types x 8 seeds: 0 bad keys, 0 nondeterminism, all keys resolve + match manifest type membership, deterministic per shop. Contract + F wiring: docs/LANES/LANE_C_AUDIO.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:46:58 +10:00
m3ultra
be9f967d19 Lane E R11 (audio): procedural audio pack — 23 assets, free/on-device
gen_audio.py: a numpy synthesis engine (osc / ADSR / filters / reverb /
seamless-loop crossfade + note sequencer + drum synth). Renders 6 ambience
beds (street day/night, rain, 3 interior roomtones) + 13 sfx (doorbell,
door, till, record-riffle, toast, tram bell/rumble, 6 footsteps) + 4
instrumental 90s music loops (record-shop, milkbar, video-synth, arcade)
-> web/assets/audio/ as OGG/Opus + M4A/AAC. 8.1 MB of the 25 MB budget.

Tool = 100% procedural synthesis (not a neural model): $0, deterministic/
seeded, perfectly loopable, parody-safe by construction. Documented in
README (§ audio) + AUDIT.md provenance.

manifest audio:{ambience,sfx,music} — file + m4a fallback + loop + gain;
interior roomtone/music carry types[] (seed-pick per shop, like facades);
footstep is {surface:[variants]}. build_manifest.py builds it,
validate_manifest.py checks it. qa.sh --strict green (audio 23, 0 err).

Verified technically (peaks <= -0.4 dBFS, all loops seamless, durations)
via spectrogram sheet + numeric — subjective ear-check is John's/F's.

E leads the audio round; F owns the v2.1 tag + push. Not pushed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 14:50:15 +10:00
m3ultra
4b4e040032 Fable (integrator): v3 epoch charter — venues & gigs (John's pick)
Epoch decided: venue district + gig system. Charter defines the vision (seeded Friday
night: posters -> queue -> band room -> crowd with identity continuity), per-lane
ownership (A gig truth in plan, B venue nightscape + night-readability polish, C band-
room archetype + watch points, D band/crowd/queue rigs, E instruments/posters/genre
beds, F gig state machine + tags), three phases (alpha = one pub end-to-end), carried
ground rules, and three non-blocking questions for John. Round 11 (audio) is the
on-ramp; round 12 opens v3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 14:18:26 +10:00
m3ultra
aa322365a9 Fable (integrator): v2.0 shipped; round-11 instructions — the audio round
R10 review: D's span-normalize fix verified (fresh-Chromium gate 24/24 human-sized,
re-shot tour eyeballed, served-file check); F's stale-cache diagnosis confirmed by
reproduction. v2.0 tagged at 9b1c54e and pushed. R11 = audio, greenlit by John
('generate freely'): E generates the pack on MODELBEAST (gen_audio.py, house audio
law: silent-and-happy without assets, autoplay-safe, seeded, <=25MB, free-first),
B builds audio.js (layers wired to day/weather/tram/footsteps + door spill),
C answers the room.audio contract, F wires interiors + smokes and tags v2.1.
A + D rest. v3 epoch pick pending John (editor / venues+gigs / multiplayer).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 14:08:32 +10:00
m3ultra
9b1c54ea81 Lane F R10: verify giant-rig fix + re-shoot tour + re-frame night_crowd
Verified Lane D's R10 buildFigure span-normalize fix: qa.sh --strict GREEN 6/6 incl. the new
no-giants scale gate (24 interior figs / 6 shops all crown 1.4-2.0m, under-ceiling). The 3.83m I
first measured live was a STALE ES-module cache in a day-old browser tab, not a regression
(served rigs.js + fresh-chromium gate both correct).

Re-shot the v2 tour on the fixed tree: browsers.png now shows human-sized figures (browser at the
rack + keeper at the counter). night_crowd.png re-framed to a readable clean-night "lit VIDEO REGAL
amid CLOSED town" (pulled out of the rain group; reuses night_neon's tighter dist-12 pose).

Honest finding: night_crowd has no visible street crowd and can't without new work. Lane D's night
patronage parks ducked-in patrons hidden INSIDE the shop (they are the interior browsers), and this
seed's video shop is a low-traffic corner (tested: 0 patronage / 0 visible at night). Flagged for D;
a visible outdoor night queue is a v3 mechanic. ~20fps in night captures is a headless software-GL
artifact (scene is light: 128 draws / 65k tris).

Full-default soak PASS (heap 54MB stable, no perf drift, 0 errors). Tour README updated with stable
v2 filenames. v2.0 annotated tag staged separately (push held for John — public release + the
night_crowd caveat on his flagged money shot).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:56:23 +10:00
m3ultra
3432fccd1f Lane D round 10: fix interior giant rigs + no-giants QA gate
R9 blocker: interior rig figures were ~2x too tall (keeper crown 3.83m in a 3.4m room,
clipping the ceiling). Root cause in rigs.js buildFigure: it normalised scale by height/headY,
where headY is the head bone's world Y in BIND POSE — but the Mixamo skeleton origin is the hips
(~mid-body), so headY is head-above-hips (~half the standing height), not feet->crown. Every rig
came out ~1.83-2.0x too tall. (The head does NOT move when posed — bind crown 3.209m vs idle
3.205m — so it's a hips-relative measure, not an animation issue.)

Fix: normalise by the feet->crown span (headY - minY, both already computed). buildFigure(rig, h)
now lands the crown at exactly h above planted feet. Fixes keepers, browser rigs, near-tier street
rigs AND the impostor bake (all ride buildFigure). Verified: all 19 fleet rigs crown==1.75m; live
keeper 3.83m -> 1.82m; determinism + dispose-leak soak unchanged.

Regression guard (required): new tools/qa/interior_scale_check.py (Playwright) enters a sample of
shops, injects browser rigs at browse points, asserts every interior fig crown in [1.4,2.0]m AND
< room dims.H AND feet planted. Wired into tools/qa.sh --strict (run_gate => a giant fails qa).
Proven bidirectional: PASS on the fix (24 figs/6 shops), FAIL on a reintroduced giant. Full
qa.sh --strict: 6 passed, 0 failed.

Re-shot laneD/r10_browsers_fixed.jpg. Figures human-sized relative to doors/fittings: yes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:01:54 +10:00
m3ultra
8bd4676593 Fable (integrator): round-10 instructions — interior giant-rig blocker, v2.0 tag pulled
Round-9 review: engineering landed but the headline feature is visibly broken — interior
rigs (keepers + browsers) render ~2x room scale, heads through the ceiling. Verified live:
head bone at world Y=3.83m in a 3.4m room vs requested 1.58-1.92m. Root cause narrowed to
bind-pose head measurement in rigs.js buildFigure (raw headY 82 at bind vs 180 animated,
~2.2x underestimate -> giant after normalize). Visible in D's own r9 acceptance shot and
F's tour 'browsers.png'; neither lane flagged it. Local v2.0 tag deleted (never pushed;
origin still ends at v2.0-rc). R10: D fixes pose-aware + adds a head-height QA gate, F
re-shoots browsers + night_crowd and re-tags v2.0 on the fixed commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 12:12:55 +10:00
m3ultra
cb3aed196a Lane F R9 F3+F4: v2.0 tour (11 shots) + full-default & all-on soaks + docs freeze
F3: tools/v2_tour.py shoots 12-shot-class tour across flag-groups → docs/shots/v2_tour/ (11 shots,
all un-letterboxed 1280x720) incl. the rain-verandah money shot (night rain + winmap-lit open video
shop + CLOSED dark neighbours), browsers-in-a-shop, dig-real, katoomba, tram_stop + contact sheet.
soak.py gained --flags; full-default soak PASS (37 chunks/20 shops/heap stable/0 errors/leak-free)
and all-on soak (winmap+dig+stock=real+weather=rain+tram) PASS identically.

F4: F-progress round-9 section + FINAL v2 flags table (roster/patronage default-on; dig/winmap/
stock/weather/plansrc/tram/noassets opt-in; goldens frozen). LANE_F_NOTES v2.0 freeze note. CityPlan
v2 contract (A's CITY_SPEC freeze) validated live: selfcheck 1727/1727 + goldens. qa.sh --strict GREEN 5/5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:57:11 +10:00
m3ultra
16c4450cd9 Lane B round-9: v2 tram (?tram=1) + tour bookmarks + rain-drop polish
- tram.js: 1 small bus, seeded out-and-back loop on the main-street spine,
  door-dwell at each busShelterStops stop, ping-pong at ends. Deterministic,
  exactly 2 draws, night headlights via lighting.isNight(). Flag-off identical.
  Shell wiring (2 lines) documented for F in LANE_B_NOTES.
- dbg.js: 8 v2 tour bookmarks (16 total) incl. rain_verandah hero shot;
  tram_stop now frames the nearest-origin (open) shelter stop.
- weather.js: cap rain gl_PointSize (clamp 2-22px) - near drops are thin
  streaks now, not blobs. Rain smoke still GREEN.
- qa.sh --strict GREEN, golden hash frozen, flags-off byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:09:41 +10:00
m3ultra
23c0667dca Lane E round 9 (E1+E2): VRAM/texture audit + half-res book/toy atlases
E1 memory audit: measured all texture sources (decoded RGBA+mips). Resident VRAM
~240MB default boot / ~420MB worst case (all flags + all shop types); stock packs
the swing. Cheap win: half-res'd book+toy atlases (quick pull-cards, not the deep
record dig — covers verified legible) → stock VRAM 323->181MB, no index/UV change,
re-published to depot. Verdict: fine to ship v2.0; flagged stockpack cache-cap (C)
+ KTX2/Basis (4-8x) for v3. E2 hygiene: validate --depot 0 err, pack-QA 3/3,
provenance-drift clean (27 live), 0 orphan atlases. Numbers + verdict in AUDIT.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 10:59:22 +10:00
m3ultra
e5d4679471 Lane A round 9: freeze CityPlan v2 contract in CITY_SPEC + closing-time occupant ruling
Ship round. Docs + one selfcheck log-line only; no generator change ⇒ all three goldens
unchanged (synthetic 0x3fa36874, osm/melbourne 0x34cfdec0, osm/katoomba 0x0f652510).

1. CITY_SPEC v2 contract FROZEN — new "CityPlan v2 — frozen producer contract" section: producer
   API, goldens table (keyed seed/plansrc/town) + how to pin a new one, town-key mechanism, osm
   normalization rules, hours/openLate/storeys laws. Matches code as shipped (F diffs claims vs
   code). Refreshed the stale round-6 Plan-sources blockquote to multi-town reality. Layer-1
   changes now require a CITY_SPEC amendment + golden re-pin in the same commit.
2. Recipe sanity — DRY-RAN end-to-end: appended a throwaway Newtown (42 shops), selfcheck auto-ran
   the full parity suite (1931/1932, only golden unpinned) and printed a paste-ready
   `newtown: 0x9045e577,`. Reverted; no 3rd town shipped. Added the selfcheck `⚠ UNPINNED — add to
   OSM_GOLDENS` line so the recipe is mechanical; fixed the recipe wording to match.
3. Closing-time occupant ruling (D's on-call Q) — ratifies + freezes what D's sim.js already does:
   half-open hours, no new entry when closed, occupants DRAIN (finish bounded dwell) never popped,
   player not force-ejected, openLate video is the night crowd. In CITY_SPEC + LANE_A_NOTES. No
   code change asked of D.

qa.sh --strict GREEN; selfcheck 1727/1727.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 10:56:19 +10:00
m3ultra
34145e24a7 Lane D round-9: interior presence — occupancy truth + browser rigs (C->D->F seam)
Follow a ped into a shop and find them browsing. D owns occupancy truth; C owns browse points;
F wires the handoff. Validated end-to-end in-shell (?stock=real). qa --strict GREEN; v1 untouched.

Occupancy (D1): patronage records who's inside which shop by shopId. New
citizens.occupancyOf(shopId) -> {count, occupants:[{seed,enteredAt,pedIndex}]} (pedIndex = the
exact ped who ducked in, for identity continuity). Cleaned on emerge/chunk-drop/stream-toggle.
Deterministic: 17 shops + occupants byte-equal across two runs (fixed a stale-occupancy leak on
stream toggle).

Browsers: keepers.js gained browse:true (faces the shelf via ry, no player-greet) + pedIndex +
seedKey. Each = a merged ped ~1 draw. 12 enter/exit cycles: count == min(occupancy,3) 12/12,
worst 61 draws <=350 (C's headroom holds), leak-free (0 GPU delta after warmup), disposed every
exit via keepers.disposeAll().

Consistency (D2): inside peds hidden on the street (0 rendered), re-emerge at the door.

F handoff verified by running F's exact interior_mode wiring (enter -> occupancyOf ->
browsePoints.slice(0,min(count,3)) -> keepers.spawn). index.html setShops door points need
shopId:s.id added. Closing-time handled by the dwell model (pending A's explicit ruling).

Evidence: docs/shots/laneD/r9_browsers_in_shop.jpg. Changed sim.js + keepers.js + test page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 10:21:10 +10:00
m3ultra
9c7c65f57c Lane C round-9: draw-headroom note for D's browser rigs (F1 handoff)
Measured worst interior with 3 two-draw rig proxies at browse points:
68 draws (opshop/hall) in the ?stock=real path — browsers add ≤6, ample
headroom. Flags the one tight spot for D/F: pre-existing GLB-on opshop/
hall is 344/350, so keep rigs impostor-cheap if ever stacked on GLB
hero-props there; F's sweep-with-browsers confirms ≤350.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:57:55 +10:00
m3ultra
c68bba8f32 Lane C round-9 C2: buy-anywhere — pull-and-buy on book spines + toy boxes
Book/toy shelves get pull-and-buy over the SAME wallet; records keep the
deep dig. New stockpack.buildBuyableShelf(): a real shelf is merged,
per-item-ADDRESSABLE meshes — one per atlas (all covers drawn from one
seeded atlas → one draw/shelf), tagged noBatch so batch.js leaves them
intact. Aim a spine/box → pull card (title/author-or-maker/$price/band
from the pack index) → BUY debits the wallet, adds to inventory, and
collapseBuyItem() zero-areas the item's quad in place (removed from the
shelf, no geometry churn). Parody stock → no buy mesh → no card. Fail-
soft per pack; atlasPool() keeps each shelf single-atlas (one draw).

Test page: E interacts (bin→dig, shelf→card) + a #shelfBuy pull card;
shelfBuySoak() buys one item per book/toy seed. Validated: raycast→card
→BUY end-to-end (quad collapses to centre, cash/inventory update),
buy-soak 6/6 collapseOk leak geo0/tex0, ?stock=real draws record 41/
book 58/toy 57 ≤350, drawSweep GLB-off 168/on 344 unchanged, placement
determinism 0 fails, qa --strict GREEN 5/5. Shot: browse_buy_r9.jpg.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:56:21 +10:00