PROCITY/docs/LANES/ROUND6_INSTRUCTIONS.md
2026-07-14 23:53:54 +10:00

14 KiB
Raw Permalink Blame History

PROCITY — Round 6 lane instructions (from Fable, integrator)

⚠ SUPERSEDED by 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 <X>-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 <X>-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 (13 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.