Commit Graph

75 Commits

Author SHA1 Message Date
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
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
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
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
m3ultra
5529d62263 Lane C round-9 handshake: browse-points interface (C→D→F seam)
Publishes decision #1's contract FIRST (before code) so D and F build
in parallel: room.browsePoints[] (0..3, deterministic per shop.seed) —
{x,z,ry,atKind,slotIndex}, same coord frame + ry convention as
counter.stand. Guarantees: walkable, reachable (door→counter flood-
fill), ≥0.6m spaced, every archetype. C adds no meshes/draws; D's rigs
ride the ≤350 interior budget. Includes the D→F→C handoff (occupancy
count → browsePoints.slice(0,min(count,3)) → one rig per point).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:27:12 +10:00
m3ultra
e8f92a0da2 Fable (integrator): round-9 lane instructions — the v2.0 ship round (interior presence, buy-anywhere, tram, tour, docs freeze)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 09:16:59 +10:00
m3ultra
3daf8df3ec Lane D round-8: shop patronage v0 + weather reaction
The crowd comes alive. Default-on for the streamed roster (behind ?patronage=0); v1 path
(?roster=v1) untouched — no patron fields, golden identity holds. qa --strict GREEN.

Patronage (D1): per-ped state machine walking -> going (steer to a nearby open shop's door,
seeded ~every 10m within 18m) -> inside (hidden, 5-20s seeded dwell) -> emerge (resumes walk).
Hours-aware: noon = all types (up to ~38 inside, 7 types); night = video-only (the openLate shop
draws the crowd, roll ramps up as streets empty). Deterministic (150 chunk-keyed ids), leak-free
(0 GPU delta over the churn), budget-neutral (inside peds hidden -> draws -1).

Weather (D2): reads Lane B's PROCITY.weather contract (no import). setWeather({state,intensity}).
Rain -> density -56% (40-60% target), walk speed +14%, more sheltering (inside 6->25); overcast
-10%; clear = v1.

Also this session: R7 post-flip shell verification (no-flag boot streams, determinism, leak-free,
?roster=v1 restores golden roster) and pushed main + v1.0/v1.1/v2.0-alpha/v2.0-beta to origin.

F wiring (setShops door-point recipe, setWeather, ?patronage=0) documented in LANE_D_NOTES.
Only web/js/citizens/sim.js + web/citizens_test.html changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 04:42:34 +10:00
m3ultra
fbc8ffa1c8 Lane C round-8: buy loop v0 + book/toy real stock (?stock=real)
C1 — buy loop v0 (new wallet.js): runtime-only economy — seeded cash
(~$60–200), buy()/broke-gate/in-memory bag, onChange for a cash chip.
World gen untouched (purchases never re-enter the plan/seeded build).
Test page: #cashChip HUD + I-toggle inventory + dig BUY IT button
(pull → bag, cash down, bin depletes); digSoak buys ≤2/visit.

C2 — book spines + toy boxes wired to ?stock=real. stockpack.js keys
each pack to a slot (record→sleeve, book→spine, toy→box); stock.js
shelfLine builds real atlas-UV cover planes for book/toy shelves
(static, no dig), fail-soft per pack. E's packs live: record 350/6,
book 311/4, toy 273/5 (items/atlases).

C3 — validated: draws ≤350 (record 41, book 42, toy 51), determinism
0 fails, leak-free (0 geo/tex delta ×20; buy-soak 12 visits/5 bought,
leakGeo0/tex0), reload resets wallet, flag-off + ?noassets untouched,
qa.sh --strict GREEN 5/5. Shot: docs/shots/laneC/buyloop_r8.jpg.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 04:33:00 +10:00
m3ultra
f952f513d0 Lane A round 8: OSM to parity + a second town (Katoomba) — carried R7 brief, full
The carried brief (slipped R7) lands complete — all four items, qa.sh --strict GREEN.

1. Selfcheck PARITY: extracted structuralSuite(plan,label) and run the FULL invariant suite
   on every (source, town) — synthetic + both OSM towns get identical coverage (storeys,
   one-shop-per-lot, facing, within/cross-block overlap, corridor coverage, determinism,
   exactly-one-openLate). ALL GREEN 1727/1727 (was 1362; OSM went subset → full ×2 towns).
2. SECOND TOWN: Katoomba (Blue Mountains, 19 shops, op-shop/book heavy) — regional contrast
   to inner-Melbourne. osm_fixture.js → OSM_TOWNS{melbourne,katoomba}+DEFAULT_TOWN;
   generatePlanFor(seed, source, {town}); ?plansrc=osm&town=katoomba. Golden 0x0f652510.
   Melbourne 0x34cfdec0 + synthetic 0x3fa36874 UNCHANGED (town key kept off the plan).
