# 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.**~~ **CORRECTED IN-ROUND BY LANE C — the margin was 31, and is 21 after C's bin.** The direction was right (`index.html:349` ships `useGLB: !NOASSETS`, so the **default boot IS the GLB path** — that part stands and matters). The *number* was stale: `C-progress.md:841`'s "GLB-on 344" is **R9-era**. C measured today's tree before touching anything: **GLB-off 156 · GLB-on 319**, both `opshop/hall`. **Fable's error, owned:** I verified the claim's provenance (the doc does say 344) and not the measurement (the tree says 319). *A document is not a tree.* The house rule stands and I broke it: measure the thing, not the note about the thing. **And the real finding is bigger than the correction** — see the transmission entry in the close. 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.