#!/usr/bin/env python3 """OpenFUT UTAS route-reachability reporter. Parses the openfut-utas-host `owner=` dispatch log (the line the host prints for EVERY request) into per-owner and per-Python-domain hit counts, so a staging preflight can assert the post-P1 invariants WITHOUT mutating anything: * economy Python hits == 0 (any nonzero => P1 economy regression) * migrated non-economy Python hits == 0 (accountinfo/settings/leaderboards/ match-reset/phishing are Rust-owned since post-P1) * remaining Python domains are exactly the documented residual set. This is dev/staging tooling (Python is fine here — production authority is Rust). It only READS a log (file path arg, or stdin); it never touches production. Log grammar (openfut-utas-host, src/lib.rs eprintln! lines), examples: utas-host owner=RUST route=economy method=GET path=/ut/game/fifa17/user/credits status=200 utas-host owner=PYTHON_FALLBACK method=GET path=/ut/game/fifa17/hub status=200 utas-host owner=RUST_OVERLAY route=userMassInfo status=200 ... utas-host owner=RUST_OBSERVE route=auth status=200 ... utas-host owner=RUST route=accountinfo status=200 Usage: openfut-reachability.py [LOGFILE] # report + gate (exit 1 on regression) hub logs ... | openfut-reachability.py # read from stdin """ import re import sys from collections import Counter OWNER_RE = re.compile(r"utas-host owner=(\S+)") PATH_RE = re.compile(r"path=(\S+)") ROUTE_RE = re.compile(r"route=(\S+)") # Non-economy routes migrated to Rust ownership post-P1. A PYTHON_FALLBACK hit on # any of these is a MIGRATION REGRESSION (the classifier lost the route). MIGRATED_NON_ECONOMY = { "user/accountinfo", "settings", "leaderboards/options", "match/reset", "phishing", # phishing/{trusteddevice,question,validate} "hub", "club/stats/global", # year/consumables/staff } # Documented residual Python-owned non-economy domains (expected > 0 until # migrated). Keep in sync with docs/PRODUCTION_AUTHORITY_MATRIX.md. RESIDUAL_PYTHON = { "openfut/account/sync", "club/stats/context", # country/league/team nation-bucket sub-screens "clientdata/userHubData", } def python_domain(path: str) -> str: """Normalize a proxied path to its domain key.""" p = path.split("?", 1)[0] if p.startswith("/openfut/account/sync"): return "openfut/account/sync" # strip /ut/game// or /ut/v2/game// prefix m = re.match(r"/ut/(?:v2/)?game/[^/]+/(.*)", p) tail = m.group(1) if m else p.lstrip("/") if tail.startswith("phishing/"): return "phishing" if tail.startswith("club/stats/country") or tail.startswith( "club/stats/league" ) or tail.startswith("club/stats/team"): return "club/stats/context" if tail.startswith("club/stats"): return "club/stats/global" if tail.startswith("clientdata/"): return "clientdata/userHubData" return tail def is_economy(line: str, route: str | None) -> bool: return route == "economy" def main() -> int: src = sys.stdin if len(sys.argv) > 1: src = open(sys.argv[1], encoding="utf-8", errors="replace") owners = Counter() economy_python = [] # economy routes that escaped to Python (BAD) migrated_regressions = [] # migrated non-economy routes that hit Python (BAD) python_domains = Counter() for line in src: mo = OWNER_RE.search(line) if not mo: continue owner = mo.group(1) owners[owner] += 1 route = (ROUTE_RE.search(line) or [None, None])[1] if ROUTE_RE.search(line) else None if owner == "PYTHON_FALLBACK": pm = PATH_RE.search(line) path = pm.group(1) if pm else "?" dom = python_domain(path) python_domains[dom] += 1 if dom in MIGRATED_NON_ECONOMY: migrated_regressions.append(path) # A properly classified economy route is owner=RUST route=economy. If an # economy tail ever shows up as PYTHON_FALLBACK that is the regression. if owner == "PYTHON_FALLBACK" and route is None: pm = PATH_RE.search(line) path = pm.group(1) if pm else "" if re.search(r"/(user/credits|store/(transaction|purchasegroup)|purchased|tradepile|" r"transfermarket|auctionhouse|trade/|item)", path, re.I): economy_python.append(path) print("=== OWNER COUNTS ===") for owner, n in owners.most_common(): print(f" {owner:18} {n}") print("=== PYTHON_FALLBACK DOMAINS ===") for dom, n in python_domains.most_common(): tag = "" if dom in MIGRATED_NON_ECONOMY: tag = " <-- REGRESSION (should be Rust)" elif dom not in RESIDUAL_PYTHON: tag = " <-- UNEXPECTED (not in residual set)" print(f" {dom:28} {n}{tag}") ok = True if economy_python: ok = False print("\nFAIL: economy routes reached Python (P1 REGRESSION):") for p in economy_python: print(f" {p}") if migrated_regressions: ok = False print("\nFAIL: migrated non-economy routes reached Python:") for p in migrated_regressions: print(f" {p}") unexpected = [d for d in python_domains if d not in RESIDUAL_PYTHON and d not in MIGRATED_NON_ECONOMY] if unexpected: print("\nWARN: undocumented Python domains (classify + add to matrix):") for d in unexpected: print(f" {d} ({python_domains[d]})") print("\nGATE:", "PASS" if ok else "FAIL") return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())