Interior audio (interior_mode): enter -> PROCITY.audio.playInterior(room.audio) with Lane C's
{musicKey,toneKey} (fade in), exit -> stopInterior() (0.7s fade). Guarded/no-op when
muted/noassets/pre-gesture -- silent-and-happy.
New smoke_audio gate (flags harness, fresh Chromium per the R10 standing law): pre-gesture silence
(ctx not unlocked), 0 boot errors, ?mute=1 muted, ?noassets=1 zero audio fetches, enter/exit
AudioNode leak-free (12 cycles, 8 shops with a music bed, live sources bounded at 5). Gesture via
keyboard -- a mouse-click gesture tripped PointerLockControls in headless.
scaffold_check: scoped the Math.random generation-ban to exempt runtime FX PLAYBACK
(web/js/world/audio.js) -- Lane B's footstep SFX variant/gain jitter is transient, not world
generation (which shop plays which bed is seeded upstream in interiors.js). Noted to B.
Soak with audio on PASS (heap 48MB stable, 0 errors, no drift). qa.sh --strict GREEN 6/6.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
Wiring (F owns index.html + interior_mode.js):
• Interior presence (C->D->F seam): occupancyOf closure -> interior_mode spawns browser rigs at
C's room.browsePoints (one per patronage occupant, cap 3); freed by exit()'s keepers.disposeAll.
Door points now carry shopId for D's occupancy truth.
• Tram (Lane B, ?tram=1): createTram + update in loop (2 draws, non-blocking).
• Buy-anywhere (Lane C C2): E aimed at a book-spine/toy-box buyMesh buys over the same wallet
(collapseBuyItem + toast); preload book+toy packs too. Records keep the dig.
Harness: graduated buy-v0 / patronage / book-toy stock warn->STRICT (katoomba golden already
strict). New warn smokes: interior presence (browsers==min(occ,3), dispose clean), shelf-buy
(book spine bought, cash drops), tram (present). Book-stock check now counts isStock OR buyMesh
(C2 tags book spines buyMesh, not isStock). flags_check GREEN 0/0; qa.sh --strict GREEN 5/5.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
Shell seams (F owns index.html): A &town=<key> -> generatePlanFor(seed,'osm',{town}) (Katoomba);
B weather (createWeather on ?weather=1; PROCITY.weather={state,intensity}, {clear,0} off; update in
loop); C session wallet bound to the dig BUY (getCash/onBuy in interior_mode); D patronage door
points -> citizens.setShops + ?patronage=0 off-switch + setWeather each frame.
Harness: stock=real + weather=rain graduated warn -> STRICT. New warn smokes: buy-v0 (dig BUY ->
wallet, cash 191->179 bag 0->1), patronage (7 peds visit shop doors/~18s), book/toy stock (6/11
real atlas meshes). Determinism gate: pinned osm/katoomba golden 0x0f652510 keyed on (seed,plansrc,
town) + fixed a hex zero-pad bug the katoomba golden exposed (0x0f652510 vs unpadded 0xf652510).
No patronage baseline re-pin needed (nets fewer draws, within tolerance). qa.sh --strict GREEN 5/5.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New js/world/weather.js + a one-line lighting.setSky(). Seeded per citySeed (weatherFor):
~57/24/19% clear/overcast/rain over 400 seeds, deterministic. Rain = ONE draw (camera-following
Points + procedural streak/fall/wind shader) + wet ground (roughness/darken on shared ground mats,
restored on dispose) + matching rainy sky dome via lighting.setSky. Fully procedural (0 depot fetch).
- Exposes window.PROCITY.weather = { state:'clear'|'overcast'|'rain', intensity:0..1 } for Lane D's
weather-reactive crowds (decision #1). Documented the contract + the exact 3-line shell wiring for F
in LANE_B_NOTES (I don't touch index.html). ?weather=1 seeded (default town rolls clear);
?weather=rain|overcast|clear forces a state for demos/smokes.
- Worst view 141 draws day / 126 night (<=300); composes with ?winmap + night (rain outside a lit
interior-mapped window = the money shot, screenshotted); deterministic; 0 console errors; flag-off
byte-identical (shell never constructs it). Deferred cut line: vertex-shader wind sway on
trees/awnings (touches other lanes' materials) — noted, not done. Tram stretch not attempted.
qa.sh --strict GREEN (5/5).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
qa.sh: flags harness warn_gate → run_gate — flags-off regression + the 4 existing flags +
all-on combo are now STRICT (a harness FAIL fails qa). New flags stay warn-level in-harness.
?stock=real wiring (F owns the shell + interior bridge): shell preloads the record stock pack
(preloadStockPack('record'), fail-soft, off under ?noassets) + passes stockReal to the bridge.
interior_mode builds makeStockAdapter(getStockPack(shop.type)) and passes it to BOTH buildInterior
(static crate sleeves) and dig.open (riffle) — null pack → parody canvas (Lane C fail-soft).
Harness: added stock=real smoke (assert non-parody = batched userData.isStock atlas meshes > 0;
verified 7 real sleeves in the record shop) + weather=1 smoke (auto-skips until Lane B lands),
both warn-level (decision #4). qa.sh --strict GREEN (5 gates); crates now show real covers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Added an interior draw-budget sweep to flags_check.py (decision #2): enter all 9 shop types,
assert <=350 draws/room, dig on so it also confirms C1's batching kept ?dig=1 riffling.
The sweep caught a real bug in interior_mode.js (F-owned): the interior/dig render never called
renderer.info.reset() while the shell runs autoReset=false (for the composer), so
renderer.info.render.calls ACCUMULATED every frame — inflating any interior draw read (and the
HUD were it shown). Fixed: reset per interior frame before both the dig and the walk render.
True per-frame result after the fix: all 9 room types <=350 draws (worst dept 96) — C1 batching
(35981ad) verified. flags_check GREEN (regression + interior budget + 4 flag smokes + all-on combo).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consumes the two orphaned street GLBs via the use-if-ready furniture path (fail-soft; ?noassets →
primitives, 0 network).
- Bins: genuinely sparse (~1/block, s+=78m, seeded side), decoupled from benches — country town not
a mall. GLB on its 0.57x0.68 footprint.
- Bus shelters ON THE STREET GRAPH (future tram/bus stops): deterministic rule = midpoint of every
MAIN edge with hashEdge(id)%3===0, road-side footpath by the kerb, long axis along the edge, open
front to road -> 1-3/town (2 in seed 20261990). Solid oriented-rect collider (furniture now returns
colliders; chunks.js merges them into getColliders) -> walk-into-it is blocked. Exported
busShelterStops(plan) -> [{edgeId,x,z,yaw}] as the stop-list contract for the vehicle loop.
- Perf: worst view WITH rig-fleet citizens 94k tris / 148 draws (<=200k / <=300, big margin);
deterministic; all-flags-on (winmap+dig+roster) composes clean, 0 errors. street_bin GLB is 3k tris
-> flagged to Lane E as a decimation candidate (bounded by the sparse cadence for now).
qa.sh --strict GREEN.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A landed the plansrc seam (52eb109); F owns the shell bootstrap. index.html: ?plansrc=osm
-> citygen.generatePlanFor(seed,'osm') (real-data 'Melbourne' 95 shops); default synthetic
'Boolarra Heads' unchanged (prime law). scaffold_check.mjs: pin BOTH goldens keyed by
(seed,plansrc) — synthetic 0x3fa36874, osm 0x34cfdec0 (deterministic) — + assert the selector
routes synthetic|osm. flags_check auto-detects + smokes plansrc + the all-on combo (4 flags).
interior_mode.js: restored binUnderAim's nearest-bin fallback (Lane C test-page parity) so E
riffles a nearby bin without perfect aim / before the bin GLB async-loads — fixes the dig
smoke's raycast-miss under fast headless AND improves real UX.
qa.sh --strict GREEN; flags_check GREEN (all 4 flags + all-on combo, 0 console errors).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
C shipped dig.js + the flag but left the in-game input/mode hook to F (LANE_C_NOTES C2).
interior_mode.js (F-owned): aim at a userData.kind==='bin' fitting + press E -> createDig +
dig.open (per-bin seed binSeed(shop.seed,pos)); the dig owns the frame (renders dig.scene,
suppresses interior walk); WALK OUT/Esc closes back to the room; closed on interior exit.
Shell (index.html): ?dig=1 -> dig: opt; procity:digOpen releases pointer-lock (riffle is
cursor-driven); the unlock->leaveShop guard reads interiorMode.digActive so opening a bin
isn't read as walking out; canvas re-locks for interior walk after close.
Fresh-chromium smoke: E opens the riffle (seeded sleeves + prices render), WALK OUT closes
to the interior, leak-free (steady-state geo/tex flat over 3 open/close cycles), 0 console
errors. Flag off => no listener, dig stays null => byte-identical to v1 (prime law).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Vuntra window trick — a passing pedestrian sees a plausible room through the shop glass with real
parallax, no geometry. One shared ShaderMaterial does single-cube interior mapping on the existing
window mesh (back wall + seeded shelves + side walls + floor/ceiling); per-shop variation via
aTangent/aTint/aSeed attributes, still 1 window draw/chunk. §3.5 held via the shared uHour/uNight +
aHours: night → open room glows warm, closed room dark.
v2 prime law: WINMAP is read from the URL in buildings.js (self-contained — no index.html seam; F
just needs a flags-table row). Flag-OFF byte-identical to v1 (windowQuad → v1 hoursQuad, v1
windowMaterial). Proven: flag-off windows MeshStandardMaterial w/ no winmap attrs; flag-on interior
ShaderMaterial; worst view 122-168 draws (<=300); 100% procedural (0 fetch delta vs ?noassets
baseline); 0 console errors; GOLDEN.hash untouched. Screenshots in docs/shots/laneB/winmap_*.
qa.sh --strict GREEN (4/4).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- B1: per-shop closed-facade visuals — dark windows + red CLOSED plate + door tooltip, batched via a
per-vertex aHours attr + two shared shaders + one uHour/uNight uniform pair on a procity:segment
event (facade atlas untouched, no per-shop materials, no per-frame poll). 22:00 → only the openLate
video shop lit, all others CLOSED; ~200 draws worst-view with citizens.
- B2: the shots 'letterbox' was camera-jammed-into-a-wall poses, not a composer bug (composer proven
correctly sized headless). Reframed street_noon/arcade/warehouse to look down a street
(streetViewPose), added the 3 missing bookmarks, night_neon now frames the lit video shop amid
CLOSED neighbours, + a defensive composer resize. shots.py: 10 un-letterboxed shots, 0 errors.
- B3: furniture GLB use-if-ready upgrade (bench + food_cart, sit on their footprints) + novelty_record
placed at record stores. novelty_record GLB is 26.5k tris (5x glb_law) → stays low-poly primitive
until Lane E decimates it; ?noassets verified 0 network.
qa.sh --strict GREEN (4/4).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- lighting.js sets scene.environment (neutral PMREM sky->ground gradient, no asset) in attachRenderer, before CitizenSim constructs; Lane D rig-fleet peds bake their impostor atlas from it and render dark if null. Verified set in street/night/interior + after interior-return.
- Re-measured worst continuous-walk view WITH citizens (pop 140, 125 active peds) via window.DBG: 263 scene / 275 total midday, 275 night -- <=300 everywhere. Radius-2 auto already the spawn default.
- Round-3 task 3 (furniture GLB footprints) standby: Fable s3.4 manifest upgrade not yet landed.
- Docs: LANE_B_NOTES.md + B-progress.md round-3 updates.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
interior_mode.js bridges Lane B's enterShop seam to Lane C buildInterior and Lane D
keepers (spawn at counter keeperStand, greet, leak-free over 5 cycles). CitizenSim
wired into the street loop (pop 140, ~96 active midday, worst ~191 draws). tools/:
qa.sh gate runner (all 4 landed gates GREEN --strict), scaffold/consistency checks,
shots.py + soak.py browser harnesses. docs/shots/ reference tree, LANE_F_NOTES
runbook, V2_IDEAS parking lot.
(F's inline seam edits to index.html/skins.js landed with the Lane B commit.)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>