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>
This commit is contained in:
m3ultra 2026-07-16 08:43:22 +10:00
parent 62e38c5df8
commit eee60e05a5
4 changed files with 99 additions and 10 deletions

View File

@ -1,5 +1,76 @@
# LANE B — NOTES (measured)
## Round 14 (Fable's ROUND14 → Lane B) — v3.0 RELEASE: alias retirement + the tour audit
Release round — no new systems. Three B files touched (`audio.js`, `venue.js`, `dbg.js`); no world
geometry, no rng, goldens frozen (`0x3fa36874` synthetic, `0x1f636349` gig — neither moved). Verified
fresh Chromium, seed 20261990, `?gigs=1`.
### 1. Alpha-alias readers dropped (debt #1 — B first; **F unblocked**)
The R12 `window.PROCITY.gigs` scalar alias (`.state/.on/.venueShopId/.cover/.paid/…`) is retired this
round. B's readers removed:
- **`audio.js`** `venueGigState()` — dropped the alias hop (`g.venueShopId === shopId → g.state`).
Now byVenue-only + the clock fallback: `g.byVenue[id]` → else `plan.gigs` night-0 vs the segment.
- **`venue.js`** `stateGetter()` — dropped the bare-string arm; reads `gigStates.byVenue || gigStates`
(F passes `gigState.byVenue`, confirmed at `index.html:379`). Seed call is now `update(null)`.
Verified live: F's `gigState.byVenue` reads `{116:'on', 404:'on', 490:'quiet'}` at NIGHT (pub + RSL
playing, band_room dark); passing it to `venue.update()` lights the marquee with **no string arm**, and
`audio.js` resolves each venue's `gig-<genreKey>` with **no alias hop**. Audio engine loads `live`.
**→ Lane F (you can now delete the alias):** an alias-surface sweep (all of `web/js/**`, `index.html`,
`tools/**`) confirms **CROSS-LANE CLEAN** — no A/C/D/E file reads the scalar alias (interior_mode.js
uses `onOf()/gigOf()`; sim.js uses shell-driven `setGig`). The only remaining alias readers are **your
own 20 lines** in `tools/flags_check.py` + `tools/qa/gig_shot.py` (`.venueShopId`×12 → `venueShopIds[0]`
or an explicit id; `.state`×5 → `stateOf(id)`; `.cover`×2 → `coverOf(id)`; `.bandName`×3 →
`bandNameOf(id)`; `gig_shot.py:81` already prefers `stateOf(id)`). Delete the alias getters at
`gig_state.js:141151` + migrate those tool lines in the same commit; `!!P.gigs` existence checks keep
working (the per-venue machine still exists). CITY_SPEC §v3 runtime block loses the alias line (with A).
### 2. Bookmark / tour audit under `?gigs=1` — **all clean, no pose fixes**
All 19 bookmarks (16 v2 + `venue_night` + `district_posters` + new `queue_night`) resolve, frame
**un-letterboxed**, **0 page errors**, ≤300 draws. The district's in-place venue conversions hijacked
**no** bookmark: `night_neon`→"Video Regal" (openLate is never converted), `dig_real`→"Retro Groove"
(a real record shop survives — multiple per type). Framing eyeballed clean on the tour spread:
`street_noon`, `venue_night`, `queue_night`, `tram_stop`, `district_posters`, `night_neon`. **No dbg.js
pose changes were needed** beyond adding `queue_night`.
- **Measurement gotcha (re-confirmed):** `DBG.shot` teleports, so shooting many bookmarks in sequence
leaves orphan chunks that inflate tris on later shots (saw 238k on `patronage_door`; **42k** on a
fresh load / shoot-twice-to-settle). Shoot from a clean load, or twice, for the true per-frame count.
- **Observation → Lane D / Fable (not a Lane B fix):** `tram_stop` is a stable **~293k tris** — real,
from close-range full-detail GLB ped rigs on the footpath (framing is fine). It's not a venue block
(F's tri gate measures at the venue block, ~64k), so it likely doesn't trip the gate, but it exceeds
the 200k reference for a tour shot; worth a look if a close ped shot lands in the release set.
### 3. `queue_night` — John's release hero (`dbg.js`)
New bookmark `queue_night` (seg 5 / NIGHT / 'on'): a close 3/4 on the pub door, biased to the venue's
`+right` (the queue side). New `poseForVenueQueue()` recomputes venue.js's frontage geometry from the
pub lot: `DIST 7 m` out on the street, `SIDE = mw/2 + 2.2 m` to the queue side, eye 2.2 m, looking at
the door lifted to y2.7 (catches the marquee). Verified: frames marquee + window glow + streetlamp
pools + the queue. **The queue is spawned by the shell's live rAF loop** (`index.html:383` builds D's
`VenueQueue` on `openOf(id)`), so F's `gig_shot.py` must drive it **live** (the automation rAF is
throttled — I confirmed the pose + D's line by manually spawning `VenueQueue` at the pub queueZone: 2
punters seat at the door, facing it).
- **→ Lane A (blocks the hero's poster):** `queue_night` frames door + marquee + queue, but **no poster
lands in the close frame** — the pub's own-frontage poster is still on A's local **Z** (the BACK
wall, `gigs.js buildPosters` item 1 `-sin,-cos`; measured 15.9 m from the +Z street facade), and the
nearest spine poster is 53 m away. Per Fable's "fix poses, don't touch world code," B did **not**
work around it. **Flip item 1 to `+sin,+cos`** and the frontage poster seats by the door — in the
existing `queue_night` frame with no pose change. (Re-flagged from R13; now it gates John's hero shot.)
### 4. Poster-repetition call (debt #4) — **FINE, no ask to E**
Tonight's 14 posters carry **2 distinct designs** (Screaming Utes / screenprint ×7, The Dead Ringers /
xerox ×7 — different skins, different bands). Variety = number of venues playing tonight (one seeded
skin per gigId), so a spine run reads as two real band bills, not a repeat. Only a town where a single
gig plays everywhere would look monotonous (rare, and reads as "a residency"). No new templates needed.
### 5. Town-wide streetlamp pools — v3.1 candidate (Fable ruling, no code)
Fable ruled the night pools **stay venue-scoped for v3.0** (town-wide needs always-on `furniture.js`
changes that fight flags-off parity in release week; venue blocks read fine — `venue_night` proves it).
Parked here as a **v3.1 candidate** alongside wind sway and bench-sit loiter. No R14 code.
---
## Round 13 (Fable's ROUND13 → Lane B) — the district reads by night (`?gigs=1`, v3.0-beta)
Four B pieces, all gig-gated (flags-off byte-identical, verified). Files: `venue.js` (rewrite),

View File

@ -205,15 +205,13 @@ export function createAudioEngine(PROCITY, { noassets = false, mute = false } =
const lot = pub && (PROCITY.plan?.lots || []).find((l) => l.id === pub.lot);
if (lot) venues.push({ shop: pub, lot, genreKey: pub.genreKey || 'pubrock' });
}
// Per-venue gig state for the muffled spill, robust across Lane F's alpha→district transition:
// 1. F R13 per-venue map window.PROCITY.gigs.byVenue[id].state
// 2. alpha alias window.PROCITY.gigs is the single machine → its state for its venueShopId
// 3. clock fallback plan.gigs (night 0) vs the current segment (works before/without F)
// Per-venue gig state for the muffled spill (R14: the R12 alpha alias hop is retired):
// 1. F's per-venue map window.PROCITY.gigs.byVenue[id] (the canonical surface since R13)
// 2. clock fallback plan.gigs (night 0) vs the current segment (works before F wires / headless)
// Returns null only when there is no gig layer at all (flags-off ⇒ byte-identical, no spill).
function venueGigState(shopId) {
const g = PROCITY.gigs;
if (g && g.byVenue) { const e = g.byVenue[shopId]; if (e) return typeof e === 'string' ? e : (e.state || 'quiet'); }
if (g && g.venueShopId === shopId && typeof g.state === 'string') return g.state;
const gigs = PROCITY.plan?.gigs; if (!gigs || !gigs.length) return null;
const ton = gigs.find((x) => x.night === 0 && x.venueShopId === shopId);
if (!ton) return 'quiet'; // dark tonight — reads true

View File

@ -137,6 +137,25 @@ export function installDBG(deps) {
|| (plan.shops || []).find((s) => s.type === 'pub');
return shop ? poseForShop(shop, dist) : null;
};
// John's release hero: a CLOSE 3/4 on the pub door during doors/on, biased to the venue's +right
// (the queue side) so punters' faces read and a verge poster enters frame. The queue + posters are
// spawned by the live shell loop when the venue opens (seg 5 = NIGHT / 'on'), so F drives this shot
// live; this just frames the door + marquee + queue zone. Geometry mirrors venue.js's frontage.
const poseForVenueQueue = () => {
const vs = venueShops();
const shop = vs.find((s) => (s.venueKind || s.type) === 'pub') || vs[0]
|| (plan.shops || []).find((s) => s.type === 'pub');
const lot = shop && lotOf(shop); if (!lot) return null;
const ry = lot.ry || 0, fx = Math.sin(ry), fz = Math.cos(ry), rx = Math.cos(ry), rz = -Math.sin(ry);
const w = lot.w || 8, d = lot.d || 10;
const cx = lot.x + fx * (d / 2), cz = lot.z + fz * (d / 2); // door centre on the street (venue.js frontage)
const mw = Math.min(w * 0.92, 6.5); // marquee width (queue head ~ +right of it)
const DIST = 7.0, SIDE = mw * 0.5 + 2.2, EYE = 2.2; // close standoff, biased to the queue side
return {
pos: [cx + fx * DIST + rx * SIDE, EYE, cz + fz * DIST + rz * SIDE],
look: [cx + rx * (mw * 0.30), 2.7, cz + rz * (mw * 0.30)], // door, lifted to catch the marquee
};
};
// Named bookmarks (LANE_F_NOTES §4). seg = day segment index (0 DAWN … 5 NIGHT).
// Predicates cover both Lane A's taxonomy (dept/stall/record/…) and the fixture's; each falls back
@ -164,6 +183,7 @@ export function installDBG(deps) {
// ── v3 gig district (round 13, ?gigs=1): the street money shots F scripts via tools/qa/gig_shot.py ──
venue_night: { seg: 5, resolve: () => poseForVenue('pub', 13) || streetViewPose(['main']) }, // ?gigs=1 — lit pub frontage + streetlamp pools + queue + posters at night
district_posters: { seg: 5, resolve: () => streetViewPose(['main']) }, // ?gigs=1 — the spine at night: poster poles + lit venue frontages receding
queue_night: { seg: 5, resolve: () => poseForVenueQueue() || poseForVenue('pub', 11) || streetViewPose(['main']) }, // ?gigs=1 — John's hero: close on the pub door at 'on', queue + marquee + poster, punters facing the door
};
const DBG = {

View File

@ -185,13 +185,13 @@ export function createVenuePresentation(plan, skins, scene, { assetBase = 'asset
scene.add(group);
// ── per-venue state → frontage glow. Accepts F's per-venue map { [venueShopId]: state }, a Map, an
// { byVenue } wrapper, OR a bare state string (alpha compat → the primary pub). Idempotent; the shell
// ── per-venue state → frontage glow. F passes its per-venue map `gigState.byVenue`
// ({ [venueShopId]: 'quiet'|'doors'|'on'|'done' }); a Map or a { byVenue } wrapper are also accepted.
// (R14: the R12 alpha bare-string arm is retired — F wires the map since R13.) Idempotent; the shell
// calls it every street frame, so pool/glow ramps and the night baseline lerp here. ────────────────
function stateGetter(gigStates) {
if (gigStates == null) return () => 'quiet';
if (typeof gigStates === 'string') return (id) => (id === primaryId ? gigStates : 'quiet');
const map = gigStates.byVenue || gigStates;
const map = gigStates.byVenue || gigStates; // F passes gigState.byVenue (per-venue map)
return (id) => {
const v = map instanceof Map ? map.get(id) : map[id];
if (v == null) return 'quiet';
@ -225,7 +225,7 @@ export function createVenuePresentation(plan, skins, scene, { assetBase = 'asset
if (poolMesh) poolMesh.visible = poolMat.opacity > 0.01;
}
}
update('quiet');
update(null); // seed: no gig states yet → every venue quiet (frontage dark, pools follow night)
function dispose() {
for (const d of disposables) d.dispose && d.dispose();