Compare commits

..

2 Commits

Author SHA1 Message Date
m3ultra
2491f1c8ec Lane F R31 wave 2 (v7.0-alpha close): THE SHOT + the ledger. docs/shots/v7_alpha/crate_panel.png — the alpha's face: Monster Robot Party's real frontage, THE CRATE open with 3 real finds (real cover thumbs, paid/where/day honest), game bar day 1 · $116 · CRATE 3 · SLEEP, driven through the REAL loop with the dig param ABSENT (the R31 flip carries the shot), 0 console errors. LANE_F_NOTES §31 (the flip both ways, the two new gate legs + their negative control, harness lesson #4 — the explicit-flag blindfold, THE BETA QUEUE filed) + F-progress R31. Tag v7.0-alpha rides this commit, LOCAL ONLY — Fable reviews and pushes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:38:07 +10:00
m3ultra
50385c0f08 Lane F R31 wave 1 (v7.0-alpha close): THE DIG FLIP — the alpha-blocker Fable's playtest found. DIG_ON = flagOn('dig') (default-ON, ?dig=0 opt-out, ?classic=1 off — the v3.1 pattern; the v2-era opt-in survived 6 epochs past the README's 'dig on' claim because every R30 gate booted dig=1 explicitly: the explicit-flag blindfold). Fenced forever: default_boot_gate asserts dig REACHABILITY on a NAKED boot (E at a bin → digActive true), classic_regression asserts dig INERT at the bin (zero pcdg DOM, measured not assumed). PROCITY.flags gains dig. Splash de-staled ('Every door opens.' — no more '(soon)'; controls line carries E riffle/sell · C crate · SLEEP to save) + README manual row (sell at counter · C crate · SLEEP = save/advance/rotate). PROCITY_QA_PORT env override (flags_check, interior_scale_check, town_matrix) so the suite runs port-isolated instead of silently reusing whatever holds :8130. Verified both ways on :8951 (naked boot digs + closes clean; dig=0 and classic zero dig DOM, classic ZERO storage calls; noassets digs parody, 0 errors). qa.sh --strict --matrix on :8931: 7/0/0/0, flags_check 0 fails 0 warns, MATRIX 10 towns green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:37:02 +10:00
8 changed files with 193 additions and 10 deletions

View File

@ -4,6 +4,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 =

View File

@ -23,6 +23,7 @@ Click **START** to lock the mouse. Then:
| **W A S D** + mouse | walk / look (first person) |
| walk into a shop door | enter the interior (open shops only — check the hours tooltip) |
| **E** (inside a shop) | open the crate at a record bin — then it's cursor-driven: click sleeves to riffle, click BUY |
| **E** (at the counter) · **C** (street) · **SLEEP** (top bar) | sell a find to the keeper (always under what you paid) · open THE CRATE — your collection · sleep = save, the day advances, stock rotates |
| click a book spine / toy box | pull-and-buy card (real price stickers in godverse shops) |
| walk out the door / **Esc** | leave the shop (Esc also frees the mouse anywhere) |
| **M** | 2D town map (street only) |

View File

