PROCITY/tools/flags_check.py
m3ultra 528829eeba Lane F R26 (v5.0-beta): the manifest consumed, the tolerance retired — tag HELD on the mint-id rule
EVERY CRATE IS DIFFERENT, and F measured it rather than taking G's 15/15 on trust — from the
indexes of record, not by entering one lucky shop: 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 not on disk.

SOURCING, NOT TIER — C was right and the gate follows C. `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. 15/15 indexes declare sourcing, every one 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 (R24's 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 rather than inventing it; G built the manifest; F now
consumes it — the shell asks for nothing unlisted. Zero 404s because nothing absent gets asked
for, not because the gate stopped looking. AND DELETING IT IMMEDIATELY CAUGHT A LEFTOVER: F had
wired the boot preload but left a second blind probe at the door, 404ing on every
keyed-but-unfetchable shop. While the gate said "8 probes, expected", a ninth was invisible too.
A named exception starts protecting new bugs the moment the real fix exists.

F's beta gate GREEN: distinctness · sourcing (real + mint rooms) · soft fall silent · ZERO 404s ·
0 console errors, no attribution exceptions. Selfcheck ALL GREEN 156,212/156,212.

TAG HELD — atlas-QA FAIL, 14 errors, and it is not the product. E's SLOT_ID_PREFIX = "sku_"
(validate_atlas.py:73) applies to every pack; E reads `sourcing` at :178 but the slot-id check is
sourcing-blind. All 14 mint crates fail; the real crate passes. A mint crate HAS no POS sku — it
was never in that shop's till — so the rule demands what cannot exist, the same shape as R24's
sweep demanding a cafe carry a godverseShopId.

F measured the rule's intent and it holds: mint_<dealgod listing id>, non-positional, sorted by
it, seeded bake, 0 ids reused across all 15 crates. Only the literal prefix fails. And G refused
to go green the easy way for the best reason — F's own R24 collision grown up: a POS sku and a
dealgod listing id are different id spaces, and filing one under `sku_` recreates that bug
INSIDE the economy, where tier-2's sold-means-gone looks up POS skus. R24's collision entered
the wrong shop; the same collision in the money layer sells the wrong record.

F did not fix E's one line: E's file, C's contract, C's call as its author (the principle Fable
ratified when C overruled `tier`) — and F is the party that benefits, since that line is the
only thing between F and the tag. Cost of waiting: one line. Cost of F being quietly wrong: a
gate that green-lights id-space collisions in the layer that moves money.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:38:44 +10:00

1273 lines
78 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""PROCITY Lane F — v2 flag enforcement harness (round-6 F1).
The machine version of what the R5 review did by hand. Three things, headless, <2 min:
1. FLAGS-OFF REGRESSION — the enforcement arm of the prime law. Boot with no flags → the plan
fingerprint must equal the golden 0x3fa36874, the settled street view must stay within the v1
budget (≤300 draws / ≤200k tris) AND within tolerance of the v1.1 snapshot, every v2 subsystem
must report inactive, and there must be ZERO console errors. A v2 flag leaking into the default
boot trips this.
2. PER-FLAG SMOKE — for each landed flag (winmap, dig, roster, +plansrc when A lands): boot flag-on,
cross ≥2 chunks, enter one interior (dig: also open+close a riffle), 0 console errors.
3. ALL-ON COMBO (flag-composition law, decision #4): every landed flag on at once, same smoke.
Run: cd web && python3 -m http.server 8130 (or let this script spawn its own)
tools/.venv/bin/python tools/flags_check.py
Wired into tools/qa.sh as a WARN-level gate this round (strict in round 7). Exit 0 = all pass;
non-zero = a check failed (qa.sh reports it as a warning this round).
"""
import sys, json, time, socket, subprocess, pathlib
try:
from playwright.sync_api import sync_playwright
except ImportError:
sys.exit("playwright not installed — python3 -m venv tools/.venv && "
"tools/.venv/bin/pip install playwright && tools/.venv/bin/python -m playwright install chromium")
ROOT = pathlib.Path(__file__).resolve().parent.parent
PORT = 8130
HOST = f'http://127.0.0.1:{PORT}'
SEED = 20261990
GOLDEN_HASH = 0x3fa36874 # frozen v2 determinism anchor (?classic boot) — UNCHANGED by the flips
GIG_GOLDEN = 0xb1d48ea1 # [R16] the DEFAULT boot's gig-augmented plan fingerprint (A's R16 corner-poster re-pin)
# flags-off DBG.shot('street_noon') snapshot. RE-PINNED at the R7 roster flip (prime-law amendment):
# old (v1.1, roster-off): 163 draws, 19000 tris
# new (R7, roster default-on): 164 draws, ~66000 tris
# Draws barely move — E1's ped sub-mesh merge makes each near-rig ~1 draw, which is exactly what let
# the roster flip to full density. Tris jump is the streamed crowd (each near-rig ~2.4k tris); it is
# inherently variable frame-to-frame (how many rigs are near at sample time), so the drift WARN below
# is DRAWS-ONLY — tris is guarded strictly by the STREET_TRIS_MAX hard cap, not a soft snapshot.
BASE_DRAWS, BASE_TRIS = 164, 66000
STREET_DRAWS_MAX, STREET_TRIS_MAX = 300, 200_000
KNOWN_FLAGS = ['winmap=1', 'dig=1', 'roster=stream'] # plansrc=osm auto-appended if Lane A landed it
# [R12] gig-night interior budget (ROUND12 law: ≤350 draws incl. the crowd — a room with a band and an
# audience in it is allowed more than a shop, which is what the ≤300 street/room numbers were tuned for).
GIG_DRAWS_MAX = 350
# `cover` is seeded per gig, so which door-charge path the default seed exercises is luck. Pin one seed
# per half of John's "free AND paid" ruling: 20261990's tonight is $10 (Screaming Utes at The Thornbury
# Hotel), 1234's is free (Dazza & The Dropbears at The Imperial). Re-pin if Lane A re-seeds the gig stream.
FREE_GIG_SEED = 1234
fails, warns = [], []
def FAIL(m): fails.append(m); print(f" \033[31m✗ FAIL\033[0m {m}")
def WARN(m): warns.append(m); print(f" \033[33m! WARN\033[0m {m}")
def OK(m): print(f" \033[32m✓\033[0m {m}")
def head(m): print(f"\n\033[1m{m}\033[0m")
def stature_ok(f):
# [R16 ledger #3] seated figs (the drummer — fig.userData.procitySeated) pass the no-giants floor at a
# SEATED band [0.9,1.5] m; standing figs stay [1.4,2.0]. A seated human fails the standing floor by design.
lo, hi = (0.9, 1.5) if f.get('seated') else (1.4, 2.0)
return lo <= f['stature'] <= hi
def port_up(port):
with socket.socket() as s:
s.settimeout(0.4)
return s.connect_ex(('127.0.0.1', port)) == 0
def ensure_server():
if port_up(PORT):
return None # reuse a running dev server
proc = subprocess.Popen(['python3', '-m', 'http.server', str(PORT)],
cwd=str(ROOT / 'web'), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
for _ in range(50):
if port_up(PORT): return proc
time.sleep(0.1)
proc.terminate(); sys.exit(f"could not start http.server on :{PORT}")
# ── page helpers ─────────────────────────────────────────────────────────────
def new_page(p):
b = p.chromium.launch()
pg = b.new_page(viewport={'width': 1280, 'height': 720})
errs = []
pg.on('console', lambda m: errs.append(m.text) if m.type == 'error' else None)
pg.on('pageerror', lambda e: errs.append(str(e)))
return b, pg, errs
def boot(pg, query):
pg.goto(f'{HOST}/index.html?seed={SEED}&dbg=1' + (('&' + query) if query else ''))
pg.wait_for_function("window.DBG && window.DBG.ready === true", timeout=20000)
pg.evaluate("() => { const o=document.getElementById('pc-start'); if(o) o.style.display='none'; }")
try:
pg.wait_for_function("() => window.PROCITY && (!window.PROCITY.fleet || window.PROCITY.fleet.ready)", timeout=12000)
except Exception:
pass
def gigs_landed(p):
"""Boot ?gigs=1; landed iff Lane A's augmentation produced a schedule (else the flag is inert)."""
b, pg, _ = new_page(p)
try:
boot(pg, '')
return bool(pg.evaluate("() => !!(window.PROCITY.plan.gigs && window.PROCITY.plan.gigs.length)"))
except Exception:
return False
finally:
b.close()
def plansrc_landed(p):
"""Boot ?plansrc=osm; landed iff the shell exposes an 'osm' plan source (else the flag is ignored)."""
b, pg, _ = new_page(p)
try:
boot(pg, 'plansrc=osm')
src = pg.evaluate("() => (window.PROCITY.plan && (window.PROCITY.plan.source||window.PROCITY.plan.planSource)) "
"|| window.PROCITY.planSource || null")
return src == 'osm'
except Exception:
return False
finally:
b.close()
# ── the three checks ─────────────────────────────────────────────────────────
def classic_regression(p):
# [R16 — THE FLIP] The prime-law covenant MOVED from "flags off" to "?classic=1": the classic boot must
# stay byte-identical to the frozen v2 baseline FOREVER — synthetic fingerprint 0x3fa36874, zero gig
# layer, no opt-in flags, the R7 streamed roster (which IS the v2.0 baseline). Same assertions as the old
# flags-off gate, now booted with ?classic=1 (the four flipped flags are off, everything else v2-default).
head('CLASSIC REGRESSION (?classic=1 → the frozen v2 covenant)')
b, pg, errs = new_page(p)
try:
boot(pg, 'classic=1')
state = pg.evaluate("""() => {
const P=window.PROCITY, D=window.DBG;
D.shot('street_noon'); // fixed pose → comparable draws/tris
const i = D.info();
const c = P.citizens || {};
return { draws:i.drawCalls, tris:i.tris, mode:i.mode,
winmap: !!(P.winmap && P.winmap.active),
digActive: !!(P.interiorMode && P.interiorMode.digActive),
streamMode: !!c.streamMode, // R7: real roster-mode flag (was reading a nonexistent prop)
// [R12] the gig layer must be wholly absent flags-off: no state machine, no schedule
// in the plan, no pub converted, and none of Lane B's venue geometry in the scene.
gigState: !!P.gigs, planGigs: (P.plan.gigs||[]).length, posters: (P.plan.posters||[]).length,
pub: !!(P.plan.shops||[]).find(s => s.venue || s.type === 'pub'),
venueGeo: !!P.scene.getObjectByName('venue-presentation') };
}""")
# determinism anchor — run Lane A's selfcheck in-process (golden fingerprint)
r = subprocess.run(['node', 'web/js/citygen/selfcheck.js'], cwd=str(ROOT),
capture_output=True, text=True)
hash_ok = (f'{GOLDEN_HASH:#010x}'.replace('0x', '0x') in r.stdout) or (hex(GOLDEN_HASH) in r.stdout) \
or ('3fa36874' in r.stdout)
# [R24] These are TWO assertions and they get two verdicts. This gate used to require
# `hash_ok and r.returncode == 0` and report BOTH failures with the fingerprint's message — so
# any unrelated red anywhere in Lane A's selfcheck (R24: 8 godverse-identity checks in a
# different town) screamed "determinism/prime-law broken", the loudest alarm we own, about a
# covenant that was provably intact (0x3fa36874, node-side, unmoved). A gate must name what it
# actually measured; F wrote this one, so F fixed it. The covenant is asserted on its own.
if hash_ok: OK(f'plan fingerprint == golden {hex(GOLDEN_HASH)} — the classic covenant holds')
else: FAIL(f'plan fingerprint != golden {hex(GOLDEN_HASH)} — determinism/prime-law broken')
if r.returncode == 0: OK('Lane A selfcheck green')
else:
tail = [l for l in r.stdout.splitlines() if l.strip().startswith('')][:3]
FAIL('Lane A selfcheck RED (separate from the covenant above): '
+ ('; '.join(t.strip() for t in tail) if tail else 'see selfcheck output'))
# winmap flips OFF under ?classic (it's default-on now); dig stays opt-in. The streamed roster IS the
# v2.0 baseline (R7 flip / prime-law amendment) — so it must be ON under ?classic.
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)')
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)')
# [R12/R16] gig layer wholly absent under ?classic. Any of these present means the gig layer escaped
# the classic gate and the frozen v2 covenant is broken.
gig_leaks = [k for k in ('gigState', 'planGigs', 'posters', 'pub', 'venueGeo') if state[k]]
if not gig_leaks:
OK('v3 gig layer wholly absent under ?classic (no state machine, no plan.gigs/posters, no pub, no venue geometry)')
else:
FAIL(f"v3 gig layer LEAKED into ?classic: {[(k, state[k]) for k in gig_leaks]}")
if state['draws'] <= STREET_DRAWS_MAX and state['tris'] <= STREET_TRIS_MAX:
OK(f"?classic street view within budget (draws {state['draws']}{STREET_DRAWS_MAX}, tris {state['tris']}{STREET_TRIS_MAX})")
else: FAIL(f"?classic street view OVER budget: draws {state['draws']}, tris {state['tris']}")
# Drift WARN is DRAWS-ONLY — tris is crowd-variable post-flip (guarded by the hard cap above).
dd = abs(state['draws'] - BASE_DRAWS) / BASE_DRAWS
if dd <= 0.40:
OK(f"draws within 40% of re-pinned baseline ({state['draws']} vs {BASE_DRAWS}); tris {state['tris']} (crowd-variable, ≤cap)")
else:
WARN(f"draws drifted >40% from baseline ({state['draws']} vs {BASE_DRAWS}) — investigate")
if errs: FAIL(f"{len(errs)} console error(s) under ?classic; first: {errs[0][:140]}")
else: OK('0 console errors under ?classic')
# R7 escape hatch: ?roster=v1 must restore the fixed roster (streamMode:false), render, no errors.
b2, pg2, errs2 = new_page(p)
try:
boot(pg2, 'roster=v1')
esc = pg2.evaluate("() => ({ streamMode: !!(window.PROCITY.citizens||{}).streamMode, mode: window.DBG.info().mode })")
if not esc['streamMode'] and esc['mode'] == 'street':
OK('escape hatch ?roster=v1 → fixed roster (streamMode:false), renders')
else: FAIL(f"escape hatch ?roster=v1 broken: {esc}")
if errs2: FAIL(f"?roster=v1: {len(errs2)} console error(s); first: {errs2[0][:140]}")
finally:
b2.close()
finally:
b.close()
def default_boot_gate(p):
# [R16 — THE FLIP] The enforcement arm of the DEFAULT law: the no-flag boot is now the full experience —
# all four flags on, the gig layer present, deterministic to the gig golden. This is the number every
# budget/smoke now means (its inverse is classic_regression above).
head('DEFAULT-BOOT GATE (the flip → the town shows its best by default)')
b, pg, errs = new_page(p)
try:
boot(pg, '')
state = pg.evaluate("""() => {
const P = window.PROCITY, D = window.DBG;
D.setSegment(5); D.shot('venue_night'); // NIGHT at a venue → the busiest default view (gigs+pools+winmap+tram)
const i = D.info();
return { flags: P.flags,
gigState: !!P.gigs, planGigs: (P.plan.gigs||[]).length, posters: (P.plan.posters||[]).length,
pub: !!(P.plan.shops||[]).find(s => s.venue), venueGeo: !!P.scene.getObjectByName('venue-presentation'),
winmapActive: !!(P.winmap && P.winmap.active), tram: !!P.scene.getObjectByName('tram'),
draws: i.drawCalls, tris: i.tris };
}""")
f = state['flags'] or {}
if f.get('gigs') and f.get('weather') and f.get('winmap') and f.get('tram') and not f.get('classic'):
OK('default boot flips all four ON (gigs · weather · winmap · tram), classic off')
else:
FAIL(f"default boot flag intent wrong: {f}")
present = [k for k in ('gigState', 'planGigs', 'posters', 'pub', 'venueGeo') if state[k]]
if len(present) == 5 and state['tram']:
OK(f"the town is live: gig layer present ({state['planGigs']} gigs, {state['posters']} posters, venue geo) · tram up (winmap is a window shader — its own smoke)")
else:
FAIL(f"default boot not fully live: gig={present}, tram={state['tram']}")
# The default boot's plan IS the gig-augmented plan; selfcheck pins its fingerprint to the gig golden
# (0xb1d48ea1) INTERNALLY and is ALL GREEN only if it matches. (selfcheck prints the base fingerprint
# in its summary but asserts the gig golden silently — so we gate on the green run, not a substring.)
r = subprocess.run(['node', 'web/js/citygen/selfcheck.js'], cwd=str(ROOT), capture_output=True, text=True)
if r.returncode == 0 and 'ALL GREEN' in r.stdout:
OK(f"default plan pins to the gig golden {hex(GIG_GOLDEN)} (selfcheck ALL GREEN — the gig-layer golden asserts pass)")
else:
FAIL(f"selfcheck not green — the gig golden {hex(GIG_GOLDEN)} or determinism broke: {r.stdout[-160:]}")
# budget on the DEFAULT boot at the busiest venue block (the number the budget law now means)
if state['draws'] <= STREET_DRAWS_MAX and state['tris'] <= STREET_TRIS_MAX:
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)")
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:
b.close()
def smoke(p, label, query, is_combo=False):
head(f'SMOKE: {label} (?{query})')
b, pg, errs = new_page(p)
try:
boot(pg, query)
# cross ≥2 chunks by teleporting between separated street nodes
chunks = pg.evaluate("""() => {
const P=window.PROCITY, D=window.DBG, seen=new Set();
const ns = P.plan.streets.nodes;
for (const idx of [0, (ns.length/3|0), (2*ns.length/3|0), ns.length-1]) {
const n = ns[idx]; D.teleport(n.x, n.z, 0);
for (let k=0;k<10;k++) (P.citizens&&P.citizens.update&&P.citizens.update(0.05));
const c = D.info().chunk; if (c!=null) seen.add(String(c));
}
return seen.size;
}""")
if chunks >= 2: OK(f'crossed {chunks} chunks streaming (flag active, no crash)')
else: WARN(f'only {chunks} distinct chunks seen (teleport/stream probe)')
# enter one interior (open shop), exit
entered = 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)) || (P.plan.shops||[]).find(x=>P.isOpen(x));
if(!s) return {ok:false};
D.enterShop(s.id);
const inMode = D.info().mode==='interior';
return {ok:inMode, shop:s.name};
}""")
if entered.get('ok'): OK(f"entered interior '{entered.get('shop')}' (mode=interior)")
else: FAIL('could not enter an interior')
# dig-specific: open one riffle + close, leak-free
if 'dig=1' in query:
dug = pg.evaluate("""() => {
const P=window.PROCITY, THREE=P.THREE, room=P.interiorMode.current;
if(!room) return {ok:false, why:'no room'};
const bins=[]; room.group.traverse(o=>{ if(o.userData&&o.userData.kind==='bin') bins.push(o); });
if(!bins.length) return {ok:false, why:'no bins'};
const bp=new THREE.Vector3(); bins[0].getWorldPosition(bp);
P.camera.position.set(bp.x,1.6,bp.z+1.8); 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 btn=document.querySelector('.pcdg-out'); if(btn) btn.click();
return { ok: opened, closed: !P.interiorMode.digActive };
}""")
pg.wait_for_timeout(300)
if dug.get('ok') and dug.get('closed'): OK('dig: riffle opened (E on bin) + closed (WALK OUT)')
elif dug.get('ok'): WARN(f"dig: opened but close not confirmed ({dug})")
else: FAIL(f"dig: riffle did not open ({dug.get('why','?')})")
pg.evaluate("() => window.DBG.exitShop && window.DBG.exitShop()")
if errs: FAIL(f"{label}: {len(errs)} console error(s); first: {errs[0][:140]}")
else: OK(f'{label}: 0 console errors')
finally:
b.close()
def interior_budget(p):
"""Decision #2: interior draw budget is law — ≤350 draws worst room. Sweep every shop type
(dig on, so this also confirms batching kept ?dig=1 riffling). C enforces via its sweep; F pins it."""
head('INTERIOR DRAW BUDGET (≤350 draws/room law — decision #2)')
b, pg, errs = new_page(p)
try:
boot(pg, 'dig=1')
pg.evaluate("() => window.DBG.setSegment(2)") # midday → shops open
types = ['record', 'opshop', 'toy', 'book', 'video', 'pawn', 'milkbar', 'dept', 'stall']
rows, seen = [], set()
for t in types:
picked = pg.evaluate("""(t) => {
const P=window.PROCITY, D=window.DBG;
const s=(P.plan.shops||[]).find(x=>x.type===t && P.isOpen(x));
if(!s) return null; D.enterShop(s.id); return {id:s.id, name:s.name};
}""", t)
if not picked:
continue
pg.wait_for_timeout(500) # let async GLB fittings load → settled worst-case draws
info = pg.evaluate("() => { const i=window.DBG.info(); return {draws:i.drawCalls, tris:i.tris, mode:i.mode}; }")
rows.append({'type': t, 'shop': picked['name'], **info})
pg.evaluate("() => window.DBG.exitShop && window.DBG.exitShop()")
pg.wait_for_timeout(150)
over = [r for r in rows if r['draws'] > 350]
for r in over:
FAIL(f"interior {r['type']} '{r['shop']}': {r['draws']} draws > 350")
if rows and not over:
worst = max(rows, key=lambda r: r['draws'])
OK(f"all {len(rows)} room types ≤350 draws (worst: {worst['type']} {worst['draws']}, tris {worst['tris']})")
elif not rows:
WARN('no interiors sampled (no open shops of the swept types)')
if errs:
FAIL(f"interior sweep: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
def _enter_type(pg, t, extra=''):
"""Enter an open shop of type `t`; returns the shop name or {err}."""
return pg.evaluate("""(t) => {
const P=window.PROCITY, D=window.DBG; D.setSegment(2);
const s=(P.plan.shops||[]).find(x=>x.type===t && P.isOpen(x));
if(!s) return {err:'no open '+t+' shop'};
D.enterShop(s.id); return { shop: s.name };
}""", t)
def _pack_ready(pg, t):
try:
pg.wait_for_function("async () => { const m = await import('./js/interiors/interiors.js');"
f" return !!(m.getStockPack && m.getStockPack('{t}')); }}", timeout=12000)
return True
except Exception:
return False
def smoke_stock(p):
"""GRADUATED STRICT (R8): ?stock=real → real GODVERSE sleeves (batched userData.isStock atlas meshes)."""
head('SMOKE: stock=real (?stock=real — real record sleeves; STRICT r8)')
b, pg, errs = new_page(p)
try:
boot(pg, 'stock=real&dig=1')
ready = _pack_ready(pg, 'record')
r = _enter_type(pg, 'record')
if r.get('err'): FAIL(f"stock=real: {r['err']}"); return
n = pg.evaluate("() => { let n=0; window.PROCITY.interiorMode.current.group.traverse(o=>{ if(o.userData&&o.userData.isStock&&o.material&&o.material.map) n++; }); return n; }")
if not ready: FAIL(f"stock=real: record pack didn't preload in 12s (E ships it) — got {n} isStock meshes")
elif n > 0: OK(f"stock=real: {n} real atlas sleeve mesh(es) in '{r['shop']}' — non-parody, batched")
else: FAIL(f"stock=real: pack loaded but 0 isStock atlas meshes in '{r['shop']}'")
if errs: FAIL(f"stock=real: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
def smoke_weather(p):
"""GRADUATED STRICT (R8): ?weather=rain → rain THREE.Points layer + PROCITY.weather.state=='rain'."""
head('SMOKE: weather (?weather=rain — rain particles + wet ground; STRICT r8)')
b, pg, errs = new_page(p)
try:
boot(pg, 'weather=rain')
st = pg.evaluate("() => { const P=window.PROCITY; return { state: P.weather&&P.weather.state, "
"intensity: P.weather&&P.weather.intensity, rainPts: !!P.scene.getObjectByProperty('type','Points') }; }")
if st['state'] == 'rain' and st['rainPts']:
OK(f"weather=rain: rain Points present + PROCITY.weather.state=='rain' (intensity {st['intensity']})")
else: FAIL(f"weather=rain broken: {st}")
if errs: FAIL(f"weather=rain: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
def smoke_buy(p):
"""NEW (warn): buy-v0 — open dig, pull + BUY a sleeve, assert wallet cash dropped + item banked."""
head('SMOKE: buy-v0 (dig BUY → session wallet; STRICT r9)')
b, pg, errs = new_page(p)
try:
boot(pg, 'dig=1')
setup = pg.evaluate("""() => {
const P=window.PROCITY, D=window.DBG; D.setSegment(2);
const rec=(P.plan.shops||[]).find(s=>s.type==='record' && P.isOpen(s));
if(!rec) return {err:'no record shop'};
D.enterShop(rec.id);
const bins=[]; P.interiorMode.current.group.traverse(o=>{if(o.userData&&o.userData.kind==='bin')bins.push(o)});
if(!bins.length) 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();
return { cash0: P.wallet.cash(), count0: P.wallet.count() };
}""")
if setup.get('err'): FAIL(f"buy-v0: {setup['err']}"); return
pg.evaluate("() => window.dispatchEvent(new KeyboardEvent('keydown',{code:'KeyE'}))") # open dig
pg.wait_for_timeout(600)
# pull the front sleeve (canvas click, no drag → dig.pull) then click its BUY IT button
pg.evaluate("""() => { const c=window.PROCITY.renderer.domElement;
c.dispatchEvent(new PointerEvent('pointerdown',{clientX:640,clientY:360,bubbles:true}));
c.dispatchEvent(new PointerEvent('pointerup',{clientX:640,clientY:360,bubbles:true})); }""")
pg.wait_for_timeout(400)
res = pg.evaluate("""() => {
const w=window.PROCITY.wallet, cash0=w.cash(), count0=w.count();
const btn=document.querySelector('.pcdg-panel button');
if(!btn) return {err:'no BUY panel (pull failed)'};
if(btn.disabled) return {err:'BUY disabled (broke?)'};
btn.click();
return { cash0, count0, cash1:w.cash(), count1:w.count() };
}""")
if res.get('err'): FAIL(f"buy-v0: {res['err']}")
elif res['cash1'] < res['cash0'] and res['count1'] == res['count0'] + 1:
OK(f"buy-v0: bought 1 — cash {res['cash0']}{res['cash1']}, bag {res['count0']}{res['count1']}")
else: FAIL(f"buy-v0: unexpected wallet delta {res}")
if errs: FAIL(f"buy-v0: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
def smoke_patronage(p):
"""NEW (warn): peds duck into open shops — assert ≥1 ped parks at a shop door over N seconds."""
head('SMOKE: patronage (peds visit open shops; STRICT r9)')
b, pg, errs = new_page(p)
try:
boot(pg, '') # default: streamed roster + patronage on
# advance the sim ~12s of ticks at a busy midday node; count peds flagged as inside/at-door
res = pg.evaluate("""() => {
const P=window.PROCITY, D=window.DBG; D.setSegment(2);
const plan=P.plan, deg={}; for(const e of plan.streets.edges){deg[e.a]=(deg[e.a]||0)+1;deg[e.b]=(deg[e.b]||0)+1;}
let best=null,bd=0; for(const n of plan.streets.nodes){const d=deg[n.id]||0;if(d>bd){bd=d;best=n;}}
D.teleport(best.x,best.z,0); P.citizens.setPopulation(200);
let everInside=0; const seen=new Set();
for(let i=0;i<360;i++){ P.citizens.update(0.05);
const list=P.citizens._activeList||[];
for(const c of list){ if(c && (c.patronInside||c.inside||c.patronTarget) && !seen.has(c.id)){ seen.add(c.id); everInside++; } }
}
return { active:(P.citizens._activeList||[]).length, everVisited: everInside, patronOn: P.citizens.patronageOn };
}""")
if not res['patronOn']: FAIL("patronage: off (patronageOn=false) — expected default-on")
elif res['everVisited'] > 0: OK(f"patronage: {res['everVisited']} ped(s) headed to/into a shop door over ~18s (active {res['active']})")
else: FAIL(f"patronage: 0 peds visited a shop over ~18s (active {res['active']}) — check door points / timing")
if errs: FAIL(f"patronage: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
def smoke_booktoy(p):
"""NEW (warn): book/toy packs — ?stock=real static shelf stock (real spine/box atlas meshes)."""
head('SMOKE: book/toy stock (?stock=real — real spines/boxes; STRICT r9)')
b, pg, errs = new_page(p)
try:
boot(pg, 'stock=real')
for t in ('book', 'toy'):
_pack_ready(pg, t)
r = _enter_type(pg, t)
if r.get('err'): FAIL(f"book/toy: {r['err']}"); continue
# R9: real static stock is EITHER an isStock atlas mesh (records/toys) OR a buyMesh shelf
# (book spines / toy boxes, C2 buy-anywhere). Parody stock has neither.
n = pg.evaluate("""() => { let n=0; window.PROCITY.interiorMode.current.group.traverse(o=>{
const u=o.userData||{}; if((u.isStock && o.material && o.material.map) || (u.buyMesh && u.buyItems && u.buyItems.length)) n++; }); return n; }""")
if n > 0: OK(f"{t} stock: {n} real stock mesh(es) in '{r['shop']}'")
else: FAIL(f"{t} stock: 0 isStock atlas meshes in '{r['shop']}' (pack missing → parody fallback?)")
pg.evaluate("() => window.DBG.exitShop && window.DBG.exitShop()")
pg.wait_for_timeout(150)
if errs: FAIL(f"book/toy: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
def smoke_presence(p):
"""NEW (warn): interior presence — enter an OCCUPIED shop, assert browser rigs == min(occupancy,3),
dispose clean on exit, 0 errors (the C→D→F seam)."""
head('SMOKE: interior presence (browsers in occupied shops; new → warn-level)')
b, pg, errs = new_page(p)
try:
boot(pg, 'stock=real')
res = pg.evaluate("""() => {
const P=window.PROCITY, D=window.DBG; D.setSegment(2);
const plan=P.plan, deg={}; for(const e of plan.streets.edges){deg[e.a]=(deg[e.a]||0)+1;deg[e.b]=(deg[e.b]||0)+1;}
let best=null,bd=0; for(const n of plan.streets.nodes){const d=deg[n.id]||0;if(d>bd){bd=d;best=n;}}
D.teleport(best.x,best.z,0); P.citizens.setPopulation(200);
for(let i=0;i<400;i++) P.citizens.update(0.05);
let shop=null,occ=null;
for(const s of plan.shops){ const o=P.citizens.occupancyOf(s.id); if(o&&o.count>0){shop=s;occ=o;break;} }
if(!shop) return {err:'no occupied shop after patronage'};
D.enterShop(shop.id);
const kl=P.interiorMode.keepers.keepers||[];
const total=kl.length, pts=(P.interiorMode.current.browsePoints||[]).length;
return { shop:shop.name, occ:occ.count, expect:Math.min(occ.count,3), totalKeepers:total, browsePoints:pts };
}""")
if res.get('err'): WARN(f"presence: {res['err']}"); return
# total keepers = 1 counter + browsers; browsers should == min(occ,3)
browsers = res['totalKeepers'] - 1
if browsers == res['expect'] and res['browsePoints'] >= 1:
OK(f"presence: {browsers} browser(s) in '{res['shop']}' == min(occupancy {res['occ']},3); {res['browsePoints']} browse points")
else:
WARN(f"presence: browsers {browsers} != expect {res['expect']} (occ {res['occ']}, pts {res['browsePoints']}, keepers {res['totalKeepers']})")
clean = pg.evaluate("() => { window.DBG.exitShop(); return (window.PROCITY.interiorMode.keepers.keepers||[]).length; }")
if clean == 0: OK("presence: browsers disposed clean on exit (0 keepers)")
else: WARN(f"presence: {clean} keeper(s) leaked after exit")
if errs: WARN(f"presence: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
def smoke_shelfbuy(p):
"""NEW (warn): buy-anywhere — E aimed at a book spine / toy box buys over the wallet (Lane C C2)."""
head('SMOKE: shelf-buy (?stock=real book/toy pull-and-buy; new → warn-level)')
b, pg, errs = new_page(p)
try:
boot(pg, 'stock=real')
_pack_ready(pg, 'book')
res = pg.evaluate("""() => {
const P=window.PROCITY, D=window.DBG; D.setSegment(2);
const bk=(P.plan.shops||[]).find(s=>s.type==='book' && P.isOpen(s));
if(!bk) return {err:'no book shop'};
D.enterShop(bk.id);
const meshes=[]; P.interiorMode.current.group.traverse(o=>{ if(o.userData&&o.userData.buyMesh&&o.userData.buyItems&&o.userData.buyItems.length) meshes.push(o); });
if(!meshes.length) return {err:'no buy meshes (pack not loaded?)'};
const m=meshes[0], it=m.userData.buyItems[0], wp=new P.THREE.Vector3();
m.localToWorld(wp.copy(it.center));
P.camera.position.set(wp.x,wp.y,wp.z+1.0); P.camera.lookAt(wp.x,wp.y,wp.z); P.camera.updateMatrixWorld();
const cash0=P.wallet.cash(), count0=P.wallet.count();
window.dispatchEvent(new KeyboardEvent('keydown',{code:'KeyE'}));
return { shop:bk.name, buyMeshes:meshes.length, cash0, count0, cash1:P.wallet.cash(), count1:P.wallet.count() };
}""")
if res.get('err'): WARN(f"shelf-buy: {res['err']}")
elif res['cash1'] < res['cash0'] and res['count1'] == res['count0'] + 1:
OK(f"shelf-buy: bought a book spine in '{res['shop']}' — cash {res['cash0']}{res['cash1']}, bag {res['count0']}{res['count1']} ({res['buyMeshes']} buy meshes)")
else: WARN(f"shelf-buy: no purchase — {res}")
if errs: WARN(f"shelf-buy: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
def smoke_tram(p):
"""NEW (warn): ?tram=1 — Lane B tram loop. Auto-skips if not landed."""
b, pg, errs = new_page(p)
try:
boot(pg, 'tram=1')
present = pg.evaluate("() => !!window.PROCITY.scene.getObjectByName('tram')")
if not present:
print("· tram=1 not landed (no 'tram' in scene) — skipping (Lane B)")
return
head('SMOKE: tram (?tram=1; new → warn-level)')
OK("tram=1: 'tram' present in scene")
if errs: WARN(f"tram: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
def smoke_audio(p):
"""R11 audio house-law (Lane F): silent-and-happy, nothing plays pre-gesture, ?mute=1 silences,
?noassets=1 fetches zero audio, and interior beds play + release AudioNodes across enter/exit.
Each arm uses a fresh Chromium context (standing law — a reused tab's module cache burned us in R10)."""
head('SMOKE: audio (R11 — silent-happy · pre-gesture silence · mute · noassets · leak)')
# 1) boot-with-audio: 0 console errors, and nothing plays before a gesture (ctx suspended → state.ready false)
b, pg, errs = new_page(p)
try:
boot(pg, '')
pg.wait_for_function("() => window.PROCITY && window.PROCITY.audio && window.PROCITY.audio.state", timeout=8000)
st = pg.evaluate("() => { const s=window.PROCITY.audio.state; return {ready:!!s.ready, muted:!!s.muted, mode:s.mode}; }")
if not st['ready']:
OK(f"pre-gesture: audio NOT unlocked (state.ready=false, mode={st['mode']}) — silent until first gesture")
else:
FAIL(f"audio unlocked before any user gesture (autoplay-policy violation): {st}")
if errs: FAIL(f"audio boot: {len(errs)} console error(s); first: {errs[0][:140]}")
else: OK("audio boot: 0 console errors (silent-and-happy)")
finally:
b.close()
# 2) ?mute=1 → engine reports muted
b, pg, errs = new_page(p)
try:
boot(pg, 'mute=1')
pg.wait_for_function("() => window.PROCITY && window.PROCITY.audio", timeout=8000)
st = pg.evaluate("() => ({muted:!!window.PROCITY.audio.state.muted, mode:window.PROCITY.audio.state.mode})")
if st['muted']: OK(f"?mute=1: engine muted (mode={st['mode']})")
else: FAIL(f"?mute=1 not honored: {st}")
if errs: FAIL(f"?mute=1: {len(errs)} console error(s)")
finally:
b.close()
# 3) ?noassets=1 → zero audio network fetches (house law: the noassets gate implies no audio)
b, pg, errs = new_page(p)
audio_reqs = []
pg.on('request', lambda r: audio_reqs.append(r.url)
if ('/assets/audio/' in r.url or r.url.endswith('.ogg') or r.url.endswith('.m4a')) else None)
try:
boot(pg, 'noassets=1')
pg.keyboard.press('Space') # a keyboard gesture unlocks the AudioContext WITHOUT a pointer-lock attempt (headless-safe) # a gesture, so the audio path is genuinely exercised
pg.evaluate("""() => { const P=window.PROCITY, D=window.DBG; D.setSegment(2);
const s=(P.plan.shops||[]).find(x=>P.isOpen(x)); if(s) D.enterShop(s.id); }""")
pg.wait_for_timeout(800)
man = pg.evaluate("() => window.PROCITY.audio.state.manifest")
if len(audio_reqs) == 0: OK(f"?noassets=1: 0 audio fetches (manifest={man})")
else: FAIL(f"?noassets=1: {len(audio_reqs)} audio fetch(es) leaked; first {audio_reqs[0]}")
if errs: FAIL(f"?noassets=1 audio: {len(errs)} console error(s)")
finally:
b.close()
# 4) interior beds play + enter/exit is AudioNode-leak-free (live BufferSources stay bounded, not O(N))
b = p.chromium.launch()
pg = b.new_page(viewport={'width': 1280, 'height': 720})
errs = []
pg.on('console', lambda m: errs.append(m.text) if m.type == 'error' else None)
pg.on('pageerror', lambda e: errs.append(str(e)))
pg.add_init_script("""
window.__live = new Set(); window.__made = 0;
const AC = window.AudioContext || window.webkitAudioContext;
if (AC) { const orig = AC.prototype.createBufferSource;
AC.prototype.createBufferSource = function () { const s = orig.call(this);
window.__made++; window.__live.add(s);
const ostop = s.stop.bind(s); s.stop = function (w) { window.__live.delete(s); return ostop(w); };
s.addEventListener('ended', () => window.__live.delete(s)); return s; }; }
""")
try:
boot(pg, '')
pg.keyboard.press('Space') # a keyboard gesture unlocks the AudioContext WITHOUT a pointer-lock attempt (headless-safe) # user gesture → unlock the AudioContext
pg.wait_for_timeout(250)
played = pg.evaluate("""async () => {
const P=window.PROCITY, D=window.DBG; D.setSegment(2);
const types=['record','milkbar','video','opshop','book','toy'];
let entered=0, withMusic=0;
for (let rep=0; rep<2; rep++) for (const t of types) {
const s=(P.plan.shops||[]).find(x=>x.type===t && P.isOpen(x)); if(!s) continue;
D.enterShop(s.id); entered++;
const a = P.interiorMode.current && P.interiorMode.current.audio;
if (a && a.musicKey) withMusic++;
await new Promise(r=>setTimeout(r,120));
D.exitShop && D.exitShop(); await new Promise(r=>setTimeout(r,120));
}
return { entered, withMusic, ready:!!P.audio.state.ready };
}""")
pg.wait_for_timeout(600)
live = pg.evaluate("() => window.__live.size")
made = pg.evaluate("() => window.__made")
if not played['ready']:
WARN(f"audio interior: ctx never unlocked in headless — leak check inconclusive ({played}, live {live}/made {made})")
elif played['entered'] >= 4 and live <= 12:
OK(f"audio interior: {played['entered']} enter/exit, {played['withMusic']} with a music bed; live AudioNodes {live} bounded (made {made}) — leak-free")
else:
FAIL(f"audio interior: {live} live AudioNodes after {played['entered']} enter/exit (made {made}) — possible leak")
if errs: FAIL(f"audio interior: {len(errs)} console error(s); first: {errs[0][:140]}")
else: OK("audio interior: 0 console errors across enter/exit cycles")
finally:
b.close()
def smoke_gigs(p):
"""R13 v3.0-beta THE DISTRICT gig layer (Lane F). The ROUND13 §Lane F.6 gates — district determinism ·
a 4-piece band · per-venue crowd ≤ watchPoints · EVERY venue's gigKey resolves to a real manifest bed
(debt #1, now across 3 genres) · gig-night interior draw budget · street budget at the busiest venue
block · no-giants (STATURE) on the stage/crowd rigs · queue ≤ cap and drains by 'done' (pending Lane D)
· ?noassets=1 still silent-and-fine with ?gigs=1 — plus John's cover ruling, now PER VENUE (paid · free
· broke-knockback · no re-charge on re-entry · a night at two venues is two covers).
Seeds are pinned because `cover` is seeded: 20261990's tonight is a $10 night (Screaming Utes at The
Thornbury Hotel) and 1234's is free (Dazza & The Dropbears at The Imperial), so BOTH halves of "free
AND paid" are covered deterministically rather than whichever the default seed happened to roll."""
head('SMOKE: gigs (R13 v3.0-beta district — determinism · 4-piece · per-venue crowd/cover · gigKeys · budgets · queue · noassets)')
if not gigs_landed(p):
print('· gigs=1 not landed (no plan.gigs) — skipping (Lane A)')
return
# 1) schedule determinism — two independent boots (fresh contexts) → byte-equal gigs + posters
sigs = []
for _ in range(2):
b, pg, errs = new_page(p)
try:
boot(pg, '')
sigs.append(pg.evaluate("() => JSON.stringify({g: window.PROCITY.plan.gigs, p: window.PROCITY.plan.posters})"))
if errs: FAIL(f"gigs boot: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
if sigs[0] == sigs[1]:
n = len(json.loads(sigs[0])['g'])
OK(f"gig schedule deterministic: two runs byte-equal ({n} nights + posters)")
else:
FAIL('gig schedule NOT deterministic across two runs (byte-diff in plan.gigs/plan.posters)')
# 2) the gig night itself: band · crowd · dance mix · budget · the live bed · no giants
b, pg, errs = new_page(p)
try:
boot(pg, '')
pg.keyboard.press('Space') # gesture → AudioContext unlock (headless-safe, no pointer-lock)
night = pg.evaluate("""async () => {
const P = window.PROCITY, D = window.DBG, THREE = P.THREE, v = new THREE.Vector3();
const id = P.gigs.venueShopIds[0]; // R15: no scalar alias — the primary venue by id
D.setSegment(5); // NIGHT — the gig is on
const state = P.gigs.stateOf(id);
D.enterShop(id);
await new Promise(r => setTimeout(r, 500));
const room = P.interiorMode.current;
if (!room) return { entered: false, state };
const crew = P.interiorMode.crew, info = P.interiorMode.crewInfo;
const mem = crew ? crew.members : [];
// STATURE, not world crown: the band stands on Lane C's stage deck (deckY ~0.32 m), so a
// perfectly human 1.73 m guitarist has a world crown of 2.05 m and a naive [1.4,2.0] world-Y
// check fails a correct band (it did, on the first R12 verify run). Measure crown MINUS the
// figure's own base y — that is what "human-sized" actually means — and keep the separate
// "never clips the ceiling" assert on the world crown, which is the real R10 failure mode.
const figs = mem.map(m => {
let crown = 0;
m.actor.fig.updateWorldMatrix(true, true);
(m.actor.inner || m.actor.fig).traverse(o => { if (o.isBone && /head/i.test(o.name)) {
o.getWorldPosition(v); crown = Math.max(crown, v.y); } });
return { part: m.part, kind: m.kind, dance: !!m.dance,
crown: +crown.toFixed(3), stature: +(crown - (m.base.y || 0)).toFixed(3),
seated: !!(m.actor.fig.userData && m.actor.fig.userData.procitySeated) }; // [R16 #3]
});
// Force one clean interior frame rather than trusting whatever the last rAF left in
// renderer.info — a throttled/backgrounded tab otherwise reports the last STREET frame
// (composer + bloom), which reads as a wildly over-budget room. Bit us on the first verify.
P.renderer.info.reset();
P.renderer.render(P.interiorMode.scene, P.camera);
return {
entered: true, state, H: +room.dims.H.toFixed(2),
watchPoints: room.watchPoints.length, stage: !!room.stage,
band: info ? info.band : 0, crowd: info ? info.crowd : 0,
dancers: mem.filter(m => m.part === 'crowd' && m.dance).length,
gigKey: room.audio.gigKey || null, musicKey: room.audio.musicKey || null,
draws: P.renderer.info.render.calls, tris: P.renderer.info.render.triangles, figs,
};
}""")
if not night.get('entered'):
FAIL(f"gig night: could not enter the venue at NIGHT ({night})")
else:
if night['state'] == 'on': OK(f"gig state machine: NIGHT → 'on' (venue {night['H']}m room)")
else: FAIL(f"gig state machine: NIGHT should be 'on', got '{night['state']}'")
# band is a 4-piece (R13 debt #3: front-line trio + a drummer on C's riser)
if night['band'] == 4: OK('band: 4-piece on the stage (front-line trio + drummer)')
else: FAIL(f"band: expected a 4-piece (trio + drummer), got {night['band']}")
# crowd cap ≤ watchPoints
if night['crowd'] <= night['watchPoints'] and night['crowd'] > 0:
OK(f"crowd: {night['crowd']} at {night['watchPoints']} watch points (≤ cap)")
else:
FAIL(f"crowd {night['crowd']} vs {night['watchPoints']} watch points (must be >0 and ≤ cap)")
# John's "a bit of both": the crowd must not be all-standing or all-dancing
d, c = night['dancers'], night['crowd']
if c and 0 < d < c: OK(f"crowd mix: {d} dancing / {c - d} standing — 'a bit of both'")
else: FAIL(f"crowd is not mixed: {d} dancing of {c} (John's ruling: some of both)")
# the live bed actually resolves (the C↔E gigKey seam F bridges — see LANE_F_NOTES §12)
if night['gigKey']: OK(f"live bed: room.audio.gigKey = {night['gigKey']} (F resolves → manifest key)")
else: FAIL('live bed: no room.audio.gigKey on a gig-on venue (Lane C opts.gig seam)')
# gig-night interior draw budget (ROUND12: ≤350 incl. the crowd)
if night['draws'] <= GIG_DRAWS_MAX:
OK(f"gig-night interior budget: {night['draws']} draws ≤ {GIG_DRAWS_MAX} (tris {night['tris']:,})")
else:
FAIL(f"gig-night interior budget: {night['draws']} draws > {GIG_DRAWS_MAX} (tris {night['tris']:,})")
# no giants on the stage/crowd rigs (R10 gate, inherited by the gig path)
rigs = [f for f in night['figs'] if f['kind'] == 'rig']
bad = [f for f in rigs if not stature_ok(f) or f['crown'] >= night['H']] # [R16 #3] seated exemption
if not rigs:
WARN(f"no-giants: crew spawned as placeholders (no fleet) — rig check n/a ({len(night['figs'])} figs)")
elif bad:
FAIL(f"no-giants: {len(bad)} gig rig(s) out of [1.4,2.0]m stature or through the {night['H']}m ceiling: {bad[:3]}")
else:
lo = min(f['stature'] for f in rigs); hi = max(f['stature'] for f in rigs)
hc = max(f['crown'] for f in rigs)
OK(f"no-giants: {len(rigs)} band+crowd rigs human-sized ({lo:.2f}{hi:.2f} m stature; "
f"tallest crown {hc:.2f} m under the {night['H']} m ceiling)")
if errs: FAIL(f"gig night: {len(errs)} console error(s); first: {errs[0][:140]}")
else: OK('gig night: 0 console errors')
finally:
b.close()
# 2b) THE DISTRICT (R13): every playing venue fires — its live bed resolves to a REAL manifest bar for
# its genre (debt #1, now across pub/band_room/rsl), its crowd is capped at its OWN watch points, and it
# is a 4-piece. Plus the queue ≤ cap / drains-by-done probe (WARN-skips until Lane D wires the seam).
music_keys = set()
try:
mani = json.loads((ROOT / 'web' / 'assets' / 'manifest.json').read_text())
music_keys = set((mani.get('audio', {}).get('music', {}) or {}).keys())
except Exception as e:
WARN(f"district: could not read manifest music keys ({e}) — gigKey-resolves check is JS-only")
b, pg, errs = new_page(p)
try:
boot(pg, '')
pg.keyboard.press('Space')
district = pg.evaluate("""async () => {
const P = window.PROCITY, D = window.DBG, THREE = P.THREE, v = new THREE.Vector3();
D.setSegment(5); // NIGHT — every playing venue is 'on'
const ids = P.gigs.venueShopIds || [];
const out = [];
for (const id of ids) {
const shop = (P.plan.shops || []).find(s => s.id === id);
if (!P.gigs.onOf(id)) { out.push({ id, kind: shop && shop.venueKind, dark: true }); continue; }
D.enterShop(id);
await new Promise(r => setTimeout(r, 450));
const room = P.interiorMode.current, info = P.interiorMode.crewInfo;
const mem = P.interiorMode.crew ? P.interiorMode.crew.members : [];
const figs = mem.map(m => {
let crown = 0; m.actor.fig.updateWorldMatrix(true, true);
(m.actor.inner || m.actor.fig).traverse(o => { if (o.isBone && /head/i.test(o.name)) {
o.getWorldPosition(v); crown = Math.max(crown, v.y); } });
return { part: m.part, kind: m.kind, stature: +(crown - (m.base.y || 0)).toFixed(3),
seated: !!(m.actor.fig.userData && m.actor.fig.userData.procitySeated) }; // [R16 #3]
});
P.renderer.info.reset(); P.renderer.render(P.interiorMode.scene, P.camera);
out.push({ id, kind: shop && shop.venueKind, genreKey: shop && shop.genreKey,
gigKey: (room && room.audio && room.audio.gigKey) || null,
band: info ? info.band : 0, crowd: info ? info.crowd : 0,
watchPoints: room ? room.watchPoints.length : 0, figs });
D.exitShop(); await new Promise(r => setTimeout(r, 200));
}
return { ids, out };
}""")
playing = [d for d in district['out'] if not d.get('dark')]
OK(f"district: {len(district['ids'])} venue(s) — {len(playing)} playing tonight, "
f"{len(district['out']) - len(playing)} dark (reads true to the seeded week)")
genres_seen = set()
for d in playing:
tag = f"{d['kind']} #{d['id']}"
want = f"gig-{d['genreKey']}"
if d['gigKey'] == want and (not music_keys or want in music_keys):
OK(f" {tag}: live bed '{d['gigKey']}' resolves to a real manifest bar (genre {d['genreKey']})")
genres_seen.add(d['genreKey'])
else:
FAIL(f" {tag}: gigKey '{d['gigKey']}' != '{want}' or not in manifest.music {sorted(music_keys)}")
if 0 < d['crowd'] <= d['watchPoints']:
OK(f" {tag}: crowd {d['crowd']}{d['watchPoints']} watch points")
else:
FAIL(f" {tag}: crowd {d['crowd']} vs {d['watchPoints']} watch points (must be >0 and ≤ cap)")
if d['band'] == 4: OK(f" {tag}: 4-piece band")
else: FAIL(f" {tag}: expected a 4-piece, got {d['band']}")
rigs = [f for f in d['figs'] if f['kind'] == 'rig']
bad = [f for f in rigs if not stature_ok(f)] # [R16 #3] seated exemption
if rigs and bad: FAIL(f" {tag}: {len(bad)} giant rig(s) by stature: {bad[:2]}")
if len(genres_seen) >= 2:
OK(f"district: {len(genres_seen)} distinct genres verified live ({', '.join(sorted(genres_seen))})")
# queue ≤ cap and drains by 'done' — Lane D's outdoor line (charter item). D shipped the VenueQueue
# class and left the street-side lifecycle to F, which wires it in the shell (spawn at doors, drive
# each frame, disposeAll at done); read via P.queueCountOf(id). The line is a seeded short 26.
q = pg.evaluate("""async () => {
const P = window.PROCITY, D = window.DBG;
if (typeof P.queueCountOf !== 'function') return { wired: false };
const id = P.gigs.venueShopIds[0];
D.setSegment(4); await new Promise(r => setTimeout(r, 700)); // DUSK — doors open, the line forms
const atDoors = P.queueCountOf(id) | 0;
D.setSegment(0); await new Promise(r => setTimeout(r, 700)); // DAWN — closing time ('done'), line gone
const atDone = P.queueCountOf(id) | 0;
return { wired: true, atDoors, atDone };
}""")
QUEUE_CAP = 6
if not q.get('wired'):
FAIL("queue: P.queueCountOf(id) not exposed — F's shell wiring missing")
elif 0 < q['atDoors'] <= QUEUE_CAP and q['atDone'] == 0:
OK(f"queue: {q['atDoors']} punters in line at doors (≤ cap {QUEUE_CAP}), drained to 0 by 'done'")
else:
FAIL(f"queue: at doors {q['atDoors']} (want 1..{QUEUE_CAP}), at done {q['atDone']} (want 0)")
if errs: FAIL(f"district: {len(errs)} console error(s); first: {errs[0][:140]}")
else: OK('district: 0 console errors across all venues')
finally:
b.close()
# 2c) STREET BUDGET at the busiest venue block (R13 law: ≤300 draws / ≤200k tris with the lit frontage,
# town-wide posters and the queue zone all in). Park on the footpath in front of the primary venue at
# NIGHT with the gig on, let the block stream in, and sample the settled composer frame.
b, pg, errs = new_page(p)
try:
boot(pg, '')
st = pg.evaluate("""async () => {
const P = window.PROCITY, D = window.DBG;
D.setSegment(5); // NIGHT — frontage lit, posters up
const id = P.gigs.venueShopIds[0], shop = (P.plan.shops || []).find(s => s.id === id);
const lot = shop && (P.plan.lots || []).find(l => l.id === shop.lot);
if (lot) {
const fx = Math.sin(lot.ry || 0), fz = Math.cos(lot.ry || 0);
D.teleport(lot.x + fx * 10, lot.z + fz * 10, Math.atan2(-fx, -fz)); // 10 m out, facing the venue
}
await new Promise(r => setTimeout(r, 700)); // chunks/frontage/posters/pools settle
let draws = Infinity, tris = 0; // take the settled floor (ignore stream spikes)
for (let i = 0; i < 12; i++) {
await new Promise(r => requestAnimationFrame(r));
if (P.renderer.info.render.calls < draws) { draws = P.renderer.info.render.calls; tris = P.renderer.info.render.triangles; }
}
return { draws, tris, venue: shop && shop.name };
}""")
if st['draws'] <= STREET_DRAWS_MAX and st['tris'] <= STREET_TRIS_MAX:
OK(f"street budget @ venue block ({st['venue']}): {st['draws']} draws ≤ {STREET_DRAWS_MAX}, {st['tris']:,} tris ≤ {STREET_TRIS_MAX:,}")
else:
FAIL(f"street budget @ venue block ({st['venue']}): {st['draws']} draws / {st['tris']:,} tris over ({STREET_DRAWS_MAX} / {STREET_TRIS_MAX:,})")
if errs: FAIL(f"street budget: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
# 3) cover ruling, PAID night (seed 20261990 → $10): debit once · no re-charge on re-entry · broke = knockback
b, pg, errs = new_page(p)
try:
boot(pg, '')
paid = pg.evaluate("""async () => {
const P = window.PROCITY, D = window.DBG;
const id = P.gigs.venueShopIds[0]; // R15: no scalar alias — the primary venue by id
D.setSegment(5);
const cover = P.gigs.coverOf(id);
if (!cover) return { skip: true, cover };
const before = P.wallet.cash();
D.enterShop(id); await new Promise(r => setTimeout(r, 350));
const inRoom1 = !!P.interiorMode.current, afterPay = P.wallet.cash();
D.exitShop(); await new Promise(r => setTimeout(r, 250));
const mid = P.wallet.cash();
D.enterShop(id); await new Promise(r => setTimeout(r, 350));
const inRoom2 = !!P.interiorMode.current, afterReentry = P.wallet.cash();
D.exitShop(); await new Promise(r => setTimeout(r, 250));
// now go broke and try again on the SAME night (stamp already paid → still free), then roll the
// night over (MORNING clears the stamp) and try broke → must be knocked back at the door.
P.wallet.buy({ title: 'qa: drain', price: P.wallet.cash() });
D.setSegment(1); D.setSegment(5); // NIGHT → MORNING → NIGHT = a new night, cover due
const brokeCash = P.wallet.cash();
D.enterShop(id); await new Promise(r => setTimeout(r, 350));
const inRoomBroke = !!P.interiorMode.current;
return { cover, before, afterPay, inRoom1, mid, afterReentry, inRoom2, brokeCash, inRoomBroke,
inv: P.wallet.inventory().map(i => i.title) };
}""")
if paid.get('skip'):
WARN(f"cover: seed {SEED} tonight is free (cover 0) — paid path not exercised here")
else:
if paid['inRoom1'] and paid['afterPay'] == paid['before'] - paid['cover']:
OK(f"cover: ${paid['cover']} debited once at the door (${paid['before']} → ${paid['afterPay']}), entry granted")
else:
FAIL(f"cover: wrong debit/entry on a paid night: {paid}")
if paid['inRoom2'] and paid['afterReentry'] == paid['mid']:
OK(f"cover: re-entry the same night is FREE (no re-charge, still ${paid['afterReentry']})")
else:
FAIL(f"cover: re-entry re-charged or was refused: {paid}")
if not paid['inRoomBroke'] and paid['brokeCash'] < paid['cover']:
OK(f"cover: broke (${paid['brokeCash']}) on a NEW night → polite knockback, no entry")
else:
FAIL(f"cover: broke player got in (or the night never rolled): {paid}")
if errs: FAIL(f"cover: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
# 4) cover ruling, FREE night (seed 1234 → cover 0): walk straight in, wallet untouched
b, pg, errs = new_page(p)
try:
pg.goto(f'{HOST}/index.html?seed={FREE_GIG_SEED}&dbg=1')
pg.wait_for_function("window.DBG && window.DBG.ready === true", timeout=20000)
pg.evaluate("() => { const o=document.getElementById('pc-start'); if(o) o.style.display='none'; }")
free = pg.evaluate("""async () => {
const P = window.PROCITY, D = window.DBG;
const id = P.gigs.venueShopIds[0]; // R15: no scalar alias — the primary venue by id
D.setSegment(5);
const cover = P.gigs.coverOf(id), before = P.wallet.cash();
D.enterShop(id); await new Promise(r => setTimeout(r, 350));
return { cover, before, after: P.wallet.cash(), inRoom: !!P.interiorMode.current, band: P.gigs.bandNameOf(id) };
}""")
if free['cover'] == 0 and free['inRoom'] and free['after'] == free['before']:
OK(f"cover: free night (seed {FREE_GIG_SEED}, {free['band']}) → walked straight in, wallet untouched (${free['after']})")
else:
FAIL(f"cover: free-night entry wrong: {free}")
if errs: FAIL(f"free night: {len(errs)} console error(s); first: {errs[0][:140]}")
finally:
b.close()
# 5) ?noassets=1 + ?gigs=1 — still silent-and-fine: zero audio/band-file fetches, gig still happens
b, pg, errs = new_page(p)
reqs = []
pg.on('request', lambda r: reqs.append(r.url)
if ('/assets/audio/' in r.url or r.url.endswith('.ogg') or r.url.endswith('.m4a')
or 'custom_bands' in r.url or r.url.endswith('.glb')) else None)
try:
boot(pg, 'noassets=1')
pg.keyboard.press('Space')
na = pg.evaluate("""async () => {
const P = window.PROCITY, D = window.DBG;
const id = P.gigs.venueShopIds[0]; // R15: no scalar alias — the primary venue by id
D.setSegment(5);
D.enterShop(id); await new Promise(r => setTimeout(r, 450));
const info = P.interiorMode.crewInfo;
return { state: P.gigs.stateOf(id), inRoom: !!P.interiorMode.current, band: info && info.band,
crowd: info && info.crowd, gigs: (P.plan.gigs || []).length,
bandName: P.gigs.bandNameOf(id), audio: P.audio && P.audio.state.manifest };
}""")
if na['inRoom'] and na['band'] == 4 and na['crowd'] > 0:
OK(f"?noassets=1&gigs=1: the gig still happens (4-piece band, crowd {na['crowd']}, placeholders)")
else:
FAIL(f"?noassets=1&gigs=1: no gig without assets: {na}")
if len(reqs) == 0:
OK(f"?noassets=1&gigs=1: 0 audio/GLB/band-file fetches (manifest={na['audio']}, bands from the generator)")
else:
FAIL(f"?noassets=1&gigs=1: {len(reqs)} asset fetch(es) leaked; first {reqs[0]}")
if errs: FAIL(f"?noassets=1&gigs=1: {len(errs)} console error(s); first: {errs[0][:140]}")
else: OK('?noassets=1&gigs=1: 0 console errors (silent-and-happy)')
finally:
b.close()
GODVERSE_TOWN = 'redhill_godverse'
GODVERSE_SHOP = 3962749 # Monster Robot Party, 147 Musgrave Rd, Red Hill QLD — the one REAL crate
GODVERSE_BASE = f'assets/stock_godverse/{GODVERSE_SHOP}/'
STOCK_MANIFEST = 'assets/stock_godverse/index.json'
def smoke_real_crate(p):
"""R26 — THE BETA GATE: every crate different, sourcing visible, and NOT ONE 404.
The lineage of this gate is the epoch's whole argument. R23's #7a would have gone green on "real
covers render" while the room held the generic v2 pack. R24's brief then said assert id equality —
which also couldn't discriminate, because both packs numbered items positionally and every atlas id
was a generic-pack id too. What survives is what a wrong pack cannot fake: TITLES only this crate
has, and the TEXTURE URL the GPU actually samples.
R26 adds the two beta claims. DISTINCTNESS: 15 keyed crates, pairwise title-disjoint — asserted
across the indexes of record, so it cannot pass by entering one lucky shop. SOURCING (C's §7.2b,
which correctly overruled the brief's `tier`): every index declares real|mint, a mint crate can
never report as real, and the gate PRINTS it.
And the 404s are gone for the right reason. R24 probed every keyed shop and ate a 404 for each one
with no atlas; F's smoke could only tolerate that by naming it — a tolerance is a confession. G's
manifest now says which (shop, type) pairs exist, so the shell asks for nothing else. ZERO console
errors, NO attribution exceptions. Subject absent ⇒ SKIP with the reason, never a quiet pass."""
head(f'SMOKE: the beta crate gate (R26 — 15 crates distinct · sourcing · zero 404s · {GODVERSE_TOWN})')
b, pg, errs = new_page(p)
four04 = []
pg.on('response', lambda r: four04.append(r.url) if r.status == 404 else None)
try:
pg.goto(f'{HOST}/{STOCK_MANIFEST}')
if 'godverseShopId' not in pg.content():
print('· SKIP: no atlas manifest — subject absent, nothing asserted (Lane G)'); return
boot(pg, f'plansrc=osm&town={GODVERSE_TOWN}&stock=real&dig=1')
res = pg.evaluate("""async ({manifest, real, base}) => {
const D = window.DBG, P = window.PROCITY;
const man = await (await fetch(manifest)).json();
// ── DISTINCTNESS across the indexes of record (not just the shop we walk into) ──
const crates = [];
for (const sh of (man.shops || [])) for (const t of (sh.types || [])) {
const u = `assets/stock_godverse/${sh.godverseShopId}/stock_${t}_index.json`;
const r = await fetch(u);
if (!r.ok) return { fail: `manifest lists ${u} but it 404s` };
const idx = await r.json();
crates.push({ id: sh.godverseShopId, type: t, manSourcing: sh.sourcing,
sourcing: idx.sourcing || null,
titles: (idx.items || []).map(i => i.title) });
}
const gen = {};
for (const t of ['record', 'book', 'toy'])
gen[t] = new Set((((await (await fetch(`assets/models/stock_${t}_index.json`)).json()).items) || [])
.map(i => i.title));
let shared = 0, vsGeneric = 0, noSourcing = 0, manMismatch = 0;
for (let i = 0; i < crates.length; i++) {
if (!crates[i].sourcing) noSourcing++;
if (crates[i].sourcing !== crates[i].manSourcing) manMismatch++;
if (crates[i].titles.some(t => gen[crates[i].type] && gen[crates[i].type].has(t))) vsGeneric++;
for (let j = i + 1; j < crates.length; j++) {
const a = new Set(crates[i].titles);
if (crates[j].titles.some(t => a.has(t))) shared++;
}
}
// ── the ROOM binds to the right crate: one REAL, one MINT, one soft fall ──
const byId = new Map((man.shops || []).map(s => [s.godverseShopId, s]));
const stocked = (P.plan.shops || []).filter(s => { const e = byId.get(s.godverseShopId);
return e && (e.types || []).includes(s.type); });
const unfetch = (P.plan.shops || []).find(s => { if (!s.godverseShopId) return false;
const e = byId.get(s.godverseShopId);
return !e || !(e.types || []).includes(s.type); });
const visit = async (shop) => {
D.exitShop(); await new Promise(r => setTimeout(r, 250));
D.enterShop('g:' + shop.godverseShopId); await new Promise(r => setTimeout(r, 1200));
const si = D.stockInfo();
const c = crates.find(c => c.id === shop.godverseShopId && c.type === shop.type);
const own = c ? new Set(c.titles) : new Set();
return { name: si.name, gid: si.godverseShopId, want: shop.godverseShopId, type: si.type,
base: si.base, packItems: si.packItems,
sourcing: c ? c.sourcing : null,
inOwn: si.packTitles.filter(t => own.has(t)).length,
fromOwnTex: si.texUrls.filter(u => u.includes(`stock_godverse/${shop.godverseShopId}/`)).length,
fromGenericTex: si.texUrls.filter(u => u.includes('assets/models/stock_')).length };
};
const realShop = stocked.find(s => s.godverseShopId === real);
const mintShop = stocked.find(s => (byId.get(s.godverseShopId) || {}).sourcing === 'mint');
const out = { crates: crates.length, shared, vsGeneric, noSourcing, manMismatch,
stocked: stocked.length,
realRoom: realShop ? await visit(realShop) : null,
mintRoom: mintShop ? await visit(mintShop) : null };
if (unfetch) { D.exitShop(); await new Promise(r => setTimeout(r, 250));
D.enterShop('g:' + unfetch.godverseShopId); await new Promise(r => setTimeout(r, 900));
const o = D.stockInfo();
out.softFall = { name: o.name, gid: o.godverseShopId, want: unfetch.godverseShopId,
packItems: o.packItems }; }
return out;
}""", {'manifest': STOCK_MANIFEST, 'real': GODVERSE_SHOP, 'base': GODVERSE_BASE})
if res.get('fail'):
FAIL(f"beta crate gate: {res['fail']}"); return
# ── distinctness (independently measured; G reported 15/15) ──
pairs = res['crates'] * (res['crates'] - 1) // 2
OK(f"DISTINCTNESS: {res['crates']} keyed crates, {pairs} pairs, {res['shared']} sharing a title") \
if res['shared'] == 0 else FAIL(f"DISTINCTNESS: {res['shared']} crate pair(s) share titles")
OK(f"DISTINCTNESS: 0/{res['crates']} crates overlap the generic v2 pack") \
if res['vsGeneric'] == 0 else FAIL(f"{res['vsGeneric']} crate(s) overlap the generic pack")
# ── sourcing (C §7.2b — an index with no `sourcing` FAILS; the gate PRINTS it) ──
OK(f"SOURCING: {res['crates']}/{res['crates']} indexes declare sourcing") \
if res['noSourcing'] == 0 else FAIL(f"SOURCING: {res['noSourcing']} index(es) declare none")
OK('SOURCING: every index agrees with the manifest') \
if res['manMismatch'] == 0 else FAIL(f"SOURCING: {res['manMismatch']} index/manifest disagreement(s)")
for label, want in (('realRoom', 'real'), ('mintRoom', 'mint')):
r = res.get(label)
if not r:
FAIL(f"{label}: no {want} crate reachable in {GODVERSE_TOWN}"); continue
if r['gid'] != r['want']:
FAIL(f"{label}: entered the WRONG shop — wanted {r['want']}, got {r['gid']}"); continue
print(f" room: {r['name']} (godverse {r['gid']}, {r['type']}) → {r['base']} [sourcing: {r['sourcing']}]")
OK(f" {want}: sourcing reports '{r['sourcing']}'") if r['sourcing'] == want \
else FAIL(f" {want} crate reports sourcing '{r['sourcing']}' — a {want} crate must never report otherwise")
OK(f" {want}: TITLE equality {r['inOwn']}/{r['packItems']} from its OWN crate") \
if (r['packItems'] > 0 and r['inOwn'] == r['packItems']) \
else FAIL(f" {want}: only {r['inOwn']}/{r['packItems']} titles are its own")
OK(f" {want}: TEXTURE provenance {r['fromOwnTex']} own / {r['fromGenericTex']} generic") \
if (r['fromOwnTex'] > 0 and r['fromGenericTex'] == 0) \
else FAIL(f" {want}: textures {r['fromOwnTex']} own / {r['fromGenericTex']} generic — wrong file")
sf = res.get('softFall')
if sf:
if sf['gid'] != sf['want']: FAIL(f"soft fall: entered the wrong shop ({sf['gid']} != {sf['want']})")
elif sf['packItems'] == 0: OK(f"soft fall: keyed, unfetchable type \"{sf['name']}\" → parody, silently")
else: FAIL(f"soft fall BROKEN: \"{sf['name']}\" inherited {sf['packItems']} items")
# ── ZERO 404s. No exceptions. The manifest is what makes this honest. ──
OK('ZERO 404s — the manifest means we never ask for an atlas that is not there') \
if not four04 else FAIL(f"{len(four04)} unexpected 404(s) — the manifest should have prevented every one; first: {four04[0]}")
OK('0 console errors (no attribution exceptions)') if not errs \
else FAIL(f'the crate: {len(errs)} console error(s); first: {errs[0][:140]}')
finally:
b.close()
def smoke_continuity(p):
"""R15 identity continuity (ledger #3): the ped who queued/surged into a venue turns up in its crowd.
Exercises F's real relay end-to-end — VenueQueue.admitOne() → onAdmit → citizens.recordVenueEntry →
tonightRoster(id) → GigCrew.spawn({roster}) → the front crowd slots carry those identities. Asserts the
seam on identities (crewInfo.fromRoster + member.pedIndex ∈ tonightRoster), the invariant crowd ⊇
roster ∩ cap, and that an EMPTY roster injects nobody (D's byte-identical law, proven here on the shell
side; D's sim soak proves the sim side). Also ledger #5 — reports SETTLED tris (after the async
instrument GLBs land), not the pre-load number C caught the R13 smoke reporting."""
head('SMOKE: continuity (R15 — the ped you followed is in the crowd · empty-roster clean · settled tris)')
if not gigs_landed(p):
print('· gigs=1 not landed (no plan.gigs) — skipping (Lane A)')
return
b, pg, errs = new_page(p)
try:
boot(pg, '')
pg.keyboard.press('Space')
res = pg.evaluate("""async () => {
const P = window.PROCITY, D = window.DBG;
const id = P.gigs.venueShopIds[0];
const rosterOf = () => (P.citizens.tonightRoster(id) || []).map(e => e.pedIndex);
// 1) EMPTY-ROSTER PATH — enter before any admit; crew must inject only what's in the roster
// (usually 0). This is the shell side of D's "empty roster = byte-identical to R13" law.
D.setSegment(5); // NIGHT — the gig is on
const rosterAtEmpty = rosterOf();
D.enterShop(id); await new Promise(r => setTimeout(r, 550));
const ei = P.interiorMode.crewInfo;
const emptyFromRoster = ei ? ei.fromRoster : -1, emptyCrowd = ei ? ei.crowd : 0;
D.exitShop(); await new Promise(r => setTimeout(r, 250));
// 2) POPULATED PATH — let the street loop spawn the queue, then admit a few EXPLICITLY (rAF
// auto-drain is throttled in automation, per B's note). Each admit fires onAdmit → recordVenueEntry.
for (let i = 0; i < 8; i++) await new Promise(r => requestAnimationFrame(r));
const q = P.venueQueues && P.venueQueues.get(id);
const admitted = [];
if (q) for (let i = 0; i < 3 && q.count() > 0; i++) { const e = q.admitOne(); if (e) admitted.push(e.pedIndex); }
const roster = rosterOf();
D.enterShop(id); await new Promise(r => setTimeout(r, 700)); // build + crew spawn
const info = P.interiorMode.crewInfo, room = P.interiorMode.current;
const crowd = P.interiorMode.crew ? P.interiorMode.crew.members.filter(m => m.part === 'crowd') : [];
const rosterCrowd = crowd.filter(m => m.fromRoster).map(m => m.pedIndex);
// 3) SETTLED TRIS (ledger #5) — immediately after build vs after the instrument GLBs + rig atlas land
P.renderer.info.reset(); P.renderer.render(P.interiorMode.scene, P.camera);
const trisImmediate = P.renderer.info.render.triangles, draws = P.renderer.info.render.calls;
await new Promise(r => setTimeout(r, 1600));
P.renderer.info.reset(); P.renderer.render(P.interiorMode.scene, P.camera);
const trisSettled = P.renderer.info.render.triangles, drawsSettled = P.renderer.info.render.calls;
return { rosterAtEmpty, emptyFromRoster, emptyCrowd, admitted, roster, rosterCrowd,
fromRoster: info ? info.fromRoster : -1, crowd: info ? info.crowd : 0,
watchPoints: room ? room.watchPoints.length : 0,
trisImmediate, draws, trisSettled, drawsSettled };
}""")
# empty-roster path: crew injects exactly what's in the roster at that moment (0 unless surge beat us)
want_empty = min(len(res['rosterAtEmpty']), res['emptyCrowd'])
if res['emptyFromRoster'] == want_empty:
OK(f"empty roster: crew injected {res['emptyFromRoster']} from a {len(res['rosterAtEmpty'])}-entry roster "
f"(crowd {res['emptyCrowd']} — no phantom continuity, R13-identical path)")
else:
FAIL(f"empty roster: fromRoster {res['emptyFromRoster']} != roster∩cap {want_empty} (roster leaked)")
# the queue admits were recorded into tonightRoster (the onAdmit → recordVenueEntry relay)
roster, admitted, rosterCrowd = res['roster'], res['admitted'], res['rosterCrowd']
if admitted and all(a in roster for a in admitted):
OK(f"relay: {len(admitted)} queue admits → tonightRoster ({admitted} ⊆ roster of {len(roster)})")
else:
FAIL(f"relay: queue admits {admitted} not all in tonightRoster {roster} (onAdmit→recordVenueEntry broke)")
# THE SEAM — crowd ⊇ roster ∩ cap, and every roster-crowd member's pedIndex is genuinely in the roster
cap = res['crowd']
want = min(len(roster), cap)
subset_ok = set(rosterCrowd).issubset(set(roster))
if res['fromRoster'] == want and want > 0 and subset_ok and set(roster[:cap]).issubset(set(rosterCrowd)):
OK(f"continuity seam: {res['fromRoster']} of {cap} crowd came from the roster "
f"(crowd ⊇ roster∩cap ✓, all pedIndex ∈ tonightRoster ✓) — the ped you followed is in the crowd")
else:
FAIL(f"continuity seam: fromRoster {res['fromRoster']} vs roster∩cap {want}; rosterCrowd {rosterCrowd} "
f"vs roster {roster} (subset={subset_ok})")
# ledger #5 — settled-tris honesty (report both; interior gate is draws, which stays low)
OK(f"settled tris: {res['trisImmediate']:,} pre-load → {res['trisSettled']:,} settled "
f"(instrument GLBs; draws {res['draws']}{res['drawsSettled']}{GIG_DRAWS_MAX} gate)")
if errs: FAIL(f"continuity: {len(errs)} console error(s); first: {errs[0][:140]}")
else: OK('continuity: 0 console errors')
finally:
b.close()
def main():
srv = ensure_server()
try:
with sync_playwright() as p:
flags = list(KNOWN_FLAGS)
if plansrc_landed(p):
flags.append('plansrc=osm'); print("· plansrc=osm detected as landed — included")
else:
print("· plansrc=osm not landed (Lane A) — skipping that flag")
classic_regression(p) # [R16] the frozen v2 covenant moved here: ?classic=1 → byte-identical
default_boot_gate(p) # [R16] the flip: the no-flag boot is the full experience (all four on)
interior_budget(p)
for fl in flags:
smoke(p, fl.split('=')[0], fl)
smoke(p, 'ALL-ON combo', '&'.join(flags), is_combo=True)
smoke_stock(p) # STRICT (r8) — ?stock=real real record sleeves
smoke_weather(p) # STRICT (r8) — ?weather=rain
smoke_buy(p) # STRICT (r9) — buy-v0
smoke_patronage(p) # STRICT (r9) — peds visit open shops
smoke_booktoy(p) # STRICT (r9) — book/toy real stock
smoke_presence(p) # new (warn) — interior presence (browsers)
smoke_shelfbuy(p) # new (warn) — buy-anywhere (book/toy shelves)
smoke_tram(p) # new (warn) — tram (auto-skips if not landed)
smoke_audio(p) # R11 — audio house-law (silent-happy · pre-gesture · mute · noassets · leak)
smoke_gigs(p) # R12/13 — the district gig layer (schedule · band · crowd · budget · cover · noassets)
smoke_continuity(p) # R15 — identity continuity (the ped you followed is in the crowd · settled tris)
smoke_real_crate(p) # R24 — THE REAL CRATE (per-shop atlas identity · soft fall · no-op off the path)
finally:
if srv: srv.terminate()
head('VERDICT')
print(json.dumps({'fails': len(fails), 'warns': len(warns)}, indent=0))
if fails:
print(f"\033[31m● flags_check RED\033[0m — {len(fails)} failure(s), {len(warns)} warning(s).")
for f in fails: print(' FAIL:', f)
sys.exit(1)
print(f"\033[32m● flags_check GREEN\033[0m — {len(warns)} warning(s).")
for w in warns: print(' warn:', w)
sys.exit(0)
if __name__ == '__main__':
main()