131 lines
6.4 KiB
Python
131 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate deterministic S6-05 OPFS/browser boundary evidence."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
|
|
CHROMIUM_TEST = ("node", "web/app/test_browser_opfs_s6_05.mjs")
|
|
PLAYWRIGHT_TEST = ("xvfb-run", "-a", "node", "scripts/test_browser_opfs_playwright_s6_05.mjs")
|
|
LOGICAL_TEST = ("node", "web/app/test_opfs_runtime_s6_05.mjs")
|
|
INPUTS = (
|
|
"context-lock.json",
|
|
"coverage/authoritative-kinematics-s6-04.report.json",
|
|
"coverage/browser-matrix-closure.report.json",
|
|
"coverage/browser-matrix-remediation.report.json",
|
|
"coverage/recovery-offline-fault-matrix.report.json",
|
|
"web/workspace/journal.mjs",
|
|
"web/workspace/migration.mjs",
|
|
"web/workspace/recovery.mjs",
|
|
"web/workspace/opfs-runtime.mjs",
|
|
"web/app/browser-opfs-worker.mjs",
|
|
"web/app/browser-opfs-probe.html",
|
|
"web/app/test_browser_opfs_s6_05.mjs",
|
|
"scripts/test_browser_opfs_playwright_s6_05.mjs",
|
|
"web/app/test_opfs_runtime_s6_05.mjs",
|
|
)
|
|
|
|
|
|
def canonical(value: Any) -> str:
|
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
|
|
|
|
def digest(value: bytes) -> str:
|
|
return hashlib.sha256(value).hexdigest()
|
|
|
|
|
|
def artifact(root: Path, path: str) -> dict[str, Any]:
|
|
value = root / path
|
|
if not value.is_file(): return {"path": path, "status": "missing"}
|
|
return {"path": path, "bytes": value.stat().st_size, "sha256": digest(value.read_bytes())}
|
|
|
|
|
|
def run(root: Path, command: tuple[str, ...]) -> dict[str, Any]:
|
|
result = subprocess.run(command, cwd=root, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
|
|
parsed = None
|
|
for line in reversed(result.stdout.decode("utf-8", errors="replace").splitlines()):
|
|
if line.lstrip().startswith("{"):
|
|
try:
|
|
parsed = json.loads(line)
|
|
break
|
|
except json.JSONDecodeError:
|
|
continue
|
|
return {
|
|
"command": list(command),
|
|
"returncode": result.returncode,
|
|
"status": "PASSED" if result.returncode == 0 else "FAILED",
|
|
"stdout_sha256": digest(result.stdout),
|
|
"stderr_sha256": digest(result.stderr),
|
|
"sample": parsed,
|
|
}
|
|
|
|
|
|
def generate(root: Path, output: Path) -> dict[str, Any]:
|
|
root = root.resolve()
|
|
chromium_runs = [run(root, CHROMIUM_TEST), run(root, CHROMIUM_TEST)]
|
|
playwright_runs = [run(root, PLAYWRIGHT_TEST), run(root, PLAYWRIGHT_TEST)]
|
|
logical_runs = [run(root, LOGICAL_TEST), run(root, LOGICAL_TEST)]
|
|
chromium_samples = [item["sample"] for item in chromium_runs]
|
|
playwright_samples = [item["sample"] for item in playwright_runs]
|
|
logical_samples = [item["sample"] for item in logical_runs]
|
|
logical_ok = all(item["returncode"] == 0 and item["sample"] and item["sample"].get("status") == "verified" for item in logical_runs)
|
|
chromium_stable = chromium_samples[0] == chromium_samples[1]
|
|
chromium_blocked = all(item and item.get("status") == "blocked" for item in chromium_samples)
|
|
playwright_ok = all(item["returncode"] == 0 and item["sample"] and item["sample"].get("status") == "verified" for item in playwright_runs)
|
|
playwright_stable = playwright_samples[0] == playwright_samples[1]
|
|
report: dict[str, Any] = {
|
|
"schema": "authoritative-browser-s6-05.v1",
|
|
"task_id": "S6-05",
|
|
"context_id": CONTEXT_ID,
|
|
"status": "verified" if logical_ok and playwright_ok else "blocked",
|
|
"implementation_status": "verified" if logical_ok and playwright_ok else "blocked",
|
|
"scope": {
|
|
"logical_contract": "journal/offline package integrity and unavailable-OPFS gate",
|
|
"physical_browser": "Chromium Dedicated Worker OPFS probe",
|
|
"required_physical_result": "verified",
|
|
},
|
|
"checks": {
|
|
"logical_contract": {"status": "verified" if logical_ok else "blocked", "runs": logical_runs},
|
|
"chromium_probe": {"status": "blocked", "runs": chromium_runs, "stable": chromium_stable, "blocked_sample": chromium_samples[0]},
|
|
"headed_playwright_probe": {"status": "verified" if playwright_ok else "blocked", "runs": playwright_runs, "stable": playwright_stable, "verified_sample": playwright_samples[0]},
|
|
"repeat_determinism": {"status": "verified" if chromium_stable and playwright_stable and logical_samples[0] == logical_samples[1] else "blocked"},
|
|
"safari_firefox_boundary": {"status": "out_of_scope", "reason": "Safari and Firefox are outside this project's migration scope"},
|
|
"physical_crash_boundary": {"status": "blocked", "reason": "No browser crash/restart oracle is available; successful OPFS round-trip does not prove crash recovery"},
|
|
"release_stop": {"status": "verified", "action": "STOP_RELEASE", "candidate_eligible": False},
|
|
},
|
|
"inputs": [artifact(root, path) for path in INPUTS],
|
|
"boundaries": [
|
|
"Headless Chromium exposed the OPFS path but navigator.storage.getDirectory did not complete (observed root/worker timeout); headed Playwright Chromium completed the sync-handle round-trip",
|
|
"Safari and Firefox are OUT_OF_SCOPE; physical browser crash/restart remains a separate boundary",
|
|
"logical journal/offline integrity is verified separately and is not an OPFS browser oracle",
|
|
"official release/oracle blockers remain STOP_RELEASE",
|
|
],
|
|
}
|
|
report["report_sha256"] = hashlib.sha256(canonical({key: value for key, value in report.items() if key != "report_sha256"}).encode()).hexdigest()
|
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
|
|
return report
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
|
|
parser.add_argument("--report", type=Path, default=Path("coverage/authoritative-browser-s6-05.report.json"))
|
|
args = parser.parse_args()
|
|
root = args.repo_root.resolve()
|
|
output = args.report if args.report.is_absolute() else root / args.report
|
|
report = generate(root, output)
|
|
print(f"authoritative_browser_s6_05={report['status']} chromium={report['checks']['chromium_probe']['status']} logical={report['implementation_status']}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|