3. "Add a town" recipe in LANE_A_NOTES (R8): extract → OSM_TOWNS → pin golden → tell F.
4. Normalization + LOG: importer bends real data to the contracts (unknown kind→opshop,
   hours-clamp so only one shop is openLate, video-preferred landmark w/ fallback) and logs
   it via {report}; selfcheck prints per-town (e.g. melbourne hoursClamped:5, openLate:video).

For F: three goldens to pin, gate keyed (seed, plansrc, town); selector gained {town} — see
LANE_A_NOTES R8. Screenshots docs/shots/laneA/osm-{melbourne,katoomba}.png.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 04:15:12 +10:00
m3ultra
a667283cf3 Lane E round 8: REAL stock packs (record 350 / book 311 / toy 273) live on depot
John unblocked GODVERSE DB access (m1ultra native-Postgres clones, read-only SELECT,
peer auth). All 3 packs rebuilt from real data:
- record 350: discogs_full.inventory (real store stock) join release/release_image,
  real Discogs cover art.
- book 311 + toy 273: dealgod.products by category_path, real product photos + prices.
Metadata parody-transformed (deterministic, no source-token leak -> no real trademarks
in pack text); cover images are real release/product art (read-only, non-economy,
decision #2); book prices seeded (source is info-only, John's call). 15 atlases
published to the depot; indexes committed (the C contract) + staged for ?localdepot;
all pass the pack-QA gate. build_stock_pack.py gained --from-tsv (extract-on-DB-box ->
build-here), portrait 176x256 book cell, seeded-price fallback. qa --strict GREEN 5/5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 04:09:14 +10:00
m3ultra
900b274a9a Lane B round-8: weather (?weather=1) — seeded rain/overcast/clear, exposes PROCITY.weather for D
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>
2026-07-15 02:34:14 +10:00
m3ultra
081755bb71 Lane E round 8 (E1+E2): book/toy stock packs + pack-QA gate
E1: build_stock_pack.py generalized over kinds — record (256x256 sleeves), book
(96x256 tall-thin spines), toy (256x256 boxes), per-kind parody word banks, same
build-time/parody/atlas/index laws. Book+toy sample packs built + staged (?localdepot)
+ indexes committed to pipeline/packs/ so Lane C wires book/toy shelves now. Parody
deterministic + no distinctive-token leak across all 3 kinds.
E2: validate_pack.py QA gate (wired into validate_manifest -> qa gate 3) — bad UV /
empty title / bad price_band / dangling atlas / dup id fails qa (proven). qa strict
GREEN 5/5.
Real 150-250-item packs blocked on GODVERSE_DSN (flagged to Fable); samples are
placeholders in the real index format. No fal spend.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 02:27:50 +10:00
m3ultra
1a20501e55 Fable (integrator): round-8 lane instructions — catch-up (A/B carried) + the town comes alive (patronage, buy loop, weather, pack breadth)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 02:06:34 +10:00
m3ultra
3724a65505 Lane C R7 C1: ?stock=real — real GODVERSE sleeves in the crates
The stockAdapter seam built in v1 finally eats. Lane E's stock pack (parody-
transformed Discogs metadata + cover atlas) feeds record-shop bins + the dig
riffle behind ?stock=real. Economy stays a stub (buying = toast).

- Contract handshake (task 1, unblocked E): confirmed E's index schema as-is
  in LANE_C_NOTES (items[{id,title,artist,price,price_band,atlas,uv}]; uv
  origin top-left, C flips V for WebGL; fail-soft; per-type packs).
- stockpack.js (new): preloadStockPack/getStockPack (cached) + makeStockAdapter.
  Atlas texture + material are shared/cached (like depot GLBs); each sleeve is
  a cover PLANE sampling the atlas at its baked UV, so all real sleeves in a
  room batch to ONE draw (record: 59 draws).
- stock.js binFan: real cover planes (else parody canvas). dig.js: riffles real
  covers, pull card shows title/artist/$price/band. interiors.js: opts.stock=
  'real' wires the adapter (warn-once + parody fallback if pack absent). Test
  page reads ?stock=real, preloads record/book/toy, passes the adapter to the dig.

