THE ?r=3 BREACH — the carried number was stale by 84 draws. Re-measured with the pin
method of record (stepwalk 2m, 802 stations, fresh headless contexts, no-store server):
default 282/291 (≤300, 9 margin at night) · classic 269 byte-exact to the R38 pin incl.
90,580 tris · ?r=3 382/391, NOT 307. Decision: GATE, not shave — the 91-draw excess is
the R+1 live-chunk window (48 vs 31 chunks) on a per-chunk cost already collapsed to
~1 draw/kind/chunk, so shaving it means cutting default-boot content to legalise a
diagnostic. ?r= above auto now declares PROCITY.budget {draws:420, diagnostic:true} and
console-warns its own law; HUD threshold + DBG.info().budget read it; new
tools/qa/r40_lane_b.py enforces it (386 ≤ 420, and >300 so the exemption is provably
load-bearing).
?classic=1 TOWN SELECTOR (Fable ruled): true by construction, now deliberate and
qa-asserted — 27 options, one fetch (the named POST_V2_EXCEPTION), picks stay classic,
zero draws, 0 errors.
FOG SIGNAGE: three silent surfaces consume A's address layer via new
createStreetLocator(plan) — HUD street row, door tooltip ('Little Paris Cafe ·
Katoomba Street'), fog-map caption. Never branches on town type; classic-gated by
construction (#pc-street absent, tooltip pre-R40-byte).
E's arcade rule applied verbatim (§40.4): roof spans, a spanned roof has no posts;
district.kind keying, explicit ARCADEKIT classic gate + ?arcadekit=0 control.
-34 post instances exactly, lane draws 120→120 (+0). Shot pair vs E's reference.
Goldens 157,647/157,647, 0x5f76e76 unmoved after every wave.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
139 lines
7.7 KiB
Python
139 lines
7.7 KiB
Python
#!/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())
|