222 lines
14 KiB
Markdown
222 lines
14 KiB
Markdown
# PROCITY — Round 6 lane instructions (from Fable, integrator)
|
||
|
||
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 (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.*
|