Validated (E's staged 24-sample; real covers drop in unchanged when E gets the
DB): determinism 0/30, leak-free rooms + real dig (0/0 over 25 cycles), draws
<=139 all rooms (record 59), fail-soft on no-pack types, ?dig=1&stock=real
leak-free. Flag-off + ?noassets untouched (procedural path byte-identical).
qa.sh --strict GREEN. F smoke hook + E contract in LANE_C_NOTES. Shot
docs/shots/laneC/stock_real_r7.jpg.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 01:23:23 +10:00
m3ultra
43edabfe56 Lane D round-7: validate ped-merge → GO for roster flip + full-density defaults + escape hatch
Lane E's R7 merge (447188a) made each ped 1 mesh/1 material (~1 draw/near-rig, fleet 116->24 at
the cap) — the gate my R6 memo named. Validated + handed F the GO.

Merge validated: bind+animate/no-T-pose, skinning/skeleton preserved, silhouettes+atlas materials
crisp (hi-vis/suit/kid), impostor bake clean, identity variety + determinism intact. GPU memory
dropped (geom 205->160, tex 141->90).

Flip numbers (full density, perChunk 16, in-shell): worst street view 241 draws <=300 (was 356
unmerged), 65,899 tris <=200k, 17 near-rigs; 30-chunk soak (726 builds/disposes) leak-free, 0 GPU
delta, heap stable. => GO.

D2: enableStream default perChunk 8->16 (full density, the merge affords it); near-cap stays 24.
D3: test page + harness default to stream now; ?roster=v1 escape hatch restores the fixed roster
(verified streamMode false + determinism). No-flag boot -> streamMode true + determinism.
F's web/index.html flip wiring (invert to default-on + ?roster=v1 escape) documented in
LANE_D_NOTES; in-shell no-flag-URL boot completes when F lands F1.

Only web/js/citizens/sim.js + web/citizens_test.html changed (+ NOTES/progress).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 00:51:51 +10:00
m3ultra
0c12dacb6d Lane E round 7 (E2): GODVERSE stock-pack pipeline (parody + atlas + index), proven
pipeline/build_stock_pack.py builds C's ?stock=real pack offline (nothing queries the
dataset at runtime): recordgod (dealgod discogs_release) → curate → parody-transform
title/artist (deterministic, shares no source token — no real trademarks) → atlas WebPs
+ JSON index {id,title,artist,price,price_band,atlas,uv}. Proven end-to-end with
--sample 24 (synthetic sleeves); sample pack staged to web/assets/models/ so Lane C
wires ?stock=real against the real index format now. Real 200-400 pack blocked on
GODVERSE_DSN (dealgod Postgres creds — flagged to Fable) + C's schema confirmation;
one command (--from-db --count 300) once they land.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 00:43:08 +10:00
m3ultra
447188a516 Lane E round 7 (E1): merge ped rigs to ONE draw each (gates roster flip)
Each of the 19 ped rigs carried 1-9 primitives / 1-3 materials (fleet 92 draws,
~116 at the 24-rig near cap). merge_ped.py (Blender) merges each to 1 primitive +
1 atlased material: join meshes -> pack material UVs into a non-overlapping atlas UV
-> EMIT-bake base-colours into one 1024 atlas -> single material. Skinning + skeleton
preserved (65 joints, walk/idle untouched, clips retarget unchanged). Fleet 92->19
draws (1/ped); near-cap 116->24 (79% cut). Verified in vendored GLTFLoader: all 19
load as SkinnedMesh + bind walk; man_suit poses byte-identical to original. Hands to
Lane D (D1) for full validation + full-density numbers, then F flips the roster.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 00:33:03 +10:00
m3ultra
15e2cc0a9f Fable (integrator): round-7 lane instructions — roster default-on flip + GODVERSE real stock + weather + strict harness
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 23:53:54 +10:00
m3ultra
24cef5a7bb Lane B round-6: place street_bin + bus_shelter GLBs (on the street graph, collision-verified)
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>
2026-07-14 23:04:14 +10:00
m3ultra
35981ade5f Lane C R6 C1: interior draw batching (<=350 draws/room law)
Review debt: book room was 1,245 draws GLB-on. Diagnosed two drivers —
multi-material stock boxes (one draw per face, 6x) and one mesh per stock
item — then batched:

- stock.js: stock boxes are single-material now; every stock mesh tagged
  userData.isStock.
- batch.js (new): batchRoom() two-tier merge, called from interiors.js before
  the GLB upgrade. Room-level merge of all isStock across every fitting
  (grouped by material, world transforms baked) + per-fitting merge of fixture
  frames (kept in-group so record bins stay raycast targets for ?dig=1 and the
  GLB upgrade can hide the fixture). Uses vendored BufferGeometryUtils
  mergeGeometries.
- glb.js: frame-hide is now by isStock tag (not frame-count), so batched
  fixtures hide while the room-level stock rides on the GLB.
