"""R0-4 — post-deploy FRONTEND gate. Three test rounds were invalidated by a stale frontend (Vercel promote skipped twice; an outdated URL once). This gate verifies the ARTIFACT the testers actually load: fetch the canonical production URL, locate the built bundle, and assert it carries the current engine's markers and points at the right backend. Run it after every Vercel promote — a failure means testers would be looking at old code. Pure stdlib (no pip installs in CI). Usage: python verify_live_frontend.py [base_url] # default: https://room-editor-9y3b.vercel.app Exit 0 = the served frontend is current; non-zero = block/alert. """ import re import sys import urllib.request CANONICAL_URL = "https://room-editor-9y3b.vercel.app" BACKEND_HOST = "modventures-room-visualizer.hf.space" # Marker inventory: object property names survive minification; function # names do not. Keep one marker per shipped feature generation so a stale # build is identifiable by WHICH markers are missing. ENGINE_MARKERS = [ ("defaultRotation", "P1-4 rotation consumption"), ("keepRug", "R3-3 rug toggle"), ("tileWidthM", "R1-3/R2-1 metric sizing"), ("groutFrac", "R2-3 procedural cells"), ("rugFlags", "R3-3 rug flags"), ("Classic Checkered", "catalog present"), ("Calacatta Marble", "R2-2 seamless pack (2026-06-12)"), ("viz2d/convert", "backend API wiring"), (BACKEND_HOST, "points at the canonical backend"), ] def fetch(url: str, timeout: int = 30) -> str: req = urllib.request.Request(url, headers={"User-Agent": "verify-live-frontend"}) with urllib.request.urlopen(req, timeout=timeout) as r: return r.read().decode("utf-8", errors="replace") def main() -> int: base = sys.argv[1] if len(sys.argv) > 1 else CANONICAL_URL print(f"frontend gate target: {base}", flush=True) html = fetch(base) ok = True title = re.search(r"([^<]*)", html) title_ok = bool(title) and "BAETES" in title.group(1) print(f" [{'PASS' if title_ok else 'FAIL'}] title is the current app " f"({title.group(1) if title else 'none'})") ok &= title_ok asset = re.search(r"assets/index-[A-Za-z0-9_-]+\.js", html) if not asset: print(" [FAIL] no built index-*.js asset referenced — not a Vite build?") raise SystemExit("FRONTEND GATE FAILED") print(f" bundle: {asset.group(0)}") bundle = fetch(f"{base}/{asset.group(0)}", timeout=60) for marker, why in ENGINE_MARKERS: passed = marker in bundle print(f" [{'PASS' if passed else 'FAIL'}] {marker} ({why})") ok &= passed if not ok: print("\nFRONTEND GATE FAILED — the served bundle is stale or " "mis-deployed. Did the Vercel production promote happen?") return 1 print("\nFRONTEND GATE PASSED") return 0 if __name__ == "__main__": raise SystemExit(main())