#!/usr/bin/env python3 """Offline integrity checks for the l35QweVxgn static candidate.""" from __future__ import annotations import hashlib import json import re import stat import sys from html.parser import HTMLParser from pathlib import Path ROOT = Path(__file__).resolve().parents[1] ALLOWED = { "README.md", "index.html", "docs/reproducibility-audit.md", "evidence/active_claims.json", "evidence/pins.json", "evidence/repository_tree.json", "evidence/route.json", "evidence/safety_screen.json", "pages/01-overview.md", "pages/02-judge-claims.md", "pages/03-paper-proof-audit.md", "pages/04-release-code-audit.md", "pages/05-safety-and-licence.md", "pages/06-evidence-boundaries.md", "scripts/test_verifier.py", "scripts/verify_release.py", } ACTIVE_CLAIMS_SHA256 = "9f6c6afb77f9c65abe1d162f8c0ebd2d2cac24d41f622510b7eacc3af2241885" CHALLENGE_REVISION = "7b5b56aebf3abe590eab9f2c241a796125cab928" PAPER_ID = "l35QweVxgn" SPACE_CARD = """--- title: Continual Learning Theory Evidence emoji: "🔎" colorFrom: blue colorTo: indigo sdk: static app_file: index.html tags: - icml2026-repro - paper-l35QweVxgn - source-proof-audit - continual-learning-theory --- """ README_BODY_SHA256 = "dc607bc0cd2114f0005f1e2e08b2cb8bba0b00b76841b3d8c7f8680497027322" EXPECTED_PINS = { "paper": { "openreview_id": PAPER_ID, "openreview_pdf_url": "https://openreview.net/pdf?id=l35QweVxgn", "title": "On the Theory of Continual Learning with Gradient Descent for Neural Networks", "authors": ["Hossein Taheri", "Avishek Ghosh", "Arya Mazumdar"], "direct_openreview_access": "challenge-gated from this machine; official search index exposed the current PDF text", "author_preprint": { "url": "https://arxiv.org/abs/2510.05573v2", "source_url": "https://arxiv.org/e-print/2510.05573v2", "source_sha256": "c9e327f4e26929d282b9481af3a6fc884e387fc08a48a344dfc38b7f95d587c5", "main_tex_sha256": "23fc0c0daefd957095a412d6d6e08f8d03ac22b3128096b939b8ed10535202d3", "metadata_sha256": "13531c175594e0bc3c804e75b0c3df29980fc8909991a02481c0081ab755d4f6", }, }, "judge_feeds": { "challenge_space_revision": CHALLENGE_REVISION, "default": { "url": "https://huggingface.co/spaces/ICML-2026-agent-repro/challenge/resolve/7b5b56aebf3abe590eab9f2c241a796125cab928/claims.json", "sha256": "af5ab2d62f786ae36861957cbd08b4188f6d4c86e67152becc661a9c5bbb9d57", "paper_claim_count": 2, }, "anchored": { "url": "https://huggingface.co/spaces/ICML-2026-agent-repro/challenge/resolve/7b5b56aebf3abe590eab9f2c241a796125cab928/claims_anchored.json", "sha256": "eb3f2d878646ca5c40da121741a613c1f9e1e10c14845f373db107e74a4ef439", "paper_claim_count": 6, }, "merge": "anchored_overrides_default", "active_source": "claims_anchored.json", }, "retrieved_utc": "2026-07-19T21:15:00Z", } EXPECTED_REPOSITORY_TREE = { "repository": "https://github.com/hosseinta2/continual-learning-with-neural-nets.git", "commit": "5e7329081bc948d4aa4159def5feed50160411b1", "commit_time": "2025-11-20T13:44:57-08:00", "tracked_file_count": 5, "tracked_files": [ {"path": "FashionMNIST", "git_blob": "5bf6480c3000ec007946bb325805fbd07a2d8ec2", "sha256": "aebd6e8561d21bdbfb5ba968c23ad96c8327da2f94ae7d8dc519686fdd4467c6"}, {"path": "README.md", "git_blob": "ac23a6611a1f0392d9b180a3ae3e1221f236c038", "sha256": "7a482f22fbf823ae60f50ba4a3d53bf344670839fddfdbce24ddc89abe35c300"}, {"path": "continual learning codes MNIST-2.ipynb", "git_blob": "5ce1279ccb62af755e019ab807b59021ede88869"}, {"path": "continual learning codes_transformer-2.ipynb", "git_blob": "4309d3baca18e26637edb9fbd33fb129bd1a3448"}, {"path": "continual_learning_codes-XOR.ipynb", "git_blob": "dd0c846b9e761016de0632800dd8b0d57a0cc5cb"}, ], "licence_status": "no licence, copying, or notice file found in the tracked tree; author material is not redistributed", } ALLOWED_HTML_TAGS = {"html", "head", "meta", "title", "body", "main", "h1", "p", "ul", "li", "a"} ALLOWED_PAGE_LINKS = { "pages/01-overview.md", "pages/02-judge-claims.md", "pages/03-paper-proof-audit.md", "pages/04-release-code-audit.md", "pages/05-safety-and-licence.md", "pages/06-evidence-boundaries.md", } def fail(message: str) -> None: raise AssertionError(message) class InertHtmlPolicyParser(HTMLParser): """Accept only the deliberately small, local static-document subset.""" def __init__(self) -> None: super().__init__(convert_charrefs=True) self.stack: list[str] = [] self.saw_doctype = False def handle_decl(self, declaration: str) -> None: if self.saw_doctype or declaration.lower() != "doctype html": fail("index.html has an invalid declaration") self.saw_doctype = True def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: if tag not in ALLOWED_HTML_TAGS: fail(f"index.html has forbidden tag: {tag}") values: dict[str, str | None] = {} for name, value in attrs: if name.startswith("on"): fail(f"index.html has event-handler attribute: {name}") if name in values: fail(f"index.html repeats attribute: {name}") values[name] = value self.verify_attributes(tag, values) if tag != "meta": self.stack.append(tag) def handle_endtag(self, tag: str) -> None: if tag == "meta" or not self.stack or self.stack[-1] != tag: fail(f"index.html has invalid closing tag: {tag}") self.stack.pop() def handle_startendtag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: fail(f"index.html has self-closing tag: {tag}") def handle_comment(self, data: str) -> None: fail("index.html has comments") def handle_pi(self, data: str) -> None: fail("index.html has a processing instruction") def unknown_decl(self, data: str) -> None: fail("index.html has an unknown declaration") def verify_attributes(self, tag: str, values: dict[str, str | None]) -> None: if tag == "html": if values != {"lang": "en"}: fail("index.html html attributes differ from the inert policy") elif tag == "meta": if values not in ({"charset": "utf-8"}, {"name": "viewport", "content": "width=device-width, initial-scale=1"}): fail("index.html meta attributes differ from the inert policy") elif tag == "a": if set(values) != {"href"} or values["href"] not in ALLOWED_PAGE_LINKS: fail("index.html link is not an allowlisted local evidence page") elif values: fail(f"index.html tag has forbidden attributes: {tag}") def verify_complete(self) -> None: if not self.saw_doctype: fail("index.html is missing the HTML doctype") if self.stack: fail(f"index.html has unclosed tags: {self.stack}") def release_files() -> set[str]: found: set[str] = set() for path in ROOT.rglob("*"): relative = path.relative_to(ROOT) if relative.parts and relative.parts[0] == ".git": continue if path.is_symlink(): fail(f"symlink not allowed: {relative}") if path.is_file(): if not stat.S_ISREG(path.stat().st_mode): fail(f"non-regular file not allowed: {relative}") if path.stat().st_mode & 0o111: fail(f"executable bit not allowed: {relative}") found.add(relative.as_posix()) return found def verify_allowlist() -> None: found = release_files() unexpected = sorted(found - ALLOWED) missing = sorted(ALLOWED - found) if unexpected: fail(f"unexpected release files: {unexpected}") if missing: fail(f"missing release files: {missing}") def verify_claims() -> None: path = ROOT / "evidence/active_claims.json" digest = hashlib.sha256(path.read_bytes()).hexdigest() if digest != ACTIVE_CLAIMS_SHA256: fail("active claims digest does not match the pinned extraction") claims = json.loads(path.read_text(encoding="utf-8")) if claims.get("paper_id") != PAPER_ID: fail("active claims paper id mismatch") if claims.get("source") != "claims_anchored.json": fail("active claims source is not anchored") if claims.get("challenge_revision") != CHALLENGE_REVISION: fail("active claims revision mismatch") records = claims.get("claims") if not isinstance(records, list) or len(records) != 6: fail("expected exactly six active anchored claims") if any(record.get("status") != "unverified" for record in records): fail("active statuses must remain unverified") def verify_space_card() -> None: readme = (ROOT / "README.md").read_text(encoding="utf-8") if not readme.startswith(SPACE_CARD): fail("README.md does not contain the expected static Space card") body = readme.removeprefix(SPACE_CARD) if hashlib.sha256(body.encode("utf-8")).hexdigest() != README_BODY_SHA256: fail("README.md body differs from the reviewed scientific boundary") def verify_pins() -> None: pins = json.loads((ROOT / "evidence/pins.json").read_text(encoding="utf-8")) if pins != EXPECTED_PINS: fail("immutable provenance pins differ from the expected manifest") def verify_pages_and_html() -> None: for page in sorted((ROOT / "pages").glob("*.md")): stripped = re.sub(r"\s+", "", page.read_text(encoding="utf-8")) if len(stripped) < 200: fail(f"page below 200 stripped characters: {page.name}") parser = InertHtmlPolicyParser() parser.feed((ROOT / "index.html").read_text(encoding="utf-8")) parser.close() parser.verify_complete() def verify_fixed_outcomes() -> None: tree = json.loads((ROOT / "evidence/repository_tree.json").read_text(encoding="utf-8")) safety = json.loads((ROOT / "evidence/safety_screen.json").read_text(encoding="utf-8")) route = json.loads((ROOT / "evidence/route.json").read_text(encoding="utf-8")) if tree != EXPECTED_REPOSITORY_TREE: fail("repository provenance and five-file inventory differ from the expected manifest") if safety.get("decision") != "admitted_static_only": fail("safety decision mismatch") if route.get("execution") != "not attempted": fail("execution boundary mismatch") if route.get("route") != "source_and_proof_audit_only": fail("route boundary mismatch") def main() -> int: verify_allowlist() verify_claims() verify_space_card() verify_pins() verify_pages_and_html() verify_fixed_outcomes() print("PASS: static candidate integrity verified offline") return 0 if __name__ == "__main__": try: raise SystemExit(main()) except AssertionError as error: print(f"FAIL: {error}", file=sys.stderr) raise SystemExit(1)