@ -1663,3 +1663,94 @@ as §30.5's closed-door: a verify that forgets the world's rules reads the rules
an inventory). Buying it again is possible **and loses money every time** (the $6/trip measurement is
exactly this); scarcity-per-day would need the save to carry per-day pull records. The no-pump clamp is
what makes the infinite crate safe. Rule it in beta with the guide bands, not here.
---
## 31. Round 31 — the v7.0-alpha CLOSE: THE DIG FLIP (wave 1) + THE TAG (wave 2)
### §31.1 The alpha-blocker (Fable's wave-0 playtest) and the flip
**The dig was default-OFF on a plain boot.** `web/index.html` still carried the v2-era opt-in
(`const DIG_ON = params.has('dig') && params.get('dig') !== '0'`) six epochs after the README's
flags table began claiming "dig on" — and every R30 game-loop gate booted `dig=1` EXPLICITLY, so
the whole suite was green while the boot every player gets had no crate-riffle at all: E at a bin
did nothing. The gates tested the game; nobody tested that the default boot IS the game.
**The flip (one line + the law):** `DIG_ON = flagOn('dig')` — default-ON, `?dig=0` opts out,
`?classic=1` master-off: the exact pattern the other flipped flags ride. `dig` joins the
`PROCITY.flags` intent surface (the gates read intent there). Measured both ways in fresh
contexts on a port-isolated server (:8951):
| boot | measured |
|---|---|
| NAKED (no params) | E at a bin → riffle **OPENED** + closed clean · `flags.dig` true · 0 errors |
| `?dig=0` | opt-out honoured — riffle does NOT open, **ZERO** dig DOM |
| `?classic=1` | dig INERT at the bin (E falls through `trySell` (null game) + shelf no-op) · ZERO `pcdg-*` DOM · **ZERO Storage-prototype calls** (instrumented) · 0 errors — the covenant untouched |
| `?noassets=1` naked | dig opens on **parody canvas** · 0 errors (existing law holds) |
*(Observation filed, pre-existing, NOT touched: `assets/gen/*.jpg` skin textures fetch under
`?noassets` — 22 fetches, fail-soft, present since v1; the dig added zero fetches to that boot.)*
### §31.2 The gate that makes the regression impossible (vacuous-gate law, turned on F's own suite)
Two new STRICT legs in `tools/flags_check.py`:
- **default_boot_gate** grows **dig REACHABILITY on the NAKED boot**: enter a record shop, E at an
aimed bin, `digActive` must go true (and close clean via WALK OUT); plus a `PROCITY.flags.dig`
intent assert. Un-flip the default again and this line goes red. Negative control: the identical
measurement on a `?dig=0` boot reads `opened:false` (pre-flight, :8951) — the detector
discriminates.
- **classic_regression** grows the **dig-INERT leg**: the old `digActive` assert was vacuously
false on the street; it is now measured AT THE BIN — E opens nothing, zero dig DOM, filed as
"measured, not assumed".
**Harness lesson #4 of the epoch (house habit): the explicit-flag blindfold.** A gate that
force-feeds its subject's flag (`dig=1` in every boot query, belt-and-braces) can never notice the
default is broken. Where a flag is CLAIMED default-on, at least one gate must exercise it with the
flag ABSENT. Now true for dig; gigs/weather/winmap/tram always had it (the default-boot gate boots
naked by construction).
### §31.3 Port isolation (small, suite-wide)
`PROCITY_QA_PORT` env override added to `flags_check.py`, `qa/interior_scale_check.py`,
`qa/town_matrix.py`. `ensure_server()` silently REUSES anything parked on :8130 — this round that
was Fable's own preview server, and a shared server is somebody else's cache semantics + somebody
else's lifecycle. The R31 re-gate rode `:8931`; default behaviour unchanged.
### §31.4 The splash (same file, same commit)
"Every door opens **(soon)**" — stale by six epochs — is gone: the tagline now sells the loop
("Every door opens. Dig the crates, sell the flips, sleep — the town remembers.") and the controls
line carries the game beats (E riffle a bin / sell at the counter · C your crate · SLEEP to save).
README manual gains the matching row (sell at the counter · C crate · SLEEP = save/advance/rotate).
### §31.5 The re-gate, the shot, the tag — RESULTS
All on the FINAL tree (commit `50385c0`), suite port-isolated on `:8931` (`PROCITY_QA_PORT` —
Fable's preview holds :8130, untouched):
- **`qa.sh --strict --matrix`: 7 passed · 0 failed · 0 warn · 0 skipped.** flags_check **0 fails /
0 warns** — both new R31 legs green in the run (`dig REACHABLE on the naked boot — E at a bin
opened the riffle (and closed clean)` · `?classic dig INERT at the bin — E opened nothing, zero
dig DOM`), all five R30 STRICT gates re-proven over the flipped tree, classic covenant fingerprint
green, no-giants green. **MATRIX GREEN — 10 towns (synthetic + fixtures + real caches) × 7 gates.**
- **The money shot: `docs/shots/v7_alpha/crate_panel.png`** — Monster Robot Party's real frontage
(redhill_godverse, `stock=real`), THE CRATE open with **3 real finds** (The Return $40 ·
First Filtration Of The Duplex Brains $15 · Blue Blackness $20 — real cover thumbs off the
per-shop atlas, every row honest: `paid $N · Monster Robot Party · day 1`), game bar
**day 1 · $116 · CRATE 3 · SLEEP**. Driven through the REAL loop with the dig param ABSENT (the
flip carries the shot): enter → E at three different bins → riffle → pull → BUY ×3 → street →
C. 0 console errors. Script: session scratchpad (`crate_shot.py`) — the PNG is the deliverable.
- **The tag: `v7.0-alpha`**, annotated, **LOCAL ONLY** on the notes commit — Fable reviews and
pushes main + tag together (treaty).
### §31.6 THE BETA QUEUE (Fable's playtest, filed NOT fixed — John's ruling queue)
1. **Spawn faces dirt/building-backs** instead of the strip — wants a cluster-pose for spawns.
2. **THE CRATE is unreachable inside shops** — C is street-only by design (hud.js); a player wants
it at the counter.
3. **Dawn sky reads dusk-orange** (cosmetic).
4. The pull card's **value hint is THE BIBLE** (chartered — the beta's guide bands).
5. Automation-only, unverified in person: **dig Esc-close under synthetic events**.
6. *(carried from §30.6)* no per-day `gone` tracking on parody/mint dig stock — scarcity-per-day
needs the save to carry pull records; rule it with the guide bands.

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 KiB

