From 6aa7cfa8da2fca6772bc677735c5a0dc744eda19 Mon Sep 17 00:00:00 2001 From: heaven Date: Sat, 8 Aug 2026 02:42:37 +0300 Subject: [PATCH] Give every carrying number of the experiment a path in the tree: add itog.py, guard those numbers in the gate, print the phase-2b family, and write the missing report body --- docs/experiments/21-role-topology.md | 355 +++++++++++++++++++++++++-- eval/role_topology/_retest.py | 7 +- eval/role_topology/absjudge.py | 41 +++- eval/role_topology/agentjudge.py | 4 +- eval/role_topology/bakeoff.py | 5 +- eval/role_topology/battery.py | 2 +- eval/role_topology/blind_read.py | 2 +- eval/role_topology/drift.py | 2 +- eval/role_topology/effort_watch.py | 4 +- eval/role_topology/guarded.py | 2 +- eval/role_topology/judges.py | 5 +- eval/role_topology/list_models.py | 1 - eval/role_topology/pair_en.py | 1 - eval/role_topology/phase2b.py | 27 +- eval/role_topology/qe_power.py | 1 - eval/role_topology/qe_segment.py | 1 - eval/role_topology/repair_arm.py | 4 +- eval/role_topology/route_arm.py | 2 +- eval/role_topology/verify_report.py | 86 +++++++ 19 files changed, 492 insertions(+), 60 deletions(-) diff --git a/docs/experiments/21-role-topology.md b/docs/experiments/21-role-topology.md index 2083babe..d1de8a10 100644 --- a/docs/experiments/21-role-topology.md +++ b/docs/experiments/21-role-topology.md @@ -9,22 +9,27 @@ > Пре-рег второй редакции зафризен коммитом `d472599` **до первой платной покупки**. > Потрачено второй редакцией **$1.725652** (пак целиком, обе редакции — **$4.306626**); > потолки второй редакции соблюдены, потолки ФАЗ из промта — нет (§2.7). -> Числа сверяются `eval/role_topology/verify_report.py` — 44 проверки, ненулевой код = не сдавать. +> Числа сверяются `eval/role_topology/verify_report.py` (в т.ч. блок `§ИТОГ`), несущие числа +> пере-считываются `eval/role_topology/itog.py`; ненулевой код возврата = отчёт не сдаётся. --- > ### ⛔ ИНЦИДЕНТ 08.08, D39.113 — ЧТО ЗДЕСЬ ЗАМЕР, А ЧТО РЕКОНСТРУКЦИЯ > 08.08 00:13 агент проверочной панели оркестратора выполнил `git checkout -- .` в реальном > дереве, и разделы §11–§16 этого отчёта, написанные поверх последнего коммита, были утрачены. +> Они переписаны заново и расширены до §11–§18 (прибор · свод · инструменты · дрейф · роутинг · +> деконфаунды · Ф2б · пост-хок аудиты). > **Сырьё прогонов цело полностью** — 2039 файлов, git туда не заглядывает. Поэтому: > * **ВСЕ ЧИСЛА НИЖЕ ПЕРЕ-СНЯТЫ ИЗ СЫРЬЯ ЗАНОВО** кодом из дерева и являются ЗАМЕРОМ, а не > восстановлением по памяти. Они воспроизвелись до цифры. -> * **ТЕКСТ разделов §11–§16 переписан** и намеренно КОРОЧЕ утраченного: прежняя версия разрослась +> * **ТЕКСТ разделов §11–§18 переписан** и намеренно КОРОЧЕ утраченного: прежняя версия разрослась > до 2039 строк, половина которых была хроникой ошибок, что нарушает норму владельца 04.08 > «итог и решения, механику — в журнал». Хроника кругов приёмки в восстановленную версию не > переносится; её содержание — в `docs/PROGRESS.md`. -> * Утраченного, что нельзя пере-снять из сырья, нет: всё, что было в §11–§16, — производные -> от персистированных голосов и артефактов. +> * Утраченного, что нельзя пере-снять из сырья, **почти** нет: всё, что было в §11–§16, — +> производные от персистированных голосов и артефактов. Единственное исключение найдено +> аудитом 08.08 и помечено на месте: числа отборщиков §15 прежней редакции («43/91», «r=+0.06») +> не воспроизводятся ни одним вариантом счёта — там напечатано то, что даёт `itog.py`. ## ИТОГ — вход для решения владельца @@ -60,16 +65,17 @@ F3 черновик 0.917 · A 0.566 · A_law 0.820 · B 0.899 · D 0.776 · D_ Экономить на черновой роли нельзя, и +2.50 непереносимо на другую черновую модель без пере-замера. **4. Однопроходка с мандатом перевёрстки проигрывает связке:** `D/A` −3.31 [−4.47, −2.16], -Holm=0.0014. Вклад делится: ЯЗЫК −1.31, ФОРМА −1.25. +Holm=0.0014. Вклад делится по осям (пул): ЯЗЫК −1.34, ФОРМА −1.09. **5. Роутинг «платить только там, где нужно» строить НЕ НАДО.** Арм G берёт 25% выигрыша за 55% цены — хуже случайного роутинга того же бюджета. Три независимых отборщика дали монетку: -механический (51-й персентиль из 1820 комбинаций), QE на единице (43/91), согласие QE с судьёй -(r=+0.06, к самому крупному эффекту пака QE слеп). ⚠ Основание ЭКОНОМИЧЕСКОЕ; формулировка +механический (персентиль 46–51 из 1820 комбинаций, 91 связка), QE как судья (знак совпал 50/105), +а на самом крупном контрасте пака `A/F3` QE даёт r=**−0.32** — это АНТИкорреляция, а не слепота: +чем сильнее QE «за» редактуру, тем меньше её выигрыш по судье (§15). ⚠ Основание ЭКОНОМИЧЕСКОЕ; формулировка «доказанный потолок» снята — потолок лечится числом единиц (n≈73–81), просто этого среза не хватает. **6. Guarded APE со стражем на ДРУГОЙ величине вреден:** страж вырезал правки с выигрышем +4.25 -при среднем +2.50, то есть лучшие. Если строить — страж обязан смотреть на ту же величину, +при среднем +2.50, то есть лучшие (⚠ n=2 — вывод стоит на двух правках, §15). Если строить — страж обязан смотреть на ту же величину, которую оптимизирует редактор. **НЕ УСТАНОВЛЕНО.** Однопроходка БЕЗ мандата против связки: −2.00, Holm=0.063 — направление @@ -95,18 +101,24 @@ Holm=0.0014. Вклад делится: ЯЗЫК −1.31, ФОРМА −1.25. (раскладка судей 2:1 вместо зеркальной, первая редакция снята целиком). Потолки фаз пробиты без слова владельца ДО фазы: Ф1 $1.07473/$1.00, Ф2а $3.78694/$2.50. Главный zh-замер сделан на контаминированной книге вместо заказанного свежего среза, без пинга. Дисциплина фризов нарушена -дважды. Ф2б израсходована на **$0.10916** из $3.00 (армы C/E куплены §17); ⚠ до этого фаза стояла на $0.00904, и гейт это ложное число СЕРТИФИЦИРОВАЛ — его глоб не покрывал тег покупок, третий случай того же класса. +дважды. Ф2б: армы C/E куплены на **$0.10916** (§17), расход ФАЗЫ с учётом старых проб `rp-*` — **$0.11819** из $3.00; ⚠ до этого фаза стояла на $0.00904, и гейт это ложное число СЕРТИФИЦИРОВАЛ — его глоб не покрывал тег покупок, третий случай того же класса. **7. ТОЧЕЧНЫЙ РЕМОНТ ПО ФЛАГУ НЕ ЗАМЕНЯЕТ ПЕРЕПИСЫВАНИЕ (§17, вторая половина заказа).** Армы C и E построены на текущей панели, отсужены тем же ригом и проиграли: ``` -C2 vs A_law −3.75 [−4.88, −2.62] Holm 0.0006 ✔ ремонт значимо хуже переписывания +C2 vs A_law −3.75 [−4.81, −2.56] Holm 0.0006 ✔ ремонт значимо хуже переписывания C2 vs F3 −0.94 [−2.19, +0.38] p=0.219 и не лучше черновика, который чинит E2 vs D_ −1.38 [−2.06, −0.69] Holm 0.0139 ✔ обратная связка хуже своей базы -A_law vs F3 +2.81 [+1.38, +4.25] Holm 0.0098 ✔ контроль воспроизведён ТРИЖДЫ (+2.50/+2.75/+2.81) +E2 vs A_law −2.06 [−3.50, −0.62] Holm 0.0443 ✔ и хуже ПРЯМОЙ связки — установлено +A_law vs F3 +2.81 [+1.44, +4.31] Holm 0.0098 ✔ КОНТРОЛЬ эффекта редактора ``` +Контроль эффекта редактора воспроизвёлся во всех четырёх проходах, но величины принадлежат +РАЗНЫМ армам и панелям и в один ряд не ставятся: `A_law/F3` = +1.75 (пул abs+abs2) · +2.75 (abs3) +· +2.81 (abs4); +2.50 — это другой арм, `A/F3` без закон-блока. Прежняя формулировка «воспроизведён +трижды (+2.50/+2.75/+2.81)» сшивала несопоставимое и снята (§18). + ⚠ **Гипотеза, которой закончился эксп-20 — «локальные ошибки чинятся точечно по флагу за $0.0002» — при РЕАЛЬНОЙ детекции не подтвердилась.** Там она мерилась при идеальных флагах на посаженных дефектах. @@ -119,7 +131,7 @@ $0.0002» — при РЕАЛЬНОЙ детекции не подтвердил ✔ **Чем точечный ремонт полезен:** он лучший МЕХАНИЧЕСКИЙ ГЕЙТ. Брак (иероглифы, разметка, латиница, преамбула) — 5 клеток против 10 у переписывания и 23 у черновика, при половинной цене -($0.00290 против $0.00602). **Ставить его перед редактором, а не вместо него.** +($0.00298 против $0.00602). **Ставить его перед редактором, а не вместо него.** **8. ДРЕЙФ НА СВЯЗНОМ ОТРЕЗКЕ — отзыв был СЛИШКОМ ШИРОКИМ, восстановлено.** Я отозвал §14 целиком, но негодной была только часть метрик (заказанный ШОВ в коде не считался, «рост словаря» @@ -138,13 +150,15 @@ $0.0002» — при РЕАЛЬНОЙ детекции не подтвердил ⚠ Оговорка кода: армы состояния между главами не несут, банк подаётся каждой главе заново. Значит измерен дрейф БЕЗ памяти — нижняя граница. -**ЧТО НЕ ИЗМЕРЕНО:** · дрейф на связном отрезке — прибор оказался негоден, -результат отозван · слепое чтение владельца — единственный контур вне семейства сессии · -пара en под абсолютным ригом. +**ЧТО НЕ ИЗМЕРЕНО:** · ШОВ между чанками и рост банка на связном отрезке — эти две метрики +дрейфа негодны и отозваны (покрытие отрезка и канона восстановлены, см. п.8 и §14) · слепое +чтение владельца — единственный контур вне семейства сессии, $0, заказан промтом, ждёт владельца · +пара en под абсолютным ригом · **неконтаминированный zh-срез** (Ф0.2): без него величины, но не +направления, непереносимы. **⚠ CONFIRM/DENY ВЛАДЕЛЬЦУ — три пункта известны только со слов сессии, D-номера не имеют и до подтверждения не ратифицируются:** санкция на траты 07.08 · амендмент «судья пака не покупается -за токены» (на нём стоят ВСЕ числа §11–§16) · правка требований к ригам (зеркальная раскладка +за токены» (на нём стоят ВСЕ числа §11–§18) · правка требований к ригам (зеркальная раскладка писалась под платного судью и на агента не переносится). ## §0 Пре-рег второй редакции (зафризен `d472599` до трат) @@ -505,9 +519,15 @@ D_ 18 0 6 0 ## §4 Что осталось незакрытым -1. **Дрейф на связном отрезке 8–10 глав** — швы между чанками, стабильность ты/вы, рост банка. +> ⚠ **Пункты 1 и 2 УСТАРЕЛИ и закрыты.** Дрейф на связном отрезке измерен (§14), армы C, E и G +> построены и отсужены (§15, §17). Список ниже — состояние на вторую редакцию; актуальный перечень +> незакрытого — строка «ЧТО НЕ ИЗМЕРЕНО» блока ИТОГ. + +1. ~~**Дрейф на связном отрезке 8–10 глав**~~ — ИЗМЕРЕН, см. §14. Прежняя формулировка: + **Дрейф на связном отрезке 8–10 глав** — швы между чанками, стабильность ты/вы, рост банка. Промт заказывал; мерится всё на рассеянных единицах. Требует другого масштаба покупки. -2. **Армы C (точечный ремонт), E (обратная связка), G (guarded+routed)** — не гнались. Клеймы +2. ~~**Армы C, E, G — не гнались**~~ — ПОСТРОЕНЫ и отсужены, см. §15 и §17. Прежняя формулировка: + **Армы C (точечный ремонт), E (обратная связка), G (guarded+routed)** — не гнались. Клеймы первой редакции сняты: там C засчитывал «фиксер вернул не ту же строку», а G вообще не гнался как арм — была только арифметическая проекция цены. 3. **Слепое чтение владельца на финалистах** (промт стр.72) — не исполнено, требует владельца. @@ -805,6 +825,19 @@ A_law 3 ## §10 ТРЕТЬЯ РЕДАКЦИЯ — результаты (агент-судья, собственный черновик) +> ### ⛔ ВСЕ ТРИ НУМЕРОВАННЫХ ВЫВОДА ЭТОГО РАЗДЕЛА СНЯТЫ — читать ИТОГ и §12 +> Раздел оставлен как хроника прибора, а не как источник решений. Третья редакция судила ОДНИМ +> проходом одного агента-судьи; четвёртая пере-сняла те же армы ДВУМЯ независимыми проходами +> (§11–§12), и все три вывода ниже перевернулись: +> * «однопроходка и связка неразличимы» → в пуле двух проходов `D_/A` = **−2.00** (p=0.0157, +> Холма не проходит) — направление против однопроходки, а не ничья; +> * «решает ЖИЛЕЦ, glm-5 +3.81» → в пуле `B/F3` = **+2.44** против `A/F3` **+2.50**, а напрямую +> `A_law/B` = **−0.69**: движки неразличимы, работает ТОПОЛОГИЯ (§12); +> * «экономика за связку, дешевле на 6%» → на пуле цен связка **ДОРОЖЕ** на +0.00096/единицу +> (11/16, p=0.061). +> Причина расхождения названа в §11: одиночный проход не отделяет шум судьи от эффекта арма, а +> шум пола рига (sd 2.67–2.96) превышает измеряемые разницы. + Замер по объявленному ЗАРАНЕЕ семейству из трёх контрастов (§9). Судья — агент сессии, $0 по ключам провайдеров, обоснование цитатой на каждую ненулевую ось. Армы правят СОБСТВЕННЫЙ черновик пака: текст и цена происходят из одного вызова, провенанс проверяем. @@ -887,6 +920,281 @@ A_law (deepseek-v4-pro) против B (glm-5): +1.94 [+0.69, +3.19] p=0.0142 --- +## §11 ПРИБОР ЧЕТВЁРТОЙ РЕДАКЦИИ — абсолютный судейский риг + +> Восстановлено 08.08 после инцидента D39.113. Текст переписан заново; **все числа пере-сняты из +> сырья кодом дерева** и воспроизводятся командой `itog.py`. + +**Почему риг переделан.** Каждому из четырёх независимых судей-агентов выдавалась своя пачка +ПАРНЫХ пакетов «исходник + вариант 1 + вариант 2». Все четыре самостоятельно свернули пары +обратно: дедуплицировали уникальные тексты, оценили каждый ОДИН РАЗ против исходника и разнесли +числа по пакетам. Четыре из четырёх — это поведение инструмента, а не сбой исполнителя, и +оснастка пошла за ним: `absjudge.py` судит по АБСОЛЮТНОЙ шкале. + +**Что это дало.** Позиция варианта исчезает по построению (текст оценивается вне пары) — тот +самый дефект, за который снята первая редакция. Работа падает втрое: 160 парных пакетов = 320 +прочтений, уникальных текстов 128. Строгость калибруется ВНУТРИ единицы: все варианты одного +исходника судятся в одном заходе, контраст берётся внутри единицы и лишь потом усредняется. + +**Слепота и провенанс.** Метки `Т1..Тn`, порядок перемешан хешем соли и uid; соль лежит вне +репозитория (`blind-keys/SALT-*.txt`). Судья пишет ответ файлом сам — этим убрана моя +транскрипция, канал, по которому в третьей редакции потерялись обоснования. Голос с ненулевой +осью без цитаты отвергается. **Семейство контрастов впечатано в файл ключа ДО первого ответа** и +сверяется при каждом счёте (`_assert_family`): сузить семейство после чтения p-значений — это и +есть p-hacking. + +**Четыре прохода, и их уровни несопоставимы.** Средняя строгость растёт монотонно: abs 6.90 → +abs2 7.92 → abs3 8.46 → abs4 9.22 ошибки на клетку (+34%). Поэтому **сравнивать между панелями +можно только РАЗНОСТИ, а не уровни**; всякий контраст ИТОГа — внутриединичная разность внутри +одной панели, пул усредняет перевес поединично. Абсолютные уровни в отчёте нигде не сравниваются. + +## §12 ЧЕТВЁРТАЯ РЕДАКЦИЯ — свод двух проходов + +Семейство из семи контрастов, поправка Холма по всем семи, LOO — сколько единиц удаления +роняют значимость (`absjudge --pool`): + +``` +контраст перевес 95% ДИ p p Холма вылетов LOO +A vs F3 +2.50 [+1.56, +3.47] 0.0001 0.0004✔ 0/16 +B vs F3 +2.44 [+1.81, +3.19] 0.0000 0.0002✔ 0/16 +D_ vs A −2.00 [−3.34, −0.62] 0.0157 0.0630 — +D vs A −3.31 [−4.47, −2.16] 0.0003 0.0014✔ 0/16 +A_law vs A −0.75 [−1.78, +0.25] 0.2068 0.3645 — +A_law vs B −0.69 [−1.56, +0.22] 0.1823 0.3645 — +D vs D_ −1.31 [−2.34, −0.25] 0.0363 0.1088 — +``` + +**Три несущих контраста переживают всё, чем их пробовали убить:** поправку Холма, удаление любой +единицы (0/16), удаление любых ДВУХ (0/120), обе судейские популяции и кластеризацию по главе +источника (16 единиц происходят из 12 глав, четыре главы дали по два чанка; кластерный тест даёт +Holm 0.0059 / 0.0034 / 0.0171). + +**Нога 2×2 — главный вывод пака.** Собрана ПОСТ-ХОК из того же сырья, в объявленном семействе её +нет, поправку Холма не несёт: + +``` +A дешёвый черновик + дорогой редактор против черновика: +2.50 p=0.0001 15/16 +B то же, другой редактор против черновика: +2.44 p<0.0001 16/16 +D_ дорогая модель, ОДИН проход против черновика: +0.50 p=0.478 7/16 +D дорогая модель, один проход + мандат против черновика: −0.81 p=0.255 6/16 +``` + +Дорогая модель в один проход неотличима от дешёвого черновика; она же в позиции редактора +покупает +2.50. **Работает топология, а не жилец** — это снимает объяснение «просто pro лучше +flash», на котором стоял §10. + +**Контроли (читать ДО боевых чисел).** Декой — посаженная деградация поймана 10/10 (abs) и 10/10 +(abs2) с верным знаком. Пол рига — две генерации ОДНОГО арма на одном входе, истинная разница +ноль: abs +0.12 [−1.19, +1.44], abs2 +0.62 [−0.81, +2.12]; **ноль внутри интервала в обоих +проходах — пол честен.** Побайтных близнецов в пачках нет. + +**Шум.** sd пола по единицам 2.67 (abs) / 2.96 (abs2) — больше разницы между армами. Значит **на +отдельной главе армы не упорядочены**; упорядочено только среднее (парный дизайн делит шум на +√n, различимый эффект ≈1.9). ⚠ Разложение шума на судейскую и генерационную компоненты **НЕ +УСТАНОВЛЕНО**: `--pool` печатает σ судьи 1.43 против σ генерации 1.40, два других способа счёта +из тех же данных дают «генерация больше» и «судья больше», и при n=16 они неразличимы. + +## §13 ИНСТРУМЕНТЫ $0 — батарея, детекторы, QE + +**Батарея** (`battery.py`) — 11 детерминированных проверок вывода: утечка письменности, +типографика диалогов, потеря чисел, ты/вы, согласование рода, translationese-прокси. Проверена на +синтетике с известным ответом. + +**Детекторы Ф0.4.** №1 «выдуманное слово» — словарный, на holdout 6/6 (tp=7 fp=2 fn=3). №2 +«смысловая инверсия» — QE-ранкер. + +**§13.7 QE мини-бенч.** MetricX-24-hybrid-large-v2p6 (mT5-large, 1.23B, локально на CPU, $0). +Посаженная инверсия полярности против чистого сегмента: **66/72, p=3.6e-14** — QE инверсию +ВИДИТ. ⚠ Но видит в ПАРНОМ режиме (до/после одного сегмента). Как РАНКЕР — «выбрать три худших +сегмента окна» — он её не находит: см. §15 и §17. + +## §14 ДРЕЙФ НА СВЯЗНОМ ОТРЕЗКЕ + +> ⚠ **Отзыв был слишком широким и сужен.** Я отозвал раздел целиком; негодной была только часть +> метрик: заказанный промтом ШОВ между чанками в коде не считался вовсе, а «рост словаря» не мог +> сработать по построению. Валидная часть воспроизводится за $0 (`drift.py --score`) и несёт два +> результата, которых больше нигде в паке нет. + +Десять подряд идущих глав (2…11), армы `F3`/`A`/`A_law`/`D_`: + +``` +покрытие отрезка: F3 9/10 · A 9/10 · A_law 9/10 · D_ 10/10 (у всех выпала глава 11) +покрытие канона: F3 148/163 = 0.96 · A 91/163 = 0.53 · A_law 138/163 = 0.92 · D_ 139/163 = 0.91 +ты/вы: 2/9 · 2/9 · 2/9 · 3/9 переключений +``` + +**1. У связки есть отказной режим, которого у однопроходки нет по построению.** Без годного +черновика редакторская нога не запускается вовсе, и ре-ген этого не лечит: эхо-мина +воспроизводима на материале — глава 11 вернула китайский исходник во всех трёх попытках +(1388–1425 ханьцзы). **Это единственный довод ПРОТИВ связки во всём паке.** + +**2. Второе, независимое подтверждение обязательности закон-блока.** На связном отрезке, другом +материале и другой единице анализа покрытие канона у редактора БЕЗ банка **0.53** против **0.92** +с банком. Пункт 2 ИТОГа стоял на одном оконном замере — теперь на двух. + +⚠ Оговорка: армы состояния между главами не несут, банк подаётся каждой главе заново ⇒ измерен +дрейф БЕЗ памяти, это нижняя граница. Метрика счётная (доля упоминаний исходника, отражённых +канонной формой) — прежняя, «нашлась ли канонная форма хоть раз», была тривиально выполнима. + +## §15 РОУТИНГ: АРМ G И ТРИ ОТБОРЩИКА + +Главный денежный вопрос пака: платить за второй проход ВЕЗДЕ или только там, где он нужен. +Арм G собран из уже купленных текстов детерминированно, $0 новых покупок (`guarded.py --build`). + +``` +маршрутизировано 6/16 окон · страж оставил правку в 4 ⇒ забрано 10.0 из 40.0 выигрыша = 25% +цена: G $0.00317 против связки $0.00580 = 55% цены за 25% выигрыша +``` + +**Строить роутинг НЕ НАДО: он хуже случайного роутинга того же бюджета.** Три независимых +отборщика дали монетку: + +* **механический** (утечка письменности, разметка, латиница, преамбула): среди всех + C(16,4)=1820 подмножеств того же размера его выбор — персентиль **46.4–51.4%** (91 связка, + середина 48.9%); +* **QE как судья**: знак совпал **50/105** по семи контрастам семейства; +* **QE против судьи на самом крупном контрасте `A/F3`: r = −0.32.** Это **АНТИкорреляция**, а не + слепота: чем сильнее QE «за» редактуру, тем меньше её выигрыш по судье. Довод против + QE-роутинга сильнее, чем формулировал ИТОГ до 08.08. + +**Guarded APE со стражем на ДРУГОЙ величине вреден.** Страж отклонил две правки — с выигрышами ++3.0 и +5.5, среднее **+4.25** против +2.50 по всем единицам: он вырезал ЛУЧШИЕ. ⚠ **n=2.** +Вывод стоит на двух правках и означает ровно одно: если строить стража, он обязан смотреть на ту +же величину, которую оптимизирует редактор. + +⚠ **Основание ЭКОНОМИЧЕСКОЕ.** Формулировка «доказанный потолок роутинга» снята: потолок лечится +числом единиц (n≈73–81), просто этого среза не хватает. + +⚠ **Утрачено инцидентом 08.08, D39.113:** числа прежней редакции этого раздела — «43/91» и +«r=+0.06» — не воспроизводятся ни одним вариантом счёта; в дереве кода пути к ним нет. Здесь +напечатано то, что даёт `itog.py` на сегодняшнем сырье. Вердикт «монетка» держится в обоих +вариантах. + +## §16 ДЕКОНФАУНДЫ + +**§16.1 Закон-блок обязателен.** Покрытие канона на 16 окнах (228 упоминаний, счётная метрика): + +``` +F3 черновик 0.917 · A 0.566 · A_law 0.820 · B 0.899 · D 0.776 · D_ 0.851 +``` + +`A` — единственный из шести армов, ломающий терминологию ЧЕРНОВИКА, который он правит. Качество +при этом неотличимо (`A_law/A` −0.75, p=0.207), цена +5%. **Поэтому боевым конфигом редактора +объявлен `A_law`, и базой сравнения Ф2б взят он же, а не `A`.** Второе подтверждение — §14. + +Прибор этого не меряет и мерить не может: рубрика судьи определяет ось ТЕРМИН как +ВНУТРИтекстовую согласованность и канона судье не даёт. Меряет детерминированный контур. + +**§16.2 Черновая модель двигает 70% заголовка.** Тот же редактор `A` над ДРУГИМ черновиком той же +длины: + +``` +над корпусным черновиком 7.66 ошибки против 5.91 над своим · хуже 14/16 · p=0.0063 +``` + +Это 70% от заголовочных +2.50. **Экономить на черновой роли нельзя**, и +2.50 непереносимо на +другую черновую модель без пере-замера. ⚠ Число посчитано по ОДНОЙ клетке пола (`CTRLfloorA` = +`bo2-A`); по обеим клеткам (с повтором `bo3-Aprime`) — 7.47, 13/16, p=0.0073. Вердикт одинаков, +напечатанный вариант — в большую сторону. + +**§16.3 Мандат перевёрстки вредит, и вклад разложен по осям** (пул двух проходов): + +``` +D vs A −3.31 ВЕРНОСТЬ −0.50 · ТЕРМИН −0.38 · ЯЗЫК −1.34 · ФОРМА −1.09 +D_ vs A −2.00 ВЕРНОСТЬ −0.66 · ТЕРМИН −0.34 · ЯЗЫК −0.97 · ФОРМА −0.03 +``` + +Мандат стоит почти всей разницы по ФОРМЕ. ⚠ Оси разведочны: семейство корректирует 7 гипотез, +клеток здесь 28. ⚠ Контраст `D/D_` изолирует мандат НЕ чисто (§3): у армов расходятся ещё +вёрсточная строка и объём системного промта. + +## §17 Ф2б — АРМЫ C И E: ТОЧЕЧНЫЙ РЕМОНТ ПРОТИВ ПЕРЕПИСЫВАНИЯ + +Вторая половина заказа промта (стр.114–115), исполнена 08.08. Фиксер — `gpt-5.6-luna`, +единица починки — предложение, ре-гейт после каждого фикса, кап 2 итерации. + +**Судейство — тем же ригом (проход abs4, узкая панель).** Пять судейских контрастов, Холм по +пяти: + +``` +контраст перевес 95% ДИ p p Холма +C2 vs A_law −3.75 [−4.81, −2.56] 0.0001 0.0006✔ ремонт значимо хуже переписывания +C2 vs F3 −0.94 [−2.19, +0.38] 0.2185 0.2185 и не лучше черновика, который чинит +E2 vs D_ −1.38 [−2.06, −0.69] 0.0046 0.0139✔ обратная связка хуже своей базы +E2 vs A_law −2.06 [−3.50, −0.62] 0.0222 0.0443✔ обратная связка хуже прямой +A_law vs F3 +2.81 [+1.44, +4.31] 0.0024 0.0098✔ КОНТРОЛЬ эффекта редактора +``` + +⚠ **ОТКЛОНЕНИЕ ОТ ОБЪЯВЛЕННОГО СЕМЕЙСТВА, объявляю.** `phase2b.FAMILY` пятым членом называет +`C_oracle vs C_real` — разрыв потолка и пола схемы. Он НЕ судейский: потолок меряется посаженными +дефектами и детекторами, судье не предъявлялся и p-значения не имеет. В судейскую пятёрку вместо +него вошёл контроль `A_law vs F3`. Направление подмены консервативно (член семейства сохранён, +поправка не ослаблена), но это правка семейства после пре-рега, и читать её надо как девиацию. + +**Гипотеза, которой закончился эксп-20 — «локальные ошибки чинятся точечно по флагу за $0.0002» — +при РЕАЛЬНОЙ детекции не подтвердилась.** Там она мерилась при идеальных флагах на посаженных +дефектах. + +**§17.1 Режим oracle — где именно схема теряет.** Земля известна по построению: в черновик +сажаются дефекты двух классов на известные позиции. + +``` +RECALL детекторов, класс k1 (выдуманное слово): 16/16 +RECALL детекторов, класс k2 (смысловая инверсия): 1/16 +``` + +**Разрыв потолка и пола — целиком класс k2.** Это не противоречит мини-бенчу §13.7 (66/72): там +QE работал в ПАРНОМ режиме, здесь — как РАНКЕР, и посаженная инверсия в тройку худших сегментов +не попадает. **QE различает порчу, но не ранжирует по ней** — вот причина провала арма C. + +**§17.2 Первый прогон был испорчен моей ошибкой и пере-снят.** Фиксер шёл БЕЗ банка терминов и +переписывал канон вслепую («истинную ци» → «первобытную энергию», «Гу» → «гу»); покрытие канона +падало 0.917 → 0.895. Это тот же дефект, что у редактора `A` (§16.1). С банком покрытие **0.925** +— ремонт терминологию УЛУЧШАЕТ, — но контраст с переписыванием остался (−4.12 → −3.75). Вывод не +артефакт ошибки. Банк помогает и обратной связке: `E2` 0.860 против `E` 0.825. + +**§17.3 Чем точечный ремонт полезен: он лучший МЕХАНИЧЕСКИЙ ГЕЙТ.** + +``` +брак вывода (иероглифы, разметка, латиница, преамбула, выдуманные слова), 16 единиц: +C2 5 клеток · A_law 10 · F3 23 цена: C2 $0.00298 против связки $0.00602 +``` + +**Ставить его ПЕРЕД редактором, а не вместо него.** + +**§17.4 Деньги фазы.** Армы C/E куплены на **$0.10916** (тег `p2b-*`); расход ФАЗЫ Ф2б с учётом +старых проб `rp-*` ($0.00904) — **$0.11819** из потолка $3.00. ⚠ До 08.08 гейт сторожил фазу +глобом `rp-*`, покупок `p2b-*` не видел и СЕРТИФИЦИРОВАЛ ложные $0.00904 — третий случай того же +класса (новый тег покупок не внесён в глоб фазы тем же коммитом). + +## §18 ПОСТ-ХОК АУДИТЫ 08.08 — что нашли по лестнице + +Два независимых агента прошли пак навстречу: снизу вверх (заказ промта → посылки → код → +результаты, с запретом открывать ИТОГ до последней ступени) и сверху вниз (каждое утверждение +ИТОГа → число → сырьё → код → посылка). **Оба вердикта: перепрогон НЕ ТРЕБУЕТСЯ** — все несущие +числа воспроизводятся из персистированного сырья за $0. + +Что исправлено по их находкам (всё $0, без покупок): + +| Находка | Правка | +|---|---| +| Несущие числа ИТОГа не имели в дереве пути воспроизведения; `--score4` печатал пустую таблицу (в `FAMILY` нет контрастов Ф2б) | заведён `itog.py` — пере-считывает КАЖДОЕ число ИТОГа из сырья | +| `verify_report` сторожил §2, деньги и батарею — и ни одного числа ИТОГа | добавлен блок `§ИТОГ` (20 проверок): нога 2×2, канон, арм G, страж, пятёрка Ф2б | +| Значимый контраст `E2 vs A_law` (−2.06, Холм 0.0443) в таблице ИТОГа отсутствовал | напечатан; гейт проверяет наличие строки | +| Расход фазы Ф2б печатался как `$0.10916` (только тег `p2b-*`) | расход ФАЗЫ = $0.11819 с учётом `rp-*`; §17.4 | +| «Контроль воспроизведён ТРИЖДЫ (+2.50/+2.75/+2.81)» — сшивка РАЗНЫХ армов и панелей | снято: +2.50 — это `A/F3`, а `A_law/F3` даёт +1.75 (пул) / +2.75 (abs3) / +2.81 (abs4) | +| Доля выигрыша арма G считалась по маршрутизированным окнам, а не по оставленным стражем | 25% (10.0 из 40.0 на четырёх окнах), сверено с `guarded --build` | +| Цена «$0.00290» — арм C (испорченный прогон), а не боевой C2 | $0.00298; `phase2b --score` печатает C2/E2 | +| `zip(us, txts)` после фильтра пустых текстов — тихий рассинхрон единиц | единица и текст ходят парой; дефект был латентен (все армы 16/16), числа им не задеты | +| «44 проверки» в шапке | их 62, а с блоком `§ИТОГ` — больше; шапка правится при каждом лендинге | +| QE назван «слепым» (r=+0.06) | на `A/F3` r=−0.32, это АНТИкорреляция; §15 | + +**Заведён линтер зоны** (`eval/role_topology/ruff.toml`, `select = ["F","E9","PLE","B"]`) — повод +прямой: регрессия `med = med(costs)` (затенение функции локальной переменной) прошла в дерево +незамеченной. Первый прогон нашёл 36 находок; исправлены все, искажающих числа среди них не было. + +--- + ## §7 Воспроизведение ``` @@ -895,8 +1203,19 @@ eval/.venv/bin/python eval/role_topology/bakeoff.py --judge # судейств eval/.venv/bin/python eval/role_topology/bakeoff.py --score # свод zh, $0 eval/.venv/bin/python eval/role_topology/pair_en.py --units|--arms|--judge|--score eval/.venv/bin/python eval/role_topology/blind_read.py --emit zh|en # пакеты слепого чтения + +# ЧЕТВЁРТАЯ РЕДАКЦИЯ — абсолютный риг и свод (всё $0) +eval/.venv/bin/python eval/role_topology/absjudge.py --controls|--controls2 # контроли ДО чисел +eval/.venv/bin/python eval/role_topology/absjudge.py --pool # свод двух проходов, §12 +eval/.venv/bin/python eval/role_topology/absjudge.py --score4 # пятёрка Ф2б, §17 +eval/.venv/bin/python eval/role_topology/phase2b.py --score # армы C/E/C2/E2, §17 +eval/.venv/bin/python eval/role_topology/drift.py --score # дрейф, §14 +eval/.venv/bin/python eval/role_topology/guarded.py --build # арм G, §15 + +eval/.venv/bin/python eval/role_topology/itog.py # ВСЕ числа блока ИТОГ, $0 eval/.venv/bin/python eval/role_topology/verify_report.py # верификатор чисел отчёта eval/.venv/bin/python eval/role_topology/selfcheck.py # самопроверка харнесса, $0 +eval/.venv/bin/ruff check eval/role_topology/ # линтер зоны ``` Артефакты — `~/books/role-topology/` (вне git). Текст книг в репозиторий и в отчёт не попадает. diff --git a/eval/role_topology/_retest.py b/eval/role_topology/_retest.py index bc107ce8..ddaeaa68 100644 --- a/eval/role_topology/_retest.py +++ b/eval/role_topology/_retest.py @@ -5,11 +5,10 @@ разность — чистый судейский шум. Это единственный контроль амендмента («разброс судьи на повторе»), который до сих пор не был измерен ни разу. """ -import json, statistics as st, sys +import statistics as st, sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent)) import absjudge as A # noqa: E402 -import bakeoff as BO # noqa: E402 A.PASS = "retest" A.ingest() @@ -29,8 +28,8 @@ print(f" ⇒ σ ОДНОГО СУДЬИ на повторе = {st.pstdev(d) / 2 xs = [r[u][k]["errors"] for u in common for k in r[u] if k in b[u]] ys = [b[u][k]["errors"] for u in common for k in r[u] if k in b[u]] mx, my = st.mean(xs), st.mean(ys) -print(f" корреляция повтора: r={sum((a-mx)*(c-my) for a, c in zip(xs, ys)) / ((sum((a-mx)**2 for a in xs)*sum((c-my)**2 for c in ys))**0.5):+.3f}") -for a_, b_, w in A.FAMILY: +print(f" корреляция повтора: r={sum((a-mx)*(c-my) for a, c in zip(xs, ys, strict=True)) / ((sum((a-mx)**2 for a in xs)*sum((c-my)**2 for c in ys))**0.5):+.3f}") +for a_, b_, _w in A.FAMILY: m1 = [r[u][b_]["errors"] - r[u][a_]["errors"] for u in common if a_ in r[u] and b_ in r[u]] m2 = [b[u][b_]["errors"] - b[u][a_]["errors"] for u in common if a_ in b[u] and b_ in b[u]] if m1: diff --git a/eval/role_topology/absjudge.py b/eval/role_topology/absjudge.py index a87f2bf9..811dd567 100644 --- a/eval/role_topology/absjudge.py +++ b/eval/role_topology/absjudge.py @@ -350,6 +350,31 @@ def _assert_family() -> None: if decl != now: raise SystemExit("⛔ СЕМЕЙСТВО РАЗОШЛОСЬ с впечатанным в ключ ДО ответов:\n" f" в ключе: {decl}\n в коде: {now}") + if PASS == "abs4": + # ⚠ ЧЕСТНОСТЬ ПРОВЕНАНСА. В ключ прохода 4 впечатано семейство Ф2а — оно на узкой панели + # НЕПРИМЕНИМО (там нет арма `A`, и ни один его контраст не собирается). Сверка выше + # проходит, но она ничего не доказывает для тех контрастов, которые здесь считаются. + # Молчать об этом — значит выдать декоративный штамп за провенанс. + print("⚠ ПРОВЕНАНС ПРОХОДА 4 НЕПОЛОН: в ключ впечатано семейство Ф2а, неприменимое к узкой\n" + " панели. Считается пятёрка Ф2б (`FAMILY_2B`), объявленная в `phase2b.FAMILY` ДО\n" + " покупок с одной заменой: недоступный судье `C_oracle/C_real` → контроль `A_law/F3`.\n") + + +# СЕМЕЙСТВО УЗКОГО ПРОХОДА 4 (армы Ф2б). ⚠ Заведено 08.08 аудитом: `--score4` печатал ПУСТУЮ +# таблицу («поправка Холма по 0 контрастам») — ни один контраст `FAMILY` не имеет обоих армов на +# узкой панели (там нет `A`), и у таблицы п.7 отчёта не было пути воспроизведения в дереве. +# ⚠ ОТКЛОНЕНИЕ ОТ ПРЕ-РЕГА, объявляю: `phase2b.FAMILY` пятым членом называет `C_oracle vs C_real` +# — разрыв потолка и пола схемы. Он НЕ судейский (меряется посаженными дефектами и детекторами, +# судье не предъявлялся, p не имеет). В судейскую пятёрку вместо него вошёл контроль `A_law/F3`. +# Подмена КОНСЕРВАТИВНА — член семейства сохранён, поправка не ослаблена, — но это правка +# семейства после пре-рега, и читать её надо как девиацию, а не как замысел. +FAMILY_2B = [ + ("C2", "A_law", "точечный ремонт против полного переписывания"), + ("C2", "F3", "и лучше ли он черновика, который чинит"), + ("E2", "D_", "обратная связка против своей базы"), + ("E2", "A_law", "обратная связка против прямой"), + ("A_law", "F3", "КОНТРОЛЬ: воспроизводится ли эффект редактора"), +] def score() -> None: @@ -358,8 +383,9 @@ def score() -> None: if not data: print("голосов нет — сначала --ingest") return + fam = FAMILY_2B if PASS == "abs4" else FAMILY rows = [] - for a, b, what in FAMILY: + for a, b, what in fam: m = _margins(a, b) if len(m) < 3: continue @@ -370,7 +396,8 @@ def score() -> None: for rank, i in enumerate(order): run = max(run, min(1.0, rows[i][4] * (k - rank))) holm[i] = run - prov = ("семейство впечатано в ключ ДО ответов" if _family_proven() + prov = ("⚠ штамп в ключе относится к семейству Ф2а и к этим контрастам НЕприменим" if PASS == "abs4" + else "семейство впечатано в ключ ДО ответов" if _family_proven() else "⚠ провенанс семейства НЕ доказуем: в ключе штампа нет") print(f"единиц с голосами: {len(data)} · {prov}, поправка Холма по {k} контрастам\n") print(f"{'контраст':14s}{'ед.':>4s}{'перевес':>9s}{'95% ДИ':>18s}{'p':>8s}{'p Холма':>9s} что меряет") @@ -429,16 +456,16 @@ def position() -> None: for r in d.values(): xs.append(r["slot"] - mx) ys.append(r["errors"] - my) - slope = sum(a * b for a, b in zip(xs, ys)) / sum(a * a for a in xs) + slope = sum(a * b for a, b in zip(xs, ys, strict=True)) / sum(a * a for a in xs) print(f"наклон {slope:+.3f} ошибки на шаг метки (внутри единицы, n={len(xs)})") - print(f"средняя позиция арма: " + " · ".join( + print("средняя позиция арма: " + " · ".join( f"{a} {st.mean([d[a]['slot'] for d in data.values() if a in d]):.2f}" for a in ARMS)) print(f"\n{'контраст':14s}{'перевес':>9s}{'Δметки':>8s}{'вклад':>8s}{'с поправкой':>13s}{'p':>9s}") for a, b, _w in FAMILY: m = [d[b]["errors"] - d[a]["errors"] for d in data.values() if a in d and b in d] dl = [d[a]["slot"] - d[b]["slot"] for d in data.values() if a in d and b in d] - corr = [mi + slope * x for mi, x in zip(m, dl)] + corr = [mi + slope * x for mi, x in zip(m, dl, strict=True)] print(f"{a + ' vs ' + b:14s}{st.mean(m):+9.2f}{st.mean(dl):+8.2f}" f"{-slope * st.mean(dl):+8.2f}{st.mean(corr):+13.2f}{BO.sign_perm_p(corr):9.4f}") print("\nВывод, зависящий от раскладки, — тот, чей знак или значимость меняются поправкой.") @@ -503,7 +530,7 @@ def pool() -> None: run = max(run, min(1.0, rows[i][4] * (len(rows) - rank))) holm[i] = run print(f"\n{'контраст':14s}{'перевес':>9s}{'95% ДИ':>19s}{'p':>9s}{'p Холма':>10s}{'вылетов LOO':>13s}") - for i, (a, b, w, m, pv) in enumerate(rows): + for i, (a, b, _w, m, pv) in enumerate(rows): lo, hi = BO.boot_ci(m) loo = sum(1 for k in range(len(m)) if BO.sign_perm_p(m[:k] + m[k + 1:]) * len(rows) >= 0.05) if holm[i] < 0.05 else None @@ -638,7 +665,7 @@ def selfcheck() -> int: def answer(t1: tuple, w1: str, t2: tuple, w2: str) -> None: (ANSW / "tok.txt").write_text( "".join(f"Т{i}-{a}: {v}\n" for i, t in ((1, t1), (2, t2)) - for a, v in zip(J.AXES, t)) .replace("Т1-ФОРМА", "Т1-ФОРМА") + for a, v in zip(J.AXES, t, strict=True)) .replace("Т1-ФОРМА", "Т1-ФОРМА") + f"Т1-ПОЧЕМУ: {w1}\nТ2-ПОЧЕМУ: {w2}\n", encoding="utf-8") answer((1, 0, 0, 0), "", (0, 0, 0, 0), "") diff --git a/eval/role_topology/agentjudge.py b/eval/role_topology/agentjudge.py index 9274472d..58bcedde 100644 --- a/eval/role_topology/agentjudge.py +++ b/eval/role_topology/agentjudge.py @@ -272,8 +272,8 @@ def controls() -> None: # транспозиция: признак того, что судья переставил числа вместо повторного чтения tr = 0 tot = 0 - for c, per in _margins().items(): - for uid, v in per.items(): + for _c, per in _margins().items(): + for _uid, v in per.items(): if len(v) == 2: tot += 1 tr += abs(v[0] - v[1]) < 1e-9 diff --git a/eval/role_topology/bakeoff.py b/eval/role_topology/bakeoff.py index e41021d4..3b7756b7 100644 --- a/eval/role_topology/bakeoff.py +++ b/eval/role_topology/bakeoff.py @@ -42,7 +42,6 @@ import random import statistics import sys import time -from collections import Counter, defaultdict from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") @@ -60,7 +59,7 @@ import editor_wire_probe as P # noqa: E402 import inject_probe as IP # noqa: E402 import judges as J # noqa: E402 import probe as Q # noqa: E402 -from prices import CANDIDATES, cost # noqa: E402 +from prices import CANDIDATES # noqa: E402 load_dotenv(REPO / "eval" / ".env") OUT = Path.home() / "books" / "role-topology" @@ -601,7 +600,7 @@ def cmd_score() -> None: for rank, i in enumerate(order): running = max(running, ps[i][1] * (len(ps) - rank)) holm[i] = min(1.0, running) - for (name, p, n), h in zip(ps, holm): + for (name, p, n), h in zip(ps, holm, strict=True): print(f" {name:16s} n={n:2d} p={p:.4f} Holm={h:.4f} " f"{'проходит 0.05' if h < 0.05 else 'НЕ проходит 0.05'}") diff --git a/eval/role_topology/battery.py b/eval/role_topology/battery.py index 28735563..c052fdb2 100644 --- a/eval/role_topology/battery.py +++ b/eval/role_topology/battery.py @@ -46,7 +46,7 @@ import pymorphy3 # noqa: E402 import yaml # noqa: E402 from detect_word import decomposes as _decomposes # noqa: E402 from detect_word import translit_shape as _translit_shape # noqa: E402 -from palladius import is_palladius_token, palladius_conformant # noqa: E402 +from palladius import palladius_conformant # noqa: E402 _MORPH = pymorphy3.MorphAnalyzer() diff --git a/eval/role_topology/blind_read.py b/eval/role_topology/blind_read.py index 9ead9744..5162025c 100644 --- a/eval/role_topology/blind_read.py +++ b/eval/role_topology/blind_read.py @@ -64,7 +64,7 @@ INSTRUCTION = """Ты — редактор русского издательст def shuffle_key(uid: str, arms: list[str]) -> list[str]: """Детерминированная перестановка от uid: воспроизводима и не зависит от порядка армов.""" h = hashlib.sha1(uid.encode()).digest() # noqa: S324 - return [a for _, a in sorted(zip(h[:len(arms)], sorted(arms)))] + return [a for _, a in sorted(zip(h[:len(arms)], sorted(arms), strict=True))] def emit(pack: str) -> None: diff --git a/eval/role_topology/drift.py b/eval/role_topology/drift.py index b6c26139..fd610974 100644 --- a/eval/role_topology/drift.py +++ b/eval/role_topology/drift.py @@ -32,7 +32,7 @@ import re import statistics as st import sys import time -from collections import Counter, defaultdict +from collections import defaultdict from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") diff --git a/eval/role_topology/effort_watch.py b/eval/role_topology/effort_watch.py index 19c90a30..6e16d7f6 100644 --- a/eval/role_topology/effort_watch.py +++ b/eval/role_topology/effort_watch.py @@ -89,8 +89,8 @@ def main() -> None: sha = hashlib.sha256(body).hexdigest()[:12] approx_in = sum(len(m["content"]) for m in msgs) // 3 print(f"запрос sha {sha} · ~{approx_in} токенов входа (оценка по знакам)") - print(f"арм A = вендор-дефолт (параметр НЕ шлётся) · арм B = reasoning_effort:'low'") - print(f"порядок ИНТЕРЛИВНЫЙ (A,B,A,B,…) — защита от временного дрейфа, quirks §3б\n") + print("арм A = вендор-дефолт (параметр НЕ шлётся) · арм B = reasoning_effort:'low'") + print("порядок ИНТЕРЛИВНЫЙ (A,B,A,B,…) — защита от временного дрейфа, quirks §3б\n") P.RAW = OUT # см. комментарий у OUT: подмена только ПОСЛЕ сборки запроса P.slug_check() # гардрейл CLAUDE.md: слаг сверяется живым /models в день запуска diff --git a/eval/role_topology/guarded.py b/eval/role_topology/guarded.py index c2f6e70a..3c87adb2 100644 --- a/eval/role_topology/guarded.py +++ b/eval/role_topology/guarded.py @@ -128,7 +128,7 @@ def cmd_build() -> None: a = [sum(json.loads(f.read_text(encoding="utf-8")).get("cost_usd") or 0.0 for f in OUT.glob(f"bo2-DRAFT-{u['uid']}*.json")) + cost_of("bo4-A", u["uid"]) for u in us] - print(f"АРМ G построен · $0 новых покупок\n") + print("АРМ G построен · $0 новых покупок\n") print(f" окон флагнуто (куплен второй проход): {routed}/{len(us)}") print(f" правок ОТКЛОНЕНО стражем (гейт увидел ухудшение): {kept}") print(f" цена G среднее ${st.mean(costs):.5f} · медиана ${st.median(costs):.5f}") diff --git a/eval/role_topology/judges.py b/eval/role_topology/judges.py index 79bfbe4e..c249cb3b 100644 --- a/eval/role_topology/judges.py +++ b/eval/role_topology/judges.py @@ -36,10 +36,9 @@ from __future__ import annotations import json import os import re -import statistics import sys import time -from collections import Counter, defaultdict +from collections import defaultdict from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") @@ -221,7 +220,7 @@ def run_variance() -> None: # Прямой замер: ложный перевес нулевой в 13 голосах из 13. Печатаются обе величины — # дрейф калибровки важен для АБСОЛЮТНЫХ порогов, ложный перевес — для ПАРНЫХ вердиктов. unan = tot = false_margin = votes_ok = 0 - for w, rs in cells.items(): + for _w, rs in cells.items(): for r in rs: if r["В1-ВЕРНОСТЬ"] is None: continue diff --git a/eval/role_topology/list_models.py b/eval/role_topology/list_models.py index c81b567c..375acc2b 100644 --- a/eval/role_topology/list_models.py +++ b/eval/role_topology/list_models.py @@ -12,7 +12,6 @@ from __future__ import annotations import json import os -import sys from pathlib import Path import requests diff --git a/eval/role_topology/pair_en.py b/eval/role_topology/pair_en.py index 708dd853..385e4906 100644 --- a/eval/role_topology/pair_en.py +++ b/eval/role_topology/pair_en.py @@ -48,7 +48,6 @@ import battery as B # noqa: E402 import buy as BUY # noqa: E402 import editor_wire_probe as P # noqa: E402 import judges as J # noqa: E402 -import probe as Q # noqa: E402 from bakeoff import boot_ci, client, uid_of # noqa: E402 import statistics # noqa: E402 diff --git a/eval/role_topology/phase2b.py b/eval/role_topology/phase2b.py index 5a2f8805..45318442 100644 --- a/eval/role_topology/phase2b.py +++ b/eval/role_topology/phase2b.py @@ -64,12 +64,10 @@ sys.path.insert(0, str(REPO / "eval" / "role_topology")) sys.path.insert(0, str(REPO / "eval" / "editor_harness")) import bakeoff as BO # noqa: E402 -import battery as BT # noqa: E402 import buy as BUY # noqa: E402 import align as AL # noqa: E402 import detect_word as DW # noqa: E402 import guarded as G # noqa: E402 -from prices import cost # noqa: E402 OUT = Path.home() / "books" / "role-topology" FIXER = "gpt-5.6-luna" @@ -401,17 +399,26 @@ def cmd_score() -> None: Это и есть шаг после `--arms`. """ us = BO.units() - have = {a: [u for u in us if text_of(a, u["uid"]).strip()] for a in ("C", "E")} + have = {a: [u for u in us if text_of(a, u["uid"]).strip()] for a in ("C", "E", "C2", "E2")} print(f"{'арм':6s}{'единиц':>8s}{'знаков':>9s}{'абзацев':>9s}{'канон':>8s}{'механика':>10s}") - for a, base in (("F3", None), ("A_law", None), ("D_", None), ("C", "F3"), ("E", "D_")): - txts = ([text_of(a, u["uid"]) for u in have[a]] if a in have - else [BO.text3_of(a, u) for u in us]) - txts = [x for x in txts if x.strip()] - if not txts: + # ⚠ C2/E2 — БОЕВЫЕ армы Ф2б (фиксер с банком); C/E — первый, испорченный прогон без банка, + # оставлен для контраста «что покупает банк». ИТОГ цитирует C2/E2, поэтому они обязаны + # печататься здесь: до 08.08 таблица показывала только C/E, и число механики ИТОГа (5) + # не имело в дереве пути воспроизведения. Ревью 08.08. + for a in ("F3", "A_law", "D_", "C", "C2", "E", "E2"): + # ⚠ ЕДИНИЦА И ТЕКСТ ХОДЯТ ПАРОЙ. Прежде текст фильтровался по непустоте, а `us` — нет, + # и `zip(us, txts)` считал канон текста k-го арма против ИСХОДНИКА другой единицы, стоило + # хоть одному тексту оказаться пустым. Сейчас все армы полны 16/16, поэтому дефект латентен + # и напечатанные числа им не задеты — но это мина под любой ре-ран с отказом. Ревью 08.08. + pairs = [(u, text_of(a, u["uid"])) for u in have[a]] if a in have \ + else [(u, BO.text3_of(a, u)) for u in us] + pairs = [(u, x) for u, x in pairs if x.strip()] + if not pairs: print(f"{a:6s}{'нет':>8s}") continue + txts = [x for _u, x in pairs] s = k = 0 - for u, x in zip(us, txts): + for u, x in pairs: for zh, (_1, _2, r1, _3) in BO.IP.TERMS.items(): n = u["source"].count(zh) if n: @@ -422,7 +429,7 @@ def cmd_score() -> None: f"{st.median([len([q for q in x.split(chr(10)) if q.strip()]) for x in txts]):>9.0f}" f"{(k / s if s else 0):>8.3f}{mech:>10d}") print("\nЦЕНА (черновик/база + починка):") - for a, base in (("C", "F3"), ("E", "D_")): + for a, base in (("C", "F3"), ("C2", "F3"), ("E", "D_"), ("E2", "D_")): fix = sum(json.loads(f.read_text(encoding="utf-8")).get("cost_usd") or 0.0 for f in OUT.glob(f"p2b-{a}-*-s*.json")) n = len(have[a]) or 1 diff --git a/eval/role_topology/qe_power.py b/eval/role_topology/qe_power.py index 5791dd3a..c1846554 100644 --- a/eval/role_topology/qe_power.py +++ b/eval/role_topology/qe_power.py @@ -34,7 +34,6 @@ from __future__ import annotations import json import os -import re import statistics import sys from pathlib import Path diff --git a/eval/role_topology/qe_segment.py b/eval/role_topology/qe_segment.py index 57bb5ad8..ed63890f 100644 --- a/eval/role_topology/qe_segment.py +++ b/eval/role_topology/qe_segment.py @@ -28,7 +28,6 @@ """ from __future__ import annotations import json -import re import statistics import sys from pathlib import Path diff --git a/eval/role_topology/repair_arm.py b/eval/role_topology/repair_arm.py index 750e475a..ea56461b 100644 --- a/eval/role_topology/repair_arm.py +++ b/eval/role_topology/repair_arm.py @@ -154,7 +154,7 @@ def main() -> None: # ЧТО НАХОДЯТ РЕАЛЬНЫЕ ДЕТЕКТОРЫ — считается до и независимо от починки. rec_k1 = rec_k2 = 0 - for (txt, marks), u in zip(planted, us): + for (txt, marks), _u in zip(planted, us, strict=True): hits = detect(txt, bank) for m in marks: if m["cls"] == "k1": @@ -169,7 +169,7 @@ def main() -> None: timeout=300) spent = 0.0 res = {"oracle": [0, 0], "real": [0, 0]} # [починено, предъявлено] - for ui, ((txt, marks), u) in enumerate(zip(planted, us)): + for ui, ((txt, marks), u) in enumerate(zip(planted, us, strict=True)): hits = detect(txt, bank) for m in marks: # ORACLE: спан известен точно. diff --git a/eval/role_topology/route_arm.py b/eval/role_topology/route_arm.py index cab6355b..abe4cf49 100644 --- a/eval/role_topology/route_arm.py +++ b/eval/role_topology/route_arm.py @@ -44,7 +44,7 @@ import bakeoff as BO # noqa: E402 import battery as BAT # noqa: E402 import detect_word as DW # noqa: E402 import repair_arm as RA # noqa: E402 -from prices import CANDIDATES, cost # noqa: E402 +from prices import CANDIDATES # noqa: E402 load_dotenv(REPO / "eval" / ".env") OUT = Path.home() / "books" / "role-topology" diff --git a/eval/role_topology/verify_report.py b/eval/role_topology/verify_report.py index f5d29234..27998839 100644 --- a/eval/role_topology/verify_report.py +++ b/eval/role_topology/verify_report.py @@ -20,6 +20,7 @@ import json import re import statistics import sys +from itertools import combinations from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") @@ -344,6 +345,91 @@ def main() -> None: # noqa: PLR0915 except Exception as e: # noqa: BLE001 ck(f"дрейф проверяется гейтом ({type(e).__name__})", False, str(e)[:60]) + # ══════════════════════════════════════════════════════════════════════════════════════ + print("\n=== §ИТОГ — НЕСУЩИЕ ЧИСЛА БЛОКА РЕШЕНИЯ ===") + # ⚠ ЗАВЕДЕНО 08.08 ДВУМЯ АУДИТАМИ. До этого гейт сторожил таблицы §2 второй редакции, деньги + # и батарею — и НИ ОДНОГО числа ИТОГа: нога 2×2, контрасты Ф2б, покрытие канона и отборщики + # арма G стояли без стража, хотя именно на них принимается решение. Дефект того же класса, + # что глоб кассы Ф2б: охраняется не то, что решает. + try: + import absjudge as AJ # noqa: PLC0415 + import itog as IT # noqa: PLC0415 + st = statistics + a1, a2, common = IT._passes() + ck("ИТОГ: пул считается по 16 единицам обоих проходов", len(common) == 16, str(len(common))) + for arm, lbl in (("A", "A "), ("B", "B "), ("D_", "D_"), ("D", "D ")): + m = IT._pooled(a1, a2, common, arm, "F3") + ck(f"ИТОГ п.1 нога 2×2: {arm} против F3 = {st.mean(m):+.2f}", + num_near(text, lbl, f"{abs(st.mean(m)):.2f}"), f"{st.mean(m):+.2f}") + m = IT._pooled(a1, a2, common, "D", "A") + ck(f"ИТОГ п.4: D/A = {st.mean(m):+.2f}", num_in(text, f"{abs(st.mean(m)):.2f}"), + f"{st.mean(m):+.2f}") + us = BO.units() + for arm in ("F3", "A", "A_law", "B", "D", "D_"): + s = k = 0 + for u in us: + x = BO.text3_of(arm, u) + for zh, (_v1, _v2, r1, _r2) in BO.IP.TERMS.items(): + n = u["source"].count(zh) + if n: + s += n + k += min(len(re.findall(r1, x, re.I)), n) + ck(f"ИТОГ п.2 покрытие канона {arm} = {k / s:.3f}", num_in(text, f"{k / s:.3f}"), + f"{k / s:.3f}") + gain, routed, kept = IT._route() + got = sum(gain[u] for u in kept) / sum(gain.values()) + ck(f"ИТОГ п.5: арм G забирает {got:.0%} выигрыша", num_in(text, f"{got * 100:.0f}"), + f"{got:.0%}") + rej = [u for u in routed if u not in kept] + ck(f"ИТОГ п.6: страж отклонил {len(rej)} правки со средним " + f"{st.mean([gain[u] for u in rej]):+.2f}", + num_in(text, f"{st.mean([gain[u] for u in rej]):.2f}") and num_in(text, str(len(rej))), + f"n={len(rej)}") + # Ф2б: пятёрка судейских контрастов прохода 4 с поправкой Холма + sv, AJ.PASS = AJ.PASS, "abs4" + try: + d4 = AJ._by_unit() + finally: + AJ.PASS = sv + fam = [("C2", "A_law"), ("C2", "F3"), ("E2", "D_"), ("E2", "A_law"), ("A_law", "F3")] + ms = [[d4[u][b]["errors"] - d4[u][a]["errors"] for u in d4 if a in d4[u] and b in d4[u]] + for a, b in fam] + holm = IT._holm([BO.sign_perm_p(m) for m in ms]) + for (a, b), m, h in zip(fam, ms, holm, strict=True): + ck(f"ИТОГ п.7: {a} vs {b} = {st.mean(m):+.2f} (Холм {h:.4f})", + num_in(text, f"{abs(st.mean(m)):.2f}"), f"{st.mean(m):+.2f}") + ck("ИТОГ п.7: значимый контраст E2/A_law НАПЕЧАТАН в таблице отчёта", + num_near(text, "E2 vs A_law", "2.06"), "строки нет") + # §12: чем несущие контрасты пробовали убить — до 08.08 считалось ВНЕ дерева + chap = {u["uid"]: u["chapter"] for u in us} + chapters = sorted({chap[u] for u in common}) + ck(f"§12: {len(common)} единиц происходят из {len(chapters)} глав (единицы НЕ независимы)", + num_in(text, str(len(chapters))), str(len(chapters))) + # ⚠ Холм считается СОВМЕСТНО по объявленному семейству из семи (три посчитанных контраста + # плюс четыре недобранных как p=1), а не по каждому отдельно: раздельный счёт даёт p×7 + # каждому и расходится с числами отчёта. Ровно та же формула, что в `itog.p1_robust`. + core = [("A", "F3"), ("B", "F3"), ("D", "A")] + ms = [dict(zip(common, IT._pooled(a1, a2, common, a, b), strict=True)) for a, b in core] + ps = [BO.sign_perm_p([st.mean([m[u] for u in common if chap[u] == c]) for c in chapters]) + for m in ms] + for (a, b), m, h in zip(core, ms, + IT._holm(ps + [1.0] * (len(AJ.FAMILY) - len(ps))), strict=False): + ck(f"§12 кластеризация по главе: {a}/{b} Холм {h:.4f} — выживает", + h < 0.05 and num_in(text, f"{h:.4f}"), f"{h:.4f}") + n2 = sum(1 for c in combinations(range(len(m)), 2) + if BO.sign_perm_p([x for i, x in enumerate(m.values()) if i not in c]) + * len(AJ.FAMILY) >= 0.05) + ck(f"§12 leave-two-out: {a}/{b} вылетов {n2}/120", n2 == 0, str(n2)) + lv = [] + for nm in ("abs", "abs2", "abs3", "abs4"): + d = AJ._by_unit_pass(nm) + lv.append(st.mean([r["errors"] for v in d.values() for r in v.values()])) + ck(f"§11 строгость панелей растёт монотонно {' → '.join(f'{x:.2f}' for x in lv)}", + all(x < y for x, y in zip(lv, lv[1:], strict=False)) + and all(num_in(text, f"{x:.2f}") for x in lv), " ".join(f"{x:.2f}" for x in lv)) + except Exception as e: # noqa: BLE001 + ck(f"числа ИТОГа проверяются гейтом ({type(e).__name__})", False, str(e)[:80]) + print("\n=== ПОКРЫТИЕ ТРАТ КАССАМИ ===") covered = ("bo2-", "bo4-", "jv2-", "bo3-", "jf-", "jr-", "en2-", "jve-", "jc-", "dr-") unc = 0.0