v5.0-alpha is NOT tagged. #7a is not a gate that failed, it's a gate that cannot be run:
G's 120 real records are in the tree and byte-identically re-bakeable, and nothing in the
game can reach them. R18's law — release rounds don't ship known breaches.
GREEN (everything except the crate): qa.sh --strict --matrix 7 passed / 0 failed / 0 warn ·
MATRIX GREEN 10 towns x 7 gates · selfcheck 152,015/152,015 (was 161,300, E retired
toowoomba) · flags harness green, so ?classic=1 still holds through the merge · tier 0
?noassets zero fetches + zero console errors (the offline law holds) · tier 1 (v2
town-wide) resolves. Tier 1 (v5, per-shop) is NEVER FETCHED.
FINDING 1 — the atlas contract contradicts itself. LANE_C_PUB:271 publishes
stock_shop_<id>_index.json; C's own loader (stockpack.js:25) fetches
${base}stock_${type}_index.json. Incompatible. G built runtime-compatible (which is why
C's base-override check passed); E built contract-compatible (which is why 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, and it costs G nothing.
FINDING 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 returns 0 ("No atlases yet =>
clean pass"), which validate_manifest.py:158 propagates. It conflates "no atlases exist"
with "no atlases matched my glob" — the first is a clean pass, the second is the exact
failure it was written to catch. Pointed at G's index by hand, deeper drift surfaced:
E's line 68 reads provenance top-level while E's own docstring says nested (where G put
it), licence vs license, and a ceiling stale by one round.
FINDING 3 — #7a would have passed vacuously. Walked into The Vintage Record (real shop,
real road) via DBG.enterShop: 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. Two vacuous gates in one round, same
species: both test the presence of a thing rather than the identity of it.
R24 — three gaps, none F-only: A+G land a godverse_id (verified live: every godverse shop
reports null; the schema has no such field) -> 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 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 — ballarat now warns at 255 m, so the thin tail
has a second member. The README v5 teaser is held with the tag; they're one claim.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
582 lines
49 KiB
Markdown
582 lines
49 KiB
Markdown
# PROCITY — Lane F progress (INTEGRATION & QA)
|
||
|
||
**Session:** 2026-07-14 · **Author:** PROCITY-F · **For review by:** fable
|
||
|
||
---
|
||
|
||
## 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_<id>_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/<key>.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=<key>` → `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`.
|