137 lines
4.1 KiB
Python
137 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Build the browser evidence index from the committed Markdown ledger."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
LEDGER = ROOT / "research" / "completion-verification" / "evidence-ledger.md"
|
|
OUTPUT = ROOT / "evaluation-site" / "assets" / "evidence-data.js"
|
|
|
|
SECTION_SLUGS = {
|
|
"完成证明与 evaluator 误差": "verification",
|
|
"测试、benchmark 和协议会不会错误认证": "protocol",
|
|
"故障发现、定位和恢复": "recovery",
|
|
"变更风险、协作和重复可靠性": "reliability",
|
|
"全文池与排除": "context",
|
|
}
|
|
|
|
|
|
def parse_link(value: str) -> tuple[str, str]:
|
|
match = re.fullmatch(r"\[([^\]]+)\]\((https?://[^)]+)\)", value.strip())
|
|
if not match:
|
|
raise ValueError(f"Invalid paper link: {value}")
|
|
return match.group(1), match.group(2)
|
|
|
|
|
|
def parse_ledger(text: str) -> list[dict[str, str]]:
|
|
section = ""
|
|
records: list[dict[str, str]] = []
|
|
|
|
for raw_line in text.splitlines():
|
|
heading = re.match(r"^## \d+\.\s+(.+)$", raw_line)
|
|
if heading:
|
|
title = heading.group(1).strip()
|
|
section = SECTION_SLUGS.get(title, "")
|
|
continue
|
|
|
|
if not raw_line.startswith("| ["):
|
|
continue
|
|
|
|
columns = [column.strip() for column in raw_line.strip().strip("|").split("|")]
|
|
if len(columns) not in {3, 5}:
|
|
raise ValueError(f"Unexpected table row with {len(columns)} columns: {raw_line}")
|
|
|
|
title, url = parse_link(columns[0])
|
|
arxiv_match = re.search(r"(\d{4}\.\d{5})", url)
|
|
if not arxiv_match:
|
|
raise ValueError(f"Missing arXiv id: {url}")
|
|
arxiv_id = arxiv_match.group(1)
|
|
title = re.sub(rf",\s*{re.escape(arxiv_id)}$", "", title)
|
|
|
|
record = {
|
|
"id": arxiv_id,
|
|
"title": title,
|
|
"url": url,
|
|
"depth": columns[1],
|
|
"theme": section,
|
|
}
|
|
|
|
if len(columns) == 5:
|
|
record.update(
|
|
{
|
|
"evidence": columns[2],
|
|
"supports": columns[3],
|
|
"boundary": columns[4],
|
|
}
|
|
)
|
|
else:
|
|
record.update(
|
|
{
|
|
"evidence": "",
|
|
"supports": "",
|
|
"boundary": columns[2],
|
|
}
|
|
)
|
|
|
|
records.append(record)
|
|
|
|
return records
|
|
|
|
|
|
def build_payload(text: str) -> dict[str, object]:
|
|
records = parse_ledger(text)
|
|
counts = {
|
|
"pool": len(records),
|
|
"audited": sum(record["depth"] in {"core", "support"} for record in records),
|
|
"core": sum(record["depth"] == "core" for record in records),
|
|
"support": sum(record["depth"] == "support" for record in records),
|
|
"context": sum(record["depth"] == "context" for record in records),
|
|
}
|
|
return {
|
|
"source": "research/completion-verification/evidence-ledger.md",
|
|
"source_sha256": hashlib.sha256(text.encode("utf-8")).hexdigest(),
|
|
"counts": counts,
|
|
"records": records,
|
|
}
|
|
|
|
|
|
def render(payload: dict[str, object]) -> str:
|
|
body = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
|
|
return (
|
|
"/* Generated by evaluation-site/build_research_data.py. */\n"
|
|
f"window.K1412_EVIDENCE={body};\n"
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"--check",
|
|
action="store_true",
|
|
help="Fail if the generated browser data is stale.",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
expected = render(build_payload(LEDGER.read_text(encoding="utf-8")))
|
|
if args.check:
|
|
if not OUTPUT.exists() or OUTPUT.read_text(encoding="utf-8") != expected:
|
|
print(f"stale: {OUTPUT.relative_to(ROOT)}")
|
|
return 1
|
|
print(f"ok: {OUTPUT.relative_to(ROOT)}")
|
|
return 0
|
|
|
|
OUTPUT.write_text(expected, encoding="utf-8")
|
|
print(f"wrote: {OUTPUT.relative_to(ROOT)}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|