#!/usr/bin/env python3 """Сверка НЕСУЩИХ ЧИСЕЛ отчёта `docs/experiments/20-editor-role.md` с сырьём. Зачем отдельным скриптом: числа в отчёт попадают через мою же аналитику, и опечатка или устаревшая цифра после пере-съёмки арма визуально неотличимы от факта. Здесь каждое заявленное число пере-выводится из персистированных артефактов заново и сравнивается с тем, что написано в тексте. Запуск: eval/.venv/bin/python eval/editor_harness/verify_report.py Ненулевой код возврата = отчёт разошёлся с сырьём и его нельзя лендить. """ from __future__ import annotations import difflib import json import statistics import sys from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") sys.path.insert(0, str(REPO / "eval" / "bank_arbitration")) RAW = Path.home() / "books" / "gu-zhenren" / "editor-harness" import editor_wire_probe as P # noqa: E402 FAILS = 0 def g(tag: str): f = RAW / f"{tag}.json" return json.loads(f.read_text(encoding="utf-8")) if f.exists() else None def ck(claim: str, ok: bool, got: str = "") -> None: global FAILS if not ok: FAILS += 1 print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {claim}" + ("" if ok else f" — сырьё даёт {got}")) def cjk(s: str) -> int: return sum(1 for ch in s if 0x3400 <= ord(ch) <= 0x9FFF) def out_text(tag: str) -> str | None: a = RAW / f"{tag}.applied.json" if a.exists(): return json.loads(a.read_text(encoding="utf-8"))["text"] r = g(tag) return r["content"] if r else None def main() -> None: # §3.1 — отношение цены дифф/full по моделям и режимам for ms, pre, claim in (("dp", "eh", 2.83), ("gl", "eh", 0.64), ("gl", "eht", 0.86), ("gr", "eh", 0.60), ("gr", "eht", 0.66), ("lu", "eh", 1.01), ("lu", "eht", 0.89)): f = [g(f"{pre}-{ms}-full-w{k}") for k in range(6)] d = [g(f"{pre}-{ms}-diff-harness-w{k}") for k in range(6)] if any(x is None for x in f + d): ck(f"§3.1 {ms}/{pre}", False, "нет артефактов") continue r = statistics.median(d[k]["cost_usd"] / f[k]["cost_usd"] for k in range(6)) ck(f"§3.1 {ms}/{pre} отношение дифф/full = {claim}", abs(r - claim) < 0.015, f"{r:.3f}") # §3.3 — цена починки и полнота снятия дефектов for ms, claim in (("dp", 0.000625), ("lu", 0.000173)): f = RAW / f"repair-{ms}.json" if not f.exists(): ck(f"§3.3 {ms}", False, "нет repair-*.json") continue rows = json.loads(f.read_text(encoding="utf-8")) med = statistics.median(x["cost"] for x in rows) ck(f"§3.3 {ms} цена одной починки = {claim}", abs(med - claim) < 1e-6, f"{med:.6f}") snyat = sum(1 for x in rows if not x["still"]) ck(f"§3.3 {ms} дефект снят 12/12", snyat == 12, str(snyat)) inf = [x for x in rows if not x["degenerate"]] strict = sum(1 for x in inf if not x["still"] and x["restored"]) ck(f"§3.3 {ms} восстановлен строго 7/8", (strict, len(inf)) == (7, 8), f"{strict}/{len(inf)}") # §4 — доля слов черновика, доживших до выхода for ms, c, claim in (("dp", "full", 0.65), ("gl", "full", 0.83), ("dp", "diff-harness", 0.98)): vals = [] for k in range(6): o = g(f"eh-{ms}-{c}-w{k}") if not o: continue dw = P.draft_of(k).split() ow = (out_text(f"eh-{ms}-{c}-w{k}") or "").split() sm = difflib.SequenceMatcher(None, dw, ow, autojunk=False) vals.append(sum(b.size for b in sm.get_matching_blocks()) / len(dw)) ck(f"§4 {ms}/{c} слов дожило {claim:.0%}", bool(vals) and abs(statistics.median(vals) - claim) < 0.015, f"{statistics.median(vals):.3f}" if vals else "нет данных") # §5.4 — утечка исходника в выход у luna for c, w_claim, n_claim in (("full", 3, 19), ("diff-harness", 3, 16)): outs = [cjk(out_text(f"eh-lu-{c}-w{k}") or "") for k in range(6)] ck(f"§5.4 luna/{c}: окон с утечкой {w_claim}, знаков {n_claim}", sum(1 for x in outs if x) == w_claim and sum(outs) == n_claim, f"{sum(1 for x in outs if x)} окон, {sum(outs)} знаков") # deepseek обязан быть чист по обоим контрактам — это утверждение отчёта for c in ("full", "diff-harness"): outs = [cjk(out_text(f"eh-dp-{c}-w{k}") or "") for k in range(6)] ck(f"§5.4 deepseek/{c}: утечки нет", sum(outs) == 0, f"{sum(outs)} знаков") print(f"\n{'ВСЕ ЧИСЛА ОТЧЁТА СХОДЯТСЯ' if not FAILS else f'РАСХОЖДЕНИЙ: {FAILS}'}") sys.exit(1 if FAILS else 0) if __name__ == "__main__": main()