John looked at R41's proof shots and named three faults. All three are fixed, at zero draw cost, and every lane corrected somebody's numbers on the way — including its own. E-LOOK (§42.1+§42.2a) — THE PREMISE WAS WRONG. "The pub props have no texture" (my brief, quoting E's own R41 note) is false: all 37 carry COLOR_0 and three.js renders it. The colour was BLEACHED — jukebox at saturation 0.02, 15 assets under 0.05; what the source library called colour was baked AO. So the fix was a palette imposed on colour already present, in place: 46 assets, draw delta +0 and tri delta +0 on every one, proved the honest way — the concatenated POSITION/NORMAL/TEXCOORD/index bytes are sha1-identical to R41, so footprints and origins provably did not move. (A first-pass bbox check ignored node rotations and "found" 3 phantom violations; thrown out for the byte comparison.) The banquette was 1990s office teal and went burgundy; the beer umbrella was white and went brewery green. THE DECK, diagnosed before being touched: 7 greys, chassis 0.05 luminance on a 0.033 bench = 1.21:1, and the one high-contrast feature was INSIDE-OUT (white ring around a dark disc; a turntable is a dark record on a light platter). Now Technics silver at 6.74:1, black vinyl, red start/stop. Node contract re-read from the shipped GLB: 4/4 + 1/1 PASS. Filed to R43, not fixed: deck_1200_rigged has NO PLINTH (bottom 43mm of 119mm lost to R41 join damage). E-CAST (§42.3) — 19 of 24 ship. Rejected: comical_luchador_01 (Mixamo Ch43, purple mask reading EL CHUPACABRA, identified from the file's own material name), a 3-heads-tall cartoon child, a robed wuxia elder, a WW2 infantryman — and dj_phrtt_01, AN UNCLOTHED BODY whose only texture is a bare-skin atlas, which was in the DEFAULT crowd and live on the depot. It is the R41 deny-list's failure class through a different door: an innocent name from another pipeline. Only rendering all 24 and LOOKING found it. Ruling 2 (prefer CC0) was REFUSED ON MEASUREMENT: Quaternius is 25 nodes / 23 joints and ZERO of 64 canonical tracks bind — it would have loaded silently and never animated. Replacements came from Mixamo's Ch## pool, filtered to the contract-clean 65-bone family (candidates ran 65..99 bones; named characters carry facial/hair bones). THE LICENCE GAP WAS UNREAD, NOT MISSING: every source FBX carries ApplicationVendor "Mixamo, Inc." internally. Adobe royalty-free, no attribution owed. B's amber rows are now real. B (§42.5) — MY ASSIGNED FIX WAS USELESS AND B PROVED IT. document.fonts.ready shipped, then measured: the atlas canvases are already byte-identical across boots, the game ships NO webfonts (document.fonts.size === 0), and ready fires 50ms BEFORE the first fillText. The real cause of unreproducible bookmarks is the wall-clock CROWD — 983 changed px of 921,600, ALL in the pedestrian band; 0 px with the crowd out. New DBG.freeze() takes stable bookmarks 2/5 -> 5/5. Also found: ?pop=0 boots 140 citizens (0 is falsy against the default). THE BENCH: D's filing correct on both counts. Front-vs-road 90.0deg -> 0.0deg, min=med=max over 60/60 instances; the streetlight arm now reaches 1.05m over the road where it reached 0.00. Both used a base yaw that pointed +Z down the street while their own comments claimed otherwise — the side term was already right, which is why it survived 41 rounds of reading. Also: on any box with CDN access 9 of 60 benches are the depot GLB, 90deg out from the house law — normalised at the geometry, once, at load. benchStops(plan) exported as one truth. C (§42.2b) — 122 vs 123 WAS THE CAMERA ASPECT, and nobody was wrong. Draw counts are post-frustum-cull and the sweep camera inherits the window aspect: 1.25->116, 1.60->122 (C's harness), 1.778->123 (F's), 2.37->124. Pinned MEASURE_ASPECT=16/9 with a 1.25 control. E's "keep the bench dark" advice was measured and declined with reason (deck-vs-bench was already 4.73:1, over threshold). "Reads as a black box" is a claim about pixels, so C measured pixels: near-black in the booth crop 57.5% -> 26.7%, distinct colours 228 -> 389. Bench rebuilt 3->7 boxes with an open record bay; headphones on the mixer; faceDoor aims booths at the player's entry — alignment within 45deg went 49% -> 92%. Two bugs found by looking: a fitting could be planted INSIDE A WALL (0.77m of a 0.86m half-depth in plaster), and every booth part sat 25mm sunk into its own worktop. D (§42.4) — five swaps IN PLACE at the same index (pickRig indexes these arrays and pedIndex is in the identity signature and the impostor atlas; removal would renumber the town). Identity signature pre- vs post-swap: 0 differing lines of 150 — who each citizen is did not move, only which body they wear. dj_phrtt_01 verified gone AT THE WIRE, not from source: a fresh default boot fetches 32 ped GLBs, none retired. THE CONTROL ARM FOUND A 41-ROUND-OLD BUG: the ped being RETIRED, comical_boy_01, binds only 3,132 of 3,460 tracks — six thumb bones lack the mixamorig prefix. It had been silently dropping six tracks per clip since it shipped. Testing the thing being thrown away found it. Imported B's benchStops mid-round: sitters now 0.0deg to the road, signatures byte-identical across the rewire. Corrected F's filing: the browse smoke was flaky-by-construction (a 16-25s race against a live sim), not deterministically red. F (§42.6) — qa.sh --strict 24 passed / 0 failed / 0 warn / 0 skipped. New Gate 11: period law (0/5 banned; CONTROL: R41's roster trips all five) and roster licence (24/24, 0 unverified; CONTROL: an amber row turns 20 red). r41_shots.py promoted warn -> strict. R41'S BYTE-HASH BAN IS REVERSED AND ITS STATED REASON WITHDRAWN AS WRONG. A gate may pin a hash under five in-run assertions (classic+flags boot, freeze().stillLive all-false, a second freeze removing 0 draws, settle until DBG.info() stops changing, hash with the dbg panel hidden): 5/5 bookmarks now pin to one sha256 over 3 fresh boots. Two findings got it there — the chunk streamer (a fixed settle is a bet on load) and the fps readout, which with everything else frozen was the last thing moving at 53-62 px. ?pop=0 fixed (Number.isFinite) — a no-op for 35 rounds. Three red flags_check gates fixed. Street: noon 282 · NIGHT 291/300 — the nine-draw margin INTACT, every R41 row reproducing to the digit. Interior at pinned 16:9: 123 @ dept/auto, margin 227. selfcheck 157,647/157,647, 0x5f76e76 unmoved. No tag — John's playtest rules the epoch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1790 lines
139 KiB
Markdown
1790 lines
139 KiB
Markdown
# PROCITY — Lane F progress (INTEGRATION & QA)
|
||
|
||
**Session:** 2026-07-14 · **Author:** PROCITY-F · **For review by:** fable
|
||
|
||
---
|
||
|
||
## Round 42 (§42.6) — PROVE THE LOOK: three paired frames, two new laws about the cast, and the byte-hash ruling reversed against its own author
|
||
|
||
*Full runbook detail (gate arms, the control transcripts, the pixel diffs): [LANE_F_NOTES §42](docs/LANES/LANE_F_NOTES.md).
|
||
Shots + their index: `docs/shots/laneF_r42/` (README in the folder).*
|
||
|
||
Every number below is from a command run today on the integrated tree — five lanes' uncommitted work
|
||
in one working copy — in fresh headless contexts on port-isolated no-store servers, seed 20261990,
|
||
1280×720. **No git command that touches the repository was run** — no status, add, commit, push,
|
||
stash or tag. The one exception, declared rather than buried: `git ls-tree -r v2.1 -- web/`, read-only
|
||
and against a tag, run once to confirm the four retired ped bodies really are in the v2.1 baseline
|
||
before re-pinning the classic covenant around them. It is the identical command `flags_check.py`
|
||
itself runs on every invocation. No tag (John's playtest session rules the epoch).
|
||
|
||
### The four asks, each with its outcome
|
||
|
||
**① [B] `?pop=0` HAS BEEN A NO-OP FOR 35 ROUNDS. Fixed, and gated.**
|
||
`index.html` (B filed it at :454) read `(parseInt(params.get('pop'), 10) || 140) >>> 0` — **0 is falsy**, so every
|
||
"town frozen" measurement any lane has quoted since the flag landed booted 140 citizens. Now
|
||
`Number.isFinite(_pop) ? _pop : 140`: a parsed 0 is finite and wins, an absent or unparseable value is
|
||
NaN and falls to the default, negatives still clamp through the `>>> 0`. **Measured both ways in
|
||
`r42_repro.py` arm 0** — `?roster=v1&pop=0` → **0 active**; **CONTROL**, the same boot with no `pop`
|
||
flag → **97 active** (target 140), so the arm is a gate and not a tautology. B's second ask (a gate
|
||
pinning a hash must assert `freeze().stillLive`) is ask ② and is enforced inside the gate that does it.
|
||
|
||
**② [B] THE BYTE-HASH RULING — REVERSED. See the ruling below; it is the round's other headline.**
|
||
|
||
**③ [C] `interior_mode.js`'s `WARDROBE_BASE` LITERAL IS GONE.** C moved the declaration into
|
||
`interiors/stockpack.js` and re-exported it from `interiors.js`; this module now imports and
|
||
re-exports **C's** constant instead of keeping a second copy that agrees with it today. `index.html:58`
|
||
is unchanged — a re-export, not a rename. The comment above that line says the two sides must spell
|
||
the base identically or the `(type, base)` cache key misses silently; the only way to guarantee that
|
||
is to have one side. `assets/wardrobe/` is now spelled **once** in the repo.
|
||
|
||
**④ [D/E] `dj_phrtt_01` — WHAT SHIPS: NOTHING. And the four the note lumped in with it were never
|
||
published at all.** The filing said "still on disk and still on the depot"; I checked the depot rather
|
||
than the note, read-only:
|
||
|
||
| body | `web/models/peds/` | `pipeline/_normalized/` | `_published.json` | `GET digalot.fyi/3god/a/<f>.glb` |
|
||
|---|---|---|---|---|
|
||
| **dj_phrtt_01** (unclothed) | present | present | **present** | **200 · 712,792 B — LIVE** |
|
||
| comical_luchador_01 · comical_boy_01 · man_elder_01 · man_soldier_ww2_01 | present | — | — | **404 · never published** |
|
||
|
||
So the depot job is **one file**, not five. And `web/models/` is **not** gitignored (`web/assets/models/`
|
||
is the one that is) and `tools/deploy_digalot_procity.sh` rsyncs **the whole of `web/`** — so today
|
||
those bytes would go out on a deploy even though nothing loads them. **Ruling: they do not ship.**
|
||
The exact commands are in the box below; F does not run a destructive publish and `web/models/**` is
|
||
Lane E's file set. Meanwhile *"impossible to return silently"* is now four assertions, not a promise:
|
||
|
||
* the name cannot come back into the fleet — `r42_roster.mjs` arm 1 reads `PED_NAMES`;
|
||
* it cannot come back through a module or a page — arm 4 scans every loadable surface;
|
||
* it cannot come back **on the wire** — D's `r42_cast.py` asserts 0 retired bodies fetched on default
|
||
**and** on `?classic=1`, now wired into `qa.sh` Gate 11, and `flags_check.py`'s classic arm now
|
||
asserts the same thing from the covenant side (see the red gates below);
|
||
* a **sixth** un-vetted body cannot appear at all — arm 5 requires every ped GLB on disk to be
|
||
shipped, a base clip, or explicitly recorded as withdrawn in `credits.json`. That arm exists
|
||
because a name deny-list could never have caught this one: its name is innocent, and what found it
|
||
was Lane E rendering the roster and looking.
|
||
|
||
```sh
|
||
# ── THE REMOVAL, for the integrator (Fable). Run at close, then re-run tools/qa.sh --strict. ──
|
||
# 1. the repo copies — all five. (web/models/ IS tracked; git will see the deletions.)
|
||
rm web/models/peds/{comical_boy_01,comical_luchador_01,man_elder_01,man_soldier_ww2_01,dj_phrtt_01}.glb
|
||
# 2. the publish staging copy — only dj_phrtt_01 was ever staged (pipeline/_normalized/ is gitignored)
|
||
rm pipeline/_normalized/dj_phrtt_01.glb
|
||
# 3. the depot ledger row, or validate_manifest.py --depot's drift check goes red on a file that has gone
|
||
python3 - <<'PY'
|
||
import json, pathlib
|
||
p = pathlib.Path('pipeline/_published.json')
|
||
rows = json.loads(p.read_text())
|
||
p.write_text(json.dumps([r for r in rows if 'dj_phrtt_01' not in json.dumps(r)], indent=1) + '\n')
|
||
print(f'{len(rows)} -> {len(json.loads(p.read_text()))} rows')
|
||
PY
|
||
# 4. the depot copy itself — LANE E / the depot host. There is NO delete verb: CITY_SPEC §depot
|
||
# documents POST /api/upload, GET /a/<f>, GET /api/list and nothing else, and publish.py only
|
||
# uploads. digalot.fyi/3god is a shared CDN (1,223 assets today) that sibling games read, so this
|
||
# is a deliberate act on the host, not a script.
|
||
# 5. THEN the deny-list can take the names permanently — r41_denylist.mjs is byte-scoped over web/,
|
||
# so adding them to BANNED is RED today by construction and GREEN the moment step 1 runs.
|
||
# 6. verify. Nothing in the R42 gates breaks: the roster gates read PED_NAMES, not the directory,
|
||
# and the quarantine arm's "declared withdrawn" bucket simply empties.
|
||
node tools/qa/r42_roster.mjs # expect: 32 GLBs = 24 shipped · 8 base clips · 0 withdrawn · 0 undeclared
|
||
python3 pipeline/validate_manifest.py --depot
|
||
bash tools/qa.sh --strict
|
||
```
|
||
|
||
### THE RULING: R41's byte-hash ban is REVERSED, and its stated reason is withdrawn as wrong
|
||
|
||
R41 ruled *no gate may pin a screenshot byte-hash* **"while the sign atlas paints `bold Npx Arial`
|
||
before the font resolves."* The symptom was real and I reproduced it independently. **The cause was
|
||
not the font.** B proved it in §42.5-C — the atlas canvases are byte-identical across boots,
|
||
`document.fonts.size === 0` because PROCITY ships no font faces, and `fonts.ready` resolves 49–60 ms
|
||
*before* the first `fillText` on 6 of 6 boots. What moves the frame is the **wall-clock crowd**.
|
||
|
||
I verified B's claim myself and **did not reproduce 5/5 first time.** Two things had to be found, and
|
||
both are now load-bearing lines in `tools/qa/r42_repro.py`:
|
||
|
||
1. **A fixed settle is a bet on how fast the box streams today.** `crossroads_busy` came back 2-of-3
|
||
in the gate and 3-of-3 byte-identical standalone; the difference was six browser launches of load
|
||
in front of it. The **chunk streamer** is the wall-clock process nobody had named — the crowd is
|
||
only the one you can see. The harness now polls `DBG.info()` and requires the whole tuple
|
||
(draws · tris · chunks · geometries · textures) to **repeat** before capturing, and prints it, so a
|
||
mismatch arrives with its diagnosis instead of as a bare inequality.
|
||
2. **Pin the render, not the instrument.** With the crowd frozen, the clock paused, the settled tuple
|
||
identical on every boot and the camera transform equal to nine decimal places, two bookmarks still
|
||
split. The entire difference was **53–62 pixels in a 7×10 box at (1252, 82)** — `#pc-fps` in the
|
||
`?dbg=1` readout printing `fps 20` on one boot and `fps 21` on the next. *(And an element
|
||
screenshot does **not** exclude an overlay: the canvas is full-viewport and the HUD composites on
|
||
top of it, so `locator('#canvas').screenshot()` captured the same pixels. `#pc-dbg` is hidden for
|
||
the capture and restored after.)*
|
||
|
||
With both, on this tree:
|
||
|
||
| `?classic=1&dbg=1&weather=0&tram=0&magpie=0&washing=0`, 3 fresh contexts each | frozen, render | frozen, whole page | **unfrozen** (control) |
|
||
|---|---|---|---|
|
||
| `street_noon` | **1 of 3** | 1 of 3 | **3 of 3** |
|
||
| `shopfront_detail` | **1 of 3** | 1 of 3 | **2 of 3** |
|
||
| `crossroads_busy` | **1 of 3** | 1 of 3 | **3 of 3** |
|
||
| `market_square` | **1 of 3** | 2 of 3 ← the fps digit | 1 of 3 |
|
||
| `night_neon` | **1 of 3** | 2 of 3 ← the fps digit | 1 of 3 |
|
||
|
||
**5 of 5.** And the strongest form available: the hashes were **written in one run and re-measured
|
||
green in a second, independent run** (`--goldens docs/shots/laneF_r42/shot_goldens.json`).
|
||
|
||
> **THE RULING (R42 §42.6), replacing R41's.** A gate **may** pin a screenshot byte-hash. Five
|
||
> conditions, every one an assertion in the same run and none of them a hope:
|
||
> **(1)** the boot carries `?classic=1&dbg=1&weather=0&tram=0&magpie=0&washing=0`;
|
||
> **(2)** `DBG.freeze()` is called **after** the pose and `freeze().stillLive` comes back **all-false**
|
||
> — *this is B's ask, and a gate that pins a hash without it is forbidden*;
|
||
> **(3)** a **second** `freeze()` after the settle removes **zero** draws, proving nothing came back;
|
||
> **(4)** the harness settles until `DBG.info()` **stops changing**, never for a fixed number;
|
||
> **(5)** the hash is of the **render** with `#pc-dbg` hidden, never of the whole page.
|
||
> `?pop=0` is not a substitute for (2) and never was — it covers the fixed roster only, and until
|
||
> today it covered nothing at all.
|
||
>
|
||
> **One clause survives from R41, for a different and now-measured reason: committed cross-round
|
||
> golden VALUES stay out of `qa.sh`.** R42 alone moved 46 prop colours, five bodies, a bench yaw and a
|
||
> booth composition; a committed pixel value would have gone red on all four while telling us nothing
|
||
> about correctness. What the gate pins is the **stability** — N fresh boots, one sha256 — which is
|
||
> the property the ruling was ever about, and it cannot rot when Lane E repaints a couch. The values
|
||
> are written to `docs/shots/laneF_r42/shot_goldens.json` for anyone who wants them, and
|
||
> `r42_repro.py --goldens PATH` re-checks them in one command.
|
||
|
||
### The new gates F owns, each with its control demonstrated
|
||
|
||
**`tools/qa/r42_roster.mjs` — RULINGS 1 AND 3 BECOME REPO LAW** (node, zero deps, ~0.1 s, two gates).
|
||
It reads `PED_NAMES` out of `rigs.js` and `credits.json`, and **not** the directory listing — Lane D's
|
||
filing, and it is right: the five retired files are still on disk *by design* (deleting a name
|
||
mid-wave shortens a pool and renumbers every `pickRig` index), so a directory gate would go red on
|
||
files that no longer ship while staying green on a body that quietly reappeared in the source.
|
||
|
||
| `--gate period` (ruling 1) | result |
|
||
|---|---|
|
||
| 1. no banned body in `PED_NAMES` | **0 of 5**, 24 shipped |
|
||
| 2. the five replacements at their own slots | `man_casual_04=normal[6]` · `person_casual_01=normal[9]` · `man_smart_01=djs[4]` · `woman_smart_02=comical[0]` · `person_youth_01=comical[1]` |
|
||
| 3. pool shape | **17 / 5 / 2 = 24**, unmoved |
|
||
| 4. no banned name on a **loadable** surface (`web/**/*.{js,mjs,html}` + `assets/manifest.json`) | **69 surfaces · 0 hits** |
|
||
| 5. **quarantine** — every ped GLB on disk is shipped, a base clip, or declared withdrawn in credits | **37 GLBs = 24 shipped · 8 base clips · 5 declared withdrawn · 0 undeclared** |
|
||
| **CONTROL** | R41's own roster replayed through the same predicates trips **all 5** by pool and index, and reports all 5 replacements missing; a *deleted* name shortens `normal` to 16, which arm 3 catches; arm 4's matcher fires on a synthetic reference |
|
||
|
||
| `--gate licence` (ruling 3) | result |
|
||
|---|---|
|
||
| every shipped ped resolves to a credits row with a real licence | **24 / 24, 0 `unverified`** — 20 on `ped-bodies-base` (Mixamo RF), 4 on `ped-bodies-dj` |
|
||
| every `required:true` row carries an attribution | pass |
|
||
| **CONTROL** | restoring R41's amber `unverified` on the roster row turns **20 peds RED**; deleting the `ped-bodies-dj` row turns `dj_techno_01` RED |
|
||
|
||
*Comments are stripped before parsing, and that is not decoration: every pool in `PED_NAMES` carries a
|
||
trailing `// was <retired name>` note, so the first cut of this gate matched inside the comments and
|
||
reported a false RED on all five.*
|
||
|
||
**`tools/qa/r42_budget.py` — THE INTERIOR LAW, WITH THE INSTRUMENT PINNED** (C's §42 ask, in a file I
|
||
own rather than an edit to `r41_integration.py`). C settled the two-round 122-vs-123 argument in
|
||
§42.2b and neither lane's bookkeeping was at fault: it is the **camera aspect** — a fixed *vertical*
|
||
fov inheriting the window's aspect, so a wider window puts more room in the frustum. C asked me to set
|
||
`camera.aspect` explicitly inside the sweep rather than inherit it. Done, with two controls:
|
||
|
||
* the identical sweep at **aspect 1.25 reads 116 @ `dept/auto`** — C's published control value, to
|
||
the digit, reproduced on demand instead of relitigated;
|
||
* the pinned sweep run in a **1280×1024** window (which would have inherited 1.25) returns
|
||
**123 @ `dept/auto`, identical to the 1280×720 run.** That is the actual claim: the number is now a
|
||
property of the tree, and the gate cannot drift when someone edits an unrelated `new_page`.
|
||
|
||
**`tools/qa/r42_repro.py` — THE BYTE-HASH GATE.** Arms and controls in the ruling above.
|
||
|
||
**`tools/qa/r42_tour.py` — the proof tour** (opt-in `--tour`, warn-level: ~8 min, and pointed at its
|
||
real outdir it rewrites the round's committed shots).
|
||
|
||
**Also wired into `qa.sh` Gate 11:** D's `r42_cast.py` and `r42_shots.py` (scratch outdir, like the
|
||
R41 shot gate). **And `r41_shots.py` is PROMOTED from warn-level to strict** — D fixed the browse arm
|
||
by removing the window rather than widening it (the frame is taken *inside* the winning room the
|
||
moment a candidate beats the incumbent, and the harness never leaves), and D was right to measure my
|
||
filing first: I called it deterministically red and it was **flaky by construction**, which is a
|
||
different defect with a different fix.
|
||
|
||
### THE PROOF SHOTS — the deliverable (`docs/shots/laneF_r42/`)
|
||
|
||
Every AFTER is framed from the committed R41 original's **own camera derivation** — the two interiors
|
||
re-run R41's exact standoff recipe, and the street reads R41's published pose straight out of its
|
||
sidecar — because a pair whose halves were framed by two different algorithms is not a pair. Every
|
||
humanoid frame carries the **R10
|
||
human-sized line** and — adopting Lane D's §42.4 idea, which is a good one — a **CAST line** resolving
|
||
every figure back to `fleet.all[pedIndex].pedName` with an explicit assertion that none of the five
|
||
retired bodies is in the picture.
|
||
|
||
| pair | before → after | draws | tris |
|
||
|---|---|---:|---:|
|
||
| **`pub_furnished_ab.jpg`** | white plastic props, **two** purple `EL CHUPACABRA` luchadors, an **unclothed body at the stage edge** → brown vinyl and beer-carpet red, **13 figures wearing 11 bodies, none retired** | 71 → **72** | 222,526 → **222,250** |
|
||
| **`record_dj_booth_ab.jpg`** | a black box → Technics silver in a composed booth | 40 → **43** | 36,252 → **41,719** |
|
||
| **`street_postures_ab.jpg`** | the R41 roster, benches side-on → recast crowd, benches turned to the road with a sitter on one | 173 → **174** | 43,562 → **42,970** |
|
||
|
||
**The street AFTER stands on the BEFORE's own camera, parsed out of the committed R41 sidecar.** The
|
||
first cut re-ran R41's live-crowd cluster search: one run landed within 40 cm of R41's pose and the
|
||
next landed 30 m away behind a wall, because the search scores a crowd that is walking while it
|
||
scores. Re-rolling until it looks right is not a method. `(8.58, 16.69) → (12.08, 10.63)` is read out
|
||
of `docs/shots/laneF_r41/street_postures.txt` at run time — a number I typed would be a different
|
||
claim — so the AFTER stands exactly where the BEFORE stood and the only variable in the pair is the
|
||
tree. **+1 draw** at that pose is the ±1 a single bookmark moves run to run (D §42.4 measured the same
|
||
on an unchanged tree); the 802-station pin below reads **0 / 0**.
|
||
|
||
Plus three single frames: **`record_dj_booth_door.jpg`** — the frame R41 never took, the booth from
|
||
Lane C's own `room.spawn` at eye 1.62 m, 7.76 m out, because *"it reads as a plain black box"* is a
|
||
claim about the **entry** view and R41 answered it with a product photograph from 2.5 m;
|
||
**`street_bench.jpg`**; **`credits_panel.jpg`** (the roster row with its per-asset Mixamo evidence, the
|
||
withdrawn row, and no ped reading `unverified`).
|
||
|
||
**The bench, measured in the frame and NOT read back off B's rule.** Instances are identified by
|
||
**position only** — a `benchStops(plan)` station within 2 cm — and the angle is then taken between the
|
||
instance's own world +Z and the foot of the perpendicular onto **Lane A's** road centreline. Neither
|
||
input is the placement rule.
|
||
|
||
| bench yaw, live scene | R42 |
|
||
|---|---|
|
||
| stations matched in the streamed window | **61 of 254** |
|
||
| **CONTROL:** the same stations offset 3 m | **0 of 61** — "coincides" is a measurement |
|
||
| bench FRONT vs the direction to the road | **min = median = max = 0.0°** (R41: 90.0°) |
|
||
| bench front vs the street's own direction | **90.0°** (R41: 0.0°) |
|
||
|
||
*One instrument note, recorded rather than tuned away:* **12 of 61** benches stand nearer a **cross**
|
||
street's centreline than their own (a station 14 m from a node is close to the corner), and attributing
|
||
those to the geometrically nearest edge reads up to **174.4°** — the bench is correctly facing the
|
||
street it belongs to and the instrument is wrong. That is the same trap B named in §42.5-A. The first
|
||
cut of this gate failed on exactly that and the fix was to use the station's own `edgeId`, which is
|
||
position-derived metadata, not the yaw rule.
|
||
|
||
### The budgets, restated on the final integrated tree
|
||
|
||
**STREET (ruling 4) — the pin method of record**, `budget_walk.py --method stepwalk --stepm 2
|
||
--fixed-yaw 0 --laps 2` = 802 stations down the main spine at yaw 0, the worst bearing:
|
||
|
||
| boot | R41 pin | **R42 measured** | law |
|
||
|---|---:|---:|---|
|
||
| default, noon (seg 2) | 282 / 100,346 | **282 / 100,346** | ≤300 ✅ **byte-exact** |
|
||
| default, **NIGHT (seg 5)** | 291 / 120,093 | **291 / 122,183** | ≤300 ✅ — **margin 9, INTACT** |
|
||
| `?classic=1`, NIGHT | 269 / 90,580 | **269 / 90,580** | ≤300 ✅ **byte-exact to the R38/R40/R41 pin** |
|
||
| `?classic=1`, noon | — | **269 / 90,592** | ≤300 ✅ |
|
||
| default, NIGHT, `?clips=0` | 291 / 82,013 | **291 / 84,103** | Δ draws **0** |
|
||
|
||
**NO LANE SPENT A STREET DRAW.** Every row that has an R41 value reproduces its draw count to the
|
||
digit — 282, 291, 269, 291 — on a tree carrying five lanes' work, including a five-body recast, a
|
||
46-asset repaint, a bench and streetlight yaw fix and a recomposed DJ booth.
|
||
|
||
**On the +2,090 night tris** (present on `?clips=0` too, so not the motion library; present on B's
|
||
control tree too, so not B's yaw work — it arrives through `models/`/`assets/`): decomposed at the
|
||
tri-worst station by rendering, hiding `citizens.group` and re-rendering, **the crowd there is worth
|
||
2,509–2,716 tris for one near rig plus the whole billboard layer, at 2 draws.** A one-for-one body
|
||
swap that trades a 1,564-tri cartoon child for a 2,738-tri teenager moves triangles by more than the
|
||
delta observed and cannot move draws. Ruling 4 governs draws; draws did not move.
|
||
|
||
**INTERIOR — the true numbers, on the pinned instrument:**
|
||
|
||
| 12 types × 6 archetypes, aspect **16:9** | worst | law | margin |
|
||
|---|---:|---|---:|
|
||
| GLB off | **93** @ `opshop/hall` | ≤350 | 257 |
|
||
| **GLB on** | **123** @ `dept/auto` | ≤350 | **227** |
|
||
| GLB on + `?stock=real` | **123** @ `dept/auto` | ≤350 | **227** |
|
||
| *control: the same sweep at aspect 1.25* | *116 @ `dept/auto`* | — | — |
|
||
| *control: pinned 16:9 in a 1280×1024 window* | *123 @ `dept/auto`* | — | — |
|
||
|
||
**Worst interior 123, MARGIN 227 — unchanged by R42.** The pub spent nothing at the worst room
|
||
(`dept` has no booth); the booth composition costs **+3 draws** where it lands, measured in the
|
||
`record_dj_booth` pair (40 → 43), inside C's declared "+4 at most, anywhere". Two rooms move under
|
||
`?stock=real` (`toy` 86 → 120, `book` 64 → 98) and neither reaches `dept`.
|
||
|
||
### Three red gates in F's own file — two caused by other lanes' R42 work landing correctly, one a race
|
||
|
||
**`tools/flags_check.py` went RED three times across this round's `--strict` runs.** None was a game
|
||
defect and none was gettable by loosening a threshold.
|
||
|
||
**① THE CLASSIC FETCH SURFACE WIDENED — and the covenant's own escape clause is the answer.**
|
||
The classic allow-list is *derived from the `v2.1` tag's file tree at gate time* (`git ls-tree`), and
|
||
four of D's five swaps sit in `PED_NAMES.normal`/`comical`, which `?classic=1` loads. So classic now
|
||
fetches `man_casual_04` · `person_casual_01` · `woman_smart_02` · `person_youth_01`, none of which
|
||
existed at v2.1. The gate's own message names the two exits: *"either the asset belongs in classic and
|
||
the covenant needs a ruling + a re-pin, or it must be gated out of `?classic`."*
|
||
|
||
**It is not gettable by gating.** Gating the replacements out of classic means classic keeps shipping
|
||
the luchador, and ruling 1 is a repo-wide law, not a default-boot law. So: a ruling, and a re-pin —
|
||
of **four named files**, recorded as SUBSTITUTIONS rather than admitted as a class, and **self-verifying**:
|
||
a substitution classifies OK *only if the body it replaces is itself in the v2.1 tree*, so the entry
|
||
cannot smuggle in an asset that was never baseline. (`man_smart_01` replaces `dj_phrtt_01` in the
|
||
`djs` pool, which is gated off classic — deliberately absent from the map, and red if it ever appears.)
|
||
|
||
**And a second assertion, because the allow-list alone would have passed the dangerous version of
|
||
this silently:** classic's wire must contain **0 of the 4 retired bodies**. A substitution that became
|
||
an *addition* grows the pool, renumbers every `pickRig` index and moves the seeded crowd — and an
|
||
allow-list that only asks "is this file permitted" cannot see that. Now measured: **4 substitutions
|
||
present, 0 retired fetched.**
|
||
|
||
**② `gig night: dancer NOT driven by a dance clip` — the R41 `smoke_djdance` bug, second edition.**
|
||
The dancer was on `venue_northern_soul`, 16.1 s, advancing 12.77 → 14.18, `position.y` flat: a
|
||
perfectly healthy dancer. The predicate demanded the live clip's duration be one of the **four** R13
|
||
Rokoko dance clips — but D's §41.3 gig widening *deliberately* installs Lane E's venue-library clips
|
||
into some crowd slots. **The assertion stopped being true in R41** and only stayed green while the
|
||
sampled slot happened to hold a Rokoko clip; R42's swap changed which slot got sampled and surfaced
|
||
it. Fixed by asking the **ClipBank** whether it holds the clip (`fleet.bank.has(name)`) instead of
|
||
pinning a clip identity the game no longer promises. Nothing is weakened — that the four dance clips
|
||
load and bind is asserted by the census arm above it and the four-fetch positive control below it,
|
||
and that a *clip* rather than the procedural fallback moves the body is `stalled` + `bouncing`, both
|
||
untouched. Same species as R41's filing 2: **a gate that pinned an identity instead of the behaviour.**
|
||
|
||
**③ `no-pump: trip 1 buy failed — {cash0: 191, disabled: True}` — a race, and the third instance of
|
||
the same anti-pattern in one file.** The R30b gate drives the REAL dig UI: press E, click the sleeve,
|
||
wait **350 ms**, then click whatever BUY button is there. On the third heavy `--strict` run of the day
|
||
it read the button still disabled on trip 1 **with $191 in the wallet** — so not affordability, just a
|
||
pull panel that had not finished resolving. The same gate was green on the two runs before it with
|
||
**byte-identical deltas `[-28, -16, -13, -13, -16]`**, which is the signature of a race, not a
|
||
regression. Widening the sleep moves the race; **polling for the precondition removes it** —
|
||
`wait_for_function` until the control exists AND is enabled, cap 6 s, and the panel's own text is now
|
||
returned so a genuine disabled button arrives with its reason instead of a bare `disabled: True`
|
||
(which is what made this cost a whole run to diagnose). The **broke-player negative control** passes
|
||
`expect_enabled=False`, because there a disabled button is the assertion and waiting six seconds for
|
||
it to enable would be waiting for the gate to fail.
|
||
|
||
**This is exactly what F filed against D in R41 and D corrected back in §42.4** — a harness that bets
|
||
a fixed number on a live process. It was in my own file three times over. The rule, now written down
|
||
in [LANE_F_NOTES §42-A/§42-F](docs/LANES/LANE_F_NOTES.md): **never `wait_for_timeout` a live thing
|
||
into readiness; poll for the precondition and assert it.** The same fix pattern closed the
|
||
byte-hash gate's chunk-streamer race in the same round.
|
||
|
||
### QA
|
||
|
||
`bash tools/qa.sh --strict` →
|
||
|
||
```
|
||
SUMMARY 24 passed · 0 failed · 0 warn · 0 skipped
|
||
● QA GREEN — every landed gate passed. (exit 0)
|
||
```
|
||
|
||
**24, up from R41's 18:** six new gates (the two node roster gates, D's `r42_cast.py` and
|
||
`r42_shots.py`, F's `r42_budget.py` and `r42_repro.py`) and **zero warn** — `r41_shots.py` moved out
|
||
of the warn column into the strict one, so the suite has no non-blocking gate left in it.
|
||
|
||
`node web/js/citygen/selfcheck.js` → **ALL GREEN 157,647/157,647, fingerprint `0x5f76e76`** — unmoved.
|
||
Nothing this round writes a plan field.
|
||
|
||
**Gate 11 is new this round** (`--no-r42` skips its browser half, like every other class): the two
|
||
node roster gates, D's `r42_cast.py` and `r42_shots.py`, F's `r42_budget.py` and `r42_repro.py`.
|
||
`--tour` adds the proof tour opt-in and warn-level: it costs ~8 minutes (the street arm HOLDS until
|
||
the live crowd offers its frame — that dwell is the method, not slack) and pointed at its real outdir
|
||
it rewrites the round's committed shots, so a routine gate run sends it to a scratch directory.
|
||
|
||
### Filed back
|
||
|
||
1. **→ LANE E / the integrator: `dj_phrtt_01.glb` is live on the shared depot** (200, 712,792 B) and
|
||
there is **no delete verb** in `publish.py` or in CITY_SPEC's depot contract — only
|
||
`POST /api/upload`, `GET /a/<f>`, `GET /api/list`. An append-only shared CDN cannot un-publish a
|
||
mistake, which is worth a spec line of its own. The other four rejects are **404** — they were
|
||
never published, so the note that lumped all five together was wrong in a way that would have sent
|
||
someone hunting for four files that do not exist.
|
||
2. **→ LANE E / LANE B: two `credits.json` globs claim the same three files and disagree about their
|
||
licence.** `models/peds/woman_*.glb` (`ped-bodies-base`, Mixamo RF) and `models/peds/woman_dj_*.glb`
|
||
(`ped-bodies-dj`, on-device TRELLIS) both match `woman_dj_01/02/03`. The gate resolves it by
|
||
longest-literal-prefix and reports the ambiguity, and the resolution is right today — but delete the
|
||
narrower row and those three silently inherit *"Adobe Mixamo"*, which is the wrong provenance
|
||
recorded confidently. Name the DJ five explicitly. (`credits.json` is not in F's editable set.)
|
||
3. **→ LANE E: two shipped peds are below the tri band E published.** `man_elder_01` is **1,602** tris
|
||
and `comical_boy_01` **1,564**, against §42.3's stated shipped band of 2,067–3,999. Both are
|
||
retired, so it costs nothing now — but the band was quoted as a contract the replacements were
|
||
measured against, and it did not describe the roster it came from.
|
||
4. **→ R43, conditional on the removal above:** once step 1 of the removal box runs, add the five
|
||
names to `r41_denylist.mjs`'s `BANNED` list. It is byte-scoped over `web/`, so it goes green the
|
||
moment the files leave and RED forever after if one comes back — the strongest available form of
|
||
"impossible to return silently", and it is unavailable until the bytes are gone.
|
||
5. **→ LANE C, for information:** `?stock=real` raises `toy` to **120** draws and `book` to **98**
|
||
(from 86 and 64). Still 3 draws under `dept`'s 123. If the wardrobe pattern spreads to more types,
|
||
the worst room may stop being `dept` — the law is fine either way, but the *sentence* everyone
|
||
quotes would change owner.
|
||
6. **→ EVERY LANE: `?pop=0` now means zero, and `DBG.freeze()` is the real knob.** Any measurement in
|
||
any lane doc that says "town frozen, `?pop=0`" was taken with 140 citizens in it.
|
||
|
||
### Files touched (only these)
|
||
|
||
`web/index.html` (`[Lane F R42]` mark) · `web/js/world/interior_mode.js` (`[Lane F R42]` mark) ·
|
||
`tools/qa.sh` (Gate 11 + the R41 shot-gate promotion) · `tools/flags_check.py` (the three red gates
|
||
above) · `tools/qa/r42_roster.mjs` (new) ·
|
||
`tools/qa/r42_repro.py` (new) · `tools/qa/r42_budget.py` (new) · `tools/qa/r42_tour.py` (new) ·
|
||
`docs/shots/laneF_r42/**` (new) · `F-progress.md` · `docs/LANES/LANE_F_NOTES.md` · `README.md`.
|
||
**No repository-touching git command was run** (the one read-only `git ls-tree v2.1` is declared at
|
||
the top of this section). **No tag.** I did not touch `web/assets/credits.json`, `web/models/**`,
|
||
`pipeline/**`, `tools/qa/r41_integration.py`, `tools/qa/r41_denylist.mjs` or any other lane's file.
|
||
|
||
---
|
||
|
||
## Round 41 (§41.6) — THE LIBRARY LANDS: five seams wired, six gates added, and the interior margin was never 162
|
||
|
||
*Full runbook detail (gate arms, the control transcripts, the A/B method): [LANE_F_NOTES §41](docs/LANES/LANE_F_NOTES.md).
|
||
Shots + their index: `docs/shots/laneF_r41/` (README in the folder).*
|
||
|
||
Every number below is from a command run today on the integrated tree — five lanes' uncommitted work
|
||
in one working copy — in fresh headless contexts on port-isolated no-store servers, seed 20261990,
|
||
1280×720. **No git command was run.** No tag (John's playtest session rules the epoch).
|
||
|
||
### The five asks, each with its outcome
|
||
|
||
**① [C] THE WARDROBE IS ON — and the arm that ships by default is still the canvas.**
|
||
Two marked edits: `interior_mode.js` gains `WARDROBE_BASE = 'assets/wardrobe/'` and
|
||
`stockBaseFor(shop)` (godverse per-shop base first — a real shop's real stock outranks the town art;
|
||
else the wardrobe base for `opshop`; else `undefined` → the town-wide packs), consumed at `enter()`
|
||
and in `stockInfo`. `index.html` preloads `preloadStockPack('opshop', { base: WARDROBE_BASE })` beside
|
||
the three town-wide preloads, inside the same `STOCK_REAL` gate. One constant, one resolver, both
|
||
sides — the R24 cache-key lesson, because `(type, base)` is the cache identity and a base spelled two
|
||
ways fails silently to parody.
|
||
|
||
| | `?stock=real` (ON) | default boot (OFF) |
|
||
|---|---|---|
|
||
| room's resolved base | `assets/wardrobe/` | `null` |
|
||
| pack items resolved at build time | **52** | 0 |
|
||
| real garment ids on rendered meshes | **69** (Puppy Jumper · Splatter Smiley Tee · Squiggle Knit Dress · Trackie Dacks…) | 0 |
|
||
| atlas the GPU is sampling | `wardrobe_atlas_00.webp` | — |
|
||
| wardrobe requests | **exactly 2** (index + one atlas) | **0** |
|
||
| console errors | 0 | 0 |
|
||
|
||
**Which ships default: the canvas.** `STOCK_REAL` is `params.get('stock') === 'real' && !NOASSETS` —
|
||
opt-in, unchanged. **Both arms A/B'd against a control tree with F's two edits reverted** (`web/`
|
||
copied, `assets`/`models`/`vendor` symlinked to the live tree so another lane's wave is identical on
|
||
both arms; town frozen `pop=0&weather=0&tram=0&magpie=0&washing=0`):
|
||
|
||
| frozen town, control vs treatment | default arm | `?stock=real` arm |
|
||
|---|---|---|
|
||
| fetch surface | **160 URLs, identical hash, added [] removed []** | **+2 URLs exactly** (`stock_opshop_index.json`, `wardrobe_atlas_00.webp`), removed [] |
|
||
| street (`shopfront_detail`) | **131 draws / 18,018 tris — identical** | 131 / 18,018 — identical |
|
||
| op-shop interior | **59 draws / 28,505 tris — identical** | 59 → **55 draws (−4)**, 28,505 → **28,065 tris** |
|
||
| geometries · textures | **199 · 111 — identical** | 199 → 194 · **111 → 100 (−11)** |
|
||
|
||
So: nothing breaks without it, byte-for-byte, and turning it on is a **draw and texture WIN** — 52
|
||
garments come off ONE shared atlas material where the procedural path built a canvas per garment.
|
||
|
||
**② [D] THREE GATES WIRED** into `qa.sh` Gate 10: `node tools/qa/r41_postures.mjs`,
|
||
`tools/.venv/bin/python tools/qa/r41_citizens.py`, `…/r41_shots.py`. The shot gate is wired with
|
||
`--outdir "${PROCITY_SHOT_OUT:-$TMPDIR/procity-r41-shots}"` so a routine gate run never rewrites the
|
||
round's committed shots; `PROCITY_SHOT_OUT=docs/shots/laneD` regenerates them deliberately. Two run
|
||
strict; **`r41_shots.py` runs warn-level** with its reason recorded at the call site — see filing 5
|
||
below, its browse arm loses its browser to the sim between the scan and the frame.
|
||
|
||
**③ [E] `pipeline/clips_verify.py` WIRED** as Gate 10's first gate (no venv, no browser, ~1 s):
|
||
**GREEN, 0 errors / 0 warnings, 46 clips / 6 groups / 3,498,124 B.**
|
||
|
||
**④ [B] THE NON-REPRODUCIBLE BOOKMARKS — confirmed, and the set is BIGGER than B measured.**
|
||
Independently, on today's tree: `?classic=1`, town frozen, 2.5 s settle, sha256 of the PNG, **3 boots
|
||
per bookmark**:
|
||
|
||
| bookmark | distinct hashes over 3 boots |
|
||
|---|---|
|
||
| `street_noon` · `shopfront_detail` · `crossroads_busy` | **3 of 3 — not reproducible** (B's finding, reproduced) |
|
||
| `market_square` | **3 of 3 — not reproducible** (B measured this one as rock-solid; it is not, here) |
|
||
| `night_neon` | **1 of 3 — STABLE** |
|
||
|
||
**THE RULING: no R41 gate pins a screenshot byte-hash on any bookmark, and none should while the sign
|
||
atlas paints `bold Npx Arial` before the font resolves.** Only `night_neon` could carry one and one
|
||
stable bookmark is not a golden suite. Everything R41 asserts is a **counter** — draw calls,
|
||
triangles, fetch surface, item ids, texture URLs, stature — all of which reproduce exactly. The tour
|
||
shots are evidence, captioned with the counters that are gated, and their README says so. **Filed to
|
||
B for R42:** one `await document.fonts.ready` before the first sign-atlas paint in `buildings.js`,
|
||
then re-measure — cheaper than a perceptual-compare harness, and it fixes the cause.
|
||
|
||
**⑤ [C] THE CORRECTED `drawSweep` — confirmed on an independent instrument, and the true numbers.**
|
||
F wrote its own sweep (`r41_integration.py` arm 2): F's own scene bookkeeping, F's own strip-and-
|
||
measure loop, `PROCITY_C.drawSweep` never called — because the thing C found broken was exactly the
|
||
sweep's scene bookkeeping.
|
||
|
||
| 12 types × 6 archetypes, seed 1990 | F's own sweep | C's instrument, today | margin to ≤350 |
|
||
|---|---:|---:|---:|
|
||
| GLB off | **93** @ `opshop/hall` | 93 @ `opshop/hall` | 257 |
|
||
| **GLB on** | **123** @ `dept/auto` | **123** @ `dept/auto` (twice) | **227** |
|
||
| GLB on + `?stock=real` | **123** @ `dept/auto` | 123 @ `dept/auto` | 227 |
|
||
| GLB on + `noKit` (pre-R41 control) | — | **116** @ `dept/auto` | 234 |
|
||
|
||
**THE PHANTOM CONTROL:** the same loop with one stale room deliberately left in the scene — the
|
||
pre-R41 behaviour, reproduced on purpose — reads **197 @ `dept/auto`, an inflation of +74 draws on
|
||
every reading**. That is C's §41.4-0 finding confirmed independently, and it decomposes R39/R40's
|
||
"worst 188" exactly: 116 of dept plus ~72 of a record shop nobody was looking at.
|
||
|
||
**THE TRUE NUMBERS, used everywhere in this close: worst interior 123 draws, law ≤350, MARGIN 227.**
|
||
Not 188/162. §41.4 spent **+7 draws at the worst room** (116 → 123). Two honest notes: C published
|
||
**122**, and today's tree reads **123** on both instruments — C's table also reads 1–4 draws light on
|
||
record/book/video/pawn/milkbar/band_room **in the `noKit` arm too**, so the difference is C's
|
||
measurement environment, not the R41 kit. And even the inflated historical reading (197) was never a
|
||
breach of the law.
|
||
|
||
### The new gates F owns, each with its control demonstrated
|
||
|
||
**`tools/qa/r41_denylist.mjs` — RULING 3 BECOMES REPO LAW** (node, zero deps, 0.8 s). Seven banned
|
||
names (`character_kit_modular`, `exports/bodies/`, `elsa_coronation_hair_wig_kh3`, `daphne_sexy`,
|
||
`mocaponline`, `SK_Mannequin`, `anatomy/`) checked two ways: banned **path components**, and banned
|
||
**bytes in every file under `web/`, binaries included** — a GLB carries its source node names in the
|
||
JSON chunk and a manifest carries the path it was published from, so "nothing references it" is a
|
||
claim about text and "nothing contains it" is the claim a licence audit asks.
|
||
**Result: 738 files · 197.3 MiB · 0 symlinks · 0 hits.**
|
||
**CONTROL, run every time:** the gate writes a plausible plant (a manifest row naming
|
||
`~/Documents/character_kit_modular/exports/bodies/body_07.glb`) into `web/assets/`, asserts the scan
|
||
goes **RED on 2 names**, removes it in a `finally`, and asserts the scan returns to **0 hits**. A
|
||
deny-list that has never fired is indistinguishable from one with a typo in its regex.
|
||
|
||
**`tools/qa/r41_integration.py` — the seams F wired and the budgets F answers for** (Playwright, five
|
||
arms, own port):
|
||
1. **the wardrobe, both arms** — the table above; the OFF arm is the control.
|
||
2. **the interior budget on F's own sweep** + the phantom control — the table above.
|
||
3. **`?noassets=1` still clean** over a walk that enters op-shop, record and pub: **0 GLB · 0 clip
|
||
GLB · 0 motion manifest · 0 wardrobe · 0 stock pack · 0 asset manifest**, 0 console errors,
|
||
106 requests swept. **CONTROL:** the same walk on `?stock=real` fetches **5 of 6** cargo classes,
|
||
so the classifier can see what it claims is absent.
|
||
4. **`?clips=0` / `?classic=1` carry no R41 cargo** — `?clips=0`: 142 distinct URLs, 0 clip cargo, 0
|
||
wardrobe, `clipStats {0 groups, 0 clips, 0 B}`. `?classic=1`: 125 URLs, same three zeros.
|
||
**CONTROL:** the default boot fetches the library (2 requests, bank 2 groups / 16 clips /
|
||
1,242,272 B), so the zero-arms are a gate and not a tautology.
|
||
5. **the deny-list from the network side** — **190 distinct URLs** across street + gig night + 3
|
||
interiors, 0 banned names; the matcher demonstrated firing on a synthetic banned URL.
|
||
|
||
### The budgets, restated on the final integrated tree
|
||
|
||
**STREET (ruling 4) — the pin method of record**, `budget_walk.py --method stepwalk --stepm 2
|
||
--fixed-yaw 0 --laps 2` = 802 stations down the main spine at yaw 0, the worst bearing:
|
||
|
||
| boot | R40 pin | **R41 measured** | law |
|
||
|---|---:|---:|---|
|
||
| default, noon (seg 2) | 282 | **282 / 100,346 tris** | ≤300 ✅ |
|
||
| default, **NIGHT (seg 5)** | 291 | **291 / 120,093 tris** | ≤300 ✅ — **margin 9, intact** |
|
||
| `?classic=1`, NIGHT | 269 / 90,580 | **269 / 90,580** | ≤300 ✅ **byte-exact** |
|
||
| default, NIGHT, `?clips=0` | — | **291** / 82,013 | Δ draws **0** |
|
||
|
||
**NO LANE SPENT A STREET DRAW.** The pin reproduces R40 to the digit on every class, and the
|
||
motion library's own A/B (`?clips=0` vs default at the same station set) is **0 draws**: clips are
|
||
skeleton-only, 0 tris, and a seated ped is a ped that was already drawn.
|
||
|
||
**INTERIOR — before/after, and the instrument correction that dwarfs both:**
|
||
|
||
| | draws | law | margin |
|
||
|---|---:|---|---:|
|
||
| R39/R40 published worst (**phantom-inflated**) | 188 | ≤350 | 162 |
|
||
| pre-R41 worst, corrected instrument (`noKit`) | **116** | ≤350 | 234 |
|
||
| **R41 worst, corrected instrument** (12 types × 6 archetypes) | **123** @ `dept/auto` | ≤350 | **227** |
|
||
| the same reading with the phantom re-introduced | 197 | ≤350 | 153 |
|
||
|
||
And the **game-side** number, which is the one a player can actually stand in — 33 real open shops
|
||
entered through the shell with GLB **and** `?stock=real`, draws read off the interior scene's own
|
||
render: **worst 110 @ `dept` (Golden Department Store), margin 240.** Per type: dept 110 · toy 110 ·
|
||
book 78 · opshop 67 · video 54 · record 51 · pawn 47 · milkbar 46 · stall 17. Venues are shut at
|
||
midday, so the pub was measured on its own clock: **49 quiet (18:30) and 77 on gig night with the band
|
||
and the crowd in the room** — the round's most-furnished interior, at 22% of the law.
|
||
|
||
### QA
|
||
|
||
`bash tools/qa.sh --strict` →
|
||
|
||
```
|
||
SUMMARY 18 passed · 0 failed · 0 warn · 0 skipped
|
||
● QA GREEN — every landed gate passed. (exit 0)
|
||
```
|
||
|
||
All six R41 gates included and green, D's warn-level shot gate passing on this run too. R39's
|
||
transmission gate independently corroborates F's sweep from the other side: **GLB-on worst room 123
|
||
(dept/auto) with E's fix vs 193 (opshop/hall) with transmission restored — 70 draws saved**, and its
|
||
GLB-off control pair is equal at 93 on both roots, as it must be.
|
||
|
||
`node web/js/citygen/selfcheck.js` → **ALL GREEN 157,647/157,647, fingerprint `0x5f76e76`** —
|
||
unmoved. Nothing this round writes a plan field.
|
||
|
||
### Filed back (two of them were red gates F fixed in its own file)
|
||
|
||
1. **`tools/flags_check.py` `_enter_record_shop` — R41 closed its search window, 8 smokes went RED.**
|
||
It took the first OPEN record shop **of six** whose counter sits ≥2.6 m from every bin (so
|
||
counter-E can never fall into `binUnderAim`'s 2.5 m fallback). §41.4's 2–4 kit bins per record shop
|
||
brought that distance down across the board: the first six now read **2.36 / 2.02 / 2.26 / 2.21 /
|
||
2.21 / 2.10 m**. Measured over twelve, **3 still clear the bar** (Colonial 2.80 · Bluey's 2.79 ·
|
||
Gazza's 2.84) — so the property survives and the window was the problem. **Widened to the whole
|
||
open set, and it now reports the shop and its margin** so the next narrowing shows up in the log
|
||
instead of arriving as eight failures. Verified first that this is not a game defect: the shell
|
||
orders `aimed bin → trySell → nearest bin`, so the sell card wins at a counter regardless of the
|
||
fallback radius. **→ LANE C, for information:** record-shop counters now sit 2.0–2.8 m from the
|
||
nearest bin. Nothing is broken; if you ever want the counter to keep a clear 2.6 m, that is the
|
||
number.
|
||
2. **`tools/flags_check.py smoke_djdance` — R41 made the mixer multi-action and the stall test lied.**
|
||
It read `mixer._actions[0]` before and after a 700 ms window and failed when `tAfter <= t`. D's
|
||
§41.3 gives every actor per-instance clip swapping and the gig widening installs a venue clip into
|
||
some crowd slots, so index 0 is no longer the live action nor the same one 700 ms later: a healthy
|
||
dancer read **t 24.19 → "tAfter" 14.21** and the gate called it a stalled page. Fixed to identify
|
||
the running action by clip **name**, follow that clip across the window, measure advance **modulo
|
||
the clip duration** (a 30.58 s loop sampled across its wrap is advancing, not stalled), and treat a
|
||
legitimate mid-window swap as advancing. Now green, and it reports both durations in play
|
||
(30.58 s and 52.42 s — the R13 dance pick and D's venue widening, side by side).
|
||
3. **→ LANE B: the bookmark instability is wider than filed** (`market_square` too — 3 distinct
|
||
hashes over 3 boots). The R42 fix is one `await document.fonts.ready` in `buildings.js`.
|
||
4. **→ LANE C: worst interior reads 123 on this tree, not 122** — on C's own instrument as well as
|
||
F's. The `noKit` arm carries the same 1–4 draw spread, so it is not the kit.
|
||
5. **→ LANE D: `r41_shots.py`'s BROWSE arm goes red on this tree, and it is the harness, not the game.**
|
||
Its street arm is excellent and reproducible (8 near-tier rigs, states `{walk 7, bench-sit 1}`, 3
|
||
distinct clips `@walk`/`sit_hands_thighs`/`walk_shopping_bag`, 126 draws, every stature clean). The
|
||
browse arm scans up to 8 shops for browsers, keeps the best, then **RE-ENTERS the winner** — and a
|
||
patronage occupant is free to walk out during the ~3 s per candidate that scan costs, so the
|
||
re-entry spawns 0 browsers and the arm asserts on an empty room. **Measured: 3 consecutive
|
||
standalone runs all land on `T & R Books` with `figures: 1` (keeper only), byte-identical at 34
|
||
draws / 36,751 tris** — while the same gate passed with `figures: 2` earlier the same day under
|
||
different machine load, with nothing in R41's shipped code moving in between. **Fix:** re-check
|
||
`k.browse` after the re-entry and re-pick, or don't re-enter — keep the winner entered. Until then
|
||
it is wired **warn-level** in `qa.sh` with that reason recorded at the call site, so it runs and
|
||
reports without failing a round for a browser who went home. (F's own tour hits the same seam and
|
||
gets a browser every run by dwelling 25 s on the street first so several shops hold occupants —
|
||
`docs/shots/laneF_r41/browse_interior.jpg`, a browser on `browse_close_cabinet` at a bookshelf.)
|
||
6. **F's own `r39_transmission.py` had an unmeasured 30 s navigation timeout, and it cost a round a
|
||
RED.** Its `pg.goto(interior_test.html?localdepot=1)` waits for `load` — every subresource — on
|
||
Playwright's 30 s default. Inside a full `--strict` run (four browser gates back to back on a
|
||
loaded box) it timed out once and printed a stack trace where an assertion belongs; the same gate
|
||
went green **twice standalone, immediately after**, and the page reaches `load` in **0.4 s over 27
|
||
requests, 3 runs of 3**. Pinned to an explicit 90 s — ~200× the measured cost, so it cannot mask a
|
||
regression, and one class of false red is out of `--strict` for good. An unstated default is a
|
||
number nobody measured.
|
||
7. **→ LANE C: `assets/wardrobe/` is now spelled in two files** — F's exported `WARDROBE_BASE` in
|
||
`interior_mode.js` (the shipping path) and a local `const` at `interior_test.html:93` (yours). They
|
||
match today. Two spellings of one cache key is the exact shape of the R23 bug, so if the wardrobe
|
||
ever moves, import the constant instead of editing both.
|
||
|
||
### Files touched (only these)
|
||
|
||
`web/index.html` (`[Lane F R41]` marks — Lane B's `[Lane B R41]` marks untouched) ·
|
||
`web/js/world/interior_mode.js` · `tools/qa.sh` · `tools/flags_check.py` ·
|
||
`tools/qa/r41_denylist.mjs` (new) · `tools/qa/r41_integration.py` (new) · `tools/qa/r41_tour.py`
|
||
(new) · `tools/qa/r39_transmission.py` (F's own gate, one timeout — filing 6) ·
|
||
`docs/shots/laneF_r41/**` (new) · `F-progress.md` · `docs/LANES/LANE_F_NOTES.md` ·
|
||
`README.md`. **No git command was run. No tag.**
|
||
|
||
---
|
||
|
||
## Round 40 (§40.6 — THE PLAYTEST HARNESS) — F8 turns John's session into tickets; the wave-1 asks settled
|
||
|
||
*Full spec (note schema · tour stops · gate arms · ask detail): [LANE_F_NOTES §40](docs/LANES/LANE_F_NOTES.md).*
|
||
|
||
**THE KEYSTONE LANDED.** `web/js/world/playtest.js` (NEW, F-owned) + marked shell seams:
|
||
|
||
- **F8 anywhere drops a note**: `{pos, mode (street/interior/dig/map), shopId+shopName,
|
||
street/locality via A's address layer (own `createStreetLocator`, zero draws), citySeed, town,
|
||
seg+clock, boolean flags, fps, draws, tris}` + a screenshot download of the CURRENT view
|
||
(interiors/open crates render via the new `interiorMode.renderFrame()`) + an overlay one-liner —
|
||
**Enter saves, Esc cancels**, typing never reaches the game, and F8 inside a shop does not eject
|
||
you (`noteActive` joins the digActive/sellActive unlock contract).
|
||
- **Delta-law persistence** (the fog's pattern): key `procity-playtest`, caps 200 notes/240 chars
|
||
FIFO at write AND validated at read, **corrupt-reject-WHOLE and loud**, import = adopt (reject
|
||
leaves current notes untouched). **Export = one tickets-JSON** (`procity-playtest/1`, 18 typed
|
||
fields per note — schema documented §40.2 of the notes) via the note-card button,
|
||
`DBG.exportNotes()`, or `PROCITY.playtest.exportJSON()`. **The store is LAZY, and a gate earned
|
||
that**: the first cut's constructor `getItem` turned R30c classic purity RED
|
||
(`?game=0: storage calls 1`) on the full strict run — fixed to read-on-first-use, R30c re-run
|
||
green, and r40_playtest.py grew its own instrumented `?game=0` zero-calls arm so the class stays
|
||
caught in F's gate too.
|
||
- **Classic-gated by construction**: dynamic import only when `flagOn('playtest')` (default-on,
|
||
`?playtest=0` opt-out) — an off boot **never fetches the module** (185/207 requests swept clean),
|
||
never grows the DOM, never touches storage (classic: localStorage 0 keys). Control demonstrated:
|
||
the default boot fetches + publishes it.
|
||
- **`?bugtour=1`**: dbg.js now loads under the flag alone, and N walks **11 stops** — spawn →
|
||
street day → shop/buy (book) → dig (record) → **op-shop bin, caption says UNARMED** → gig night →
|
||
tram → rain (caption: re-run `&weather=rain` if the seed rolled dry) → night → fog map → second
|
||
town via the selector — every stop a caption of what to look at, DBG bookmarks reused verbatim,
|
||
F8 live throughout.
|
||
- **Gate `tools/qa/r40_playtest.py` GREEN, all arms + controls**: off-byte-identical (+ its
|
||
fetched-on-default control) · the REAL F8 key path round-trips (download → overlay → Enter →
|
||
export schema-validates 18/18 → reload persists same id → Esc arm → both reject arms) · the tour
|
||
advances through all 11 stops with **0 console errors**, no `?dbg=1` needed.
|
||
|
||
**THE FIVE ASKS:** ① D's `door_footpath_check.mjs` **wired** (qa.sh Gate 9, plain node, ~2 s) ·
|
||
② B's `r40_lane_b.py` **wired** (Gate 9, Playwright class) · ③ D's doorRect derivation **measured
|
||
and FILED BACK**: through D's own classifier + shipped snap, the `d/2+0.05` doorRect step reads
|
||
synthetic strict **447→446**, walkable **473→472**, and **breaks 1 previously-passing point**
|
||
(violates D's own FIX-ONLY arm) — real towns 100% either way; the 20 plaza misses are stall-packing
|
||
geometry, not nudge length; shell stays `+0.6`, numbers handed to D · ④ A's debt **D2 CLOSED**:
|
||
the shell's `isOpen` is A's canonical (no-hours ⇒ OPEN) via the barrel, every `PROCITY.isOpen`
|
||
consumer (audio + 19 tools call sites) inherits it; remaining third impls listed in the notes
|
||
(sim.js `_openAt` — already A's semantics; dbg.js openLate predicates) · ⑤ README `?r=N` row now
|
||
states the R40 law (**391 measured, ≤420 declared, gated**; 307 gone) + flags_check's selector
|
||
label now reads **RULED R40**.
|
||
|
||
**QA:** `bash tools/qa.sh --strict` — **GREEN** (summary line in the round-close message; Gate 9's
|
||
three new gates all pass) · selfcheck **157,647/157,647 ALL GREEN, fingerprint `0x5f76e76`** —
|
||
zero goldens moved (the harness is DOM + storage; it renders nothing and writes no plan).
|
||
Shots: `docs/shots/laneF_r40/r40_f8_note_overlay.png` · `r40_bugtour_caption.png`.
|
||
|
||
**Parked (unchanged, per the round brief):** op-shop bin contents · fog default-on · v9 third
|
||
layer · the audio listen pass — all become F8 tickets the moment John plays. **No tag.**
|
||
|
||
---
|
||
|
||
## Round 39 (v9 WAVE 1 — THE CITY IS THE GAME) — five gates, five controls, and the interior margin is 162
|
||
|
||
*Full detail: [LANE_F_NOTES §39](docs/LANES/LANE_F_NOTES.md). (R38's close lives in
|
||
[LANE_F_NOTES §38](docs/LANES/LANE_F_NOTES.md) — it never got a row here.)*
|
||
|
||
**`tools/qa.sh --strict --matrix` GREEN — 10 passed · 0 failed · 0 warn · 0 skipped**, on the true
|
||
final tree, with three new gates wired in as Gate 8. 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. The interior margin is 162 draws,
|
||
not 21 and never 6.** Lane E's transmission fix takes the worst room from **329 → 188** draws.
|
||
Measured with `tools/qa/r39_transmission.py`: two port-isolated no-store roots, identical inode for
|
||
inode except one file — the shipped `bookshelf` GLB vs a copy with `transmissionFactor: 1`
|
||
re-inserted (BIN sha1 identical, only the JSON chunk moved) — both driven through
|
||
`interior_test.html`'s own `drawSweep`.
|
||
|
||
| `drawSweep` worst | glass bookshelf (= the pre-E tree) | 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 vs the ≤350 law | **21** | **162** | **+141** |
|
||
|
||
`opshop` 329→174 (−47%) · `book` 247→125 (−49%) · **every other type 0**, which is the shape Lane C
|
||
predicted from an isolated room, reproduced 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 I fetched all four R39 GLBs off `digalot.fyi` and
|
||
scanned them: **sha1-identical to the local tree, 0 transmissive materials, 4/4.**
|
||
|
||
**The five gates, each with its control demonstrated in the same run:**
|
||
|
||
| gate | verdict | the arm that could have failed |
|
||
|---|---|---|
|
||
| **39.1 address** (`r39_address.mjs`) | GREEN | **1,192/1,219 shops (97.8%)**, 23/23 `ways` supplier, 857/857 membership agree / 0 wrong. **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 it is the ONLY alarm that catches the 3 m case |
|
||
| **39.2 fog** (`r39_runtime.py`) | GREEN | **HIDES** 4/72 at spawn, 0 of 61 distant shops known · **REVEALS** 2,125 m → 58 shops · **FRONT TEST** 0 of 59 shopfronts learned when probed from 5 m behind · **+0 draws AND +0 tris**, four fresh browsers, both orders, drained, 3 reads each · 8/8 corrupt saves rejected · null store on classic/`game=0`/`fog=0` |
|
||
| **39.3 THE SIGN** (`r39_runtime.py`) | GREEN | shops in front of the player at spawn **4/4 · 4/4 · 4/4 · 1/1**; with the pre-R39 sign re-derived on the same tree, **0 on all four** |
|
||
| **39.4 rummage bin** (`r39_runtime.py`) | 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. **THE CONTENTS DID NOT SHIP** — on the default street boot the tub is `kind:'prop'`, 0 dig targets, E opens nothing |
|
||
| **39.5 transmission** (`r39_transmission.py --validator`) | GREEN | the validator **HARD FAILS rc 1** on a fabricated glass asset and **PASSES on the shipped tree** — both halves, or "it failed" only proves the gate is always red |
|
||
|
||
**Re-baselined, without falsifying a release record** (`tools/qa/r39_shots.py` →
|
||
`docs/shots/laneF_r39/`): the R32 cluster-pose spawn frame and the R35 first-five splash were both
|
||
shot at the old pose. `v7_tour/02_the_strip.png` and `v7_beta/first_five_splash.png` are **kept as
|
||
history**; the re-baseline ships its own BEFORE, camera parked at the old `-sin` pose on the same
|
||
tree. Katoomba's BEFORE is a paddock and a blank back wall; the AFTER is the awnings and the
|
||
shopfront run. The R35 *gate* needed no re-baselining — `smoke_first_five` is pose-independent and
|
||
was green either side.
|
||
|
||
**Three corrections, all mine to make and all measured:** LANE_A_NOTES §39's "a 30 m shift returns 0
|
||
street names" — it returns 40/12/47 wrong ones, because on a dense grid a big shift lands you on the
|
||
next street · LANE_B_NOTES §39'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
|
||
harness bug, recorded: I first pointed 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.**
|
||
|
||
---
|
||
|
||
## Round 37 (v8 WAVE 0 — THE FLOOR) — the budget of record settled · two vacuous gates repaired
|
||
|
||
**0.3 — THE BUDGET OF RECORD: 292 draws / 124,793 tris. The audit was right and the spike was
|
||
right; they were measuring different BEARINGS.** Two sessions disagreed by 90 draws (280 walked vs
|
||
191 teleport-walk). Measured on one tree, one box: worst draw **per bearing**, 12 bearings × 102
|
||
stations down the synthetic spine — **0° 284 · 30° 248 · 60° 208 · 90° 177 · 120° 196 · 150° 214 ·
|
||
180° 211 · 210° 207 · 240° 178 · 270° 182 · 300° 223 · 330° 267.** **110 draws of spread from the
|
||
camera direction alone**, and the peak sits at **yaw 0 — the default argument of
|
||
`DBG.teleport(x, z, ry = 0)`.** The audit took the default and landed on the worst bearing; the
|
||
spike supplied a yaw and landed mid-range. Both numbers were correct; they were not the same
|
||
quantity. Lane B's independent R37 walk (276 draws / **99,880** tris) and mine (282 / **99,878**)
|
||
agree on triangles **to 2** — same frame, two harnesses. **Viewport is irrelevant** (1280×720 and
|
||
2560×1440 identical to the byte). The R+1 dispose window is worth **+19 draws** (31 vs 27 live
|
||
chunks), not the 60–80 folklore claimed — the other ~100 draws between bookmark and walked-worst are
|
||
*place* and *bearing*. **THE PIN** (default boot, `7bd96e5`): synthetic **292 draws** (night, 31 live
|
||
chunks) / 120,145 tris; `?classic=1` 269 / 90,580; corpus tri worst **124,793 on fitzroy_real** —
|
||
the audit's "99–124k" range confirmed at its top. **Margin: 8 draws, ~75k tris.** Only 8 frames of
|
||
810 exceed 280; classic never does. Also found: **`?r=3` measures 307 draws and breaches the ≤300
|
||
law** (ungated override, filed). Independently confirmed B's kerb change at **+0 draws / −8 tris**.
|
||
Headless SwiftShader — but draw/tri counts are counted in JS, not by the GPU, and the viewport
|
||
control proves it; **frame TIME is still unmeasured on any real GPU and no proposal should claim it.**
|
||
|
||
**0.3b — THE SHARED INSTANCE CAP: 256.** Measured: an `InstancedMesh` costs **+1 draw at every N
|
||
from 32 to 2048**, and `N × tris/instance` **always submitted** (the instance bounding sphere spans
|
||
the town — at N=2048 all 368,640 prop tris go down the pipe from anywhere). So draws are not the
|
||
constraint; triangles are. 256 × a 2–4 tri billboard cell = 512–1,024 tris = **0.5% of budget**;
|
||
CPU to rewrite all 256 matrices, **0.01 ms/frame**. Binding companions: **≤24 tris/instance at the
|
||
cap**, and **spend it proximity-first** — measured anchor supply runs synthetic 246 poles / 6.8 km
|
||
to bendigo_real **7,325 poles / 188 km**, so 256 sprayed town-wide on bendigo is one instance per
|
||
730 m and statistically unencounterable.
|
||
|
||
**0.2 — `smoke_tram` REBUILT.** It asserted `!!getObjectByName('tram')`, printed a SKIP and returned
|
||
on absence, and **passed on the fenced path** because `tram.js:110-114` adds a named EMPTY group.
|
||
Now STRICT with four arms: RUNS (route/stops, and the body moves **19.8 m over 45 frames** under the
|
||
*shell's own* loop) · BEHAVES (an isolated tram, fixed dt, 400 s: dwell time accounts to **exactly
|
||
9 × 3.5 s**, max cruising tick 0.4665 ≤ SPEED·dt 0.45, every discontinuity bounded by 2·LANE with
|
||
≥1 end reversal, riding **3.2 m off Lane A's own main-edge centreline**) · GENUINELY ABSENT on the
|
||
fenced katoomba_real (0 stops, **0 group children**, 0 m moved) · OFF under `?tram=0`/`?classic=1`.
|
||
**Both controls demonstrated every run** — the run-predicate pointed at the fenced town and the
|
||
absence-predicate pointed at the running tram must each go red.
|
||
|
||
**0.1 (F half) — THE KERB GATE: the ruling is in the paint, and the control fires at EXACTLY 14.**
|
||
New STRICT `smoke_kerb`. A's corrected selfcheck asks the plan-side question; F's asks the runtime
|
||
one, of the paint — **raycast straight down at every rendered poster and classify the topmost ground
|
||
hit by MATERIAL IDENTITY** (`skins.groundMat()` is cached, so the road mesh's material *is* the
|
||
object the factory returns — no mesh names, no strings, nothing that drifts when B edits). Topmost,
|
||
not first, because B put the carriageway on top at junctions. Six arms, all green: **14 posters
|
||
rendered == 14 in `plan.posters`, every one within 0 m** (nobody had ever asserted the poster you see
|
||
is the poster A placed) · **detector control A** — a main centreline classifies ROAD at y 0.04 ·
|
||
**detector control B** — 13 m off it classifies FOOTPATH at y 0.02 · **THE RULING: 0 of 14 posters on
|
||
road paint**, tally `{footpath: 14}` · **THE CONTROL: the same leg fails under `?verge=0` at EXACTLY
|
||
14**, tally `{road: 14}` · **CLASSIC KEEPS ITS PAINT** — 13 m off the centreline is still ROAD under
|
||
`?classic=1`, 0 posters. **Two lanes, two wholly independent methods, the same number 14**: A counted
|
||
plan-side by offset in node; F counted paint-side by raycast in the browser off B's geometry.
|
||
|
||
**0.5 — `classic_regression` GETS A NETWORK ALLOW-LIST**, derived from the **v2 tag at gate time**
|
||
(`git ls-tree` + v2.0's own manifest), never a deny-list. **The brief's shape would have missed the
|
||
breach it was written for**: the R36 widening was `models/peds/woman_dj_01.glb` — *same-origin* — so
|
||
"four depot GLBs + same-origin" is green straight through it. `/models/peds` is enumerated by the
|
||
v2.0 tree (exactly the covenanted 21 files; today's dir holds 32). The nine depot fetches are
|
||
**baseline, not a breach** — proven by booting the actual v2.0 tree and capturing its wire.
|
||
**Control every run**: injecting the R36 breach itself plus a never-existed asset turns the clean
|
||
verdict into exactly 2 named violations. F measured classic's surface as **+6 files** vs the real
|
||
v2.0 tree and **held rather than ruling**; **Fable ruled** (`4a7c0f1`): the 5 audio files are
|
||
**RATIFIED BASELINE** (v2.1 was v2's final tagged state, so audio shipped inside v2 as the furniture
|
||
GLBs did) and `assets/towns/index.json` is **NOT baseline** but a **named, printed exception**. Landed
|
||
in the mechanism by moving the authority tag **v2.0 → v2.1** — surgical, not generous:
|
||
`git diff v2.0 v2.1 -- web/` is the audio pack + `audio.js` + four wiring edits, **zero other assets,
|
||
zero deletions**, so the tag change admits precisely the ratified audio. Gate output confirms:
|
||
same-origin baseline **47 → 52**, exceptions **2 → 1**, control still discriminating. "Should
|
||
`?classic=1` show the town selector at all?" → v8 debt ledger, R38+.
|
||
|
||
**THE CLOSE — `qa.sh --strict --matrix` on the true final tree (A + B + Fable + F all landed,
|
||
port-isolated :8975): 7 passed · 0 failed · 0 warn · 0 skipped — QA GREEN.** Selfcheck
|
||
**157,407/157,407** (A's +1,055 poster assertions, green on the ruled street) · manifest 0 err/0 warn ·
|
||
**flags harness 0 fails / 0 warns** (classic fp `0x5f76e76`, 162 draws/16,642 tris, fetch surface
|
||
clean vs **v2.1** with 1 named exception and the injection control firing · default boot gig golden
|
||
`0xec7a2d39`, 131 draws/23,382 tris, dig reachable naked · the new kerb gate and the rebuilt tram
|
||
gate both green with every control) · figure-scale 29 figs/6 shops · **MATRIX GREEN 10 towns × 7
|
||
gates**, tram verdicts unchanged (fenced on bendigo/fremantle/katoomba_real — the same three the
|
||
rebuilt smoke uses for its absence arm). **No tag: wave 0 levels the floor, it does not ship a
|
||
version.** *Caveat kept honest in the notes: the matrix's own budget column is clean-load bookmark
|
||
numbers (69–161 draws) — a regression signal, not the budget of record, which this very round proved
|
||
under-reports a walked-and-turned frame by ~120 draws. Making the matrix walk-and-turn is filed to
|
||
the debt ledger, not smuggled into a floor round.*
|
||
|
||
Full record: LANE_F_NOTES §37.
|
||
|
||
---
|
||
|
||
## Round 36 (THE v7.0 CLOSE, wave 2) — the gate, the freeze, the tag
|
||
|
||
**Zero alpha-blockers stood** (Fable's wave-0 naked-boot playtest walked the whole loop by hand),
|
||
so wave 2 was pure close: E's three flags measured shut, the full gate over the final tree, docs
|
||
frozen, **tag `v7.0` (annotated, LOCAL — Fable pushes)**.
|
||
|
||
**E's 2 a.m.-asset flags, closed by measurement:** (a) the 5 trellis DJ bodies' bind skeletons
|
||
measure **0.984–0.989 units** vs the legacy roster's 173.46 (cm) — the figure-scale gate grew a
|
||
DJ-body leg that spawns all five BY NAME through the real browse path: statures **1.64–1.89 m**,
|
||
all in [1.4,2.0], feet planted, rig-kind verified (the old sample's `(i*7)%19` could never reach
|
||
indices 17–21). (b) New STRICT `smoke_djdance`: the 4 Rokoko clips bind **64/65 tracks, sole
|
||
unbound `mixamorigReference`** (gate on binds + NaN, never node counts — no template pin); a
|
||
gig-night dancer's live action IS a dance clip (identity by duration, mixer advancing,
|
||
`position.y` flat = the clip moves the body); `?classic` fetches **zero dance clips** (positive
|
||
control: default fetched 4); `?noassets` procedural sway ALIVE (placeholder dancers bounce
|
||
0.07 m — the inverse pair proves the branch). (c) Rokoko zone = E's w1 provenance, nothing
|
||
runtime.
|
||
|
||
**THE HOLD → RESOLVED (the five-holds law, end-to-end in one round):** wave 2 found `?classic=1`
|
||
fetching the 5 DJ body GLBs (unconditional `PED_NAMES.normal` members, rigs.js, `b339402` —
|
||
classic's crowd pool widened 17→22 ungated). F WARNED it in the suite and held; Fable ruled
|
||
"classic keeps its people"; D fixed it in rigs.js alone (`2fd4c1c`: base-17 restored, the five
|
||
append behind `djs = dance` riding the shell's existing `!CLASSIC` signal; classic's 129-citizen
|
||
census byte-identical to a `b339402^` shadow tree). F's wave-2.5 re-gate on the true final tree:
|
||
the warn flipped to "✓ zero DJ-body fetches" — **flags harness 0 fails / 0 warns**, default boot
|
||
keeps all 22 bodies and the organic gig-crowd proof. Held, ruled, fixed by the owning lane,
|
||
re-proven — nothing improvised.
|
||
|
||
**The gate (final tree, :8946 port-isolated): `qa.sh --strict --matrix` 7 passed · 0 failed ·
|
||
0 warn · 0 skipped; flags harness 0 fails / 1 warn wave 2 (the held finding, deliberate) →
|
||
**0 fails / 0 warns on the wave-2.5 re-gate** (true final tree, run 3 on the healed box — the
|
||
first two re-gate runs died to a box-level fork famine, ~10,600 leaked m3panel processes, fixed
|
||
at source; starvation artifacts, not gates); selfcheck
|
||
156,352/156,352; figure-scale 29 figs / 6 shops; MATRIX GREEN 10 towns × 7 gates.** The R32–35
|
||
smokes all re-green on the final tree: bible (ledger Σask $333 → Σoffer $135, scarcity across
|
||
reload, ?game=0 control) · hunt (rumor + auto-clear + travel day 1→2→3, same-town free) ·
|
||
godbay (one-hammer byte-identical, SOLD net $19 bounded, the paper on the wake toast) ·
|
||
first-five (HILLS HOIST $191 splash, beats on the real path, seeded across browsers). Classic
|
||
covenant fp `0x5f76e76`, gig golden `0xec7a2d39`, world fp `0xb1c7843e` unmoved.
|
||
|
||
**The freeze:** README v7.0 section (the loop + the morning-paper manual row) · V7 charter risks
|
||
retired-or-carried v4-style (economy RETIRED-structural, bot verifier carried to the live tier ·
|
||
schema RETIRED · re-emit identity CARRIED, never fired · scope RETIRED · v6 collision RETIRED) ·
|
||
LANE_F_NOTES §36. Full record: LANE_F_NOTES §36.
|
||
|
||
---
|
||
|
||
## Round 35 (v7.0 close, wave 2) — THE FIRST FIVE MINUTES (Fable, solo, JING5)
|
||
|
||
A fresh game arrives HUNTING: one seeded artist want (`rng(citySeed,'firstwant',0)` × the
|
||
parody roster) named on the splash with the pocket money ("You're hunting HILLS HOIST — $191
|
||
in your pocket"), pointing the rumors and the tour at the same target. Three toast-beats walk
|
||
the hustle off existing events (walk-in → digOpen → first find → first sale-or-listing, then
|
||
the listener removes itself). Returning saves get no tutorial; ?game=0/classic never reach it.
|
||
#pc-toast body-mounted so beats and morning papers surface inside shops too. STRICT
|
||
`smoke_first_five` green (roster want + splash · real-path beats · returning/?game=0 controls
|
||
· cross-browser seed determinism). Full record: LANE_F_NOTES §35.
|
||
|
||
---
|
||
|
||
## Round 34 (v7.0 close, wave 1) — GODBAY: the offline auction (Fable, solo, JING5)
|
||
|
||
⚖ LIST from the crate (mail-in consignment) → `listings` on procity-save/1 → resolves the day
|
||
after, wherever the day moves (sleep, travel, imports — adopt() and sleep() share the
|
||
resolver). THE ONE-HAMMER LAW: identity-seeded (never day-seeded) — a passed-in item re-lists
|
||
to the same result forever, no retry farm. 15% pass · uniform-across-band hammer (grail 2×low)
|
||
· 12% war ×1.5–2 · 12% fees, net floor $1; EV ≈ 0.8×mid so random flips bleed and only finds
|
||
profit; keeper = floor, auction = bounded ceiling. The morning paper rides the wake/arrival
|
||
toast ("⚖ SOLD 'Cane Toad Blues' — hammer $22, $19 after fees"). Band table imported from C's
|
||
bible.js (one authority), PRNG from core. STRICT `smoke_godbay` green (real-DOM consign ·
|
||
byte-identical double-sleep · bounded honest resolution · travel hammers · game=0 control).
|
||
Full record: LANE_F_NOTES §34.
|
||
|
||
---
|
||
|
||
## Round 33 (v7.0-beta wave 2) — THE HUNT: wantlist · rumor · travel (Fable, solo, JING5)
|
||
|
||
☆ WANT on the pull card → `wants` on procity-save/1 (optional, capped, deduped); buying a
|
||
matching item clears the hunt; THE WANTLIST section in the crate panel. The keeper gossips:
|
||
seeded 45% banner mutter naming a hunted want + a real cached town. TRAVEL COSTS A DAY:
|
||
adopting a save from another townKey (town OR seed) charges +1 day in save.js adopt() — load
|
||
and import both pay, same-town stays free (byte-equality gates untouched); arrival toast names
|
||
the fare. New STRICT `smoke_wantlist_travel` (real-card want · rumor scan · auto-clear ·
|
||
+1-day with intact cash/collection · same-town control · return fare · ?game=0 zero-DOM).
|
||
Full record: LANE_F_NOTES §33.
|
||
|
||
---
|
||
|
||
## Round 32 (v7.0-beta wave 1) — THE BIBLE + scarcity + the §31.6 queue (Fable, solo, JING5)
|
||
|
||
Guide bands derived in `js/interiors/bible.js` (E's thresholds mirrored, no pack re-emit) →
|
||
the pull/sell cards carry the **guide stamp**; parody bins roll seeded bands + 10% collector/
|
||
grail **gems** (asking under the keeper's bandLow offer — margin by construction); §9.1 sell
|
||
basis swapped to bandLow exactly as pre-ratified (alpha entries keep the pricePaid basis).
|
||
**Scarcity:** optional `pulls` on procity-save/1 — a bought dig slot stays gone all day
|
||
(pick-then-omit, zero collateral), pruned on sleep; real-sourced pulls never expire. §31.6
|
||
queue: cluster-pose spawn (main-street doors, classic keeps the frozen pose) · THE CRATE +
|
||
game bar live in interiors (C anywhere, yields to the cards) · DAWN dome swaps to high-cirrus
|
||
(weather override wins). **Gates:** new STRICT `smoke_bible` (band law over every bin ·
|
||
ledger closes to the cent on the GEM's bin, Σask $333 → Σoffer $135, gems +$21 · emptied bin
|
||
0 across reload · `?game=0` control re-opens 16/16) + sell_routing amended to the exact
|
||
formula (the fence moved from the receipt to the band). Full record: LANE_F_NOTES §32.
|
||
|
||
---
|
||
|
||
## Round 31 (v7.0-alpha CLOSE) — THE DIG FLIP + the tag
|
||
|
||
**Fable's wave-0 playtest found the one alpha-blocker: the dig was default-OFF on a plain boot.**
|
||
`index.html` still carried the v2-era opt-in (`params.has('dig')`) six epochs after the README
|
||
claimed "dig on" — and every R30 game-loop gate booted `dig=1` explicitly, so the suite was green
|
||
while E at a bin did nothing on the boot every player gets. Fixed with the one-line flip
|
||
(`DIG_ON = flagOn('dig')` — ?dig=0 opt-out, ?classic=1 off, `dig` on the flags surface) and fenced
|
||
with two new STRICT legs: **default_boot_gate asserts dig REACHABILITY on a NAKED boot** (E at an
|
||
aimed bin → digActive true → closed clean) and **classic_regression asserts dig INERT at the bin**
|
||
(zero `pcdg-*` DOM, zero Storage calls — measured, not assumed). *Harness lesson #4: the
|
||
explicit-flag blindfold — a gate that force-feeds its subject's flag can never notice the default
|
||
is broken; every claimed-default-on flag needs at least one flag-ABSENT gate.* Splash de-staled
|
||
("Every door opens." — the "(soon)" is over; controls line = E riffle/sell · C crate · SLEEP to
|
||
save) + the README manual row. `PROCITY_QA_PORT` override so the suite never silently reuses
|
||
whatever holds :8130 (this round: Fable's own preview server).
|
||
|
||
**The re-gate (final tree, :8931): `qa.sh --strict --matrix` 7 passed · 0 failed · 0 warn ·
|
||
0 skipped; flags_check 0/0 incl. both new legs; MATRIX 10 towns green.** Money shot:
|
||
`docs/shots/v7_alpha/crate_panel.png` — Monster Robot Party's frontage, THE CRATE with 3 real
|
||
finds (real cover thumbs, honest paid/where/day), game bar day 1 · $116 · CRATE 3 · SLEEP, driven
|
||
through the real loop with the dig param absent. **Tag `v7.0-alpha` — annotated, LOCAL ONLY**
|
||
(Fable pushes). THE BETA QUEUE (Fable's design wants, filed NOT fixed) is in LANE_F_NOTES §31.6.
|
||
Commits: `50385c0` (flip + gates + splash) · shot + notes commit (tag target). Full record: §31.
|
||
|
||
---
|
||
|
||
## Round 30 (v7.0-alpha — THE SAVE AND THE CRATE) — F: the contract first, then the save core + tomorrow
|
||
|
||
**The contract shipped before a line of implementation** (`53fcd3c`): `window.PROCITY.game =
|
||
{ day, cash, collection[], townKey, save(), load(), sleep(), export(), import(json), wallet,
|
||
recordFind(), removeFind() }` + the `procity-save/1` schema + the rotation law, published in
|
||
LANE_F_NOTES §30 — and it worked exactly as the order intended: **B picked up §30.4 within the
|
||
session** (venue.js "tonight" already reads `gigs.weekNight`; hud.js collection/SLEEP surface in
|
||
flight), and C's already-published §9 asks (`type` stamped at buy, `title`/`artist`, removal via the
|
||
game API, `wallet.sell` mirrored) are honoured in the entry shape.
|
||
|
||
**Then the layer** (`207bcff`): `web/js/world/save.js` (F-owned, new) — versioned localStorage save,
|
||
THE DELTA LAW BY SHAPE (payload keys are exactly `[cash, collection, day, savedAt, schema, town]` —
|
||
no world field can even be expressed); corrupted/foreign → **loud reject, raw stashed, fresh start,
|
||
town boots**; save on sleep + beforeunload; export/import for moving machines. The wallet is a
|
||
game-backed facade of C's exact v0 interface, so every proven seam (dig onBuy, shelf buy, cover,
|
||
C's onSell) is unchanged — cash just became authoritative from the save. Buy seams record finds;
|
||
`?classic=1`/`?game=0` never construct the layer — **ZERO Storage calls, instrumented, not trusted**.
|
||
|
||
**SLEEP=TOMORROW:** `sleep()` → day+1 → gig `weekNight` re-keys the EXISTING week schedule → wake at
|
||
DAWN → save. Stock rotation is a runtime day salt on the EXISTING `stk-*` streams (2 marked seam
|
||
lines in C's interiors/layout) + the dig's `binSeed` — **plan generation never sees `day`** (A's #6
|
||
boundary). **Day 1 IS the pre-v7 town** (measured: `?game=0` stock fp == fresh-game day-1 fp), and
|
||
**REAL-sourced crates never rotate** (Monster Robot fp identical across sleeps; the mint crate rotates).
|
||
|
||
**Two amendments filed loudly, with measurement, not slipped in:** gig night = **(day − 1) % 7**, not
|
||
the brief's literal `day % 7` (day starts at 1 — the literal form flips a fresh boot's gigs off night 0,
|
||
breaking the R13 cover/band gates before the player ever sleeps); and sleep saves AFTER incrementing
|
||
(disk always carries the morning you woke into). Fable ratifies or overrules at review.
|
||
|
||
**Numbers:** 7 verify legs green in fresh no-store contexts — $12 dig buy == pricePaid recorded · sell
|
||
halves credit/remove correctly · parody fp `3396372961 → 1818111125` across a sleep, byte-identical
|
||
same-day AND across reboots · corrupt blob = 1 loud reject + stash + clean boot · `qa.sh --strict`
|
||
**6 passed · 0 failed · 0 warn** with the game layer default-ON. Two F-harness bugs caught and fixed
|
||
in the run (the post-sleep CLOSED door; a transform-based fingerprint that was vacuous over batched
|
||
parody stock — hash the VERTEX data). Full record: `LANE_F_NOTES` §30.
|
||
|
||
**Held for the later F session (per the brief):** ledger #5 gates + the sell-card E-key routing.
|
||
|
||
### Wave 3 — THE GATES SESSION: the routing landed, five STRICT gates, the loop closes end-to-end
|
||
|
||
**The held join is in.** E inside a shop now routes: **aimed bin → dig · near counter with sellables →
|
||
C's sell card · forgiving nearest-bin → dig · shelf-buy** (`interior_mode.js`; 3 marked shell seams in
|
||
`index.html` — `procity:sellOpen` unlock + `sellActive` on the Esc/relock guards, the dig's exact
|
||
contract). `onSell` does F's two §9.3 halves in the safe order: `removeFind` first (veto ⇒ nothing
|
||
moves), credit second. **The loop, measured through the real DOM:** dig pull **$9 debited** → walk to
|
||
the counter → E → card → SELL **+$4** (< $9, the clamp) → collection 1→0 → street HUD **$186 ·
|
||
CRATE 0**. One amendment filed with its measurement: the old nearest-bin fallback ignored aim and would
|
||
have swallowed E at any counter near a bin — aimed-raycast now outranks the sell trigger, the
|
||
forgiving pick runs after it; both branches proven live (Moe Vinyl counter: card opens; Kelpie Sound
|
||
bin, wrong aim: dig opens).
|
||
|
||
**The gates (ledger #5), all landed as STRICT suite smokes in `flags_check.py` — no more session-local
|
||
instruments:** (a) **save/load determinism** — scripted buy 2/sell 1/sleep 2× via B's real button →
|
||
export → fresh browser → import **byte-equal** (state 243 B, normalized export 269 B), plan fp
|
||
`0xb1c7843e` **unchanged** across the import, weekNight re-keyed 2; (b) **no-pump v0** — 5 real
|
||
UI round trips **all strictly negative** (Σ −$30), plus **C's negative control**: broke player,
|
||
`wallet.buy` invoked-and-false on the real shelf path ⇒ **recordFind unreachable, zero entry** (dig BUY
|
||
disables outright), with a positive arm proving the control discriminates; (c) **classic purity** —
|
||
Storage-prototype instrument pre-page-script: `?classic=1`/`?game=0` **0 calls, 0 game DOM ids**,
|
||
positive control sees the default boot's `procity-save` read + all 7 ids, plan fp game-on == game-off;
|
||
(d) **rotation determinism over VERTEX DATA** (the wave-3 binding) — day 1/2/3 fps all distinct,
|
||
re-entry byte-identical each day, **cross-boot byte-identical off the persisted save**, `?game=0` ==
|
||
day 1 (88,320 hashed bytes / 20 stock meshes printed — the subject provably present). Harness bug #3
|
||
caught en route: the HUD is hidden in interiors by design; the gate read it on the street.
|
||
**NO TAG** — per the brief, the alpha tags when it plays (Fable's call, likely R31). Full record:
|
||
`LANE_F_NOTES` §30.6.
|
||
|
||
---
|
||
|
||
## Round 26 (v5.0-beta — EVERY CRATE DIFFERENT) — F: the manifest consumed, the tolerance retired
|
||
|
||
**Every crate is different, and F measured it rather than taking G's word.** Not by entering one lucky
|
||
shop — from the indexes of record: **15 keyed crates, 105 pairs, 0 sharing a title**; **0 crates overlap
|
||
the generic v2 pack**; **0 ids reused across crates** (G's sku fix holds at scale — the R24 vacuity dead
|
||
at the source, not 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.** `tier` is the charter's ladder rung, and a mint atlas is *also* rung
|
||
1: still a file, still offline, still deterministic. What differs is where the stock came from, which is
|
||
`sourcing: real|mint`. E built to C's contract over the brief on the principle that the contract's author
|
||
owns the contract's shape. F's gate asserts it: 15/15 declare sourcing, every index agrees with the
|
||
manifest, **a mint crate can never report as real**, and the gate PRINTS it per room — provenance a human
|
||
never reads isn't doing anything (the R24 licence lesson, one round on).
|
||
|
||
**The 404 tolerance is deleted, not tuned.** R24 probed every keyed shop and ate a 404 per atlas-less one;
|
||
F's smoke could only cope by *naming* them — and a tolerance is a confession. F filed the real fix as a
|
||
contract ask instead of inventing it; G built the manifest; **F now consumes it**, and the shell asks for
|
||
nothing that isn't listed. Zero 404s because nothing absent gets asked for, not because the gate stopped
|
||
looking. **And deleting the tolerance immediately caught a leftover**: F had wired the *boot* preload but
|
||
left a second blind probe at the *door*, which 404'd on every keyed-but-unfetchable shop. While the gate
|
||
said "8 probes, expected", a ninth would have been invisible too. A named exception protects new bugs the
|
||
moment the real fix exists.
|
||
|
||
**Gate: `flags_check` GREEN** — distinctness · sourcing (real + mint rooms) · soft fall silent · **ZERO
|
||
404s** · **0 console errors, no attribution exceptions**. Full record: `LANE_F_NOTES` §26.
|
||
|
||
---
|
||
|
||
## Round 24 (v5.0-alpha — THE ALPHA COMPLETION) — **the crate WALKS; F held the tag on A's gate**
|
||
|
||
**THE CRATE WALKS.** Boot `redhill_godverse`, walk down Musgrave Road into **Monster Robot Party**,
|
||
riffle **its own real crate**, buy a real record at its real price. Proven falsifiably: room base =
|
||
`assets/stock_godverse/3962749/` · **titles 120/120 in its atlas, 0/120 in the generic pack** · **the
|
||
GPU samples `stock_godverse/3962749/stock_record_atlas_0*.webp`, 0 textures from the generic pack** ·
|
||
bought *"Abstract Latin Lounge II (Part II)"* **@ $15**, cash **191 → 176**, exact debit, count 0→1 ·
|
||
reboot restores 191/0 (gone-this-session, back next boot). Ladder re-proved: `?noassets` → **zero
|
||
network requests**; keyed-but-atlas-less shop 2289 → 404 → **parody, live**; `newtown_real` → base
|
||
`null`, 350 items — **F's wire is a no-op off the godverse path**. Full record: `LANE_F_NOTES` §24.
|
||
|
||
**F held `v5.0-alpha` anyway. The tree is red — 156,193/156,201 — and none of it is the product.**
|
||
2 reds are goldens G's re-emit legitimately moved (A ran *before* G, so A could not pin them). 6 are
|
||
**conflict #2: A's identity sweep contradicts Fable's own standing Ruling 2**, which *mandates* mixed
|
||
caches ("merge census heroes **WITH** the donor's texture shops"). A cafe from OSM has no GODVERSE
|
||
identity and never will. G measured it, escalated it to "A's call, Fable's ruling", nobody ruled, and
|
||
it arrived at F.
|
||
|
||
**F measured the hazard that gate guards: it does not exist.** 10/10 and 18/18 unique ids, **zero
|
||
duplicates**; and partial keying does not mis-stock — F watched 2289 fall soft to parody in the
|
||
browser, exactly as charter risk #3 prescribes. **A's own two checks disagree** — `validateTownCache`
|
||
reasons it correctly ("Missing = tier 0 (soft). Duplicate = mis-stocked (hard). They are not the same
|
||
failure") and warns; the sweep hard-fails the same condition.
|
||
|
||
**Why F didn't just fix it: F is the party that benefits.** 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. The
|
||
cost of waiting is an hour. Recommended (A's call): the sweep follows A's own validator; A pins the
|
||
four goldens in the same commit; F re-gates and tags. One commit.
|
||
|
||
**Four findings:**
|
||
1. **The honest-gate spec was itself vacuous.** The brief specified id equality — but both packs number
|
||
items **positionally** (`rec_0000…`), so **all 120 atlas ids are also generic-pack ids**. An
|
||
id-equality gate goes green on the generic pack: the exact failure the vacuous-gate law was written
|
||
to stop, reproduced inside its own first application. Titles (0 overlap) and the **texture URL** —
|
||
the file the pixels come from — are what discriminate. F's gate asserts on those.
|
||
2. **F's own selector was lying about the epoch's shop.** `DBG.enterShop(3962749)` → "no shop matching":
|
||
a GODVERSE shop has two ids and F's R22 selector knew only the plan id. Fixed.
|
||
3. **A's uniqueness check is a silent risk-#3 hole** (new — G didn't catch this): on a mixed cache every
|
||
texture shop maps to `undefined` and they **all collapse into one Set entry**, so it fails on the
|
||
undefineds and **cannot detect a real duplicate**. Fix it *over the defined ids* — that makes the
|
||
guard stronger, not weaker.
|
||
4. **The licence flag stopped reaching a human.** E's validator now sees the atlas (R23's blind gate is
|
||
closed ✓) but prints `provenance.licence` (British) while G now emits `license` (American, per C) —
|
||
so `FLAG BEFORE ANY PUBLIC/COMMERCIAL RELEASE`, on real product photos, **silently never prints**
|
||
while the gate says OK. E, one line, G already specified it.
|
||
|
||
---
|
||
|
||
## Round 23 (v5.0-alpha — THE FIRST REAL CRATE) — **F HELD the tag**
|
||
|
||
**`v5.0-alpha` is NOT tagged. The crate is real; it has no street.** G's 120 real records off a real
|
||
shop's POS are in the tree and byte-identically re-bakeable — genuinely the artifact the charter asked
|
||
for. Nothing in the game can reach them. `#7a` isn't a gate that failed, it's a gate that **cannot be
|
||
run**. R18's law: *release rounds don't ship known breaches.* Full record: `LANE_F_NOTES` §23.
|
||
|
||
**F executed the merge that never happened** (charter law #5) — `main` had no `stock_godverse/**` at all,
|
||
so the branch F was asked to tag lacked the round's headline. Review had happened, C had verified
|
||
pre-merge and passed; only the `git merge` was missing. Disjointness verified before merging, not
|
||
assumed. Zero conflicts → `43b035a`.
|
||
|
||
**What passed — everything except the crate.** `qa.sh --strict --matrix` **7 passed · 0 failed · 0 warn**;
|
||
**MATRIX GREEN — 10 towns × 7 gates**; selfcheck **ALL GREEN 152,015/152,015** (was 161,300 — E retired
|
||
toowoomba); flags harness green, so the `?classic=1` covenant still holds through the merge. Tier 0
|
||
`?noassets` → **zero fetches, zero console errors** (the offline law holds); tier 0 default boot on the
|
||
godverse town; tier 1 (v2 town-wide) `?stock=real`. **Tier 1 (v5, per-shop) — the round's whole
|
||
deliverable — is never fetched.** A, D, E and C's work is sound; only the rung this round was built to
|
||
add is unattached.
|
||
|
||
**Three findings, in order of how much they cost:**
|
||
1. **The atlas contract contradicts itself.** `LANE_C_PUB:271` publishes `stock_shop_<id>_index.json`;
|
||
C's own loader at `stockpack.js:25` fetches `${base}stock_${type}_index.json`. **Incompatible.** G
|
||
built runtime-compatible (so C's base-override check passed); E built contract-compatible (so E's
|
||
validator can't see it). **Nobody was wrong — the contract was.** F recommends amending to G's shape;
|
||
it's the only one the runtime can load.
|
||
2. **The gate built to catch that was blind and green.** `validate_atlas.py` globs a filename that can't
|
||
exist at runtime, finds nothing, and `return 0` — so it passes whether the atlas is absent, misnamed,
|
||
or perfect. It had never seen one until F pointed it at G's by hand, which then surfaced *deeper* drift
|
||
(E's code reads provenance top-level, E's own docstring says nested, G nested it; `licence` vs
|
||
`license`; a ceiling stale by one round). *A gate that returns 0 on an empty set is not a gate.*
|
||
3. **`#7a` would have passed vacuously.** F walked into The Vintage Record (real shop, real road) and the
|
||
crates filled with **real covers — from E's 350-item town-wide v2 pack**, not the crate. Zero requests
|
||
to `stock_godverse/`, ever. Eyeballed as written, the gate goes green and proves nothing.
|
||
|
||
**R24 order — three gaps, none F-only:** **A+G** land a `godverse_id` (verified: *every* godverse shop
|
||
reports `godverse_id: null`; the schema has no such field, and C's rule is "C does not invent it") →
|
||
**E** builds `redhill_real` (one Overpass fetch — E's file, outward-facing; **not F's to invent**) →
|
||
**C** keys the pack cache by `type+base` (today a cache hit returns before `base` is read, so two stocked
|
||
shops collide *and fail-soft breaks*) → **F** passes the per-shop base and re-gates. F deliberately did
|
||
**not** build its half: untestable with no street and no identity field. Filed, not guessed.
|
||
|
||
**Also:** A's spacing warn earned on day one — with toowoomba retired, **ballarat now warns at 255 m**.
|
||
The thin tail has a second member.
|
||
|
||
---
|
||
|
||
## Round 22 (v4.0 — THE EPOCH CLOSE) — F closed it
|
||
|
||
- **The blocking gate CLOSED.** F's R21 re-measure made the tri diet blocking; B diagnosed, the driver
|
||
traced A→**E**, E fixed it, F re-measured: katoomba `venue_night` **221,935 → 108,365 (−51%)**, worst town
|
||
124,682 — the 200k stress edge is closed. Selfcheck **161,300/161,300** (A's 36 goldens + 23 towns).
|
||
- **`DBG.enterShop(selector)`** (Lane G's ask) — id · type · exact name · substring, most-specific-first.
|
||
- **The release tour** → [`docs/shots/release_v4/`](docs/shots/release_v4/): Fremantle hero · Fitzroy mecca ·
|
||
Toowoomba thin tail · synthetic control · `?classic=1` covenant. All posed through F's R21 cluster
|
||
primitive — which earned itself twice: the first cut posed via `street_noon` and **the mecca came out as
|
||
bare bitumen**; re-posed through the cluster, Fitzroy reads.
|
||
- **⚠ Honest finding (filed v4.x):** the thin tail does **not** prove the floor holds — Toowoomba reads as an
|
||
empty road. The floor is a **ratio**, not a count: 0.008 shops/edge vs the heroes' 0.06–0.08. The frame
|
||
ships labelled honestly rather than swapped for a flattering town.
|
||
- Docs (README v4 "the towns are the product", notes §22), `qa.sh --strict --matrix` green, tag **`v4.0`**.
|
||
|
||
---
|
||
|
||
## Round 20–21 (v4.0-beta THE FIVE TOWNS) — F never ran R20; closed R21
|
||
|
||
**R20: F never ran** (no gate, no tag) — the honest record is in LANE_F_NOTES §20. Unlike R18 (where F ran
|
||
and *held* on evidence), the slot simply didn't execute; the round also left C's door-soak, B's selector
|
||
index, and D's relocation finding open. R21 closed all four.
|
||
|
||
**R21: the beta gate.** Waves [A,B,E] → [C,D] → [F]. All five lanes in; F gated:
|
||
- **Matrix 10 towns × 7 gates** — boot/determ/district/roads/**tram**/noassets green on all ten; selfcheck
|
||
154141/154141; `qa.sh --strict` GREEN.
|
||
- **Tram dimension (new)** — B's R20 ruling verified exactly: Newtown runs 16 stops (was 149 — B's phantom
|
||
fix), Castlemaine 10; Katoomba/Fremantle/Bendigo **fenced** at 3/2/1 shops fronted. Needed a new seam:
|
||
the shell now exposes `window.PROCITY.tram` (B built `routeInfo` for F's smoke; nothing published it).
|
||
- **⚠ venue_night re-measure — recorded, premise falsified.** A's cap worked (posters 438→111) but the
|
||
density widening ate it: katoomba **200,965 (+965) → 221,935 (+21,935, +11%)**; bendigo 201k. Draws fine
|
||
(151/300). Filed as R22's first item (the tri-diet backlog already lives there).
|
||
- **Selector ruling** — always in the HUD (the towns *are* the product), + the corollary that F's harness
|
||
must hide the HUD for beauty shots (the R19 alpha shot's selector-in-frame was F's own gap).
|
||
- **Beta money shot** — [Fremantle's real street at golden hour](docs/shots/v4_beta/fremantle_mainstreet.png):
|
||
a corridor of shopfronts, footpaths, a crossing, and a genuinely busy crowd. Tag **`v4.0-beta`**.
|
||
|
||
---
|
||
|
||
## Round 18–19 (v4.0-alpha REAL ROADS) — F held R18 correctly, then closed R19
|
||
|
||
**R18: F did NOT run — the gate would have failed by design, so F held the tag** and filed the failure list
|
||
(the alpha's charter deliverable). Real roads landed structurally but the selfcheck was RED (katoomba golden
|
||
unpinned + poster-kerb breach on all 5 real towns) and the town was fragmented (20 shops over 799 edges = an
|
||
empty field). Classic/default stayed frozen — the alpha changed nothing outside the cache path. Fable's
|
||
verdict agreed: "F never ran — correctly."
|
||
|
||
**R19: the close.** A resolved the findings (fragmentation cull/join, poster clamp, goldens pinned), D
|
||
re-measured, B shipped the town selector. F ran:
|
||
- **B's `BIG_CITY` one-liner** (`e6fcb7b`, early): `shops>120 || edges>200` — real Katoomba's 799-edge graph
|
||
now streams like a big city. **280k → ~12k tris.** B proved it; F owns index.html.
|
||
- **The alpha gate** — `town_matrix.py` grew a `roads` dimension (a v2 cache must build the REAL graph,
|
||
`mode='roads'` via the plan_osm report sink); classic/default proven untouched; budgets in the green.
|
||
- **The money shot** — [Katoomba's real main street](docs/shots/v4_alpha/katoomba_mainstreet.png): asphalt +
|
||
lane markings, footpaths, a pedestrian crossing, a walking crowd, gig posters, B's town selector in-frame.
|
||
**PROCITY's look on real bones.** Tag **`v4.0-alpha`**. (Honest note: 19 real shops sit ~1/edge — a real
|
||
*street*, not yet a dense strip; density is a v4.0-beta concern.)
|
||
|
||
---
|
||
|
||
## Round 17 (v3.2 THE TAIL + THE SCOUT) — F: the town matrix + the close
|
||
|
||
The tail drained (drummer lean + bench-sit, wind sway, backline verify, --verify fix) and E's real-map scout
|
||
landed (5 real AU town caches + A's cache contract + hardened plan_osm). **F closed it.**
|
||
|
||
- **Real towns boot in the engine** — the barrel re-exports `registerTownCache`; the shell fetches +
|
||
registers `assets/towns/<key>.json` before the plan generates (fail-soft; opt-in; never on default/classic).
|
||
`?plansrc=osm&town=bendigo_real` → **Bendigo, 9 shops, 3-venue district, 0 errors**.
|
||
- **Town-matrix smoke** (`tools/qa/town_matrix.py`, `qa.sh --matrix`) — **10 towns × 5 gates all green**:
|
||
2 synthetic + 3 fixtures + 5 real caches, each boot / deterministic / in-budget (69–216 draws) / a 3-venue
|
||
district / silent under `?noassets`. Castlemaine at the 6-shop floor still lands a district. **The existing
|
||
CityPlan contract eats real Australian geography** — the v4 = THE REAL MAP evidence.
|
||
- Close: `qa.sh --strict --soak` green, docs (LANE_F_NOTES §17, README), tag **`v3.2`**.
|
||
|
||
---
|
||
|
||
## Round 16 (v3.1 THE FLIP) — F bookends; the town shows its best by default
|
||
|
||
The gig layer soaked through three v3 stages, so v3.1 flips it (and the mature v2 flags) ON by default.
|
||
F owns the flip; A–E landed their polish against it (seated drummer, town-wide pools, backline, corner-poster).
|
||
|
||
**Opening commit `a4946b7` (the round's foundation):**
|
||
- **The flip** — `index.html` `CLASSIC` + `flagOn()`: gigs/weather/tram default-on, `loadPedFleet({sit:!CLASSIC})`,
|
||
`window.PROCITY.flags`; `buildings.js` winmap default-on + classic-aware. **`?classic=1`** forces the exact v2 boot.
|
||
- **The covenant moved** — `flags_off_regression → classic_regression` (byte-identical `0x3fa36874`); a new
|
||
`default_boot_gate` (all four flags on, gig layer present, gig golden `0xb1d48ea1`, budget); every gig smoke
|
||
re-pointed to the DEFAULT boot; qa.sh gate renamed.
|
||
- **Seated-stature gate** (ledger #3) — `interior_scale_check.py` + `flags_check.py` accept `procitySeated` figs
|
||
at [0.9,1.5] m; the drummer passes at **1.32 m**.
|
||
|
||
Verified: `?classic` byte-identical (0x3fa36874, gig absent); default = full town (3 venues / 3 genres, gig golden,
|
||
112 draws @ venue block); classic/default/district/continuity/cover all green. **Close: re-baseline, re-shot tour
|
||
→ `release_v3_1/` + a `?classic=1` before/after, docs (CITY_SPEC v3.1 flip+classic law, README flags table,
|
||
LANE_F_NOTES §16), tag `v3.1`.**
|
||
|
||
---
|
||
|
||
## Round 15 (v3.0 RELEASE CLOSE, `?gigs=1`) — F ran last; the release is closed
|
||
|
||
R14 landed A–E's hardening but **F never ran**, so v3.0 stayed untagged. R15 = the close. All deps were in
|
||
before F ran (A's poster flip + new golden `0x4f4a549d`, D's amp + R14 continuity seam, B/C verifies). The
|
||
six-item brief, each committed atomically:
|
||
|
||
- **Continuity wiring + smoke** (`687d23f`) — the ped you followed is in the crowd: `crew.spawn({roster})` +
|
||
the queue `onAdmit → recordVenueEntry` relay; `smoke_continuity` asserts the seam on identities + settled tris.
|
||
- **Alias deletion** (`146b5f3`) — the R12 scalar alias retired; every read keyed by `venueShopId`; tools + spec migrated.
|
||
- **Full-week soak** (`edc0c47`, `tools/qa/week_soak.py`) — 7 nights × all venues: cover/roster per night, leak-free (plateau), 28 cycles.
|
||
- **Money-shot tour** (`6a35b29`, `tools/qa/tour_shots.py`) — `docs/shots/release_v3/` + contact.html; hero `queue_night` shows A's now-visible poster; settled measurements.
|
||
- **Docs** — README v3 section, LANE_F_NOTES §14/§15, this note, five freezes confirmed.
|
||
- **Acceptance + tag** — `qa.sh --strict` 6/6 + continuity smoke + week soak green → tag **`v3.0`**.
|
||
|
||
**Ledger #5 vindicated C:** the gig interior settles to **~182k tris** after the 5 instrument GLBs load (not
|
||
the ~34k measured pre-load); the interior gate is **draws** (≤56), so no budget violation — the tour + smoke
|
||
now measure settled. Full runbook: [`LANE_F_NOTES.md §15`](docs/LANES/LANE_F_NOTES.md).
|
||
|
||
---
|
||
|
||
## Round 13 (v3.0-beta — THE DISTRICT, `?gigs=1`) — WIRED + VERIFIED + TAGGED
|
||
|
||
A–E landed, then D (4-piece + instruments + queue + RY fix). F wired the district together and it fires.
|
||
**Seed 20261990 → "Boolarra Heads": 3 venues, 2 playing tonight — The Exchange Hotel (pub, `gig-pubrock`)
|
||
+ an RSL (`gig-covers`), one dark (reads true).** All 7 F tasks done, `qa.sh --strict` GREEN (6/6, 0 warn),
|
||
tag **`v3.0-beta`**. Shots: `docs/shots/laneF/r13_v3beta_gig_interior.png` + `r13_v3beta_venue_night.png`.
|
||
|
||
**What F did (ROUND13 §Lane F):**
|
||
1. **Per-venue state machine** — `gig_state.js` rewritten: one latch per venue off the shared
|
||
`procity:segment` spine, keyed by `venueShopId`; all three hard-won R12 laws preserved. New runtime
|
||
surface `window.PROCITY.gigs.byVenue` + per-venue getters (`onOf/openOf/gigOf/coverOf/paidOf…`); the
|
||
R12 single-venue alias kept one round. B's `venue.js`/`audio.js` + D's `setGig` read it.
|
||
2. **Walla + applause** — the crowd bed `crowd-walla` rides the interior tone layer under the live band bed
|
||
while `on`; an `applause` sting fires once on the `on→done` edge while inside. Verified: `iMusic=gig-pubrock`,
|
||
`iTone=crowd-walla`.
|
||
3. **Cover per venue** — the door seam keys off the shop entered, so a night at two venues is two covers.
|
||
4. **Debt #1 closed — was a LIVE BUG** — E renamed `pubrock-live→gig-pubrock`, so F's R12 `GIG_BED` bridge
|
||
(rewrote `gig-pubrock→pubrock-live`) pointed at a dead key → the band would have mimed in silence.
|
||
Deleted; the room's `audio.gigKey` (already the manifest key) feeds the engine directly.
|
||
5. **Stature gate** — `interior_scale_check.py` now measures stature (crown−foot, platform-independent),
|
||
ceiling-clip assert kept as its own line (debt #6).
|
||
6. **District smokes** — `smoke_gigs`: band→4, a per-venue loop (every gigKey resolves across 3 genres,
|
||
crowd ≤ its own watchPoints, no-giants), street budget at the busiest venue block, the queue assert,
|
||
plus the R12 determinism/cover/noassets/flags-off arms. All green.
|
||
7. **The outdoor queue** — D shipped `VenueQueue` and left the street-side lifecycle to F (its documented
|
||
seam): F wires one queue per venue in the shell (spawn at doors off B's `queueZoneFor`, drive each frame,
|
||
dispose by `done`), exposed as `queueCountOf(id)`. Verified: 2 in line at doors → 0 by done.
|
||
|
||
**Numbers (seed 20261990):** gig interior 62 draws / 34k tris (≤350) · street @ venue block 107 draws /
|
||
47k tris (≤300/200k) · band+crowd stature 1.61–1.81 m under a 4 m ceiling · cover $10 debited-once/re-entry-
|
||
free/broke-knockback + free night (seed 1234) · flags-off fingerprint 0x3fa36874 unchanged, gig layer wholly
|
||
absent. Full runbook + the R13 debt-ledger closeout in [`LANE_F_NOTES.md §13`](docs/LANES/LANE_F_NOTES.md).
|
||
|
||
---
|
||
|
||
## Round 12 (v3.0-alpha — the gig layer, `?gigs=1`) — WIRED + VERIFIED
|
||
|
||
A–E all landed before me, so F did what F does: turned five parts into one Friday night. **Seed
|
||
20261990 → "Boolarra Heads", venue = The Thornbury Hotel, tonight = SCREAMING UTES, $10 on the door.**
|
||
Acceptance shot: `docs/shots/laneF/r12_v3alpha_gig.png`.
|
||
|
||
| F task (ROUND12 §Lane F) | result |
|
||
|---|---|
|
||
| **1 — gig state machine** | ✅ new F-owned `web/js/world/gig_state.js`. `quiet → doors (DUSK) → on (NIGHT) → done (DAWN) → quiet`, verified forwards AND backwards on the hand-driven clock. On `window.PROCITY.gigs`. |
|
||
| **2 — `?gigs=1` seam + flags-off** | ✅ shell bootstrap fetches `custom_bands.json` (no fetch when flag-off or `?noassets=1`) → `generatePlanFor(seed, src, {gigs:true, customBands})`. Flags-off verified **wholly absent**: no state machine, no `plan.gigs`/`posters`, no pub, no venue geometry, zero band-file fetches, golden `0x3fa36874` unmoved. |
|
||
| **3 — cover charge** | ✅ all three halves of John's ruling verified live: **$10 debited once** (191→181, `cover — Screaming Utes` in the wallet), **re-entry the same night free** (181→181), **broke on a new night → polite knockback, no entry**. Free nights (seed 1234) walk straight in, wallet untouched. Zero new UI — rides the R8 buy seam. |
|
||
| **4 — audio wiring** | ✅ gig bed inside via C's `gigKey`, B's muffled spill outside reads `PROCITY.gigs.state`. **Found + bridged a real C↔E key mismatch — see below.** |
|
||
| **5 — smokes** | ✅ `smoke_gigs` in `flags_check.py`: schedule determinism · band==3 · crowd ≤ watchPoints · dance mix · bed resolves · ≤350 draws · no-giants · `?noassets=1`+`?gigs=1`. Plus the v3 arm of the flags-off regression. |
|
||
| **6 — shot + tag** | ✅ shot captured. Tag pending the harness run (below). |
|
||
|
||
**Numbers.** Gig-night interior **42–50 draws** with the rig fleet, **171 draws** on `?noassets=1`
|
||
placeholders (worst; budget ≤350) · ~40k tris · street at the venue 69 draws/29.6k tris · **leak-free**
|
||
(geo 66→66, tex 63→63 over 4 gig cycles) · band 3 + crowd 8/8 watch points, **3 dancing / 5 standing**
|
||
· band stature 1.68–1.77 m, crowd 1.60–1.77 m, tallest crown 2.09 m under a 4.0 m ceiling.
|
||
|
||
**John's drop-in works:** 7 of your 10 hand-written names fill all 7 nights (Screaming Utes, The Dead
|
||
Ringers, The Rissoles, The Flannel Shirts, Boganvillea, Chunderbolt, Amyl & The Servos). Covers rolled
|
||
`10,0,0,3,2,0,9` — 3 free of 7. Under `?noassets=1` it falls back to the pure generator ("The Wombatts")
|
||
with zero fetch, exactly as A specced.
|
||
|
||
### Three bugs F found in its own work (all caught by verification, fixed, re-verified)
|
||
1. **The state machine only advanced when polled.** Driving NIGHT→DAWN→MORNING→NIGHT with nothing
|
||
reading in between (player inside the pub, audio muted) **skipped the night roll** — the cover was
|
||
never due again. Now it rides `procity:segment`, which `lighting.js` fires on every change.
|
||
2. **Rewinding the clock left `done` stuck** on a midday town (the latch only cleared at MORNING).
|
||
3. **The night only rolled if the clock passed through DAWN** — so a *jump* from NIGHT to MORNING left
|
||
last night's paid stamp valid and **a broke punter walked straight in**. Caught by my own smoke, not
|
||
by hand: stepping with `[`/`]` always crosses DAWN, so only jumps (DBG/shots/soak) exposed it. The
|
||
roll now keys on landing in the daytime, not on having observed one particular segment.
|
||
|
||
Also worth recording, because it nearly shipped a wrong gate: **"no giants" must measure stature, not
|
||
world crown.** The band stands on C's 0.32 m deck, so a perfectly human 1.73 m guitarist has a 2.05 m
|
||
world crown and the naive R10 check **failed a correct band**. The smoke now measures `crown − base.y`
|
||
and keeps the world-crown assert only for "never clips the ceiling" — the real R10 failure mode.
|
||
|
||
### Three findings for other lanes (detail + rulings in `LANE_F_NOTES.md` §12)
|
||
1. **⚠ C ↔ E: the gig bed key never met.** C emits `gig-pubrock` (and asked E for that name); E shipped
|
||
**`pubrock-live`**. Unresolved, the pub plays to a **silent room** and — because the audio law fails
|
||
soft — *nothing errors*, you just get a band miming. **F bridged it at the glue** so alpha ships.
|
||
This is the R1 facade-skin filename trap again. → **needs a named contract line in CITY_SPEC.**
|
||
2. **⚠ D + E: E's 5 instrument GLBs landed; nothing wires them.** D holds primitives and published
|
||
`instrumentFor` as the seam "when they land" — they've landed, nobody owns the join. Non-blocking
|
||
(asset law; reads fine in the shot). F did **not** silently do it inside D's rig. → R13.
|
||
3. **⚠ spec ↔ C: the band has no drummer.** ROUND12 §D.1 says "drummer seated bob" and E built a merged
|
||
kit; C's `bandPoses` are guitar/**vocal**/bass and the drum riser is set-dressing. D flagged it and
|
||
built to C's poses. A spec/implementation divergence **no gate can catch** → Fable/John to rule.
|
||
|
||
### Harness note
|
||
This MacBook had **no Chromium/Chrome**, so the Playwright gates couldn't run locally; John authorised
|
||
the install (done — 93.5 MB, Playwright CDN). `/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 — but **node isn't on the non-interactive ssh PATH**: use
|
||
`env PATH=/opt/homebrew/bin:$PATH tools/qa.sh --strict` or every node gate reports a bogus FAIL.
|
||
|
||
---
|
||
|
||
## Round 11 (the audio round) — DONE ✅ (v2.1)
|
||
|
||
John greenlit "generate freely". E generated the pack (23 assets, `web/assets/audio/`, 8.7 MB ≤ 25),
|
||
B built the WebAudio engine (`audio.js`, self-unlocking, self-ticking), C added `room.audio =
|
||
{musicKey,toneKey}` to buildInterior. **Lane F wired + verified + tagged v2.1:**
|
||
|
||
| task | result |
|
||
|---|---|
|
||
| **F1 — interior beds** | ✅ `interior_mode` enter → `PROCITY.audio.playInterior(current.audio)` (fade in), exit → `stopInterior()` (0.7 s fade). Guarded/no-op when muted/noassets/pre-gesture. 2 lines, the seam B/C designed for. |
|
||
| **F2 — audio smokes** | ✅ new `smoke_audio` in the flags harness (fresh Chromium): **pre-gesture silence** (ctx not unlocked), **0 boot errors**, **?mute=1** muted, **?noassets=1 zero audio fetches**, **enter/exit leak-free** (12 cycles, 8 shops with a music bed, live AudioNodes bounded at 5, made 42). Caught + fixed my own gesture-via-mouse-click tripping PointerLockControls in headless → gesture via keyboard. |
|
||
| **F3 — soak + tag** | ✅ soak with audio on **PASS** (heap 48 MB stable, 0 errors, no drift). `qa.sh --strict` **GREEN 6/6**. **v2.1 tagged + pushed.** |
|
||
|
||
**Cross-lane (F's own gate, noted to B):** B's `audio.js:143,145` use `Math.random()` for footstep SFX
|
||
variant + gain jitter — legitimate transient runtime FX (which shop plays which bed is seeded upstream
|
||
in `interiors.js`), but it tripped F's generation-determinism `Math.random` ban. F **scoped the ban
|
||
correctly**: `web/js/world/audio.js` is runtime *playback*, not generation → exempted in
|
||
`scaffold_check.mjs` (B may instead add a `// cosmetic` marker; either is fine).
|
||
|
||
---
|
||
|
||
## Round 10 (fix the R9 giant-rig blocker, re-shoot, re-tag) — verify + ship
|
||
|
||
R9 shipped a blocking regression: interior rig figures came out ~2× room scale (keeper head 3.83 m in
|
||
a 3.4 m room) because `rigs.js buildFigure` normalized off the head bone's **bind-pose** Y. **Lane D
|
||
fixed it** (R10, commit `3432fcc`): normalize by the feet→crown **span**, plus a new `qa.sh --strict`
|
||
no-giants gate (`interior_scale_check.py`). Lane F verify + re-shoot + soak + re-tag:
|
||
|
||
| task | result |
|
||
|---|---|
|
||
| **F1 — verify the fix** | ✅ `qa.sh --strict` **GREEN 6/6** incl. the new scale gate: **24 interior figs / 6 shops all crown ∈ [1.4,2.0] m and under-ceiling** (Second Time Redfern, the giant scene: 1.66–1.86 m). Live-measured too — the 3.83 m I first saw was a **stale ES-module cache in a day-old browser tab**, not a regression (served + fresh-chromium code both correct). |
|
||
| **F2 — re-shoot** | ✅ `browsers.png` re-shot: browser at the rack + keeper at the counter, **human-sized** (*figures human-sized vs door/fittings: yes*). `night_crowd.png` re-framed to a readable "lit VIDEO REGAL amid CLOSED town" — **see the honest note below.** |
|
||
| **F3 — soak** | ✅ full-default soak **PASS**: 37 chunks, 20 shops, **heap 54 MB stable (no perf drift** from the keeper fix — it's a build-time scale calc), draws 248, **0 errors**. Tri budget still ~327k > 200k (pre-existing rig-model item, D/E decimation — not an R10 change, not the blocker). |
|
||
| **F4/6 — docs** | ✅ tour README updated with stable v2 filenames + captions; this addendum is the R10 release note. |
|
||
| **F5 — tag** | ⏸ `v2.0` annotated tag staged on the fixed commit; **holding the push for John** given the night_crowd finding (a public release + his flagged money shot). |
|
||
|
||
**Honest finding — `night_crowd` has no crowd, and it can't without new work.** John flagged it as
|
||
near-black/empty. Re-framed it readable (clean night, tight on the neon). But a visible *street* crowd
|
||
does not form: Lane D's night patronage parks ducked-in patrons **hidden inside** the shop (they are
|
||
the interior browsers in `browsers.png`), and seed 20261990's video shop is a **low-traffic corner**
|
||
(tested default streamed roster + patronage-on at night: **0 patronage, 0 visible peds** at the shop).
|
||
This is not a framing bug and not (clearly) a regression — it's that the "night crowd" was always the
|
||
*interior* presence. A visible outdoor queue is a **v3 Lane-D mechanic** (loiter-outside-the-openLate-
|
||
shop). The ~20 fps in night captures is a **headless software-GL artifact** (128 draws / 65k tris is light).
|
||
→ Flagged for D: confirm night patronage at the openLate shop still behaves as the v2.0-rc notes claim.
|
||
|
||
---
|
||
|
||
## Round 9 (the v2.0 ship round) — DONE (v2.0 tag pulled, re-tagged in R10)
|
||
|
||
All lanes landed (C browse points + buy-anywhere, D browser rigs, A CITY_SPEC freeze + closing-time
|
||
ruling, E VRAM audit + half-res book/toy atlases, B tram + tour bookmarks). F wired the one cross-lane
|
||
seam, graduated the harness, shot the tour, soaked, and tagged the epoch.
|
||
|
||
| task | status |
|
||
|---|---|
|
||
| **F1 — interior presence (C→D→F)** | ✅ `interior_mode` reads `occupancyOf(shop.id)` (closure to `citizens`) and spawns browser rigs at C's `room.browsePoints`, one per patronage occupant, **cap 3**; freed by `exit()`'s `keepers.disposeAll`. Door points carry `shopId`. Verified: enter occupied "Second Time Redfern" → 1 browser == `min(occ,3)`, disposed clean, 0 errors. |
|
||
| **also wired** | ✅ **Tram** (`?tram=1`, `createTram`+update). **Buy-anywhere** (C2): E at a book-spine/toy-box `buyMesh` buys over the wallet (`collapseBuyItem`+toast); preload book+toy packs. Verified: bought a book spine, cash 191→177. |
|
||
| **F2 — smokes to strict** | ✅ buy-v0 / patronage / book-toy / katoomba **graduated warn→STRICT**. New warn smokes: interior-presence, shelf-buy, tram. Fixed the book-stock check to count `isStock OR buyMesh` (C2 tags spines `buyMesh`). `qa.sh --strict` GREEN 5/5, harness 0 fails / 0 warns. |
|
||
| **F3 — tour + soaks** | ✅ `tools/v2_tour.py` → **11 shots** (`docs/shots/v2_tour/`, un-letterboxed) incl. the **rain-verandah money shot** (rain + winmap-lit open video shop + CLOSED dark neighbours at night) + browsers + dig-real + katoomba + tram_stop + contact sheet. **Both soaks PASS** (full-default + all-on: 37 chunks, 20 shops, heap stable, 0 errors, leak-free). |
|
||
| **F4 — docs freeze + tag v2.0** | ✅ Flags table finalized (below); **`v2.0` tagged** on a green tree. |
|
||
| **Marshal** | C1 (the round's SPOF) landed early; all lanes committed; no slips. |
|
||
|
||
### v2.0 flags — FINAL
|
||
| flag | owner | status | default |
|
||
|---|---|---|---|
|
||
| roster | D | **graduated → DEFAULT-ON** (R7 flip); `?roster=v1` escape hatch | on |
|
||
| patronage | D | default-on (`?patronage=0` off) | on |
|
||
| `?dig=1` | C | opt-in (crate riffle) | off |
|
||
| `?winmap=1` | B | opt-in (parallax window glass) | off |
|
||
| `?stock=real` | C/E | opt-in (real GODVERSE covers/spines/boxes + buy-anywhere) | off |
|
||
| `?weather=1` | B | opt-in (seeded rain/overcast/clear; `?weather=rain` forces) | off |
|
||
| `?plansrc=osm[&town=]` | A | opt-in (real-OSM towns: melbourne, katoomba) | off (synthetic) |
|
||
| `?tram=1` | B | opt-in (spine tram loop) | off |
|
||
| `?noassets=1` | — | opt-in (primitive-only, zero-network) | off |
|
||
Determinism goldens frozen: synthetic `0x3fa36874` · osm/melbourne `0x34cfdec0` · osm/katoomba `0x0f652510`.
|
||
|
||
---
|
||
|
||
## Round 8 (catch-up + the town comes alive → v2.0-rc) — DONE ✅
|
||
|
||
All lanes landed (E packs, B weather, A 2nd town, C buy, D patronage). F wired the shell seams,
|
||
graduated the harness, tagged.
|
||
|
||
| task | status |
|
||
|---|---|
|
||
| **shell wiring (F owns index.html)** | ✅ **A** `&town=<key>` → `generatePlanFor(seed,'osm',{town})` (Katoomba, 19 shops, verified). **B** weather (3-line contract): `createWeather` on `?weather=1`, `PROCITY.weather={state,intensity}` ({clear,0} when off for D), `weather.update` in loop. **C** session `wallet` (createWallet(seed)) bound to the dig BUY (`getCash`/`onBuy` in interior_mode). **D** patronage: shop door-points per chunk → `citizens.setShops`, `?patronage=0` off-switch, `citizens.setWeather` each frame. All boot clean (0 console errors, settled). |
|
||
| **F1 — smoke graduations + additions** | ✅ `stock=real` + `weather=rain` **graduated warn→STRICT**. New warn smokes: **buy-v0** (dig BUY→wallet: cash 191→179, bag 0→1), **patronage** (7 peds visit shop doors/~18s), **book/toy stock** (6/11 real atlas meshes). Katoomba golden `0x0f652510` pinned in the determinism gate (fixed a hex zero-pad bug it caught). |
|
||
| **F2 — patronage baseline** | ✅ No re-pin needed — flags-off regression held (patronage nets *fewer* draws, inside tolerance; ≤300 cap + draws-±40% both green). |
|
||
| **F3 — tag v2.0-rc** | ✅ **TAGGED.** qa.sh --strict GREEN 5/5 (harness strict). |
|
||
| **Marshal** | All lanes committed round-8; no slips this round. |
|
||
|
||
---
|
||
|
||
## Round 7 (the flip round → v2.0-beta) — DONE ✅ (v2.0-beta tagged)
|
||
|
||
Critical path resolved: E1 ped-merge (`447188a`), E2 stock pack (`0c12dac`), **D's GO** (`43edabf`),
|
||
C1 `?stock=real` (`3724a65`) all landed. F executes flip → strict harness → tag.
|
||
|
||
| task | status |
|
||
|---|---|
|
||
| **F1 — roster flip (prime-law amendment)** | ✅ **DONE + verified.** `index.html`: streamed roster is now the **default** (`chunkStream:{radius:2}` per D's spec); `?roster=v1` = escape hatch (fixed roster); `?roster=stream` still accepted (no-op). Hours-aware `setNightLivelyChunks` on the open-late block. **Same commit** re-pins the flags-off baseline: **old (163 draws/19k tris, roster-off) → new (164 draws/~66k tris, roster-on)** — draws barely move (E1's merge = ~1 draw/rig, the whole point), tris = the crowd (D worst-view 65,899; drift-WARN now draws-only, tris under the hard cap). **Plan goldens UNCHANGED (`0x3fa36874`).** Also fixed the harness's roster probe (was reading a nonexistent prop → blind) + flipped the assertion to require streamMode ON + added an escape-hatch check. Verified: default boot streamMode:true (146–150 active), `?roster=v1` streamMode:false (96), both render, 0 errors, ≤300 draws, harness GREEN. |
|
||
| **F2 — harness to strict + new-flag smokes** | ✅ **DONE + verified.** `qa.sh` flags gate `warn_gate → run_gate` — the harness (flags-off regression + 4 existing flags + all-on combo) is now **STRICT** (a FAIL fails qa). Wired **`?stock=real`** (F owned the seam): shell preloads the record pack + passes `stockReal`; `interior_mode` builds `makeStockAdapter(getStockPack(shop.type))` and passes it to **both** `buildInterior` (static crate sleeves) and `dig.open` (riffle) — fail-soft to parody if the pack's absent. Added `stock=real` + `weather=1` smokes at **warn-level** (decision #4). Verified: crates show **real GODVERSE covers** (smoke: 7 real atlas sleeve meshes, non-parody batched); weather auto-skips (Lane B not landed); `qa.sh --strict` GREEN (5 gates). |
|
||
| **F3 — tag v2.0-beta** | ✅ **TAGGED** (no-hostage rule). Criteria met: roster default-on + escape hatch ✓, harness strict+green ✓, `?stock=real` real sleeves ✓, qa strict green ✓. |
|
||
| **⚠ schedule-harness flag** | **B (`?weather=1`) and A (osm parity + 2nd town) did NOT land round-7** — no commits, tree clean. Both are additive (not headline keys); tagged v2.0-beta without them per the no-hostage rule. Weather smoke is in the harness ready to graduate the moment B ships. Flagging for Fable: two lanes slipped this round. |
|
||
|
||
---
|
||
|
||
## Round 6 (finish v2 wiring + pay review debts → v2.0-alpha) — DONE ✅ (v2.0-alpha tagged)
|
||
|
||
Sequencing (Fable): **F1 enforcement harness lands FIRST** (protects the round), then F re-smokes as
|
||
lanes land, adds C's ≤350-draw interior assertion when C1 lands, extends the determinism gate if A's
|
||
`?plansrc` lands, and tags `v2.0-alpha` when all-green (3 flags acceptable if A no-shows).
|
||
|
||
| task | status |
|
||
|---|---|
|
||
| **F1 enforcement harness** | ✅ **DONE + wired.** `tools/flags_check.py` (self-contained server, headless, ~90s): **flags-off regression** (golden hash `0x3fa36874` + subsystems-inactive + ≤300/≤200k budget + ±40% snapshot + 0 errors) · **per-flag smokes** (winmap/dig/roster: cross ≥2 chunks + enter interior; dig opens+closes a riffle) · **all-on combo** (flag-composition law, decision #4). Wired into `qa.sh` as a **warn-level gate** (`--no-flags` skips fast; auto-skips without the Playwright venv). Current: **all smokes GREEN**, regression clean. |
|
||
| **F2 plansrc** | ✅ **DONE + verified.** A landed the full seam (`52eb109`). Wired the shell bootstrap (`index.html`, F-owned): `?plansrc=osm` → `citygen.generatePlanFor(seed,'osm')` → real-data town **"Melbourne", 95 shops** (synthetic default = "Boolarra Heads" unchanged). Pinned **both goldens** in `scaffold_check.mjs`: synthetic `0x3fa36874`, **osm `0x34cfdec0`** (deterministic, 2 runs). Harness auto-detects plansrc + smokes it (enters OSM shop "Mui Karaoke Rooms") + includes it in the all-on combo. Also restored `binUnderAim`'s **nearest-bin fallback** (matches C's test page) so E riffles a nearby bin without perfect aim / before the bin GLB finishes loading. Synthetic golden frozen (selfcheck 1362/1362). |
|
||
| **≤350 interior draw assertion** | ✅ **DONE.** C1 batching landed (`35981ad`). Added an interior draw-budget sweep to `flags_check.py` (enter all 9 shop types, assert ≤350 draws/room, dig on so it also confirms batching kept riffling). **Caught a real bug in my own `interior_mode.js`**: it rendered the interior without `renderer.info.reset()` while `autoReset=false`, so draws *accumulated* per frame (inflated 864/644/…). Fixed (reset per interior frame, both dig + walk render). True per-frame result: **all 9 room types ≤350 (worst dept 96)** — C's batching verified. |
|
||
| **F3 tag `v2.0-alpha`** | ⏳ all landed flags smoke-green + regression green + qa strict green → tag (3 flags OK if A no-shows). |
|
||
|
||
Base: v1.1 (`3a831fe`), HEAD `40fefd9`. `qa.sh --strict` GREEN.
|
||
|
||
---
|
||
|
||
## Round 5 (v1.1 close-out + v2 foundations) — DONE ✅ (v1.1 tagged; ?dig=1 wired)
|
||
|
||
Lane F role: **v1.1 watch** + v2 flag-seam wiring + QA-harness-for-v2 + shared-tree marshal.
|
||
Full tracking in [`LANE_F_NOTES §10`](docs/LANES/LANE_F_NOTES.md).
|
||
|
||
| task | status |
|
||
|---|---|
|
||
| **fal un-park check** | ✅ Verified already done — `039c179` un-parked fal in Lane E's brief (decision #4 + Lane E task 3: glass-case + bus-shelter + optional magazine-rack, ≤2 attempts, costs logged in AUDIT.md). **Nothing for F to change.** |
|
||
| **archive R3/R4 instruction claims** (F4) | ✅ Added "SUPERSEDED by R5" banner to `ROUND3_INSTRUCTIONS.md` + `ROUND4_INSTRUCTIONS.md`. |
|
||
| **v2 flags table** (F4) | ✅ Scaffolded in LANE_F_NOTES §10 (`?plansrc=osm` A, `?winmap=1` B, `?dig=1` C, `?roster=stream` D) — all pending owning-lane specs. |
|
||
| **F1 tag v1.1** | ✅ **TAGGED.** C1 (`aa9c140`: record_crate fixed + counter_till re-map) + E1 (`c522464`: 6 hero props baked 288k→48k, ≤8k each, live on depot) landed. Verified: depot serves baked props (counter_till 7999, drinks_fridge 8000, listening_booth 7986 tris ≤8k), C's `glb.js` maps them, record/opshop interiors render the **counter_till GLB** with keeper stand pose working + no double till, bins upgraded. Re-captured the 2 interior tour shots (fresh chromium → baked props). qa GREEN. `git tag v1.1` (local). |
|
||
| **F2 wire `?dig=1`** | ✅ **DONE + verified.** C shipped `dig.js` + the flag but left the in-game input/mode hook to F (LANE_C_NOTES §C2). Wired the consumer pattern into `interior_mode.js` (F-owned): aim at a `userData.kind==='bin'` fitting + press **E** → `createDig`/`dig.open` (per-bin seed via `binSeed(shop.seed,pos)`); the dig owns the frame (render `dig.scene`, no interior walk); WALK OUT / Esc closes → back to the room. Shell seam (`index.html`): `?dig=1` → `dig:` opt; `procity:digOpen` releases pointer-lock (riffle is cursor-driven); unlock→leaveShop guard reads `interiorMode.digActive` so opening a bin ≠ leaving. **Fresh-chromium smoke:** E opens riffle (seeded sleeves + prices render), WALK OUT closes to interior, **leak-free** (steady-state geo/tex flat over 3 cycles), **0 console errors**. Flag off ⇒ no listener/no dig ⇒ byte-identical to v1. |
|
||
| **F2 — other v2 seams** | B `?winmap` / D `?roster=stream` are self-contained (no shell input hook needed); smoked clean (boot, 0 errors, budget OK). A `?plansrc` not taken. |
|
||
| **F3 v2 QA harness** | ⏳ determinism gate (first-interior-hash per seed) · flags-off regression check (v2-prime-law enforcement) · v2 smoke → qa.sh warn-gate. Build as flags land. |
|
||
|
||
Base state: v1.0 (`2698b40`), `qa.sh --strict` GREEN 4/4, tree clean.
|
||
|
||
---
|
||
|
||
## Round 4 (v1-tag round) — DONE ✅ (all 6 gates green; v1.0 tagged)
|
||
|
||
Executed Fable's `ROUND4_INSTRUCTIONS.md` Lane F tasks. All lanes committed R4; a 6-auditor
|
||
adversarial v1-readiness workflow returned **GO**; `v1.0` tagged locally on the F finalization commit.
|
||
|
||
| task | status |
|
||
|---|---|
|
||
| **F1 setExposure** | ✅ Wired (`9416d0a`) **then correctly removed** (`aad9baa`): D3 (`a5e4b64`) made the impostor `toneMapped:true`, so `citizens.setExposure()` became a no-op tone-mapped globally via `renderer.toneMappingExposure`. Removed only *after* D committed (removing earlier would regress night brightness). Final state: no dead call. |
|
||
| **F2 storeys-checker** | ✅ `permittedMax = max>=2 ? min(max+1,3) : max` (Lane A's rule, `9416d0a`). Consistency **0 warnings** across 6 seeds; single-storey-type-at-2 still trips. **A confirmed** (`5b8c806`). |
|
||
| **F3 gate-3 budget** | ✅ **PASS.** E1 decimated peds (`518678d`, 924k→46k, ≤3k each) + **D2 sign-off** (`a5e4b64`, fleet ≈59.6k at 24-cap). Official measure: busiest node, pop 200, midday, 12-yaw peak → **draws 138 (≤300) · tris ~31k (≤200k)**. Baseline 372k. |
|
||
| **F4 gate-6 shots** | ✅ **PASS.** Against committed B2 (`6323d58`, `dbg.js` letterbox+poses): `shots.py v1_tour` → **10 full-frame 1280×720, no letterbox**; §3.5 **night closed-facade** (CLOSED plates + lit openLate video "VIDEO REGAL") + real **record/opshop interiors** (keeper + hero props). Fixed `shots.py` so interior shots capture via `enterShop(id)` (were falling back to a street bookmark). |
|
||
| **F5 tag v1.0** | ✅ **TAGGED** on the F finalization commit (gate table all-green + shots committed). Local only — John pushes. |
|
||
| **audit** | 6-auditor v1-readiness workflow: 5 GREEN (gates 1/3/5/6 + release hygiene) + 1 procedural "land finalization commit first" → resolved. GO. |
|
||
|
||
---
|
||
|
||
## Round 3 (lead lane) — DONE, verified live
|
||
|
||
Executed Fable's `ROUND3_INSTRUCTIONS.md` Lane F tasks. All wiring verified in-browser on seed 20261990.
|
||
|
||
| task | result |
|
||
|---|---|
|
||
| **§3.5 hours / closed shops** | `isOpen()` gate on `enterShop`. Midday: open. **Night (22:00): all closed except the openLate video rental** (Video Regal [11,23]) — verified toy shop blocked, video enters. |
|
||
| **§3.4 `?noassets`** | **Zero network** (Perf API: 0 GLB/depot/manifest/ped-model requests); 100% primitive, legible. |
|
||
| **§3.4 interior GLB** | `useGLB` → fitting GLBs load from the **3GOD depot** (bookshop shelves, +8 meshes, visually confirmed). |
|
||
| **§3.4 rig fleet** | 19 ped GLBs → peds **and** keepers upgrade placeholder→**GLB rigs**; **leak-free** (shared-cache proven, cycle2/3 flat). |
|
||
| **`--depot` on qa.sh** | `validate_manifest.py --depot` = 0 err (16 GLBs live). **qa.sh GREEN 4/4.** |
|
||
| **Gate 2 soak** | **PASS** both modes — assets: 20 interiors leak-free (+GLB-cache warmup), heap 98MB stable; noassets: 37 chunks, heap 15MB. 0 console errors. |
|
||
| **Gate 4 asset-free** | **PASS.** |
|
||
| **Gate 6 shots** | `docs/shots/v1_tour/` (10 shots) + contact sheet captured; letterbox + 3 fallback bookmarks are Lane B dbg.js. |
|
||
|
||
**Findings → other lanes** (details in [`LANE_F_NOTES §9`](docs/LANES/LANE_F_NOTES.md)):
|
||
- **D/E:** rig ped GLBs are **tri-heavy** (~45k each) → gate 3 tris **~279k > 200k** budget (draws fine ~200/300).
|
||
Decimate the ped GLBs; F holds `?pop=140` until then.
|
||
- **B:** add 3 shots bookmark poses; fix the headless composer letterbox in `DBG.shot()`; render the §3.5
|
||
closed-facade visual (dark windows + CLOSED plate; predicate `window.PROCITY.isOpen`).
|
||
- Fixed **5 F-owned harness bugs** so `shots.py`/`soak.py` actually run (DBG async-poll, `DBG.plan`→`PROCITY.plan`,
|
||
`enterShop` bare-id, `shop.lot` is an id, midday-pin + GLB-cache warmup + budget→warning).
|
||
|
||
**Git:** working tree synced to canonical `main` (862bbcd) mid-session — my R2 work is committed as `aa4ed4f`.
|
||
R3 edits below are staged by pathspec (see Housekeeping). Nothing pushed.
|
||
|
||
---
|
||
|
||
## TL;DR (round 2 — for context)
|
||
|
||
All five lanes (A–E) landed; F stood up the QA harness, **adversarially reviewed and fixed its own
|
||
tooling**, then wired the lanes into **a walkable, living town** — interiors, shopkeepers, and street
|
||
crowds all integrated and verified live:
|
||
|
||
1. **Interiors — every door opens (§3.2).** A door opens into a themed, seeded, self-lit 90s-AU
|
||
interior; walk it, walk back out (or Esc), room disposes. Verified across **all 9 shop types**,
|
||
**leak-free**, worst build **8.4 ms**. F-owned bridge [`web/js/world/interior_mode.js`](web/js/world/interior_mode.js).
|
||
2. **Shopkeepers (§3.3a).** A keeper stands at the counter of every shop and **turns to greet** you;
|
||
disposed with the room, **leak-free over 5 cycles**. (Asset-free placeholder; rig-fleet upgrade later.)
|
||
3. **Street crowds (§3.3b).** Deterministic pedestrians walk the footpaths (`graph = plan.streets`
|
||
directly), density follows time-of-day (busy midday, empty at night), LOD-tiered. **Draw budget
|
||
holds** — worst main-street view **~191 draws** (budget 300). Shell wiring in [`index.html`](web/index.html).
|
||
4. **Plan swap (§3.1)** + **facade skins fix** (prior pass): boots on Lane A's real town, textured.
|
||
|
||
`tools/qa.sh` is **GREEN (4/4 gates, 0 skipped)**. Remaining: manifest GLB upgrades + `?noassets`
|
||
(§3.4 — also upgrades keepers/peds to shared rigs), hours/closed-shops (§3.5), draw-budget shave.
|
||
Scoped in [`docs/LANES/LANE_F_NOTES.md`](docs/LANES/LANE_F_NOTES.md) §3.4–§3.5,§7.
|
||
|
||
---
|
||
|
||
## Lane readiness — ALL COMPLETE ✅ (`node tools/qa/scaffold_check.mjs`)
|
||
|
||
| Lane | status | notes |
|
||
|---|---|---|
|
||
| A CITYGEN | **5/5** | plan.js, registry.js, names.js, selfcheck.js, map.html — determinism gate green, verified in-browser |
|
||
| B STREETSCAPE | **4/4** | index.html shell, chunks.js streamer, buildings.js, fixture_plan.js — walkable town verified live |
|
||
| C INTERIORS | **2/2** | interiors.js (`buildInterior`), interior_test.html |
|
||
| D CITIZENS | **3/3** | rigs.js, sim.js (`CitizenSim`), keepers.js (`KeeperManager`), citizens_test.html |
|
||
| E ASSETS | **3/3** | manifest.json (validates green), validate_manifest.py, AUDIT.md + pipeline |
|
||
|
||
---
|
||
|
||
## What F built / did this session
|
||
|
||
### QA harness (F-owned, `tools/`) — trustworthy, all green
|
||
| file | what | state |
|
||
|---|---|---|
|
||
| `tools/qa.sh` | gate runner; `--strict` = v1 umbrella | ✅ 4/4 green |
|
||
| `tools/qa/scaffold_check.mjs` | scaffold + PRNG determinism (golden-locked) + live readiness matrix + plan-determinism | ✅ |
|
||
| `tools/qa/consistency_check.mjs` | cross-lane gate: plan ↔ registry ↔ assets ↔ manifest | ✅ |
|
||
| `tools/shots.py` · `tools/soak.py` | Playwright beauty-tour + 10-min soak/budget/`--noassets` (gates 2,3,4,6) | ✅ ready; need Lane B `window.DBG` hook (§4) |
|
||
|
||
### Adversarial self-review (Workflow, 23 agents)
|
||
Ran a 4-auditor + verifier workflow against F's own scripts + runbook. It confirmed **14 real
|
||
defects in F's tooling** (false-confidence checks + wrong API assumptions). **All applied:**
|
||
- `scaffold_check`: JSON round-trip was a **tautology** (fixed → real deep-compare w/ `-0` norm);
|
||
Math.random-ban regex had **false negatives** (fixed → anchored to comments); irange golden check
|
||
re-seeded per draw (fixed → single stream).
|
||
- `consistency_check`: added a **storeys-vs-registry-range** check, fixed a dead late-night branch,
|
||
removed a dead metric.
|
||
- `soak.py`: heap heuristic no longer silently passes when heap isn't reported; shop count now
|
||
requires **verified interior loads** (not just `enterShop` calls); `--minutes` now honored.
|
||
- `shots.py`: fixed an `IndexError` on a trailing `--seed`.
|
||
- **Runbook** (`LANE_F_NOTES.md`): corrected against the **real landed APIs** — `chunkIndex` returns
|
||
`{chunkSize,chunks}` (not callable); chunks is a `createChunkManager` **factory** with no lifecycle
|
||
hooks; `CitizenSim` is **graph-driven, not per-chunk**; keepers use `spawn/remove/disposeAll`.
|
||
|
||
### Integration passes (verified live in-browser)
|
||
- **`web/js/world/interior_mode.js`** (new F-owned bridge) + thin marked seam in `index.html` →
|
||
**§3.2 interiors: every door opens.** `enterShop` looks the shop up in the plan, attaches its lot
|
||
dims, calls Lane C `buildInterior`, and switches to a new `interior` mode that renders the room in
|
||
its own scene and walks it via the room's occupancy grid. Exit on walk-out / `procity:exitShop` /
|
||
Esc → `dispose()` + player restored to the door. **Verified:** door → Toy Shop renders; exit
|
||
state-machine correct; **leak-free** (134 geo/37 tex → 217 loaded → 134/37 after dispose); all **9
|
||
shop types** `ok+pathOK`, worst build **8.4 ms**; **0 console errors**.
|
||
- **Keepers (§3.3a)** — wired into `interior_mode.js`: `KeeperManager` (no fleet → placeholder actor),
|
||
spawned at the counter stand pose (`places.find(p=>p.userData.keeperStand)`), ticked in the interior
|
||
loop, `disposeAll()` on exit. **Verified:** 11-mesh keeper at the counter, **greets** the player
|
||
(turns 0.21 rad, eases back), **leak-free over 5 cycles/5 types** (net growth 0).
|
||
- **Citizens (§3.3b)** — `CitizenSim` in `index.html`: `graph = plan.streets` (no adapter needed),
|
||
`setPopulation(140)` (`?pop=`), `setTimeOfDay((seg+frac)/6)` + `update(dt)` in the street loop,
|
||
`visibilitychange→setPaused`. Fixed a NaN bug (`getClock().hour` is a string). **Verified:** 96
|
||
active midday, peds walk footpaths ~1.5 m/s, budget-safe (worst **~191 draws** across all main edges).
|
||
- **`web/js/citygen/index.js`** (barrel) → game boots on Lane A's real generated town (was the
|
||
fixture). Verified: console "using Lane A generatePlan", HUD "Boolarra Heads · 493 shops".
|
||
- **`web/js/world/skins.js`** (1-line chokepoint fix) → `facadeMat` normalizes full-filename skins.
|
||
Verified: `facade-*.jpg` requests now **200 OK**, town textured (was all flat-grey).
|
||
|
||
### Docs (F-owned)
|
||
- `docs/LANES/LANE_F_NOTES.md` — integration runbook (real APIs, wiring steps, QA gates, DBG hook request).
|
||
- `docs/V2_IDEAS.md` — parked scope (content firehose, override layer, weather, venue, …).
|
||
- `docs/shots/` — reference-shot tree (the other lanes are already dropping acceptance shots into it).
|
||
|
||
---
|
||
|
||
## Findings for the lanes (F flags; the two blocking-visual ones are already fixed)
|
||
|
||
| # | lane | finding | status |
|
||
|---|---|---|---|
|
||
| 1 | B | shell imported non-existent `js/citygen/index.js` → ran on fixture | **fixed by F** (barrel) |
|
||
| 2 | B | `skins.js facadeMat` double-wrapped full-filename skins → facades 404 | **fixed by F** (normalize) |
|
||
| 3 | B | street measures **~334 draw calls** at spawn (radius 3) — over CITY_SPEC ≤300 | open — shave (radius 2 or more merging) |
|
||
| 4 | A | ~10% of shop names have a **doubled possessive** ("Tex's's Toys") — `names.js` | open (cosmetic) |
|
||
| 5 | A | some shops have **storeys outside the registry type range** (e.g. video at 2, range [1,1]) | open — confirm corner-anchor intent |
|
||
| — | contract | `facadeSkin` is a full filename in the registry but skins.js expected a bare key | **worth a CITY_SPEC line** so ground/awning/interior skins avoid the same trap |
|
||
|
||
---
|
||
|
||
## Remaining integration (scoped in LANE_F_NOTES §3; ready to execute)
|
||
|
||
1. ✅ ~~**Interiors (§3.2)**~~ — **DONE**.
|
||
2. ✅ ~~**Citizens + keepers (§3.3)**~~ — **DONE** (both verified live; see Integration passes above).
|
||
3. **Manifest + `?noassets` (§3.4)** — GLB upgrades from `manifest.json`; primitive-fallback flag.
|
||
Doubles as the **rig-fleet upgrade** that makes keepers/peds cheap (shared meshes) so `?pop=` can rise.
|
||
4. **Hours / closed shops (§3.5)** and the **draw-budget shave** (finding 3).
|
||
5. **Full QA gates 1–4,6** once Lane B exposes the `window.DBG` hook (§4) my harnesses drive.
|
||
|
||
*Next natural step is §3.4 (manifest + `?noassets`) — it's the asset-free gate AND the fleet upgrade
|
||
that upgrades placeholder keepers/peds to shared GLB rigs. §3.5 (hours) is a small data-driven follow-on.*
|
||
|
||
**Note on verification:** pointer-lock doesn't engage in the automated browser, so a real *walk* is
|
||
best playtested by a human (`cd web && python3 -m http.server 8130` → `localhost:8130?seed=20261990`).
|
||
F drives/inspects via `window.PROCITY` + JS for programmatic checks.
|
||
|
||
## Housekeeping
|
||
- Nothing committed (all working tree). F's edits are minimal, commented `[Lane F integration]`,
|
||
reversible. **New F-owned file:** `web/js/world/interior_mode.js` (interior + keeper bridge). Shell
|
||
seam edits in `index.html`: interior wiring (import, bridge create, `enterShop`/`leaveShop`, loop
|
||
branch, `setMode` lock, Esc-exit, M/P guards, `window.PROCITY` hooks) + citizens (import, `CitizenSim`
|
||
create + `setPopulation`, street-loop `setTimeOfDay`+`update`, `visibilitychange` pause). Also edited:
|
||
`skins.js` (facade fix, prior pass). Suggested commit once reviewed — **use a pathspec** (lanes share
|
||
one tree, see memory): `git commit -- web/js/world/interior_mode.js web/index.html web/js/world/skins.js
|
||
web/js/citygen/index.js tools docs/LANES/LANE_F_NOTES.md docs/V2_IDEAS.md docs/shots F-progress.md`
|
||
msg `Lane F: integration — interiors + keepers + street peds + QA harness + barrel + skins fix`.
|
||
- A local `python3 -m http.server 8130` is running for the smoke test (harmless; kill when done).
|
||
- Lane prompts hard-code `/Users/jing/Documents/PROCITY`; real path is `/Users/m3ultra/Documents/procity`.
|
||
|
||
---
|
||
|
||
## Round 38 (v8 WAVE 1 — INHABITATION) — the budget reconciliation · four new gates, four controls · the round close
|
||
|
||
**THE BUDGET OF RECORD DOES NOT MOVE: 292 / 300 draws, margin still EIGHT.** The brief's naive sum
|
||
(292 + washing 1 + magpie 1 = 294) is wrong in our favour. **The pinned worst-draw frame is a NIGHT
|
||
frame and both draw-spending layers are structurally zero at night** — the washing comes in
|
||
(`count = 0`), the magpie roosts (`count = 0`). Wave 1's one draw is spent in daylight, where the
|
||
walked peak sits ten draws lower. Two pinned trees (`6bc7926` control, byte-identical under `web/` to
|
||
R37's `7bd96e5`; `4514cb9` treatment, byte-identical under `web/js` to the final `74d6257`), assets
|
||
symlinked to the live tree on both sides, 802 stations @ 2 m at **yaw 0**, sim + tram **LIVE**:
|
||
**night 292 → 292 (+0)** / 120,145 → 122,879 tris; **noon 282 → 283 (+1)** / 99,872 → 103,118;
|
||
`?classic=1` **269 / 90,580 → unchanged**; `fitzroy_real` **172 / 130,727 → unchanged**;
|
||
`bendigo_real` **204 / 131,492 → unchanged even with `?magpie=1` forced**. The control reproduced
|
||
R37's pin **to the byte, twice**. Every revert is exact: `?yards=0` at night gives the control's three
|
||
numbers; `?yards=0` at noon gives 282 / 99,872, isolating the noon +1 to the washing alone.
|
||
|
||
**Lane B's "control 346" — answered rather than dismissed.** As a *delta* it is the wall-clock ped
|
||
spawn artifact R37 named (114 draws of swing between two browser instances measures the sim, not the
|
||
build), and B was right to hide the sim. But 346 > 300 is a claim about the world, and neither
|
||
`stepwalk` (walks the chunks in, photographs a half-dressed street) nor `dwell` (fills the street,
|
||
arrives clean at 27 chunks) can adjudicate it. **`tools/qa/budget_walk.py` gains `--method
|
||
stepsettle`**: the audit's fixed-yaw stations, each **drained then held N real rAF frames** — R+1
|
||
dispose residue AND a fully-acquired crowd in the same frame. **Like for like — the same 101 stations at 8 m, yaw 0 — the settle is worth +3 draws at noon (277→280 control, 278→281 R38) and −9 at night (287→278 both trees), and the sign flip is the finding: at night the settled street RETIRES near rigs to impostors (tris 120,145→100,939), so the crowd goes home and a sampler that never waits does not see it. **The highest draw count anywhere in the reconciliation — 4 methods × 2 trees × 2 segments × 5 towns — is 292, the pin. Nothing read over 300.** The mechanism that could legitimately have breached it (full crowd inside a full R+1 dispose window) now reads 280/281. B's 346 was the artifact, and the refusal is now backed by the measurement that could have falsified it.
|
||
|
||
**A correction to my own R37 pin:** the corpus TRI worst is **130,727** on `fitzroy_real`, not
|
||
124,793 — and it reads 130,727 on the **control** tree, so it is my R37 sampling density (12-bearing
|
||
8 m sweep) versus this round's 2 m fixed-yaw stations, not a wave-1 cost. Re-pinned. *A pin is only
|
||
as good as its station spacing, and mine had not said so.*
|
||
|
||
**FOUR NEW GATES, FOUR CONTROLS DEMONSTRATED** (`smoke_ambient` · `smoke_character` · `smoke_magpie`
|
||
· `smoke_yards`, all wired into `flags_check.main()`):
|
||
- **§1.1 audio** — six sources anchored (63/8/81/32/39/57, B's numbers exactly); dog **audible at
|
||
5.9 m with 1 play scheduled** (for a one-shot `audible` is only published once `plays > 0`, so one
|
||
word covers fetch→decode→gain→schedule→counter); **hours enforced** (sprinkler audible 06:30 → idle
|
||
12:30, same pose); **day-of-week enforced** (mower idle Mon → audible Sat, same pose); **pan is a
|
||
bearing** (yaw 0/90/180/270 → +0.00 +0.85 −0.00 −0.85); **zero draws byte-identical to
|
||
`?ambient=0`**. **THE CONTROL:** a second server, root symlinked file-for-file except one patched
|
||
`manifest.json` with `sfx['dog-bark-fence']` renamed → that source reports **`no-entry`, 0 plays,
|
||
never audible**, other five unchanged, **0 console errors**. Stated on every run: audible OUTPUT is
|
||
unverifiable headlessly — **nobody in this loop has ears**.
|
||
- **§1.2 character** — classic is the frozen v1 literals with all tints null and 5 ground meshes;
|
||
**all 23 shipped keys matched per-town including the two carrying `state: ''`**; **the control
|
||
fires** (an unknown key returns `matched:false`); **21 distinct palettes across 23 towns** (a single
|
||
shared palette FAILS by name); and the two orphans are **on the GPU** — `ground-gravel.jpg` bound on
|
||
Castlemaine, `ground-reddust.jpg` on Darwin, read off the live material.
|
||
- **§1.3 magpie** — the latch arm verbatim with its **positive control first** (TRUE before the roll);
|
||
day-roll clears it; **localStorage gained NO new key** (baseline taken *after* a `game.save()` —
|
||
the first cut blamed the magpie for v7's own write); **the season is 23 distinct positions across
|
||
24 towns, run UNFORCED**, against B's first cut's 24-of-24-at-214, with `newtown_real` in season on
|
||
day 1 and 129/1000 seeds hitting the 11.5% design ratio; **territory 6 → 157** synthetic → bendigo;
|
||
**+1 draw / +182 tris** at one pose reused across both boots.
|
||
- **§1.4 yards** — 145 house + 36 yard = 181 lots, 181 hoists → 394 items, 256 active, **0 at night**;
|
||
the cost decomposition at one pose with the sim quiet: **box props +0 draws** (and **−2**, because
|
||
the 36 `yard` lots used to fall through to `buildShopfront()`) with **+1,936 tris**, **washing
|
||
exactly +1 draw / +512 tris**; and on real towns it **SKIPS LOUDLY** — a `⊘ SCOPE` line printing
|
||
0 house/yard lots / 0 hoists / mesh never created, next to the synthetic 181 that makes the zero
|
||
attributable.
|
||
|
||
**Also caught, in my own gates:** the pan arm's first draft used two bearings and read `+0.00/−0.00`
|
||
because that anchor sat on the forward axis — it would have called a working panner inconclusive.
|
||
Four bearings. *The gate's first draft failed the standard the gate exists to enforce.*
|
||
|
||
**SUITE: `tools/qa.sh --strict --matrix` GREEN on the true final tree** (`74d6257` — E's session-2
|
||
publish landed mid-round, `web/` diff vs B's `4514cb9` is manifest + one thumb, **no code**, and both
|
||
were already in the tree both budget servers read): **7 passed · 0 failed · 0 warn · 0 skipped**;
|
||
**flags_check GREEN 0/0** with all four new gates inside it; **MATRIX GREEN 10 towns × 7 gates**,
|
||
0 fetches under `?noassets` on all ten, tram verdicts unchanged. Wave 1 moved nothing in the matrix.
|
||
|
||
**Held: nothing.** Recorded, not fixed (other people's files): `magpie.js` says **154 triangles** at
|
||
`:9`/`:123` and **182** at `:42` — measured 182, so two stale comments in B's file; E's three
|
||
published GLBs are manifest-and-depot but not under `web/assets/models/` (staged in
|
||
`pipeline/_normalized/`, nothing loads them, validator's contract covers it — **not a defect**).
|
||
Carried debt unchanged: A's D1–D4 · the matrix's budget column is bookmark numbers · `?r=3` breaches
|
||
at 307 · the `?classic=1` town-selector question.
|
||
|
||
**No tag** — wave 1 is content; the epoch tags when it is whole.
|