- interior_test.html: drawSweep() asserts <=350 draws across all type x
  archetype (GLB off and on), folded into the soak; exposed as
  window.PROCITY_C.drawSweep for F's harness (hook in LANE_C_NOTES).

Result (drawSweep, seed 1990): GLB-off worst 152, GLB-on worst 313
(opshop/hall) — book 1,245 -> 189. Determinism 0 fails / 810, leak-free, dig
still riffles batched bins, worst warm build 38ms (<50), visually identical.
qa.sh --strict GREEN (5 gates).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:01:04 +10:00
m3ultra
5db3155991 Lane D round-6: harden streamed roster + default-on readiness memo
Flag ?roster=stream stays default-off; v1 path byte-identical (citizen-0 = R4 golden, passes
F1 flags-off regression). Full evidence memo in LANE_D_NOTES for the R7 default-on decision.

- Soak (in-shell, real town): 242 chunk builds/disposes over 3 walks -> 0 GPU geom/tex delta;
  16 shop enter/exit -> 0 delta; heap oscillates 50->77->54MB (GC, no leak); 188 chunk-keyed
  identities re-derive from seed; 0 console errors.
- Hours-aware density: added setNightLivelyChunks + NIGHT_LIVELY_FLOOR so the open-late video
  block stays lively at night (31->16) while ordinary streets go near-empty (65->6). F call-site
  documented (openLate lot -> chunkKey via sim.chunkKeyAt).
- Composition with ?dig=1: roster is street-tier, inert while interior/dig open (frame loop is
  street-branch-only); enter/exit record shops leak-free with stream on. No shared state with any
  v2 flag -> all-on combo safe.
- Budget gap found + made safe: each decimated ped is 8 sub-meshes / 2 materials = ~7 draws/rig,
  so full-density stream (perChunk 16) hits 356 draws (>300 street budget). Set default perChunk 8
  (291 draws, budget-safe today). The clean unlock for full-density default-on = merge ped
  sub-meshes by material (8->2 -> ~2 draws/rig -> perChunk 16 fits ~260); documented as R7 work,
  F1-safe (v1 spawn view has ~0 near-rigs).

Only web/js/citizens/sim.js + web/citizens_test.html changed (+ NOTES/progress).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 22:56:39 +10:00
m3ultra
52eb1090ef Lane A round 6: second plan source ?plansrc=osm (real-data OSM import) — full seam
The carried-over OSM plan source (no-showed R4–R5) lands complete, invariants green — a
second CityPlan producer behind the same contract, so B–F consume it unchanged.

- osm_fixture.js: 95 REAL inner-Melbourne secondhand/charity/music/book/toy/antique shops,
  extracted deterministically from thriftgod city_source.json (Overpass cache), imported as a
  module ⇒ ZERO network.
- plan_osm.js: generatePlanOSM(seed) — project lat/lon→metres, bin shops into latitude-band
  avenues off a spine (real row + real E–W order preserved), march into a valid non-overlapping
  CityPlan, centre on origin, report true 362×486 bbox, real shop names, one openLate video.
- index.js: generatePlanFor(seed, source) selector — default 'synthetic' BYTE-IDENTICAL
  (golden 0x3fa36874 untouched); 'osm' → the importer. generatePlanOSM exported for F.
- selfcheck.js: parameterized by source — OSM runs the full structural invariant suite
  (frontEdge/facing/no-overlap/chunk-coverage/finite/JSON/determinism/openLate) + its own
  golden 0x34cfdec0; synthetic-only brief checks skipped. ALL GREEN 1362/1362.
- map.html: ?plansrc=osm renders it (reference wiring for F). Screenshot in docs/shots/laneA/.
- CITY_SPEC: plan-source note; LANE_A_NOTES R6: seam + goldens + shape-diffs for F's gate (F2).

Honest cut: street geometry regularized (real row+order drive it, exact metres do not);
name-parody deferred. qa.sh --strict GREEN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 22:47:38 +10:00
m3ultra
2511610373 Lane E round 6 (E1): publish.py merges (not clobbers) + provenance-drift QA gate
- publish.py: _published.json now MERGE (read existing → union → sorted write), never
  replace — fixes the R5 --only clobber (27→1). Proven: a real no-op re-publish kept it
  at 27. Added --verify + post-publish drift-check vs the live depot (/api/list procity_*).
- validate_manifest.py: new provenance-drift gate — every manifest procity_* GLB must be
  in _published.json or QA fails. Proven: simulated clobber (→1) exits 1 w/ per-file errors.
