From 2fed3d060f3fea3c25c4e5c5411c520c431bad9c Mon Sep 17 00:00:00 2001 From: heaven Date: Tue, 11 Aug 2026 11:30:03 +0300 Subject: [PATCH] Raise the pack ceiling to 6.15 on the owner's sanction, funding the A1B re-buy that a session defect made necessary --- eval/dovodka/contour.py | 81 ++++++++++++++++++++++++++++++----------- eval/dovodka/money_d.py | 14 ++++++- 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/eval/dovodka/contour.py b/eval/dovodka/contour.py index 5faadbbd..0bfa88af 100644 --- a/eval/dovodka/contour.py +++ b/eval/dovodka/contour.py @@ -165,45 +165,71 @@ def canon_gaps(src: str, out: str) -> list[str]: # «батарея + канон-гейт»; (2) значения проверок — СЛОВАРИ, и наивный фильтр, будь он даже # достижим, вымел бы в список мест ЗНАМЕНАТЕЛИ: «проверка sentences дала 90», «words дала 1154». # То есть починка одного типа сделала бы арм хуже, чем сломанный. Поэтому whitelist, а не эвристика. +# ⚠⚠ ВТОРАЯ ПОЧИНКА (11.08, после того как 14 клеток A1B из 31 вернули ПУСТОТУ на потолке вывода, +# $0.170 впустую). Первая редакция whitelist'а брала поля-СЧЁТЧИКИ, и два из них были ядовиты: +# +# * `palladius.nonconformant_bank` — это счёт строк САМОГО БАНКА, не прошедших палладиевскую +# проверку (`battery.py:159` — `for d in bank_dst`). К переводу он не относится ВООБЩЕ: число +# одинаково во всех 32 единицах, потому что банк один. Фиксеру говорилось «имена/термины +# переданы мимо банка (8 шт.)», и он искал в тексте то, чего в тексте нет. Рассуждение +# выросло вшестеро против A1 (40 694 знака против 7 400) и съело весь бюджет вывода. +# * `ty_vy.mixed_in_unit` — адреса не имеет и сам докстринг проверки (`battery.py:365-370`) +# называет её «сигналом к просмотру, а не счётом ошибок»: смешение между РАЗНЫМИ +# собеседниками легитимно. Инструкцией фиксеру такое быть не может. +# +# НОРМА, ЗАВЕДЁННАЯ ЭТИМ: **флаг без адреса — не место.** Контракт арма — «черновик плюс СПИСОК +# МЕСТ»; поле, которое не может назвать, ЧТО чинить, в список не идёт и остаётся описательным. +# Поэтому ниже у каждого поля указано, откуда берётся адрес, и поля без адреса исключены. BATTERY_VIOLATIONS = { - "palladius": ("nonconformant_bank",), - "cjk_leak": ("over_probe_threshold", "over_prod_threshold"), - "typography": ("violations", "ascii_quotes", "hyphen_as_dash"), - "numbers": ("lost_n", "invented_n"), - "ty_vy": ("mixed_in_unit",), - "gender": ("mismatched",), + # поле-признак поле-адрес (None = адрес находится по самому признаку) + "palladius": (("nonconformant_text", "nonconformant_text_words"),), + "cjk_leak": (("over_prod_threshold", None),), + "typography": (("ascii_quotes", None), ("hyphen_as_dash", None)), + "numbers": (("lost_n", "lost"), ("invented_n", "invented")), + "gender": (("mismatched", "cases"),), } BATTERY_SAYS = { - "palladius.nonconformant_bank": "имена/термины переданы мимо банка ({v} шт.)", - "cjk_leak.over_probe_threshold": "в переводе остались иероглифы исходника", - "cjk_leak.over_prod_threshold": "в переводе остались иероглифы исходника (сверх прод-порога)", - "typography.violations": "нарушения типографики ({v} шт.)", - "typography.ascii_quotes": "прямые кавычки вместо «ёлочек» ({v} шт.)", - "typography.hyphen_as_dash": "дефис вместо тире ({v} шт.)", - "numbers.lost_n": "числа исходника потеряны в переводе ({v} шт.)", - "numbers.invented_n": "в переводе появились числа, которых нет в исходнике ({v} шт.)", - "ty_vy.mixed_in_unit": "внутри единицы смешаны «ты» и «вы»", - "gender.mismatched": "род персонажа расходится с карточкой ({v} шт.)", + "palladius.nonconformant_text": "не-палладиевская транскрипция имени: {a}", + "cjk_leak.over_prod_threshold": "в переводе остались иероглифы исходника: {a}", + "typography.ascii_quotes": "прямые кавычки вместо «ёлочек», {v} шт.", + "typography.hyphen_as_dash": "дефис вместо тире, {v} шт.", + "numbers.lost_n": "величины исходника отсутствуют в переводе: {a}", + "numbers.invented_n": "в переводе есть величины, которых нет в исходнике: {a}", + "gender.mismatched": "род персонажа расходится с карточкой: {a}", } +# ОПИСАТЕЛЬНЫЕ (печатаются как ось результата, фиксеру НЕ передаются — адреса нет): +# ty_vy.mixed_in_unit · palladius.nonconformant_bank · typography.violations (агрегат) def battery_flags(u: dict, draft: str) -> list[str]: - """Нарушения батареи на тексте. Пустой список — законный ответ; ИСКЛЮЧЕНИЕ — не законный.""" + """Нарушения батареи НА ТЕКСТЕ, каждое с адресом. Падение = дефект, НЕ глотаем.""" import battery as B # noqa: PLC0415 + import re # noqa: PLC0415 bank = BANK.PR.bank() unit = B.Unit(source=u["source"], draft=draft, final=draft, bank_dst={v["dst"] for v in bank.values()}, term_pairs=[(t, v["dst"]) for t, v in bank.items() if u["source"].count(t)]) - r = B.run_unit(unit) # падение = дефект, НЕ глотаем + r = B.run_unit(unit) out = [] for check, fields in BATTERY_VIOLATIONS.items(): val = r.get(check) if not isinstance(val, dict): continue - for f in fields: - v = val.get(f) - if isinstance(v, (int, float, bool)) and v: - out.append("БАТАРЕЯ: " + BATTERY_SAYS[f"{check}.{f}"].format(v=int(v))) + for sig, addr_field in fields: + v = val.get(sig) + if not (isinstance(v, (int, float, bool)) and v): + continue + if addr_field: + addr = val.get(addr_field) or [] + elif check == "cjk_leak": + addr = sorted(set(re.findall(r"[一-鿿]", draft)))[:12] + else: + addr = [] + if addr_field and not addr: # признак есть, адреса нет — не место + continue + says = BATTERY_SAYS[f"{check}.{sig}"] + out.append("БАТАРЕЯ: " + says.format( + v=int(v), a="; ".join(f"«{x}»" for x in addr[:12]))) return out @@ -389,6 +415,17 @@ def cmd_selftest() -> int: ck("в места батареи не попали ЗНАМЕНАТЕЛИ (sentences/words/lines)", not any(w in f for f in bf for w in ("sentences", "words", "lines", "candidates")), "; ".join(bf[:2])) + # ⚠ ФЛАГ БЕЗ АДРЕСА — НЕ МЕСТО. Норма заведена после того, как безадресный флаг (и притом + # относящийся к БАНКУ, а не к тексту) отправил фиксера в неограниченный перебор: 14 клеток + # A1B из 31 упёрлись в потолок вывода и вернули пустоту, $0.170 впустую. Гейт требует, чтобы + # каждое место батареи несло либо цитату в «ёлочках», либо счёт находимого по признаку класса. + allb = [f for x in us if base_draft(x["uid"]).strip() + for f in battery_flags(x, base_draft(x["uid"]))] + noaddr = [f for f in allb if "«" not in f and " шт." not in f] + ck("каждое место батареи несёт АДРЕС (цитату либо находимый класс)", + not noaddr, f"без адреса {len(noaddr)}: {noaddr[:2]}") + ck("батарея не флагует свойства БАНКА вместо свойств текста", + not any("мимо банка" in f for f in allb)) # ⚠ A2: арм, которого не существовало — вызов падал TypeError, ветка отказа была недостижима. m2 = a2_msgs(u["source"]) ck("A2 собирается (однопроходка уравненного мандата)", len(m2) >= 2) diff --git a/eval/dovodka/money_d.py b/eval/dovodka/money_d.py index 1bd654c2..8379d0dd 100644 --- a/eval/dovodka/money_d.py +++ b/eval/dovodka/money_d.py @@ -101,9 +101,19 @@ TAGS = { # Это ПЕРЕКЛАДКА внутри санкционированного пакета, пакетный потолок $6.00 не двигается; сумма # фазовых потолков 5.83 ≤ 6.00. Спрошено потому, что срабатывание гарда = вопрос владельцу по # заказу, а пак 23 потерял пункт CONFIRM/DENY ровно за то, что решил подобное сам. -CEILINGS = {"ФД-A": 1.680, "ФД-B": 0.630, "ФД-C": 0.170, "ФД-D": 0.050, +# ⚠ ПОТОЛОК $6.00 → $6.15 (санкция владельца 11.08, «поднимай»), ФД-A 1.680 → 1.950. +# ПРИЧИНА — ОШИБКА СЕССИИ, И ЭТО ЗАПИСАНО ЗДЕСЬ, А НЕ СМЯГЧЕНО В ОТЧЁТЕ. Первая редакция списка +# мест для арма A1B несла поле `palladius.nonconformant_bank` — счёт строк САМОГО БАНКА, не +# прошедших палладиевскую проверку. К переводу оно не относится: число одинаково во всех единицах, +# и фиксер искал в тексте то, чего в тексте нет. Рассуждение выросло вшестеро против A1 (40 694 +# знака против 7 400); 14 клеток из 31 упёрлись в потолок вывода и вернули ПУСТОТУ — $0.170 +# впустую. Ещё $0.186 стоили 16 завершившихся клеток, отправленных в карантин ради ОДНОРОДНОСТИ +# арма: они построены на том же отравленном списке и одним армом с починенными быть не могут. +# Без этой ошибки $6.00 хватало. Норма, заведённая ею: **флаг без адреса — не место**, и селфтест +# `contour.py` теперь роняет харнесс, если безадресный флаг попадёт в список фиксеру. +CEILINGS = {"ФД-A": 1.950, "ФД-B": 0.630, "ФД-C": 0.170, "ФД-D": 0.050, "ФД-E": 2.370, "ФД-F": 0.720, "ФД-G": 0.210} -PACK_CEILING = 6.00 +PACK_CEILING = 6.15 M22.configure(ZONE=ZONE, OUT=OUT, TAGS=TAGS, CEILINGS=CEILINGS, PACK_CEILING=PACK_CEILING, GLOB_ALL="dv-*.json", ROSTER=ROSTER)