132 lines
7.9 KiB
Python
132 lines
7.9 KiB
Python
#!/usr/bin/env python3
|
||
"""Ф0.3 — ПРОВЕРКА ИСПОЛНЕНИЕМ четырёх починок, заказанных эксп-21 §8. $0.
|
||
|
||
Промт эксп-22 объявляет их обязательными ДО первой покупки. Список §8 писался как «что надо
|
||
сделать», и часть пунктов эксп-21 закрыл сам, не вычеркнув из списка. Поэтому здесь не чтение
|
||
кода, а ЗАПУСК: каждый пункт проверяется на входе с известным ответом, а где возможно — на том
|
||
самом артефакте, из-за которого пункт появился.
|
||
|
||
§8-1 гейт ре-гена черновика обязан ловить и целевую письменность (был принят и оплачен
|
||
английский выход с долей латиницы 0.79);
|
||
§8-2 карта раскладки слепого чтения лежит ВНЕ каталога пакетов;
|
||
§8-3 обоснование судьи персистируется, и голос с ненулевой осью без цитаты отвергается;
|
||
§8-4 три докстринга, названные лживыми, больше не противоречат исполняемому коду.
|
||
|
||
Ненулевой код возврата = покупать нельзя.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import io
|
||
import json
|
||
import re
|
||
import sys
|
||
from contextlib import redirect_stdout
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
ZONE = REPO / "eval" / "role_topology"
|
||
sys.path.insert(0, str(ZONE))
|
||
|
||
RAW21 = Path.home() / "books" / "role-topology"
|
||
|
||
BAD = 0
|
||
|
||
|
||
def chk(name: str, ok: bool, detail: str = "") -> None:
|
||
global BAD # noqa: PLW0603
|
||
BAD += not ok
|
||
print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {name}" + (f" {detail}" if detail else ""))
|
||
|
||
|
||
def c1_draft_gate() -> None:
|
||
"""Гейт письменности: пустой · эхо исходника · не целевой язык · годный."""
|
||
import bakeoff as BO # noqa: PLC0415
|
||
chk("§8-1 пустой выход отвергнут", bool(BO.draft_reject_reason(" ")))
|
||
chk("§8-1 эхо исходника отвергнуто",
|
||
BO.draft_reject_reason("第一章 蛊真人转世重生,方源看着眼前的青茅山,心中冷笑" * 8)
|
||
== "эхо исходника")
|
||
chk("§8-1 английский выход отвергнут",
|
||
BO.draft_reject_reason("The young man looked at the mountain and smiled coldly. " * 8)
|
||
== "выход не на целевом языке")
|
||
chk("§8-1 русский выход принят",
|
||
BO.draft_reject_reason("Юноша посмотрел на гору и холодно усмехнулся. " * 8) == "")
|
||
# Регрессия на ТОМ САМОМ артефакте: он оплачен и принят прежним гейтом.
|
||
f = RAW21 / "bo2-DRAFT-34749d6ccc-r2.json"
|
||
if f.exists():
|
||
why = BO.draft_reject_reason(json.loads(f.read_text(encoding="utf-8"))["content"])
|
||
chk("§8-1 исторический артефакт (латиница) отвергается", why != "", f"вердикт: {why!r}")
|
||
else:
|
||
chk("§8-1 исторический артефакт доступен", False, f"нет {f}")
|
||
|
||
|
||
def c2_blind_map() -> None:
|
||
"""Карта раскладки не должна лежать в каталоге, который отдают читателю."""
|
||
import blind_read as BR # noqa: PLC0415
|
||
chk("§8-2 карта вне каталога пакетов", BR.KEYS.resolve() != BR.PACKETS.resolve(),
|
||
f"пакеты {BR.PACKETS.name} · карта {BR.KEYS.name}")
|
||
maps = list(BR.KEYS.glob("*-MAP.json")) if BR.KEYS.exists() else []
|
||
stray = list(BR.PACKETS.glob("*MAP*")) if BR.PACKETS.exists() else []
|
||
chk("§8-2 карт в каталоге пакетов нет", not stray, f"карт в ключах: {len(maps)}")
|
||
|
||
|
||
def c3_judge_reasons() -> None:
|
||
"""Ненулевая ось без цитаты обязана быть отвергнута, и отказ обязан снимать прежний голос."""
|
||
import absjudge as AJ # noqa: PLC0415
|
||
buf = io.StringIO()
|
||
with redirect_stdout(buf):
|
||
rc = AJ.selfcheck()
|
||
out = buf.getvalue()
|
||
chk("§8-3 selfcheck рига без провалов", rc == 0, f"провалов {rc}")
|
||
for probe in ("ненулевая ось без цитаты отвергнута",
|
||
"голое имя оси без цитаты отвергнуто",
|
||
"отказ СНЯЛ прежний годный голос"):
|
||
chk(f"§8-3 регрессия «{probe}»", f"[OK ] {probe}" in out)
|
||
# Персист: у боевых голосов эксп-21 обоснование обязано быть непустым там, где ось ненулевая.
|
||
votes = list((RAW21 / "aj-abs4-votes").glob("*.json"))
|
||
if votes:
|
||
bad = 0
|
||
tot = 0
|
||
for f in votes:
|
||
for r in json.loads(f.read_text(encoding="utf-8")).values():
|
||
tot += 1
|
||
if sum(r.get(a, 0) for a in ("ВЕРНОСТЬ", "ТЕРМИН", "ЯЗЫК", "ФОРМА")) and \
|
||
not (r.get("why") or "").strip():
|
||
bad += 1
|
||
chk("§8-3 боевые голоса несут обоснование", bad == 0, f"{tot - bad}/{tot} клеток с текстом")
|
||
|
||
|
||
def c4_docstrings() -> None:
|
||
"""Три докстринга, названные лживыми. Проверяем ПРОТИВОРЕЧИЕ КОДУ, а не наличие слов."""
|
||
bo = (ZONE / "bakeoff.py").read_text(encoding="utf-8")
|
||
ju = (ZONE / "judges.py").read_text(encoding="utf-8")
|
||
doc_bo = bo.split('"""')[1] if '"""' in bo else ""
|
||
doc_ju = ju.split('"""')[1] if '"""' in ju else ""
|
||
# (а) «черновик стоит $0» — код черновик ПОКУПАЕТ (`run_draft`) и кладёт цену в экономику.
|
||
chk("§8-4 докстринг bakeoff не утверждает «черновик стоит $0»",
|
||
not re.search(r"черновик\s+стоит\s+\$0", doc_bo))
|
||
chk("§8-4 код черновик действительно покупает", "def run_draft" in bo and "bo2-DRAFT-" in bo)
|
||
# (б) «вердикт при ≥2 повторах из 3» — REPS_PER_ORDER=1, правило вердикта другое.
|
||
import bakeoff as BO # noqa: PLC0415
|
||
claims_2of3 = bool(re.search(r"≥\s*2\s+(?:повтор\w*\s+)?из\s+3", doc_bo))
|
||
chk("§8-4 докстринг bakeoff не обещает правило «≥2 из 3»", not claims_2of3,
|
||
f"REPS_PER_ORDER={BO.REPS_PER_ORDER}")
|
||
# (в) состав судей в докстринге judges.py обязан совпадать с кортежем JUDGES.
|
||
import judges as J # noqa: PLC0415
|
||
named = [m for m in J.JUDGES if m in doc_ju]
|
||
chk("§8-4 докстринг judges называет ровно действующий состав",
|
||
len(named) == len(J.JUDGES) and "grok-4.3` выпал" in doc_ju,
|
||
f"{', '.join(J.JUDGES)}")
|
||
|
||
|
||
def main() -> int:
|
||
print("Ф0.3 — ПОЧИНКИ ЭКСП-21 §8, ПРОВЕРКА ИСПОЛНЕНИЕМ\n")
|
||
c1_draft_gate()
|
||
c2_blind_map()
|
||
c3_judge_reasons()
|
||
c4_docstrings()
|
||
print(f"\n{'ВСЕ ЧЕТЫРЕ ПУНКТА ЗАКРЫТЫ' if not BAD else f'ОТКРЫТО ПУНКТОВ: {BAD} — ПОКУПКИ ЗАПРЕЩЕНЫ'}")
|
||
return BAD
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(1 if main() else 0)
|