PROCITY/tools/qa/r41_integration.py
m3ultra d14cf7fa6e Lane F R41 §41.6: 18 gates green — the deny-list, the wardrobe switch, and no byte-hash goldens
qa.sh --strict: 18 passed · 0 failed · 0 warn · 0 skipped. selfcheck 157,647/157,647,
fingerprint 0x5f76e76 unmoved. No tag — John's playtest session rules the epoch.

THE FIVE ASKS. [C] wardrobe wired via exported WARDROBE_BASE + stockBaseFor(shop), preloaded
inside the existing STOCK_REAL gate: ON under ?stock=real (52 pack items, 69 garment ids on
rendered meshes, 1 atlas, exactly 2 requests) and CHEAPER (59->55 draws, 111->100 textures);
OFF is what ships by default, A/B'd against a reverted control tree — default arm identical
on every counter (160 URLs same hash, 131/18,018 street, 59/28,505 interior). [D] three gates
wired (r41_shots.py writes to TMPDIR so gate runs never rewrite committed shots). [E]
clips_verify wired — 46 clips / 6 groups / 3,498,124 B.

[B's bookmark finding] CONFIRMED AND WIDER: street_noon, shopfront_detail, crossroads_busy
AND market_square all give 3 distinct hashes across 3 boots; only night_neon is stable.
RULING: no gate pins a screenshot byte-hash on any bookmark — everything R41 asserts is a
counter. Filed to B: one await document.fonts.ready in buildings.js.

[C's drawSweep] CONFIRMED INDEPENDENTLY with F's own sweep (never calls C's): GLB-on worst
123 @ dept/auto. The phantom control (one stale room left in scene) reads 197 — +74 on every
reading — decomposing R39/R40's '188' as 116 real + ~72 phantom.

NEW GATES, controls demonstrated. r41_denylist.mjs (ruling 3): 738 files / 197.3 MiB scanned
by path AND bytes, 7 banned names, 0 hits; the control plants a banned manifest row every run
-> RED on 2 names -> removed -> GREEN. r41_integration.py: wardrobe both arms · interior <=350
on both instruments · ?noassets=1 zero across six fetch classes over a 3-shop walk (control
fetches 5/6) · ?clips=0 142 URLs and ?classic=1 125 URLs with zero R41 cargo.

BUDGETS. Street: noon 282 · NIGHT 291/120,093 — margin 9, INTACT · classic 269 byte-exact ·
?clips=0 NIGHT 291, delta 0. No lane spent a street draw. Interior: true pre-R41 116, R41 123
@ dept/auto, margin 227; over 33 real shops worst 110; pub 49 quiet / 77 gig night.

FILED BACK: flags_check's _enter_record_shop went RED on 8 smokes because §41.4's bins put
every one of the first six record-shop counters inside 2.6 m — widened to the whole open set,
verified not a game defect. smoke_djdance read _actions[0] and D's per-instance clip swapping
made the mixer multi-action, so a healthy dancer was called a stall — now follows the clip by
name. D's r41_shots.py browse arm is deterministically red here (re-enters the winner after a
~3 s/candidate scan, by which time the occupant has left) — wired WARN-level with the reason
at the call site, fix handed to D. F's own r39_transmission.py had an unmeasured 30 s goto
default that flaked once under a full strict run; pinned to 90 s.

11 proof frames in docs/shots/laneF_r41/ with sidecars: street_postures (5 rigs, 3 clips
playing) against its ?clips=0 control (1 clip) · street_lean · browse_interior · pub_furnished
+ the honest kit before/after pair · record_dj_booth · opshop_wardrobe on/off · credits_panel
with ODbL on screen. Every humanoid frame carries its R10 human-sized line.

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

355 lines
19 KiB
Python

#!/usr/bin/env python3
"""PROCITY Lane F — R41 §41.6 INTEGRATION GATE: the seams F wired, and the budgets F is answerable for.
tools/.venv/bin/python tools/qa/r41_integration.py [--no-sweep]
Five arms, each carrying its own control (R25 vacuous-gate law — a gate that has never been able to
fail is a gate nobody can trust):
1. THE WARDROBE, BOTH ARMS Lane C's §41.4 op-shop pack, turned on by F's one line. ON
(F's ask #1 from C) (?stock=real): the room resolves base `assets/wardrobe/`, the
52-item pack, real garment ids/titles on rendered meshes, and the
GPU sampling the atlas file itself. OFF (default boot) is the
CONTROL: zero wardrobe requests, base null, zero garment ids —
the procedural canvas, which is what ships by default.
2. THE INTERIOR BUDGET, ON F'S 12 types x 6 archetypes, GLB on and GLB+stock=real, ≤350 law.
OWN INSTRUMENT This does NOT call PROCITY_C.drawSweep: it is F's own loop with
(F's ask #5 from C) F's own scene bookkeeping, because the thing C found broken in
R39/R40 was exactly the sweep's scene bookkeeping. Ships the
PHANTOM CONTROL: the same loop with one stale room deliberately
left in the scene reproduces the pre-R41 inflation, so "the
instrument was wrong, not the rooms" is a measurement here too.
3. ?noassets=1 STILL CLEAN Boot + enter a shop: zero GLB, zero clip GLB, zero motion
manifest, zero wardrobe, zero stock-pack requests. CONTROL: the
same walk on a ?stock=real boot fetches all of those classes.
4. ?clips=0 / ?classic=1 Neither boot may carry one byte of R41 cargo (clips, wardrobe,
CARRY NO R41 CARGO kit fittings via the manifest are C's, measured separately).
CONTROL: the default boot does fetch the clip groups.
5. DENY-LIST AT RUNTIME Ruling 3 again, from the other side: every URL the running game
requests across street + interior + gig night is checked against
the banned names. The static byte scan (r41_denylist.mjs) proves
nothing banned is IN web/; this proves nothing banned is ASKED
FOR. CONTROL: the matcher is shown firing on a synthetic URL.
Fresh headless context per arm, own port-isolated no-store server (this repo's ES-module cache burn).
Exit 0 green, 1 red.
"""
import sys, os, json, time, socket, subprocess, pathlib
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
PORT = int(os.environ.get('PROCITY_R41_INT_PORT', '8988'))
HOST = f'http://127.0.0.1:{PORT}'
SEED = 20261990
DRAW_LAW = 350
# The town is FROZEN for every measured arm: everything below moves on wall-clock time (R38's recorded
# trap), and a live town measured twice differs from itself.
FREEZE = 'pop=0&weather=0&tram=0&magpie=0&washing=0'
BANNED = ['character_kit_modular', 'exports/bodies/', 'elsa_coronation_hair_wig_kh3',
'daphne_sexy', 'mocaponline', 'sk_mannequin', 'anatomy/']
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(cond, m):
(OK if cond else FAIL)(m)
return cond
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(port):
with socket.socket() as s:
s.settimeout(0.4); return s.connect_ex(('127.0.0.1', port)) == 0
def serve(root, port):
proc = subprocess.Popen([sys.executable, '-c', NOSTORE, str(port), str(root)],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
for _ in range(80):
if port_up(port): return proc
time.sleep(0.1)
proc.terminate(); raise SystemExit(f'could not serve on :{port}')
def new_page(p):
b = p.chromium.launch()
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, query):
pg.goto(f'{HOST}/index.html?seed={SEED}&dbg=1&{FREEZE}' + (('&' + query) if query else ''))
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'; }")
def paths(reqs):
"""server-relative paths only — blob:/data: URLs are canvas textures, not a fetch surface."""
return [u.split('?')[0].split(f':{PORT}/')[-1] for u in reqs if not u.startswith(('blob:', 'data:'))]
# ── arm 1: THE WARDROBE ──────────────────────────────────────────────────────────────────────────
def arm_wardrobe(p):
head('1. THE WARDROBE — Lane C §41.4 turned on by F, and the default arm that is its control')
out = {}
for q, label in (('stock=real', 'ON (?stock=real)'), ('', 'OFF (default boot)')):
b, pg, errs, reqs = new_page(p)
try:
boot(pg, q)
entered = pg.evaluate("() => window.DBG.enterShop('opshop')")
pg.wait_for_timeout(2500) # atlas decode + the room's first frames
si = pg.evaluate("() => window.DBG.stockInfo()")
inf = pg.evaluate("() => window.DBG.info()")
wr = [u for u in paths(reqs) if 'wardrobe' in u]
ids = [i for i in (si.get('renderedIds') or []) if str(i).startswith('wr_')]
tex = [t for t in (si.get('texUrls') or []) if 'wardrobe_atlas' in t]
out[label] = dict(si=si, inf=inf, wr=wr, ids=ids, tex=tex, errs=errs)
note(f"{label}: shop {entered.get('entered')} \"{entered.get('name')}\" · base={si.get('base')!r} · "
f"packItems={si.get('packItems')} · {inf.get('drawCalls')} draws / {inf.get('tris')} tris")
if q:
check(si.get('base') == 'assets/wardrobe/', f'{label}: room resolved base assets/wardrobe/ (got {si.get("base")!r})')
check(si.get('packItems') == 52, f'{label}: the 52-item pack is resolved AT BUILD TIME (getStockPack is sync)')
check(len(ids) > 0, f'{label}: {len(ids)} real garment ids on rendered meshes '
f'(e.g. {", ".join(str(t) for t in (si.get("renderedTitles") or [])[:3])})')
check(len(tex) == 1, f'{label}: the GPU is sampling the wardrobe atlas itself, {len(tex)} atlas texture')
check(sorted(wr) == ['assets/wardrobe/stock_opshop_index.json', 'assets/wardrobe/wardrobe_atlas_00.webp'],
f'{label}: exactly 2 wardrobe requests — the index and ONE atlas ({len(wr)} seen)')
else:
check(not wr, f'{label}: 0 wardrobe requests — THE CONTROL, and what ships by default')
check(si.get('base') is None and si.get('packItems') == 0,
f'{label}: base null, no pack — the procedural garment canvas, unchanged')
check(not ids, f'{label}: 0 wardrobe item ids rendered')
check(not errs, f'{label}: 0 console errors')
finally:
b.close()
on, off = out['ON (?stock=real)'], out['OFF (default boot)']
d_draw = on['inf']['drawCalls'] - off['inf']['drawCalls']
d_tex = on['inf']['textures'] - off['inf']['textures']
note(f"the swap is a WIN, not a cost: {off['inf']['drawCalls']}{on['inf']['drawCalls']} draws ({d_draw:+d}), "
f"{off['inf']['textures']}{on['inf']['textures']} textures ({d_tex:+d}) — 52 garments off ONE shared atlas")
check(d_draw <= 0 and d_tex <= 0, 'the wardrobe costs no draws and no textures (one atlas replaces N canvases)')
# ── arm 2: THE INTERIOR BUDGET, F'S OWN SWEEP ────────────────────────────────────────────────────
SWEEP = r"""
async ({ glb, stock, phantom }) => {
const S = window.PROCITY_C, THREE = S.THREE;
// F's OWN bookkeeping: strip EVERY interior out of the scene first, including the page's `current`.
// This is the exact step whose absence made R39/R40's numbers carry a phantom second room.
const strip = () => { for (const o of S.scene.children.filter(o => o.userData && o.userData.kind === 'interior')) S.scene.remove(o); };
strip();
let manifest = null;
if (glb) manifest = await fetch('assets/manifest.json?x=' + Date.now()).then(r => r.ok ? r.json() : null).catch(() => null);
// THE PHANTOM CONTROL: put one unrelated room in the scene and LEAVE IT THERE for the whole sweep —
// the pre-R41 behaviour, reproduced deliberately.
let ghost = null;
if (phantom) {
ghost = S.buildInterior({ id: 'record', type: 'record', seed: 1990, storeys: 1 }, THREE,
{ useGLB: glb, manifest, stock: stock ? 'real' : undefined });
S.scene.add(ghost.group); if (glb) await ghost.glbReady;
}
const measure = async (type, arch) => {
const r = S.buildInterior({ id: type, type, seed: 1990, storeys: 1 }, THREE,
{ archetype: arch, useGLB: glb, manifest,
stock: stock ? 'real' : undefined,
stockBase: (stock && type === 'opshop') ? S.WARDROBE_BASE : undefined });
S.scene.add(r.group); if (glb) await r.glbReady;
S.camera.position.set(r.dims.W * 0.3, r.dims.H * 0.6, r.dims.D / 2 - 0.8);
S.camera.lookAt(0, 0.9, -r.dims.D * 0.15);
S.renderer.info.reset(); S.renderer.render(S.scene, S.camera);
const d = S.renderer.info.render.calls;
S.scene.remove(r.group); r.dispose();
return d;
};
let worst = 0, worstAt = '', rooms = 0; const perType = {};
for (const t of S.SHOP_TYPES) {
let tw = 0;
for (const a of [undefined, ...S.ARCHETYPE_KEYS]) {
const d = await measure(t, a); rooms++;
if (d > tw) tw = d;
if (d > worst) { worst = d; worstAt = `${t}/${a || 'auto'}`; }
}
perType[t] = tw;
}
if (ghost) { S.scene.remove(ghost.group); ghost.dispose(); }
S.rebuild();
return { worst, worstAt, rooms, perType };
}
"""
def arm_budget(p):
head(f'2. THE INTERIOR BUDGET — F\'s own sweep (NOT PROCITY_C.drawSweep), 12 types x 6 archetypes, law ≤{DRAW_LAW}')
b, pg, errs, reqs = new_page(p)
try:
pg.goto(f'{HOST}/interior_test.html?seed={SEED}&stock=real')
pg.wait_for_function('window.PROCITY_C && window.PROCITY_C.SHOP_TYPES', timeout=45000)
pg.wait_for_timeout(1500) # let the page's own stock preloads resolve
res = {}
for label, opts in (('GLB off', dict(glb=False, stock=False, phantom=False)),
('GLB on', dict(glb=True, stock=False, phantom=False)),
('GLB on + stock=real', dict(glb=True, stock=True, phantom=False))):
r = pg.evaluate(SWEEP, opts)
res[label] = r
note(f"{label}: worst {r['worst']} @ {r['worstAt']} over {r['rooms']} rooms "
f"(margin {DRAW_LAW - r['worst']})")
check(r['worst'] <= DRAW_LAW, f'{label}: worst room {r["worst"]}{DRAW_LAW} — margin {DRAW_LAW - r["worst"]}')
# the headline number C published, independently re-derived
worst_on = res['GLB on']['worst']
check(res['GLB on']['worstAt'].startswith('dept'),
f"the worst room is a dept room ({res['GLB on']['worstAt']}) — C's §41.4 headline, on F's instrument")
# THE PHANTOM CONTROL — reproduce the pre-R41 sweep bug on purpose
ph = pg.evaluate(SWEEP, dict(glb=True, stock=False, phantom=True))
infl = ph['worst'] - worst_on
note(f"PHANTOM CONTROL (one stale room left in the scene, the pre-R41 behaviour): "
f"worst {ph['worst']} @ {ph['worstAt']} — inflation +{infl} draws")
check(infl > 0, f"the phantom is real and reproducible: leaving one room in the scene adds +{infl} draws to EVERY "
f"reading — C's §41.4-0 finding confirmed on an independent instrument")
check(ph['worst'] <= DRAW_LAW, f'even the inflated reading is under the law ({ph["worst"]}{DRAW_LAW}) — '
f'no historical number was ever a real breach')
check(not errs, f'0 console errors over {sum(r["rooms"] for r in res.values()) + ph["rooms"]} room builds')
return res, ph
finally:
b.close()
# ── arm 3: ?noassets=1 ───────────────────────────────────────────────────────────────────────────
CARGO = {
'GLB (props/rigs)': lambda u: u.endswith('.glb'),
'clip group': lambda u: '/models/clips/' in u,
'motion manifest': lambda u: 'motion_manifest.json' in u,
'wardrobe': lambda u: 'assets/wardrobe/' in u,
'stock pack': lambda u: 'stock_' in u and u.endswith('_index.json'),
'asset manifest': lambda u: u.endswith('assets/manifest.json'),
}
def walk_and_classify(p, q):
b, pg, errs, reqs = new_page(p)
try:
boot(pg, q)
pg.evaluate("() => window.DBG.shot('street_noon')")
for sel in ('opshop', 'record', 'pub'):
pg.evaluate(f"() => window.DBG.enterShop('{sel}')")
pg.wait_for_timeout(700)
pg.evaluate("() => window.DBG.exitShop()")
pg.wait_for_timeout(200)
pg.wait_for_timeout(800)
ps = paths(reqs)
return {k: [u for u in ps if f(u)] for k, f in CARGO.items()}, ps, errs
finally:
b.close()
def arm_noassets(p):
head('3. ?noassets=1 — the asset law, over a walk that enters three shops (op shop, record, pub)')
clean, ps_clean, errs_clean = walk_and_classify(p, 'noassets=1')
for k, hits in clean.items():
check(not hits, f'?noassets=1: 0 {k} requests' + (f' — SAW {hits[:3]}' if hits else ''))
check(not errs_clean, f'?noassets=1: 0 console errors ({len(ps_clean)} requests swept)')
ctrl, ps_ctrl, _ = walk_and_classify(p, 'stock=real')
got = [k for k, v in ctrl.items() if v]
note(f'CONTROL (?stock=real, same walk): {len(ps_ctrl)} requests, cargo classes present = {got}')
check(len(got) >= 5, f'the classifier is not vacuous — the control boot fetches {len(got)}/6 cargo classes')
return clean, ctrl
# ── arm 4: ?clips=0 / ?classic=1 ─────────────────────────────────────────────────────────────────
def arm_clip_gates(p):
head('4. ?clips=0 and ?classic=1 — neither boot may carry one byte of R41 cargo')
res = {}
for q, label in (('clips=0', '?clips=0'), ('classic=1', '?classic=1'), ('', 'default')):
b, pg, errs, reqs = new_page(p)
try:
boot(pg, q)
pg.wait_for_timeout(2000) # a wrongly-gated dynamic import has time to land
ps = paths(reqs)
clips = [u for u in ps if '/models/clips/' in u or 'motion_manifest.json' in u or 'clipbank.js' in u]
wr = [u for u in ps if 'assets/wardrobe/' in u]
stats = pg.evaluate("() => (window.PROCITY.citizens && window.PROCITY.citizens.clipStats) "
"? window.PROCITY.citizens.clipStats() : null")
res[label] = dict(n=len(set(ps)), clips=clips, wr=wr, stats=stats, errs=errs)
note(f'{label}: {len(set(ps))} distinct URLs · clip cargo {len(clips)} · wardrobe {len(wr)} · clipStats {stats}')
if q:
check(not clips, f'{label}: 0 clip GLBs, 0 motion manifest, 0 clipbank.js')
check(not wr, f'{label}: 0 wardrobe requests')
check(not errs, f'{label}: 0 console errors')
finally:
b.close()
check(len(res['default']['clips']) > 0,
f"CONTROL: the default boot DOES fetch the clip library ({len(res['default']['clips'])} requests) — "
f"so the two zero-arms above are a gate, not a tautology")
return res
# ── arm 5: the deny-list, from the network side ──────────────────────────────────────────────────
def arm_denylist_runtime(p):
head('5. DENY-LIST AT RUNTIME (ruling 3) — nothing banned is even ASKED FOR, across a full walk')
b, pg, errs, reqs = new_page(p)
try:
boot(pg, 'stock=real&gigs=1')
pg.evaluate("() => window.DBG.setSegment(5)")
pg.evaluate("() => window.DBG.shot('venue_night')")
for sel in ('record', 'pub', 'opshop'):
pg.evaluate(f"() => window.DBG.enterShop('{sel}')")
pg.wait_for_timeout(900)
pg.evaluate("() => window.DBG.exitShop()")
pg.wait_for_timeout(200)
pg.wait_for_timeout(1200)
ps = paths(reqs)
hits = [u for u in ps if any(t in u.lower() for t in BANNED)]
check(not hits, f'{len(set(ps))} distinct URLs across street + gig night + 3 interiors — 0 banned names'
+ (f' — SAW {hits[:3]}' if hits else ''))
probe = 'assets/models/character_kit_modular/exports/bodies/body_07.glb'
check(any(t in probe.lower() for t in BANNED), 'CONTROL: the matcher fires on a synthetic banned URL')
check(not errs, '0 console errors over the walk')
finally:
b.close()
def main():
proc = serve(ROOT / 'web', PORT)
try:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
arm_wardrobe(p)
if '--no-sweep' not in sys.argv:
arm_budget(p)
arm_noassets(p)
arm_clip_gates(p)
arm_denylist_runtime(p)
finally:
proc.terminate()
print('')
if fails:
print(f'\033[31m● FAIL\033[0m — {len(fails)} problem(s):')
for f in fails: print(f' · {f}')
return 1
print('\033[32m● PASS\033[0m — wardrobe both arms, interior budget on F\'s own instrument, '
'?noassets clean, clip gates cargo-free, deny-list clean at runtime')
return 0
if __name__ == '__main__':
sys.exit(main())