# PROCITY — Round 6 lane instructions (from Fable, integrator) > **⚠ SUPERSEDED by [ROUND7_INSTRUCTIONS.md](ROUND7_INSTRUCTIONS.md) (2026-07-14).** Round 6 is > complete — ALL six lanes delivered; v2.0-alpha tagged (`6948372`). Historical reference only. Date: 2026-07-14 · Written after the Fable round-5 review (`d71420f`). You are an Opus 4.8 agent assigned ONE lane (read your session prompt). Read this file, your lane spec, your `-progress.md`, and NOTES addressed to you before touching code. Repo state: everything committed on `main` at `d71420f`. **v1.1 tagged** (`3a831fe`). `tools/qa.sh --strict` GREEN 4/4 (golden `0x3fa36874`, manifest `--depot` 0 errors, **27 procity GLBs live**). Three v2 flags live and default-off: `?winmap=1` (B), `?dig=1` (C+F), `?roster=stream` (D). fal balance ~$21.36 after E's $0.64 round-5 run. ## Where we are / what round 6 is Round 5 delivered v1.1 + three of four v2 foundations. Round 6 **finishes the v2 wiring and pays the debts the review found**: - **Lane A did not run in round 5** — the `?plansrc=osm` plan source carries over whole. It is the round's biggest single item. - **F3 (the harness that enforces the v2 prime law) never landed** — right now nothing automatically catches a v2 flag leaking into the default path. That's unacceptable for a codebase with three live flags; it lands this round, early. - **Interior draw counts are hot** — C's own R5 measurements: book room **1,245 draws**, opshop 702 (tris are fine post-bake). Almost certainly unbatched per-item stock. Fix before any more interior content lands. - **Five published GLBs have no consumer**: `glass_case`, `magazine_rack`, `crate_stack` (C, unmapped — they landed after C's R5 commits) and `street_bin`, `bus_shelter` (B, unplaced). - **publish.py is destructive** — it glob-rewrites `pipeline/_published.json`, which clobbered the record to 1 entry in R5 (Fable regenerated it from the live depot). E fixes the script this round. End-of-round milestone: if all four flags are live and the combined smoke is green, F tags **`v2.0-alpha`** (local — John pushes). ## The v2 prime law (still in force) Default boot stays v1-identical. Golden hash `0x3fa36874` frozen. Flags default-off. `qa.sh --strict` green at every commit. New this round (decision #4): **flags must compose**. ## Integrator decisions (so nobody blocks) 1. **A's round-5 brief carries verbatim** (ROUND5_INSTRUCTIONS §Lane A): plan-source contract + OSM fixture import behind `?plansrc=osm`, fixture checked in, zero network. Nothing else in round 6 hard-depends on it — F extends the determinism gate when it lands, and simply doesn't if it doesn't. A: if the full import is too big for one session, land the contract + fixture + a partial importer that boots, and document the cut line. 2. **Interior draw budget is now law: ≤350 draws worst room** (street gate stays ≤300 / ≤200k). C enforces via its sweep; F adds it to the harness once C's fix lands. Rationale: interiors render alone (no street behind), so 350 is generous — 1,245 is a bug, not a budget problem. 3. **publish.py becomes non-destructive**: merge into `_published.json` (union of existing + newly pushed), never replace. `_published.json` is the provenance record of everything ever published — treat it like AUDIT.md. 4. **Flag-composition law**: any subset of v2 flags on together must work. F smokes the all-on combo (`?winmap=1&dig=1&roster=stream` + plansrc if landed). If two flags fight, the later-landing lane owns the fix; F arbitrates. 5. **fal spend**: no new gens authorized this round. The $21.36 balance sits. (Re-gen of a *failed* existing prop at ≤2 attempts is fine if something turns out broken in use — log it in AUDIT.md.) 6. **Tag `v2.0-alpha`** (F, end of round): all four flags landed + flags-off regression green + all-on combo smoke green + qa.sh strict green. If A doesn't land, tag anyway with three flags and note it — don't hold the milestone hostage. ## Process rules (unchanged — agents run concurrently) - ONE shared working tree. `git status` first; stage only your files by exact path; options before `--`; `add` new files first; commit immediately; pin a durable per-lane ref. - Leave `qa.sh --strict` GREEN. Update `-progress.md`; cross-lane asks in your NOTES. - F owns `web/index.html` + `tools/`; document call sites for F instead of editing. - `procityprogress.txt` / `settings.local.json` stay untracked. - Repo: `/Users/m3ultra/Documents/procity`. Ports: `~/Documents/thriftgod` (A's import), `~/Documents/90sDJsim` (reference). Dev server `:8130`. ## Sequencing ``` F1 (flags-off regression + v2 smoke) ── land FIRST, everything else runs under it E1 (publish.py fix) ── early, protects the record while others publish C1 (interior draw batching) ──→ C2 (map 3 new fittings) ──→ F re-smoke interiors A1 (plansrc, carried) ─────────→ F2 (determinism gate + plansrc smoke, if landed) B1 (street_bin + bus_shelter) · D1 (roster hardening) ── parallel all green ──→ F3 (tag v2.0-alpha) ``` --- ## Lane A (Citygen) — the second plan source (CARRIED FROM ROUND 5, unchanged) Your round-5 brief carries verbatim — execute ROUND5_INSTRUCTIONS.md §Lane A. Summary: 1. **Extract the contract**: document the exact CityPlan shape `generatePlan` returns in CITY_SPEC (nodes/edges/blocks/lots/shops/registry/hours/openLate/seeds); add a `planSource` selector — default `'synthetic'` byte-identical (golden hash law), `'osm'` behind `?plansrc=osm`. 2. **Build the OSM source**: port the relevant parts of thriftgod `server.py:433 build_city()` reading a **checked-in fixture** (one real AU town's shops, few hundred max, extracted from thriftgod's `city_source.json` cache) → dedupe → suburb inference → parody names → seeded hours → valid CityPlan. Deterministic (fixture+seed → same plan, hash it). **Zero network.** 3. **Prove it downstream**: `?plansrc=osm` boots the shell end-to-end — streets, doors, interiors; invariants green (parameterize selfcheck by source; synthetic golden hash untouched). Screenshot the imported town. 4. Document the seam in LANE_A_NOTES for F's determinism-gate extension. **Integrator note (decision #1):** if one session can't finish, land contract + fixture + a booting partial importer and document the cut — a half-landed seam beats another no-show. **Acceptance**: default boot byte-identical; `?plansrc=osm` boots a real-data town with invariants green; fixture committed; zero network; qa green. ## Lane B (Streetscape) — place the street props 1. **Place `street_bin_01` and `bus_shelter_01`** (published R5, no consumer yet). Bins: seeded placement along footpaths (corner/mid-block, sparse — this is a country town, not a mall). Bus shelter: main-street edges, oriented to the kerb, sensible spacing (1–3 per town) — note it's also the future stop for V2's tram/bus loop, so place it ON the street graph (an edge attribute or deterministic edge pick), not free-floating, and document the placement rule in LANE_B_NOTES for whoever builds the vehicle loop. 2. **Footprint/collision verify** both (same bar as your R4 furniture verify): planted, no sidewalk clipping, player collision sane, correct facing, deterministic per seed. 3. **Re-measure** worst continuous-walk view with citizens: still ≤300 draws (the props are instanced/batched like the rest of your furniture — don't add per-instance materials). 4. Optional polish: a `winmap` bookmark shot for the gallery (day + night through glass). **Acceptance**: both props placed deterministically + collision-verified; worst view ≤300 draws; placement rule documented; qa green. ## Lane C (Interiors) — kill the draw count, then map the three orphans 1. **Interior draw batching (do FIRST — it's now law, decision #2: ≤350 draws worst room).** Your R5 numbers: book 1,245 / opshop 702 / milkbar 573 draws. Diagnose first (one `renderer.info` breakdown per room type — is it per-item stock meshes? per-sleeve canvases? fitting clones?), then batch: merge static stock into per-fitting or per-room geometry (`BufferGeometryUtils.mergeGeometries` on shared-material groups), or instance repeated items. Constraints: determinism (same seed → same room), dig compatibility (`?dig=1` riffle needs its bin's sleeves — keep dug bins' items addressable or rebuild the bin's mesh on dig-open), leak-free dispose, keeper/stock contracts unchanged. Re-measure ALL room types after; extend your sweep to **assert ≤350** so it can't regress. 2. **Map the three orphaned fittings** in `glb.js` (published + in manifest since E3/E2, after your R5 commits): `glass_case` → toy/pawn/milkbar (display case — front-of-room or counter-adjacent), `magazine_rack` → milkbar/pawn (it's the R3 thin-wire reject reborn via fal — eyeball it in-room), `crate_stack` → record/opshop back-room dressing. Real- metre scale + per-kind yaw as ever; sweep + soak with them on; extend the contact sheet. 3. Re-run the dig soak (`?dig=1`) after the batching change — the riffle must still work on the batched bins. **Acceptance**: worst room ≤350 draws with the sweep asserting it; all three fittings live in rooms, validated, screenshotted; dig still works + leak-free; qa green. ## Lane D (Citizens) — harden the streamed roster toward default-on The roster flips to default in v2 proper; this round produces the evidence for that call. 1. **Full soak with `?roster=stream` on**: the standard gate-2 walk (≥30 chunks, ≥15 shops) plus your far-field walk — leak-free (renderer.info at baseline after chunk disposes), 0 console errors, heap stable, determinism (same seed + walk → same identities). 2. **Hours-aware density** (if not already in): streamed spawns respect the day fraction like v1; at night, density drops and the openLate video shop's block stays lively. 3. **Flag-composition check with C**: `?roster=stream&dig=1` — entering a dig from a street with a streamed roster, exiting, walking on. No interaction with interior mode (roster is street-tier), but prove it. 4. **The numbers memo**: write LANE_D_NOTES "roster default-on readiness" — perf deltas (draws/tris/frame-time worst view vs v1 roster), memory over a 30-chunk walk, density uniformity evidence, known gaps. F + Fable use it to decide the flip in round 7. **Acceptance**: streamed-roster soak green; composition with dig proven; readiness memo written with real numbers; flag still default-off; qa green. ## Lane E (Assets) — make the pipeline non-destructive, then on-call 1. **Fix publish.py (decision #3)**: merge newly-published files into `_published.json` (read existing → union → sorted write), never replace. While you're in there: after any publish, verify the file against the live depot list (`/api/list` filtered to `procity_*`) and warn on drift. Add a small check to `validate_manifest.py` (or a new gate): every manifest `depot:` file must appear in `_published.json` — so a future clobber fails QA instead of hiding. 2. **Re-verify the record**: run your fixed publish path's verify against the depot and confirm the 27-entry `_published.json` Fable regenerated is exactly right (fix if not). 3. **On-call**: B may need bus-shelter/bin scale or origin tweaks once placed; C may find `magazine_rack`/`glass_case` issues in-room (fal re-gen of a *broken* prop is authorized at ≤2 attempts per decision #5, logged). Answer within your NOTES. 4. **No speculative generation** — the depot grows consumer-driven only. No fal spends beyond decision #5's broken-prop clause. **Acceptance**: publish.py merges (proven by a dry-run + a real no-op run), QA catches manifest-vs-published drift; `_published.json` verified against depot; asset asks answered; qa green. ## Lane F (Integration) — the enforcement harness, then the alpha tag 1. **F1 — flags-off regression + v2 smoke (LAND FIRST; carried from R5 where it slipped).** Per your own LANE_F_NOTES §"v2 QA harness extensions": - **Flags-off regression**: boot with no flags → assert plan hash == golden + a settled draws/tris snapshot within tolerance of the v1.1 baseline. This is the enforcement arm of the prime law — with three live flags it is overdue. - **v2 smoke**: for each flag (`winmap`, `dig`, `roster`, + `plansrc` when A lands): boot flag-on, walk 2 chunks, enter 1 interior (dig: open one riffle), 0 console errors. - **All-on combo** (decision #4): one smoke with every landed flag on simultaneously. - Wire into qa.sh as a **warn-level gate this round** (strict in round 7). Keep it headless-runnable and fast (<2 min) or it won't get run. 2. **F2 — plansrc integration (only if A lands)**: wire the shell plan bootstrap selector, extend the determinism gate to pin (seed, plansrc) hashes, add plansrc to the smoke. 3. **F3 — tag `v2.0-alpha`** (decision #6): all landed flags smoke-green + flags-off regression green + qa strict green → tag locally with a summary (three flags is acceptable if A no-shows again — note it in the tag message). Update the flags table + runbook + F-progress. John pushes. 4. Add C's interior ≤350-draw assertion to the harness once C1 lands (a per-room-type check in the smoke or soak — coordinate via NOTES). 5. Marshal duty continues. **Acceptance**: flags-off regression + per-flag + all-on smokes running in qa.sh (warn); determinism gate extended if plansrc lands; `v2.0-alpha` tagged on a green tree; runbook + flags table current. --- *— Fable (integrator). Round 5's lesson: the review caught a clobbered provenance file, a skipped harness, and a silent no-show — all three were invisible to lanes' own green self-reports. F1 exists so the machine catches the next one. Commit atomically, pin your refs, leave it green.*