- Record verified: 27-entry _published.json is byte-exact vs the live depot.
- qa.sh --strict GREEN (5 gates incl. F1 harness).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 22:37:55 +10:00
m3ultra
40fefd9299 Fable (integrator): round-6 lane instructions — finish v2 wiring + pay review debts (A plansrc carried, F enforcement harness, C draw batching, orphaned assets)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 22:21:31 +10:00
m3ultra
d71420f482 Fable R5 review: regenerate _published.json from live depot (27 GLBs — publish.py rewrite had clobbered it to 1 entry) + true-up v2 flags table
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 22:17:32 +10:00
m3ultra
019304d647 Lane E round 5 (E2): depot growth — street bin + record-crate stack (consumer-driven)
v2 features are procedural (no forced GLB needs), so E2 is light per Fable's "no
unconsumed entries": two synthetic depot props with clear homes — council-bin ->
procity_street_bin_01 (Lane B street dressing) + milk-crate-stack ->
procity_fit_crate_stack_01 (Lane C floor piles / dig). Both 3k tris, base-origin,
re-normalized to procity names + thumbs, published, verified loading, validate --depot
0 err. Manifest 18 fittings + 10 furniture (28 GLBs). Also noted B3 resolved
(novelty_record 26.5k->8k via E1 bake).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 21:27:13 +10:00
m3ultra
8644d3f7ac Lane E round 5 (E3): fal Hunyuan glass_case + bus_shelter + magazine_rack (~$0.64)
The 2 R3 TRELLIS glass rejects + optional magazine-rack, generated on fal.ai
Hunyuan3D v2 (John-authorized, $22 balance). pipeline/gen_props_fal.py drives the
fal operator using the FAL_KEY from MODELBEAST settings (never hardcoded). Frosted/
opaque flux_local concepts gave the reconstructor a surface (clear glass defeated
TRELLIS). glass-case 1 att, bus-shelter 2 att (opaque back wall), magazine-rack 1 att
= ~$0.64 total, guardrail held. All baked to <=8k via bake_lowpoly.py (switched to
EMIT albedo bake — Hunyuan materials bake near-black under DIFFUSE). Verified loading
in vendored GLTFLoader, published, validate --depot 0 err. Manifest 17 fittings + 9
furniture; aliases wire glass_case/glass_cabinets/magazine_rack/fridge/listening_corner.
Costs logged in AUDIT R5. Last 2 primitive-only fittings retired.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 21:20:44 +10:00
m3ultra
5671a06aef Lane C R5 C3: re-measure interiors after Lane E's hero-prop bake
E baked the 6 heavy hero props to <=8k tris (c522464). Re-measured every
interior GLB-on (cache-busted to bypass the dev browser's stale HTTP cache;
production clients fetch baked directly):
- record 43.6k tris (C3 target, ~273k pre-bake), draws 322
- milkbar 38.9k tris (was 468k — the R4 over-budget flag is now CLOSED)
- all rooms <=107k tris; book is the draw-call outlier (1245 draws from ~9
  bookshelf GLBs + ~600 primitive spines) — tris fine, interiors render
  one-at-a-time so gate 3 unaffected; instancing spines is a future lever.
Full table in docs/LANES/LANE_C_GLB_VALIDATION.md. Round 5 complete for Lane C.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 21:16:15 +10:00
m3ultra
93a3168a99 Lane D round-5: chunk-streamed roster (v2) behind ?roster=stream
Implements the R3 design note (was design-only). Default-off; the v1 whole-graph path is
byte-identical when the flag is off (shell citizen-0 signature matches the R4 golden exactly).

- chunk-local identity keyed (seed, chunkKey, i) — same seed + chunk -> same residents,
  independent of town size / visit order; home edge picked from that chunk's edges.
- windowed spawn/despawn: hook-driven via Lane B's onChunkBuilt/onChunkDisposed (sim auto-
  detects and stops polling) + a camera-poll fallback that needs zero Lane B changes.
- global near-cap + mixer stagger + rig pool + impostor layer unchanged, run across live chunks.
- owner-chunk despawn (no cross-boundary re-keying); per-chunk time-of-day thinning (hours-aware).

Proven on a 9x9 fixture grid: constant far-field density (~58-129 within 70m at spawn AND 150m
out, vs v1's uniform ~12-15); deterministic (507 chunk-keyed identities re-derive from seed);
leak-free (2 full grid walks / ~380 chunk cycles -> 0 geom/tex delta, pool capped 30); budget
unchanged (near-cap 24 + 1 impostor draw, 2.31ms/frame). qa.sh --strict GREEN.

Only web/js/citizens/sim.js + web/citizens_test.html changed. Flag + shell call-site documented
in LANE_D_NOTES for Lane F to wire web/index.html.

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