PROCITY/tools/qa/r41_citizens.py
m3ultra 78f49f7113 Lane D R41 §41.3: the town stops walking — 99.3% to 78.1%, at zero draws
THE ROUND IN ONE MEASUREMENT (12 samples x 146 active, ?clips=0 vs default — a new flag that
turns off the library and nothing else): walking 99.3% -> 78.1% · bench-sit 0 -> 9.5% · lean
0 -> 7.1% · stopped in own idle 0.4% -> 5.1% · DISTINCT CLIPS ACROSS THE CROWD 4 -> 20.
The town was 99.3% people walking because standing still had nowhere to happen.

Wiring: new postures.js + clipbank.js. idles.glb (10/10) drives a per-citizen deterministic
idle on every near-tier actor plus the seeded shopkeeper. locomotion gives 33.7% of walkers a
shopping bag. sitlean (8/8, lazy) puts 4 sits on Lane B's ACTUAL benches and 4 leans on
shopfront walls. browse (5/8, lazy) is a real BROWSE state at C's browse points, seeded per
(shopId, slot). venue (5/6, lazy) widens the gig crowd, plus a publican pouring and a record
keeper in headphones. social (0/8) is never fetched — two-person conversation needs a paired
state machine, filed to R42.

Cost: boot = 4 requests, 1.24 MB / 16 clips resident; the rest lazy on first need; heap delta
+3.34 MB; mixer median 0.1 ms both arms. ?clips=0 / ?classic=1 / ?noassets=1 fetch ZERO clips
— not even clipbank.js (dynamic import). No shell edit needed.

DRAWS: +0 on every bookmark (street_noon 193, crossroads 108, night_crowd 128, market_square
94, night_neon 111, interior 110 — identical both arms). Ruling 4 respected exactly.

DETERMINISM: 150 citizens, two fresh contexts, byte-equal posture signature. Controls: seed+1
differs; EVERY clip GLB delayed 2 s -> identical signature (posture is a pure function of
(citySeed, id), never of residency). 6 new streams collide with none of the 12 pre-R41 keys.

TWO FINDINGS THAT CHANGED THE DESIGN: the idle pool was INVISIBLE — wired only to the R17/R29
node loiter, so only 0.8% of citizens were ever stopped; and the lean never fired at all (0 in
a 9 s run). Both moved to the patronage stride check. Bench stations are GATED not trusted:
14/14 derived stations coincide with real instanced geometry within 2 cm, and the control
(same stations offset 2 m) matches 0/14. Filed to B: one benchStops(plan) export retires the
mirror, and furniture.js puts the bench's front ALONG the street rather than facing the road,
contradicting its own comment.

Leak: +0 geometries, +1 texture over 6 enter/exit cycles. Goldens 157,647/157,647, 0x5f76e76.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 18:11:30 +10:00

