Fix three acceptance defects in the D4 summary: decoy rebuilt with this pack's plants, three axes per arm as the order requires, two inadmissible packets annulled

This commit is contained in:
heaven 2026-08-14 03:42:24 +03:00
parent 668ee24007
commit 01485125a5
2 changed files with 68 additions and 2 deletions

View file

@ -238,11 +238,19 @@ def score() -> int:
if fp > 0.20 or tp < 0.60:
print("\n⛔ адъюдикация не засчитывается — контроли не пройдены")
return 1
print("\nДОЛЯ ВЕРИФИЦИРОВАННЫХ ПРЕТЕНЗИЙ ПО АРМАМ")
# ⚠ ТОЛЬКО НЕСУЩИЕ ОСИ. Контраст считается по ВЕРНОСТЬ+ЯЗЫК (эррата П-5): ТЕРМИН отдан
# детерминированному канон-гейту, ФОРМА исключена. Претензии по этим двум осям в поправку
# НЕ идут, иначе она правила бы не ту величину.
# ⚠ И ОТДЕЛЬНО ПРО ФОРМУ — ДЕФЕКТ ПРИБОРА, НАЙДЕННЫЙ САМИМ АДЪЮДИКАТОРОМ: окно «МЕСТО В
# ПЕРЕВОДЕ» подаётся через `norm()`, то есть БЕЗ пунктуации и в нижнем регистре. Судить
# вёрстку и пунктуацию по такому окну нельзя в принципе — то, что проверяется, стёрто
# нормализацией. Ось ФОРМА исключается дважды: и как невходящая в сумму, и как непроверяемая.
print("\nДОЛЯ ВЕРИФИЦИРОВАННЫХ ПРЕТЕНЗИЙ ПО АРМАМ (только несущие оси ВЕРНОСТЬ+ЯЗЫК)")
rate = {}
for arm in ARMS:
ks = [k for k, v in key.items() if v.get("arm") == arm and not v.get("fake")
and not v.get("planted") and k in ans]
and not v.get("planted") and k in ans
and v.get("axis") in ("ВЕРНОСТЬ", "ЯЗЫК")]
if ks:
rate[arm] = sum(1 for k in ks if ans[k]) / len(ks)
print(f" {arm}: {rate[arm]:.0%} подтверждено (n={len(ks)})")

View file

@ -50,6 +50,14 @@ def _load(name: str, path: Path):
S = _load("sud", ZONE / "sud.py")
AJ = sys.modules["absjudge"]
# ⚠⚠ ПЕРЕКЛЮЧИТЬ КЛЮЧИ НА СВОЙ ПАК — ИНАЧЕ ДЕКОЙ ВОССТАНАВЛИВАЕТСЯ ЧУЖИМИ ПОСАДКАМИ.
# `absjudge.KEYS` — глобаль, по умолчанию указывающая на `~/books/role-topology/blind-keys`
# (пак 21). Её переключает `sud.setup()`, но свод его НЕ вызывает: он только импортирует модуль.
# Из-за этого `AJ._plant()` в `variant()` брал PLANTS.json ЧУЖОГО пака — 7 посадок вместо наших 6,
# и первое правило там другое. Восстановленный так текст декоя не совпадает с тем, что реально
# видел судья, поэтому метрика «цитат не найдено в своём варианте» считалась против неверного
# текста. Поймано приёмкой 14.08, проверено исполнением: AJ.KEYS указывал на role-topology.
AJ.KEYS = KEYS_D4 = Path.home() / "books" / "dovodka" / "blind-keys-d4"
BO = sys.modules.get("bakeoff") or _load("bakeoff", REPO / "eval" / "role_topology" / "bakeoff.py")
AX = ("ВЕРНОСТЬ", "ТЕРМИН", "ЯЗЫК", "ФОРМА")
@ -158,6 +166,54 @@ def holm(ps: list[float]) -> list[float]:
return adj
ALL_ARMS = ("A0", "A4", "A6", "A6F", "A3", "A2", "A1B", "A1")
def three_axes(d: dict) -> None:
"""⚠ ТРИ ОСИ ПО КАЖДОМУ АРМУ — прямое требование заказа (:95): «Ответ по каждому арму
печатать ТРЕМЯ осями: судья · канон-покрытие · цена. H-1/H-2а/H-2б сверяются по всем трём,
а не по одной удобной». Первая редакция свода печатала ОДНУ судейскую ось по ТРЁМ первичным
контрастам, и за этим пропал ответ на прямой вопрос заказа про арм A4 («полировка немедленный
кандидат в прод», :86-87). Поймано приёмкой 14.08.
Канон считается на ВЫХОДЕ арма, а не на его входе: вход мерился при покупке, а сверяется
качество результата. Цена медиана оплаченной клетки арма из сырья, а не из сметы.
"""
import json as _j # noqa: PLC0415
import statistics as _st # noqa: PLC0415
us = {x["uid"]: x for x in S.units()}
print("\n" + "=" * 78)
print("ТРИ ОСИ ПО КАЖДОМУ АРМУ (требование заказа :95) — судья · канон · цена")
print("=" * 78)
print(f" {'арм':5s}{'судья':>8s}{'канон':>9s}{'$/клетка':>11s} что это")
what = {"A0": "боевой ПОЛНЫЙ ПЕРЕПИС (эталон)", "A4": "перепис + канон-фиксер ПОСЛЕ",
"A6": "dspro-черновик + смысловой контур", "A6F": "dspro-черновик + флаги",
"A3": "черновик + смысловой критик", "A2": "однопроходка уравненного мандата",
"A1B": "черновик + флаги (батарея+канон)", "A1": "черновик + только канон-флаги"}
for arm in ALL_ARMS:
sc = [v["carry"] for (u, a, h), v in d.items() if a == arm]
if not sc:
continue
cov = []
for u in us.values():
t = S.C.base_a0(u["uid"]) if arm == "A0" else S.text_of(arm, u)
if t.strip():
k, n = S.C.BANK.coverage(u["source"], t)
if n:
cov.append(k / n)
pr = []
for f in S.C.OUT.glob(f"{S.C.tag(arm, '')}*.json"):
if any(x in f.name for x in (".ERROR", ".LENGTH", ".FLAGSV1")):
continue
r = _j.loads(f.read_text(encoding="utf-8"))
if r.get("cost_usd"):
pr.append(r["cost_usd"])
print(f" {arm:5s}{_st.mean(sc):8.2f}{(_st.mean(cov) if cov else 0):9.3f}"
f"{(_st.median(pr) if pr else 0):11.5f} {what.get(arm, '')}")
print(" судья — ошибок на единицу (ВЕРНОСТЬ+ЯЗЫК), МЕНЬШЕ лучше")
print(" канон — доля покрытия банка на ВЫХОДЕ арма, БОЛЬШЕ лучше")
print(" цена — медиана оплаченной клетки арма по сырью; A0 куплен прошлыми паками")
def main() -> int:
gates_only = "--gates" in sys.argv
fam: dict = {}
@ -192,6 +248,8 @@ def main() -> int:
if not passes:
print(" ⇒ семейство НЕ имеет права говорить «не хуже»: его вердикты по "
"H-2а/H-2б понижаются до ОПИСАТЕЛЬНЫХ (П-2)")
if fam.get("claude"):
three_axes(fam["claude"]["d"])
if gates_only or not fam:
return 0
print("\n" + "=" * 78)