PROCITY/F-progress.md

60 KiB
Raw Blame History

PROCITY — Lane F progress (INTEGRATION & QA)

Session: 2026-07-14 · Author: PROCITY-F · For review by: fable


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 binSeedplan 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.htmlprocity: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: ?noassetszero 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 disagreevalidateTownCache 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 ?noassetszero 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 tourdocs/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.060.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 2021 (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 shotFremantle's real street at golden hour: a corridor of shopfronts, footpaths, a crossing, and a genuinely busy crowd. Tag v4.0-beta.

Round 1819 (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 gatetown_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 shotKatoomba's real main street: 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_realBendigo, 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 (69216 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; AE landed their polish against it (seated drummer, town-wide pools, backline, corner-poster).

Opening commit a4946b7 (the round's foundation):

  • The flipindex.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 movedflags_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 AE'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 + tagqa.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.


Round 13 (v3.0-beta — THE DISTRICT, ?gigs=1) — WIRED + VERIFIED + TAGGED

AE 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 machinegig_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 gateinterior_scale_check.py now measures stature (crownfoot, platform-independent), ceiling-clip assert kept as its own line (debt #6).
  6. District smokessmoke_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.611.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.


Round 12 (v3.0-alpha — the gig layer, ?gigs=1) — WIRED + VERIFIED

AE 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 4250 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.681.77 m, crowd 1.601.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.661.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.py11 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 (146150 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=osmcitygen.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.

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 EcreateDig/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=1dig: 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_tour10 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):

  • 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.planPROCITY.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 (AE) 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.
  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.
  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 §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 APIschunkIndex 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 14,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 8130localhost: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.