# PROCITY — Lane F progress (INTEGRATION & QA) **Session:** 2026-07-14 · **Author:** PROCITY-F · **For review by:** fable --- ## Round 41 (§41.6) — THE LIBRARY LANDS: five seams wired, six gates added, and the interior margin was never 162 *Full runbook detail (gate arms, the control transcripts, the A/B method): [LANE_F_NOTES §41](docs/LANES/LANE_F_NOTES.md). Shots + their index: `docs/shots/laneF_r41/` (README in the folder).* Every number below is from a command run today on the integrated tree — five lanes' uncommitted work in one working copy — in fresh headless contexts on port-isolated no-store servers, seed 20261990, 1280×720. **No git command was run.** No tag (John's playtest session rules the epoch). ### The five asks, each with its outcome **① [C] THE WARDROBE IS ON — and the arm that ships by default is still the canvas.** Two marked edits: `interior_mode.js` gains `WARDROBE_BASE = 'assets/wardrobe/'` and `stockBaseFor(shop)` (godverse per-shop base first — a real shop's real stock outranks the town art; else the wardrobe base for `opshop`; else `undefined` → the town-wide packs), consumed at `enter()` and in `stockInfo`. `index.html` preloads `preloadStockPack('opshop', { base: WARDROBE_BASE })` beside the three town-wide preloads, inside the same `STOCK_REAL` gate. One constant, one resolver, both sides — the R24 cache-key lesson, because `(type, base)` is the cache identity and a base spelled two ways fails silently to parody. | | `?stock=real` (ON) | default boot (OFF) | |---|---|---| | room's resolved base | `assets/wardrobe/` | `null` | | pack items resolved at build time | **52** | 0 | | real garment ids on rendered meshes | **69** (Puppy Jumper · Splatter Smiley Tee · Squiggle Knit Dress · Trackie Dacks…) | 0 | | atlas the GPU is sampling | `wardrobe_atlas_00.webp` | — | | wardrobe requests | **exactly 2** (index + one atlas) | **0** | | console errors | 0 | 0 | **Which ships default: the canvas.** `STOCK_REAL` is `params.get('stock') === 'real' && !NOASSETS` — opt-in, unchanged. **Both arms A/B'd against a control tree with F's two edits reverted** (`web/` copied, `assets`/`models`/`vendor` symlinked to the live tree so another lane's wave is identical on both arms; town frozen `pop=0&weather=0&tram=0&magpie=0&washing=0`): | frozen town, control vs treatment | default arm | `?stock=real` arm | |---|---|---| | fetch surface | **160 URLs, identical hash, added [] removed []** | **+2 URLs exactly** (`stock_opshop_index.json`, `wardrobe_atlas_00.webp`), removed [] | | street (`shopfront_detail`) | **131 draws / 18,018 tris — identical** | 131 / 18,018 — identical | | op-shop interior | **59 draws / 28,505 tris — identical** | 59 → **55 draws (−4)**, 28,505 → **28,065 tris** | | geometries · textures | **199 · 111 — identical** | 199 → 194 · **111 → 100 (−11)** | So: nothing breaks without it, byte-for-byte, and turning it on is a **draw and texture WIN** — 52 garments come off ONE shared atlas material where the procedural path built a canvas per garment. **② [D] THREE GATES WIRED** into `qa.sh` Gate 10: `node tools/qa/r41_postures.mjs`, `tools/.venv/bin/python tools/qa/r41_citizens.py`, `…/r41_shots.py`. The shot gate is wired with `--outdir "${PROCITY_SHOT_OUT:-$TMPDIR/procity-r41-shots}"` so a routine gate run never rewrites the round's committed shots; `PROCITY_SHOT_OUT=docs/shots/laneD` regenerates them deliberately. Two run strict; **`r41_shots.py` runs warn-level** with its reason recorded at the call site — see filing 5 below, its browse arm loses its browser to the sim between the scan and the frame. **③ [E] `pipeline/clips_verify.py` WIRED** as Gate 10's first gate (no venv, no browser, ~1 s): **GREEN, 0 errors / 0 warnings, 46 clips / 6 groups / 3,498,124 B.** **④ [B] THE NON-REPRODUCIBLE BOOKMARKS — confirmed, and the set is BIGGER than B measured.** Independently, on today's tree: `?classic=1`, town frozen, 2.5 s settle, sha256 of the PNG, **3 boots per bookmark**: | bookmark | distinct hashes over 3 boots | |---|---| | `street_noon` · `shopfront_detail` · `crossroads_busy` | **3 of 3 — not reproducible** (B's finding, reproduced) | | `market_square` | **3 of 3 — not reproducible** (B measured this one as rock-solid; it is not, here) | | `night_neon` | **1 of 3 — STABLE** | **THE RULING: no R41 gate pins a screenshot byte-hash on any bookmark, and none should while the sign atlas paints `bold Npx Arial` before the font resolves.** Only `night_neon` could carry one and one stable bookmark is not a golden suite. Everything R41 asserts is a **counter** — draw calls, triangles, fetch surface, item ids, texture URLs, stature — all of which reproduce exactly. The tour shots are evidence, captioned with the counters that are gated, and their README says so. **Filed to B for R42:** one `await document.fonts.ready` before the first sign-atlas paint in `buildings.js`, then re-measure — cheaper than a perceptual-compare harness, and it fixes the cause. **⑤ [C] THE CORRECTED `drawSweep` — confirmed on an independent instrument, and the true numbers.** F wrote its own sweep (`r41_integration.py` arm 2): F's own scene bookkeeping, F's own strip-and- measure loop, `PROCITY_C.drawSweep` never called — because the thing C found broken was exactly the sweep's scene bookkeeping. | 12 types × 6 archetypes, seed 1990 | F's own sweep | C's instrument, today | margin to ≤350 | |---|---:|---:|---:| | GLB off | **93** @ `opshop/hall` | 93 @ `opshop/hall` | 257 | | **GLB on** | **123** @ `dept/auto` | **123** @ `dept/auto` (twice) | **227** | | GLB on + `?stock=real` | **123** @ `dept/auto` | 123 @ `dept/auto` | 227 | | GLB on + `noKit` (pre-R41 control) | — | **116** @ `dept/auto` | 234 | **THE PHANTOM CONTROL:** the same loop with one stale room deliberately left in the scene — the pre-R41 behaviour, reproduced on purpose — reads **197 @ `dept/auto`, an inflation of +74 draws on every reading**. That is C's §41.4-0 finding confirmed independently, and it decomposes R39/R40's "worst 188" exactly: 116 of dept plus ~72 of a record shop nobody was looking at. **THE TRUE NUMBERS, used everywhere in this close: worst interior 123 draws, law ≤350, MARGIN 227.** Not 188/162. §41.4 spent **+7 draws at the worst room** (116 → 123). Two honest notes: C published **122**, and today's tree reads **123** on both instruments — C's table also reads 1–4 draws light on record/book/video/pawn/milkbar/band_room **in the `noKit` arm too**, so the difference is C's measurement environment, not the R41 kit. And even the inflated historical reading (197) was never a breach of the law. ### The new gates F owns, each with its control demonstrated **`tools/qa/r41_denylist.mjs` — RULING 3 BECOMES REPO LAW** (node, zero deps, 0.8 s). Seven banned names (`character_kit_modular`, `exports/bodies/`, `elsa_coronation_hair_wig_kh3`, `daphne_sexy`, `mocaponline`, `SK_Mannequin`, `anatomy/`) checked two ways: banned **path components**, and banned **bytes in every file under `web/`, binaries included** — a GLB carries its source node names in the JSON chunk and a manifest carries the path it was published from, so "nothing references it" is a claim about text and "nothing contains it" is the claim a licence audit asks. **Result: 738 files · 197.3 MiB · 0 symlinks · 0 hits.** **CONTROL, run every time:** the gate writes a plausible plant (a manifest row naming `~/Documents/character_kit_modular/exports/bodies/body_07.glb`) into `web/assets/`, asserts the scan goes **RED on 2 names**, removes it in a `finally`, and asserts the scan returns to **0 hits**. A deny-list that has never fired is indistinguishable from one with a typo in its regex. **`tools/qa/r41_integration.py` — the seams F wired and the budgets F answers for** (Playwright, five arms, own port): 1. **the wardrobe, both arms** — the table above; the OFF arm is the control. 2. **the interior budget on F's own sweep** + the phantom control — the table above. 3. **`?noassets=1` still clean** over a walk that enters op-shop, record and pub: **0 GLB · 0 clip GLB · 0 motion manifest · 0 wardrobe · 0 stock pack · 0 asset manifest**, 0 console errors, 106 requests swept. **CONTROL:** the same walk on `?stock=real` fetches **5 of 6** cargo classes, so the classifier can see what it claims is absent. 4. **`?clips=0` / `?classic=1` carry no R41 cargo** — `?clips=0`: 142 distinct URLs, 0 clip cargo, 0 wardrobe, `clipStats {0 groups, 0 clips, 0 B}`. `?classic=1`: 125 URLs, same three zeros. **CONTROL:** the default boot fetches the library (2 requests, bank 2 groups / 16 clips / 1,242,272 B), so the zero-arms are a gate and not a tautology. 5. **the deny-list from the network side** — **190 distinct URLs** across street + gig night + 3 interiors, 0 banned names; the matcher demonstrated firing on a synthetic banned URL. ### The budgets, restated on the final integrated tree **STREET (ruling 4) — the pin method of record**, `budget_walk.py --method stepwalk --stepm 2 --fixed-yaw 0 --laps 2` = 802 stations down the main spine at yaw 0, the worst bearing: | boot | R40 pin | **R41 measured** | law | |---|---:|---:|---| | default, noon (seg 2) | 282 | **282 / 100,346 tris** | ≤300 ✅ | | default, **NIGHT (seg 5)** | 291 | **291 / 120,093 tris** | ≤300 ✅ — **margin 9, intact** | | `?classic=1`, NIGHT | 269 / 90,580 | **269 / 90,580** | ≤300 ✅ **byte-exact** | | default, NIGHT, `?clips=0` | — | **291** / 82,013 | Δ draws **0** | **NO LANE SPENT A STREET DRAW.** The pin reproduces R40 to the digit on every class, and the motion library's own A/B (`?clips=0` vs default at the same station set) is **0 draws**: clips are skeleton-only, 0 tris, and a seated ped is a ped that was already drawn. **INTERIOR — before/after, and the instrument correction that dwarfs both:** | | draws | law | margin | |---|---:|---|---:| | R39/R40 published worst (**phantom-inflated**) | 188 | ≤350 | 162 | | pre-R41 worst, corrected instrument (`noKit`) | **116** | ≤350 | 234 | | **R41 worst, corrected instrument** (12 types × 6 archetypes) | **123** @ `dept/auto` | ≤350 | **227** | | the same reading with the phantom re-introduced | 197 | ≤350 | 153 | And the **game-side** number, which is the one a player can actually stand in — 33 real open shops entered through the shell with GLB **and** `?stock=real`, draws read off the interior scene's own render: **worst 110 @ `dept` (Golden Department Store), margin 240.** Per type: dept 110 · toy 110 · book 78 · opshop 67 · video 54 · record 51 · pawn 47 · milkbar 46 · stall 17. Venues are shut at midday, so the pub was measured on its own clock: **49 quiet (18:30) and 77 on gig night with the band and the crowd in the room** — the round's most-furnished interior, at 22% of the law. ### QA `bash tools/qa.sh --strict` → ``` SUMMARY 18 passed · 0 failed · 0 warn · 0 skipped ● QA GREEN — every landed gate passed. (exit 0) ``` All six R41 gates included and green, D's warn-level shot gate passing on this run too. R39's transmission gate independently corroborates F's sweep from the other side: **GLB-on worst room 123 (dept/auto) with E's fix vs 193 (opshop/hall) with transmission restored — 70 draws saved**, and its GLB-off control pair is equal at 93 on both roots, as it must be. `node web/js/citygen/selfcheck.js` → **ALL GREEN 157,647/157,647, fingerprint `0x5f76e76`** — unmoved. Nothing this round writes a plan field. ### Filed back (two of them were red gates F fixed in its own file) 1. **`tools/flags_check.py` `_enter_record_shop` — R41 closed its search window, 8 smokes went RED.** It took the first OPEN record shop **of six** whose counter sits ≥2.6 m from every bin (so counter-E can never fall into `binUnderAim`'s 2.5 m fallback). §41.4's 2–4 kit bins per record shop brought that distance down across the board: the first six now read **2.36 / 2.02 / 2.26 / 2.21 / 2.21 / 2.10 m**. Measured over twelve, **3 still clear the bar** (Colonial 2.80 · Bluey's 2.79 · Gazza's 2.84) — so the property survives and the window was the problem. **Widened to the whole open set, and it now reports the shop and its margin** so the next narrowing shows up in the log instead of arriving as eight failures. Verified first that this is not a game defect: the shell orders `aimed bin → trySell → nearest bin`, so the sell card wins at a counter regardless of the fallback radius. **→ LANE C, for information:** record-shop counters now sit 2.0–2.8 m from the nearest bin. Nothing is broken; if you ever want the counter to keep a clear 2.6 m, that is the number. 2. **`tools/flags_check.py smoke_djdance` — R41 made the mixer multi-action and the stall test lied.** It read `mixer._actions[0]` before and after a 700 ms window and failed when `tAfter <= t`. D's §41.3 gives every actor per-instance clip swapping and the gig widening installs a venue clip into some crowd slots, so index 0 is no longer the live action nor the same one 700 ms later: a healthy dancer read **t 24.19 → "tAfter" 14.21** and the gate called it a stalled page. Fixed to identify the running action by clip **name**, follow that clip across the window, measure advance **modulo the clip duration** (a 30.58 s loop sampled across its wrap is advancing, not stalled), and treat a legitimate mid-window swap as advancing. Now green, and it reports both durations in play (30.58 s and 52.42 s — the R13 dance pick and D's venue widening, side by side). 3. **→ LANE B: the bookmark instability is wider than filed** (`market_square` too — 3 distinct hashes over 3 boots). The R42 fix is one `await document.fonts.ready` in `buildings.js`. 4. **→ LANE C: worst interior reads 123 on this tree, not 122** — on C's own instrument as well as F's. The `noKit` arm carries the same 1–4 draw spread, so it is not the kit. 5. **→ LANE D: `r41_shots.py`'s BROWSE arm goes red on this tree, and it is the harness, not the game.** Its street arm is excellent and reproducible (8 near-tier rigs, states `{walk 7, bench-sit 1}`, 3 distinct clips `@walk`/`sit_hands_thighs`/`walk_shopping_bag`, 126 draws, every stature clean). The browse arm scans up to 8 shops for browsers, keeps the best, then **RE-ENTERS the winner** — and a patronage occupant is free to walk out during the ~3 s per candidate that scan costs, so the re-entry spawns 0 browsers and the arm asserts on an empty room. **Measured: 3 consecutive standalone runs all land on `T & R Books` with `figures: 1` (keeper only), byte-identical at 34 draws / 36,751 tris** — while the same gate passed with `figures: 2` earlier the same day under different machine load, with nothing in R41's shipped code moving in between. **Fix:** re-check `k.browse` after the re-entry and re-pick, or don't re-enter — keep the winner entered. Until then it is wired **warn-level** in `qa.sh` with that reason recorded at the call site, so it runs and reports without failing a round for a browser who went home. (F's own tour hits the same seam and gets a browser every run by dwelling 25 s on the street first so several shops hold occupants — `docs/shots/laneF_r41/browse_interior.jpg`, a browser on `browse_close_cabinet` at a bookshelf.) 6. **F's own `r39_transmission.py` had an unmeasured 30 s navigation timeout, and it cost a round a RED.** Its `pg.goto(interior_test.html?localdepot=1)` waits for `load` — every subresource — on Playwright's 30 s default. Inside a full `--strict` run (four browser gates back to back on a loaded box) it timed out once and printed a stack trace where an assertion belongs; the same gate went green **twice standalone, immediately after**, and the page reaches `load` in **0.4 s over 27 requests, 3 runs of 3**. Pinned to an explicit 90 s — ~200× the measured cost, so it cannot mask a regression, and one class of false red is out of `--strict` for good. An unstated default is a number nobody measured. 7. **→ LANE C: `assets/wardrobe/` is now spelled in two files** — F's exported `WARDROBE_BASE` in `interior_mode.js` (the shipping path) and a local `const` at `interior_test.html:93` (yours). They match today. Two spellings of one cache key is the exact shape of the R23 bug, so if the wardrobe ever moves, import the constant instead of editing both. ### Files touched (only these) `web/index.html` (`[Lane F R41]` marks — Lane B's `[Lane B R41]` marks untouched) · `web/js/world/interior_mode.js` · `tools/qa.sh` · `tools/flags_check.py` · `tools/qa/r41_denylist.mjs` (new) · `tools/qa/r41_integration.py` (new) · `tools/qa/r41_tour.py` (new) · `tools/qa/r39_transmission.py` (F's own gate, one timeout — filing 6) · `docs/shots/laneF_r41/**` (new) · `F-progress.md` · `docs/LANES/LANE_F_NOTES.md` · `README.md`. **No git command was run. No tag.** --- ## Round 40 (§40.6 — THE PLAYTEST HARNESS) — F8 turns John's session into tickets; the wave-1 asks settled *Full spec (note schema · tour stops · gate arms · ask detail): [LANE_F_NOTES §40](docs/LANES/LANE_F_NOTES.md).* **THE KEYSTONE LANDED.** `web/js/world/playtest.js` (NEW, F-owned) + marked shell seams: - **F8 anywhere drops a note**: `{pos, mode (street/interior/dig/map), shopId+shopName, street/locality via A's address layer (own `createStreetLocator`, zero draws), citySeed, town, seg+clock, boolean flags, fps, draws, tris}` + a screenshot download of the CURRENT view (interiors/open crates render via the new `interiorMode.renderFrame()`) + an overlay one-liner — **Enter saves, Esc cancels**, typing never reaches the game, and F8 inside a shop does not eject you (`noteActive` joins the digActive/sellActive unlock contract). - **Delta-law persistence** (the fog's pattern): key `procity-playtest`, caps 200 notes/240 chars FIFO at write AND validated at read, **corrupt-reject-WHOLE and loud**, import = adopt (reject leaves current notes untouched). **Export = one tickets-JSON** (`procity-playtest/1`, 18 typed fields per note — schema documented §40.2 of the notes) via the note-card button, `DBG.exportNotes()`, or `PROCITY.playtest.exportJSON()`. **The store is LAZY, and a gate earned that**: the first cut's constructor `getItem` turned R30c classic purity RED (`?game=0: storage calls 1`) on the full strict run — fixed to read-on-first-use, R30c re-run green, and r40_playtest.py grew its own instrumented `?game=0` zero-calls arm so the class stays caught in F's gate too. - **Classic-gated by construction**: dynamic import only when `flagOn('playtest')` (default-on, `?playtest=0` opt-out) — an off boot **never fetches the module** (185/207 requests swept clean), never grows the DOM, never touches storage (classic: localStorage 0 keys). Control demonstrated: the default boot fetches + publishes it. - **`?bugtour=1`**: dbg.js now loads under the flag alone, and N walks **11 stops** — spawn → street day → shop/buy (book) → dig (record) → **op-shop bin, caption says UNARMED** → gig night → tram → rain (caption: re-run `&weather=rain` if the seed rolled dry) → night → fog map → second town via the selector — every stop a caption of what to look at, DBG bookmarks reused verbatim, F8 live throughout. - **Gate `tools/qa/r40_playtest.py` GREEN, all arms + controls**: off-byte-identical (+ its fetched-on-default control) · the REAL F8 key path round-trips (download → overlay → Enter → export schema-validates 18/18 → reload persists same id → Esc arm → both reject arms) · the tour advances through all 11 stops with **0 console errors**, no `?dbg=1` needed. **THE FIVE ASKS:** ① D's `door_footpath_check.mjs` **wired** (qa.sh Gate 9, plain node, ~2 s) · ② B's `r40_lane_b.py` **wired** (Gate 9, Playwright class) · ③ D's doorRect derivation **measured and FILED BACK**: through D's own classifier + shipped snap, the `d/2+0.05` doorRect step reads synthetic strict **447→446**, walkable **473→472**, and **breaks 1 previously-passing point** (violates D's own FIX-ONLY arm) — real towns 100% either way; the 20 plaza misses are stall-packing geometry, not nudge length; shell stays `+0.6`, numbers handed to D · ④ A's debt **D2 CLOSED**: the shell's `isOpen` is A's canonical (no-hours ⇒ OPEN) via the barrel, every `PROCITY.isOpen` consumer (audio + 19 tools call sites) inherits it; remaining third impls listed in the notes (sim.js `_openAt` — already A's semantics; dbg.js openLate predicates) · ⑤ README `?r=N` row now states the R40 law (**391 measured, ≤420 declared, gated**; 307 gone) + flags_check's selector label now reads **RULED R40**. **QA:** `bash tools/qa.sh --strict` — **GREEN** (summary line in the round-close message; Gate 9's three new gates all pass) · selfcheck **157,647/157,647 ALL GREEN, fingerprint `0x5f76e76`** — zero goldens moved (the harness is DOM + storage; it renders nothing and writes no plan). Shots: `docs/shots/laneF_r40/r40_f8_note_overlay.png` · `r40_bugtour_caption.png`. **Parked (unchanged, per the round brief):** op-shop bin contents · fog default-on · v9 third layer · the audio listen pass — all become F8 tickets the moment John plays. **No tag.** --- ## Round 39 (v9 WAVE 1 — THE CITY IS THE GAME) — five gates, five controls, and the interior margin is 162 *Full detail: [LANE_F_NOTES §39](docs/LANES/LANE_F_NOTES.md). (R38's close lives in [LANE_F_NOTES §38](docs/LANES/LANE_F_NOTES.md) — it never got a row here.)* **`tools/qa.sh --strict --matrix` GREEN — 10 passed · 0 failed · 0 warn · 0 skipped**, on the true final tree, with three new gates wired in as Gate 8. Lane A's selfcheck reproduces exactly: **157,647/157,647 ALL GREEN, fingerprint `0x5f76e76` — zero goldens moved**, in a round that added an address layer, a fog ledger, a spawn move and a new interior fitting. **THE ROUND'S BIGGEST NUMBER IS NOT THE ONE THE LEDGER ASKED FOR. The interior margin is 162 draws, not 21 and never 6.** Lane E's transmission fix takes the worst room from **329 → 188** draws. Measured with `tools/qa/r39_transmission.py`: two port-isolated no-store roots, identical inode for inode except one file — the shipped `bookshelf` GLB vs a copy with `transmissionFactor: 1` re-inserted (BIN sha1 identical, only the JSON chunk moved) — both driven through `interior_test.html`'s own `drawSweep`. | `drawSweep` worst | glass bookshelf (= the pre-E tree) | E's fix (shipped) | Δ | |---|---|---|---| | GLB-on | **329** `opshop/hall` | **188** `dept/auto` | **−141** | | GLB-off | 162 `opshop/hall` | 162 `opshop/hall` | 0 — the control's own control | | margin vs the ≤350 law | **21** | **162** | **+141** | `opshop` 329→174 (−47%) · `book` 247→125 (−49%) · **every other type 0**, which is the shape Lane C predicted from an isolated room, reproduced at sweep scale by a harness C did not write. **The worst interior room in the game is now `dept/auto` — a room with no bookshelf in it.** The default boot fetches these GLBs from the public depot, so I fetched all four R39 GLBs off `digalot.fyi` and scanned them: **sha1-identical to the local tree, 0 transmissive materials, 4/4.** **The five gates, each with its control demonstrated in the same run:** | gate | verdict | the arm that could have failed | |---|---|---| | **39.1 address** (`r39_address.mjs`) | GREEN | **1,192/1,219 shops (97.8%)**, 23/23 `ways` supplier, 857/857 membership agree / 0 wrong. **RED ARM: a 3 m shift error raises adelaide 89 → 98 resolved shops** (Lane A's number to the digit) while inventing 9 names — `stats().shiftCheck` reads DISAGREE on all 72 perturbations, and it is the ONLY alarm that catches the 3 m case | | **39.2 fog** (`r39_runtime.py`) | GREEN | **HIDES** 4/72 at spawn, 0 of 61 distant shops known · **REVEALS** 2,125 m → 58 shops · **FRONT TEST** 0 of 59 shopfronts learned when probed from 5 m behind · **+0 draws AND +0 tris**, four fresh browsers, both orders, drained, 3 reads each · 8/8 corrupt saves rejected · null store on classic/`game=0`/`fog=0` | | **39.3 THE SIGN** (`r39_runtime.py`) | GREEN | shops in front of the player at spawn **4/4 · 4/4 · 4/4 · 1/1**; with the pre-R39 sign re-derived on the same tree, **0 on all four** | | **39.4 rummage bin** (`r39_runtime.py`) | GREEN | 120 rooms: tub 60/60 both arms, `kind:'bin'` **0/60 default vs 60/60 armed**, 0 carves, 60/60 deterministic, reachable 0.80–1.03 m. **THE CONTENTS DID NOT SHIP** — on the default street boot the tub is `kind:'prop'`, 0 dig targets, E opens nothing | | **39.5 transmission** (`r39_transmission.py --validator`) | GREEN | the validator **HARD FAILS rc 1** on a fabricated glass asset and **PASSES on the shipped tree** — both halves, or "it failed" only proves the gate is always red | **Re-baselined, without falsifying a release record** (`tools/qa/r39_shots.py` → `docs/shots/laneF_r39/`): the R32 cluster-pose spawn frame and the R35 first-five splash were both shot at the old pose. `v7_tour/02_the_strip.png` and `v7_beta/first_five_splash.png` are **kept as history**; the re-baseline ships its own BEFORE, camera parked at the old `-sin` pose on the same tree. Katoomba's BEFORE is a paddock and a blank back wall; the AFTER is the awnings and the shopfront run. The R35 *gate* needed no re-baselining — `smoke_first_five` is pose-independent and was green either side. **Three corrections, all mine to make and all measured:** LANE_A_NOTES §39's "a 30 m shift returns 0 street names" — it returns 40/12/47 wrong ones, because on a dense grid a big shift lands you on the next street · LANE_B_NOTES §39's "a fresh export has no `known` key" — true of `?fog=0`, no longer of a default boot, because B's own `+sin` fix means you spawn in front of four shopfronts · and my own harness bug, recorded: I first pointed the transmission control at `web/assets/models/` when `validate_manifest.py` reads `pipeline/_normalized/`, and nearly filed a defect against Lane E on the strength of it. **Aim the control at the code path, not the plausible path.** **Held → Lane E:** `check_transmission()` skips any GLB with no local `_normalized` copy and never scans `web/assets/models/`. Demonstrated with a live control: remove the local copy and the same glass asset passes rc 0. A depot-only transmissive GLB — the exact shape of `longbench` and `streetlight` — would ship unseen. **No tag.** --- ## Round 37 (v8 WAVE 0 — THE FLOOR) — the budget of record settled · two vacuous gates repaired **0.3 — THE BUDGET OF RECORD: 292 draws / 124,793 tris. The audit was right and the spike was right; they were measuring different BEARINGS.** Two sessions disagreed by 90 draws (280 walked vs 191 teleport-walk). Measured on one tree, one box: worst draw **per bearing**, 12 bearings × 102 stations down the synthetic spine — **0° 284 · 30° 248 · 60° 208 · 90° 177 · 120° 196 · 150° 214 · 180° 211 · 210° 207 · 240° 178 · 270° 182 · 300° 223 · 330° 267.** **110 draws of spread from the camera direction alone**, and the peak sits at **yaw 0 — the default argument of `DBG.teleport(x, z, ry = 0)`.** The audit took the default and landed on the worst bearing; the spike supplied a yaw and landed mid-range. Both numbers were correct; they were not the same quantity. Lane B's independent R37 walk (276 draws / **99,880** tris) and mine (282 / **99,878**) agree on triangles **to 2** — same frame, two harnesses. **Viewport is irrelevant** (1280×720 and 2560×1440 identical to the byte). The R+1 dispose window is worth **+19 draws** (31 vs 27 live chunks), not the 60–80 folklore claimed — the other ~100 draws between bookmark and walked-worst are *place* and *bearing*. **THE PIN** (default boot, `7bd96e5`): synthetic **292 draws** (night, 31 live chunks) / 120,145 tris; `?classic=1` 269 / 90,580; corpus tri worst **124,793 on fitzroy_real** — the audit's "99–124k" range confirmed at its top. **Margin: 8 draws, ~75k tris.** Only 8 frames of 810 exceed 280; classic never does. Also found: **`?r=3` measures 307 draws and breaches the ≤300 law** (ungated override, filed). Independently confirmed B's kerb change at **+0 draws / −8 tris**. Headless SwiftShader — but draw/tri counts are counted in JS, not by the GPU, and the viewport control proves it; **frame TIME is still unmeasured on any real GPU and no proposal should claim it.** **0.3b — THE SHARED INSTANCE CAP: 256.** Measured: an `InstancedMesh` costs **+1 draw at every N from 32 to 2048**, and `N × tris/instance` **always submitted** (the instance bounding sphere spans the town — at N=2048 all 368,640 prop tris go down the pipe from anywhere). So draws are not the constraint; triangles are. 256 × a 2–4 tri billboard cell = 512–1,024 tris = **0.5% of budget**; CPU to rewrite all 256 matrices, **0.01 ms/frame**. Binding companions: **≤24 tris/instance at the cap**, and **spend it proximity-first** — measured anchor supply runs synthetic 246 poles / 6.8 km to bendigo_real **7,325 poles / 188 km**, so 256 sprayed town-wide on bendigo is one instance per 730 m and statistically unencounterable. **0.2 — `smoke_tram` REBUILT.** It asserted `!!getObjectByName('tram')`, printed a SKIP and returned on absence, and **passed on the fenced path** because `tram.js:110-114` adds a named EMPTY group. Now STRICT with four arms: RUNS (route/stops, and the body moves **19.8 m over 45 frames** under the *shell's own* loop) · BEHAVES (an isolated tram, fixed dt, 400 s: dwell time accounts to **exactly 9 × 3.5 s**, max cruising tick 0.4665 ≤ SPEED·dt 0.45, every discontinuity bounded by 2·LANE with ≥1 end reversal, riding **3.2 m off Lane A's own main-edge centreline**) · GENUINELY ABSENT on the fenced katoomba_real (0 stops, **0 group children**, 0 m moved) · OFF under `?tram=0`/`?classic=1`. **Both controls demonstrated every run** — the run-predicate pointed at the fenced town and the absence-predicate pointed at the running tram must each go red. **0.1 (F half) — THE KERB GATE: the ruling is in the paint, and the control fires at EXACTLY 14.** New STRICT `smoke_kerb`. A's corrected selfcheck asks the plan-side question; F's asks the runtime one, of the paint — **raycast straight down at every rendered poster and classify the topmost ground hit by MATERIAL IDENTITY** (`skins.groundMat()` is cached, so the road mesh's material *is* the object the factory returns — no mesh names, no strings, nothing that drifts when B edits). Topmost, not first, because B put the carriageway on top at junctions. Six arms, all green: **14 posters rendered == 14 in `plan.posters`, every one within 0 m** (nobody had ever asserted the poster you see is the poster A placed) · **detector control A** — a main centreline classifies ROAD at y 0.04 · **detector control B** — 13 m off it classifies FOOTPATH at y 0.02 · **THE RULING: 0 of 14 posters on road paint**, tally `{footpath: 14}` · **THE CONTROL: the same leg fails under `?verge=0` at EXACTLY 14**, tally `{road: 14}` · **CLASSIC KEEPS ITS PAINT** — 13 m off the centreline is still ROAD under `?classic=1`, 0 posters. **Two lanes, two wholly independent methods, the same number 14**: A counted plan-side by offset in node; F counted paint-side by raycast in the browser off B's geometry. **0.5 — `classic_regression` GETS A NETWORK ALLOW-LIST**, derived from the **v2 tag at gate time** (`git ls-tree` + v2.0's own manifest), never a deny-list. **The brief's shape would have missed the breach it was written for**: the R36 widening was `models/peds/woman_dj_01.glb` — *same-origin* — so "four depot GLBs + same-origin" is green straight through it. `/models/peds` is enumerated by the v2.0 tree (exactly the covenanted 21 files; today's dir holds 32). The nine depot fetches are **baseline, not a breach** — proven by booting the actual v2.0 tree and capturing its wire. **Control every run**: injecting the R36 breach itself plus a never-existed asset turns the clean verdict into exactly 2 named violations. F measured classic's surface as **+6 files** vs the real v2.0 tree and **held rather than ruling**; **Fable ruled** (`4a7c0f1`): the 5 audio files are **RATIFIED BASELINE** (v2.1 was v2's final tagged state, so audio shipped inside v2 as the furniture GLBs did) and `assets/towns/index.json` is **NOT baseline** but a **named, printed exception**. Landed in the mechanism by moving the authority tag **v2.0 → v2.1** — surgical, not generous: `git diff v2.0 v2.1 -- web/` is the audio pack + `audio.js` + four wiring edits, **zero other assets, zero deletions**, so the tag change admits precisely the ratified audio. Gate output confirms: same-origin baseline **47 → 52**, exceptions **2 → 1**, control still discriminating. "Should `?classic=1` show the town selector at all?" → v8 debt ledger, R38+. **THE CLOSE — `qa.sh --strict --matrix` on the true final tree (A + B + Fable + F all landed, port-isolated :8975): 7 passed · 0 failed · 0 warn · 0 skipped — QA GREEN.** Selfcheck **157,407/157,407** (A's +1,055 poster assertions, green on the ruled street) · manifest 0 err/0 warn · **flags harness 0 fails / 0 warns** (classic fp `0x5f76e76`, 162 draws/16,642 tris, fetch surface clean vs **v2.1** with 1 named exception and the injection control firing · default boot gig golden `0xec7a2d39`, 131 draws/23,382 tris, dig reachable naked · the new kerb gate and the rebuilt tram gate both green with every control) · figure-scale 29 figs/6 shops · **MATRIX GREEN 10 towns × 7 gates**, tram verdicts unchanged (fenced on bendigo/fremantle/katoomba_real — the same three the rebuilt smoke uses for its absence arm). **No tag: wave 0 levels the floor, it does not ship a version.** *Caveat kept honest in the notes: the matrix's own budget column is clean-load bookmark numbers (69–161 draws) — a regression signal, not the budget of record, which this very round proved under-reports a walked-and-turned frame by ~120 draws. Making the matrix walk-and-turn is filed to the debt ledger, not smuggled into a floor round.* Full record: LANE_F_NOTES §37. --- ## Round 36 (THE v7.0 CLOSE, wave 2) — the gate, the freeze, the tag **Zero alpha-blockers stood** (Fable's wave-0 naked-boot playtest walked the whole loop by hand), so wave 2 was pure close: E's three flags measured shut, the full gate over the final tree, docs frozen, **tag `v7.0` (annotated, LOCAL — Fable pushes)**. **E's 2 a.m.-asset flags, closed by measurement:** (a) the 5 trellis DJ bodies' bind skeletons measure **0.984–0.989 units** vs the legacy roster's 173.46 (cm) — the figure-scale gate grew a DJ-body leg that spawns all five BY NAME through the real browse path: statures **1.64–1.89 m**, all in [1.4,2.0], feet planted, rig-kind verified (the old sample's `(i*7)%19` could never reach indices 17–21). (b) New STRICT `smoke_djdance`: the 4 Rokoko clips bind **64/65 tracks, sole unbound `mixamorigReference`** (gate on binds + NaN, never node counts — no template pin); a gig-night dancer's live action IS a dance clip (identity by duration, mixer advancing, `position.y` flat = the clip moves the body); `?classic` fetches **zero dance clips** (positive control: default fetched 4); `?noassets` procedural sway ALIVE (placeholder dancers bounce 0.07 m — the inverse pair proves the branch). (c) Rokoko zone = E's w1 provenance, nothing runtime. **THE HOLD → RESOLVED (the five-holds law, end-to-end in one round):** wave 2 found `?classic=1` fetching the 5 DJ body GLBs (unconditional `PED_NAMES.normal` members, rigs.js, `b339402` — classic's crowd pool widened 17→22 ungated). F WARNED it in the suite and held; Fable ruled "classic keeps its people"; D fixed it in rigs.js alone (`2fd4c1c`: base-17 restored, the five append behind `djs = dance` riding the shell's existing `!CLASSIC` signal; classic's 129-citizen census byte-identical to a `b339402^` shadow tree). F's wave-2.5 re-gate on the true final tree: the warn flipped to "✓ zero DJ-body fetches" — **flags harness 0 fails / 0 warns**, default boot keeps all 22 bodies and the organic gig-crowd proof. Held, ruled, fixed by the owning lane, re-proven — nothing improvised. **The gate (final tree, :8946 port-isolated): `qa.sh --strict --matrix` 7 passed · 0 failed · 0 warn · 0 skipped; flags harness 0 fails / 1 warn wave 2 (the held finding, deliberate) → **0 fails / 0 warns on the wave-2.5 re-gate** (true final tree, run 3 on the healed box — the first two re-gate runs died to a box-level fork famine, ~10,600 leaked m3panel processes, fixed at source; starvation artifacts, not gates); selfcheck 156,352/156,352; figure-scale 29 figs / 6 shops; MATRIX GREEN 10 towns × 7 gates.** The R32–35 smokes all re-green on the final tree: bible (ledger Σask $333 → Σoffer $135, scarcity across reload, ?game=0 control) · hunt (rumor + auto-clear + travel day 1→2→3, same-town free) · godbay (one-hammer byte-identical, SOLD net $19 bounded, the paper on the wake toast) · first-five (HILLS HOIST $191 splash, beats on the real path, seeded across browsers). Classic covenant fp `0x5f76e76`, gig golden `0xec7a2d39`, world fp `0xb1c7843e` unmoved. **The freeze:** README v7.0 section (the loop + the morning-paper manual row) · V7 charter risks retired-or-carried v4-style (economy RETIRED-structural, bot verifier carried to the live tier · schema RETIRED · re-emit identity CARRIED, never fired · scope RETIRED · v6 collision RETIRED) · LANE_F_NOTES §36. Full record: LANE_F_NOTES §36. --- ## Round 35 (v7.0 close, wave 2) — THE FIRST FIVE MINUTES (Fable, solo, JING5) A fresh game arrives HUNTING: one seeded artist want (`rng(citySeed,'firstwant',0)` × the parody roster) named on the splash with the pocket money ("You're hunting HILLS HOIST — $191 in your pocket"), pointing the rumors and the tour at the same target. Three toast-beats walk the hustle off existing events (walk-in → digOpen → first find → first sale-or-listing, then the listener removes itself). Returning saves get no tutorial; ?game=0/classic never reach it. #pc-toast body-mounted so beats and morning papers surface inside shops too. STRICT `smoke_first_five` green (roster want + splash · real-path beats · returning/?game=0 controls · cross-browser seed determinism). Full record: LANE_F_NOTES §35. --- ## Round 34 (v7.0 close, wave 1) — GODBAY: the offline auction (Fable, solo, JING5) ⚖ LIST from the crate (mail-in consignment) → `listings` on procity-save/1 → resolves the day after, wherever the day moves (sleep, travel, imports — adopt() and sleep() share the resolver). THE ONE-HAMMER LAW: identity-seeded (never day-seeded) — a passed-in item re-lists to the same result forever, no retry farm. 15% pass · uniform-across-band hammer (grail 2×low) · 12% war ×1.5–2 · 12% fees, net floor $1; EV ≈ 0.8×mid so random flips bleed and only finds profit; keeper = floor, auction = bounded ceiling. The morning paper rides the wake/arrival toast ("⚖ SOLD 'Cane Toad Blues' — hammer $22, $19 after fees"). Band table imported from C's bible.js (one authority), PRNG from core. STRICT `smoke_godbay` green (real-DOM consign · byte-identical double-sleep · bounded honest resolution · travel hammers · game=0 control). Full record: LANE_F_NOTES §34. --- ## Round 33 (v7.0-beta wave 2) — THE HUNT: wantlist · rumor · travel (Fable, solo, JING5) ☆ WANT on the pull card → `wants` on procity-save/1 (optional, capped, deduped); buying a matching item clears the hunt; THE WANTLIST section in the crate panel. The keeper gossips: seeded 45% banner mutter naming a hunted want + a real cached town. TRAVEL COSTS A DAY: adopting a save from another townKey (town OR seed) charges +1 day in save.js adopt() — load and import both pay, same-town stays free (byte-equality gates untouched); arrival toast names the fare. New STRICT `smoke_wantlist_travel` (real-card want · rumor scan · auto-clear · +1-day with intact cash/collection · same-town control · return fare · ?game=0 zero-DOM). Full record: LANE_F_NOTES §33. --- ## Round 32 (v7.0-beta wave 1) — THE BIBLE + scarcity + the §31.6 queue (Fable, solo, JING5) Guide bands derived in `js/interiors/bible.js` (E's thresholds mirrored, no pack re-emit) → the pull/sell cards carry the **guide stamp**; parody bins roll seeded bands + 10% collector/ grail **gems** (asking under the keeper's bandLow offer — margin by construction); §9.1 sell basis swapped to bandLow exactly as pre-ratified (alpha entries keep the pricePaid basis). **Scarcity:** optional `pulls` on procity-save/1 — a bought dig slot stays gone all day (pick-then-omit, zero collateral), pruned on sleep; real-sourced pulls never expire. §31.6 queue: cluster-pose spawn (main-street doors, classic keeps the frozen pose) · THE CRATE + game bar live in interiors (C anywhere, yields to the cards) · DAWN dome swaps to high-cirrus (weather override wins). **Gates:** new STRICT `smoke_bible` (band law over every bin · ledger closes to the cent on the GEM's bin, Σask $333 → Σoffer $135, gems +$21 · emptied bin 0 across reload · `?game=0` control re-opens 16/16) + sell_routing amended to the exact formula (the fence moved from the receipt to the band). Full record: LANE_F_NOTES §32. --- ## Round 31 (v7.0-alpha CLOSE) — THE DIG FLIP + the tag **Fable's wave-0 playtest found the one alpha-blocker: the dig was default-OFF on a plain boot.** `index.html` still carried the v2-era opt-in (`params.has('dig')`) six epochs after the README claimed "dig on" — and every R30 game-loop gate booted `dig=1` explicitly, so the suite was green while E at a bin did nothing on the boot every player gets. Fixed with the one-line flip (`DIG_ON = flagOn('dig')` — ?dig=0 opt-out, ?classic=1 off, `dig` on the flags surface) and fenced with two new STRICT legs: **default_boot_gate asserts dig REACHABILITY on a NAKED boot** (E at an aimed bin → digActive true → closed clean) and **classic_regression asserts dig INERT at the bin** (zero `pcdg-*` DOM, zero Storage calls — measured, not assumed). *Harness lesson #4: the explicit-flag blindfold — a gate that force-feeds its subject's flag can never notice the default is broken; every claimed-default-on flag needs at least one flag-ABSENT gate.* Splash de-staled ("Every door opens." — the "(soon)" is over; controls line = E riffle/sell · C crate · SLEEP to save) + the README manual row. `PROCITY_QA_PORT` override so the suite never silently reuses whatever holds :8130 (this round: Fable's own preview server). **The re-gate (final tree, :8931): `qa.sh --strict --matrix` 7 passed · 0 failed · 0 warn · 0 skipped; flags_check 0/0 incl. both new legs; MATRIX 10 towns green.** Money shot: `docs/shots/v7_alpha/crate_panel.png` — Monster Robot Party's frontage, THE CRATE with 3 real finds (real cover thumbs, honest paid/where/day), game bar day 1 · $116 · CRATE 3 · SLEEP, driven through the real loop with the dig param absent. **Tag `v7.0-alpha` — annotated, LOCAL ONLY** (Fable pushes). THE BETA QUEUE (Fable's design wants, filed NOT fixed) is in LANE_F_NOTES §31.6. Commits: `50385c0` (flip + gates + splash) · shot + notes commit (tag target). Full record: §31. --- ## Round 30 (v7.0-alpha — THE SAVE AND THE CRATE) — F: the contract first, then the save core + tomorrow **The contract shipped before a line of implementation** (`53fcd3c`): `window.PROCITY.game = { day, cash, collection[], townKey, save(), load(), sleep(), export(), import(json), wallet, recordFind(), removeFind() }` + the `procity-save/1` schema + the rotation law, published in LANE_F_NOTES §30 — and it worked exactly as the order intended: **B picked up §30.4 within the session** (venue.js "tonight" already reads `gigs.weekNight`; hud.js collection/SLEEP surface in flight), and C's already-published §9 asks (`type` stamped at buy, `title`/`artist`, removal via the game API, `wallet.sell` mirrored) are honoured in the entry shape. **Then the layer** (`207bcff`): `web/js/world/save.js` (F-owned, new) — versioned localStorage save, THE DELTA LAW BY SHAPE (payload keys are exactly `[cash, collection, day, savedAt, schema, town]` — no world field can even be expressed); corrupted/foreign → **loud reject, raw stashed, fresh start, town boots**; save on sleep + beforeunload; export/import for moving machines. The wallet is a game-backed facade of C's exact v0 interface, so every proven seam (dig onBuy, shelf buy, cover, C's onSell) is unchanged — cash just became authoritative from the save. Buy seams record finds; `?classic=1`/`?game=0` never construct the layer — **ZERO Storage calls, instrumented, not trusted**. **SLEEP=TOMORROW:** `sleep()` → day+1 → gig `weekNight` re-keys the EXISTING week schedule → wake at DAWN → save. Stock rotation is a runtime day salt on the EXISTING `stk-*` streams (2 marked seam lines in C's interiors/layout) + the dig's `binSeed` — **plan generation never sees `day`** (A's #6 boundary). **Day 1 IS the pre-v7 town** (measured: `?game=0` stock fp == fresh-game day-1 fp), and **REAL-sourced crates never rotate** (Monster Robot fp identical across sleeps; the mint crate rotates). **Two amendments filed loudly, with measurement, not slipped in:** gig night = **(day − 1) % 7**, not the brief's literal `day % 7` (day starts at 1 — the literal form flips a fresh boot's gigs off night 0, breaking the R13 cover/band gates before the player ever sleeps); and sleep saves AFTER incrementing (disk always carries the morning you woke into). Fable ratifies or overrules at review. **Numbers:** 7 verify legs green in fresh no-store contexts — $12 dig buy == pricePaid recorded · sell halves credit/remove correctly · parody fp `3396372961 → 1818111125` across a sleep, byte-identical same-day AND across reboots · corrupt blob = 1 loud reject + stash + clean boot · `qa.sh --strict` **6 passed · 0 failed · 0 warn** with the game layer default-ON. Two F-harness bugs caught and fixed in the run (the post-sleep CLOSED door; a transform-based fingerprint that was vacuous over batched parody stock — hash the VERTEX data). Full record: `LANE_F_NOTES` §30. **Held for the later F session (per the brief):** ledger #5 gates + the sell-card E-key routing. ### Wave 3 — THE GATES SESSION: the routing landed, five STRICT gates, the loop closes end-to-end **The held join is in.** E inside a shop now routes: **aimed bin → dig · near counter with sellables → C's sell card · forgiving nearest-bin → dig · shelf-buy** (`interior_mode.js`; 3 marked shell seams in `index.html` — `procity:sellOpen` unlock + `sellActive` on the Esc/relock guards, the dig's exact contract). `onSell` does F's two §9.3 halves in the safe order: `removeFind` first (veto ⇒ nothing moves), credit second. **The loop, measured through the real DOM:** dig pull **$9 debited** → walk to the counter → E → card → SELL **+$4** (< $9, the clamp) → collection 1→0 → street HUD **$186 · CRATE 0**. One amendment filed with its measurement: the old nearest-bin fallback ignored aim and would have swallowed E at any counter near a bin — aimed-raycast now outranks the sell trigger, the forgiving pick runs after it; both branches proven live (Moe Vinyl counter: card opens; Kelpie Sound bin, wrong aim: dig opens). **The gates (ledger #5), all landed as STRICT suite smokes in `flags_check.py` — no more session-local instruments:** (a) **save/load determinism** — scripted buy 2/sell 1/sleep 2× via B's real button → export → fresh browser → import **byte-equal** (state 243 B, normalized export 269 B), plan fp `0xb1c7843e` **unchanged** across the import, weekNight re-keyed 2; (b) **no-pump v0** — 5 real UI round trips **all strictly negative** (Σ −$30), plus **C's negative control**: broke player, `wallet.buy` invoked-and-false on the real shelf path ⇒ **recordFind unreachable, zero entry** (dig BUY disables outright), with a positive arm proving the control discriminates; (c) **classic purity** — Storage-prototype instrument pre-page-script: `?classic=1`/`?game=0` **0 calls, 0 game DOM ids**, positive control sees the default boot's `procity-save` read + all 7 ids, plan fp game-on == game-off; (d) **rotation determinism over VERTEX DATA** (the wave-3 binding) — day 1/2/3 fps all distinct, re-entry byte-identical each day, **cross-boot byte-identical off the persisted save**, `?game=0` == day 1 (88,320 hashed bytes / 20 stock meshes printed — the subject provably present). Harness bug #3 caught en route: the HUD is hidden in interiors by design; the gate read it on the street. **NO TAG** — per the brief, the alpha tags when it plays (Fable's call, likely R31). Full record: `LANE_F_NOTES` §30.6. --- ## Round 26 (v5.0-beta — EVERY CRATE DIFFERENT) — F: the manifest consumed, the tolerance retired **Every crate is different, and F measured it rather than taking G's word.** Not by entering one lucky shop — from the indexes of record: **15 keyed crates, 105 pairs, 0 sharing a title**; **0 crates overlap the generic v2 pack**; **0 ids reused across crates** (G's sku fix holds at scale — the R24 vacuity dead at the source, not papered over in F's gate). 4 crates in Red Hill + 11 in Newtown = the manifest's 15 exactly, and the manifest lists nothing that isn't on disk. **Sourcing, not tier — C was right.** `tier` is the charter's ladder rung, and a mint atlas is *also* rung 1: still a file, still offline, still deterministic. What differs is where the stock came from, which is `sourcing: real|mint`. E built to C's contract over the brief on the principle that the contract's author owns the contract's shape. F's gate asserts it: 15/15 declare sourcing, every index agrees with the manifest, **a mint crate can never report as real**, and the gate PRINTS it per room — provenance a human never reads isn't doing anything (the R24 licence lesson, one round on). **The 404 tolerance is deleted, not tuned.** R24 probed every keyed shop and ate a 404 per atlas-less one; F's smoke could only cope by *naming* them — and a tolerance is a confession. F filed the real fix as a contract ask instead of inventing it; G built the manifest; **F now consumes it**, and the shell asks for nothing that isn't listed. Zero 404s because nothing absent gets asked for, not because the gate stopped looking. **And deleting the tolerance immediately caught a leftover**: F had wired the *boot* preload but left a second blind probe at the *door*, which 404'd on every keyed-but-unfetchable shop. While the gate said "8 probes, expected", a ninth would have been invisible too. A named exception protects new bugs the moment the real fix exists. **Gate: `flags_check` GREEN** — distinctness · sourcing (real + mint rooms) · soft fall silent · **ZERO 404s** · **0 console errors, no attribution exceptions**. Full record: `LANE_F_NOTES` §26. --- ## Round 24 (v5.0-alpha — THE ALPHA COMPLETION) — **the crate WALKS; F held the tag on A's gate** **THE CRATE WALKS.** Boot `redhill_godverse`, walk down Musgrave Road into **Monster Robot Party**, riffle **its own real crate**, buy a real record at its real price. Proven falsifiably: room base = `assets/stock_godverse/3962749/` · **titles 120/120 in its atlas, 0/120 in the generic pack** · **the GPU samples `stock_godverse/3962749/stock_record_atlas_0*.webp`, 0 textures from the generic pack** · bought *"Abstract Latin Lounge II (Part II)"* **@ $15**, cash **191 → 176**, exact debit, count 0→1 · reboot restores 191/0 (gone-this-session, back next boot). Ladder re-proved: `?noassets` → **zero network requests**; keyed-but-atlas-less shop 2289 → 404 → **parody, live**; `newtown_real` → base `null`, 350 items — **F's wire is a no-op off the godverse path**. Full record: `LANE_F_NOTES` §24. **F held `v5.0-alpha` anyway. The tree is red — 156,193/156,201 — and none of it is the product.** 2 reds are goldens G's re-emit legitimately moved (A ran *before* G, so A could not pin them). 6 are **conflict #2: A's identity sweep contradicts Fable's own standing Ruling 2**, which *mandates* mixed caches ("merge census heroes **WITH** the donor's texture shops"). A cafe from OSM has no GODVERSE identity and never will. G measured it, escalated it to "A's call, Fable's ruling", nobody ruled, and it arrived at F. **F measured the hazard that gate guards: it does not exist.** 10/10 and 18/18 unique ids, **zero duplicates**; and partial keying does not mis-stock — F watched 2289 fall soft to parody in the browser, exactly as charter risk #3 prescribes. **A's own two checks disagree** — `validateTownCache` reasons it correctly ("Missing = tier 0 (soft). Duplicate = mis-stocked (hard). They are not the same failure") and warns; the sweep hard-fails the same condition. **Why F didn't just fix it: F is the party that benefits.** Changing a red gate to green, in another lane's file, to unblock F's own tag, on the one check guarding "never mis-stocked" with Monster Robot's real crate as the prize — that move would retroactively devalue every green F has ever reported. The cost of waiting is an hour. Recommended (A's call): the sweep follows A's own validator; A pins the four goldens in the same commit; F re-gates and tags. One commit. **Four findings:** 1. **The honest-gate spec was itself vacuous.** The brief specified id equality — but both packs number items **positionally** (`rec_0000…`), so **all 120 atlas ids are also generic-pack ids**. An id-equality gate goes green on the generic pack: the exact failure the vacuous-gate law was written to stop, reproduced inside its own first application. Titles (0 overlap) and the **texture URL** — the file the pixels come from — are what discriminate. F's gate asserts on those. 2. **F's own selector was lying about the epoch's shop.** `DBG.enterShop(3962749)` → "no shop matching": a GODVERSE shop has two ids and F's R22 selector knew only the plan id. Fixed. 3. **A's uniqueness check is a silent risk-#3 hole** (new — G didn't catch this): on a mixed cache every texture shop maps to `undefined` and they **all collapse into one Set entry**, so it fails on the undefineds and **cannot detect a real duplicate**. Fix it *over the defined ids* — that makes the guard stronger, not weaker. 4. **The licence flag stopped reaching a human.** E's validator now sees the atlas (R23's blind gate is closed ✓) but prints `provenance.licence` (British) while G now emits `license` (American, per C) — so `FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE`, on real product photos, **silently never prints** while the gate says OK. E, one line, G already specified it. --- ## Round 23 (v5.0-alpha — THE FIRST REAL CRATE) — **F HELD the tag** **`v5.0-alpha` is NOT tagged. The crate is real; it has no street.** G's 120 real records off a real shop's POS are in the tree and byte-identically re-bakeable — genuinely the artifact the charter asked for. Nothing in the game can reach them. `#7a` isn't a gate that failed, it's a gate that **cannot be run**. R18's law: *release rounds don't ship known breaches.* Full record: `LANE_F_NOTES` §23. **F executed the merge that never happened** (charter law #5) — `main` had no `stock_godverse/**` at all, so the branch F was asked to tag lacked the round's headline. Review had happened, C had verified pre-merge and passed; only the `git merge` was missing. Disjointness verified before merging, not assumed. Zero conflicts → `43b035a`. **What passed — everything except the crate.** `qa.sh --strict --matrix` **7 passed · 0 failed · 0 warn**; **MATRIX GREEN — 10 towns × 7 gates**; selfcheck **ALL GREEN 152,015/152,015** (was 161,300 — E retired toowoomba); flags harness green, so the `?classic=1` covenant still holds through the merge. Tier 0 `?noassets` → **zero fetches, zero console errors** (the offline law holds); tier 0 default boot on the godverse town; tier 1 (v2 town-wide) `?stock=real`. **Tier 1 (v5, per-shop) — the round's whole deliverable — is never fetched.** A, D, E and C's work is sound; only the rung this round was built to add is unattached. **Three findings, in order of how much they cost:** 1. **The atlas contract contradicts itself.** `LANE_C_PUB:271` publishes `stock_shop__index.json`; C's own loader at `stockpack.js:25` fetches `${base}stock_${type}_index.json`. **Incompatible.** G built runtime-compatible (so C's base-override check passed); E built contract-compatible (so E's validator can't see it). **Nobody was wrong — the contract was.** F recommends amending to G's shape; it's the only one the runtime can load. 2. **The gate built to catch that was blind and green.** `validate_atlas.py` globs a filename that can't exist at runtime, finds nothing, and `return 0` — so it passes whether the atlas is absent, misnamed, or perfect. It had never seen one until F pointed it at G's by hand, which then surfaced *deeper* drift (E's code reads provenance top-level, E's own docstring says nested, G nested it; `licence` vs `license`; a ceiling stale by one round). *A gate that returns 0 on an empty set is not a gate.* 3. **`#7a` would have passed vacuously.** F walked into The Vintage Record (real shop, real road) and the crates filled with **real covers — from E's 350-item town-wide v2 pack**, not the crate. Zero requests to `stock_godverse/`, ever. Eyeballed as written, the gate goes green and proves nothing. **R24 order — three gaps, none F-only:** **A+G** land a `godverse_id` (verified: *every* godverse shop reports `godverse_id: null`; the schema has no such field, and C's rule is "C does not invent it") → **E** builds `redhill_real` (one Overpass fetch — E's file, outward-facing; **not F's to invent**) → **C** keys the pack cache by `type+base` (today a cache hit returns before `base` is read, so two stocked shops collide *and fail-soft breaks*) → **F** passes the per-shop base and re-gates. F deliberately did **not** build its half: untestable with no street and no identity field. Filed, not guessed. **Also:** A's spacing warn earned on day one — with toowoomba retired, **ballarat now warns at 255 m**. The thin tail has a second member. --- ## Round 22 (v4.0 — THE EPOCH CLOSE) — F closed it - **The blocking gate CLOSED.** F's R21 re-measure made the tri diet blocking; B diagnosed, the driver traced A→**E**, E fixed it, F re-measured: katoomba `venue_night` **221,935 → 108,365 (−51%)**, worst town 124,682 — the 200k stress edge is closed. Selfcheck **161,300/161,300** (A's 36 goldens + 23 towns). - **`DBG.enterShop(selector)`** (Lane G's ask) — id · type · exact name · substring, most-specific-first. - **The release tour** → [`docs/shots/release_v4/`](docs/shots/release_v4/): Fremantle hero · Fitzroy mecca · Toowoomba thin tail · synthetic control · `?classic=1` covenant. All posed through F's R21 cluster primitive — which earned itself twice: the first cut posed via `street_noon` and **the mecca came out as bare bitumen**; re-posed through the cluster, Fitzroy reads. - **⚠ Honest finding (filed v4.x):** the thin tail does **not** prove the floor holds — 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. The frame ships labelled honestly rather than swapped for a flattering town. - Docs (README v4 "the towns are the product", notes §22), `qa.sh --strict --matrix` green, tag **`v4.0`**. --- ## Round 20–21 (v4.0-beta THE FIVE TOWNS) — F never ran R20; closed R21 **R20: F never ran** (no gate, no tag) — the honest record is in LANE_F_NOTES §20. Unlike R18 (where F ran and *held* on evidence), the slot simply didn't execute; the round also left C's door-soak, B's selector index, and D's relocation finding open. R21 closed all four. **R21: the beta gate.** Waves [A,B,E] → [C,D] → [F]. All five lanes in; F gated: - **Matrix 10 towns × 7 gates** — boot/determ/district/roads/**tram**/noassets green on all ten; selfcheck 154141/154141; `qa.sh --strict` GREEN. - **Tram dimension (new)** — B's R20 ruling verified exactly: Newtown runs 16 stops (was 149 — B's phantom fix), Castlemaine 10; Katoomba/Fremantle/Bendigo **fenced** at 3/2/1 shops fronted. Needed a new seam: the shell now exposes `window.PROCITY.tram` (B built `routeInfo` for F's smoke; nothing published it). - **⚠ venue_night re-measure — recorded, premise falsified.** A's cap worked (posters 438→111) but the density widening ate it: katoomba **200,965 (+965) → 221,935 (+21,935, +11%)**; bendigo 201k. Draws fine (151/300). Filed as R22's first item (the tri-diet backlog already lives there). - **Selector ruling** — always in the HUD (the towns *are* the product), + the corollary that F's harness must hide the HUD for beauty shots (the R19 alpha shot's selector-in-frame was F's own gap). - **Beta money shot** — [Fremantle's real street at golden hour](docs/shots/v4_beta/fremantle_mainstreet.png): a corridor of shopfronts, footpaths, a crossing, and a genuinely busy crowd. Tag **`v4.0-beta`**. --- ## Round 18–19 (v4.0-alpha REAL ROADS) — F held R18 correctly, then closed R19 **R18: F did NOT run — the gate would have failed by design, so F held the tag** and filed the failure list (the alpha's charter deliverable). Real roads landed structurally but the selfcheck was RED (katoomba golden unpinned + poster-kerb breach on all 5 real towns) and the town was fragmented (20 shops over 799 edges = an empty field). Classic/default stayed frozen — the alpha changed nothing outside the cache path. Fable's verdict agreed: "F never ran — correctly." **R19: the close.** A resolved the findings (fragmentation cull/join, poster clamp, goldens pinned), D re-measured, B shipped the town selector. F ran: - **B's `BIG_CITY` one-liner** (`e6fcb7b`, early): `shops>120 || edges>200` — real Katoomba's 799-edge graph now streams like a big city. **280k → ~12k tris.** B proved it; F owns index.html. - **The alpha gate** — `town_matrix.py` grew a `roads` dimension (a v2 cache must build the REAL graph, `mode='roads'` via the plan_osm report sink); classic/default proven untouched; budgets in the green. - **The money shot** — [Katoomba's real main street](docs/shots/v4_alpha/katoomba_mainstreet.png): asphalt + lane markings, footpaths, a pedestrian crossing, a walking crowd, gig posters, B's town selector in-frame. **PROCITY's look on real bones.** Tag **`v4.0-alpha`**. (Honest note: 19 real shops sit ~1/edge — a real *street*, not yet a dense strip; density is a v4.0-beta concern.) --- ## Round 17 (v3.2 THE TAIL + THE SCOUT) — F: the town matrix + the close The tail drained (drummer lean + bench-sit, wind sway, backline verify, --verify fix) and E's real-map scout landed (5 real AU town caches + A's cache contract + hardened plan_osm). **F closed it.** - **Real towns boot in the engine** — the barrel re-exports `registerTownCache`; the shell fetches + registers `assets/towns/.json` before the plan generates (fail-soft; opt-in; never on default/classic). `?plansrc=osm&town=bendigo_real` → **Bendigo, 9 shops, 3-venue district, 0 errors**. - **Town-matrix smoke** (`tools/qa/town_matrix.py`, `qa.sh --matrix`) — **10 towns × 5 gates all green**: 2 synthetic + 3 fixtures + 5 real caches, each boot / deterministic / in-budget (69–216 draws) / a 3-venue district / silent under `?noassets`. Castlemaine at the 6-shop floor still lands a district. **The existing CityPlan contract eats real Australian geography** — the v4 = THE REAL MAP evidence. - Close: `qa.sh --strict --soak` green, docs (LANE_F_NOTES §17, README), tag **`v3.2`**. --- ## Round 16 (v3.1 THE FLIP) — F bookends; the town shows its best by default The gig layer soaked through three v3 stages, so v3.1 flips it (and the mature v2 flags) ON by default. F owns the flip; A–E landed their polish against it (seated drummer, town-wide pools, backline, corner-poster). **Opening commit `a4946b7` (the round's foundation):** - **The flip** — `index.html` `CLASSIC` + `flagOn()`: gigs/weather/tram default-on, `loadPedFleet({sit:!CLASSIC})`, `window.PROCITY.flags`; `buildings.js` winmap default-on + classic-aware. **`?classic=1`** forces the exact v2 boot. - **The covenant moved** — `flags_off_regression → classic_regression` (byte-identical `0x3fa36874`); a new `default_boot_gate` (all four flags on, gig layer present, gig golden `0xb1d48ea1`, budget); every gig smoke re-pointed to the DEFAULT boot; qa.sh gate renamed. - **Seated-stature gate** (ledger #3) — `interior_scale_check.py` + `flags_check.py` accept `procitySeated` figs at [0.9,1.5] m; the drummer passes at **1.32 m**. Verified: `?classic` byte-identical (0x3fa36874, gig absent); default = full town (3 venues / 3 genres, gig golden, 112 draws @ venue block); classic/default/district/continuity/cover all green. **Close: re-baseline, re-shot tour → `release_v3_1/` + a `?classic=1` before/after, docs (CITY_SPEC v3.1 flip+classic law, README flags table, LANE_F_NOTES §16), tag `v3.1`.** --- ## Round 15 (v3.0 RELEASE CLOSE, `?gigs=1`) — F ran last; the release is closed R14 landed A–E's hardening but **F never ran**, so v3.0 stayed untagged. R15 = the close. All deps were in before F ran (A's poster flip + new golden `0x4f4a549d`, D's amp + R14 continuity seam, B/C verifies). The six-item brief, each committed atomically: - **Continuity wiring + smoke** (`687d23f`) — the ped you followed is in the crowd: `crew.spawn({roster})` + the queue `onAdmit → recordVenueEntry` relay; `smoke_continuity` asserts the seam on identities + settled tris. - **Alias deletion** (`146b5f3`) — the R12 scalar alias retired; every read keyed by `venueShopId`; tools + spec migrated. - **Full-week soak** (`edc0c47`, `tools/qa/week_soak.py`) — 7 nights × all venues: cover/roster per night, leak-free (plateau), 28 cycles. - **Money-shot tour** (`6a35b29`, `tools/qa/tour_shots.py`) — `docs/shots/release_v3/` + contact.html; hero `queue_night` shows A's now-visible poster; settled measurements. - **Docs** — README v3 section, LANE_F_NOTES §14/§15, this note, five freezes confirmed. - **Acceptance + tag** — `qa.sh --strict` 6/6 + continuity smoke + week soak green → tag **`v3.0`**. **Ledger #5 vindicated C:** the gig interior settles to **~182k tris** after the 5 instrument GLBs load (not the ~34k measured pre-load); the interior gate is **draws** (≤56), so no budget violation — the tour + smoke now measure settled. Full runbook: [`LANE_F_NOTES.md §15`](docs/LANES/LANE_F_NOTES.md). --- ## Round 13 (v3.0-beta — THE DISTRICT, `?gigs=1`) — WIRED + VERIFIED + TAGGED A–E landed, then D (4-piece + instruments + queue + RY fix). F wired the district together and it fires. **Seed 20261990 → "Boolarra Heads": 3 venues, 2 playing tonight — The Exchange Hotel (pub, `gig-pubrock`) + an RSL (`gig-covers`), one dark (reads true).** All 7 F tasks done, `qa.sh --strict` GREEN (6/6, 0 warn), tag **`v3.0-beta`**. Shots: `docs/shots/laneF/r13_v3beta_gig_interior.png` + `r13_v3beta_venue_night.png`. **What F did (ROUND13 §Lane F):** 1. **Per-venue state machine** — `gig_state.js` rewritten: one latch per venue off the shared `procity:segment` spine, keyed by `venueShopId`; all three hard-won R12 laws preserved. New runtime surface `window.PROCITY.gigs.byVenue` + per-venue getters (`onOf/openOf/gigOf/coverOf/paidOf…`); the R12 single-venue alias kept one round. B's `venue.js`/`audio.js` + D's `setGig` read it. 2. **Walla + applause** — the crowd bed `crowd-walla` rides the interior tone layer under the live band bed while `on`; an `applause` sting fires once on the `on→done` edge while inside. Verified: `iMusic=gig-pubrock`, `iTone=crowd-walla`. 3. **Cover per venue** — the door seam keys off the shop entered, so a night at two venues is two covers. 4. **Debt #1 closed — was a LIVE BUG** — E renamed `pubrock-live→gig-pubrock`, so F's R12 `GIG_BED` bridge (rewrote `gig-pubrock→pubrock-live`) pointed at a dead key → the band would have mimed in silence. Deleted; the room's `audio.gigKey` (already the manifest key) feeds the engine directly. 5. **Stature gate** — `interior_scale_check.py` now measures stature (crown−foot, platform-independent), ceiling-clip assert kept as its own line (debt #6). 6. **District smokes** — `smoke_gigs`: band→4, a per-venue loop (every gigKey resolves across 3 genres, crowd ≤ its own watchPoints, no-giants), street budget at the busiest venue block, the queue assert, plus the R12 determinism/cover/noassets/flags-off arms. All green. 7. **The outdoor queue** — D shipped `VenueQueue` and left the street-side lifecycle to F (its documented seam): F wires one queue per venue in the shell (spawn at doors off B's `queueZoneFor`, drive each frame, dispose by `done`), exposed as `queueCountOf(id)`. Verified: 2 in line at doors → 0 by done. **Numbers (seed 20261990):** gig interior 62 draws / 34k tris (≤350) · street @ venue block 107 draws / 47k tris (≤300/200k) · band+crowd stature 1.61–1.81 m under a 4 m ceiling · cover $10 debited-once/re-entry- free/broke-knockback + free night (seed 1234) · flags-off fingerprint 0x3fa36874 unchanged, gig layer wholly absent. Full runbook + the R13 debt-ledger closeout in [`LANE_F_NOTES.md §13`](docs/LANES/LANE_F_NOTES.md). --- ## Round 12 (v3.0-alpha — the gig layer, `?gigs=1`) — WIRED + VERIFIED A–E all landed before me, so F did what F does: turned five parts into one Friday night. **Seed 20261990 → "Boolarra Heads", venue = The Thornbury Hotel, tonight = SCREAMING UTES, $10 on the door.** Acceptance shot: `docs/shots/laneF/r12_v3alpha_gig.png`. | F task (ROUND12 §Lane F) | result | |---|---| | **1 — gig state machine** | ✅ new F-owned `web/js/world/gig_state.js`. `quiet → doors (DUSK) → on (NIGHT) → done (DAWN) → quiet`, verified forwards AND backwards on the hand-driven clock. On `window.PROCITY.gigs`. | | **2 — `?gigs=1` seam + flags-off** | ✅ shell bootstrap fetches `custom_bands.json` (no fetch when flag-off or `?noassets=1`) → `generatePlanFor(seed, src, {gigs:true, customBands})`. Flags-off verified **wholly absent**: no state machine, no `plan.gigs`/`posters`, no pub, no venue geometry, zero band-file fetches, golden `0x3fa36874` unmoved. | | **3 — cover charge** | ✅ all three halves of John's ruling verified live: **$10 debited once** (191→181, `cover — Screaming Utes` in the wallet), **re-entry the same night free** (181→181), **broke on a new night → polite knockback, no entry**. Free nights (seed 1234) walk straight in, wallet untouched. Zero new UI — rides the R8 buy seam. | | **4 — audio wiring** | ✅ gig bed inside via C's `gigKey`, B's muffled spill outside reads `PROCITY.gigs.state`. **Found + bridged a real C↔E key mismatch — see below.** | | **5 — smokes** | ✅ `smoke_gigs` in `flags_check.py`: schedule determinism · band==3 · crowd ≤ watchPoints · dance mix · bed resolves · ≤350 draws · no-giants · `?noassets=1`+`?gigs=1`. Plus the v3 arm of the flags-off regression. | | **6 — shot + tag** | ✅ shot captured. Tag pending the harness run (below). | **Numbers.** Gig-night interior **42–50 draws** with the rig fleet, **171 draws** on `?noassets=1` placeholders (worst; budget ≤350) · ~40k tris · street at the venue 69 draws/29.6k tris · **leak-free** (geo 66→66, tex 63→63 over 4 gig cycles) · band 3 + crowd 8/8 watch points, **3 dancing / 5 standing** · band stature 1.68–1.77 m, crowd 1.60–1.77 m, tallest crown 2.09 m under a 4.0 m ceiling. **John's drop-in works:** 7 of your 10 hand-written names fill all 7 nights (Screaming Utes, The Dead Ringers, The Rissoles, The Flannel Shirts, Boganvillea, Chunderbolt, Amyl & The Servos). Covers rolled `10,0,0,3,2,0,9` — 3 free of 7. Under `?noassets=1` it falls back to the pure generator ("The Wombatts") with zero fetch, exactly as A specced. ### Three bugs F found in its own work (all caught by verification, fixed, re-verified) 1. **The state machine only advanced when polled.** Driving NIGHT→DAWN→MORNING→NIGHT with nothing reading in between (player inside the pub, audio muted) **skipped the night roll** — the cover was never due again. Now it rides `procity:segment`, which `lighting.js` fires on every change. 2. **Rewinding the clock left `done` stuck** on a midday town (the latch only cleared at MORNING). 3. **The night only rolled if the clock passed through DAWN** — so a *jump* from NIGHT to MORNING left last night's paid stamp valid and **a broke punter walked straight in**. Caught by my own smoke, not by hand: stepping with `[`/`]` always crosses DAWN, so only jumps (DBG/shots/soak) exposed it. The roll now keys on landing in the daytime, not on having observed one particular segment. Also worth recording, because it nearly shipped a wrong gate: **"no giants" must measure stature, not world crown.** The band stands on C's 0.32 m deck, so a perfectly human 1.73 m guitarist has a 2.05 m world crown and the naive R10 check **failed a correct band**. The smoke now measures `crown − base.y` and keeps the world-crown assert only for "never clips the ceiling" — the real R10 failure mode. ### Three findings for other lanes (detail + rulings in `LANE_F_NOTES.md` §12) 1. **⚠ C ↔ E: the gig bed key never met.** C emits `gig-pubrock` (and asked E for that name); E shipped **`pubrock-live`**. Unresolved, the pub plays to a **silent room** and — because the audio law fails soft — *nothing errors*, you just get a band miming. **F bridged it at the glue** so alpha ships. This is the R1 facade-skin filename trap again. → **needs a named contract line in CITY_SPEC.** 2. **⚠ D + E: E's 5 instrument GLBs landed; nothing wires them.** D holds primitives and published `instrumentFor` as the seam "when they land" — they've landed, nobody owns the join. Non-blocking (asset law; reads fine in the shot). F did **not** silently do it inside D's rig. → R13. 3. **⚠ spec ↔ C: the band has no drummer.** ROUND12 §D.1 says "drummer seated bob" and E built a merged kit; C's `bandPoses` are guitar/**vocal**/bass and the drum riser is set-dressing. D flagged it and built to C's poses. A spec/implementation divergence **no gate can catch** → Fable/John to rule. ### Harness note This MacBook had **no Chromium/Chrome**, so the Playwright gates couldn't run locally; John authorised the install (done — 93.5 MB, Playwright CDN). `/Volumes/m3ultra/Documents/procity` is a **mount of m3ultra's `~/Documents/procity`** (same tree), so `ssh m3ultra@100.89.131.57` runs the real gates on the real files — but **node isn't on the non-interactive ssh PATH**: use `env PATH=/opt/homebrew/bin:$PATH tools/qa.sh --strict` or every node gate reports a bogus FAIL. --- ## Round 11 (the audio round) — DONE ✅ (v2.1) John greenlit "generate freely". E generated the pack (23 assets, `web/assets/audio/`, 8.7 MB ≤ 25), B built the WebAudio engine (`audio.js`, self-unlocking, self-ticking), C added `room.audio = {musicKey,toneKey}` to buildInterior. **Lane F wired + verified + tagged v2.1:** | task | result | |---|---| | **F1 — interior beds** | ✅ `interior_mode` enter → `PROCITY.audio.playInterior(current.audio)` (fade in), exit → `stopInterior()` (0.7 s fade). Guarded/no-op when muted/noassets/pre-gesture. 2 lines, the seam B/C designed for. | | **F2 — audio smokes** | ✅ new `smoke_audio` in the flags harness (fresh Chromium): **pre-gesture silence** (ctx not unlocked), **0 boot errors**, **?mute=1** muted, **?noassets=1 zero audio fetches**, **enter/exit leak-free** (12 cycles, 8 shops with a music bed, live AudioNodes bounded at 5, made 42). Caught + fixed my own gesture-via-mouse-click tripping PointerLockControls in headless → gesture via keyboard. | | **F3 — soak + tag** | ✅ soak with audio on **PASS** (heap 48 MB stable, 0 errors, no drift). `qa.sh --strict` **GREEN 6/6**. **v2.1 tagged + pushed.** | **Cross-lane (F's own gate, noted to B):** B's `audio.js:143,145` use `Math.random()` for footstep SFX variant + gain jitter — legitimate transient runtime FX (which shop plays which bed is seeded upstream in `interiors.js`), but it tripped F's generation-determinism `Math.random` ban. F **scoped the ban correctly**: `web/js/world/audio.js` is runtime *playback*, not generation → exempted in `scaffold_check.mjs` (B may instead add a `// cosmetic` marker; either is fine). --- ## Round 10 (fix the R9 giant-rig blocker, re-shoot, re-tag) — verify + ship R9 shipped a blocking regression: interior rig figures came out ~2× room scale (keeper head 3.83 m in a 3.4 m room) because `rigs.js buildFigure` normalized off the head bone's **bind-pose** Y. **Lane D fixed it** (R10, commit `3432fcc`): normalize by the feet→crown **span**, plus a new `qa.sh --strict` no-giants gate (`interior_scale_check.py`). Lane F verify + re-shoot + soak + re-tag: | task | result | |---|---| | **F1 — verify the fix** | ✅ `qa.sh --strict` **GREEN 6/6** incl. the new scale gate: **24 interior figs / 6 shops all crown ∈ [1.4,2.0] m and under-ceiling** (Second Time Redfern, the giant scene: 1.66–1.86 m). Live-measured too — the 3.83 m I first saw was a **stale ES-module cache in a day-old browser tab**, not a regression (served + fresh-chromium code both correct). | | **F2 — re-shoot** | ✅ `browsers.png` re-shot: browser at the rack + keeper at the counter, **human-sized** (*figures human-sized vs door/fittings: yes*). `night_crowd.png` re-framed to a readable "lit VIDEO REGAL amid CLOSED town" — **see the honest note below.** | | **F3 — soak** | ✅ full-default soak **PASS**: 37 chunks, 20 shops, **heap 54 MB stable (no perf drift** from the keeper fix — it's a build-time scale calc), draws 248, **0 errors**. Tri budget still ~327k > 200k (pre-existing rig-model item, D/E decimation — not an R10 change, not the blocker). | | **F4/6 — docs** | ✅ tour README updated with stable v2 filenames + captions; this addendum is the R10 release note. | | **F5 — tag** | ⏸ `v2.0` annotated tag staged on the fixed commit; **holding the push for John** given the night_crowd finding (a public release + his flagged money shot). | **Honest finding — `night_crowd` has no crowd, and it can't without new work.** John flagged it as near-black/empty. Re-framed it readable (clean night, tight on the neon). But a visible *street* crowd does not form: Lane D's night patronage parks ducked-in patrons **hidden inside** the shop (they are the interior browsers in `browsers.png`), and seed 20261990's video shop is a **low-traffic corner** (tested default streamed roster + patronage-on at night: **0 patronage, 0 visible peds** at the shop). This is not a framing bug and not (clearly) a regression — it's that the "night crowd" was always the *interior* presence. A visible outdoor queue is a **v3 Lane-D mechanic** (loiter-outside-the-openLate- shop). The ~20 fps in night captures is a **headless software-GL artifact** (128 draws / 65k tris is light). → Flagged for D: confirm night patronage at the openLate shop still behaves as the v2.0-rc notes claim. --- ## Round 9 (the v2.0 ship round) — DONE (v2.0 tag pulled, re-tagged in R10) All lanes landed (C browse points + buy-anywhere, D browser rigs, A CITY_SPEC freeze + closing-time ruling, E VRAM audit + half-res book/toy atlases, B tram + tour bookmarks). F wired the one cross-lane seam, graduated the harness, shot the tour, soaked, and tagged the epoch. | task | status | |---|---| | **F1 — interior presence (C→D→F)** | ✅ `interior_mode` reads `occupancyOf(shop.id)` (closure to `citizens`) and spawns browser rigs at C's `room.browsePoints`, one per patronage occupant, **cap 3**; freed by `exit()`'s `keepers.disposeAll`. Door points carry `shopId`. Verified: enter occupied "Second Time Redfern" → 1 browser == `min(occ,3)`, disposed clean, 0 errors. | | **also wired** | ✅ **Tram** (`?tram=1`, `createTram`+update). **Buy-anywhere** (C2): E at a book-spine/toy-box `buyMesh` buys over the wallet (`collapseBuyItem`+toast); preload book+toy packs. Verified: bought a book spine, cash 191→177. | | **F2 — smokes to strict** | ✅ buy-v0 / patronage / book-toy / katoomba **graduated warn→STRICT**. New warn smokes: interior-presence, shelf-buy, tram. Fixed the book-stock check to count `isStock OR buyMesh` (C2 tags spines `buyMesh`). `qa.sh --strict` GREEN 5/5, harness 0 fails / 0 warns. | | **F3 — tour + soaks** | ✅ `tools/v2_tour.py` → **11 shots** (`docs/shots/v2_tour/`, un-letterboxed) incl. the **rain-verandah money shot** (rain + winmap-lit open video shop + CLOSED dark neighbours at night) + browsers + dig-real + katoomba + tram_stop + contact sheet. **Both soaks PASS** (full-default + all-on: 37 chunks, 20 shops, heap stable, 0 errors, leak-free). | | **F4 — docs freeze + tag v2.0** | ✅ Flags table finalized (below); **`v2.0` tagged** on a green tree. | | **Marshal** | C1 (the round's SPOF) landed early; all lanes committed; no slips. | ### v2.0 flags — FINAL | flag | owner | status | default | |---|---|---|---| | roster | D | **graduated → DEFAULT-ON** (R7 flip); `?roster=v1` escape hatch | on | | patronage | D | default-on (`?patronage=0` off) | on | | `?dig=1` | C | opt-in (crate riffle) | off | | `?winmap=1` | B | opt-in (parallax window glass) | off | | `?stock=real` | C/E | opt-in (real GODVERSE covers/spines/boxes + buy-anywhere) | off | | `?weather=1` | B | opt-in (seeded rain/overcast/clear; `?weather=rain` forces) | off | | `?plansrc=osm[&town=]` | A | opt-in (real-OSM towns: melbourne, katoomba) | off (synthetic) | | `?tram=1` | B | opt-in (spine tram loop) | off | | `?noassets=1` | — | opt-in (primitive-only, zero-network) | off | Determinism goldens frozen: synthetic `0x3fa36874` · osm/melbourne `0x34cfdec0` · osm/katoomba `0x0f652510`. --- ## Round 8 (catch-up + the town comes alive → v2.0-rc) — DONE ✅ All lanes landed (E packs, B weather, A 2nd town, C buy, D patronage). F wired the shell seams, graduated the harness, tagged. | task | status | |---|---| | **shell wiring (F owns index.html)** | ✅ **A** `&town=` → `generatePlanFor(seed,'osm',{town})` (Katoomba, 19 shops, verified). **B** weather (3-line contract): `createWeather` on `?weather=1`, `PROCITY.weather={state,intensity}` ({clear,0} when off for D), `weather.update` in loop. **C** session `wallet` (createWallet(seed)) bound to the dig BUY (`getCash`/`onBuy` in interior_mode). **D** patronage: shop door-points per chunk → `citizens.setShops`, `?patronage=0` off-switch, `citizens.setWeather` each frame. All boot clean (0 console errors, settled). | | **F1 — smoke graduations + additions** | ✅ `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). Katoomba golden `0x0f652510` pinned in the determinism gate (fixed a hex zero-pad bug it caught). | | **F2 — patronage baseline** | ✅ No re-pin needed — flags-off regression held (patronage nets *fewer* draws, inside tolerance; ≤300 cap + draws-±40% both green). | | **F3 — tag v2.0-rc** | ✅ **TAGGED.** qa.sh --strict GREEN 5/5 (harness strict). | | **Marshal** | All lanes committed round-8; no slips this round. | --- ## Round 7 (the flip round → v2.0-beta) — DONE ✅ (v2.0-beta tagged) Critical path resolved: E1 ped-merge (`447188a`), E2 stock pack (`0c12dac`), **D's GO** (`43edabf`), C1 `?stock=real` (`3724a65`) all landed. F executes flip → strict harness → tag. | task | status | |---|---| | **F1 — roster flip (prime-law amendment)** | ✅ **DONE + verified.** `index.html`: streamed roster is now the **default** (`chunkStream:{radius:2}` per D's spec); `?roster=v1` = escape hatch (fixed roster); `?roster=stream` still accepted (no-op). Hours-aware `setNightLivelyChunks` on the open-late block. **Same commit** re-pins the flags-off baseline: **old (163 draws/19k tris, roster-off) → new (164 draws/~66k tris, roster-on)** — draws barely move (E1's merge = ~1 draw/rig, the whole point), tris = the crowd (D worst-view 65,899; drift-WARN now draws-only, tris under the hard cap). **Plan goldens UNCHANGED (`0x3fa36874`).** Also fixed the harness's roster probe (was reading a nonexistent prop → blind) + flipped the assertion to require streamMode ON + added an escape-hatch check. Verified: default boot streamMode:true (146–150 active), `?roster=v1` streamMode:false (96), both render, 0 errors, ≤300 draws, harness GREEN. | | **F2 — harness to strict + new-flag smokes** | ✅ **DONE + verified.** `qa.sh` flags gate `warn_gate → run_gate` — the harness (flags-off regression + 4 existing flags + all-on combo) is now **STRICT** (a FAIL fails qa). Wired **`?stock=real`** (F owned the seam): shell preloads the record pack + passes `stockReal`; `interior_mode` builds `makeStockAdapter(getStockPack(shop.type))` and passes it to **both** `buildInterior` (static crate sleeves) and `dig.open` (riffle) — fail-soft to parody if the pack's absent. Added `stock=real` + `weather=1` smokes at **warn-level** (decision #4). Verified: crates show **real GODVERSE covers** (smoke: 7 real atlas sleeve meshes, non-parody batched); weather auto-skips (Lane B not landed); `qa.sh --strict` GREEN (5 gates). | | **F3 — tag v2.0-beta** | ✅ **TAGGED** (no-hostage rule). Criteria met: roster default-on + escape hatch ✓, harness strict+green ✓, `?stock=real` real sleeves ✓, qa strict green ✓. | | **⚠ schedule-harness flag** | **B (`?weather=1`) and A (osm parity + 2nd town) did NOT land round-7** — no commits, tree clean. Both are additive (not headline keys); tagged v2.0-beta without them per the no-hostage rule. Weather smoke is in the harness ready to graduate the moment B ships. Flagging for Fable: two lanes slipped this round. | --- ## Round 6 (finish v2 wiring + pay review debts → v2.0-alpha) — DONE ✅ (v2.0-alpha tagged) Sequencing (Fable): **F1 enforcement harness lands FIRST** (protects the round), then F re-smokes as lanes land, adds C's ≤350-draw interior assertion when C1 lands, extends the determinism gate if A's `?plansrc` lands, and tags `v2.0-alpha` when all-green (3 flags acceptable if A no-shows). | task | status | |---|---| | **F1 enforcement harness** | ✅ **DONE + wired.** `tools/flags_check.py` (self-contained server, headless, ~90s): **flags-off regression** (golden hash `0x3fa36874` + subsystems-inactive + ≤300/≤200k budget + ±40% snapshot + 0 errors) · **per-flag smokes** (winmap/dig/roster: cross ≥2 chunks + enter interior; dig opens+closes a riffle) · **all-on combo** (flag-composition law, decision #4). Wired into `qa.sh` as a **warn-level gate** (`--no-flags` skips fast; auto-skips without the Playwright venv). Current: **all smokes GREEN**, regression clean. | | **F2 plansrc** | ✅ **DONE + verified.** A landed the full seam (`52eb109`). Wired the shell bootstrap (`index.html`, F-owned): `?plansrc=osm` → `citygen.generatePlanFor(seed,'osm')` → real-data town **"Melbourne", 95 shops** (synthetic default = "Boolarra Heads" unchanged). Pinned **both goldens** in `scaffold_check.mjs`: synthetic `0x3fa36874`, **osm `0x34cfdec0`** (deterministic, 2 runs). Harness auto-detects plansrc + smokes it (enters OSM shop "Mui Karaoke Rooms") + includes it in the all-on combo. Also restored `binUnderAim`'s **nearest-bin fallback** (matches C's test page) so E riffles a nearby bin without perfect aim / before the bin GLB finishes loading. Synthetic golden frozen (selfcheck 1362/1362). | | **≤350 interior draw assertion** | ✅ **DONE.** C1 batching landed (`35981ad`). Added an interior draw-budget sweep to `flags_check.py` (enter all 9 shop types, assert ≤350 draws/room, dig on so it also confirms batching kept riffling). **Caught a real bug in my own `interior_mode.js`**: it rendered the interior without `renderer.info.reset()` while `autoReset=false`, so draws *accumulated* per frame (inflated 864/644/…). Fixed (reset per interior frame, both dig + walk render). True per-frame result: **all 9 room types ≤350 (worst dept 96)** — C's batching verified. | | **F3 tag `v2.0-alpha`** | ⏳ all landed flags smoke-green + regression green + qa strict green → tag (3 flags OK if A no-shows). | Base: v1.1 (`3a831fe`), HEAD `40fefd9`. `qa.sh --strict` GREEN. --- ## Round 5 (v1.1 close-out + v2 foundations) — DONE ✅ (v1.1 tagged; ?dig=1 wired) Lane F role: **v1.1 watch** + v2 flag-seam wiring + QA-harness-for-v2 + shared-tree marshal. Full tracking in [`LANE_F_NOTES §10`](docs/LANES/LANE_F_NOTES.md). | task | status | |---|---| | **fal un-park check** | ✅ Verified already done — `039c179` un-parked fal in Lane E's brief (decision #4 + Lane E task 3: glass-case + bus-shelter + optional magazine-rack, ≤2 attempts, costs logged in AUDIT.md). **Nothing for F to change.** | | **archive R3/R4 instruction claims** (F4) | ✅ Added "SUPERSEDED by R5" banner to `ROUND3_INSTRUCTIONS.md` + `ROUND4_INSTRUCTIONS.md`. | | **v2 flags table** (F4) | ✅ Scaffolded in LANE_F_NOTES §10 (`?plansrc=osm` A, `?winmap=1` B, `?dig=1` C, `?roster=stream` D) — all pending owning-lane specs. | | **F1 tag v1.1** | ✅ **TAGGED.** C1 (`aa9c140`: record_crate fixed + counter_till re-map) + E1 (`c522464`: 6 hero props baked 288k→48k, ≤8k each, live on depot) landed. Verified: depot serves baked props (counter_till 7999, drinks_fridge 8000, listening_booth 7986 tris ≤8k), C's `glb.js` maps them, record/opshop interiors render the **counter_till GLB** with keeper stand pose working + no double till, bins upgraded. Re-captured the 2 interior tour shots (fresh chromium → baked props). qa GREEN. `git tag v1.1` (local). | | **F2 wire `?dig=1`** | ✅ **DONE + verified.** C shipped `dig.js` + the flag but left the in-game input/mode hook to F (LANE_C_NOTES §C2). Wired the consumer pattern into `interior_mode.js` (F-owned): aim at a `userData.kind==='bin'` fitting + press **E** → `createDig`/`dig.open` (per-bin seed via `binSeed(shop.seed,pos)`); the dig owns the frame (render `dig.scene`, no interior walk); WALK OUT / Esc closes → back to the room. Shell seam (`index.html`): `?dig=1` → `dig:` opt; `procity:digOpen` releases pointer-lock (riffle is cursor-driven); unlock→leaveShop guard reads `interiorMode.digActive` so opening a bin ≠ leaving. **Fresh-chromium smoke:** E opens riffle (seeded sleeves + prices render), WALK OUT closes to interior, **leak-free** (steady-state geo/tex flat over 3 cycles), **0 console errors**. Flag off ⇒ no listener/no dig ⇒ byte-identical to v1. | | **F2 — other v2 seams** | B `?winmap` / D `?roster=stream` are self-contained (no shell input hook needed); smoked clean (boot, 0 errors, budget OK). A `?plansrc` not taken. | | **F3 v2 QA harness** | ⏳ determinism gate (first-interior-hash per seed) · flags-off regression check (v2-prime-law enforcement) · v2 smoke → qa.sh warn-gate. Build as flags land. | Base state: v1.0 (`2698b40`), `qa.sh --strict` GREEN 4/4, tree clean. --- ## Round 4 (v1-tag round) — DONE ✅ (all 6 gates green; v1.0 tagged) Executed Fable's `ROUND4_INSTRUCTIONS.md` Lane F tasks. All lanes committed R4; a 6-auditor adversarial v1-readiness workflow returned **GO**; `v1.0` tagged locally on the F finalization commit. | task | status | |---|---| | **F1 setExposure** | ✅ Wired (`9416d0a`) **then correctly removed** (`aad9baa`): D3 (`a5e4b64`) made the impostor `toneMapped:true`, so `citizens.setExposure()` became a no-op tone-mapped globally via `renderer.toneMappingExposure`. Removed only *after* D committed (removing earlier would regress night brightness). Final state: no dead call. | | **F2 storeys-checker** | ✅ `permittedMax = max>=2 ? min(max+1,3) : max` (Lane A's rule, `9416d0a`). Consistency **0 warnings** across 6 seeds; single-storey-type-at-2 still trips. **A confirmed** (`5b8c806`). | | **F3 gate-3 budget** | ✅ **PASS.** E1 decimated peds (`518678d`, 924k→46k, ≤3k each) + **D2 sign-off** (`a5e4b64`, fleet ≈59.6k at 24-cap). Official measure: busiest node, pop 200, midday, 12-yaw peak → **draws 138 (≤300) · tris ~31k (≤200k)**. Baseline 372k. | | **F4 gate-6 shots** | ✅ **PASS.** Against committed B2 (`6323d58`, `dbg.js` letterbox+poses): `shots.py v1_tour` → **10 full-frame 1280×720, no letterbox**; §3.5 **night closed-facade** (CLOSED plates + lit openLate video "VIDEO REGAL") + real **record/opshop interiors** (keeper + hero props). Fixed `shots.py` so interior shots capture via `enterShop(id)` (were falling back to a street bookmark). | | **F5 tag v1.0** | ✅ **TAGGED** on the F finalization commit (gate table all-green + shots committed). Local only — John pushes. | | **audit** | 6-auditor v1-readiness workflow: 5 GREEN (gates 1/3/5/6 + release hygiene) + 1 procedural "land finalization commit first" → resolved. GO. | --- ## Round 3 (lead lane) — DONE, verified live Executed Fable's `ROUND3_INSTRUCTIONS.md` Lane F tasks. All wiring verified in-browser on seed 20261990. | task | result | |---|---| | **§3.5 hours / closed shops** | `isOpen()` gate on `enterShop`. Midday: open. **Night (22:00): all closed except the openLate video rental** (Video Regal [11,23]) — verified toy shop blocked, video enters. | | **§3.4 `?noassets`** | **Zero network** (Perf API: 0 GLB/depot/manifest/ped-model requests); 100% primitive, legible. | | **§3.4 interior GLB** | `useGLB` → fitting GLBs load from the **3GOD depot** (bookshop shelves, +8 meshes, visually confirmed). | | **§3.4 rig fleet** | 19 ped GLBs → peds **and** keepers upgrade placeholder→**GLB rigs**; **leak-free** (shared-cache proven, cycle2/3 flat). | | **`--depot` on qa.sh** | `validate_manifest.py --depot` = 0 err (16 GLBs live). **qa.sh GREEN 4/4.** | | **Gate 2 soak** | **PASS** both modes — assets: 20 interiors leak-free (+GLB-cache warmup), heap 98MB stable; noassets: 37 chunks, heap 15MB. 0 console errors. | | **Gate 4 asset-free** | **PASS.** | | **Gate 6 shots** | `docs/shots/v1_tour/` (10 shots) + contact sheet captured; letterbox + 3 fallback bookmarks are Lane B dbg.js. | **Findings → other lanes** (details in [`LANE_F_NOTES §9`](docs/LANES/LANE_F_NOTES.md)): - **D/E:** rig ped GLBs are **tri-heavy** (~45k each) → gate 3 tris **~279k > 200k** budget (draws fine ~200/300). Decimate the ped GLBs; F holds `?pop=140` until then. - **B:** add 3 shots bookmark poses; fix the headless composer letterbox in `DBG.shot()`; render the §3.5 closed-facade visual (dark windows + CLOSED plate; predicate `window.PROCITY.isOpen`). - Fixed **5 F-owned harness bugs** so `shots.py`/`soak.py` actually run (DBG async-poll, `DBG.plan`→`PROCITY.plan`, `enterShop` bare-id, `shop.lot` is an id, midday-pin + GLB-cache warmup + budget→warning). **Git:** working tree synced to canonical `main` (862bbcd) mid-session — my R2 work is committed as `aa4ed4f`. R3 edits below are staged by pathspec (see Housekeeping). Nothing pushed. --- ## TL;DR (round 2 — for context) All five lanes (A–E) landed; F stood up the QA harness, **adversarially reviewed and fixed its own tooling**, then wired the lanes into **a walkable, living town** — interiors, shopkeepers, and street crowds all integrated and verified live: 1. **Interiors — every door opens (§3.2).** A door opens into a themed, seeded, self-lit 90s-AU interior; walk it, walk back out (or Esc), room disposes. Verified across **all 9 shop types**, **leak-free**, worst build **8.4 ms**. F-owned bridge [`web/js/world/interior_mode.js`](web/js/world/interior_mode.js). 2. **Shopkeepers (§3.3a).** A keeper stands at the counter of every shop and **turns to greet** you; disposed with the room, **leak-free over 5 cycles**. (Asset-free placeholder; rig-fleet upgrade later.) 3. **Street crowds (§3.3b).** Deterministic pedestrians walk the footpaths (`graph = plan.streets` directly), density follows time-of-day (busy midday, empty at night), LOD-tiered. **Draw budget holds** — worst main-street view **~191 draws** (budget 300). Shell wiring in [`index.html`](web/index.html). 4. **Plan swap (§3.1)** + **facade skins fix** (prior pass): boots on Lane A's real town, textured. `tools/qa.sh` is **GREEN (4/4 gates, 0 skipped)**. Remaining: manifest GLB upgrades + `?noassets` (§3.4 — also upgrades keepers/peds to shared rigs), hours/closed-shops (§3.5), draw-budget shave. Scoped in [`docs/LANES/LANE_F_NOTES.md`](docs/LANES/LANE_F_NOTES.md) §3.4–§3.5,§7. --- ## Lane readiness — ALL COMPLETE ✅ (`node tools/qa/scaffold_check.mjs`) | Lane | status | notes | |---|---|---| | A CITYGEN | **5/5** | plan.js, registry.js, names.js, selfcheck.js, map.html — determinism gate green, verified in-browser | | B STREETSCAPE | **4/4** | index.html shell, chunks.js streamer, buildings.js, fixture_plan.js — walkable town verified live | | C INTERIORS | **2/2** | interiors.js (`buildInterior`), interior_test.html | | D CITIZENS | **3/3** | rigs.js, sim.js (`CitizenSim`), keepers.js (`KeeperManager`), citizens_test.html | | E ASSETS | **3/3** | manifest.json (validates green), validate_manifest.py, AUDIT.md + pipeline | --- ## What F built / did this session ### QA harness (F-owned, `tools/`) — trustworthy, all green | file | what | state | |---|---|---| | `tools/qa.sh` | gate runner; `--strict` = v1 umbrella | ✅ 4/4 green | | `tools/qa/scaffold_check.mjs` | scaffold + PRNG determinism (golden-locked) + live readiness matrix + plan-determinism | ✅ | | `tools/qa/consistency_check.mjs` | cross-lane gate: plan ↔ registry ↔ assets ↔ manifest | ✅ | | `tools/shots.py` · `tools/soak.py` | Playwright beauty-tour + 10-min soak/budget/`--noassets` (gates 2,3,4,6) | ✅ ready; need Lane B `window.DBG` hook (§4) | ### Adversarial self-review (Workflow, 23 agents) Ran a 4-auditor + verifier workflow against F's own scripts + runbook. It confirmed **14 real defects in F's tooling** (false-confidence checks + wrong API assumptions). **All applied:** - `scaffold_check`: JSON round-trip was a **tautology** (fixed → real deep-compare w/ `-0` norm); Math.random-ban regex had **false negatives** (fixed → anchored to comments); irange golden check re-seeded per draw (fixed → single stream). - `consistency_check`: added a **storeys-vs-registry-range** check, fixed a dead late-night branch, removed a dead metric. - `soak.py`: heap heuristic no longer silently passes when heap isn't reported; shop count now requires **verified interior loads** (not just `enterShop` calls); `--minutes` now honored. - `shots.py`: fixed an `IndexError` on a trailing `--seed`. - **Runbook** (`LANE_F_NOTES.md`): corrected against the **real landed APIs** — `chunkIndex` returns `{chunkSize,chunks}` (not callable); chunks is a `createChunkManager` **factory** with no lifecycle hooks; `CitizenSim` is **graph-driven, not per-chunk**; keepers use `spawn/remove/disposeAll`. ### Integration passes (verified live in-browser) - **`web/js/world/interior_mode.js`** (new F-owned bridge) + thin marked seam in `index.html` → **§3.2 interiors: every door opens.** `enterShop` looks the shop up in the plan, attaches its lot dims, calls Lane C `buildInterior`, and switches to a new `interior` mode that renders the room in its own scene and walks it via the room's occupancy grid. Exit on walk-out / `procity:exitShop` / Esc → `dispose()` + player restored to the door. **Verified:** door → Toy Shop renders; exit state-machine correct; **leak-free** (134 geo/37 tex → 217 loaded → 134/37 after dispose); all **9 shop types** `ok+pathOK`, worst build **8.4 ms**; **0 console errors**. - **Keepers (§3.3a)** — wired into `interior_mode.js`: `KeeperManager` (no fleet → placeholder actor), spawned at the counter stand pose (`places.find(p=>p.userData.keeperStand)`), ticked in the interior loop, `disposeAll()` on exit. **Verified:** 11-mesh keeper at the counter, **greets** the player (turns 0.21 rad, eases back), **leak-free over 5 cycles/5 types** (net growth 0). - **Citizens (§3.3b)** — `CitizenSim` in `index.html`: `graph = plan.streets` (no adapter needed), `setPopulation(140)` (`?pop=`), `setTimeOfDay((seg+frac)/6)` + `update(dt)` in the street loop, `visibilitychange→setPaused`. Fixed a NaN bug (`getClock().hour` is a string). **Verified:** 96 active midday, peds walk footpaths ~1.5 m/s, budget-safe (worst **~191 draws** across all main edges). - **`web/js/citygen/index.js`** (barrel) → game boots on Lane A's real generated town (was the fixture). Verified: console "using Lane A generatePlan", HUD "Boolarra Heads · 493 shops". - **`web/js/world/skins.js`** (1-line chokepoint fix) → `facadeMat` normalizes full-filename skins. Verified: `facade-*.jpg` requests now **200 OK**, town textured (was all flat-grey). ### Docs (F-owned) - `docs/LANES/LANE_F_NOTES.md` — integration runbook (real APIs, wiring steps, QA gates, DBG hook request). - `docs/V2_IDEAS.md` — parked scope (content firehose, override layer, weather, venue, …). - `docs/shots/` — reference-shot tree (the other lanes are already dropping acceptance shots into it). --- ## Findings for the lanes (F flags; the two blocking-visual ones are already fixed) | # | lane | finding | status | |---|---|---|---| | 1 | B | shell imported non-existent `js/citygen/index.js` → ran on fixture | **fixed by F** (barrel) | | 2 | B | `skins.js facadeMat` double-wrapped full-filename skins → facades 404 | **fixed by F** (normalize) | | 3 | B | street measures **~334 draw calls** at spawn (radius 3) — over CITY_SPEC ≤300 | open — shave (radius 2 or more merging) | | 4 | A | ~10% of shop names have a **doubled possessive** ("Tex's's Toys") — `names.js` | open (cosmetic) | | 5 | A | some shops have **storeys outside the registry type range** (e.g. video at 2, range [1,1]) | open — confirm corner-anchor intent | | — | contract | `facadeSkin` is a full filename in the registry but skins.js expected a bare key | **worth a CITY_SPEC line** so ground/awning/interior skins avoid the same trap | --- ## Remaining integration (scoped in LANE_F_NOTES §3; ready to execute) 1. ✅ ~~**Interiors (§3.2)**~~ — **DONE**. 2. ✅ ~~**Citizens + keepers (§3.3)**~~ — **DONE** (both verified live; see Integration passes above). 3. **Manifest + `?noassets` (§3.4)** — GLB upgrades from `manifest.json`; primitive-fallback flag. Doubles as the **rig-fleet upgrade** that makes keepers/peds cheap (shared meshes) so `?pop=` can rise. 4. **Hours / closed shops (§3.5)** and the **draw-budget shave** (finding 3). 5. **Full QA gates 1–4,6** once Lane B exposes the `window.DBG` hook (§4) my harnesses drive. *Next natural step is §3.4 (manifest + `?noassets`) — it's the asset-free gate AND the fleet upgrade that upgrades placeholder keepers/peds to shared GLB rigs. §3.5 (hours) is a small data-driven follow-on.* **Note on verification:** pointer-lock doesn't engage in the automated browser, so a real *walk* is best playtested by a human (`cd web && python3 -m http.server 8130` → `localhost:8130?seed=20261990`). F drives/inspects via `window.PROCITY` + JS for programmatic checks. ## Housekeeping - Nothing committed (all working tree). F's edits are minimal, commented `[Lane F integration]`, reversible. **New F-owned file:** `web/js/world/interior_mode.js` (interior + keeper bridge). Shell seam edits in `index.html`: interior wiring (import, bridge create, `enterShop`/`leaveShop`, loop branch, `setMode` lock, Esc-exit, M/P guards, `window.PROCITY` hooks) + citizens (import, `CitizenSim` create + `setPopulation`, street-loop `setTimeOfDay`+`update`, `visibilitychange` pause). Also edited: `skins.js` (facade fix, prior pass). Suggested commit once reviewed — **use a pathspec** (lanes share one tree, see memory): `git commit -- web/js/world/interior_mode.js web/index.html web/js/world/skins.js web/js/citygen/index.js tools docs/LANES/LANE_F_NOTES.md docs/V2_IDEAS.md docs/shots F-progress.md` msg `Lane F: integration — interiors + keepers + street peds + QA harness + barrel + skins fix`. - A local `python3 -m http.server 8130` is running for the smoke test (harmless; kill when done). - Lane prompts hard-code `/Users/jing/Documents/PROCITY`; real path is `/Users/m3ultra/Documents/procity`. --- ## Round 38 (v8 WAVE 1 — INHABITATION) — the budget reconciliation · four new gates, four controls · the round close **THE BUDGET OF RECORD DOES NOT MOVE: 292 / 300 draws, margin still EIGHT.** The brief's naive sum (292 + washing 1 + magpie 1 = 294) is wrong in our favour. **The pinned worst-draw frame is a NIGHT frame and both draw-spending layers are structurally zero at night** — the washing comes in (`count = 0`), the magpie roosts (`count = 0`). Wave 1's one draw is spent in daylight, where the walked peak sits ten draws lower. Two pinned trees (`6bc7926` control, byte-identical under `web/` to R37's `7bd96e5`; `4514cb9` treatment, byte-identical under `web/js` to the final `74d6257`), assets symlinked to the live tree on both sides, 802 stations @ 2 m at **yaw 0**, sim + tram **LIVE**: **night 292 → 292 (+0)** / 120,145 → 122,879 tris; **noon 282 → 283 (+1)** / 99,872 → 103,118; `?classic=1` **269 / 90,580 → unchanged**; `fitzroy_real` **172 / 130,727 → unchanged**; `bendigo_real` **204 / 131,492 → unchanged even with `?magpie=1` forced**. The control reproduced R37's pin **to the byte, twice**. Every revert is exact: `?yards=0` at night gives the control's three numbers; `?yards=0` at noon gives 282 / 99,872, isolating the noon +1 to the washing alone. **Lane B's "control 346" — answered rather than dismissed.** As a *delta* it is the wall-clock ped spawn artifact R37 named (114 draws of swing between two browser instances measures the sim, not the build), and B was right to hide the sim. But 346 > 300 is a claim about the world, and neither `stepwalk` (walks the chunks in, photographs a half-dressed street) nor `dwell` (fills the street, arrives clean at 27 chunks) can adjudicate it. **`tools/qa/budget_walk.py` gains `--method stepsettle`**: the audit's fixed-yaw stations, each **drained then held N real rAF frames** — R+1 dispose residue AND a fully-acquired crowd in the same frame. **Like for like — the same 101 stations at 8 m, yaw 0 — the settle is worth +3 draws at noon (277→280 control, 278→281 R38) and −9 at night (287→278 both trees), and the sign flip is the finding: at night the settled street RETIRES near rigs to impostors (tris 120,145→100,939), so the crowd goes home and a sampler that never waits does not see it. **The highest draw count anywhere in the reconciliation — 4 methods × 2 trees × 2 segments × 5 towns — is 292, the pin. Nothing read over 300.** The mechanism that could legitimately have breached it (full crowd inside a full R+1 dispose window) now reads 280/281. B's 346 was the artifact, and the refusal is now backed by the measurement that could have falsified it. **A correction to my own R37 pin:** the corpus TRI worst is **130,727** on `fitzroy_real`, not 124,793 — and it reads 130,727 on the **control** tree, so it is my R37 sampling density (12-bearing 8 m sweep) versus this round's 2 m fixed-yaw stations, not a wave-1 cost. Re-pinned. *A pin is only as good as its station spacing, and mine had not said so.* **FOUR NEW GATES, FOUR CONTROLS DEMONSTRATED** (`smoke_ambient` · `smoke_character` · `smoke_magpie` · `smoke_yards`, all wired into `flags_check.main()`): - **§1.1 audio** — six sources anchored (63/8/81/32/39/57, B's numbers exactly); dog **audible at 5.9 m with 1 play scheduled** (for a one-shot `audible` is only published once `plays > 0`, so one word covers fetch→decode→gain→schedule→counter); **hours enforced** (sprinkler audible 06:30 → idle 12:30, same pose); **day-of-week enforced** (mower idle Mon → audible Sat, same pose); **pan is a bearing** (yaw 0/90/180/270 → +0.00 +0.85 −0.00 −0.85); **zero draws byte-identical to `?ambient=0`**. **THE CONTROL:** a second server, root symlinked file-for-file except one patched `manifest.json` with `sfx['dog-bark-fence']` renamed → that source reports **`no-entry`, 0 plays, never audible**, other five unchanged, **0 console errors**. Stated on every run: audible OUTPUT is unverifiable headlessly — **nobody in this loop has ears**. - **§1.2 character** — classic is the frozen v1 literals with all tints null and 5 ground meshes; **all 23 shipped keys matched per-town including the two carrying `state: ''`**; **the control fires** (an unknown key returns `matched:false`); **21 distinct palettes across 23 towns** (a single shared palette FAILS by name); and the two orphans are **on the GPU** — `ground-gravel.jpg` bound on Castlemaine, `ground-reddust.jpg` on Darwin, read off the live material. - **§1.3 magpie** — the latch arm verbatim with its **positive control first** (TRUE before the roll); day-roll clears it; **localStorage gained NO new key** (baseline taken *after* a `game.save()` — the first cut blamed the magpie for v7's own write); **the season is 23 distinct positions across 24 towns, run UNFORCED**, against B's first cut's 24-of-24-at-214, with `newtown_real` in season on day 1 and 129/1000 seeds hitting the 11.5% design ratio; **territory 6 → 157** synthetic → bendigo; **+1 draw / +182 tris** at one pose reused across both boots. - **§1.4 yards** — 145 house + 36 yard = 181 lots, 181 hoists → 394 items, 256 active, **0 at night**; the cost decomposition at one pose with the sim quiet: **box props +0 draws** (and **−2**, because the 36 `yard` lots used to fall through to `buildShopfront()`) with **+1,936 tris**, **washing exactly +1 draw / +512 tris**; and on real towns it **SKIPS LOUDLY** — a `⊘ SCOPE` line printing 0 house/yard lots / 0 hoists / mesh never created, next to the synthetic 181 that makes the zero attributable. **Also caught, in my own gates:** the pan arm's first draft used two bearings and read `+0.00/−0.00` because that anchor sat on the forward axis — it would have called a working panner inconclusive. Four bearings. *The gate's first draft failed the standard the gate exists to enforce.* **SUITE: `tools/qa.sh --strict --matrix` GREEN on the true final tree** (`74d6257` — E's session-2 publish landed mid-round, `web/` diff vs B's `4514cb9` is manifest + one thumb, **no code**, and both were already in the tree both budget servers read): **7 passed · 0 failed · 0 warn · 0 skipped**; **flags_check GREEN 0/0** with all four new gates inside it; **MATRIX GREEN 10 towns × 7 gates**, 0 fetches under `?noassets` on all ten, tram verdicts unchanged. Wave 1 moved nothing in the matrix. **Held: nothing.** Recorded, not fixed (other people's files): `magpie.js` says **154 triangles** at `:9`/`:123` and **182** at `:42` — measured 182, so two stale comments in B's file; E's three published GLBs are manifest-and-depot but not under `web/assets/models/` (staged in `pipeline/_normalized/`, nothing loads them, validator's contract covers it — **not a defect**). Carried debt unchanged: A's D1–D4 · the matrix's budget column is bookmark numbers · `?r=3` breaches at 307 · the `?classic=1` town-selector question. **No tag** — wave 1 is content; the epoch tags when it is whole.