View File

@ -24,7 +24,10 @@ except ImportError:
"tools/.venv/bin/pip install playwright && tools/.venv/bin/python -m playwright install chromium")
ROOT = pathlib.Path(__file__).resolve().parent.parent
PORT = 8130
# [R31] PROCITY_QA_PORT: run the suite on an isolated port. The default (:8130) is also the house preview
# port — ensure_server() would silently REUSE a dev server already parked there, and a shared server is
# somebody else's cache semantics + somebody else's lifecycle. Gates want their own server or a named one.
PORT = int(os.environ.get('PROCITY_QA_PORT', '8130'))
HOST = f'http://127.0.0.1:{PORT}'
SEED = 20261990
GOLDEN_HASH = 0x5f76e76 # [R27w5] AMENDED. Was 0x3fa36874 "forever" since R16 — and that town
@ -173,6 +176,30 @@ def classic_regression(p):
if state['winmap'] or state['digActive']:
FAIL(f"a flipped/opt-in flag leaked into ?classic (winmap/dig): {state}")
else: OK('flipped + opt-in flags off under ?classic (winmap/dig)')
# [R31 — THE DIG FLIP, classic side] dig is default-ON now, so classic's dig-lessness must be
# MEASURED at a bin, not inferred from an idle digActive (which is vacuously false on the street):
# enter a record shop, E aimed at a bin — the riffle must NOT open and ZERO dig DOM may exist
# (createDig is never constructed off-flag; the E falls through trySell(null game) + shelf no-op).
dug = pg.evaluate("""() => {
const P = window.PROCITY, D = window.DBG; D.setSegment(2);
const s = (P.plan.shops || []).find(x => x.type === 'record' && P.isOpen(x));
if (!s) return { err: 'no open record shop' };
D.enterShop(s.id);
const bins = []; P.interiorMode.current.group.traverse(o => { if (o.userData && o.userData.kind === 'bin') bins.push(o); });
if (!bins.length) { D.exitShop(); return { err: 'no bins' }; }
const bp = new P.THREE.Vector3(); bins[0].getWorldPosition(bp);
P.camera.position.set(bp.x, 1.6, bp.z + 1.4); P.camera.lookAt(bp.x, bp.y + 0.4, bp.z); P.camera.updateMatrixWorld();
window.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyE' }));
const opened = !!P.interiorMode.digActive;
const dom = document.querySelectorAll('[class^="pcdg"]').length;
D.exitShop();
return { opened, dom };
}""")
if dug.get('err'): WARN(f"?classic dig-inert leg unmeasured — {dug['err']}")
elif not dug['opened'] and dug['dom'] == 0:
OK('?classic dig INERT at the bin — E opened nothing, zero dig DOM (measured, not assumed)')
else:
FAIL(f"?classic dig LEAKED: E at a bin → opened={dug['opened']}, dig DOM nodes={dug['dom']}")
if state['streamMode']:
OK('R7 roster flip in effect — streamed roster ACTIVE under ?classic (the v2 baseline)')
else: FAIL('roster flip REGRESSED — ?classic is not streaming (expected streamMode:true)')
@ -255,6 +282,37 @@ def default_boot_gate(p):
OK(f"default-boot street budget @ venue block: {state['draws']} draws ≤ {STREET_DRAWS_MAX}, {state['tris']:,} tris ≤ {STREET_TRIS_MAX:,}")
else:
FAIL(f"default-boot street OVER budget @ venue block: {state['draws']} draws / {state['tris']:,} tris (Fable scope call — not silent-trim)")
# [R31 — THE DIG FLIP GATE] Fable's wave-0 playtest found the alpha-blocker this whole suite
# missed: every R30 game-loop gate booted `dig=1` EXPLICITLY (belt-and-braces that became a
# blindfold), so the suite was green while the NAKED boot's E did nothing at a bin — index.html
# still carried the v2-era opt-in. The gates tested the game; nobody tested that the default boot
# IS the game. So this gate now proves dig REACHABILITY with zero dig params: enter a record shop,
# E at an aimed bin, the riffle must open. Un-flip the default again and this line goes red.
f2 = state['flags'] or {}
if not f2.get('dig'):
FAIL(f"dig intent OFF on the naked boot (PROCITY.flags.dig falsy) — the R31 flip regressed: {f2}")
dug = pg.evaluate("""() => {
const P = window.PROCITY, D = window.DBG; D.setSegment(2);
const s = (P.plan.shops || []).find(x => x.type === 'record' && P.isOpen(x));
if (!s) return { why: 'no open record shop' };
D.enterShop(s.id);
const bins = []; P.interiorMode.current.group.traverse(o => { if (o.userData && o.userData.kind === 'bin') bins.push(o); });
if (!bins.length) { D.exitShop(); return { why: 'no bins' }; }
const bp = new P.THREE.Vector3(); bins[0].getWorldPosition(bp);
P.camera.position.set(bp.x, 1.6, bp.z + 1.4); P.camera.lookAt(bp.x, bp.y + 0.4, bp.z); P.camera.updateMatrixWorld();
window.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyE' }));
const opened = !!P.interiorMode.digActive;
const out = document.querySelector('.pcdg-out'); if (out) out.click();
const closed = !P.interiorMode.digActive;
D.exitShop();
return { opened, closed };
}""")
if dug.get('opened') and dug.get('closed'):
OK('dig REACHABLE on the naked boot — E at a bin opened the riffle (and closed clean); the default boot IS the game')
elif dug.get('opened'):
WARN(f"naked-boot dig opened but close unconfirmed — {dug}")
else:
FAIL(f"THE DIG IS OFF ON A PLAIN BOOT — the R31 alpha-blocker is back: {dug}")
if errs: FAIL(f"{len(errs)} console error(s) on the default boot; first: {errs[0][:140]}")
else: OK('0 console errors on the default boot')
finally:

