251 lines
16 KiB
Markdown
251 lines
16 KiB
Markdown
# PROCITY — Round 5 lane instructions (from Fable, integrator)
|
||
|
||
> **⚠ SUPERSEDED by [ROUND6_INSTRUCTIONS.md](ROUND6_INSTRUCTIONS.md) (2026-07-14).** Round 5 is
|
||
> complete — v1.1 tagged (`3a831fe`); B/C/D/E/F delivered, Lane A's task carried to round 6.
|
||
> Historical reference only — EXCEPT §Lane A, which round 6 carries verbatim.
|
||
|
||
Date: 2026-07-14 · Written after **v1.0 tagged** (`2698b40`, all 6 QA gates green, 6-auditor
|
||
GO). 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`, `docs/V2_IDEAS.md`, and any
|
||
NOTES addressed to you before touching code.
|
||
|
||
Repo state: everything committed on `main` at `2698b40` = `v1.0`. `tools/qa.sh --strict`
|
||
GREEN 4/4 (selfcheck 1301/1301, fingerprint `0x3fa36874`, consistency 0 warnings, manifest
|
||
`--depot` 0 errors, 23 GLBs live). Tag is local; pushing is John's.
|
||
|
||
## Theme: v1.1 close-out + v2 foundations
|
||
|
||
v1 shipped: seed → walkable 90s-AU town, every door opens, keepers, crowds, hours, day/night.
|
||
Round 5 does two things at once:
|
||
|
||
1. **v1.1 (small, this week's polish):** land the ONE v1 loose end — Lane C re-maps the two
|
||
assets Lane E shipped late in R4 (`record_crate` fixed, `counter_till` new) — plus the
|
||
hero-prop lowpoly bake. F tags `v1.1`.
|
||
2. **v2 foundations (the real round):** each lane starts its highest-leverage V2_IDEAS item,
|
||
**behind a flag, default-off**. The parking-lot doc was written so v2 is wiring, not
|
||
rework — this round proves that.
|
||
|
||
## The v2 prime law (read twice)
|
||
|
||
**Default boot must stay v1-identical.** Every v2 feature lands behind a query flag or config
|
||
default-off. `GOLDEN.hash` (`0x3fa36874`) must not move unless Lane A intentionally versions
|
||
it (it should not this round). `qa.sh --strict` green at every commit. If your feature is on
|
||
and something breaks, that's your bug; if your feature is OFF and anything changes, that's a
|
||
land-blocker.
|
||
|
||
## Integrator decisions (so nobody blocks)
|
||
|
||
1. **v2 starts with the content firehose** (V2_IDEAS #1): Lane C ports `dig.js` (the
|
||
crate-riffle interaction) running on the EXISTING procedural stock via the `stockAdapter`
|
||
seam. Real GODVERSE/BaseGod stock + economy is round 6+ — do NOT wire external databases
|
||
or thriftgod's `mint()` this round.
|
||
2. **Hero-prop bake-to-lowpoly is UN-PARKED** (was decision R4#2). Lane C's measurements
|
||
(record interior ~273k tris; `drinks_fridge` 77k, `counter_till` 32k) justify it. Lane E
|
||
bakes: target **≤8k tris per hero prop** with baked base-colour (bake THEN simplify —
|
||
R3 taught us raw TRELLIS shell-soup won't simplify; a voxel-remesh + texture re-bake will).
|
||
3. **OSM plan import uses cached/fixture data ONLY.** Lane A builds the second plan source
|
||
against thriftgod's `city_source.json` cache (or a checked-in fixture extract). NO live
|
||
Overpass/network calls from the repo — the asset-free/zero-network law extends to citygen.
|
||
4. **fal.ai spend is AUTHORIZED (John, 2026-07-14: account topped up to $22).** Lane E runs
|
||
the fallback gens — glass-case + bus-shelter, plus one optional magazine-rack attempt —
|
||
at Hunyuan v2 ~14–17¢/gen, ≤2 attempts each (expected total well under $1). Nothing else
|
||
on fal without a per-item reason logged in AUDIT.md. Pushing stays John's. Audio stays
|
||
greenfield-parked (needs an asset-sourcing decision from John).
|
||
5. **v1.1 tag**: Lane F tags after C1 (re-map) and E1 (bake) land and the tour still looks
|
||
right. v2 flags do NOT need to be done for v1.1.
|
||
|
||
## Process rules (unchanged — agents run concurrently)
|
||
|
||
- ONE shared working tree. `git status` first; stage **only your files by exact path**;
|
||
options before `--` in `git commit`; `add` new files first; commit immediately; pin a
|
||
durable ref (`git branch lane-<x>/round5-<topic>` / tag).
|
||
- Leave `qa.sh --strict` GREEN. Update `<X>-progress.md`; cross-lane asks go in your
|
||
NOTES file, not in others' code. F owns `web/index.html` + `tools/` seams — document your
|
||
call sites for F instead of editing (the D→F setExposure pattern from R3/R4).
|
||
- `procityprogress.txt` / `settings.local.json` stay untracked; don't commit them.
|
||
- Real repo path `/Users/m3ultra/Documents/procity`. Source repos for ports:
|
||
`~/Documents/90sDJsim` (dig.js), `~/Documents/thriftgod` (city_source/plan import),
|
||
`~/Documents/dealgod/GODVERSE.md` (read-only reference this round).
|
||
|
||
## Sequencing
|
||
|
||
```
|
||
C1 (re-map counter_till + record_crate) ─┐
|
||
E1 (hero-prop bake ≤8k) ──→ C verifies ─┼─→ F1 (tag v1.1)
|
||
│
|
||
A2 (plan-source seam) · B2 (window shader) · C2 (dig.js) · D2 (streamed roster) · E2 (depot grow)
|
||
└────────── all behind flags ─────────→ F2 (v2 smoke + runbook)
|
||
```
|
||
C1 is quick — do it first so F can tag v1.1 early. All v2 items are parallel and independent.
|
||
|
||
---
|
||
|
||
## Lane A (Citygen) — the second plan source (OSM import seam)
|
||
|
||
V2_IDEAS: *"Real-OSM plan import (thriftgod `build_city()` route)"* — `generatePlan(seed)`
|
||
becomes one of two plan sources behind the same CityPlan contract, so B–F never change.
|
||
|
||
1. **Extract the contract.** Define/verify a single `CityPlan` producer interface (whatever
|
||
`generatePlan` returns IS the contract — document its exact shape in CITY_SPEC if not
|
||
already precise: nodes/edges/blocks/lots/shops/registry fields, hours, openLate, seeds).
|
||
Add a `planSource` selector: default `'synthetic'` (today's path, byte-identical output —
|
||
golden hash law), new `'osm'` behind `?plansrc=osm`.
|
||
2. **Build the OSM source.** Port the relevant parts of thriftgod `server.py:433 build_city()`:
|
||
read a **checked-in fixture** extracted from thriftgod's `city_source.json` cache (pick one
|
||
real AU town's worth of shops, a few hundred max — commit the fixture under
|
||
`web/assets/fixtures/` or `tools/fixtures/`), then dedupe → suburb inference → parody
|
||
names → seeded hours → emit a valid CityPlan. Deterministic: same fixture + seed → same
|
||
plan (hash it).
|
||
3. **Prove it downstream.** Boot the shell with `?plansrc=osm`: streets stream, doors open,
|
||
interiors build, selfcheck-style invariants hold (write an osm-mode selfcheck variant or
|
||
parameterize `selfcheck.js` by source — do NOT touch the synthetic golden hash). Screenshot
|
||
the imported town for docs.
|
||
4. Document the seam in LANE_A_NOTES for F's determinism-gate extension (F2).
|
||
|
||
**Acceptance**: default boot byte-identical (golden `0x3fa36874` untouched); `?plansrc=osm`
|
||
boots a real-data town end-to-end with invariants green; fixture committed, zero network;
|
||
qa green.
|
||
|
||
## Lane B (Streetscape) — the Vuntra window trick (interior-mapping shader)
|
||
|
||
V2_IDEAS: *"parallax interior-mapping shader so a passing pedestrian sees a plausible room
|
||
through the glass without building it."* You own the facade glass + atlas — this is yours.
|
||
|
||
1. **Interior-mapping shader** on shop window glass, behind `?winmap=1`: classic
|
||
single-cube parallax interior mapping (seeded per-shop room: wall/floor colours from the
|
||
shop's skin palette, a couple of shelf silhouettes). Must respect §3.5: **closed shops at
|
||
night show dark rooms** (reuse `window.PROCITY.isOpen`); open shops glow warm.
|
||
2. **Keep it batched.** One shader/material on the existing glass geometry (or a clone at the
|
||
same draw count) — per-instance variation via attributes/uniforms, NOT per-shop materials.
|
||
Re-measure worst continuous-walk view with citizens: **≤300 draws** flag-on.
|
||
3. **Determinism + fallback**: seeded from `shop.seed`; flag-off path byte-identical to v1
|
||
(same materials, same draws). `?noassets=1` + winmap must not fetch anything (procedural
|
||
only).
|
||
4. Screenshot day + night (open vs closed) → `docs/shots/laneB/` for the v2 gallery.
|
||
|
||
**Acceptance**: flag-off identical to v1; flag-on ≤300 draws worst view, rooms visible
|
||
through glass with parallax, closed shops dark at night; zero fetches; qa green.
|
||
|
||
## Lane C (Interiors) — v1.1 re-map, then the dig
|
||
|
||
1. **v1.1 re-map (DO FIRST — F tags v1.1 on it).** Lane E's R4 late shipment
|
||
(LANE_E_NOTES → Lane C):
|
||
- `record_crate` is FIXED (sourceless-texture slots stripped; verified loading in the
|
||
vendored GLTFLoader). Your `crate`/`recordBin` mapping already points at it — remove the
|
||
"fails to load" fail-soft comment, verify the bins actually upgrade now.
|
||
- `counter_till` is NEW (`procity_fit_counter_till_01.glb`, 1.61×0.64 m, 1.15 m tall,
|
||
base-origin): **re-add `counter` → `'counter_till'`** to `KIND_TO_GLB` (NOT the old 4 m
|
||
`counter`/balcao). Decide what happens to your separate `cash_register` counter-top
|
||
attach on counters that now have a modelled till — avoid double tills (keep
|
||
cash_register for shop types where it reads better, or drop the attach when the GLB
|
||
counter is live; your call, document it).
|
||
- Keeper stand pose must still work on the GLB counter (the counter contract).
|
||
- Re-run sweep + soak (same bar: 0 throws/leaks, <50 ms), update the contact sheet,
|
||
commit → tell F (v1.1).
|
||
2. **Port `dig.js` — the crate riffle (v2 headline, behind `?dig=1`).** Source:
|
||
`~/Documents/90sDJsim/web/world/dig.js` (self-contained, `onBuy/getCash/spawnClerk/
|
||
spawnFittings` callback API, ported across 3 games before). Scope for this round:
|
||
- Walk to a record bin in a record store → riffle interaction (flip through sleeves) using
|
||
the EXISTING procedural stock via your `stockAdapter(shop, slotKind)` seam — the sleeves
|
||
you already generate become the riffled items. Seeded order per bin.
|
||
- Buy/economy: STUB it (a toast + remove-from-bin is fine; `getCash` returns a constant).
|
||
Real stock/economy is round 6+ (integrator decision #1).
|
||
- Keep it interior-mode-only and leak-free: entering/leaving the dig disposes cleanly
|
||
(extend your soak to enter a dig per interior when the flag is on).
|
||
- F owns the input/mode seam in the shell — document the exact hook you need
|
||
(e.g. `interiorMode.onInteract(fitting)`) in LANE_C notes; F wires it (F2). Build against
|
||
your test page in the meantime.
|
||
3. Re-measure the record interior after E1's baked props land (was ~273k tris) — record the
|
||
new number in LANE_C_GLB_VALIDATION.md.
|
||
|
||
**Acceptance**: C1 committed early, bins + counter GLB-live, no double till, soak green →
|
||
F tags v1.1; `?dig=1` riffles a real bin on procedural stock, leak-free, flag-off identical;
|
||
qa green.
|
||
|
||
## Lane D (Citizens) — implement the chunk-streamed roster
|
||
|
||
Your R3 design note (LANE_D_NOTES) was design-only by instruction. v2 un-parks it: implement,
|
||
behind `?roster=stream`.
|
||
|
||
1. **Implement per your own design**: chunk-local identity keying (same seed + chunk → same
|
||
residents), windowed spawn/despawn driven by Lane B's `ctx.onChunkBuilt/onChunkDisposed`
|
||
hooks (they exist — B shipped them in R2), global near-cap across live chunks, ownership
|
||
hand-off, and the v1 path untouched when the flag is off.
|
||
2. **Prove the wins**: (a) street density stays constant as you walk arbitrarily far from
|
||
spawn (v1's fixed roster thins out — show a before/after count at distance);
|
||
(b) determinism — same seed, same walk → same identities encountered; (c) leak-free over
|
||
≥30 chunk builds/disposes (renderer.info back to baseline); (d) budget — worst view with
|
||
the streamed roster ≤ v1's numbers (impostor/near-cap machinery unchanged).
|
||
3. **Hours-aware density (small, if time allows)**: streamed spawns read the day fraction the
|
||
way v1 does; optionally bias spawn weights toward open shops at night (the queue/patronage
|
||
sim stays v2-later — don't build schedules this round).
|
||
4. Document the flag + any shell call-site change for F in LANE_D_NOTES (F wires index.html).
|
||
|
||
**Acceptance**: flag-off identical to v1 (determinism + golden identities); flag-on constant
|
||
far-field density, deterministic, leak-free, within budget; design note updated to as-built;
|
||
qa green.
|
||
|
||
## Lane E (Assets) — the bake pass + depot growth
|
||
|
||
1. **Hero-prop bake-to-lowpoly (un-parked, DO FIRST — C re-measures on it).** The 6 heavy
|
||
TRELLIS props (`drinks_fridge` 77k, `milkshake_mixer`, `arcade_cabinet` 25k,
|
||
`listening_booth`, `novelty_record`, `counter_till` 32k): voxel-remesh (or manifold
|
||
retopo) → simplify to **≤8k tris** → re-bake base-colour (+normal if cheap) from the
|
||
original onto the new UVs. R3's lesson stands: simplify-only won't work on shell-soup;
|
||
the re-bake is the point. Pipeline it (`pipeline/bake_lowpoly.py` or a Blender headless
|
||
script) so it's repeatable; keep originals in a gitignored dir; record before/after in
|
||
AUDIT.md. Eyeball every prop (no melted labels/faces) → contact sheet. Re-publish to the
|
||
depot; `validate --depot` 0 errors. Tell C to re-measure.
|
||
2. **Depot growth for v2 (second priority).** From `~/Documents/3D=models/` pick a shortlist
|
||
(~8–12) that serves this round's v2 features — dig-scene props (crate variants, price-gun,
|
||
listening-station headphones), street-life props for D's roster (shopping bags, prams),
|
||
B's window-room silhouettes if any read better as real minis. Normalize to GLB law
|
||
(metres, +Y up, base origin, ≤5k where they're not focal), thumbs, manifest, publish,
|
||
AUDIT provenance. Do NOT add manifest entries no lane consumes — check the other lanes'
|
||
round-5 notes or ask in yours.
|
||
3. **fal.ai fallback run (NOW AUTHORIZED — decision #4).** Generate `glass-case` and
|
||
`bus-shelter` via fal Hunyuan v2 (the two TRELLIS glass rejects), plus ONE optional
|
||
magazine-rack attempt if the first two go well. ≤2 attempts per prop; expected total
|
||
<$1 of the $22 balance. Then the normal funnel: normalize (bake-to-lowpoly if heavy per
|
||
task 1) → eyeball thumbnails (no melted glass/legible branding) → publish → manifest.
|
||
Log every paid gen + cost in AUDIT.md provenance. If a prop fails both attempts, it keeps
|
||
its primitive — do not burn extra attempts.
|
||
4. **Not doing** (still parked): MESHGOD_BATCH re-runs beyond the above, audio assets.
|
||
|
||
**Acceptance**: 6 hero props ≤8k tris with baked textures live on the depot, visually clean,
|
||
C's interior re-measure confirms the drop; glass-case/bus-shelter resolved (shipped or
|
||
documented-failed at ≤2 attempts) with costs logged; new shortlist normalized + published
|
||
with consumers identified; validate --depot 0 errors; qa green.
|
||
|
||
## Lane F (Integration) — v1.1 tag, v2 seams, gates that grow
|
||
|
||
1. **Tag v1.1** once C1 + E1 land: verify the record/milkbar/opshop interiors live (GLB
|
||
counter + till story correct, bins upgraded, baked props look right), re-capture ONLY the
|
||
two interior tour shots if visibly improved, qa green → `git tag v1.1` (local; John
|
||
pushes).
|
||
2. **Wire the v2 flag seams** as lanes document them (you own `web/index.html` + `tools/`):
|
||
A's `?plansrc=`, B's `?winmap=`, C's `?dig=1` interact hook (this one is real work — the
|
||
interior-mode input seam; C specifies, you own the mode state machine), D's
|
||
`?roster=stream`. Keep every flag default-off and independent.
|
||
3. **Extend the QA harness for v2** (V2_IDEAS "determinism across engines"):
|
||
- determinism gate: pin plan-hash + first-interior-hash per (seed, plansrc) — synthetic
|
||
stays `0x3fa36874`; add the osm fixture hash when A lands.
|
||
- a **flags-off regression check**: boot with no flags, assert plan hash + a settled
|
||
draws/tris snapshot within tolerance of v1 numbers (catches "my flag leaks when off").
|
||
- a v2 smoke: boot each flag on, walk 2 chunks, enter 1 interior (+1 dig if C landed),
|
||
0 console errors. Wire into qa.sh as a non-strict (warning) gate this round; strict
|
||
next round.
|
||
4. **Runbook**: add a §v2 flags table (flag → owner → state → smoke result) to LANE_F_NOTES;
|
||
archive the R3/R4 instruction files' "current" claims (one line at top: superseded by R5).
|
||
5. Ongoing: shared-tree marshal — if two lanes collide on a file, you arbitrate.
|
||
|
||
**Acceptance**: v1.1 tagged on a green tree; all landed v2 flags wired, independent,
|
||
default-off proven by the flags-off regression check; v2 smoke running in qa.sh (warn-level);
|
||
runbook current.
|
||
|
||
---
|
||
|
||
*— Fable (integrator). The four hinges (stockAdapter, plan-source contract, shop.seed law,
|
||
B's mode machine) are now load-bearing — if your task fights one of them, stop and leave me a
|
||
note instead of bending it. Commit atomically, pin your refs, leave it green.*
|