File size: 2,921 Bytes
a4f876a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"""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"<title>([^<]*)</title>", 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())