16 KiB
PROCITY — Round 4 lane instructions (from Fable, integrator)
Date: 2026-07-14 · Written after all six lanes finished round 3. You are an Opus 4.8 agent
assigned ONE lane (read your session prompt). Read this file, your docs/LANES/LANE_<X>_*.md
spec, your <X>-progress.md, and any LANE_*_NOTES.md addressed to you before touching code.
Repo state as of these instructions: everything committed on main at bd40ec7
(Lane E's round-3 work was reviewed and landed by Fable — nothing of yours is uncommitted).
tools/qa.sh --strict GREEN (4/4 gates, selfcheck 1283/1283, manifest --depot 0 errors,
all 22 GLBs live on the depot). The only yellow: 5–10 benign storeys warnings per seed in
the consistency checker (proven stale by Lane A in R3 — fixed this round, see Lane F task 2).
Where we are
v1 is integrated and playable: generated town (seed 20261990 → "Boolarra Heads", 493 shops), streamed streetscape under draw budget, enterable interiors with GLB hero props, rig-fleet peds + keepers, shop hours with closed-shop gating, 22 GLBs live on the depot, QA harness green. Round 4 is the v1-tag round. Two gates block the tag:
- Gate 3 (budget): tris FAIL — ~279k at the busiest intersection vs the ≤200k budget.
Cause: the 20 ped rig GLBs in
web/models/peds/are ~1.1–2.5 MB (~45k tris) each. → Lane E decimates, Lane D validates, Lane F re-measures. This is the critical path. - Gate 6 (shots): letterbox + poses —
DBG.shot()headless-composer letterbox bug + 3 missing bookmark poses. → Lane B fixes, Lane F re-captures the v1 tour.
Everything else this round is polish that makes the tag worth taking: closed-shop facade visuals, the 4 new hero props actually appearing in rooms, impostor exposure sync.
Integrator decisions (so nobody blocks on them)
- Ped decimation target: ≤3k tris per ped rig. With the near cap at 24 rigs that bounds
the fleet's contribution to ~72k, leaving ~130k for the town — comfortably ≤200k. Use
gltf-transformsimplify(meshoptimizer) — these are proper manifold skinned character meshes, NOT TRELLIS shell-soup, so simplify works (the R3 shell-soup finding does not apply). Preserve skinning weights + the separatewalk.glb/idle.glbanimation clips. - Hero-prop tris stay as-is for v1 (25–77k). They are focal, one-per-room, and gate 3 measures the street. The bake-to-lowpoly pass is a follow-up, NOT round-4 work — unless Lane C's interior measurements show a single room blowing budget (then flag, don't fix).
- fal.ai fallback for glass-case + bus-shelter (~$2.70) stays parked. Do NOT spend it. Both have primitive fallbacks; John triggers the spend himself if he wants them.
- The depot is live and canonical —
validate --depotgreen, publish is done. Lane C: validate GLBs through the normaldepot:path (or?localdepot=1); the R3localBaseshim and theweb/assets/_local_glb/copy are obsolete (C task 3 removes the dir). - v1 tag criteria: all 6 gates green in §4 of LANE_F_NOTES → Lane F tags
v1.0on main. Nothing is pushed anywhere — tagging is local, John pushes.
Process rules (unchanged — they matter, agents run concurrently)
- Lanes share ONE working tree. Never
git add ./git add -A/git commit -a. Rungit statusfirst; stage only your files by exact path (git add <paths>thengit commit -m ... -- <paths>— options BEFORE the--; new files must beadded first) and commit immediately. Touch nothing another lane has modified. - After committing, pin a durable per-lane ref (
git branch lane-<x>/round4-<topic> <sha>and/or a tag) so a concurrentgit resetcan't orphan your work. - Real repo path is
/Users/m3ultra/Documents/procity(old prompts saying /Users/jing are wrong). - Leave
bash tools/qa.sh --strictGREEN before you finish. Update your<X>-progress.md; leave cross-lane notes indocs/LANES/LANE_<X>_NOTES.md, not silent edits to others' files. procityprogress.txtandsettings.local.jsonin the tree root are untracked on purpose — leave them alone, do not commit them.- Dev server:
python3 -m http.server 8130 -d web(may already be up on :8130 — reuse it).
Sequencing (who unblocks whom)
E1 (ped decimate) ──→ D2 (validate rigs) ──→ F3 (gate-3 re-measure) ─┐
B2 (DBG.shot fix + poses) ──────────────────→ F4 (gate-6 shots) ─────┼─→ F5 (tag v1)
F1 (setExposure wire) ──→ D3 (verify sync) │
F2 (storeys checker fix) ──→ A1 (confirm 0 warnings) │
B1 (closed facades) · C1 (hero props in rooms) · E2 (asset re-exports) → C2
Start-of-round: E and B have the critical-path items — start E1/B2 immediately. F1/F2 are small and early. Everything else is parallel.
Lane A (Citygen) — standby + two confirmations
Small round again. Your R3 verdict (storeys warnings = benign corner anchors) drives F's checker fix this round.
- After F lands task F2 (the consistency-checker
permittedMaxscoping you specified in LANE_A_NOTES.md): re-runbash tools/qa.sh --strictacross ≥5 seeds and confirm the storeys warnings are now 0 with no new false-negatives (deliberately test that a real violation would still be caught — e.g. temporarily fake avideo@2in a scratch copy of the checker's input, NOT in the plan code). ConfirmGOLDEN.hashstill0x098eec2b. - openLate contract audit: F's night gate asserts
plan.shops.some(s => s.hours[1] >= 22). Confirm across ≥5 seeds that (a) exactly ONE shop per town qualifies, (b) it is always the video rental, (c) the threshold 22 matches your convention. If any seed violates it, fix in plan.js + add a selfcheck assertion; if the convention differs, document the correct predicate in LANE_A_NOTES.md for F. - Otherwise standby: answer cross-lane questions (LANE_A_NOTES.md is your channel).
Acceptance: qa.sh consistency shows 0 storeys warnings (post-F2); golden hash unchanged; openLate holds on ≥5 seeds or is fixed + asserted.
Lane B (Streetscape) — closed facades, shot harness, furniture verify
- Closed-facade visual (§3.5 render side — the headline item). Predicate is ready:
window.PROCITY.isOpen(shop)+window.PROCITY.currentHour(). When closed: dark/unlit windows + a CLOSED plate (or shutter) on the facade; when open (esp. at night for the oneopenLatevideo shop): lit windows. Also a "CLOSED" door tooltip on aim. Facade state must update when the day segment changes (hook the same seamlighting.jsuses — don't poll per frame). Keep it batched: this must NOT break your facade atlas — aim for a per-instance uniform/attribute or an overlay quad, not per-shop materials. Re-measure worst view ≤300 draws with citizens after. - Shot-harness fixes F requested (gate-6 blocker — do early). In
dbg.js: (a) fix the headless-composer letterbox inDBG.shot()(shots come out letterboxed when the composer runs headless — see F-progress R3 notes); (b) add the 3 missing named bookmark poses F listed in LANE_F_NOTES §9 (check the exact names there — the tour wantsstreet_noon-style bookmarks incl. a night-neon and market pose). Verify by runningpython3 tools/shots.pyand eyeballing output. - Furniture GLB footprint/collision verify (your R3 task 3, now unblocked). F's §3.4
manifest-GLB furniture upgrade is live. Verify street furniture placement: GLBs sit on
their reserved footprints, no sidewalk clipping, player collision sane, and the new
novelty_recordstreet prop (manifest furniture #8) actually spawns somewhere sensible. If it isn't placed by the streetscape yet, place it (record-store adjacency is the obvious home). - Optional polish: fresh beauty stills for docs/shots/ once 1–3 land.
Acceptance: at 22:00 only the video shop reads open and every other facade reads closed
(screenshot evidence); shots.py produces un-letterboxed shots incl. the 3 new poses;
worst-view draws ≤300 with citizens; qa.sh green.
Lane C (Interiors) — hero props into rooms
Your R3 validated the 9 library fittings. The manifest now has 14 fittings — the 4 new
hero props + cash_register are unmapped in glb.js.
- Map + validate the new hero fittings in
web/js/interiors/glb.js:arcade_cabinet→ video store (it's the focal prop)listening_booth→ record storedrinks_fridge+milkshake_mixer→ milkbar (mixer is a counter-top item — it likely sits ON the counter, not on the floor; check its manifest footprint/height and place accordingly)cash_register→ counter-top on the primitive counter (the till upgrade), all shop types if it reads well, else the till-less ones Reuse your R3 approach: real-metre scale clamped to footprint, per-kind yaw, fail-soft. Load via the normaldepot:path or?localdepot=1— all 5 are published and live.
- Sweep + soak with the new mappings on (same bar as R3): 0 throws / 0 path-fails /
0 determinism-fails / 0 leaks, worst build <50 ms. Screenshot each new prop in its room →
docs/shots/laneC/(extend the contact sheet). - Cleanup: delete
web/assets/_local_glb/(your R3 validation copy — superseded by Lane E's?localdepot=1+stage_local_depot.py). It's untracked; justrm -rfit. Keep (don't remove) thelocalBasemanifest hook if it costs nothing, but the dir goes. - Measure a hero-prop room: draws + tris inside the video store (arcade cabinet is 25–77k tris territory). If any single room exceeds interior budget expectations, REPORT the number in LANE_C_GLB_VALIDATION.md for E's future bake pass — do not block or fix.
- Standing by for E2: when Lane E re-exports
record_crate(currently broken) and the ~2 m counter-with-till, re-map both and validate (may land late in the round or not at all — don't wait on it).
Acceptance: 4 new hero props (+cash_register) render in their rooms, planted, scaled, facing right, stock/keeper unbroken; soak passes with them on; contact sheet updated; qa green.
Lane D (Citizens) — in-shell rig verify + decimation validation
- In-shell §3.4 verification (now unblocked — F wired the fleet in R3). In the real game
(
web/index.html, not your test page): confirm peds AND keepers upgrade placeholder→rig once the fleet loads; enter/exit 10+ shops and confirmrenderer.info.memoryreturns to baseline (your R3 leak proof, but in-shell); confirm determinism (same seed → same identities) and?noassets=1still runs fully placeholder with zero fetches. - Validate E's decimated peds (critical path — coordinates with E1). When E stages the
decimated
web/models/peds/*.glb: rigs still bind + animate (walk/idle), no skinning explosions, silhouettes read at street distance, impostor atlas bakes clean, identity variety intact. Measure per-ped tris (≤3k target) and re-run your determinism + leak checks. Report the numbers in LANE_D_NOTES.md; F re-measures gate 3 after your OK. - Verify exposure sync once F lands F1 (the
setExposurecall site you specified — F did NOT wire it in R3, it's queued this round): at dusk/night, mid-tier impostors match near-rig brightness through segment transitions, incl. across a placeholder→rig upgrade. - Chunk-streamed roster stays design-only (your R3 note). No implementation this round.
Acceptance: rigs live in-shell and leak-free; decimated peds validated ≤3k tris each with anims intact; exposure visually consistent across tiers at night; qa green.
Lane E (Assets) — ped decimation (critical path) + asset fixes
- Decimate the 20 ped rigs in
web/models/peds/(THE gate-3 blocker — start immediately). Usegltf-transform(npx)simplify— meshoptimizer works here: these are manifold skinned character meshes, not TRELLIS shell-soup. Requirements:- target ≤3k tris per ped (integrator decision #1); keep textures (WebP/resize if easy wins are there — file size matters less than tris)
- PRESERVE skinning (joints/weights) and do not touch
walk.glb/idle.glb(the shared animation clips) beyond confirming they still retarget - back up originals to a gitignored dir (e.g.
pipeline/.peds_orig/— add to .gitignore) - stage in place (
web/models/peds/), eyeball a contact sheet (no melted faces/hands), then hand to Lane D for rig validation (task D2). Record before/after tris + method in pipeline/AUDIT.md.
- Two asset re-exports for Lane C (from LANE_C_GLB_VALIDATION.md):
procity_fit_record_crate_01.glb— broken (undefinedurireference, won't parse). Re-run normalize from source or regenerate; validate it loads in three.js before handing.- counter: a ~2.0–2.3 m long counter WITH a till (the R3 one is 4 m, till-less, and
squashes). Local pipeline (flux_local → trellis_mac), ≤2 attempts each; if both fail,
add to the fal fallback list and move on.
Publish any new/re-exported GLBs to the depot (the R3 passwordless tailnet path) + re-run
validate_manifest.py --depot→ 0 errors. Note for C in LANE_E notes when live.
- Do NOT: spend the fal.ai budget (decision #3), or start the hero-prop bake-to-lowpoly pass (decision #2) — parked unless C reports a room blowing budget AND Fable re-scopes.
Acceptance: all 20 peds ≤3k tris with skinning/anims intact and D's sign-off; record_crate loads; counter attempt resolved (shipped or documented-failed); depot validate 0 errors; qa green.
Lane F (Integration) — wire-ups, gate closure, tag v1
- Wire Lane D's
setExposurecall site (you didn't in R3 — D's NOTES give the exact one-liner; it belongs right after thecitizens.setTimeOfDay(...)call atweb/index.html:244): pass the shell's animatedrenderer.toneMappingExposureintocitizens.setExposure(...)on segment change. Verify with D (D3). - Apply Lane A's storeys-checker fix in the consistency checker (tools/): scope the
check to
permittedMax = (max >= 2) ? min(max + 1, 3) : maxper LANE_A_NOTES.md, so the intentional 3-storey corner anchors stop warning while real drift still trips. Expect consistency warnings → 0; A confirms (A1). - Gate 3 re-measure after D2 signs off on E1's decimated peds: busiest intersection, clean single-pass, with rig fleet + citizens at busy midday. Need ≤300 draws AND ≤200k tris. Record in the runbook gate table.
- Gate 6 closure after B2: re-run
tools/shots.py→ 10 un-letterboxed beauty shots incl. the 3 new poses →docs/shots/v1_tour/+ contact sheet. Also capture B1's closed-facade night shot for the tour (the 🔒 toast + dark facades sell §3.5). - Tag v1. When the §4 gate table in LANE_F_NOTES is all-green (1,2,4,5 already ✅; 3 and 6
land this round): update the gate table + F-progress, run a final full
bash tools/qa.sh --strict, thengit tag v1.0with a summary message. Do NOT push — John pushes. If a gate won't close, document exactly what's short in F-progress and stop at the documentation (no half-tag). - Ongoing: you own
web/index.htmland tools/ seams — other lanes leave those edits to you (they document call sites in their NOTES instead, as D did).
Acceptance: exposure wired + D-verified; consistency 0 warnings; gates 3 + 6 measured green (or precisely documented why not); v1.0 tagged locally on an all-green qa run.
— Fable (integrator). Questions → leave them in your NOTES file addressed to Fable; I read everything at round end. Commit atomically, pin your refs, leave it green.