206 lines
15 KiB
Markdown
206 lines
15 KiB
Markdown
# 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.
|
||
|
||
## ROUND CLOSED (Fable) — **v9 IS OPEN. 10 passed · 0 failed · 0 warn · 0 skipped. No tag.**
|
||
|
||
Selfcheck **157,647/157,647**, fingerprint `0x5f76e76`, **zero goldens moved**, MATRIX GREEN 10
|
||
towns × 7 gates. Three new gates added coverage without moving a single existing verdict.
|
||
|
||
**THE TOWN KNOWS ITS OWN STREETS.** 1,192 of 1,219 corpus shops resolve to a real Australian
|
||
street name — 97.8%, 100% on 17 of 23 towns — and **zero wrong names**, validated against a
|
||
second independent resolver (857 shop-bearing edges, 0 disagree). Katoomba's map now says
|
||
*Katoomba Street*. `map.html` had never rendered a real street in four epochs of holding the data.
|
||
|
||
**THE MAP IS A MAP.** The fog costs **+0 draws AND +0 tris** (four fresh browsers, both orders,
|
||
arms proven distinguishable first) and framing to what you know takes a shop from **0.83 × 1.11 px
|
||
to 29.4 × 39.2 — 35× on bowral**. B's sharper measurement: an exact-palette count found **26,506
|
||
shop-coloured pixels on the synthetic and ZERO on Katoomba** — all 72 shops rasterised to not one
|
||
pixel of their own colour. The map wasn't hard to read on real towns; it was drawing nothing.
|
||
|
||
**THE FOURTH AND FIFTH INSTANCES OF THE SAME BUG.** `(−sin,−cos)` at `index.html:384`/`:450`:
|
||
**not one door point in the entire corpus was on a footpath** (0/493, 0/72, 0/139, 0/30) — Lane D's
|
||
peds have been ducking into the back fence since R8 — and the spawn put **0 shops in front of the
|
||
player on 4 of 4 towns**, which is precisely the "faces dirt and building-backs" complaint from
|
||
Fable's own R31 playtest, finally explained. Two characters; 0 → 4 on all four towns.
|
||
**B wrote the lineage down (R13 `RY_FLIP` · R15 posters · R27 facades · R39 these two) with the
|
||
keeper insight: *every instance was caught by a picture or by a consumer with a different
|
||
convention, never by a metric — a heading has no falsifiable arm inside the file that computes
|
||
it.*** And then the table paid for itself **inside the same round**: wiring E's magpie, B pointed a
|
||
measurement at the heading *because the lineage said to*, and found instance five — E's bird faces
|
||
+Z, the file flies −Z, **the magpie would have swooped tail-first**. First one ever caught before
|
||
shipping.
|
||
|
||
**THREE GLBs WERE SHIPPING AS GLASS.** `transmissionFactor: 1` forces three.js's transmission
|
||
pre-pass — **every opaque draw issued twice**. E scanned all 103 GLBs: `bookshelf` (shipped and
|
||
hurting), plus `longbench` and `streetlight` — **unwired STREET assets E had itself recommended to
|
||
B**, where a pre-pass would have cost ~292 draws against eight of margin. Armed, not fired. Fixed
|
||
at source with BIN sha1 identical, republished, sha1-verified off the public depot, **and gated**:
|
||
`validate_manifest.py` now hard-fails on it.
|
||
|
||
**THE INTERIOR MARGIN'S THIRD VALUE — and the epoch's cleanest lesson in three numbers:**
|
||
**6** (a stale doc I quoted) → **21** (a correct measurement of a tree about to change) → **162**
|
||
(the tree as it now stands). F re-measured through a true A/B — the shipped GLB vs a copy with the
|
||
glass re-inserted, BIN identical — and got worst-room **329 → 188**: `opshop` −47%, `book` −49%,
|
||
every other type 0. **The worst interior room is now `dept/auto` — a room with no bookshelf in it.**
|
||
E's fix took, and it bought **+141 draws**.
|
||
|
||
**THE OP SHOP CAN BE DUG** — `rummageBin`, 72/72 placed and reachable, 0 carves, rest-of-room
|
||
byte-identical, **contents deliberately unarmed** (default boot: `kind:'prop'`, 0 dig targets) and
|
||
held for John. Alongside it, C's table of what a player can actually do in each of 12 shop types —
|
||
including that **the default boot contains not one buyable mesh anywhere**, and that
|
||
`interactable:true` is not a verb.
|
||
|
||
**Two self-caught errors worth keeping.** F aimed its transmission control at the plausible path
|
||
(`web/assets/models/`) rather than the code path (`pipeline/_normalized/`) and **nearly filed a
|
||
defect against Lane E on the strength of it** — *aim the control at the code path, not the
|
||
plausible path*. And B, ordered to fix its own fallback magpie, made the change, measured it, found
|
||
**true white went 33 → 28 px** (a downward-facing surface catches no sun, so the white it moved
|
||
into view reads grey against E's 755) and reported it as a half-win **that strengthens the ruling
|
||
against its own bird**.
|
||
|
||
**F's judgement call, ratified:** it re-shot the spawn gates but **refused to overwrite
|
||
`v7_tour/` and `v7_beta/`** — *re-shooting a tagged epoch's release tour on a v9 tree falsifies a
|
||
record.* They stay as history.
|
||
|
||
**Filed to R40 (gate gap, not a live defect):** `check_transmission()` skips any GLB with no local
|
||
`pipeline/_normalized/` copy and never scans `web/assets/models/` — demonstrated live, the glass
|
||
asset **passes rc 0** with the local copy removed. **A depot-only transmissive GLB — exactly the
|
||
shape of `longbench`/`streetlight` — would ship unseen.** Also carried: A's D1–D4 · the matrix
|
||
budget column is bookmark numbers · `?r=3` breaches at 307 ungated · the arcade's awning posts
|
||
stand two rows 1.00 m apart down a 5.00 m lane and should be designed with the roof · should
|
||
`?classic=1` show the town selector · **nobody in this loop has ears.**
|
||
|
||
**Street budget: 292/300 stands** — both new layers are zero-draw and measured at zero, the spawn
|
||
is a pose, the bin is interior.
|
||
|
||
## 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.
|