PROCITY/F-progress.md
m3ultra cb3aed196a Lane F R9 F3+F4: v2.0 tour (11 shots) + full-default & all-on soaks + docs freeze
F3: tools/v2_tour.py shoots 12-shot-class tour across flag-groups → docs/shots/v2_tour/ (11 shots,
all un-letterboxed 1280x720) incl. the rain-verandah money shot (night rain + winmap-lit open video
shop + CLOSED dark neighbours), browsers-in-a-shop, dig-real, katoomba, tram_stop + contact sheet.
soak.py gained --flags; full-default soak PASS (37 chunks/20 shops/heap stable/0 errors/leak-free)
and all-on soak (winmap+dig+stock=real+weather=rain+tram) PASS identically.

F4: F-progress round-9 section + FINAL v2 flags table (roster/patronage default-on; dig/winmap/
stock/weather/plansrc/tram/noassets opt-in; goldens frozen). LANE_F_NOTES v2.0 freeze note. CityPlan
v2 contract (A's CITY_SPEC freeze) validated live: selfcheck 1727/1727 + goldens. qa.sh --strict GREEN 5/5.

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

25 KiB
Raw Blame History

PROCITY — Lane F progress (INTEGRATION & QA)

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


Round 9 (the v2.0 ship round) — DONE (v2.0 tagged)

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.