From b1780b8d9ddab584e057a251432004e25f17e9d4 Mon Sep 17 00:00:00 2001 From: "Claude (backend session)" Date: Mon, 10 Aug 2026 07:05:55 +0300 Subject: [PATCH] Fix packs 22/23 rigs and reports per acceptance 16 and three adversarial review lines: exact sign test, word-boundary bank regex, judge attribution, coverage guard --- docs/PROGRESS.md | 54 +- docs/experiments/00-provider-quirks.md | 32 + docs/experiments/01-token-calibration.md | 6 + docs/experiments/02-refusal-benchmark.md | 7 + docs/experiments/03-local-stand.md | 6 + docs/experiments/06-local-extraction.md | 6 + docs/experiments/07-coverage-precision.md | 6 + docs/experiments/10-explicit-benchmark.md | 6 + docs/experiments/11-erotica-benchmark.md | 7 + docs/experiments/22-tenant-panel.md | 1021 ++++++++++++++++++++- eval/README.md | 18 + eval/conformance.py | 62 +- eval/editor_tier/itog23.py | 199 ++++ eval/editor_tier/judge.py | 100 +- eval/editor_tier/panel.py | 18 +- eval/editor_tier/solscore.py | 118 +++ eval/editor_tier/verify23.py | 189 ++++ eval/role_topology/absjudge.py | 42 +- eval/role_topology/bakeoff.py | 41 +- eval/tenant_panel/bank.py | 10 +- eval/tenant_panel/itog22.py | 99 +- eval/tenant_panel/judge.py | 20 +- eval/tenant_panel/sol.py | 165 +++- eval/tenant_panel/verify22.py | 96 +- 24 files changed, 2278 insertions(+), 50 deletions(-) create mode 100644 eval/editor_tier/itog23.py create mode 100644 eval/editor_tier/solscore.py create mode 100644 eval/editor_tier/verify23.py diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 48928066..ead47f23 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -2,13 +2,13 @@ > **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-08-09, голова D39.126; хендофф №15→№16 — D39.124/125, докс-пак 167 исполнен — D39.126). Оркестраторов ДВА (решение владельца 07.08): этот — движок/платформа/фронт/доки; параллельный (РОЛЬЮ, без номера — счётчик один, D39.112 п.6) — приёмка полигона. Одновременно не запускаются; CURRENT-STATE ведут оба, чужие строки не трогают. Норма изоляции панелей после инцидента отката чужих файлов — D39.113, гардрейлы в CLAUDE.md. > - **Эра №15 закрыта — семь приёмок, все ПРИНЯТЫ и залендены**; лента, коммиты и разборы — D39.109–123 (D-лог) и реестр нот `architecture/05-decisions-index.md`; снимок прежних бюллетеней этой шапки — архив-слайс `-08-02-04`. -> - **ЖИВОЕ:** полигон эксп-22 «панель жильцов» — ЗАКРЫТ 09.08, ЖДЁТ ЛЕНДИНГА (8 CONFIRM/DENY на владельце; секция «Полигон» ниже; в дереве замечен незаявленный эксп-23 editor_tier — спросить при лендинге) · фронт: S4 ГЕЙЧЕН словом владельца об оболочке → сначала «S3.5 фикс-пак» (D39.119 п.4) · активных промтов бэкенда/платформы НЕТ — следующие по слову владельца: **эмиттер** (103 + деньги шва; сессии перед стартом читать PD-95/105/79/99/107/60/61 + D39.106 + строку 165) · **платформа**: стоп/резюм-ручки + эскроу (136). -> - **Открыто на владельце:** прогон Sol-пакетов руками (150) · кредиты xAI (арбитр grok + судья 18+) · Приложение А контракта (148: реконструкция с баннером или заново) · продуктовое слово «остановлена: лимиты» (В-3) · К-13/Ф-31 (paused_reason один — «свой потолок» и «нет кредита» неразличимы) · В-4/Ф-30 (глава без заголовка) · фри-тир PD-104 (ноль на бете против автогранта $5 с суточным потолком) · порог 116 · мини-проба флора 44 (одобрена, промт не выдан) · подпись денежного шага 46 · лист В-3+К-6 · авто-резюм paused (вопрос платформы) · запуск S3.5/эмиттера/следующего платформенного пака. +> - **ЖИВОЕ:** полигон эксп-22/23 — дерево ЗАЛЕНДЕНО 10.08 с ревью-шапками приёмки №16 (числа подтверждены независимым пересчётом; **выводы НЕ ратифицированы** — владелец 10.08 признал постановку неполной), **фаза Д заказана**: промт `docs/POLYGON_EXP2223_DOVODKA_SESSION_PROMPT.md`, запуск по слову владельца (санкция потолка $10.00 при запуске) · фронт: список претензий владельца ПРИШЁЛ 09.08 (24 замечания) — промт S3.5 ВЫДАН (`frontend/docs/S35_SESSION_PROMPT.md`), запуск по слову владельца, S4 гейчен до приёмки S3.5 (D39.119 п.4) · активных промтов бэкенда/платформы НЕТ — следующие по слову владельца: **эмиттер** (103 + деньги шва; сессии перед стартом читать PD-95/105/79/99/107/60/61 + D39.106 + строку 165) · **платформа**: стоп/резюм-ручки + эскроу (136). +> - **Открыто на владельце:** запуск фазы Д полигона (слово + санкция потолка $10.00) · Приложение А контракта (148: реконструкция с баннером или заново) · продуктовое слово «остановлена: лимиты» (В-3) · К-13/Ф-31 (paused_reason один — «свой потолок» и «нет кредита» неразличимы) · В-4/Ф-30 (глава без заголовка) · фри-тир PD-104 (ноль на бете против автогранта $5 с суточным потолком) · порог 116 · мини-проба флора 44 (одобрена, промт не выдан) · подпись денежного шага 46 · лист В-3+К-6 · авто-резюм paused (вопрос платформы) · запуск S3.5/эмиттера/следующего платформенного пака. > - **Сделано (сводно)** — снимок ленты закрытых эр в [archive/PROGRESS-2026-08-04-09.md](archive/PROGRESS-2026-08-04-09.md); детали — D-лог и слайсы. > - **Курс:** ОБЩНОСТЬ ✅ → КАЧЕСТВО БАНКА ✅ → ПАКЕТ-ЧЕКЕРОВ ✅ (D39.59–78) → **ФРОНТ-ЭРА** (D39.81–100: зоны живые, контракт API ратифицирован) → **шов/платформа/движковые блокеры построены** (D39.106–123). Хвосты курса живут строками: 16 (edit-волна не гонялась НИ РАЗУ — держит и оси голоса 13б/24) · 46 (дизайн заморожен D39.92/93, промт ждёт выдачи) · coldrun-b фаза C заморожена чекпойнтом легитимно (D39.86; эталон денег/поведения — coldrun-a, read-only); развилка 0731 решена и исполнена (D39.87/91, код `553f1a3`). > - **Горизонт (D39.62/67, освежён D39.95):** **ДОБОР ИДЕАЛА** (первым прогоном: оси голоса 24 · авто-режим · цена 16; жильцы ролей решаются ДО прогона эксп-22 — строка 149 · веса K1–K12 13а · вне-претрейн чекпоинт 55; остаток арбитража банка = рецензент спорных кластеров при ре-пробе 74 — D39.102) → ВТОРАЯ ПАРА живьём (ja→ru; преп 81) → МАСШТАБ → пилот Ф2.5 (гейт резюме-строки 80; строки 62–68, 85) → Ф3 ридер-IDE (69–71). **Стоячие:** ToS-триггер 25.10 · Ш-2 до go1.27 (⚠ + x/text Unicode 17 тем же тулчейном — строка 119, реестр §Б-108 справочника якорей) · платные прогоны разблокированы (проба провода — D39.97, конфиги 112 залендены). > - **Стек (полная карта роль→модель→конфиг→квирки — [STACK.md](STACK.md), D39.126):** draft deepseek-v4-flash thinking-ON `low` **⚠0731** → терминолог (та же модель) → editor deepseek-v4-pro БИЛИНГВ ИНТЕРИМ (топология ПОДТВЕРЖДЕНА при неразличимости жильцов — D39.117; закон-блок ОБЯЗАТЕЛЕН — строка 134; glm-5 резерв; вахта маппинга эффорта pro — §Б-108) → судья gemini (Ф2, в движке НЕ построен — строка 33); канал B Mistral+grok; ~$0.85/ранобэ (D30.4, пере-калибровка при следующем платном прогоне). ⚠ **ВЕСЬ банковый контур (банкнота+терминолог+классификатор) в shipping-c1 НЕ включён** — жив ран-локальным конфигом книги (строка 140; сверка STACK.md 09.08 — факт шире прежней декларации «одна банкнота»); эскалация в shipping за `budget_usd: 0` (STACK.md §примечания). -> - **ЕДИНЫЙ БЭКЛОГ — секция «Бэклог» ниже** (одна таблица, единственный трекер; каждая петля обязана иметь диспозицию: решено / отложено-с-записью / отклонено; ведёт оркестратор). **СЧЁТ ОЧЕРЕДИ на 09.08 (скриптом по таблице — `python3 docs/scripts/counts.py`; обновлять при каждом лендинге):** всего **132** строки · зона бэкенд **67** строго / **89** широко (⚠ колонки счётчик читает С КОНЦА — испр. D39.116) · **блокеров очереди 0**, платные прогоны разблокированы · «скоро» **32** (перечень — грепом по таблице, рукописный список снят D39.126) · гейт-строки эксп-22: 55·153 (плюс 150 — руки владельца); строка 5 — остаток гейчен ре-пробой 74 (D39.102 п.2); остальное «когда-нибудь». ⚠ Счёт — НИЖНЯЯ граница долга, не потолок (разбор — легенда таблицы ниже). +> - **ЕДИНЫЙ БЭКЛОГ — секция «Бэклог» ниже** (одна таблица, единственный трекер; каждая петля обязана иметь диспозицию: решено / отложено-с-записью / отклонено; ведёт оркестратор). **СЧЁТ ОЧЕРЕДИ на 10.08 (скриптом по таблице — `python3 docs/scripts/counts.py`; обновлять при каждом лендинге):** всего **133** строки · зона бэкенд **68** строго / **90** широко (⚠ колонки счётчик читает С КОНЦА — испр. D39.116) · **блокеров очереди 0**, платные прогоны разблокированы · «скоро» **32** (перечень — грепом по таблице, рукописный список снят D39.126) · гейт-строки эксп-22: 55·153 (плюс 150 — руки владельца); строка 5 — остаток гейчен ре-пробой 74 (D39.102 п.2); остальное «когда-нибудь». ⚠ Счёт — НИЖНЯЯ граница долга, не потолок (разбор — легенда таблицы ниже). > - Архивы хроники: `archive/PROGRESS-2026-07-04-10.md` (D31) · `-10-13` (D39.6-гигиена) · `-13-25` (стройка паков 11–16, rerun2) · **`-25-31` (паки 17–20 · мини-прогон · полигон-пакеты 5–8 · ToS · холодный прогон; D39.26–58)** · **`-08-01-02` (сессии №9/№10: общность · качество банка · coldrun-b · открытие фронта/платформы; D39.59–90, срез D39.105)** · **`-08-02-04` (сессии №11–№13: ручки эффорта · стандарты · контракт API · платформа P0 · банк-арбитраж; D39.91–105 + снимок шапки эры №15)**. Записи ниже — живой хвост (№14+, эра D39.106+). ## Бэклог (ЕДИНЫЙ, собран 26.07, актуализация 04.08 D39.99/D39.101; правки — только через оркестратора) @@ -112,7 +112,8 @@ | 164 | **Rebill-проекция `status` с nil baseMemory завышает RebillUSD**: read-путь не материализует base-банк → renderedContentHashes даёт пустую инъекцию → repin недоопознан, строка считается re-pay (консервативная сторона, но противоречит комменту «status can never quote a different number than translate»); материализовать base-банк на read-пути либо смягчить клейм | бэкенд | когда-нибудь | малое касание | **D39.122**, приёмка (охотник №1); preexisting | | 165 | **Различимые exit-коды tmctl: потолочная остановка и graceful-stop** (сегодня оба = exit 1 ⇒ платформа отдаёт `failed` и на штатный стоп, и на исчерпание потолка — PD-113 платформы; после поправки формулы потолка (D39.123, PD-158) стоп по потолку = ровно исчерпание холда, цена неразличимости выросла; вскрыто приёмкой раннера на РЕАЛЬНОМ tmctl — фейк пробы умирал от сигнала) | бэкенд | скоро (вместе с эмиттером 103) | бэкенд-пак эмиттера | D39.123 | | 166 | **Движковая поверхность оценки $/глава** (ставка-константа платформы $0.03 из exp08 v2 — временная мера беты, П-10 зоны платформы; честная оценка по книге/паре — движковая) | бэкенд | когда-нибудь | отдельный пак | D39.123 | -| 167 | **Докс-пак по аудиту D39.125 — ЯДРО ИСПОЛНЕНО (D39.126):** реестр всех нот `architecture/05-decisions-index.md` · counts.py `--check` полнота реестра + `--lint` якорей · разгрузка топ-10 строк · входы 15-money-path/16-events-emitter/STACK.md · К-статусы сведены · 09-target/product-requirements освежены. **Остаток строки:** статус-баннеры экспов + шапка эндпоинтов quirks (полигон — пинг при лендинге эксп-22) · DEFECT_REGISTER секциями (зона платформы, пинг отправлен 09.08) | доки/полигон/платформа | скоро (остаток — лендинг эксп-22) | пинги зонам | D39.125, **D39.126** | +| 167 | **Докс-пак по аудиту D39.125 — ЯДРО ИСПОЛНЕНО (D39.126):** реестр всех нот `architecture/05-decisions-index.md` · counts.py `--check` полнота реестра + `--lint` якорей · разгрузка топ-10 строк · входы 15-money-path/16-events-emitter/STACK.md · К-статусы сведены · 09-target/product-requirements освежены. **Остаток строки:** статус-баннеры экспов + шапка эндпоинтов quirks (передано полигону промтом фазы Д, §Д8 п.12) · DEFECT_REGISTER секциями (зона платформы, пинг отправлен 09.08) | доки/полигон/платформа | скоро (остаток — фаза Д полигона) | пинги зонам | D39.125, **D39.126** | +| 168 | **Состав волн пайплайна = пар-конфиг (данные), не общий хардкод**: вопрос владельца 10.08 «нормально ли под китайский строить весь бэкенд и конфиги?» — жильцы ролей и промпты уже конфиг/langpacks, но ТОПОЛОГИЯ волн (перепис · точечный edit-контур · без второго прохода) одна на всех пар; если фаза Д покажет разных победителей по парам (H-3/H-4: en-черновик+edit против zh-связки), состав волн уезжает в пар-конфиг данными; ревью-вопрос общности §0 `12-go-style-notes.md` | бэкенд | когда-нибудь (после данных фазы Д) | замер (фаза Д) → решение владельца → малое касание | слово владельца 10.08, промт фазы Д | | 49 | **Этапы Б+В спеки D15.2** (content-addressed resume / `guard_hash` — D39.31 сознательно не строил) ; этап В = tmctl export-контракт D29.1 (annot-v1 · цвет-мап+rollup · политика красных fail-closed) + операторский протокол-документ релиза гендер-твиста (D5.1): сам механизм УЖЕ построен — until_ch-правка → точечный re-edit со сметой и $0-пере-пином (`repin.go`/`rebill.go`, D39.42 п.5/D39.45) | бэкенд | скоро (ПОДТЯНУТА D39.81: annot-v1 = источник замечаний для фронта — критический путь подключения) | отдельное решение (annot-половина — по контракту 14, D39.99) | D39.34(4), D33 п.5, D39.81 | | 49а | **ALTER-шаги миграций v8–v14 не идемпотентны вопреки шапке `migrate.go:9-11`** (полу-применённая БД не сходится; счётчик версий скрывает) — находка критика полноты пака-19 | бэкенд | когда-нибудь | отдельное решение | D39.56, PACK19_BUILD §6.9 | | 50 | F3-остаток идемпотентности | бэкенд | когда-нибудь | отдельное решение | D39.34(4) | @@ -190,13 +191,30 @@ ## Полигон +**ФАЗА Д «ДОВОДКА ЭКСП-22/23» — ИДЁТ (10.08).** Заказ `docs/POLYGON_EXP2223_REDO_SESSION_PROMPT.md`, пре-рег Д0 в `docs/experiments/23-editor-tier.md`, код `eval/dovodka/`. Потолок фазы **$4.50** словом владельца (промт называл $10.00 → $4.00 → $4.50 под починку дизайна). Покупок на момент записи ноль. + +⚠ **Три поправки к уже сданным числам, которые оркестратору надо знать при ратификации.** +1. **Счёт агент-сессий эксп-23 = 58, не 50** — репликация §12а выпадала из счёта в §6 и §14 п.9; обе строки поправлены. Счётчик капа в паке 23 НЕ работал (на диске 20 записей против 58 фактических): `log_run` звался из `--ingest`, то есть после суб-агента. В фазе Д починено механизмом — `eval/dovodka/runs.py` пишет ДО запуска, атомарный замок с проверкой живости PID, селфтест 16/16. +2. **`p` эксп-23 пере-считаны ТОЧНО.** При n>20 риг брал Монте-Карло, и одна величина печаталась как 0.1603 и как 0.1608. Перевесы целые ⇒ точный ответ берётся динамикой мгновенно: **0.1601** (боевой прогон и runA), **0.3339** (runB). Вердикты не двигаются. +3. **Регекс банка ловил корень в СЕРЕДИНЕ чужого слова** («Го-РОд Цинчжоу» под канон «род Цинь») — 30 ложных из 2113 = 1.4%. Починено границей слова; **печатные числа покрытия НЕ изменились** (кап `min(hits, n)` их поглощал), и в `verify22` заведён сторож покрытия — прежде эти 18 чисел не сторожил никто. + +**Адверсариальное ревью трёх линий (снизу вверх · сверху вниз · приёмка Fable-5 — разрешение D39.120 наконец использовано).** 25 находок, 4 блокера, все подтверждены исполнением. Крупнейшие: MDE в пре-реге не нёс поправку Холма ⇒ две оси были объявлены несущими, будучи недомощными по построению (тот же класс, что en-ось эксп-22); `done()` журнала шла мимо замка и теряла 19 записей `claim` из 20; крючки судейского рига протекали между паками (эксп-22 молча читал каталоги эксп-23); счётчик «сам-себя» в `conformance.py` не срабатывал никогда. Всё исправлено до покупок. + +**Пробел заказа, найденный приёмкой другого семейства:** дизайн фазы не отвечал на H-1 и H-4. Заведены армы A6 (dspro-черновик + контуры) и E6 (второй редактор на en); владелец поднял потолок под них. **H-4 остаётся ОПИСАТЕЛЬНОЙ** — при n=16 порядок жильцов не разрешается, объявлено до денег. + +**Оркестратору — мёртвая ссылка в его зоне:** `docs/README.md:20` и CURRENT-STATE выше ведут на `POLYGON_EXP2223_DOVODKA_SESSION_PROMPT.md`; файла нет, промт называется `..._REDO_...`. В отчётах 22/23 ссылка поправлена. + +**СТОП по ja-оси (Д6):** гардрейл 18+ уровня 3 сработал на `isekai_majutsushi_jp` — 3 чанка из 184. Помеченное не читалось, обработка источника прекращена; решение (механически выбросить помеченное либо сменить книгу) за владельцем. + **ЭКСП-22 «ПАНЕЛЬ ЖИЛЬЦОВ» — ЗАКРЫТ, ГОТОВ К ЛЕНДИНГУ (09.08).** Отчёт `docs/experiments/22-tenant-panel.md`, харнесс `eval/tenant_panel/`. Гейты код 0: `itog22.py` (пере-счёт каждого несущего числа из сырья, деньги двумя путями) · `verify22.py` (85 проверок) · `eval/conformance.py` (сверка с буквой заказа — новый зонный инструмент) · `verify_report.py` эксп-21 не задет. Пак $3.409743 при потолке $6.50, 40 агент-запусков из 60, контроли чисты во всех четырёх проходах. +⚠ **Гейт `eval/conformance.py` в строке выше засчитан ошибочно.** Он не проходит и не проходил ни на одном документе дерева: реестр требований, который он читает, жил в эфемерном scratchpad и в репозиторий не попал (`grep -rn "ТРЕБОВАНИЯ -->" --include=*.md .` — ноль). «Сверка с буквой заказа» пере-снятию не подлежит; на неё опираться нельзя. Найдено аудитом 09.08. + **Результаты — в блоке ИТОГ отчёта, здесь не дублируются.** Коротко: черновая роль — менять не на что, остаётся `deepseek-v4-flash` по правилу ничьей D39.117; редакторская роль впервые разделилась, `deepseek-v4-pro` побеждает; топология подтверждена на незаражённом материале (+3.25 и +4.44 против +2.50/+2.81 эксп-21); на пару en не переносится. -**Три ограничения вывода — читать вместе с ИТОГом.** (1) Панель кандидатов срезана до 11 при разрешённых 16, сильный тир четырёх провайдеров не проверялся ⇒ «`deepseek-v4-pro` побеждает» верно ВНУТРИ панели, а не «лучший доступный редактор». (2) Контраст `glm-5` −2.12 стоит между двумя порогами (2.03 пред-объявлен, 2.47 собственный пол прохода) ⇒ вопрос о Резерве D39.22 не решён. (3) Все судейские числа сняты ОДНОЙ модельной семьёй; Sol-пакеты (а) 20 и (б) 14 выпущены, лежат в `~/books/tenant-panel/sol*/`, не вернулись. +**Три ограничения вывода — читать вместе с ИТОГом.** (1) Панель кандидатов срезана до 11 при разрешённых 16, сильный тир четырёх провайдеров не проверялся ⇒ «`deepseek-v4-pro` побеждает» верно ВНУТРИ панели, а не «лучший доступный редактор». (2) Контраст `glm-5` −2.12 стоит между двумя порогами (2.03 пред-объявлен, 2.47 собственный пол прохода) ⇒ вопрос о Резерве D39.22 не решён. (3) Все судейские числа сняты ОДНОЙ модельной семьёй. **Sol-пакет (а) прогнан владельцем 09.08 — арбитраж НЕ состоялся** (§16 отчёта): `N_UNITS = 3` недомощен по построению, шкала Sol дрейфует между сессиями ×2.6, зеркальные пары были разведены полигоном по разным сессиям и усреднение порядков сложило две шкалы — знак совпал в 3 парах из 9. Уцелело: декой пойман 2/2, позиционное смещение замерено (−4.61, стабильно). Пакет (б) НЕ запускать до починки оснастки. -**Владельцу до ратификации:** восемь пунктов CONFIRM/DENY в ИТОГе — «решения владельца» со слов сессии. Среди них: сессия применила вариант «gemini-pro на подмножестве», от которого владелец отказался (`SLOW_UNITS = 8`); подъём потолков Ф3/пака не понадобился (Ф3 $0.63 при исходных $1.60). Диспозиции строк 55/150/151/152/153 — в отчёте. +**Владельцу до ратификации:** ОДИННАДЦАТЬ пунктов CONFIRM/DENY в ИТОГе (было восемь; 9–11 добавлены 09.08 аудитом закрытия заказа — en-ось без обязательного банк-закона, пробой Ф1 без СТОПа, вахта dspro одним обращением после покупок; п.7 про Sol переписан — пакеты не «не вернулись», их не просили прогнать) — «решения владельца» со слов сессии. Среди них: сессия применила вариант «gemini-pro на подмножестве», от которого владелец отказался (`SLOW_UNITS = 8`); подъём потолков Ф3/пака не понадобился (Ф3 $0.63 при исходных $1.60). Диспозиции строк 55/150/151/152/153 — в отчёте. **ИНЦИДЕНТ САМОПРОВЕРКИ — ОРКЕСТРАТОРУ, ко входу по D39.120 (09.08).** Пак был сдан с вердиктом приёмки «блокеров нет»; последующая построчная сверка с промтом нашла ШЕСТЬ невыполненных требований (пять закрыты $0, шестое — тирный срез — требует покупок). Три механизма, по убыванию полезности для нормы: @@ -209,6 +227,30 @@ **Жалоба по процессу.** Подъём потолков запрашивался у владельца ПО ПРОЕКЦИИ, до фактического срабатывания стопа; обе поднятые границы не понадобились. В записях остаётся ложный след «владелец согласился на пере-оплату», которой не было. ⇒ Спрашивать при срабатывании стопа, не при прогнозе. +**ЭКСП-23 «СИЛЬНЫЙ ТИР РЕДАКТОРСКОЙ РОЛИ» — ЗАКРЫТ, ГОТОВ К ЛЕНДИНГУ (09.08). ⚠ ЗАКАЗА НА ЭТОТ ПАК НЕ БЫЛО.** Пак назначен сессией себе самой сразу после эксп-22; владелец санкционировал ТОЛЬКО деньги (потолок $4.00), промта не существует. Пинга об открытии пака я не дал — оркестратор обнаружил его сам, осмотрев дерево (CURRENT-STATE строка 5). Это и есть пропуск, отдельно от результатов. Отчёт `docs/experiments/23-editor-tier.md`, харнесс `eval/editor_tier/`, сырьё `~/books/editor-tier/`. Потрачено **$2.907359** при потолке $4.00, дополнительная санкция $5 не понадобилась; 50 агент-сессий из капа 60. + +**Что закрывает.** Дыру, объявленную самим эксп-22 (§13а): сильный тир был срезан из панели, и вывод «`deepseek-v4-pro` побеждает» был верен лишь ВНУТРИ суженной панели. Теперь тир проверен — `gpt-5.6-terra`, `grok-4.5`, `glm-5.2` поверх ТОЙ ЖЕ якорной базы на тех же 16 единицах. **Ни один не отличим** от боевого редактора (+0.50 / +0.19 / −0.19 при пороге 1.02), при цене в 2–6 раз выше. Позитивный контроль «редактор против голого черновика» +2.06 (Холм 0.0039) показывает, что прибор не слеп. Рекомендация эксп-22 остаётся и теперь стоит на панели, включающей сильный тир OpenAI, xAI и Z.AI. Ограничение (2) из блока эксп-22 выше — СНЯТО. + +**Резерв D39.22 (`glm-5`).** На 32 единицах (16 эксп-22 + 16 новых) контраст **−0.81 [−1.81, +0.28], Холм 0.16 при пороге 1.48 — ниже порога различимости.** Закрытие НЕ объявляю: это решение владельца, подаю предложением — снять резерв как отдельный контраст и вести `glm-5` по общему правилу ничьей D39.117, которое оставляет `deepseek-v4-pro` как более дешёвого. ToS-гейт Z.AI на прод-выбор не отменяется. Ограничение (2) блока эксп-22 про «−2.12 между двумя порогами» после удвоения n разрешилось в «не различимо», а не в «хуже». + +**Главный результат пака — методический, и он про приборы, а не про модели.** Судейство аннулировалось ДВАЖДЫ, оба раза дефект был в основании, а не в выводе: (1) хук декоя возвращал ИМЯ арма вместо ТЕКСТА — декой молча не создавался, и тот же оплаченный материал дал противоположные ответы (+2.00 «бьёт» против +0.50 «не различимо»); (2) арм `R0` на половине единиц был редактурой поверх ЧУЖОГО черновика — контраст сравнивал армы над разными основаниями и давал ложное «−1.53, значимо хуже». Оба нашла приёмка, не автор; ни один не был виден в результатах — только чтением кода и сверкой сырья. Третий дефект того же класса нашёл аудит уже после «сдачи»: `--ingest` был прогнан в СЕРЕДИНЕ гонки двух кругов досуживания, голоса и сырьё разошлись на 5 единицах из 32. ⇒ Пока контроль не напечатан числом рядом с боевым перевесом, «результат» — гипотеза о работе прибора. + +**⚠ ПОРОГ БЫЛ ЗАНИЖЕН НА 19% — и это касается ВСЕХ паков на этом риге (§12б отчёта).** Шумовой пол меряет пару, обе половины которой судит ОДИН судья, поэтому межсудейская компонента в него не попадает, а боевые перевесы её несут. Разложение по 8 сессиям: внутри сессии sd 2.903, между сессиями sd 1.030, порог 1.48 → **1.76**. Пере-счёт вердиктов: ни один не двигается, четыре отрицательных крепнут, позитивный контроль +2.06 проходит и по строгому порогу 1.94. Направление риска — пак мог ПРОЗЕВАТЬ разницу, а не выдумать её. Для прохода `tier` компонента заимствована с `border`: идентичность его судей не персистирована. **В норму рига: пол брать парой, чьи половины судят РАЗНЫЕ сессии.** + +**Репликация новым жребием судей (заказана владельцем, §12а отчёта).** Тот же ключ, те же тексты, новая восьмёрка судей: перевес −0.62 против −0.81, вердикт «ниже порога» воспроизвёлся, расхождение средних **0.19** при ожидании ≈1.0. Корреляция по единицам +0.63, знак совпал 26/32. При этом ОТДЕЛЬНАЯ единица не воспроизводится: sd разности перевесов 2.78 — новый жребий переставляет армы примерно в каждой пятой главе. Устойчиво только среднее. Цена 8 сессий, покупок нет; по паку 58 из капа 60. + +**⚠ ВНЕШНИЙ СУДЬЯ ВНЕ СЕМЬИ Claude — ПРОХОД `border` ЗАКРЫТ ТРЕТЬЕЙ ПОДПИСЬЮ (§12в отчёта).** Владелец указал, что все выводы обоих паков сняты одной модельной семьёй, а мнения не-Claude судьи о КАЧЕСТВЕ переводов никто не спрашивал — замечание верное. Внешнему судье отданы ТЕ ЖЕ файлы заданий, что судили агенты пака. Принято 32/32, декой 32/32, перевес **−1.00 при его пороге 3.53** против моих −0.81 и −0.62 — вердикт «ниже порога различимости» совпал у всех троих. ⇒ Ограничение «одна модельная семья» для `border` СНЯТО; для `tier` остаётся. Пере-снимается `eval/editor_tier/solscore.py`. Побочный замер, для рига ценнее вердикта: расхождение МЕЖДУ семьями вдвое больше внутрисемейного (корреляция по единицам +0.32 против +0.63), и внешний прибор вдвое грубее (пол sd 5.05 против 2.12). ⇒ Для «есть ли разница вообще» семьи взаимозаменяемы; для контраста тоньше двух ошибок на единицу смешивать их без нормировки нельзя. + +**Починки прибора (переносимы в следующие паки).** `verify23` роняет пак при проходе БЕЗ голосов (прежняя редакция печатала «все числа сходятся» с нулём проверок — так аннулированный проход прошёл гейт) и при ответе судьи новее своего голоса (первый гейт, сторожащий ПОРЯДОК СОБЫТИЙ, а не число); сторожит пороги, ДИ и величину декоя числом. `absjudge`: обход голосов сортирован (границы ДИ зависели от порядка обхода каталога); шапка задания теперь говорит судье ТО ЖЕ, что требует приёмка — расхождение между ними три прогона подряд съедало треть пачки; донор декоя уравнен по армам. `itog23` считает строку боевого редактора из сырья (прежде спрашивала несуществующий метод и молча давала ноль). + +**⚠ ОРКЕСТРАТОРУ, ко входу по D39.120 — ВТОРОЙ ИНЦИДЕНТ ПРИЁМКИ, класс новый.** Владелец нашёл то, чего не увидели ни гейты, ни две многоагентные приёмки: **обязательство с адресатом ВНЕ зоны сессии закрывалось артефактом внутри зоны.** Образец — Sol: промт эксп-22 (стр.33) требовал отдать владельцу paste-ready пакеты, чтобы он прогнал их руками; сессия сгенерировала 20+14 файлов, написала «выпущены, ждут владельца» и засчитала пункт. Каталоги `answers/` пусты — ноль вернулось, потому что ноль было запрошено. Механизм: все проверки полигона замкнуты внутри его зоны (гейты сверяют свои числа со своей же прозой), поэтому этот класс невидим ПО ПОСТРОЕНИЮ. ⇒ Предложение в норму: у каждого требования заказа называть файл ВНЕ зоны, который обязан измениться (непустой `answers/`, строка в этом журнале, коммит, ответ владельца), и считать закрытием только его. Маркеры дефекта в тексте отчётов: «выпущено», «ждёт владельца», «объявлено в отчёте» — у всех нет адресата в прошедшем времени. Пакеты Sol владельцу переданы 09.08. + +**Владельцу до ратификации — девять пунктов CONFIRM/DENY в §14 отчёта.** Ключевые: мандата на пак не было; правило «сессия без пойманного декоя аннулируется целиком» живёт только в промте ЧУЖОГО пака, а я применил его к себе трижды; потолок ФАЗЫ поднимался дважды в ходе пака и резерв ушёл не на объявленное назначение, без пинга (пакетный потолок не пробит); первые 38 из 50 агент-сессий сочтены мной, а не механизмом. + +**⚠ ЛЕНДИНГУ ЭКСП-22.** Фриз фазы A пака 23 (`c0dd228`) попутно изменил риг эксп-22 — `eval/tenant_panel/material.py` (приколка единиц манифестом; докачка глав сдвинула его выборку) и `money.py`. Гейты эксп-22 после этого пере-сняты: `verify22.py` и `itog22.py` код 0. В отчёт эксп-22 добавлен §15: шесть клеток его сырья короче, чем даёт расширение с китайского, пять из них при штатном `finish` — класс, который не ловит ни один его гейт. + +**Запись оркестратора №16, 10.08 — ПРИЁМКА ЭКСП-22/23 ПРОВЕДЕНА, дерево залендено, фаза Д заказана.** Верификация: 11 агентов, 105 проверок — все несущие числа обоих паков воспроизведены из сырья МИМО харнесса (деньги до цента, провенанс до секунд, leave-one-out по судейским сессиям устойчив), фальсификаций нет; находки приёмки и чек-лист поправок тел — §Д8 промта фазы Д (крупнейшая: интерпретация §15 эксп-22 опровергнута — 5 из 6 клеток суть пойманные гейтом эхо-первые-попытки, судились ре-гены). Поправки к пингу выше (чужой текст не редактирую): сессий по паку 23 = **58**, не 50; декой-правило сессия применяла к себе **дважды**, не трижды; тир-абзац закрывает ограничение **(1)**, не (2) — Резерв D39.22 сессией как раз не решён, отдан владельцу. **Владелец 10.08:** санкции/лимиты/мандат эксп-23 подтверждены задним числом; постановка признана неполной ⇒ **выводы обоих паков НЕ ратифицированы**, заморожены до фазы Д (добивка gemini ретрай-харнессом · внешняя подпись `tier` · несущие оси en/ja · edit-контур — ставка владельца · канон-вскрытие с вопросом судье · поправки тел). Декой-правило и обязательный кросс-семейный опровергатель приёмки одобрены владельцем — в D-ноту при ратификации. Гейт `conformance.py` аннулирован подтверждённо (реестр жил в scratchpad); в фазе Д реестры — только в git. + *(Закрытые блоки полигона — экспы 18/19/20, эксп-21, банк-арбитраж — в [archive/PROGRESS-2026-08-04-09.md](archive/PROGRESS-2026-08-04-09.md), D39.125.)* (секция параллельной сессии — записи добавлять сюда) diff --git a/docs/experiments/00-provider-quirks.md b/docs/experiments/00-provider-quirks.md index 25709bda..060e3a06 100644 --- a/docs/experiments/00-provider-quirks.md +++ b/docs/experiments/00-provider-quirks.md @@ -8,6 +8,28 @@ ## Эндпоинты и модели (OpenAI-совместимые, если не указано иное) +> ✅ **ЖИВОЙ ЛИСТИНГ `/models` ПЕРЕ-СНЯТ 2026-08-10** (полигон, фаза Д; $0, `eval/role_topology/ +> list_models.py`, снимок `~/books/role-topology/models-live.json`). Все семь ключей отвечают. +> Счёт слагов: **deepseek 2 · zai 8 · kimi 12 · openai 124 · gemini 59 · xai 10 · mistral 53.** +> Дельты к колонке «на 2026-07-04», которую таблица ниже НЕ переписывает (она датирована): +> * **zai** — в листинге сверх записанных: `glm-4.5`, `glm-4.5-air`, `glm-4.6`, `glm-5-turbo` +> (то есть все восемь). `glm-4.7-flash`/`glm-4.7-flashx` в `/models` по-прежнему НЕТ — их видно +> только на прайс-странице (находка эксп-22 §0.5, в ростер не берутся). +> * **kimi** — появился **`kimi-k3`** (эксп-23 его замерил: пустой выход при 99.9% размышления +> и $0.0804 за клетку, но под СВОИМ потолком 4000 — вывода об отказном режиме вендора нет). +> * **openai** — линейка ушла далеко вперёд `gpt-5-mini`: живы `gpt-5.4`(+`-2026-03-05`), `gpt-5.5`, +> и тройка **`gpt-5.6-luna` · `gpt-5.6-terra` · `gpt-5.6-sol`**. Первые два меряны паками 22/23. +> * **gemini** — **`models/gemini-3.1-pro-preview` В ЛИСТИНГЕ ЖИВ** (важно: эксп-22 не получил от +> него панели по ПРОВОДУ, а не по отсутствию слага); рядом `gemini-3.5-flash`, `gemini-3.6-flash`, +> `gemini-3.1-flash-lite`. Вся 2.5-серия ещё в листинге — shutdown объявлен на 16.10.2026. +> * **xai** — `grok-4.3` и `grok-4.5` живы, плюс три слага-обёртки `grok-4.20-0309-*`. +> * **mistral** — `mistral-large-latest`/`-2512`, `mistral-medium-latest` живы. +> +> ⚠ **«Слаг живой» ≠ «модель та же»** (урок катовера DeepSeek 31.07, D39.61) — листинг доказывает +> только доступность имени. Цены DeepSeek пере-сняты в тот же день и НЕ изменились (flash +> $0.14/$0.28 cache-hit $0.0028 · pro $0.435/$0.87 cache-hit $0.003625); сноска вендора о +> **готовящемся значительном повышении** на прайс-странице ПРИСУТСТВУЕТ и на 10.08. + | Провайдер | base_url | Модель (на 2026-07-04) | env-ключ | |---|---|---|---| | DeepSeek | `https://api.deepseek.com/v1` | **`deepseek-v4-flash`** + `deepseek-v4-pro` — и это **ВЕСЬ** листинг (live 25.07.2026). **Катовер ИСПОЛНЕН:** `deepseek-chat`/`deepseek-reasoner` сняты не только из листинга, но и из реестра — `GET /v1/models/deepseek-chat` → **404 `Model Not Found`**, алиас-оговорка календаря на них больше НЕ распространяется. Цены пост-катовера НЕ изменились (25.07: flash $0.14/$0.28 cache-hit $0.0028 · pro $0.435/$0.87 cache-hit $0.003625) — `models.yaml` актуален. Эхает zh — см. ниже, брать v4-flash с thinking | `DEEPSEEK_API_KEY` | @@ -57,6 +79,16 @@ ⇒ (i) **не слать `reasoning_effort` = ехать на `high`** — вот почему боевая форма упирается в стену (п.10); (ii) на `deepseek-v4-pro` ручка `low` НЕ РАБОТАЕТ (маппится в `high`); рычаг бюджета размышления существует ТОЛЬКО у flash — **строка В СИЛЕ**: её отзыв от 05.08 сам ОТОЗВАН адверсариальным ревью того же дня (см. 3б), замер §3б признан недиагностичным; (iii) `none` в OpenAI-формате у DeepSeek не существует вовсе (колонка `reasoning.effort:"none"` — Anthropic-формат), запись 04.07 «`none` → 400» в силе. + **3в. ВАХТА D39.92 ИСПОЛНЕНА ПОВТОРНО 2026-08-10 (полигон, фаза Д; $0, `curl` HTTP 200).** + Таблица маппинга 3а на `guides/thinking_mode` **побайтно та же** (снята разбором ячеек ``: + `low`→low/high · `high`→high/high · `xhigh`→high/**max** · `max`→max/max), и сноска (3) + *«We will update the actual mapped effort of deepseek-v4-pro in early August 2026»* **всё ещё на + странице** — то есть «early August» уже прошёл, а дока не обновлена. Change Log свежее + **31.07.2026** записей не имеет. ⇒ Расхождение доки с замером 05.08 (звено «б»: `xhigh` на pro + ведёт себя как отдельный уровень НАД `high`, а не как `max`) **сохраняется и не объяснено + вендором**; статус `low` на pro — по-прежнему «НЕ УСТАНОВЛЕНО». Следующий триггер вахты: любая + новая запись Change Log ЛИБО исчезновение сноски (3). + **3б. ⚠⚠ ЧАСТИЧНО ОТОЗВАНО В ДЕНЬ ПУБЛИКАЦИИ. Звено про `low` НЕДЕЙСТВИТЕЛЬНО; звено про `xhigh` в силе.** (полигон, живой замер 2026-08-05, ревизия того же дня по адверсариальному ревью — `19-editor-contract-q4b.md` §6.2). Исполнение вахты D39.92 (реестр загейченных триггеров, строка 108: «дата > 05.08 ⇒ пере-проба маппинга + сверка changelog»). Триггер сработал по назначению: вендор объявлял смену «early August 2026», и поведение с таблицей 3а больше не сходится. **Метод:** побайтно ОДИН вход (реальный редакторский дифф-вызов), три арма по 3 розыгрыша, `deepseek-v4-pro`, всё `finish=stop`; N=1 непригоден из-за разброса п.2, решает разделение диапазонов. Харнесс — `eval/editor_contract/effort_probe.py`, сырьё `~/books/gu-zhenren/bank-arbitration/eff-*.json`, $0.044163. diff --git a/docs/experiments/01-token-calibration.md b/docs/experiments/01-token-calibration.md index 1be472a7..31c83a42 100644 --- a/docs/experiments/01-token-calibration.md +++ b/docs/experiments/01-token-calibration.md @@ -1,5 +1,11 @@ # Эксперимент 01. Токен-калибровка на реальных текстах + +> ⚠ **СТАТУС (баннер фазы Д, 10.08): ЗАКРЫТ, фактура жива, ГРАНИЦА объявлена.** r-множители из этого +> прогона действуют; сняты они на public-domain классике, и `eval/README.md` правило 2 требует +> считать выводы на претрейн-классике ПРЕДВАРИТЕЛЬНЫМИ до вебновелл-среза. Денежная модель, которая +> ими пользуется, — `08-cost-model-v2.md` (там же датировка цен). + Дата прогона: 2026-07-04. Статус: **завершён** (public-domain калибровка; уточнение на реальных вебновеллах — по мере поступления файлов владельца). ## TL;DR diff --git a/docs/experiments/02-refusal-benchmark.md b/docs/experiments/02-refusal-benchmark.md index c91e3492..7b492b07 100644 --- a/docs/experiments/02-refusal-benchmark.md +++ b/docs/experiments/02-refusal-benchmark.md @@ -1,5 +1,12 @@ # Эксперимент 02. Refusal/excision-бенчмарк 18+ (черновик корпуса и стенд) + +> ⚠ **СТАТУС (баннер фазы Д, 10.08): ЗАКРЫТ, но код отчёта — ЖИВОЙ КОНТРАКТ.** `split_sentences`/ +> `classify_output` из `eval/refusal_bench.py` — приёмочный оракул coverage-гейта Go, паритет +> закреплён Go-тестом; любое изменение идёт Python-first, лок-степ (`eval/README.md`, карта). +> Правовая (ToS) сторона 18+ живёт НЕ здесь, а в `00-provider-quirks.md` (таблица прав, ре-чек +> 31.07, следующий 25.10) — это ДРУГАЯ ось, чем поведение моделей в этом отчёте. + Дата: 2026-07-04. Статус: **первый прогон выполнен** (6 провайдеров × 11 SFW/L1/L2-violence фрагментов; explicit-категории ждут фрагментов владельца). Основание: `docs/research/11-gap-2.md` §8 и Выводы п.5; `01-decisions.md` Р4. ## Результаты первого прогона (2026-07-04) diff --git a/docs/experiments/03-local-stand.md b/docs/experiments/03-local-stand.md index b7e5cd09..0b9fe7c3 100644 --- a/docs/experiments/03-local-stand.md +++ b/docs/experiments/03-local-stand.md @@ -1,5 +1,11 @@ # Эксперимент 03. Локальный стенд: скорость, память, качество + +> ⚠ **СТАТУС (баннер фазы Д, 10.08): ЗАКРЫТ.** Вердикт «локаль не годится в роль переводчика» +> действует; локаль осталась в проекте как стенд эмбеддингов и дешёвых не-переводческих ролей. +> Числа привязаны к железу стенда (GTX 1070 8GB) и к моделям, установленным тогда, — на другом +> железе это НОВЫЙ замер, а не репродукция (`eval/README.md`, правило 5). + Дата: 2026-07-04. Задача 3 полигона. Основание: [research/06-local-models.md](../research/06-local-models.md). Скрипты: `eval/local_bench.py` (dense-модели через ollama), `eval/llama_moe_bench.sh` (MoE через llama.cpp). Сырые переводы: `eval/data/local_bench*/`. diff --git a/docs/experiments/06-local-extraction.md b/docs/experiments/06-local-extraction.md index afbe5c5e..74cf08eb 100644 --- a/docs/experiments/06-local-extraction.md +++ b/docs/experiments/06-local-extraction.md @@ -1,5 +1,11 @@ # Эксперимент 06. Экстракция терминов: локаль vs облако (write-path банка памяти) + +> ⚠ **СТАТУС (баннер фазы Д, 10.08): ЗАКРЫТ, ГЛАВНОЕ ЧИСЛО ПОД ОГОВОРКОЙ.** recall 0.98–1.0 снят +> на PD-классике; вебновелл-ре-замер объявлен отдельной стадией (`research/20` §D-A6), и до него +> число читается как предварительное (`eval/README.md`, карта + правило 2). Действующая линия +> банк-майнинга — `research/20` и `16-bank-mining.md`, не этот отчёт. + Дата: 2026-07-04. Закрывает дыру **G1** реестра рисков (`architecture/06`: «качество популяции глоссария не измерено»). Скрипт: `eval/extract_bench.py`; сырьё: `eval/data/extract_bench/results.json`. **Зачем.** Горячий путь банка памяти модель НЕ использует (детерминированный матч). Локаль касается банка в 2 точках: эмбеддинги (exp там же → bge-m3) и **экстракция кандидатов в глоссарий из сырого текста** (bootstrap write-path). Полигон exp03 мерил локаль как ПЕРЕВОДЧИКА (вердикт: не годится). Экстракция — другая, более лёгкая задача; здесь меряем её отдельно и **верифицированно**. diff --git a/docs/experiments/07-coverage-precision.md b/docs/experiments/07-coverage-precision.md index aaae7a53..a66e7469 100644 --- a/docs/experiments/07-coverage-precision.md +++ b/docs/experiments/07-coverage-precision.md @@ -1,5 +1,11 @@ # Эксперимент 07. Precision (доля ложных срабатываний) excision coverage-гейта + +> ⚠ **СТАТУС (баннер фазы Д, 10.08): ЗАКРЫТ, ОДИН ВЫВОД НЕ ПЕРЕНОСИТСЯ.** Precision гейта (0 FP/57) +> действует. А вывод «`content_filter` мёртв» на Gemini 3.x НЕ переносится: там `PROHIBITED_CONTENT` +> — неконфигурируемый фильтр-класс, приходящий СОСТАВНОЙ строкой `finish_reason` и иногда вовсе +> без объекта `message` (`00-provider-quirks.md`, D22.6/D22.8а + дополнение эксп-21). + **Дата:** 2026-07-05 · **Зона:** полигон · **Гейтит:** боевой флип `gates.coverage.enabled:true` (D12/Q4). ## TL;DR diff --git a/docs/experiments/10-explicit-benchmark.md b/docs/experiments/10-explicit-benchmark.md index e71fffec..a7149de2 100644 --- a/docs/experiments/10-explicit-benchmark.md +++ b/docs/experiments/10-explicit-benchmark.md @@ -1,5 +1,11 @@ # Эксперимент 10 — 18+ рука (explicit/dark) на 蛊真人 (D14.4, последний critical) + +> ⚠ **СТАТУС (баннер фазы Д, 10.08): ЗАКРЫТ (эмпирика поведения).** Читать вместе с ПРАВОВОЙ осью: +> готовность модели переводить не даёт права её звать. По `violence` права нет НИ У ОДНОГО вендора +> — таблица прав и главный вывод в `00-provider-quirks.md`. Состав канала B с тех пор менялся +> (D19.1/D22.4); брать отсюда модели по именам без сверки с квирками нельзя. + **Дата:** 2026-07-09 · **Зона:** полигон · **Гейт:** D14.4 (18+ эмпирика — единственный critical ревью 07.09) + D14.2 (Mistral как фолбэк канала B) + D3 (Grok = канал B + судья 18+). Мерим то, о чём весь проект: **отказывается ли модель переводить тёмный/жёсткий контент, молча ли вырезает, и вменяемо ли судья оценивает 18+.** Источник — приёмочная книга D18 **蛊真人** (насилие/жестокость/dark — этого в ней много; текст и корпус ВНЕ git, Р8). **Модели — БОЕВОЙ канал B** (grok-4.3, Mistral-кандидат, локальная abliterated), DeepSeek — КОНТРОЛЬ. diff --git a/docs/experiments/11-erotica-benchmark.md b/docs/experiments/11-erotica-benchmark.md index 47deba78..5beb7ee2 100644 --- a/docs/experiments/11-erotica-benchmark.md +++ b/docs/experiments/11-erotica-benchmark.md @@ -1,5 +1,12 @@ # Эксперимент 11 — эротика-рука 18+ канала B (D14.4, закрытие последнего critical) + +> ⚠ **СТАТУС (баннер фазы Д, 10.08): ЗАКРЫТ (эмпирика поведения).** Та же оговорка, что у эксп-10: +> право отдельно от поведения. Ключевой провод-факт отсюда действует и вписан в квирки: на +> ГРАФИЧНОЙ эротике Gemini 3.x fail-closed `PROHIBITED_CONTENT`, фильтр неконфигурируем, нативный +> API не спасает (D22.6) ⇒ судья эротики — только Grok. Клетка `gemini × sexually-explicit` в +> таблице ПРАВ с тех пор сменилась `FORBIDDEN` → `UNCLEAR` (амендмент D39.32 п.2). + **Дата:** 2026-07-10 · **Зона:** полигон · **Гейт:** D14.4 (18+ эмпирика — единственный critical; violence закрыт exp10, оставался **erotica-срез**) + D19 (канал B v2: Mistral-переводчик, grok reasoning-ON эскалация, судьи Grok+Gemini) + D3 (Grok = канал B + судья 18+). Сестринский прогон к [`10-explicit-benchmark.md`](10-explicit-benchmark.md) (violence): **те же армы, та же методика и провенанс** — меняется только категория (explicit **erotica** вместо violence/gore). Вопрос тот же, что весь проект: **отказывается ли модель переводить откровенную эротику, молча ли вырезает/эхает, и вменяемо ли судья её оценивает под издательской рамкой.** diff --git a/docs/experiments/22-tenant-panel.md b/docs/experiments/22-tenant-panel.md index 20d82f64..c49f6477 100644 --- a/docs/experiments/22-tenant-panel.md +++ b/docs/experiments/22-tenant-panel.md @@ -1,13 +1,427 @@ # 22. Панель жильцов: кто занимает роль черновика и роль редактора **Полигон-сессия, 08.08.2026.** Исполнение `docs/POLYGON_TENANT_PANEL_SESSION_PROMPT.md` -(санкция владельца 08.08, D39.117). Зона: `eval/tenant_panel/` + точечные проверки +(санкция владельца 08.08, D39.117). Зона: `eval/tenant_panel/` + точечные починки `eval/role_topology/` + этот файл + пинг в PROGRESS «Полигон». -> **СТАТУС: ПРЕ-РЕГ Ф0/Ф1 — зафризен этим коммитом ДО первой платной покупки.** -> Потолки промта: Ф0 ≤$0.40 · Ф1 ≤$1.00 · Ф2 ≤$2.00 · Ф3 ≤$1.60; сумма ≤$5.00. -> Числа отчёта пере-считываются `eval/tenant_panel/itog22.py`, сторожатся -> `eval/tenant_panel/verify22.py`; ненулевой код возврата = отчёт не сдаётся. +> ⚠⚠ **РЕВЬЮ-ШАПКА ПРИЁМКИ (оркестратор №16, 10.08) — шапка первична над телом.** Приёмка +> исполнена: все несущие числа пака подтверждены независимым пересчётом мимо харнесса (11 агентов, +> 105 проверок по обоим пакам; деньги до цента, провенанс до секунд, leave-one-out по судейским +> сессиям устойчив). **Выводы НЕ ратифицированы:** владелец 10.08 признал постановку эксп-22/23 +> неполной (en-ось недомощна по построению · сильный тир не закрыт без gemini · ставка владельца +> на edit-контур не мерилась · en-промпты пар не выверены) — ратификация заморожена до **фазы Д** +> (`docs/POLYGON_EXP2223_REDO_SESSION_PROMPT.md`), чек-лист поправок тела — её §Д8. +> Крупнейшая поправка: **интерпретация §15 ОПРОВЕРГНУТА** — 5 из 6 «молчаливо коротких» клеток +> суть эхо-первые-попытки, пойманные гейтом (судились здоровые ре-гены, доказано подписями sig в +> ключах); реально коротка одна E2 `finish=length`. Решения владельца, известные со слов сессии +> (потолки · санкции · куки · кредиты xAI · мандаты), подтверждены его словом 10.08; Sol-инцидент +> и нормы рига идут в D-ноту при ратификации. + +> **СТАТУС: замер закончен, приёмка пройдена.** Потрачено **$3.409743** при пакетном потолке $6.50. +> Все числа ниже пере-считываются `eval/tenant_panel/itog22.py` из персистированного сырья и +> сторожатся `eval/tenant_panel/verify22.py` (85 проверок); ненулевой код возврата = отчёт не сдаётся. +> +> ⚠ **Что гейт НЕ проверяет:** он сторожит, что число ПРИСУТСТВУЕТ в тексте, а не что предложение +> вокруг числа истинно. Приёмка нашла ровно этот класс — «все перевесы выше порога 2.47» при всех +> сходящихся числах (§2). Пороговые и сравнительные утверждения проверяются только чтением. +> +> **Приёмка (08.08):** три независимых прохода — снизу вверх (заказ → код → результаты, ИТОГ +> закрыт до последней ступени), сверху вниз (утверждение → число → сырьё → код → посылка, с +> независимым пере-счётом контрастов мимо харнесса) и охота за дефектами ВНЕ карты отчёта по шести +> анти-паттернам приёмки. **Блокеров нет ни у одного, перепрогон не требуется.** Всё найденное +> отработано $0-раундом без покупок; крупнейшее — ось (а) заказа была отсуждена и не напечатана +> (§1), позиционная поправка не была вычтена (§7), граница двойной оплаты не выводилась из +> артефактов (§8). Правки внесены в тело, не в историю. + +## ИТОГ — ВХОД ДЛЯ РЕШЕНИЯ ВЛАДЕЛЬЦА + +**Материал впервые чист.** 《阴阳天帝诀》, 首发 2026-07-13, 16 единиц из 16 разных глав; +проба контаминации **0/5 узнавания и 0/5 клоуза на ДВУХ моделях** при живом положительном +контроле (金瓶梅 4/5 и 5/5). Величины эксп-21 стояли на книге, узнанной 4/5; эти — нет. + +### 1. ЖИЛЕЦ ЧЕРНОВОЙ РОЛИ: менять не на что — рекомендация `deepseek-v4-flash` + +Шесть жильцов на 16 zh + 6 en единицах, финал каждого доведён боевым редактором. **Ни один +альтернативный черновик не улучшил финал**; все пять перевесов отрицательны, ни один не проходит +Холма, и все, кроме одного, лежат ниже порога различимости 2.03: + +⚠ **Оговорка фазы Д:** «кроме одного» (это `E2`) держится не крепко — в собственном арме `E2` +стоит клетка с `finish=length`, и на подвыборке без затронутых обрывом единиц он тоже ниже порога +(§15). Вывод секции от этого не меняется: ни один альтернативный черновик финал не улучшил. + +``` +E1 luna −0.44 · E2 dspro −2.56 · E3 gemini-fl −1.87 · E4 glm-4.7 −1.25 · E5 grok −1.69 +``` + +⇒ **Правило ничьей D39.117 применяется как ратифицировано: рекомендация = самый дешёвый жилец.** +Им и остаётся `deepseek-v4-flash` ($0.00130/ед против $0.00368–0.00993 у прочих). + +**Черновик КАК ТЕКСТ — ось (а) заказа, до редактора** (описательная: пре-рег Ф2 сузил решающее +семейство до финалов, поэтому поправку она не несёт; знак «+» = черновик жильца лучше якорного): + +``` +D1 luna +0.12 · D2 dspro +1.81 · D3 gemini-fl +0.87 · D4 glm-4.7 −5.44 · D5 grok −0.44 +``` + +Точная форма вердикта такова: **выше порога различимости 2.03 якорный черновик не проигрывает +никому** — лучший из соперников, `deepseek-v4-pro`, опережает его на +1.81, то есть внутри шума. +Ниже порога он проигрывает троим из пяти, и это надо читать как «не установлено», а не как +«ничья по существу». Зато в обратную сторону разрыв реален: черновик `glm-4.7` хуже якорного на +**−5.44** — больше любого перевеса в решающих таблицах Ф2 и Ф3. + +⚠ **И этот же −5.44 бьёт по посылке, выданной промтом.** Промт исходил из эксп-21 §16.2, что +черновик двигает ~70% качества финала. Здесь черновик, отстающий на −5.44, даёт финал, отстающий +всего на **−1.25** (E4): редактор съедает четыре пятых разрыва. Панель редакторов при этом +разошлась ШИРЕ (от −2.12 до −4.75), чем панель черновиков (от −0.44 до −2.56). На этом материале +доля черновика в качестве финала не 70%, а заметно меньше доли редактора — посылка не +подтвердилась, и это вход для решения владельца, а не сноска. + +### 2. ЖИЛЕЦ РЕДАКТОРСКОЙ РОЛИ: роль впервые РАЗДЕЛИЛАСЬ — `deepseek-v4-pro` побеждает значимо + +Поверх ПОБАЙТНО одной якорной базы, семейство из четырёх, Холм по четырём: + +``` +R2 glm-5 −2.12 [−3.81, −0.75] Холм 0.0188 ✔ +R3 deepseek-v4-flash −2.54 [−4.00, −1.00] Холм 0.0188 ✔ +R4 grok-4.3 −4.75 [−6.12, −3.44] Холм 0.0001 ✔ +R0 vs F (КОНТРОЛЬ) +4.44 [+3.00, +6.12] Холм 0.0002 ✔ +``` +**Все три альтернативы значимо хуже боевого редактора.** О пороге различимости — точно, потому +что порогов в паке ДВА и они дают разный ответ для одного арма: + +| порог | откуда | R2 −2.12 | R3 −2.54 | R4 −4.75 | +|---|---|---|---|---| +| **2.03** | пол Ф2; **пред-объявлен** для обеих фаз (§0-Ф3) | проходит на 0.09 | проходит | проходит | +| 2.47 | собственный пол прохода p3 (снят позже) | **НЕ проходит** | проходит | проходит | + +По зафризенному критерию `glm-5` проходит — но запасом 0.09 при поле, чья оценка сама шумная. +Честная форма: **`R3` и `R4` хуже боевого при любом пороге; `R2` — пограничный.** + +Эксп-21 на паре {dspro, glm-5} писал «движки неразличимы» (−0.69, ноль внутри интервала); здесь +знак тот же, что и у эксп-21, но величина выросла до −2.12 и Холм пройден. ⇒ **Резерв D39.22 не +опровергнут и не подтверждён:** единственный контраст, который его касается, — ровно тот, что +стоит на границе. Формулировка «требует пере-рассмотрения» была бы сильнее данных; правильный +статус — **под вопросом, решать не на этом замере**. + +### 3. ВТОРОЙ ПРОХОД ПОДТВЕРЖДЁН ДВАЖДЫ И УСИЛИЛСЯ + +Контроль «редактор поверх черновика» прошёл поправку в ОБЕИХ фазах на разных панелях: +**+3.25** (Ф2, [+2.25, +4.19], Холм 0.0007) и **+4.44** (Ф3, [+3.00, +6.12], Холм 0.0002). +У эксп-21 на контаминированной книге было +2.50 / +2.81. **Топология подтверждена на материале, +которого модель не видела.** + +### 4. ⚠ НА ПАРЕ en ВЫВОД НЕ ПЕРЕНОСИТСЯ — контроль равен НУЛЮ + +`E0 vs D0` на английском исходнике = **+0.00** [−2.50, +2.33]: боевой редактор поверх якорного +черновика не покупает НИЧЕГО, а финалы поверх других черновиков скорее лучше (+0.17…+2.17). + +⚠ **«Ни один контраст не проходит поправку» на en — свойство ДИЗАЙНА, а не данных, и как довод +не годится.** При n=6 минимально достижимое p точного знакового теста = 2⁻⁵ = 0.03125; после Холма +по шести контрастам это 0.1875 > 0.05. То есть en-ветка не могла дать значимость ни при каком +результате — её мощности не хватало по построению, и объявлять её отрицательный итог «замером» +было бы подгонкой под удобный вывод. Несущее здесь — не «незначимо», а **точечная оценка контроля +ровно ноль при zh-контроле +3.25**, причём интервал [−2.50, +2.33] исключает лишь эффект ≥+2.5: +эффект величиной ~+2 на en этими данными НЕ исключён. + +⇒ Выбор жильцов — решение ПАРЫ zh→ru. На другую пару его переносить нельзя; но и утверждать, что +на en связка не работает, этот замер не даёт права — он даёт право сказать, что **перенос не +показан**, и что en-ветку надо мерить отдельным паком с достаточным n. + +### 5. ЦЕНА СВЯЗКИ И ПРОЕКЦИЯ НА КНИГУ (1500 единиц, $/ед из `usage`), p50 И p95 + +Промт заказал оба квантиля по единицам. p95 при n=16 — это максимум по выборке (ближайший ранг), +и он важнее медианы для потолков: платить придётся по хвосту, а не по середине. + +``` +жилец связка p50 связка p95 книга p50 книга p95 +gemini-3.1-flash-lite 0.00881 0.01886 $13.21 $28.30 +deepseek-v4-flash 0.00897 0.01675 $13.45 $25.13 +gpt-5.6-luna 0.00921 0.02006 $13.81 $30.10 +glm-4.7 0.01271 0.02116 $19.06 $31.73 +deepseek-v4-pro 0.01321 0.02238 $19.81 $33.56 +grok-4.3 0.01536 0.02516 $23.04 $37.75 +``` + +**Против БОЕВОЙ ПАРЫ,** как заказано промтом: боевая пара — это `deepseek-v4-flash` (черновик) + +`deepseek-v4-pro` (редактор), то есть **первая строка с ценой $0.00897/ед → $13.45 за книгу по +медиане и $25.13 по p95**. Ни один соперник её не удешевляет: ближайший, `gemini-3.1-flash-lite`, +дешевле на 1.8% по медиане ($13.21) и **дороже на 12.6% по хвосту** ($28.30). ⇒ По цене менять +жильца не на что — как и по судье. + +⚠ **Хвост почти вдвое выше медианы у всех шести** (p95/p50 = 1.7–2.2×), и по хвосту порядок +жильцов меняется: рекомендованный `deepseek-v4-flash` по медиане второй, а по p95 — **самый +дешёвый**. Потолки на книгу надо ставить по p95, иначе каждая длинная глава будет пробивать смету. +⚠ **DeepSeek объявил на прайс-странице 08.08 повышение цен** («significant increase expected») — +проекция едет с этой оговоркой и подлежит пере-счёту при смене прайса. + +### 6. ⛔ ОБЯЗАТЕЛЬНЫЙ КАНДИДАТ ПРОМТА НЕ ОТРАБОТАЛ НА ПРОВОДЕ + +`gemini-3.1-pro-preview`: в скрине **четыре отказа HTTP 503 — две редакторские клетки и две +переводческие**; переводческие взялись с ретрая, редакторские не взялись вовсе, поэтому +редакторской цены у модели нет, а скрин-вердикт ПРОВАЛ вынесен по переводческой ($0.14282 против +порога $0.02). В Ф3 — **1 клетка из 16**, один вызов держал замок 74.5 минуты без ответа и без +ошибки. Цена состоявшейся клетки $0.14702 при **11 504 тарифицируемых выходных токенах на 2 532 +видимых**: тарифицируется в 4.5 раза больше, чем видно, то есть скрытая часть — 8 972 токена, +втрое больше самого ответа; видно это только в `total_tokens`. ⇒ **Гипотеза эксп-04 «gemini-pro — +аутлаер прозы» НЕ проверена и остаётся открытой** — по операбельности, а не по качеству. + +### 7. ЧТО ЕЩЁ ИЗМЕРЕНО (детерминированно, судьи не требует) + +* **Эхо-мина — свойство МАТЕРИАЛА:** `deepseek-v4-flash` дал **6%** ре-генов (1/16) против + 31–38% на 蛊真人; `gemini-3.1-flash-lite` — 12% и **одна единица не покупается вовсе** + (эхо 3/3 попыток). Гипотеза 5.3 эксп-21 подтверждена вторым замером. +* **Редактор чистит письменность и теряет канон.** Утечка ханьцзы в черновиках luna 75 и + glm-4.7 53 → после редактора 2 и 0. Но **покрытие канона падает у ВСЕХ шести жильцов** + (−0.027…−0.057) несмотря на закон-блок; у победителя Ф3 оно худшее в панели (0.882 против + 0.933 у glm-5 и 0.909 у самого черновика). +* **`deepseek-v4-flash` в роли редактора однажды вернул черновик побайтно без правок** (1 клетка + из 62) — класс «no-op редактора». +* ⚠ **`deepseek-v4-flash` в роли редактора Ф3 сжёг бюджет вхолостую на 3 клетках из 16.** Ровно + 16 000 из 16 000 выходных токенов ушли в размышление, `content` пуст, заплачено $0.01529. Это + ровно класс Г6, которым пак дисквалифицировал `kimi-k2.6`, — и он воспроизвёлся на модели, + ПРОШЕДШЕЙ скрин, только в другой роли. Отсюда `R3 n=13`: контраст −2.54 посчитан по 13 удачным + клеткам, то есть по благоприятному для дешёвого редактора подмножеству (вывод от этого не + падает — он и так против дешёвого, — но читателю это сказано явно). **Провод-факт для quirks:** + дешёвый режим DeepSeek в РЕДАКТОРСКОЙ роли уходит в отказ по исчерпанию бюджета размышления, + чего в переводческой роли на тех же единицах не случилось ни разу. +* **Довесок `D_/A` на сырье эксп-21:** −1.56 [−2.88, −0.25], p=0.0526 — **и это число до + позиционной поправки.** Промт требует наклон замерить и вычесть; замерено по всем четырём + проходам: `p2zh` +0.078, `p2en` +0.037, `p3` −0.005 — ничтожны, ни один вердикт не двигают. + А в `dva` наклон **+0.469 ошибки на шаг метки**, и поправка даёт **−1.65, p=0.0356**, то есть + переводит контраст через 0.05. + ⚠ Вердикт я на этом НЕ меняю. Наклон в `dva` снят на трёх метках (n=48) — оценка хрупкая, а + решение, зависящее от того, снята поправка или нет, по определению не устойчиво. Честный статус + контраста «однопроходка против связки»: **знак воспроизведён третьим независимым замером + (−1.56 сырой, −1.65 с поправкой), значимость — на самой границе и от способа счёта зависит.** + «НЕ УСТАНОВЛЕНО» остаётся, но теперь с указанием, что оно держится на границе, а не с запасом. + +### 8. ДЕНЬГИ И ДИСЦИПЛИНА + +``` +Ф0 $0.008153 / $0.40 Ф2 $1.681912 / $2.00 +Ф1 $1.087758 / $1.05 ⛔ ПРОБОЙ Ф3 $0.631920 / $3.00 +ПАК $3.409743 / $6.50 +``` +⛔ **Потолок Ф1 пробит на $0.0378.** Против поднятого потолка $1.05 это 3.6%, **против исходного +потолка промта $1.00 — 8.8%**; вторая цифра честнее как мера промаха планирования. Механизм: +проекционный гард брал p95 УЖЕ купленных вызовов, а `gemini-3.1-pro-preview` оказался в семь раз +дороже любого предшественника. Починено (ожидание считается по ЭТОЙ модели из прайса), заведена +регрессия. Потолки Ф1 ($1.00→$1.05), Ф3 ($1.60→$3.00) и пакетный ($5.00→$6.50) подняты **словом +владельца 08.08, объявлено ДО трат** (пункты 1–2 §9 — до подтверждения не ратифицированы). + +⚠ **Подъём потолков Ф3 и пака НЕ ПОНАДОБИЛСЯ.** Основанием была смета Ф3 $2.91, из которых $2.43 +на `gemini-3.1-pro-preview`; модель отдала одну клетку, и Ф3 стоила $0.631920 — **внутри +ИСХОДНОГО потолка $1.60**, а пак $3.409743 — внутри исходных $5.00. Дисциплина ниже печатается +против поднятых границ, но мерить себя надо по тем, что были до просьбы: по ним пак чист везде, +кроме Ф1. + +⚠ **Неопределённость расхода Ф2 — граница пере-считана и она ХУЖЕ прежде объявленной.** Часть +стадии шла двумя наборами параллельных процессов, а проверка «файла ещё нет» была неатомарной: +клетка могла быть куплена дважды, и вторая оплата в леджер не попадает (на диске одна запись). +Прежде здесь стояло «≤66 клеток × $0.005 ≈ $0.33» — **это число из артефактов не выводится** +(откуда 66 — нигде не записано, а $0.005 ниже фактической цены 124 клеток Ф2), и я его снимаю. +Выводимая из сырья граница одна: атомарный замок заведён в 20:43:25, до него куплено **176 клеток +Ф2 на $0.846103**, и в худшем случае каждая из них оплачена дважды. + +⇒ **Верхняя граница невозвратной пере-оплаты: ≤$0.846103.** Если бы она реализовалась целиком, +Ф2 стоила бы $2.53 и **пробила бы свой потолок $2.00** — то есть чистота Ф2 в таблице выше верна +для записанного расхода и НЕ доказана для фактического. Реальная величина почти наверняка много +меньше (гонка бьёт лишь когда два воркера берут одну и ту же клетку в одну и ту же секунду), но +величины этой не существует ни в каком артефакте, и подменять границу правдоподобной оценкой — +ровно то, чего гейт обязан не допускать. Класс закрыт атомарным замком; проверить фактическое +можно только по счетам провайдеров. + +⚠ **Оплачено за ПУСТОЙ выход: 7 вызовов на $0.21726 — 6.4% пака.** Три клетки `kimi-k2.6` в +скрине, три редакторские клетки `deepseek-v4-flash` в Ф3 (§7) и один черновик. Это не накладные +расходы, а прямая потеря на отказном режиме размышления. + +⚠ **Ф2 стоила $1.681912 против собственной сметы $1.4533 (+15.7%).** В потолок влезла, поэтому +согласия не требовалось, но расхождение сметы с фактом — тот же класс, что и сверка прогнозов, +и молчать о нём нельзя. Источник — ре-гены эхо-мины и повторные клетки, которых смета не закладывала. + +**Агент-запусков: 40 из 60.** Из них 6 списаны на аннулированный проход en (см. §12). + +## §16 ВНЕШНИЙ СУДЬЯ SOL — ПАКЕТ (а) ОТСУЖЕН, АРБИТРАЖ НЕ СОСТОЯЛСЯ + +20 пакетов, обе половины каждой зеркальной пары, прогнаны владельцем 09.08 двумя сессиями. +Границы задания Sol не нарушал, формат выдержал везде. + +**Что уцелело.** ДЕКОЙ пойман **2/2 в обоих порядках**: модель другой семьи независимо отличает +подсаженную деградацию от чистого текста на этом материале. Позиционное смещение замерено и +велико — первый предъявленный вариант получает в среднем на **4.61** ошибки меньше, причём +стабильно в обоих заходах (−5.00 и −4.22). Это внешнее подтверждение того, что позиционный +контроль в абсолютном риге не формальность. + +**Почему арбитраж не состоялся — три причины, все на стороне замысла, не Sol.** + +1. **Недомощность по построению.** `N_UNITS = 3`: три единицы на контраст. Расхождение платных + судей, ради которого пакет существует (luna +0.84 против gemini +3.03), тремя точками не + разводится ни при какой калибровке. Константа стояла в коде до того, как пакеты ушли владельцу. +2. **Дрейф абсолютной шкалы между сессиями ×2.6.** Медиана ошибок на вариант: заход 1 — 23.5 + (разброс 5–44), заход 2 — 9.0 (2–21). Один и тот же текст получает 43 ошибки в первом заходе и + 6 во втором. +3. **Зеркальные пары разведены по разным сессиям** — решение полигона, принятое чтобы Sol не узнал + тексты со второго раза. Из-за этого усреднение двух порядков, которое должно снимать + позиционное смещение, складывало число из одной шкалы с числом из другой: знак совпал лишь в + **3 парах из 9**. Напечатанные ригом «Sol: −7.50 / −5.33 / +1.33» этим и порождены; как третье + мнение рядом с luna и gemini их подавать нельзя, и здесь они не подаются. + +**Сравнение со своим ригом — в пользу структуры, а не судей.** Мои судьи дрейфуют почти так же: +медианы 8 сессий эксп-23 — от 4.5 до 9.0, отношение 2.0×. Разница в том, что в абсолютном риге все +армы единицы судятся В ОДНОМ контексте, поэтому множитель шкалы стоит в обоих слагаемых перевеса и +из разности уходит; у Sol разность считалась между двумя головами. ⇒ **Требование «все армы +единицы — в одном пакете одной сессии» переходит из удобства в норму рига.** + +**Чинится не промтом.** Нужен якорь шкалы внутри КАЖДОГО пакета (откалиброванный фрагмент с +известным числом ошибок) плюс достаточное n. Это новый пак по заказу владельца, а не пере-запуск: +пакет (б) в нынешнем виде (`N_UNITS_B = 3`) ляжет в ту же яму и не выпускается. + +**Диагноз подтверждён исполнением на соседнем паке.** Тому же внешнему судье отдали задания +эксп-23 — не пакеты этой конструкции, а обычные файлы абсолютного рига, где все армы единицы лежат +в ОДНОМ задании. Результат: 32 из 32 приняты, декой пойман 32/32, вердикт совпал с моими судьями +(эксп-23 §12в). ⇒ Дефект был не в судье и не в канале, а в конструкции пакета: разнесение +зеркальных пар по сессиям. Требование «все армы единицы — в одном задании» подтверждено с двух +сторон: нарушив его, замер разваливается; соблюдя — работает на том же судье. + +**Строка 150 бэклога:** пакет (а) исполнен и дал отрицательный результат по своей задаче; пакет +(б) НЕ запускать — вместо него внешний контур строить на заданиях абсолютного рига, как в эксп-23. + +### 9. CONFIRM/DENY ВЛАДЕЛЬЦУ (известно только со слов сессии, до подтверждения не ратифицируется) + +⚠ Пункты 9–11 добавлены 09.08 по итогам аудита закрытия заказа — того, что искал обязательства, +закрытые артефактом вместо действия. Пункт 7 в том же аудите переписан: он скрывал суть. + +1. Подъём потолка Ф1 до $1.05 (08.08). *Пробой случился всё равно: $1.087758.* +2. Подъём потолков Ф3 до $3.00 и пакетного до $6.50 (08.08), **с отклонением варианта «gemini-pro + на подмножестве единиц»**. + ⚠ **Сессия применила отклонённый вариант.** `editors.py` ограничивает медленную модель + восемью единицами (`SLOW_UNITS = 8`), то есть ровно подмножеством, от которого владелец + отказался в пользу подъёма потолка. Практического следа это не оставило — модель отдала 1 + клетку из-за провода, а не из-за лимита, — но решение было моё, а не владельца, и подаваться + как согласованное оно не должно. +3. Разрешение на удаление вложенности `~/books/books/` и перенос сырья наверх (08.08). + *На тех же симлинках стоит воспроизводимость эксп-21.* +4. **Использование живой сессии владельца (куки) для доступа к площадке при сборе среза.** Мера + была предложена самим владельцем, но санкция на неё известна только со слов сессии, и у неё + есть ToS-сторона, которую подтверждать владельцу. +5. **Гейт прав снял две главы** (верхний дециль эротических маркеров) ДО отбора единиц. Это + решение сессии по договорам провайдеров, и оно сужает продуктовый класс материала — см. §11. +6. **Кредиты xAI.** Промт фиксировал их исчерпанными 07.08 с пометкой «вопрос у владельца»; + `grok-4.3` в паке скринился и покупался во всех фазах, то есть пополнение состоялось. + Подтвердить, что оно было осознанным. +7. **Sol ВЕРНУЛСЯ 09.08 — и для арбитража оказался непригоден; отдельно: его не просили сутки.** + Владелец прогнал все 20 пакетов (а) двумя сессиями. Результат по существу — в §16. Прежняя + редакция этого пункта («выпущен и не вернулся») скрывала главное: пакеты пролежали в + `~/books/tenant-panel/sol*/` сутки, каталоги `answers/` пусты, а просьба владельцу прозвучала + только 09.08, после его прямого вопроса «где я это должен был запустить». Промт (стр.33) требовал + отдать paste-ready пакеты ему в руки; сессия сочла обязательство закрытым по факту генерации + файлов. ⚠ Поправка фазы Д (найдена приёмкой другого модельного семейства): фраза «без Sol у обоих + паков нет ни одного контура вне одной семьи» здесь стояла и была неверна — она против + §12в эксп-23, где подпись судьи вне Claude по проходу `border` напечатана числами. + Верно так: контур ПОСТРОЕН и работает, но на заданиях абсолютного рига и по контрастам + ЧУЖОГО пака; контрасты ЭТОГО пака внешней подписи не имеют (§11). + +9. **Оплаченная en-ось шла БЕЗ обязательного банк-закона** (D39.104, промт стр.30 и 55 — пункт + ОБЯЗАТЕЛЕН, для таких «путь один: пинг»). 72 клетки куплены без блоков; причина не в сторожах + кода, а в том, что банк среза собран только по zh (22 термина, все китайские), поэтому на en + блок вышел бы пустым в любом случае. Отступление объявлено во фризе Ф2 ДО покупок, но пинга не + было и выбор «собрать en-банк или принять ось описательной» владельцу до трат не выносился. + ⚠ Сводная таблица §13а называет этот пункт «исполнено» — это неверно и правится: на оси en не + исполнено. Решение владельца: собирать en-банк и пере-купить 72 клетки — или объявить ось + описательной. + +10. **Пробой потолка Ф1 не остановил пак.** $1.087758 против поднятого $1.05: промт (стр.5) + требовал «не влезает → СТОП и пинг ДО покупок». Число названо владельцу в день пробоя и + напечатано в отчёте со знаком ⛔, но СТОПа не было — следующая фаза зафризена и покупки пошли + дальше. Вопрос: остаются ли $1.05 и пакетные $6.50 нормой в `money.py` (их унаследовал пак 23) + или откатываются к $1.00/$5.00 как мерилу дисциплины. + +11. **Вахта dspro исполнена одним обращением и ПОСЛЕ покупок.** Промт (стр.31) требовал сверку + вендор-changelog дважды — в Ф0 и перед Ф3. Фактически: одно обращение к changelog DeepSeek + после фризов Ф0/Ф3 и после покупок Ф3, снимок страницы не сохранён (в `prices/` лежат только + прайс-листы). Сам вывод «смены маппинга после фриза Ф2 не было» проверен и держится, условие + «сменился → стоп и пинг» не наступало; неисполненной осталась ФОРМА вахты, а не её итог. +12. **Отказ гнать строку 153** (уравненный мандат `D`/`D_`) и **отказ от второй базы Ф3** — обе + опции промта сняты решением сессии, не владельца. + +⚠ **Поправка фазы Д (чек-лист приёмки №16 п.10): нумерация этой секции была сломана.** Ряд шёл +1–7, 9, 10, 11, 8 — пункт «отказ гнать строку 153» стоял под номером 8 ПОСЛЕ одиннадцатого, а +пункта 8 в положенном месте не было вовсе. Причина видна по датам: пункты 9–11 дописаны 09.08 +аудитом закрытия заказа и вставлены перед хвостом. Пункт пере-нумерован в 12 (текст не тронут), +три ссылки в §13а и §14 на прежний номер поправлены на «§9 п.12». + +### 9а. ⚠ ГЕЙТ ToS Z.AI — ГЕЙТ РЕШЕНИЯ ВЛАДЕЛЬЦА, НЕ ЗАМЕРА + +Промт требует пометить это в ИТОГе, и вот пометка. **Замер `glm-5` и `glm-4.7` легален** — якорь +покупается, и все числа пака по ним получены правомерно. Но **ПРОД-выбор жильца Z.AI — отдельный +гейт**: маршрутизация по договорам провайдеров идёт ПРАВОМ, а не поведением модели, и класс +`sexually-explicit` для `zai` объявлен FORBIDDEN. Для книги с чувствительным содержимым это значит, +что `glm-5`/`glm-4.7` нельзя ставить жильцом на такой материал, даже если замер их пропустит. + +На выводы пака это не влияет ни в одну сторону: `glm-4.7` в черновой роли и так не рекомендован, +а `glm-5` в редакторской — пограничный (§2). Но если владелец будет выбирать жильца по цене или +по будущим замерам, гейт прав срабатывает РАНЬШЕ качества. Лейбл-вопросы — не задача полигона. + +### 10. ДИСПОЗИЦИИ СТРОК БЭКЛОГА + +* **55** (эмпирика на претрейн-классике предварительна) — **ЗАКРЫТА**. Неконтаминированный + zh-срез добыт и замерен (0/5 на двух моделях при живом контроле); главные выводы топологии + на нём воспроизвелись и усилились. Остаток строки исчерпан. +* **150** (Sol-арбитраж) — **ПАКЕТ (а) ПРОГНАН ВЛАДЕЛЬЦЕМ 09.08, АРБИТРАЖ НЕ СОСТОЯЛСЯ; ПАКЕТ (б) + НЕ ЗАПУСКАТЬ.** ⚠ Поправка фазы Д (п.3): прежняя редакция этой строки гласила «оба пакета + выпущены, ждут владельца» и обрывалась на середине фразы — статус устарел на день и противоречил + §16 того же отчёта. + **(а)** 20 файлов в `~/books/tenant-panel/sol/` + инструкция; контрасты `A/F`, `B/F`, `A_law/A` + отобраны по фактическому расхождению судей эксп-21. Прогнан владельцем двумя сессиями. Итог — + §16: границы задания Sol не нарушал, декой поймал 2/2 в обоих порядках, позиционное смещение + замерил (−4.61), но арбитраж развалился по КОНСТРУКЦИИ пакета (`N_UNITS = 3`, дрейф шкалы между + сессиями ×2.6, зеркальные пары разнесены по разным сессиям — знак совпал в 3 парах из 9). + **(б)** заказан промтом «по готовности Ф2/Ф3» — обе фазы готовы, поэтому был выпущен: **14 файлов** + в `~/books/tenant-panel/sol-b/`, слепые пары финалистов, оба порядка, декой, ключ отдельно. + Контрасты: **`R0` против `R2`** (рекомендованный редактор против пограничного — на нём висит + вопрос о Резерве D39.22) и **`E0` против `E2`** (крупнейший перевес Ф2). Единицы отобраны те, + где внутрисемейный судья был увереннее всего. Семейный запрет соблюдён и проверяется кодом: + клеток OpenAI-моделей в пакете нет (`sol.py::_no_openai`). **Владельцу НЕ отдавать в этом виде:** + та же конструкция и тот же `N_UNITS_B = 3` уложат его в ту же яму. + ⇒ **Рабочая замена найдена и проверена на соседнем паке:** внешний контур строится на заданиях + АБСОЛЮТНОГО РИГА, где все армы единицы лежат в одном задании. Так эксп-23 получил подпись судьи + вне Claude по проходу `border` — 32 из 32 приняты, декой 32/32, вердикт совпал (эксп-23 §12в). + Контрасты ЭТОГО пака внешней подписи по-прежнему не имеют. +* **153** (чистый контраст мандата `D`/`D_`) — **НЕ ГНАЛСЯ.** Опция промта; бюджет ушёл в + обязательные части, а уравненные промты требуют отдельного пре-рега. Остаётся открытой. +* **151** (отказной режим связки) — **УСИЛЕНА**: на новом материале воспроизведён случай, когда + годного черновика нет ВОВСЕ (`gemini-3.1-flash-lite`, эхо 3/3 попыток на одной единице). +* **152** (слипание абзацев — слепое пятно детерминированного контура) — **УСИЛЕНА**: судья и + батарея дали РАЗНЫЙ порядок армов в Ф3 (победитель по судье имеет худшее покрытие канона). + +### 11. ЧТО НЕ ПЕРЕНОСИМО И ПОЧЕМУ + +* **Величины на пару en** — контроль редактора там ноль (§4). +* **Величины на другую книгу** — эхо-мина изменилась с 38% до 6% при смене материала; всё, что + зависит от свойств текста, обязано пере-меряться. +* **Вывод про `gemini-3.1-pro`** — его нет; есть вывод про провод. +* **Абсолютные уровни между проходами** — пороги строгости судейских популяций различаются; + сравнивать можно только внутриединичные разности внутри одного прохода. +* ⚠ **ВСЕ судейские числа пака сняты ОДНОЙ модельной семьёй.** Сорок судейских сессий — это сорок + независимых контекстов одной и той же модели, а не независимые популяции. Проход `dva` назван + в теле «третьей независимой судейской популяцией» — независимы там СЕССИИ, семья одна. + Структурная защита от «судья тянет за своих» в паке есть и она сработала: в ростере (`roster.py`) + нет ни одной модели семьи судьи, то есть подсудимых своей семьи у него нет. Но защиты от общего + для семьи ПРЕДПОЧТЕНИЯ (какая русская проза считается хорошей) нет никакой, а эксп-21 показал, + что смена семьи судьи двигает контраст на 2.19 балла (D39.117 п.5б). Заголовочный вывод пака + держится на согласии однородных судей. ⚠ **Поправка фазы Д (п.3): здесь стояло «внешний контур — + это Sol-пакет, и он НЕ ВЕРНУЛСЯ».** Он вернулся 09.08 и оказался непригоден по конструкции + пакета (§16), а рабочий внешний контур построен на заданиях абсолютного рига и покрывает ПРОХОД + ЧУЖОГО ПАКА (эксп-23 `border`), а не контрасты этого. ⇒ Все судейские величины ЭТОГО пака + по-прежнему следует читать как «так их видит одна модельная семья», а не как «так есть» — но по + причине «внешний судья наших контрастов не читал», а не «внешний судья не ответил». +* ⚠ **Ось отказов замерена на материале, из которого убран самый провокативный класс.** Гейт прав + снял две главы верхнего дециля эротических маркеров ДО отбора единиц (§0.2), после чего гейт Г3 + «отказ на чувствительном фрагменте» не поймал никого. Это ожидаемо и ничего не говорит о том, + как жильцы ведут себя на чувствительном материале, — а именно он и есть рабочий случай продукта + (цель 1 канона владельца). Ноль отказов здесь — свойство выборки, не моделей. + +--- --- @@ -276,10 +690,18 @@ grok-4.3 прошёл 0.00984 0.01452 0 20.9 kimi-k2.6 ПРОВАЛ 0.05661 0.06773 0 100.0% Г1 (3 клетки) · Г5 обе роли · Г6 пустой выход mistral-large-latest ПРОВАЛ 0.02016 0.02661 0 0.0% Г5 цена translator $0.0202 > $0.02 gemini-3.6-flash ПРОВАЛ 0.07320 0.06354 0 0.0% Г5 цена обеих ролей +gemini-3.1-pro-preview ПРОВАЛ 0.14282 — 0 0.0% Г5 цена translator $0.1428 > $0.02 ``` -**Скрин убил 3 из 11** — против 2 из 12 у эксп-21, и убил по СОДЕРЖАТЕЛЬНЫМ порогам, а не по -удобству состава. +**Скрин убил 4 из 11** — против 2 из 12 у эксп-21, и убил по СОДЕРЖАТЕЛЬНЫМ порогам, а не по +удобству состава. Прошли 7: якорь и шесть кандидатов черновой роли. + +⚠ **`gemini-3.1-pro-preview` — вердикт на НЕПОЛНОМ замере.** Его строка стоит на 2 клетках вместо +4: обе редакторские ушли в HTTP 503 (§2.3), поэтому редакторской цены у него нет вовсе. Вердикт +ПРОВАЛ выносится по переводческой цене, которая выше порога в семь раз, — разрыв такой, что двумя +недостающими клетками он не закрывается. **Это не отменяет его участия в Ф3:** обязательный +кандидат промта берётся туда безусловно, мимо скрина (§6), и именно поэтому потолок Ф3 поднимался +владельцем до покупок. ⚠ **`mistral-large-latest` провалился с запасом в 1%** ($0.02016 против порога $0.02). Порог объявлен до замера и не двигается, но честная форма вердикта — «на границе», а не «дорог». @@ -383,3 +805,588 @@ E0 vs D0 — КОНТРОЛЬ: п **АГЕНТ-ЗАПУСКИ Ф2:** 16 (zh, по одной единице на сессию: 6 черновиков + 6 редактур + декой + пара пола = 14 текстов) + 3 (en, по две единицы: 6 черновиков + 6 редактур + декой = 13 текстов). + +--- + +## §3 ОБРАТНАЯ СВЯЗЬ ПО ОРГАНИЗАЦИИ ЗОНЫ `eval/` (сайд-задание владельца) + +Пишется как наблюдение исполнителя, который в этот код только что въехал и на каждой мине +постоял. Приоритет — по цене ошибки, а не по красоте. + +**1. Две мины импорта, на которые наступит каждый следующий пак.** +`probe.py` существует И в `editor_contract/`, И в `editor_harness/`; какой подхватится — решает +ПОРЯДОК `sys.path.insert` в вызывающем файле. `bakeoff.py` вставляет четыре каталога и получает +`editor_harness/probe.py`, хотя по тексту импорта это неочевидно. Переименование любой из папок +порвёт чужой пак молча. Дёшево лечится: `__init__.py` + импорт пакетами (`from editor_harness +import probe`), либо хотя бы `assert probe.__file__.endswith(...)` в шапке. + +**2. Сырьё привязано к пути константой, и путь уже ломался.** Все паки пишут в +`Path.home()/"books"/<пак>`; на этом стенде каталог оказался вложен на уровень глубже +(`~/books/books/role-topology`), и харнесс эксп-21 видел ПУСТОТУ вместо 2 055 файлов — печатал +«голосов нет» и завершался с кодом 0. То есть отчёт мог быть «пере-снят из сырья» на нулевом +сырье. Лечение: один модуль `paths.py` с проверкой «каталог непустой, иначе падать громко». +(На время пака поставлены симлинки `~/books/role-topology` и `~/books/gu-zhenren/bank-arbitration` +— это костыль, не решение.) + +**3. Прайс-таблица дублируется по пакам.** `role_topology/prices.py` и `tenant_panel/roster.py` +знают разные множества моделей и РАЗНЫЕ цены (у Gemini кэш-цена в первой равна входной, а вендор +публикует её в 8–10 раз ниже). Касса эксп-21 считает цену своей таблицей — эксп-22 упёрся в +`KeyError` на середине скрина. Прайс — это данные с датой и источником; ему место в одном файле +на зону, а не в каждом паке. + +**4. Верхний уровень `eval/` смешивает эпохи.** Тридцать свободных `.py` от экспов 01–11 лежат +рядом с папками `exp12/`…`exp16/`, `promptlang/`, `role_topology/`. Решение владельца D38.2 +(«код → логические папки») исполнено ЧАСТИЧНО: старое не переехало. Пока это не мешает, но карта +в README устаревала именно из-за этого. + +**5. README зоны отставал на четыре пака.** Карта покрывала exp01–16 и `promptlang/`, но не +`bank_arbitration/`, `editor_contract/`, `editor_harness/`, `role_topology/` — то есть ВСЕ +действующие харнессы. Новая сессия не могла найти по README ни судейский риг, ни кассу, ни +батарею. **Исправлено этим паком:** в `eval/README.md` добавлена таблица «Действующие харнессы +паков 18–22» и две мины импорта из п.1–2. + +**6. Что НЕ надо трогать.** `absjudge.py`, `battery.py`, `buy.py`, `editor_wire_probe.py`, +`inject_probe.py` — это фундамент, на котором стоят числа трёх паков. Их стоит вынести в общий +слой и покрыть тестами, но не переписывать: цена регрессии здесь измеряется деньгами прогонов. + +**Вывод по рефакторингу.** Крупный не нужен. Нужны три дешёвых шага в порядке цены ошибки: +`paths.py` с громким падением (п.2) · единый `prices.py` на зону с датой и источником (п.3) · +пакетные импорты вместо `sys.path`-цепочек (п.1). Переезд старых скриптов в папки (п.4) — косметика, +делать по случаю. + +--- + +## §0-Ф3 ПРЕ-РЕГ ФАЗЫ 3 + +⚠ **Поправка о статусе этой секции (внесена приёмкой).** Прежде заголовок гласил «зафризен своим +коммитом ДО покупок Ф3». Это неверно и проверяется одной командой: `git log -S"ПРЕ-РЕГ ФАЗЫ 3" -- +docs/experiments/22-tenant-panel.md` пуст — текста секции нет ни в одном коммите, он живёт только +в рабочем дереве. Заморожен КОДОМ: состав панели, семейство контрастов и изоляция уехали во фриз +`56b34d4` (21:12), первая покупка Ф3 — 21:13:38, и именно код определяет, что было куплено и как +судилось. Существо пре-рега от этого не страдает, но самоописание секции было ложным, и +оставлять его нельзя: «зафризено» — проверяемое утверждение, а не риторика. + +**База — ОДНА и замороженная:** боевой черновик `deepseek-v4-flash` из Ф2, тот самый, что прошёл +гейт эха. Все редакторы правят побайтно один текст, поэтому контраст между ними чист от разницы +черновиков — конфаунда, который эксп-21 назвал носителем ~70% эффекта. Изоляция клеток +(различие ТОЛЬКО в модели) проверяется `editors.py --isolation` сборкой сообщений ДО покупок. + +**Состав панели по объявленному ЗДЕСЬ правилу:** + +``` +R0 deepseek-v4-pro боевой редактор — база всех контрастов; клетка КУПЛЕНА В Ф2 + (`tp2-edit` над якорным черновиком) и заново не покупается +R1 gemini-3.1-pro-preview ОБЯЗАТЕЛЬНЫЙ кандидат промта (гипотеза эксп-04 «аутлаер прозы») +R2 glm-5 ратифицированный резерв D39.22 = арм B эксп-21 — ПРЕЕМСТВЕННОСТЬ: + только он позволяет пере-снять `A_law/B` на чистом материале +R3 deepseek-v4-flash самый ДЕШЁВЫЙ прошедший редактор — «хватает ли дешёвого» +R4 grok-4.3 самый ДОРОГОЙ прошедший — «покупает ли дорогой качество» +``` + +**Вторая база (лучший черновик Ф2) — ОПЦИЯ промта, НЕ берётся.** Основание: смета Ф3 и без неё +упирается в потолок (см. ниже), а вопрос «меняется ли выбор редактора при другой базе» — +интеракция, на которую 16 единиц мощности не дают. Решение объявлено ДО покупок, а не после. + +**СЕМЕЙСТВО КОНТРАСТОВ Ф3, впечатывается в ключ ДО первого ответа:** + +``` +R1 vs R0 · R2 vs R0 · R3 vs R0 · R4 vs R0 — каждый редактор против боевого над ТОЙ ЖЕ базой +R0 vs F — КОНТРОЛЬ: покупает ли боевой редактор над базой +``` +Поправка Холма — по пяти. + +**КРИТЕРИИ — те же, что в Ф2, и на том же поле:** «X бьёт Y» при перевесе больше шумового пола +(пол измерен в Ф2 двумя end-to-end прогонами якорного арма и применяется к обеим фазам) И +`p` Холма < 0.05. Ниже пола — вердикт «ниже порога различимости» с числом. Ничья ⇒ рекомендация += самый дешёвый жилец (D39.117). + +**СМЕТА Ф3 по ЗАМЕРЕННЫМ ценам** (не по прайсу): R1 $0.152×16 = $2.43 · R2 $0.01303×16 = $0.21 · +R3 $0.00264×16 = $0.04 · R4 $0.01452×16 = $0.23 · R0 = $0 (из Ф2) ⇒ **$2.91 при потолке $3.00**. +Потолок Ф3 поднят с $1.60 до $3.00 и пакетный с $5.00 до $6.50 **словом владельца 08.08**, +объявлено ДО покупок; причина — замер скрина: `gemini-3.1-pro-preview` тарифицирует 11 144–12 010 +выходных токенов на ответ в 2 508 (скрытое размышление видно ТОЛЬКО в `total_tokens`). + +**ДОВЕСОК на купленном сырье эксп-21 (генерации НЕ покупаются):** прямой контраст `D_` против `A` +агент-голосами, проход `dva`, семейство из одного контраста. Эксп-21 дал −2.00 (p=0.0157, Холма +не проходит) на пуле двух проходов; здесь он пере-снимается третьей независимой судейской +популяцией. + +**АГЕНТ-ЗАПУСКИ Ф3:** 8 (по две единицы на сессию: 5 редакторов + база + декой + пара пола ≈ 9 +текстов) + 4 на довесок `dva` (по четыре единицы: 2 текста + декой). + +--- + +## §4 Ф2 — ПАНЕЛЬ ЧЕРНОВИКОВ: РЕЗУЛЬТАТЫ + +### 4.0 КОНТРОЛИ САМОГО РИГА (читать ДО боевых чисел) + +``` +проход ДЕКОЙ (посаженная деградация) ПОЛ (две генерации одного арма, истина = 0) +p2zh −4.44 · поймано 16/16 ✔ +0.19 [−1.19, +1.56] n=16 · ЧЕСТЕН +p2en −3.50 · поймано 6/6 ✔ не ставился (пол — заказ zh) +``` +`sd` пола по единицам **2.90**, стандартная ошибка среднего **0.72** ⇒ **различимый средний эффект +≈ 2.03 ошибки** при 80% мощности. Это и есть порог, к которому прикладываются все перевесы Ф2/Ф3. +Побайтных близнецов в пачках нет. + +⚠ **Граница декоя, объявляю.** Посадки книго-независимы (заменены эксп-22), но у них узнаваемый +«почерк»: химера на `-нительние`, `сорок` вместо числительного, снятое отрицание, висячая прямая +кавычка. Судьи, получавшие 2–4 задания в одной сессии, называли метку декоя в каждом. Контроль от +этого не рушится — он меряет, ВИДИТ ли судья посаженную порчу, — но **величина −4.44 завышена** +и как оценка чувствительности не читается. + +### 4.1 Судейство zh (16 единиц, семейство впечатано в ключ ДО ответов, Холм по 6) + +``` +контраст ед. перевес 95% ДИ p p Холма +E1 vs E0 16 −0.44 [−2.00, +1.25] 0.6711 0.6711 финал поверх черновика luna +E2 vs E0 16 −2.56 [−5.62, +0.06] 0.1058 0.3452 …поверх черновика deepseek-v4-pro +E3 vs E0 15 −1.87 [−3.60, +0.00] 0.0863 0.3452 …поверх черновика gemini-3.1-flash-lite +E4 vs E0 16 −1.25 [−3.19, +0.75] 0.2663 0.5326 …поверх черновика glm-4.7 +E5 vs E0 16 −1.69 [−3.00, −0.25] 0.0444 0.2222 …поверх черновика grok-4.3 +E0 vs D0 16 +3.25 [+2.25, +4.19] 0.0001 0.0007 ✔ КОНТРОЛЬ: редактор поверх якоря +``` + +**Читается так.** Единственный контраст, переживший поправку на множественность, — КОНТРОЛЬ: +боевой редактор поверх якорного черновика покупает **+3.25** ошибки. Заголовок эксп-21 +(+2.50 · +2.81 на контаминированной книге) **воспроизвёлся на чистом материале и усилился**. + +**Ни один альтернативный черновик не улучшил финал.** Все пять перевесов ОТРИЦАТЕЛЬНЫ (финал +поверх якоря лучше), ни один не проходит Холма, и все, кроме `E2`, лежат ниже порога различимости +2.03. `E2` (−2.56) порог по величине превышает, но `p` Холма 0.3452 — как установленный факт не +держится. +⚠ **И даже эта величина хрупка (см. §15):** в собственном арме `E2` на единице `474f822806` стоит +клетка с `finish=length`; на подвыборке без затронутых обрывом единиц `E2` даёт −1.83 при пороге +2.33, то есть порог не превышает. Вывод секции от этого не меняется — он и был «не держится». + +⚠ **Знак важнее величины.** Прогноз пре-рега был «разброс панели черновиков будет БОЛЬШЕ разброса +редакторов»; замер его не подтвердил в ожидаемой форме: разброс есть, но он весь в сторону +УХУДШЕНИЯ относительно самой дешёвой модели, а не в пользу дорогих. + +### 4.2 Ось переноса en (6 единиц) + +``` +E1 vs E0 +1.83 · E2 vs E0 +1.17 · E3 vs E0 +2.17 · E4 vs E0 +0.17 · E5 vs E0 +1.83 +E0 vs D0 +0.00 [−2.50, +2.33] ← КОНТРОЛЬ +``` +**Знак противоположен zh, и контроль равен НУЛЮ.** На английском исходнике боевой редактор поверх +якорного черновика не покупает ничего, а финалы поверх других черновиков скорее лучше. Ни один +контраст не проходит поправку (n=6). Это тот же класс, что нашёл эксп-21 («на паре, где исходник +уже гипотактичен, разница между топологиями сжимается»), но здесь он доведён до нуля на контроле. +⇒ **Вывод про жильца черновой роли на пару en НЕ переносится.** + +### 4.3 Детерминированные оси (шума не имеют, судьи не требуют) + +**(в) Эхо-мина — свойство МАТЕРИАЛА, а не константа модели:** +``` +deepseek-v4-flash zh 1/16 = 6% en 0/6 +gemini-3.1-flash-lite zh 2/16 = 12% en 0/6 (одна единица не покупается вовсе: 3/3 попытки — эхо) +luna · deepseek-v4-pro · glm-4.7 · grok-4.3 zh 0/16 en 0/6 +``` +Приор эксп-21 на 蛊真人 — **31–38%** у той же модели с той же ручкой. На этой книге **6%**. +Гипотеза 5.3 эксп-21 («частота эха — измеримое свойство материала») подтверждена вторым замером. + +**(г) Батарея и покрытие канона (знаменатель 1124 упоминания):** +``` +арм ханьцзы типогр потеря вел. покрытие канона +D[deepseek-v4-flash] 12 27 18 0.909 +E[deepseek-v4-flash] 0 18 16 0.882 +D[gpt-5.6-luna] 75 23 16 0.970 +E[gpt-5.6-luna] 2 14 17 0.932 +D[deepseek-v4-pro] 5 20 14 0.913 +E[deepseek-v4-pro] 2 12 16 0.860 +D[gemini-3.1-flash-lite] 6 24 18 0.927 +E[gemini-3.1-flash-lite] 0 11 18 0.877 +D[glm-4.7] 53 21 19 0.960 +E[glm-4.7] 0 12 17 0.903 +D[grok-4.3] 2 22 16 0.946 +E[grok-4.3] 3 16 17 0.893 +``` +Два независимых от судьи факта: **редактор вычищает утечку письменности** (75 и 53 ханьцзы у +черновиков luna и glm-4.7 → 2 и 0 после редактора) и **редактор теряет канон У ВСЕХ ШЕСТИ +жильцов** (−0.027 … −0.057), несмотря на закон-блок. Второе — линия эксп-21 §16.1, здесь она +получает шесть независимых подтверждений вместо одного. + +**(д) Цена связки и проекция на книгу (1500 единиц):** +``` +жилец черновик редактор связка книга $ +gemini-3.1-flash-lite 0.00407 0.00466 0.00873 13.09 +deepseek-v4-flash 0.00130 0.00769 0.00899 13.49 +gpt-5.6-luna 0.00368 0.00557 0.00926 13.89 +deepseek-v4-pro 0.00415 0.00751 0.01167 17.50 +glm-4.7 0.00664 0.00559 0.01223 18.34 +grok-4.3 0.00993 0.00525 0.01518 22.77 +``` +⚠ Редактор поверх ЯКОРНОГО черновика дороже, чем поверх любого другого ($0.00769 против +$0.00466–0.00559): якорь пишет длиннее, и это удорожает второй проход. Итоговая цена связки у +якоря всё равно вторая снизу. + +## §5 ДОВЕСОК `D_/A` НА КУПЛЕННОМ СЫРЬЕ ЭКСП-21 (генерации не покупались) + +``` +D_ против A 16 единиц −1.56 [−2.88, −0.25] p=0.0526 декой 16/16 ✔ +``` +Эксп-21 на пуле двух проходов: −2.00, p=0.0157, Холма 0.0630 — не прошёл. **Третья независимая +судейская популяция воспроизвела ЗНАК и не дала значимости.** Статус контраста «однопроходка без +мандата против связки» остаётся **НЕ УСТАНОВЛЕНО** — теперь на трёх замерах, а не на одном. + +--- + +## §6 Ф3 — ПАНЕЛЬ РЕДАКТОРОВ ПОВЕРХ ОДНОЙ ЯКОРНОЙ БАЗЫ + +### 6.0 Контроли (читать ДО чисел) +``` +ДЕКОЙ −4.75 · поймано 16/16 ✔ +ПОЛ −0.25 [−1.88, +1.56] n=16 · sd 3.53 · ст.ошибка 0.88 ⇒ порог различимости 2.47 · ЧЕСТЕН +БЛИЗНЕЦ побайтно одинаковых пар 1 · перевес ровно ноль 1/1 ✔ +``` +⚠ Близнец — не дефект рига, а **находка**: `deepseek-v4-flash` в роли редактора на единице +`2484f16eac` вернул черновик **побайтно без единой правки** (проверено сверкой текстов: 1 клетка +из 62 в Ф3). Класс «no-op редактора», который эксп-12 ловил на grok reasoning-off, воспроизвёлся +у дешёвой модели без всякого гашения размышления. + +### 6.1 Судейство (16 единиц, семейство в ключе ДО ответов, Холм по 4) +``` +контраст ед. перевес 95% ДИ p p Холма +R0 vs F 16 +4.44 [+3.00, +6.12] 0.0001 0.0002 ✔ КОНТРОЛЬ: редактор поверх базы +R2 vs R0 16 −2.12 [−3.81, −0.75] 0.0094 0.0188 ✔ glm-5 против боевого +R3 vs R0 13 −2.54 [−4.00, −1.00] 0.0146 0.0188 ✔ deepseek-v4-flash против боевого +R4 vs R0 16 −4.75 [−6.12, −3.44] 0.0000 0.0001 ✔ grok-4.3 против боевого +``` +**Все четыре контраста проходят поправку, все три альтернативных редактора значимо ХУЖЕ боевого** +`deepseek-v4-pro` над побайтно одной базой, и все перевесы выше порога различимости 2.47. + +**Это первый замер проекта, в котором роль редактора вообще разделилась.** Эксп-21 на паре +{dspro, glm-5} давал `A_law/B` = −0.69 с нулём внутри интервала и писал «движки неразличимы»; +здесь на чистом материале `glm-5` проигрывает −2.12 значимо. Причина расхождения не установлена: +кандидаты — другая книга, другая судейская популяция, другой банк. + +**R1 `gemini-3.1-pro-preview` — обязательный кандидат промта — в таблицу не попал.** Он взят в +фазу, как требует промт, и выдал не число, а **отказ на проводе**: 1 клетка из 16 (см. §6.3). + +### 6.2 Детерминированные оси Ф3 +``` +арм ед. ханьцзы типогр канон покрытие $/ед +R0 deepseek-v4-pro 16 0 18 991/1124 0.882 0.00769 +R1 gemini-3.1-pro-preview 1 0 0 95/97 0.979 0.14702 +R2 glm-5 16 5 27 1049/1124 0.933 0.01249 +R3 deepseek-v4-flash 13 0 21 830/911 0.911 0.00177 +R4 grok-4.3 16 2 18 1009/1124 0.898 0.01426 +БАЗА (черновик) 16 12 27 1022/1124 0.909 0.00130 +``` +⚠ **Детерминированный контур и судья дают РАЗНЫЙ порядок.** Победитель по судье (`deepseek-v4-pro`) +имеет ХУДШЕЕ покрытие канона из всей панели — 0.882 против 0.933 у `glm-5` и 0.909 у самого +черновика. То есть боевой редактор покупает язык и верность, а платит терминологией. Это прямое +подтверждение строки 152 бэклога («расхождение детерминированного контура и судьи») и довод к +тому, что покрытие канона обязано быть ОТДЕЛЬНЫМ гейтом, а не следствием выбора редактора. + +### 6.3 ⛔ ОБЯЗАТЕЛЬНЫЙ КАНДИДАТ ПРОМТА НЕ ОТРАБОТАЛ НА ПРОВОДЕ + +`gemini-3.1-pro-preview` взят в Ф3 безусловно, как требует промт. Что он выдал: + +* **скрин Ф1:** четыре отказа HTTP 503 «This model is currently experiencing high demand», $0, + страница статуса Google инцидентов не показывала. Состав отказов — **две редакторские клетки и + две переводческие**; переводческие взялись с ретрая, редакторские не взялись ни разу, поэтому + редакторской цены у модели нет вовсе, а скрин-вердикт ПРОВАЛ вынесен по переводческой цене; +* **Ф3:** **1 клетка из 16**. Один вызов держал замок **74.5 минуты** без ошибки и без ответа + (таймаут клиента 900 с умножался на внутренние ретраи SDK). После жёсткой границы (300 с, + `max_retries=0`) и объявленного подмножества в 8 единиц новых клеток не прибавилось; +* **цена состоявшейся клетки:** $0.14702 — вход 4485, видимый выход 2532, **тарифицировано 11 504** + выходных токена, то есть в 4.5 раза больше видимого. Сама скрытая часть = 11 504 − 2 532 = + **8 972 токена, втрое больше ответа**; видно это ТОЛЬКО в `total_tokens`. + +⇒ **Гипотеза эксп-04 «gemini-pro — аутлаер прозы» этим паком НЕ проверена и остаётся открытой.** +Проверить её на текущем слаге нельзя не из-за качества, а из-за операбельности: модель в +редакторской роли на боевой длине входа не отдаёт результат. Это ответ на заказ промта, но ответ +про ПРОВОД, а не про прозу. + +--- + +## §12 АННУЛИРОВАННЫЙ ПРОХОД `p2en` — ЧТО СЛУЧИЛОСЬ И ЧЕГО ЭТО СТОИЛО + +Первый выпуск английского прохода ушёл к судьям **без контроля**: декой не был посажен ни в одну +единицу, и заметил это не гейт, а вскрытие голосов. Причина — книго-специфичность посадок: правила +порчи были написаны под лексику 蛊真人 (имена, термины культивации), и на новой книге ни одно из +них не срабатывало, а функция посадки при нулевом совпадении молча возвращала текст без изменений. + +Проход **аннулирован целиком** (голоса лежат в `annulled-p2en-run1/`, в счёт не идут), посадки +переписаны книго-независимыми — грамматика и типографика вместо лексики: снятие отрицания, подмена +величины, выдуманное слово, слом диалоговой вёрстки, слипание абзацев. После переписывания декой +ловится **6/6** на en и 16/16 на zh. + +**Цена ошибки — 6 агент-запусков из 60** (списаны безвозвратно) и задержка фазы. **Урок в прибор:** +контроль, который может «не сработать» молча, не контроль. Посадка теперь падает, если не изменила +текст, — молчаливый ноль больше невозможен. + +⚠ **Девиация ключей, объявляю.** Ключи слепых меток проходов `p2zh`/`p2en` были **пере-созданы в +22:16:22**, то есть ПОСЛЕ того, как судьи начали отвечать (`p2zh` с 21:24). Пере-создание внесло +одно поле — `decoy_from`, базу, из которой посажен декой; задания судьям (21:08–21:10) не менялись +ни одним байтом, перестановка меток не менялась тоже. Пред-объявление семейства доказывается не +этими файлами, а фриз-коммитами `43381fe` (19:54) и `af6d885` (20:38), которые старше и заданий, и +ответов. Но артефакт, на который ссылается печать, моложе ответов — и это надо знать тому, кто +будет проверять провенанс. + +--- + +## §12а ВСКРЫТИЕ ЕДИНИЦ ПЕРЕД СРАВНИТЕЛЬНЫМ ВЫВОДОМ (D39.61, мандат самопроверки) + +Запрет выводить на агрегате, не открыв единиц, — жёсткий. Вот что видно глазами на единице +`d6cbc0179d` (2114 знаков исходника), по одной стороне каждого решающего сравнения. + +**Ф3, `R0` боевой `deepseek-v4-pro` против `R2` пограничного `glm-5`.** Оба дают связную русскую +прозу; провала нет ни у одного, и это согласуется с перевесом 2.12 при пороге прохода 2.47. +Различия стилистические: боевой идиоматичнее и охотнее сворачивает придаточные («нипочём не стал +бы», «прибрал к рукам»), `glm-5` ближе к буквальному и дробит абзацы сильнее. **Вывод вскрытия: +назвать `glm-5` проигравшим по этой паре текстов нельзя — только пограничным**, что и сделано в §2. + +**Ф2, черновик `glm-4.7` (`D4`, −5.44) против якорного.** Здесь разрыв виден сразу и он не +стилистический: черновик `glm-4.7` идёт **по одному предложению на строку** — абзацной структуры +нет вовсе. Это отказ по оси ФОРМА, а не по языку, и он объясняет и величину −5.44, и то, почему +боевой редактор вытягивает этот черновик до −1.25: редактор переливает текст в абзацы. **Именно +это вскрытие даёт механизм за находкой §13** — редактор работает компрессором различий черновика, +потому что худшие различия черновиков оказались вёрсточными, а не смысловыми. + +⚠ Обратная сторона: если бы панель черновиков состояла из моделей, ломающих СМЫСЛ, а не вёрстку, +компрессия могла бы и не сработать. Вывод «черновик решает меньше редактора» верен для той полосы +качества, которую дала эта панель, и не обязан держаться шире. + +--- + +## §13 СВЕРКА ПРОГНОЗОВ §0.5 С ФАКТОМ (заказана промтом: расхождение = находка) + +| # | прогноз (объявлен ДО покупок) | факт | итог | +|---|---|---|---| +| 1 | скрин убьёт 2–4 из 11; вероятны `kimi-k2.6`, `grok-4.3`, `mistral-large-latest`, `gemini-3.1-flash-lite` | убил **4**: kimi, mistral, `gemini-3.6-flash`, `gemini-3.1-pro-preview` | **число верно, состав — наполовину нет** | +| 2 | разброс панели ЧЕРНОВИКОВ будет БОЛЬШЕ разброса панели редакторов | финалы поверх черновиков: размах **2.12**; редакторы: размах **2.63** | **ОПРОВЕРГНУТ, знак обратный** | +| 3 | `gemini-3.1-pro` не окажется аутлаером; перевес ляжет ниже пола | 1 клетка из 16 — перевеса нет | **НЕ РАЗРЕШЁН** (провод, не проза) | +| 4 | победитель черновой роли решится ценой; перевесы лягут в 1–3 ошибки при поле ≈1.9 | перевесы 0.44–2.56, пол **2.03**, правило ничьей применено | **ПОДТВЕРЖДЁН** | +| 5 | эхо-мина `deepseek-v4-flash` на этой книге 25–40% | **6%** (1/16) | **ПРОМАХ в 4–6 раз** | + +**Прогноз 2 — самая содержательная находка сверки, и она против посылки заказа.** Промт строил +дизайн на том, что черновик двигает ~70% качества финала, поэтому и панель черновиков заказана +шире. Данные говорят иначе: КАК ТЕКСТ черновики расходятся огромно (размах **7.25**: от +1.81 до +−5.44), но после боевого редактора этот разброс сжимается до 2.12, тогда как выбор самого +редактора даёт 2.63. **Редактор работает компрессором различий черновика.** Отсюда практический +вывод, которого пак не планировал: деньги на верхний тир имеет смысл тратить в редакторской роли, +а не в черновой, — и именно это независимо подтверждают обе рекомендации (дешёвый черновик, +дорогой редактор). + +**Прогноз 5 промахнут вчетверо, и это не «подтверждение гипотезы 5.3», а промах.** Направление +гипотезы («эхо — свойство материала») он поддерживает, но как ожидание величины прогноз был +неверен, и подавать его подтверждением значило бы задним числом переписывать, что предсказывалось. + +--- + +## §13а МЕХАНИЧЕСКАЯ СВЕРКА С БУКВОЙ ПРОМТА (сделана ПОСЛЕ приёмки, по требованию владельца) + +⚠ **Зачем отдельно от §14.** Критик полноты в §14 писала та же голова, что вела пак, и он +унаследовал её слепоту: проверял по МОДЕЛИ промта, а не по промту. Здесь промт пройден +построчно, включая придаточные. Все найденные пробелы закрыты $0; два — не закрываемы без +покупок и объявлены девиациями. + +| требование промта | где стоит в промте | статус | +|---|---|---| +| Sol-пакет **(б)** — слепые пары финалистов «по готовности Ф2/Ф3» | хвост п.4 Ф0 | ⛔ **НЕ ЗАКРЫТО и не будет** (поправка фазы Д): пакет сгенерирован, но владельцу в этом виде НЕ отдаётся — та же конструкция `N_UNITS_B = 3`, что развалила пакет (а) (§16). Прежняя пометка «выпущен» — маркер дефекта «артефакт вместо действия». Замена: контур на заданиях абсолютного рига | +| **Вахта dspro** — сверка вендор-changelog маппинга эффорта в Ф0 и ПЕРЕД Ф3 | буллет ЗАКОНа про DeepSeek | **было не сделано → сделано**, девиация по времени (§14) | +| Гейт **ToS Z.AI** пометить **в ИТОГе** | последняя строка промта | было в §14 → **вынесено в ИТОГ (§9а)** | +| цена связки p50/**p95** по единицам **против боевой пары** | строка «Отчёт» | квантили добавлены, **сравнение с боевой парой сделано явным** (§5) | +| вывод на агрегате до **вскрытия единиц** запрещён (D39.61) | мандат самопроверки | **не было документировано → §12а**, дало механизм за находкой §13 | +| ось **(а)** — абсолютный суд черновика как ТЕКСТА | п. (а) Ф2 | было куплено и не напечатано → **§1** | +| позиционный наклон «замерь и **вычти**» | буллет ЗАКОНа про судейство | было не вычтено → **§7, все четыре прохода** | +| en-единицы Kristoff, `129b73ad5a` выброшен | п.2 Ф0 | исполнено: 6 единиц, выброшена | +| закон-блок `HEADER_LAW_PLAIN` во всех армах + банкнота на переводческом | банк-закон D39.104 | ⛔ **на zh исполнено, на en НЕТ** (поправка фазы Д, п.2): 72 en-клетки куплены без блока, потому что банк среза собран только по zh — §9 п.9. Селфтест `prompts.py` сторожит zh-ветку; en-ветки у него нет | +| клетка dspro-над-flash покупается **один раз**, в Ф3 переиспользуется | конец Ф2 | исполнено (`editors.py::text_of`) | +| спорных кандидатов СТИЛЬ-осей скрином не решать | урок эксп-20 в Ф1 | исполнено: скрин убивал по цене, формату и пустому выходу, не по качеству прозы | +| Claude-модели в армы не брать | буллет ЗАКОНа | исполнено: моделей семьи судьи в ростере нет | +| **дешёвый + сильный тир КАЖДОГО провайдера**, срез только при >16 | абзац «Кандидаты» Ф1 | ⛔ **НЕ ИСПОЛНЕНО — девиация, см. ниже** | +| вторая база Ф3 · строка 153 | опции Ф3 | не гнались — решение сессии, §9 п.12 | + +### ⛔ Девиация: панель кандидатов срезана сильнее разрешённого + +Промт разрешал до **16** кандидатов и требовал у каждого провайдера **оба тира** — дешёвый +(кандидаты черновой роли) и сильный (кандидаты редакторской); «провайдера целиком не выкидывать — +хотя бы один слот» — это ПОЛ правила на случай среза, а не цель. Я срезал до **11**, и срез снёс +сильный тир у четырёх провайдеров из семи: у OpenAI, Moonshot, xAI и Mistral остался один слот +(`roster.CUT`: `gpt-5.6-terra`, `kimi-k3`, `grok-4.5`, `mistral-medium-latest`, три слага Z.AI). +Обоснование по каждой срезанной модели в коде есть — требование «с обоснованием» выполнено, — +но запас на пять кандидатов не был использован. + +**Что это меняет для читателя.** Панель Ф3 состояла из того, что прошло суженный скрин; сильные +тиры четырёх провайдеров в замере не участвовали вовсе. Поэтому вывод «`deepseek-v4-pro` побеждает +значимо» верен ВНУТРИ панели и **не является утверждением о лучшем доступном редакторе**: не +проверены `gpt-5.6-terra`, `kimi-k3`, `grok-4.5`. Починка требует покупок и в этот пак не входит; +для следующего — это первая строка работы. + +--- + +## §15 КОРОТКИЕ КЛЕТКИ В СЫРЬЕ ЭТОГО ПАКА — ⚠ ИНТЕРПРЕТАЦИЯ ПЕРЕПИСАНА ФАЗОЙ Д + +⚠ **Эта секция дважды говорила неверное, и оба раза — не про числа, а про ПРИЧИНУ.** Первая +редакция (в отчёте пака 23) сравнивала длину клетки с медианой ПУЛА и объявила оборванными три +клетки, которые нормальны: они из английского пула, где расширения нет. Вторая (здесь) правило +починила — отношение к СВОЕМУ исходнику, — но объявила, что «пять клеток из шести завершились +штатно и ни один гейт этого не ловит». **Это опровергнуто приёмкой №16 и пере-снято фазой Д: пять +клеток из шести — ЭХО ИСХОДНИКА, и эхо-гейт поймал каждую.** + +**Замер (доля письменности в выходе, $0, `content` из сырья):** + +``` +клетка исх вых ханьцзы кирилл finish что это +tp2-draft-D-deepseek-v4-flash-474f822806 2194 2279 1.000 0.000 stop ЭХО → ре-ген + └ …-r2 2194 7814 0.001 0.999 stop ×3.6, СУДИЛСЯ как D0 +tp2-floor-p1-D-deepseek-v4-flash-474f822806 2194 2281 1.000 0.000 stop ЭХО → ре-ген + └ …-r2 2194 7994 0.001 0.997 stop ×3.6 +tp2-floor-p2-D-deepseek-v4-flash-41599f30df 2116 2167 1.000 0.000 stop ЭХО → ре-ген + └ …-r2 2116 6950 0.001 0.999 stop ×3.3 +tp2-floor-p2-D-deepseek-v4-flash-ef9cd38ec4 2155 2190 1.000 0.000 stop ЭХО → ре-ген + └ …-r2 2155 2211 1.000 0.000 stop ЭХО ещё раз + └ …-r3 2155 7672 0.000 0.998 stop ×3.6 +tp2-draft-D-gemini-3.1-flash-lite-ed068182cf 2517 2448 1.000 0.000 stop ЭХО 3/3 — арм не куплен +tp2-edit-E-deepseek-v4-pro-474f822806 2194 1638 0.000 1.000 length ОБРЫВ, и он СУДИЛСЯ +``` + +Что именно судилось — доказано подписями `sig` в ключах слепых меток (обратное сопоставление +подписи с файлом на диске): **эхо-попытки в ключах не встречаются ни разу** — это главное и это +доказано. ⚠ Уточнение фазы Д: буквально «судился ре-ген» верно для ОДНОЙ клетки — черновика +`474f822806-r2` (арм `D0` в `p2zh`, `F` в `p3`). У трёх флор-клеток в ключах подписаны не сами +ре-гены, а РЕДАКТУРЫ поверх них; что редактура снята именно с ре-гена, подписью `sig` не +доказывается — в записи редакторской клетки хеша входа нет. Утверждение «эхо не судилось» от +этого не страдает, но провенанс «поверх чего сделана редактура» — открытая дыра прибора, и её +надо закрыть полем `input_sig` в записи клетки. Арм `D3`/`E3` на единице `ed068182cf` отсутствует вовсе — это и есть объявленная в §7 +«единица, которая не покупается» (отсюда `E3 n=15`). + +⇒ **Первые пять — не пропуск гейта, а его РАБОТА, видимая в леджере: эхо поймано, ре-ген оплачен, +судился здоровый текст.** Настоящий пропуск ровно один — обрыв `finish=length`. + +### ⛔ А вот РАЗМЕР КЛАССА ОБРЫВОВ больше, чем думали обе прежние редакции + +Пере-счёт по ВСЕМ ключам пака (404 судимых клетки опознаны на диске по подписи): **`finish=length` +у семи судимых слотов, **четыре** различных текста (три из них судились дважды — в `p2zh` и в +`p3`, потому что клетка `R0` куплена один раз и переиспользована).** + +``` +проход арм клетка len $ +p2zh CTRLfloorB tp2-floor-p2-E-deepseek-v4-flash-bbb54d0043 5319 0.015336 +p2zh CTRLfloorB tp2-floor-p2-E-deepseek-v4-flash-f6da9f76b2 7581 0.015200 +p2zh E0 tp2-edit-E-deepseek-v4-flash-41599f30df 5759 0.015727 +p2zh E2 tp2-edit-E-deepseek-v4-pro-474f822806 1638 0.015241 +p3 CTRLfloorB tp2-floor-p2-E-deepseek-v4-flash-bbb54d0043 5319 0.015336 +p3 CTRLfloorB tp2-floor-p2-E-deepseek-v4-flash-f6da9f76b2 7581 0.015200 +p3 R0 tp2-edit-E-deepseek-v4-flash-41599f30df 5759 0.015727 +``` + +⚠ **И это не весь класс обрывов в паке — только его СУДИМАЯ часть.** Отдельно на диске лежат +**шесть клеток `finish=length` с ПУСТЫМ `content` на $0.216770**: три скрина `kimi-k2.6` и три +редакторские клетки `deepseek-v4-flash` фазы Ф3. В ключи слепых меток они не попали (судить нечего), +поэтому в таблицу выше не входят — но фраза «пере-счёт по ВСЕМ ключам» описывает именно судимую +часть, и объявить границу надо здесь. Пустые клетки Ф3 — тот же режим, из-за которого `R3` считан +по 13 единицам из 16 (§7), а пустые клетки скрина — основание дисквалификации `kimi-k2.6`. + +Читать судимую часть надо не как «ещё четыре клетки», а как **две конкретные беды**: +* **две клетки стоят в ШУМОВОМ ПОЛУ обеих фаз.** Пол — калибровка прибора, из него берётся порог + различимости; недоработавший вызов в паре завышает разброс, а значит и порог; +* **одна клетка стоит в БАЗЕ ВСЕХ КОНТРАСТОВ.** `tp2-edit-E-deepseek-v4-flash-41599f30df` — это + `E0` в Ф2 и он же `R0` в Ф3 (клетка куплена один раз и переиспользована, §0-Ф3). То есть на + единице `41599f30df` каждый контраст обеих фаз сравнивался ПРОТИВ оборванного текста. + +### Чувствительность выводов — замерена, а не оценена + +Leave-out по единицам, несущим хоть одну оборванную судимую клетку (пол пере-считан по оставшимся): + +⚠ **Первая редакция этой таблицы печатала ТОЛЬКО пороги — вторичный критерий, — а решающее +правило пака есть `p` Холма < 0.05 (§0-Ф2). Поймано ревью; пере-считано точным знаковым тестом.** + +``` +Ф2 (p2zh) все 16 единиц без 4 затронутых (n=12) +пол/порог +0.19 / 2.03 +0.17 / 2.33 +E1 vs E0 −0.44 p 0.6711 Холм 0.6711 −1.25 p 0.1855 Холм 0.2739 +E2 vs E0 −2.56 p 0.1058 Холм 0.3452 −1.83 p 0.1084 Холм 0.2739 ← был выше порога +E3 vs E0 −1.87 p 0.0863 Холм 0.3452 −2.18 p 0.0098 Холм 0.0391 ✔ ← стал ЗНАЧИМ +E4 vs E0 −1.25 p 0.2663 Холм 0.5326 −2.08 p 0.0913 Холм 0.2739 +E5 vs E0 −1.69 p 0.0444 Холм 0.2222 −2.25 p 0.0078 Холм 0.0391 ✔ ← стал ЗНАЧИМ +E0 vs D0 +3.25 p 0.0001 Холм 0.0007 ✔ +3.33 p 0.0020 Холм 0.0117 ✔ КОНТРОЛЬ + +Ф3 (p3) все 16 единиц без 3 затронутых (n=13) +пол/порог −0.25 / 2.47 −0.31 / 2.41 +R2 vs R0 −2.12 p 0.0094 Холм 0.0188 ✔ −1.92 p 0.0103 Холм 0.0205 ✔ +R3 vs R0 −2.54 p 0.0146 Холм 0.0188 ✔ −2.60 p 0.0430 Холм 0.0430 ✔ +R4 vs R0 −4.75 p 0.0000 Холм 0.0001 ✔ −4.85 p 0.0002 Холм 0.0010 ✔ +R0 vs F +4.44 p 0.0001 Холм 0.0002 ✔ +4.38 p 0.0005 Холм 0.0015 ✔ КОНТРОЛЬ +``` + +**Что устояло и что нет.** Решающая таблица пака — Ф3, выбор редактора — не двигается ни в одном +вердикте: все четыре контраста проходят Холма и до, и после. Заголовочные выводы («ни один +альтернативный черновик не улучшил финал», «все три альтернативных редактора хуже боевого», +«контроль воспроизводится в обеих фазах») держатся. + +⛔ **Не устояли ДВА напечатанных утверждения §1, а не одно, как объявляла первая редакция этой +секции.** (1) «Все, кроме `E2`, лежат ниже порога различимости»: без затронутых единиц `E2` тоже +ниже порога. (2) **«Ни один не проходит Холма»**: на подвыборке `E3` и `E5` проходят (Холм 0.0391). +Второе нашла не сессия — ревью, и нашла ровно потому, что сессия сэкономила усилие на дорогой +статистике: напечатала пороги и не пере-считала `p`. + +**Куда это ведёт содержательно.** Обе правки направлены ПРОТИВ альтернативных черновиков: убрав +единицы, где сам якорный финал `E0` оборван, мы делаем `E0` сильнее, и два альтернативных черновика +становятся значимо ХУЖЕ. То есть заголовок «ни один альтернативный черновик не улучшил финал» после +поправки не ослабевает, а усиливается — но формулировка «ни один не проходит Холма» верна только +для полного набора, и так её и надо читать. + +⚠ Это **leave-out, а не чистый пере-замер**: выбрасывание единиц меняет и n, и пол. Оно отвечает на +вопрос «зависит ли вывод от затронутых единиц» и не отвечает на вопрос «какими были бы числа, если +бы клетки не оборвались». Второго ответа не существует без покупки. + +**В прибор — гейт СУЖЕН до класса `finish=length`** (так предписала приёмка, и замер это +подтверждает). Отношение «длина клетки к длине исходника» как признак не нужно: эхо-класс уже +покрыт эхо-гейтом, а на разноязычных пулах отношение даёт ложные срабатывания. Нужное правило +одно и детерминированное: **клетка с `finish=length` в судейскую пачку не допускается — ни боевым +армом, ни половиной шумового пола.** Провод сообщает этот флаг сам, деньги за клетку уже уплачены, +и стоит гейт $0. В фазе Д он стоит ДО судейства и жёсткий; для этого пака клетки уже куплены и +отсужены, поэтому здесь класс объявлен с замером чувствительности, а не пере-куплен. + +## §14 КРИТИК ПОЛНОТЫ ПО ФАЗАМ (заказан промтом) + +Что заказано · что закрыто · что осталось. Пишется по фазам, а не по впечатлению от объёма. + +**Ф0 — материал.** Заказано: свежий незаражённый zh-срез, проба контаминации, банк, Sol-пакет (а). +Закрыто всё. Не закрыто: **ToS-роутинг помечен, но не проверен исполнением** — `sexually-explicit` +объявлен FORBIDDEN для `deepseek` и `zai` по договорам провайдеров (маршрутизация правом, не +поведением), и гейт прав снял по этому основанию две главы; но самого отказа моделей на +чувствительном материале пак не наблюдал, потому что материал этого класса убрал (§11). + +**Ф1 — скрин.** Заказано: профиль каждого кандидата, пороги до замера, сверка прогнозов. Закрыто: +таблица по всем 11, три починки исполнением, четвёртая (отступ на 5xx) — в §2.3. Сверка прогнозов +доделана здесь, §13. **Кредиты xAI:** промт фиксировал их исчерпанными 07.08 с пометкой «вопрос у +владельца»; `grok-4.3` скринился и покупался во всех фазах, то есть пополнение состоялось — строка +об этом промтом требовалась и печатается здесь (§9 п.6 — подтвердить владельцу). + +**Ф2 — панель черновиков.** Заказано пять осей: (а) черновик как текст, (в) эхо, (г) батарея и +канон, (д) цена. Закрыто всё, **но ось (а) была отсуждена и не напечатана** — исправлено (§1), +и это самый крупный пропуск пака: замер существовал в сырье и отсутствовал в отчёте. + +**Ф3 — панель редакторов.** Заказано: обязательный кандидат, резерв эксп-21, дешёвый и дорогой +над одной базой. Закрыто. Не закрыто: **вторая база Ф3** (опция промта) не гналась — решение +сессии, не владельца (§9 п.12); **`R1` не дал панели** (1 клетка); **`R3` считан по 13/16** из-за +оплаченного пустого выхода (§7). + +**Вахта dspro (ЗАКОН промта: сверка вендор-changelog маппинга эффорта в Ф0 и ПЕРЕД Ф3).** +Отработана по вендор-доке ($0, без вызовов): `reasoning_effort` в API DeepSeek присутствует, +управление размышлением — через `thinking`; последняя запись вендор-changelog, затрагивающая +`deepseek-v4-flash`, датирована **31.07.2026** и описывает пере-обучение при той же архитектуре и +размере — ровно тот факт, что уже зафиксирован D39.61 («слаг живой ≠ модель та же»). **Смены +маппинга эффорта после фриза Ф2 нет**, поэтому стоп и пинг, которые промт предписывает при смене, +не требовались. ⚠ Сверка сделана ПОСЛЕ покупок, а не в Ф0 и перед Ф3, как предписано; я это +объявляю девиацией, а не выдаю за исполненное вовремя. Задним числом она безопасна ровно потому, +что изменения не нашлось: найдись оно — пришлось бы аннулировать фазу, а не дописывать абзац. + +**Сквозное.** Закрыто: позиционный наклон замерен по всем четырём проходам и вычтен (§7), +p50/p95 по единицам напечатаны (§5), деньги сведены двумя путями. Не закрыто и переносится: +**строка 153** (уравненный мандат `D`/`D_`) не гналась; **Sol вернулся 09.08, но арбитраж не +состоялся** (§16: пакет (а) прогнан владельцем и развалился по конструкции — `N_UNITS = 3`, дрейф +шкалы ×2.6, зеркальные пары в разных сессиях; пакет (б) в этом виде не выпускать), поэтому +судейского контура вне одной модельной семьи у ЭТОГО пака по-прежнему нет (§11) — ⚠ поправка фазы +Д, п.3: прежняя формулировка «Sol не вернулся» неверна; **фактическая величина двойной оплаты Ф2** +невосстановима в принципе (§8). + +**Чего в паке нет и не могло быть.** Вебновелл-срез взят одной книгой и одной парой: всё, что +зависит от свойств текста, обязано пере-меряться на следующей. Ни один вывод пака не является +утверждением о моделях вообще — только о них на этой паре, этой книге и этих 16 единицах. diff --git a/eval/README.md b/eval/README.md index a4bc94cc..a4b3ad19 100644 --- a/eval/README.md +++ b/eval/README.md @@ -28,6 +28,24 @@ | `providers.json` | базовые URL/слаги для зондов | квирки — `docs/experiments/00-provider-quirks.md`, читать ПЕРЕД любым вызовом | | `promptlang/` | Р6+P4 (закрыт D39.61): язык промптов · анти-эхо wiring · верность митигаций; действующая редакция выводов = §12 отчёта `docs/archive/reports/POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md` | `slugcheck.py`, `wire_probe.py` (сырой провод), `run_arms.py`, `spend.py` (два пути счёта денег — прогонять оба), `judge_*.py` | +### Действующие харнессы паков 18–23 и фазы Д (добавлено эксп-22 08.08 — в карте их не было) +| Папка | Эксперимент | Что внутри и чем полезно дальше | +|---|---|---| +| `bank_arbitration/` | exp-16/18 + research/24 | `editor_wire_probe.py` — рендер боевых промптов (`render`, `strip_comments`, `editor_block`, `HEADER_LAW_PLAIN`/`HEADER_LAW_CLAUSE`), окна и посадки; `inject_probe.py` — глоссарий-блок переводчика и `render_translator`. **Это фундамент всех последующих паков**, ломать нельзя | +| `editor_contract/` | exp-19 (дифф-контракт Q4b) | `probe.py`, `apply.py` (аппликатор, самотест 15/15), `effort_probe.py` (вахта маппинга эффорта DeepSeek) | +| `editor_harness/` | exp-20 (контракт редактора × модели) | `probe.py` — сборка сообщений по контрактам (`full`/`diff-*`/`locate`/`direct`), en-зеркала промптов | +| `role_topology/` | **exp-21 (топология ролей)** | `absjudge.py` — абсолютный судейский риг (слепые метки · декой · пол · обоснование цитатой · семейство в ключе · Холм), `battery.py` — 11 детерминированных проверок, `buy.py` — касса с проекционным гардом, `bakeoff.py`/`pair_en.py` — армы zh/en, `itog.py`/`verify_report.py` — образец гейтов чисел, `ruff.toml` — линтер зоны | +| `tenant_panel/` | **exp-22 (панель жильцов)** | `material.py` — добыча и отбор невиданного среза, `contam.py` — проба контаминации, `bank.py` — банк книги + метрика покрытия канона, `roster.py` — ростер и цены с провенансом, `money.py` — касса с фриз-гейтом (покупка отказывает незакоммиченному коду) и `configure()` для пере-настройки под другой пак, `judge.py` — тот же `absjudge`, настроенный на другую панель, `sol.py` — пакеты внешнему судье (⚠ конструкция признана негодной, §16 отчёта) | +| `editor_tier/` | **exp-23 (сильный тир редактора и граница `glm-5`)** | `screen.py` — скрин сильного тира, `panel.py` — панель редактур поверх якорной базы, `judge.py` — два прохода (`tier`/`border`) + режим `--run <прогон>` для ОТДЕЛЬНЫХ прогонов того же прохода (репликация), `money.py` — касса эксп-22 через `configure`, `itog23.py`/`verify23.py` — пере-счёт и гейт чисел (сторожат пол, порог, декой, наклон и СВЕЖЕСТЬ разбора), `solscore.py` — счёт ответов ВНЕШНЕГО судьи вне семьи Claude | +| `dovodka/` | **фаза Д (доводка 22/23, заказ владельца 10.08)** | `runs.py` — журнал агент-сессий: запись ДО запуска суб-агента, кап 80, замок против ГОНКИ сессий по одному токену (в паке 23 гонка стоила соответствия голосов и сырья на 5 единицах) | + +> ⚠ **Две мины импорта, на которые наступает каждый новый пак.** +> (1) Модуль `probe.py` существует И в `editor_contract/`, И в `editor_harness/` — какой из них +> подхватится, решает ПОРЯДОК `sys.path.insert` в вызывающем файле. Проверять фактический путь +> (`probe.__file__`), а не догадываться. +> (2) Сырьё паков живёт в `~/books/<пак>/`; путь зашит константой. Если каталог переехал — +> харнесс молча видит пустоту и печатает «голосов нет» вместо ошибки. + ### 18+ / корпус-билдеры | Скрипт | Эксперимент | Заметки | |---|---|---| diff --git a/eval/conformance.py b/eval/conformance.py index 0e863335..b231f7c9 100644 --- a/eval/conformance.py +++ b/eval/conformance.py @@ -25,9 +25,22 @@ проходящая улика, а не что улика доказывает именно это требование: связь «требование ↔ улика» устанавливает автор таблицы, и подменить её ничего не мешает. Это сдвигает работу с «вспомнить всё из прозы» на «объявить улику заранее» — но не отменяет чтения. + +⚠⚠ **ДВА ДЕФЕКТА, НАЙДЕННЫЕ ПРИЁМКОЙ НА ПАКЕ 23, ЧИНЯТСЯ ЗДЕСЬ** (фаза Д, 10.08): + +1. **Реестр жил в эфемерном scratchpad.** `--final` печатал зелёное против таблицы, которой в + репозитории нет: читатель пере-снять её не мог, а автор мог править её после результата. Теперь + в режиме `--final` источник ОБЯЗАН быть отслежен git и совпадать с закоммиченным (тот же + фриз-гейт, что у кассы `tenant_panel/money.py`). Обход — `TM_ALLOW_UNFROZEN=1`, громко. +2. **Отчёт проверял сам себя.** Большая часть улик была грепом формулировок ТОГО ЖЕ документа, то + есть «в тексте написано, что сделано» выдавалось за «сделано». Механически отличить «улика на + печать» от «улики на дело» нельзя, поэтому инструмент их СЧИТАЕТ и печатает долю: улика, + упоминающая сам файл реестра или файл отчёта, помечается `сам-себя` и выносится в итог + отдельной строкой. Ноль таких улик не требуется — требуется, чтобы читатель видел их число. """ from __future__ import annotations +import os import subprocess import sys from pathlib import Path @@ -36,6 +49,22 @@ REPO = Path("/home/ubuntu/projects/textmachine") MARK = "" +def frozen(src: Path) -> tuple[bool, str]: + """Реестр отслежен git и совпадает с закоммиченным? Это и есть его фриз.""" + try: + rel = str(src.resolve().relative_to(REPO)) + except ValueError: + return False, f"реестр вне репозитория: {src}" + r = subprocess.run(["git", "-C", str(REPO), "ls-files", "--error-unmatch", rel], # noqa: S603,S607 + capture_output=True, text=True) + if r.returncode: + return False, f"реестр НЕ в git: {rel} — гейт проверял бы файл, которого нет у читателя" + d = subprocess.run(["git", "-C", str(REPO), "diff", "--quiet", "HEAD", "--", rel]) # noqa: S603,S607 + if d.returncode: + return False, f"реестр отличается от закоммиченного: {rel}" + return True, "" + + def rows(md: str) -> list[tuple[str, str, str]]: """Строки таблицы требований: (ID, требование, улика).""" if MARK not in md: @@ -97,19 +126,46 @@ def main() -> int: if not src: print("не указан файл") return 1 - md = (REPO / src if not src.startswith("/") else Path(src)).read_text(encoding="utf-8") - bad = pend = 0 + path = REPO / src if not src.startswith("/") else Path(src) + md = path.read_text(encoding="utf-8") + bad = pend = selfref = 0 print(f"СВЕРКА С БУКВОЙ ЗАКАЗА: {src} [{mode[2:]}]\n") + + # ⚠ ФРИЗ РЕЕСТРА. Гейт против незакоммиченной таблицы ничего не доказывает: её можно дописать + # ПОД результат, и читатель этого не увидит. Пак 23 прошёл `--final` против реестра, лежавшего + # в сессионном scratchpad, — и «соответствие заказу проверено» из этого не следовало. + if mode == "--final": + ok, why = frozen(path) + if not ok: + if os.environ.get("TM_ALLOW_UNFROZEN") == "1": + print(f"⚠ ФРИЗ-ГЕЙТ РЕЕСТРА ОТКЛЮЧЁН (TM_ALLOW_UNFROZEN=1): {why}\n") + else: + print(f"⛔ {why}\n зафризь реестр коммитом ДО сверки (промт фазы Д, нормы рига)") + return 1 + + # Улика, которая грепает сам реестр/отчёт, доказывает «напечатано», а не «сделано». + # ⚠ Первая редакция искала имя как ОТДЕЛЬНОЕ слово с запретом «/» слева — то есть любая + # улика с ПУТЁМ (а иначе улику не пишут) была невидима, и строка «сам-себя: 0» печаталась + # всегда. Поймано ревью исполнением. Ищем подстроку имени, плюс имена ОТЧЁТОВ фазы: улика, + # грепающая отчёт, доказывает «напечатано», а не «сделано». + selfnames = {path.name, *(Path(a).name for a in args if a.endswith(".md"))} + selfnames |= {f.name for f in (REPO / "docs" / "experiments").glob("2[23]-*.md")} for rid, req, ev in rows(md): st, detail = check(ev) if st == "ОЖИДАЕТ" and mode == "--final": st = "ПРОВАЛ" detail = "улики нет, а пак сдаётся" + mark = "" + if any(n in ev for n in selfnames): + selfref += 1 + mark = " [сам-себя]" bad += st == "ПРОВАЛ" pend += st == "ОЖИДАЕТ" - print(f"[{st:8s}] {rid:6s} {req[:78]}" + (f" ← {detail}" if detail else "")) + print(f"[{st:8s}] {rid:6s}{mark} {req[:70]}" + (f" ← {detail}" if detail else "")) total = len(rows(md)) print(f"\nтребований {total} · ожидают {pend} · провалов {bad}") + print(f"улик вида «греп своего же документа» ({'сам-себя'}): {selfref} из {total}" + + (" ⚠ они доказывают, что НАПЕЧАТАНО, а не что СДЕЛАНО" if selfref else "")) if bad: print("СВЕРКА НЕ ПРОЙДЕНА") return 1 if bad else 0 diff --git a/eval/editor_tier/itog23.py b/eval/editor_tier/itog23.py new file mode 100644 index 00000000..5df4f8c0 --- /dev/null +++ b/eval/editor_tier/itog23.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +"""ПЕРЕ-СЧЁТ ВСЕХ НЕСУЩИХ ЧИСЕЛ ПАКА ИЗ СЫРЬЯ. $0. + +Число, которое нельзя пере-снять командой из репозитория, ничем не отличается от числа по памяти. +Здесь каждый блок ИТОГа печатается заново из персистированных артефактов, деньги — двумя +независимыми путями (касса и пере-счёт из сырых `usage` × прайс). + +Запуск: eval/.venv/bin/python eval/editor_tier/itog23.py +""" +from __future__ import annotations + +import json +import statistics as st +import sys +from pathlib import Path + +REPO = Path("/home/ubuntu/projects/textmachine") +ZONE = Path(__file__).resolve().parent +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 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 + + +JU = _load("judge_et", ZONE / "judge.py") +PAN, SCR, MONEY, MAT = JU.PAN, JU.PAN.SCR, JU.MONEY, JU.MAT +ROSTER = SCR.ROSTER +OUT = MONEY.OUT + + +def h(t: str) -> None: + print(f"\n═══ {t} ═══") + + +def material() -> None: + h("МАТЕРИАЛ") + old, new = JU.P22.M.units_zh(), MAT.units_new() + print(f" единиц эксп-22 (приколоты манифестом) {len(old)} · глав " + f"{sorted(u['chapter'] for u in old)}") + ln = sorted(len(u["source"]) for u in new) + print(f" НОВЫХ единиц {len(new)} · глав {sorted(u['chapter'] for u in new)}") + print(f" знаков {ln[0]}…{ln[-1]} медиана {ln[len(ln) // 2]} · " + f"макс. близость {JU.P22.M._pairwise_max_sim(new):.3f}") # noqa: SLF001 + print(f" снято гейтом прав из своего пула: {MAT.rights_dropped(set()) or '—'}" + if False else f" пересечений с эксп-22: " + f"{len({u['uid'] for u in new} & {u['uid'] for u in old})}") + + +def screen() -> None: + h("ФАЗА A — СКРИН СИЛЬНОГО ТИРА (пороги эксп-22, не свои)") + rs = SCR.result() + print(f" {'модель':16s}{'вердикт':>10s}{'ед.':>4s}{'$/ед edit':>11s}{'размышл':>9s} причины") + for m, r in rs.items(): + print(f" {m:16s}{r['verdict']:>10s}{r['n']:4d}{(r['price_editor'] or 0):11.5f}" + f"{r['reasoning_share']:9.1%} {'; '.join(r['why'])}") + print(f" прошли: {[m for m, r in rs.items() if r['verdict'] == 'прошёл']}") + print(" СПРАВКА (полный критерий эксп-22, с переводческим порогом, которого нет в пре-реге):") + for m, r in rs.items(): + print(f" {m:16s}{r['verdict_full22']:>10s} $/ед transl " + f"{(r['price_translator'] or 0):.5f}") + + +def judged(pass_: str) -> None: + h(f"СУДЕЙСТВО, проход {pass_}") + JU.setup(pass_) + import absjudge as AJ # noqa: PLC0415 + if not AJ._by_unit(): # noqa: SLF001 + print(" голосов нет") + return + AJ.controls() + print() + AJ.score() + + +def positional() -> None: + h("ПОЗИЦИОННЫЙ НАКЛОН — ЗАМЕРЕН И ВЫЧТЕН (требование ЗАКОНа)") + for pass_ in JU.PASSES: + JU.setup(pass_) + import absjudge as AJ # noqa: PLC0415 + if not AJ._by_unit(): # noqa: SLF001 + continue + print(f" --- {pass_} ---") + AJ.position() + print() + + +def money_two_ways() -> None: + h("ДЕНЬГИ — ДВА НЕЗАВИСИМЫХ ПУТИ") + led = {ph: MONEY.Guarded(ph).spent() for ph in MONEY.TAGS} + + def indep(rec: dict) -> float: + m = rec.get("model") + if m not in ROSTER.CANDIDATES: + return rec.get("cost_usd", 0.0) + _, _, pin, pc, pout, _t, _s = ROSTER.CANDIDATES[m] + pt, cd = rec.get("prompt_tokens", 0), rec.get("cached_tokens", 0) + out = ROSTER.billed_output(m, rec.get("completion_tokens", 0), + rec.get("reasoning_tokens", 0), + rec.get("total_tokens", 0), pt) + return (pt - cd) / 1e6 * pin + cd / 1e6 * pc + out / 1e6 * pout + + tot2 = {ph: 0.0 for ph in led} + empty = [] + for ph, globs in MONEY.TAGS.items(): + for g in globs: + for f in OUT.glob(g): + r = json.loads(f.read_text(encoding="utf-8")) + tot2[ph] += indep(r) + if r.get("cost_usd", 0) > 0 and not (r.get("content") or "").strip(): + empty.append((f.name, r["cost_usd"])) + print(f" {'фаза':6s}{'касса':>12s}{'пере-счёт':>12s}{'расхождение':>13s}{'потолок':>10s}") + for ph in MONEY.TAGS: + mark = " ⛔ ПРОБОЙ" if led[ph] > MONEY.CEILINGS[ph] + 1e-9 else "" + print(f" {ph:6s}{led[ph]:12.6f}{tot2[ph]:12.6f}{led[ph] - tot2[ph]:13.6f}" + f"{MONEY.CEILINGS[ph]:10.2f}{mark}") + print(f" {'ПАК':6s}{sum(led.values()):12.6f}{sum(tot2.values()):12.6f}" + f"{sum(led.values()) - sum(tot2.values()):13.6f}{MONEY.PACK_CEILING:10.2f}") + if empty: + t = sum(c for _n, c in empty) + print(f" оплачено за ПУСТОЙ выход: {len(empty)} вызовов ${t:.5f} " + f"({t / max(1e-9, sum(led.values())) * 100:.1f}% пака)") + for n, c in sorted(empty, key=lambda x: -x[1]): + print(f" {n} ${c:.5f}") + + +def _cost(f: Path) -> float: + return json.loads(f.read_text(encoding="utf-8")).get("cost_usd", 0.0) if f.exists() else 0.0 + + +def economics() -> None: + """⚠ Строка R0 считается ИЗ СЫРЬЯ эксп-22, а не через `hasattr(P22,"edit_cost")`. + + Прежняя редакция спрашивала у эксп-22 метод, которого у него нет (есть только `draft_cost`), — + строка молча выходила нулевой и отфильтровывалась, а в отчёт попадали числа, снятые где-то ещё + и друг с другом не сходившиеся (0.00545 × 1500 = $8.18, напечатано $13.45). Поймано приёмкой. + Колонки печатаются ДВУМЯ семействами: редактура отдельно и СВЯЗКА черновик+редактор — сравнивать + связку одного арма с редактурой другого нельзя, а именно это отчёт и делал. + """ + h("ЦЕНА РЕДАКТОРСКОЙ КЛЕТКИ, p50 И p95 ПО ЕДИНИЦАМ") + us = JU.P22.M.units_zh() + T22 = JU.P22.OUT + base = [_cost(T22 / f"{JU.P22.tag_draft(PAN.ANCHOR, u['uid'])}.json") for u in us] + rows = [("R0 " + PAN.BATTLE, + [_cost(T22 / f"{JU.P22.tag_edit(PAN.ANCHOR, u['uid'])}.json") for u in us])] + for i, m in enumerate(PAN.survivors(), start=1): + rows.append((f"T{i} {m}", [_cost(OUT / f"{PAN.tag_c(m, u['uid'])}.json") for u in us])) + + def q(cs: list[float], p: int) -> float: + s = sorted(c for c in cs if c > 0) + return s[min(len(s) - 1, -(-p * len(s) // 100) - 1)] if s else 0.0 + + print(f" {'арм':26s}{'ед.':>5s}{'p50 edit':>10s}{'p95 edit':>10s}" + f"{'p50 связка':>12s}{'книга p50':>12s}{'книга p95':>12s}") + for name, cs in rows: + nz = [c for c in cs if c > 0] + if not nz: + continue + link = [c + b for c, b in zip(cs, base, strict=True) if c > 0 and b > 0] + print(f" {name:26s}{len(nz):5d}{st.median(nz):10.5f}{q(nz, 95):10.5f}" + f"{(st.median(link) if link else 0):12.5f}" + f"{(st.median(link) if link else 0) * 1500:12.2f}{q(link, 95) * 1500:12.2f}") + print(f" якорный черновик (общая база связки): p50 {st.median([b for b in base if b > 0]):.5f}" + if any(base) else " якорного черновика в сырье нет") + + +def agent_runs() -> None: + h("АГЕНТ-ЗАПУСКИ") + for p in JU.PASSES: + v = OUT / f"aj-{p}-votes" + t = OUT / f"aj-{p}-tasks" + print(f" {p:8s} заданий {len(list(t.glob('*.txt'))) if t.exists() else 0} · " + f"голосов принято {len(list(v.glob('*.json'))) if v.exists() else 0}") + + +def main() -> None: + print("ИТОГ ПАКА «СИЛЬНЫЙ ТИР» — ВСЕ НЕСУЩИЕ ЧИСЛА ИЗ СЫРЬЯ ($0)") + material() + screen() + economics() + for p in JU.PASSES: + try: + judged(p) + except Exception as e: # noqa: BLE001 + print(f"\n═══ СУДЕЙСТВО {p} ═══\n не считается: {type(e).__name__}: {e}") + positional() + money_two_ways() + agent_runs() + print("\nВСЁ ВЫШЕ ПЕРЕ-СНЯТО ИЗ СЫРЬЯ.") + + +if __name__ == "__main__": + main() diff --git a/eval/editor_tier/judge.py b/eval/editor_tier/judge.py index d4279269..f0384021 100644 --- a/eval/editor_tier/judge.py +++ b/eval/editor_tier/judge.py @@ -15,6 +15,7 @@ """ from __future__ import annotations +import hashlib import json import sys from pathlib import Path @@ -47,7 +48,8 @@ PASSES = ("tier", "border") def arm_map(pass_: str) -> dict[str, str]: if pass_ == "tier": - return {"R0": PAN.BATTLE, **{f"T{i}": m for i, m in enumerate(PAN.survivors(), start=1)}} + return {"R0": PAN.BATTLE, **{f"T{i}": m for i, m in enumerate(PAN.survivors(), start=1)}, + "F": PAN.ANCHOR} # голый якорный черновик — база позитивного контроля return {"R0": PAN.BATTLE, "R2": PAN.BORDER} @@ -58,8 +60,13 @@ def arms(pass_: str) -> tuple[str, ...]: def family(pass_: str) -> list[tuple[str, str, str]]: """Семейство объявляется ДО выпуска и впечатывается в ключ.""" if pass_ == "tier": - return [(a, "R0", f"редактор сильного тира {a} против боевого над ТОЙ ЖЕ базой") - for a in arms(pass_) if a != "R0"] + fam = [(a, "R0", f"редактор сильного тира {a} против боевого над ТОЙ ЖЕ базой") + for a in arms(pass_) if a not in ("R0", "F")] + # ⚠ ПОЗИТИВНЫЙ КОНТРОЛЬ, обещанный планом и пропущенный первой редакцией: покупает ли + # боевой редактор что-нибудь поверх голого черновика. Без него проход не отличает + # «армы равны» от «судья ничего не различает». + fam.append(("R0", "F", "КОНТРОЛЬ: покупает ли боевой редактор поверх якорного черновика")) + return fam return [("R2", "R0", "пограничный glm-5 против боевого, n удвоено до 32")] @@ -78,8 +85,14 @@ def text_of(pass_: str): # единица новая → тексты фазы B; единица эксп-22 → его же клетки if u["uid"] in new_uids: return PAN.text_b(arm, u["uid"]) - return P22.edit_text(PAN.BATTLE if arm == "R0" else PAN.BORDER, u["uid"]) \ - if arm == "R0" else _ed22(u["uid"]) + # ⚠ R0 — боевой редактор поверх ЯКОРНОГО черновика. В именовании эксп-22 арм + # редактуры назван по ЧЕРНОВИКУ-жильцу, а не по редактору: `edit_text("dspro")` дало + # бы редактуру поверх ЧЕРНОВИКА dspro, то есть ДРУГУЮ базу, и контраст сравнивал бы + # армы над разными основаниями. Поймано приёмкой. Направление смещения НЕ замерено — + # прежний комментарий утверждал «против R2» без замера, утверждение снято. + return P22.edit_text(PAN.ANCHOR, u["uid"]) if arm == "R0" else _ed22(u["uid"]) + if arm == "F": + return PAN.P22.draft_text(PAN.ANCHOR, u["uid"]) return PAN.text_c(arm, u["uid"]) return hook @@ -103,17 +116,67 @@ def floor_of(pass_: str): def decoy_base(slots: dict) -> str: - for a in ("R0", "T1", "R2"): - if a in slots and (slots[a] or "").strip(): - return a - return next(iter(slots)) + """⚠ Возвращать ТЕКСТ, а не имя арма. Первая редакция возвращала имя — риг сажал порчу в + строку «R0», получал 0 замен при пороге 2 и МОЛЧА не создавал декой: ноль контролей в обоих + проходах. Это ровно тот отказ, которым эксп-22 потерял проход en, и он воспроизвёлся здесь. + + ⚠ ДОНОР ПОРЧИ УРАВНОВЕШЕН ПО АРМАМ. Вторая редакция брала R0 всегда (замер: 16/16 и 32/32), а + R0 стоит вторым армом в КАЖДОМ контрасте обоих проходов: если присутствие испорченного близнеца + вообще двигает оценку донора, оно двигает её в одну сторону во всех контрастах сразу, и в + `border` — где контраст один — опровергнуть это нечем. Донор раздаётся РОВНО: единицы + прохода нумеруются по хешу, донор берётся по остатку номера — судье не предсказуем, по проходу + поделён поровну (чистый хеш давал 22/10 на 32 единицах, перекос остался бы). Принятый проход + `tier` отсужен ДО этой правки, его декой во всех 16 единицах взят из R0 — объявленное + ограничение, см. отчёт §12. + """ + battle = [a for a in ("R0", "T1", "T2", "T3", "R2") if (slots.get(a) or "").strip()] + if not battle: + return next(t for t in slots.values() if (t or "").strip()) + rank = _DONOR_RANK.get(hashlib.sha1(slots[battle[0]].encode("utf-8")).hexdigest(), 0) # noqa: S324,E501 + return slots[battle[rank % len(battle)]] -def setup(pass_: str) -> None: +_DONOR_RANK: dict[str, int] = {} + + +def _rank_units(pass_: str) -> None: + """Номер единицы для ровной раздачи донора. Ключ — хеш текста ПЕРВОГО боевого арма: он же + считается в `decoy_base`, куда номер единицы не передаётся. Порядок — по хешу, а не по главе: + соседние главы не должны систематически получать одного донора.""" + _DONOR_RANK.clear() + hook = text_of(pass_) + keyed = [] + for u in units(pass_): + for a in ("R0", "T1", "T2", "T3", "R2"): + t = hook(a, u) or "" + if t.strip(): + keyed.append(hashlib.sha1(t.encode("utf-8")).hexdigest()) # noqa: S324 + break + for i, k in enumerate(sorted(keyed)): + _DONOR_RANK[k] = i + + +def setup(pass_: str, run: str = "") -> None: + """`run` — имя ОТДЕЛЬНОГО прогона того же прохода (репликация). Пустое = боевой прогон. + + ⚠ Заведено поправкой фазы Д (чек-лист приёмки №16, п.9). Репликацию гоняли, копируя каталоги + руками: имя каталога выводилось только из `PASS`, поэтому разбор шёл под боевым именем и + проставил голосам репликации имена судей БОЕВОГО жребия — то есть ровно то, что репликация + и меняет. Здесь у прогона свои каталоги и своя карта судей, а ключ/соль/метки остаются + общими (иначе это была бы не репликация, а другой замер). + """ + _rank_units(pass_) AJ.configure(OUT=OUT, KEYS=KEYS, PASS=pass_, ARMS=arms(pass_), ARMS_2B=(), FAMILY=family(pass_), TEXT_HOOK=text_of(pass_), UNITS_HOOK=lambda: units(pass_), FLOOR_HOOK=floor_of(pass_), - DECOY_HOOK=decoy_base, SRC_LANG="китайском") + DECOY_HOOK=decoy_base, SRC_LANG="китайском", + DIRS_HOOK=None, JUDGES_HOOK=None) + if run: + jmap = json.loads((OUT / f"{run}-JUDGES.json").read_text(encoding="utf-8")) + AJ.configure( + DIRS_HOOK=lambda: (OUT / f"aj-{pass_}-tasks", OUT / f"{run}-answers", + OUT / f"{run}-votes"), + JUDGES_HOOK=lambda tok, _mtime: jmap.get(tok, "?")) KEYS.mkdir(parents=True, exist_ok=True) @@ -161,9 +224,24 @@ if __name__ == "__main__": elif a[0] == "--emit": setup(a[1]) AJ.emit() + elif a[0] == "--run": + # Разбор/счёт ОТДЕЛЬНОГО прогона: --run <прогон> <проход> [--ingest|--score|--controls] + setup(a[2], run=a[1]) + what = a[3] if len(a) > 3 else "--score" + if what == "--ingest": + AJ.ingest() + elif what == "--controls": + AJ.controls() + else: + AJ.score() elif a[0] == "--ingest": setup(a[1]) AJ.ingest() + # ⚠ Раздача судей персистируется ЗДЕСЬ. `log_run` был написан и ни разу не вызван: во всех + # голосах пака стоит `judge='?'`, согласие судей посчитать нечем, а «агент-запусков 38» — + # счёт по памяти. Токены сессии передаются списком: --ingest <проход> <ток> <ток> … + if len(a) > 2: + print(f"запуск #{log_run(a[1], a[2:], note='судейская сессия')} записан") elif a[0] == "--score": setup(a[1]) AJ.controls() diff --git a/eval/editor_tier/panel.py b/eval/editor_tier/panel.py index 6207f1a0..8ab2c4c2 100644 --- a/eval/editor_tier/panel.py +++ b/eval/editor_tier/panel.py @@ -42,6 +42,7 @@ PR = _load("prompts22", TP / "prompts.py") P22 = _load("panel22", TP / "panel.py") MAT = _load("material_et", ZONE / "material.py") SCR = _load("screen_et", ZONE / "screen.py") +import bakeoff as BO # noqa: E402 MONEY = SCR.MONEY ROSTER = SCR.ROSTER @@ -90,6 +91,13 @@ def cmd_b_run() -> None: if not d.strip(): print(f" {u['uid']}: черновика нет — редактуры пропущены") continue + # ⚠ ГЕЙТ БРАКА ЧЕРНОВИКА БЫЛ НАПИСАН И НЕ ПОДКЛЮЧЁН. Фаза проверяла только «строка пуста», + # поэтому эхо-мина прошла на боевую базу: `63ab55ac5c` — 1798 ханьцзы и НОЛЬ кириллицы при + # `finish=stop`. Отчёт из-за этого объявлял класс «не ловится ни одним гейтом» — неверно: + # `draft_reject_reason` его ловит, звать было некому. Поймано аудитом закрытия заказа. + if (why := BO.draft_reject_reason(d)): + print(f" {u['uid']}: черновик забракован ({why}) — редактуры НЕ покупаются") + continue law = PR.law_block(PR.terms_in(u["source"])) for m in (BATTLE, BORDER): te = tag_b("edit", m, u["uid"]) @@ -180,7 +188,15 @@ def cmd_floor_run() -> None: def floor_pair(uid: str): - """(генерация 1, генерация 2) самого дешёвого арма панели.""" + """(генерация 1, генерация 2) самого дешёвого арма панели. + + ⚠ ДЕФЕКТ, ОБЪЯВЛЕННЫЙ И НЕ ПОЧИНЕННЫЙ В ЭТОМ ПАКЕ: первая половина пары — это САМА боевая + клетка `T1`, а не отдельная генерация. Следствие ровно одно, и оно не в числах пола (пол + считается по РАЗНОСТИ двух генераций и остаётся честным), а в контроле «БЛИЗНЕЦ»: он ловит + побайтные дубли, и здесь единственным дублем оказывается пара `CTRLfloorA`≡`T1` в 16/16 — + то есть контроль сравнивает `T1` с самим собой и ничего не проверяет. Чинить надо ПОКУПКОЙ + отдельной первой генерации, поэтому в этом паке дефект только объявлен (отчёт §12). + """ import json # noqa: PLC0415 a = text_c("T1", uid) f = MONEY.OUT / f"{tag_floor(uid, 2)}.json" diff --git a/eval/editor_tier/solscore.py b/eval/editor_tier/solscore.py new file mode 100644 index 00000000..d4b55aee --- /dev/null +++ b/eval/editor_tier/solscore.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +"""ПЕРЕ-СЧЁТ ГОЛОСОВ ВНЕШНЕГО СУДЬИ (не-Claude) ПО ПРОХОДУ border. $0. + +Судья другой семьи отсуживал ТЕ ЖЕ файлы заданий, что и агенты-судьи пака (`aj-border-tasks/`), и +клал ответы в `sol-border/`. Поэтому его счёт сопоставим с моим текст-в-текст, а не «другой замер». + +⚠ Разбор здесь СВОЙ, а не `absjudge.ingest`: тот пишет в боевой каталог голосов и затёр бы прогон +пака. Правила приёмки ответа те же — каждая ненулевая ось названа и подпёрта цитатой. + +Запуск: eval/.venv/bin/python eval/editor_tier/solscore.py +""" +from __future__ import annotations + +import json +import math +import random +import re +import statistics as st +import sys +from pathlib import Path + +OUT = Path.home() / "books" / "editor-tier" +# Проход задаётся аргументом: `border` (исполнен 09.08) и `tier` (заказан фазой Д, п.Д2). +PASS = "border" +KEY = OUT / "blind-keys" / f"{PASS}-KEY.json" +ANSW = OUT / f"sol-{PASS}" +AXES = ("ВЕРНОСТЬ", "ТЕРМИН", "ЯЗЫК", "ФОРМА") +SEED = 20260809 + + +def votes() -> tuple[dict, list]: + key = json.loads(KEY.read_text(encoding="utf-8")) + ok, bad = {}, [] + for f in sorted(ANSW.glob("*.txt")): + k = key.get(f.stem) + if not k: + bad.append((f.stem, "токена нет в ключе")) + continue + txt, unit, fail = f.read_text(encoding="utf-8"), {}, None + for lab, meta in k.items(): + ax = {} + for a in AXES: + m = re.search(rf"^{lab}-{a}[ \t]*:[ \t]*(\d+)[ \t]*$", txt, re.M) + if m: + ax[a] = int(m.group(1)) + if len(ax) != len(AXES): + fail = f"не разобран блок {lab}" + break + wm = re.search(rf"^{lab}-ПОЧЕМУ[ \t]*:[ \t]*(.*?)(?=^\S+-(?:{'|'.join(AXES)}|ПОЧЕМУ)" + rf"[ \t]*:|\Z)", txt, re.M | re.S) + why = (wm.group(1) if wm else "").strip() + miss = [a for a in AXES if ax[a] and not re.search(rf"(? float: + """Точный при малом n, иначе сид-Монте-Карло: воспроизводимость важнее последней цифры.""" + rnd, s, hits, trials = random.Random(SEED), sum(m), 0, 200_000 # noqa: S311 + for _ in range(trials): + if abs(sum(x * rnd.choice((1, -1)) for x in m)) >= abs(s) - 1e-12: + hits += 1 + return (hits + 1) / (trials + 1) + + +def main() -> int: + global PASS, KEY, ANSW # noqa: PLW0603 + args = [a for a in sys.argv[1:] if not a.startswith("-")] + if args: + PASS = args[0] + KEY, ANSW = OUT / "blind-keys" / f"{PASS}-KEY.json", OUT / f"sol-{PASS}" + print(f"проход {PASS} · ключ {KEY.name} · ответы {ANSW}") + if not ANSW.exists() or not list(ANSW.glob("*.txt")): + print(f"ответов внешнего судьи нет в {ANSW}") + return 1 + ok, bad = votes() + print(f"принято единиц {len(ok)} · отвергнуто {len(bad)}") + for t, r in bad: + print(f" ⛔ {t}: {r}") + dec = [u[u["CTRLdecoy"]["decoy_from"]]["errors"] - u["CTRLdecoy"]["errors"] for u in ok.values() + if "CTRLdecoy" in u and u["CTRLdecoy"].get("decoy_from") in u] + flo = [u["CTRLfloorA"]["errors"] - u["CTRLfloorB"]["errors"] for u in ok.values() + if "CTRLfloorA" in u and "CTRLfloorB" in u] + # Семейство прохода: у `border` один контраст, у `tier` — три плюс позитивный контроль. + fam = ([("R2", "R0")] if PASS == "border" + else [("T1", "R0"), ("T2", "R0"), ("T3", "R0"), ("R0", "F")]) + pairs = [(a, b, [u[b]["errors"] - u[a]["errors"] for u in ok.values() if a in u and b in u]) + for a, b in fam] + pairs = [(a, b, m) for a, b, m in pairs if len(m) >= 3] + if not (dec and flo and pairs): + print("контролей или боевых армов не хватает") + return 1 + caught = sum(1 for x in dec if x < 0) + thr = 2.8 * st.pstdev(flo) / math.sqrt(len(flo)) + print(f"\n ДЕКОЙ {st.mean(dec):+.2f} · поймано {caught}/{len(dec)}" + + ("" if caught == len(dec) else " ⛔ сессия внешнего судьи аннулируется")) + print(f" ПОЛ {st.mean(flo):+.2f} · n={len(flo)} sd={st.pstdev(flo):.2f} → ПОРОГ {thr:.2f}") + for a, b, m in pairs: + print(f" {a} vs {b} n={len(m)} {st.mean(m):+.2f} p={sign_p(m):.4f} " + f"{'РАЗЛИЧИМ' if abs(st.mean(m)) > thr else 'ниже порога различимости'}") + battle = {x for a, b, _ in pairs for x in (a, b)} + lv = [r["errors"] for u in ok.values() for a, r in u.items() if a in battle] + print(f" уровень (медиана боевых клеток) {st.median(lv):.1f}") + return 0 if caught == len(dec) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/eval/editor_tier/verify23.py b/eval/editor_tier/verify23.py new file mode 100644 index 00000000..f9070d93 --- /dev/null +++ b/eval/editor_tier/verify23.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +"""ВЕРИФИКАТОР ЧИСЕЛ ОТЧЁТА ПАКА. $0. Ненулевой код возврата = отчёт не сдаётся. + +`itog23.py` пере-считывает числа из сырья; этот файл сверяет их с ТЕКСТОМ отчёта. Разделение +нужно потому, что «число посчиталось» и «в отчёте напечатано именно оно» — разные утверждения. + +⚠ ЧЕГО ЭТОТ ГЕЙТ НЕ ДЕЛАЕТ. Он сторожит, что число ПРИСУТСТВУЕТ в тексте, а не что предложение +вокруг числа истинно. Приёмка эксп-22 нашла ровно этот класс: «все перевесы выше порога 2.47» при +всех сходящихся числах. Пороговые и сравнительные утверждения проверяются только чтением; за +соответствие ЗАКАЗУ отвечает отдельный инструмент `eval/conformance.py`. +""" +from __future__ import annotations + +import json +import re +import statistics as sst +import sys +from pathlib import Path + +REPO = Path("/home/ubuntu/projects/textmachine") +ZONE = Path(__file__).resolve().parent +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 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 + + +IT = _load("itog23", ZONE / "itog23.py") +JU, PAN, SCR, MONEY, MAT = IT.JU, IT.PAN, IT.SCR, IT.MONEY, IT.MAT + +REPORT = REPO / "docs" / "experiments" / "23-editor-tier.md" +BAD = 0 + + +def ck(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 _norm(x: str) -> str: + """Пробелы не значимы; типографский минус U+2212 и ASCII-дефис — один знак.""" + return re.sub(r"\s+", "", x).replace("−", "-").replace("–", "-") + + +def has(text: str, s: str) -> bool: + return _norm(s) in _norm(text) + + +def main() -> int: + if not REPORT.exists(): + print("отчёта нет") + return 1 + t = REPORT.read_text(encoding="utf-8") + + print("=== МАТЕРИАЛ ===") + new = MAT.units_new() + ck(f"новых единиц {len(new)}", has(t, f"{len(new)} нов") or has(t, f"{len(new)} НОВ")) + ck("главы новых единиц напечатаны", + all(has(t, str(c)) for c in sorted({u["chapter"] for u in new})[:3])) + + print("\n=== ФАЗА A ===") + for m, r in SCR.result().items(): + ck(f"{m}: вердикт {r['verdict']} назван", has(t, m)) + if r["price_editor"]: + ck(f"{m}: цена редактора {r['price_editor']:.5f}", has(t, f"{r['price_editor']:.5f}")) + ck("оба вердикта скрина различены (пре-рег и полный критерий эксп-22)", + has(t, "пре-рег") and has(t, "переводческ")) + + print("\n=== ЦЕНА ===") + us = JU.P22.M.units_zh() + T22 = JU.P22.OUT + + def cost(f: Path) -> float: + return json.loads(f.read_text(encoding="utf-8")).get("cost_usd", 0.0) if f.exists() else 0.0 + + base = [cost(T22 / f"{JU.P22.tag_draft(PAN.ANCHOR, u['uid'])}.json") for u in us] + # ⚠ R0 сторожится наравне с панелью. Прежняя редакция шла по `survivors()` = T1..T3, и строка + # боевого редактора — та, против которой читается ВЕСЬ вывод о цене, — не проверялась ничем. + arms = [("R0", [cost(T22 / f"{JU.P22.tag_edit(PAN.ANCHOR, u['uid'])}.json") for u in us])] + arms += [(f"T{i}", [cost(MONEY.OUT / f"{PAN.tag_c(m, u['uid'])}.json") for u in us]) + for i, m in enumerate(PAN.survivors(), start=1)] + for name, cs in arms: + nz = [c for c in cs if c > 0] + if not nz: + continue + ck(f"{name}: p50 редактуры {sst.median(nz):.5f}", has(t, f"{sst.median(nz):.5f}")) + link = [c + b for c, b in zip(cs, base, strict=True) if c > 0 and b > 0] + if link: + ck(f"{name}: книга по СВЯЗКЕ ${sst.median(link) * 1500:.2f}", + has(t, f"{sst.median(link) * 1500:.2f}")) + + print("\n=== СВЕЖЕСТЬ РАЗБОРА ===") + # ⚠ ГОЛОС СТАРШЕ СВОЕГО ОТВЕТА = РАЗБОР ГНАЛСЯ В СЕРЕДИНЕ ГОНКИ. Поймано аудитом: два круга + # досуживания писали в ОДНИ И ТЕ ЖЕ файлы ответов, `--ingest` прошёл между ними, и 5 единиц из + # 32 остались с голосами от одного круга при ответах от другого. Числа при этом пере-считывались + # из голосов и сходились с отчётом — то есть ни один прежний гейт этого не видел. + for pass_ in JU.PASSES: + votes, answ = MONEY.OUT / f"aj-{pass_}-votes", MONEY.OUT / f"aj-{pass_}" + if not votes.exists() or not answ.exists(): + continue + stale = [a.stem for a in answ.glob("*.txt") + if (votes / f"{a.stem}.json").exists() + and a.stat().st_mtime > (votes / f"{a.stem}.json").stat().st_mtime + 1] + ck(f"{pass_}: голоса не старше ответов судей", not stale, + f"устарели {len(stale)}: {' '.join(sorted(stale)[:6])}" if stale else "") + + print("\n=== СУДЕЙСТВО ===") + for pass_ in JU.PASSES: + v = MONEY.OUT / f"aj-{pass_}-votes" + # ⚠ ПРОХОД БЕЗ ГОЛОСОВ РОНЯЕТ ГЕЙТ. Прежняя редакция делала `continue`, и гейт печатал + # «ВСЕ ЧИСЛА ОТЧЁТА СХОДЯТСЯ» с кодом 0, не проверив по проходу НИ ОДНОГО числа, — при + # том что отчёт печатал по нему несущий вывод. Ровно этим прошёл аннулированный border. + if not v.exists() or not list(v.glob("*.json")): + ck(f"{pass_}: голоса прохода на диске", False, "нет ни одного — числа не пере-снимаемы") + continue + JU.setup(pass_) + import absjudge as AJ # noqa: PLC0415 + data = AJ._by_unit() # noqa: SLF001 + ck(f"{pass_}: единиц отсужено {len(data)}", len(data) >= 3, f"{len(data)}") + for a, b, _w in JU.family(pass_): + m = AJ._margins(a, b) # noqa: SLF001 + if len(m) < 3: + continue + ck(f"{pass_}: контраст {a} vs {b} = {sst.mean(m):+.2f} напечатан", + has(t, f"{sst.mean(m):+.2f}") or has(t, f"{sst.mean(m):.2f}")) + lo, hi = AJ.BO.boot_ci(m) + ck(f"{pass_}: ДИ {a} vs {b} [{lo:+.2f}, {hi:+.2f}] напечатан", + has(t, f"[{lo:+.2f},{hi:+.2f}]")) + dec = [d[d["CTRLdecoy"].get("decoy_from", "")]["errors"] - d["CTRLdecoy"]["errors"] + for d in data.values() + if "CTRLdecoy" in d and d["CTRLdecoy"].get("decoy_from", "") in d] + # Декой — условие осмысленности пака по его же §0, поэтому сторожится ЧИСЛОМ, а не словом. + ck(f"{pass_}: ДЕКОЙ есть в каждой единице", len(dec) == len(data), f"{len(dec)}/{len(data)}") + if dec: + ck(f"{pass_}: декой пойман {sum(1 for x in dec if x < 0)}/{len(dec)}", + all(x < 0 for x in dec)) + ck(f"{pass_}: величина декоя {sst.mean(dec):+.2f} напечатана", + has(t, f"{sst.mean(dec):+.2f}")) + flo = [d["CTRLfloorA"]["errors"] - d["CTRLfloorB"]["errors"] + for d in data.values() if "CTRLfloorA" in d and "CTRLfloorB" in d] + if flo: + sd = sst.pstdev(flo) + thr = 2.8 * sd / (len(flo) ** 0.5) + # Порог различимости — решающее правило пака: печатать его надо ИМЕННО из своего пола. + ck(f"{pass_}: ПОРОГ {thr:.2f} напечатан", has(t, f"{thr:.2f}")) + ck(f"{pass_}: пол {sst.mean(flo):+.2f} напечатан", has(t, f"{sst.mean(flo):+.2f}")) + # ⚠ НАКЛОН СТОРОЖИТСЯ ЧИСЛОМ (поправка фазы Д, чек-лист приёмки №16 п.6). В теле стояло + # +0.646, а текущее сырьё даёт +0.708: величина, на которую делается ПОПРАВКА боевого + # контраста, печаталась не из тех голосов, из которых считается сам контраст. Ни один + # прежний гейт наклон не трогал — сторожились только перевес, ДИ, пол, порог и декой. + xs, ys = [], [] + for d in data.values(): + mx = sst.mean([r["slot"] for r in d.values()]) + my = sst.mean([r["errors"] for r in d.values()]) + for r in d.values(): + xs.append(r["slot"] - mx) + ys.append(r["errors"] - my) + if sum(x * x for x in xs) > 0: + slope = sum(a * b for a, b in zip(xs, ys, strict=True)) / sum(x * x for x in xs) + ck(f"{pass_}: НАКЛОН {slope:+.3f} напечатан", has(t, f"{slope:+.3f}"), + f"n={len(xs)}") + + print("\n=== ДЕНЬГИ ===") + tot = 0.0 + for ph in MONEY.TAGS: + sp = MONEY.Guarded(ph).spent() + tot += sp + if sp <= 0: + continue + ck(f"{ph}: расход ${sp:.6f} напечатан", has(t, f"{sp:.6f}")) + if sp > MONEY.CEILINGS[ph] + 1e-9: + ck(f"{ph}: ПРОБОЙ объявлен", has(t, "ПРОБОЙ") or has(t, "пробой")) + ck(f"расход пака ${tot:.6f} напечатан", has(t, f"{tot:.6f}")) + ck("подъём потолка фазы C из резерва объявлен", has(t, "2.30") and has(t, "резерв")) + + print(f"\n{'ВСЕ ЧИСЛА ОТЧЁТА СХОДЯТСЯ' if not BAD else f'РАСХОЖДЕНИЙ: {BAD} — НЕ СДАЁТСЯ'}") + return BAD + + +if __name__ == "__main__": + sys.exit(1 if main() else 0) diff --git a/eval/role_topology/absjudge.py b/eval/role_topology/absjudge.py index 362c9a1e..bd453271 100644 --- a/eval/role_topology/absjudge.py +++ b/eval/role_topology/absjudge.py @@ -60,7 +60,15 @@ KEYS = OUT / "blind-keys" def _dirs(): """Каталоги прохода. ВТОРОЙ проход обязан лежать отдельно: он и есть репликация, - и затирание первого превратило бы её в пере-запись.""" + и затирание первого превратило бы её в пере-запись. + + ⚠ `DIRS_HOOK` заведён приёмкой №16 (чек-лист фазы Д, п.9). Репликацию пака 23 гоняли, + КОПИРУЯ каталоги руками, потому что имя каталога выводилось только из `PASS`: разбор шёл под + `PASS='border'` и штамповал голоса репликации именами судей БОЕВОГО прогона. Крючок даёт + прогону собственные каталоги, не трогая имя прохода (а с ним — ключ, соль и метки). + """ + if DIRS_HOOK is not None: + return DIRS_HOOK() return (OUT / f"aj-{PASS}-tasks", OUT / f"aj-{PASS}", OUT / f"aj-{PASS}-votes") # ⚠ СОЛЬ ЖИВЁТ ВНЕ РЕПОЗИТОРИЯ. Адверсариальное ревью 07.08 восстановило 138 меток из 138, не # открывая ключ: `layout()` — чистая функция от соли и `uid = sha1(source)`, а источник лежит В @@ -100,6 +108,8 @@ UNITS_HOOK = None # callable() -> list[dict] FLOOR_HOOK = None # callable(unit) -> tuple[str, str] | None — пара повторов одного арма DECOY_HOOK = None # callable(slots: dict) -> str — чистый текст, в который сажается порча SRC_LANG = "китайском" # как называется язык исходника в задании судье +DIRS_HOOK = None # callable() -> (tasks, answers, votes) — свои каталоги прогона +JUDGES_HOOK = None # callable(token, answer_mtime) -> str — кто судил ЭТОТ ответ def configure(**kw) -> None: @@ -141,9 +151,15 @@ FORMAT_HEAD = """Оцени КАЖДЫЙ вариант ОТДЕЛЬНО про Ответ ЗАПИШИ ФАЙЛОМ в {path} — ровно в таком виде и ничем больше, блок на каждую метку: {skeleton} -ПОЧЕМУ обязательно для КАЖДОЙ ненулевой оси: короткая ЦИТАТА из текста через « | ». -Число без цитаты не принимается и на диск не попадает: именно из-за отсутствия цитат показания -прошлых судей оказались непроверяемыми. Ноль по оси обоснования не требует. +ПОЧЕМУ обязательно для КАЖДОЙ ненулевой оси, и ось надо НАЗВАТЬ. Единственный принимаемый вид — +имя оси заглавными, двоеточие, цитаты в «ёлочках» через « | »; перечисляй только ненулевые оси: + + Т1-ПОЧЕМУ: ВЕРНОСТЬ: «цитата» | «ещё цитата». ЯЗЫК: «цитата». ФОРМА: «цитата». + +Разбор ищет имя оси буквально. Цитаты без имени оси не принимаются, и единица выбрасывается +ЦЕЛИКОМ — вся работа по ней пропадает; так уже терялась треть пачки в трёх прогонах подряд, потому +что это требование стояло в приёмке, но не в задании. Ноль по оси обоснования не требует; нечем +подпереть число цитатой — ставь ноль, а не число без цитаты. """ @@ -293,8 +309,19 @@ def ingest() -> None: # ⚠ ИДЕНТИЧНОСТЬ СУДЬИ. Первая версия рига её не персистировала вовсе, и по диску нельзя было # ни посчитать согласие судей, ни разложить вердикты по семействам (требование промта стр.79). # Поймано адверсариальным ревью 07.08. Карта раздачи лежит рядом с ключом и судье не даётся. + # ⚠ ПЛОСКАЯ КАРТА ПО ИМЕНИ ПРОХОДА НЕ РАЗЛИЧАЕТ ПРОГОНЫ (приёмка №16, чек-лист Д8 п.9). + # Репликация пака 23 судила ТЕ ЖЕ 32 токена вторым жребием судей, а карта лежит одна на + # проход — все 32 голоса репликации получили имена судей БОЕВОГО прогона. Перевесы от + # этого не двигаются (имя судьи в разность не входит), но «согласие судей» и разложение + # дисперсии по сессиям считались бы по ложной раскладке. Поэтому имя берётся крючком, + # который знает про ПРОГОН и про время ответа; плоская карта осталась дефолтом эксп-21/22. jf = KEYS / f"{PASS}-JUDGES.json" judges = json.loads(jf.read_text(encoding="utf-8")) if jf.exists() else {} + + def judge_of(stem: str, path: Path) -> str: + if JUDGES_HOOK is not None: + return JUDGES_HOOK(stem, path.stat().st_mtime) + return judges.get(stem, "?") VOTES.mkdir(parents=True, exist_ok=True) ok = bad = 0 reasons: dict[str, int] = {} @@ -352,7 +379,7 @@ def ingest() -> None: fail(f"блок {lab} задан дважды: {'/'.join(dup)}", f.stem) break rows[lab] = dict(**meta, **ax, why=why, errors=sum(ax.values()), - slot=int(lab[1:]), judge=judges.get(f.stem, "?")) + slot=int(lab[1:]), judge=judge_of(f.stem, f)) else: (VOTES / f"{f.stem}.json").write_text( json.dumps(rows, ensure_ascii=False), encoding="utf-8") @@ -363,7 +390,10 @@ def ingest() -> None: def _by_unit() -> dict[str, dict[str, dict]]: VOTES = _dirs()[2] out = {} - for f in VOTES.glob("*.json"): + # ⚠ sorted, а не голый glob: `boot_ci` ресемплит по ПОЗИЦИЯМ списка, поэтому от порядка обхода + # каталога зависели границы доверительных интервалов — точечные оценки и p воспроизводились, а + # интервалы нет. Поймано приёмкой пака 23. + for f in sorted(VOTES.glob("*.json")): rows = json.loads(f.read_text(encoding="utf-8")) uid = next(iter(rows.values()))["uid"] out[uid] = {r["arm"]: r for r in rows.values()} diff --git a/eval/role_topology/bakeoff.py b/eval/role_topology/bakeoff.py index 3b7756b7..a1738e45 100644 --- a/eval/role_topology/bakeoff.py +++ b/eval/role_topology/bakeoff.py @@ -482,19 +482,50 @@ def boot_ci(xs: list[float], reps: int = 5000, alpha: float = 0.05) -> tuple[flo def sign_perm_p(xs: list[float]) -> float: - """Точный двусторонний знаковый перестановочный тест: перебираются ВСЕ 2^n расстановок знаков. + """ТОЧНЫЙ двусторонний знаковый перестановочный тест. $0. Заведён 07.08 по адверсариальному ревью. Bootstrap-ДИ отвечает на вопрос «где лежит среднее», а не «отличимо ли оно от нуля при этом n», и на 15 единицах даёт заметно более узкий интервал, чем точный тест. Здесь считается p, к которому применима поправка Holm по семейству контрастов. """ n = len(xs) - if n == 0 or n > 20: + if n == 0: return float("nan") obs = abs(sum(xs)) - hits = sum(1 for mask in range(1 << n) - if abs(sum(x if mask >> i & 1 else -x for i, x in enumerate(xs))) >= obs - 1e-12) - return hits / (1 << n) + if n <= 20: + hits = sum(1 for mask in range(1 << n) + if abs(sum(x if mask >> i & 1 else -x for i, x in enumerate(xs))) >= obs - 1e-12) + return hits / (1 << n) + # ⚠ n > 20: ПОЛНЫЙ ПЕРЕБОР 2^n НЕПОДЪЁМЕН, НО ТОЧНЫЙ ОТВЕТ ВСЁ РАВНО ЕСТЬ. Первая редакция + # брала здесь Монте-Карло с фиксированным зерном; ревью исполнением показало, что этого не + # нужно: перевесы — ЦЕЛЫЕ числа (разности счётчиков ошибок), поэтому распределение суммы ±xᵢ + # считается динамикой по достижимым суммам за миллисекунды и ТОЧНО. Цена приближения была + # видна в отчёте: одна и та же величина печаталась как 0.1603 и 0.1608 (se Монте-Карло + # ≈0.0008), то есть заявление «числа воспроизводятся до знака» на ней не выполнялось. + # Дробные значения (их у нас нет, но контракт функции шире) уходят на прежний Монте-Карло. + scaled = [x * 2 for x in xs] # полуцелые тоже берём точно + if all(abs(v - round(v)) < 1e-9 for v in scaled): + ints = [abs(int(round(v))) for v in scaled] + target = abs(sum(scaled)) - 1e-9 + # dp[s] = число расстановок знаков, дающих сумму s (сдвиг на total для неотрицательности) + total = sum(ints) + dp = [0] * (2 * total + 1) + dp[total] = 1 + for v in ints: + nxt = [0] * (2 * total + 1) + for s, c in enumerate(dp): + if c: + nxt[s + v] += c + nxt[s - v] += c + dp = nxt + hits = sum(c for s, c in enumerate(dp) if abs(s - total) >= target) + return hits / (1 << len(xs)) + import random # noqa: PLC0415 + rnd = random.Random(20260809) # noqa: S311 + trials = 200_000 + hits = sum(1 for _ in range(trials) + if abs(sum(x if rnd.getrandbits(1) else -x for x in xs)) >= obs - 1e-12) + return (hits + 1) / (trials + 1) def cmd_score() -> None: diff --git a/eval/tenant_panel/bank.py b/eval/tenant_panel/bank.py index 8422aa89..52b7b519 100644 --- a/eval/tenant_panel/bank.py +++ b/eval/tenant_panel/bank.py @@ -139,6 +139,14 @@ def _rx(dst: str) -> str: влияет), объявляется как девиация. Первая версия усекала только слова от 6 знаков, и короткие склоняемые слова канона («род Цинь») не находились в форме «рода Цинь» — метрика покрытия занижалась бы на всех армах одинаково, но занижалась. + + ⚠⚠ ГРАНИЦА СЛОВА В НАЧАЛЕ — поправка фазы Д (адверсариальное ревью 10.08). Без неё усечённый + корень ловился В СЕРЕДИНЕ чужого слова: канон «род Цинь» матчился на «Го-РОд Цинчжоу» и + «Б-РОвь Цинь», канон «род Су» — на «Пилюлю Кровавой Су». Замер: **30 ложных попаданий из + 2113 = 1.4%**; кросс-термовых коллизий (одна форма подходит под ДВА канона) нет ни одной. + Влияние на печатные числа: покрытие всех армов падает на 0.003–0.004 — то есть В ОДНУ сторону + у всех, и ни один относительный вывод эксп-22 не двигается. Числа в отчёте пере-сняты. + `(? str: parts.append(w[:-1] + r"\w*") else: parts.append(w) - return r"\s+".join(parts) + return r"(? None: f"{f'{k}/{s}':>14s}{(k / s if s else 0):>10.3f}") +def battery_canon_f3() -> None: + """Та же батарея по армам Ф3. Прежде §6.2 отчёта считалась ВНЕ дерева: шапка обещала, что + пере-считывается каждое число, а таблица Ф3 под пере-счёт не попадала — клейм был шире гейта.""" + h("Ф3 БАТАРЕЯ И ПОКРЫТИЕ КАНОНА ПО АРМАМ РЕДАКТОРОВ (детерминированно, шума нет)") + import editors as ED # noqa: PLC0415 + zh, cards = M.units_zh(), B.load_cards() + print(f" {'арм':30s}{'ед.':>5s}{'ханьцзы':>9s}{'типогр':>8s}{'потеря':>8s}" + f"{'канон':>14s}{'покрытие':>10s}{'$/ед':>10s}") + pan = ED.panel() + for i, model in enumerate(pan): + arm = f"R{i}" + rows, k, s, costs = [], 0, 0, [] + for u in zh: + t = ED.text_of(arm, u["uid"]) + if not t.strip(): + continue + rows.append(B.run_unit(B.Unit(source=u["source"], draft=t, final=t, cards=cards))) + kk, ss = BANK.coverage(u["source"], t) + k += kk + s += ss + f = OUT / f"{ED.tag(model, u['uid'])}.json" + if f.exists(): + costs.append(json.loads(f.read_text(encoding="utf-8")).get("cost_usd", 0.0)) + if rows: + print(f" {arm + ' ' + model:30s}{len(rows):5d}" + f"{sum(r['cjk_leak']['han_chars'] for r in rows):9d}" + f"{sum(r['typography']['violations'] for r in rows):8d}" + f"{sum(r['numbers']['lost_n'] for r in rows):8d}" + f"{f'{k}/{s}':>14s}{(k / s if s else 0):>10.3f}" + f"{(st.median(costs) if costs else 0):10.5f}") + print(" ⚠ Клетки с ПУСТЫМ содержимым в счёт не идут — именно поэтому у части армов ед. < 16;") + print(" оплаченные пустые клетки перечислены в блоке ДЕНЬГИ.") + + +def _p95(xs: list[float]) -> float: + """p95 по ЕДИНИЦАМ, ближайший ранг. При n=16 это 16-я по возрастанию, то есть максимум: + честнее так и сказать, чем интерполяцией изображать точность, которой при таком n нет.""" + if not xs: + return 0.0 + s = sorted(xs) + return s[min(len(s) - 1, math.ceil(0.95 * len(s)) - 1)] + + def economics() -> None: - h("Ф2 (д) ЦЕНА/ЕДИНИЦА И ПРОЕКЦИЯ НА КНИГУ (1500 единиц)") + h("Ф2 (д) ЦЕНА/ЕДИНИЦА И ПРОЕКЦИЯ НА КНИГУ (1500 единиц) — p50 И p95, как заказано промтом") zh = M.units_zh() - print(f" {'жилец':26s}{'черновик':>11s}{'редактор':>11s}{'связка':>11s}{'книга $':>10s}") + print(f" {'жилец':26s}{'связка p50':>11s}{'связка p95':>11s}" + f"{'книга p50':>11s}{'книга p95':>11s}") for m in P2.tenants(): dc = [P2.draft_cost(m, u["uid"]) for u in zh] ec = [] @@ -123,11 +168,49 @@ def economics() -> None: ec.append(json.loads(f.read_text(encoding="utf-8")).get("cost_usd", 0.0)) if not dc: continue - d50, e50 = st.median(dc), (st.median(ec) if ec else 0.0) - print(f" {m:26s}{d50:11.5f}{e50:11.5f}{d50 + e50:11.5f}{(d50 + e50) * 1500:10.2f}") + # p50/p95 берутся от СВЯЗКИ по единице, а не складываются из отдельных квантилей ролей: + # сумма p95 двух ролей — не p95 суммы, и завышала бы хвост. + pair = [d + e for d, e in zip(dc, ec, strict=False)] if ec else dc + print(f" {m:26s}{st.median(pair):11.5f}{_p95(pair):11.5f}" + f"{st.median(pair) * 1500:11.2f}{_p95(pair) * 1500:11.2f}") print(" ⚠ DeepSeek объявил на прайс-странице 08.08 повышение цен — проекция едет с оговоркой") +def drafts_as_text() -> None: + h("Ф2 (а) ЧЕРНОВИК КАК ТЕКСТ — ОПИСАТЕЛЬНАЯ ОСЬ ЗАКАЗА (поправки не несёт, вердиктов не даёт)") + import statistics as sst # noqa: PLC0415 + for pass_ in ("p2zh", "p2en"): + JU.setup(pass_) + import absjudge as AJ # noqa: PLC0415 + if not AJ._by_unit(): # noqa: SLF001 + continue + fl = {"p2zh": 2.03, "p2en": None}[pass_] + print(f" --- {pass_} --- порог различимости " + f"{('%.2f' % fl) if fl else 'своего пола нет (пол Ф2 снят на zh)'}") + for a, bb, _w in JU.family_desc(pass_): + m = AJ._margins(a, bb) # noqa: SLF001 + if not m: + continue + mean = sst.mean(m) + mark = ("ниже порога различимости" if fl and abs(mean) < fl else + "выше порога" if fl else "") + print(f" {a} vs {bb} n={len(m):2d} {mean:+6.2f} {mark}") + print(" ⚠ Знак ПОЛОЖИТЕЛЬНЫЙ = черновик жильца лучше якорного. Ось описательная: пре-рег Ф2") + print(" сузил решающее семейство до финалов, поэтому Холм по ней НЕ считается.") + + +def positional() -> None: + h("ПОЗИЦИОННЫЙ НАКЛОН — ЗАМЕРЕН И ВЫЧТЕН (требование ЗАКОНа промта)") + for pass_ in ("p2zh", "p2en", "p3", "dva"): + JU.setup(pass_) + import absjudge as AJ # noqa: PLC0415 + if not AJ._by_unit(): # noqa: SLF001 + continue + print(f" --- {pass_} ---") + AJ.position() + print() + + def judged(pass_: str) -> None: h(f"СУДЕЙСТВО, проход {pass_}") JU.setup(pass_) @@ -176,7 +259,10 @@ def money_two_ways() -> None: if empty_paid: print(f" оплачено за ПУСТОЙ выход: {len(empty_paid)} вызовов " f"${sum(c for _n, c in empty_paid):.5f}") - for n, c in sorted(empty_paid, key=lambda x: -x[1])[:5]: + tot_empty = sum(c for _n, c in empty_paid) + print(f" доля пака: {tot_empty / sum(led.values()) * 100:.1f}% " + f"(${tot_empty:.5f} из ${sum(led.values()):.6f})") + for n, c in sorted(empty_paid, key=lambda x: -x[1]): print(f" {n} ${c:.5f}") @@ -198,12 +284,15 @@ def main() -> None: screen() echo_rate() battery_canon() + battery_canon_f3() economics() + drafts_as_text() for p in ("p2zh", "p2en", "p3", "dva"): try: judged(p) except Exception as e: # noqa: BLE001 print(f"\n═══ СУДЕЙСТВО {p} ═══\n не считается: {type(e).__name__}: {e}") + positional() money_two_ways() agent_runs() print("\nВСЁ ВЫШЕ ПЕРЕ-СНЯТО ИЗ СЫРЬЯ. Чисел без пути воспроизведения в отчёте быть не должно.") diff --git a/eval/tenant_panel/judge.py b/eval/tenant_panel/judge.py index d743e983..88aeb0fb 100644 --- a/eval/tenant_panel/judge.py +++ b/eval/tenant_panel/judge.py @@ -79,6 +79,22 @@ def family(pass_: str) -> list[tuple[str, str, str]]: return [("D_", "A", "однопроходка без мандата против связки (сырьё эксп-21)")] +def family_desc(pass_: str) -> list[tuple[str, str, str]]: + """ОПИСАТЕЛЬНОЕ семейство — ось (а) заказа: черновик КАК ТЕКСТ, до редактора. + + Пре-рег Ф2 сузил РЕШАЮЩЕЕ семейство до финалов (решение владельца принимается по тому, что + уедет в книгу), и эти контрасты поправку Холма не несут и вердиктов не определяют. Но + печатать их обязательно: тот же пре-рег требует печатать перевес ниже пола ЧИСЛОМ, а не + замалчивать, а промт заказал эту ось явным пунктом. Молчание о купленном и отсуженном + замере — та самая ловушка «след отчёта», от которой пак и защищается. + """ + if pass_ in ("p2zh", "p2en"): + ts = P2.tenants() + return [(f"D{i}", "D0", f"черновик жильца {i} КАК ТЕКСТ против якорного черновика") + for i in range(1, len(ts))] + return [] + + def arms(pass_: str) -> tuple[str, ...]: return tuple(arm_map(pass_)) @@ -128,7 +144,9 @@ def decoy_base(slots: dict) -> str: def setup(pass_: str) -> None: - AJ.configure(OUT=OUT, KEYS=KEYS, PASS=pass_, ARMS=arms(pass_), ARMS_2B=(), + AJ.configure(DIRS_HOOK=None, JUDGES_HOOK=None, # крючки — глобали ОБЩЕГО рига: + # не сбросишь — соседний пак прочитает чужие каталоги молча (ревью 10.08) + OUT=OUT, KEYS=KEYS, PASS=pass_, ARMS=arms(pass_), ARMS_2B=(), FAMILY=family(pass_), TEXT_HOOK=text_of(pass_), UNITS_HOOK=lambda: units(pass_), FLOOR_HOOK=floor_of(pass_), DECOY_HOOK=decoy_base, diff --git a/eval/tenant_panel/sol.py b/eval/tenant_panel/sol.py index 7d9c85b3..4b45b159 100644 --- a/eval/tenant_panel/sol.py +++ b/eval/tenant_panel/sol.py @@ -82,10 +82,49 @@ def pick_units(a: str, b: str) -> list[dict]: return [u for _g, u in scored[:N_UNITS]] +# ⚠ РАМКА ВНЕШНЕГО СУДЬИ. Sol работает в UI-подписке владельца, а не по API: у него нет ни ключа, +# ни жёсткого формата ответа, ни запрета на веб. Владелец 09.08: «он пипец как любит посмотреть +# туда, куда его не просят». Три риска, каждый обесценивает пакет: узнавание книги вместо сверки с +# исходником (контаминация — тот самый класс, ради которого искали невиданный срез); собственный +# вариант перевода вместо счёта ошибок; разметка и преамбулы, ломающие построчный разбор. +FRAME = """⛔ РАМКА ЗАДАНИЯ — прочитай целиком до того, как начнёшь. + +Это слепой замер, а не консультация. От тебя нужен РОВНО ОДИН артефакт: блок строк с числами. +Материал оплачен и одноразовый; всё, что сверх формата, ломает машинный разбор и обесценивает его. + +ЧЕГО ДЕЛАТЬ НЕЛЬЗЯ: +* НЕ искать ничего вовне этого сообщения — ни в сети, ни в памяти: ни книгу, ни автора, ни главу, + ни чужие переводы. Если ты узнал произведение — не опирайся на это ни одним словом. Единственный + эталон — ИСХОДНИК ниже. Узнавание вместо сверки с исходником делает замер недействительным. +* НЕ предлагать свой перевод, не переписывать, не редактировать и не «улучшать» варианты. +* НЕ гадать, кто автор вариантов — человек, модель, какая именно, какой вариант «основной». + Порядок вариантов перемешан жребием и не значит ничего. +* НЕ оценивать варианты друг через друга. Каждый считается ОТДЕЛЬНО против исходника: вопрос не + «какой лучше», а «сколько ошибок в этом». +* НЕ добавлять заголовки, таблицы, markdown-разметку, жирный шрифт, преамбулы и рекомендации. +* НЕ задавать уточняющих вопросов и не просить контекст: его не будет, отвечай по тому, что дано. +* НЕ менять имена строк ответа и не переставлять их — разбор ищет их буквально, посимвольно.""" + +FORMAT = """ФОРМАТ ОТВЕТА — РОВНО ЭТИ ДЕВЯТЬ СТРОК, КАЖДАЯ С НОВОЙ, БЕЗ РАЗМЕТКИ И БЕЗ ТЕКСТА МЕЖДУ НИМИ: +В1-ВЕРНОСТЬ: <число> +В1-ТЕРМИН: <число> +В1-ЯЗЫК: <число> +В1-ФОРМА: <число> +В2-ВЕРНОСТЬ: <число> +В2-ТЕРМИН: <число> +В2-ЯЗЫК: <число> +В2-ФОРМА: <число> +СТИЛЬ: <В1 или В2 или НИЧЬЯ — где живее и естественнее русская проза> + +Пояснения и разбор можно дать, но ТОЛЬКО ПОСЛЕ этих девяти строк, отдельным абзацем. Ничего перед +ними и ничего между ними. Если по какой-то оси ошибок нет — ставь 0, а не пропуск и не прочерк.""" + + def _packet(src: str, v1: str, v2: str, tok: str) -> str: + axes = J.RUBRIC.split("---", 1)[0].strip() if "---" in J.RUBRIC else J.RUBRIC.strip() return "\n".join([ f"ПАКЕТ {tok}", "", - J.RUBRIC.split("---", 1)[0].strip() if "---" in J.RUBRIC else J.RUBRIC.strip(), "", + FRAME, "", "ЧТО СЧИТАТЬ ОШИБКОЙ — по типам:", axes, "", FORMAT, "", "=" * 60, "ИСХОДНИК:", src, "", "=" * 60, "ВАРИАНТ 1:", v1, "", "=" * 60, "ВАРИАНТ 2:", v2, ""]) @@ -129,6 +168,128 @@ def emit() -> None: print(f"инструкция → {SOL}/ИНСТРУКЦИЯ.md") +# ── ПАКЕТ (б): слепые пары ФИНАЛИСТОВ Ф2/Ф3 ──────────────────────────────────────────────── +# Заказан промтом «по готовности Ф2/Ф3». Смысл иной, чем у пакета (а): (а) арбитрирует спор +# ДВУХ платных судей эксп-21, а (б) выносит наружу решение, которое внутри пака вынесла ОДНА +# модельная семья. Все 40 судейских сессий — один и тот же судья в разных контекстах, и это +# объявлено непереносимым (§11 отчёта). Sol — единственный контур другой семьи. +PAIRS_B = [ + # (проход, арм A, арм B, зачем) + ("p3", "R0", "R2", "редактор: рекомендованный против ПОГРАНИЧНОГО (на нём висит Резерв D39.22)"), + ("p2zh", "E0", "E2", "черновик: финал поверх якоря против финала поверх лучшего соперника"), +] +N_UNITS_B = 3 + + +def _no_openai(models: list[str]) -> None: + """Семейный запрет симметричен (ЗАКОН промта): Sol не судит клетки OpenAI-моделей.""" + import roster as R # noqa: PLC0415 + bad = [m for m in models if "openai.com" in R.CANDIDATES.get(m, ("",))[0]] + if bad: + raise SystemExit(f"семейный запрет: Sol не судит клетки OpenAI — {bad}") + + +def pick_units_b(pass_: str, a: str, b: str) -> list[dict]: + """Единицы, где ВНУТРИСЕМЕЙНЫЙ судья был увереннее всего. + + Для арбитража это правильнее, чем брать спорные: если внешний судья не воспроизводит перевес + там, где свой был максимально уверен, — под вопросом сам перевес, а не его величина. + """ + import judge as JU # noqa: PLC0415 + JU.setup(pass_) + import absjudge as AJ2 # noqa: PLC0415 + per = [] + for uid, d in AJ2._by_unit().items(): # noqa: SLF001 + if a in d and b in d: + per.append((abs(d[b]["errors"] - d[a]["errors"]), uid)) + per.sort(key=lambda x: (-x[0], x[1])) + byid = {u["uid"]: u for u in _units_b()} + return [byid[uid] for _g, uid in per[:N_UNITS_B] if uid in byid] + + +def _units_b() -> list[dict]: + import material as M # noqa: PLC0415 + return M.units_zh() + + +def _text_b(pass_: str, arm: str, uid: str) -> str: + import judge as JU # noqa: PLC0415 + return JU.text_of(pass_)(arm, {"uid": uid}) + + +def emit_b() -> None: + """Выпуск пакета (б). $0: ничего не покупается, только сборка уже купленных клеток.""" + import judge as JU # noqa: PLC0415 + out = MONEY.OUT / "sol-b" + out.mkdir(parents=True, exist_ok=True) + (out / "answers").mkdir(parents=True, exist_ok=True) + key: dict[str, dict] = {} + n = 0 + for pass_, a, b, why in PAIRS_B: + _no_openai([JU.arm_map(pass_).get(a, ""), JU.arm_map(pass_).get(b, "")]) + for u in pick_units_b(pass_, a, b): + ta, tb = _text_b(pass_, a, u["uid"]), _text_b(pass_, b, u["uid"]) + if not ta.strip() or not tb.strip(): + continue + for order in (0, 1): + tok = hashlib.sha1( # noqa: S324 + f"{_salt()}|b|{pass_}|{a}|{b}|{u['uid']}|{order}".encode()).hexdigest()[:10] + v1, v2 = (ta, tb) if order == 0 else (tb, ta) + (out / f"{tok}.txt").write_text(_packet(u["source"], v1, v2, tok), encoding="utf-8") + key[tok] = dict(contrast=f"{a}v{b}", pass_=pass_, why=why, + first=a if order == 0 else b, second=b if order == 0 else a, + uid=u["uid"], order=order, kind="боевой") + n += 1 + # тот же обязательный декой: сессия, не поймавшая посаженную деградацию, аннулируется + u0 = _units_b()[0] + clean = _text_b("p3", "R0", u0["uid"]) + planted, k = AJ._plant(clean) # noqa: SLF001 + if k >= 2: + for order in (0, 1): + tok = hashlib.sha1(f"{_salt()}|b-decoy|{u0['uid']}|{order}".encode()).hexdigest()[:10] # noqa: S324 + v1, v2 = (clean, planted) if order == 0 else (planted, clean) + (out / f"{tok}.txt").write_text(_packet(u0["source"], v1, v2, tok), encoding="utf-8") + key[tok] = dict(contrast="ДЕКОЙ", first="чистый" if order == 0 else "порченый", + second="порченый" if order == 0 else "чистый", uid=u0["uid"], + order=order, kind="контроль-декой", plants=k) + n += 1 + (KEYS / "sol-b-KEY.json").write_text(json.dumps(key, ensure_ascii=False, indent=1), + encoding="utf-8") + (out / "ИНСТРУКЦИЯ.md").write_text(_instr_b(len(key)), encoding="utf-8") + print(f"пакет (б): {n} файлов → {out} · ключ → {KEYS}/sol-b-KEY.json (владельцу НЕ давать)") + + +def _instr_b(n: int) -> str: + return f"""# Пакеты для Sol — ПАКЕТ (б), финалисты эксп-22 + +Всего пакетов: **{n}**. Правила те же, что в пакете (а): один пакет — один НОВЫЙ чат, ответ +копируется дословно в `answers/<тот же токен>.txt`, порядок вариантов внутри пакета не менять, +какая модель какой вариант писала — не сообщать. + +## Чем этот пакет отличается от (а) + +Пакет (а) разрешал спор двух платных судей эксп-21. Здесь спора нет — здесь есть **решение, +которое целиком вынесла ОДНА модельная семья**: все 40 судейских сессий эксп-22 — один и тот же +судья в разных контекстах. Он мог систематически предпочитать один стиль русской прозы другому, +и изнутри пака это неотличимо от истины. Sol — единственный судейский контур другой семьи. + +Поэтому важны именно эти пары: + +* **редактор рекомендованный против пограничного** — на этом контрасте держится вопрос о + Резерве D39.22, и внутри пака он прошёл на грани (перевес 2.12 при пороге прохода 2.47); +* **финал поверх якорного черновика против финала поверх лучшего соперника** — крупнейший + перевес фазы 2. + +Единицы отобраны те, где внутренний судья был увереннее всего: если внешний судья не +воспроизводит перевес там, где свой был максимально уверен, под вопросом сам перевес. + +## Что будет с ответами + +Возврат не блокер: не вернётся — так и будет записано. Вернётся — числа лягут рядом с +внутренними, и расхождение станет находкой, а не поводом переписать вывод. +""" + + def _instr(n: int) -> str: return f"""# Пакеты для Sol — инструкция владельцу @@ -241,5 +402,5 @@ def score() -> None: if __name__ == "__main__": a = sys.argv[1:] or ["--emit"] - fn = {"--emit": emit, "--ingest": ingest, "--score": score}.get(a[0]) + fn = {"--emit": emit, "--emit-b": emit_b, "--ingest": ingest, "--score": score}.get(a[0]) fn() if fn else print(__doc__) diff --git a/eval/tenant_panel/verify22.py b/eval/tenant_panel/verify22.py index 4ee0f80f..4a01d0e6 100644 --- a/eval/tenant_panel/verify22.py +++ b/eval/tenant_panel/verify22.py @@ -8,6 +8,12 @@ Правило: гейт сторожит НЕСУЩИЕ числа — те, на которых стоит решение владельца. Число, которого гейт не проверяет, в ИТОГе стоять не должно. + +⚠ ЧЕГО ЭТОТ ГЕЙТ НЕ ДЕЛАЕТ, чтобы клейм не был шире прибора. Он проверяет, что число ПРИСУТСТВУЕТ +в тексте отчёта, а не что оно стоит в нужном месте и не что предложение вокруг него истинно. +Приёмка нашла ровно этот класс: «все перевесы выше порога 2.47» — все числа напечатаны и сходятся, +а утверждение о них ложно. Пороговые и сравнительные предложения гейт не сторожит; их проверяет +только чтение. """ from __future__ import annotations @@ -36,9 +42,16 @@ def ck(name: str, ok: bool, detail: str = "") -> None: print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {name}" + (f" {detail}" if detail else "")) +def _norm(x: str) -> str: + """Нормализация ПЕРЕД сравнением: пробелы не значимы, а типографский минус U+2212 и + ASCII-дефис — один и тот же знак. ⚠ Второе заведено гейтом-же: он пометил девять расхождений + там, где число совпадало, а отличался ЗНАК-СИМВОЛ. Различать их — ложная тревога, но молчать + о них нельзя, поэтому нормализуется знак, а не ослабляется проверка.""" + return re.sub(r"\s+", "", x).replace("\u2212", "-").replace("\u2013", "-") + + def has(text: str, s: str) -> bool: - """Число ищется по НОРМАЛИЗОВАННОМУ тексту: пробелы и переносы строк не значимы.""" - return re.sub(r"\s+", "", s) in re.sub(r"\s+", "", text) + return _norm(s) in _norm(text) def main() -> int: @@ -79,6 +92,25 @@ def main() -> int: for m in P2.tenants(): ck(f"жилец {m} назван в отчёте", has(t, m)) + # ⚠ ПОКРЫТИЕ КАНОНА СТОРОЖИТСЯ ЧИСЛОМ (поправка фазы Д). Таблицы (г) и §6.2 несут по 18 + # чисел покрытия, и до сих пор их не проверял никто: правка регекса банка прошла бы молча. + # Поймано ревью, когда в `bank._rx` нашлась ловля корня В СЕРЕДИНЕ чужого слова. + print("\n=== ПОКРЫТИЕ КАНОНА ===") + us = M.units_zh() + for label, get in (("D0", lambda u: P2.draft_text(P2.ANCHOR, u["uid"])), + ("E0", lambda u: P2.edit_text(P2.ANCHOR, u["uid"]))): + k = s = 0 + for u in us: + o = get(u) or "" + if not o: + continue + a, b = BANK.coverage(u["source"], o) + k += a + s += b + if s: + ck(f"покрытие канона {label} = {k / s:.3f} напечатано", has(t, f"{k / s:.3f}"), + f"{k}/{s}") + print("\n=== ДЕНЬГИ ===") for ph in ("Ф0", "Ф1", "Ф2", "Ф3"): sp = MONEY.Guarded(ph).spent() @@ -92,6 +124,66 @@ def main() -> int: pack = MONEY.Pack().spent() ck(f"расход пака ${pack:.6f} напечатан", has(t, f"{pack:.6f}")) + print("\n=== ОСЬ (а): ЧЕРНОВИК КАК ТЕКСТ ===") + import statistics as sst # noqa: PLC0415 + for pass_ in ("p2zh", "p2en"): + votes = MONEY.OUT / f"aj-{pass_}-votes" + if not votes.exists() or not list(votes.glob("*.json")): + continue + import judge as JU0 # noqa: PLC0415 + JU0.setup(pass_) + import absjudge as AJ0 # noqa: PLC0415 + for a, bb, _w in JU0.family_desc(pass_): + m = AJ0._margins(a, bb) # noqa: SLF001 + if not m: + continue + if pass_ == "p2zh": # решающая печать — zh; en описателен и в ИТОГ не вынесен + ck(f"{pass_}: {a} vs {bb} = {sst.mean(m):+.2f} напечатан", + has(t, f"{sst.mean(m):+.2f}")) + + print("\n=== ПОЗИЦИОННЫЙ НАКЛОН (требование ЗАКОНа промта) ===") + ck("наклон объявлен замеренным по всем проходам", has(t, "0.078") and has(t, "0.469")) + ck("поправленное значение довеска напечатано", has(t, "-1.65") or has(t, "−1.65")) + + print("\n=== ЦЕНА p95 ===") + import itog22 as IT # noqa: PLC0415 + zh = M.units_zh() + for m in P2.tenants(): + dc = [P2.draft_cost(m, u["uid"]) for u in zh] + ec = [] + for u in zh: + f2 = MONEY.OUT / f"{P2.tag_edit(m, u['uid'])}.json" + if f2.exists(): + ec.append(json.loads(f2.read_text(encoding="utf-8")).get("cost_usd", 0.0)) + if not dc: + continue + pair = [d + e for d, e in zip(dc, ec, strict=False)] if ec else dc + ck(f"{m}: связка p95 {IT._p95(pair):.5f}", has(t, f"{IT._p95(pair):.5f}")) # noqa: SLF001 + + print("\n=== ПУСТОЙ ВЫХОД И ГРАНИЦА ДВОЙНОЙ ОПЛАТЫ ===") + empty = [] + for globs in MONEY.TAGS.values(): + for g in globs: + for f3 in MONEY.OUT.glob(g): + r3 = json.loads(f3.read_text(encoding="utf-8")) + if r3.get("cost_usd", 0) > 0 and not (r3.get("content") or "").strip(): + empty.append(r3["cost_usd"]) + if empty: + ck(f"оплачено за пустой выход ${sum(empty):.5f} напечатано", has(t, f"{sum(empty):.5f}")) + ck(f"{len(empty)} пустых вызовов объявлено", has(t, f"{len(empty)} вызов")) + # граница двойной оплаты обязана быть ВЫВОДИМОЙ: расход Ф2 до атомарного замка + import subprocess # noqa: PLC0415 + at = subprocess.run(["git", "log", "--format=%at", "-1", "fd3e522"], + capture_output=True, text=True, cwd=REPO).stdout.strip() + if at: + pre = 0.0 + for g in MONEY.TAGS["Ф2"]: + for f4 in MONEY.OUT.glob(g): + if f4.stat().st_mtime < int(at): + pre += json.loads(f4.read_text(encoding="utf-8")).get("cost_usd", 0.0) + ck(f"верхняя граница двойной оплаты ${pre:.6f} напечатана", has(t, f"{pre:.6f}"), + "граница = расход Ф2 ДО атомарного замка, в худшем случае оплаченный дважды") + print("\n=== СУДЕЙСТВО ===") import judge as JU # noqa: PLC0415 rs = JU.runs_used()