#!/usr/bin/env python3 """PROCITY Lane B — R40 §40.3 gate: the ?r= budget law · classic's town selector · fog signage. [Lane B R40] NEW FILE (races no other lane's edits). Three rulings, each with both arms asserted so none of them is vacuous. → Lane F: one line in qa.sh wires it (see LANE_B_NOTES §40). 1. THE ?r= BUDGET LAW (§40.3-1). `?r=` above the auto radius is a DIAGNOSTIC boot and now declares its own law (index.html: `PROCITY.budget` = {draws: 420, tris: 200k, diagnostic: true} + a console warn). Measured R40 (pin method, stepwalk 2 m yaw 0 laps 2, synthetic, seed 20261990): default 282 noon / 291 night vs ?r=3 382 noon / 391 night at 45-48 live chunks — structural (the live-chunk window, not a regression), so the round's ruling is GATE, not shave. Asserted here: the declaration (both arms: default says 300/not-diagnostic, r=3 says 420/diagnostic + warns) AND the measurement — a night stepwalk under r=3 must stay ≤ the boot's OWN declared ceiling. If the town grows past 420 this gate goes red and the ceiling is re-pinned consciously. If r=3 ever measures ≤300 the exemption is unexercised — reported, not failed. 2. CLASSIC'S TOWN SELECTOR (§40.3-2, Fable's R40 ruling: classic SHOWS it — "information, not gameplay state"). Asserted: `?classic=1` boots with the selector present, ≥20 town options, and its towns-fetch surface is exactly the one named POST_V2_EXCEPTION (assets/towns/index.json). 3. FOG SIGNAGE (§40.3-3, classic-gated by construction). Asserted both arms: the default boot's HUD carries the `#pc-street` row and it RESOLVES (non-"—" standing at a shopfront — on the synthetic that is a locality label, LANE_A_NOTES §39: print `label`, never branch on town type); classic's HUD contains NO `#pc-street` node at all (setAddresses never called ⇒ byte-identical DOM). Run: tools/.venv/bin/python tools/qa/r40_lane_b.py (own no-store server, fresh contexts) """ import pathlib, sys sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent)) from budget_walk import SEED, SPINE_JS, STEPWALK_JS, boot, serve, summarise # the pinned method, not a reimplementation try: from playwright.sync_api import sync_playwright except ImportError: sys.exit("playwright not installed — tools/.venv/bin/pip install playwright") ROOT = pathlib.Path(__file__).resolve().parent.parent.parent PORT = 8967 DIAG_CAP = 420 # the ?r>auto diagnostic ceiling (index.html declares it; this must match) STREET_CAP = 300 fails = [] def OK(m): print(f" \033[32m✓\033[0m {m}") def FAIL(m): fails.append(m); print(f" \033[31m✗ FAIL\033[0m {m}") def NOTE(m): print(f" \033[33m·\033[0m {m}") def new_page(b): pg = b.new_page(viewport={'width': 1280, 'height': 720}) state = {'errs': [], 'warns': [], 'reqs': []} pg.on('console', lambda m: (state['errs'] if m.type == 'error' else state['warns']).append(m.text) if m.type in ('error', 'warning') else None) pg.on('request', lambda r: state['reqs'].append(r.url)) return pg, state def main(): srv = serve(ROOT / 'web', PORT) host = f'http://127.0.0.1:{PORT}' with sync_playwright() as p: b = p.chromium.launch() # ── arm A: default boot declares the street law; signage row present and resolving ── print("\n\033[1m▶ default boot — street law declared · fog signage resolves\033[0m") pg, st = new_page(b) boot(pg, host, '', None) r = pg.evaluate("""() => { const B = window.PROCITY.budget; return { budget: B, streetRow: !!document.getElementById('pc-street'), selector: !!document.getElementById('pc-town-select') }; }""") (OK if r['budget'] and r['budget']['draws'] == STREET_CAP and not r['budget']['diagnostic'] else FAIL)( f"default PROCITY.budget = {r['budget']} (expect draws {STREET_CAP}, diagnostic false)") (OK if r['streetRow'] else FAIL)("default HUD has the #pc-street signage row") # stand at a shopfront (the arcade bookmark fronts the dept anchor) and let the throttle run pg.evaluate("() => window.DBG.shot('street_noon')") pg.wait_for_timeout(700) # ≥ 6 street frames at any sane fps txt = pg.evaluate("() => (document.getElementById('pc-street')||{}).textContent || ''") (OK if txt and txt != '—' else FAIL)(f"street row RESOLVES at a shopfront: '{txt}'") (OK if not st['errs'] else FAIL)(f"0 console errors (got {len(st['errs'])})") pg.close() # ── arm B: classic — selector shown (the R40 ruling), signage absent by construction ── print("\n\033[1m▶ ?classic=1 — town selector shown · signage DOM absent\033[0m") pg, st = new_page(b) boot(pg, host, 'classic=1', None) pg.wait_for_timeout(1200) # let the index.json upgrade land r = pg.evaluate("""() => { const sel = document.getElementById('pc-town-select'); return { nOpts: sel ? sel.options.length : 0, streetRow: !!document.getElementById('pc-street'), budget: window.PROCITY.budget }; }""") (OK if r['nOpts'] >= 20 else FAIL)(f"classic selector present with {r['nOpts']} options (≥20)") town_fetches = [u for u in st['reqs'] if '/assets/towns/' in u] only_index = all(u.endswith('/assets/towns/index.json') for u in town_fetches) (OK if town_fetches and only_index else FAIL)( f"classic towns-fetch surface is exactly the named exception (index.json): {town_fetches}") (OK if not r['streetRow'] else FAIL)("classic HUD has NO #pc-street node (byte-identical by construction)") (OK if r['budget'] and not r['budget']['diagnostic'] else FAIL)("classic declares the street law") (OK if not st['errs'] else FAIL)(f"0 console errors (got {len(st['errs'])})") pg.close() # ── arm C: ?r=3 — declares its own law, warns, and MEASURES inside it (night, worst class) ── print("\n\033[1m▶ ?r=3 — diagnostic law declared + warned · measured ≤ its own ceiling\033[0m") pg, st = new_page(b) boot(pg, host, 'r=3', 5) r = pg.evaluate("() => window.PROCITY.budget") (OK if r and r['draws'] == DIAG_CAP and r['diagnostic'] else FAIL)( f"?r=3 PROCITY.budget = {r} (expect draws {DIAG_CAP}, diagnostic true)") warned = any('DIAGNOSTIC boot' in w for w in st['warns']) (OK if warned else FAIL)("the ?r= console warning fired (the flag documents its own law)") path = pg.evaluate(SPINE_JS, 800) raw = pg.evaluate(STEPWALK_JS, {'path': path, 'stepm': 8, 'laps': 1, 'fixedYaw': 0}) s = summarise('r3/night/stepwalk8', raw) worst = s['draws_worst'] (OK if worst <= DIAG_CAP else FAIL)( f"?r=3 night worst {worst} draws ≤ its declared ceiling {DIAG_CAP} " f"(p50 {s['draws_p50']}, tris worst {s['tris_worst']:,}, n={s['n']})") (OK if s['tris_worst'] <= 200_000 else FAIL)(f"?r=3 tris worst {s['tris_worst']:,} ≤ 200,000 (tri law unchanged)") if worst <= STREET_CAP: NOTE(f"?r=3 measured ≤{STREET_CAP} — the exemption is unexercised; consider retiring it") else: OK(f"non-vacuous: ?r=3 exceeds the street law ({worst} > {STREET_CAP}) — the exemption is load-bearing") (OK if not st['errs'] else FAIL)(f"0 console errors (got {len(st['errs'])})") pg.close() b.close() srv.shutdown() print() if fails: print(f"\033[31m✗ r40_lane_b: {len(fails)} assertion(s) failed\033[0m") return 1 print("\033[32m✓ r40_lane_b: all assertions green\033[0m") return 0 if __name__ == '__main__': sys.exit(main())