FIVE GATES, FIVE CONTROLS, each demonstrated in the same run. New F-owned tools wired into qa.sh as Gate 8: r39_address.mjs · r39_transmission.py · r39_runtime.py · r39_shots.py (--no-r39 skips the browser half). Lane A's selfcheck reproduces exactly: 157,647/157,647 ALL GREEN, fingerprint 0x5f76e76 — ZERO GOLDENS MOVED, in a round that added an address layer, a fog ledger, a spawn move and a new interior fitting. THE ROUND'S BIGGEST NUMBER IS NOT THE ONE THE LEDGER ASKED FOR. Two port-isolated no-store roots, the same inode for every byte except one file — the shipped bookshelf GLB vs a copy with transmissionFactor:1 re-inserted (BIN sha1 IDENTICAL, only the JSON chunk moved) — both through interior_test.html's own drawSweep: GLB-on worst 329 (opshop/hall) -> 188 (dept/auto) -141 GLB-off worst 162 (opshop/hall) -> 162 (opshop/hall) 0 — the control's own control margin vs <=350 21 -> 162 +141 opshop 329->174 (-47%) · book 247->125 (-49%) · every other type 0, exactly the shape Lane C predicted from an isolated room, at sweep scale, by a harness C did not write. The worst interior room in the game is now dept/auto — a room with no bookshelf in it. The default boot fetches these GLBs from the public depot, so all four R39 GLBs were fetched off digalot.fyi and scanned: sha1 identical to the local tree, 0 transmissive materials, 4/4. Binding fact #3's third and final value. 39.1 ADDRESS — GREEN. 1,192/1,219 shops (97.8%), 23/23 'ways', 857/857 membership agree / 0 wrong (and the control is asserted to have CHECKED 857 edges). No cache -> supplier 'district', 0 names, still labels 72/72 from district+block. RED ARM: a 3 m shift error RAISES adelaide 89 -> 98 resolved shops (Lane A's number to the digit) while INVENTING 9 names; stats().shiftCheck reads DISAGREE on all 72 perturbations. And the honest limit: at 3 m the membership control reads 0 disagreements, because those shops front ways OSM leaves unnamed — only the shift cross-derivation catches it. 39.2 FOG — GREEN, both arms on one boot. HIDES 4/72 at spawn, 0 of 61 distant shops known. REVEALS 2,125 m -> 58 shops. FRONT TEST: probe from 5 m BEHIND each of 59 unknown shopfronts, 0 learned. +0 DRAWS AND +0 TRIS, four freshly launched browsers, both orders, drained, 3 reads each, with the arms asserted distinguishable first. 8/8 corrupt saves rejected, state untouched. Null store on classic/game=0/fog=0; game=0 and fog=0 pixel-identical, default differs. 39.3 THE SIGN — GREEN. Shops in front of the player at spawn: 4/4 · 4/4 · 4/4 · 1/1, and ZERO on all four with the pre-R39 sign re-derived on the same tree. The live spawn is asserted equal to the +sin derivation, so the shell is what is scored. Re-baselined WITHOUT falsifying a release record: the v7 tour and v7-beta frames are kept as history; docs/shots/laneF_r39/ ships the new pair with its own BEFORE. The R35 gate itself is pose-independent and was green either side. 39.4 RUMMAGE BIN — GREEN. 120 rooms: tub 60/60 both arms, kind:'bin' 0/60 default vs 60/60 armed, 0 carves, 60/60 deterministic, reachable 0.80-1.03 m by a flood fill written here. THE CONTENTS DID NOT SHIP: on the default street boot the tub is kind:'prop', 0 dig targets, E opens nothing. 39.5 TRANSMISSION VALIDATOR — GREEN and it FIRES: hard-fails rc 1 on the fabricated glass asset AND passes on the shipped tree. THREE CORRECTIONS, ALL MEASURED. A's "a 30 m shift returns 0 street names" — it returns 40/12/47 wrong ones; a big shift lands you on the next street, it does not degrade to null. B's "a fresh export has no `known` key" — true of ?fog=0, no longer of a default boot, because B's own +sin fix means you spawn in front of four shopfronts. And my own: I first aimed the transmission control at web/assets/models/ when validate_manifest.py reads 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. HELD -> LANE E: check_transmission() skips any GLB with no local _normalized copy and never scans web/assets/models/. Demonstrated with a live control — remove the local copy and the same glass asset passes rc 0. A depot-only transmissive GLB (the exact shape of longbench and streetlight) would ship unseen. No tag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3002 lines
223 KiB
Markdown
3002 lines
223 KiB
Markdown
# LANE F — NOTES (integration runbook + QA gates)
|
||
|
||
*Lane F is integration + QA. It runs **after** A–E land. This file is the durable technical spec:
|
||
the exact wiring edits, the contracts F needs from each lane, and the QA gate procedures. It is
|
||
written **before** A–E finished so the wiring is a fast, mechanical pass the moment they do —
|
||
and so each lane can see the small hook F will ask of it and build it in up front.*
|
||
|
||
Status of this file: **living**. Update the ✅/⏳ markers as wiring lands. Snapshot of lane
|
||
readiness lives in [`F-progress.md`](../../F-progress.md); run `node tools/qa/scaffold_check.mjs`
|
||
for the live matrix.
|
||
|
||
> **v2.0 FROZEN (round 9).** Nine rounds: seed → walkable town → weather, a shopping crowd,
|
||
> follow-a-ped-into-a-shop presence, and real stock you can buy. Current gate reality + the
|
||
> final v2 flags table live in [`F-progress.md`](../../F-progress.md); `qa.sh --strict` GREEN 5/5
|
||
> (scaffold+determinism · consistency · citygen selfcheck · manifest --depot · v2 flags harness STRICT).
|
||
> CityPlan v2 contract frozen by Lane A in CITY_SPEC; validated live by selfcheck (1727/1727) + goldens.
|
||
|
||
---
|
||
|
||
## 0. Ground truth / environment
|
||
|
||
- Repo root on this machine: `/Users/m3ultra/Documents/procity` (the lane prompts say
|
||
`/Users/jing/Documents/PROCITY` — that path is from the authoring machine; **use the real path
|
||
above**). Reference repos are cloned alongside: `../90sDJsim`, `../thriftgod`. Ultra box
|
||
(existing 3D library / Blender / clip bank): `ssh johnking@100.91.239.7`.
|
||
- Serve: `cd web && python3 -m http.server 8130`. Node 26, Python 3.14 present.
|
||
- `web/package.json` has `"type":"module"` (Lane A) so `node web/js/**/*.js` runs as ESM.
|
||
- **F never edits another lane's internals.** F adds *wiring* — new files it owns, and small,
|
||
clearly-marked hook edits at the seams. Where a hook must live inside a lane's file, F first
|
||
looks for the hook that lane already exposed (see contracts below); the lane owns its file, F
|
||
owns the glue.
|
||
|
||
## 1. Files Lane F owns / creates
|
||
|
||
| path | purpose |
|
||
|---|---|
|
||
| `tools/qa.sh` | the QA gate runner (scaffold + citygen selfcheck + manifest validator) ✅ |
|
||
| `tools/qa/scaffold_check.mjs` | scaffold + PRNG determinism law + **live lane-readiness matrix** ✅ |
|
||
| `tools/shots.py` | Playwright tour-capture → `docs/shots/v1_tour/` + contact sheet (ported from 90sDJsim `tools/shots.py`) |
|
||
| `tools/soak.py` | Playwright 10-minute scripted-walk soak (chunk/heap/leak/error gate) |
|
||
| `web/js/citygen/index.js` | barrel so B's `import('./js/citygen/index.js')` resolves to A's `plan.js` ✅ (§3.1) |
|
||
| `web/js/world/interior_mode.js` | **interior bridge** — B's shell ↔ C's `buildInterior`; owns the `interior` mode branch ✅ (§3.2) |
|
||
| `docs/V2_IDEAS.md` | parked-scope catalogue ✅ |
|
||
| `docs/shots/` | reference shot tree (`laneA…E/`, `v1_tour/`, `before/`, `references/`) |
|
||
| `F-progress.md` | status report for fable |
|
||
|
||
The wiring is kept as **focused F-owned modules** (e.g. `interior_mode.js`) that the shell imports,
|
||
rather than logic sprinkled through B's files. Each seam edit inside `index.html` is one line,
|
||
commented `[Lane F integration]`, and reversible. That keeps ownership clean: B owns the shell +
|
||
mode machine, C/D own their libraries, F owns the glue modules and the marked seams that call them.
|
||
|
||
---
|
||
|
||
## 2. Contracts F depends on (verify these as lanes land)
|
||
|
||
Pulled from CITY_SPEC + each lane prompt. The scaffold check's readiness matrix tracks presence;
|
||
these are the **shapes** F wires against. If a lane ships a different shape, fix it here and in
|
||
the wiring, not by guessing.
|
||
|
||
> **⚠ Corrected against the REAL landed code (2026-07-14).** The bullets below are what the lanes
|
||
> actually shipped — several differ from the spec-derived guesses in an earlier draft (verified by
|
||
> reading each file + the Lane F self-audit workflow).
|
||
|
||
**Lane A — `web/js/citygen/plan.js`** *(landed, verified)*
|
||
- `generatePlan(citySeed) → CityPlan` (schema in CITY_SPEC §Layer 1). <3ms, deterministic, JSON-clean.
|
||
- `chunkIndex(plan) → { chunkSize, chunks }` where `chunks["cx,cz"] = { lots:[id…], shops:[…], edges:[…] }`
|
||
(a **plain object, not a callable**; a lot may bucket into several chunks). Also exports `chunkKey(cx,cz)`.
|
||
- `web/js/core/registry.js` exports `SHOP_TYPES` — per type `{ label, facades:[<full filename>.jpg…],
|
||
sign, interior, fittings:[…], storeys:[min,max], hours:{open,close} }`. **`facades` are full
|
||
filenames** (`facade-timber-teal.jpg`), not bare keys — see the skins seam in §7.
|
||
- `web/map.html` — 2D plan viewer (verified rendering real towns). `web/js/citygen/index.js` **is the
|
||
barrel Lane F added** so the shell's `import('./js/citygen/index.js')` resolves (§3.1).
|
||
|
||
**Lane B — `web/index.html` + `web/js/world/*`** *(landed, verified live)*
|
||
- Mode state machine `MODE ∈ {map, street, interior}` (interior branch not yet used), one renderer.
|
||
- **`createChunkManager(plan, scene, ctx)`** — a **factory**, not a class; returns
|
||
`{ update(pos), warmup(pos), getColliders(x,z), setNight(bool), getDoorMeshes(), count, dispose() }`.
|
||
**No `onChunkBuilt/onChunkDisposed` lifecycle hooks** — so §3.3 must drive citizens off the street
|
||
graph, not chunk lifecycle (or F adds a hook; prefer graph-driven).
|
||
- Door pick is wired via HUD: `createHUD({ onEnterShop: (shopId,name)=>enterShop(...) })`; `enterShop()`
|
||
dispatches `procity:enterShop` and is otherwise **a stub** (`hud.showToast('interiors coming')`) —
|
||
this is the seam F completes in §3.2. Interior returns via `procity:exitShop`.
|
||
- HUD shows draws / tris / fps / seed / time-of-day (F reads these for the budget gate).
|
||
- Exposes `window.PROCITY = { plan, scene, camera, renderer, chunks, lighting, player, skins }`
|
||
(F builds the `window.DBG` harness hook on top of this — §4).
|
||
|
||
**Lane C — `web/js/interiors/interiors.js`** *(landed, verified)*
|
||
- `buildInterior(shop, THREE, opts={registry,archetype,stockAdapter}) → { group, spawn:{x,z,ry},
|
||
exits:[…], places:[…], dims, recipe:{counterPos,…}, dispose() }`. Matches the wiring plan.
|
||
- Caller must attach lot dims: pass `{ ...shop, lot: plan.lots.find(l=>l.id===shop.lot) }`.
|
||
`dispose()` frees tracked GPU resources (soak gate asserts baseline).
|
||
|
||
**Lane D — `web/js/citizens/*`** *(landed, verified)*
|
||
- `sim.js` exports **`class CitizenSim`** — `new CitizenSim({ renderer, scene, camera, citySeed,
|
||
graph, fleet, group })`. It is **street-graph driven, not per-chunk**: there is no
|
||
`spawnRoster/despawn/setDaySegment` — build it once from the street graph and tick it in the loop.
|
||
- `keepers.js` exports **`class KeeperManager`** — `new KeeperManager({ camera, citySeed, fleet })`,
|
||
then `spawn(target, { x, z, ry, shopId, type }) → handle`, `remove(handle)`, `disposeAll()`,
|
||
`update(dt, playerPos)`. (Not `spawnFor/disposeFor`.)
|
||
|
||
**Lane E — `web/assets/manifest.json` + `pipeline/validate_manifest.py`**
|
||
- Manifest: `{depot, fittings:{id:{file,footprint,thumb}}, furniture:{…}, skins:{facade,sky,ground,wall,interior}}`.
|
||
- `python3 pipeline/validate_manifest.py` green (F's gate 5 runs it).
|
||
- B/C read the manifest for GLB upgrades; **primitive fallbacks must still fire under `?noassets=1`**.
|
||
|
||
---
|
||
|
||
## 3. Wiring checklist (LANE_F §Wiring — expanded to exact steps)
|
||
|
||
All of this lives in `web/js/world/wiring.js` (F-owned) + the minimal hooks each lane exposes.
|
||
|
||
### 3.1 Plan source swap (A → B) ✅ DONE (verified live)
|
||
- Lane B already reads `?seed=N` (default `20261990`), guards the citygen import, and surfaces the
|
||
town name + seed in the start panel and HUD. **But it imported `./js/citygen/index.js`, which did
|
||
not exist** (Lane A shipped `plan.js`), so it silently fell back to the 14-shop fixture.
|
||
- **Fix:** Lane F added `web/js/citygen/index.js` (barrel re-exporting `generatePlan/chunkIndex/chunkKey`
|
||
from `plan.js`). Verified live: shell logs "using Lane A generatePlan", HUD shows the real town
|
||
("Boolarra Heads", 523 shops), 31 chunks stream.
|
||
- Map key **M** is wired by Lane B to an in-shell minimap (not map.html). Fine for v1.
|
||
- **Gate:** plan determinism asserted green by `scaffold_check.mjs`. Map-PNG determinism pending the
|
||
shot harness + DBG hook (§4).
|
||
|
||
### 3.2 enterShop → interiors (B ↔ C) ✅ DONE (verified live, 2026-07-14)
|
||
Implemented as an **F-owned bridge module `web/js/world/interior_mode.js`** (`createInteriorMode`),
|
||
plus a thin marked seam in the shell. The bridge renders the interior into its **own dark
|
||
THREE.Scene** — the street scene is simply left frozen (not disposed → instant return, CITY_SPEC L3
|
||
"pause, not dispose"), which is cleaner than hiding B's ground+chunk meshes in a shared scene.
|
||
```
|
||
enterShop(shopId, name): // shell seam (was the toast stub)
|
||
shop = plan.shops.find(s => s.id === shopId) // id is numeric; matches door.shopId
|
||
lot = plan.lots.find(l => l.id === shop.lot) // attach {w,d} — Lane C sizes the room to it
|
||
interiorMode.enter({ ...shop, lot:{w,d} }, name); setMode('interior')
|
||
interiorMode.enter(shop): // F module
|
||
current = buildInterior(shop, THREE); scene.add(current.group) // Lane C
|
||
doorReturn = {x,y,z,ry of the player on the street}
|
||
camera → current.spawn (x, 1.6, z, ry); exitArmed = false
|
||
loop [MODE==='interior']: if interiorMode.update(dt,keys) leaveShop()
|
||
update: walk via current._debug.grid occ/bounds (mirrors interior_test.html) → renderer.render(ownScene)
|
||
exit arms once player steps >2m from the door, then fires within 1m of exits[].toStreet
|
||
leaveShop() / 'procity:exitShop' / Esc(unlock): // three exit routes, all → leaveShop
|
||
interiorMode.exit(): current.dispose() (frees GPU + unparents); camera → doorReturn
|
||
setMode('street') // pointer stayed locked → walking resumes at once
|
||
```
|
||
- **Verified live** (seed 20261990 "Boolarra Heads"): door → **Toy Shop "Numbat Playthings"**
|
||
renders (ivy wallpaper, stocked shelves, counter, exit doorway); exit state-machine correct
|
||
(no instant-exit at spawn; arms then fires at the door); **leak-free** — GPU memory 134 geo/37 tex
|
||
→ 217 loaded → **back to 134/37 after `dispose()`**. Swept **all 9 shop types** (toy/video/pawn/
|
||
opshop/book/record/stall/dept/milkbar), every one `ok+pathOK+leftClean`, worst build **8.4ms**
|
||
(budget 50), zero console errors.
|
||
- **Gate:** determinism (byte-identical placement per seed) is asserted by Lane C's own soak page;
|
||
memory-to-baseline after `dispose()` confirmed above. Real pointer-lock walk-in is the one bit the
|
||
automated browser can't drive — needs a human (or the DBG hook in §4).
|
||
|
||
### 3.3 Citizens + keepers (D)
|
||
**Keepers ✅ DONE (verified live).** Wired into the interior bridge (`interior_mode.js`), not the shell:
|
||
`new KeeperManager({ camera, citySeed: plan.citySeed })` — **no `fleet` passed → asset-free placeholder
|
||
actor** (rig-fleet upgrade is later, with §3.4). On `enter`, find the counter interactable
|
||
(`places.find(p => p.userData.keeperStand)` — Lane C tags `{x,z,ry}` there) and
|
||
`keepers.spawn(interior.group, { x, z, ry, shopId, type })`; tick `keepers.update(dt)` each interior
|
||
frame (playerPos defaults to the camera); `keepers.disposeAll()` on `exit`, before the room disposes.
|
||
- **Verified:** placeholder keeper (11-mesh humanoid) stands at the counter pose (record shop:
|
||
`{2.55,-2.46, ry 1.571}`), **turns to greet** a nearby player (0.21 rad) and eases back when they
|
||
leave (~0), disposes to 0. **Leak-free**: 5 enter/leave cycles over 5 shop types → after-leave
|
||
memory constant at 96 geo/16 tex, **net growth 0**; 0 console errors.
|
||
|
||
**CitizenSim (street peds) ✅ DONE (verified live).** Happy surprise: **`plan.streets` already IS the
|
||
graph** — `{ nodes:[{id,x,z}], edges:[{id,a,b,width,kind}] }` — so `graph: plan.streets` passes
|
||
straight in, **no adapter needed** (the runbook's earlier `streetGraphFrom` is unnecessary). In the
|
||
shell: `new CitizenSim({ renderer, scene, camera, citySeed: plan.citySeed, graph: plan.streets })`,
|
||
`setPopulation(140)` (`?pop=` tunable), then in the **street** branch each frame:
|
||
`setTimeOfDay((clk.seg + clk.frac)/6)` + **`update(dt)`** — note `update` takes **only dt** and reads
|
||
`this.camera.position` internally (the runbook's `update(dt,pos)` was wrong). `visibilitychange →
|
||
setPaused`. No fleet → asset-free placeholder(near)/impostor(mid) tiers; sim self-manages NEAR/MID/FAR
|
||
LOD off camera distance (not chunk-driven — chunks.js has no lifecycle hook).
|
||
- **⚠ gotcha (fixed):** `getClock().hour` is a **display string** (`'12:30'`), so `hour/24` → NaN →
|
||
0 peds. Use the numeric day fraction `(seg + frac)/6` instead.
|
||
- **Verified:** 140 pop → **96 active midday** (density curve; `~7` at night), peds walk footpaths at
|
||
~1.5 m/s, deterministic per seed (Lane D property; F passes the same graph+seed). **Budget:** 1 near
|
||
placeholder ≈ **11 draws** (11 meshes), bounded by `NEAR_MAX=24`; but a hamlet never fills it —
|
||
worst across all 6 main/high edges was **near 3, ~191 composer draws** (budget 300). Coexists with
|
||
interiors+keepers (round-trip regression clean); 0 console errors.
|
||
- **Note for §3.4:** placeholder near-tier is the draw cost; the **rig-fleet upgrade** (shared meshes +
|
||
impostor promotion) is what lets `?pop=` rise well past 140. Until then 140 is the safe lively default.
|
||
|
||
### 3.4 Manifest + `?noassets` + rig-fleet upgrade ✅ DONE (R3, verified live)
|
||
Wired in the shell (all gated by `NOASSETS = ?noassets=1`):
|
||
- **Rig fleet:** `loadPedFleet('models/peds/')` (19 char GLBs + walk/idle clips exist locally) → passed
|
||
to `new CitizenSim({…,fleet})` and (via `createInteriorMode({…,fleet})`) to `KeeperManager`. Peds +
|
||
keeper **upgrade placeholder→shared GLB rigs** once `fleet.whenReady` resolves (verified:
|
||
`citizens.mode==='rig'`, `keeperKind==='rig'`, detailed characters on the street + behind counters).
|
||
- **Interior GLB:** `preloadManifest()` at boot + `buildInterior(shop,THREE,{useGLB:!NOASSETS})` →
|
||
Lane C `glb.js` swaps fitting primitives for **depot GLBs** (`depot:<file>` → 3GOD, reachable).
|
||
Verified: bookshop fittings load real GLBs (+8 meshes, dense shelves). *Not all kinds map* — `counter`
|
||
is intentionally primitive; `record_crate` is a known-failing GLB → fail-soft to primitive.
|
||
- **`?noassets=1`:** `fleet=null`, manifest not preloaded, `useGLB=false`. Verified **zero network**
|
||
(Performance API: 0 GLB, 0 depot, 0 manifest, 0 ped-model requests); 100% primitive, still legible.
|
||
- **Leak-free:** rig keeper + interior GLBs dispose clean — the persistent geometry is the intentional
|
||
**shared `loadGLB` cache** (cold entry +N geo, re-entries flat: proven cycle2/3 = 189→189). soak.py
|
||
pre-warms this cache so the gate is honest.
|
||
- **⚠ finding (D/E):** GLB **character models are triangle-heavy** (~45k each near rig) → busiest view
|
||
tris ~279k, soak peak ~1.0M, over the **200k** soft budget. Draws are fine (~200/300). **Decimate the
|
||
ped GLBs** (and heavy fittings) to hit the tri budget; **F holds `?pop=140`** until then (raising pop
|
||
adds near rigs = more tris). See Cross-lane requests §9.
|
||
|
||
### 3.5 Hours / closed shops ✅ DONE (R3, verified live)
|
||
- `currentHour()` parses `lighting.getClock().hour` (`'HH:MM'`→float); `isOpen(shop)` = `open ≤ H < close`.
|
||
Both exposed on `window.PROCITY`. **`enterShop` gates on it**: closed → `🔒 CLOSED · opens HH:00`, no build.
|
||
- Lane A ships exactly one **`openLate:true`** landmark/town — always the **video rental** (Video Regal
|
||
`[11,23]` etc.). Verified: **midday** toy shop `[10,16]` enters; **night (22:00)** toy shop blocked,
|
||
**only the video shop opens** (the night destination).
|
||
- **Cross-lane (B):** the facade *visual* closed-state (dark windows + CLOSED plate) + a "CLOSED" door
|
||
tooltip are Lane B's rendering — `window.PROCITY.isOpen(shop)` is the predicate. See §9.
|
||
|
||
### 3.5 Hours / closed shops (A data → B facade + C gate) ⏳
|
||
- `isOpen(shop, seg)` from `shop.hours:[open,close]` vs the current day segment (map 6 segments →
|
||
hour). Closed ⇒ B renders dark windows + CLOSED plate (facade state); enterShop → locked toast.
|
||
- Exactly one late-night shop per town (Lane A seeds a shop whose `hours` run late). **F asserts it
|
||
exists**: `plan.shops.some(s => s.hours[1] >= 22)` (tune threshold to A's convention).
|
||
- **Gate:** at night, ≥1 open shop; closed shops visibly closed + un-enterable.
|
||
|
||
---
|
||
|
||
## 4. QA gates — ALL GREEN ✅ (v1.0 criterion met, R4). `tools/qa.sh --strict` is the static umbrella.
|
||
|
||
Measured against the fully-committed R4 tree (seed 20261990, "Boolarra Heads"), verified live +
|
||
by a 6-auditor adversarial v1-readiness workflow (2026-07-14) → **GO**.
|
||
|
||
| # | gate | criterion | result (R4) |
|
||
|---|---|---|---|
|
||
| 1 | **Determinism** | seed → identical plan (deep-equal + golden fingerprint) + selfcheck | ✅ scaffold_check green; selfcheck **1301/1301**, fingerprint **0x3fa36874** (intended A2 shift, not drift); two node processes → identical `sha256(plan)` per seed; chunkIndex covers every lot |
|
||
| 2 | **Soak** | walk ≥30 chunks, ≥15 shops; geo/tex baseline after each interior; heap stable; **0** console errors | ✅ **37 chunks, 20 shops, heap 38→38 MB, 0 errors, leak-free** |
|
||
| 3 | **Budget** | busiest intersection: ≤300 draws, ≤200k tris (settled clean measure) | ✅ **draws 138 · tris ~31k** (baseline 372k; peds decimated 924k→46k ≤3k each, D2 sign-off) |
|
||
| 4 | **Asset-free** | `?noassets=1` full playthrough, zero crashes, zero network | ✅ **heap 13→13 MB, 0 errors, 0 asset requests** |
|
||
| 5 | **Selfchecks** | `selfcheck.js` + `validate_manifest.py --depot` green via qa.sh | ✅ **qa.sh --strict 4/4**; manifest `--depot`: **23 GLBs live, 0 err** |
|
||
| 6 | **Shots** | `docs/shots/v1_tour/` 10 beauty shots + contact sheet | ✅ **10 full-frame 1280×720, no letterbox**, 3 new poses resolve; incl. §3.5 **night closed-facade** (CLOSED plates + lit openLate video) + real **record/opshop interiors** (keeper + hero props) |
|
||
|
||
> Note: soak peak-draw/tri *warnings* are transient teleport/impostor-atlas-bake sampling spikes;
|
||
> gate 3's settled busiest-intersection measure is the authoritative budget check (draws 138 / tris 31k).
|
||
|
||
### DBG hook — LANDED (Lane B `web/js/world/dbg.js`, `?dbg=1`)
|
||
`window.DBG` exposes `ready / shot(name) / teleport(x,z,ry) / setSegment(seg) / enterShop(id) /
|
||
exitShop() / info()` and drives `tools/shots.py` + `tools/soak.py` (mirrors 90sDJsim's `window.DBG`).
|
||
Bookmarks: street_noon, arcade, market_square, milkbar_dusk, night_neon, crossroads_busy,
|
||
residential_collar, warehouse_fringe. **Interior tour shots** (record/opshop) are captured by
|
||
`enterShop(id)` directly (not via `shot()`, which forces street mode) — see `tools/shots.py`.
|
||
|
||
---
|
||
|
||
## 5. Order of operations once lanes land
|
||
|
||
1. ✅ `node tools/qa/scaffold_check.mjs` — A's plan determinism + registry confirmed present.
|
||
2. ✅ Wire §3.1 (plan swap) → generated seed boots on the shell (barrel + skins fix, verified live).
|
||
3. ✅ Wire §3.2 (interiors) → door opens, enter/walk/exit + dispose verified across all 9 types.
|
||
4. ✅ Wire §3.3 (citizens + keepers) → keeper greets at the counter; peds walk the graph, budget-safe.
|
||
5. ✅ Wire §3.4 (manifest + `?noassets` + rig-fleet) and §3.5 (hours) — all verified live (R3).
|
||
6. ◑ Gates: 2 & 4 **PASS** (soak/asset-free), 5 **GREEN** (`--depot`), 3 tris-blocked on D/E decimation,
|
||
6 shots need Lane B bookmark poses. `docs/shots/v1_tour/` captured.
|
||
7. ⏳ `tools/qa.sh --strict` green + gate 3 tris ⇒ tag v1 (pending D/E ped-GLB decimation).
|
||
|
||
## 7. Live smoke-test findings (2026-07-14, real generated town, seed 20261990)
|
||
|
||
Booted the wired game in a browser and drove the camera via `window.PROCITY`. State:
|
||
`Boolarra Heads · 523 shops · 711 lots · 31 chunks live · 31 door meshes · 24.7k tris`.
|
||
|
||
- ✅ **Seed → real town** works after the barrel fix (§3.1). Contiguous strip, awnings, verandah
|
||
posts, doors, windows, gum trees, sky dome, 6-segment day cycle — all render.
|
||
- ✅ **Facade skins seam (FIXED).** `skins.js facadeMat` built `assets/gen/facade-<name>.jpg` but
|
||
Lane A's `facadeSkin` is the **full filename** (`facade-fibro-blue.jpg`) → every facade requested
|
||
`facade-facade-…jpg.jpg` (404 → flat colour). Lane F normalized the name at the `facadeMat`
|
||
chokepoint (strip a leading `facade-` + image extension; idempotent for bare keys). Verified:
|
||
facade JPEGs now return **200 OK**, town is textured. *Root cause is a cross-lane naming contract
|
||
ambiguity — registry stores filenames, skins.js expected bare keys. Worth a CITY_SPEC line so
|
||
ground/awning/interior skins don't hit the same trap.*
|
||
- ⚠ **Budget:** ~**334 draw calls** at spawn (radius 3), over the CITY_SPEC ≤300. Tris fine (~25k).
|
||
Options for Lane B: drop default radius to 2, or merge more per chunk. Gate 3 will fail until shaved.
|
||
- ⏳ **Interiors not wired** (§3.2), **citizens not wired** (§3.3), **manifest/`?noassets` not wired**
|
||
(§3.4) — the remaining integration passes.
|
||
|
||
## 8. Open questions for the lanes (surface, don't guess)
|
||
- **B:** will you expose the `window.DBG` hook (§4) and the `ChunkManager` lifecycle callbacks
|
||
(§3.3)? If not, F wires citizens via a MutationObserver-style scan — slower, please prefer callbacks.
|
||
- **A:** which field marks the "open late" shop, and what's the `hours` encoding (24h ints? segment idx)?
|
||
- **C:** does `places` tag the counter slot with a stable `kind:'counter'` so keepers find it?
|
||
- **E:** is `?noassets` honored inside your manifest-consuming code, or does F gate it at the shell?
|
||
- **D:** roster spawn/despawn API name + does it accept a chunk key directly?
|
||
|
||
---
|
||
|
||
## 9. Cross-lane requests (Lane F → others, R3) — the integration surfaced these; not silent-editing your files
|
||
|
||
**→ Lane D / Lane E — decimate the ped GLBs (blocks gate 3 tris).** The rig fleet (`web/models/peds/*.glb`)
|
||
is wired and works, but the character models are triangle-heavy: **~45k tris each near rig**, so the
|
||
busiest-intersection view hits **~279k tris** (soak peak ~1.0M) vs the **200k** CITY_SPEC budget. Draws are
|
||
fine (~200/300 — the shared-mesh sharing worked). Please **decimate/LOD the 19 ped GLBs** (target ≤~10–15k
|
||
tris each) so gate 3 passes; then F can raise `?pop=` past 140. Same applies to any heavy interior fitting GLB.
|
||
|
||
**→ Lane B — three dbg.js / shell items:**
|
||
1. **shots.py bookmark poses (gate 6).** `dbg.js BOOKMARKS` defines `street_noon/arcade/market_square/
|
||
milkbar_dusk/night_neon`, but F's v1_tour also uses `warehouse_fringe / residential_collar /
|
||
crossroads_busy` — these fall back to `street_noon` (identical frames). Please add those 3 poses.
|
||
2. **shots letterbox.** In headless (1280×720), `DBG.shot()`→`composer.render()` renders into a square
|
||
sub-region even though renderer/canvas/camera are correctly sized — the EffectComposer render targets
|
||
don't rebuild on the initial resize. A real viewport toggle in shots.py didn't fix it; likely the
|
||
composer needs an explicit `setSize`/reset on first `shot()`. (In-game, real window sizes render fine.)
|
||
3. **§3.5 closed-facade visual.** F gates *enterability* by hours (`window.PROCITY.isOpen(shop)`); the
|
||
*visual* closed state — dark windows + a CLOSED plate, and a "CLOSED" door tooltip in `hud.js` — is
|
||
your facade/HUD render. Predicate is ready on `window.PROCITY`.
|
||
|
||
**Note:** F-owned harness bugs found + fixed this round (so the gates actually run): `shots.py`/`soak.py`
|
||
polled `window.DBG` before its async install (race → false "absent"); read `DBG.plan` (unexposed → use
|
||
`window.PROCITY.plan`); `enterShop({shopId})` vs the bare-id contract; `shop.lot` treated as an object
|
||
(it's a lot **id**); soak now pins midday (else §3.5 closes shops mid-walk) + pre-warms the shared GLB
|
||
cache (else the one-time upload reads as a leak) + budget peaks demoted to warnings (DBG.info samples spike
|
||
during streaming; gate 3's clean single-pass is authoritative).
|
||
|
||
---
|
||
|
||
## 10. Round 5 — v1.1 close-out + v2 flag seams (Lane F tracking)
|
||
|
||
Round 5 (v1.1 polish + v2 foundations behind flags). **v2 prime law:** default boot stays
|
||
v1-identical; `GOLDEN.hash 0x3fa36874` must not move; `qa.sh --strict` green at every commit.
|
||
|
||
### v1.1 tag — ⏳ WATCH (blocked on C1 + E1)
|
||
Tag `v1.1` once **Lane C** re-maps `counter_till` + `record_crate` (C1) and **Lane E** bakes the
|
||
hero props ≤8k (E1). F then: verify record/milkbar/opshop interiors (GLB counter+till correct, bins
|
||
upgraded, baked props look right), re-capture ONLY the 2 interior tour shots if improved, qa green →
|
||
`git tag v1.1` (local). *Nothing to tag yet — HEAD `039c179`, no round-5 lane commits.*
|
||
|
||
### v2 flag seams — table (F wires `web/index.html` + `tools/` as lanes document them)
|
||
Every flag **default-off + independent**. Wire only from the owning lane's NOTES call-site spec.
|
||
|
||
| flag | owner | what | F wiring | state |
|
||
|---|---|---|---|---|
|
||
| `?plansrc=osm` | A | second plan source (OSM fixture) behind the CityPlan contract | select in shell plan bootstrap | ❌ **A did not run R5** — task carries to R6 |
|
||
| `?winmap=1` | B | parallax interior-mapping window shader | none needed (self-contained in `buildings.js`) | ✅ landed `2a923b6` |
|
||
| `?dig=1` | C | dig.js crate-riffle on procedural stock | wired by F (`26d68cd`): `DIG_ON` → `interiorMode`, Esc/pointer-lock guards | ✅ landed `7131f53` + F2 |
|
||
| `?roster=stream` | D | chunk-streamed citizen roster | none needed (self-contained in `sim.js`) | ✅ landed `93a3168` |
|
||
|
||
*(table updated by Fable in the R5 review — F3 harness extensions below did NOT land in R5; they
|
||
are the top Lane F item for R6.)*
|
||
|
||
### v2 QA harness extensions (F3) — build as flags land
|
||
- **Determinism gate:** pin `plan-hash` + `first-interior-hash` per `(seed, plansrc)`. Synthetic
|
||
stays `0x3fa36874`; add the osm-fixture hash when A lands.
|
||
- **Flags-off regression check:** boot no-flags → assert plan hash + a settled draws/tris snapshot
|
||
within tolerance of the v1 baseline (catches a v2 flag leaking into the default path). *This is the
|
||
enforcement arm of the v2 prime law — build early, before flags land.*
|
||
- **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 (warn) gate** this round; strict next round.
|
||
|
||
### Marshal
|
||
Shared-tree arbiter this round: F owns `web/index.html` + `tools/`; if two lanes collide on a file,
|
||
F arbitrates. Cross-lane asks → the owning lane's NOTES, not silent edits.
|
||
|
||
---
|
||
|
||
## 12. Round 12 — v3.0-alpha gig layer (`?gigs=1`). F wires it last; findings below.
|
||
|
||
**What F built.** `web/js/world/gig_state.js` (new, F-owned) — the Friday-night state machine; the
|
||
`?gigs=1` seam + `custom_bands.json` bootstrap in `index.html`; the cover charge at the door; the band +
|
||
crowd + gig-bed wiring in `interior_mode.js`; `smoke_gigs` in `tools/flags_check.py` (6 gates) + the
|
||
v3 arm of the flags-off regression.
|
||
|
||
### The state machine (`gig_state.js`) — the two things that are load-bearing
|
||
1. **It listens, it doesn't poll.** `lighting.js` dispatches `procity:segment` on every segment change;
|
||
the latch rides that. A poll-only machine sees only the segments it is sampled on — driving
|
||
NIGHT→DAWN→MORNING→NIGHT with nothing reading in between (player inside the pub, audio muted, so
|
||
neither F's street loop nor B's audio rAF is reading) **skipped the night roll entirely**: the night
|
||
never advanced and the cover was never due again. Caught live, fixed, re-verified.
|
||
2. **`state` is a getter, not a field.** B's audio engine self-ticks on its own rAF and reads
|
||
`PROCITY.gigs.state` while the player is *inside* a shop — i.e. when F's street loop is not running.
|
||
A pushed field goes stale there and the spill lies about the gig.
|
||
|
||
3. **The night rolls on WHERE the clock lands, not on watching `done` go by.** First cut incremented the
|
||
night when leaving `'done'` forwards — which meant a clock *jump* straight from NIGHT to MORNING (never
|
||
touching DAWN) never rolled the night at all, and **a broke punter walked in on last night's paid
|
||
stamp**. `smoke_gigs` caught it; stepping with `[`/`]` always passes through DAWN, so only a jump
|
||
(DBG/shots/soak all jump) exposed it. Now: the latch clears wherever it clears, and landing in the
|
||
**daytime** (MORNING…ARVO) is what rolls the night; landing on the **doors** means we rewound out of
|
||
the gig — same night, stays paid.
|
||
|
||
`done` is real, not decorative: the segment→hour map tops out at NIGHT (22:00) so the gig owns the whole
|
||
night segment, and "after the gig" can only be DAWN (closing time — band packing up, crowd draining per
|
||
A's closing-time ruling). Latch clears on any non-DAWN segment so rewinding the clock (`[`) doesn't leave
|
||
`done` stuck on a midday town — it did exactly that on the first verify.
|
||
|
||
### ⚠ Finding 1 (Lane C ↔ Lane E) — the gig bed key never met. **F bridged it; needs a contract line.**
|
||
`LANE_C_PUB.md` asks E to name the live bed **`gig-pubrock`** (C emits `room.audio.gigKey = 'gig-' +
|
||
genreKey`). E shipped it as **`pubrock-live`** (`gig:true`, `types:['pub']`) — which is also the key B's
|
||
street spill hardcodes. Neither is wrong; they just never met. Unresolved, `AUDIO.music['gig-pubrock']`
|
||
is `undefined` and **the pub plays to a silent room** — and because the audio law fails soft, nothing
|
||
errors: you just get a band miming. F resolves it at the glue (`interior_mode.js GIG_BED` + a generic
|
||
`gig-<genre>` → `<genre>-live` fallback). **This is the R1 facade-skin filename trap again** (registry
|
||
stored filenames, `skins.js` expected bare keys). → **C/E/Fable: pick one and put it in CITY_SPEC as a
|
||
named contract**, so genre #2 doesn't rediscover this. F's smoke now asserts `gigKey` resolves.
|
||
|
||
### ⚠ Finding 2 (Lane D + Lane E) — E's instrument GLBs landed; nothing wires them.
|
||
E shipped 5 instrument GLBs this round (`electric_guitar`, `bass_guitar`, `drum_kit`, `guitar_amp`,
|
||
`mic_stand`, all in `manifest.fittings` with footprints + thumbs). D's band holds **primitives** and
|
||
published `spawn(..., { instrumentFor })` as the injection seam — D-progress says E's GLBs "drop in via
|
||
`opts.instrumentFor(role)` **when they land**". They have landed. Nobody owns the join. Not blocking
|
||
(asset law: a primitive band is still a gig, and it reads fine in the shot), but it is a dangling R12
|
||
deliverable. F did **not** do it silently: picking manifest ids and orienting/scaling instruments inside
|
||
D's rig is D's call, and the R12 no-drummer question below wants answering in the same pass. → **R13.**
|
||
|
||
### ⚠ Finding 3 (ROUND12 spec ↔ Lane C) — the band has no drummer.
|
||
ROUND12 §Lane D.1 specifies "guitarist/bassist strum-sway, **drummer seated bob**", and E was asked for
|
||
"drum kit (one merged kit)". C's `stage.bandPoses` are **guitar / vocal / bass** — a front-line trio; the
|
||
drum riser is set-dressing with no pose. D built to C's poses and flagged it. So the shipped alpha band is
|
||
3-across-the-front with a mic stand where the spec expected a kit. Cosmetic, non-blocking, deterministic —
|
||
but it is a **spec/implementation divergence that no gate can catch**. → **Fable/John: rule for R13** (add
|
||
a 4th `drums` pose on the riser, or amend the spec to a front-line trio).
|
||
|
||
### Gate lesson — "no giants" must measure STATURE, not world crown.
|
||
The R10 gate asserts head-bone world Y ∈ [1.4, 2.0]. The band stands on C's **0.32 m stage deck**, so a
|
||
perfectly human 1.73 m guitarist has a world crown of **2.05 m** and a naive world-Y check **fails a
|
||
correct band** (it did, first run). `smoke_gigs` measures `crown − base.y` for the band/crowd and keeps
|
||
the world-crown assert only for "never clips the ceiling", which is the real R10 failure mode.
|
||
`tools/qa/interior_scale_check.py` is unchanged (keepers/browsers stand on the floor, base.y = 0) — but
|
||
**if anyone ever puts a keeper on a platform, that gate inherits this bug.**
|
||
|
||
### → Lane B (small, R13): no venue bookmark for shots
|
||
`dbg.js BOOKMARKS` has no venue pose, and `DBG.shot(name)` forces street mode **and re-poses/re-segments**
|
||
— so there is no way to drive a scripted "lit pub frontage at night, gig on" street shot (the alpha's other
|
||
money shot; your marquee + posters + spill are the whole street story of this round). F verified the
|
||
frontage lights correctly (`emissiveIntensity` 1.7 at `on`, 71 draws / 29.6k tris at the venue) but shot it
|
||
by hand. **Ask:** add a `venue_night` bookmark (pose at the pub frontage, `seg:5`) — then F's
|
||
`tools/qa/gig_shot.py` can capture the street side too. This is the same "3 missing bookmark poses" ask
|
||
from R3 §9, so it may be worth doing the set in one pass.
|
||
|
||
### Harness note — where the browser gates actually run
|
||
This session's box had **no Chromium and no Chrome**, so every Playwright gate (flags harness + scale
|
||
gate) is un-runnable locally until `tools/.venv/bin/python -m playwright install chromium`. The repo at
|
||
`/Volumes/m3ultra/Documents/procity` is a **mount of m3ultra's `~/Documents/procity`** — same tree, so
|
||
`ssh m3ultra@100.89.131.57` runs the real gates on the real files. **Gotcha:** node is not on the
|
||
non-interactive ssh PATH — `env PATH=/opt/homebrew/bin:$PATH tools/qa.sh --strict`, or every node gate
|
||
reports a bogus FAIL (`node: command not found`).
|
||
|
||
### R12 numbers (measured, seed 20261990 → "Boolarra Heads", venue = The Thornbury Hotel)
|
||
| thing | number |
|
||
|---|---|
|
||
| gig-night interior, rig fleet | **42–50 draws** · ~40k tris (budget **≤350**) |
|
||
| gig-night interior, `?noassets=1` placeholders (**worst**) | **171 draws** (budget ≤350) |
|
||
| gig-night street at the venue | 69 draws · 29.6k tris (budget 300 / 200k) |
|
||
| band / crowd | **3** on the deck · **8** at 8 watch points · **3 dancing / 5 standing** |
|
||
| figure scale | band stature 1.68–1.77 m (crown ≤2.09 m under a 4.0 m ceiling); crowd 1.60–1.77 m |
|
||
| leak, 4 gig enter/exit cycles | geo 66→66 · tex 63→63 (**zero**) |
|
||
| tonight's covers (7 nights) | `10, 0, 0, 3, 2, 0, 9` — 3 free of 7 (~half, as seeded) |
|
||
| custom band names surfacing | **7 of John's 10** fill all 7 nights; `?noassets=1` → pure generator |
|
||
|
||
---
|
||
|
||
## 13. Round 13 — v3.0-beta THE DISTRICT (`?gigs=1`). F wires it last; findings below.
|
||
|
||
**What F built (all 7 tasks from ROUND13 §Lane F).** All committed Lane-F-only; verified green on m3ultra
|
||
(`qa.sh --strict`, the focused district gate, the scale gate). A single WARN — the queue — is a documented
|
||
handoff to D, not a hole (below).
|
||
|
||
1. **Per-venue state machine** (`gig_state.js`, rewritten). One latch PER VENUE keyed by `venueShopId`; a
|
||
town of 2–4 venues runs 2–4 independent latches off the SAME `procity:segment` spine. All three hard-won
|
||
R12 laws are preserved verbatim inside `createVenueLatch` (listens-not-polls · `state` is a getter ·
|
||
night rolls on WHERE the clock lands). New runtime surface on `window.PROCITY.gigs`: **`.byVenue`** →
|
||
`{ [id]: state }` (B's `venue.js`/`audio.js` read it), per-venue **`stateOf/onOf/openOf/gigOf/coverOf/
|
||
bandNameOf/paidOf(id)` + `markPaidOf(id)` + `.venueShopIds`**. The R12 single-venue alias
|
||
(`.state/.on/.venueShopId/.cover/.paid/…` → the **primary** venue = `gigs[0]`'s) is kept **one round** so
|
||
B's mid-round single-venue callers don't break — it retires next round. Contract pinned in CITY_SPEC §v3.
|
||
*Tonight* per venue = its night-0 gig (matches A's posters + B's frontage, all night-0-keyed); a
|
||
dark-tonight venue latches 'quiet' all session, which is true to the seeded week.
|
||
|
||
2. **Walla + applause** (`interior_mode.js`). While a venue's gig is `on`, the crowd bed
|
||
`ambience['crowd-walla']` rides the interior TONE layer under the live band bed (you hear the room, not
|
||
the fridge hum) — it reuses B's existing `playInterior({musicKey,toneKey})`, no new audio API. On the
|
||
`on → done` edge while the player is inside (crowd's last-song → the clock rolls NIGHT→DAWN), a one-shot
|
||
`sfx['applause']` sting fires ONCE (`applauded` latch, re-armed per enter). Fail-soft throughout (no-op
|
||
muted / `?noassets` / pre-gesture). Verified live: `iMusic=gig-pubrock`, `iTone=crowd-walla`, edge fires,
|
||
0 errors.
|
||
|
||
3. **Cover charge PER VENUE** (`index.html`). The door seam now keys off the shop being ENTERED
|
||
(`openOf/coverOf/paidOf/markPaidOf(shopId)`), so a night at two venues is two covers; each latch holds
|
||
its own runtime paid-stamp. Verified: paid ($10 debited once, re-entry free), broke-knockback, free night.
|
||
|
||
4. **Debt #1 CLOSED — and the R12 bridge was a LIVE BUG.** E renamed the pub bed `pubrock-live → gig-pubrock`
|
||
this round (the canonical `gig-<genreKey>`), so F's R12 `GIG_BED`/`gigBedKey` bridge — which rewrote
|
||
`gig-pubrock → pubrock-live` — was now pointing at a manifest key that **no longer exists**: the band
|
||
would have mimed in a silent room. DELETED. The room's `audio.gigKey` (already the manifest key) feeds the
|
||
engine directly, zero mapping. Verified across two genres live: `gig-pubrock` (pub), `gig-covers` (rsl).
|
||
|
||
5. **Gate hardening — stature into the shared gate** (`interior_scale_check.py`, debt #6). The human-size
|
||
assert now measures **stature = crown − foot** (feet→crown span, platform-independent), with the
|
||
world-crown "never clips the ceiling" assert kept as its OWN line. The day a keeper stands on a platform,
|
||
the gate won't fail a correct figure. Floor figures foot≈0 ⇒ stature≈crown ⇒ backward-compatible.
|
||
Verified: 24 figs across 6 shops, statures 1.60–1.86 m.
|
||
|
||
6. **District smokes** (`flags_check.py smoke_gigs`, extended). band 3→**4** · a new **§2b DISTRICT** loop
|
||
that enters EVERY playing venue and asserts its `gigKey` resolves to a real manifest bar (debt #1 across 3
|
||
genres), per-venue crowd ≤ its own watchPoints, 4-piece, no-giants-by-stature · **§2c street budget** at
|
||
the busiest venue block (settled-floor sample) · queue probe (WARN-pending, below) · plus the R12
|
||
determinism / cover / noassets / flags-off arms. All green.
|
||
|
||
### ⚠ Cross-lane ask (Lane F → Lane D) — the QUEUE seam. F's smoke is READY; it WARN-skips until D exposes it.
|
||
D owns the outdoor queue (`web/js/citizens/queue.js` `VenueQueue`, charter item, landing this round). B has
|
||
already published the zone: **`window.PROCITY.venuePresentation.queueZoneFor(id) → {x,z,ry,len}`** and
|
||
`.queueZones`. F drives the surge per venue via `citizens.setGig(id, open)` (done). What F's smoke needs to
|
||
verify "queue ≤ cap and drains by done" is a read seam on the sim — **proposed contract:**
|
||
`window.PROCITY.citizens.queueInfo(venueShopId) → { count, cap }` (live count while doors/on; `0` once the
|
||
gig is `done`). Wire the queue's spawn to `queueZoneFor(id)` at `doors` and expose `queueInfo`, and F's smoke
|
||
flips WARN→assert with no further change. (If D picks a different accessor name, tell F and F adjusts the one
|
||
line in `smoke_gigs §2b`.)
|
||
|
||
### R13 numbers (measured, seed 20261990 → "Boolarra Heads", 3 venues)
|
||
| thing | number |
|
||
|---|---|
|
||
| district | **3 venues** — 2 playing tonight (pub #116 The Exchange Hotel · rsl #404), 1 dark (reads true) |
|
||
| live beds resolve | pub → `gig-pubrock` · rsl → `gig-covers` (both real manifest bars, debt #1 across genres) |
|
||
| band / crowd | **4-piece** each venue · pub crowd **8/8** watch points · **rsl crowd 12/12** (the cap stress case) |
|
||
| gig-night interior (rig) | **62 draws** · 34k tris (budget ≤350) · `?noassets` still a full 4-piece gig |
|
||
| street @ venue block, night | **99 draws** · 28k tris (budget 300 / 200k) — lit frontage + posters + queue zone in |
|
||
| figure scale | band+crowd stature **1.61–1.81 m**, tallest crown 2.12 m under the 4 m ceiling |
|
||
| cover, per venue | $10 debited once · re-entry free · broke knockback · free night (seed 1234) — all hold |
|
||
| flags-off prime law | fingerprint **0x3fa36874** unchanged · gig layer wholly absent · 0 errors |
|
||
|
||
**Status:** F's wiring is done, committed, and green. **Waiting on Lane D** for (a) the queue seam
|
||
(`queueInfo`) so the queue smoke goes live, and (b) the band polish (E's instrument GLBs in hand, drummer
|
||
facing/RY_FLIP) — the band already lands at 4, human-sized, 0 errors, so this is polish not a blocker. Once D
|
||
commits: re-run the gates, flip the queue assert, capture the district money shots, tag **v3.0-beta**.
|
||
|
||
*(R13 epilogue: D landed mid-session; F wired the queue (D shipped `VenueQueue`, left the street-side
|
||
lifecycle to F), the queue smoke went live, both money shots shipped, and **`v3.0-beta`** was tagged +
|
||
pushed. The `queueInfo` proposal above was superseded by D's `VenueQueue.count()` + F's shell `queueCountOf`.)*
|
||
|
||
---
|
||
|
||
## 14. Round 14 — F never ran; the brief carried in full to R15.
|
||
|
||
R14 was the "v3.0 release round." A–E landed excellent hardening (D's identity-continuity seam, B's
|
||
alias-reader retirement + `queue_night` hero + 19-bookmark tour audit, A's 400-seed invariant sweep + §v3
|
||
freeze, C's adversarial venue audit, E's provenance freeze) **but Lane F never ran** — so v3.0 was not
|
||
tagged and F's whole release brief (continuity wiring, alias deletion, week soak, the tour, the tag) was
|
||
untouched. Fable carried it verbatim into R15. Nothing F-authored landed in R14.
|
||
|
||
## 15. Round 15 — THE v3.0 RELEASE CLOSE (`?gigs=1`). F ran last; the six-item brief, all green.
|
||
|
||
A first (poster flip + golden re-pin) → B/C verify → D one-line amp move → **F closes**. All deps were in
|
||
before F ran (verified against origin). The six F items (ROUND15 §Lane F), each committed atomically by
|
||
pathspec:
|
||
|
||
1. **Continuity wiring + smoke** (ledger #3) — D published the seam in R14; F wired its three touches:
|
||
`crew.spawn(…, { roster: rosterOf(shop.id) })` in `interior_mode.js` (front crowd slots become tonight's
|
||
roster), `rosterOf:(id)=>citizens.tonightRoster(id)` + `onAdmit:(e)=>citizens.recordVenueEntry(id,e)` on
|
||
the queue in the shell (the clear is automatic on `setGig(id,false)`). New `smoke_continuity` asserts the
|
||
seam on **identities** (crowd ⊇ roster ∩ cap · every `member.pedIndex ∈ tonightRoster` · `crewInfo.fromRoster`),
|
||
the empty-roster path injects nobody (shell side of D's byte-identical law), and **settled tris**
|
||
(ledger #5). Commit `687d23f`.
|
||
2. **Alias deletion** (ledger #2) — the R12 single-venue scalar alias is **gone**; every read is keyed by
|
||
`venueShopId`. Deleted the alias block + `primaryId`/`P` in `gig_state.js`; migrated `flags_check.py` +
|
||
`gig_shot.py` (`.venueShopId→venueShopIds[0]` · `.state→stateOf(id)` · `.cover→coverOf(id)` ·
|
||
`.bandName→bandNameOf(id)`); deleted the two CITY_SPEC §v3 alias sentences + added the omitted
|
||
`nightOf(id)`. B's R14 sweep left the cross-lane readers clean; only F's tools remained. One atomic
|
||
commit `146b5f3`.
|
||
3. **Full-week soak** (`tools/qa/week_soak.py`, opt-in `qa.sh --soak`, non-strict) — 7 nights × every playing
|
||
venue in one context: latch state per `plan.gigs` (dark venues quiet), cover **stamped per venue per
|
||
night** (debited once, re-entry free, due again next night), roster **clears per night** (no
|
||
accumulation), and geo/tex return to a **warmed baseline** across ≥20 cycles — a leak is unbounded
|
||
per-cycle growth; warm-up cache residue **plateaus** (the memTrail proves it). Commit `edc0c47`.
|
||
4. **The money-shot tour** (`tools/qa/tour_shots.py`, ledger #4/#5) — one deterministic run → `docs/shots/
|
||
release_v3/` + `contact.html`: `venue_night`, `district_posters`, **`queue_night` (John's hero — A's
|
||
flipped frontage poster now reads from the street)**, `gig_interior` (4-piece + real instruments + a
|
||
roster crowd), `rain_verandah`, `tram_stop`. **Settled discipline**: measured after the async instrument
|
||
GLBs / posters / chunks land (shoot-twice), clean per-group loads; queue driven **live** (explicit admits,
|
||
not throttled rAF auto-drain). Commit `6a35b29`.
|
||
5. **Docs assembly** — README v3 section (what shipped · flags table · the tour), this §14/§15 runbook,
|
||
F-progress current, the five freezes confirmed committed (A `a3911a4`/`35da83c`, B `eee60e0`/`a0b5784`,
|
||
C `62e38c5`/`2446ae3`, D `9a65f51`/`66aec0f`, E `55f6698`).
|
||
6. **Acceptance + tag** — `qa.sh --strict` 6/6 + `smoke_continuity` + `week_soak.py` green → tag **`v3.0`**.
|
||
|
||
### Numbers (measured, seed 20261990 → "Boolarra Heads", 3 venues)
|
||
| thing | number |
|
||
|---|---|
|
||
| district | 3 venues — 2 playing (The Exchange Hotel `gig-pubrock` + an RSL `gig-covers`), 1 dark |
|
||
| gig interior, settled | 4-piece · crowd 8 (2 from the roster) · **56 draws** · **35k→182k tris** (pre-load→settled; gate is draws ≤350) |
|
||
| street @ venue block, night | `queue_night` 121 draws / 62k tris · `district_posters` 186 / 134k (busiest) — all ≤300/200k |
|
||
| week soak | 28 enter/exit cycles · $60 covers · geo/tex flat across the back half (115/86→115/84) · 0 errors |
|
||
| continuity seam | 2 queue admits carried into the crowd · empty roster 0-injected · `member.pedIndex ∈ tonightRoster` |
|
||
| **settled-tris honesty** (ledger #5) | C was right: the interior settles to **~182k** tris after the 5 instrument GLBs load, not the ~34k measured pre-load |
|
||
|
||
### The gotcha ledger closed this round
|
||
- **Alias retirement is a three-party move**: B swept cross-lane readers (R14), A left the spec sentences
|
||
for F (avoiding a doc-ahead-of-code race), F deleted the code + migrated its own tools + the spec in ONE
|
||
commit. No half-renamed state ever existed.
|
||
- **Settled measurement** (ledger #5): a headless smoke that reads `renderer.info` right after `enterShop`
|
||
sees the room BEFORE its async instrument GLBs decode — ~34k, not the ~182k settled truth. The tour + the
|
||
continuity smoke both wait/shoot-twice now; the interior gate stays on **draws** (which is load-order
|
||
stable), so there was never a budget violation, only a dishonest number.
|
||
- **Drive live-spawned things explicitly**: automation throttles rAF, so the queue's seeded auto-drain may
|
||
not fire on schedule — `smoke_continuity`/`week_soak`/`tour_shots` all call `admitOne()` / step the loop
|
||
explicitly rather than trust the timer.
|
||
|
||
---
|
||
|
||
## 16. Round 16 — v3.1 THE FLIP (`?classic=1` is the new covenant). F bookends; opening below, close after.
|
||
|
||
The town shows its best by default. After three v3 stages the gig layer flips ON, and the mature v2 flags
|
||
(`weather`, `winmap`, `tram`) flip with it. F owns the flip; A–E landed their v3.1 polish against it (D's
|
||
seated drummer, B's town-wide pools + default re-baseline, C's backline unification, A's corner-poster +
|
||
no-spine fixture, E's `sit.glb`).
|
||
|
||
### The flip (ledger #1) — the covenant MOVED from "flags off" to `?classic=1`
|
||
- **Shell** (`index.html`): `const CLASSIC = has('classic') && get('classic')!=='0'`, `const flagOn = name
|
||
=> !CLASSIC && get(name)!=='0'`. `gigs`/`weather`/`tram` construct via `flagOn`; `loadPedFleet({sit:!CLASSIC})`
|
||
gives the drummer his clip while keeping classic zero-fetch; `window.PROCITY.flags` publishes the intent.
|
||
- **`buildings.js`**: `winmap` reads the SAME rule (default-on, classic-off) — the one cross-lane flip line,
|
||
because winmap's flag lives in B's file. Marked `[Lane F R16 THE FLIP]`.
|
||
- **Two goldens, two gates:** classic → `0x3fa36874` (byte-identical v2 forever, the frozen regression
|
||
target); default → the gig golden `0xb1d48ea1`. `flags_off_regression` became **`classic_regression`**
|
||
(boot `?classic=1`); a new **`default_boot_gate`** proves the no-flag boot is the full experience (all four
|
||
flags on, gig layer present, gig golden green, budget). Every gig smoke re-points to the DEFAULT boot.
|
||
|
||
### Seated-stature gate (ledger #3) — published in the brief, no mid-round handshake
|
||
A seated figure fails the standing no-giants floor by design. Contract (in the round brief so D built to it):
|
||
D tags seated figs `fig.userData.procitySeated = true`; F's `interior_scale_check.py` + `flags_check.py`
|
||
accept them at **stature ∈ [0.9, 1.5] m** (still < ceiling, feet planted); standing stays [1.4, 2.0]. Landed
|
||
in the opening commit. Verified live: the seated drummer passes at **1.32 m stature**.
|
||
|
||
### Numbers (measured, seed 20261990 → "Boolarra Heads")
|
||
| thing | number |
|
||
|---|---|
|
||
| `?classic=1` | byte-identical v2: fingerprint 0x3fa36874, gig layer absent, streamed roster on, 160 draws / 21k tris |
|
||
| default boot | all four flags on · gig golden 0xb1d48ea1 · 14 gigs, 15 posters · **112 draws @ the venue block** (≤300) |
|
||
| district (default) | 3 venues, **3 playing** (corner-poster re-pin shifted the schedule) · 3 genres live |
|
||
| seated drummer | **1.32 m** stature — passes the seated band [0.9,1.5]; the standing crew 1.68–1.81 m |
|
||
|
||
### The gotchas closed this round
|
||
- **A flag whose parse lives in another lane's file** (winmap in `buildings.js`) still flips as part of F's
|
||
structural change — one marked line, same `!classic && !=='0'` rule as the shell.
|
||
- **selfcheck prints the base fingerprint in its summary but asserts the GIG golden silently**, so the
|
||
default-boot gate gates on the ALL-GREEN run (which includes the gig-golden assert), not a stdout substring.
|
||
- **`P.winmap` isn't exposed** — the default gate verifies winmap *intent* via `window.PROCITY.flags`; the
|
||
actual shader is B's own `winmap` smoke.
|
||
- **`?classic` must zero the fetch delta**, so `sit.glb` is gated on `!CLASSIC` (not just on the fleet) — a
|
||
raw sit fetch under classic would break the covenant even though the drummer never spawns there.
|
||
|
||
---
|
||
|
||
## 17. Round 17 — v3.2 THE TAIL + THE SCOUT. F: the town matrix + the close (ledger #7).
|
||
|
||
The tail drained (D's drummer lean + bench-sit, B's wind sway, C's backline verify, E's `--verify` fix) and
|
||
the real-map scout ran (E's 5 real AU town caches + A's cache contract + hardened `plan_osm`). F wired the
|
||
last mile and gated the whole matrix.
|
||
|
||
### Real towns boot in the engine (the F wiring)
|
||
E's caches sat on disk (`web/assets/towns/*.json`) + registered in the node selfcheck, but **the shell never
|
||
loaded them** — `?plansrc=osm&town=bendigo_real` silently fell back to Melbourne. F closed the gap: the barrel
|
||
(`index.js`) re-exports `registerTownCache`/`validateTownCache`; the shell fetches `assets/towns/<TOWN>.json`
|
||
+ registers it before `generatePlanFor` when the town isn't a checked-in fixture. **Fail-soft** — 404 /
|
||
invalid / `?noassets` → plan_osm's fixture fallback; the scout data is opt-in, never on the default or
|
||
`?classic` path. Verified: `?plansrc=osm&town=bendigo_real` → **Bendigo, 9 shops, a 3-venue district, 0 errors**.
|
||
|
||
### The town matrix (`tools/qa/town_matrix.py`, opt-in `qa.sh --matrix`) — the v4 evidence
|
||
Boots every town on the DEFAULT boot and gates boot / determinism / budget / district / `?noassets`. **All
|
||
10 × 5 green:**
|
||
|
||
| town | shops | budget (draws/tris) | district |
|
||
|---|---|---|---|
|
||
| synthetic/20261990 · 1234 | 493 · 486 | 124d/57k · 147d/71k | 3v ✓ |
|
||
| osm/melbourne · katoomba · silverton | 95 · 19 · 12 | 216d · 70d · 70d | 3v ✓ |
|
||
| real/bendigo · castlemaine · fremantle · katoomba · newtown | 9 · 6 · 21 · 20 · 18 | 82 · 86 · 159 · 69 · 93 d | 3v ✓ |
|
||
|
||
The headline: **the existing CityPlan contract eats real Australian geography** — Castlemaine at the 6-shop
|
||
floor still lands a 3-venue district; every real town is deterministic, in-budget, and silent under
|
||
`?noassets`. No new game systems — exactly the bounded scout the round asked for. This table is the evidence
|
||
John charters **v4 = THE REAL MAP** on.
|
||
|
||
### Gotchas
|
||
- **A published cache + selfcheck ≠ a bootable town.** The node sweep proved the *plan* generates; the
|
||
*browser* boot needed the shell to fetch+register the cache. Classic integration seam — F owns it.
|
||
- **`?noassets` can't fetch a real cache**, so a real-town + noassets boot correctly falls back to a fixture
|
||
(still silent-and-fine). The matrix's noassets gate tests that fallback, not the real town — as it should.
|
||
|
||
---
|
||
|
||
## 18. Round 18 — v4.0-alpha REAL ROADS. **F correctly did NOT run — the gate would have failed by design.**
|
||
|
||
The alpha gave `katoomba_real` its real OSM street geometry (A's schema v2 `roads[]` + graph lift; E's road
|
||
fetch). F's charter deliverable was "as much the failure list as the town" — and F, running the gate, found
|
||
the alpha **not green**, so F held the tag and filed the list rather than shipping a red release. This was
|
||
the round working as designed (Fable's R18 verdict agreed: "F never ran — correctly").
|
||
|
||
**What F caught (all Lane A plan-gen, surfaced with measurements):**
|
||
- **`qa.sh` selfcheck RED** — `katoomba_real`'s real-roads golden **UNPINNED** (the A→E→A finalization
|
||
handshake never fired: E's roads landed *after* A's commits), and the 4 trailing towns' goldens stale
|
||
(pinned on marched output, now roads).
|
||
- **Poster-kerb-clearance RED on all 5 real towns** — the real edge explosion (232 "main" edges) put spine
|
||
posters **1.4–3.9 m into the road**.
|
||
- **Fragmentation** — 20 shops scattered over 799 real edges / 607 m ⇒ no walkable strip; the money-shot
|
||
attempt was a vast empty field (D's finding: 105 components, 42% dead street-metres). And B's separate
|
||
finding: the `BIG_CITY` heuristic misfired (20 shops < 120 ⇒ small-town streaming on a 799-edge graph ⇒
|
||
**280k tris**, breaching the 200k gate).
|
||
|
||
**What was already green (the isolation held):** real roads ride the **cache schema, not a flag**, so
|
||
classic + default + synthetic + fixtures were untouched (`0x3fa36874` frozen) — the alpha changed nothing
|
||
outside the flagged path. The pipeline worked; the geometry needed the hardening the alpha existed to reveal.
|
||
|
||
## 19. Round 19 — the v4.0-alpha CLOSE. F: B's budget one-liner (early) + the gate (last).
|
||
|
||
A resolved the findings this round (fragmentation ruling — main component + joins, cull shopless islands;
|
||
poster clamp; goldens pinned; junction-protected DP), D re-measured and verified, C re-soaked, B shipped the
|
||
town selector. F ran two things:
|
||
|
||
1. **B's `BIG_CITY` one-liner** (`index.html`, landed EARLY per the order so D/B re-measures saw it):
|
||
`BIG_CITY = shops > 120 || (streets.edges > 200)`. A real town can have few shops but a huge road graph;
|
||
the graph now trips big-city streaming. **Verified: real Katoomba 799 edges → 61 draws / ~12k tris (was
|
||
280k), 0 errors.** B proved it; F owns `index.html` so F landed it.
|
||
2. **The alpha gate + the roads dimension** — `town_matrix.py` grew a `roads` gate: a real cache (schema v2
|
||
with `roads[]`) must build the REAL street graph (`norm.mode === 'roads'`, read via the plan_osm
|
||
`opts.report` sink), while synthetic/fixture towns stay marched/synthetic by design. Plus the classic +
|
||
default gates prove the alpha is still isolated to the cache path. Money shot: **Katoomba's real main
|
||
street** — asphalt + lane markings, footpaths, a pedestrian crossing, a walking crowd, gig posters,
|
||
streetlamps, B's town selector in-frame — PROCITY's look on real bones, in budget.
|
||
|
||
### The honest notes on the alpha (two, both filed for v4.0-beta)
|
||
1. **Density.** Real Katoomba's 19-shop cache is genuinely spread ~1 shop per real edge (`shopsOnBest = 1`),
|
||
so the alpha is a real *street* with the town's life on it, not a dense retail strip — that density is a
|
||
beta concern (more shops per cache, or focusing the retail spine).
|
||
2. **One open budget finding (non-blocking).** The matrix's worst-case view (`venue_night` — NIGHT, the lit
|
||
venue block, all posters up) measures **200,965 tris on `katoomba_real`, +965 over the 200k soft ceiling
|
||
(0.5%)**. Driver: the poster count scales with the real *edge* count (438 posters for 19 shops), not shop
|
||
count. Normal play (the midday money shot) is 121k — well under. `qa.sh --strict` is GREEN; this rides
|
||
the opt-in `--matrix` warn-gate, not a blocking gate. Beta hardening: A caps posters per real town.
|
||
The alpha's job was **real roads that boot, stream in budget in play, place a district, and read as a real
|
||
place** — that shipped; the venue_night stress view is the one filed edge.
|
||
|
||
---
|
||
|
||
## 20. Round 20 — v4.0-beta THE FIVE TOWNS (density). **F never ran — the honest record.**
|
||
|
||
R20 widened the towns (E: katoomba 20→80, five towns to 292 shops, "subtle" enforced by construction),
|
||
proved the directive from the crowd side (D's A/B: heroes kept footfall 17→19 while the street got 3.8×
|
||
busier), capped posters 438→31 (A), and ruled the tram by shop adjacency (B: the alpha tram fronted **zero
|
||
shops on 4 of 5 real towns** — "it really was a highway bus"). Excellent round.
|
||
|
||
**F did not run, and there was no tag.** Unlike R18 — where F ran the gate and *held* on evidence — R20's F
|
||
slot simply never executed. The round also left C's density door-soak undone, B's selector still hardcoding
|
||
`hud.js:11` instead of E's index, and D's relocation finding (katoomba's pub 1,000 m from the retail cluster
|
||
— a gig with no passing crowd) without an owner. R21 closed all four.
|
||
|
||
## 21. Round 21 — the v4.0-beta CLOSE. F: the beta gate + the tag (ledger #6).
|
||
|
||
Waves ran [A, B, E] → [C, D] → [F]. A biased `pickVenues` toward retail-cluster adjacency (D's finding) and
|
||
re-pinned once; B derived the selector from E's `index.json`; C soaked the five towns' doors at density; D
|
||
verified the relocation (**A's bias PASSES**); E pulled 22 towns / 1192 shops forward as non-gating data.
|
||
|
||
### The matrix — 10 towns × 7 gates (`--matrix`)
|
||
`boot · determ · district · roads · tram · noassets` **green on all ten**. Two budget flags (below).
|
||
Selfcheck **154141/154141**, `qa.sh --strict` GREEN.
|
||
|
||
### Tram (#6c) — B's R20 ruling verified exactly
|
||
| town | verdict |
|
||
|---|---|
|
||
| newtown_real | **runs, 16 stops** (B's phantom-stop fix: was 149) |
|
||
| castlemaine_real | runs, 10 stops |
|
||
| katoomba / fremantle / bendigo | **fenced** — 3 / 2 / 1 shops fronted, all < the 5-shop floor |
|
||
|
||
New seam: the shell now exposes `window.PROCITY.tram` — B built `routeInfo` *for F's smoke* but nothing
|
||
published it. F owns index.html, so F wired it. The matrix reads `{fenced, stops, shopsFronted}`.
|
||
|
||
### ⚠ The venue_night re-measure (#6b) — RECORDED, and the premise came out false
|
||
The brief expected A's poster cap to close F's R19 `+965`-tri edge. **It did not — it widened:**
|
||
| | R19 | R21 |
|
||
|---|---|---|
|
||
| katoomba_real venue_night | 200,965 (+965, **+0.5%**) | **221,935 (+21,935, +11%)** |
|
||
| bendigo_real venue_night | — | 201k (**+0.5%**) |
|
||
| fremantle / newtown / castlemaine | — | 184k / 179k / 112k ✓ |
|
||
|
||
**A's cap worked** (posters 438→111). The cause is the *density widening*: katoomba 19→72 shops + D's 3.8×
|
||
busier street more than ate the savings. **Draws are fine everywhere** (151/300 — half budget); this is
|
||
tris-only, at the worst night stress view. Not a blocking gate (it rides the opt-in `--matrix` warn), and
|
||
the brief's instruction was "record the new number" — recorded. **Filed as R22's first item**, where the
|
||
tri-diet backlog (instrument LOD · tram tri diet) already lives. katoomba is the outlier: a 966-edge graph
|
||
carrying 72 shops.
|
||
|
||
### Ruling (#6d) — selector gating: **always in the HUD**, not `?dbg`-gated
|
||
THE REAL MAP's product *is* the towns; gating their discovery behind a debug flag would hide the epoch's
|
||
headline feature. It's unobtrusive, shows friendly names, and lives in the HUD overlay. **Corollary (F's own
|
||
gap):** money shots must hide the HUD — the R19 alpha shot had the selector in-frame because F's harness
|
||
screenshotted the full viewport. The beta shot hides it.
|
||
|
||
### The beta money shot (#6e)
|
||
[`docs/shots/v4_beta/fremantle_mainstreet.png`](../shots/v4_beta/fremantle_mainstreet.png) — Fremantle's real
|
||
street at golden hour: buildings lining both sides down the corridor, footpaths, a crossing, gum trees,
|
||
streetlamps, and a genuinely busy crowd (the density widening, visible). 126 draws / 167k tris, HUD hidden.
|
||
Posed by projecting the densest **spatial** shop cluster (10 shops within 60 m) onto its front edge and
|
||
standing on the road — per-edge shop counts are ~1 on a real graph, so "densest edge" is the wrong pose
|
||
heuristic for real towns.
|
||
|
||
---
|
||
|
||
## 22. Round 22 — **v4.0: THE EPOCH CLOSE.** F: the re-measure, the selector, the tour, the tag.
|
||
|
||
### The blocking gate — CLOSED (#1b)
|
||
F's R21 re-measure made the tri diet blocking ("release rounds don't ship known breaches"). B diagnosed
|
||
layer-by-layer, the driver traced **A → E** (A's own commit said so: "B's numbers say the fix is E's"), and
|
||
**E fixed it**. F re-measured:
|
||
|
||
| `venue_night` | R21 | R22 |
|
||
|---|---|---|
|
||
| katoomba_real | 221,935 (+11%) | **108,365** (−51%) |
|
||
| bendigo_real | 201k (+0.5%) | **124,682** |
|
||
| fremantle_real | 184k | **88,397** |
|
||
|
||
**Worst 124,682 — the 200k stress edge is closed.** Selfcheck **161,300/161,300** (A's 36 goldens + the
|
||
23-town pack). The measure-first loop worked end to end: F measured, B diagnosed, E owned, F re-measured.
|
||
|
||
### `DBG.enterShop(selector)` (#3 — Lane G's ask)
|
||
Grew from id-only to a selector: id · registry type · exact name · name substring, resolved most-specific
|
||
first so an id-shaped string can't be shadowed. `DBG.enterShop('record')` / `('The Exchange Hotel')` / `(116)`
|
||
— cross-repo QA one-liners on any of 23 towns without an id lookup.
|
||
|
||
### The release tour (#4) — `docs/shots/release_v4/`
|
||
Five frames: the Fremantle golden-hour hero · the Fitzroy mecca · the Toowoomba thin tail · the synthetic
|
||
control · the `?classic=1` covenant. Every real-town frame poses through **F's R21 cluster primitive**
|
||
(project the densest SPATIAL cluster onto its front edge, stand on the road); B's R22 bookmark fix anchors
|
||
on the same retail heart, so tour and bookmarks agree by construction. HUD hidden throughout.
|
||
|
||
**The primitive earned itself twice.** First cut of the tour posed via `DBG.shot('street_noon')` and the
|
||
**mecca came out as bare bitumen** — the same trap as R21. Re-posed through the cluster primitive, Fitzroy
|
||
reads (7 shops/60 m retail heart, trees, benches, crowd). *Per-edge is always the wrong heuristic on a real
|
||
graph.*
|
||
|
||
### ⚠ The honest thin-tail finding (filed for v4.x)
|
||
The brief asked for a thin-tail frame as **"proof the floor holds."** It doesn't, and the frame says so:
|
||
**Toowoomba reads as an empty road, not a town.** The cause is a RATIO, not a count —
|
||
|
||
| town | shops / edges | shops per edge |
|
||
|---|---|---|
|
||
| fremantle (hero) | 79 / 1,056 | 0.075 |
|
||
| fitzroy (mecca) | 139 / 2,408 | 0.058 |
|
||
| **toowoomba (thin tail)** | **12 / 1,459** | **0.008** — 7–9× sparser |
|
||
|
||
Toowoomba is a real city whose *secondhand* cache is 12 shops, spread across its full road graph. The floor
|
||
isn't "≥N shops" — it's **shops-per-street-metre**. v4.x options: clip the graph to the retail core for
|
||
thin caches, or set a ratio floor in the pack. The frame ships **labelled honestly** rather than swapped for
|
||
a flattering town — same discipline as the R21 re-measure that got worse and said so.
|
||
|
||
---
|
||
|
||
## 23. Round 23 — v5.0-alpha THE FIRST REAL CRATE. **F HELD the tag — the crate has no street.**
|
||
|
||
**Verdict: `v5.0-alpha` NOT tagged.** The round's headline artifact is real and good — G's 120 real
|
||
records off a real shop's POS are in the tree, byte-identically re-bakeable. It cannot be reached from
|
||
the game. `#7a` (riffle the real crate, buy it) is not a gate that failed; it is a gate that **cannot be
|
||
run**. R18's law: *release rounds don't ship known breaches.*
|
||
|
||
### What F did run
|
||
|
||
| gate | result |
|
||
|---|---|
|
||
| **`qa.sh --strict --matrix`** | **7 passed · 0 failed · 0 warn** — QA GREEN |
|
||
| **town-matrix** | **MATRIX GREEN — 10 towns × 7 gates**, all green |
|
||
| citygen selfcheck | **ALL GREEN 152,015/152,015** (was 161,300 — E retired toowoomba) |
|
||
| flags harness (classic regression · default boot · STRICT) | **GREEN, 0 warnings** — the covenant holds through the merge |
|
||
| manifest validator · scaffold · consistency · no-giants | GREEN |
|
||
| tier 0 — `?noassets=1` | **zero fetches, zero console errors** — the offline law holds |
|
||
| tier 0 — default boot, godverse town | town loads (18 real Newtown shops), stock → parody |
|
||
| tier 1 (v2, town-wide) — `?stock=real` | 350-item pack resolves, real cover art |
|
||
| **tier 1 (v5, per-shop) — the round's deliverable** | **NEVER FETCHED** |
|
||
| tier 2 | N/A by law (no server code this round) |
|
||
|
||
### The merge that never executed (F did it — charter law #5)
|
||
Fable's mid-round merge never ran: `main` had **no `web/assets/stock_godverse/**` at all**, so the branch
|
||
F was asked to gate and tag did not contain the round's headline. The review had happened (the shared
|
||
checkout was parked on `lane-G-godverse` for it) and C verified the atlas from a temp worktree —
|
||
deliberately pre-merge — and passed. Only the `git merge` was missing. F executed it as the integration
|
||
lane, disjointness **verified before merging, not assumed** (G: `G-progress`, `G3 doc`,
|
||
`pipeline/godverse_*`, `stock_godverse/**` · main-since-fork: `C-progress`, `LANE_C_PUB` · overlap: none).
|
||
Zero conflicts, exactly as law #5 predicted. → `43b035a`.
|
||
|
||
### ⚠ FINDING 1 — the atlas contract contradicts itself. Nobody was wrong; the contract was.
|
||
|
||
| | says the index is |
|
||
|---|---|
|
||
| **C's published contract** — `LANE_C_PUB:271` | `stock_shop_<godverseShopId>_index.json` |
|
||
| **C's own runtime loader** — `stockpack.js:25` | `` fetch(`${base}stock_${type}_index.json`) `` |
|
||
|
||
**These are mutually incompatible.** An atlas named per the published contract can *never* be loaded by
|
||
the code that is supposed to load it. G shipped `3962749/stock_record_index.json` — **runtime-compatible**,
|
||
which is exactly why C's base-override verification passed. E globbed `stock_shop_*_index.json` —
|
||
**contract-compatible**, which is why E's validator never saw the atlas. Two lanes implemented opposite
|
||
halves of a self-contradicting spec and both were correct.
|
||
|
||
**F's recommendation (measured, not preferred):** amend the contract to G's shape —
|
||
`stock_godverse/<godverseShopId>/stock_<type>_index.json`. It is the only shape the runtime can load,
|
||
it keys per-shop by directory, and it costs G nothing (already emitted). E's glob follows. C owns the
|
||
wording (LANE_C_PUB); this is the amendment ledger #3 anticipated.
|
||
|
||
### ⚠ FINDING 2 — the gate built to catch this was blind, and green
|
||
|
||
`validate_atlas.py` globs for a filename that cannot exist at runtime, finds nothing, prints *"no
|
||
per-shop atlases yet — nothing to validate"* and **`return 0`**. `validate_manifest.py:158` propagates
|
||
that green (its comment states the intent: *"No atlases yet ⇒ clean pass"*). So the round's QA gate passes
|
||
whether the atlas is **absent, misnamed, or perfect** — it has never once looked at one. E built it in
|
||
wave 1 with no atlas to test against; on first contact (F, at close) it was blind.
|
||
|
||
The flaw is precise and the fix is a one-liner: the validator **conflates "no atlases exist" with "no
|
||
atlases matched my glob."** The first is a legitimate clean pass; the second is exactly the failure it
|
||
was written to catch. Cross-check the directory — if `stock_godverse/*/` is non-empty but no index
|
||
matched, that is an **ERR**, not a pass. *A gate that returns 0 on an empty set had better be certain the
|
||
set is empty.*
|
||
|
||
Pointing it at G's index by hand (filename drift removed) shows the drift is **deeper than the name**:
|
||
- **`ERR missing/empty provenance ['license','attribution','generator','snapshot']`** — but G *carries*
|
||
`generator` and `snapshot`. E's line 68 reads `idx.get(f)` — **top-level** — while E's own docstring
|
||
(line 14) specifies them **nested under `provenance`**, which is where G put them. **E's code
|
||
contradicts E's doc**; G's atlas would fail even with E's exact field names.
|
||
- **`licence` vs `license`** — G used the British spelling; E's `REQUIRED_PROV` wants American.
|
||
- **`attribution`** — G folds the substance into `licence.covers`; E wants the field.
|
||
- **`WARN 2 atlases — C's rule is 1/shop`** — E built to C's *pre-correction* ceiling; C amended §7.2 to
|
||
`≤2 atlases @≤2048²` in the same wave. Stale by one round.
|
||
|
||
### ⚠ FINDING 3 — `#7a` would have passed vacuously
|
||
The brief says: *"boot `newtown_godverse` with `?stock=real` … riffle — **real covers render**."* They do.
|
||
F walked into **The Vintage Record** (a real Newtown shop, on its real road) via `DBG.enterShop`, and the
|
||
crates filled with **real cover art** — from E's **350-item town-wide v2 pack** (`Sunburnt Milk Bar Vol. 2`,
|
||
`Midnight Departure`), the same pack every record shop in every town has had since v2. **Not** Monster
|
||
Robot Party's 120. Network confirms: every stock fetch goes to `assets/models/`; **zero requests to
|
||
`assets/stock_godverse/`, ever.** Had F eyeballed the gate as written, it would have gone green and proven
|
||
nothing. *Two vacuous gates in one round — same species: both test the presence of a thing rather than
|
||
the identity of it.*
|
||
|
||
### The exact failure list for R24 — three gaps, none of them F-only
|
||
|
||
1. **The crate has no street.** [E + G] No town cache contains shop `3962749`; `redhill_real` does not
|
||
exist. Needs a `build_towns.py` TOWNS entry (~`center (-27.4553, 153.0064), span_km 2.4`) + one
|
||
outward-facing Overpass fetch → `redhill_real`, then `godverse_town.py redhill_real redhill_godverse`
|
||
with monsterrobot injected into `shops[]`. **F did not do this: E's file, G's generator, and an
|
||
outward-facing fetch — inventing town data is not F's to invent.**
|
||
2. **No identity field exists to key an atlas by.** [A + G] Verified in the live runtime: *every* shop in
|
||
`newtown_godverse` reports `godverse_id: null` — the cache schema carries no such field at all. C's
|
||
rule is *"C consumes whatever identity field lands, C does not invent it"* — so nobody has landed it.
|
||
A owns the CityPlan/cache contract; G emits it.
|
||
3. **The runtime wire.** [C + F] `stockpack.js` keys `_packs`/`_resolved` by **type alone** and returns a
|
||
cache hit before `base` is ever read (`stockpack.js:24`), so two stocked shops collide **and fail-soft
|
||
breaks** — an unstocked shop of a cached type inherits another shop's crate. C specced the seam (key by
|
||
`type+base`, per-shop preload on entry, LRU=1 + dispose) and owns the file; F then passes the per-shop
|
||
base at `interior_mode.js:192` and drops the town-wide preload at `index.html:199`.
|
||
|
||
**F deliberately did not build its half of #3 this round.** It is untestable while #1 and #2 are open
|
||
(no street to walk to, no identity field to pass), and F would be writing against three-quarters of a
|
||
spec that does not exist yet. Filed, not guessed. R24 order: **A+G (identity) → E (the town) → C (the
|
||
keying) → F (the wire + re-gate)**, and the crate walks.
|
||
|
||
### Also found
|
||
- **The godverse town is in no gate.** `newtown_godverse` is a shipping town cache that boots fine (F
|
||
verified: 18 real shops, real roads) but the town-matrix covers `newtown_real`, not it — so the one
|
||
town the whole epoch is built on is ungated. F's matrix, F's gap; it lands with the R24 re-gate rather
|
||
than a held round.
|
||
- **A's spacing warn earns immediately:** with toowoomba retired, **`ballarat_real` now warns at median
|
||
255 m > 150 m**. The thin tail has a second member — A's warn is doing exactly its job (warn, not error;
|
||
E curates). Filed for E's next curation pass.
|
||
- **`?noassets=1` + `&town=<key>` silently boots a *different town*** (the fixture, not the cache) — F's
|
||
own R17 wiring gates the cache fetch on `!NOASSETS`, correctly (noassets = zero network), but the tier
|
||
ladder's *"same shop under `?noassets`"* can't mean the same shop for a real town. Tier 0 for **stock**
|
||
is provable on the default boot; tier 0 for the **town** is a fixture fallback by design. Wording, not a bug.
|
||
|
||
---
|
||
|
||
## 24. Round 24 — v5.0-alpha THE ALPHA COMPLETION. **The crate WALKS. F held the tag anyway — A's gate.**
|
||
|
||
**THE CRATE WALKS.** You can boot `redhill_godverse`, walk down Musgrave Road into **Monster Robot
|
||
Party**, riffle **its own real crate**, and buy a real record for its real price. Proven falsifiably,
|
||
not by eyeball:
|
||
|
||
| assertion | result |
|
||
|---|---|
|
||
| room resolved base | **`assets/stock_godverse/3962749/`** (not the town-wide base) |
|
||
| **title equality** | **120/120 in Monster Robot's atlas · 0/120 in the generic town pack** |
|
||
| **texture provenance** (the pixels) | **2 atlases, both `stock_godverse/3962749/stock_record_atlas_0*.webp` · 0 from the generic pack** |
|
||
| network | `stock_godverse/3962749/` index + both atlases, `200` |
|
||
| **buy** | `rec_0000` *"Abstract Latin Lounge II (Part II)"* @ **$15** → cash **191 → 176**, debited exactly 15, count 0→1 |
|
||
| gone-this-session → back next boot | reboot ⇒ wallet 191/0 restored (no persistence layer exists) |
|
||
|
||
**Tier ladder, re-proved end to end:** tier 0 `?noassets` on a godverse town → **zero network requests**,
|
||
no base, no pack (the offline law survives the wire) · **soft fall live**: `Restoration Station`
|
||
(keyed 2289, no atlas) → `404` → null → parody, and the boot log shows those 404s happening in
|
||
production · **no cross-contamination**: after visiting other shops the real crate still resolves its
|
||
own 120 · **plain towns untouched**: `newtown_real` → 0 keyed, base `null`, 350 items, textures from
|
||
`models/` — **F's wire is a complete no-op off the godverse path**, byte-identical to pre-v5.
|
||
|
||
### What F built (#6a)
|
||
`godverseBaseFor(shop)` — **exported and used by both** the shell's boot preload and the room's lookup,
|
||
deliberately: C's cache identity is the LITERAL `(type, base)` pair, so two sides building that string
|
||
differently would miss the cache and drop a real shop to parody — a bug that looks exactly like "no
|
||
stock" and would sail straight past a covers-render eyeball. One function, one string, one key.
|
||
Boot-preloads keyed shops (`getStockPack` is sync and the room is built the instant the door opens, so
|
||
a pack first requested at `enter()` is not resolved in time), gated on `stockReal` and never on the id
|
||
alone, so `?noassets` fetches nothing.
|
||
|
||
### ⚠ FINDING 1 — the honest-gate spec was itself vacuous. The law's first application broke the law.
|
||
The brief specified #7a assert **"rendered titles ∈ the atlas index (id equality, not 'covers render')."**
|
||
**Id equality cannot discriminate.** Measured: both packs number their items **positionally** —
|
||
`rec_0000, rec_0001, …` — so the atlas's ids are `rec_0000..rec_0119` and the town-wide pack's are
|
||
`rec_0000..rec_0349`. **All 120 atlas ids are also generic-pack ids (120/120 overlap).** An id-equality
|
||
gate goes **green on the generic pack** — the exact failure the law was written to stop, reproduced
|
||
inside the fix for it. *Ids here are slot numbers, not identity.*
|
||
**What actually discriminates:** titles (**0 overlap**) and, strongest, the **texture URL** — the file
|
||
the GPU samples. A pack that merely looks right cannot fake the URL its pixels come from. F's gate
|
||
asserts on both, and `DBG.stockInfo()` exposes them so the assertion is scriptable, not eyeballed.
|
||
|
||
### ⚠ FINDING 2 — F's own selector was lying about the epoch's shop
|
||
`DBG.enterShop(3962749)` returned **`no shop matching`**. A GODVERSE shop has *two* ids — its plan id
|
||
(local, renumbered per town: Monster Robot is **8**) and its `godverseShopId` (**3962749**, the real POS
|
||
id, which is what the atlas, every G-side query and every cross-repo bug report names it by). F's R22
|
||
selector only knew the first. Fixed (plan id still matches first, so no existing call shifts). *The
|
||
harness F shipped for Lane G could not name the one shop the epoch is built on.*
|
||
|
||
### The 8 reds — F did NOT clear them, and did not touch A's file
|
||
`156,193/156,201`. They are **not** product defects; F proved the product correct above. They split:
|
||
|
||
**2 — goldens moved by G's re-emit** (`newtown_godverse` base `0xcf69b387`, gig `0x1e266f60`), exactly as
|
||
ledger #2 intends: adding `godverseShopId` changes the cache, so it changes the plan. A ran *before* G,
|
||
so A could not pin what G had not yet emitted. Plus 4 UNPINNED warns (`redhill_godverse`,
|
||
`redhill_real`). **F did not re-pin**: if conflict #2 resolves by changing the caches, these move again,
|
||
and the amendment law wants the spec change and the pin in ONE commit. Pin them with the resolution.
|
||
|
||
**6 — conflict #2: A's identity sweep contradicts Fable's own standing Ruling 2.** Ruling 2
|
||
([FABLE_TO_LANE_G.md:33](FABLE_TO_LANE_G.md)) *mandates* mixed caches — "merge census heroes **WITH** the
|
||
donor's texture shops (cafes/bakeries…)". A cafe lifted from OSM has no GODVERSE identity and never will;
|
||
minting one would invent identity, which is charter risk #3 itself. A's sweep asserts
|
||
`keyed === cache.shops.length` — i.e. that no such cache may exist. It is a gate authored in the same
|
||
wave as the thing it gates, with no mixed cache in existence to test against: **the identical species as
|
||
E's blind validator in R23**, one round later, in the opposite direction. G measured it and escalated it
|
||
to "A's call, Fable's ruling"; nobody ruled; it arrived at F.
|
||
|
||
**F's measurement of the hazard the gate guards: it does not exist.** `redhill_godverse` 10 keyed /
|
||
**10 unique**; `newtown_godverse` 18 keyed / **18 unique**; **zero duplicates**. And partial keying does
|
||
not mis-stock — F watched shop 2289 fall soft to parody in the browser, which is precisely what charter
|
||
risk #3 prescribes ("fail-soft to tier 0, never mis-stocked"). **A's own two checks already disagree**:
|
||
`validateTownCache` reasons it out correctly — *"Missing = tier 0 (soft). Duplicate = mis-stocked (hard).
|
||
They are not the same failure"* — and warns; the sweep hard-fails the same condition.
|
||
|
||
### ⚠ FINDING 3 — the uniqueness check is a silent risk-#3 hole (new; G did not catch this one)
|
||
`ok(new Set(seated.map(s => s.godverseShopId)).size === seated.length, 'godverse ids unique')`. On a
|
||
mixed cache every texture shop maps to `undefined`, and **all of them collapse into ONE Set entry** — so
|
||
the check fails on the undefineds, *not* on a duplicate, and it **cannot detect a real duplicate** while
|
||
any shop is unkeyed. It is the arm A called "the arm that earns its keep" for charter risk #3, and on
|
||
every cache Ruling 2 mandates it is **structurally incapable of doing its job, in both directions**.
|
||
Whoever fixes the coverage assert must not paper over this one: uniqueness belongs **over the defined
|
||
ids** (`seated.filter(s => s.godverseShopId !== undefined)`), where it would actually bite. That change
|
||
makes the risk-#3 guard **stronger**, not weaker.
|
||
|
||
### Why F held the tag — and why this hold is not R23's
|
||
R23 held because the **product** was broken. **R24's product is finished and proven.** What is red is a
|
||
**gate**, in **Lane A's file**, on a **schema question that is A's to answer** ("what marks the godverse
|
||
layer?" has at least three valid designs). F did not resolve it, for one reason above all: **F is the
|
||
party that benefits.** F changing a red gate to green, in another lane's file, to unblock F's own tag,
|
||
on the one check guarding "never mis-stocked" with Monster Robot's real crate as the prize — that move
|
||
would retroactively devalue every green F has ever reported. F's only asset is that F never shades a
|
||
result. The cost of waiting is an hour; the cost of F being quietly wrong is the charter's worst risk.
|
||
**Recommended (A's call): the sweep follows A's own validator — missing ⇒ warn (tier 0, soft), duplicate
|
||
⇒ error (hard, over defined ids), coverage asserted over the keyed layer, not the inherited one.** Then
|
||
A pins the four goldens in the same commit and F re-gates and tags. It is one commit's work.
|
||
|
||
### ⚠ FINDING 4 — the licence flag stopped reaching a human, and the gate says OK
|
||
R23's blind validator is **fixed** — E's `validate_atlas` now finds and validates the atlas
|
||
(`3962749/stock_record_index.json · Monster Robot Party · items=120 atlases=2 px=2048 OK`). But G's
|
||
conflict-#1 leftover is real and still open: `validate_atlas.py:198` prints from
|
||
`provenance.licence` — **British only** — while G's re-emit carries **`provenance.license`** (American,
|
||
per C's corrected contract). So `lic = {}`, and the licence line **silently never prints**. E's *check*
|
||
(:105) accepts either spelling, so the gate goes **OK**; only the evidence vanished.
|
||
|
||
That evidence is not decoration. E's own comment at :199 says *"the licence is PRINTED — clearing it is
|
||
a human call"* — it is the **mechanism** by which charter law #3 reaches a person, and the string it
|
||
failed to print is **`FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE`**, on an atlas of a real shop's real
|
||
product photographs with real artist names. A gate that is green while the one thing it exists to
|
||
surface is invisible is the vacuous-gate law's own species, wearing the licence law's coat.
|
||
**Fix (E, one line, G already specified it):** `.get("licence") or .get("license")` at :198.
|
||
|
||
**F did not patch it.** It is E's file, F is not blocked on it (the tag is held regardless), and G
|
||
already routed it. Filing beats reaching across a lane boundary for a one-liner — the same restraint F
|
||
applied to A's gate, for the same reason.
|
||
|
||
### ⚠ FINDING 5 — F's own gate cried the loudest wolf we own (F's bug, F fixed it)
|
||
The classic-regression gate reported **`plan fingerprint != golden 0x3fa36874 — determinism/prime-law
|
||
broken`**. The covenant was never touched (`0x3fa36874`, node-side, unmoved; G said so too). F wrote
|
||
this gate in R16 with `if hash_ok and r.returncode == 0` — **two assertions, one verdict, reported with
|
||
the fingerprint's message** — so *any* unrelated red anywhere in Lane A's selfcheck (here: 8
|
||
godverse-identity checks in a different town) screams that the byte-identical-forever covenant is
|
||
broken. Fixed: two assertions, two verdicts, and the selfcheck failure now names its own lane and
|
||
quotes its own reds. *A gate must name what it actually measured — the same law as the vacuous gates,
|
||
one turn further round: it isn't enough to fail for a reason, you have to fail for YOUR reason.*
|
||
|
||
### ⚠ FINDING 6 — the id spaces collide, and it bit F's own selector inside the anti-vacuous gate
|
||
Plan ids are per-town (1..N); godverse ids are real POS ids (31, 767, 3962749). **They share a numeric
|
||
space.** Silky Oak Furnature's godverseShopId is **31** — and another shop's *plan* id is also **31**.
|
||
F's first cut at the R24 selector tried plan id first, so `enterShop(31)` silently entered **Wholefood
|
||
Cafe** instead. The smoke's soft-fall assertion then **passed on an unkeyed cafe** — a vacuous pass,
|
||
inside the gate F wrote to stop vacuous passes, in the round whose whole theme is *ids that aren't
|
||
identity*. Fixed both ends: `'g:<n>'` selects a godverse id explicitly; a bare number stays the plan id
|
||
(back-compatible) and now **reports the collision** rather than choosing quietly; and the smoke enters
|
||
via `g:` and asserts the shop it landed in **is the one it asked for** before asserting anything about it.
|
||
|
||
*The lift itself is clean* — F checked: cache 10 keyed → plan 9 keyed (Empire Revival didn't seat), every
|
||
one a real secondhand shop. **No cafe is keyed. There is no mis-stocking.**
|
||
|
||
### ⚠ FINDING 7 — fail-soft-by-404 vs the 0-console-errors law (a contract gap, filed not papered)
|
||
F's boot preload must resolve a keyed shop's pack *before* the door opens (`getStockPack` is sync and
|
||
the room builds on the same tick), so it probes every keyed shop. 9 of Red Hill's 10 have no atlas →
|
||
`404` → null → parody. **That is the fail-soft law working**, and it is also **8 console errors on every
|
||
godverse boot** — a browser-level log no `.catch()` can suppress. The house law is zero.
|
||
**The tension is structural: you cannot probe blind and stay silent.** The fix is not to loosen the law
|
||
but to stop probing for what isn't there: **an atlas index in G's namespace** (or a `hasStock` flag on
|
||
the cache's shop entries, A+G) so the shell preloads exactly the shops that have atlases — 404s then
|
||
mean a genuinely missing file, which is what fail-soft is *for*. **Filed as a contract ask, not
|
||
invented.** Until it lands, F's smoke counts the probes, names them, and fails on any other error —
|
||
tolerance stated out loud, never blanket-ignored.
|
||
|
||
---
|
||
|
||
## 26. Round 26 — v5.0-beta EVERY CRATE DIFFERENT. F: the manifest consumed, the tolerance retired.
|
||
|
||
### The 404 tolerance is gone — and that is the point
|
||
R24's wire preloaded a pack for **every keyed shop** and ate a `404` for each one with no atlas. That was
|
||
the fail-soft law working, and it was also **console errors on every godverse boot**. F's smoke could only
|
||
cope by *naming* the probes — and **a tolerance is a confession, not a fix**. F filed the real fix as a
|
||
contract ask rather than inventing it: *an atlas index in G's namespace, so we never ask for what isn't
|
||
there.* G built it. **F now consumes it** (`assets/stock_godverse/index.json` → `{godverseShopId, types[],
|
||
sourcing}`): the shell preloads only manifest-listed `(shop, type)` pairs, and the gate asserts **ZERO
|
||
404s with no attribution exceptions**. The 404s are gone because there is nothing absent to ask for — not
|
||
because the gate stopped looking. Fail-soft still covers the case that matters (manifest says yes, file is
|
||
gone), which is what fail-soft was always *for*. Fetched only when the town has keyed shops, so a plain
|
||
town stays byte-identical: no manifest, no fetch.
|
||
|
||
### Distinctness — G reported 15/15; F measured it, from the indexes of record
|
||
Not by entering one lucky shop: **105 pairs across all 15 keyed crates, 0 sharing a title**; **0 crates
|
||
overlapping the generic v2 pack**; **0 ids reused across crates** — G's sku fix holds at scale, which is
|
||
the R24 vacuity dead at the source rather than papered over in F's gate. 4 crates in Red Hill + 11 in
|
||
Newtown = the manifest's 15 exactly, and the manifest lists nothing that isn't on disk.
|
||
|
||
### Sourcing, not tier — C was right and the gate follows C
|
||
C ruled the brief's `tier` field wrong: `tier` is the **charter's ladder rung**, and a mint atlas is *also*
|
||
rung 1 — a mint crate is still a deterministic file, still offline, still tier 1. What differs is where the
|
||
stock **came from**, which is `sourcing: real|mint`. E built to C's contract over Fable's brief on the
|
||
principle that *the contract's author is the authority for the contract's shape*, and Fable ratified it.
|
||
F's gate asserts `sourcing`: every index declares it, every index agrees with the manifest, and **a mint
|
||
crate can never report as real**. The gate prints it per room, because provenance a human never reads is
|
||
provenance that isn't doing anything — the same lesson as R24's silent licence line.
|
||
|
||
*Note for whoever reads the runtime:* C's pack (`stockpack.js`) does **not** carry `sourcing` through —
|
||
the gate reads it from the index of record, which is correct (the index is the artifact; B's street mark
|
||
reads the manifest). Not a gap; recorded so nobody re-derives it.
|
||
|
||
### ⚠ The tolerance came out and immediately caught the leftover
|
||
F wired the **boot** preload to the manifest, ran the gate with the 404 tolerance **deleted**, and it went
|
||
red on exactly one 404: `stock_godverse/31/stock_opshop_index.json`. That was F's *other* probe — R24 also
|
||
preloaded a shop's pack **on entry**, so walking into a keyed shop whose type has no atlas fired a fetch
|
||
that could only ever 404. Removed: the door does a lookup, never a fetch. The manifest is consulted once,
|
||
at boot, and by the time a door opens the pack is either resolved or was never going to exist.
|
||
|
||
The lesson is the round's, not just the bug's: **the tolerance was hiding a second instance of the very
|
||
thing it tolerated.** While the gate said "8 atlas-probe 404s, expected", the ninth would have been
|
||
invisible too. A named exception is still an exception; it earns its keep only until the real fix exists,
|
||
and the moment it does, the exception must die or it starts protecting new bugs.
|
||
|
||
### The gate, green
|
||
`DISTINCTNESS 15 crates / 105 pairs / 0 shared · 0 vs the generic pack · SOURCING 15/15 declared, all
|
||
agreeing with the manifest · Monster Robot Party → sourcing REAL, 120/120 own titles, 2 own textures / 0
|
||
generic · Presents of Mind → sourcing MINT, 16/16 own, 1 own / 0 generic · soft fall silent · ZERO 404s ·
|
||
0 console errors, no attribution exceptions.`
|
||
|
||
### ⚠ THE HOLD — `v5.0-beta` NOT tagged. `atlas-QA FAIL — 14 error(s)`, and it is not the product.
|
||
`qa.sh --strict --matrix` → **6 passed · 1 failed · 0 warn**. The one failure is the manifest validator:
|
||
E's `validate_atlas` rejects **all 14 mint crates** (the real crate passes). Everything else is green —
|
||
selfcheck **ALL GREEN 156,212/156,212** · **MATRIX GREEN 10 towns × 7 gates** · flags harness (carrying
|
||
F's beta gate) · consistency · figure-scale. **One gate, one assertion, 14 identical errors, one line.**
|
||
That ratio is the whole argument for the hold: the beta is finished, and what stands between it and the
|
||
tag is a rule meeting a case it was written before.
|
||
|
||
**The conflict:** `SLOT_ID_PREFIX = "sku_"` (`validate_atlas.py:73`) is a module constant applied to
|
||
**every** pack — E reads `sourcing` at :178 but the slot-id check is **sourcing-blind**. C §7.2a's rule is
|
||
*"derive from the source's own stable key, never position, unique across ALL packs"*, and it specifies
|
||
`sku_<POS sku>` **"for a real-stock (godverse) pack"**. **A mint crate has no POS sku by construction** —
|
||
it was never in that shop's till. Demanding one demands a thing that cannot exist: the same shape as R24's
|
||
sweep demanding a cafe carry a `godverseShopId`, and as the brief's `tier` demanding a rung where the
|
||
question was provenance.
|
||
|
||
**F measured the rule's INTENT, and it holds:** mint ids are `mint_<dealgod listing id>` — a real external
|
||
key, **non-positional**, items sorted by it, the bake seeded (`random.Random(158)` over an id-ordered
|
||
pool), and **0 ids reused across all 15 crates** (F's own pairwise measurement). Stable, unique,
|
||
non-positional. Only the literal prefix fails.
|
||
|
||
**G refused to go green the easy way, and was right to** — worth recording, because it is F's own R24
|
||
finding grown up: *"a POS sku and a dealgod listing id are different id spaces; filing dealgod listing ids
|
||
under `sku_` recreates that bug INSIDE the economy — tier-2's sold-means-gone looks up POS skus, and
|
||
`sku_6031122` (a dealgod id) is indistinguishable by shape from a real sku."* R24's plan-id-31 vs
|
||
godverse-id-31 collision entered the wrong shop; the same collision in the money layer sells the wrong
|
||
record. G caught it **before** shipping rather than after. Emitting `sku_` would have made this gate green
|
||
and the economy wrong.
|
||
|
||
**Why F did not fix E's one line:** identical to R24's conflict #2, and the answer has not changed. It is
|
||
**E's file implementing C's contract**, the question ("does §7.2a's sku rule bind a mint pack?") is **C's
|
||
to answer as the contract's author** — the principle Fable ratified when C overruled `tier` — and **F is
|
||
the party that benefits**, since the fix is the only thing between F and the tag it was sent to place.
|
||
F's whole worth is that F never shades a result. Cost of waiting: one line. Cost of F being quietly wrong:
|
||
a gate that green-lights id-space collisions in the layer that moves money.
|
||
|
||
**The fix (C rules, E lands it, one line):** gate `SLOT_ID_PREFIX` on `sourcing == 'real'`; for `mint`,
|
||
assert `mint_`-prefixed + non-positional + unique — which F has already measured true. Then F re-gates
|
||
and tags. **Everything else for the beta is done and green.**
|
||
|
||
### The fence went in and the hold ended — wave 4
|
||
C ruled and both lanes titled their commit the same thing: **"my rule was the stale thing."** E's one line:
|
||
|
||
```python
|
||
SLOT_ID_PREFIX = {"real": "sku_", "mint": "mint_"} # sourcing-scoped, not one space
|
||
```
|
||
|
||
Fable's ruling — **prefixes are namespace fences** — and E's comment makes G's judgment the law:
|
||
*"`sku_` names a POS copy on a shelf, `mint_` names a dealgod listing standing in for one… a prefix that
|
||
lies doesn't fail a gate, it **SELLS THE WRONG RECORD** out of a real shop to satisfy a minted stand-in.
|
||
Never treat them as one space (**same standing note as plan-ids vs godverse-ids**)."*
|
||
|
||
That closing clause is F's R24 collision — plan id 31 vs godverse id 31, which quietly walked the harness
|
||
into a cafe — promoted from a finding to a standing law, and re-derived independently by G in the layer
|
||
that moves money. The bug F found by entering the wrong shop is the bug G refused to ship by selling the
|
||
wrong record. **The rule bent; the id spaces didn't.** That is the correct direction: a rule is a
|
||
description of an intent, and when a legitimate case fails it, the description is what's wrong — provided
|
||
somebody has measured that the intent still holds. F measured it before the ruling (non-positional,
|
||
seeded, stable, 0 reuse across 15 crates), which is what made the ruling a decision rather than a guess.
|
||
|
||
**`atlas-QA OK — 15 per-shop atlas(es) valid, 15 licence line(s) shown, 0 warning(s)`** — and that second
|
||
number is R24's finding #4 at full scale: fifteen `FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE` lines now
|
||
reach a human, where one silently printed nothing.
|
||
|
||
---
|
||
|
||
## 27. Round 27 — v5.0 THE LIVE CRATE. F: the reader, and the server killed on purpose.
|
||
|
||
### ⚠ RULING NEEDED — "ZERO console errors on server death" is unachievable, and F measured why
|
||
The brief's arm 2: *"kill the server mid-session → next read degrades to tier 1 with **ZERO console
|
||
errors**."* **It cannot.** A `fetch` to a dead host logs `net::ERR_CONNECTION_REFUSED` at the **browser**
|
||
level — no `.catch()` suppresses it, because nothing in JS ever sees it. To log zero, the reader would
|
||
have to know the server is dead **without asking**, which is not a thing. **One refused connection is the
|
||
irreducible cost of discovering death.**
|
||
|
||
This is R26's 404 lesson one layer up, and it has the same shape: *you cannot probe blind and stay
|
||
silent.* R26's answer was to stop probing blind — the manifest told us what existed. There is no
|
||
manifest for "is the server alive"; the only way to know is to ask.
|
||
|
||
**F changed its own assertion, and the new one is STRICTLY STRONGER:**
|
||
- **ZERO errors from game code** (nothing threw, nothing warned) — the part that was always the point.
|
||
- **EXACTLY ONE refused connection per session** — which *proves the circuit breaker*. Without the
|
||
breaker it is one error per shop entry, forever. **"Zero" could never tell a working breaker from a
|
||
broken one, because both fail it.** An assertion that fails on correct behaviour teaches people to
|
||
ignore it — the R24 lesson (A's sweep) pointing at F this time.
|
||
|
||
**F is the beneficiary here** (this assertion is the last thing between F and the tag), so it is filed
|
||
loudly rather than quietly relaxed, with the impossibility measured and the replacement named. Fable
|
||
ratifies or overrules at the retro. The precedent is C's `tier`→`sourcing` and C+E's mint-id fence: the
|
||
lane that owns the thing rules on it **with measurement**, in writing, where it can be argued with.
|
||
|
||
### ⚠ F's own frame assertion was weak — a number without a control is not a measurement
|
||
ARM 2 first reported `worst frame 201ms` and **passed**, because F had written `< 250ms` — an arbitrary
|
||
number. 201ms is twelve dropped frames; if the death caused it, that is a stutter and the arm should have
|
||
gone red. **But entering a shop rebuilds an interior**, which costs the same with or without a server —
|
||
so an absolute threshold measures Lane C's room build, not the network failure. Fixed: the arm now walks
|
||
the *same shop with the breaker tripped* (no network at all) and asserts the **delta**. The question is
|
||
"did the death cost anything", not "is entering fast". *F caught this in F's own gate, one round after
|
||
telling A the same thing.*
|
||
|
||
### What the gate proves (arms 1 + sandbox, unchanged and green)
|
||
`gone[3] → 117 survivors · items[] 120, identity AND ORDER intact · COLLATERAL 0` — one sale removes one
|
||
record, it does not re-stock the shop (C's §8, and the assertion C warned was the cheap one to get wrong:
|
||
both pick-then-omit and filter-the-array hide the sold record, so absence proves nothing). `gone` carries
|
||
the atlas id form **verbatim** — zero transformation, the R26 fence holding. And the sandbox both ways:
|
||
a real sale removes a game record (real→game), while **every write verb answers 501** — G's point, and a
|
||
stronger absence proof than 404: a 404 means a handler ran and declined; **501 means there is no write
|
||
handler in the process at all.** A mint shop 404s: only the one real shop is live, which is the point.
|
||
|
||
**The subject exists only because G built it.** Production `gone` is EMPTY — 0 of crate 550's 120 records
|
||
have sold since 2026-07-01 — so this gate would have passed vacuously forever on live data. G flagged it
|
||
and shipped `make-fixture` before the gate could be born blind. *"A fixture is not a lie; a green assert
|
||
over an absent subject is."*
|
||
|
||
### The tour — 3 of 4 frames, and the 4th is FILED not shipped
|
||
`docs/shots/release_v5/`: **the real crate** (Monster Robot Party's own 120 records in its bins, price
|
||
stickers, the keeper at the counter — the money shot, and it reads), **a mint crate** (captioned MINT:
|
||
plausible, not real — a tour is exactly where that distinction gets quietly lost), and **the classic
|
||
covenant** (Boolarra Heads, `0x3fa36874`, five epochs on). Zero page errors.
|
||
|
||
**The frontage frame does not land, and F is not shipping it green.** Three attempts: the cluster
|
||
primitive posed at the town's densest cluster (which in Red Hill is 2–3 shops/60 m and *does not contain
|
||
Monster Robot*) → a photograph of an empty footpath. Anchoring on the shop's own lot fixed the position
|
||
and still faced **along** the edge → the road, shop out of frame. Facing the lot along its road-normal
|
||
still frames bitumen. **The R21/R22 trap, third and fourth time** — and the lesson has grown: the cluster
|
||
primitive answers *"where is this town's heart"*, which is **not** the question a frontage frame asks.
|
||
Red Hill's heart isn't Monster Robot; a sparse town has no heart to speak of. Anchor on the SUBJECT when
|
||
the frame is about the subject.
|
||
|
||
What's actually wrong is unmeasured, and that is why it isn't shipped: F does not yet know whether the
|
||
building is where the lot says, whether `frontEdge` is the road you'd stand on, or whether the pose needs
|
||
B's own frontage anchor (B built `poseForVenue`/bookmarks for exactly this). **Next: ask B** — B owns the
|
||
frontage and shipped the stocked-shopfront mark this round; F re-deriving B's anchor in a tour script is
|
||
how you get three bad frames. The interior money shot needed none of this: the room poses itself.
|
||
|
||
### ⚠ THE HOLD — `v5.0` NOT tagged. Every real town's shops face away from their street.
|
||
B's micro-task found it while fixing F's frame, and **F's three photographs of empty road are the
|
||
evidence**: it was never the pose. `plan_osm.js:443` aims the lot's **−Z** at the road ("facade (−Z)
|
||
faces road"); `buildings.js:404` draws the facade on the **local +Z face**. Exactly opposite. The
|
||
shopfront — sign, door, R26 crate mark — lands on the **far side**, and the street gets a blank wall.
|
||
**Since R18.** v4.0 shipped on it. Nine rounds of tours shot it. `katoomba_real`'s `street_noon`, the
|
||
bookmark the tours have used since R9, is 46 draws of bare road.
|
||
|
||
Root cause: **CITY_SPEC contradicts itself** — `:71` (the lots contract) says −Z, `:335` (R15 posters)
|
||
says "+Z, B's canon" — and each generator implements one half. **Synthetic follows B's canon and looks
|
||
right, which is why every golden passes and nobody saw it for nine rounds.** The epoch's signature
|
||
species, one last time, and the worst instance of it: not a gate that fails on correct behaviour, but a
|
||
gate suite that is *green over a product that is wrong*.
|
||
|
||
**F verified it, and F's first check was VACUOUS — worth recording, because it nearly ended the round the
|
||
wrong way.** F measured each lot's `ry` against CITY_SPEC:71's own convention and got **35/35 and 72/72
|
||
FACING** — a clean contradiction of B. But `plan_osm` implements :71, so testing :71's data against :71's
|
||
rule is self-agreement: it cannot fail. **A self-consistent check over a self-contradicting spec passes
|
||
under either convention** — which is precisely why nine rounds of checks never caught this, and why F
|
||
nearly reported "B is wrong" with a green number in hand. The only test that sees it compares the two
|
||
conventions. F's frames already had.
|
||
|
||
**Not F's to fix**, and B was right not to either: it is **A's file**, it moves **every real-town plan**
|
||
(so every real-town golden re-pins under the amendment law — 36 of them), and **a self-contradicting spec
|
||
is Fable's to rule**, not a lane's to pick a side of. B proved the one-liner works (`ry = atan2(-nx,-nz)`
|
||
→ katoomba 0/72 → 72/72, and F's money shot appears 4 m from Musgrave Road) and reverted it. That is the
|
||
same discipline F applied to A's sweep and E's validator, arriving from the other direction.
|
||
|
||
**Order: Fable rules the spec → A lands the line + re-pins the goldens in one commit (amendment law) → F
|
||
re-gates, re-shoots the tour, tags `v5.0`.** Everything else for the release is done and green: the
|
||
kill-the-server gate, the reader, the sandbox, the interior money shot (interiors pose themselves and are
|
||
untouched by this — the crate is real either way).
|
||
|
||
*And B's own postscript is the lesson twice over: B credited "chunk-buildings 348 tris (0.2%)" to the
|
||
instancing work in R22 — 348 tris is ~29 boxes. It wasn't efficiency, it was the buildings being absent
|
||
from frame. B read the evidence as a win for five rounds. A number you like is the easiest vacuous gate
|
||
of all.*
|
||
|
||
### Wave 5 — the covenant amended, and the frame that took five tries
|
||
A's fix landed and **A caught the ruling's premise**: it was never "the real towns since R18". Synthetic is
|
||
broken identically — **0 toward / 681 away, dot = −1.0000** — and so is the marched path, and all three
|
||
fixtures. **EVERY TOWN, EVERY PATH, SINCE v1.** A booted `?classic=1`, stood on the synthetic main street,
|
||
found three blank grey boxes, walked *behind* them and found **NUMBAT PLAYTHINGS facing the dirt**. The
|
||
covenant town has been a row of back walls since the first commit.
|
||
|
||
**F re-pinned its own files** per A's handoff (A doesn't edit other lanes' files, correctly):
|
||
`GOLDEN_HASH 0x3fa36874 → 0x5f76e76` · `GIG_GOLDEN → 0xec7a2d39` · melbourne · katoomba · the captions.
|
||
**And A caught a live hazard in F's gate**: `hash_ok` carried `or ('3fa36874' in r.stdout)` — a bare-string
|
||
OR-clause that was dead the moment the covenant moved, and worse than dead, because it would go green off
|
||
a *sentence about* the old covenant appearing in stdout. Deleted. The check derives from `GOLDEN_HASH` or
|
||
it doesn't exist.
|
||
|
||
**John's ruling is the epoch's best line: the covenant is anti-drift machinery, not a defect
|
||
preservative.** `0x3fa36874` did exactly its job for five epochs — it never drifted. It just was never
|
||
right, and **no hash can catch what was wrong in the first commit**. A golden proves *nothing changed*; it
|
||
cannot prove *anything is correct*. Every gate we own was green over a world of back walls, and the only
|
||
thing that ever saw it was a human standing in the street looking at a wall.
|
||
|
||
**The frontage frame took five attempts and four of them were F's fault:**
|
||
1. cluster pose → the town's heart, which in Red Hill isn't Monster Robot. Empty road.
|
||
2. lot-anchored, facing along the edge → the road, shop beside the lens.
|
||
3. lot-normal facing → bitumen.
|
||
4. **B's helper — and F threw the result away.** `poseForShopFront` *returns* a pose; it does not apply
|
||
one. F called it, error-checked it, and never touched the camera, so the frame was the spawn paddock.
|
||
*A helper that hands you a value is not a helper that does the thing* — and F's own error-check passed,
|
||
because the call succeeded perfectly. The vacuous check, one last time, in the last frame of the epoch.
|
||
5. Applied it → **MONSTER ROBOT PARTY**, sign, door, awning, a ped walking in off Musgrave Road.
|
||
|
||
Attempts 1–3 were also never winnable: the shops faced backwards the whole time. **F was posing a camera
|
||
at a wall and tuning the geometry.** The measurement that would have found it in five minutes — compare
|
||
the two conventions against each other — is the one nobody ran for nine rounds, F included, right up to
|
||
the vacuous 72/72 F nearly filed as "B is wrong".
|
||
|
||
---
|
||
|
||
## 28. Round 28 — v5.1 THE SPIKE AND THE SWEEP. **The sweep landed. The spike did not reach the game.**
|
||
|
||
### ⚠ F's pose gate has NO SUBJECT — SKIPPED with the reason, per the law
|
||
The brief: *"New-clip figure checks (lean/look poses through the scale gate), a bookmark spot-check
|
||
(leaning peds in old frames)."* **There are no new poses in the game, so there is nothing to check:**
|
||
- **`lean` does not exist.** E measured it rather than assumed it — the stash's 34 clips contain no
|
||
lean, and `find -iname "*lean*"` across ultra returns nothing. The brief and the kickoff both named it
|
||
as already-cached; **the premise was half wrong**, and E found it the only way anyone ever finds these:
|
||
by looking instead of trusting the list.
|
||
- **`look.glb` is shipped, verified, published — and NOTHING BINDS IT.** `loadPedFleet()` loads
|
||
`walk.glb`, `idle.glb`, and `sit` (gated). No `look`. E's own handoff said it plainly: *"→ D: it's
|
||
ready. Gate it behind an opt the way rigs.js gates sit."* **D's entire R28 commit is
|
||
`LANE_D_NOTES.md` — 48 lines, docs only.** The run order said *"When E's clips land: D (#1 wiring)"*;
|
||
the log says **D committed BEFORE E shipped** (`afad68b` precedes `640bdbb`). Nobody was careless —
|
||
the wave order was, and the file on disk looks exactly like a delivered spike from every angle except
|
||
the one that matters.
|
||
|
||
**The R17/R23 pattern, fifth appearance, and the cheapest one yet to have missed**: an asset on disk,
|
||
verified, published to a public depot, sha1-checked by fetching the bytes BACK — and no consumer. Every
|
||
check E ran was real and passed. None of them could see that nothing loads it. *A gate on the artifact
|
||
cannot see the wire.*
|
||
|
||
### So: v5.1 SHIPS, and it does not claim the spike
|
||
Nothing is broken — an unbound clip is inert, the game is unchanged and correct. This is not R18's
|
||
"known breach"; it is an **unfinished item**, and holding a polish tag over an inert file would be
|
||
precious. **What ships is the sweep, and the tag says exactly that:**
|
||
- **C's `audioEmitter`** — the till rings from the counter, the bed from the stage. C's own idea, parked
|
||
since R7, and C found its own v3.0 note had specified it wrong on both halves (*"the panner was
|
||
inert"*).
|
||
- **Two items KILLED with the measurement** — B's awning ripple and D's instrument LOD, the oldest tri
|
||
item on the books. They don't leave the backlog "deferred"; they leave it **gone**, with the number
|
||
that killed them. *A backlog that only grows is a backlog nobody measures.*
|
||
- **`look.glb`** — staged on the depot, and honestly described as staged.
|
||
|
||
**Filed for R29 (D, one opt):** `rigs.js` gains `look` the way it gates `sit`; F passes it from the shell.
|
||
Then the pose gate has a subject and F will run it. **v6 has not begun in-game — the tag must not say it
|
||
has.**
|
||
|
||
---
|
||
|
||
## 29. Round 29 wave 0 — **the pose gate gains its subject. v6's first clip is in the game.**
|
||
|
||
R28's gate had **no subject** and skipped with the reason printed. D bound the clip in wave 0, so the
|
||
gate binds too — and it is now permanent, not a hand-proof:
|
||
|
||
| assertion | result |
|
||
|---|---|
|
||
| default boot: `look.glb` **bound** | **6.5 s, 64 tracks** |
|
||
| **the glance reaches the game** | **peak 46 of 396 peds glancing** · 0 console errors |
|
||
| `?classic=1`: the clip | **never fetched** — the zero-fetch-delta covenant holds |
|
||
| `?classic=1`: the behaviour | **inert at the source** — 0 of 269 peds, **and the rng stream still draws** |
|
||
|
||
**That last row is the whole design, and it's D's cleverness worth naming: the determinism lives in the
|
||
DRAW, not the outcome.** D takes from a dedicated `glanceRng` stream *always* — so the seeded sequence is
|
||
identical whether or not a clip exists — and only flips the flag when `fleet.lookClip` is real. Under
|
||
`?classic` the roll still happens, the flag never sets, nothing is fetched, and the covenant is
|
||
byte-identical. Add a behaviour without touching a hash.
|
||
|
||
**And the gate asserts the clip's ABSENCE under classic, not just "0 glancing."** "0 glancing" alone
|
||
passes on a fleet that failed to load entirely — the vacuous form of this exact check. Present on the
|
||
default boot + absent under classic is the pair that can't both be satisfied by a broken fleet.
|
||
|
||
### ⚠ F's gate crashed the suite before it ran — worth recording
|
||
The first cut checked for its subject with `pg.goto('.../look.glb')`. Navigating to a `.glb` starts a
|
||
**download**, Playwright aborts the navigation, and the exception took `flags_check` down with it —
|
||
before a single assertion ran. **A gate that dies looking for its subject is worse than one that skips:
|
||
it doesn't report "no subject", it reports nothing at all, and takes the suite with it.** The
|
||
subject-presence check now reads the disk. *The vacuous-gate law has a sibling: a gate must survive its
|
||
own subject being absent.*
|
||
|
||
**No tag** — wave 0 rides Spike 2's close, which is gated on John's footage. Nothing else is owed on m3.
|
||
|
||
---
|
||
|
||
## 30. Round 30 — v7.0-alpha THE SAVE CORE (ledger #1) + SLEEP=SAVE=TOMORROW (ledger #3)
|
||
|
||
### §30.1 THE CONTRACT — `window.PROCITY.game` (PUBLISHED FIRST, per the brief; B builds on this, C reads it at the counter)
|
||
|
||
`window.PROCITY.game` is **null** under `?classic=1` and under `?game=0`; on every other boot it is:
|
||
|
||
```js
|
||
window.PROCITY.game = {
|
||
day, // getter, int ≥ 1. DAY 1 IS THE PRE-v7 TOWN: stock streams carry no day salt on day 1,
|
||
// so a fresh game's shops are byte-identical to a ?game=0 boot. Rotation begins at the
|
||
// first sleep. (Falsifiable: day-1 stock == no-game stock; day-2 stock != day-1 stock.)
|
||
cash, // getter, int dollars — always equals PROCITY.wallet.cash()
|
||
collection, // getter → the LIVE array. READ-ONLY for every consumer (C's §9.3 law); the only
|
||
// mutations are game.recordFind (buy seam) and game.removeFind (sell seam).
|
||
townKey, // string for THIS boot: `${plansrc}/${town || 'default'}@${seed}`
|
||
// e.g. 'synthetic/default@20261990' · 'osm/redhill_godverse@20261990'
|
||
save(), // → bool. Serializes schema procity-save/1 → localStorage['procity-save'].
|
||
load(), // → bool (ran once at construction). Corrupted/foreign blob → LOUD reject
|
||
// (console.error), raw stashed at localStorage['procity-save.rejected'], fresh start.
|
||
// THE TOWN NEVER BREAKS: the save holds only player deltas (the delta law), so a bad
|
||
// save can cost you your stuff, never the world.
|
||
sleep(), // → the new day (int). day+1 → gig weekNight re-keys to (day − 1) % 7 (see the §30.3
|
||
// amendment) → wake at DAWN (lighting segment 0) → save(). Stock streams re-seed (§30.3).
|
||
export(), // → JSON string — the exact procity-save/1 payload (John moves machines).
|
||
import(json), // → bool. Validated exactly like load(). true ⇒ state replaced + saved + day-derived
|
||
// state applied (weekNight etc). false ⇒ loud reject, CURRENT STATE UNTOUCHED.
|
||
wallet, // the wallet-compatible facade — PROCITY.wallet IS this object when the game is on.
|
||
// Full Lane C v0 interface: cash() start() canBuy(p) buy(o) sell(o, offer)
|
||
// inventory() count() onChange(fn). buy/sell/inventory keep C's exact semantics
|
||
// (sell credits the offer + drops the matching v0-inventory entry, per §9.3);
|
||
// cash is game-backed so a loaded save's cash is authoritative.
|
||
recordFind(shop, info), // F's buy-seam internal (interior_mode calls it) — appends a collection
|
||
// entry stamped with townKey + day. Exposed so the gates can drive it.
|
||
removeFind(entry), // → bool. Identity (===) removal — THE only way the collection shrinks.
|
||
// C's sell counter: onSell ⇒ F calls wallet.sell(item, offer) +
|
||
// game.removeFind(entry). Returns false (nothing moves) if entry ∉ collection.
|
||
};
|
||
```
|
||
|
||
**Collection entry shape** (C's §9.2 asks honoured: `type` stamped at buy time, `title`/`artist` carried):
|
||
|
||
```js
|
||
{ townKey, // where in the world — the boot key above
|
||
shopId, // plan shop id (always present)
|
||
godverseShopId, // the real POS id, ONLY when the shop has one (the two id spaces stay separate — R24 law)
|
||
type, // the ITEM's stock type: 'record' (any dig, incl. an opshop bin) · 'book' · 'toy' (shelf
|
||
// buys). Sellability is type = type, fail-closed (C §9.2) — this field is load-bearing.
|
||
sku, // pack item id (`sku_*` / `mint_*` / `rec_*` — the sku IS the identity, R25/26 fences)…
|
||
slotId, // …OR, for parody items with no pack id: `<binKey>#<offerIndex>` (dig). Exactly one
|
||
// of sku|slotId is set.
|
||
title, artist, // display (B's collection card; C's sell card falls back to sku/slotId when absent)
|
||
pricePaid, // int dollars actually debited
|
||
dayFound } // game.day at purchase
|
||
```
|
||
|
||
**Save schema `procity-save/1`** (localStorage key `procity-save`):
|
||
|
||
```js
|
||
{ schema: 'procity-save/1', day, cash, town: townKey, collection: [entries], savedAt }
|
||
```
|
||
|
||
THE DELTA LAW, enforced by shape: no plan, no shop, no stock, no clock, no world field of any kind ever
|
||
enters this object — the world regenerates from seed. Validation on load/import is exact-schema-string +
|
||
field checks (day int ≥ 1 · cash finite ≥ 0 · collection entries carry townKey/pricePaid/dayFound and
|
||
one of sku|slotId); any miss rejects the WHOLE save loudly. Versioning: a future `procity-save/2`
|
||
migrates-or-rejects, never silently coerces.
|
||
|
||
**Save points:** `sleep()` and `beforeunload`. (Note, measured against the brief's "save, day+1" order:
|
||
sleep increments FIRST and saves the post-sleep state, so disk always carries the morning you woke into —
|
||
the literal order would leave disk one day stale between sleep and unload. Same falsifiable outcome, one
|
||
fewer window.)
|
||
|
||
### §30.2 The laws, wired from birth
|
||
|
||
- **`?classic=1`** → `createGame` is NEVER CALLED: no game object, no wallet facade, no listener, ZERO
|
||
localStorage touches (save.js has no module-scope storage access — the classic-purity gate in ledger #5
|
||
measures this rather than trusting it). `?game=0` → same absence, C's plain `createWallet` runs the
|
||
session exactly as pre-v7. Default boot → game on (the v3.1 flip precedent).
|
||
- **Game money is game money:** the save carries dollars that exist nowhere else; import validates shape,
|
||
not provenance — there is no bridge to anything real, in either direction.
|
||
|
||
### §30.3 SLEEP = TOMORROW — the rotation law (ledger #3, runtime only)
|
||
|
||
- **The day salt.** The parody/mint stock pick streams re-seed per (shop, day) by salting the EXISTING
|
||
runtime streams — plan generation NEVER sees `day` (A verifies the boundary, ledger #6):
|
||
- interior stock: `buildInterior(shop, THREE, { …, stockDay: day })` → `ctx.stockSalt = ':d'+day` →
|
||
layout.js appends it to the `stk-*` sub-stream salts (one marked seam line each in interiors.js +
|
||
layout.js — `[Lane F R30 seam]`). Fittings/layout/audio streams untouched: the ROOM is the same
|
||
room every day; what rotates is which items the streams pick into it.
|
||
- the dig: `binSeed(shop.seed, binKey + '@d' + day)` (interior_mode, F-owned).
|
||
- `day === 1` or no game layer ⇒ NO salt (byte-identical to pre-v7 — the day-1 convention above).
|
||
- **REAL-sourced stock never rotates.** A shop whose per-shop pack the godverse manifest declares
|
||
`sourcing:'real'` (Monster Robot Party) gets NO day salt, ever — its crate is a real crate. `mint`
|
||
crates and parody stock rotate. (The town-wide v2 packs are real *images* used as seeded set-dressing,
|
||
not a real shop's inventory — they rotate with the parody streams. Ruled here so nobody re-derives it.)
|
||
- **Gig night = (day − 1) % 7 — AMENDED from the brief's literal `day % 7`, with the measurement.**
|
||
`day` starts at 1, so the literal form keys a FRESH game's first night to night 1 — flipping the
|
||
default boot's gigs off night 0 before the player ever sleeps, breaking the R13 cover/band gates and
|
||
B's night-0-keyed frontage, and contradicting the day-1-is-the-pre-v7-town convention this same
|
||
contract establishes for stock. (day − 1) % 7 keeps the brief's intent exactly — every sleep walks
|
||
the existing seeded week — and day 1 IS night 0. `gig_state.js` (F-owned) gains `setWeekNight(n)` +
|
||
`get weekNight`; every venue's latch re-keys to its night-n gig (plan.gigs is already the whole week;
|
||
the three hard-won latch laws untouched). Cover stamps reset with the re-key (new night ⇒ cover due),
|
||
correct by John's R12 ruling. Fable ratifies or overrules at review — filed loudly, not slipped in.
|
||
- **Wake at dawn:** `sleep()` → `lighting.setSegment(0)` (fires `procity:segment`, so every latch +
|
||
facade observes the morning — the listens-not-polls law does the work for free).
|
||
|
||
### §30.4 Cross-lane notes on this contract
|
||
|
||
- **→ B (collection UI + SLEEP surface, ledger #4):** everything you need is `PROCITY.game`
|
||
(`day`/`cash`/`collection`/`sleep()`) + `wallet.onChange` for the cash chip. Cover thumbs: resolve via
|
||
the entry's `sku` against the shop's pack where present; parody entries have no thumb (title/artist
|
||
card is correct-and-readable, per the brief).
|
||
- **→ B (found while wiring weekNight):** `venue.js:38` keys "tonight" to **night 0** — correct pre-game,
|
||
but after the first sleep the marquee/frontage would advertise night-0's band while the latch plays
|
||
night-(day%7)'s. `PROCITY.gigs.weekNight` (and `game.day`) are published; one line in venue.js when
|
||
you touch the HUD. Filed, not silently fixed (your file).
|
||
- **→ C (sell counter, §9.3):** your asks are honoured — `type` stamped at buy time, `title`/`artist`
|
||
carried, removal via `game.removeFind(entry)`, credit via the facade's `sell(item, offer)` (mirrors
|
||
your wallet.js exactly). The E-key routing hook (aimed bin → dig · near counter + sellable → sell card
|
||
· else shelf-buy) is F's and lands with the sell wiring pass, not this one.
|
||
- **→ A (ledger #6):** the day salt rides `buildInterior` opts / `binSeed` only — grep-provable that
|
||
`generatePlan*`/plan_osm never receive `day`.
|
||
|
||
### §30.5 RESULTS — implemented, measured, committed (`53fcd3c` contract · `207bcff` implementation)
|
||
|
||
All measured in FRESH browser contexts on a port-isolated no-store server (:8791 — the module-cache lesson),
|
||
seed 20261990, plus `redhill_godverse?stock=real` for the crate legs. Seven legs, all green:
|
||
|
||
| leg | measured |
|
||
|---|---|
|
||
| the contract | default boot: `game` live — day 1 · cash **$191 == wallet.cash()** · collection 0 · townKey `synthetic/default@20261990` · weekNight 0 · full API incl. `wallet.sell` · 0 errors |
|
||
| classic purity | `?classic=1` AND `?game=0`: `game === null`, **ZERO Storage-prototype calls** (instrumented before any page script, not trusted) · wallet $191 (C's v0) · 0 errors |
|
||
| buy → collection | dig pull: **$12 debited**, entry `{type:'record', slotId:'20_-317#0', pricePaid:12, dayFound:1, title:'Sunburnt'}` — debit == pricePaid, shape == §30.1 |
|
||
| sell seam halves | `wallet.sell` credited $3 (179→182) · `removeFind` true then **false on re-remove** · collection 0 |
|
||
| sleep | `sleep()` → day 2 · **woke at DAWN (seg 0)** · weekNight 1 · localStorage payload keys exactly `[cash, collection, day, savedAt, schema, town]` — the delta law by shape, no world field |
|
||
| rotation | parody shop vertex-data fp: day 1 `3396372961` → day 2 `1818111125` (**rotates**) · re-entry same day **byte-identical** · after reload **still identical** (cross-boot determinism) · `?game=0` fp **== day-1 fp** (the day-1 convention, measured) |
|
||
| persistence | reload: day 2 · cash · collection 1 · weekNight 1 all restored (beforeunload save) · `export()` valid · `import()` of garbage + foreign schema **rejected loudly, state untouched**; real payload adopted + weekNight re-keyed |
|
||
| corrupt save | pre-seeded bad blob → **1 loud reject**, raw stashed at `procity-save.rejected`, fresh start ($191, day 1), **town boots, 0 other errors** |
|
||
| the crates | **REAL** (Monster Robot Party g:3962749): base `stock_godverse/3962749/`, fp **identical** day 1 → day 2 — real never rotates · **MINT** (Presents of Mind g:767): fp **rotates** — mint does |
|
||
|
||
`qa.sh --strict` **6 passed · 0 failed · 0 warn** over the landed layer — classic regression, default-boot
|
||
gate, buy-v0, cover (paid + free), the R26 crate gate, R27 live gate, glance, no-giants: all green with the
|
||
game layer default-ON.
|
||
|
||
**Two of F's own harness bugs caught by the run (recorded per house habit):**
|
||
1. **The post-sleep CLOSED door.** First fp read after `sleep()` failed to enter the shop — because the
|
||
town woke at DAWN and the shop was **correctly closed** (the hours law doing its job). The harness now
|
||
sets midday before entering. A verify that forgets the world has rules will read the rules as bugs.
|
||
2. **The fingerprint was vacuous for parody rooms.** It hashed mesh transforms + first UVs — but
|
||
`batchRoom` merges parody stock into merged geometry at IDENTITY transform, so it hashed constants and
|
||
reported "no rotation" over stock that rotates. (Mint showed through only because atlas UV rects land
|
||
in the first 8 floats.) Fixed: hash the **vertex data** (position + uv arrays), where the day-salted
|
||
picks/jitters actually live. The vacuous-measurement species, caught in F's own tool, same round it
|
||
re-read the law to everyone else.
|
||
|
||
**Live cross-lane note (seen mid-session, not F's commit):** B picked up §30.4 within the hour —
|
||
`venue.js` "tonight" is already a live getter off `PROCITY.gigs.weekNight`, and `hud.js` has the
|
||
collection/SLEEP surface in flight. The contract-first order did exactly what it exists to do.
|
||
|
||
**Held / next session (per the brief):** ledger #5 gates (save/load determinism scripted session ·
|
||
no-pump v0 · classic-purity localStorage gate · rotation determinism) — a LATER F session. The sell-card
|
||
E-key routing (dig → sell → shelf priority, §9.4) rides the sell wiring pass alongside it.
|
||
|
||
### §30.6 THE GATES SESSION (wave 3) — the routing landed, five gates wired STRICT, all green
|
||
|
||
All measured on the house harness (:8130, fresh Playwright contexts per gate — no module cache to lie).
|
||
Committed as five new STRICT smokes in `tools/flags_check.py` (`smoke_sell_routing` ·
|
||
`smoke_save_determinism` · `smoke_nopump` · `smoke_classic_purity` · `smoke_rotation`), so every future
|
||
`qa.sh` run re-proves them; the session-local instruments from §30.5 are now suite property.
|
||
|
||
**THE E-KEY SELL ROUTING (§9.4, the held join) — landed + verified live through the real UI:**
|
||
`interior_mode.js` grew `trySell()` (C's `createSell`/`nearCounter`, lazily built, only when the game is
|
||
on) and the E chain is now: **AIMED bin → dig · else near counter with ≥1 sellable → sell card · else
|
||
forgiving nearest-bin → dig · else shelf-buy.** F does both §9.3 halves in `onSell`, in this order:
|
||
`game.removeFind(item)` FIRST (false ⇒ keeper veto — no credit can exist for an item not in the
|
||
collection), then `wallet.sell(item, offer)`. Shell seams (3 lines, `index.html`): `procity:sellOpen` →
|
||
`player.unlock()`, and `sellActive` guards the Esc-leaves-shop + click-relock paths (the dig's exact
|
||
contract). The full loop, measured through the real DOM: **dig pull $9 debited → entry
|
||
{type:'record', sku|slotId, pricePaid:9} → E at the counter → C's card → SELL +$4 (< $9, the clamp) →
|
||
collection 1→0 → back on the street B's HUD reads $186 · CRATE 0.** Negative legs: empty collection ⇒
|
||
E opens NOTHING (open() false, zero DOM); `?game=0` ⇒ no game, no card, zero errors.
|
||
|
||
**One routing amendment, filed with the measurement (F's own file, F's own routing):** `binUnderAim`'s
|
||
forgiving fallback picks the nearest bin within 2.5 m of the camera and **ignores aim** — at a counter
|
||
that sits near a bin it would swallow every E and the sell card could never open there. The chain now
|
||
asks aimed-raycast FIRST (the §9.4 contract's literal "aimed bin"), lets the sell trigger decline, and
|
||
only then runs the forgiving pick. Both sides proven live: at Moe Vinyl's counter-adjacent bin the card
|
||
now opens (`sell:true, dig:false` — the old chain opened the dig); by a bin away from the counter with
|
||
a sellable held and aim wrong, the dig still opens (`dig:true, sell:false` — the fallback survives).
|
||
|
||
**THE GATES (ledger #5) — each with its subject named and touched:**
|
||
|
||
| gate | verdict + numbers |
|
||
|---|---|
|
||
| (a) save/load determinism | scripted session buy 2 · sell 1 · sleep 2× (via **B's real SLEEP button**) → day 3 · $177 · 1 item → export → FRESH browser → import: state **byte-equal** (243 B) + savedAt-normalized export **byte-equal** (269 B) · plan fp **unchanged** `0xb1c7843e` across export→import (the delta law's falsifiable form) · weekNight re-keyed to 2 == (3−1)%7 · 0 errors both contexts |
|
||
| (b) no-pump v0 | 5 real round trips (dig UI pull+BUY → counter card SELL-all): **5/5 strictly negative, deltas [−6,−6,−6,−6,−6], Σ −$30** — identical deltas are the deterministic re-seeded crate, not a stuck instrument (the controls prove discrimination) |
|
||
| (b′) C's negative control | broke player ($0 via import): dig BUY **disabled**, clicked anyway — zero debit, zero entry · shelf path: `wallet.buy` **INVOKED once (counted, not mocked), returned false, recordFind unreachable** — no entry, cash still $0 · positive arm: same aim with cash → debit $14 == entry.pricePaid, 1 entry — **C's pump is dead, and the control can tell** |
|
||
| (c) classic purity | Storage **prototype** instrumented before any page script: `?classic=1` AND `?game=0` → **0 calls**, game null, **0 of B's 7 game DOM ids** · POSITIVE control: default boot → instrument saw the `procity-save` getItem + all 7 ids present (a zero nobody proved the instrument could see would be vacuous) · plan fp game-on **==** game-off `0xb1c7843e` (the layer moves zero plan bytes; classic's own fp differs by design — its golden is classic_regression's) |
|
||
| (d) rotation determinism | **VERTEX-DATA fp (position+uv arrays), per the wave-3 binding** — transforms measured vacuous in §30.5: day 1 `0x50edd91b` (**88,320 bytes / 20 stock meshes** — subject present, printed) · re-entry byte-identical · day 2 `0x2121f187` ≠ day 1, re-entry identical · day 3 `0x2f4a9b93` ≠ both · **cross-boot: day-3 fp byte-identical after a full reboot** off the persisted save · `?game=0` fp **== day-1 fp** (the pre-v7 town IS day 1, measured again from the suite) |
|
||
| (e) the suite | all five wired **STRICT** into flags_check (FAIL-level, house pattern) · `qa.sh --strict --matrix` full run: see §30.6 numbers below |
|
||
|
||
**Harness bug #3 of the round (recorded per house habit):** the routing gate first read B's HUD
|
||
*inside* the shop and called it stale — but the HUD is **hidden and unrefreshed in interior mode by
|
||
design** (`hud.setVisible(m === 'street')`; `gameUI.refresh()` rides the street loop). The gate now
|
||
walks back onto the street and reads it where a player could — $186 · CRATE 0, correct. Same species
|
||
as §30.5's closed-door: a verify that forgets the world's rules reads the rules as bugs.
|
||
|
||
**Design fact, filed not fixed (alpha-acceptable, for the beta ledger):** parody/mint dig stock has no
|
||
`gone` tracking — the same $11 sleeve is back in the bin on re-entry (the crate is a seeded stream, not
|
||
an inventory). Buying it again is possible **and loses money every time** (the −$6/trip measurement is
|
||
exactly this); scarcity-per-day would need the save to carry per-day pull records. The no-pump clamp is
|
||
what makes the infinite crate safe. Rule it in beta with the guide bands, not here.
|
||
|
||
---
|
||
|
||
## 31. Round 31 — the v7.0-alpha CLOSE: THE DIG FLIP (wave 1) + THE TAG (wave 2)
|
||
|
||
### §31.1 The alpha-blocker (Fable's wave-0 playtest) and the flip
|
||
|
||
**The dig was default-OFF on a plain boot.** `web/index.html` still carried the v2-era opt-in
|
||
(`const DIG_ON = params.has('dig') && params.get('dig') !== '0'`) six epochs after the README's
|
||
flags table began claiming "dig on" — and every R30 game-loop gate booted `dig=1` EXPLICITLY, so
|
||
the whole suite was green while the boot every player gets had no crate-riffle at all: E at a bin
|
||
did nothing. The gates tested the game; nobody tested that the default boot IS the game.
|
||
|
||
**The flip (one line + the law):** `DIG_ON = flagOn('dig')` — default-ON, `?dig=0` opts out,
|
||
`?classic=1` master-off: the exact pattern the other flipped flags ride. `dig` joins the
|
||
`PROCITY.flags` intent surface (the gates read intent there). Measured both ways in fresh
|
||
contexts on a port-isolated server (:8951):
|
||
|
||
| boot | measured |
|
||
|---|---|
|
||
| NAKED (no params) | E at a bin → riffle **OPENED** + closed clean · `flags.dig` true · 0 errors |
|
||
| `?dig=0` | opt-out honoured — riffle does NOT open, **ZERO** dig DOM |
|
||
| `?classic=1` | dig INERT at the bin (E falls through `trySell` (null game) + shelf no-op) · ZERO `pcdg-*` DOM · **ZERO Storage-prototype calls** (instrumented) · 0 errors — the covenant untouched |
|
||
| `?noassets=1` naked | dig opens on **parody canvas** · 0 errors (existing law holds) |
|
||
|
||
*(Observation filed, pre-existing, NOT touched: `assets/gen/*.jpg` skin textures fetch under
|
||
`?noassets` — 22 fetches, fail-soft, present since v1; the dig added zero fetches to that boot.)*
|
||
|
||
### §31.2 The gate that makes the regression impossible (vacuous-gate law, turned on F's own suite)
|
||
|
||
Two new STRICT legs in `tools/flags_check.py`:
|
||
|
||
- **default_boot_gate** grows **dig REACHABILITY on the NAKED boot**: enter a record shop, E at an
|
||
aimed bin, `digActive` must go true (and close clean via WALK OUT); plus a `PROCITY.flags.dig`
|
||
intent assert. Un-flip the default again and this line goes red. Negative control: the identical
|
||
measurement on a `?dig=0` boot reads `opened:false` (pre-flight, :8951) — the detector
|
||
discriminates.
|
||
- **classic_regression** grows the **dig-INERT leg**: the old `digActive` assert was vacuously
|
||
false on the street; it is now measured AT THE BIN — E opens nothing, zero dig DOM, filed as
|
||
"measured, not assumed".
|
||
|
||
**Harness lesson #4 of the epoch (house habit): the explicit-flag blindfold.** A gate that
|
||
force-feeds its subject's flag (`dig=1` in every boot query, belt-and-braces) can never notice the
|
||
default is broken. Where a flag is CLAIMED default-on, at least one gate must exercise it with the
|
||
flag ABSENT. Now true for dig; gigs/weather/winmap/tram always had it (the default-boot gate boots
|
||
naked by construction).
|
||
|
||
### §31.3 Port isolation (small, suite-wide)
|
||
|
||
`PROCITY_QA_PORT` env override added to `flags_check.py`, `qa/interior_scale_check.py`,
|
||
`qa/town_matrix.py`. `ensure_server()` silently REUSES anything parked on :8130 — this round that
|
||
was Fable's own preview server, and a shared server is somebody else's cache semantics + somebody
|
||
else's lifecycle. The R31 re-gate rode `:8931`; default behaviour unchanged.
|
||
|
||
### §31.4 The splash (same file, same commit)
|
||
|
||
"Every door opens **(soon)**" — stale by six epochs — is gone: the tagline now sells the loop
|
||
("Every door opens. Dig the crates, sell the flips, sleep — the town remembers.") and the controls
|
||
line carries the game beats (E riffle a bin / sell at the counter · C your crate · SLEEP to save).
|
||
README manual gains the matching row (sell at the counter · C crate · SLEEP = save/advance/rotate).
|
||
|
||
### §31.5 The re-gate, the shot, the tag — RESULTS
|
||
|
||
All on the FINAL tree (commit `50385c0`), suite port-isolated on `:8931` (`PROCITY_QA_PORT` —
|
||
Fable's preview holds :8130, untouched):
|
||
|
||
- **`qa.sh --strict --matrix`: 7 passed · 0 failed · 0 warn · 0 skipped.** flags_check **0 fails /
|
||
0 warns** — both new R31 legs green in the run (`dig REACHABLE on the naked boot — E at a bin
|
||
opened the riffle (and closed clean)` · `?classic dig INERT at the bin — E opened nothing, zero
|
||
dig DOM`), all five R30 STRICT gates re-proven over the flipped tree, classic covenant fingerprint
|
||
green, no-giants green. **MATRIX GREEN — 10 towns (synthetic + fixtures + real caches) × 7 gates.**
|
||
- **The money shot: `docs/shots/v7_alpha/crate_panel.png`** — Monster Robot Party's real frontage
|
||
(redhill_godverse, `stock=real`), THE CRATE open with **3 real finds** (The Return $40 ·
|
||
First Filtration Of The Duplex Brains $15 · Blue Blackness $20 — real cover thumbs off the
|
||
per-shop atlas, every row honest: `paid $N · Monster Robot Party · day 1`), game bar
|
||
**day 1 · $116 · CRATE 3 · SLEEP**. Driven through the REAL loop with the dig param ABSENT (the
|
||
flip carries the shot): enter → E at three different bins → riffle → pull → BUY ×3 → street →
|
||
C. 0 console errors. Script: session scratchpad (`crate_shot.py`) — the PNG is the deliverable.
|
||
- **The tag: `v7.0-alpha`**, annotated, **LOCAL ONLY** on the notes commit — Fable reviews and
|
||
pushes main + tag together (treaty).
|
||
|
||
### §31.6 THE BETA QUEUE (Fable's playtest, filed NOT fixed — John's ruling queue)
|
||
|
||
1. **Spawn faces dirt/building-backs** instead of the strip — wants a cluster-pose for spawns.
|
||
2. **THE CRATE is unreachable inside shops** — C is street-only by design (hud.js); a player wants
|
||
it at the counter.
|
||
3. **Dawn sky reads dusk-orange** (cosmetic).
|
||
4. The pull card's **value hint is THE BIBLE** (chartered — the beta's guide bands).
|
||
5. Automation-only, unverified in person: **dig Esc-close under synthetic events**.
|
||
6. *(carried from §30.6)* no per-day `gone` tracking on parody/mint dig stock — scarcity-per-day
|
||
needs the save to carry pull records; rule it with the guide bands.
|
||
|
||
## §32 — R32: v7.0-beta wave 1 — THE BIBLE + scarcity + the §31.6 queue (Fable, solo round, JING5)
|
||
|
||
**The round in one line:** the guide bands land (derived, not baked), gems exist (parody bins only,
|
||
by construction), a bought slot stays gone for the day (pulls ride the save), and §31.6 items 1–3
|
||
are fixed. All gates green; the no-pump law survives the §9.1 basis swap structurally.
|
||
|
||
### §32.1 What shipped, by seam
|
||
|
||
- **`js/interiors/bible.js`** (C) — the band table mirroring E's pipeline thresholds
|
||
(record 8/25/60 · book 5/15/40 · toy 6/20/50), `bandRange`/`guideText`/`sellBasis`. Derivation
|
||
ruling: NO pack re-emit (charter risk #3 never opens); if E's thresholds move, bible.js moves in
|
||
the same commit.
|
||
- **dig.js** (C) — parody sleeves roll seeded bands (25/45/22/8%) + a 10% gem on collector/grail
|
||
(asking strictly under the keeper's own bandLow offer: collector $2–9 vs $12 · grail $5–20 vs
|
||
$30); fixed 9-draw stream shape per item; `rare` now ≡ grail (matches real packs). Real offers
|
||
carry `band` off `price_band`. `open({gone})` omits pulled slots AFTER the draw (the R27
|
||
pick-then-omit seam, zero collateral) — real via `'sku:<id>'`, parody via original offer index
|
||
(`o.i` survives filtering, identity preserved). The pull card + sell card gain the **guide
|
||
stamp** (`guide $8–24` / `$60+`) — present-fields-only, and the card NEVER says "gem": spotting
|
||
asking-under-guide is the player's skill.
|
||
- **sell.js** (C) — §9.1 basis swap executed exactly as pre-ratified: `sellOffer(sellBasis(entry))`
|
||
= `min(bandLow−1, max(1, floor(bandLow·0.5)))` when banded, pricePaid basis for alpha-era
|
||
entries (they sell exactly as before — no migration needed).
|
||
- **save.js** (F) — optional `pulls: string[]` on procity-save/1 (validated loudly when present;
|
||
absent on alpha saves — old savers drop it silently, never reject). `recordPull`/`pullsFor`
|
||
seams; pruned on sleep/import (day-tagged keys from other days are unreachable by construction;
|
||
untagged = real-sourced, kept — sold means gone). Cap 600 FIFO. Collection entries gain
|
||
optional `band` (recordFind stamps dig + shelf buys).
|
||
- **interior_mode.js** (F) — pull keys `${shopId}|${binKey}${pullTag}#${slot}`; pullTag =
|
||
`@d<game.day>` for rotating crates (INCLUDING day 1 — the seed stays untagged per the day-1
|
||
convention, the ledger doesn't), `''` for real-sourced. The SEED input is untouched (A's
|
||
rotation boundary holds). `digOffers` honest surface (index/ask/band/sku) for the gate.
|
||
- **hud.js + index.html** (B/F) — §31.6-2: `#pc-game` rides document.body (position:fixed, z 30),
|
||
ticks in every mode (`hud.tickGame()` from the interior/map branches), yields to the dig/sell
|
||
cards and the map; C toggles the crate anywhere. §31.6-1: cluster-pose spawn — main-street
|
||
shop doors (block kind, both generators), stand at the door nearest the cluster heart, face the
|
||
longer run of shopfronts; pure plan math, zero draws; `?classic=1` keeps the frozen pose.
|
||
§31.6-3: DAWN swaps the dome to `high-cirrus` (lighting `dawnSky` opt, null under classic);
|
||
a weather `setSky` is now an explicit override that always wins.
|
||
|
||
### §32.2 THE GATE (`smoke_bible`, STRICT) + the R30 amendment
|
||
|
||
Scans EVERY bin of the record shop off `digOffers`: band law on all offers (in-band unless a
|
||
collector/grail gem), then runs the buy-EVERYTHING ledger on **the gem's bin** (leg is
|
||
non-vacuous by construction — 80 offers/5 bins, bin1 holds 2 gems on the pinned seed, grail
|
||
asking $19 vs keeper $30). Ledger closes exactly: Σask $333 → Σoffer $135 (gems alone +$21,
|
||
every non-gem strictly negative). Scarcity: the emptied bin re-opens at 0, STAYS 0 across a full
|
||
reload (16 pulls in the save); negative control: `?game=0` re-opens it at 16 — the detector
|
||
discriminates. 0 console errors. **R30 amendment (sell_routing):** `credited < paid` was the
|
||
alpha inequality; the gate now asserts the exact formula (`credited == min(basis−1, basis//2)`,
|
||
basis = bandLow-or-pricePaid, `credited < basis`) — the fence moved from the receipt to the
|
||
band, which is what charter law #2 always said.
|
||
|
||
### §32.3 Environment note (JING5)
|
||
|
||
`web/assets/models/` is GITIGNORED build output — a fresh checkout runs stock=real/book-toy/
|
||
no-pump gates RED with 404s (the R31-green tree lives on m3ultra). Fixed here by rsync from
|
||
m3ultra. If a third machine joins the rotation, sync that dir (49 M) or re-run E's pipeline.
|
||
|
||
## §33 — R33: v7.0-beta wave 2 — THE HUNT (wantlist · rumor · travel) (Fable, solo, JING5)
|
||
|
||
- **save.js:** optional `wants: [{artist?|title?|sku?}]` on procity-save/1 (validated
|
||
exact-and-loud when present; ≥1 string field per entry; cap 40; absent-when-empty keeps the
|
||
alpha byte shape). `recordWant` (case-insensitive dedupe) / `findWant` (a want covers an item
|
||
when every field the WANT names matches — sku exact, artist/title by value) / `removeWant`
|
||
(identity). **TRAVEL in `adopt()`:** adopted save's `town !== townKey` ⇒ `day += 1`,
|
||
`traveled = true`, prunePulls — load AND import both pay the fare; same-town adoption free
|
||
(save-determinism byte-equality untouched). `game.traveled` getter → the shell's arrival toast.
|
||
- **dig.js:** the pull card's ☆ WANT line — construction-gated on `opts.onWant` (game absent ⇒
|
||
zero want DOM, the classic-pure pattern). ☆ click → ★ ON THE WANTLIST (a stamp, not a button,
|
||
once wanted).
|
||
- **interior_mode.js:** onWant/isWanted seams into dig.open; buy of a matching item removes the
|
||
want (+ '✓ off the wantlist' toast). **THE RUMOR** in the enter() banner: seeded
|
||
`xmur3('rumor:<citySeed>:<shopId>:<day>')` → 45% roll → hunted want + a town from
|
||
RUMOR_TOWNS (the repo's real cached towns, current town excluded). Runtime-only stream —
|
||
A's boundary holds; no game / empty wants ⇒ the line never exists.
|
||
- **hud.js:** THE WANTLIST section in the crate panel (non-empty only): artist/title rows, ✕
|
||
stops hunting; refresh watches wants length so an open panel follows adds/clears.
|
||
- **Gate:** `smoke_wantlist_travel` STRICT (see ROUND33_INSTRUCTIONS for the green run) — real
|
||
card path, seeded rumor scan, auto-clear, +1-day travel with same-town negative control and
|
||
the return fare, `?game=0` zero-DOM control, 0 console errors.
|
||
- **Matrix note:** town_matrix shares one browser across towns, so travel drifts the day as it
|
||
walks the matrix — safe because determinism double-boots the SAME town (free by the
|
||
same-town rule) and no matrix gate reads absolute day. Reasoned before landing, not after.
|
||
- **The shot found what the gate didn't (harness lesson #5 candidate):** R32's crate-in-shops
|
||
surface BUILT only in street update() frames — a door entered fast after boot had no game
|
||
surface inside at all, and the interior tickGame() happily refreshed a UI that never existed.
|
||
Green gates missed it because every gate's boot idles on the street long enough to build.
|
||
Fixed: tickGame() now builds AND refreshes (any mode); found by taking the money shot, which
|
||
entered the shop faster than any gate does. Shots are a detector, not decoration.
|
||
|
||
## §34 — R34: GODBAY, the offline auction (v7.0 close opens) (Fable, solo, JING5)
|
||
|
||
- **save.js:** `godbayHammer(entry, townKey)` — THE ONE-HAMMER LAW: seeded by ITEM IDENTITY
|
||
(`godbay:<townKey>:<sku|slotId>:<dayFound>:<pricePaid>`), never by resolution day — a
|
||
passed-in item re-lists to the same result forever, no retry farm. Fixed 4-draw stream. Rolls:
|
||
15% passed in (comes home free) · uniform hammer across the band (grail top = 2×low; unbanded
|
||
alpha finds auction around pricePaid — honest, never invented) · 12% bidding war ×1.5–2.0 ·
|
||
12% commission, net floor $1. EV ≈ 0.8×mid — under in-band asking EV, so random flipping
|
||
bleeds and only FINDS profit; the keeper's half-of-low stays the instant floor, the auction
|
||
is the gambler's ceiling (bounded 2×high×0.88). PRNG imported from core/prng.js; the band
|
||
table imported from C's bible.js (one authority — no mirror after all).
|
||
- `listings` optional on procity-save/1 (entry shape + listDay, same back-compat law).
|
||
`listFind` (identity, entry leaves the crate — can't also sell it) · `resolveAuctions()` runs
|
||
in adopt() AND sleep(): sleep, travel, and day-moving imports all hammer what they pass.
|
||
`takeAuctionNews()` drains the transient morning paper (NOT saved — the ledger is cash/
|
||
collection; the paper is a toast).
|
||
- **hud.js:** ⚖ LIST on crate find rows (mail-in consignment) + AT AUCTION section; rebuild
|
||
watches listings. **index.html:** the wake toast and the arrival toast fold the paper in.
|
||
- **Gate `smoke_godbay` (STRICT):** consign through the real crate DOM → save carries it ·
|
||
ONE-HAMMER determinism (a frozen pre-sleep export slept twice lands byte-identical) · bounded
|
||
honest resolution (SOLD net $19 on a standard record, ≤ 2×24×0.88) · the wake toast carried
|
||
"⚖ SOLD Cane Toad Blues — hammer $22, $19 after fees" · the ride to melbourne hammered a
|
||
listing on arrival · ?game=0 has no crate to list from · 0 console errors.
|
||
- G's later LIVE tier must match these laws (the ladder rule: the offline tier is the contract).
|
||
|
||
## §35 — R35: THE FIRST FIVE MINUTES (Fable, solo, JING5)
|
||
|
||
Fresh-game detection = day 1 ∧ empty collection ∧ empty listings (no flag in the save — the
|
||
delta law stays clean; a returning empty-crate player re-seeing beats is onboarding working,
|
||
not a bug). Seeded first want: `rng(citySeed,'firstwant',0)` × dig.js ARTISTS (exported R35);
|
||
guarded on empty wants so the persisted want isn't re-added. Splash gains #pc-hunt (fresh+game
|
||
only). Beats: ① on the go-click (900ms), ② on first `procity:digOpen`, ③ on first find via
|
||
wallet.onChange, send-off on first sale-or-listing (listener self-removes). #pc-toast body-
|
||
mounted (z 31) — visible in interiors/map; hud.dispose removes it. Gate `smoke_first_five`
|
||
STRICT (see ROUND35_INSTRUCTIONS). All v7.0 SYSTEMS are now built — tour + freeze + tag remain,
|
||
tag gated on John's playtest.
|
||
- **Cross-round amendment the suite caught (the reason full runs exist):** R35's seeded first
|
||
want changed the FRESH-BOOT CONTRACT (wants starts at 1, not 0) and the R33 gate's three
|
||
fresh-boot assumptions went red on the full run — green solo, red together. Amended to a
|
||
baseline snapshot (base = wants at boot; card adds base+1; the rumor may name ANY live want;
|
||
buying clears ITS want back to base). A feature that moves a boot invariant must sweep every
|
||
gate that assumed it — grep the harness for the invariant before calling a round closed.
|
||
|
||
## §36 — R36: THE v7.0 CLOSE (wave 2 — the gate, the freeze, the tag)
|
||
|
||
### §36.1 The two playtests (the tag's precondition, both on record)
|
||
|
||
- **Fable, wave 0 (fresh naked boot, in person): ZERO alpha-blockers.** The whole loop by hand:
|
||
splash hook → strip spawn → onboarding beats → BIBLE guide on the cards → ☆ WANT (the
|
||
splash-seeded HILLS HOIST, add + auto-clear-on-find) → ⚖ LIST → sleep → the morning paper
|
||
("SOLD Milk Bar Dreams — hammer $8, $7 after fees") → travel Boolarra→Katoomba costing a day,
|
||
the save riding along. The v7.x queue (filed, NOT fixed, per the five-holds law):
|
||
⚖ discoverability · LIST confirm-click · the paper as a bigger artifact · keeper-rumor
|
||
untested-by-hand. Fable's verdict line, kept for the tag: *"zero alpha-blockers; the game
|
||
charged me $12 tuition for ignoring its own advice."*
|
||
- **R31's alpha playtest** stands as the precedent that found the dig default-off blocker; this
|
||
one found none — the explicit-flag blindfold lesson (harness #4) held: the wave-0 boot was
|
||
NAKED, and the R31/R35 flag-absent gates had kept the default boot the game.
|
||
|
||
### §36.2 E's wave-1 flags, closed by measurement (the 2 a.m. assets)
|
||
|
||
E's R36 w1 sweep (`83bce93`) regularized `046668e`+`b339402` (9 assets, not the brief's 7:
|
||
4 Rokoko dance clips + **5** trellis DJ bodies) and flagged three things at F's gate. All three
|
||
measured on the final tree:
|
||
|
||
1. **Flag (a) — the bind-scale seam.** The DJ bodies' bind skeletons measure **0.9835–0.9893
|
||
units** vs the legacy roster's ~173.46 (cm convention) — E's "~0.95–0.99" confirmed at
|
||
runtime. `rigs.js:163` span-normalise absorbs it at spawn: the figure-scale gate grew a
|
||
**DJ-body leg** (interior_scale_check.py) that spawns all five BY NAME through the real
|
||
browse-spawn path and asserts stature — **woman_dj_01 0.986u→1.64 m · woman_dj_02
|
||
0.987u→1.71 m · woman_dj_03 0.984u→1.81 m · dj_techno_01 0.986u→1.87 m · dj_phrtt_01
|
||
0.989u→1.89 m**, all in [1.4,2.0], feet planted, rig-kind verified (not placeholder — the
|
||
subject provably exercised; the old browse sample's `(i*7)%19` could never reach fleet
|
||
indices 17–21). Bonus contact: the pinned seed's own gig crowd contains dj_phrtt_01 and
|
||
woman_dj_02 organically.
|
||
2. **Flag (b) — gate on binds + NaN, never node counts.** New STRICT `smoke_djdance`: runtime
|
||
bind census post `_canon`/`_rotOnly` — every clip binds **64/65 tracks, sole unbound =
|
||
`mixamorigReference`** (Rokoko's root; matches E's node-side 65/66 census and 90sDJsim's
|
||
in-game measurement). No structural-template pin anywhere in F's suite — the 68-node/52-joint
|
||
Rokoko shape passes because nothing wrongly asserts the sit/look 66/65 template.
|
||
3. **Flag (c) — ROKOKO zone** — E's provenance/licence row verified in w1; nothing for the
|
||
runtime gate beyond (b).
|
||
|
||
### §36.3 `smoke_djdance` — what the 2 a.m. commit claimed, now proven (STRICT)
|
||
|
||
The commit verified "page boots" only. The gate now proves: **4 dance clips load and
|
||
canonicalise** on the default boot · a gig-night **dancer's live action IS a dance clip**
|
||
(identity by duration — 30.5–52.4 s is unambiguous vs idle; mixer provably advancing;
|
||
`fig.position.y` FLAT, so the clip moves the body, not the fallback) · **`?classic=1` fetches
|
||
ZERO dance clips** (positive control: the default boot fetched all 4 — the detector
|
||
discriminates) · **`?noassets=1`**: zero GLB fetches, dancers exist as placeholders, and the
|
||
**procedural sway fallback is ALIVE** — bounce amplitude 0.07 m vs the clip-driven 0.00 (the
|
||
inverse pair proves which branch moved the body). 0 console errors on every boot.
|
||
|
||
### §36.4 THE HOLD — held, ruled, RESOLVED (the five-holds law working end-to-end)
|
||
|
||
**As held (wave 2):** `?classic=1` fetched the 5 trellis DJ body GLBs — unconditional
|
||
`PED_NAMES.normal` roster members (rigs.js, Lane D's file, landed in `b339402`) — which also
|
||
widened classic's seeded crowd pool 17→22 normal on July 20. The plan fingerprint is plan-side
|
||
so the covenant gates stayed green, and draws were in tolerance — but the classic FETCH SURFACE
|
||
and crowd identity moved without a ruling, six epochs after the covenant froze. F WARNED it in
|
||
`smoke_djdance` (the machine record) and did not improvise in D's file during the close.
|
||
|
||
**RESOLVED (wave 2.5, Fable's ruling, D's fix — `2fd4c1c`, LANE_D_NOTES §36): CLASSIC KEEPS
|
||
ITS PEOPLE.** `PED_NAMES.normal` restored to the covenanted base 17 (exact pre-July-20 list);
|
||
the five moved to `PED_NAMES.djs`, admitted by a new `loadPedFleet` opt **`djs = dance`** — the
|
||
default tracks the existing dance flag, which the shell already passes as `!CLASSIC`, so the
|
||
gate arrived with ZERO shell edits and the five append AFTER the base 17 (no slot renumbers in
|
||
either mode). D's measurement: classic **0/5 DJ fetches, 17-pool, 129-citizen census
|
||
byte-identical to a `b339402^` shadow tree** (the SAME crowd, ped for ped, not merely the same
|
||
width); default keeps all 22 (37 DJ-bodied street peds) and F's organic gig-crowd proof
|
||
(dj_phrtt_01 + woman_dj_02 in the pinned seed's venue crew) is preserved. F's re-gate on the
|
||
true final tree: the `smoke_djdance` warn line flipped to **"✓ ?classic=1: zero DJ-body
|
||
fetches"** — flags harness **0 fails / 0 warns**, and the DJ-body figure-scale leg still green
|
||
(the five spawn on the default boot exactly as before). The hold cost one extra wave and
|
||
nothing else — held rather than improvised, ruled by the party that owns the covenant, fixed by
|
||
the lane that owns the file, re-proven by the gate that held it.
|
||
|
||
### §36.5 The gate — full `qa.sh --strict --matrix` on the final tree (:8946, port-isolated)
|
||
|
||
**SUMMARY: 7 passed · 0 failed · 0 warn · 0 skipped — QA GREEN.** The per-gate record:
|
||
|
||
- **scaffold + consistency + citygen selfcheck**: ALL GREEN **156,352/156,352** checks.
|
||
- **manifest validator**: OK — 0 errors, 0 warnings (48 facades · 85 skin groups · 33 GLBs ·
|
||
28 audio; atlas-QA 15 per-shop atlases valid, 15 licence lines SHOWN; depot live).
|
||
- **flags harness: 0 fails / 1 warn** (the warn was §36.4's held DJ-roster finding, by design —
|
||
**0 fails / 0 warns on the wave-2.5 re-gate** after D's fix, see the addendum below).
|
||
- classic covenant: fp `0x5f76e76` holds · dig INERT at the bin · gig layer wholly absent ·
|
||
162 draws / 16,642 tris (baseline 164) · 0 errors.
|
||
- default boot: all four flags ON · 14 gigs / 14 posters / venue geo / tram · gig golden
|
||
`0xec7a2d39` · 131 draws / 23,390 tris @ venue block · **dig REACHABLE naked** (R31's fence).
|
||
- R30 gates re-green: sell routing ($12 buy → +$4 == min(basis−1, basis/2), HUD follows) ·
|
||
save determinism (byte-equal import, 255 B state / 316 B export, world fp `0xb1c7843e`
|
||
unmoved, weekNight 2 == (3−1)%7) · no-pump (5/5 strictly negative, Σ −$59, both controls
|
||
discriminate) · classic purity (ZERO Storage calls classic AND game=0; positive control
|
||
sees the save + all 7 game DOM ids) · rotation (day fps `0x50edd91b → 0x2121f187 →
|
||
0x2f4a9b93`, re-entry + cross-boot byte-identical, ?game=0 == day 1, 88,320 bytes hashed).
|
||
- **R32 bible**: 80 offers / 5 bins in-band-or-gem · bin1 holds 2 gems (grail asking $19 vs
|
||
keeper $30) · ledger closes Σask $333 → Σoffer $135 (gems +$21) · emptied bin 0/16 across
|
||
reload · ?game=0 re-opens 16/16.
|
||
- **R33 hunt**: ☆→★ through the real card (save carries 2 wants) · keeper muttered the rumor
|
||
("THE VERANDAHS? saw one in Castlemaine") · buy cleared ITS want · travel synthetic→melbourne
|
||
day 1→2 with $179 + 1 find riding · same-town free · return fare day 3 · ?game=0 zero DOM.
|
||
- **R34 godbay**: consign through the crate (collection 2→1) · ONE HAMMER byte-identical
|
||
across a double sleep · SOLD net $19 ≤ 2×$24×0.88 · the paper rode the wake toast
|
||
("⚖ SOLD Cane Toad Blues — hammer $22, $19 after fees") · travel hammered on arrival ·
|
||
?game=0 no auction house.
|
||
- **R35 first-five**: fresh game hunts HILLS HOIST with $191 on the splash · beats ②③ + the
|
||
send-off on the real path · returning save gets no tutorial · second fresh browser hunts the
|
||
SAME want (seeded) · ?game=0 control.
|
||
- **R36 smoke_djdance**: all green per §36.3 (binds 64/65 · dancers clip-driven · classic zero
|
||
dance fetch · noassets sway 0.067 m).
|
||
- **figure-scale gate**: **29 figs / 6 shops** all human-sized incl. the new DJ leg (§36.2's
|
||
five, 0.984–0.989 u → 1.64–1.89 m).
|
||
- **MATRIX GREEN — 10 towns × 7 gates** (boot · determinism · budget · district · roads · tram ·
|
||
noassets): 2 synthetic + 3 fixtures + 5 real caches, budgets 66–161 draws, every town
|
||
byte-equal on re-boot, 0 fetches under ?noassets across all ten.
|
||
|
||
**Wave-2.5 addendum — the re-gate on the TRUE final tree** (after D's `2fd4c1c` roster fix +
|
||
Fable's frontier commit `622a5e0`; run 3, healed box, port-isolated):
|
||
**SUMMARY 7 passed · 0 failed · 0 warn · 0 skipped — QA GREEN · MATRIX GREEN 10 towns ×
|
||
7 gates · flags harness 0 fails / 0 WARNS** (the §36.4 warn flipped to "✓ ?classic=1: zero
|
||
DJ-body fetches" — D's gate holds under the full suite, not just standalone). Everything else
|
||
unmoved from wave 2: selfcheck 156,352/156,352 · classic fp `0x5f76e76`, 162 draws/16,642 tris ·
|
||
default gig golden `0xec7a2d39`, 131 draws/23,390 tris · Rokoko binds 64/65 · noassets sway
|
||
0.071–0.075 m · figure-scale 29 figs/6 shops with the DJ leg's exact wave-2 statures
|
||
(0.984–0.989 u → 1.64–1.89 m) · rotation fps `0x50edd91b/0x2121f187/0x2f4a9b93` byte-identical
|
||
per day and cross-boot. *Honest record: re-gate runs 1 and 2 died mid-suite to a BOX-LEVEL fork
|
||
famine (m3panel had leaked ~10,600 orphaned `pmset|tail` processes over 2.5 days — fixed at
|
||
source, reaper installed); both partial REDs were starvation artifacts, not gates — every gate
|
||
that completed in those runs matched run 3's verdicts line for line.*
|
||
|
||
### §36.6 The freeze + the tag
|
||
|
||
- **README**: v7.0 section (the loop dig→guide→want→travel→list→paper, the delta-law machinery,
|
||
the classic covenant) + manual row for the morning paper (☆ WANT and ⚖ LIST rows landed with
|
||
their rounds).
|
||
- **V7_THE_GAME.md**: the five charter risks get their retired-or-carried verdicts, v4-style
|
||
(written by F under the single-lane-close ruling): economy RETIRED-structural (bot verifier
|
||
carried to the live tier) · schema drift RETIRED · re-emit identity CARRIED (never fired;
|
||
owned-item stability check still unbuilt — next re-emit round owns it) · scope gravity
|
||
RETIRED · v6 collision RETIRED (the 2 a.m. commits were the one contact, now swept + gated).
|
||
- **Tag `v7.0`** — annotated, **LOCAL ONLY** (the R35 close law: the tag waited for hands, and
|
||
the hands played twice). Fable reviews, pushes main + tag, writes the epoch retro.
|
||
|
||
---
|
||
|
||
## §37 — R37: v8 WAVE 0, THE FLOOR. F items 0.3 (the budget of record) · 0.2 (`smoke_tram`) · 0.5 (the classic network allow-list)
|
||
|
||
### §37.1 THE BUDGET OF RECORD — SETTLED. **292 draws / 120,145 tris. The audit was right; so was the spike; they were measuring different bearings.**
|
||
|
||
**THE PIN.** Default boot · synthetic golden town (seed 20261990) · tree `7bd96e5` (post-kerb) ·
|
||
walked the main-street spine · **1280×720, and the number is viewport-independent (proven below)**:
|
||
|
||
| | worst frame | where | live chunks | note |
|
||
|---|---|---|---|---|
|
||
| **draws, DEFAULT** | **292** of 300 | x −4.1, z 318.3 (s = 728 m), **NIGHT** | **31** (R+1 dispose window at max) | tris at that frame: 81,623 |
|
||
| **tris, DEFAULT** | **120,145** of 200,000 | x 4.1, z 60 (s = 466 m), NIGHT | 28 | draws at that frame: 238 |
|
||
| draws, `?classic=1` | 269 | same point | 31 | tris there 52,058 |
|
||
| tris, `?classic=1` | 90,580 | same point | 28 | draws there 215 |
|
||
| clean-load bookmark (`street_noon`) | 162–164 | — | ~22 | what the suite has been quoting for 20 rounds |
|
||
|
||
**The worst-draw frame and the worst-tri frame are NOT the same frame.** Quote them as two maxima,
|
||
never as one view. The real margin is **8 draws** and **~75k tris**. Spend triangles, not draws —
|
||
the charter's law survives its own re-measurement, and is now tighter than it was written.
|
||
|
||
**ACROSS THE CORPUS** (bearing sweep, 12 bearings × 8 m stations, default boot, `7bd96e5`) — the
|
||
synthetic golden town is the **draw**-worst town we ship; a dense real town is the **tri**-worst:
|
||
|
||
| town | shops / edges | worst draws | worst tris | live chunks |
|
||
|---|---|---|---|---|
|
||
| **synthetic** (golden) | 493 / 22 | **292** (night) · 282 (noon) | 120,145 | 31 |
|
||
| **fitzroy_real** | 139 / 2,408 | 200 (night) · 199 (noon) | **124,793** ← corpus tri worst | 33–35 |
|
||
| newtown_real | 67 / 1,626 | 162 (night) | 118,620 | 33–35 |
|
||
| melbourne (fixture, r=3 + shadows) | — | 226 (night) | 80,926 | 26 |
|
||
| katoomba (fixture) | — | 161 (night) | 43,366 | 10 |
|
||
|
||
**So the audit's tri range "99k–124k of 200k" is CONFIRMED and now pinned at its top: 124,793 on
|
||
`fitzroy_real`.** Real towns are draw-cheap and tri-dear (a real OSM grid has no single long straight
|
||
corridor to line chunks up down, but carries far more road geometry); the synthetic town is the
|
||
opposite. **A proposal must state which of the two it is spending.**
|
||
|
||
**Rarity, so nobody reads 292 as typical:** over 810 stations at 2 m along the spine, exactly
|
||
**8 frames of 810 exceed 280 draws at night, 1 of 810 at noon; `?classic=1` never does** (worst 269).
|
||
p50 is 177 draws / 62,975 tris at night. It is a real peak on a real walk, not a median.
|
||
|
||
#### The 280-vs-191 discrepancy, resolved: **it is the CAMERA BEARING, and nothing else.**
|
||
|
||
The two sessions disagreed by 90 draws because one of them turned the camera and the other did not.
|
||
Measured on one tree, one box, one afternoon — worst draw **per bearing**, 12 bearings × 102 stations
|
||
down the same spine (synthetic, default, noon):
|
||
|
||
| bearing | 0° | 30° | 60° | 90° | 120° | 150° | 180° | 210° | 240° | 270° | 300° | 330° |
|
||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||
| worst draws | **284** | 248 | 208 | 177 | 196 | 214 | 211 | 207 | 178 | 182 | 223 | **267** |
|
||
|
||
**110 draws of spread from the camera direction alone**, on identical positions. And the peak sits at
|
||
**yaw 0** — which is the *default argument* of `DBG.teleport(x, z, ry = 0)`. (The spread is a
|
||
synthetic-town property: its spine is one long straight corridor, so one bearing lines up 31 chunks.
|
||
On the real corpus the same sweep spreads only 141→200 on `fitzroy_real` and 113→162 on
|
||
`newtown_real` — a real OSM grid has no such corridor. Another reason to say which town you measured.)
|
||
|
||
- The city-patterns audit walked with `DBG.teleport(x, z)` and got **280**. It landed on the worst
|
||
bearing by taking the default. Reproduced here at **282** (8 m stations) / **292** (2 m stations, night).
|
||
- Lane B's R37 A/B walk, same method, got **276 draws / 99,880 tris**. My fixed-yaw-0 8 m walk at noon
|
||
gives **282 draws / 99,878 tris** — **the triangle counts agree to 2** across two independent
|
||
harnesses. Same frame. B's 6 fewer draws are station phase (102 one-way stations vs my 204
|
||
out-and-back, which lands 2 m off B's grid on the return leg and finds a marginally worse one).
|
||
- The animal spike supplied a yaw and got **191**; my facing-travel walk gets **205 (noon) / 215
|
||
(night)**. Same measurement, mid-range bearing. The spike's conclusion ("I cannot reproduce 280")
|
||
was correct *and* its number was correct — they are different quantities.
|
||
|
||
**So: the audit's 280 is VINDICATED and is the number to spend against — rounded up to 292.** The
|
||
spike's 191 is retired as the budget of record: it is a bearing-specific figure, not a worst frame.
|
||
|
||
#### What the dispose window is actually worth: **+19 draws, not +60–80.**
|
||
|
||
The audit's *mechanism* claim reproduces exactly — the worst frame does carry **31 live chunks**
|
||
(R = 2 on a BIG_CITY town, DISPOSE_R = 3), and I measure 31–32 on synthetic and 34–35 on
|
||
`newtown_real`. But teleporting *directly* to the worst point and standing there (clean arrival, 27
|
||
live chunks, 400 frames, fully settled) reads **265**, against **284** for the same point walked into.
|
||
So the R+1 residue is **+19 draws ≈ 4.75 draws per extra live chunk**. The other ~100 draws
|
||
separating the bookmark from the walked worst are the *place* and the *bearing*, not the streamer.
|
||
|
||
#### The second under-reporting mechanism, named because it will bite the next measurer
|
||
|
||
`CitizenSim` acquires near-tier rigs on a **per-frame budget** (`sim.js`: `NEAR_MAX 24`, and the
|
||
comment "if it fails this frame, ride as an impostor instead"). A sampler that renders **one** frame
|
||
per station — which is what `DBG.teleport()` does, and what every bookmark-style measurement does —
|
||
photographs a half-dressed street. Measured: standing still at the same point for 260 frames climbs
|
||
**222 → 231 draws**, and a 1 m-per-frame walk (17 m/s, outrunning the streamer) reads **211** where a
|
||
settled walk reads **213** and a settled *turn* reads 284. Any future budget claim must state its
|
||
settle policy or it is not comparable.
|
||
|
||
#### Sensitivity — the pin is only meaningful with the boot stated
|
||
|
||
| variant | worst draws | worst tris | verdict |
|
||
|---|---|---|---|
|
||
| default (`r` auto = 2, shadows off) | 282 / 292 (noon/night) | 99,870 / 120,143 | **the pin** |
|
||
| **`?r=3`** | **307 / 317** | 100,618 / 120,891 | **BREACHES the 300 gate.** 38–48 live chunks |
|
||
| `?shadows=1` | 243 / 249 | 137,566 / **157,635** | +38 draws, +38% tris (the depth pass redraws casters) |
|
||
| viewport 2560×1440 vs 1280×720 | **282 / 292 — identical** | **identical to the byte** | **resolution is irrelevant**; both are 16:9, and three's culling is frustum-shaped, not pixel-shaped |
|
||
| `melbourne` fixture (not BIG_CITY ⇒ r = 3 + shadows) | 210 / 226 | 55,094 / 80,926 | the small-graph towns are cheap |
|
||
| `katoomba` fixture | — / 161 | — / 43,366 | — |
|
||
| `newtown_real`, facing travel | 139 / 148 | 89,887 / 107,694 | real towns are draw-cheap, tri-dear |
|
||
|
||
**Finding for the round (not F's to fix): `?r=3` on the default town measures 307 draws and breaches
|
||
the ≤300 law.** `index.html:147` picks R = 2 for a BIG_CITY (>120 shops or >200 edges) and R = 3
|
||
otherwise; the `?r=` override is ungated and no gate covers it. It is a diagnostic flag, not a
|
||
supported boot — F has documented it as such in the README flags table and files the question of
|
||
whether to clamp it. Same story, smaller, for `?shadows=1` (tris 157,635, still legal).
|
||
|
||
#### Honest limits
|
||
|
||
Everything above is **headless Chromium / SwiftShader**. That is *not* a caveat on these numbers:
|
||
`renderer.info.render.calls` and `.triangles` are counted by three.js in JavaScript, downstream of
|
||
nothing the GPU does — SwiftShader cannot change a draw count. The viewport control above confirms it
|
||
empirically (identical to the byte at 4× the pixels). **What headless cannot tell you is frame
|
||
TIME**, and this pin says nothing about it. Nobody has ever measured procity's frame time on a real
|
||
GPU, and no proposal this epoch should claim a millisecond figure. If John ever wants the ms number,
|
||
it needs a hand on a machine with a display; the draw/tri budget does not.
|
||
|
||
#### The re-pin — where the number is asserted, and what each line should now say
|
||
|
||
F re-pins **§37.1 (this section)**. The rest are other authors' files; **flagged, not edited**:
|
||
|
||
| file · line | carries | verdict |
|
||
|---|---|---|
|
||
| `docs/V8_SOMEONE_LIVES_HERE.md:162` (Laws) | "the walked worst frame is 280/300; tris 99–124k of 200k; every proposal states its draw cost against **280**" | **UPGRADE to 292 / 124,793.** The law is right, the figures were low. 280 → **292** (8 draws of margin, not 20); the tri range's top is now measured, not estimated. |
|
||
| `docs/REVIEW_CITY_PATTERNS.md:25` | "peaks at 280 draws of 300 … the real margin is about twenty" | **VINDICATED, and tighten.** 280 reproduces at 282–292. "About twenty" → **eight**. Its 31-live-chunk mechanism claim also reproduces exactly. |
|
||
| `docs/REVIEW_CITY_PATTERNS.md:119` | "a main-spine control of 263 / 123,602" | consistent with this session (fixed-yaw walk, 282/99,878 noon · 292/120,145 night); no correction needed. |
|
||
| `docs/THE_FRONTIER.md:145` | "the draw budget of record is **280/300 walked**" | **280 → 292.** |
|
||
| `docs/ANIMALS_ANSWER.md:22,111` | "**191 default / 167 classic** … can't reproduce 280 … re-pin it first" | **RESOLVED, and the spike was not wrong** — 191 is a *bearing-specific* figure, correct for its method. Retire it as the budget of record; the spike's instruction ("re-pin it first") is now discharged. |
|
||
| `docs/CITY_SPEC.md:693` | "≤ 300 draw calls in street mode, ≤ 200k tris" | **UNCHANGED.** That is the *cap*, not the measurement. Still the law; we now sit 8 under it. |
|
||
| `tools/flags_check.py:47` `BASE_DRAWS, BASE_TRIS = 164, 66000` | classic's clean-load `street_noon` drift baseline | **UNCHANGED and deliberately not the budget of record** — it is a *bookmark* number for drift detection (currently reads 162), and this session is the proof that a bookmark under-reports a walk by ~120 draws. Left alone so the ±40% drift arm keeps working; do not confuse the two. |
|
||
|
||
*Method + rig: [`tools/qa/budget_walk.py`](../../tools/qa/budget_walk.py) — a no-store port-isolated
|
||
server over a `git archive`'d tree (immune to the other lanes editing the shared working tree
|
||
mid-measurement), four methods (`walk` continuous-motion through the real rAF loop sampling every
|
||
frame · `sweep` position × bearing · `dwell` steady-state · `stepwalk` reproducing the audit's
|
||
`DBG.teleport` method), `--fixed-yaw`, `--viewport`, `--holdframes`, `--extra`.*
|
||
|
||
**Independent confirmation of Lane B's `+0 draws` kerb claim.** Same method, same stations, pinned
|
||
`62c0a21` vs post-kerb `7bd96e5`: noon **282 → 282 draws, 99,878 → 99,870 tris**; night **292 → 292,
|
||
120,151 → 120,143**. +0 draws and −8 tris at the worst frame, measured by a harness B did not write.
|
||
|
||
### §37.2 THE SHARED INSTANCE CAP — SETTLED: **256.**
|
||
|
||
*One number, for any future town-wide standing layer (fauna, flock, street props, washing, litter).
|
||
Three proposals independently invented 120, 250 and "plus gulls" for the same layer; this ends that.*
|
||
|
||
**Measured cost model** (`InstancedMesh` sprayed over an 800 m box, default boot, seg 2, re-rendered
|
||
per sample — note `DBG.info()` alone reads the *previous* frame, which is how a first cut of this
|
||
probe reported a triumphant +0 for everything):
|
||
|
||
| N | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 |
|
||
|---|---|---|---|---|---|---|---|
|
||
| draws | **+1** | +1 | +1 | +1 | +1 | +1 | +1 |
|
||
| tris, 2-tri cell | 64 | 128 | 256 | **512** | 1,024 | 2,048 | 4,096 |
|
||
| tris, 180-tri prop | 5,760 | 11,520 | 23,040 | **46,080** | 92,160 | 184,320 | 368,640 |
|
||
| CPU, rewriting every matrix | 0.005 ms | 0.005 | 0.01 | **0.01** | 0.015 | 0.015 | 0.05 |
|
||
|
||
**+1 draw at every N, from 32 to 2048** — confirmed, so draws are *not* the constraint. **Triangles
|
||
are, and they are always submitted**: the instance-aware bounding sphere spans the whole town, so the
|
||
layer is effectively never frustum-culled — at N = 2048 standing in one street, all 368,640 prop
|
||
triangles go down the pipe. The cost is exactly `N × tris_per_instance`, everywhere, always.
|
||
|
||
**THE CAP: N = 256 live instances per shared standing layer, one InstancedMesh, town-wide.** Because:
|
||
|
||
1. **Triangles.** At the geometry these layers actually use (a 2–4 tri billboard cell), 256 costs
|
||
512–1,024 tris — **0.3–0.5% of the 200k budget, ~1% of the measured 80k headroom**. Ten such
|
||
layers would still be under 2% of budget. The cap is generous where it is cheap.
|
||
2. **It carries a companion, or it means nothing.** The cost is `N × tris/instance`, so the cap is
|
||
binding only with a per-instance ceiling: **≤ 24 tris per instance at the cap** (6,144 tris,
|
||
~3% of budget). A heavier mesh spends the same ceiling on fewer instances. A 180-tri prop at 256
|
||
is 46,080 tris — **over half the entire measured headroom for one layer**, and it must not.
|
||
3. **Encounter density — the number is above the covenanted town's whole anchor set.** Measured
|
||
anchor supply (derived on the plan graph: a pole every 24 m of main+side, a tree every 18 m of any
|
||
edge): synthetic **246 poles / 366 trees over 6.8 km**; `darwin_real` 1,773 / 2,407; `katoomba_real`
|
||
3,373 / 4,656; `newtown_real` 4,408 / 6,049; `fitzroy_real` 4,549 / 6,169; `bendigo_real`
|
||
**7,325 / 9,759 over 188 km**. So on the default boot 256 exceeds the entire pole set — the cap
|
||
never thins the layer there, the anchors do. On the real corpus 256 is **3–14% occupancy**.
|
||
4. **Therefore, binding corollary: the cap is spent PROXIMITY-FIRST, never sprayed town-wide.**
|
||
Allocate the 256 to the anchors nearest the camera and re-anchor on chunk build/dispose (Lane B's
|
||
R37 `ctx.onChunkBuilt`/`onChunkDisposed`, item 0.4 — now wired). A layer that scatters 256
|
||
instances across bendigo's 188 km of road is one instance per 730 m and is *statistically
|
||
unencounterable* — the exact failure the review's magpie correction named.
|
||
5. **CPU is a non-issue at this cap**: 0.01 ms/frame to rewrite all 256 matrices, 0.06% of a 16.7 ms
|
||
frame. It only becomes interesting past ~2048.
|
||
|
||
**Also re-confirmed while there** (the review's biggest strategic finding, reproduced independently):
|
||
**all five real towns sampled are 100% `use:'shop'` lots with the single district `mainstreet`.**
|
||
Synthetic carries six districts and `{shop 452, house 145, stall 40, yard 36, infill 7, anchor 1}`.
|
||
Every residential/market/yard idea is still synthetic-only until Ruling 2's civic fetch lands.
|
||
|
||
### §37.3 `smoke_tram` — the vacuous gate, rebuilt (item 0.2)
|
||
|
||
**What it was** (`flags_check.py:617-630`, R9 → R36): one assertion,
|
||
`!!scene.getObjectByName('tram')`; a printed SKIP and an early `return` on absence; warn-level.
|
||
Three faults, each fatal on its own:
|
||
|
||
1. **It passed on the fenced path.** `tram.js:110-114` adds a **named EMPTY group** to the scene
|
||
before returning the no-op stub, so the single assertion is TRUE on every town where the tram
|
||
deliberately does not exist. The gate was green precisely where it was meant to be looking. The
|
||
repaired gate now *prints that as a standing record* on every run:
|
||
`[record] the pre-R37 assertion returns TRUE here — a 0-child group on a town with no tram.`
|
||
2. **SKIP-and-return on a missing subject** — vacuous-gate law, inside our own suite, since R9.
|
||
3. **It never asserted the tram was a tram** — no route, no stops, no motion.
|
||
|
||
**What it is now — STRICT, four arms, both controls demonstrated every run:**
|
||
|
||
- **RUNS** (default boot, synthetic): route resolved (6 main edges, 810 m, 172 shops fronted),
|
||
2 stops, and — under the **shell's own rAF loop, nothing driven by the harness** — the body moves
|
||
**19.8 m over 45 frames across 45 distinct positions**.
|
||
- **BEHAVES** (an *isolated* tram built from the same plan into a throwaway scene, fixed dt = 0.05 s,
|
||
400 s — so the shell's own tick cannot contaminate the integration): travels **3,300 m of an 810 m
|
||
route**; **9 stop dwells accounting for exactly 31.95 s of door time = 9 × DWELL 3.5 s**; max
|
||
cruising tick **0.4665 m ≤ SPEED·dt 0.45 m**; **4 end reversals**, each an exact 2·LANE lane swap,
|
||
and **every** discontinuity bounded by 2·LANE (0.681–6.402 m) so nothing else is teleporting it;
|
||
and it rides **3.2 m off the centreline (median, range 2.98–3.20)** measured against **Lane A's own
|
||
main-edge segments**, not against tram.js's polyline. LANE is 3.2.
|
||
- **GENUINELY ABSENT** on the fenced `katoomba_real`: `fenced: true`, **0 stops, 0 group children,
|
||
0 m moved over 30 frames**, reason string on record. If katoomba ever stops being fenced the gate
|
||
**FAILS** with instructions to re-pick (R21: katoomba 3 / fremantle 2 / bendigo 1 shops fronted) —
|
||
it does not skip. An absence arm without a subject is the original bug wearing a new hat.
|
||
- **OFF**: `?tram=0` and `?classic=1` — `PROCITY.tram === null` **and** no `'tram'` in the scene.
|
||
- **CONTROL A**: the RUN predicate pointed at the fenced town's subject → **RED, required.**
|
||
**CONTROL B**: the ABSENCE predicate pointed at the running tram → **RED, required.** Both
|
||
predicates are extracted as functions for exactly this reason; each is *proven* to discriminate on
|
||
this tree in this run rather than asserted to.
|
||
|
||
**Three findings for Lane B, filed not fixed** (B's file; none is a defect the gate should hide):
|
||
the end-of-line reversal **teleports the tram 6.4 m sideways** in one tick (`dir` flips ⇒ the LANE
|
||
offset mirrors) — a visible lane-swap pop at both ends; the stop snap moves it up to `EPS` = 2.5 m;
|
||
and at each polyline bend the lane offset kinks by `2·LANE·sin(θ/2)` (measured up to 0.68 m). All
|
||
three are bounded by 2·LANE and the gate now asserts exactly that, so any *new* discontinuity is red.
|
||
|
||
### §37.4 `classic_regression` gets a NETWORK ALLOW-LIST (item 0.5)
|
||
|
||
`classic_regression` had made **no network assertion at all** for 21 rounds — it watched the plan
|
||
fingerprint, the flags, the draws, the console, and nothing watched the wire. That is why R36's
|
||
fetch-surface widening needed a brand-new smoke to catch it.
|
||
|
||
**The authority is git, not an opinion.** The allow-list is *derived from the `v2.0` tag at gate time*:
|
||
|
||
- same-origin asset ⇒ its path must exist in `git ls-tree -r v2.0 -- web/`;
|
||
- depot asset (`digalot.fyi/3god/a/<file>`) ⇒ its basename must appear in **v2.0's own**
|
||
`web/assets/manifest.json`;
|
||
- code (same-origin `.js`/`.mjs`/document) is allowed as a class — a module that fetches nothing is
|
||
not a fetch-surface change. Every **asset** is enumerated. `blob:`/`data:` are not network
|
||
(GLTFLoader mints one blob per embedded texture — 60+ of them, all noise).
|
||
|
||
**THE BRIEF'S SHAPE WOULD NOT HAVE CAUGHT THE BREACH IT WAS WRITTEN FOR.** The round instructions
|
||
say "the four v2-era depot furniture GLBs plus **same-origin**". The R36 breach was
|
||
`/models/peds/woman_dj_01.glb` — **same-origin**. An allow-list with a same-origin free pass is green
|
||
straight through it. Here `/models/peds` is enumerated by the v2.0 tree, which contains **exactly the
|
||
covenanted 21 files** (17 `PED_NAMES.normal` + 2 comical + `walk.glb` + `idle.glb`) — git-verified:
|
||
v2.0's `rigs.js` roster is byte-identical to today's post-`2fd4c1c` list, and today's
|
||
`web/models/peds/` holds **32** files, so 11 post-v2 bodies/clips are excluded by construction.
|
||
|
||
**The nine depot fetches are BASELINE, not a breach, and must never be "fixed" away.** Four street
|
||
(`bench` · `bin` · `bus_shelter` · `food_cart` — `furniture.js` `GLB_FILES`, byte-identical at
|
||
`24cef5a` and at `v2.0`) plus five record-shop fittings (`counter_till` · `crate_stack` ·
|
||
`listening_booth` · `record_crate` · `wire_shelf`) reached through `useGLB: !NOASSETS`, whose wiring
|
||
in `index.html` is byte-identical to v2.0. Proven the hard way rather than argued: I checked out the
|
||
**actual v2.0 tree**, served it, booted it and captured its wire — it fetches the same nine.
|
||
|
||
**Anti-vacuous arm**: a REQUIRED v2 core (the four depot street GLBs, `walk.glb`, `idle.glb`, a named
|
||
ped body, `manifest.json`) must all be observed, or the clean verdict is refused as unearned.
|
||
|
||
**THE CONTROL, run every time, not once**: after the verdict is taken, the gate injects
|
||
**`models/peds/woman_dj_01.glb` — the R36 breach itself, replayed** — plus
|
||
`assets/gen/__gate_control_v8_palette.jpg`, a name that has never existed (a 404 is still a request
|
||
on the wire), and asserts the clean verdict becomes **exactly 2 violations, named**. Green:
|
||
`the allow-list DISCRIMINATES — … turned a clean verdict into exactly 2 violations`.
|
||
|
||
#### 🟠 MEASURED AND CARRIED FOR FABLE: classic's fetch surface has a **post-v2.0 delta of +6 files**
|
||
|
||
Booting the real `v2.0` tree and diffing its wire against today's `?classic=1`:
|
||
|
||
| file(s) | landed | round |
|
||
|---|---|---|
|
||
| `assets/audio/ambience-street-day.ogg`, `-night`, `ambience-rain`, `music-record-shop`, `roomtone-retail` | `f7dd44f` | R11, the WebAudio engine |
|
||
| `assets/towns/index.json` | `078245f` → `4b01449` | R20/R21, the town selector |
|
||
|
||
Nothing else. **They are allowed on the list** — they have shipped through 26 rounds and five tags,
|
||
and making the suite permanently yellow over settled behaviour is how a warn becomes wallpaper — but
|
||
they are **PRINTED every run** so they cannot go invisible, and they are **UNRATIFIED**. Ruling
|
||
needed: either they are part of what the covenant preserves (say so, and the list carries them by
|
||
right), or classic should not be fetching them (and they get gated like the DJ bodies were).
|
||
**F did not improvise a verdict on a six-epoch-old fact.**
|
||
|
||
### §37.4a Fable's ruling on the +6 delta, landed in the mechanism (`4a7c0f1`)
|
||
|
||
**The audio is RATIFIED BASELINE and git makes that exact rather than generous.** Fable ruled the
|
||
covenant preserves "the exact v2 town" and **`v2.1` was v2's final tagged state**, so the audio pack
|
||
shipped *inside* v2 exactly as the furniture GLBs did. So the allow-list's authority moved
|
||
**`v2.0` → `v2.1`** — and that is a surgical widening, not a shrug: `git diff v2.0 v2.1 -- web/` is
|
||
the audio pack, `js/world/audio.js` and four wiring edits. **Zero other assets. Zero deletions.** The
|
||
tag change therefore admits precisely the five ratified audio files and nothing else, and the depot
|
||
GLB names survive unchanged in v2.1's manifest. Verified in the gate output: same-origin baseline
|
||
assets **47 → 52**, carried exceptions **2 → 1**, control still discriminating.
|
||
|
||
**`assets/towns/index.json` is NOT baseline** and is now labelled so it can never read as if it were.
|
||
It prints every run as `EXCEPTION (NOT v2.1 baseline)` with its provenance (v4 selector machinery,
|
||
`078245f` R20 / `4b01449` R21) and Fable's reasoning (no geometry, no identity, no seeded draw — not
|
||
the R36 species, not gettable in a floor round). The open question — *should `?classic=1` show the
|
||
town selector at all?* — is on the v8 debt ledger for R38+.
|
||
|
||
*Also recorded by Fable: the allow-list mechanism (derive from `git ls-tree <tag>` at gate time) is
|
||
the correct one and the brief's "four depot GLBs + same-origin" shape was wrong; and the "viewport is
|
||
the variable" hypothesis was Fable's, disproven by F's measurement, and is noted as Fable's.*
|
||
|
||
### §37.6 THE KERB GATE (item 0.1, F half) — **the ruling is in the paint, and the control fires at exactly 14**
|
||
|
||
`smoke_kerb`, STRICT, wired into the harness after `smoke_tram`. Lane A's corrected selfcheck asks
|
||
the **plan-side** question (is the poster's offset inside `vergeBand(e)[0]`?). That is right for A's
|
||
file — but it still never reads a number that came from B, which is the precise failure that made the
|
||
old gate vacuous ("the gate and its subject were the same function agreeing with itself"). So F's
|
||
gate asks the **runtime** question, of the paint:
|
||
|
||
> **raycast straight down at every rendered poster and classify the topmost ground hit.**
|
||
|
||
Classification is by **material identity** — `skins.groundMat()` is a cached factory, so the road
|
||
mesh's material *is* the object `groundMat('djsim-road')` returns. No mesh names, no string matching,
|
||
nothing that drifts when B edits the file. **Topmost, not first**, deliberately: B put the carriageway
|
||
*on top* at junctions (`ROAD_Y` 0 → 0.04) so roads run through intersections, and what the player sees
|
||
under a poster is the top surface.
|
||
|
||
**All six arms green on the final tree:**
|
||
|
||
| arm | result |
|
||
|---|---|
|
||
| **subject** | **14 posters rendered == 14 in `plan.posters`**, every one within **0 m** of its planned position — read off the real `venue-posters` `InstancedMesh` matrices. *Nobody had ever asserted that the poster you see is the poster A placed.* Zero posters ⇒ FAIL, never a skip. |
|
||
| **detector control A** | the centreline of the longest main (`main@28 m`, 137.5 m) classifies **ROAD at y = 0.04** — the detector can see bitumen |
|
||
| **detector control B** | **13 m** off that centreline (1 m inboard of the corridor edge) classifies **FOOTPATH at y = 0.02** — the ruling is in the geometry |
|
||
| **THE RULING** | **0 of 14** posters stand on road paint on the default boot. Surface tally: `{djsim-footpath: 14}` |
|
||
| **THE CONTROL** | the same leg under **`?verge=0` fails at EXACTLY 14** — tally `{djsim-road: 14}`. Keyed to B's flag, not to a line number, matching A's discipline |
|
||
| **classic keeps its paint** | 13 m off the centreline is **still ROAD** under `?classic=1`, and classic renders **0 posters** — Fable's mid-round ruling holds; the covenanted render did not move on a render-side ruling |
|
||
|
||
**The two detector controls are the load-bearing part.** "0 posters on road" is a sentence about
|
||
nothing unless the classifier can be shown to say *road* and to say *footpath* on the same boot. Both
|
||
are asserted every run, so the green cannot come from a detector that is silent.
|
||
|
||
**A cross-lane agreement worth recording: two lanes, two entirely independent methods, the same
|
||
number.** Lane A counted 14 plan-side, by offset against `vergeBand(e)[0]`, in node, over the plan.
|
||
F counted 14 paint-side, by raycast against merged mesh materials, in the browser, over Lane B's
|
||
geometry. The default boot flips both to 0/clear. That is the kerb ruling proven from both ends.
|
||
|
||
*Honest note on the semantics:* a poster clear of *its own* edge's carriageway could still stand on a
|
||
**crossing** street's carriageway at a junction, where B's `ROAD_Y` fix puts road on top. F's gate
|
||
would call that on-road and A's would not. **Measured: it does not happen on the default boot** (0 of
|
||
14). If it ever does, the two gates disagreeing is the finding, not a bug in either.
|
||
|
||
### §37.5 Housekeeping
|
||
|
||
- **`flags_check.py --only <name[,name…]>`** for single-gate development runs. It prints a
|
||
`PARTIAL RUN … (NOT a suite verdict)` banner and a `PARTIAL-GREEN` verdict, so a subset can never
|
||
be mistaken for a suite pass in a log. `qa.sh` invokes `flags_check.py` with no arguments; unaffected.
|
||
- **README flags table** gains `?verge=0` (Lane B's R37 falsifiability control — B flagged that the
|
||
table is F's file) and a row for `?r=N` / `?shadows=0|1` naming the `?r=3` breach measured above.
|
||
The `?verge=0` row states A's corrected band figures, not the charter's headline: **9 m is the
|
||
synthetic town's 28 m main only**; a real main is 24 m ⇒ 7 m, and `side`@12 — **72% of the whole
|
||
corpus** — is 3 m.
|
||
- **Lane B's three tram discontinuities** (the 6.4 m end-of-line lane swap, the 2.5 m `EPS` stop
|
||
snap, the ≤0.68 m bend kink) arrived as measurements and are now *asserted* by the rebuilt
|
||
`smoke_tram`: every discontinuity must be **bounded by 2·LANE**, with **≥1** at exactly 2·LANE
|
||
(proving it reached the end and turned). No action beyond keeping them here; a new one goes red.
|
||
- Interim regression on `7bd96e5`: `classic_regression` · `default_boot_gate` · `smoke_tram` —
|
||
0 fails / 0 warns. Superseded by the full suite below.
|
||
|
||
### §37.7 THE ROUND CLOSE — `qa.sh --strict --matrix` on the true final tree
|
||
|
||
Tree: Lane A `e4d7762` + Lane B `47db478`/`79177bc` + Fable `7bd96e5`/`4a7c0f1` + F `2396885`,
|
||
`6afd1ed`, `f522021`. Port-isolated on `:8975`.
|
||
|
||
> **SUMMARY: 7 passed · 0 failed · 0 warn · 0 skipped — QA GREEN.**
|
||
|
||
- **scaffold + consistency + citygen selfcheck: ALL GREEN, `157,407/157,407`** (was 156,352 — Lane A's
|
||
+1,055 new poster-clearance assertions, three arms, all landing green on the ruled street).
|
||
- **manifest validator**: OK — 0 errors, 0 warnings (48 facades · 85 skin groups · 33 GLBs · 28 audio;
|
||
atlas-QA 15 per-shop atlases valid, 15 licence lines SHOWN; depot live).
|
||
- **flags harness: 0 fails / 0 WARNS.**
|
||
- **classic covenant**: fp `0x5f76e76` · dig INERT at the bin · gig layer wholly absent ·
|
||
**162 draws / 16,642 tris** · 0 errors · **fetch surface CLEAN against `v2.1`** — 125 requests,
|
||
68 code modules / 52 same-origin v2.1 assets / 4 depot assets in v2.1's manifest / **1 named
|
||
exception** (`towns/index.json`, printed) · **the allow-list control fired** (2 injected → 2 named
|
||
violations).
|
||
- **default boot**: all four flags ON · 14 gigs / 14 posters / venue geo / tram · gig golden
|
||
`0xec7a2d39` · 131 draws / 23,382 tris @ venue block · dig REACHABLE naked.
|
||
- **`smoke_kerb` (new)**: all six arms green — §37.6. Control at exactly 14 under `?verge=0`.
|
||
- **`smoke_tram` (rebuilt)**: all arms + **both** controls green — §37.3.
|
||
- R11 audio · R12/13 gigs · R15 continuity · R29 glance · R30–R35 game gates · R36 djdance ·
|
||
R24 real crate · R27 tier-2 kill-the-server: all re-green on the ruled street. `?classic=1` still
|
||
fetches zero `look.glb` and zero dance clips.
|
||
- **figure-scale gate**: 29 interior figs across 6 shops all in [1.4, 2.0] m, incl. the R36 DJ-body leg
|
||
(0.984–0.989 u → 1.64–1.89 m).
|
||
- **MATRIX GREEN — 10 towns × 7 gates** (boot · determ · budget · district · roads · tram · noassets):
|
||
2 synthetic + 3 fixtures + 5 real caches, every town byte-equal on re-boot, 0 fetches under
|
||
`?noassets` across all ten, tram verdicts unchanged (runs on synthetic/fixtures/castlemaine/newtown;
|
||
**fenced on bendigo, fremantle, katoomba_real** — the same three the rebuilt `smoke_tram` uses for
|
||
its absence arm).
|
||
|
||
**One honest caveat on the matrix's own budget column**, so this round's central finding is not
|
||
undone by its own suite: the matrix budgets (**69–161 draws**) are `DBG.shot('venue_night')` **clean-load
|
||
bookmark** numbers. §37.1 is the proof that a bookmark under-reports a walked-and-turned frame by
|
||
~120 draws. The matrix column is a *regression* signal (has this town's cost moved?), **not** the
|
||
budget of record. The budget of record is §37.1's 292 / 124,793, and nothing else in the repo
|
||
measures it. Making the matrix walk-and-turn every town is a real improvement and a real cost —
|
||
**filed to the v8 debt ledger, not smuggled into a floor round.**
|
||
|
||
**No tag.** Wave 0 levels the floor; it does not ship a version.
|
||
|
||
---
|
||
|
||
## §38 — R38: v8 WAVE 1, INHABITATION. F: the budget reconciliation · the four gates · the round close
|
||
|
||
**Verdict: WAVE 1 SHIPS. The budget of record does NOT move — still 292 / 300 draws, and the margin
|
||
is still EIGHT, not six.** The brief's naive sum (292 + washing 1 + magpie 1 = 294) is wrong, and it
|
||
is wrong in our favour. **The pinned worst-draw frame is a NIGHT frame, and both of wave 1's
|
||
draw-spending layers are structurally zero at night**: the washing comes in (`mesh.count = 0`) and
|
||
the magpie roosts (`mesh.count = 0`). Wave 1's one draw is spent in daylight, where the walked peak
|
||
sits **ten draws lower**. Measured on two pinned trees, one box, one afternoon — not reasoned.
|
||
|
||
### §38.1 THE BUDGET RECONCILIATION — **292 / 300 stands. +0 draws at the pinned worst frame.**
|
||
|
||
**Method: R37 §37.1's, unchanged, so the two rounds are comparable.** Two **port-isolated no-store
|
||
servers** over `git archive`'d trees. `f38_ctrl` = **`6bc7926`** (pre-B, pre-E) — byte-identical under
|
||
`web/` to R37's pinned `7bd96e5` (`git diff --stat 7bd96e5 6bc7926 -- web/` is empty, so the control
|
||
IS the tree the pin was taken on). `f38_treat` = **`4514cb9`** (B on top of E) — byte-identical under
|
||
`web/js` + `web/index.html` to the round's true final `74d6257` (E's session-2 publish is assets only).
|
||
**`web/assets` is a SYMLINK to the live tree on BOTH sides**, so Lane E's asset wave and its mid-round
|
||
follow-up are the same bytes in both browsers and cannot be the difference. Default boot, seed
|
||
20261990, synthetic golden town, **1280×720**, `--method stepwalk --stepm 2 --fixed-yaw 0 --laps 2` =
|
||
**802 stations** down the main spine at **yaw 0, the worst bearing** (R37: 110 draws of spread from
|
||
the camera direction alone, peak at the *default argument* of `DBG.teleport`). **The citizen sim and
|
||
the tram are LIVE**, because the default boot is what the pin measures.
|
||
|
||
**The control reproduces the R37 pin to the byte, twice, on a fresh checkout: 292 draws / 120,145
|
||
tris, 31 live chunks, at x −4.1 z 319.2.** The method is repeatable; that is what makes the rest of
|
||
this table an argument rather than an anecdote.
|
||
|
||
| synthetic, default boot, 802 stations @ 2 m, yaw 0, **sim LIVE** | control `6bc7926` | R38 `4514cb9` | delta |
|
||
|---|---|---|---|
|
||
| **NIGHT (seg 5) — worst draws** | **292** | **292** | **+0** |
|
||
| NIGHT — worst tris | 120,145 | 122,879 | +2,734 |
|
||
| **NOON (seg 2) — worst draws** | **282** | **283** | **+1** |
|
||
| NOON — worst tris | 99,872 | 103,118 | +3,246 |
|
||
| night p50 draws | 177 | 176 | −1 |
|
||
| `?classic=1`, night | **269 / 90,580** | **269 / 90,580** | **0 / 0** |
|
||
| `fitzroy_real`, night (corpus tri worst) | **172 / 130,727** | **172 / 130,727** | **0 / 0** |
|
||
| `bendigo_real`, noon | **204 / 131,492** | **204 / 131,492** | **0 / 0** |
|
||
| `bendigo_real`, noon, **`?magpie=1` forced** | — | **204 / 131,492** | **0 / 0** |
|
||
|
||
**The reverts are EXACT, not approximate.** On the treatment tree: `?yards=0` at night →
|
||
**292 / 120,145**, all three of the control's numbers; all four flags off
|
||
(`yards=0&washing=0&magpie=0&ambient=0`) → **292 / 120,145**; and `?yards=0` **at noon** →
|
||
**282 / 99,872**, which isolates the noon **+1 to the washing and to nothing else**. `?magpie=1`
|
||
forced at noon still reads **283** — the bird is +1 *where he is*, and where he is is never the
|
||
worst frame.
|
||
|
||
**Settled at the pinned worst point** (`dwell`, 700 frames at x −4.1 z 318.3 yaw 0, sim live, crowd
|
||
fully acquired): night control **270 / 60,476** vs R38 **270 / 61,276** — **+0 draws**; noon control
|
||
**265 / 55,734** vs R38 **266 / 57,046** — **+1**, the washing again, in daylight again.
|
||
|
||
**Lane B's isolated costs reproduce independently in my gates** (`smoke_yards`, `smoke_magpie`, at a
|
||
fixed pose with the citizen sim quiet on both sides via `?roster=v1&pop=0`): washing **+1 draw /
|
||
+512 tris**, magpie **+1 draw / +182 tris**, every yard box prop **+0 draws**. B's numbers are B's
|
||
numbers and they are also mine.
|
||
|
||
**One finding B did not claim and I will:** the yard build is **2 draws CHEAPER** than what it
|
||
replaced at a residential pose (`?washing=0` 191 draws vs `?yards=0` 193). The 36 `yard` lots that
|
||
fell through to `buildShopfront()` since v1 carried facade, signage and awning materials; they are now
|
||
`boxMats`-only fenced blocks. That is why B's 102-frame sum went **down** 13 draws while the worst
|
||
frame went up 1.
|
||
|
||
### §38.2 LANE B's "control 346": **the artifact is real AND the mechanism is real. Both, separately.**
|
||
|
||
B reported that with the sim live the same step read **control 346 vs treatment 232**, and correctly
|
||
refused to use it. As a *delta* it is the artifact R37 §37.1 named: `CitizenSim` acquires near-tier
|
||
rigs on a per-frame budget (`sim.js NEAR_MAX 24`) and peds spawn on **wall-clock** time, so how many
|
||
rigs are dressed at sample time is a function of how long that browser has been sitting there. A
|
||
114-draw swing between two browser instances measures the sim, not the build. B was right to hide it.
|
||
|
||
**But 346 is also above 300, and "it is an artifact" is not an answer to that.** R37 named *two*
|
||
under-reporting mechanisms and each of my existing methods defeats exactly one:
|
||
|
||
| method | R+1 dispose residue | crowd acquisition | reads at the pinned point |
|
||
|---|---|---|---|
|
||
| `stepwalk` (the pin) | **yes** — 31 live chunks | **no** — 1 frame/station | **292** |
|
||
| `dwell` | **no** — clean arrival, 27 chunks | **yes** — 700 frames | **270** |
|
||
| **`stepsettle` (new this round)** | **yes** | **yes** | *below* |
|
||
|
||
`tools/qa/budget_walk.py` gains **`--method stepsettle`**: the audit's fixed-yaw stations, each one
|
||
**drained (`chunks.pending → 0`) and then held `--holdframes` real rAF frames** so the shell's own
|
||
`frame()` runs the sim to steady state before the sample is taken. It is Lane B's method — drain,
|
||
then read through the composer — with R37's bearing discipline on top. It is the only one of the four
|
||
that can adjudicate a >300 claim, and it exists because B's 346 deserved an answer rather than a
|
||
dismissal.
|
||
|
||
**THE ANSWER: a >300 transient is NOT reproducible, on either tree, by any of the four methods.**
|
||
Like for like — the **same 101 stations at 8 m, yaw 0, one lap**, so the ONLY difference between the
|
||
two rows is the settle:
|
||
|
||
| 101 stations @ 8 m, yaw 0, sim live | `stepwalk` (1 frame/station) | `stepsettle` (drained + 25 frames) | what the settle is worth |
|
||
|---|---|---|---|
|
||
| **NIGHT** control | **287** / 120,145 | **278** / 100,939 | **−9 draws** |
|
||
| **NIGHT** R38 | **287** / 122,879 | **278** / 104,178 | −9 |
|
||
| **NOON** control | **277** / 99,872 | **280** / 111,308 | **+3 draws** |
|
||
| **NOON** R38 | **278** / 103,118 | **281** / 115,169 | +3 |
|
||
|
||
**Settling is worth +3 draws at noon and −9 at night, and the sign flip is the finding.** At noon
|
||
the crowd fills in — R37's mechanism, in the direction R37 predicted, and smaller than R37's
|
||
standing-still +9. **At night it goes the other way**: the single-frame teleport photographs rigs
|
||
still dressed from the previous station and the settled street *retires them to impostors*
|
||
(tris fall 120,145 → 100,939, and a near rig is ~2.4k tris). The night crowd goes home; a sampler
|
||
that never waits does not see it happen.
|
||
|
||
**So the highest draw count anywhere in this reconciliation — four methods × two trees × two
|
||
segments × five towns — is 292, and it is the pin.** Nothing read over 300. The mechanism that
|
||
*could* legitimately have breached it (a fully-acquired crowd standing inside a full R+1 dispose
|
||
window) is now measured and it reads **280 / 281**: nineteen draws under the cap, eleven under the
|
||
pin. **Lane B's 346 was the artifact, and B was right to refuse it.** What I will add is that the
|
||
refusal is now backed by a measurement rather than by a citation — the method that could have
|
||
falsified it exists, was run, and did not.
|
||
|
||
*(One caution the same table teaches: `stepsettle`'s 278 is below `stepwalk`'s 292 mostly because it
|
||
walked 101 stations and the pin walks 802. **Station spacing, not settle policy, is the single
|
||
biggest lever on a walked worst-frame number** — see §38.3, where it moved a corpus pin by 6k tris.
|
||
Any future budget claim must state its station spacing as well as its settle policy and its bearing.)*
|
||
|
||
### §38.3 A CORRECTION TO MY OWN R37 PIN: the corpus TRI worst is **130,727**, not 124,793.
|
||
|
||
R37 pinned `fitzroy_real` at **124,793 tris** off a 12-bearing × 8 m *sweep*. Walked at **2 m stations
|
||
at yaw 0**, the same tree reads **130,727** — and it reads 130,727 on the **control**, so this is a
|
||
sampling-density correction to my own number, not a wave-1 cost. A finer station grid finds a worse
|
||
frame; that is what finer grids are for. **Re-pin: corpus tri worst 130,727 / 200,000** (35% headroom).
|
||
The draw pin is untouched. *A pin is only as good as its station spacing, and mine had not said so.*
|
||
|
||
### §38.4 THE FOUR GATES — each with its falsifiability control DEMONSTRATED, in the same run
|
||
|
||
`tools/flags_check.py` gains **`smoke_ambient` · `smoke_character` · `smoke_magpie` · `smoke_yards`**,
|
||
wired into `main()` before `smoke_tier2`. The house rule they all obey, in one line: **a gate that
|
||
cannot fail is not a gate.** This round earned that sentence twice over — R37 killed two vacuous
|
||
gates, and R38's own §1.3 season mechanism produced a constant (24 of 24 towns at 214 days) that
|
||
*would have gated green* because `?magpie=1` forces the state. Lane B caught its own bug. Every arm
|
||
below either flips a subject between two boots that differ in exactly one thing, or asserts a
|
||
negative that a broken build would satisfy — never "the object exists".
|
||
|
||
#### (a) `smoke_ambient` — §1.1, the audio inhabitation layer. **GREEN, 0 warns.**
|
||
|
||
| arm | result |
|
||
|---|---|
|
||
| six sources published, table == `character.js`'s `AMBIENCE_SOURCES` | ✓ |
|
||
| all six resolve in the **shipped** manifest; none reads `no-entry` unpatched | ✓ (so the control below means something) |
|
||
| all six anchored on the default boot | **63 dog · 8 mower · 81 radio · 32 sprinkler · 39 roller-door · 57 magpie-carol** — B's numbers exactly |
|
||
| **positive control** — one source reaches `audible` | dog-bark-fence **audible at 5.9 m, gain 0.25, 1 play scheduled**. For a one-shot, `audible` is only published once `plays > 0`, so this single word covers fetch → decode → layer gain → scheduling → counter |
|
||
| **HOURS is enforced** | sprinkler ([6,10)) **audible at 06:30 → idle at 12:30, same pose** |
|
||
| **DAY-OF-WEEK is enforced** | mower (weekend-only) **idle on day 1 (Mon) → audible on day 6 (Sat), same pose, same segment** |
|
||
| **PAN is a bearing, not a constant** | yaw 0/90/180/270° → **+0.00 +0.85 −0.00 −0.85** |
|
||
| **ZERO DRAWS is structural** | default **191 draws / 30,342 tris == `?ambient=0`, byte for byte** |
|
||
| `?classic=1` | layer OFF, no sources constructed |
|
||
| **THE FALSIFIABILITY CONTROL (kept verbatim)** | a second server on its own port whose root is `web/` **symlink-for-symlink** except one patched `manifest.json` — `audio.sfx['dog-bark-fence']` renamed. That source reports **`no-entry`, 0 plays, never `audible`**, anchors still 63; the other five are **unchanged**; **0 console errors** (a missing key is silence by house law, not an error) |
|
||
|
||
**The pan arm's first cut was itself nearly vacuous and I am recording it.** Two bearings (0°/180°)
|
||
read `+0.00 / −0.00`, because that anchor happened to sit on the forward axis — a working panner
|
||
would have been reported "inconclusive". Four bearings cannot be dodged by geometry. *The gate's own
|
||
first draft failed the standard the gate exists to enforce.*
|
||
|
||
**What this gate cannot do, said plainly:** it cannot confirm a human would HEAR anything. The
|
||
autoplay policy needs a trusted gesture and headless output goes nowhere. Everything downstream of
|
||
the unlock is measured; the acoustics are not. **Nobody in this loop has ears.** The gate prints that
|
||
sentence on every run rather than leaving it in a notes file nobody opens.
|
||
|
||
#### (b) `smoke_character` — §1.2, the per-town character vector. **GREEN, 0 warns.**
|
||
|
||
- **Classic BY CONSTRUCTION**: the default boot's character is the frozen v1 literals
|
||
(`grass`/`djsim-road`/`djsim-footpath`/`brickpave`), **all four tints null**, kerb `0x9a948c`, sky
|
||
null; all five ground meshes untinted `#ffffff` plus the flat kerb; **still exactly 5 town-wide
|
||
meshes**. No flag test is involved — a boot with no town key has no character, which is what
|
||
`?classic=1` and the synthetic default both get.
|
||
- **The per-town arm, against the SHIPPED index**: all **23** keys carry their own entry, including
|
||
the two that carry `state: ''` — `newtown_godverse` and `redhill_godverse`. A `state`-keyed
|
||
implementation would have dropped **exactly those two**, silently. Asserted against
|
||
`assets/towns/index.json`, not against a list in the gate.
|
||
- **THE CONTROL**: `townCharacter('__no_such_town_r38')` returns **`matched:false`**, label
|
||
`unmatched`, and so does `townCharacter(null)`. Without this arm, "matched is true for all 23" is a
|
||
claim a `matched: true` constant would satisfy.
|
||
- **Not vacuous as a table**: **21 distinct palettes across 23 towns** (the 2 repeats are the
|
||
deliberate godverse↔real sibling pairs). B's count, reproduced by a harness B did not write. A
|
||
single shared palette would FAIL this arm by name.
|
||
- **The two orphans are on the GPU, not just in a table**: booted live, `castlemaine_real` binds
|
||
**`ground-gravel.jpg`**, `darwin_real` binds **`ground-reddust.jpg`**, `hobart_real` binds
|
||
**`ground-grass-temperate.jpg`** — read off the live material's texture URL. Both orphans had been
|
||
selected by **nothing** since v1.
|
||
|
||
#### (c) `smoke_magpie` — §1.3, the magpie. **GREEN, 0 warns.**
|
||
|
||
- **THE LATCH, kept verbatim, with its positive control first**: after 1 swoop `gotYouToday` is
|
||
**TRUE** (without that, "false afterwards" is worthless); `game.sleep()` → day 1→2, `gotYouToday`
|
||
**false**, `swoopsToday` **0**, `dayRolls` 1, mode back to **`perched`**.
|
||
- **THE DELTA LAW**: **localStorage gained NO new key** across the swoop and the roll, and the save
|
||
blob mentions none of magpie/swoop/washing/yard (keys: cash, collection, day, savedAt, schema,
|
||
town, wants). *The baseline is taken **after** a `game.save()` on purpose:* the first run of this
|
||
arm reported `procity-save` as "new" and blamed the magpie for v7's own write. A delta law needs a
|
||
correctly-drawn baseline or it fabricates violations.
|
||
- **THE SEASON IS NOT A CONSTANT — the arm B's own bug demands, run UNFORCED** (`forced:false`
|
||
asserted first, or the arm is vacuous): **23 distinct positions in the year across 24 towns**,
|
||
min 0 / max 275 days out, against the first cut's **24 of 24 at 214**. **`newtown_real` is in
|
||
season on day 1** on the default seed, and **129/1000 `?seed=` values (12.9%)** put the synthetic
|
||
default in season against a design ratio of 42/364 = **11.5%** — the distribution, not one lucky
|
||
boot. Every one of B's season numbers reproduces.
|
||
- **TERRITORY SCALES WITH THE ROAD GRAPH**: synthetic **6** over 6.78 km → `bendigo_real` **157** over
|
||
188.6 km (1 per ~1.2 km both times). **A fixed-capacity magpie passes every other arm in this gate**
|
||
— which is why this one is here.
|
||
- **+1 DRAW, and the 0 is by absence**: same pose on both boots (taken once off the `?magpie=1` boot
|
||
and reused verbatim — `?magpie=0` has no `PROCITY.magpie` to ask, and two different poses is not an
|
||
A/B), sim quiet: **165 → 166 draws (+1), 22,378 → 22,560 tris (+182)** at instance count 1.
|
||
`?magpie=0` constructs nothing at all.
|
||
|
||
#### (d) `smoke_yards` — §1.4, backyard Australia. **GREEN, 0 warns, and it SKIPS LOUDLY.**
|
||
|
||
- **The subject is real**: **145 house + 36 yard = 181 lots**; **181 hoists → 394 items, cap 256, 256
|
||
active** (the cap BINDS here, so proximity-first spending is doing real work); at **night the count
|
||
is 0** — the washing comes in, and that zero is the vendored early-return, not a small number.
|
||
- **The cost decomposition, both halves falsifiable at one pose, sim quiet on all three boots**
|
||
(residential centroid `[9.04, 0.87]`): `?yards=0` **193 d / 30,582 t** · `?washing=0` **191 d /
|
||
32,518 t** · default **192 d / 33,030 t**. So: **the box props add 0 draws** (and in fact save 2 —
|
||
see §38.1) while moving **+1,936 tris**, and **the washing costs exactly +1 draw / +512 tris**. A
|
||
per-chunk yards module would have read +1 × up to 31 live chunks here. If `?yards=0` were
|
||
decoration the triangles would not move, and the gate FAILS on that explicitly.
|
||
- **THE LOUD SKIP**: on `newtown_real` (67 lots) and `bendigo_real` (35 lots) the gate prints a
|
||
`⊘ SCOPE` line — **0 house/yard lots, 0 hoists, 0 washing items, mesh never created, flag still
|
||
ON** — and says in the same breath that the synthetic arm found 181, *which is what makes the zero
|
||
a scope statement and not an empty run*. §1.4 is synthetic-only until Ruling 2's civic fetch lands
|
||
districts, and the suite now says so out loud every time instead of passing quietly.
|
||
|
||
### §38.5 THE SUITE — **`tools/qa.sh --strict --matrix` GREEN on the true final tree**
|
||
|
||
Run on the live working tree, which at the time of the run equalled **`74d6257`** (Lane E's session-2
|
||
publish landed mid-round; its `web/` diff against B's `4514cb9` is **manifest + one thumb, no code**,
|
||
and both were already present in the tree both budget servers read — so the reconciliation above
|
||
measured the final tree, not a stale one).
|
||
|
||
```
|
||
SUMMARY 7 passed · 0 failed · 0 warn · 0 skipped
|
||
● QA GREEN — every landed gate passed.
|
||
```
|
||
|
||
- **`flags_check` GREEN — 0 fails, 0 warns**, with all four new R38 gates inside it (they print their
|
||
own `GATE R38 §1.x` banners in the suite log, and `smoke_yards` prints its two `⊘ SCOPE` lines).
|
||
- **MATRIX GREEN — 10 towns × 7 gates** (boot · determinism · budget · district · roads · tram ·
|
||
noassets): 2 synthetic + 3 fixtures + 5 real caches, every town byte-equal on re-boot, **0 fetches
|
||
under `?noassets` across all ten**, tram verdicts unchanged (fenced on bendigo / fremantle /
|
||
katoomba_real, runs elsewhere). Wave 1 moved nothing in the matrix.
|
||
- scaffold_check · consistency · manifest validator (`--depot`) · interior figure-scale: all green.
|
||
29 interior figs across 6 shops still human-sized; the R36 DJ bodies still spawn as rigs.
|
||
- Total: **flags harness ~18 min**, whole suite ~19 min, on this box with two budget harnesses
|
||
running beside it.
|
||
|
||
### §38.6 HELD, AND HANDED ON
|
||
|
||
Nothing is held. Wave 1 gates green and the budget did not move. The following are **recorded, not
|
||
fixed**, because they are other people's files or other rounds' scope:
|
||
|
||
1. **`magpie.js`'s own header contradicts itself on the triangle count** — `:9` and `:123` say
|
||
**154 triangles**, `:42` says **182**, and the measured number (my gate, an independent harness) is
|
||
**182**. Two stale comments in Lane B's file; flagged, not edited. It is the kind of drift R38's own
|
||
§1.3 season bug was made of.
|
||
2. **E's three published GLBs are in the manifest but not under `web/assets/models/`** — they live in
|
||
`pipeline/_normalized/` and on the public depot, and `validate_manifest.py --depot` passes because
|
||
its contract is "local `_normalized` **OR** HEAD 200 on the depot". Nothing loads them (B declined
|
||
all three on measured budget). **Not a defect** — recorded so nobody reads the manifest as a
|
||
shipping list.
|
||
3. **The corpus TRI pin needs its station spacing quoted with it** (§38.3). Filed as a method note on
|
||
`budget_walk.py`, which now carries four methods with four different biases; the tool's docstring
|
||
says which is which.
|
||
4. Carried from R37, unchanged: **A's D1–D4** · **the matrix's budget column is bookmark numbers**
|
||
(still a regression signal, not the budget of record) · **`?r=3` breaches at 307** and is still an
|
||
ungated diagnostic override · **should `?classic=1` show the town selector at all**.
|
||
|
||
**No tag.** Wave 1 is content; the epoch tags when it is whole.
|
||
|
||
## §39 — R39: v9 WAVE 1, THE CITY IS THE GAME. F: five gates, five controls — and the interior margin is **162**, not 21 and never 6
|
||
|
||
**Verdict: v9 wave 1 SHIPS.** Four lanes landed the biggest single-round surface since the epoch
|
||
opened and every arm reproduces. **The round's biggest number is not the one the ledger asked for:
|
||
Lane E's transmission fix takes the worst interior room from 329 draws to 188, and the margin
|
||
against the ≤350 law from 21 to 162.** The op-shop bin that this round spent so much care costing
|
||
against a 21-draw margin is now spending against a 162-draw one.
|
||
|
||
Everything below is measured on the true final tree (`43c7f42` + this lane's `tools/**`), on this
|
||
box, with each gate's falsifiability control demonstrated in the same run. New tools, all F-owned:
|
||
`tools/qa/r39_address.mjs` · `tools/qa/r39_transmission.py` · `tools/qa/r39_runtime.py` ·
|
||
`tools/qa/r39_shots.py`, wired into `qa.sh` as Gate 8 (`--no-r39` skips the browser half).
|
||
|
||
### §39.1 THE INTERIOR MARGIN, RE-MEASURED — **21 → 162, and the worst room is no longer an op shop**
|
||
|
||
Fable's binding fact #3 was corrected in-round twice: "six" was R9-era, and Lane C measured today's
|
||
tree at **GLB-off 156 / GLB-on 319**, → **329** after the bin, margin **21**. That correction was
|
||
right *and it was taken before Lane E's transmission fix landed.* Re-measured here through
|
||
`interior_test.html`'s own `drawSweep` — the ≤350 law's own instrument, 12 types × 7 archetypes —
|
||
against **two port-isolated no-store roots that are the same inode for every byte except one file**:
|
||
the shipped `procity_fit_bookshelf_01.glb`, and a copy with `transmissionFactor: 1` re-inserted
|
||
(JSON chunk rewritten, **BIN sha1 identical**, `ab1fa806…`). `?localdepot=1` on both, so the GLB
|
||
comes off the served root and the swap is the only variable.
|
||
|
||
| `drawSweep` worst room | with the glass bookshelf (= the pre-E tree) | with E's fix (shipped) | Δ |
|
||
|---|---|---|---|
|
||
| **GLB-on** | **329** (`opshop/hall`) | **188** (`dept/auto`) | **−141** |
|
||
| GLB-off | 162 (`opshop/hall`) | 162 (`opshop/hall`) | **0 — the control's own control** |
|
||
| **margin against the ≤350 law** | **21** | **162** | **+141** |
|
||
|
||
Per type, GLB-on worst archetype, glass → fixed: **`opshop` 329 → 174 (−155, −47%)** ·
|
||
**`book` 247 → 125 (−122, −49%)** · **every other type 0** — record 135, toy 158, video 123, pawn
|
||
136, milkbar 142, dept 188, stall 144, pub 112, band_room 111, rsl 122, all unchanged to the draw.
|
||
That is exactly the shape Lane C predicted from an isolated room (`opshop`/hall 191→104 −46%,
|
||
`book`/hall 101→51 −50%, `dept`/hall 116→116 as the control), reproduced at the sweep's own scale by
|
||
a harness C did not write. **The falsifiable arm E asked for fires: the sweep dropped.**
|
||
|
||
Three things follow and they are worth writing down:
|
||
|
||
1. **The worst interior room in the game is now `dept/auto` at 188 draws** — a room with no
|
||
bookshelf in it, which is why it did not move. The op shop was only the worst room because it was
|
||
being rendered twice.
|
||
2. **GLB-off is 162 on both roots**, which is what makes the −141 attributable: a GLB-off room loads
|
||
no GLB, so if the two roots differed for any other reason this row would have shown it.
|
||
*(Lane C reports GLB-off worst **161**; I measure **162**, same room, same tree. One draw, not
|
||
chased — recorded so the next person to quote a number knows two harnesses differ by it.)*
|
||
3. **The default boot fetches these GLBs from the public depot, not from `web/assets/models/`**
|
||
(`loaders.js:29` — `?localdepot` is opt-in), so I fetched all four R39 GLBs off
|
||
`https://digalot.fyi/3god/a/` and scanned them: **bytes sha1-identical to the local tree, 0
|
||
transmissive materials, 4/4.** E's publish is verified independently of E's own verification.
|
||
|
||
### §39.2 GATE — THE ADDRESS LAYER (39.1) · `node tools/qa/r39_address.mjs` · **GREEN**
|
||
|
||
Fable asked for ≥97% corpus resolution and a loud failure with no roads cache, and Lane A's binding
|
||
warning set the shape: **coverage is not a proxy for correctness.** So the gate reports coverage and
|
||
then spends its remaining arms trying to make a *high*-coverage build fail.
|
||
|
||
| arm | result |
|
||
|---|---|
|
||
| corpus resolution, counted here over all 23 caches | **1,192 / 1,219 shops (97.8%)**, 29,865/30,986 edges (96.4%), **100% on 18 of 23 towns**, worst adelaide 80.9% |
|
||
| supplier | `ways` on **23/23** |
|
||
| shift cross-derivation (`address.js`'s recovery vs `plan_osm`'s published `norm.shift`, 0.01 m) | **agree on 23/23** |
|
||
| wrong names, against an independently written point-membership resolver over shop-bearing edges | **857/857 agree, 0 disagree** — and the control **checked 857 edges**, asserted, because a control that quietly checks nothing is the R37 species |
|
||
| **no cache** (`null`, `undefined`, and `roads[].name` stripped) | supplier `district`, **0** named edges, **0** street names on all three — and it still labels **72/72** katoomba shops from district+block (*"the south end of the main street"*). A degrade, not an outage, and visible in `stats()` |
|
||
| synthetic, same code path | supplier `district`, **493/493** labelled, 0 nulls — one consumer contract, two suppliers |
|
||
| purity | **23/23** towns byte-identical after the call |
|
||
|
||
**THE RED ARM — the wrong-shift sweep (8 directions × 3 magnitudes × 3 towns, 72 perturbations).**
|
||
A wrong shift is the one input that renames a whole town, so it is what the gate is built around:
|
||
|
||
| adelaide_real (truth **89**/110 resolved, 0 wrong) | best of 8 directions |
|
||
|---|---|
|
||
| off by **3 m** | **98 shops resolved — NINE MORE THAN THE TRUTH**; 0 renamed outright, **9 given a street name the correctly-shifted build honestly refused**; 0/74 membership disagreements |
|
||
| off by 8 m | 96 resolved (+7); 10 invented |
|
||
| off by 30 m | 40 resolved (−49); 3 renamed outright, 2/28 membership disagreements |
|
||
|
||
**Lane A's number reproduces to the digit: 89 → 98.** A gate that read coverage would have gone
|
||
*greener* on a broken build. `stats().shiftCheck` reads **DISAGREE on all 72 perturbations**, which
|
||
is what this gate actually stands on.
|
||
|
||
**And the honest limit of the second alarm, which matters:** at 3 m the *membership* control reads
|
||
**0 disagreements** while 9 names are being invented — because those nine shops front ways OSM leaves
|
||
**unnamed**, so membership has no answer there and stays silent. **Only the shift cross-derivation
|
||
catches the 3 m case.** That is why this gate asserts the cross-derivation on all 23 towns and never
|
||
the coverage, and it is a load-bearing detail for anyone who later "simplifies" these two arms into
|
||
one.
|
||
|
||
**And it is live in the game, not just in node.** Read off the running shell during the fog gate:
|
||
`katoomba_real` → `addresses.stats().supplier === 'ways'`, synthetic → `'district'`, and
|
||
`minimap.view.labels === true` on **both** — the consumer draws street labels through one code path
|
||
with no town-type branch, which is the whole design constraint A published.
|
||
|
||
**One correction to LANE_A_NOTES §39, measured.** A's note says a 30 m shift error makes
|
||
castlemaine/katoomba/adelaide *"return 0 street names rather than a town full of wrong ones."*
|
||
Measured here, 30 m keeps **adelaide 40 · castlemaine 12 · katoomba 47** names and they include wrong
|
||
ones. **A big shift does not degrade to null on a dense grid — it lands you on the next street.**
|
||
The conclusion A drew from it is unchanged and if anything stronger: the shift arm must be
|
||
exact-or-nothing, because there is no magnitude at which being wrong announces itself in the output.
|
||
|
||
### §39.3 GATE — THE SIGN (B, `a982f3c`) · `r39_runtime.py --only sign` · **GREEN**
|
||
|
||
The picture-free arm Lane B specified, on four towns, with its "before" computed on the same tree:
|
||
both spawn derivations (`+sin` as shipped, `−sin` as it read from R32 until this round) are rebuilt
|
||
from the **live plan in the page**, the shipped pose is **read off the live player and asserted equal
|
||
to the `+sin` derivation** (4/4 towns, <0.01 m) — so the thing being scored is the shell, not a
|
||
re-implementation — and then one scorer runs at both poses.
|
||
|
||
| town | shipped (`+sin`) | the pre-R39 sign, same tree |
|
||
|---|---|---|
|
||
| synthetic | (6.1, −19.8) · 4 within 25 m, **4 in front** | (25.5, −17.9) · 3 within 25 m, **0 in front** |
|
||
| katoomba_real | (−200.0, −474.4) · 4 within 25 m, **4 in front** | (−219.0, −471.3) · 3 within 25 m, **0 in front** |
|
||
| bowral_real | (1774.0, −171.7) · 4 within 25 m, **4 in front** | (1755.1, −168.5) · 2 within 25 m, **0 in front** |
|
||
| fitzroy_real | (−22.8, −184.9) · 1 within 25 m, **1 in front** | (−25.4, −204.0) · 1 within 25 m, **0 in front** |
|
||
|
||
**> 0 on 4/4 after, ZERO on 4/4 before** — the gate discriminates, and it goes red the moment the
|
||
sign flips back. The shell's own probe agrees at boot (4 · 4 · 4 · 1 shops discovered). 0 console
|
||
errors across all four boots. Every one of Lane B's numbers is also mine.
|
||
|
||
**THE TWO RE-BASELINED PICTURE GATES** (`tools/qa/r39_shots.py` → `docs/shots/laneF_r39/`):
|
||
|
||
- **The R32 cluster-pose spawn.** `docs/shots/v7_tour/02_the_strip.png` photographs the old pose.
|
||
**I did not overwrite it** — it is an artefact of the tagged v7.0 epoch and re-shooting a release
|
||
tour on a v9 tree falsifies a record. The re-baseline is `the_strip-{BEFORE,AFTER}*.png` and
|
||
`the_strip_katoomba-{BEFORE,AFTER}*.png`, each pair shot on the **same tree** with the camera
|
||
parked at the two poses. Katoomba's BEFORE is the paddock and the blank back wall, exactly as B
|
||
reported; the AFTER is the awnings and the shopfront run.
|
||
- **The R35 first-five-minutes smoke needed no re-baselining as a GATE, only as a picture.**
|
||
`smoke_first_five` reads the hunt line and drives the beats through `DBG.enterShop`, so it is
|
||
pose-independent — it was green before and after, and it is green in this round's suite. What moved
|
||
is the frame its subject is *seen* in: `first_five-AFTER-plus-sin.png`, *"You're hunting HILLS
|
||
HOIST — $191 in your pocket"* over a street that now has shops in it. `v7_beta/first_five_splash.png`
|
||
is likewise kept as history.
|
||
|
||
### §39.4 GATE — THE FOG (B, `0ee7a84`) · `r39_runtime.py --only fog` · **GREEN**
|
||
|
||
Both arms on the same boot, or it is vacuous. The walk is a **shopper's walk** — the edges the most
|
||
shops front, not the longest edges (my first cut walked katoomba's 5.8 km of rural arterial and
|
||
revealed one shop, which measures the walker, not the fog).
|
||
|
||
| | katoomba_real (72 shops) | synthetic (493 shops) |
|
||
|---|---|---|
|
||
| **ARM 1 — HIDES** | 4/72 known at spawn, **0 of the 61 shops beyond 120 m known** | 4/493 known, **0 of 410 beyond 120 m** |
|
||
| **ARM 2 — REVEALS** | 2,125 m walked → **58 shops / 51 segments** (+54/+50) | 2,100 m → **405 shops / 16 segments** (+401/+15) |
|
||
| **ARM 3 — the FRONT test** | stand 5 m **behind** each of the 14 still-unknown shopfronts and probe: **0 learned**, 14 refusals | 45 unknown shopfronts probed from behind: **0 learned**, 197 refusals |
|
||
| the map moves with the ledger | 3,478 → 135 px of shop colour (the frame grows with the walk) | 2,202 → 16,328 px |
|
||
| lot size on the map | fresh **29.4×39.2 px** vs the shipped full-plan **1.42×1.89 px** | 20.97×41.16 vs 5.22×10.24 |
|
||
| save round-trip | 58 shop + 51 edge ids, **567 B** | 405 + 16 ids, **1,758 B** |
|
||
| corrupt-save arms | **8/8 rejected, live state untouched** | 8/8 |
|
||
|
||
ARM 3 is the side test's red arm and it is decisive: a radius-only fog would have handed over all 59
|
||
of those shopfronts through their own back walls. On the shopper's walk itself the same test refuses
|
||
**6 times on katoomba and 0 on the synthetic strip** — free where a shopper stands, decisive behind
|
||
the buildings, which is the shape Lane B claimed.
|
||
|
||
**ZERO DRAWS, and the arm is non-vacuous by assertion:** four freshly launched browsers, two orders
|
||
(default-first and `?fog=0`-first), each boot **drained** (`chunks.pending → 0`, settle, re-teleport
|
||
to read) and read **three times** so the arm carries its own noise floor; the fog flag and the store's
|
||
presence are asserted opposite on the two arms *before* their counters are compared.
|
||
|
||
```
|
||
yaw 0 : default 171 draws [171,172] / 24,507 tris [23196,27193] · ?fog=0 171 / 24,507 — identical
|
||
yaw 90: default 103 draws [102,103] / 25,121 tris [23058,25121] · ?fog=0 103 / 25,121 — identical
|
||
```
|
||
|
||
**Identical in BOTH counters, both yaws, both orders.** Map mode reads the same 103 draws in mode
|
||
`map` — structural, `index.html`'s frame loop never calls `composer.render()` there.
|
||
|
||
*A method note worth keeping:* my first cut booted both arms **in one browser** and read a **12,000
|
||
triangle gap at identical draw counts**. That is HTTP-cache warmth — the second boot has more of the
|
||
asset fleet resolved by sample time — and the only way to see it is to reverse the order. One browser
|
||
per arm makes the two readings identical to the byte. **Two arms in one browser is not an A/B.**
|
||
|
||
**The null arm:** `?classic=1`, `?game=0` and `?fog=0` all produce a **null store** (3/3), which is
|
||
the pre-R39 map with no flag test in `minimap.js` at all. `?game=0` and `?fog=0` draw a
|
||
**pixel-identical** map (FNV `0xddb5b939`) and the default boot's differs (`0x672e276b`) — the hash
|
||
is sensitive to the map's content, which is what makes the equality mean something.
|
||
|
||
**A CORRECTION TO LANE_B_NOTES §39, and it is B's own fix that caused it.** B records *"a fresh
|
||
export has no `known` key"*. On the shipped tree the default boot discovers **4 shops before the
|
||
player touches a key** — because the `+sin` spawn now puts you in front of four shopfronts — so a
|
||
default boot's first export **does** carry `known`. The claim the delta law is actually about
|
||
survives and is asserted instead: **a boot that learns nothing (`?fog=0`) exports
|
||
`[cash, collection, day, savedAt, schema, town, wants]` — no `known` key, the pre-R39 bytes.**
|
||
|
||
### §39.5 GATE — THE RUMMAGE BIN (C, `eeba920`) · `r39_runtime.py --only bin` · **GREEN**
|
||
|
||
Both arms, or the gate only proves that a tub exists. The tub is identified off the room's **own
|
||
placement summary** (`placement[].kind === 'rummageBin'`), not a bounding-box guess, and reachability
|
||
is a **4-connected flood fill from the room spawn** written here rather than a flag the builder set
|
||
about itself.
|
||
|
||
| 6 archetypes × 6 seeds × GLB on/off = 120 rooms | default (`dig:false`) | ARMED (`{rummageBin:{dig:true}}`) |
|
||
|---|---|---|
|
||
| tub placed | **60/60** | **60/60** |
|
||
| `kind:'bin'` | **0/60** | **60/60** |
|
||
| dig aim targets in `places` | **0** | **60** |
|
||
| pathOK / corridor carves | 60/60 · **0** | 60/60 · **0** |
|
||
| deterministic (byte-equal placement over two builds) | 60/60 | 60/60 |
|
||
| nearest reachable cell to the tub | — | **0.80–1.03 m** (dig proximity 2.5 m, aimed reach 3.2 m) |
|
||
|
||
**THE CONTENTS DID NOT SHIP, proven on the boot every player gets:** on the default street boot
|
||
(*"Second Time Redfern"*, fittings `counter · clothesRack ×2 · metalShelf ×2 · bookshelf ×2 ·
|
||
crateStack · rummageBin · art ×3`) the tub is in the room as **`kind:'prop'`**, there are **0**
|
||
`kind:'bin'` objects and **0** dig targets, and standing in front of it and pressing **E opens
|
||
nothing**. Lane C's hold is intact and I am not the one to break it.
|
||
|
||
Lane C's 72/72 is 6 archetypes; `ARCHETYPE_KEYS` on this tree is **5**, so my 120 rooms are
|
||
5 × 6 × 2 × 2 arms. C's reachability range (0.77–1.15 m) and mine (0.80–1.03 m) agree.
|
||
|
||
### §39.5b THE MAGPIE'S GLB ADOPTION (B, `f0b9a1d`) — checked on the wire, because it is a blind-probe shape
|
||
|
||
B wired E's magpie GLB as `furniture.js`'s use-if-ready second path and states that `?noassets` and
|
||
`?classic` still get the procedural bird. A module-scope `loadGLB` is exactly the shape that leaks a
|
||
fetch past the asset-free law, so I watched the wire rather than the flag:
|
||
|
||
| boot | `magpie.state.asset` | depot/model fetches | magpie GLB fetched |
|
||
|---|---|---|---|
|
||
| default (`?magpie=1` to force the season) | **`glb`** | 37 | yes — `digalot.fyi/3god/a/procity_street_magpie_01.glb` |
|
||
| **`?noassets=1&magpie=1`** | **`primitive`** | **0** | **no** |
|
||
| `?classic=1` | **null — never constructed** | 25 (the classic layer's own, allow-listed) | no |
|
||
|
||
`state.asset` is read off the live module, so the gate reads which bird is bound instead of inferring
|
||
it from a fetch. The matrix's `?noassets` arm (0 fetches on all ten towns) is the standing guard.
|
||
|
||
### §39.6 GATE — THE TRANSMISSION VALIDATOR (E, `12eb571`) · **GREEN, and it FIRES**
|
||
|
||
`validate_manifest.py`'s R39 hard-fail is live, so the gate is: **make it fire.** A fabricated glass
|
||
`bookshelf` (BIN sha1 identical, only the JSON chunk moved) dropped into a mirror of the repo where
|
||
every other path is a symlink:
|
||
|
||
- **validator HARD FAILS, rc 1**: *"material[0] 'mtl_10218_Bookshelves_v1' has transmissionFactor 1.0
|
||
— three.js will run a transmission PRE-PASS and issue every opaque draw in that scene TWICE"*
|
||
- **validator PASSES on the shipped tree** — the positive control, without which "it failed" only
|
||
proves the gate is always red
|
||
- shipped tree re-scanned: **0 transmissive materials in 59 GLBs under `web/assets/models/` and 59
|
||
under `pipeline/_normalized/`**, and 0 in the 4 fetched off the public depot
|
||
|
||
**MY OWN HARNESS BUG, RECORDED BECAUSE IT IS THIS ROUND'S OWN SPECIES.** My first cut planted the
|
||
glass asset in `web/assets/models/` and reported *"the validator did NOT fail — E's gate is
|
||
vacuous."* It was **my** error: `check_transmission()` reads `pipeline/_normalized/<file>`. I pointed
|
||
a gate at a file it never opens and nearly filed a defect against another lane on the strength of it.
|
||
*Aim the control at the code path, not at the plausible path.*
|
||
|
||
**→ LANE E, one ask (your file, not mine).** `check_transmission()` returns silently when
|
||
`pipeline/_normalized/<file>` is absent (`# depot-only asset: nothing to parse here`), and it never
|
||
scans `web/assets/models/`. **Demonstrated, not inferred:** with the local `_normalized` copy removed
|
||
and the manifest still pointing at it, the same glass asset **passes, rc 0**. So a depot-only
|
||
transmissive GLB — exactly the shape of `longbench` and `streetlight`, which are in the manifest and
|
||
not wired — would ship unseen. Two lines fix it: scan `web/assets/models/<file>` when `_normalized`
|
||
has no copy, and warn (not skip) when neither exists.
|
||
|
||
### §39.7 THE SUITE — `tools/qa.sh --strict --matrix` on the true final tree
|
||
|
||
```
|
||
SUMMARY 10 passed · 0 failed · 0 warn · 0 skipped
|
||
● QA GREEN — every landed gate passed.
|
||
```
|
||
|
||
- **Lane A's selfcheck reproduces exactly: 157,647/157,647 ALL GREEN, fingerprint `0x5f76e76`.**
|
||
Zero goldens moved this round, on a round that added an address layer, a fog ledger, a spawn move
|
||
and a new interior fitting.
|
||
- flags harness GREEN (0 fails, 0 warns) — including `smoke_first_five`, which the spawn move does
|
||
not touch, and `smoke_classic_purity`.
|
||
- MATRIX GREEN, 10 towns × 7 gates; manifest validator `--depot` green; interior figure-scale green.
|
||
- Gate 8 (new): `r39_address` · `r39_transmission --validator` · `r39_runtime` — all green.
|
||
- A **baseline** run of the pre-Gate-8 suite on the same tree was also green (7/0/0/0), so the three
|
||
new gates added coverage without moving any existing verdict.
|
||
|
||
### §39.8 HELD, AND HANDED ON
|
||
|
||
1. **→ LANE E:** the validator's depot-only blind spot (§39.6). Demonstrated with a running control.
|
||
2. **→ FABLE / the record:** binding fact #3 needs its third and final value. **The interior margin
|
||
is 162 draws** (worst room `dept/auto` 188 / 350) as of `12eb571`. The lineage is worth keeping
|
||
because it is a lesson in three parts: **6** (a stale doc), **21** (a correct measurement of a
|
||
tree that was about to change), **162** (the measurement after the change). Every one of them was
|
||
honestly arrived at.
|
||
3. **→ LANE A (note, not a defect):** the 30 m shift claim in LANE_A_NOTES §39 (§39.2 above).
|
||
4. **→ LANE B (note, not a defect):** "a fresh export has no `known` key" is now true of `?fog=0`
|
||
only (§39.4 above).
|
||
5. **The 1-draw GLB-off disagreement with Lane C** (161 vs 162, same room, same tree). Not chased.
|
||
6. Carried, unchanged: **A's D1–D4** · the matrix's budget column is bookmark numbers · **`?r=3`
|
||
breaches at 307** and is still an ungated diagnostic override · should `?classic=1` show the town
|
||
selector at all · **nobody in this loop has ears**.
|
||
|
||
**The street budget was not re-measured this round and does not need to be:** the fog and the address
|
||
layer are both zero-draw by construction and measured at zero, the spawn move is a pose, and the bin
|
||
is interior. The pin stands at **292/300**. What moved is the *interior* budget, and it moved in our
|
||
favour by 141 draws.
|
||
|
||
**No tag.**
|