View File

@ -24,10 +24,10 @@ Run: tools/.venv/bin/python tools/qa/interior_scale_check.py [--seed N]
Setup: the same Playwright venv as tools/soak.py / tools/flags_check.py.
Needs Lane B's window.DBG hook (?dbg=1) + window.PROCITY.interiorMode (Lane C/F).
"""
import sys, time, socket, subprocess, pathlib
import sys, os, time, socket, subprocess, pathlib
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
PORT = 8130
PORT = int(os.environ.get('PROCITY_QA_PORT', '8130')) # [R31] port-isolated suite runs (see flags_check.py)
HOST = f'http://127.0.0.1:{PORT}'
SEED = 20261990
if '--seed' in sys.argv:

View File

@ -16,10 +16,10 @@ Gates per town (all on the DEFAULT boot — gigs/weather/winmap/tram on, post-R1
Run: tools/.venv/bin/python tools/qa/town_matrix.py
Needs the Playwright venv + Lane B's ?dbg=1 hook + Lane F's real-cache shell wiring (R17).
"""
import sys, time, socket, subprocess, pathlib
import sys, os, time, socket, subprocess, pathlib
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
PORT = 8130
PORT = int(os.environ.get('PROCITY_QA_PORT', '8130')) # [R31] port-isolated suite runs (see flags_check.py)
HOST = f'http://127.0.0.1:{PORT}'
STREET_DRAWS_MAX, STREET_TRIS_MAX = 300, 200_000
# [R21 ledger #6c] B's R20 tram ruling: on a REAL-ROADS town the tram only runs if its best main chain

