Compare commits
2 Commits
d96cbde004
...
2491f1c8ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2491f1c8ec | ||
|
|
50385c0f08 |
@ -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 =
|
||||
|
||||
@ -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) |
|
||||
|
||||
@ -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.
|
||||
|
||||
BIN
docs/shots/v7_alpha/crate_panel.png
Normal file
BIN
docs/shots/v7_alpha/crate_panel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 894 KiB |
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user