#!/usr/bin/env python3 """КАССА ПАКА «СИЛЬНЫЙ ТИР». $0 сама по себе; через неё идут ВСЕ платные вызовы пака. Не копия кассы эксп-22, а её НАСТРОЙКА. Механизм там проверенный и дорого доставшийся — фриз-гейт, атомарный замок клетки, проекционный сторож перед каждым вызовом, два независимых пути счёта, — и копирование размножило бы будущие расхождения вместо того, чтобы их ловить. Пак-специфика (зона кода, каталог сырья, теги фаз, потолки, прайс-таблица) подставляется через `money.configure`. Потолки — санкция владельца 09.08: пак ≤$4.00. Фазовые суммируются в $3.35, разница — объявленный резерв на ре-гены эхо-мины и ретраи, а не запас «на всякий случай»: тратится он только через те же теги и виден в отчёте. """ from __future__ import annotations import sys from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") ZONE = Path(__file__).resolve().parent sys.path.insert(0, str(ZONE)) sys.path.insert(0, str(REPO / "eval" / "tenant_panel")) sys.path.insert(0, str(REPO / "eval" / "role_topology")) def _load(name: str, path: Path): """⚠ Ростер пака грузится ПО ПУТИ. `import roster` находил ростер эксп-22: три `sys.path.insert(0, …)` подряд кладут ПОСЛЕДНИЙ вставленный первым, и своя папка оказывалась в хвосте. Поймано печатью селфтеста — «моделей 11» вместо 15, то есть проверка «цена считается для ВСЕХ моделей» молча проверяла только старые.""" import importlib.util # noqa: PLC0415 spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec) sys.modules[name] = mod spec.loader.exec_module(mod) return mod # ⚠ И касса эксп-22 тоже грузится ПО ПУТИ. `import money` здесь работал лишь по счастливому # порядку sys.path: стоило вызвать модуль из файла, который кладёт свою папку первой, как он # находил САМ СЕБЯ и падал циклическим импортом. Правило без исключений: чужое — только _load. M22 = _load("money22", REPO / "eval" / "tenant_panel" / "money.py") ROSTER_ET = _load("roster_et", ZONE / "roster.py") OUT = Path.home() / "books" / "editor-tier" # Теги покупок по фазам. ⚠ Новый тег вписывается СЮДА тем же коммитом, что и код, который его # заводит: расход мимо глоба невидим для кассы, и эксп-21 напоролся на это трижды. TAGS = { "ФA": ("et-scr-*.json",), # скрин сильного тира "ФB": ("et-unit-*.json",), # 16 новых единиц для границы glm-5 (черновик + R0 + R2) "ФC": ("et-ed-*.json", "et-floor-*.json"), # панель + ШУМОВОЙ ПОЛ своего прохода } # ⚠ Потолок ФC поднят $1.80 → $2.30 из ОБЪЯВЛЕННОГО В ПЛАНЕ резерва пака ($0.65), объявляется до # покупок фазы. Две причины, обе замерные: пре-рег-критерий пропустил ТРОИХ (панель $1.844 вместо # сметы на двоих) и плану нужен СВОЙ шумовой пол — вторая независимая генерация самого дешёвого # арма панели, ещё $0.29. Пакетный потолок $4.00 (санкция владельца) НЕ двигается: смета пака $2.93. CEILINGS = {"ФA": 1.10, "ФB": 0.45, "ФC": 2.30} PACK_CEILING = 4.00 M22.configure(ZONE=ZONE, OUT=OUT, TAGS=TAGS, CEILINGS=CEILINGS, PACK_CEILING=PACK_CEILING, GLOB_ALL="et-*.json", ROSTER=ROSTER_ET) # Всё остальное берётся у настроенной кассы — своих реализаций тут нет намеренно. Guarded, Pack, purchase, model_expect = M22.Guarded, M22.Pack, M22.purchase, M22.model_expect def report() -> None: print(f"{'фаза':6s}{'потрачено':>12s}{'потолок':>10s}") tot = 0.0 for ph in TAGS: sp = Guarded(ph).spent() tot += sp mark = " ⛔ ПРОБОЙ" if sp > CEILINGS[ph] + 1e-9 else "" print(f"{ph:6s}{sp:12.6f}{CEILINGS[ph]:10.2f}{mark}") print(f"{'ПАК':6s}{tot:12.6f}{PACK_CEILING:10.2f}") def selftest() -> int: """Обязан пройти ДО первой покупки пака.""" bad = 0 # 1. касса смотрит в СВОЁ сырьё, а не в сырьё эксп-22 if M22.OUT != OUT: print("[ПРОВАЛ] касса не пере-настроена на своё сырьё") bad += 1 if M22.BUY.OUT != OUT: print("[ПРОВАЛ] buy.OUT указывает мимо каталога пака — покупки уедут к соседу") bad += 1 # 2. цена считается для ВСЕХ моделей, включая новые (эксп-22 упал тут на середине скрина) for m in ROSTER_ET.CANDIDATES: try: if not M22.BUY.cost(m, 4500, 0, 2500, 1000, 8000) > 0: raise ValueError(m) except Exception as e: # noqa: BLE001 print(f"[ПРОВАЛ] цена не считается: {m} ({type(e).__name__}: {e})") bad += 1 # 3. в таблице кассы ДОЛЖНЫ быть новые кандидаты — иначе подставился чужой ростер missing = [m for m in ("gpt-5.6-terra", "kimi-k3", "grok-4.5", "glm-5.2") if m not in ROSTER_ET.CANDIDATES] if missing: print(f"[ПРОВАЛ] в таблице кассы нет новых кандидатов: {missing}") bad += 1 # 4. потолки фаз не превышают пакетный if sum(CEILINGS.values()) > PACK_CEILING + 1e-9: print("[ПРОВАЛ] сумма фазовых потолков выше пакетного") bad += 1 # 5. КАЖДЫЙ тег покрыт глобом GLOB_ALL — иначе покупка невидима для `model_expect` for globs in TAGS.values(): for g in globs: if not g.startswith("et-"): print(f"[ПРОВАЛ] тег вне шаблона пака: {g}") bad += 1 # 6. фриз-гейт обязан отказывать коду вне зоны пака import inspect # noqa: PLC0415 if M22.ZONE != ZONE: print("[ПРОВАЛ] фриз-гейт сторожит чужую зону") bad += 1 _ = inspect print(f"касса: тегов {sum(len(v) for v in TAGS.values())} · моделей " f"{len(ROSTER_ET.CANDIDATES)} · провалов {bad}") return bad if __name__ == "__main__": if "--selftest" in sys.argv: sys.exit(1 if selftest() else 0) report()