#!/usr/bin/env python3 """Ad-hoc verification for VerdictTank v5.0 proposal page. Not a test suite. Checks the four things that can actually be wrong here: 1. Content rules (no em/en dashes, no double hyphens in prose, no excluded model) 2. HTML structural integrity (tag balance, required sections, no dangling anchors) 3. Factual grounding (every headline number traces to the validation data) 4. Deploy parity (local == served == remote, archive intact) """ import hashlib import re import subprocess import sys import urllib.request from html.parser import HTMLParser LOCAL = "/root/projects/verdicttank/index-v5.0.html" BASE = "https://proposals.itpropartner.com/verdicttank" SSH = ["ssh", "-o", "StrictHostKeyChecking=no", "-i", "/root/.ssh/itpp-infra", "root@152.53.241.111"] DOCROOT = "/home/ippadmin/htdocs/proposals.itpropartner.com/verdicttank" results = [] def check(name, ok, detail=""): results.append((name, ok, detail)) print(f"{'PASS' if ok else 'FAIL'} {name}" + (f" | {detail}" if detail else "")) class TextExtractor(HTMLParser): """Rendered-text extractor: skips style/script so CSS class names (.callout--fail, .stat--hero) don't produce false double-hyphen hits.""" def __init__(self): super().__init__() self.text, self.skip = [], False self.stack, self.mismatched = [], [] self.ids, self.hrefs = set(), [] self.void = {"meta", "br", "img", "hr", "input", "link", "source"} def handle_starttag(self, tag, attrs): a = dict(attrs) if a.get("id"): self.ids.add(a["id"]) if a.get("href", "").startswith("#"): self.hrefs.append(a["href"][1:]) if tag in ("style", "script"): self.skip = True if tag not in self.void: self.stack.append(tag) def handle_endtag(self, tag): if tag in ("style", "script"): self.skip = False if self.stack and self.stack[-1] == tag: self.stack.pop() else: self.mismatched.append((tag, list(self.stack[-3:]))) def handle_data(self, data): if not self.skip: self.text.append(data) html = open(LOCAL, encoding="utf-8").read() p = TextExtractor() p.feed(html) prose = " ".join(p.text) print("=" * 72) print("1. CONTENT RULES (checked against rendered prose, not raw markup)") print("=" * 72) for label, needle in [("em dash", "\u2014"), ("en dash", "\u2013"), ("double hyphen", "--"), ("excluded model family", "5.6")]: n = prose.count(needle) check(f"zero {label}", n == 0, f"{n} occurrence(s)" if n else "clean") # rfptank must appear ONLY as a defensive holding, never as a product surface rfp_ctx = [prose[max(0, m.start() - 90):m.start() + 60] for m in re.finditer(r"rfptank\.com", prose)] defensive = all(re.search(r"defensive|legacy|holding", c, re.I) for c in rfp_ctx) check("rfptank.com only as defensive holding", defensive and len(rfp_ctx) > 0, f"{len(rfp_ctx)} mention(s), all qualified") check("verdicttank.com is primary domain", prose.count("verdicttank.com") >= 2) print() print("=" * 72) print("2. HTML STRUCTURAL INTEGRITY") print("=" * 72) check("no unclosed tags", not p.stack, str(p.stack[:5]) if p.stack else "balanced") check("no mismatched close tags", not p.mismatched, str(p.mismatched[:3]) if p.mismatched else "clean") REQUIRED = ["thesis", "pipeline", "validation", "errors", "panel", "worked", "pricing", "competitive", "deployment", "legal"] missing = [s for s in REQUIRED if s not in p.ids] check(f"all {len(REQUIRED)} required sections present", not missing, f"missing: {missing}" if missing else "thesis..legal") dangling = sorted(set(h for h in p.hrefs if h and h not in p.ids)) check("no dangling in-page anchors", not dangling, f"broken: {dangling}" if dangling else f"{len(p.hrefs)} anchors resolve") nsec = len(re.findall(r""), f"{nsec} sections") print() print("=" * 72) print("3. FACTUAL GROUNDING (headline numbers must trace to validation data)") print("=" * 72) # Ground truth from the 2026-08-12 validation run FACTS = { "14": "material errors caught", "-0.40": "aggregate delta vs solo", "4.94": "panel aggregate mean", "5.34": "solo aggregate mean", "4.40": "RFP Tank panel", "4.93": "RFP Tank solo", "6.14": "VentureBuilt panel", "6.10": "VentureBuilt solo", "4.29": "CartMySupply panel", "5.00": "CartMySupply solo", "8.2": "Financial Integrity high seat", "2.8": "Execution Feasibility outlier", } for val, desc in FACTS.items(): check(f"cites {val} ({desc})", val in prose) # Arithmetic self-consistency: the deltas must actually subtract correctly for panel, solo, delta, name in [(4.40, 4.93, -0.53, "RFP Tank"), (6.14, 6.10, 0.04, "VentureBuilt"), (4.29, 5.00, -0.71, "CartMySupply"), (4.94, 5.34, -0.40, "Aggregate")]: check(f"{name} delta arithmetic", abs((panel - solo) - delta) < 0.005, f"{panel} - {solo} = {panel - solo:+.2f}") # Error taxonomy: parse the summary cards from the page itself rather than # trusting a hardcoded dict, then reconcile against the per-proposal detail rows. cards = re.findall( r'
]*>
(\d+)
([^<]+)
', html) classes = [(int(n), t) for n, t in cards if "Total" not in t] total_card = [int(n) for n, t in cards if "Total" in t] check("taxonomy summary card present", len(total_card) == 1) check("taxonomy classes sum to headline", sum(n for n, _ in classes) == total_card[0] == 14, " + ".join(f"{n}" for n, _ in classes) + f" = {sum(n for n, _ in classes)}") # Every class card must be backed by that many tagged rows in the detail tables tagged_rows = len(re.findall(r'(?:Revenue|Competitive|Execution|Legal|Team)', html)) check("detail rows match headline count", tagged_rows == 14, f"{tagged_rows} tagged findings") per_class = {} for cls in ["Revenue", "Competitive", "Execution", "Legal", "Team"]: per_class[cls] = len(re.findall(rf'{cls}', html)) for n, label in classes: key = next(k for k in per_class if k.lower() in label.lower()) check(f"'{label.strip()}' card == detail rows", per_class[key] == n, f"card {n} vs {per_class[key]} rows") check("mean is labelled mean, not median", "Median 4.7" not in prose and "Mean 4.7" in prose) # The page must NOT claim score elevation anywhere elevation_claims = re.findall( r"(higher score|raises? (?:your )?scores?|score elevation works|boosts? (?:your )?scores?)", prose, re.I) bad = [c for c in elevation_claims if "higher score" not in c.lower()] check("makes no score-elevation claim", not bad, f"{bad}" if bad else "'higher score' appears only in negation") check("publishes the FAIL result", "THESIS FAIL" in prose or "thesis fail" in prose.lower()) check("discloses 8-of-11 seat coverage", "8 of 11" in prose) print() print("=" * 72) print("4. DEPLOY PARITY") print("=" * 72) local_md5 = hashlib.md5(html.encode()).hexdigest() served = urllib.request.urlopen(f"{BASE}/index.html", timeout=30).read() check("served index.html == local source", hashlib.md5(served).hexdigest() == local_md5, local_md5[:12]) remote = subprocess.run(SSH + [f"md5sum {DOCROOT}/index.html"], capture_output=True, text=True, timeout=60) check("on-disk index.html == local source", remote.stdout.split()[0] == local_md5 if remote.stdout else False, "verified against disk, not cache") for path in ["index.html", "index-v4.0.html", "architecture.html", "judge-pool-spec.md"]: try: code = urllib.request.urlopen(f"{BASE}/{path}", timeout=20).status except Exception as e: code = str(e) check(f"HTTP 200 {path}", code == 200, str(code)) v4 = urllib.request.urlopen(f"{BASE}/index-v4.0.html", timeout=30).read().decode(errors="replace") check("archive is genuinely v4.0", "VerdictTank v4.0" in v4, re.search(r"([^<]*)", v4).group(1).strip()) check("v5 served page is v5.0", "Error Detection Density" in served.decode(errors="replace")) ownership = subprocess.run( SSH + [f"stat -c '%U:%G' {DOCROOT}/index.html {DOCROOT}/index-v4.0.html"], capture_output=True, text=True, timeout=60) owners = ownership.stdout.split() check("ownership is ippadmin:ippadmin", bool(owners) and all(o == "ippadmin:ippadmin" for o in owners), " ".join(owners)) print() print("=" * 72) failed = [n for n, ok, _ in results if not ok] print(f"RESULT: {len(results) - len(failed)}/{len(results)} checks passed") if failed: print("FAILED:") for f in failed: print(f" - {f}") print("=" * 72) sys.exit(1 if failed else 0)