View File

@ -31,10 +31,10 @@
<div id="pc-start">
<h1>PROCITY</h1>
<div class="sub">A procedurally generated, walkable 90s-Australian shopping town.
Every door opens (soon). Walk the strip, watch the day turn.</div>
Every door opens. Dig the crates, sell the flips, sleep — the town remembers.</div>
<button id="pc-go">walk into town</button>
<div class="seed" id="pc-startseed"></div>
<div class="sub" style="font-size:12px;opacity:.7">WASD move · shift run · mouse look · click a door · [ ] time of day · M map · P screenshot</div>
<div class="sub" style="font-size:12px;opacity:.7">WASD move · shift run · mouse look · click a door · E riffle a bin / sell at the counter · C your crate · SLEEP to save · [ ] time · M map</div>
</div>
<script type="module">
@ -190,8 +190,14 @@ const minimap = createMinimap(plan);
const fleet = NOASSETS ? null : loadPedFleet('models/peds/', { sit: !CLASSIC, look: !CLASSIC }); // [R16] sit.glb (the seated drummer) + [R29] look.glb (the street glance) on the default boot; ?classic keeps the zero-fetch-delta covenant
if (!NOASSETS) preloadManifest(); // prime the manifest so the first interior can upgrade its fittings
// [Lane F integration] interior bridge — owns the 'interior' branch (Lane C buildInterior + Lane D keeper)
// [Lane F §F2] ?dig=1 enables Lane C's crate-riffle inside record interiors (v2). Off ⇒ byte-identical to v1.
const DIG_ON = params.has('dig') && params.get('dig') !== '0';
// [Lane F R31 — THE DIG FLIP] The crate-riffle is DEFAULT-ON: `?dig=0` opts out, `?classic=1` forces it off
// — the exact flagOn pattern the other flipped flags ride. The v2-era opt-in (`params.has('dig')`) survived
// here six epochs after the README's flags table claimed "dig on", and every R30 game-loop gate booted
// `dig=1` explicitly — so the whole suite was green while the plain boot (the boot every player gets) had
// no dig at all: E at a bin did nothing (Fable's R31 wave-0 playtest, the alpha-blocker). The default-boot
// gate now asserts dig REACHABILITY on a naked boot, so un-flipping this line goes red in qa, not in a
// player's hands. ?noassets composes unchanged: the dig falls to parody canvas (existing law).
const DIG_ON = flagOn('dig');
// [Lane F §F2 stock=real] real GODVERSE sleeves in the crates (v2). Preload the record pack at boot so
// the first interior/dig can batch real covers; fail-soft (missing pack → parody canvas). Off under ?noassets.
const STOCK_REAL = params.get('stock') === 'real' && !NOASSETS;
@ -527,7 +533,8 @@ window.PROCITY = { plan, scene, camera, renderer, chunks, lighting, player, skin
// [Lane F R16 — THE FLIP] the flag-intent surface: what SHOULD be on this boot (the gates verify intent vs
// reality). Default boot = all four flipped flags true; ?classic=1 = all false + classic true; each `=0` opts out.
flags: { classic: CLASSIC, gigs: GIGS_ON, weather: flagOn('weather'), winmap: flagOn('winmap'), tram: flagOn('tram'),
game: GAME_ON }, // [R30] the v7 game layer (default-on; ?game=0 / ?classic=1 → off)
game: GAME_ON, // [R30] the v7 game layer (default-on; ?game=0 / ?classic=1 → off)
dig: DIG_ON }, // [R31] the dig flip (default-on; ?dig=0 / ?classic=1 → off) — gate reads intent here
THREE, get mode() { return MODE; } }; // [Lane F] bridge + drive hooks
// [Lane B R11 audio] street WebAudio engine — self-unlocking on the first gesture; silent with
// zero/blocked assets or ?mute=1; ?noassets=1 ⇒ no audio fetches. Exposes window.PROCITY.audio so