diff --git a/docs/LANES/ROUND39_INSTRUCTIONS.md b/docs/LANES/ROUND39_INSTRUCTIONS.md new file mode 100644 index 0000000..99617e5 --- /dev/null +++ b/docs/LANES/ROUND39_INSTRUCTIONS.md @@ -0,0 +1,126 @@ +# PROCITY — Round 39 instructions (from Fable, integrator) + +Date: 2026-07-26 · **R38 closed green — v8 wave 1 shipped and the town is inhabited** (21 palettes, +36 backyards, the magpie, six panning ambient layers; budget held at **292/300, eight draws of +margin**, because the worst frame is a night frame and both new layers are zero at night). + +**JOHN HAS RULED: do the street names.** *"yes do street names for sure and what else can we get on +with? just keep it going."* Round 39 opens **v9 — THE CITY IS THE GAME** with its two zero-draw +keystones, and keeps every other lane busy on work that needs no ruling. + +Charters: [V9_THE_CITY_IS_THE_GAME.md](../V9_THE_CITY_IS_THE_GAME.md) (the hunt; read Fable's +verification header first) · [SALE_RED_TICKET_DAY.md](../SALE_RED_TICKET_DAY.md) (the round and the +Band Floor Law) · [V8_SOMEONE_LIVES_HERE.md](../V8_SOMEONE_LIVES_HERE.md) (in flight; wave 2 later). + +## THE BINDING FACTS (verified by Fable — do not re-derive, do not contradict without numbers) + +1. **17,835 of 19,132 road ways in the shipped caches carry a `name` — 93.2%.** Counted + independently. `plan_osm.js:363` builds each way as `rawWays.push({ kind: roadEdgeKind(rd.kind), + pl })` — it keeps the kind and the geometry and **discards `rd.name`**. Every real street in 23 + Australian towns is in this repo and the game has never said one out loud. +2. **The names are recoverable without touching citygen's output.** Plan nodes ARE snapped + projected way points (`plan_osm.js:361-372`, `SNAP=3`), so plan edges lie ON the named ways: + median distance from a plan-edge midpoint to its nearest named way is **0.03–0.90 m** (worst + town katoomba), and **97.8% of corpus shops (1,192/1,219) resolve to a street name**. +3. **THE INTERIOR MARGIN IS SIX DRAWS, NOT 191.** `index.html:349` ships `useGLB: !NOASSETS`, so + the **default boot is the GLB path**, and `C-progress.md:841` records that sweep at **GLB-on + 344** against the ≤350 cap. Any interior work in any epoch spends against **six**. +4. **Vertical movement is fiction.** `player.js:64` and `interior_mode.js:485` both write `y = EYE` + unconditionally every frame on every path — no gravity, no floor query, no storey state, no + floor height in any data structure. **Nothing this round proposes stairs.** +5. **Only record shops and market stalls have a diggable crate** — `kind:'bin'` is minted at + `fittings.js:60`/`:75` (`recordBin`, `crate`) and used by exactly two recipes (`theme.js:38-39` + record, `:196` stall). The op shop's `crateStack` is labelled *"back-room dressing"* in its own + comment. **You cannot dig in an op shop**, in a game built on op-shop DNA. + +## The ledger + +### 39.1 — THE ADDRESS LAYER *(Lane A — the approved thing, and the prerequisite for everything else)* +Recover the names and publish the vocabulary. A new **pure** module (no THREE, no fetch, no plan +mutation): `createAddresses(plan, cache|null)` → `streetOf(edgeId)` · `localityOf(shopId)` → +`{street, district, block, side, label}` · `cohort(predicate)` → `shopId[]`. +- **Real towns:** street name by nearest named way to the edge midpoint (≤8 m tolerance). +- **Synthetic:** no roads cache and only 22 edges → the label comes from `district.kind` + block: + *"the market end"*, *"the arcade"*, *"the backstreets"*. **Same field, different supplier, one + consumer contract** — this matters, because consumers must not branch on town type. +- The synthesis proposes a **one-line** upgrade: write `norm.shift = {shx, shz}` at + `plan_osm.js:510` into the **caller-supplied `opts.report` sink, never onto the plan object**, so + it moves no golden and breaks no classic byte. **Take it if it is genuinely that clean; measure + and report if it is not.** +- **Goldens must not move.** State it plainly with your selfcheck numbers. If one moves, stop. +- Publish the contract in LANE_A_NOTES §39 in your FIRST commit — B and C both consume it. + +### 39.2 — THE FOG *(Lane B — the keystone, zero draws)* +`minimap.js:52-63` draws **every lot in the plan from the first frame**, ungated: one M keypress +hands over all 493 synthetic shops. Measured cost of earning that yourself: **5,330 m synthetic / +9,622 m fitzroy / 5,797 m northbridge** of walking — **4.8 / 8.7 / 5.3 game-days** at WALK 4.6 m/s +against the 240-second day. +- Gate the map to shops the player has **walked past**; discovery is **player state**, saved under + the delta law (size it: 24 towns × N shops, bounded, and prove export/import + corrupt-reject). +- **Frame the map to what you know.** The payoff is the reason to do it: a shop rectangle goes from + **0.8×1.1 px to 29×39 px on bowral — a 35× legibility gain** (21× katoomba, 17× adelaide, 11× + fitzroy). The map is currently useless on the 21 towns that are supposed to be 21 mental maps. +- **BINDING corrections from the judges:** discovery is a **proximity probe, not + `chunks.onChunkBuilt`** (real towns are all BIG_CITY ⇒ R=2, so the outermost live chunk centre is + 128 m out and its far corner 181 m — chunk-built discovery would hand you shops you never saw). + And classic-gate it **by construction**: `createMinimap(plan)` becomes `createMinimap(plan, game + ? knownProvider : null)`, where a null provider is today's exact behaviour — the same pattern + `ground.js` already uses, so `minimap.js` needs no flag test at all. +- Consume A's `localityOf` for street labels on the map once A publishes. + +### 39.3 — THE OP SHOP CAN'T BE DUG *(Lane C — the game's namesake activity)* +PROCITY is built on thriftgod's op-shop DNA and **an op shop has no diggable bin**. Its recipe is +clothes racks, metal shelves, a bookshelf, a trestle table and a `crateStack` its own comment calls +back-room dressing. Meanwhile the dig — the crown jewel, soaked since v2 — fires only in record +shops and market stalls. +- **Measure first**: what can a player actually DO in each of the 12 shop types today? Enumerate it + honestly (dig / pull-and-buy / nothing) and put the table in your notes. That table is the + deliverable even if nothing else lands. +- **Then the mechanism**: give the op shop a diggable bin (a bric-a-brac tub, a $2 book crate, a + rummage bin — you own the fitting and the recipe). The *mechanism* is uncontroversial and yours. +- **FLAG, do not decide: what is IN it.** Records mixed in? Books and toys through the existing + pull-and-buy pack? Its own item class? That is a John taste call and it is the difference between + "op shops now dig" and "op shops now dig *records*", which changes the game's economy. Propose, + measure the options, and hold. +- **Spend against SIX draws** (fact 3). Say what your bin costs and prove it. + +### 39.4 — TWO MAGPIES, AND THE ARCADE'S ASSETS *(Lane E)* +- **Settle the magpie** (filed from R38): B ships a 182-tri procedural bird with vertex-colour + white; E published an 894-tri tinted GLB that reads properly. **Render them side by side at the + size the bird is actually seen** (64/48/32 px, the same sheet form as your tint render) and + recommend. Fable rules on the picture, not the prose. +- **The arcade** is a real 42 m covered lane with **17 shops** and it is dressed as nothing — no + props, no signage, no roof texture, no reverb. Generate its kit for a later round: a two-dollar- + shop A-frame, a key-cutter sign, hanging shopfront signage, a tiled arcade floor skin. Same laws: + $0, on-device, 🟢, provenance rows, and **size every asset by `tris × the instance count the + consuming lane implies`** — the law your own `paling_fence` bought us. +- Correct `magpie.js`'s two stale tri comments (154 vs measured 182) — **that is Lane B's file, so + write the ask, do not edit it.** + +### 39.5 — THE GATES AND THE CLOSE *(Lane F, last)* +Gate 39.1–39.3 with falsifiability controls; full `qa.sh --strict --matrix`; round close. **No tag.** +Specifically: prove the address layer resolves ≥97% of corpus shops and **fails loudly** on a town +with no roads cache; prove the fog hides what has not been walked and **reveals what has** (both +arms, or it is vacuous); prove classic and `?noassets` untouched; and re-measure the interior +budget against fact 3 if C's bin lands. + +## Laws + +Everything standing. This round: **zero draws is the requirement for 39.1 and 39.2** — they are +information design and must cost nothing on the street · the interior margin is **six** · +pathspec-atomic commits (four lanes share the tree) · no pushes, no tags — Fable pushes at close · +every gate ships its control demonstrated. + +⚠️ **Tree note:** a background session may still be editing `web/js/world/venue.js`. No lane touches +it this round. + +## Order + +1. **Parallel: A (39.1, contract in the first commit) · B (39.2) · C (39.3) · E (39.4).** +2. **Then F (39.5).** + +## Standing — for John, when he wants them + +The op-shop bin's **contents** (39.3) · whether the fog is default-on or a mode · and the v9 shape +itself: the address layer and the fog are its first two layers, and the third (the unmarked door / +semi-hidden retail) waits on how these two feel.