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

225 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
`<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.*