420 lines
24 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 D — R41 §41.3 runtime gate: THE MOTION LIBRARY IN THE LIVE GAME.
tools/.venv/bin/python tools/qa/r41_citizens.py [--seed N] 0 green · 1 red
Six arms. Every arm carries the control that makes it non-vacuous, and every number is measured in a
FRESH headless context against a no-store server (this project's documented ES-module cache burn).
1. BOOT LEDGER what the library actually costs: clip fetches at boot, bytes, groups resident,
and the heap delta against `?clips=0` — a control arm that turns off the motion
library and NOTHING else (`?classic=1` changes the ped pool, the fog, the game
and half the shell, so it cannot measure this).
2. LAZY LOADING the four non-boot groups are NOT fetched at boot, and browse.glb arrives on the
first interior with browsers. CONTROL: it is absent before that visit.
3. DETERMINISM two fresh contexts, same seed → byte-equal posture signature over the whole
active crowd. CONTROL: a different seed differs. Plus: the assignment is
independent of LOAD ORDER — the same run with the library forced late still
produces the same signature, which is the property lazy loading could break.
4. DRAW TABLE worst street view and worst interior, `?clips=0` vs default, over the same
bookmarks and the same seed. Ruling 4: the street law is the boot's own
declared budget and there is no room, so the delta must be ≤ 0 in effect.
5. ASSET-FREE `?noassets=1` runs (chunks build, no console errors, 0 clip fetches, 0 clipbank
module fetch) and `?classic=1` keeps its zero-fetch-delta covenant.
6. BENCH BINDING every bench station the sim derives for a LOADED chunk coincides with real
instanced geometry in Lane B's scene, position and yaw. CONTROL: the same
stations offset by 2 m must NOT match — otherwise "coincides" means nothing.
Plus a leak arm: repeated interior enter/exit leaves geometries/textures flat.
"""
import sys, os, time, json, socket, subprocess, pathlib
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
PORT = int(os.environ.get('PROCITY_R41_PORT', '8986'))
HOST = f'http://127.0.0.1:{PORT}'
SEED = 20261990
if '--seed' in sys.argv: SEED = int(sys.argv[sys.argv.index('--seed') + 1])
BOOT_GROUPS = {'idles.glb', 'locomotion.glb'}
LAZY_GROUPS = {'browse.glb', 'sitlean.glb', 'venue.glb', 'social.glb'}
BOOKMARKS = ['street_noon', 'crossroads_busy', 'night_crowd', 'market_square', 'night_neon']
fails = []
def FAIL(m): fails.append(m); print(f" \033[31m✗ FAIL\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 note(m): print(f" \033[33m·\033[0m {m}")
def check(c, m): (OK if c else FAIL)(m); return c
NOSTORE = r'''
import sys, http.server, functools
class H(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate')
super().end_headers()
def log_message(self, *a): pass
http.server.HTTPServer(('127.0.0.1', int(sys.argv[1])), functools.partial(H, directory=sys.argv[2])).serve_forever()
'''
def port_up(p):
with socket.socket() as s:
s.settimeout(0.4); return s.connect_ex(('127.0.0.1', p)) == 0
def serve():
pr = subprocess.Popen([sys.executable, '-c', NOSTORE, str(PORT), str(ROOT / 'web')],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
for _ in range(80):
if port_up(PORT): return pr
time.sleep(0.1)
pr.terminate(); raise SystemExit(f'could not serve on :{PORT}')
def new_page(p):
b = p.chromium.launch(args=['--js-flags=--expose-gc'])
pg = b.new_page(viewport={'width': 1280, 'height': 720})
errs, reqs = [], []
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.on('request', lambda r: reqs.append(r.url))
return b, pg, errs, reqs
def boot(pg, q='', dbg=True, wait=True):
pg.goto(f'{HOST}/index.html?seed={SEED}' + (('&' + q) if q else '') + ('&dbg=1' if dbg else ''))
if wait:
if dbg: pg.wait_for_function('window.DBG && window.DBG.ready === true', timeout=45000)
else: pg.wait_for_function('window.PROCITY && window.PROCITY.chunks && window.PROCITY.chunks.count > 0', timeout=45000)
pg.evaluate("() => { const o=document.getElementById('pc-start'); if(o) o.style.display='none'; }")
def clip_reqs(reqs):
return [u.rsplit('/', 1)[-1].split('?')[0] for u in reqs
if '/models/clips/' in u or 'motion_manifest.json' in u or 'clipbank.js' in u]
HEAP = "() => { if (window.gc) window.gc(); return performance.memory ? performance.memory.usedJSHeapSize : null; }"
# max draw calls over N successive natural frames at the current pose
DRAWMAX = r"""
async (n) => {
const P = window.PROCITY;
let mx = 0, mt = 0;
for (let i = 0; i < n; i++) {
await new Promise(r => requestAnimationFrame(() => r()));
const r = P.renderer.info.render;
if (r.calls > mx) mx = r.calls;
if (r.triangles > mt) mt = r.triangles;
}
return { draws: mx, tris: mt, budget: (P.budget || {}).draws || 300 };
}
"""
BENCHES = r"""
async (offset) => {
const P = window.PROCITY, C = P.citizens;
const mod = await import('./js/citizens/sim.js');
const M4 = P.scene.matrixWorld.constructor;
const m = new M4();
// every instanced world transform standing in the built scene right now
const inst = [];
P.scene.traverse(o => {
if (!o.isInstancedMesh) return;
o.updateWorldMatrix(true, false);
for (let i = 0; i < o.count; i++) {
o.getMatrixAt(i, m);
const w = m.clone().premultiply(o.matrixWorld), e = w.elements;
inst.push({ x: e[12], z: e[14], yaw: Math.atan2(e[8], e[10]) });
}
});
// ...against the sim's own derivation, restricted to chunks Lane B has actually BUILT (a station
// in an unstreamed chunk has no geometry to match and would be a false negative).
const cam = P.camera.position;
let tested = 0, hit = 0, yawOk = 0; const misses = [];
for (let i = 0; i < C.edges.length; i++) {
for (const st of mod.benchStationsFor(C.edges[i])) {
if (Math.hypot(st.x - cam.x, st.z - cam.z) > 90) continue; // inside the streamed window
const tx = st.x + offset, tz = st.z;
tested++;
let bd = 1e9, by = 0;
for (const p of inst) { const d = Math.hypot(p.x - tx, p.z - tz); if (d < bd) { bd = d; by = p.yaw; } }
if (bd <= 0.02) {
hit++;
let dy = Math.abs(((st.yaw - by) % (Math.PI * 2) + Math.PI * 3) % (Math.PI * 2) - Math.PI);
if (dy < 1e-3 || Math.abs(dy - Math.PI) < 1e-3) yawOk++; // exact, mod 2pi
} else if (misses.length < 5) misses.push({ x: +st.x.toFixed(2), z: +st.z.toFixed(2), nearest: +bd.toFixed(3) });
}
}
return { tested, hit, yawOk, misses, instances: inst.length };
}
"""
POSTURE_SIG = "() => window.PROCITY.citizens.postureSignature().join('\\n')"
CLIP_STATS = "() => window.PROCITY.citizens.clipStats()"
def main():
from playwright.sync_api import sync_playwright
srv = serve()
try:
with sync_playwright() as p:
# ── 1 + 2: boot ledger and lazy loading ──────────────────────────────────────────────
head('1. BOOT LEDGER — what the motion library costs at boot, measured')
b, pg, errs, reqs = new_page(p)
boot(pg)
pg.wait_for_function("() => window.PROCITY.fleet && window.PROCITY.fleet.bank && window.PROCITY.fleet.bank.manifest", timeout=20000)
pg.wait_for_timeout(1500)
boot_fetch = clip_reqs(reqs)
st_on = pg.evaluate(CLIP_STATS)
heap_on = pg.evaluate(HEAP)
glbs = [f for f in boot_fetch if f.endswith('.glb')]
check(set(glbs) == BOOT_GROUPS,
f'boot fetches exactly the 2 eager groups: {sorted(glbs)} (+ manifest + clipbank.js '
f'= {len(boot_fetch)} requests)')
check(st_on['manifest'], f"manifest parsed — catalogue {st_on['catalogue']} clips")
note(f"resident at boot: {st_on['groups']} groups / {st_on['clips']} clips / {st_on['bytes']} B "
f"({st_on['bytes'] / 1048576:.2f} MB of Lane E's 3.34 MB)")
note(f"heap after boot (library ON): {heap_on / 1048576:.1f} MB" if heap_on else 'heap unavailable')
head('2. LAZY LOADING — the other four groups are not paid for until they are wanted')
check(not (set(glbs) & LAZY_GROUPS),
f'none of {sorted(LAZY_GROUPS)} fetched at boot')
# let the street run: sitlean.glb should arrive on the first sit/lean INTENT, not before
pg.evaluate("() => window.DBG.shot('crossroads_busy')")
pg.wait_for_timeout(12000)
after_street = clip_reqs(reqs)
check('sitlean.glb' in after_street,
f'sitlean.glb arrives on the first street sit/lean intent (not at boot)')
check('browse.glb' not in after_street and 'venue.glb' not in after_street,
'browse.glb + venue.glb still unfetched — CONTROL for the interior arm below')
check('social.glb' not in after_street, 'social.glb never fetched (no two-person state yet — R42)')
# first interior with browsers pulls browse.glb
shop = pg.evaluate("""() => { const P = window.PROCITY, C = P.citizens;
const c = (P.plan.shops || []).map(s => ({ id: s.id, n: C.occupancyOf(s.id).count }))
.filter(s => s.n > 0).sort((a, b) => b.n - a.n)[0];
if (c) window.DBG.enterShop(c.id); return c || null; }""")
pg.wait_for_timeout(3000)
after_int = clip_reqs(reqs)
if shop:
check('browse.glb' in after_int, f'browse.glb arrives on the first interior with browsers (shop {shop["id"]})')
else:
note('no shop had occupants during this window — browse arm skipped')
st_int = pg.evaluate(CLIP_STATS)
note(f"resident after a street run + one interior: {st_int['groups']} groups / "
f"{st_int['clips']} clips / {st_int['bytes']} B")
check(not errs, f'0 console errors on the default boot ({len(reqs)} requests swept)')
# ── 6b: leak — repeated enter/exit with more clips resident ───────────────────────────
head('6b. LEAK — repeated interior enter/exit with the library resident')
pg.evaluate("() => window.DBG.exitShop()")
pg.wait_for_timeout(800)
# WARM FIRST, then measure. Cycle 1 legitimately grows the caches (that room's stock GLBs,
# its wallpaper texture, the browse group) — counting it as a leak measures cold-start, not
# retention. Same shop every cycle so the comparison is like for like.
leak_shop = pg.evaluate("""() => { const P = window.PROCITY, C = P.citizens;
const c = (P.plan.shops || []).map(s => ({ id: s.id, n: C.occupancyOf(s.id).count }))
.filter(s => s.n > 0).sort((a, b) => b.n - a.n)[0];
return c ? c.id : (P.plan.shops[0] || {}).id; }""")
pg.evaluate("(id) => window.DBG.enterShop(id)", leak_shop)
pg.wait_for_timeout(1400)
pg.evaluate("() => window.DBG.exitShop()")
pg.wait_for_timeout(900)
base = pg.evaluate("() => window.DBG.info()")
for _ in range(6):
pg.evaluate("(id) => window.DBG.enterShop(id)", leak_shop)
pg.wait_for_timeout(900)
pg.evaluate("() => window.DBG.exitShop()")
pg.wait_for_timeout(700)
after = pg.evaluate("() => window.DBG.info()")
st_end = pg.evaluate(CLIP_STATS)
dg = after['geometries'] - base['geometries']
dt = after['textures'] - base['textures']
check(abs(dg) <= 2 and abs(dt) <= 2,
f'shop {leak_shop}, warm baseline then 6 more enter/exit cycles: geometries '
f'{base["geometries"]}{after["geometries"]} ({dg:+d}), textures '
f'{base["textures"]}{after["textures"]} ({dt:+d})')
check(st_end['clips'] == st_int['clips'] and st_end['groups'] == st_int['groups'],
f'clip bank does not grow on re-entry: {st_end["groups"]} groups / {st_end["clips"]} clips '
f'(one fetch per group, promise-cached)')
b.close()
# ── 1b: the ?clips=0 control arm (heap + fetch delta) ────────────────────────────────
head('1b. CONTROL ARM ?clips=0 — the library off, and nothing else changed')
b, pg, errs, reqs = new_page(p)
boot(pg, 'clips=0')
pg.wait_for_timeout(3000)
off_fetch = clip_reqs(reqs)
heap_off = pg.evaluate(HEAP)
st_off = pg.evaluate(CLIP_STATS)
check(not off_fetch, f'?clips=0 fetches NOTHING clip-related — not the GLBs, not the manifest, '
f'not even clipbank.js ({len(reqs)} requests swept)')
check(st_off['groups'] == 0 and st_off['clips'] == 0, '?clips=0: no bank, 0 clips resident')
check(not errs, '?clips=0: 0 console errors')
if heap_on and heap_off:
note(f'HEAP DELTA (library ON OFF): {(heap_on - heap_off) / 1048576:+.2f} MB '
f'(on {heap_on / 1048576:.1f} MB · off {heap_off / 1048576:.1f} MB)')
b.close()
# ── 3: determinism ───────────────────────────────────────────────────────────────────
head('3. DETERMINISM — same seed → same postures, byte-equal across fresh contexts')
sigs = []
for i in range(2):
b, pg, errs, reqs = new_page(p)
boot(pg)
pg.evaluate("() => window.DBG.shot('street_noon')")
pg.wait_for_timeout(2500)
sigs.append(pg.evaluate(POSTURE_SIG))
b.close()
n = len(sigs[0].splitlines())
check(sigs[0] == sigs[1] and n > 0,
f'{n} active citizens, two fresh browser contexts → byte-equal posture signature')
b, pg, errs, reqs = new_page(p)
pg.goto(f'{HOST}/index.html?seed={SEED + 1}&dbg=1')
pg.wait_for_function('window.DBG && window.DBG.ready === true', timeout=45000)
pg.evaluate("() => { const o=document.getElementById('pc-start'); if(o) o.style.display='none'; }")
pg.evaluate("() => window.DBG.shot('street_noon')")
pg.wait_for_timeout(2500)
other = pg.evaluate(POSTURE_SIG)
check(other != sigs[0], f'CONTROL: seed {SEED + 1} gives a DIFFERENT signature '
f'({len(other.splitlines())} citizens) — byte-equal is not constant')
b.close()
# load-order independence: the property lazy loading could plausibly break
b, pg, errs, reqs = new_page(p)
pg.route('**/models/clips/*.glb', lambda route: (time.sleep(2.0), route.continue_())[1])
boot(pg)
pg.evaluate("() => window.DBG.shot('street_noon')")
pg.wait_for_timeout(2500)
slow = pg.evaluate(POSTURE_SIG)
check(slow == sigs[0],
'CONTROL: with every clip GLB delayed 2 s, the signature is IDENTICAL — posture is '
'assigned from (citySeed, id) alone, never from what happens to be resident')
b.close()
# ── 4: draw table ────────────────────────────────────────────────────────────────────
head('4. DRAW TABLE — worst street view and worst interior, library OFF vs ON')
table = {}
for arm, q in (('clips=0', 'clips=0'), ('default', '')):
b, pg, errs, reqs = new_page(p)
boot(pg, q)
pg.wait_for_timeout(6000)
row = {}
for bm in BOOKMARKS:
pg.evaluate('(n) => window.DBG.shot(n)', bm)
pg.wait_for_timeout(2500)
pg.evaluate('(n) => window.DBG.shot(n)', bm)
d = pg.evaluate(DRAWMAX, 24)
row[bm] = d
# worst interior: enter the biggest room type we can reach
pg.evaluate("() => window.DBG.enterShop('record')")
pg.wait_for_timeout(2500)
row['interior(record)'] = pg.evaluate(DRAWMAX, 24)
pg.evaluate("() => window.DBG.exitShop()")
table[arm] = row
b.close()
print(f" {'view':<20} {'clips=0':>10} {'default':>10} {'Δ':>6} budget")
worst = {'clips=0': 0, 'default': 0}
for k in list(table['clips=0']):
a0, a1 = table['clips=0'][k]['draws'], table['default'][k]['draws']
bud = table['default'][k]['budget'] if not k.startswith('interior') else 350
if not k.startswith('interior'):
worst['clips=0'] = max(worst['clips=0'], a0); worst['default'] = max(worst['default'], a1)
flag = '' if a1 <= bud else ' OVER'
print(f" {k:<20} {a0:>10} {a1:>10} {a1 - a0:>+6} <={bud}{flag}")
check(a1 <= bud, f'{k}: {a1} draws <= {bud}')
note(f"worst STREET view: clips=0 {worst['clips=0']} · default {worst['default']} "
f"(delta {worst['default'] - worst['clips=0']:+d})")
ints = (table['clips=0']['interior(record)']['draws'], table['default']['interior(record)']['draws'])
note(f'interior(record): clips=0 {ints[0]} · default {ints[1]} (delta {ints[1] - ints[0]:+d}), '
f'margin to 350 = {350 - ints[1]}')
# ── 5: asset-free + classic ──────────────────────────────────────────────────────────
head('5. ASSET-FREE — ?noassets=1 still runs, ?classic=1 keeps its zero-fetch-delta covenant')
for q, label in (('noassets=1', '?noassets=1'), ('classic=1', '?classic=1')):
b, pg, errs, reqs = new_page(p)
boot(pg, q)
pg.wait_for_timeout(4000)
cr = clip_reqs(reqs)
st = pg.evaluate(CLIP_STATS)
chunks = pg.evaluate("() => window.PROCITY.chunks.count")
act = pg.evaluate("() => window.PROCITY.citizens.stats.active")
check(not cr, f'{label}: 0 clip fetches, 0 manifest fetch, 0 clipbank.js fetch')
check(st['groups'] == 0 and st['clips'] == 0, f'{label}: no bank (clipStats {st["groups"]}/{st["clips"]})')
check(chunks > 0 and act > 0, f'{label}: town builds and the crowd walks ({chunks} chunks, {act} active)')
check(not errs, f'{label}: 0 console errors')
b.close()
# ── 6: bench binding ─────────────────────────────────────────────────────────────────
head('6. BENCH BINDING — the sim\'s bench stations ARE Lane B\'s benches')
b, pg, errs, reqs = new_page(p)
boot(pg)
pg.evaluate("() => window.DBG.shot('crossroads_busy')")
pg.wait_for_timeout(5000)
real = pg.evaluate(BENCHES, 0.0)
ctrl = pg.evaluate(BENCHES, 2.0)
check(real['tested'] >= 10 and real['hit'] == real['tested'],
f"{real['hit']}/{real['tested']} derived stations coincide with real instanced "
f"geometry within 2 cm (scene holds {real['instances']} instances)")
check(real['yawOk'] == real['hit'],
f"{real['yawOk']}/{real['hit']} also match the bench yaw exactly (mod 2pi)")
check(ctrl['hit'] == 0,
f"CONTROL: the same stations shifted 2 m match {ctrl['hit']}/{ctrl['tested']}"
f"so 'coincide' is a real measurement, not a hit on any nearby prop")
if real['misses']: note(f"misses: {real['misses']}")
b.close()
# ── 7: the gig crowd's widened vocabulary ────────────────────────────────────────────
head("7. VENUE CLIPS — the gig crowd's vocabulary widens, the v3 dance pick survives")
b, pg, errs, reqs = new_page(p)
boot(pg, 'gigs=1')
pg.evaluate("() => window.DBG.setSegment(2)") # MIDDAY — no gig anywhere
pg.wait_for_timeout(5000)
pre = clip_reqs(reqs)
check('venue.glb' not in pre,
'CONTROL: at midday, with no gig on, venue.glb is not fetched at all')
pg.evaluate("() => window.DBG.setSegment(5)") # NIGHT — the doors open
pg.wait_for_timeout(5000)
mid = clip_reqs(reqs)
check('venue.glb' in mid,
'venue.glb arrives on GIG NIGHT, from the street — sim.setGig kicks it when F opens '
'the doors, so it is resident before the player walks in')
g = pg.evaluate("""async () => {
const P = window.PROCITY, D = window.DBG;
if (!P.gigs || !P.gigs.venueShopIds || !P.gigs.venueShopIds.length) return { ok: false, why: 'no gig venue' };
const id = P.gigs.venueShopIds[0];
D.enterShop(id);
await new Promise(r => setTimeout(r, 3500));
const crew = P.interiorMode.crew;
if (!crew) return { ok: false, why: 'no crew (gig not on)', state: P.gigs.stateOf(id) };
const mem = crew.members.filter(m => m.part === 'crowd');
return { ok: true, state: P.gigs.stateOf(id), crowd: mem.length,
swapped: mem.filter(m => m.venueId).length,
kept: mem.filter(m => !m.venueId).length,
pending: mem.filter(m => m.want).length,
clips: [...new Set(mem.map(m => m.venueId).filter(Boolean))].sort(),
dancers: mem.filter(m => m.dance).length };
}""")
print(' gig:', json.dumps(g))
if g.get('ok'):
check(g['swapped'] > 0 and g['kept'] > 0,
f"widening, not replacement: {g['swapped']}/{g['crowd']} crowd slots take a venue clip, "
f"{g['kept']} keep their R13 pick verbatim ({', '.join(g['clips'])})")
check(g['pending'] == 0, 'every swapped slot has its clip installed (self-heal drained)')
else:
note(f"gig arm skipped: {g.get('why')}")
check(not errs, '?gigs=1: 0 console errors')
b.close()
finally:
srv.terminate()
print()
if fails:
print(f"\033[31m● RED\033[0m — {len(fails)} failure(s)")
for f in fails: print(' ' + f)
return 1
print("\033[32m● PASS\033[0m — boot ledger, lazy loading, determinism, draw table, asset-free and bench binding all green")
return 0
if __name__ == '__main__':
sys.exit(main())