diff --git a/CLAUDE.md b/CLAUDE.md index b18035ef..a5322cf2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -71,7 +71,7 @@ Go-бэкенд издательского художественного пер ## Онбординг новой сессии (порядок чтения) **Развилка ПЕРВАЯ, до всего остального — есть ли у тебя хендофф-промт (файл задания, выданный оркестратором).** -- **С промтом:** этот файл → СВОЙ промт; его карта чтения (≤5 позиций) — ЗАКОН, дальше только по её ссылкам. README, CURRENT-STATE и голову журнала решений читать НЕ нужно: ратифицированное вложено в тело промта, остальное берётся грепом по мере вопросов. +- **С промтом:** этот файл → СВОЙ промт; его карта чтения (≤5 позиций) — ЗАКОН, дальше только по её ссылкам. README, CURRENT-STATE и голову журнала решений читать НЕ нужно, ЕСЛИ карта промта их не называет (у ролевого промта оркестратора называет — там его номер и очередь): ратифицированное вложено в тело промта, остальное берётся грепом по мере вопросов. - **Без промта (холодный вход):** полный маршрут ниже. 1. Этот файл → `docs/README.md` (карта) → CURRENT-STATE в `docs/PROGRESS.md`. Непонятный жаргон/сокращения — `docs/glossary.md`. diff --git a/START_PROMT.MD b/START_PROMT.MD index 2afcf0dc..9b50159e 100644 --- a/START_PROMT.MD +++ b/START_PROMT.MD @@ -92,4 +92,12 @@ hello my dear Идея проекта V5, 26.07.2026 1) Идеи по фронту. -2) Так как фронт будет писаться под веб сначала, то нужно максимально хорошо сделать для ранжирования гуглом и нужно узнать как там сейчас работают актульные гугловские алгоритмы чтобы сайт не банился и не деранжировался. Просто для примера: политика куков, страницы сироты (на которую никто не ссылается) и так далее \ No newline at end of file +2) Так как фронт будет писаться под веб сначала, то нужно максимально хорошо сделать для ранжирования гуглом и нужно узнать как там сейчас работают актульные гугловские алгоритмы чтобы сайт не банился и не деранжировался. Просто для примера: политика куков, страницы сироты (на которую никто не ссылается) и так далее + +Идеи проекта V6, 18.08.2026 +Идея переделки архитектуры бэкенда: +1) Делаем map reduce запросы в дешевую модель которая майнит банк памяти, находит сущности, РЕЖЕТ книгу по сценам (границы сцен, но граница сцены это просто удобная порция где не теряется смысл (сцены можно склеить, но если сцена крупная она должна вмещаться в контекст модели)) и просим вернуть джейсон. Не переводит, не пишит текст, выдача джейсон с полями, явно приписанная схема джейсона, указывать смещение в тексте (координаты терминов или имен, сцен). Майнинг ВСЕХ терминов. ЕЩЕ СЛУЖЕБНАЯ ИНФА О КНИГЕ, у нас итак достроена сейчас в бэкенде для txt книг нарезка по главам, но можно еще что то сюда завернуть например поиск идиом или еще других вещей чтоб сократить путь критику и дать возможность редактору банка отработать и принять решение +2) Перевод банка памяти в хорошую модель. Просить принять хорошую модель взять на себе редакторскую роль ПРИНЯТИЯ РЕШЕНИЯ С ОБОСНОВАНИЕМ. Хорошо приложить контекст термина, например первого появления термина. Еще можно разделить сущности: имена отдельно, магические предметы отдельно (разные правила перевода), мы кстати так и делаем или нет? Потому что у нас есть чанкование +3) Сам перевод в хорошую модель. Четко задать роль (ты литературный переводчик) Даем оригинал текста + банк памяти (исключительно релевантные, не давать слишком много контекста - у нас вроде есть это). Плюс опционально появились ли новые термины или сомнительное место которое не покрыто базой. Размер фрагмента - границы сцен. И я бы не подсовывал хорошей модели дешевый черновой вариант. Запретить что то? Менять канон? Зажать промтом, но не бесконечными правилами, а рамками и по каким критериям мы будем провериять результат +4) Литературный критик. Получает исходный текст, банк памяти и не переписывает а дает фидбек: вот тут звучит не по русски, тут термин упал. Зажать модель промтом чтоб не выдавал минорные проблемы, просить явно не предлагать решения, а только зафиксировать расхождение. Заставить критика жаловаться, а не чинить. МАЙНИНГ ЗАМЕЧАНИЙ (новый банк памяти считай замечаний). Просим критика выделить сцену, проблемное место, уверенность в проблемном месте, что пошло не так. Сложная архитектура для критика, так как есть риск лишних правок, в том числе есть правки разной сложности: смысловой (где переврали сюжет), простой случай когда проблема 1 термине непереведенном или непереведенном не так, потеря смысла, неестественный русский (калька языка), и другие проблемы например времен. Хороший критик классифицирует проблему, но не более сверх, тогда редактор понимает куда копать. +5) Запрос в модель редактора точечного фикса конкретных проблем. Постим строку банка проблемных мест и просим исправить. Затем инжектим фиксы в текст. Это edit точечный редактор. Не переписывальщик ни в коем случае он не переписывает ничего \ No newline at end of file diff --git a/docs/ORCHESTRATOR_SESSION_PROMPT.md b/docs/ORCHESTRATOR_SESSION_PROMPT.md index 7349b8d2..d87b3ef7 100644 --- a/docs/ORCHESTRATOR_SESSION_PROMPT.md +++ b/docs/ORCHESTRATOR_SESSION_PROMPT.md @@ -146,7 +146,7 @@ D-ссылка грепается по D-логу · mid-flight аддендум в отчёте» = дефект, гейты зоны его не видят по построению · триаж находок — МАШИННАЯ сверка «каждая рекомендация → живой носитель», не глазами · отчёт с пошаговым планом сверяется ПО ПУНКТАМ плана · каждое «отложено/вернуться» получает -строку бэклога или жильца реестра выселенных тел (`architecture/13-tech-debt-anchors.md` §Б) ТЕМ ЖЕ +строку бэклога или жильца реестра загейченных триггеров (`13-tech-debt-anchors.md` §Б-108) ТЕМ ЖЕ лендингом · при закрытии строки её тело грепается на «остаток/реопен/гейт», гейты в ДРУГИХ строках пере-диспозиционируются · глаз владельца — рабочий рубеж: прайми его артефактами, не пересказом. diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index fcb2b264..afb391e6 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1,6 +1,6 @@ # Журнал прогресса -> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-08-23, голова D39.156 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №19** (единственный носитель — здесь; роль передана 22.08, №18 закрыт D39.155): (а) **P7 платформы ПРИНЯТ С ФИКС-ЛИСТОМ и ЗАЛЕНДЕН, сессия закрыта владельцем 20.08** (D39.153; зонный фикс-лист из тринадцати пунктов плюс врезанный первым блокер свипа — пинг №18 в `platform/docs/platform-PROGRESS.md`; ⚠ прежний врезанный пример «первым `ContractVersion` 0.3.0 при формах 0.4.0» СНЯТ — константа поднята и запинена гейтом против канона лендингом `31f1f82`) · (а1) **контракт API v0 0.4.0 РАТИФИЦИРОВАН** (D39.152; PD-327 закрыт; зеркало фронта отстаёт ратифицированно, при разморозке `cmp`) · (б) **лендинг петель полигона (фаза Д) — ОТКРЫТ**: приёмка ФАЗЫ у параллельного оркестратора, лендинг за этим; ⚠ в дереве живая НЕЗАКОММИЧЕННАЯ работа полигона: **13 изменённых файлов (+2166/−79) и 6 новых**, состав — пинг №20 в секции «Полигон». Индекс на 22.08 ЧИСТ (`git diff-index --cached HEAD` → 0; прежняя редакция писала «частично ЗАСТЕЙДЖЕННАЯ» — испр. аудитом доков). Не трогать и не «прибирать»; сессия жива, стейдж может появиться в любой момент, поэтому форма коммита остаётся строго pathspec · (в) **дофикс-пак P8-FIX ПРИНЯТ И ЗАЛЕНДЕН 22.08** (D39.154, лендинг `31f1f82`): блокер свипа закрыт четырьмя механизмами и пере-проверен живой пробой оркестратора, деньги сошлись двумя путями из сырого леджера; мои четыре строки регистра — `PD-371`…`PD-374`. ⚠ **Порядок вышел ОБРАТНЫМ этой очереди:** промт дофикса требовал запуска ПОСЛЕ читающего пака, запущен был раньше. Цена перестановки, названная мной вслух 21.08 («блокер живёт всё время читающего пака»), тем самым НЕ заплачена — это лучший исход, чем планировалось; в минус — §4.7 (аддендум релеем) пуст и читающий пак пойдёт по только что переписанному коду. **ЧТО ДАЛЬШЕ С ПЛАТФОРМОЙ — решено 22.08 при актуализации доков, порядок такой.** **(1) Читающий пак P8-REVIEW** (`platform/docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md`) — следующая работа зоны, запуск по слову владельца. Промт ЖИВ и переписан 22.08 в трёх местах, которые дофикс сделал ложными: «два денежных дефекта не заведены» (заведены и закрыты — `PD-333`/`PD-334`, вместо них дан их КЛАСС и живой образец `PD-372`) · приор оси 4 «наблюдаемость без ручки» (ручка построена ⇒ вопрос стал «достаточна ли она») · абсолют «оси ни в один дифф не входили» (сужен: системного разбора не было, кусками входили). **Довод ЗА пак — базовая ставка, а не вера:** первый же серьёзный взгляд на ОДНУ из четырёх осей (очередь, 21.08) дал пред-продовый блокер, проживший два пака под статусом `fixed`. Осей осталось три, и на двух из них лежат деньги и вход. **(2) ПОРЯДОК РАБОТ ПО ШВУ — ратифицирован D39.156 (23.08), исполняется строго так.** **(2а) Движковый пак — АКТИВНЫЙ ПРОМТ ЗОНЫ** (`BACKEND_SEAM_PACK_SESSION_PROMPT.md`, выдан 23.08, запуск по слову владельца): конвенционные дефолты `mined_delta`/`mined_rejects` · $0-глагол приёма правок банка · путь к файлу ключей аргументом (снимает блокер **211**) · строгость сид-загрузчика · путь артефакта и версия документа в `status --json` (снимает движковую половину **213**). **(2б) Контрактный минор — ПОСЛЕ него и ОДНИМ куском:** снос отменённой пер-термной модели + новая дверь, тело которой выводится из словаря глагола, а не сочиняется до него; сюда же строки **200**, **203** и хвосты компаньона. ⚠ Гигиена держится сознательно: снести дверь сейчас и завести в следующем миноре — две перегенерации фронта вместо одной; фолбэк объявлен — если пак застрянет надолго, гигиену шипить отдельно. **(2в) Платформенный пак — последним:** воркер решений → глагол перед возобновлением · путь ключей из конфига · снятие дубля вывода пути · дев-супервизор на тот же механизм ключей. **(3) `sqlc` отдельной сессией** (решение владельца 22.08, `BACKLOG.md` П-19, граница 41 запрос в пяти файлах) — механическая работа, не мешать её с содержательной. **(4) Живой прогон книги насквозь** — строка 202, гейт прежний: ПОСЛЕ холодного прогона движка. ⚠ **На владельце и блокирует пункт своей темы:** развилка владения `book.yaml` (строка 199а, зона сдала четырьмя вариантами с ценой, рекомендация зоны C или D+B) — без неё труба доставки правок банка не строится, а верхнего края у неё теперь нет вовсе · (г) **контракт:** сквозная полоса прогресса (строка 200, слово владельца 20.08) и хвосты релеев (203) едут тем же минором, что и дверь приёма правок банка — состав собирается ПОСЛЕ ратификации нормы шва, см. пункт (2) · (д) свободные бэкенд: 160 Этап 0, фикс-лист ФЧ строкой 197 (носителя-сессии нет по слову владельца); ⚠ строки 176/181/187 СНЯТЫ с таблицы при лендинге 20.08 — они закрыты ещё D39.149, а очередь всё это время звала 181 свободной работой (поймано ревью доков; остатки живут строками 194/196/197) · (е) фронтовый фрагмент pre-commit хука получает норму «жалуйся владельцу» — **решение владельца 21.08: ДА**, исполняется первым касанием зоны (пинг в её журнале). **Открытые обязательства, которых нет больше нигде:** веса `pro` — строка 172-г НЕ закрыта, прогон мерил классификатор на flash; дешёвый вход — спросить полигон, говорят ли что-то данные фазы Д (там pro гоняется редактором), отдельный замер не покупать без этого · `docs/experiments/00-provider-quirks.md` несёт протухшие цены DeepSeek («не изменились, flash $0.14/$0.28») — зона полигона, чинить пингом · санкция на платный прогон в архивном промте пака честности была РАЗОВОЙ · **норма плотности комментариев — ОТВЕЧЕНА владельцем 21.08:** режем ВОДУ, а не длину — счёт строк «пиши одну-две» негодный гейт и снят; уходит пересказ решений, провенанс и изложение исследования вместо ссылки, остаётся всё, что из одной функции не выводится, сколько бы строк ни заняло (норма переписана — `12-go-style-notes` §1; PD-255 платформы этим закрывается). ⚠ Метод-урок: её хендофф объявил «вопросов на владельце нет», а строка регистра просила именно решения — вопрос был закрыт ОБЪЯВЛЕНИЕМ, поймано ревьюером полноты выгрузки. Оркестраторов ДВА (решение владельца 07.08): этот — движок/платформа/фронт/доки; параллельный (РОЛЬЮ, без номера — счётчик один, D39.112 п.6) — приёмка полигона. Одновременно не запускаются; CURRENT-STATE ведут оба, чужие строки не трогают. Норма изоляции панелей — D39.113, гардрейлы в CLAUDE.md.) +> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-08-23, голова D39.156 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №19** (единственный носитель — здесь; роль передана 22.08, №18 закрыт D39.155): (а) **P7 платформы ПРИНЯТ С ФИКС-ЛИСТОМ и ЗАЛЕНДЕН, сессия закрыта владельцем 20.08** (D39.153; зонный фикс-лист из тринадцати пунктов плюс врезанный первым блокер свипа — пинг №18 в `platform/docs/platform-PROGRESS.md`; ⚠ прежний врезанный пример «первым `ContractVersion` 0.3.0 при формах 0.4.0» СНЯТ — константа поднята и запинена гейтом против канона лендингом `31f1f82`) · (а1) **контракт API v0 0.4.0 РАТИФИЦИРОВАН** (D39.152; PD-327 закрыт; зеркало фронта отстаёт ратифицированно, при разморозке `cmp`) · (б) **лендинг петель полигона (фаза Д) — ОТКРЫТ**: приёмка ФАЗЫ у параллельного оркестратора, лендинг за этим; ⚠ в дереве живая НЕЗАКОММИЧЕННАЯ работа полигона: состав МЕНЯЕТСЯ, пока сессия жива, и снимок здесь протухает за сутки (проверено 23.08: было записано 13+6, в дереве 14+5) — читать командой `git status --short -- eval/`; пинг №20 — в секции «Полигон». Индекс на 22.08 ЧИСТ (`git diff-index --cached HEAD` → 0; прежняя редакция писала «частично ЗАСТЕЙДЖЕННАЯ» — испр. аудитом доков). Не трогать и не «прибирать»; сессия жива, стейдж может появиться в любой момент, поэтому форма коммита остаётся строго pathspec · (в) **дофикс-пак P8-FIX ПРИНЯТ И ЗАЛЕНДЕН 22.08** (D39.154, лендинг `31f1f82`): блокер свипа закрыт четырьмя механизмами и пере-проверен живой пробой оркестратора, деньги сошлись двумя путями из сырого леджера; мои четыре строки регистра — `PD-371`…`PD-374`. ⚠ **Порядок вышел ОБРАТНЫМ этой очереди:** промт дофикса требовал запуска ПОСЛЕ читающего пака, запущен был раньше. Цена перестановки, названная мной вслух 21.08 («блокер живёт всё время читающего пака»), тем самым НЕ заплачена — это лучший исход, чем планировалось; в минус — §4.7 (аддендум релеем) пуст и читающий пак пойдёт по только что переписанному коду. **ЧТО ДАЛЬШЕ С ПЛАТФОРМОЙ — решено 22.08 при актуализации доков, порядок такой.** **(1) Читающий пак P8-REVIEW** (`platform/docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md`) — следующая работа зоны, запуск по слову владельца. Промт ЖИВ и переписан 22.08 в трёх местах, которые дофикс сделал ложными: «два денежных дефекта не заведены» (заведены и закрыты — `PD-333`/`PD-334`, вместо них дан их КЛАСС и живой образец `PD-372`) · приор оси 4 «наблюдаемость без ручки» (ручка построена ⇒ вопрос стал «достаточна ли она») · абсолют «оси ни в один дифф не входили» (сужен: системного разбора не было, кусками входили). **Довод ЗА пак — базовая ставка, а не вера:** первый же серьёзный взгляд на ОДНУ из четырёх осей (очередь, 21.08) дал пред-продовый блокер, проживший два пака под статусом `fixed`. Осей осталось три, и на двух из них лежат деньги и вход. **(2) ПОРЯДОК РАБОТ ПО ШВУ — ратифицирован D39.156 (23.08), исполняется строго так.** **(2а) Движковый пак — АКТИВНЫЙ ПРОМТ ЗОНЫ** (`BACKEND_SEAM_PACK_SESSION_PROMPT.md`, выдан 23.08, запуск по слову владельца): конвенционные дефолты `mined_delta`/`mined_rejects` · $0-глагол приёма правок банка · путь к файлу ключей аргументом (снимает блокер **211**) · строгость сид-загрузчика · путь артефакта и версия документа в `status --json` (снимает движковую половину **213**). **(2б) Контрактный минор — ПОСЛЕ него и ОДНИМ куском:** снос отменённой пер-термной модели + новая дверь, тело которой выводится из словаря глагола, а не сочиняется до него; сюда же строки **200**, **203** и хвосты компаньона. ⚠ Гигиена держится сознательно: снести дверь сейчас и завести в следующем миноре — две перегенерации фронта вместо одной; фолбэк объявлен — если пак застрянет надолго, гигиену шипить отдельно. **(2в) Платформенный пак — последним:** воркер решений → глагол перед возобновлением · путь ключей из конфига · снятие дубля вывода пути · дев-супервизор на тот же механизм ключей. **(3) `sqlc` отдельной сессией** (решение владельца 22.08, `BACKLOG.md` П-19, граница 41 запрос в пяти файлах) — механическая работа, не мешать её с содержательной. **(4) Живой прогон книги насквозь** — строка 202, гейт прежний: ПОСЛЕ холодного прогона движка. ⚠ **На владельце и блокирует пункт своей темы:** развилка владения `book.yaml` (строка 199а, зона сдала четырьмя вариантами с ценой, рекомендация зоны C или D+B) — без неё труба доставки правок банка не строится, а верхнего края у неё теперь нет вовсе · (г) **контракт:** сквозная полоса прогресса (строка 200, слово владельца 20.08) и хвосты релеев (203) едут тем же минором, что и дверь приёма правок банка — состав собирается ПОСЛЕ ратификации нормы шва, см. пункт (2) · (д) свободные бэкенд: 160 Этап 0, фикс-лист ФЧ строкой 197 (носителя-сессии нет по слову владельца); ⚠ строки 176/181/187 СНЯТЫ с таблицы при лендинге 20.08 — они закрыты ещё D39.149, а очередь всё это время звала 181 свободной работой (поймано ревью доков; остатки живут строками 194/196/197) · (е) фронтовый фрагмент pre-commit хука получает норму «жалуйся владельцу» — **решение владельца 21.08: ДА**, исполняется первым касанием зоны (пинг в её журнале). **Открытые обязательства, которых нет больше нигде:** веса `pro` — строка 172-г НЕ закрыта, прогон мерил классификатор на flash; дешёвый вход — спросить полигон, говорят ли что-то данные фазы Д (там pro гоняется редактором), отдельный замер не покупать без этого · `docs/experiments/00-provider-quirks.md` несёт протухшие цены DeepSeek («не изменились, flash $0.14/$0.28») — зона полигона, чинить пингом · санкция на платный прогон в архивном промте пака честности была РАЗОВОЙ · **норма плотности комментариев — ОТВЕЧЕНА владельцем 21.08:** режем ВОДУ, а не длину — счёт строк «пиши одну-две» негодный гейт и снят; уходит пересказ решений, провенанс и изложение исследования вместо ссылки, остаётся всё, что из одной функции не выводится, сколько бы строк ни заняло (норма переписана — `12-go-style-notes` §1; PD-255 платформы этим закрывается). ⚠ Метод-урок: её хендофф объявил «вопросов на владельце нет», а строка регистра просила именно решения — вопрос был закрыт ОБЪЯВЛЕНИЕМ, поймано ревьюером полноты выгрузки. Оркестраторов ДВА (решение владельца 07.08): этот — движок/платформа/фронт/доки; параллельный (РОЛЬЮ, без номера — счётчик один, D39.112 п.6) — приёмка полигона. Одновременно не запускаются; CURRENT-STATE ведут оба, чужие строки не трогают. Норма изоляции панелей — D39.113, гардрейлы в CLAUDE.md.) > - **Эра №15 закрыта — семь приёмок, все ПРИНЯТЫ и залендены**; лента, коммиты и разборы — D39.109–123 (D-лог) и реестр нот `architecture/05-decisions-index.md`; снимок прежних бюллетеней этой шапки — архив-слайс `-08-02-04`. > - **ЖИВОЕ:** полигон — **фаза Д ИДЁТ** (заказ 10.08; деньги санкционированы 15.08 напрямую полигону — ⚠ числа потолка в носителях расходятся, фактическую цепь сверить при лендинге петель; ja-книга `enkan_no_hate_ja`; при ратификации фазы Д в D-ноту: декой-правило + обязательный кросс-семейный опровергатель приёмки — одобрены 10.08; свежие фриз-коммиты полигона в дереве — НЕ трогать) · платформа и контракт — статус в строке выше, здесь НЕ дублируется (один носитель на факт) · **фронт ЗАМОРОЖЕН** (D39.136 п.2 + **D39.147: морозится ДАЛЬШЕ лендинга P7 — слово владельца 17.08; P7 зону НЕ размораживает, S5-промт не выдаётся, разморозка отдельным словом по достижении сквозного пути**; перечень первого касания зоны — синк зеркала на ТЕКУЩИЙ канон (не на «0.3.0»: канон с тех пор ушёл на 0.4.0 и уйдёт дальше строкой 200; оба минора уедут в ОДНУ перегенерацию — D39.152 п.7) + перегенерация типов + моки + гейт утечки конвейера + ФС-1..12 + Ф-63/Ф-28 + фразы В-11 по словарю кодов — не отменён, ждёт разморозки) · закрытые стройки эры — лентой нот: эмиттер D39.131 · P5/P6 D39.130/132 · migrate D39.134 · S4+0.2.3 D39.135 · DeepSeek-репин D39.137 (тела — D-лог и слайсы). > - **Открыто на владельце:** **развязка git с origin** (локальная линия ИСТИННА, force-push его рукой; не пуллить) · Приложение А контракта (148: фразы — по словарю кодов 0.3.0, структура готова) · продуктовое слово «остановлена: лимиты» (В-3) · В-4/Ф-30 (глава без заголовка — движковая половина строка 160) · мини-проба флора 44 (одобрена, промт не выдан; число — после ре-пробы 188) · подпись денежного шага 46 · лист В-3+К-6 · авто-резюм paused (вопрос платформы) · **схема time-based DeepSeek** (доклад — архив-слайс `PROGRESS-2026-08-14-15`; рекомендация: пик оставить + операционное правило «прогоны в долины», scheduler не начинать без ответа вендора об отметке тарификации) · **вход ратификации фазы Д:** посылка «dspro дешевле glm» в пике ПЕРЕВЁРНУТА (×1.26 дороже, D39.137 п.4) · **возражение Sol по проходу `tier` эксп-23** (⚠ расхождение носителей СНЯТО 22.08 срезкой: второй носитель — фраза «возражение снято, и владелец назвал причину» — уехал в слайс `archive/PROGRESS-2026-08-10-15.md` вместе с хроникой, и её довод там читается целиком: Sol судил СВОЁ семейство, что запрещено D13.3. Живым остаётся ровно этот пункт на владельце; подтвердить или снять — при лендинге петель фазы Д) · фронт-вопросы зонного журнала: В-7 (плотность; держит Ф-54) · В-8 (слово состояния в дереве) · В-9 (языки интерфейса — механизм готов, ПТ-36) · Ф-38 (вкладка «Замечания»). Закрытые пункты листа (PD-104 · В-10 · В-11-форма · Ф-56/57/61/62/63 · Ф-28 · ПТ-33-граница · 116 · 126 · 172-пин · PD-203 · санкции фазы Д · дизайн 160/161 · title) — в нотах D39.136–145, здесь не держатся. diff --git a/docs/README.md b/docs/README.md index 7b1fef1c..b25941d3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,7 +17,7 @@ - `research/` — фактура ресёрчей; у принятых — ревью-шапки, часть тел под ⚠ superseded: **читай баннер прежде содержимого**. Ключевые для навигации: 15 голос · 16 ридер-IDE · 17 внешняя критика · 18 рычаги качества · 19 нарезка · 20 банк-майнинг · 21 обзор транспорта · 22 доменные харнессы · 23 шов движок↔платформа · 25 холодное ревью шва — **читать перед любым кодом стыка** · 24 арбитраж банка · 26 официальные практики Anthropic · 28 контракт-ревью API v0 — **носитель для исполняющих сессий, читать оригинал, не пересказ**. - [PROGRESS.md](PROGRESS.md) — журнал: CURRENT-STATE + **ЕДИНЫЙ БЭКЛОГ** (единственный трекер) + живой хвост хроники. НЕ источник решений. - `scripts/counts.py` — **производные числа доков считаются им, а не руками** (голова по трём носителям · счёт очереди и зон · вес открытых строк регистра платформы · полнота реестра нот); `--check` даёт ненулевой код на расхождении; `--lint` — линтер file:line-якорей живых доков (D39.126). Его же зовёт зонный хук `scripts/githooks/pre-commit`: **`--lint` якорей — при коммите, задевающем ЛЮБОЙ док или `CLAUDE.md`; `--check` чисел и головы — при коммите с D-логом или PROGRESS** (оба `--from-index`, предупреждает, не блокирует). ⚠ Шапка систематически отстаёт от хвоста у ЛЮБОГО автора — потому проверка механическая, а не «посмотрю внимательно». -- **Активные хендофф-промты — какой файл ТВОЙ** (состав обновляется при каждом лендинге, D39.80; статусы паков, хроника и причины — CURRENT-STATE и D-лог, здесь НЕ дублируются): +- **Активные хендофф-промты — какой файл ТВОЙ** (состав обновляется при каждом лендинге, D39.80; здесь — только то, что отвечает на вопрос «какой файл мой»; хроника, причины и статусы РАБОТ — CURRENT-STATE и D-лог): | Роль | Активный промт | Статус | |---|---|---| | Оркестратор | [ORCHESTRATOR_SESSION_PROMPT.md](ORCHESTRATOR_SESSION_PROMPT.md) | роль и нормы; счётчик роли — CURRENT-STATE | diff --git a/eval/conformance.py b/eval/conformance.py index b231f7c9..3792a4c6 100644 --- a/eval/conformance.py +++ b/eval/conformance.py @@ -77,7 +77,14 @@ def rows(md: str) -> list[tuple[str, str, str]]: if out: # таблица кончилась break continue - cells = [c.strip() for c in s.strip("|").split("|")] + # ⚠ Markdown экранирует вертикальную черту внутри ячейки как `\|`. Наивный `split("|")` + # рвал такую ячейку пополам, и улика приходила обрезанной: у R55 в оболочку уезжало + # `... | wc -l) -ge 7` без начала, что давало «Syntax error» и ЛОЖНЫЙ КРАСНЫЙ, а у R37 — + # ЛОЖНЫЙ ЗЕЛЁНЫЙ, потому что обрезок случайно возвращал 0. Гейт, который сам портит + # улику, не проверяет ничего. Восстанавливается ОБЫЧНАЯ черта: в markdown `\|` и означает + # литеральный `|`, и в оболочку должен уйти именно он. + cells = [c.replace("\x00", "|").strip() + for c in s.strip("|").replace("\\|", "\x00").split("|")] if len(cells) < 3 or set(cells[0]) <= set("-: "): continue if cells[0].upper() in ("ID", "№"): diff --git a/eval/dovodka/HANDOFF-21-08.md b/eval/dovodka/HANDOFF-21-08.md new file mode 100644 index 00000000..3aa08a5c --- /dev/null +++ b/eval/dovodka/HANDOFF-21-08.md @@ -0,0 +1,382 @@ +# ХЕНДОФФ: полигон, следующая сессия (готовлено 20.08 вечером) + +> Читать ЦЕЛИКОМ, не грепом. Прошлые передачи теряли пункты именно из-за чтения грепом. +> Предыдущие слои: `SESSION2-LOG.md` разделы 15.1–15.21 (по ходу этой сессии) · +> `PLAN-17-08.md` (курс) · отчёт `docs/experiments/23-editor-tier.md` секции Д21, Д23 · +> `КОНТЕКСТ-ДЛЯ-КОНСУЛЬТАЦИИ.md` (сжатая картина проекта для внешнего собеседника). + +--- + +## 0. ГЛАВНОЕ, ЧТО ИЗМЕНИЛОСЬ ЗА СЕССИЮ + +**Сменился ПРИБОР.** Решение владельца 20.08 дословно: «меняем принцип судейства… теперь корми +фабл 5 и пусть он решает какой из вариантов ближе по художественному смыслу, лучше по переводу», +и позже: «Теперь судим так все время». Слепое чтение Fable-5 — ПЕРВИЧНЫЙ прибор; счёт ошибок +остаётся брак-скрином. + +**Основание замерено, а не принято на веру.** На панели, которую читал сам владелец: + +``` +пара прибор Спирмен с владельцем +zh чтение Fable (чистый пакет) +0.80 +zh счёт ошибок +0.80 +en чтение Fable (чистый пакет) +0.80 +en счёт ошибок −0.80 +``` + +Новый прибор согласен с владельцем ОДИНАКОВО на обеих парах; старый меняет ЗНАК от пары к паре. + +⚠ **И тут же найдена моя ошибка:** в пакете, который читал владелец, стояла подсказка — «вариант +с наименьшим числом ошибок читался почти хуже всех». Пере-прогон на НЕЙТРАЛЬНОМ пакете (тексты +побайтно те же) дал +0.80 вместо +1.00: подсказка стоила 0.20, верх и низ порядка устояли. +**Норма, заведённая этим: пакет чтения не имеет права называть ни вердикт другого прибора, ни +существование контроля.** + +--- + +## 1. ЧТО ЖДЁТ СЛЕДУЮЩУЮ СЕССИЮ — ГОТОВО К ЗАПУСКУ + +**15 пакетов слепого чтения английской пары, по ОДНОЙ главе в пакете, панель из ОДИННАДЦАТИ +архитектур.** Всё куплено, покупать больше нечего. + +``` +пакеты ~/books/chtenie-rol/ЧТЕНИЕ-en-arch2-.md (15 штук, ~17 тыс. знаков каждый) +ключи ~/books/dovodka/blind-keys-rol/rol-KEY-arch2-.json ЧИТАТЕЛЮ НЕ ДАВАТЬ +ответы ~/books/chtenie-rol/ОТВЕТ-en-arch2-.md (заготовки созданы) +``` + +Панель: `ZD` голый черновик · `Z0` боевая связка (продакшен) · `ZF` её вторая генерация +(контроль шума) · `ZP` однопроходка-склейка · `Z8` обогащённый черновик · `Z8R` он же плюс перепис · +`Z1` критик → ПОЛНЫЙ перепис (ставка владельца H-2б) · `Z7` однопроходка + канон-фиксер · +`RN` промт-РОЛЬ с экзаменом · `RC` он же без экзамена · `RB` он же с ПЕРЕВЁРНУТЫМ экзаменом +(формулировка владельца). + +⚠ **Одна глава из 16 пропущена и это НАПЕЧАТАНО, а не умолчано:** `100b088f71` — у неё нет клетки +`RN` (единственная, что не собралась после двух попыток). Пакетов 15. +⛔ **ПОПРАВКА 21.08: строка ниже была НЕВЕРНА, и это ровно тот класс, о котором предупреждает шапка +файла.** Утверждение «прежние пакеты `arch` УДАЛЕНЫ» я записал как исполненное, не исполнив: на +диске лежат **16 пакетов `ЧТЕНИЕ-en-arch-.md`, 16 пустых `ОТВЕТ-en-arch-.md` и 16 ключей +`rol-KEY-arch-.json`** (панель из 8, ответов нет ни одного — проверено размером файлов). +Удалять не стал: сырьё чужой рукой не сносят, а механической коллизии глобов между `arch-` и +`arch2-` нет (проверено). Опасность ЧЕЛОВЕЧЕСКАЯ — принять один тег за другой; для того здесь и +стоит эта поправка. + +~~Прежние 15 пакетов с тегом `arch` (панель из 8) УДАЛЕНЫ~~ — держать две панели одной пары значит +однажды свести ответы разных панелей одним ключом. + +**Работать с `arch2`. Пакеты `arch` существуют, не прочитаны и не годятся: панель у них из 8.** + +**Сборка ПРОВЕРЕНА СПЛОШЬ** (владелец просил убедиться до передачи): +* 15 пакетов, 15 уникальных глав, 15 РАЗНЫХ раскладок меток; +* **165 текстов сверены побайтно с содержимым клеток своих армов — расхождений 0**; +* в рамке КАЖДОГО пакета стоит указание владельца «торопиться не надо» (проверено гейтом); +* исходник в каждом пакете — тот самый, что у главы; +* имён армов и путей к ключу в пакетах нет; +* рамка нейтральна: не называет ни контроля, ни другого прибора. +⚠ Первая проверка дала 16 «утечек» — это была ЛОЖНАЯ тревога моей же проверки: она искала слово +«метки», которое в рамке стоит легально («метки перемешаны»). Проверка переделана на поиск ИМЁН +АРМОВ. Не повторить эту ошибку в обратную сторону: тревога гейта требует вскрытия, а не отмены. + +**Как запускать:** по ОДНОМУ агенту Fable-5 на пакет (решение владельца: главы большие, читателю +с несколькими сразу тяжело). Промт-рамку взять из журнала §15.13/§15.21 — она уже отработала +четырежды. Обязательно: идентичность читателя в файл ДО запуска · `runs.py --claim` ДО запуска · +греп-аудит транскрипта ПОСЛЕ (образец кода в журнале). + +✔ **КАП СНЯТ 20.08 словом владельца** («Можно поднять, но я не припомню что б ставил тебе жёсткие +ограничения»): 100 → 200, свободно 107, на 16 глав хватает. ⚠ Уточнение для истории: число 80 +пришло из ЗАКАЗА (:25), владелец снял его ещё 15.08, а 100 поставил в код Я САМ — то есть сессия +упиралась в СВОЁ ограничение и подала его владельцу как чужое. Не повторять: перед тем как назвать +границу «ограничением владельца», найти его слово. + +--- + +## 2. ЧТО УСТАНОВЛЕНО ЭТОЙ СЕССИЕЙ — числа, которые нельзя потерять + +### 2.1 ⭐ Механизм цены вскрыт ТРЕЙСОМ, а не выведен + +Сохранение трейса размышления заведено по флагу `TM_KEEP_TRACE=1` (`role_topology/buy.py`). +Прямая мера: доля предложений ФИНАЛЬНОГО текста, встречающихся в трейсе дословно — **медиана 96%**. +Разбор трейса: черновик в уме 54% абзацев · структура 11% · **само-проверка 4%** · глоссарий 3%. + +⇒ **Размышление у `deepseek-v4-pro` — это ЧЕРНОВИК, а не проверка. Модель пишет перевод дважды: +в уме и в ответе. Мы платим за обе копии по цене выхода.** +⇒ Следствие для архитектуры: **боевая двухстадийная связка дёшева БЛАГОДАРЯ топологии — она +выносит черновик из канала размышления дорогой модели в выход дешёвой** ($3.96 против $1.32 за +миллион), и результат при этом виден, проверяем и переиспользуем, а не выбрасывается. +⚠ Режим БИМОДАЛЕН: одна клетка из шести в ум не писала вовсе (1 180 токенов против 23 000). + +### 2.2 Промт-роль: измерен, лучше склейки, дороже вчетверо + +Новый промт однопроходки собран как РОЛЬ (прежний был СКЛЕЙКОЙ двух боевых промтов с мета-фразой +«отдельного редактора после тебя НЕ БУДЕТ»). Ядро — `eval/dovodka/prompts/onepass-core.md`, +пар-специфика подтягивается из файлов пары, второго источника правды нет. + +``` +арм что это размышление $/клетка перевёрстка +ZP прежняя склейка 950 0.0177 1.77 +RN роль, экзамен на смелости 28 809 0.1265 1.60 +RB роль, экзамен ПЕРЕВЁРНУТ 23 301 0.1050 1.14 +RC роль, экзамен вырезан 11 268 0.0593 1.37 +Z0 боевая связка (для сравнения) — 0.0253 1.79 +``` + +Слепое чтение абляции (3 главы, 5 вариантов): **`RB > Z0 > RC > RN > ZF`**. +⇒ формулировка ВЛАДЕЛЬЦА («брак = просвечивающий исходный язык») бьёт обе мои редакции и встала +выше продакшена. ⛔ **НО контроль шума КРАСНЫЙ** — две генерации связки на 2-м и 5-м местах, +то есть порядок при n=3 не разрешим. Уцелевает: `RB` не хуже продакшена, и три редакции промта +упорядочены между собой. + +### 2.3 ⛔ ЧТО НЕ СРАВНИМО МЕЖДУ СОБОЙ — поймано владельцем + +Четыре слепых чтения этой сессии сделаны на РАЗНЫХ главах: +A и B — глава `ed068182cf` · C — `1431129de6, c73f4857e7, 89614f9bfa` · D — `ef9cd38ec4, +89614f9bfa, b2a7464dbd`. **У C и D общая ОДНА глава из трёх, у A/B с C/D — НИ ОДНОЙ.** +⇒ фразы «арм был третьим, стал четвёртым» — НЕ повторный замер. Внутри одного чтения сравнения +законны, между чтениями нет. Ради этого и собраны 16 пакетов одной панели на одних главах. + +### 2.4 Замер критика (фаза ФД-K, остановлен владельцем) + +4 клетки одной главы: фраза о параллельности текстов срезала размышление на 55% и цену вдвое — +и находки тоже вдвое (24 → 12). **Критик НЕ видит удалённого целого абзаца** (проверено: слов из +пропавшего куска нет ни у одной модели), при том что снятую частицу «не» ловит. +Реальный дефект в черновике удорожает критика **в 14 раз** против чистого. +`glm-5` с включённым размышлением не влезает в потолок вывода на китайском. +⇒ полноту обязан ловить детерминированный гейт, а не критик. + +### 2.5 Английский экстрактор ломает абзацы — наш баг + +`pair_en.raw_text` сносит из epub все теги, включая `

`: в книге **11 447 абзацев**, а модели +едет один блок. Чинить на месте НЕЛЬЗЯ: uid единицы = `sha1(source.strip())`, правка пробелов +переименует всё — замерено, что повиснут **289 оплаченных клеток на $2.64** и семь файлов ключей. +Починка сделана НА ПОДАЧЕ (арм `RA`, функция `podacha.restore_paragraphs`, проверено 16/16). +⛔ **ДОЛГ ЧУЖОЙ ЗОНЕ: настоящая починка — в бэкенде. Пинг оркестратору обязателен.** + +### 2.6 Приз best-of-2 пере-считан ЧЕСТНО (не круговым способом) + +Выбирает читатель A, оценивает читатель B (и наоборот): +``` +все главы: +0.28 позиции p=0.30 — шум +расхождение ≥10% текста: +2.36 позиции p=0.0065 — 12 подтвердили, 2 опровергли +расхождение <10%: −1.33 позиции p=0.95 — отбор ВРЕДИТ +``` +⇒ наивный «бери лучший из двух» бесполезен; на разошедшихся главах приз реален и больше +оракульного. Порог 10% выбран post-hoc — заморозить пре-регом и проверить на новых главах. + +### 2.7 Г5 закрыта: вердикт по H-1 устоял + +`itog_d4.py --sens`. Арифметика находки ревизии воспроизведена побайтно, но переворот живёт только +в сценарии, который возвращает пачку аннулированной сессии и одновременно снимает пачки-валидации. +Смежно закрыты P12, P07, R73/P40. ⛔ ОТКРЫТА **P42** — два пре-рег-правила о маржевом гейте +противоречат, и от выбора зависит, остаётся ли единственный CONFIRM фазы. **Вопрос владельцу.** + +--- + +## 3. ДЕНЬГИ + +``` +ФД-K 0.2927 / 0.40 замер подачи входа критику, zh (остановлен владельцем) +ФД-L 0.0000 / 1.30 то же, en — не начиналось +ФД-M 2.0066 / 2.10 однопроходка как роль, zh +ФД-N 0.0000 / 2.40 то же, en — не начиналось +ПАК 14.0221 / 18.30 свободно 4.28 +``` +Санкции владельца этой сессии: «Хорошо, поехали» на $1.70 (ФД-K/L) и «Бюджет разрешаю какой +нужен» (ФД-M/N). ⚠ Смета ФД-M была занижена ВШЕСТЕРО: считалась по клеткам старой склейки, у +которой размышления в 26 раз меньше. Норма: цену клетки НОВОГО промта нельзя оценивать по клеткам +СТАРОГО. + +--- + +## 4. КОМАНДЫ + +``` +eval/.venv/bin/python eval/dovodka/rol.py --selftest # гейт покрытия боевых промтов +eval/.venv/bin/python eval/dovodka/rol.py --show zh|en # промт целиком, как поедет +eval/.venv/bin/python eval/dovodka/rol.py --coverage zh|en # диспозиция КАЖДОГО боевого правила +eval/.venv/bin/python eval/dovodka/rol.py --dry # смета +eval/.venv/bin/python eval/dovodka/rol.py --canon # $0-гейт канона по армам +eval/.venv/bin/python eval/dovodka/rol.py --emit-read en --panel=… --tag=… --each --n=16 +eval/.venv/bin/python eval/dovodka/itog_d4.py --axis=d4 --sens # Г5 целиком +eval/.venv/bin/python eval/dovodka/money_d.py --report | --selftest +eval/.venv/bin/python eval/dovodka/runs.py --status | --claim … | --done N +TM_KEEP_TRACE=1 …rol.py --buy zh RC RB --n=3 # покупка с сохранением трейса +``` + +--- + +## 5. ЛОВУШКИ, ДОБАВИВШИЕСЯ ЭТОЙ СЕССИЕЙ + +1. **Гейт, сертифицирующий правило по ключевому слову, обходится словом из правила + ПРОТИВОПОЛОЖНОГО смысла.** Мой гейт покрытия считал «убирай лишние повторы» покрытым словом + «разнообразь», которое стоит в правиле «НЕ разнообразь». Поймала приёмка Fable-5. +2. **Правила короче 25 знаков гейт не сканировал вовсе** — порог длины был слепым пятном. +3. **Мёртвая строка таблицы неотличима от живой.** Заведена проверка «каждая строка обязана + сработать хотя бы раз». +4. **Свой же пакет чтения может нести подсказку.** См. §0. +5. **Тревога собственной проверки может быть ложной** (слово «метки» в рамке). Вскрывать, а не + отменять. +6. **Фриз-гейт кассы срабатывает на ЛЮБУЮ правку покупающего файла** — правил код после фриза, + покупка отказана. Это правильно; фризить ПЕРЕД покупкой. +7. **Оборванные на потолке клетки надо уводить в карантин ПЕРЕД перекупкой** — кэш `purchase` + смотрит на существование файла и вернёт старую запись. +8. **Ключ каждой панели чтения — в СВОЁМ файле.** Пере-эмиссия с другим составом армов переписала + бы раскладку уже прочитанной панели, и ответ читателя стал бы мусором молча. + +--- + +## 6. ЧТО СПРОСИТЬ У ВЛАДЕЛЬЦА ПЕРВЫМ ДЕЛОМ + +1. **Кап агент-сессий: осталось 7 из 100**, а на полное чтение 16 глав нужно 16. Поднимать? +2. ~~**P42**~~ — **СНЯТ С ПОВЕСТКИ РЕШЕНИЕМ ВЛАДЕЛЬЦА 20.08.** См. §8. +3. ~~Нужны ли новые редакции промта в английской панели~~ — **ВЛАДЕЛЕЦ СКАЗАЛ ДА (20.08).** + См. §7. + + +--- + +## 7. РЕШЕНИЕ ВЛАДЕЛЬЦА 20.08: НОВЫЕ РЕДАКЦИИ ПРОМТА — В АНГЛИЙСКУЮ ПАНЕЛЬ + +Дословно: «Нужны, можешь занести в хэндофф?» + +**Что докупить ПЕРЕД чтением английской панели:** + +``` +арм что это модель клеток ~цена +RN роль, экзамен с риском на смелости deepseek-v4-pro 16 $0.35 +RC роль, экзамен ВЫРЕЗАН deepseek-v4-pro 16 $0.35 +RB роль, экзамен ПЕРЕВЁРНУТ (лучшая редакция) deepseek-v4-pro 16 $0.35 + ИТОГО ≈ $1.05 +``` + +⚠ **Смета ОРИЕНТИРОВОЧНАЯ и снята с КИТАЙСКИХ клеток.** Английская глава короче китайской, но +норма, выученная этой сессией дорого: цену клетки нельзя оценивать по клеткам другого промта или +другой пары. Перед покупкой прогнать `rol.py --dry` и сверить с фактом на первых трёх клетках. +Касса ФД-N: потрачено $0, потолок $2.40 — влезает с запасом. + +⚠ **Порядок работ жёсткий:** сперва докупить, потом ПЕРЕ-эмитировать пакеты с расширенной панелью +под НОВЫМ тегом (`--tag=arch2`), потому что пере-эмиссия с другим составом армов переписывает +раскладку меток. Существующие 16 пакетов `arch` НЕ трогать — если их уже прочитали, ответы +станут мусором. + +**Панель станет из 11 армов:** `ZD Z0 ZF ZP Z8 Z8R Z1 Z7` + `RN RC RB`. +⚠ Одиннадцать вариантов одной главы — много для одного читателя. Решать владельцу: либо две +сессии на главу с разными половинами панели и общим якорем (`Z0` в обеих), либо панель урезать. +Резать решением сессии ЗАПРЕЩЕНО заказом. + +**Команда после покупки:** +``` +eval/.venv/bin/python eval/dovodka/rol.py --emit-read en \ + --panel=ZD,Z0,ZF,ZP,Z8,Z8R,Z1,Z7,RN,RC,RB --tag=arch2 --each --n=16 +``` +и обязательная сплошная сверка сборки — образец кода в журнале §15.21 (128 текстов побайтно +против клеток, отсутствие имён армов в пакете, нейтральность рамки, разные раскладки). + + +--- + +## 8. РЕШЕНИЕ ВЛАДЕЛЬЦА 20.08: SOL ПАРКУЕТСЯ, СУДИТ ТОЛЬКО FABLE + +Дословно: «Давай пока забьем на сол и отдадим все судейство фаблу. Посмотрим что он скажет на +одинаковых главах и какое судейство проведет. А потом на сол если хватит времени посмотрим». + +**Что это меняет механически:** + +* **P42 снят с повестки, но НЕ закрыт.** Противоречие двух пре-рег-правил о маржевом гейте + существует и касается единственного CONFIRM фазы (H-2а). Оно перестаёт быть блокером, потому + что вердикт выносил СЧЁТЧИК, а счётчик понижен до брак-скрина. Когда дойдут руки до Sol — + вернуть в повестку. **Не считать «снят» синонимом «решён»: правило в пре-реге по-прежнему + противоречит само себе, и следующая сессия обязана это видеть.** +* **Норма П-1 «два судейских семейства» временно не исполняется, и это объявленное отступление, + а не забывчивость.** Все вердикты нового прибора выносятся ОДНИМ семейством (fable). Писать это + рядом с каждым выводом, как фаза писала «вывод стоит на одном семействе» по японской ноге. +* **Контроль вместо второго семейства — внутренний:** в каждой панели чтения стоят ДВЕ генерации + одной боевой связки (`Z0`/`ZF`). Читатель, который разводит их далеко, сам себя дисквалифицирует; + читатель, который ставит их рядом, доказал разрешение на этой панели. За четыре прогона Fable + трижды опознал эту пару чтением, НЕ будучи о ней предупреждён. +* **Что Sol всё ещё может дать, когда вернёмся:** он единственное независимое семейство, и его + расхождение с fable будет мерой «сколько в вердикте от прибора, а не от текста». Пока этой меры + нет вовсе — это главная незакрытая дыра нового прибора, и её надо называть вслух. + +**Куплено под расширенную панель (санкция «Нужны»):** `RN`/`RC`/`RB` на английской паре, +16 глав каждый, смета $1.05, касса ФД-N (потолок $2.40). Покупка шла с `TM_KEEP_TRACE=1`. + +--- + +## 9. ⛔⛔ ПОПРАВКА К СОБСТВЕННОМУ ВЫВОДУ, СДЕЛАННАЯ В КОНЦЕ СЕССИИ + +**Английская докупка опровергла вывод §2.1, и его надо читать вместе с этой поправкой.** + +``` + китайская пара английская пара +прежняя склейка 950 4 494 +промт-РОЛЬ (RN) 27 039 (×28) 5 804 (×1.29) +рез критериев (RC) 11 268 (×12) 3 586 (×0.80) +``` + +⇒ **Разгон размышления ×28 — свойство КИТАЙСКОГО МАТЕРИАЛА, а не промта.** На английском тот же +самый промт стоит на четверть дороже склейки, а резаный вариант — ДЕШЕВЛЕ неё. +⇒ Формулировка «промт, который просит творчества, оплачивается вторым черновиком» СНЯТА как общее +утверждение: она верна на плотном ханьском входе и не воспроизводится на английском. + +⚠ **И это было в нашем же вендор-бюллетене, прочитанном в тот же день:** `00-provider-quirks.md` +п.7 — «разгон думания привязан к ПЛОТНОМУ ХАНЬСКОМУ ВХОДУ, не к модели вообще: при сопоставимом +входе zh требует ×7.8 размышления против en». Я это читал и всё равно приписал эффект промту. +**Норма: эффект, замеренный на ОДНОЙ паре, не называть свойством промта, пока он не проверен на +второй. Пара — конфаундер по умолчанию, а не деталь.** + +**ЧТО ПРИ ЭТОМ УСТОЯЛО И СТАЛО ПРОЧНЕЕ.** Черновик в уме модель пишет на ОБЕИХ парах: +``` +китайская 93–97% финального текста написано внутри размышления (6 клеток) +английская 88–94% (47 клеток) +``` +Механизм подтверждён на 53 клетках. Само-проверки в трейсе 4%. ⇒ **размышление у dspro — черновик, +а не проверка**, это остаётся в силе. Меняется СЛЕДСТВИЕ: не «двухстадийность спасает от второго +черновика», а «второй черновик пишется всегда, но дорого обходится только на плотном входе». + +**ЧТО ЭТО МЕНЯЕТ ДЛЯ ПРОДУКТА.** Возражение «однопроходка дорога» снимается ДЛЯ АНГЛИЙСКОЙ ПАРЫ — +той самой, где владелец при слепом чтении поставил однопроходку ПЕРВОЙ. На китайской возражение +остаётся. Вопрос переформулируется: не «однопроходка дорога вообще», а «однопроходка дорога на +плотном китайском» — и это вопрос к материалу, а не к архитектуре. + +--- + +## 10. ПОЛНЫЙ СПИСОК АРТЕФАКТОВ СЕССИИ (для компакта — что где лежит) + +**Код зоны, изменён/создан:** +``` +eval/dovodka/rol.py однопроходка как РОЛЬ: сборка промта, гейт покрытия + боевых правил, покупка, эмиттер слепого чтения +eval/dovodka/prompts/onepass-core.md ядро нового промта (пар-блоки подтягиваются из пары) +eval/dovodka/podacha.py замер подачи входа критику + restore_paragraphs +eval/dovodka/itog_d4.py + режим --sens (Г5) и рычаги --drop/--restore +eval/dovodka/money_d.py + фазы ФД-K/L/M/N, пакетный потолок 13.80 → 18.30 +eval/dovodka/runs.py кап 100 → 200 (слово владельца 20.08) +eval/role_topology/buy.py + сохранение трейса по флагу TM_KEEP_TRACE=1 +eval/dovodka/HANDOFF-21-08.md этот файл +eval/dovodka/КОНТЕКСТ-ДЛЯ-КОНСУЛЬТАЦИИ.md картина проекта для внешнего собеседника +eval/dovodka/PLAN-16-08.md + баннер «закрыт» +eval/dovodka/PLAN-17-08.md + шапка состояния на 20.08 +eval/dovodka/SESSION2-LOG.md + разделы 15.1–15.22 +docs/experiments/23-editor-tier.md + секции Д21 (Г5) и Д23 (пре-рег промта-роли) +``` + +**Сырьё:** +``` +~/books/dovodka/dv-k-*, dv-l-* замер подачи критику (zh куплен частично, en не начинался) +~/books/dovodka/dv-m-* промт-роль на китайской паре (RN/RC/RB) +~/books/dovodka/dv-n-* промт-роль на английской паре (RN/RC/RB, 47 клеток) +~/books/dovodka/podacha-plants.json правильные ответы посадок +~/books/dovodka/blind-keys-rol/ ключи всех панелей чтения + READERS-*.json +~/books/chtenie-rol/ЧТЕНИЕ-en-arch2-*.md 15 готовых пакетов +~/books/chtenie-vladeltsa-z17/ОТВЕТ-*.FABLE.md, *.ЧИСТЫЙ.md калибровка прибора +~/books/chtenie-vladeltsa-z17/ЧТЕНИЕ-*.НЕЙТРАЛЬНЫЙ.md пакеты без подсказки +``` + +**Коммиты сессии (полигон коммитит только фризы покупающего кода):** +`650b4fe` `824bd20` `273d2df` `7f15323` `3075566` `276c85b` `db02b7a` и фикс пропуска глав. + +**Деньги на конец сессии:** +``` +ФД-K 0.2927/0.40 · ФД-L 0.0000/1.30 · ФД-M 2.0066/2.10 · ФД-N 1.3593/2.40 +ПАК 15.38/18.30 · свободно 2.92 +``` diff --git a/eval/dovodka/PLAN-16-08.md b/eval/dovodka/PLAN-16-08.md new file mode 100644 index 00000000..47c26073 --- /dev/null +++ b/eval/dovodka/PLAN-16-08.md @@ -0,0 +1,566 @@ +> ⚠ **ЗАКРЫТ И ВЫПОЛНЕН.** Это ПРОШЛЫЙ курс (16.08); он привёл к заходу Д17 и +> исчерпан им. Живой курс — `PLAN-17-08.md`, состояние на сегодня — `HANDOFF-21-08.md`. +> Читать целиком НЕ надо: здесь только исторические основания решений захода Д17. + +# ПРОМТ-ПЛАН полигон-сессии: фаза Д, продолжение после сессии №2 (16.08) + +> Пишется САМОМУ СЕБЕ на случай сжатия контекста и передачи. Читать ЦЕЛИКОМ, не грепом: +> прошлая передача потеряла целый пункт заказа именно из-за чтения грепом. +> Предыдущие слои: заказ `docs/POLYGON_EXP2223_REDO_SESSION_PROMPT.md` (207 строк, читать целиком) · +> хендофф №1 `docs/POLYGON_PHASE_D_HANDOFF.md` · рабочий журнал `eval/dovodka/SESSION2-LOG.md` (рядом). +> Отчёт фазы — `docs/experiments/23-editor-tier.md`, секции Д0–Д16 (~2500 строк). + +--- + +## 0. ГЛАВНОЕ В ОДНОМ АБЗАЦЕ + +Дуга экспериментов 19→23 честно ответила на вопрос «какая модель лучше в роли редактора» и +устойчиво его закрыла. Но она мерила **счёт локальных ошибок**, а продуктовая цель владельца — +**живая русская проза без translationese**, и под неё прибора нет. 16.08 слепое чтение показало, +что на английской оси порядок по счёту ошибок и порядок по читаемости **не связаны вовсе** +(Спирмен −0.10), причём наш прибор штрафовал ровно то, что покупает читаемость — вольность +обращения с буквой. Владелец постановил: **штрафовать можно только за перевирание смысла, за +вольность — нельзя**. Это меняет шкалу, и старые судейские числа с новыми сравнивать будет нельзя. +⇒ Следующий шаг — не новые модели, а **починка прибора**, затем маленький честный заход. + +### 0.1 Словарь — расшифровать до чтения дальше + +* **Боевая связка** (то, что стоит в продакшене): дешёвая модель `deepseek-v4-flash` переводит + главу начерно → дорогая `deepseek-v4-pro` получает исходник ПЛЮС черновик и **переписывает всё + заново**. Два вызова. В таблицах обозначена `A0` (китайская ось), `E0` (английская), `J0` (японская). +* **Однопроходка**: ОДИН вызов дорогой модели, которой дают исходник и **объединённую инструкцию** — + переводчика плюс редактора. Ни черновика, ни второго прохода. Обозначена `A2` / `E2` / `J2`. +* **Точечный контур**: черновик + нечто, что находит места ошибок (детерминированные флаги ЛИБО + LLM-критик) + фиксер, правящий ТОЛЬКО указанные места. Обозначен `A1`/`A1B` (флаги) и `A3` (критик). + Трогает единицы процентов текста, остальное остаётся черновиком — в этом его слабость. +* **Канон-фиксер ПОСЛЕ**: боевая связка плюс отдельный проход, восстанавливающий термины банка + (`A4` / `E4`). Единственный арм, чинящий канон, не двигая судейскую ось; цена аддитивна. +* **Гипотезы владельца:** H-1 «проблема в черновике» (хороший черновик + точечный редактор не хуже + связки) · H-2а «флаги → фиксер не хуже переписа» · H-2б «смысловой критик → фиксер ЛУЧШЕ переписа» + (главная ставка) · H-3 «на английском перепис не нужен» · H-4 «перевес dspro — свойство китайской пары». +* **Пол (шумовой)**: пара из двух генераций ОДНОГО лечения. Разница их оценок = шум прибора, + из неё строится порог различимости. Половины обязаны судиться РАЗНЫМИ сессиями. +* **Декой**: намеренно испорченный вариант в пачке. Грубый — ловит «судья вообще смотрит?». + Маржевый — тонкая порча размером с искомый эффект, ловит «судья способен увидеть разницу такого + размера?». Без второго вывод «не различимо» неотличим от слепоты прибора. + +--- + +## 1. РЕШЕНИЯ ВЛАДЕЛЬЦА — дословно и с последствиями + +| дата | что сказал | что это значит механически | +|---|---|---| +| 15.08 | «Подтверждаю подъём расходов, сколько тебе нужно» | потолок записан числом **$6.85** в `money_d.py`; на 16.08 потрачено ~$6.09 | +| 15.08 | про кап агент-сессий: «почему оставшиеся? ты можешь наспамить под 90» | кап 80 снят его словом; `runs.py` продолжает считать СУДЕЙСКИЕ сессии (на 16.08 — 72) | +| 15.08 | «Можно пересудить [tier]. Но не подгонять. Нужно понять, кто ошибается из судей» | исполнено, см. §3.2 | +| 15.08 | «gpt 5.6 sol это и есть модель» | **Sol = семейство OpenAI** ⇒ его возражение по `tier` дисквалифицировано (судил свою семью) | +| 15.08 | про японскую ногу: «Бери, ладно» | куплено и отсужено, см. §3.3 | +| 15.08 | про ja-книгу: «не помню; замена была потому что флагнули по 18+; кажется, я аппрувнул выкачивание» | дословной санкции на ЗАМЕНУ нет; `R37` реестра честно красный; в отчёте писать этой формулировкой, НЕ «по слову владельца» | +| 16.08 | «Штрафовать за вольность нельзя — живой язык один из приоритетов бэкенда. Штрафовать можно только за перевирания смысла исходника. В остальном можно менять, убирать англоязычность, переставлять текст» | **МЕНЯЕТ РУБРИКУ СУДЬИ.** См. §4.1 — это первый пункт плана | +| 16.08 | «Новые траты я разрешаю, перепровести эксп я разрешаю» | санкция на заход §4.3 | +| 16.08 | про цены DeepSeek: «мы с оркестратором проводим пересчёт его цен и внесём в документацию, после я тебе скажу (или ты мне напомни)» | **НАПОМИНАТЬ.** См. §5.1 — это блокер денежных выводов | + +--- + +## 2. ЧТО УЖЕ ПОСТРОЕНО И РАБОТАЕТ (не переделывать) + +Всё в зоне `eval/dovodka/`, все селфтесты зелёные, линтер чист. + +| файл | что делает | команды | +|---|---|---| +| `gain.py` | **калибровка усиления судьи** — ловит пачку, легшую на пол шкалы | `--density` (гейт: нулей ≥25% → пачка не несёт «не различимо») · `--samearm` · `--agree` · `--floor` · `--tier` · `--selftest` | +| `naklon.py` | **гейт позиционного наклона** (норма требовала, кода не было) | без флагов — все 7 проходов; `--pass=d4`; `--selftest` (синтетика с известным ответом) | +| `tier2.py` | пере-судейство прохода `tier` починенным заданием | `--emit` · `--score` · `--selftest` | +| `ja6.py` | японская нога H-4: панель `J0`/`J6`/`D0` + контроли; ⚠ `read()` чинён 16.08 — считает и разведённые `p{1,2}-answers` своего семейства, и ПЛОСКИЙ `answers/` внешнего пакета | `--emit` · `--score` · `--sol` (пакет внешнему судье) · `--score-sol` · `--selftest` | +| `chtenie.py` | **прибор ткани**: слепое чтение человеком | `--emit` · `--score` | +| `adjud.py` | адъюдикация находок; **контроли починены** (4 дефекта) | `--emit` · `--controls` (гейт неразличимости классов) · `--score` · `--selftest` | +| `itog_d4.py` | свод оси; чинён: считает ОБА семейства, печатает **полную цену единицы** | `--axis=d4\|d3\|d6\|d1` `--fam=claude\|sol` `--gates` | +| `sud.py` | судейский риг; селфтест **перестал быть прибит к китайской панели** | `--axis=<ось> --selftest` | +| `money_d.py` | касса, потолок $6.85, фазы ФД-A…ФД-H | `--report` · `--selftest` | +| `runs.py` | журнал судейских сессий, запись ДО запуска | `--claim <прогон> <проход> <ток>…` · `--done ` · `--status` | +| `conformance.py` (в `eval/`) | сверка с буквой заказа; **починен парсер** | `eval/conformance.py eval/dovodka/requirements.md --plan` | + +**Три коммита-фриза сессии №2** (полигон коммитит только пре-рег-фризы, основание вслух ПЕРЕД +каждым): `a03ac66` (пре-рег `tier2` + `gain.py`) · `8c68828` (покупающий код ja-ноги) · +`22a8a6b` (покупающий код пола ja-ноги + журнал). Остальное дерево НЕ коммичено — лендит оркестратор. + +--- + +## 3. ЧТО УСТАНОВЛЕНО — с числами, которые нельзя потерять + +### 3.1 Прибор: строгость счёта есть свойство ПРОГОНА, а не оси + +Тот же арм `R0`, те же 16 единиц, то же семейство судей, подписи текста совпадают **16/16**: +проход `tier` дал **0.69** ошибки на единицу при 9 нулях из 16, проход `border` — **4.31** при нуле +нулей; по-единично ниже в 15 из 16. Размах внутри семейства claude 1.41…6.47. + +⇒ **межпроходные сравнения абсолютных чисел недействительны.** Под это попадает вывод «дешёвая +модель на японском проваливается сильнее других пар» (5.44 против 2.78 — разные прогоны) и все +заимствованные пороги. Внутри одной пачки сравнения законны — там сдвиг сокращается в контрасте. + +Доля нулей по проходам claude: `tier` 38% (24% без обоснования) · Д3 en 10% · Д6 3% · +`border` 0 · **Д4 zh 2 нуля на 713 клеток** · Д1 0. По сессиям выбиваются ровно две +(`d3r2/p1/д-52` 24%, `d3r2/p2/д-55` 21%). ⇒ выводы Д4/Д1/`border` этой болезнью НЕ заражены. + +### 3.2 Проход `tier` пере-сужен — вывод пака 23 восстановлен + +Пре-рег зафризен `a03ac66` ДО первого ответа. Те же тексты, новый ключ со своей солью, старый не +тронут. Правки: убран `CTRLfloorA` (был побайтно равен боевому арму `T1` в 16/16), добавлен +маржевый декой, запрещён молчаливый ноль, в рубрику внесены пропускавшиеся классы. + +Результат на 16 единицах, 126 клеток, замечаний годности 0: + +``` +арм старый новый сдвиг гейты +R0 боевой 0.69 3.44 +2.75 плотность 3.77, нулей 6% (было 38%) — ГОДНО +T1 glm-5.2 1.00 3.56 +2.56 пол 16 пар sd 2.63 → порог 1.84 +T2 gpt-5.6-terra 0.25 2.38 +2.12 грубый декой 16/16 +T3 grok-4.5 0.62 2.94 +2.31 МАРЖЕВЫЙ ДЕКОЙ +2.50 против 1.84 — ПРОЙДЕН +F голый черновик 2.56 6.56 +4.00 +``` + +Вердикт: `T1` −0.12 · `T2` **+1.06** · `T3` +0.50 — все ниже порога 1.84; контроль `R0 vs F` +−3.12 p=0.0042. ⇒ **«сильный тир не отличим от боевого редактора» ВОССТАНОВЛЕН и впервые стоит +на приборе с доказанной чувствительностью.** Возражение Sol (+8.69) не воспроизводится (+1.06) +и вдобавок дисквалифицировано: `gpt-5.6-terra` — семейство самого Sol. + +⚠ Пере-судейство сделано ОДНИМ семейством: второе на этом контрасте дисквалифицировано. + +### 3.3 Японская нога H-4 — куплена, отсужена, гейты зелёные + +`J6` = `glm-5` поверх той же японской базы, 9 клеток, $0.046329, все `finish=stop`. Плюс свой пол +(7 из 9 вторых генераций редактора; один вызов завис на 9+ минут — класс известен). + +``` +J0 deepseek-v4-pro 1.44 J6 vs J0 −0.44 p=0.6875 ниже порога 2.17 +JFLO вторая генерация 1.86 J0 vs D0 +2.78 p=0.0039 редактор бьёт черновик +J6 glm-5 1.89 грубый декой +2.89 ПРОЙДЕН · маржевый +2.80 ПРОЙДЕН +D0 черновик 4.22 +``` + +**Разница между ДВУМЯ редакторами (0.44) не больше разницы между двумя прогонами ОДНОГО +редактора (0.42).** Это самый чистый способ сказать «не различимо». + +**Второе семейство прогнано владельцем 16.08 (`ja6.py --score-sol`) и СВОИ КОНТРОЛИ НЕ ВЗЯЛО:** +плотность 5.06 (нулей 6%) · пол 7 пар sd 2.64 → порог 2.80 · **грубый декой +2.22 против 2.80 — +НЕ ПРОЙДЕН · маржевый +1.80 — НЕ ПРОЙДЕН**. Декой пойман ПО НАПРАВЛЕНИЮ, но собственный шум Sol +так широк, что даже намеренная грубая порча в его порог не укладывается ⇒ **на японской оси у Sol +нет разрешения**, его пачка по норме аннулируется. Направление при этом совпало: `J6/J0` −1.00 +(мои −0.44), `J0/D0` +4.00 (мои +2.78). Вывод по H-4 не меняется, но опирается на ОДНО семейство. +Третий раз за фазу пол Sol оказывается вдвое шире — свойство его харнесса, а не оси. + +⇒ **H-4 не подтверждается на всех трёх парах**: `deepseek-v4-pro` первый на zh, на en (1.31 +против 2.72) и на ja (1.44 против 1.89). Порядок редакторов от языка не зависит. Требование +заказа (:115) исполнено ВПЕРВЫЕ. + +### 3.4 Разложение контрастов ПО ОСЯМ — за всю дугу не делалось ни разу + +``` +A1B/A0 −3.13 = ВЕРНОСТЬ −0.94 + ЯЗЫК −2.19 +A3/A0 −1.87 = ВЕРНОСТЬ −0.26 + ЯЗЫК −1.61 ← ставка владельца +A6/A0 −1.06 = ВЕРНОСТЬ +0.12 + ЯЗЫК −1.19 ← носитель H-1 +A4/A0 −0.13 +E0/D0 +1.25 = ВЕРНОСТЬ +0.94 + ЯЗЫК +0.31 + ТЕРМИН +0.75 + ФОРМА +0.69 +J0/J2 +0.11 = ВЕРНОСТЬ 0.00 + ЯЗЫК +0.11 +``` + +⇒ дешёвые контуры проигрывают переписи **прозой, а не смыслом**; носитель H-1 по смыслу +боевой связке не уступает вовсе; на английском редактор покупает в основном термины, верность и +вёрстку — то есть H-3 опровергнута только в слове «ТОЛЬКО»; на японском второй проход по смыслу +не покупает ничего. + +### 3.5 Экономика — полная цена ЕДИНИЦЫ, а не вызова + +``` +zh: A2 однопроходка 0.00408 · A0 связка 0.00603 · A1B 0.00793 · A4 0.01419 · A3 0.01546 (2.56×A0) +en: E2 однопроходка 0.00471 · E0 связка 0.00885 · E4 0.01321 +черновик flash 0.00096 · перепис dspro 0.00507 · gpt-5.6-terra 0.04408 (×9) · grok-4.5 0.05276 +``` + +⇒ «кандидат в прод вдвое дешевле» ЛОЖНО — он в 1.5–2.4 раза **дороже**; это платная страховка +канона. Ставка H-2б стоит 2.56× боевой связки и по счёту ошибок хуже неё. + +### 3.6 ⛔ СЛЕПОЕ ЧТЕНИЕ 16.08 — ПЕРЕВОРАЧИВАЕТ АНГЛИЙСКУЮ ОСЬ + +Пакет `~/books/chtenie-vladeltsa/` (`ЧТЕНИЕ-zh.md`, `ЧТЕНИЕ-en.md`), **ответы там же**: +`ОТВЕТ-zh.md` и `ОТВЕТ-en.md` — это улики, их не терять. Ключ отдельно в +`~/books/dovodka/blind-keys-chtenie/chtenie-KEY.json`. Пере-счёт: `chtenie.py --score`. +Читал не владелец лично, а **Fable по его поручению**; владелец мнение одобрил и сам отметил, +что «нашёл меньше замечаний, перевод читаемый». + +| ось | порядок судьи (счёт ошибок) | порядок читателя | Спирмен | +|---|---|---|---| +| **zh** (единица `ed068182cf`) | `A0` > `A4` > `A6` > `A3` > черновик | `A4` > `A0` > `A6` > черновик > **`A3`** | **+0.80** | +| **en** (единица `27cb3a7e69`) | `E0` > `E3` > `E2` > черновик > `E6` | **`E2`** > `E6` > `E0` > `E3` > черновик | **−0.10** | + +* На китайском прокси держится; перестановка `A0`/`A4` внутри шума. Но читатель поставил `A3` + (смысловой контур, ставка владельца) **последним, ниже голого черновика**. +* На английском связи нет вовсе. **Однопроходка `E2` — первая у читателя, третья у судьи; + наша боевая связка `E0` — третья у читателя, первая у судьи.** +* ⚠ **Читатель назвал механизм ВСЛЕПУЮ и попал:** «у Т3 локальных отклонений от оригинала больше, + чем у Т2, а читать его заметно лучше. Счётчик ошибок должен был поставить Т3 ниже Т2 — если так + и вышло, порядок разошёлся не случайно, а системно». Т3 = `E2`, Т2 = `E0`. Так и вышло. + +⚠ Одна единица на ось, английская всего 1.9 тыс. знаков, читатель — модель. Это **калибровка, +а не замер**: она говорит «прибор и читатель расходятся», а НЕ «однопроходка лучше связки». + +### 3.6а Черновая роль: панель мерялась, победила ценой + +Эксп-22 мерил ШЕСТЬ кандидатов черновой роли: `deepseek-v4-flash` (медиана $0.00096) · +`gemini-3.1-flash-lite` ($0.00385) · `deepseek-v4-pro` ($0.00387) · `gpt-5.6-luna` ($0.00357) · +`glm-4.7` ($0.00656) · `grok-4.3` ($0.00976). Вердикт — **ничья по качеству, побеждает самый +дешёвый** (правило D39.117). То есть «поставить дипсик-про черновиком» УЖЕ проверено и не выиграло. +⇒ Низкое качество черновика — свойство не модели, а РОЛИ (и, возможно, промта — см. `K8` в §4.3). + +Двухступенчатость бьёт однопроходку на всех трёх парах ПО СЧЁТУ ОШИБОК: zh 4.00 против 6.51 · +en 1.31 против 2.38 · ja 2.22 против 2.39. ⚠ Но на английском слепое чтение дало обратный порядок +(§3.6), и это расхождение не разрешено. + +### 3.7 Что уцелевает независимо от смены шкалы + +1. Редактор поверх дешёвого черновика покупает много — три языка, два семейства судей, слепое + чтение (черновик внизу везде). +2. Дорогие редакторы не бьют `deepseek-v4-pro` при цене ×9 — на приборе с сертификатом. +3. Выбор редактора от языка не зависит. +4. Банк терминов работает; **банк-дисциплина и качество прозы — разные умения** (gemini: канон + 0.986 против 0.884 у боевого при равной судейской оси). +5. Схемы «залатать черновик точечно» проиграли и по счёту, и по чтению. ⚠ Но точечный ремонт + **не выброшен ратификацией**: D39.117 п.3 отклонил его как ЗАМЕНУ редактора и принял как + «механический ПРЕ-ГЕЙТ ПЕРЕД редактором» и «ремонт ТОЛЬКО с банком». Число «$0.0002 чинит всё» + из эксп-20 при этом мерилось при ИДЕАЛЬНОЙ детекции и как несущее — пало; в фазе Д оно всё ещё + служило основанием армов и прогнозов. Не переиспользовать без оговорки. +6. Гипотеза эксп-04 «проза gemini — аутлаер» не подтверждается (после снятия запрещённой клетки + с `finish=length` точечная оценка ровно 0.000). +7. Потери канона у боевого редактора — в основном пере-формулировка, а не пропажа термина + (⚠ но автоматическая КЛАССИФИКАЦИЯ этих мест негодна — секция Д5 отчёта объявлена + НЕИСПОЛНЕННОЙ, см. §5.3). + +--- + +## 4. ПЛАН. Порядок здесь важнее содержания + +### 4.1 ШАГ ПЕРВЫЙ — ПОЧИНИТЬ СУДЬЮ ПОД ПРАВИЛО ВЛАДЕЛЬЦА ($0) + +Правило 16.08: **штрафовать только за перевирание смысла исходника; за вольность — нельзя.** +Живой язык — приоритет продукта; переставлять текст, убирать англоязычность, менять структуру +разрешено. + +⚠ **Рубрика живёт в ЧЕТЫРЁХ местах, править надо все, иначе семейства разъедутся:** +`eval/dovodka/judge-prompts/core.md` (ядро обвязки) · `claude.md` и `sol.md` (обвязки семейств) · +константы `HEAD` в эмиттерах `sud.py`, `tier2.py`, `ja6.py` (шапка САМОГО задания — именно она +достаётся обоим семействам и обеспечивает паритет П-4). После правки прогнать `paritet.py` +и `promptdiff.py`. + +Что сделать в рубрике: + +* **ВЕРНОСТЬ остаётся и сужается до искажений факта:** инверсия адресата реплики · снятое или + добавленное отрицание · подмена числа, разряда, ранга, единицы · выдуманный или потерянный факт · + перевёрнутое состояние действия/идиомы. Это и есть «перевирание смысла». +* **ЯЗЫК разворачивается:** сейчас он ловит «то, чего носитель не напишет». Оставить ТОЛЬКО это + (калька, канцелярит, несуществующее слово, рассогласование) и **явно запретить штрафовать за**: + перестановку предложений, слияние/дробление абзацев, замену оборота на более живой русский, + синонимическую замену, добавление связок. Прямо написать судье: «вольность ради живой русской + фразы — НЕ ошибка; ошибка — только то, что носитель не напишет». +* **ФОРМА**: сейчас не входит в несущую сумму. Проверить, не штрафует ли она ИСПОЛНЕНИЕ мандата + перевёрстки (такое уже переворачивало знак вывода однажды). +* **ТЕРМИН** отдан детерминированному банк-гейту, судью им не грузить. + +⚠ **Последствие, которое обязано быть напечатано:** после смены рубрики старые судейские числа с +новыми **несравнимы**. Все панели дуги останутся действительными только внутри своей шкалы. + +⚠ Пере-судить по новой рубрике придётся хотя бы **боевую связку против однопроходки** на обеих +осях — иначе новую шкалу не с чем сверить. + +⚠⚠ **ПОРЯДОК ИЗМЕНЁН ПО ЗАМЕЧАНИЮ ВЛАДЕЛЬЦА 16.08.** Первая редакция ставила чтение ПЕРЕД +покупкой новых армов. Это половина работы впустую: читать старые армы, зная, что панель меняется, +незачем. Правильный порядок — **починить рубрику → докупить новые армы → ОДНО слепое чтение по +ПОЛНОЙ панели (старые + новые) → отсудить починенной рубрикой.** Тогда чтение делается один раз и +отвечает сразу на оба вопроса: следит ли счётчик за читаемостью И какая архитектура читается лучше. +Читай §4.2 и §4.3 в этом порядке: сначала 4.3 (покупка), потом 4.2 (чтение по всему). + +### 4.2 ШАГ ТРЕТИЙ — ПРИБОР ТКАНИ НА НОРМАЛЬНОМ n ($0, только агент-сессии) + +Одна единица на ось ничего не решает. Нужно слепое ранжирование на **8–12 единицах на ось**, +двумя-тремя независимыми читателями (агенты РАЗНЫХ семейств: claude, Sol через владельца, Fable). + +Контроли (они уже описаны в пре-реге П-6, `23-editor-tier.md:1114-1122`): +* декой обязан ранжироваться последним; +* половины шумового пола — соседями (если читатель разводит две генерации одного лечения далеко, + его ранжирование — шум); +* порядок вариантов перемешан, метки слепые, ключ вне рабочего каталога. + +Готовый скелет — `chtenie.py`; нужно расширить с одной единицы на выборку и добавить контроли. + +**Исход решает всё дальнейшее:** ранги согласуются со счётом ошибок → счёт остаётся дешёвым +прокси; расходятся (как сейчас на en) → **до починки прибора новые платные замеры бессмысленны**. + +### 4.3 ШАГ ВТОРОЙ — МАЛЕНЬКИЙ ЧЕСТНЫЙ ЗАХОД (санкция владельца 16.08 есть) + +⚠ **Смета по НОВОМУ прайсу: ~$2.9 на пике, ~$1.7 на офф-пике** (прежняя оценка «около доллара» +считалась по июльскому пину). Покупать офф-пик; перед покупкой пере-пинить `roster.py` (§5.1). + +Три схемы, обе пары (zh и en), 16 единиц на пару, базы частично куплены: + +1. **боевая связка** — контроль (черновик → полный перепис); +2. **однопроходка** — один вызов, объединённый мандат (на en она прочиталась лучшей); +3. **черновик → критик → ПОЛНЫЙ ПЕРЕПИС по его замечаниям** (`K1`) — сильнейшая форма идеи + владельца. ⚠ Отличие от проигравшего `A3`: там правщик был ТОЧЕЧНЫМ и трогал проценты текста; + здесь перепис полный, а буллеты критика лишь ОБУСЛОВЛИВАЮТ его. + +Эндпойнты — **оба сразу**: починенный счёт ошибок (§4.1) И ранг читаемости (§4.2). Пре-рег до +покупок: мощность, пороги, правило расхождения, статусы осей. + +⛔⛔ **ОДНОПРОХОДКА ЛОМАЕТ БАНК ТЕРМИНОВ — блокер, найден владельцем 16.08, проверен исполнением.** +`backend/prompts/zh-ru/terminologist.md:22`: «Варианты черновиков (строка `drafts:`) — это +свидетельства, а не голосование». Терминолог видит всю книгу сразу и выбирает канонический перевод +термина, опираясь на то, КАК ЕГО ПЕРЕДАВАЛИ ЧЕРНОВИКИ по кускам. Без черновика майнингу не из чего +собирать свидетельства. +⇒ **Однопроходка не альтернатива связке, а альтернатива только ВТОРОМУ проходу.** Черновик нужен +не только ради качества, но и ради банка. Честное сравнение — не «связка против однопроходки», а +«черновик + перепис» против «черновик + нечто другое поверх него». +⇒ И это объясняет, почему слепое чтение поставило однопроходку первой, а продуктовым решением это +не становится: на ОДНОЙ главе банк не нужен — термины внутри главы согласованы сами собой; на книге +в 1500 глав та же схема даст разъезжающиеся имена. +⚠ **ПОПРАВКА ВЛАДЕЛЬЦА 16.08 — это НЕ блокер, а недостающий кусок архитектуры.** Дословно: +«однопроходка допустима, как вариант чтоб оттуда после вырезать термины и edit точечным +редактором их заменить». Выход однопроходки — тоже перевод, значит он годится терминологу как +свидетельство; банк майнится ИЗ НЕГО, а термины приводятся к канону точечным редактором. +⇒ **Кандидат `K7`: однопроходка → майнинг банка из её выхода → канон-фиксер.** Точечный редактор +здесь адекватен дефекту: термины локальны, а канон-фиксер УЖЕ замерен и работает (покрытие +0.892→0.937 на zh, 0.927→0.956 на en; судейскую ось не двигает). +Цена единицы: zh 0.00408 + 0.00816 = **~0.0122** против 0.00603 у связки (дороже); +en 0.00471 + 0.00436 = **~0.0091** против 0.00885 (вровень). ⇒ на английском `K7` стоит столько же, +сколько связка, и по слепому чтению однопроходка там читалась ЛУЧШЕ — это самый дешёвый способ +проверить, не выиграла ли она случайно. +⇒ Что при этом остаётся правдой: **банк без черновика не собирается**, поэтому однопроходка не +может быть просто «связкой минус первый проход» — ей нужен свой источник свидетельств. + +⇒ **Кандидат `K8`, самый дешёвый из всех: ОБОГАТИТЬ ПРОМТ ЧЕРНОВИКА.** Вопрос владельца 16.08: +«почему такое низкое качество черновика? может, промт черновика от редактора сильно отличается и +черновик тут выступает в роли недоведённого до ума инструмента?» — **подтверждено текстом**: +`backend/prompts/zh-ru/translator.md` = 22 строки, `editor.md` = 41, и разница не в объёме, а в +том, что есть ТОЛЬКО у редактора: весь блок ДИСКУРС-ПЕРЕВЁРСТКИ (четыре шага, как передавать +китайский паратаксис русской гипотаксической прозой) + FEWSHOT с ТРЕМЯ разобранными примерами + +правило чэнъюй обоими компонентами. У переводчика от всего этого одна строка «избегай канцелярита +и калек». +⇒ Замер: тот же черновик тем же `deepseek-v4-flash`, но с промтом, куда перенесены блок +перевёрстки и FEWSHOT. Цена — ЦЕНА ЧЕРНОВИКА ($0.00096/единица, входные токены длиннее и +кэшируются), то есть ~$0.03 на 16 единиц. **Самый дешёвый кандидат в списке и не проверенный ни разу.** +⚠ Контраргумент, который надо снять замером, а не рассуждением: арм `A2` (однопроходка с +УРАВНЕННЫМ мандатом) уже нёс мандатные части промта редактора и всё равно проиграл по счёту +ошибок — **но перенесён ли в него FEWSHOT, из эрраты Э-5 не следует**. Сверить ДО замера: если +примеров там не было, `K8` не дублирует `A2`, а закрывает его дыру. + +⚠ Обязательно в панель — **арм голого черновика на китайской оси**: его там нет, и поэтому вклад +самого контура не отделим от разрыва «черновик против переписа». + +⚠ Если запускать `K1`: сначала починить `contour.py:611-619` — `critic_places()` берёт ЛЮБУЮ +строку, начинающуюся с дефиса, без разбора вердикта, из-за чего фиксеру уходили **подтверждения** +критика («верно», «допустимо», «не ошибка») — 311 строк из 1696 = **18.3%**. Ставка владельца была +испытана инструментом, который на пятой части работы правил заведомо исправное. + +### 4.4 ЧЕГО НЕ ДЕЛАТЬ СЕЙЧАС + +* Новых панелей моделей — вопрос «какой редактор» закрыт устойчиво. +* Роутинга «платить дорого только там, где нужно» — закрыт отрицательно (отборщики-монетки). +* Второго последовательного переписа — итеративный дрейф, и обратная связка уже проигрывала. +* Дифф-контракта вместо переписа — дороже на thinking-модели и не берёт распределённый дефект. +* Любых покупок DeepSeek до пере-снятия прайса (§5.1). + +--- + +## 5. ДОЛГИ И ОТКРЫТОЕ + +### 5.1 ⛔ ДЕНЬГИ — ЛЕДЖЕР НЕ ИЗМЕРЯЕТ, А ВЫЧИСЛЯЕТ (блокер) + +`roster.cost()` ВЫЧИСЛЯЕТ `cost_usd` из зашитого пина; провайдер сумму не возвращает. + +**СТАТУС НА 16.08: цены пере-сняты владельцем с оркестратором — но ТОЛЬКО в бэкенде.** +`backend/configs/models.yaml` несёт новый пик (`prices_checked: 2026-08-15`): flash $0.44/$1.32 +cache-hit $0.014 · pro $1.32/$3.96 cache-hit $0.044. +⛔ **`eval/tenant_panel/roster.py:33-35` ПО-ПРЕЖНЕМУ СТАРЫЙ** (pro 0.435/0.003625/0.87, помечен +«live 08.08»). Значит СЛЕДУЮЩАЯ покупка полигона снова посчитается по июльскому прайсу: касса +покажет неправду, а проекционный гард зарезервирует втрое меньше нужного и пропустит покупку. +⇒ **ПЕРВОЕ ДЕЙСТВИЕ ПЕРЕД ЛЮБОЙ ПОКУПКОЙ — пере-пинить `roster.py`.** Это риг эксп-22 (чужой пак): +по норме — отдельный коммит с его заголовком плюс пере-снятие его гейтов (`verify22.py`, `itog22.py`). + +**ПЕРЕ-СЧЁТ ФАЗЫ ПО НОВОМУ ПРАЙСУ (сделан 16.08, 473 клетки DeepSeek: pro 430, flash 43):** + +| счёт | сумма | +|---|---| +| в кассе (июльский пин), часть DeepSeek | **$5.3996** | +| та же работа по **пиковому** прайсу | **$15.5050** — ×2.87 | +| та же работа по **офф-пику** (50%) | ~$8.95 | +| не-DeepSeek клетки (не пере-считывались) | $2.3852 | + +Деньги УЖЕ списаны по ценам, действовавшим в момент покупки, — там пин был верен, перерасхода нет. +Число важно для БУДУЩЕГО, и оно двигает три вещи: +1. смета заходов утраивается (см. §4.3); +2. потолок $6.85 в кассе больше ничего не охраняет, пока `roster.py` не пере-пинен; +3. ⚠ **правило ничьей может пере-решиться.** Оно звучит «при равном качестве берём дешёвого», и по + нему `deepseek-v4-pro` побеждал `glm-5` и сильный тир при разрыве ×9. Теперь `pro` подорожал + втрое, а `glm-5` (Z.AI) не дорожал — разрыв сжался. На этом правиле стоит вся рекомендация по + редакторской роли; пере-считать её ценой ОБЯЗАТЕЛЬНО. + +Отдельная девиация к объявлению: последняя цифра потолка, приписанная слову владельца в файлах, +была $4.50, а расход дошёл до $6.00 ДО санкции 15.08. Цепь подъёмов прошла все самопроверки, +потому что **фриз-гейт сверяет ПОКУПАЮЩИЙ файл в стеке, а не кассу** — потолки правятся в рабочем +дереве и действуют немедленно. + +### 5.2 ⛔ СВЕРКА ВНУТРЕННЕЙ РЕВИЗИИ: 65 находок из 131 в отчёт не попали + +Источники: `~/books/dovodka/revizia-fazy-D-11-08.json` (82) и `priemka-D4-14-08.json` (49). +Требуют пере-проверки исполнением — я их принял со слов сверки, сам не верифицировал: + +1. **H-1 МОЖЕТ ПЕРЕХОДИТЬ ПОРОГ.** Вердикт `A6/A0` определяют две пачки прогона, объявленного + «валидацией цепочки», судимого 11.08 по НЕ замороженному промту; паритет обвязок для них не + проверялся. Без них: пол n=13 sd 1.4058 → порог 1.0917, `A6/A0` −1.0938 → **ПЕРЕШЁЛ**. + Отчёт печатает «НЕ УСТАНОВЛЕНА». Вместе с §3.4 (`A6` по верности +0.12) картина другая. +2. Пачка `69de717f3f` осталась в замере, хотя её сессия **аннулирована за сравнение вариантов**. + Снятие двигает пороги обоих семейств и все контрасты. +3. Унаследованный гейт честности пола свод не гоняет; на данных claude его вердикт — + «ПОЛ СМЕЩЁН, боевые числа снимаются» (сдвиг +1.8 между наборами p1/p2). claude несёт оба CONFIRM. +4. Два пре-рег-правила о маржевом гейте противоречат: по букве П-1 пачка Sol аннулируется → + у H-2а остаётся одно семейство → «эскалация», а не CONFIRM. +5. Порог назван смещённым в **противоположные стороны** двумя находками; ни одна не в отчёте ⇒ + калибровка порога неизвестна ПО ЗНАКУ. +6. Донор декоя во всех 62 пачках оси Д4 — `A0` (тот же дефект, что объявлен у `tier` и починен в `ja6`). +7. `A4`/`A1B`: клетки оплачены при НУЛЕВОМ изменении текста; `A4/A0` на 4 из 31 нулевой + ПО ПОСТРОЕНИЮ (то же вскрытие сделано для `E4`, для `A4` — нет). + +⚠ Среди неотражённого — **12 инструментов, признанных негодными ВНУТРИ самой ревизии**, и трое +из них гейты числят ЗЕЛЁНЫМИ: `promptdiff.py` (объявленное расхождение матчится по ПРЕФИКСУ строки; +отсутствующий файл пар-пакета гейт не роняет) · `canon.py` (снятая ревью классификация всё равно +пишется в артефакт — отсюда негодность секции Д5) · `material_ja.py` (фильтр AI-меток объявлен +механическим, кода его не существует). Прежде чем опираться на любой из них — вскрыть. + +**Полный список сохранён человекочитаемым:** `~/books/dovodka/СВЕРКА-РЕВИЗИИ-16-08.md` (35 КБ). +Кроме 65 неотражённых находок там **7 прямых противоречий телу отчёта** и **12 инструментов, +признанных негодными внутри самой ревизии** — эти два числа в §5.2 выше не раскрыты, читать в файле. + +### 5.3 Прочие открытые долги + +* **Адъюдикация английской оси** — контроли починены и проверены замером, прогона не было. + ⚠ `adjud.py` намертво прошит на китайскую ось (ключи `blind-keys-d4`, каталог `sud-d4`, + армы `A3`/`A0`) — для английской нужна параметризация. +* **Секция Д5 объявлена НЕИСПОЛНЕННОЙ:** поле `why` в `~/books/dovodka/canon-dissect.json` — + мусор («этот», «родов», «дочь»), классификация внутренней ревизией снята и всё равно писалась. + Честное закрытие — ручная пере-классификация 24 мест ($0). +* **`E1` побайтно равен черновику на 5 единицах из 16** — объявлено в Д14.4, но панель не + пере-считана без них. +* **Реестр требований:** 16 самореферентных улик из 89 (было 19, шесть заменил на артефактные). + Провалов два: `R37` (провенанс ja-книги) и `R64` (реестр не закоммичен — лендит оркестратор). +* **Селфтесты `sud.py`**: d4 зелёный, d3 4 провала, d6 1, d1 2 — все настоящие, диагнозы в журнале. + ⚠ `MIN_FLOOR["d6"]=3` — константа, поставленная под зелень; НЕ править, это вопрос владельцу. +* **Пакет Sol по японской ноге** отдан: `~/books/judging/ja6-sol/ORCHESTRATOR.md`. После прогона — + `ja6.py --score-sol`. Пять промтов сессий, p1 и p2 разведены (проверено: 0 промтов смешивают). +* **Эррата zh-канона**: поправка границы слова живёт в коде, в сохранённый банк не дошла + (завышение +0.0039) — правка закрытой оси, ратифицирует оркестратор. + +--- + +## 6. ЛОВУШКИ — на чём споткнёшься + +1. **НЕ пере-эмитировать существующие слепые ключи.** Раскладка меток — чистая функция от соли, + uid и НАБОРА армов; эмиссия с другим составом переписывает ключ отсуженной оси, и разбор + сопоставит ответы с ЧУЖИМИ армами. Новый проход = НОВЫЙ ключ со своей солью (так сделаны + `tier2`, `ja6`, `chtenie`). +2. **Пакет внешнему судье — ТРЁХУРОВНЕВЫЙ**, как в фазе Д: `ORCHESTRATOR.md` («по одному агенту + на промт») + `prompts/*.md` + задания. Двухдокументная форма эксп-23 НЕ годится: харнесс + владельца читает всё одной сессией, и половины шумового пола попадают одному судье. + Путь к ключу в пакете **не называть** — «не открывай blind-keys/» значит показать пальцем. +3. **Правила счёта — в САМОМ задании**, не только в обвязке: асимметрия инструкций между + семействами купила часть расхождения в 12 раз на проходе `tier` (норма паритета П-4). +4. **Запрет дочерних агентов** вписывать явно — сессия с ними перестаёт быть тем составом, + который зарегистрирован до запуска. +5. **Замок кассы снимать ТОЛЬКО у мёртвого процесса** (`ps -eo pid,cmd | grep ...`). Покупка + идемпотентна — просто перезапускать. Редактор думает 60–90 с на клетку, зависания до 9+ минут + бывают (в паке 22 вызов держал замок 74 минуты); ставить `timeout` ≥900 и не опрашивать часто. +6. **Фриз-гейт кассы**: покупающий код обязан быть ЗАКОММИЧЕН до покупки. Полигон вправе фризить — + основание вслух ПЕРЕД коммитом, форма `git commit -- <явные пути>`, никогда `git add -A`. +7. **`--wide-plants` на японской книге НЕ помогает** (проверено: 5 из 9 в обоих случаях). Регексы + посадок оказались не только пар-, но и КНИГО-зависимы. +8. **Не считать по половине пачки.** Пороги, снятые на неполном поле, читаются увереннее, чем есть + (на этом уже горела ось Д1: порог вырос 1.00 → 1.47, когда пол добрал пары). +9. **Промт черновика и промт редактора радикально асимметричны** (22 строки против 41): у + редактора есть весь блок дискурс-перевёрстки и FEWSHOT с тремя разобранными примерами, у + переводчика — одна строка «избегай канцелярита». ⚠ Но арм `A2` (однопроходка с УРАВНЕННЫМ + мандатом) это учитывал и всё равно проиграл по счёту ошибок — **проверить, перенесён ли в него + FEWSHOT**, это самая сильная часть промта и в эррате она не упомянута. + +--- + +## 7. МНЕНИЕ FABLE-5 (архитектурный аудит 16.08) — что взять + +Полный отчёт был в эфемерном транскрипте; существенное: + +* Подозрение о самоподгонке харнесса **подтверждается наполовину**: прибор не мерит ткань, а + эндпойнт под неё (П-6) принят владельцем 11.08 и не построен. Но ядро вывода («перепис покупает + много») не артефакт — держится независимыми контурами и внешней литературой. +* **Три механизма смещения В ПОЛЬЗУ большого переписывающего вызова:** боевой промт редактора + прошёл три редакции и валидирован тем же классом судейства, а контуры шли первой редакцией с + багами в свою сторону; мандат вёрстки исполняет только перепис, а судья за вёрстку штрафует; + отрицательные результаты трижды оказывались свойством прибора, а не армов. +* **Идея владельца испытана НЕ в сильнейшей форме.** Пустая клетка — «критик-буллеты → + ПЕРЕПИСЫВАТЕЛЬ» (`K1`). Вторая пустая — «перепис → критик → точечная починка» (страховка смысла + ПОСЛЕ переписа, там точечная форма адекватна дефекту, потому что остаточные ошибки локальны). +* Внешние данные: на вебновелльном бенчмарке zh→en **DeepSeek-V3 (5.16) выше GPT-4o (5.09)**; + китайские модели систематически бьют западные на китайском исходнике. То есть «фронтир не + выигрывает» правдоподобно и снаружи. Но если преимущество фронтира есть, оно в ткани, а её + автометрики и LLM-судьи не видят (LitEval: опознают человеческий перевод ≤20%). +* **Кандидаты сверх `K1`:** перепис → критик → фиксер · два переписа + слепой селектор (два + прогона одного редактора расходятся как два разных — это замерено на `JFLO`) · монолингвальная + полировка ткани без исходника · перепис с окном соседних глав. Все дешевле $0.015 на единицу. +* **Чего Fable не установил:** валидность счёта ошибок как прокси (П-6 не построен); долю знаков, + которую трогает фиксер (спан-счёт даёт 0.2–3.7%, difflib — 8.5% медиана и до 81%); перенос + выводов на другие книги и пары. + +--- + +## 8. КОМАНДЫ + +``` +eval/.venv/bin/python eval/dovodka/gain.py --density --agree --floor --tier +eval/.venv/bin/python eval/dovodka/naklon.py # все проходы; --selftest +eval/.venv/bin/python eval/dovodka/tier2.py --score +eval/.venv/bin/python eval/dovodka/ja6.py --score # и --score-sol после прогона Sol +eval/.venv/bin/python eval/dovodka/chtenie.py --score +eval/.venv/bin/python eval/dovodka/adjud.py --selftest ; --controls +eval/.venv/bin/python eval/dovodka/itog_d4.py --axis=d4 # --axis=d3|d6|d1, --fam=sol +eval/.venv/bin/python eval/dovodka/sud.py --axis=d3 --selftest +eval/.venv/bin/python eval/dovodka/money_d.py --report ; --selftest +eval/.venv/bin/python eval/dovodka/runs.py --status +eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --plan +``` + +Шум `SyntaxWarning` фильтровать. Интерпретатор — `eval/.venv/bin/python` из корня репо. + +--- + +## 9. ЧЕМ ЗАКРЫВАЕТСЯ РАБОТА (иначе непонятно, что считать сделанным) + +1. **Секция в отчёте** `docs/experiments/23-editor-tier.md` на каждый шаг плана. Нумерация уже + занята по Д16 включительно — следующая свободная Д17. +2. **Пре-рег ДО покупок и ДО первого судейского ответа**, зафризенный коммитом с основанием вслух. + Пре-рег обязан нести: состав панели · правило решения · пороги · мощность (MDE против ЦЕЛИ) · + статус оси (несущая/описательная) · что замер НЕ может. +3. **Гейты зелёные ИЛИ красные с диагнозом.** Константы под зелень не подгонять — это норма + проекта, нарушение = аннулирование куска. +4. **Пинг в `docs/PROGRESS.md` секция «Полигон»** (фронт и платформа туда не пишут, полигон пишет). +5. **Обновлённый `eval/dovodka/SESSION2-LOG.md` (рядом)** или его преемник — по ходу, не в конце. +6. **CONFIRM/DENY владельцу — только с артефактом-носителем.** Полигон не ратифицирует. +7. Дерево НЕ коммитить, кроме пре-рег-фризов; лендит оркестратор. + +--- + +## 10. КАК РАЗГОВАРИВАТЬ С ВЛАДЕЛЬЦЕМ + +* Он просил **меньше аббревиатур** и продуктовых формулировок: не «H-2б» и «A3», а «схема + критик-плюс-точечная-правка». Термины расшифровывать при первом употреблении. +* Он **не всегда читает длинные тексты** — важное выносить в первые строки. +* Он ловит дефекты процесса лучше гейтов: за сессию №2 нашёл структуру пакета Sol (я ошибся), + устаревший прайс (леджер оказался не измерителем) и слепоту прибора к вольности. **Его реплики + проверять исполнением, а не отмахиваться.** +* Обязательство с адресатом вне зоны закрывается ТОЛЬКО изменением файла вне зоны. «Выпущено», + «ждёт владельца», «объявлено в отчёте» — маркеры дефекта. +* Полигон не ратифицирует выводы. CONFIRM/DENY — владельцу через оркестратора, с носителем. diff --git a/eval/dovodka/PLAN-17-08.md b/eval/dovodka/PLAN-17-08.md new file mode 100644 index 00000000..370c06f9 --- /dev/null +++ b/eval/dovodka/PLAN-17-08.md @@ -0,0 +1,376 @@ +> ⚠⚠ **СОСТОЯНИЕ НА 20.08 — читать ЭТО прежде плана ниже.** Курс за день сдвинулся сильнее, чем +> написано в теле; тело оставлено как есть, потому что в нём основания решений. +> +> **1. СМЕНИЛСЯ ПРИБОР.** Решение владельца: первичный — слепое чтение Fable-5, счёт ошибок — +> брак-скрин. Замерено: чтение согласно с владельцем +0.80 на ОБЕИХ парах, счёт +0.80 на +> китайской и −0.80 на английской. ⇒ **все прежние вердикты об архитектурах вынесены прибором, +> который на английской паре смотрит в обратную сторону, и подлежат пере-суду.** 16 пакетов на +> это уже собраны и проверены (см. `HANDOFF-21-08.md`). +> **2. ШАГ 1 (Г5) ЗАКРЫТ**, вердикт по H-1 устоял — секция отчёта Д21. +> **3. ШАГ 2 (подача входа критику) ОСТАНОВЛЕН владельцем** после того, как замер показал: критик +> не видит удалённого абзаца ВООБЩЕ, поэтому «сохранил ли он бдительность» мерить не на чем. +> **4. Г1в/Г1 частично отвечены:** фраза о параллельности срезает размышление на 55% и находки на +> 50%; «след в след» — неправда на ОБЕИХ парах (проверено позиционно), врать модели нельзя. +> **5. Г12 усилена до механизма:** трейс показал, что 96% финального текста модель пишет ВНУТРИ +> размышления. Размышление у dspro — черновик, а не проверка. ⇒ двухстадийная связка дёшева +> БЛАГОДАРЯ топологии: она выносит черновик из дорогого канала размышления в дешёвый выход. +> **6. Появилась новая живая ветка, которой в плане нет:** промт однопроходки как РОЛЬ. Лучшая +> редакция — формулировка ВЛАДЕЛЬЦА («брак = просвечивающий исходный язык»), она обошла обе мои +> и встала выше продакшена в слепом чтении, но при КРАСНОМ контроле шума и вчетверо дороже. +> **7. Г5-строка ниже была НЕВЕРНА ПО ЗНАКУ** — исправлено в теле. + +# ПЛАН НОВОГО КУРСА — проверка живых гипотез (17.08) + +> Пишется после закрытия захода Д17 и архитектурного разговора с владельцем про V6. +> Предыдущие слои: `PLAN-16-08.md` (прошлый курс, ВЫПОЛНЕН) · `SESSION2-LOG.md` §12–§14 · +> отчёт `docs/experiments/23-editor-tier.md` секции Д17–Д20 · заказ +> `docs/POLYGON_EXP2223_REDO_SESSION_PROMPT.md` · хендофф `docs/POLYGON_PHASE_D_HANDOFF.md`. +> Читать ЦЕЛИКОМ: прошлая передача теряла пункты именно из-за чтения грепом. + +--- + +## 0. ГДЕ МЫ ОКАЗАЛИСЬ + +Заход Д17 ответил на вопрос, ради которого затевался, и ответ сместил всю рамку. + +1. **Второй проход нужен** — голый черновик проигрывает всему и всегда, по всем приборам и трём + парам. Единственное, что не пошатнулось за пять экспериментов. +2. **Какой именно второй проход — приборы не различают.** Все схемы переписывания легли в + неразличимую кучу под починенной рубрикой. +3. **Причина названа и подтверждена трижды независимо: собственный разброс ОДНОГО способа от + прогона к прогону перекрывает разницу МЕЖДУ способами.** Счёт на zh (sd 4.42 между двумя + генерациями связки), машинные читатели на zh, владелец на en (опознал пару близнецов по тексту + и развёл на два места: «один прогон приличный, второй сырой»). +4. **Счётчик — браковщик, а не выборщик.** Он следит за читаемостью там, где у него есть диапазон + (широкая шкала +0.68), и слепнет на чистом тексте (узкая +0.42). Формулировка владельца: + «счётчиком отсеивать брак, чтением выбирать текст в книгу». +5. **Деньги конвейера — это размышление, и тратится оно на выравнивание двух текстов.** 85% цены + дорогой клетки. Перепис думает ×12 против однопроходки только потому, что ему дают черновик. + +⇒ Курс меняется: раньше мы искали «лучшую архитектуру», теперь ищем **условия, при которых +архитектуры вообще становятся различимы**, и **чинить конвейер там, где он жжёт деньги впустую**. + +--- + +## 1. ЖИВЫЕ ГИПОТЕЗЫ — реестр + +Каждая проверяема, у каждой назван замер и цена. Порядок в §2, не здесь. + +| # | гипотеза | откуда | цена | +|---|---|---|---| +| **Г1** | Выровненный вход схлопывает размышление сличающей стадии | ОДНО наблюдение: критик видел черновик 78 абзацев против 77 строк исходника (0.96:1) и думал 1084; en 8 против 1 — думал 9576 | ~$0.3 | +| **Г1а** | ⛔ После РЕДАКТОРА выравнивание исчезает (77 строк → 43 абзаца), значит критик V6 попадёт в дорогой режим даже на zh | замер 17.08 | считано: V6 на Гу ~$340 против $226 у связки | +| **Г1в** | ⭐ Достаточно СКАЗАТЬ критику, что тексты идут параллельно — размышление упадёт без всякой разметки | идея владельца 17.08; нынешний промт критика не говорит о связи текстов НИЧЕГО, кроме «исходник и его перевод» | ~$0.15 | +| **Г1б** | Разметку можно поручить ОТДЕЛЬНОМУ дешёвому вызову-экстрактору, не трогая переводчика | квирки :51 — эхо бьёт по переводу, не по JSON-выводу | ~$0.2 | +| **Г1г** | Локальное эмбеддинговое выравнивание (LaBSE/vecalign/BERTalign) даёт таблицу пар с приемлемой точностью на ХУДОЖЕСТВЕННОЙ прозе | идея владельца; модели уже в кэше машины | **$0** за прогон, только CPU-время | +| **Г2** | «Мало думает» = «плохо работает»: ленивый критик находит хуже | zh критик 1084 токена, 43 находки на 2 тыс. знаков, контур проиграл | в комплекте с Г1 | +| **Г3** | Языковому критику исходник НЕ нужен — монолингвальная критика не хуже | ось ЯЗЫК решает, а она видна без оригинала | ~$0.4 | +| **Г4** | Кэш префикса срезает вход многостадийного конвейера | используем 44.8% из возможного | $0, перекладка запросов | +| ~~**Г5**~~ | ~~вердикт по H-1 зависит от состава пачек~~ **ЗАКРЫТА 20.08, Д21: вердикт устоял** ⚠ и формулировка была НЕВЕРНА ПО ЗНАКУ — «переходит порог» значило бы «дорогой черновик ХУЖЕ связки», то есть H-1 опровергается, а не подтверждается | находка ревизии №1 | $0, потрачено | +| **Г6** | Счётчик работает при широком диапазоне, слепнет на чистом | разбиение +0.68 против +0.42, но post-hoc | ~$1.5 (трудный en материал) | +| **Г7** | Дорогая модель + ПОЛНЫЙ промт → точечная зачистка ≥ связки | пустая клетка; `A6` был дорогой моделью с ТОЩИМ промтом | ~$1.2 | +| **Г8** | Промт однопроходки, собранный как РОЛЬ, а не склейка, лучше нынешнего | нынешний несёт мета-фразу «редактора после тебя не будет» | ~$0.5 | +| **Г9** | Банк, собранный из хороших переводов, лучше банка из дешёвых черновиков | никогда не мерилось; майнер режет исходник, черновики дают лишь `drafts:` | ~$0.3 | +| **Г10** | Разделение типов сущностей (имена / титулы / предметы) улучшает банк | терминолог даёт одно правило на всех | ~$0.3 | +| **Г11** | Сцена как единица нарезки лучше чанка | эксп-15: все эффекты когезии под порогом, сигнала нет | не считана | +| **Г12** | Несколько прогонов на метод — УСЛОВИЕ любого сравнения архитектур | п.3 §0 | ×2 к любому замеру | + +--- + +## 1а. ЧТО УЖЕ ЗАКРЫТО — НЕ ПЕРЕОТКРЫВАТЬ БЕЗ НОВОГО ОСНОВАНИЯ + +Дописано при перечитывании: без этого списка следующая сессия честно потратит деньги на то, +что уже куплено и отвечено. + +| закрыто | чем | вердикт | +|---|---|---| +| «критик → ПОЛНЫЙ перепис» (`K1`/`Z1`) | Д17, обе пары | −0.06 по счёту, у владельца 3-е и 4-е места. **ОТРИЦАТЕЛЬНО** | +| «обогащённый промт черновика» (`K8`/`Z8`) | Д17, обе пары | хуже голого на en, эхо-мина на zh. **ОТРИЦАТЕЛЬНО** | +| «дешёвая модель в роли редактора» | эксп-22 Ф3 | −2.54 против боевого + 3 клетки сожгли бюджет. **ОТРИЦАТЕЛЬНО** | +| «дорогая модель черновиком» | эксп-22 Ф2 | ничья, побеждает дешёвый. **ЗАКРЫТО** | +| «какая модель лучше редактор» | 5 моделей zh, 2-я на en/ja, 3 «сильных тира» | dspro первый везде. **ЗАКРЫТО устойчиво** | +| «gemini — аутлаер прозы» (эксп-04) | Д1, 15 клеток | не подтверждается, цена ×30. **ЗАКРЫТО** | +| H-4 «перевес dspro — свойство китайской пары» | zh/en/ja | не подтверждается на всех трёх. **ЗАКРЫТО** | +| роутинг «платить дорого адресно» | эксп-21 | отборщики-монетки. **ЗАКРЫТО** | +| второй последовательный перепис | эксп-20/21 | итеративный дрейф. **ЗАКРЫТО** | +| дифф-контракт вместо переписа | эксп-19 | дороже на thinking-модели. **ЗАКРЫТО** | + +⚠ **Единственный ЖИВОЙ продуктовый кандидат из измеренного: «однопроходка + глоссарный проход».** +По судейской оси от связки не отличается, по банку ЛУЧШЕ неё на обеих парах (zh 1.38 против 2.88, +en 0.31 против 0.56), стоит один полный вызов вместо двух, и владелец при слепом чтении поставил +однопроходку первой на английском. Его слабое место — банк без черновика (см. Г9). + +--- + +## 1б. РЕШЕНИЕ ВЛАДЕЛЬЦА, КОТОРОЕ ЖДЁТ ЕГО САМОГО (не гипотеза, а развилка) + +**Черновая роль по НАШЕМУ ЖЕ правилу должна уйти с DeepSeek.** Правило D39.117 — «при равном +качестве берём дешёвого», качество шести черновых моделей было ничьёй. После подорожания 16.08: + +``` +gpt-5.6-luna $0.00358/глава +gemini-3.1-flash-lite $0.00383 +deepseek-v4-flash $0.00444 ← был 0.00097, в 3.7 раза дешевле следующего +``` + +На книге в 1500 глав: текущая связка $40 против $23 у пары `flash-lite + glm-5`. +⚠ **Перед сменой — эхо-проба альтернатив:** у flash эхо-мина есть и она перевооружается +ослабленным thinking; у остальных не мерена НИ РАЗУ. Владелец сказал «это разговор предстоит». + +--- + +## 2. ПОРЯДОК РАБОТ. Он важнее содержания + +### ✔ ШАГ 1 — Г5. ВЫПОЛНЕН 20.08, $0. Секция отчёта Д21 + +**Вердикт «H-1 НЕ УСТАНОВЛЕНА» устоял.** Инструмент — `itog_d4.py --sens` (режим в СВОДЕ, нового +файла не заводилось). Арифметика находки воспроизведена побайтно; переворот живёт только в +сценарии, который возвращает пачку аннулированной сессии и одновременно снимает пачки-валидации, +то есть применяет норму в одну сторону и отменяет в другую. На нынешнем дереве запас **−0.0710**. + +Тем же заходом закрыты смежные находки: **P12** (пачка `69de717f3f` уже в карантине, строка +сверки устарела) · **P07** (наклон наборов реален, вердиктов не двигает) · **R73/P40** (калибровка +порога названа ПО ЗНАКУ: пол на Д4 честен, для H-1 контраста завышен в 1.5×). + +⛔ **Осталась открытой P42 и это ВОПРОС ВЛАДЕЛЬЦУ**, не замер: два пре-рег-правила о маржевом +гейте несовместимы, и от выбора между ними зависит, остаётся ли единственный CONFIRM фазы +подтверждением или становится эскалацией. Объявлено девиацией в Д21.6. + +**Что вынес сверх задания:** `A6/A0` во всех пяти способах счёта стоит ВПЛОТНУЮ к порогу +(−0.62 · −0.07 · −0.51 · +0.002 · −0.007) при шаге шкалы в одну ошибку ⇒ это не «эффекта нет», а +«эффект ровно размером с шумом прибора» — та же болезнь, что Д17 намерил на новом материале. +И `A6` испытывался ТОЩИМ промтом переводчика, поэтому клетка «дорогая модель + ПОЛНЫЙ промт» +(она же ядро V6) остаётся пустой — это шаг 4. + +### ШАГ 2 — Г1в + Г1 + Г2 ОДНИМ ЗАМЕРОМ (~$0.4). БЕЗ ВЫРАВНИВАНИЯ, БЕЗ РАЗМЕТКИ + +⚠ **Замер УРЕЗАН по итогам спора 17.08.** Прежняя редакция предлагала подавать критику +пронумерованные ПАРЫ, то есть требовала решить задачу выравнивания. Это преждевременно: вся +ветка висит на ОДНОМ наблюдении, которое я же и объяснил. Сначала проверяем наблюдение, потом +строим схемы — норма, заведённая этим разговором. + +**ПЕРВЫМ — вариант владельца (Г1в), он дешевле и чище.** Меняется ТОЛЬКО ИНСТРУКЦИЯ, данные +не трогаются вовсе. Сейчас промт критика говорит о связи двух текстов буквально одно: «тебе дан +ИСХОДНИК и его ЧЕРНОВОЙ ПЕРЕВОД». Модель вынуждена сама выяснять, как они соотносятся, — и, +похоже, именно на это уходят те 9 576 токенов. + +Добавляем фразу о том, что тексты идут ПАРАЛЛЕЛЬНО. Образ владельца: два листа из разных тетрадей, +наложенные друг на друга, — тексты идут рядом, а не строчка в строчку. + +⚠ **ФОРМУЛИРОВКА РЕШАЕТ, И ВРАТЬ НЕЛЬЗЯ.** «Предложение за предложением» — НЕПРАВДА: наш же промт +редактора обязывает сливать абзацы (77 строк исходника → 43 абзаца). Соврём — модель будет искать +соответствие, не найдёт и потратит БОЛЬШЕ, а не меньше. Говорить только правду: порядок сохранён · +перевод покрывает исходник целиком · членение может отличаться · читай оба параллельно и НЕ ищи +точных пар предложений. + +**ВТОРЫМ — мой вариант:** тот же кусок, но **исходник разбит по предложению на строку** +(перевод НЕ трогаем, выравнивания НЕ делаем). Детерминированно, $0. + +**Гонять лучше все четыре комбинации** (инструкция вкл/выкл × разбивка вкл/выкл) на одном +материале: тогда видно, что решает — слова или форма подачи. Всё равно копейки. + +⚠⚠ **РИСК, КОТОРЫЙ ЗАМЕР ОБЯЗАН ПРОВЕРИТЬ НАРАВНЕ С ТОКЕНАМИ.** Фраза «перевод покрывает исходник +целиком» может заставить критика ПЕРЕСТАТЬ ИСКАТЬ ПРОПУСКИ, а потерянный факт — один из главных +классов нашей рубрики. Выигрыш в токенах, купленный слепотой к целому классу дефектов, — это не +выигрыш. Поэтому в пре-рег отдельной строкой: считать долю находок класса «пропущено/потеряно» +до и после, и падение этой доли считать ПРОВАЛОМ варианта, даже если токены упали. + +Смотрим ТРИ вещи: +* **токены размышления** — упали или нет (Г1в/Г1); +* **находки в целом** — те же, лучше или хуже (Г2); +* **находки класса «пропущено»** — не исчезли ли (риск выше). + +**Если упали при тех же находках** — гипотеза жива, и тогда имеет смысл строить разметку (Г1б). +**Если нет** — вся ветка закрывается, и мы экономим себе месяц проектирования. Это и есть цель +шага: закрыть ветку дёшево, а не подтвердить её дорого. + +⚠ Обещание «минус треть цены конвейера» СНЯТО: оно висело на неподтверждённом. +⚠ Пре-рег обязателен: что считаем «те же находки». Сравнение — по адъюдикации, а не на глаз. + +### ШАГ 2б — Г1г: ЭМБЕДДИНГОВОЕ ВЫРАВНИВАНИЕ, ЕСЛИ ДЕШЁВОЕ НЕ СРАБОТАЛО ($0) + +Гонится ТОЛЬКО если шаги 2 и 2а не дали адресации. Инструмент локальный, вызовов к API нет вовсе. + +**Что уже есть на машине (проверено):** `sentence-transformers`, `torch+cpu`, 16 ядер, GTX 1070; +в кэше лежат `LaBSE`, `bge-m3`, `Qwen3-Embedding-0.6B`. Кандидаты-алгоритмы: **vecalign** +(динамическое программирование по эмбеддингам, умеет МНОГИЕ-КО-МНОГИМ) и **BERTalign** (на LaBSE, +авторы целились в художественную прозу). Gale-Church по длинам для zh↔ru слабая. + +**⛔ ОГРАНИЧЕНИЕ ПРОДА: в облаке только CPU + диск, GPU нет.** Поэтому: +* выравнивание — НЕ горячий путь, один раз на книгу, результат на диске; +* объём режется: эмбеддить смысловые ходы (43–78 на кусок), а не предложения · сперва ЯКОРЯ + (термины банка, числа, реплики) прибивают опорные точки, эмбеддинги — только на промежутках · + модель меньше + int8/ONNX; +* **роли разведены: в лаборатории это ИЗМЕРИТЕЛЬ, в проде адреса берутся из номеров смысловых + ходов по построению.** Прод на эмбеддинги опираться НЕ должен. + +**Точность мерим БЕЗ ручной разметки, инструментами, которые у нас уже есть:** банк терминов +(термин в отрезке исходника обязан быть канонной формой в выровненном отрезке перевода) и маржевые +посадки (мы сами знаем, куда вставили порчу). ⚠ Чужие цифры качества из статей НЕ переносить: их +мерили там, где перевод следует за оригиналом близко, а у нас редактор обязан сливать абзацы. + +### ШАГ 2а — ЕСЛИ Г1 ПОДТВЕРДИЛАСЬ: РАЗМЕТКА ОТДЕЛЬНЫМ ВЫЗОВОМ (~$0.2) + +⛔ **Переводчику разметку НЕ поручать — это измеренный класс отказа.** Обогащённый черновик +получил ОДИН добавленный структурный блок и вернул исходник вместо перевода на 4 главах из 16; +запрет D21.6 говорит то же про языковые констрейнты в reasoning-ослабленных путях (эхо 3/10 → 9/10). + +Разметку делает **отдельный дешёвый вызов-экстрактор**: вход исходник + готовый перевод, выход +ТОЛЬКО соответствие «номер смыслового хода → первые слова абзаца». Ни строчки текста не +переписывает. Безопасно по квирк-бюллетеню (:51): эхо бьёт по переводу, не по JSON-выводу. + +**Алгоритма сопоставления при этом НЕТ и не нужно.** Если ходы пронумерованы на шаге 1, а блоки +несут те же номера, соответствие возникает по построению. Проверка разметки детерминированна: +каждый номер ровно один раз · порядок монотонный · длина блока в разумном отношении к длине хода · +термины банка хода N присутствуют в блоке N. Не сошлось — блок «без адреса», обрабатывается как сейчас. + +### ШАГ 3 — Г3 (~$0.4) + +Монолингвальный языковой критик: только русский текст, исходника нет вовсе. Сравнить его находки +по оси ЯЗЫК с находками нынешнего двуязычного. Если не хуже — сличение из этой стадии убирается +целиком, а это самая дешёвая экономия из всех возможных. + +### ШАГ 4 — Г7 + Г8, ПУСТЫЕ КЛЕТКИ АРХИТЕКТУРЫ V6 (~$1.7) + +Обе покупаются одним заходом, потому что делят базу: +* **Г8**: промт однопроходки, собранный НАЧИСТО как роль литературного переводчика с инжектом + действий — без мета-фразы про отсутствующего редактора, без следов склейки. +* **Г7**: её выход + точечная зачистка по классифицированным замечаниям критика. + +Это и есть урезанный V6 на одной паре. ⚠ Брать ОБЕ пары: разница zh/en в этой фазе оказалась +не языковой, а структурной (выровненность исходника), и на одной паре вывод не встанет. + +### ШАГ 5 — Г9 + Г10, БАНК (~$0.6) + +Пере-собрать банк из выходов однопроходки и из дорогих черновиков, сравнить с нынешним по составу +и покрытию. Отдельно — терминолог с РАЗДЕЛЁННЫМИ типами сущностей. + +**Почему не раньше:** банк определяет связность книги на тысячах глав, но эффект его качества наш +прибор не видит вовсе — он сравнивает армы при ОДНОМ банке. Значит замер тут не судейский, а +детерминированный (состав, покрытие, устойчивость формы), и его надо ставить отдельно от +качественных шагов, чтобы не смешать. + +### ШАГ 6 — Г6, ТРУДНЫЙ АНГЛИЙСКИЙ МАТЕРИАЛ (~$1.5) + +Длинные плотные английские главы вместо коротких и чистых. Проверяет, восстанавливается ли +согласие счётчика с чтением при широком диапазоне. Разбиение +0.68/+0.42 сделано ПОСЛЕ данных — +это разведка, и подтверждать её надо на новом материале, иначе это подгонка. + +### ШАГ 7 — Г4, КЭШ ($0) + +Переложить порядок запросов так, чтобы исходник сцены был общим неизменным префиксом всех стадий. +Замер прямой: доля `cached_tokens` до и после. Сейчас 44.8%. + +### ШАГ 8 — ПЕРЕНОСИМОСТЬ ВЫВОДА НА ДРУГУЮ МОДЕЛЬ (~$0.5) + +Всё измеренное получено при модели-константе `deepseek-v4-pro`. Вывод честно звучит «какая +архитектура лучше ДЛЯ ЭТОЙ модели». Прогнать победившую архитектуру на `glm-5` и проверить, что +порядок не зависит от жильца. ⚠ Ставить ПОСЛЕ шагов 2–4: пока архитектура не выбрана, переносить +нечего. + +### ШАГ 9 — СЛЕПОЕ ЧТЕНИЕ ЧЕЛОВЕКОМ НА НОРМАЛЬНОМ n ($0) + +Владелец прочёл 1 китайскую главу и 2 английских. Пре-рег П-6 просил 8–12 единиц на ось. +Его чтение оказалось ТОЧНЕЕ машинных читателей (взял контроль там, где они провалили), поэтому +расширение выборки — самый дешёвый способ усилить решающий эндпойнт. Ограничение известно и +названо им самим: это его время. + +--- + +## 3. ЧТО ПРОНИЗЫВАЕТ ВСЕ ШАГИ — Г12 + +**Сравнение архитектур по ОДНОМУ прогону запрещено.** Это вывод захода Д17, подтверждённый тремя +приборами. Любой платный замер выше планируется в ДВУХ генерациях на метод, иначе он померяет шум. + +Цена этого честная: ×2 к смете. Альтернатива — мерить и не знать, что померил. + +--- + +## 4. ДОЛГИ ФАЗЫ Д — их надо закрыть, но они НЕ курс + +Не двигают знание, но без них фаза не сдаётся. Порядок внутри — от того, что искажает выводы, +к тому, что искажает отчётность. + +1. **Адъюдикация английской оси** — единственное, что блокирует единственный несущий вывод фазы + (H-3 в эскалации). `adjud.py` прошит на zh, нужна параметризация. $0. +2. **65 находок ревизии** — шаг 1 курса разбирает первую; остальные разобрать там же. +3. **Секция Д5** — ручная пере-классификация 24 мест. $0. +4. **Синхронизация отчёта:** сводка Д9 противоречит Д15–Д20 в четырёх клетках · Д16 не отражает + аннулирование японской пачки Sol её же контролями · деньги в Д17 протухли (напечатана смета + $1.06 и потолки $0.65, факт $11.72 из $12.10) · тела эрраты Э-17.1 в отчёте нет. +5. **Реестр требований:** строк на Д17–Д20 нет вовсе, 16 самореферентных улик, R64 красный. +6. **Пинг в `docs/PROGRESS.md` секция «Полигон»** — последняя запись 15.08. +7. **Селфтесты `sud.py` красные на трёх осях** (d3 — 4 провала, d6 — 1, d1 — 2), все настоящие, + диагнозы в журнале. ⚠ `MIN_FLOOR["d6"]=3` — константа, поставленная под зелень; НЕ править, + это вопрос владельцу. +8. **`E1` побайтно равен черновику на 5 единицах из 16** — объявлено в Д14.4, панель без них не + пере-считана. +9. **Эррата zh-канона:** поправка границы слова живёт в коде, в сохранённый банк не дошла + (завышение покрытия +0.0039). Правка закрытой оси — ратифицирует оркестратор. +10. **`chtenie.py --score-calib` не пере-считывает число −0.10**, ради воспроизводимости которого + объявлен: печатает только порядок читателя. Долг назван в докстринге, не закрыт. +11. **Пакет второму семейству (Sol) по заходу Д17** — не собирался. Решение владельца нужно: + отдавать или объявить заход односемейным явно. + + +--- + +## 5. ЧЕГО НЕ ДЕЛАТЬ + +* **Не строить V6 целиком.** Пять стадий сразу — это замер, в котором нельзя понять, что сработало. + Шаги 2–5 покупают его по кускам, и каждый кусок отвечает на свой вопрос. +* **Не выбирать архитектуру по деньгам.** Разброс между схемами 15–40%, между временем суток — + ровно вдвое. Экономить надо расписанием прогона, решать — качеством. +* **Не переносить отрицательные результаты по точечным контурам на V6.** Там фиксер чинил + дешёвый черновик; в V6 он чинит перевод хорошей модели. Другая работа. +* **Не сравнивать числа разных проходов.** Рубрика менялась (Д18), прогоны разной строгости. +* **Не покупать до пере-снятия прайса**, если вендор снова его двинет: `zakhod.price_gate` + отказывает автоматически, но проверять руками при первом запуске сессии. + +--- + +## 6. ЛОВУШКИ, ДОБАВИВШИЕСЯ ЗА ЭТУ СЕССИЮ + +1. **Файл клетки ≠ годная клетка.** Считать СОДЕРЖИМОЕ и `finish=stop`. Я на этом сел и доложил + владельцу «80 клеток куплено», когда годных было 23. +2. **Пред-полётная проба обязательна** перед циклом на сотню клеток (`zakhod.preflight`): 96 + запросов ушли в `402` и выглядели как успешный прогон. +3. **Оценивать цену клетки ПО СВОЕЙ ПАРЕ**, а не по общему пулу: китайская глава впятеро длиннее + английской, общая медиана врёт вдвое. +4. **Раскладка меток в пакете ЧЕЛОВЕКУ — одна на всю пару.** Раскладка на отрывок сделала ответ + владельца нерасшифровываемым, и его работа пропала. +5. **Разборщик обязан принимать человеческую форму ответа.** Требовать машинной и молча терять + ответ — дефект инструмента, а не читателя. +6. **Пере-эмиссия не имеет права трогать пару, которая уже прочитана.** Ключ ДОГРУЖАТЬ. +7. **Гейт, который не может упасть, ничего не сторожит.** Пустая панель в селфтесте — ПРОВАЛ, + а не пропуск. +8. **`git checkout` поверх грязного дерева запрещён** — владелец отказал в пермишене, и правильно. +13. **Обогащение промта ДЕШЁВОЙ модели перевооружает эхо-мину.** Замер Д17: мандат перевёрстки, + добавленный к черновику, дал 4 негодные клетки из 16 на zh (модель вернула исходник вместо + перевода) против 1 из 16 в базе. Причина документирована: черновик ходит с `reasoning_effort: + low`, а запрет D21.6 говорит не вносить языковые констрейнты в промты reasoning-ослабленных + путей без per-model замера. **Любое обогащение промта на дешёвой модели — только с эхо-гейтом + и ре-геном.** +14. **Идентичность судей и ЧИТАТЕЛЕЙ персистится ДО запуска.** Я закрыл её постфактум + (`READERS-z17.json`) — это слабее нормы. У прохода `tier` отсутствие такого файла сделало + причину расхождения в 12 раз НЕВОССТАНОВИМОЙ. +15. **Три инструмента зоны признаны негодными ВНУТРИ ревизии, а гейты числят их зелёными:** + `promptdiff.py` (расхождение матчится по префиксу строки; отсутствующий файл пар-пакета гейт + не роняет) · `canon.py` (снятая ревью классификация всё равно пишется в артефакт — отсюда + негодность Д5) · `material_ja.py` (фильтр AI-меток объявлен механическим, кода не существует). + Опираться на любой — только после вскрытия. +16. **Д17 отсужен ОДНИМ семейством.** Норма П-1 требует двух там, где возможно. Пакет для Sol по + заходу НЕ собирался. Все выводы Д17–Д20 стоят на семействе claude плюс чтение владельца. + + +--- + +## 7. ЧЕМ ЗАКРЫВАЕТСЯ РАБОТА + +1. Пре-рег ДО каждой покупки, зафризенный коммитом с основанием вслух. +2. Мощность (`power.py`) напечатана ЧИСЛОМ до денег — не словами. За это уже была девиация Э-17.2. +3. Гейты зелёные ИЛИ красные с диагнозом; константы под зелень не подгонять. +4. Секции в отчёте: следующая свободная — **Д21**. +5. Адверсариальная приёмка другим модельным семейством — она за эту сессию нашла 8 дефектов + замера и 4 неверных утверждения в моём же отчёте. Без неё не сдавать. +6. Дерево НЕ коммитить, кроме пре-рег-фризов; лендит оркестратор. diff --git a/eval/dovodka/SESSION2-LOG.md b/eval/dovodka/SESSION2-LOG.md index 7a1e4851..3b8b0e8b 100644 --- a/eval/dovodka/SESSION2-LOG.md +++ b/eval/dovodka/SESSION2-LOG.md @@ -457,3 +457,1220 @@ DeepSeek на пик `flash $0.44/$1.32/кэш $0.014` · `pro $1.32/$3.96/кэ что кому подаётся). Контраст мерит топологию только при модели-константе — иначе это конфаундер эксп-19. Цена этого: вывод честно звучит «какая архитектура лучше ДЛЯ ЭТОЙ модели». Переносимость закрывается отдельным дешёвым замером ПОСЛЕ захода: победившая архитектура на `glm-5`. + +## 14. ЧЕКПОЙНТ 16.08 — ПАНЕЛЬ КУПЛЕНА, СУДЕЙСТВО НЕДОСУЖЕНО + +**Куплено ПОЛНОСТЬЮ, годные клетки (не файлы):** zh `Z8` 16 · `Z8R` 12/12 · `Z1` 16 · `Z7` 16 · +`ZF` 16 · en все пять армов по 16. `Z8R` на zh ограничен 12 ПО ПОСТРОЕНИЮ: на четырёх главах +обогащённый черновик забракован эхо-гейтом, редактировать нечего — объявлено, не спрятано. +Деньги: забукировано $11.72, фактически списано ~$8.9 (покупки шли в вендорский офф-пик, касса +намеренно пишет пик). Потолки ФД-I 3.95 · ФД-J 1.95 · пакетный 12.10. + +**Судейство ЭМИТИРОВАНО и НЕДОСУЖЕНО.** Ключ `~/books/dovodka/blind-keys-z17/z17-KEY.json` +(соль `z17-2026-08-16`), задания `~/books/judging/z17/{zh,en}/{p1,p2}-tasks`. +Ответов на диске: **zh p1 3/12 · zh p2 3/12 · en p1 15/16 · en p2 15/16**. +Воркфлоу `wf_b1370361-af2` остановлен вместе с процессом; **возобновление — +`Workflow({scriptPath: "…/workflows/scripts/z17-judging-wf_b1370361-af2.js", +resumeFromRunId: "wf_b1370361-af2"})`**, выполненные агенты вернутся из кэша. +Сессии #73–#86 зарегистрированы ДО запуска, НЕ закрыты `runs.py --done`. + +⚠ **Пере-эмитировать `zsud.py --emit` НЕЛЬЗЯ** — за ключом уже лежат 36 ответов, и гейт +переэмиссии это отказывает намеренно: раскладка меток есть функция соли и набора армов. + +**Не начато:** слепое чтение (`chtenie.py --emit`, панель собирается на 8 главах каждой оси, +селфтест зелёный) · свод `zsud.py --score` · канон-гейт `zsud.py --canon` · секции отчёта по +результатам · пинг в `docs/PROGRESS.md`. + +**Долги фазы, не тронутые этой сессией:** 65 находок ревизии · адъюдикация английской оси · +секция Д5 · `E1`≡черновик · 16 самореферентных улик · красные селфтесты `sud.py` · эррата +zh-канона · строки Д17 в реестре требований (заказ требует реестр в git ДО покупок — нарушено). + +--- + +## 14. АРХИТЕКТУРНЫЙ РАЗГОВОР 17.08 — V6 ВЛАДЕЛЬЦА И ЭКОНОМИКА КОНВЕЙЕРА + +Владелец выложил идею V6 (`START_PROMT.MD`, строки 97–103): пять стадий — дешёвый map-reduce +майнинг банка и нарезка по сценам с JSON-выдачей · решение по банку хорошей моделью с +обоснованием · перевод хорошей моделью БЕЗ подсовывания дешёвого черновика · литературный критик, +который ЖАЛУЕТСЯ и классифицирует, но не чинит, с майнингом замечаний в свой банк · точечный +редактор, инжектящий фиксы. Ниже — что из нашего сырья про это уже известно. + +### 14.1 Что V6 чинит по существу + +Разложение по осям (Д14.11) показало: точечные контуры проигрывают ПРОЗОЙ, а не смыслом +(`A3/A0` верность −0.26 при языке −1.61). Причина механическая — фиксер трогает 0.2–3.7% знаков, +остальное остаётся черновиком. **В V6 точечный редактор чинит не черновик, а перевод хорошей +модели: плохой прозы, до которой он не дотягивается, там нет.** Наши шесть отрицательных замеров +по контурам на эту конфигурацию НЕ переносятся — у неё другая работа. + +### 14.2 ⛔ ГЛАВНАЯ НАХОДКА РАЗГОВОРА: деньги конвейера — это РАЗМЫШЛЕНИЕ, и оно тратится +### на ВЫРАВНИВАНИЕ ДВУХ ТЕКСТОВ + +Разбор токенов по ролям (медианы, цена по текущему пину; у DeepSeek размышление тарифицируется +по цене ВЫХОДА — $3.96/1M против $1.32 за вход): + +| роль | вход | выход | из них РАЗМЫШЛЕНИЕ | $/клетка | +|---|---|---|---|---| +| перепис zh | 5 502 | 16 790 | **13 978** | 0.0737 | +| критик zh | 4 522 | 3 730 | **1 084** | 0.0207 | +| перепис en | 2 627 | 10 330 | 9 718 | 0.0444 | +| **критик en** | 1 403 | 10 098 | **9 576** | **0.0418** | +| однопроходка zh | 3 024 | 3 620 | **842** | 0.0143 | + +⇒ **85% цены дорогой клетки — то, что модель надумала про себя.** Перепис думает в 12 раз больше +однопроходки, потому что ему дают черновик и просят сверяться. **Дешёвый черновик, взятый ради +экономии, и есть самая дорогая статья конвейера.** + +⇒ ⚠ **Возражение владельца подтвердилось: критик НЕ дешевле по природе.** На английском он думает +9 576 токенов, чтобы выдать список из 11 пунктов на 522 токена, и стоит $0.0418 — почти как +полный перепис ($0.0444). Моя оценка «V6 дешевле» держалась на КИТАЙСКОМ критике и рухнула бы, +веди он себя как английский (V6 на Гу стал бы ~$340 вместо $190 против $226 у связки). + +### 14.3 ⛔ ПОЧЕМУ КРИТИКИ РАЗОШЛИСЬ В 9 РАЗ — ОБЪЯСНЕНИЕ НАЙДЕНО ЗАМЕРОМ + +``` +структура исходника: zh — 77 строк на кусок, 28 знаков на строку + en — 1 строка на кусок, 1642 знака на строку +``` + +**Китайская вебновелла набрана по предложению на строку — она УЖЕ ВЫРОВНЕНА.** Критику не нужно +восстанавливать соответствие, он видит готовые пары. Английский приходит сплошным потоком, и +9 576 токенов размышления уходят на выстраивание соответствия между двумя блобами. + +⇒ **Дорого не «сличать два текста», а сличать два НЕВЫРОВНЕННЫХ текста.** + +### 14.4 ПЯТЬ АРХИТЕКТУРНЫХ ПРЕДЛОЖЕНИЙ, ВЫРОСШИХ ИЗ ЭТОГО + +1. **Пред-выравнивание снаружи модели.** Подавать сличающей стадии пронумерованную таблицу пар + вместо двух текстов. Выравнивание детерминированно и бесплатно (пунктуация, длина, якоря + банка). Побочная выгода крупнее экономии: **у претензии появляется машинный адрес** — номер + строки вместо цитаты, которую мы сейчас ищем регексами (и на этом уже ловили дефекты). +2. **Снять с критика то, что ловит программа.** Термины — банк-гейт, типографику и числа — + батарея, всё это $0. Оставить критику ОДИН класс: «звучит переведённым». +3. **Языковому критику исходник не нужен вовсе.** Калька и канцелярит диагностируются + монолингвально. Тогда критиков два и оба дешёвые: языковой (только русский, сличения нет) и + смысловой (выровненные пары, узкий мандат). +4. **То же лекарство переписывателю** — не давать черновик (это замерено: 842 против 9 976 + токенов), а если нужен ради банка — давать выровненным. +5. **Кэш префикса.** Замер: используем на **44.8%** (1.19M из 2.65M токенов входа). Кэш-хит у + dspro $0.044 против $1.32 — в 30 раз дешевле. В пятистадийном V6 исходник сцены проходит + через перевод, критика и фиксер: если он общий неизменный ПРЕФИКС, две стадии из трёх платят + за него по кэш-цене. + +### 14.5 СМЕТА КНИГ (замеренные цены клеток, пере-оценённые по текущему пину) + +Мастер Гу — **7 778 990 знаков** (6.2M иероглифов). Кристофф — **1 626 475 знаков**. + +| архитектура | Гу, пик / офф-пик | Кристофф, пик / офф-пик | +|---|---|---| +| боевая связка | $226 / **$113** | $40 / **$20** | +| однопроходка + глоссарный проход | $195 / **$97** | $27 / **$14** | +| V6 (оценка, доля фиксов 35% — ДОГАДКА) | $190 / **$95** | $70 / **$35** | + +⚠ Разброс между архитектурами (15–40%) МЕНЬШЕ разброса между временем суток (ровно вдвое). +Планировать прогон на офф-пик выгоднее, чем выбирать архитектуру. ⇒ **выбор архитектуры — не про +деньги**, решать надо по качеству и по связности на дистанции в тысячи глав. + +### 14.6 ФАКТИЧЕСКИЕ ОТВЕТЫ НА ВОПРОСЫ ВЛАДЕЛЬЦА + +* **«Давали ли дорогому черновику полный промт?»** — НЕТ. Арм `A6` делался `deepseek-v4-pro` + тем же тощим промтом переводчика (`panel.py:142` → `translator_msgs`). Поменяли модель, не + поменяли задание. А «дорогая модель + полный качественный промт» — это и есть однопроходка + (её мандат несёт блок перевёрстки, проверено селфтестом). +* **«Разделяем ли типы сущностей в терминологе?»** — НЕТ. Один список, одно правило («имена и + топонимы транскрипцией, титулы и реалии переводом»); поле `type:` есть, но промт сам говорит, + что оно неточно. +* **«Было ли: хороший черновик + майнинг банка + точечный фиксер?»** — половина. Дорогой черновик + + точечный контур (`A6`) — только zh, 16 глав. Майнинг банка из хороших переводов — НИКОГДА. +* ⚠ **СНЯТО МОЁ ПРЕЖНЕЕ УТВЕРЖДЕНИЕ** «однопроходка ломает майнинг банка». Преувеличение: майнер + режет кандидатов из ИСХОДНИКА (`bank.py:127`), черновики дают терминологу лишь строку `drafts:` + как свидетельства. Без черновика — деградация подсказки, а не поломка майнинга. + +### 14.7 РАЗБОР ГИПОТЕЗЫ О ВЫРАВНИВАНИИ (17.08, спор с владельцем — он прав дважды) + +**Ход разговора, потому что он важнее итога.** Я предложил подавать сличающей стадии выровненные +пары вместо двух текстов и объяснил этим разрыв размышления (zh 1084 против en 9576). Владелец +возразил: после художественной перевёрстки абзацы не сопоставимы, несколько китайских абзацев +ложатся в один русский, задача нетривиальна. Я проверил на ОТРЕДАКТИРОВАННОМ тексте — 77 строк +исходника против 43 абзацев, 1.73:1 — и признал, что поторопился. + +**Владелец возразил САМ СЕБЕ и оказался прав:** критику подавали не отредактированный текст, а +ЧЕРНОВИК. Пере-замер: + +``` +zh: 77 строк исходника → 78 абзацев ЧЕРНОВИКА = 0.96:1 ← это видел критик, думал 1 084 +zh: 77 строк исходника → 43 абзаца ОТРЕДАКТИРОВАННОГО = 1.73:1 +en: 1 строка исходника → 8 абзацев ЧЕРНОВИКА = 0.12:1 ← это видел критик, думал 9 576 +``` + +⇒ Дешёвая модель, вопреки инструкции «не копируй построчную разбивку», скопировала её почти +дословно. **Китайский критик действительно получил готовые пары.** Связка «выровнен вход → мало +размышления» держится: одна модель, одна роль, разрыв ×9. + +### 14.8 ⛔ СЛЕДСТВИЕ, КОТОРОЕ ЛОМАЕТ СМЕТУ V6 + +Владелец: «если критику подавать уже отредактированный текст, там точно не будет выравнивания». +Верно, и в V6 критик получает именно ХОРОШИЙ ПЕРЕВОД, а не черновик. Значит он попадёт в +«английский режим» ДАЖЕ НА КИТАЙСКОМ. + +Пере-счёт: китайский критик стоил $0.021/клетка НА ВЫРОВНЕННОМ входе; на невыровненном он идёт к +цене переписа ($0.05–0.07). ⇒ **V6 на Мастере Гу становится ~$340 вместо $190, то есть в полтора +раза ДОРОЖЕ боевой связки ($226), а не дешевле.** Моя прежняя смета V6 недействительна. + +⇒ **Выравнивание перестало быть «идеей для экономии» и стало условием жизнеспособности V6.** + +### 14.9 КАК СОПОСТАВЛЯТЬ — АЛГОРИТМА НЕТ, И ЭТО НЕ ОГОВОРКА + +Вопрос владельца: «какой алгоритм сопоставления с оригиналом?» Ответ: сопоставления НЕТ. +Если шаг 1 режет книгу на смысловые ходы с номерами, а перевод выходит блоками с ТЕМИ ЖЕ +номерами, соответствие возникает ПО ПОСТРОЕНИЮ — производитель сам заявил, что чему отвечает. +Проверка разметки детерминированна и бесплатна: каждый номер ровно один раз · порядок монотонный · +длина блока в разумном отношении к длине хода · термины банка хода N присутствуют в блоке N. +Не сошлось — блок помечается «без адреса» и обрабатывается как сейчас. + +### 14.10 ⛔ ВТОРОЕ ВОЗРАЖЕНИЕ ВЛАДЕЛЬЦА, КОТОРОЕ ПЕРЕДЕЛЫВАЕТ СХЕМУ + +«Не испортит ли это качество? Мы просим модель, которая занимается основным переводом, делать +разметку — как бы она не поплыла». **Возражение подпёрто нашим же замером:** обогащённый черновик +получил ОДИН добавленный структурный блок и вернул исходник вместо перевода на 4 главах из 16. +Плюс запрет D21.6: языковые констрейнты в reasoning-ослабленных путях поднимали эхо 3/10 → 9/10. +⇒ Нагружать переводчика разметкой НЕЛЬЗЯ, это измеренный класс отказа. + +**Лечение:** разметку делает ОТДЕЛЬНЫЙ дешёвый вызов — вход исходник + готовый перевод, выход +ТОЛЬКО соответствие (номер хода → первые слова абзаца), ни строчки текста не переписывается. +Безопасно по квирк-бюллетеню (`00-provider-quirks.md:51`): «для дешёвой роли ЭКСТРАКЦИИ (не +перевод) thinking можно выключить — echo бьёт по переводу, не по JSON-выводу». Маркировка — это +экстракция. Основной перевод сохраняет один мандат и не рискует; провал разметки перевод не портит. + +### 14.11 ЧТО СНЯТО С МОИХ ЖЕ СЛОВ + +* **Снято обещание «выравнивание срежет треть цены конвейера»** — висело на одном наблюдении, + объяснённом мною же. До замера это догадка. +* **Снята прежняя смета V6** (§14.5): она считана по китайскому критику на выровненном входе, + которого в V6 не будет. +* ⚠ **Норма, заведённая этим разговором:** прежде чем строить схему поверх наблюдения, проверить + наблюдение отдельным дешёвым замером. Мы дошли до третьего этажа проектирования на фундаменте + из одного совпадения. + +### 14.12 ЭМБЕДДИНГОВОЕ ВЫРАВНИВАНИЕ — идея владельца, инвентаризация возможного (17.08) + +Владелец: «а что если через эмбеддинги построить таблицу оригинал/перевод… может есть современные +мультиязычные эмбеддинговые алгосы?» + +**Класс инструментов, который сюда ложится (называю как кандидатов, не как выбор):** +* **LaBSE** (Google, 109 языков) и **LASER** (Meta) — эмбеддинги, обученные ИМЕННО на задаче + «найти перевод этого предложения», а не на общей близости смысла; +* **vecalign** — динамическое программирование по эмбеддингам, умеет МНОГИЕ-КО-МНОГИМ (склеенные + отрезки), то есть ровно тот случай, который назвал владелец: несколько абзацев исходника в один + русский; +* **BERTalign** — новее, на LaBSE, авторы целились в ХУДОЖЕСТВЕННУЮ прозу с перестройкой; +* классика Gale-Church (по длинам) для zh↔ru слабая: соотношение длин пляшет, общих слов нет. + +**Инвентаризация машины (проверено исполнением):** `sentence-transformers 5.6.0`, `transformers`, +`torch 2.12.1+cpu`, 16 ядер, GTX 1070 8 ГБ. В кэше УЖЕ ЛЕЖАТ `LaBSE`, `bge-m3`, +`Qwen3-Embedding-0.6B` — то есть проверить можно немедленно и за $0. + +**⛔ ОГРАНИЧЕНИЕ ВЛАДЕЛЬЦА: в облаке только CPU + диск, видеокарты нет.** Из-за этого: +* выравнивание — **не горячий путь**: делается один раз на книгу и лежит на диске; +* объём работы режется тремя рычагами: эмбеддить не предложения, а СМЫСЛОВЫЕ ХОДЫ (43–78 на кусок + против сотен предложений) · сперва ЯКОРЯ (термины банка, числа, реплики, имена) прибивают + опорные точки, эмбеддинги нужны лишь на промежутках · модель меньше + int8/ONNX; +* **и главное разведение ролей:** в ЛАБОРАТОРИИ выравнивание — измерительный инструмент (проверить, + работают ли дешёвые механизмы); в ПРОДЕ адреса берутся из номеров смысловых ходов ПО ПОСТРОЕНИЮ, + и ни эмбеддингов, ни GPU там не нужно. ⇒ эмбеддинги — способ ПРОВЕРИТЬ более дешёвый механизм, + а не сам механизм. + +**Чего я не знаю:** как эти алгоритмы ведут себя на НАШЕЙ прозе. Заявленное качество меряют на +корпусах, где перевод следует за оригиналом близко; у нас редактор ОБЯЗАН сливать абзацы (77 строк +→ 43 абзаца). Числа из чужих статей сюда не переносятся. Плюс поле движется быстро — сверка с +текущим состоянием отдельным ресёрчем не помешает. + +**Чем мерить точность БЕЗ ручной разметки (у нас уже есть):** банк терминов — если отрезок +исходника содержит термин, выровненный отрезок перевода обязан содержать его канонную форму; +и маржевые посадки — мы сами знаем, куда вставили порчу, и выравнивание обязано указать туда же. + +--- + +## 15. СЕССИЯ №4 (20.08) — КУРС `PLAN-17-08.md`, ШАГ 1 + +Восстановление контекста после компакта: прочитаны ЦЕЛИКОМ заказ (`POLYGON_EXP2223_REDO_SESSION_PROMPT.md`, +207 строк), хендофф, `PLAN-16-08.md`, `PLAN-17-08.md`, этот журнал, `СВЕРКА-РЕВИЗИИ-16-08.md`, +бриф владельца `START_PROMT.MD` (V0–V6), отчёт секции Д17–Д20, CURRENT-STATE, инвентаризация `~/books`. + +### 15.1 ШАГ 1 — Г5 ЗАКРЫТ. $0, ни одного платного вызова + +Инструмент: `itog_d4.py --sens` (новый режим В СВОДЕ, отдельного файла не заводил — энтропия). +Плюс два рычага `--drop=`/`--restore=`, оба ПУСТЫ по умолчанию: печатаемый вердикт не меняется. +Правило решения записано в докстринге `sens()` ДО прогона. + +**Результат — вердикт «H-1 НЕ УСТАНОВЛЕНА» устоял.** Секция отчёта **Д21**. Ключевое: + +1. Арифметика находки P13 воспроизведена ПОБАЙТНО (n=13 · sd 1.4058 · порог 1.0917 · + `A6/A0` −1.0938 · запас +0.0020) — спор не о вычислении. +2. Переворот живёт только в сценарии, который ВОЗВРАЩАЕТ пачку аннулированной сессии и + ОДНОВРЕМЕННО снимает пачки-валидации: норма применена в одну сторону и отменена в другую. + На нынешнем дереве снятие валидации даёт запас **−0.0710** — ниже порога. +3. Посылка «другой режим замера» по УРОВНЮ не подтверждается: `д-01` отклоняется на +2.1 sd, + а неоспариваемая `д-21` соседнего набора — на +2.4 sd. По промту проверить нечем: транскрипт + `agent-aa9688762dc325180.jsonl` с диска исчез. +4. **Калибровка порога закрыта ПО ЗНАКУ** (находка ревизии №6, R73 против P40): шум самого + контраста против пола — `A1B/A0` 1.03× · `A3/A0` 1.01× · `A6/A0` **0.65×**. Пол на Д4 + откалиброван честно, а для несущего H-1 контраста ЗАВЫШЕН в 1.5×. Число R73 (1.40×) — + свойство прохода `border`, переносить не следовало. +5. **Шум судьи зависит от АРМА**: тот же текст, две сессии — `A0` 2.24, `A3` 3.18, `A1` 3.26. + Контурные тексты оцениваются в полтора раза менее устойчиво, чем перепис. +6. Наклон наборов реален (p1 строже p2 на +1.50 ошибки, пол +1.79, p=0.0117), но контрастов не + задевает: армы сбалансированы по половинам, снятие пачки убирает все армы единицы разом. + Контраст ВНУТРИ половины даёт те же числа до 4-го знака. + +**Честный итог сильнее печатаемого:** `A6/A0` во всех пяти способах счёта встаёт вплотную к порогу +(запас −0.62 · −0.07 · −0.51 · +0.002 · −0.007) при шаге шкалы в ОДНУ ошибку. Это «эффект ровно +размером с шумом прибора», то есть та же болезнь, что заход Д17 намерил на новом материале. + +### 15.2 ⛔ МОЯ ОШИБКА В ПЛАНЕ, НАЙДЕНА ЧТЕНИЕМ КОДА + +`PLAN-17-08.md` нёс строку «Г5: H-1 переворачивается, дорогой черновик ВСЁ-ТАКИ помогает». +**Неверно по знаку.** В своде минус = «арм ХУЖЕ эталона» (сверка: `A1B/A0` −3.53 при `A1B` 7.52 +против `A0` 4.00). Значит «перешёл порог» читалось бы «дорогой черновик + контур ЗНАЧИМО ХУЖЕ +связки» — H-1 опровергается, а не подтверждается. Строка в плане исправлена. + +### 15.3 Смежные находки ревизии, разобранные тем же заходом + +| находка | статус после проверки исполнением | +|---|---| +| **P13** H-1 переворачивается | **ЗАКРЫТА**: арифметика верна, вывод не следует (Д21.1–21.2) | +| **P12** пачка `69de717f3f` в замере | **ЗАКРЫТА ИСПОЛНЕНИЕМ**: на диске `.ANNULLED`, свод её не читает; база уже n=14/1.65. Строка сверки 16.08 устарела | +| **P07** гейт честности пола | **снята по существу**: сдвиг реален, объявлен, вердиктов не двигает (Д21.4) | +| **R73/P40** порог смещён в противоположные стороны | **ЗАКРЫТА ЗАМЕРОМ**: направление названо числом (Д21.3) | +| **P42** два пре-рег-правила о маржевом гейте | ⛔ **ОТКРЫТА — ВОПРОС ВЛАДЕЛЬЦУ.** Объявлена девиацией в Д21.6; сессия решать не вправе | + +### 15.4 Команды + +``` +eval/.venv/bin/python eval/dovodka/itog_d4.py --axis=d4 --sens # весь замер Г5 +eval/.venv/bin/python eval/dovodka/itog_d4.py --axis=d4 # базовый свод (не изменился) +``` + +### 15.5 ШАГ 2 — ПОКУПКА ОСТАНОВЛЕНА ГАРДОМ НА ПЕРВОЙ ЖЕ ГЛАВЕ. Что найдено + +Санкция владельца 20.08 «Хорошо, поехали» на $1.70. Построен `eval/dovodka/podacha.py` +(зафризен `650b4fe` вместе с потолками ФД-K/ФД-L). Куплено 4 клетки одной китайской главы, +потрачено $0.2927 из $0.40 — **гард отказал и остановил прогон**, как и положено по норме. + +**1. Цена клетки впятеро выше сметы.** $0.101 против сметных $0.0204. Причина найдена и +разведена БЕСПЛАТНО: не потолок вывода (арм `ZF` — побайтно тот же запрос, что боевая связка, +но с потолком 32000 против 16000 — даёт всего ×1.23 размышления на 16+16 клетках), а САМА +ПОСАДКА. На одной и той же главе, тем же промтом, той же моделью: чистый черновик 1 612 токенов +размышления, черновик с удалённым абзацем — **22 887, то есть ×14**. +⇒ **реальный дефект в черновике запускает ту же дорогую работу выравнивания, что и невыровненный +формат.** Это находка, а не помеха: цена критика определяется не длиной входа, а тем, сколько +несоответствий он вынужден локализовать. + +**2. Идея владельца работает — на первом же замере.** Та же глава, та же модель: +размышление 22 887 → 10 211 (−55%), цена $0.101 → $0.0485. +⚠ **Но находок стало вдвое меньше: 24 → 12.** Это ровно тот риск, который пре-рег объявил ДО +покупки, и он реализовался на первой клетке. + +**3. ⛔ ГЛАВНАЯ НАХОДКА, И ОНА НЕ ПРО ДЕНЬГИ: критик НЕ ВИДИТ ПРОПАВШЕГО АБЗАЦА.** Из черновика +удалён целый абзац («Хо Цзунь с трудом поднялся из-под обломков…», 124 знака). Критик выдал +24 находки, все локальные, и **не упомянул пропажу ни словом** — при том что в том же тексте +поймал снятую частицу «не». `glm-5` тоже не упомянул. Мандат критика прямо требует искать +«потери смысла». ⇒ **пропуски обязан ловить детерминированный гейт покрытия, а не критик.** + +**4. Мой детектор пропажи оказался негодным, и поймала его НУЛЕВАЯ МОДЕЛЬ** — 3 ложных +срабатывания из 4 на чистых клетках. Проверены ещё два правила: строгое (маркер пропажи И редкие +слова в ОДНОЙ строке) не срабатывает ВООБЩЕ ни на одной клетке; счётное (число находок класса +«пропущено») сигнала не даёт — на испорченной клетке их 2 против 2·3·3·7 на чистых. +⇒ эндпойнт «сохранил ли критик бдительность к пропускам» этим способом не меряется, потому что +базовая бдительность УЖЕ нулевая. + +**5. `glm-5` с включённым размышлением НЕ ВЛЕЗАЕТ в потолок вывода на китайском:** клетка +`dv-k-p0gl-13c5f52fa3` вернулась `finish=length` — деньги списаны, текста нет. Квирк-бюллетень +(:52) предупреждал, риск был объявлен в шапке файла до покупки, и он материализовался. + +**Что это меняет в дизайне.** Дорог именно абзацный пропуск; без него замер возвращается к +цене чистого критика (zh ~$0.008, en ~$0.042 за клетку) и ВЕСЬ план влезает в санкционированные +$1.70. Отказ от абзацной посадки ничего не теряет, потому что её эндпойнт уже отвечен: критик +пропуска не видит вовсе. Решение — за владельцем, сессия панель не режет. + +### 15.6 РЕШЕНИЕ ВЛАДЕЛЬЦА 20.08: ПРОГОН ОСТАНОВЛЕН + +Дословно: «Прогон предлагаю стопнуть раз критик не находит проблемы пропуска абзацев он скорее +всего еще сильнее будет деградировать». Английская пара НЕ покупалась вовсе (ФД-L $0.00); +на китайской остались 4 клетки одной главы, $0.2927. Процессов нет, замков нет, дерево чистое. + +⚠ **МОЯ ФОРМУЛИРОВКА ВВЕЛА ВЛАДЕЛЬЦА В ЗАБЛУЖДЕНИЕ, И ЭТО МОЯ ОШИБКА, А НЕ ЕГО.** Я написал +«абзацная посадка» и «словесная посадка», и он прочёл это как ФОРМУ ПОДАЧИ — будто предлагается +кормить критика таблицей «слово ↔ слово». Ничего такого в замере нет: «посадка» — это намеренная +порча, которую я вношу в черновик, чтобы знать правильный ответ заранее; вход критика всё время +остаётся прежним, целый исходник плюс целый перевод. Термин рига был употреблён в разговоре с +владельцем без расшифровки — прямое нарушение нормы «продуктовые формулировки, термины +расшифровывать при первом употреблении». + +**Возражение владельца по существу — верное и в замере подтверждается.** Дробить вход мельче +означает увеличить число кусков, которые модель обязана сопоставить; фразеологизм и идиома +живут в границах фразы, а не слова, и наш собственный критик ловит их именно как ЦЕЛОЕ («愿赌服输 +— смысл „проиграл — расплачивайся“, а не „уговор дороже денег“»). Мельчить нельзя. + +**Что остаётся правдой после остановки** (одна глава, 3 годные клетки — сигнал, не вывод): +1. фраза о параллельности срезает размышление на 55% и цену вдвое, но и находки вдвое; +2. критик не видит удалённого абзаца, ловя при этом снятую частицу «не» в том же тексте; +3. реальный дефект в черновике удорожает критика в 14 раз против чистого; +4. `glm-5` с включённым размышлением не влезает в потолок вывода на китайском. + +### 15.7 ⛔ ПОПРАВКА К МОЕМУ ЖЕ ОБЪЯСНЕНИЮ: «КИТАЙСКИЙ КРИТИК ПОЛУЧАЛ ГОТОВЫЕ ПАРЫ» — НЕВЕРНО + +Вопрос владельца 20.08: «а если критику сказать, что текст выровненный, перевод идёт след в след?» +Проверено ДО ответа, бесплатно, на 16 китайских главах. + +**Счёт кусков совпадает, ПОЗИЦИИ — нет.** + +``` +ровно 1:1 по числу кусков 1 глава из 16 (расхождение 0…17) +корреляция длин по одинаковому номеру медиана +0.12 (≈ ноль) +реплика исходника на том же номере, что реплика перевода 193/385 = 50% + при шансовом поле ≈33% (такова доля реплик в тексте) +``` + +⇒ **«след в след» — НЕПРАВДА и на китайском тоже.** Прежняя запись §14.7 («китайский критик +действительно получил готовые пары») выведена из МЕДИАН счёта кусков (78 строк → 80 абзацев, +0.96:1) и позиционной проверки не проходит. Медиана скрыла разброс. + +**Что от объяснения уцелело — не выравненность, а ЗЕРНИСТОСТЬ.** На китайском обе стороны нарезаны +кусками по 24–30 знаков и идут примерно в одном порядке, поэтому критик работает локально даже при +сдвиге на пару позиций. На английском одна строка против восьми абзацев — локальной опоры нет +вовсе. Разрыв размышления ×9 замерен и остаётся фактом; моё объяснение его ослаблено. + +**Побочное наблюдение, полезное для дела:** глава `9120ad112e` — единственная, где счёт кусков +совпал точно (79/79), и там же якорь реплик даёт **96%**. То есть соответствие держится, пока не +случится слияние или дробление, после чего сдвигается ВСЁ последующее. Значит лечение — не «сказать +модели», а дешёвая ПЕРЕ-СИНХРОНИЗАЦИЯ по якорям (реплики уже дают 50% против шансовых 33%). + +⚠ И вывод для формулировки: врать модели нельзя не по этике, а по механике — пре-рег объявил +заранее, что при ложном обещании точных пар модель будет искать соответствие, не найдёт и потратит +БОЛЬШЕ. Правдивая версия (порядок сохранён · покрытие обязано быть полным · членение может +отличаться · точных пар не искать) уже куплена и дала −55% размышления при −50% находок. + +### 15.8 ГИПОТЕЗА, РАДИ КОТОРОЙ СТОИТ ЖИТЬ ДАЛЬШЕ — И ЕЁ ЦЕНА, СНЯТАЯ С КУПЛЕННОГО ($0) + +Владелец 20.08: «подумать и придумать ахуенное решение, вероятную гипотезу которую можно проверить». +Ниже — четыре бесплатных замера по УЖЕ КУПЛЕННОМУ сырью, в порядке, в котором они делались. + +**Замер 1 — снос регистра. ГИПОТЕЗА НЕ ПОДТВЕРДИЛАСЬ.** Идея: порядок владельца решается не +смыслом, а регистром (он назвал «шествовала» вместо ярости, смягчённый мат, вежливое «Пойдёмте» в +выплюнутой реплике). Посчитал на его же панели плотность мата, восклицаний, длину фразы, долю +реплик — против исходника. Порядок владельца (`ZP > ZF > Z1 > Z0`) НЕ воспроизводится: признаки +дают `ZF > Z1 > Z0 > ZP`, его фаворит последний. Причина видна: на прочитанных им главах мата +0.61 и 0.00 на 1000 знаков, восклицаний ноль, длины фраз в пределах пары процентов — агрегатные +признаки регистра варианты не различают вовсе. Идея снята, $0. + +**Замер 2 — РАЗМЕР ПРИЗА. Он больше всего, что мерила дуга.** По 32 прочитанным единицам +(обе пары, оба машинных читателя): + +``` +размах между ВСЕМИ архитектурами (Z8R 2.16 … Z1 2.88) 1.78 позиции +разброс ДВУХ ПРОГОНОВ ОДНОЙ связки (Z0 против ZF) 2.59 позиции +выигрыш «взять лучшую из двух» против «взять любую» +1.30 позиции +лучшая смена архитектуры (Z0 2.50 → Z8R 2.16) +0.34 позиции +``` + +⇒ **выбор ПРОГОНА стоит вчетверо больше выбора АРХИТЕКТУРЫ.** Цена — один лишний дешёвый вызов +против полной пере-сборки конвейера. + +**Замер 3 — ПРИЗ ОРАКУЛЬНЫЙ, И ЧИТАТЕЛЬ ЕГО НЕ БЕРЁТ.** Два независимых машинных читателя, +выбирая между теми же двумя прогонами, совпали **9 из 16 = 56% при случайных 50%, p=0.40**. +Монетка. То есть +1.30 — это ПОТОЛОК при идеальном отборщике, а не достижимая величина. +⚠ Здесь же поймана моя ошибка в пробе: ответы читателей лежат в `.md`, а я искал `.txt`, и первая +редакция замера напечатала «прочитано обоими: 0». Число 56% снято после починки. + +**Замер 4 — ⭐ ГДЕ ОТБОРЩИК ВСЁ-ТАКИ РАБОТАЕТ.** Посчитал текстовое расхождение двух прогонов +(`difflib`, $0) и сверил с согласием читателей: + +``` +более РАЗЛИЧНАЯ половина (расхождение ≥10% текста): читатели совпали 6/8 = 75% +более ПОХОЖАЯ половина (расхождение <10%): совпали 3/8 = 38% — ниже монетки +четыре самые различные главы (16…38%): совпали 4 из 4 +самая похожая (0.3% расхождения): разошлись +zh: медиана расхождения 18% · согласие 6/8 · en: медиана 6% · согласие 3/8 +``` + +⇒ **разница zh/en — не языковая, а РАСХОЖДЕНЧЕСКАЯ**, и это независимо подтверждает поправку +владельца из Д20.6 («дело не в языке, а в диапазоне шкалы»), снятую на другом приборе. + +**ГИПОТЕЗА К ПРОВЕРКЕ (шаг 2 нового курса вместо остановленного):** +> Два прогона одной дешёвой связки плюс БЕСПЛАТНЫЙ детектор расхождения. Разошлись сильно — +> выбор осмыслен и его берёт читатель-модель; почти совпали — выбирать нечего, берём любой и +> не платим. Приз до +1.30 позиции против ≤0.34 у любой смены архитектуры. + +⚠ **Порог 10% выбран ПО ЭТИМ ЖЕ ДАННЫМ — это разведка, а не проверка.** Гипотеза обязана +проверяться на НОВЫХ главах с порогом, объявленным ДО них. n=16, разбиение по медиане post-hoc. + +**Смета проверки:** вторая генерация связки ~$0.027/глава (замер), 16 глав × 2 пары = **$0.86**; +чтение бесплатно (агент-сессии). Плюс уже потраченные $0.29 на остановленный замер подачи. + +### 15.9 ВОПРОС ВЛАДЕЛЬЦА 20.08 ПРО ПРОМТ ОДНОПРОХОДКИ — ОТВЕТ ПО КОДУ, НЕ ПО ПАМЯТИ + +«Ты писал хороший промт для неё, чётко ограниченный? Который я писал в стартпромте?» + +**НЕТ.** `contour.a2_msgs` собирает однопроходку так: боевой промт ПЕРЕВОДЧИКА плюс мандатная +часть боевого промта РЕДАКТОРА, механически склеенные — четыре строки выброшены (`A2_DROP`), +четыре подставлены (`A2_SUBST`). Среди подстановок есть прямая мета-фраза про наш конвейер: + +``` +"Твой мандат — художественная редактура черновика со сверкой по исходнику:" + → "Отдельного редактора после тебя НЕ БУДЕТ, поэтому мандат редактуры входит в твой:" +``` + +**Почему так сделано — основание честное, но оно измерительное, а не продуктовое.** Контраст +«однопроходка против связки» в эксп-21 был загрязнён: у арма без редактора системный промт вдвое +короче (×1.92), и замеренная разница могла быть разницей ОБЪЁМА ИНСТРУКЦИИ, а не топологии. +Уравнивание было правильным ДЛЯ ЗАМЕРА. Цена — **однопроходка ни разу не испытывалась как +продуктовый промт**, только как уравненный по объёму двойник связки. + +**И при этом она уже выигрывает, неся гандикап:** по судейской оси от связки не отличается; по +банку лучше на ОБЕИХ парах (потерянных терминов на главу 1.38 против 2.88 на zh, 0.31 против 0.56 +на en); при слепом чтении владелец поставил её ПЕРВОЙ на английском; стоит один вызов вместо двух. + +**Цена двух прогонов однопроходки против одной связки — по текущему прайсу, из замеренных токенов:** + +``` +zh: связка (черновик $0.00514 + перепис $0.02016) = $0.02531 · две однопроходки $0.03498 = 1.38× +en: связка (черновик $0.00193 + перепис $0.03527) = $0.03720 · две однопроходки $0.04235 = 1.14× +``` + +⇒ За 1.14–1.38 цены нынешнего прода можно получить ДВА независимых текста и выбрать лучший. +Удвоение самой связки стоило бы 2.0×. **Это соединяет самый сильный измеренный кандидат (E) с +самым сильным измеренным рычагом (A) почти без доплаты.** + +**Что осталось дотестировать в однопроходке, по убыванию:** +1. **Промт начисто как РОЛЬ** (пункт 3 брифа V6): роль литературного переводчика · оригинал плюс + ТОЛЬКО релевантный банк · рамки, а не бесконечные правила · критерии проверки результата · + БЕЗ мета-фразы про отсутствующего редактора. Сравнить со склейкой. +2. **Банк без черновика** — единственное измеренное слабое место. Терминолог опирается на то, как + термин передавали черновики. Замер детерминированный, судейства не требует. +3. **Границы сцен как единица нарезки** (тот же пункт брифа) — сейчас режем по числу знаков, + не делалось ни разу. +4. **Два прогона однопроходки** плюс детектор расхождения — см. цену выше. + +### 15.10 ⛔ ВЛАДЕЛЕЦ ПОЙМАЛ ПОТЕРЮ ПРАВИЛ В НОВОМ ПРОМТЕ. Покупка остановлена, гейт заведён + +Вопрос 20.08: «Ты этот промт перегенерил на основе двух имеющихся? Там же довольно много +пропущено? Ты читал оригиналы промтов переводчика и редактора?» + +**Читал — и всё равно потерял три правила.** Покупка остановлена немедленно; успели уйти только +три пред-полётные пробы ($0.00174), боевых клеток НЕТ. Просроченный замок мёртвого процесса снят +законно (процесс проверен — его нет). + +**Что было потеряно, построчной сверкой с `backend/prompts/zh-ru/{translator,editor}.md`:** + +1. ⛔⛔ **«Повтор, стоящий в параллельных позициях самого исходника, — авторский рефрен: сохраняй + его, не разнообразь лексику там, где автор повторяется намеренно.»** Потеря ОПАСНАЯ: мой новый + промт прямо говорит «буквальность здесь не достоинство» и разрешает синонимическую замену, + то есть толкает ровно в обратную сторону. Модель вычистила бы авторский рефрен как «лишний + повтор». Возвращено, и не отдельным пунктом, а ГРАНИЦЕЙ внутри блока «ТЫ ВПРАВЕ» — там, где + свобода объявлена, там же назван её предел. +2. **«Кальки жестов и идиом · неверно понятые реалии»** с конкретным примером (поклон как «ударил + головой»). Возвращено запретом 3: жест, идиома и реалия названы тремя местами, где подстрочник + врёт чаще всего. +3. **«Не пересочиняй сцены».** Возвращено в запрет 2. + +Плюс одно СОЗНАТЕЛЬНОЕ снятие, теперь объявленное явно: боевое «не сокращай» отменено решением +владельца 16.08 («вольность не ошибка»); полнота охраняется не запретом сокращать, а запретом +терять смысловые ХОДЫ — то есть на уровне содержания, а не числа слов. + +**Заведён ГЕЙТ ПОКРЫТИЯ (`rol.py --coverage`, часть селфтеста).** Каждое правило обоих боевых +промтов перечислено характерной подстрокой, у каждого объявлена ДИСПОЗИЦИЯ, и селфтест роняет +замер, если хоть одно правило не покрыто: + +``` +"в промте" правило стоит в ядре нового промта (проверяется вхождением ключевых слов) +"блок пары ДОСЛОВНО" правило приезжает пар-блоком — сверяется САМА СТРОКА боевого промта +"покрыто смыслом" покрыто более полным местом промта, но не дословно; место названо +"снято" снято сознательно, с основанием прямо в таблице +``` + +Итог гейта: **zh — 38 правил, непокрытых 0** (18 в промте · 7 дословно блоком · 4 смыслом · +9 снято); **en — 43 правила, непокрытых 0** (18 · 12 · 4 · 9). + +⚠ Различие «дословно» и «смыслом» заведено ОТДЕЛЬНЫМИ диспозициями намеренно: смешать их значит +потерять ровно ту границу, на которой первая редакция и погорела. И проверка блока сверяет строку, +а не начало блока: слабая проверка пропустила бы усечённый блок. + +⚠ **Урок шире этого файла: обещание «я всё перенёс» проверяемо только механизмом.** Гейт вдобавок +сломается ГРОМКО, если чужая зона правит боевой промт, — сейчас такая правка молча меняла бы смысл +арма. + +**Мнение Fable-5 заказано ДО покупки** (просьба владельца): полный контекст проекта, оба боевых +промта, новый промт, гейт покрытия; вопросы — как он зажимал бы промт для такой задачи, разбор +построчно, что мы потеряли (сверить самому, нашей таблице не доверять), и замечания по замерам. + +### 15.11 ПРИЁМКА FABLE-5 ПРОМТА-РОЛИ (20.08). Три дефекта гейта, один — тяжёлый + +Заказана владельцем ДО покупки. Всё существенное пере-проверено моим исполнением, не принято +на слово. + +**ПОДТВЕРЖДЕНО ИСПОЛНЕНИЕМ:** + +1. ⛔⛔ **Гейт покрытия сертифицировал правило словом из правила ПРОТИВОПОЛОЖНОГО смысла.** + Строка `("editor","лишние повторы","промт","разнообразь")` считала боевое «убирай лишние + повторы» покрытым, потому что слово «разнообразь» есть в промте — но стоит оно в + РЕФРЕН-ГРАНИЦЕ, то есть там, где повторы убирать ЗАПРЕЩЕНО. Требования убирать немотивированный + повтор в новом промте не было вовсе (проверено грепом). Гейт, заведённый именно против потерь, + сам произвёл потерю и напечатал «покрыто». Сертификат пере-привязан; в промт и в критерий ЯЗЫК + вернулось «немотивированные повторы убирай». +2. **Короткие правила гейт не сканировал вовсе:** порог `len(s) < 25` выбрасывал строку + «- Сноски: {{footnotes}}» (23 знака). Порог снижен до 10; правил на zh стало 39 против 38. +3. **Мёртвые строки таблицы неотличимы от живых:** три строки не срабатывали ни разу + (пере-считано точно; первая грубая проверка дала 14 — моя ошибка сопоставления по усечённой + строке). Заведена проверка «каждая строка COVERAGE обязана сработать хотя бы раз», две + строки-заголовка удалены как мёртвые. +4. **Потеряна оговорка «Максимума длины абзаца нет»** — клауза внутри покрытого буллета. + Гейт слеп к потерям на уровне полуфразы; класс зафиксирован, оговорка возвращена. + +**НЕ ПОДТВЕРДИЛОСЬ:** висячая ссылка «в глоссарии ниже» при отсутствии банк-блока — на всех +32 главах обеих пар термы банка есть, ссылка всегда обеспечена. Дефект латентный, а не живой; +условность «(если он приложен)» всё равно возвращена. + +**ПРИНЯТО ИЗ РАЗБОРА, сверх дефектов** (каждое — одна строка, промт не разбух): +* «Живой не значит нарядный: если герой идёт — он идёт, а не шествует» — закрывает КЛАСС, из + которого пришла главная претензия владельца, а не отдельный случай; +* «Сворачивая описание, ты сжимаешь СЛОВА, а не факты» — снимает рассогласование зернистости: + ядро охраняло смысловые ХОДЫ, пар-блок — смысловые АТОМЫ, и «сворачивать описание» было + легально по одному порогу и нелегально по другому; +* мотивация канона («книга выходит на тысячу с лишним глав, форма имени обязана совпасть») — + правило Anthropic «давай мотивацию, а не только запрет»; +* оговорка про иноязычные вставки — иначе «не оставляй исходный язык» выполет французский слой, + который пар-правила велят СОХРАНЯТЬ; +* пример «поклон — не ударил головой» обезличен: это китайский кэтоу, уехавший в пар-нейтральное + ядро, — ровно та утечка, которую запрещает гардрейл общности; +* удалён дублирующий запрет (гигиена вывода стояла дважды) — правило Anthropic «минимально + необходимая структура»; +* императив «проверь себя по ним сам, молча» снят, критерии оставлены УТВЕРЖДЕНИЕМ. Основание + двойное: документация Anthropic прямо предупреждает, что перенесённые инструкции самопроверки + дают ИЗБЫТОЧНУЮ проверку — лишние токены и латентность; а у нас размышление тарифицируется как + выход и составляет 85% цены дорогой клетки. **Дельта `reasoning_tokens` RN против ZP объявляется + обязательным печатаемым числом отчёта** — проверка бесплатна, токены уже пишутся в клетки. + +### 15.12 ⭐ КРОСС-ЧИТАТЕЛЬСКИЙ ПРИЗ — БЕСПЛАТНЫЙ ЗАМЕР, КОТОРЫЙ ПЕРЕВОРАЧИВАЕТ ВАРИАНТ A + +Возражение Fable: приз «лучший из двух прогонов» (+1.30 позиции) измерен ТЕМ ЖЕ прибором, которым +предлагается выбирать, — круговое рассуждение. Развязка стандартная: выбирает прибор S, оценивает +прибор E ≠ S. У нас ДВА независимых читателя, значит считается за $0 на купленном. + +Метод: выбор читателя A между двумя прогонами связки, а оценка выигрыша — по рангам читателя B +(и симметрично). + +``` +ВСЕ главы: n=16 · выигрыш +0.28 позиции · 18 подтвердили, 14 опровергли · p=0.2983 +расхождение ≥10% текста: n=7 · выигрыш +2.36 позиции · 12 подтвердили, 2 опровергли · p=0.0065 +расхождение <10%: n=9 · выигрыш −1.33 позиции · 6 подтвердили, 12 опровергли · p=0.9519 +``` + +⇒ **Три вывода, и все три меняют картину:** +1. **Наивный «лучший из двух» бесполезен** — в среднем +0.28 при p=0.30, то есть шум. Круговой + приз +1.30 был в основном регрессией к среднему на шуме ранжировщика, как Fable и сказал. +2. **Но на разошедшихся главах приз РЕАЛЕН и БОЛЬШЕ оракульного: +2.36 позиции, p=0.0065** — + и это уже НЕ круговое число: выбирал один читатель, оценивал другой. +3. **На похожих главах отбор ВРЕДИТ: −1.33.** То есть применять его вслепую хуже, чем не + применять вовсе. Бесплатный детектор расхождения перестаёт быть оптимизацией и становится + УСЛОВИЕМ работоспособности схемы. + +⚠ Порог 10% по-прежнему выбран post-hoc на этих же данных, n=7 против n=9. Кросс-читательская +конструкция снимает КРУГОВОСТЬ, но не снимает подгонку порога: он обязан быть заморожен пре-регом +и проверен на новых главах. + +### 15.13 РЕШЕНИЕ ВЛАДЕЛЬЦА 20.08: СМЕНА ПРИБОРА. Первичным становится СЛЕПОЕ ЧТЕНИЕ + +Дословно: «меняем принцип судейства. Вот тот текст что ты давал мне на слепое чтение, теперь корми +фабл 5 и пусть он решает какой из вариантов ближе по художественному смыслу, лучше по переводу». + +**Что запущено.** Fable-5 читает ТУ ЖЕ панель, что читал владелец (`ЧТЕНИЕ-{zh,en}.md`, армы +`Z0`/`ZF`/`ZP`/`Z1`), двумя отдельными сессиями — по одной на пару, чтобы порядок одной не +переносился на другую. Ответы в `ОТВЕТ-{zh,en}.FABLE.md`, файл владельца не трогается. + +⚠ **Идентичность читателя записана ДО запуска** — `blind-keys-chtenie-z17/READERS-fable-vladelets.json`. +В заходе Д17 я записал её постфактум, и это было объявлено нарушением нормы; здесь порядок +соблюдён. Сессия зарегистрирована `runs.py` ДО запуска (#91, судья д-91). + +⚠ Читателю запрещены: `blind-keys*`, `dv-*.json`, код зоны, каталоги `~/books/dovodka`, +`~/books/chtenie-z17`, `~/books/judging`, файл ответа владельца, дифф-инструменты и дочерние +агенты. Транскрипт проверяется греп-аудитом ПОСЛЕ — механизм тот же, что у судейских сессий. + +**Что это решает.** Панель та же, значит порядок владельца становится ЭТАЛОНОМ. Совпадёт порядок +Fable с его порядком — у нас есть масштабируемый первичный прибор вместо счётчика ошибок, +у которого Спирмен с владельцем на английском **−0.64**. Разойдётся — прибор не готов, и это тоже +результат за $0. + +### 15.14 ЭКСТРАКТОР: почему чинить его на месте нельзя и что сделано вместо + +Владелец: «Ну почини экстрактор. В чём проблема то?» + +**Проблема механическая и замерена.** `bakeoff.uid_of` = `sha1(source.strip())`, то есть uid +единицы есть хеш ТЕКСТА ЦЕЛИКОМ, вместе с пробелами. Возврат абзацев меняет пробелы ⇒ меняет все +uid. Цена: **289 оплаченных клеток на $2.6385** теряют адресата, плюс семь файлов слепых ключей и +все судейские ответы английской пары. Манифест это поймает и остановит покупку (гейт работает), +но история оси всё равно умрёт. + +**Что сделано вместо.** Починка НА ПОДАЧЕ: тот же текст той же единицы, тот же uid, но с +возвращёнными границами абзацев из epub. Функция взята у `podacha.py`, второй раз не написана; +проверено — текст всех 16 английских единиц находится в абзацной версии побайтно. + +⚠ **И это ОТДЕЛЬНЫЙ АРМ `RA`, а не подмена входа у `RN`.** Иначе несущий контраст смешал бы два +эффекта, и разделить их было бы нечем: +``` +RN / ZP — эффект ПРОМТА (вход у обоих ОДИНАКОВЫЙ, плоский) +RA / RN — эффект ПОДАЧИ (промт у обоих ОДИНАКОВЫЙ, новый) +``` +У китайской пары `RA` невозможен по построению: её исходник абзацы не терял. + +⛔ **ДОЛГ ЧУЖОЙ ЗОНЕ, назвать оркестратору:** настоящая починка — в бэкенде, у продуктового +экстрактора epub. Полигонный `pair_en.raw_text` чинить нельзя ценой оси; бэкенд правится своей +зоной. Пинг обязателен. + +### 15.15 РЕФАКТОРИНГ ПРОМТА И ЧТО ПОЙМАЛ СОБСТВЕННЫЙ ГЕЙТ + +Возвращённые правила и правки по Fable раздули ядро с 2743 до 3835 знаков — системный промт стал +**1.22× склейки** вместо 1.05×, то есть вернулся конфаундер эксп-21 (там было ×1.92, и вывод +оказался про ОБЪЁМ инструкции, а не про топологию). + +Лечение — то, что советовали и владелец («можно дорефакторить или что-то выкинуть»), и Fable, и +документация Anthropic («минимально необходимая структура»): **два пересекавшихся списка сведены +в ОДИН.** Было «ЧЕГО НЕЛЬЗЯ — четыре запрета» плюс «ПО КАКИМ КРИТЕРИЯМ — пять критериев», причём +запрет про жест/идиому размазывался между СМЫСЛОМ и ЯЗЫКОМ, гигиена вывода была только в +запретах, а РЕГИСТР — только в критериях. Модель получала две почти совпадающие таксономии и +должна была сама решать, какая главная. Стало: **пять осей, у каждой в теле — что считается +нарушением**, и одна строка «других запретов нет». Объём 1.19×. + +⚠ **ГЕЙТ ПОКРЫТИЯ ПОЙМАЛ МОЙ ЖЕ РЕФАКТОРИНГ:** после слияния списков **девять** правил боевых +промтов потеряли сертифицирующие подстроки («Перевирать факт» → «Перевран факт», «Терять +смысловые ходы» → «Пропал смысловой ход» и так далее). Правила остались, слова изменились — +и гейт честно упал, вместо того чтобы промолчать. Последний случай был тоньше прочих: подстрока +«жест, идиома и реалия» не нашлась, потому что фраза разорвана переносом строки; сертификат +пере-привязан на «подстрочник врёт». Это первое подтверждение, что гейт ловит не только чужие +правки, но и мои собственные. + +### 15.16 ⭐ НОВЫЙ ПРИБОР ОТКАЛИБРОВАН ПРОТИВ ВЛАДЕЛЬЦА. И моя же подсказка в пакете снята замером + +**Прогон 1 — пакет, который читал владелец.** Fable-5 воспроизвёл его порядок ТОЧНО на обеих парах: + +``` +zh владелец Z0 > ZF > ZP > Z1 Fable Z0 > ZF > ZP > Z1 Спирмен +1.00 +en владелец ZP > ZF > Z1 > Z0 Fable ZP > ZF > Z1 > Z0 Спирмен +1.00 +``` + +Против счёта ошибок на английском у обоих **−0.80**. Контроль шума на китайском (две генерации +одной связки) Fable взял так же, как владелец, — поставил соседями; машинные читатели захода Д17 +на той же оси его ПРОВАЛИЛИ, разведя ту же пару на три позиции. Аудит обеих сессий чист: по два +вызова инструментов, ключей и диффов не касались. + +⛔ **НО В ПАКЕТЕ БЫЛА МОЯ ПОДСКАЗКА, и я нашёл её сам, перечитав собственный текст.** Шапка +пакета говорила читателю: «вариант с наименьшим числом ошибок читался почти хуже всех». Это +наводка, и она была в том же пакете, который читал ВЛАДЕЛЕЦ, — то есть часть совпадения могла +объясняться общим внушением от меня, а не общим вкусом. + +**Прогон 2 — НЕЙТРАЛЬНЫЙ пакет.** Тексты и метки побайтно те же (проверено ассертом), рамка +переписана без единого упоминания счётчика, контроля и расхождений. Свежие читатели, чистый +контекст. Английская пара: + +``` +владелец ZP > ZF > Z1 > Z0 +Fable с подсказкой ZP > ZF > Z1 > Z0 Спирмен с владельцем +1.00 +Fable БЕЗ подсказки ZF > ZP > Z1 > Z0 Спирмен с владельцем +0.80 +счёт ошибок Z0 > ZF > Z1 > ZP Спирмен с владельцем −0.80 +``` + +⇒ **Подсказка стоила 0.20 и перестановки двух верхних мест. ГЛАВНОЕ УСТОЯЛО: боевая связка `Z0`, +у которой по счётчику НОЛЬ ошибок, у чистого читателя по-прежнему ПОСЛЕДНЯЯ из четырёх.** +Значит вывод «счётчик и читатель на английском смотрят в разные стороны» — не артефакт моей +наводки. Это его первая настоящая проверка. + +⚠ **И сверх задания: чистый читатель НЕ ЗНАЛ, что в панели есть контрольная пара** (из нейтральной +рамки это убрано), и всё равно написал «Т4 — почти клон Т2». Т4=`Z0`, Т2=`ZF` — это ровно две +генерации одной связки. Он опознал их чтением, без подсказки и без диффа. + +⚠ При этом развёл он их на 1-е и 4-е места. По букве гейта это «порядок читателя = шум», но Д20.3 +уже установила предел этого гейта: он не умеет отличить «читатель шумит» от «арм шумит», а на +английской паре две генерации связки расходятся сильно — это замерено независимо (расхождение +текста, §15.12). + +**Норма, заведённая этим:** пакет слепого чтения не имеет права называть читателю ни вердикт +другого прибора, ни существование контроля. Прежняя рамка объясняла ему, зачем всё это нужно, — +и объясняла слишком много. + +### 15.17 ИТОГ КАЛИБРОВКИ НОВОГО ПРИБОРА — обе пары, чистый пакет, чистый аудит + +``` +пара кто порядок Спирмен с владельцем +zh ВЛАДЕЛЕЦ (эталон) Z0 > ZF > ZP > Z1 +1.00 +zh Fable, пакет С ПОДСКАЗКОЙ Z0 > ZF > ZP > Z1 +1.00 +zh Fable, пакет ЧИСТЫЙ Z0 > ZP > ZF > Z1 +0.80 +zh счёт ошибок (СТАРЫЙ) ZF > Z0 > ZP > Z1 +0.80 + +en ВЛАДЕЛЕЦ (эталон) ZP > ZF > Z1 > Z0 +1.00 +en Fable, пакет С ПОДСКАЗКОЙ ZP > ZF > Z1 > Z0 +1.00 +en Fable, пакет ЧИСТЫЙ ZF > ZP > Z1 > Z0 +0.80 +en счёт ошибок (СТАРЫЙ) Z0 > ZF > Z1 > ZP −0.80 +``` + +⇒ **НОВЫЙ ПРИБОР СОГЛАСЕН С ВЛАДЕЛЬЦЕМ ОДИНАКОВО НА ОБЕИХ ПАРАХ (+0.80 и +0.80). +СТАРЫЙ согласен на китайской (+0.80) и АНТИ-согласен на английской (−0.80).** +Это и есть основание сменить прибор: дело не в том, что счётчик хуже вообще, а в том, что он +меняет ЗНАК от пары к паре, а чтение — нет. + +**Значимость посчитана точным перестановочным счётом, а не на глаз.** На панели из четырёх +вариантов случайная перестановка даёт Спирмена ≥ +0.80 в 4 случаях из 24 (0.167). Обе пары +независимо дали ≥ +0.80 ⇒ **p = 0.028**. Пакет с подсказкой дал точное совпадение на обеих +(1/24 каждая) ⇒ p = 0.0017. +⚠ Это счёт по ОДНОЙ панели на пару. Он говорит «совпадение вряд ли случайно» и НЕ говорит +«прибор работает на книге»: 4 варианта × 2 панели — калибровка, а не валидация. + +**Что чистый читатель сделал СВЕРХ задания, не зная о контроле** (из нейтральной рамки убрано +всякое упоминание, что контрольная пара существует): +* на английском назвал «Т4 — почти клон Т2»; это `Z0` и `ZF`, две генерации ОДНОЙ боевой связки; +* на китайском назвал «Т4 и Т1 дословно совпадают целыми фразами, Т1 читается как редактура этого + же черновика» — то есть опознал родство армов ЧТЕНИЕМ, без диффа и без подсказки; +* независимо от владельца нашёл сквозной дрейф пола персонажа между отрывками — класс дефекта, + который наш судья не видит ПО ПОСТРОЕНИЮ (он работает поотрывочно). + +**Аудит обеих чистых сессий:** по 2 вызова инструментов, ноль обращений к ключам, сырью, коду, +чужим ответам и дифф-инструментам. + +**Что снято и что осталось.** Снято подозрение, что совпадение произведено моей наводкой: на +чистом пакете верх и низ порядка устояли, боевая связка с НУЛЁМ ошибок по счётчику осталась +последней у читателя. Осталась перестановка двух средних мест — она и есть цена подсказки (0.20). + +### 15.18 ⛔ РЕЗУЛЬТАТ ЗАМЕРА ПРОМТА-РОЛИ: лучше склейки, но не стоит своей цены + +**Слепое чтение, первичный прибор (решение владельца 20.08).** Панель: прежняя склейка `ZP` · +новая роль `RN` · боевая связка `Z0` · её вторая генерация `ZF`. Три самые длинные китайские +главы с полной панелью, рамка нейтральная, ключ отдельно, идентичность читателя записана ДО +запуска, сессия #92 зарегистрирована ДО. Аудит: 6 вызовов, только файлы пакета, нарушений 0. + +``` +общий порядок: Z0 > ZF > RN > ZP +отрывок 1: Z0 > ZF > ZP > RN +отрывок 2: Z0 > RN > ZF > ZP +отрывок 3: RN и ZF в паритете +контроль шума (две генерации связки Z0/ZF): места 1 и 2 — СОСЕДИ, гейт ВЗЯТ +``` + +**Что это значит по существу:** + +1. **Новая роль БЬЁТ прежнюю склейку** (3-е место против 4-го). Направление правки верное. +2. **Но обе однопроходки проигрывают боевой связке**, причём связка занимает оба верхних места + ОБЕИМИ своими генерациями — то есть выигрыш не случайный розыгрыш. +3. **Выигрыш над склейкой — одна позиция на трёх отрывках, и по отрывкам он неустойчив:** + на первом `RN` последний, на втором второй. Внутри разброса. +4. **Цена: ×6.3 против склейки** ($0.1116 против $0.0177), плюс 3 клетки из 13 оборваны на + потолке вывода. ⇒ **новая роль своей цены НЕ СТОИТ на этих уликах.** + +⭐ **ДИАГНОЗ ЧИТАТЕЛЯ, и он объясняет ОБА числа сразу.** Про новую роль он написал: +«самый точный по деталям … но проза самая серая: гладкопись подстрочника». То есть промт сделал +модель ТОЧНЕЕ и МЕРТВЕЕ — ровно наоборот замыслу, при том что вольность в нём разрешена явно, +а поднимать слог запрещено прямым текстом. + +Связная гипотеза, объясняющая и ×26 размышления, и серую прозу: **я напечатал в промте пять осей, +по которым результат будут проверять, — и модель занялась удовлетворением чек-листа.** Она думает +в 26 раз больше, потому что взвешивает пять критериев; и пишет площе, потому что оптимизирует +проверяемое, а не читаемое. Это ровно тот отказ, о котором предупреждали и Fable («не сделает ли +блок критериев модель осторожной вместо смелой»), и документация Anthropic (перенесённые +инструкции самопроверки дают избыточную проверку). Я снял оттуда императив, но оставил перечень — +и этого хватило. + +⚠ **Статус: ОПИСАТЕЛЬНО.** Три отрывка, одна пара, один читатель. Это направление и механизм, +а не доказанная величина. + +⭐ **ВТОРОЕ НЕЗАВИСИМОЕ ПОДТВЕРЖДЕНИЕ ОСТРОТЫ ЧИТАТЕЛЯ:** не зная, что в панели есть контроль, +он написал «Т1 и Т4 читаются как черновик и доведённая редакция одного текста». Т1 и Т4 — это +`Z0` и `ZF`, две генерации ОДНОЙ связки. Он опознал их чтением уже второй раз подряд, на другой +панели и в другой сессии. + +**Что из этого следует для курса.** Проверяемая и дешёвая ветка теперь одна: **выкинуть из промта +блок критериев и померить размышление заново.** Если оно вернётся к тысяче, а чтение не ухудшится — +промт чинится удалением, а не деньгами, и это ровно то, что владелец предлагал с самого начала. + +### 15.19 ⭐⭐ МЕХАНИЗМ ВСКРЫТ ТРЕЙСОМ: размышление у dspro — это ЧЕРНОВИК, а не проверка + +Абляция (3 главы, два реза, трейс сохранён по флагу `TM_KEEP_TRACE=1` — совет приёмки Fable-5): + +``` +арм размышление $/клетка перевёрстка годных +RN экзамен, риск на смелости 28 809 0.1265 1.60 2/3 +RB экзамен ПЕРЕВЁРНУТ 23 301 0.1050 1.14 2/3 +RC экзамена НЕТ 11 268 0.0593 1.37 3/3 +ZP прежняя склейка (эталон) 950 0.0177 1.77 31/31 +``` + +RC против RN: размышление ×0.42, цена ×0.50. RB против RN: ×0.86 и ×0.88. +⇒ **рез помогает, перевёртывание риска — почти нет.** Но лучший рез всё ещё думает ×12 против +склейки, то есть объявленный экзамен был НЕ главной причиной. + +**И трейс объясняет, чем он был.** Прямая мера: доля предложений ФИНАЛЬНОГО текста, встречающихся +в трейсе дословно: + +``` +RB 89614f9bfa размышление 31 515 · 11/11 = 100% +RB b2a7464dbd размышление 23 301 · 61/65 = 94% +RB ef9cd38ec4 размышление 13 337 · 46/76 = 61% +RC b2a7464dbd размышление 23 410 · 66/67 = 99% +RC ef9cd38ec4 размышление 11 268 · 69/71 = 97% +RC 89614f9bfa размышление 1 180 · 0/72 = 0% ← ЕДИНСТВЕННАЯ дешёвая клетка +МЕДИАНА: 96% +``` + +⇒ **96% финального текста написано ВНУТРИ канала размышления.** Разбор трейса по содержанию: +черновик перевода в уме 54% абзацев · разбор структуры 11% · само-проверка **всего 4%** · +перечисление глоссария 3%. + +⛔ **Значит и моя гипотеза, и гипотеза приёмки были НЕВЕРНЫ по механизму.** Мы оба думали, что +деньги жжёт верификационный цикл по объявленным критериям. Верификации там 4%. Деньги жжёт то, +что **модель пишет весь перевод дважды: сперва черновиком в размышлении, потом набело в ответе.** +Мы платим за обе копии по цене выхода. + +⚠ **Режим БИМОДАЛЕН.** Одна клетка из шести (RC на главе `89614f9bfa`) в ум не писала вовсе: +1 180 токенов размышления, 0% совпадения, цена в двадцать раз ниже соседей. То есть «писать в уме» +— это состояние, в которое модель входит или не входит, а не плавная функция промта. + +### 15.20 СЛЕДСТВИЕ ДЛЯ АРХИТЕКТУРЫ, которого никто не искал + +Прежняя склейка думает 950 токенов и в ум не пишет. Новый промт — пишет. Разница между ними — +приглашение к ремеслу: «ты вправе», «живая фраза», регистр, «не украшай». **Промт, который просит +творчества, у думающей модели с неотключаемым размышлением оплачивается вторым черновиком.** + +⇒ **Боевая двухстадийная связка дёшева не вопреки, а благодаря своей топологии: она выносит +черновик ИЗ канала размышления дорогой модели В ВЫХОД дешёвой.** То же самое, что дорогая модель +делает у себя в уме за $3.96/1M, дешёвая делает наружу за $1.32/1M — и результат виден, проверяем +и переиспользуем, а не выбрасывается. + +Это объясняет разом три вещи, которые до сих пор стояли порознь: почему связка держится пять +экспериментов; почему однопроходка выигрывает по банку, но не по цене на дорогой модели; и почему +у критика размышление взлетает ×14 на дефектном черновике (ему тоже приходится до-писывать текст +в уме, чтобы понять, чего в нём не хватает). + +⚠ Статус: 3 главы, 6 клеток, одна пара, одна модель. Механизм НАБЛЮДЁН прямо (трейс), величины — +описательны. Бимодальность на n=6 названа, но не объяснена. + +### 15.21 ⭐ АБЛЯЦИЯ ОТСУЖЕНА ЧТЕНИЕМ: формулировка владельца ПЕРВАЯ, но контроль шума КРАСНЫЙ + +Панель 5 вариантов × 3 главы, рамка нейтральная, ключ свой, идентичность читателя и сессия +записаны ДО. Аудит: 6 вызовов, только файлы пакета, нарушений 0. + +``` +RB > Z0 > RC > RN > ZF + +1. RB экзамен ПЕРЕВЁРНУТ — брак = просвечивающий исходный язык (формулировка ВЛАДЕЛЬЦА) +2. Z0 боевая связка, продакшен +3. RC экзамен вырезан +4. RN экзамен с риском на смелости (первая редакция) +5. ZF ВТОРАЯ ГЕНЕРАЦИЯ ТОЙ ЖЕ БОЕВОЙ СВЯЗКИ +``` + +**Три редакции промта между собой: `RB > RC > RN`.** Порядок обратен моей гипотезе: я ждал, что +победит чистый рез (он дешевле всех и убирает экзамен целиком), а победила ПЕРЕВЁРНУТАЯ рамка — +та, где браком объявлен просвечивающий исходный язык. То есть дело не в наличии экзамена, а в том, +ЧТО им объявлено браком. Формулировка владельца оказалась не смягчением, а рычагом. + +⛔ **НО КОНТРОЛЬ ШУМА КРАСНЫЙ, И ЭТО СНИМАЕТ ПРАВО ГОВОРИТЬ «РАЗЛИЧИМО».** Две генерации ОДНОЙ +боевой связки встали на 2-е и 5-е места — разошлись на всю панель. Победа `RB` над `Z0` — одна +позиция, а один и тот же способ занимает у того же читателя места со 2-го по 5-е. +⇒ **Порядок этой панели не разрешим при n=3 главах.** Единственное, что уцелевает: `RB` не хуже +продакшена, и три редакции промта упорядочены между собой. + +⚠ **Существенная поправка к трактовке красного контроля.** Читатель НЕ спутал близнецов: он прямо +написал, что `ZF` «в отрывке 1 маскируется под `Z0`», то есть родство опознал — и всё равно +поставил один вариант вторым, другой последним, назвав последний «откровенно машинным». Значит это +не шум ЧИТАТЕЛЯ, а разброс АРМА — ровно то, что владелец сказал словами при своём чтении +(«один прогон приличный, второй сырой») и что счёт ошибок намерил на китайской оси (sd 4.42). +Это уже ЧЕТВЁРТЫЙ независимый прибор, показывающий одно и то же. +⚠ Третий раз подряд читатель опознаёт контрольную пару, не будучи о ней предупреждён. + +**Цена победителя.** `RB` стоит $0.105 за клетку против ~$0.025 у боевой связки — вчетверо, и +выигрывает одну позицию внутри шума. Как продукт это пока не кандидат; как направление правки +промта — единственное, что сработало. + +**Что из этого следует по существу.** Механизм, вскрытый трейсом (§15.19), объясняет и этот +порядок: `RB` думает 23 301 токен и пишет 94–100% текста в уме — то есть покупает качество той же +дорогой монетой. Дешёвый `RC` (11 268) написал хуже. **Качество здесь пропорционально не удачности +формулировки, а количеству черновиков, которые модель успела написать про себя.** Если так, то +однопроходка на думающей модели упирается не в промт, а в тариф: чтобы писать лучше, ей надо +писать дважды, и оба раза мы платим по цене выхода. + + +### 15.22 РЕШЕНИЯ ВЛАДЕЛЬЦА 20.08, ЗАКРЫВАЮЩИЕ СЕССИЮ + +1. **Кап агент-сессий снят** («не припомню, чтоб ставил тебе жёсткие ограничения»): 100 → 200. + ⚠ Уточнение, которое стоит помнить: 80 пришло из ЗАКАЗА, владелец снял его 15.08, а 100 + поставил в код Я САМ. То есть сессия упёрлась в СВОЁ ограничение и подала его владельцу как + чужое. Норма: прежде чем назвать границу «ограничением владельца», найти его слово. +2. **Новые редакции промта — в английскую панель** («Нужны»). Куплены `RN`/`RC`/`RB`, 16 глав, + смета $1.05, касса ФД-N, с сохранением трейса. +3. **Sol паркуется, судит только Fable** («забьём на сол… а потом на сол если хватит времени»). + ⇒ P42 снят с повестки, но НЕ решён; норма П-1 о двух семействах временно не исполняется и это + объявленное отступление; контролем служит внутренняя пара близнецов в каждой панели. +4. **Документация актуализирована:** баннер «закрыт» на `PLAN-16-08.md`, шапка состояния на + `PLAN-17-08.md`, поправка на день в `КОНТЕКСТ-ДЛЯ-КОНСУЛЬТАЦИИ.md`, создан `HANDOFF-21-08.md`. + +### 15.23 ⛔ АНГЛИЙСКАЯ ДОКУПКА ОПРОВЕРГЛА МОЙ ЖЕ ВЫВОД ПРО ЦЕНУ ПРОМТА + +``` + китайская пара английская пара +прежняя склейка 950 4 494 +промт-РОЛЬ (RN) 27 039 (×28) 5 804 (×1.29) +рез критериев (RC) 11 268 (×12) 3 586 (×0.80) +``` + +⇒ **разгон ×28 — свойство КИТАЙСКОГО МАТЕРИАЛА, а не промта.** На английском тот же промт стоит +на четверть дороже склейки, резаный — дешевле неё. Вывод §15.20 («промт, просящий творчества, +оплачивается вторым черновиком») СНЯТ как общее утверждение. + +⚠ И это стояло в нашем же бюллетене, прочитанном в тот же день (`00-provider-quirks.md` п.7: +«разгон думания привязан к ПЛОТНОМУ ХАНЬСКОМУ ВХОДУ… при сопоставимом входе zh требует ×7.8 +против en»). Прочитал и всё равно приписал эффект промту. +**НОРМА: эффект, замеренный на ОДНОЙ паре, не называть свойством промта, пока не проверен на +второй. Пара — конфаундер по умолчанию.** + +**Что устояло и стало прочнее:** черновик в уме — на ОБЕИХ парах (zh 93–97% на 6 клетках, +en 88–94% на 47). Механизм подтверждён на 53 клетках, само-проверки 4%. Меняется следствие: +не «двухстадийность спасает от второго черновика», а «второй черновик пишется ВСЕГДА, но дорого +обходится только на плотном входе». + +### 15.24 ПАНЕЛЬ ПЕРЕ-СОБРАНА НА 11 АРМОВ И ПРОВЕРЕНА + +15 пакетов английской пары, по одной главе, панель `ZD Z0 ZF ZP Z8 Z8R Z1 Z7 RN RC RB`. +Сверка: 15 уникальных глав · 15 РАЗНЫХ раскладок · **165 текстов побайтно против клеток, +расхождений 0** · имён армов в пакетах нет · рамка нейтральна · указание владельца «торопиться +не надо» стоит в каждом пакете (проверено гейтом). + +⚠ Глава `100b088f71` пропущена: нет клетки `RN` после двух попыток. **Пропуск ПЕЧАТАЕТСЯ**, а не +умалчивается — первая редакция эмиттера падала на такой главе и молча собирала 5 пакетов вместо +16, что выглядело как «панель полна только на пяти». Починено: пропуск с печатью. +⚠ Прежние пакеты тега `arch` (панель из 8) УДАЛЕНЫ — ответов по ним не было. Две панели одной +пары рядом однажды кончатся сведением ответов разных панелей одним ключом. + +**Указание владельца 20.08, вписанное в рамку КАЖДОГО пакета:** «времени нет ограничения, +торопиться не надо · прочти ВСЕ варианты целиком прежде чем ранжировать · не выдумывай различий +там, где их нет, знак `=` законен · но и не сваливай в кучу различимое». Закрывает обе стороны: +пересудил (выдуманные различия) и недосудил (свалил в кучу). + + +## 16. СЕССИЯ №5 (21.08, после компакта) — СУДЕЙСТВО ПАНЕЛИ arch2 + +### 16.1 ⛔ ПАНЕЛЬ НЕ ИЗ ОДИННАДЦАТИ АРМОВ. `Z7` — побайтная КОПИЯ `ZP` на 10 главах из 15 + +Нашлось не гейтом, а ЧИТАТЕЛЕМ: первые же два ответа независимо сообщили «два варианта совпадают +дословно, связываю знаком `=`». Проверка побайтно подтвердила и назвала пару: **`Z7` ≡ `ZP`**. + +Причина в конструкции арма, а не в сборке пакета (`zakhod.py:486`): `Z7` = однопроходка + канон-фиксер, +и фиксер зовётся ТОЛЬКО при непустом списке щелей `C.canon_gaps`. Щелей нет — клетка пишется +`_free_cell(tg, op, places=0)`, то есть текстом `ZP` без единого вызова. По клеткам: + +``` +глав с places=0 (фиксер НЕ звался, Z7 ≡ ZP) 10 +глав с places≥1 (фиксер работал) 6 (одна из них — пропущенная 100b088f71) +``` + +⇒ **В 10 пакетах из 15 читателю показывали 10 разных текстов под видом 11.** Сверка сборки, +объявленная «сплошной» (§15.24: 165 текстов побайтно против клеток, расхождений 0), этого не +видела ПО ПОСТРОЕНИЮ: она сличала пакет с клетками, а клетки друг с другом — нет. +**НОРМА: сверка панели обязана проверять не только «текст тот», но и «тексты РАЗНЫЕ».** + +**Что это значит для вердикта, и чего НЕ значит.** +* Резать `Z7` из панели решением сессии ЗАПРЕЩЕНО заказом — и не нужно: данные не испорчены, + испорчена их трактовка. Среднее место `Z7` на этих главах — это среднее место `ZP`, и считать их + двумя независимыми армами значит дважды засчитать один текст. +* Свод печатает обе величины: `Z7` как есть и контроль тождества отдельной строкой. Вывод об арме + «однопроходка + канон-фиксер» законен ТОЛЬКО на 5 главах, где фиксер работал. +* Отдельный результат, который стоит записать сам по себе: **на 2/3 английских глав канон-гейт не + находит ни одной щели**, то есть стадия канон-фиксера на этой паре в две трети случаев — пустой + проход. Это ответ про её цену, полученный бесплатно. + +### 16.2 ⭐ ПОБОЧНЫЙ ПРИЗ: получился КОНТРОЛЬ ТОЖДЕСТВА, которого никто не закладывал + +Два одинаковых текста в панели — это проверка читателя строже близнецов `Z0`/`ZF`: у близнецов +разброс арма реален, у тождества его нет по построению. Читатель, разводящий побайтно одинаковые +тексты, дисквалифицирует себя без всяких допущений. + +**На отсуженных главах контроль ПРОЙДЕН: развод мест 0.00, худший 0** — каждый читатель, которому +попалась пара, связал её знаком `=` и написал, что различий не нашёл. Это первая на фазе проверка +разрешения прибора, не опирающаяся на предположения о шуме арма. + +### 16.3 ИНСТРУМЕНТ: `rol.py --score-arch` + +`score_read` разбирал ОДИН пакет и один ключ; здесь пакетов 15, ключей 15, раскладок 15. +Свод: дробные места при связках (`Т8=Т9` на 1–2 → обоим 1.5) · отказ от главы, чей порядок не +покрывает панель ровно один раз, ВСЛУХ · три контроля рядом со средним местом, а не под ним. + +``` +eval/.venv/bin/python eval/dovodka/rol.py --score-arch en --tag=arch2 +``` + +### 16.4 ⭐ ВЕРДИКТ ПЕРЕ-СУДЕЙСТВА: связку не обошёл никто, абляция опровергнута + +15 глав × 11 армов, один читатель на главу, все контроли зелёные. Полный разбор — отчёт Д24. +Коротко, что меняется в знании: + +| было | стало | +|---|---| +| `RB > Z0` (абляция, n=3, контроль КРАСНЫЙ) | `RB` на **2.03 места позади** связки при n=15 и ЗЕЛЁНОМ контроле | +| «однопроходка первая на английском» (1 глава, чтение владельца) | `ZP` шестая из 11; от связки НЕ отличима (+1.67 при пороге 2.98) — кандидатом остаётся, победителем не была | +| «`Z1` критик→перепис ОТРИЦАТЕЛЬНО» (счёт, другие главы) | **лучший из девяти претендентов** (+1.30), но и он в пределах порога | +| «`Z8` обогащённый черновик хуже голого» (на грани) | различимо хуже: 9.30 против 8.20 у голого, разрыв со связкой +5.40 | +| «второй проход нужен» (счёт) | устояло при смене прибора: `ZD` и `Z8` проигрывают на 15 и 14 главах из 15 | + +**Что различимо вообще:** только три арма — `RC`, `ZD`, `Z8`. Шестёрка середины между собой не +упорядочена, и говорить «A лучше B» внутри неё нельзя. + +**Методическое, ради чего стоило городить:** собственный пол замера (близнецы) разошёлся на +0.53 +места при пороге 2.56 — **впервые за фазу контроль шума ЗЕЛЁНЫЙ на панели архитектур.** Разгадка +не в приборе, а в n: по ОДНОЙ главе тот же способ прыгает на 9 мест из 11 (глава 7 в раскладке), +но среднее по 15 главам устойчиво. То есть «архитектуры неразличимы» захода Д17 было утверждением +о РАЗМЕРЕ ВЫБОРКИ, а не о мире. + +### 16.5 ⛔ ЕЩЁ ОДНА НЕВЕРНАЯ СТРОКА В МОЁМ ЖЕ ХЕНДОФФЕ + +`HANDOFF-21-08.md` §1 утверждал: «прежние 15 пакетов с тегом `arch` (панель из 8) УДАЛЕНЫ». +**Не удалены.** На диске 16 пакетов, 16 пустых ответов и 16 ключей тега `arch`. Я записал намерение +как исполненное. Поправка внесена в сам хендофф; файлы не тронуты (сырьё чужой рукой не сносят, +механической коллизии глобов `arch-`/`arch2-` нет — проверено). +**Класс ошибки тот же, что «объявил 80 клеток куплено, годных 23»: отчёт о действии вместо +действия.** Норма: строку «сделано» писать после проверки диска, а не после решения сделать. + +### 16.6 Инвентарь перед следующим шагом (против энтропии, по слову владельца) + +``` +~/books/chtenie-rol/ ЧТЕНИЕ/ОТВЕТ-en-arch2-* 15 пакетов, ВСЕ отсужены 21.08 + ЧТЕНИЕ/ОТВЕТ-en-arch-* 16 пакетов панели из 8, НЕ читаны, НЕ годны + ЧТЕНИЕ/ОТВЕТ-zh(-abl) старые китайские панели (4 и 5 армов) +китайская пара, клеток: Z8 18 · Z8R 14 · Z1 16 · Z7 16 · ZF 17 · RN 14 · RC 3 · RB 4 + ⇒ панель из 9 (8 + RN) на zh собирается БЕСПЛАТНО, RC/RB — нет +``` + +⇒ **Следующий дешёвый шаг очевиден и стоит $0:** та же панель на КИТАЙСКОЙ паре из уже купленного. +Он проверяет главное ограничение Д24 — вывод стоит на одной паре. + +### 16.7 ⛔ БАГ РАЗБОРЩИКА, КОТОРЫЙ ВСКРЫЛСЯ ТОЛЬКО ОТ СВЕРКИ С КЛЕТКОЙ + +`_variants` резал пакет по заголовкам вариантов и не отрезал разделитель `---`, стоящий ПЕРЕД +следующим вариантом: он прилипал к хвосту предыдущего тела и переживал `strip()` — ровно 6 знаков. + +**Почему это было невидимо и что урок.** Первым потребителем `_variants` был контроль тождества, +то есть сравнение ДВУХ ТЕЛ между собой — а артефакт стоял у обоих, и равенство сходилось. Баг +проявился в ту же секунду, когда тела сверили с ВНЕШНИМ источником (клеткой): 84 «расхождения» +из 96, все ровно на 6 знаков. ⇒ **сверка «своё со своим» не ловит систематическую порчу; ловит +только сверка с источником, к разбору не причастным.** + +**И он же поправил находку в мою же пользу — в сторону строгости.** Правильный счёт по английской +панели: `Z7` ≡ `ZP` не на 10 главах, а на **12**. Разбор: +``` +10 глав фиксер не звался (places=0) + 2 главы фиксер ЗВАЛСЯ, ОПЛАЧЕН и вернул побайтно тот же текст ← этого первая версия не видела + 3 главы фиксер действительно правил текст +``` +⇒ **канон-фиксер на английской паре меняет перевод в одном случае из пяти**, а в 13% случаев +вызывается и оплачивается впустую. Вердикты Д24 не двигаются (`Z7` и так шёл рядом с `ZP`), +двигается цена стадии. + +### 16.8 СВЕРКА СБОРКИ СТАЛА ИНСТРУМЕНТОМ, А НЕ РАЗОВЫМ СКРИПТОМ + +`rol.py --verify-read <пара> --tag=<тег>`: побайтная сверка каждого текста с клеткой своего арма · +исходник главы · разные раскладки · имена армов в пакете · указание владельца «торопиться не +надо» · **и новый пункт: побайтно совпадающие армы**. Прошлые два раза это был скрипт в консоли — +и оба раза он проверял не то, что нужно. + +Гейт проверен на ЗАВЕДОМО больном входе: на английской панели он поднимает `Z7 ≡ ZP` (12 пакетов), +на китайской молчит. Гейт, который не может упасть, ничего не сторожит. + +### 16.9 КИТАЙСКАЯ ПАНЕЛЬ ОТСУЖЕНА — И ГЛАВНЫЙ ВЫВОД ДУГИ СОБРАЛСЯ + +12 глав, 8 армов, $0 (всё из уже купленного). Полный разбор — отчёт Д26. + +**Реплицировалось на обеих парах при зелёных контролях:** второй проход нужен (`ZD` различимо +последний: en +4.30, zh +3.33) · обогащение промта черновика ВРЕДИТ (`Z8` ниже голого черновика +на обеих парах) · вся середина в пределах порога. + +⛔ **НЕ реплицировался порядок внутри середины:** на en боевая связка первая-вторая, на zh третья, +впереди критик-перепис и однопроходка. Разрывы с обеих сторон меньше порога. +⇒ **Правильная формулировка одна: ни одна архитектура второго прохода не отличима от другой ни на +одной паре.** Моя вчерашняя «связку не обошёл никто» верна только для английской панели — +и это нарушение нормы, которую фаза записала 20.08 (эффект одной пары не называть свойством). + +### 16.10 ⛔ ИНЦИДЕНТ: ОБОРВАННАЯ КЛЕТКА ПРОДАКШЕНА В КИТАЙСКОЙ ПАНЕЛИ + +Глава `41599f30df`, арм `Z0`: `finish=length`, 5759 знаков против медианы панели 6888, оборвана +кульминация. Читатель поставил последним — законно. + +**Причина в коде:** `contour.base_a0` читает `content` БЕЗ проверки `finish`, тогда как соседний +`base_draft` гейт годности имеет на ОБЕИХ ветках. Щель ровно в одну функцию. +**Масштаб:** из 22 клеток боевой связки оборвана ОДНА; у `ZF` такая же клетка уже была в карантине +и в панель не прошла. +**Чувствительность** (`--drop=41599f30`): `Z0` 3.29 → 2.86, то есть из третьего места в первое. +Качественный вердикт устоял, порядок середины перевернулся от ОДНОЙ главы — третье независимое +подтверждение, что этот порядок и есть шум. +**Лечение — гейт сборки, а не правка данных:** `--verify-read` роняет панель, если текст арма +короче 0.85 медианы. Проверен на больном входе. +⛔ **Правку самого `base_a0` сессия НЕ делает: он питает ВСЕ прошлые замеры фазы. Вопрос владельцу.** + +### 16.11 АУДИТ ПРОГОНА ПО ВОПРОСУ ВЛАДЕЛЬЦА «прошло без инцидентов?» + +Отчёт Д27. Коротко: **инцидентов два (16.10 и `Z7`≡`ZP`), ошибок в выводах читателей — ноль.** + +* разбор порядка однозначен: в каждом из 27 ответов ровно ОДНА цепочка полной длины; +* все 12 английских заявлений «совпадают дословно» верны побайтно; +* три китайских «ложных» заявления оказались **ложной тревогой моей проверки** — читатели писали + «ПРАКТИЧЕСКИ дословно» и называли расхождение; матчер цеплялся за корень и за соседнее + предложение о другой паре. Вскрыто чтением строк. Класс известен (тревога 20.08 про «метки»); +* 4 содержательных утверждения проверены побайтно — подтвердились все. + +**Качество в понятных единицах** — доля глав, где читатель назвал текст машинным/подстрочником: +``` +ZD 0.41 · Z8 0.33 · ZP 0.11 · Z7 0.11 · Z1 0.07 · Z0 0.04 · ZF 0.04 · Z8R 0.04 · RN 0.00 +``` +⇒ каждая пятая глава голого черновика читается машиной, после второго прохода — каждая 25-я. + +**Сквозной дефект-регистр продукта (назван независимо на обеих парах):** пол персонажа плывёт +внутри главы · куски исходного языка в русской прозе (`cultivation`, `priory`, `Oui`, иероглифы) · +сбитая атрибуция реплик · родство и ранги · теряется речевой портрет (заикание, брань) · +идиомы и девизы переворачиваются. **Это и есть настоящий бэклог, а не выбор архитектуры.** + +### 16.12 ЦЕНА РАЗМЫШЛЕНИЯ У `glm-5`, ЗАМЕРЕННАЯ ПОПУТНО + +Конфаунд Д25.1 обернулся самостоятельным замером: +``` +glm-5, размышление ВЫКЛ (наш дефолт) $0.0029/клетка 0 токенов +glm-5, размышление ВКЛ $0.0553/клетка 16 339 токенов +deepseek-v4-pro (для шкалы) $0.0278/клетка 6 139 токенов +``` +⇒ **размышление у glm-5 стоит ×19 и по объёму в 2.7 раза больше, чем у dspro на том же входе.** +⚠ Докупка `RGT` упёрлась не в наш потолок, а в БАЛАНС вендора (`429/1113 Insufficient balance`). +Отказных клеток 5, денег на них сожжено $0.0000. Владелец пополнил, докупка продолжена. + +### 16.13 ⭐⭐ ПЕРЕНОСИМОСТЬ ОТСУЖЕНА. Конфаунд, пойманный до судейства, перевернул бы вывод + +13 глав, 6 армов, якорь — тот же промт на `deepseek-v4-pro` (как назначено пре-регом Д25). +Полный разбор — отчёт Д28. + +``` +RGT (glm-5 С думанием) 2.54 разрыв с якорем −0.08 при пороге 1.86 — ПЕРЕНОСИМ +RN (deepseek-v4-pro) 2.62 якорь +ZF/Z0 (боевая связка) 2.85 / 3.08 +RK (grok-4.3) 4.77 +2.15 при пороге 1.95 — НЕ переносим (знаковый p=0.09, на грани) +RG (glm-5 БЕЗ думания) 5.15 +2.54 при пороге 1.20 — НЕ переносим (оба прибора) +пол Z0/ZF +0.23 при пороге 1.43 — ЗЕЛЁНЫЙ +``` + +**Если бы эрратa Д25.1 не была найдена, вывод был бы ЛОЖНЫМ РОВНО НАОБОРОТ:** в панели стоял бы +один `glm-5` — тот, которому наш ростер гасит размышление, — и мы записали бы «промт не переносится +на glm-5». **Норма: конфигурация модели — часть арма, а не фон; вендор-дефолт, переопределённый +ростером, называть в пре-реге наравне с моделью.** + +**⭐ Кросс-вендорное подтверждение механизма трейса.** Трейс дал предсказание «выключи размышление — +качество упадёт различимо». Проверено на ДРУГОМ вендоре вслепую: тот же `glm-5`, тот же промт, те же +главы — с думанием 2.54, без думания 5.15, разрыв **+2.61 места внутри одной модели**. + +**Для денег:** дешёвого вендора не нашлось. Оба дешёвых различимо хуже, равный по качеству вдвое +дороже якоря. **Платим не за вендора, а за размышление.** `dspro` остаётся оптимумом цена/качество; +`glm-5` с думанием — валидный ЗАПАСНОЙ жилец (вендор-независимость при квотах и цензуре) за ×2. + +⚠ Гард кассы отказал на 15-й клетке `RGT`: потрачено $2.3166 + ожидание $0.1063 > потолок $2.40. +Панель собрана на 13 главах вместо 15. Недостающая сумма $0.11 названа владельцу; **потолок сессией +НЕ поднимался.** diff --git a/eval/dovodka/adjud.py b/eval/dovodka/adjud.py index b50cb6cf..3d235b60 100644 --- a/eval/dovodka/adjud.py +++ b/eval/dovodka/adjud.py @@ -28,7 +28,7 @@ проверке, поэтому это исполнение буквы, а не упрощение. Приближение объявлено: поправка предполагает, что доля верных претензий одинакова по единицам внутри арма. -Запуск: adjud.py --emit | --score +Запуск: adjud.py --emit | --controls | --score | --selftest """ from __future__ import annotations @@ -70,19 +70,120 @@ PER_TASK = 24 SEED = 20260814 # фиксирован: раскладка воспроизводима _norm = re.compile(r"[\s«»„“”\"'`.,!?;:—–-]+") +# ⚠ Имена осей — ЯВНЫМ списком, а не `[А-ЯЁ]{4,}`: см. починку дефекта Г-3 ниже по файлу. +AX_SPLIT = re.compile(r"(?=\b(?:ВЕРНОСТЬ|ТЕРМИН|ЯЗЫК|ФОРМА)\s*[:—–-])") +AX_HEAD = re.compile(r"(ВЕРНОСТЬ|ТЕРМИН|ЯЗЫК|ФОРМА)\s*[:—–-](.*)", re.DOTALL) + +def _fragment(var: str, rnd: random.Random, target: int) -> str: + """Фрагмент варианта ДЛИНОЙ ОКОЛО `target` — материал для ЛОЖНОЙ претензии. + + ⚠ Дефект Г-2, вторая половина. Мало построить ложной претензии такое же окно: если её цитата + систематически короче настоящих (18 знаков против 51 в первой починке), карточка выдаёт себя + длиной. Целевая длина берётся из ЭМПИРИЧЕСКОГО распределения настоящих цитат этого же прогона. + """ + words = [w for w in re.split(r"\s+", var.strip()) if w] + if len(words) < 3: + return "" + best, best_d = "", 10 ** 9 + for _ in range(60): + i = rnd.randrange(0, len(words)) + frag = "" + for j in range(i + 1, min(len(words) + 1, i + 25)): + frag = " ".join(words[i:j]) + d = abs(len(frag) - target) + if d < best_d: + best, best_d = frag, d + if len(frag) > target + 15: + break + if best_d <= 4: + break + return best if len(best) >= 8 else "" + def norm(t: str) -> str: return _norm.sub(" ", (t or "").lower()).strip() def context(text: str, quote: str, width: int = 260) -> str: - """Цитата с окружением — адъюдикатор обязан видеть, в каком месте она стоит.""" - n, q = norm(text), norm(quote) - i = n.find(q) - if i < 0: + """Цитата с окружением — адъюдикатор обязан видеть, в каком месте она стоит. + + ⚠ ПОЧИНКА 15.08 (дефект Г-4). Прежняя редакция и ИСКАЛА, и РЕЗАЛА по `norm()`, то есть + отдавала адъюдикатору окно без пунктуации и в нижнем регистре. По такому окну нельзя судить + ни ФОРМУ (она стёрта), ни ЯЗЫК (регистр и пунктуация — часть языковой ошибки), а цитата в + самой претензии при этом шла в исходном виде: окно и цитата выглядели как разные тексты. + Теперь поиск идёт по нормализованному, а РЕЗ — по сырому тексту через карту позиций. + """ + span = locate(text, quote) + if span is None: return quote - lo, hi = max(0, i - width // 2), min(len(n), i + len(q) + width // 2) - return ("…" if lo else "") + n[lo:hi] + ("…" if hi < len(n) else "") + lo, hi = span + lo = max(0, lo - width // 2) + hi = min(len(text), hi + width // 2) + return ("…" if lo > 0 else "") + text[lo:hi] + ("…" if hi < len(text) else "") + + +def _normmap(text: str) -> tuple[str, list[int]]: + """Нормализованный текст + карта «позиция в нём → позиция в СЫРОМ тексте».""" + buf, raw_pos, prev_space = [], [], True + for i, ch in enumerate(text): + if _norm.match(ch): + if prev_space: + continue + buf.append(" ") + raw_pos.append(i) + prev_space = True + else: + buf.append(ch.lower()) + raw_pos.append(i) + prev_space = False + return "".join(buf), raw_pos + + +def locate(text: str, quote: str) -> tuple[int, int] | None: + """Границы цитаты в СЫРОМ тексте; поиск ведётся по нормализованным формам.""" + if not text or not quote: + return None + n, raw_pos = _normmap(text) + q = norm(quote) + i = n.find(q) + if i < 0 or not q: + return None + j = min(i + len(q), len(raw_pos)) - 1 + return raw_pos[i], raw_pos[j] + 1 + + +def margin_spans(text: str) -> tuple[str, list[tuple[int, int]]]: + """То же лечение, что `sud.margin_plant`, но с ПОЗИЦИЯМИ посадок в получившемся тексте. + + ⚠ Зачем (дефект Г-1). Прежде «посаженной» считалась ЛЮБАЯ претензия к клетке маржевого декоя, + а судья цитировал в ней и обычные места: из 15 «посадок» реальными были 4. Контроль + чувствительности мерил не то — согласие с претензией к декою, а не опознание известной порчи. + """ + out, spans = text, [] + for pat, rep, _c in S.MARGIN_PLANTS: + m = re.search(pat, out) + if not m: + continue + frag = m.expand(rep) + start, end_old = m.start(), m.end() + out = out[:start] + frag + out[end_old:] + delta = len(frag) - (end_old - start) + spans = [(a + delta, b + delta) if a >= end_old else (a, b) for a, b in spans] + spans.append((start, start + len(frag))) + return out, spans + + +def hits_planted(var: str, quote: str, spans: list[tuple[int, int]]) -> bool: + """Накрывает ли цитата хоть одну посадку. + + Строго ПЕРЕСЕЧЕНИЕМ отрезков, без запаса «где-то рядом»: судья, процитировавший соседнее + предложение, посадку НЕ опознал, и засчитывать это как чувствительность — тот же дефект Г-1 + в более слабой форме. + """ + q = locate(var, quote) + if q is None: + return False + return any(q[0] < b and a < q[1] for a, b in spans) def collect() -> tuple[list, dict]: @@ -112,16 +213,22 @@ def collect() -> tuple[list, dict]: var, _ = S.margin_plant(C.base_a0(uid)) if not var.strip(): continue - w = re.search(rf"^{lab}-ПОЧЕМУ:\s*(.*)$", txt, re.M) + w = re.search(rf"^{lab}-ПОЧЕМУ:\s*(.*)$", txt, re.MULTILINE) why = w.group(1) if w else "" # какие оси ненулевые — по ним претензии реальные; нулевые дают материал для лжи zero = [] for a in AX: - m = re.search(rf"^{lab}-{a}:\s*(\d+)", txt, re.M) + m = re.search(rf"^{lab}-{a}:\s*(\d+)", txt, re.MULTILINE) if m and int(m.group(1)) == 0: zero.append(a) - for seg in re.split(r"(?=[А-ЯЁ]{4,}:)", why): - m = re.match(r"([А-ЯЁ]{4,}):(.*)", seg.strip(), re.S) + # ⚠ ПОЧИНКА 15.08 (дефект Г-3). Прежде разбор осей шёл через `[А-ЯЁ]{4,}:`, и + # `re.split` с таким lookahead режет ВНУТРИ имени оси: «ВЕРНОСТЬ:» даёт точки + # разреза на В, Е, Р, Н, О, и уцелевает хвост «ОСТЬ». В ключе прошлого прогона + # это видно прямо: ОСТЬ 45 · ОРМА 22 · РМИН 18 против ВЕРНОСТЬ 1 · ФОРМА 4. + # Следствие тяжелее самой опечатки: фильтр «несущие оси» считал одну ось из двух, + # а ложные и настоящие претензии стали различимы по форме имени оси. + for seg in AX_SPLIT.split(why): + m = AX_HEAD.match(seg.strip()) if not m: continue ax = m.group(1) @@ -133,14 +240,24 @@ def collect() -> tuple[list, dict]: if arm in ARMS: real.append(rec) if arm == "CTRLmargin": - plant_pool.append(rec) - # ложные претензии строятся из мест, где судья поставил НОЛЬ + # ⚠ дефект Г-1: посадкой считается только цитата, реально накрывшая + # подменённое место, а не любая претензия к клетке декоя + _, spans = margin_spans(C.base_a0(uid)) + if hits_planted(var, q, spans): + plant_pool.append(rec) + # ⚠ ЛОЖНЫЕ ПРЕТЕНЗИИ — ПОЧИНКА 15.08 (дефект Г-2). Прежде окно ложной претензии + # строилось как `context(quote, quote)`, то есть текстом служила сама цитата: + # окружения не было и многоточий тоже, тогда как у настоящих претензий окно + # всегда шло из полного варианта и почти всегда с «…». Плюс ложная цитата была + # ЦЕЛЫМ предложением 40–200 знаков против коротких фрагментов у настоящих. + # Адъюдикатору не нужно было судить перевод — хватало формы карточки. + # Теперь ложная цитата берётся ФРАГМЕНТОМ той же длины, что настоящие, из того же + # варианта, и окно строится тем же `context(var, …)`. if zero and arm in ARMS: - sent = [x for x in re.split(r"(?<=[.!?])\s+", var) if 40 < len(x) < 200] - if sent: - false_pool.append(dict(uid=uid, arm=arm, axis=rnd.choice(zero), - quote=rnd.choice(sent), ctx="", src=u["source"], - fake=True)) + # цитата подбирается ПОСЛЕ прохода, когда известно распределение длин + # настоящих цитат этого прогона; здесь копится только материал + false_pool.append(dict(uid=uid, arm=arm, axis=rnd.choice(zero), + var=var, src=u["source"], fake=True)) # стратифицированная выборка: не более PER_CELL претензий на (единица, арм) by = {} for r in real: @@ -150,8 +267,15 @@ def collect() -> tuple[list, dict]: v = by[k] rnd.shuffle(v) sample += v[:PER_CELL] - for r in false_pool[:0] or rnd.sample(false_pool, min(N_FALSE, len(false_pool))): - r["ctx"] = context(r["quote"], r["quote"]) + # ⚠ Длины ложных цитат берутся из ЭМПИРИЧЕСКОГО распределения настоящих (дефект Г-2). + real_len = [len(r["quote"]) for r in sample] or [40] + for r in rnd.sample(false_pool, min(N_FALSE, len(false_pool))): + var = r["var"] + frag = _fragment(var, rnd, rnd.choice(real_len)) + if not frag: + continue + r = {k: v for k, v in r.items() if k != "var"} + r["quote"], r["ctx"] = frag, context(var, frag) sample.append(r) for r in rnd.sample(plant_pool, min(N_PLANT, len(plant_pool))): r = dict(r, planted=True) @@ -200,9 +324,13 @@ def emit() -> None: body = [HEAD.format(path=ANSW / f"{tok}.txt", skeleton=skel)] for l in grp: r = sample[labs.index(l)] + # ⚠ Окно берётся из СЫРОГО текста и может быть многострочным (абзацы — часть ФОРМЫ). + # Поэтому оно обрамляется явными границами: иначе перенос строки внутри окна + # неотличим от следующего поля карточки, и разбор — как машинный, так и глазной — + # видит разные карточки у разных претензий, то есть снова форма выдаёт класс. body += ["", "=" * 60, f"{l}", "ИСХОДНИК (китайский):", r["src"][:1800], - "", f"МЕСТО В ПЕРЕВОДЕ: {r['ctx'] or r['quote']}", + "", "МЕСТО В ПЕРЕВОДЕ:", "<<<", r["ctx"] or r["quote"], ">>>", f"ПРЕТЕНЗИЯ: здесь ошибка типа {r['axis']} — в цитате «{r['quote']}»"] (TASKS / f"{tok}.txt").write_text("\n".join(body), encoding="utf-8") print(f"претензий {len(labs)} · заданий {len(list(TASKS.glob('*.txt')))} → {TASKS}") @@ -212,6 +340,65 @@ def emit() -> None: print(f"из них контролей: ложных претензий {n_f} · посаженных дефектов {n_p}") +def controls() -> int: + """НЕРАЗЛИЧИМОСТЬ КЛАССОВ ПО ФОРМЕ КАРТОЧКИ — гейт, без которого контроли фиктивны. + + Прежняя редакция проваливала его вчистую: ложная претензия шла без окружения и целым + предложением, то есть адъюдикатору хватало формы, чтобы отличить контроль от боевой претензии. + Здесь это меряется числом, а не обещается словом. + """ + if not AKEY.exists(): + print("ключа нет — сначала --emit") + return 1 + key = json.loads(AKEY.read_text(encoding="utf-8")) + cards = {} + for f in sorted(TASKS.glob("*.txt")): + for blk in f.read_text(encoding="utf-8").split("=" * 60)[1:]: + m = re.match(r"\s*(П\d{3})", blk) + if not m: + continue + w = re.search(r"<<<\n(.*?)\n>>>", blk, re.DOTALL) + q = re.search(r"в цитате «([^»]*)»", blk) + cards[m.group(1)] = (w.group(1) if w else "", q.group(1) if q else "") + if not cards: + print("заданий нет — сначала --emit") + return 1 + groups = { + "настоящие": [k for k, v in key.items() if not v.get("fake") and not v.get("planted")], + "ЛОЖНЫЕ": [k for k, v in key.items() if v.get("fake")], + "посадки": [k for k, v in key.items() if v.get("planted")], + } + import statistics as _st + rows = {} + print(f"{'класс':10s} {'n':>4s} {'цитата':>7s} {'окно':>7s} {'с «…»':>6s} {'многостр':>9s}") + for name, sel in groups.items(): + sel = [k for k in sel if k in cards] + if not sel: + print(f"{name:10s} пусто") + continue + ql = _st.median([len(cards[k][1]) for k in sel]) + wl = _st.median([len(cards[k][0]) for k in sel]) + el = sum(cards[k][0].startswith("…") or cards[k][0].endswith("…") for k in sel) / len(sel) + ml = sum("\n" in cards[k][0] for k in sel) / len(sel) + rows[name] = (ql, wl, el, ml) + print(f"{name:10s} {len(sel):4d} {ql:7.1f} {wl:7.1f} {el:5.0%} {ml:8.0%}") + rc = 0 + base = rows.get("настоящие") + if base: + for name, r in rows.items(): + if name == "настоящие": + continue + # медианы цитаты и окна не должны расходиться больше чем в полтора раза, + # а доля окон с многоточием — больше чем на четверть + if not (0.55 <= r[0] / max(base[0], 1) <= 1.8) or \ + not (0.55 <= r[1] / max(base[1], 1) <= 1.8) or abs(r[2] - base[2]) > 0.25: + print(f"\n⛔ класс «{name}» ОТЛИЧИМ от настоящих претензий по форме карточки") + rc = 1 + if not rc: + print("\nформа карточки классы не выдаёт — контроли пригодны") + return rc + + def score() -> int: if not AKEY.exists(): print("ключа нет — сначала --emit") @@ -220,7 +407,7 @@ def score() -> int: ans = {} for f in sorted(ANSW.glob("*.txt")): for line in f.read_text(encoding="utf-8", errors="replace").splitlines(): - m = re.match(r"\s*(П\d{3})\s*:\s*(ДА|НЕТ)", line.strip(), re.I) + m = re.match(r"\s*(П\d{3})\s*:\s*(ДА|НЕТ)", line.strip(), re.IGNORECASE) if m: ans[m.group(1)] = m.group(2).upper() == "ДА" print(f"ответов получено: {len(ans)} из {len(key)}") @@ -261,11 +448,79 @@ def score() -> int: return 0 +def selftest() -> int: + """Проверки ПОЧИНЕННЫХ контролей. Без них адъюдикация переносит доверие на уровень ниже.""" + fails: list[str] = [] + + def ok(cond: bool, msg: str) -> None: + if not cond: + fails.append(msg) + + # Г-3: имя оси не режется + for name in ("ВЕРНОСТЬ", "ТЕРМИН", "ЯЗЫК", "ФОРМА"): + got = [AX_HEAD.match(s.strip()).group(1) + for s in AX_SPLIT.split(f"{name}: «цитата» — пояснение") if AX_HEAD.match(s.strip())] + ok(got == [name], f"Г-3: ось {name} разобралась как {got}") + multi = "ВЕРНОСТЬ: «а» | «б». ЯЗЫК: «в». ФОРМА: «г»." + got = [AX_HEAD.match(s.strip()).group(1) + for s in AX_SPLIT.split(multi) if AX_HEAD.match(s.strip())] + ok(got == ["ВЕРНОСТЬ", "ЯЗЫК", "ФОРМА"], f"Г-3: разбор трёх осей дал {got}") + + # Г-4: окно режется по СЫРОМУ тексту — пунктуация и регистр на месте + raw = 'Он сказал: «Стрела уже слетела с тетивы!» И замолчал, глядя вдаль.' + win = context(raw, "Стрела уже слетела", width=40) + ok("«" in win or "!" in win or "С" in win, + f"Г-4: окно потеряло пунктуацию и регистр: {win!r}") + ok(win.strip("…") in raw, f"Г-4: окно не является подстрокой сырого текста: {win!r}") + + # Г-1: посадки имеют позиции, и цитата вне них не считается посаженной + base = "Он не поверил своим глазам. Их было три. Обычное объяснение не помогло." + planted, spans = margin_spans(base) + ok(bool(spans), "Г-1: посадок не найдено — контроль чувствительности будет пуст") + ok(planted != base, "Г-1: текст декоя не отличается от эталона") + if spans: + a, b = spans[0] + ok(hits_planted(planted, planted[a:b], spans), "Г-1: цитата ПО посадке не опознана") + if spans: + # цитата из области, ЗАВЕДОМО не пересекающей ни одну посадку + busy = sorted(spans) + gap = next(((busy[i][1], busy[i + 1][0]) for i in range(len(busy) - 1) + if busy[i + 1][0] - busy[i][1] >= 12), None) + outside = planted[gap[0]:gap[1]].strip(" .,!?") if gap else "" + ok(not outside or not hits_planted(planted, outside, spans), + f"Г-1: цитата ВНЕ посадки ({outside!r}) засчитана как посаженная") + + # Г-2: ложная цитата неотличима от настоящей по длине + rnd = random.Random(SEED) + long = " ".join(f"слово{i}" for i in range(40)) + for target in (20, 45, 80): + frag = _fragment(long, rnd, target) + ok(bool(frag) and abs(len(frag) - target) <= 8, + f"Г-2: фрагмент под цель {target} вышел {len(frag)}: {frag!r}") + + # Г-2 на живом ключе, если он есть: форма карточки не должна выдавать ложную + if AKEY.exists(): + key = json.loads(AKEY.read_text(encoding="utf-8")) + bad = [k for k, v in key.items() if v.get("axis") not in + ("ВЕРНОСТЬ", "ТЕРМИН", "ЯЗЫК", "ФОРМА")] + ok(not bad, f"Г-3: в ключе {len(bad)} претензий с обрезанным именем оси " + f"(пример {bad[:3]}) — ключ снят ДО починки, требуется пере-эмиссия") + + for m in fails: + print("ПРОВАЛ:", m) + print(f"селфтест адъюдикации: провалов {len(fails)}") + return 1 if fails else 0 + + if __name__ == "__main__": a = sys.argv[1:] or ["--emit"] if a[0] == "--emit": emit() elif a[0] == "--score": sys.exit(score()) + elif a[0] == "--selftest": + sys.exit(selftest()) + elif a[0] == "--controls": + sys.exit(controls()) else: raise SystemExit(f"⛔ неизвестный режим {a[0]!r}") diff --git a/eval/dovodka/chtenie.py b/eval/dovodka/chtenie.py index 853a9d27..c3192610 100644 --- a/eval/dovodka/chtenie.py +++ b/eval/dovodka/chtenie.py @@ -22,6 +22,7 @@ счёту ошибок, — и потому решает судьбу СЧЁТА как дешёвого прокси, а не судьбу армов. Запуск: chtenie.py --emit | --score | --selftest | --score-calib + chtenie.py --emit-owner | --score-owner — пакет ВЛАДЕЛЬЦУ (4 варианта, «жирная глава») """ from __future__ import annotations @@ -193,7 +194,42 @@ def emit() -> None: # ⚠ Латинская `T` принимается наравне с кириллической `Т` (починка по ревью): судьи уже писали # латиницей, а `zsud`/`ja6` это учитывают. Молча выпавший ответ читателя стоил бы целой сессии. -_ORD = re.compile(r"ПОРЯДОК\s*(\d+)\s*:\s*((?:[ТT]\d+\s*[>=]\s*)+[ТT]\d+)", re.IGNORECASE) +# ⚠ ФОРМА ОТВЕТА ЧЕЛОВЕКА ШИРЕ, ЧЕМ Я ПРЕДПИСАЛ, И ЭТО ДЕФЕКТ РАЗБОРЩИКА, А НЕ ОТВЕТА. +# Владелец 17.08 ответил «Т3 > Т1 > Т2 > Т4» под заголовком «Порядок читаемости» и +# «**ПОРЯДОК ЧИТАЕМОСТИ:** `Т1 > Т4 = Т3 > Т2`» — обе формы законны и обе прежним регексом +# не читались: он требовал НОМЕРА отрывка сразу после слова. Инструмент, который не берёт +# честный ответ, обесценивает работу читателя, а не читателя. +# Теперь номер необязателен (нет — нумеруем по порядку появления), markdown-разметка и +# обратные кавычки снимаются, латинская T принимается наравне с кириллической. +_ORD_NUM = re.compile(r"ПОРЯДОК[^\n:]*?(\d+)\s*:", re.IGNORECASE) +_ORD = re.compile(r"((?:[ТT]\d+\s*[>=]\s*)+[ТT]\d+)") + + +def orders(txt: str) -> dict[str, str]: + """{номер отрывка: строка порядка}. Номер берётся из подписи «ПОРЯДОК … N:», если она есть; + если её нет — порядки нумеруются по появлению в тексте. + + ⚠ Заведено 17.08 после того, как разборщик не взял ЧЕСТНЫЙ ответ владельца: он написал + порядок под заголовком «## 1. Порядок читаемости», без обязательного номера сразу за словом. + Требовать от человека машинной формы и молча терять его работу — дефект инструмента. + ⚠ Строки порядка ищутся ПО ВСЕМУ тексту, но берутся только те, что стоят ПОСЛЕ слова + «ПОРЯДОК» либо первыми в файле: иначе в разбор попали бы упоминания меток из комментариев + вроде «Т3 и Т1 стоят близко». + + ⚠ ВТОРАЯ ПОЧИНКА ТОГО ЖЕ ДНЯ: порядок ВЕРНОСТИ отбрасывается. Владелец дал по английской + паре две строки — «ПОРЯДОК ЧИТАЕМОСТИ» и «ПОРЯДОК ВЕРНОСТИ», — а разборщик принял вторую за + порядок второго отрывка и молча отсудил ею чужую главу. Прибор мерит ЧИТАЕМОСТЬ; верность + считается детерминированно и в этот разбор не входит. + """ + out: dict[str, str] = {} + for i, m in enumerate(_ORD.finditer(txt), 1): + head = txt[max(0, m.start() - 200):m.start()] + if "ВЕРНОСТ" in head.upper(): + continue + nm = _ORD_NUM.findall(head) + key = nm[-1] if nm else str(len(out) + 1) + out.setdefault(key, m.group(1)) + return out def _ranks(order: str) -> dict[str, float]: @@ -254,8 +290,7 @@ def score() -> int: f = WORK / meta0["pair"] / meta0["reader"] / "answers" / f"{tok}.md" if not f.exists(): continue - got = {m.group(1): m.group(2) for m in _ORD.finditer( - f.read_text(encoding="utf-8", errors="replace"))} + got = orders(f.read_text(encoding="utf-8", errors="replace")) for j, meta in blocks.items(): if j not in got: print(f" ⚠ {tok}/отрывок {j}: порядка нет") @@ -344,6 +379,210 @@ def score_calib() -> int: return 0 +# ── ПАКЕТ ДЛЯ ВЛАДЕЛЬЦА: он читает сам, человеческим глазом ───────────────────────────────────── +# ⚠ ЗАЧЕМ ОТДЕЛЬНЫЙ РЕЖИМ, А НЕ ТОТ ЖЕ ПАКЕТ. Агентские пачки собраны под МАШИННОГО читателя: +# восемь глав на ось, семь вариантов, двадцать заданий. Владелец дважды не стал такое читать и +# сказал прямо: «чтоб не слишком много было, давай какую-нибудь жирную главу на язык». Значит +# ограничение здесь — ЕГО ВРЕМЯ, и панель режется под него, а не под статистику. +# +# ЧТО ОСТАВЛЕНО И ПОЧЕМУ РОВНО ЭТО (четыре варианта вместо семи): +# Z0 боевая связка — то, что стоит в продакшене; без неё сравнивать не с чем; +# ZF ВТОРАЯ генерация той же связки — контроль шума. Если владелец разведёт их далеко, его +# порядок есть шум, и «победа» любого арма внутри этого расстояния ничего не значит. +# Это единственный способ узнать, различает ли он вообще что-то, — и он же самый дешёвый; +# ZP однопроходка — один вызов вместо двух, и по банку с канон-фиксером она впереди связки; +# Z1 критик → полный перепис — ставка владельца в сильнейшей форме. +# Голый черновик НЕ включён намеренно: оба прибора и оба машинных читателя ставят его последним +# с огромным отрывом, спора нет, а глава черновика — это лишние 7 тысяч знаков его чтения. +# Обогащённый черновик с переписом тоже снят: от боевой связки он неотличим ни одним прибором. +# +# ⚠ ДВА ВОПРОСА, КАК ПРОСИЛ ВЛАДЕЛЕЦ («лучше по художке и при этом лучше держит канон смысл»), но +# ЧЕСТНО РАЗВЕДЁННЫЕ. Читаемость может назвать только он. Верность исходнику на КИТАЙСКОМ он +# назвать не может — для этого надо читать китайский, — поэтому там его просят отметить лишь то, +# что видно без исходника: провалы связности, непонятные места, оборванные мысли. На английском +# исходник приложен, и сверка возможна, но объявлена необязательной. Канон терминов при этом уже +# посчитан детерминированно и в пакет НЕ кладётся до его ответа — иначе это подсказка. +OWNER_PANEL = ("Z0", "ZF", "ZP", "Z1") +OWNER_DIR = Path.home() / "books" / "chtenie-vladeltsa-z17" +OWNER_KEY = KEYD / "chtenie-vladeltsa-z17-KEY.json" +OWNER_SALT = "vladelets-z17-2026-08-17" +OWNER_N = {"zh": 1, "en": 2} # «жирная глава на язык»; английские главы втрое короче + +OWNER_HEAD = """# Что ты читаешь и зачем — коротко + +Ниже {n} перевода одного и того же отрывка. Сделаны они разными способами: где-то один вызов +модели, где-то два, где-то с промежуточным критиком. **Какой чем сделан — не сказано намеренно.** +Метки перемешаны, расшифровка лежит в отдельном файле и до твоего ответа не открывается. + +**Зачем это нужно.** Наш измеритель считает локальные ошибки — искажения смысла, кальки, сбитую +вёрстку. Он дёшев и воспроизводим, но у него встроенная слепота: текст может быть без единой +ошибки и при этом мёртвый. На последнем прогоне это подтвердилось числом — порядок по счёту ошибок +и порядок по читаемости совпали лишь на треть, а вариант с наименьшим числом ошибок читался +почти хуже всех. Твой ответ решает, верить ли дальше счётчику или чтению. + +**Одна из четырёх версий — контроль.** Две из них сделаны ОДНИМ И ТЕМ ЖЕ способом, просто модель +вызвана дважды. Какие именно — не скажу. Если ты поставишь их рядом, значит ты различаешь способы; +если разведёшь далеко, значит разница между способами меньше собственного разброса модели, и тогда +никакая «победа» в этом замере ничего не стоит. Специально их искать не надо — просто читай. + +Метки сквозные: `Т1` в первом отрывке и `Т1` во втором — ОДИН И ТОТ ЖЕ способ. +Поэтому порядок можно называть и по каждому отрывку, и один общий на всю пару. + +⚠ **Что может сбить.** Вёрстка выдаёт метод: часть вариантов сливает построчный исходник в русские +абзацы, часть сохраняет разбивку. Это видно ДО чтения — не давай форме решать за тебя. + +--- + +## Что записать — в файл `{answer}` + +1. **ПОРЯДОК ЧИТАЕМОСТИ** — строкой: `Т3 > Т1 > Т2 > Т4`, от лучшего к худшему. + Неразличимые соединяй через `=`. Это главный вопрос: какой текст ты взял бы в книгу. +2. **ПОРЯДОК ВЕРНОСТИ** — {fidelity} +3. **По фразе на вариант**: почему. Годится и «серо, но гладко», и «живой ритм, но во втором + абзаце теряется, кто кому говорит». + +Если какой-то вариант покажется откровенно машинным — скажи прямо, это самое ценное. + +--- +""" + +# ⚠ Верность исходнику владелец может назвать только там, где читает исходный язык. На китайском +# он его не читает, и просить «сверь смысл» значило бы просить невыполнимого — а невыполнимая +# просьба в задании обесценивает и выполнимую часть. Поэтому на zh просят то, что видно БЕЗ +# исходника: провалы связности. Это не подмена вопроса, а его честная граница. +OWNER_FID = { + "zh": ("исходник китайский, и сверять его тебе нечем — поэтому этот пункт ПРОПУСТИ.\n" + " Вместо него отметь то, что видно и без исходника: где текст теряет связность,\n" + " где непонятно, кто что делает, где мысль обрывается. Такие места почти всегда\n" + " и оказываются враньём про исходник."), + "en": ("исходник приложен, английский. Если станешь сверять — назови порядок по верности\n" + " отдельной строкой. Не станешь — пропусти, это необязательно."), +} + + +def emit_owner(only: list[str] | None = None) -> None: + """Пакет владельцу. `only` — пересобрать ТОЛЬКО названные пары. + + ⚠⚠ ПЕРЕ-ЭМИССИЯ НЕ ИМЕЕТ ПРАВА ТРОНУТЬ ПАРУ, КОТОРУЮ ВЛАДЕЛЕЦ УЖЕ ПРОЧЁЛ. Раскладка меток + есть чистая функция соли и набора армов; пересборка ЦЕЛОГО пакета переписала бы ключ и + китайской пары тоже — а её ответ уже дан и расшифрован. Тогда та же расшифровка при следующем + запуске дала бы ДРУГИЕ армы, и работа владельца превратилась бы в мусор молча. Ровно так фаза + Д однажды потеряла вердикт `E0/D0` (+1.56 → +0.38). Поэтому ключ здесь ДОГРУЖАЕТСЯ, а не + создаётся заново, и пары вне `only` не пересчитываются. + ⚠ Прежний ответ по пересобираемой паре не удаляется, а уводится в архив: это улика — работа + владельца, испорченная дефектом МОЕЙ сборки, а не его чтением. + """ + key: dict = {"_что": "слепое чтение ВЛАДЕЛЬЦЕМ, панель захода Д17", "_соль": OWNER_SALT, + "_панель": list(OWNER_PANEL), + "_контроль": "Z0 и ZF — две генерации ОДНОЙ боевой связки; развёл далеко = шум"} + if OWNER_KEY.exists(): + key = {**json.loads(OWNER_KEY.read_text(encoding="utf-8")), **key} + OWNER_DIR.mkdir(parents=True, exist_ok=True) + for pair, langname, srcname in (("zh", "КИТАЙСКАЯ", "китайский"), + ("en", "АНГЛИЙСКАЯ", "английский")): + if only and pair not in only: + print(f" {pair}: не в списке пересборки — ключ и задание НЕ трогаются") + continue + prev = OWNER_DIR / f"ОТВЕТ-{pair}.md" + if prev.exists() and _ORD.search(prev.read_text(encoding="utf-8")): + arch = OWNER_DIR / f"ОТВЕТ-{pair}.ИСПОРЧЕННЫЙ-ПАКЕТ.md" + arch.write_text(prev.read_text(encoding="utf-8"), encoding="utf-8") + prev.unlink() + print(f" {pair}: прежний ответ уведён в {arch.name} (улика, не удалён)") + tx_all = ZS.texts_of(pair) + # «жирная глава»: самая длинная из тех, где панель полна — длинная честнее короткой + cand = [u for u in tx_all if all(tx_all[u].get(a, "").strip() for a in OWNER_PANEL)] + cand.sort(key=lambda u: -sum(len(tx_all[u][a]) for a in OWNER_PANEL)) + take = cand[:OWNER_N[pair]] + ans = OWNER_DIR / f"ОТВЕТ-{pair}.md" + body = [f"# {langname} ПАРА — слепое чтение\n", + OWNER_HEAD.format(n=len(OWNER_PANEL), answer=ans, fidelity=OWNER_FID[pair])] + for j, uid in enumerate(take, 1): + # ⚠ РАСКЛАДКА ОДНА НА ВСЮ ПАРУ, А НЕ СВОЯ У КАЖДОГО ОТРЫВКА (починка 17.08). + # Первая редакция солила раскладку номером главы, и метка `Т1` означала РАЗНЫЕ + # армы в первом и втором английском отрывке. Владелец — как поступил бы любой + # читатель — дал ОДИН сводный порядок на пару и говорил о «Т1» как об одном + # варианте; сопоставить это с армами оказалось нечем, и английская половина + # его работы пропала не по его вине. Машинным читателям своя раскладка на + # единицу нужна (они судят поединично), человеку — вредна. + lay = layout(pair, "OWNER-ALL-v2", "OWNER", OWNER_PANEL) + body += [f"\n# ОТРЫВОК {j}\n", f"## Исходник ({srcname})\n", + tx_all[uid]["_src"], ""] + for lab, arm in lay.items(): + body += ["\n---\n", f"## Отрывок {j}, вариант {lab}\n", tx_all[uid][arm], ""] + key.setdefault(pair, {})[str(j)] = { + "uid": uid, "метки": lay, + "знаков": {a: len(tx_all[uid][a]) for a in OWNER_PANEL}} + (OWNER_DIR / f"ЧТЕНИЕ-{pair}.md").write_text("\n".join(body), encoding="utf-8") + if not ans.exists(): + ans.write_text( + f"# Ответ — {langname.lower()} пара\n\n" + + "".join(f"ПОРЯДОК ЧИТАЕМОСТИ {j}: \n" for j in range(1, len(take) + 1)) + + "\nЗаметки по вариантам:\n", encoding="utf-8") + n = sum(len(tx_all[u][a]) for u in take for a in OWNER_PANEL) + print(f" {pair}: глав {len(take)} · вариантов {len(OWNER_PANEL)} · {n // 1000} тыс. знаков") + OWNER_KEY.write_text(json.dumps(key, ensure_ascii=False, indent=1), encoding="utf-8") + print(f"\nпакет → {OWNER_DIR}") + print(f"ключ → {OWNER_KEY} (НЕ открывать до ответа)") + + +def score_owner() -> int: + if not OWNER_KEY.exists(): + print("ключа нет — сначала --emit-owner") + return 1 + key = json.loads(OWNER_KEY.read_text(encoding="utf-8")) + sc, _bad = ZS.read() + for pair in ("zh", "en"): + f = OWNER_DIR / f"ОТВЕТ-{pair}.md" + if not f.exists() or pair not in key: + continue + txt = f.read_text(encoding="utf-8") + got = orders(txt) + if not got: + print(f"{pair}: порядка нет — ждём ответа владельца") + continue + print(f"\n=== {pair} ===") + # ⚠ СТОРОЖ НЕОДНОЗНАЧНОЙ РАСШИФРОВКИ (заведён 17.08 по факту). Если в ключе у отрывков + # РАЗНЫЕ раскладки меток, а читатель дал ОДИН сводный порядок, то механическое применение + # этого порядка к каждой раскладке даёт столько же РАЗНЫХ расшифровок, сколько отрывков, и + # все они одинаково «правдоподобны». Так и вышло с английской парой: один и тот же ответ + # владельца дал «однопроходка первая» на первом отрывке и «однопроходка ПОСЛЕДНЯЯ» на + # втором. Печатать это как результат — значит выдать артефакт разметки за замер. + maps = [tuple(sorted(m["метки"].items())) for m in key[pair].values()] + if len(set(maps)) > 1 and len(got) < len(key[pair]): + print(" ⛔ РАСШИФРОВКА НЕОДНОЗНАЧНА И НЕ ВЫВОДИТСЯ. У отрывков РАЗНЫЕ раскладки") + print(" меток, а порядок дан ОДИН сводный: одна и та же метка означает в них") + print(" разные армы. Это дефект СБОРКИ ПАКЕТА, а не ответа читателя.") + print(" Что уцелело — устойчивое к раскладке: какие метки читатель поставил") + print(" РЯДОМ или связал знаком «=». Ниже только это.") + for j, meta in key[pair].items(): + if j not in got: + continue + r = _ranks(got[j]) + pairs = [(a, b) for a in r for b in r if a < b and abs(r[a] - r[b]) <= 1] + for a, b in pairs: + arms = {meta["метки"].get(a), meta["метки"].get(b)} + print(f" {a} и {b} рядом → в этом отрывке это {' и '.join(sorted(x for x in arms if x))}") + continue + for j, meta in key[pair].items(): + if j not in got: + continue + r = _ranks(got[j]) + rows = [(meta["метки"][lab], r[lab]) for lab in meta["метки"] if lab in r] + print(f" отрывок {j} (глава {meta['uid']}):") + for arm, rank in sorted(rows, key=lambda x: x[1]): + errs = [ZS._carry(ax) for (pp, u, aa, _h), ax in sc.items() + if pp == pair and aa == arm and u == meta["uid"]] + e = f"{st.mean(errs):.1f}" if errs else "—" + print(f" {rank:.1f} {arm:4s} ошибок по судье {e}") + d = {a: rk for a, rk in rows} + if "Z0" in d and "ZF" in d: + gap = abs(d["Z0"] - d["ZF"]) + print(f" КОНТРОЛЬ ШУМА: две генерации одной связки разведены на {gap:.1f} " + f"позиции → {'порядок несёт сигнал' if gap <= 1 else 'ПОРЯДОК = ШУМ'}") + return 0 + + def selftest() -> int: fails = [] ok = lambda c, m: fails.append(m) if not c else None # noqa: E731 @@ -351,8 +590,11 @@ def selftest() -> int: ok(r == {"Т3": 1.0, "Т1": 2.5, "Т5": 2.5, "Т2": 4.0}, f"разбор порядка сломан: {r}") ok(abs(_spearman([1, 2, 3], [1, 2, 3]) - 1.0) < 1e-9, "Спирмен не даёт +1 на совпадении") ok(abs(_spearman([1, 2, 3], [3, 2, 1]) + 1.0) < 1e-9, "Спирмен не даёт −1 на инверсии") - m = _ORD.search("ПОРЯДОК 2: Т3 > Т1 > Т5") - ok(bool(m) and m.group(1) == "2", "строка порядка не находится") + ok(orders("ПОРЯДОК 2: Т3 > Т1 > Т5") == {"2": "Т3 > Т1 > Т5"}, "нумерованный порядок") + ok(list(orders("## Порядок читаемости\n\nТ3 > Т1 > Т2 > Т4").values()) == ["Т3 > Т1 > Т2 > Т4"], + "ЧЕЛОВЕЧЕСКАЯ форма ответа без номера не читается — на ней уже терялась работа владельца") + ok(list(orders("**ПОРЯДОК ЧИТАЕМОСТИ:** `Т1 > Т4 = Т3 > Т2`").values()) == ["Т1 > Т4 = Т3 > Т2"], + "порядок в markdown-разметке не читается") for pair in Z.PAIRS: u = pick(pair) ok(len(u) == N_UNITS, f"{pair}: единиц {len(u)}, ожидалось {N_UNITS}") @@ -375,6 +617,8 @@ def selftest() -> int: if __name__ == "__main__": a = sys.argv[1:] or ["--selftest"] fn = {"--emit": emit, "--score": lambda: sys.exit(score()), + "--emit-owner": lambda: emit_owner([x for x in a[1:] if x in ("zh", "en")] or None), + "--score-owner": lambda: sys.exit(score_owner()), "--score-calib": lambda: sys.exit(score_calib()), "--selftest": lambda: sys.exit(selftest())}.get(a[0]) if not fn: diff --git a/eval/dovodka/d1_gemini.py b/eval/dovodka/d1_gemini.py index 556bae4c..b13edd5c 100644 --- a/eval/dovodka/d1_gemini.py +++ b/eval/dovodka/d1_gemini.py @@ -167,9 +167,15 @@ def cmd_status() -> None: fail = [a for a in at if not a.get("ok")] print(f"Д1 · собрано {len(have)}/{len(us)} · порог заказа ≥{N_ENOUGH}") print(f" попыток всего {len(at)} · удачных {len(ok)} · отказов {len(fail)}") - if at: - span = (max(a.get("ts", 0) for a in at) - min(a.get("ts", 0) for a in at)) / 3600 - print(f" окно журнала: {span:.1f} ч (заказ требует ≥24 ч до вердикта «не отдаёт»)") + # ⚠ Окно считается ТОЛЬКО по записям с меткой времени. Записи первой редакции её не несли, + # и `.get("ts", 0)` давал минимум 0 — то есть отсчёт от эпохи и «окно 496 315 часов». + # Число выглядело солидно и означало ровно ничего. + ts = [a["ts"] for a in at if a.get("ts")] + if len(ts) >= 2: + print(f" окно журнала: {(max(ts) - min(ts)) / 3600:.2f} ч по {len(ts)} записям с меткой " + f"(заказ требует ≥24 ч ДО вердикта «не отдаёт»)") + elif at: + print(f" окно журнала: записей с меткой времени {len(ts)} — окно не доказуемо") codes: dict = {} for a in fail: codes[str(a.get("code"))] = codes.get(str(a.get("code")), 0) + 1 diff --git a/eval/dovodka/itog_d4.py b/eval/dovodka/itog_d4.py index b72a2383..e62841b2 100644 --- a/eval/dovodka/itog_d4.py +++ b/eval/dovodka/itog_d4.py @@ -24,6 +24,7 @@ и запрет механизирован тем, что пороги считаются ДО контрастов и печатаются рядом с ними. Запуск: itog_d4.py [--gates] # только гейты, без боевых чисел + itog_d4.py --sens # Г5: чувствительность вердикта H-1 к составу пачек ($0) """ from __future__ import annotations @@ -107,17 +108,47 @@ def variant(arm: str, u: dict, half: int) -> str: if arm == "CTRLfloor": return S.floor_pair(u, half) if arm == "CTRLdecoy": - return AJ._plant(S.C.base_a0(u["uid"]))[0] # noqa: SLF001 + return AJ._plant(S.C.base_a0(u["uid"]))[0] return S.text_of(arm, u) +# ⚠ БАЗА АРМА — то, поверх чего он работает и что уже оплачено к моменту его вызова. Держится +# ДАННЫМИ, потому что состав панели у каждой оси свой; числа — медианы по сырью паков 22/23. +BASE_COST_BY_AXIS = { + "d4": {"A0": 0.00603, "A1": 0.00096, "A1B": 0.00096, "A3": 0.00096, "A2": 0.0, + "A4": 0.00603, "A6": 0.00387, "A6F": 0.00387}, + "d3": {"E0": 0.00096, "E1": 0.00096, "E3": 0.00096, "E4": 0.00096 + 0.00789, + "E2": 0.0, "E6": 0.00096, "D0": 0.0}, + "d6": {"J0": 0.00096, "J2": 0.0, "D0": 0.0}, + "d1": {"A0": 0.00603, "R1": 0.00096}, +} + + def _keys_for(root: Path) -> Path: """Каталог ключей ЭТОГО семейства. Ключи разведены (`sud.py`), потому что эмиссия пачек одного семейства переписывала бы раскладку другого. Свод обязан читать ответы тем же ключом, каким они выпускались, иначе метки сопоставятся с ЧУЖИМИ армами и разбор даст 0 меток — ровно это и случилось на первом прогоне семейства Sol.""" fam = "claude" if root.name.startswith("sud-") else root.name.split("-")[0] - return KEYS if fam == "claude" else KEYS.parent / f"{KEYS.name}-{fam}" + if fam == "claude": + return KEYS + # ⚠ Ключи разведены по семействам НЕ на всех осях: разводить их начали на Д1/Д3/Д6, после + # аварии с пере-эмиссией, а китайская ось выпускала ОБА семейства одним ключом. Жёсткое + # `-{fam}` роняло свод на sol-d4-work («62 ответа, разобрано 0 меток»), то есть заявленный + # носитель чисел Д4 не воспроизводил ось вовсе. Разведённый ключ — если он есть; иначе общий. + split = KEYS.parent / f"{KEYS.name}-{fam}" + return split if split.exists() else KEYS + + +# ⚠ СОСТАВ ПАЧЕК — ПАРАМЕТР ТОЛЬКО ДЛЯ ЧУВСТВИТЕЛЬНОСТИ, И ОБА СПИСКА ПУСТЫ ПО УМОЛЧАНИЮ. +# Печатаемый вердикт при этом не меняется ни на знак: `--drop`/`--restore` существуют, чтобы +# пере-считать его ПРИ ДРУГОМ составе и напечатать разницу рядом, а не чтобы подобрать удобный. +# Заведены под Г5 (находка ревизии P13: снятие двух пачек прогона-валидации 11.08 переворачивает +# решение по H-1). Без такого рычага пере-счёт делался копией дерева — то есть числами, которые +# никто не может воспроизвести. +DROP = set(next((a.split("=", 1)[1] for a in sys.argv if a.startswith("--drop=")), "").split(",")) - {""} +RESTORE = set(next((a.split("=", 1)[1] for a in sys.argv + if a.startswith("--restore=")), "").split(",")) - {""} def read_family(root: Path) -> tuple[dict, list]: @@ -134,9 +165,19 @@ def read_family(root: Path) -> tuple[dict, list]: continue key_all = json.loads(kf.read_text(encoding="utf-8")) for f in sorted((root / f"{half}-answers").glob("*.txt")): + tok = f.name.split(".")[0] if ".ANNULLED" in f.name: + if tok not in RESTORE: + continue + # ⚠ Пачка, отправленная в карантин и ПЕРЕ-СУЖЕННАЯ, лежит на диске дважды. + # Вернуть карантинную копию значит посчитать единицу дважды разными судьями — + # молча, потому что ключ у обеих один. Возвращать можно только то, чему замены нет. + if (f.parent / f"{tok}.txt").exists(): + raise SystemExit(f"⛔ {tok} пере-суждена: возврат карантинной копии удвоил бы " + f"единицу; --restore допустим только для пачек без замены") + if tok in DROP: continue - key = key_all.get(f.stem) + key = key_all.get(tok) if not key: continue txt = f.read_text(encoding="utf-8", errors="replace") @@ -146,13 +187,13 @@ def read_family(root: Path) -> tuple[dict, list]: # ⚠ `[ \t]*`, а не `\s*`: `\s` включает перевод строки, и оценка # могла считаться со СЛЕДУЮЩЕЙ строки. Тот же класс `absjudge.py:351` # чинил у себя; здесь он оставался. - m = re.search(rf"^{lab}-{a}:[ \t]*(\d+)", txt, re.M) + m = re.search(rf"^{lab}-{a}:[ \t]*(\d+)", txt, re.MULTILINE) if m: sc[a] = int(m.group(1)) if len(sc) < 4: bad.append((root.name, half, f.stem, lab, "не все оси")) continue - w = re.search(rf"^{lab}-ПОЧЕМУ:\s*(.*)$", txt, re.M) + w = re.search(rf"^{lab}-ПОЧЕМУ:\s*(.*)$", txt, re.MULTILINE) why = w.group(1) if w else "" if sum(sc.values()) and not why.strip(): bad.append((root.name, half, f.stem, lab, "ненулевая без обоснования")) @@ -221,16 +262,16 @@ def three_axes(d: dict) -> None: ⚠ Канон считается на ВЫХОДЕ арма, а не на его входе: вход мерился при покупке, а сверяется качество результата. Цена — медиана оплаченной клетки арма из сырья, а не из сметы. """ - import json as _j # noqa: PLC0415 - import statistics as _st # noqa: PLC0415 + import json as _j + import statistics as _st us = {x["uid"]: x for x in S.units()} print("\n" + "=" * 78) print("ТРИ ОСИ ПО КАЖДОМУ АРМУ (требование заказа :95) — судья · канон · цена") print("=" * 78) - print(f" {'арм':5s}{'судья':>8s}{'канон':>9s}{'$/клетка':>11s} что это") + print(f" {'арм':5s}{'судья':>8s}{'канон':>9s}{'$/клетка':>11s}{'$/единица':>12s} что это") what = WHAT_AXIS[AXIS] - _unused = {"A0": "", "A4": "", - "A6": "", "A6F": "", + _dead = { + "A0": "", "A4": "", "A6": "", "A6F": "", "A3": "черновик + смысловой критик", "A2": "однопроходка уравненного мандата", "A1B": "черновик + флаги (батарея+канон)", "A1": "черновик + только канон-флаги"} for arm in ALL_ARMS: @@ -244,20 +285,227 @@ def three_axes(d: dict) -> None: k, n = S.C.BANK.coverage(u["source"], t) if n: cov.append(k / n) - pr = [] + pr, per_unit = [], {} # ⚠ Глоб по тегу арма захватывал и клетки КРИТИКА (`dv-a-a3-crit-*`), поэтому # медиана «$/клетка» у A3 и A6 считалась по смеси двух ролей. Найдено приёмкой. for f in S.C.OUT.glob(f"{S.C.tag(arm, '')}*.json"): - if any(x in f.name for x in (".ERROR", ".LENGTH", ".FLAGSV1", "-crit-")): + # ⚠ Суффиксы карантина перечислены ПОЛНОСТЬЮ. `.STALE` и `.TWIN` заведены фазой Д на ходу + # (устаревшие бесплатные клетки арма и побайтные близнецы пола), и их отсутствие здесь + # тянуло медиану цены вниз нулевыми клетками. Найдено чтением собственных коммитов. + if any(x in f.name for x in (".ERROR", ".LENGTH", ".FLAGSV1", ".STALE", ".TWIN")): continue r = _j.loads(f.read_text(encoding="utf-8")) - if r.get("cost_usd"): - pr.append(r["cost_usd"]) + c = r.get("cost_usd") or 0 + # ⚠ ДВЕ РАЗНЫЕ ВЕЛИЧИНЫ, И ПУТАТЬ ИХ ДОРОГО. «$/клетка» — цена ОДНОГО вызова; + # «$/единица» — цена всей работы арма над единицей ПЛЮС база, на которой он стоит. + # У армов с двумя вызовами (критик + фиксер) это отличается втрое: у A3 клетка + # фиксера 0.00644, а единица — 0.02014 плюс черновик. Отчёт печатал первую цифру + # в колонке, по которой сравнивают экономику, и ставка H-2б выглядела средней + # по цене, будучи самой дорогой в панели. + if "-crit-" not in f.name and c: + pr.append(c) + uid = r.get("uid") or f.stem.split("-")[-1] + per_unit[uid] = per_unit.get(uid, 0.0) + c + base = BASE_COST_BY_AXIS.get(AXIS, {}).get(arm, 0.0) + full = (_st.median([v + base for v in per_unit.values() if v > 0]) + if any(v > 0 for v in per_unit.values()) else base) print(f" {arm:5s}{_st.mean(sc):8.2f}{(_st.mean(cov) if cov else 0):9.3f}" - f"{(_st.median(pr) if pr else 0):11.5f} {what.get(arm, '')}") + f"{(_st.median(pr) if pr else 0):11.5f}{full:12.5f} {what.get(arm, '')}") print(" судья — ошибок на единицу (ВЕРНОСТЬ+ЯЗЫК), МЕНЬШЕ лучше") print(" канон — доля покрытия банка на ВЫХОДЕ арма, БОЛЬШЕ лучше") - print(" цена — медиана оплаченной клетки арма по сырью; A0 куплен прошлыми паками") + print(" $/клетка — медиана ОДНОГО оплаченного вызова арма") + print(" $/единица — вся работа арма над единицей ПЛЮС база, на которой он стоит:") + _b = BASE_COST_BY_AXIS.get(AXIS, {}) + print(f" {' · '.join(f'{a}+{c:.5f}' for a, c in sorted(_b.items()) if c)}") + print(" (замер по сырью: черновик flash 0.00096, перепис dspro 0.00507)") + + +def margins_half(d: dict, a: str, b: str) -> list[float]: + """Перевес ЗА арм `a`, посчитанный ВНУТРИ половины и лишь потом усреднённый по единице. + + ⚠ Зачем вторая формула рядом со штатной. Наборы p1/p2 судятся РАЗНЫМИ сессиями, и между ними + намерен систематический сдвиг строгости (находка ревизии P07: пол claude даёт +1.8 ошибки в + пользу p2). Штатная `margins` берёт среднее арма по тем половинам, где он ЕСТЬ, — а половины + у армов заполнены не одинаково (`A6` 15/16 против `A0` 30/31). Там, где арм присутствует + только в одной половине, в его число входит уровень ЭТОЙ сессии, и разность двух армов несёт + кусок межсессионного сдвига как будто это разница армов. Здесь разность берётся там, где оба + арма судила ОДНА сессия, поэтому уровень сессии сокращается тождественно. + """ + out = [] + for uid in sorted({k[0] for k in d}): + per = [d[(uid, b, h)]["carry"] - d[(uid, a, h)]["carry"] + for h in ("p1", "p2") if (uid, a, h) in d and (uid, b, h) in d] + if per: + out.append(st.mean(per)) + return out + + +def _levels(d: dict) -> dict: + """Средний счёт боевых армов по СЕССИЯМ судейского журнала: {(проход, судья): (n, среднее)}. + + ⚠ Это единственный оставшийся способ проверить посылку P13. Находка называла пачки + `0dce349331`/`0ffee70740` «другим режимом замера» на двух основаниях: не замороженный промт и + уровень счёта 9.94 против 6.39 у основной волны. Транскрипта сессии на диске больше нет + (проверено `find` по всем каталогам агентов) — промт сверить не с чем. Уровень сверить можно, + и вопрос ставится честно: выбивается ли д-01 из РАЗБРОСА ОСТАЛЬНЫХ СЕССИЙ или такой шаг между + сессиями у этой оси обычный. Во втором случае снятие двух пачек — не норма, а выбор. + """ + led = json.loads((Path.home() / "books" / "dovodka" / "agent-runs.json").read_text("utf-8")) + battle = set(ALL_ARMS) + out: dict = {} + for r in led: + if r.get("run") != AXIS: + continue + half = r.get("pass_") + sc = [v["carry"] for (uid, arm, h), v in d.items() + if h == half and arm in battle and uid in _uids_of(r, half)] + if sc: + out[(half, r["judge"], tuple(r["tokens"]))] = (len(sc), st.mean(sc)) + return out + + +def _uids_of(run: dict, half: str) -> set: + """uid единиц, которые судила эта сессия. Пачка названа ТОКЕНОМ, ключ переводит его в uid.""" + kf = KEYS / f"{AXIS}{half}-KEY.json" + if not kf.exists(): + return set() + key_all = json.loads(kf.read_text(encoding="utf-8")) + out = set() + for tok in run.get("tokens", []): + for meta in (key_all.get(tok) or {}).values(): + out.add(meta["uid"]) + return out + + +def sens() -> int: + """Г5 — ЧУВСТВИТЕЛЬНОСТЬ ВЕРДИКТА H-1 К СОСТАВУ ПАЧЕК. $0, ни одного вызова к моделям. + + ⚠ ПРАВИЛО РЕШЕНИЯ ОБЪЯВЛЕНО ДО ПРОГОНА (иначе это подбор сценария, а не замер): + вердикт по H-1 считается ПЕРЕ-РЕШЁННЫМ, только если он одинаков во ВСЕХ сценариях, снятие + в которых обосновано нормой проекта. Если знак решения зависит от выбора сценария — печатается + именно это, а не удобная половина. Запас над порогом печатается числом рядом: решение, + выигранное с запасом меньше собственного шага шкалы (1 ошибка), вердиктом не является. + """ + print("=" * 78) + print("Г5 — ЧУВСТВИТЕЛЬНОСТЬ H-1 К СОСТАВУ ПАЧЕК ($0, пере-анализ уже купленных ответов)") + print("=" * 78) + print(" правило решения объявлено в докстринге ДО прогона: вердикт пере-решён только при") + print(" совпадении во ВСЕХ обоснованных сценариях; запас печатается числом\n") + global DROP, RESTORE # состав пачек — параметр сценария, см. их объявление + grid = [ + ("S0 дерево как есть (что печатает свод)", set(), set()), + ("S1 −валидация 11.08 (P13)", {"0dce349331", "0ffee70740"}, set()), + ("S2 +69de717f3f назад (база ревизии)", set(), {"69de717f3f"}), + ("S3 база ревизии −валидация (счёт P13)", {"0dce349331", "0ffee70740"}, {"69de717f3f"}), + ] + keep = (DROP, RESTORE) + rows = [] + for name, drop, restore in grid: + DROP, RESTORE = drop, restore + d, _bad = read_family(FAMILIES["claude"]) + sd, n = floor_sd(d) + thr = 2.8 * sd / (n ** 0.5) if n > 1 else float("nan") + line = {"name": name, "sd": sd, "n": n, "thr": thr, "d": d} + for a, b, _w in PRIMARY: + m = margins(d, a, b) + mh = margins_half(d, a, b) + line[a] = (len(m), st.mean(m), BO.sign_perm_p(m) if len(m) > 2 else float("nan"), + len(mh), st.mean(mh) if mh else float("nan"), + BO.sign_perm_p(mh) if len(mh) > 2 else float("nan")) + rows.append(line) + DROP, RESTORE = keep + for a, b, what in PRIMARY: + print(f"### КОНТРАСТ {a}/{b} — {what}") + print(f" {'сценарий':40s}{'пол n':>6s}{'порог':>7s}{'перевес':>9s}" + f"{'запас':>7s}{'p Холма*':>9s} вердикт") + for r in rows: + mean = r[a][1] + gap = abs(mean) - r["thr"] + adj = holm([r[x][2] if r[x][2] == r[x][2] else 1.0 for x, _y, _z in PRIMARY]) + pa = adj[[x for x, _y, _z in PRIMARY].index(a)] + ok = gap > 0 and pa < 0.05 + print(f" {r['name']:40s}{r['n']:6d}{r['thr']:7.2f}{mean:+9.2f}{gap:+7.2f}" + f"{pa:9.4f} {'ПЕРЕШЁЛ' if ok else 'ниже порога'}") + print(f" {'—— тот же контраст ВНУТРИ половины (P07)':40s}") + for r in rows: + _k, _m, _p, kh, meanh, ph = r[a] + gap = abs(meanh) - r["thr"] + print(f" {r['name']:40s}{r['n']:6d}{r['thr']:7.2f}{meanh:+9.2f}{gap:+7.2f}" + f"{ph:9.4f} {'ПЕРЕШЁЛ' if gap > 0 and ph < 0.05 else 'ниже порога'} ед.{kh}") + print() + print(" * Холм считается ПО ТРЁМ контрастам внутри своего сценария, как в пре-реге\n") + print("=" * 78) + print("УРОВЕНЬ СУДЕЙСКИХ СЕССИЙ — выбивается ли прогон-валидация из разброса остальных") + print("=" * 78) + lv = _levels(rows[0]["d"]) + for half in ("p1", "p2"): + xs = [(j, n, m, t) for (h, j, t), (n, m) in lv.items() if h == half] + if not xs: + continue + vals = [m for _j, _n, m, _t in xs] + print(f" {half}: сессий {len(xs)} · среднее {st.mean(vals):.2f} · " + f"разброс {min(vals):.2f}…{max(vals):.2f}" + + (f" · sd {st.pstdev(vals):.2f}" if len(vals) > 1 else "")) + for j, n, m, t in sorted(xs, key=lambda x: -x[2]): + mark = " ← прогон-валидация 11.08" if "0dce349331" in t else "" + print(f" {j:8s} клеток {n:3d} ошибок/клетку {m:6.2f}{mark}") + print("\n" + "=" * 78) + print("СИСТЕМАТИЧЕСКИЙ СДВИГ НАБОРОВ (P07) — на нём стоит вся формула порога") + print("=" * 78) + d0 = rows[0]["d"] + both = [(v["carry"] - d0[(u, a, "p2")]["carry"]) + for (u, a, h), v in d0.items() if h == "p1" and (u, a, "p2") in d0 and a in ALL_ARMS] + fl = [d0[(u, "CTRLfloor", "p1")]["carry"] - d0[(u, "CTRLfloor", "p2")]["carry"] + for u in {k[0] for k in d0} + if (u, "CTRLfloor", "p1") in d0 and (u, "CTRLfloor", "p2") in d0] + print(f" боевые армы, одна единица в обеих половинах: n={len(both)} " + f"сдвиг p1−p2 {st.mean(both):+.2f} · sd {st.pstdev(both):.2f}") + print(f" пол (те же половины): n={len(fl)} сдвиг {st.mean(fl):+.2f} · sd {st.pstdev(fl):.2f} " + f"· p={BO.sign_perm_p(fl):.4f}") + print(" ⇒ если сдвиг пола ≈ сдвиг боевых армов, пол меряет РАЗНИЦУ СЕССИЙ, а не шум оценки;") + print(" сбалансированный по половинам контраст этот сдвиг сокращает, несбалансированный — нет") + print("\n БАЛАНС ПОЛОВИН ПО АРМАМ (несбалансированный арм несёт уровень своей сессии):") + for arm in ALL_ARMS: + n1 = sum(1 for (u, a, h) in d0 if a == arm and h == "p1") + n2 = sum(1 for (u, a, h) in d0 if a == arm and h == "p2") + print(f" {arm:5s} p1 {n1:3d} · p2 {n2:3d}" + (" ⚠ перекос" if abs(n1 - n2) > 1 else "")) + # ⚠ КАЛИБРОВКА ПОРОГА ПО ЗНАКУ (находка ревизии №6: R73 против P40). Две выжившие находки + # называли порог смещённым в ПРОТИВОПОЛОЖНЫЕ стороны, и ни одна не проверялась на ЭТОЙ оси: + # R73 мерила на проходе `border` (контраст в 1.40× шумнее пола), P40 рассуждала. Здесь то же + # меряется прямо: шум САМОГО контраста той же структуры — перевес, посчитанный в p1, против + # перевеса в p2. Если он равен полу, порог откалиброван; больше — вердикты смелее данных; + # меньше — прибор строже, чем нужно. + sd0, n0 = floor_sd(d0) + print("\n" + "=" * 78) + print("КАЛИБРОВКА ПОРОГА ПО ЗНАКУ — пол против ШУМА САМОГО КОНТРАСТА (ревизия №6)") + print("=" * 78) + print(f" пол, из которого строится порог: n={n0} sd={sd0:.4f} → порог {2.8 * sd0 / n0**0.5:.4f}") + for a, b, _w in PRIMARY: + xs = [] + for u in {k[0] for k in d0}: + if all((u, x, h) in d0 for x in (a, b) for h in ("p1", "p2")): + xs.append((d0[(u, b, "p1")]["carry"] - d0[(u, a, "p1")]["carry"]) + - (d0[(u, b, "p2")]["carry"] - d0[(u, a, "p2")]["carry"])) + if len(xs) < 3: + continue + sd = st.pstdev(xs) + own = 2.8 * sd / len(xs) ** 0.5 + m = margins(d0, a, b) + print(f" {a}/{b}: n={len(xs):2d} sd={sd:.4f} ({sd / sd0:.2f}× пола) → СВОЙ порог {own:.4f}" + f" · перевес {st.mean(m):+.4f} · запас {abs(st.mean(m)) - own:+.4f}") + print(" ⚠ «свой порог» — не замена объявленному: пре-рег зафризил формулу ПО ПОЛУ, и менять её") + print(" после взгляда на вердикты нельзя. Это проверка КАЛИБРОВКИ, а не второе решающее правило.") + # ⚠ Вторая сторона той же калибровки: пол строится на ДВУХ ГЕНЕРАЦИЯХ, то есть несёт и шум + # порождения, и шум судьи. Чистый шум судьи виден там, где текст ОДИН И ТОТ ЖЕ, а сессии + # разные — на боевых армах, которые лежат в обеих половинах. Если он БОЛЬШЕ пола, порог занижен. + print("\n ЧИСТЫЙ ШУМ СУДЬИ (ОДИН И ТОТ ЖЕ текст, две сессии) против пола:") + for arm in ALL_ARMS: + xs = [d0[(u, arm, "p1")]["carry"] - d0[(u, arm, "p2")]["carry"] for u in {k[0] for k in d0} + if (u, arm, "p1") in d0 and (u, arm, "p2") in d0] + if len(xs) > 2: + print(f" {arm:5s} n={len(xs):2d} среднее {st.mean(xs):+.2f} sd={st.pstdev(xs):.4f}" + f" ({st.pstdev(xs) / sd0:.2f}× пола)") + return 0 def main() -> int: @@ -350,4 +598,4 @@ def main() -> int: if __name__ == "__main__": - sys.exit(main()) + sys.exit(sens() if "--sens" in sys.argv else main()) diff --git a/eval/dovodka/ja6.py b/eval/dovodka/ja6.py new file mode 100644 index 00000000..4239921e --- /dev/null +++ b/eval/dovodka/ja6.py @@ -0,0 +1,505 @@ +#!/usr/bin/env python3 +"""ЯПОНСКАЯ НОГА H-4 — слепой проход `J0` против `J6`. $0 (агент-сессии + харнесс владельца). + +Гипотеза H-4: перевес `deepseek-v4-pro` в редакторской роли есть свойство пары zh→ru, и на другой +паре ПОРЯДОК ЖИЛЬЦОВ может смениться. Порядок нельзя увидеть, имея на паре одного редактора: ось +Д6 меряла «покупает ли редактор что-нибудь вообще», а не «какой редактор лучше». На zh панель есть +(эксп-22), на en второй редактор куплен (`E6`), на ja его не было — требование заказа (:115) +печатать сравнение порядка между zh/en/ja поэтому не исполнялось. + +⚠ СТАТУС РЕЗУЛЬТАТА — ОПИСАТЕЛЬНЫЙ, и это не смягчается никаким исходом: ось объявлена +разведочной ДО денег (`power.FORCED_DESCRIPTIVE`, n=9, MDE 2.90 против цели 2.00). + +⚠ ЧЕМУ НАУЧИЛ `tier` И ЧТО ЗДЕСЬ СДЕЛАНО ИНАЧЕ (пре-регистрируется ДО первого ответа): + 1. ПОЛОВИНА ПОЛА НЕ ЯВЛЯЕТСЯ БОЕВЫМ АРМОМ. В паке 23 `CTRLfloorA` побайтно равнялся арму `T1` + в 16/16 — контроль сравнивал `T1` сам с собой. Здесь пара пола — две генерации БОЕВОГО + РЕДАКТОРА (`J0` и `JFLO`), и они РАЗВЕДЕНЫ ПО НАБОРАМ: `J0` живёт в наборе p1, `JFLO` — в p2. + Судья никогда не видит обе половины в одной пачке, а порог несёт межсессионную компоненту. + 2. ПОЛ СНЯТ С ТОЙ ОПЕРАЦИИ, КОТОРАЯ В ПАНЕЛИ. Пол оси Д6 снят с точечного фиксера — операции + почти детерминированной (4 побайтных близнеца из 8 пар). Панель здесь — панель РЕДАКТОРОВ, + поэтому и шум мерится повторной генерацией редактора. + 3. ДОНОР ДЕКОЯ УРАВНЕН: в паке 23 донором во всех 16 единицах был `R0`, то есть чувствительность + сертифицировалась в окрестности одного арма. Здесь донор чередуется `J0`/`J6` по чётности. + 4. МАРЖЕВЫЙ ДЕКОЙ ЕСТЬ (норма П-2) — без него «не различимо» неотличимо от слепоты прибора. + 5а. ⚠ ОГРАНИЧЕНИЕ ГЕЙТА ЧУВСТВИТЕЛЬНОСТИ ОБЪЯВЛЯЕТСЯ ДО ПРОГОНА: маржевый декой садится лишь на + 5 единиц из 9 — регексы посадок не находят на этой книге нужных мест. Расширенный список + (`--wide-plants`, который на другой японской выборке давал 8/9) проверен и НЕ помогает: + те же 5 из 9. Значит гейт П-2 на этой ноге снят пятью точками, и право говорить «не хуже» + он подпирает слабее, чем на осях с полным покрытием. Сказано ДО ответов, а не после. + 5. МОЛЧАЛИВЫЙ НОЛЬ ЗАПРЕЩЁН заданием, а рубрика несёт классы, которые пачка `tier` пропускала. + 6. КЛЮЧ СВОЙ, соль своя. Ключи осей Д6 и `tier` не трогаются: раскладка меток есть функция + соли, uid и НАБОРА армов, и эмиссия поверх переписала бы отсуженную раскладку. + +⚠ ПАРИТЕТ ОБВЯЗОК (норма П-4): оба семейства получают ПОБАЙТНО ОДНО задание и одни правила счёта. +Расхождение прохода `tier` в 12 раз частично куплено тем, что харнессу Sol дали правило плотности +(«две одинаковые ошибки — это два»), которого агенты claude не получали. Здесь это правило стоит +в САМОМ задании, то есть достаётся обоим. + +Запуск: ja6.py --emit | --score | --sol | --score-sol | --selftest +""" +from __future__ import annotations + +import collections +import hashlib +import importlib.util +import json +import re +import statistics as st +import sys +from pathlib import Path + +REPO = Path("/home/ubuntu/projects/textmachine") +ZONE = Path(__file__).resolve().parent +for d in ("dovodka", "editor_tier", "tenant_panel", "role_topology", "bank_arbitration"): + sys.path.insert(0, str(REPO / "eval" / d)) + +OUT = Path.home() / "books" / "dovodka" +WORK = Path.home() / "books" / "judging" / "ja6" +KEYD = OUT / "blind-keys-ja6" +AX = ("ВЕРНОСТЬ", "ТЕРМИН", "ЯЗЫК", "ФОРМА") +CARRY = ("ВЕРНОСТЬ", "ЯЗЫК") +SALT = "ja6-2026-08-15" +PER_SESSION = 4 +SETS = ("p1", "p2") +NL = chr(10) # перевод строки как имя: файл собирается генератором внутри f-строк + +_axre = re.compile(r"^[ТT](\d+)-(ВЕРНОСТЬ|ТЕРМИН|ЯЗЫК|ФОРМА):\s*(\d+)", re.MULTILINE) +_whyre = re.compile(r"^[ТT](\d+)-ПОЧЕМУ:(.*)$", re.MULTILINE) + + +def _load(name: str, path: Path): + 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 + + +def _sud(): + return sys.modules.get("sud") or _load("sud", ZONE / "sud.py") + + +def cell(tag: str) -> str: + f = OUT / f"{tag}.json" + if not f.exists(): + return "" + d = json.loads(f.read_text(encoding="utf-8")) + return (d.get("content") or "") if d.get("finish") == "stop" else "" + + +def units() -> list[dict]: + MJ = sys.modules.get("mat_ja6") or _load("mat_ja6", ZONE / "material_ja.py") + return MJ.units() + + +def panel(u: dict, half: str, idx: int) -> dict[str, str]: + """Армы набора. `J0` только в p1, `JFLO` только в p2 — половины пола не встречаются в пачке.""" + S = _sud() + uid = u["uid"] + j0, j6, d0 = cell(f"dv-c-j0-{uid}"), cell(f"dv-h-j6-{uid}"), cell(f"dv-c-d0-{uid}") + jflo = cell(f"dv-h-jflo-{uid}") + base = j0 if half == "p1" else jflo + if not (base.strip() and j6.strip() and d0.strip()): + return {} + donor = j0 if idx % 2 == 0 else j6 # донор декоя уравнен по армам + out = {("J0" if half == "p1" else "JFLO"): base, "J6": j6, "D0": d0} + dec, n_d = S.AJ._plant(donor) if hasattr(S, "AJ") else (None, 0) + if dec and n_d >= 2: + out["CTRLdecoy"] = dec + mg, n_m = S.margin_plant(donor) + if n_m >= 2: + out["CTRLmargin"] = mg + return out + + +def layout(uid: str, half: str, arms) -> dict[str, str]: + order = sorted(arms) + seed = hashlib.sha256(f"{SALT}|{uid}|{half}|{'|'.join(order)}".encode()).hexdigest() + rank = sorted(order, key=lambda a: hashlib.sha256(f"{seed}|{a}".encode()).hexdigest()) + return {f"Т{i}": a for i, a in enumerate(rank, 1)} + + +HEAD = """Ты — литературный редактор-эксперт. Тебе дан ИСХОДНИК на японском и {n} независимых русских перевода этого же фрагмента под слепыми метками. + +Посчитай ОШИБКИ в каждом варианте по типам: + ВЕРНОСТЬ — сказано не то, что в исходнике: искажение, инверсия, выдумка, пропуск факта. + Сюда же входят, и их пропускают чаще всего: + · инверсия адресата или участника реплики (кто кому говорит, кто кого бьёт); + · потеря или подмена разряда, ранга, числа, единицы измерения; + · потеря вежливостного или social-регистра там, где он несёт смысл; + · состояние действия: совершилось против ещё не совершилось; + · снятое или, наоборот, добавленное отрицание. + ТЕРМИН — имя или термин передан не так, как в других местах того же текста. + ЯЗЫК — то, чего носитель не напишет: калька, канцелярит, несуществующее слово, кривой + порядок слов, рассогласование. + ФОРМА — вёрстка и пунктуация: оформление реплик, кавычки, тире, абзацы. + +Оцени КАЖДЫЙ вариант ОТДЕЛЬНО против исходника — не сравнивая варианты между +собой. Порядок вариантов ничего не значит и специально перемешан. + +Числа — это СЧЁТ ошибок, а не оценка по шкале. Две одинаковые ошибки в одном варианте — это два. + +⚠ НОЛЬ — ЭТО УТВЕРЖДЕНИЕ, А НЕ ОТСУТСТВИЕ ОТВЕТА. Ноль означает «я сверил этот вариант с +исходником по этой оси и дефектов не нашёл». Поэтому ПОЧЕМУ обязательно для КАЖДОЙ метки, в том +числе при нулях: если по всем осям ноль, напиши «прочитано, дефектов не найдено». Пустое ПОЧЕМУ +не принимается и на диск не попадает. + +Ответ ЗАПИШИ ФАЙЛОМ в {path} — ровно в таком виде и ничем больше, блок на каждую метку: + +{skeleton} +ПОЧЕМУ для НЕНУЛЕВОЙ оси: короткая ЦИТАТА из текста через « | ». +Число без цитаты не принимается и на диск не попадает. +""" + + +def emit() -> None: + key: dict[str, dict] = {"_проход": "ja6", "_семейство": ["J6/J0"], + "_контроль": ["J0/D0"], "_пол": ["J0(p1) против JFLO(p2)"]} + made = 0 + for half in SETS: + (WORK / f"{half}-tasks").mkdir(parents=True, exist_ok=True) + (WORK / f"{half}-answers").mkdir(parents=True, exist_ok=True) + for idx, u in enumerate(units()): + for half in SETS: + texts = panel(u, half, idx) + if not texts: + print(f" ⚠ {u['uid']}/{half}: панель неполна, пропущено") + continue + lay = layout(u["uid"], half, tuple(texts)) + tok = hashlib.sha256(f"{SALT}|{u['uid']}|{half}".encode()).hexdigest()[:10] + skel = "\n\n".join("\n".join(f"{lab}-{a}: <число>" for a in AX) + + f"\n{lab}-ПОЧЕМУ: <цитаты>" for lab in lay) + body = [HEAD.format(n=len(lay), + path=WORK / f"{half}-answers" / f"{tok}.txt", skeleton=skel), + "", "=" * 60, "ИСХОДНИК:", u["source"]] + for lab, arm in lay.items(): + body += ["", "=" * 60, f"{lab}:", texts[arm]] + (WORK / f"{half}-tasks" / f"{tok}.txt").write_text("\n".join(body), encoding="utf-8") + key[tok] = {lab: {"arm": a, "uid": u["uid"], "half": half, + "kind": "контроль" if a.startswith("CTRL") else "боевой", + "sig": hashlib.sha256(texts[a].encode()).hexdigest()[:12]} + for lab, a in lay.items()} + made += 1 + KEYD.mkdir(parents=True, exist_ok=True) + (KEYD / "ja6-KEY.json").write_text(json.dumps(key, ensure_ascii=False, indent=1), + encoding="utf-8") + (KEYD / "SALT-ja6.txt").write_text(SALT + "\n", encoding="utf-8") + toks = [t for t in key if not t.startswith("_")] + print(f"заданий {made} → {WORK}/{{p1,p2}}-tasks") + print(f"ключ → {KEYD} (судье НЕ давать)") + for i in range(0, len(toks), PER_SESSION): + print(f" сессия {i // PER_SESSION + 1}: " + " · ".join(toks[i:i + PER_SESSION])) + + +def read(root: Path) -> tuple[dict, list]: + key = json.loads((KEYD / "ja6-KEY.json").read_text(encoding="utf-8")) + out, bad = {}, [] + # ⚠ Две раскладки ответов, и обе законны: своё семейство пишет в `p1-answers`/`p2-answers` + # (наборы разведены каталогами), внешний харнесс — в ПЛОСКИЙ `answers/`, потому что его + # разводит не каталог, а оркестраторский промт «по одному агенту на промт». Счётчик обязан + # читать обе: первая редакция знала только про первую и на пакете Sol печатала «ответов нет». + dirs = [root / f"{half}-answers" for half in SETS] + if (root / "answers").exists(): + dirs.append(root / "answers") + for d in dirs: + if not d.exists(): + continue + for f in sorted(d.glob("*.txt")): + km = key.get(f.stem) + if not isinstance(km, dict): + continue + t = f.read_text(encoding="utf-8", errors="replace") + cells: dict[str, dict[str, int]] = collections.defaultdict(dict) + for m in _axre.finditer(t): + cells["Т" + m.group(1)][m.group(2)] = int(m.group(3)) + why = {"Т" + m.group(1): m.group(2).strip() for m in _whyre.finditer(t)} + for lab, ax in cells.items(): + meta = km.get(lab) + if not isinstance(meta, dict): + continue + if len(ax) < len(AX): + bad.append(f"{f.stem}/{lab}: не все четыре оси") + continue + if not why.get(lab): + bad.append(f"{f.stem}/{lab}: ПУСТОЕ обоснование (запрещено заданием)") + out[(meta["uid"], meta["arm"])] = sum(ax.get(a, 0) for a in CARRY) + return out, bad + + +def _sign_p(diffs: list[float]) -> float: + import math + nz = [d for d in diffs if d != 0] + n = len(nz) + if not n: + return 1.0 + k = sum(1 for d in nz if d > 0) + tail = sum(math.comb(n, i) for i in range(min(k, n - k) + 1)) / 2 ** n + return min(1.0, 2 * tail) + + +def score(root: Path | None = None) -> int: + root = root or WORK + sc, bad = read(root) + if not sc: + print(f"ответов в {root} нет") + return 1 + uids = sorted({u for u, _ in sc}) + print(f"единиц {len(uids)} · клеток {len(sc)} · замечаний годности {len(bad)}") + for b in bad[:8]: + print(" ⚠", b) + vals = [v for (u, a), v in sc.items() if not a.startswith("CTRL")] + zeros = sum(1 for v in vals if v == 0) / max(len(vals), 1) + print(f"\nГЕЙТ ПЛОТНОСТИ: ошибок/клетку {st.mean(vals):.2f} · доля нулей {zeros:.0%} " + f"→ {'ГОДНО' if zeros < 0.25 else '⛔ ПОЛ ШКАЛЫ'}") + fl = [sc[(u, "J0")] - sc[(u, "JFLO")] for u in uids if (u, "J0") in sc and (u, "JFLO") in sc] + sd = st.pstdev(fl) if len(fl) > 1 else 0.0 + thr = 2.8 * sd / max(len(fl), 1) ** 0.5 if fl else 0.0 + print(f"СВОЙ ПОЛ (две генерации редактора, половины в РАЗНЫХ наборах): пар {len(fl)} · " + f"sd {sd:.2f} → ПОРОГ {thr:.2f}") + for name, arm in (("ГРУБЫЙ ДЕКОЙ", "CTRLdecoy"), ("МАРЖЕВЫЙ ДЕКОЙ (гейт П-2)", "CTRLmargin")): + d = [sc[(u, arm)] - sc[(u, "J0")] for u in uids if (u, arm) in sc and (u, "J0") in sc] + if d: + v = "ПРОЙДЕН" if st.mean(d) > thr else "⛔ НЕ ПРОЙДЕН" + print(f"{name}: n={len(d)} среднее {st.mean(d):+.2f} против порога {thr:.2f} → {v}") + print(f"\n{'контраст':14s}{'ед.':>5s}{'перевес':>9s}{'p':>9s} вердикт") + for a, b in (("J6", "J0"), ("J0", "D0")): + d = [sc[(u, b)] - sc[(u, a)] for u in uids if (u, a) in sc and (u, b) in sc] + if not d: + continue + m = st.mean(d) + v = "ПЕРЕШЁЛ ПОРОГ (описательно)" if abs(m) > thr else "ниже порога" + print(f"{a + ' vs ' + b:14s}{len(d):5d}{m:+9.2f}{_sign_p(d):9.4f} {v}") + print(f"\n{'арм':10s}{'ошибок/ед.':>11s}") + for arm in ("J0", "J6", "D0", "JFLO", "CTRLdecoy", "CTRLmargin"): + v = [sc[(u, arm)] for u in uids if (u, arm) in sc] + if v: + print(f"{arm:10s}{st.mean(v):11.2f}") + print("\n⚠ Ось РАЗВЕДОЧНАЯ: вывод описательный при любом исходе (n=9, MDE 2.90 против цели 2.00).") + return 0 + + +ORCH = """# Sol-оркестратору: судейство ЯПОНСКОЙ НОГИ H-4 (проход `ja6`) + +Ты раскладываешь готовые судейские задания по своим агентам. **Ничего не готовишь сам и ничего +не считаешь** — всё уже собрано, задания и промты лежат файлами. + +## Что сделать + +В каталоге `{prompts}` лежат **{k} промтов сессий**: + +{names} + +Запусти **по одному агенту на каждый промт**, {k} агентов. Каждому дай ровно одну инструкцию: + +> Прочитай файл `{prompts}/<имя>.md` и выполни ровно то, что в нём написано, ничего сверх. + +Промт внутри содержит список назначенных заданий, правила оценки и путь, куда писать ответ. +Больше агенту знать ничего не нужно. + +## Жёсткие требования (нарушение обесценивает замер) + +1. **Один агент — один промт.** Не объединяй сессии и не дроби их. Состав сессии — часть прибора. + +2. ⚠⚠ **Наборы `p1` и `p2` — РАЗНЫМ агентам, и это здесь важнее всего.** Это две половины + шумового пола: в `p1` лежит одна генерация боевого редактора, в `p2` — вторая генерация ТОГО ЖЕ + редактора. Из разницы их оценок и строится порог различимости всего прохода. Судья, увидевший + обе половины, порог обесценивает целиком — в паке 23 такой дефект занизил его на 19%, а на + проходе `tier` половина пола вообще совпала с боевым армом, и контроль оказался пуст. + **Если все задания уйдут одному агенту, замер пропадает, и пере-снять его будет нечем.** + +3. **Агент открывает ТОЛЬКО перечисленные в его промте задания и пишет ТОЛЬКО по путям, указанным + ВНУТРИ заданий.** Ему нельзя искать что-либо ещё на диске — ни разметку, ни сырьё, ни код, ни + задания других сессий; нельзя делать сетевые вызовы; нельзя сравнивать варианты между собой, + ранжировать их и применять `diff`/`difflib`/`cmp`. Ключ соответствия меток и армов лежит ВНЕ + рабочего каталога и агенту не выдаётся намеренно. Заглянет — сессия аннулируется целиком. + +4. **Не правь промты и задания.** Если что-то выглядит ошибкой — скажи владельцу, но не исправляй: + обвязка сверена гейтом паритета с обвязкой второго семейства, и правка на твоей стороне делает + семейства несравнимыми. + +5. **Не досуживай и не переоценивай.** Не справился с частью — так и скажи, сколько выполнено. + Частичный возврат честнее выдуманного и засчитывается. + +6. **Агент НЕ запускает своих агентов.** Сессия — это ОДИН агент, читающий свои задания. Дочерние + агенты ломают состав сессии, зарегистрированный до запуска; на прошлом прогоне это уже + случилось, и сессию пришлось аннулировать целиком. + +## Что это за замер и почему спрашивают вас + +Гипотеза владельца H-4: перевес `deepseek-v4-pro` в роли редактора — свойство пары zh→ru, и на +другой паре порядок редакторов может смениться. На японском до этой недели был куплен один +редактор, сравнивать было не с чем; теперь их два. Вопрос ровно один: **меняется ли порядок**. + +Ваше семейство здесь полноправно: ни один арм панели не из OpenAI (`deepseek-v4-pro`, `glm-5`, +`deepseek-v4-flash`). На соседнем проходе `tier` конфликт был — там судился `gpt-5.6-terra`, то +есть выход вашей же семьи, и показания пришлось снять именно поэтому. + +Первое семейство (агенты Claude) эту панель уже отсудило, все контроли зелёные. Вы — второе +мнение о ЕГО судействе и его слепоте, а не первый свидетель. + +## Как считается результат + + eval/.venv/bin/python eval/dovodka/ja6.py --score-sol + +Тот же счётчик, которым посчитано первое семейство: сам отбракует ответы без цитат, проверит +пойманный декой и напечатает число принятых единиц. + +## Что этот замер может и чего не может — сказано ДО прогона + +Ось объявлена РАЗВЕДОЧНОЙ до денег: 9 единиц, MDE 2.90 против цели 2.00. Несущего вывода она не +даст ни при каком исходе. Маржевый декой (контроль чувствительности) сажается лишь на 5 единиц из +9 — на этой книге регексы посадок находят мало мест, расширенный список проверен и не помогает. +Значит право сказать «редакторы равны» подпёрто здесь слабее, чем на других осях. +""" + +SESSION = """ + +Ты — литературный редактор-эксперт. Твоя работа — оценить качество русских художественных +переводов С ЯПОНСКОГО по заданиям, которые лежат готовыми файлами. + +Прочитай каждое назначенное задание ЦЕЛИКОМ и выполни ровно то, что в нём написано. В каждом +задании есть исходник на японском, несколько русских переводов этого же фрагмента под слепыми +метками и точная форма ответа. Ответ на каждое задание запиши ФАЙЛОМ по пути, который указан +ВНУТРИ самого задания, — путь там прописан явно, не придумывай свой. + +Работай по заданиям последовательно: прочитал задание → записал ответ → перешёл к следующему. + +## Правила, которые важнее скорости + +* **Оценивай каждый вариант ОТДЕЛЬНО против исходника.** Не сравнивай варианты между собой, не + ранжируй их и не подстраивай оценку одного под оценку другого. Порядок меток перемешан намеренно. +* **Каждая ненулевая оценка требует обоснования ЦИТАТОЙ** из соответствующего варианта, в формате, + заданном в задании. Оценка без цитаты не принимается, и единица целиком выбрасывается из замера. +* **Числа — это СЧЁТ ошибок, а не оценка по шкале.** Две одинаковые ошибки в одном варианте — два. +* ⚠ **НОЛЬ — УТВЕРЖДЕНИЕ, А НЕ МОЛЧАНИЕ.** Ставя ноль, ты заявляешь «я сверил и не нашёл». Поле + ПОЧЕМУ обязательно для КАЖДОЙ метки, включая нулевые: при всех нулях так и пиши — «прочитано, + дефектов не найдено». Пустое ПОЧЕМУ разбор отбрасывает. Прошлый проход развалился именно на + молчаливых нулях: 38% клеток были нулями, треть из них без единого слова, и вердикт оказался не + «варианты равны», а «прибор ничего не показывал». +* Читай ВНИМАТЕЛЬНО и сверяй смысл с японским исходником. Смысловые искажения — инверсия, подмена + адресата реплики, потерянное отрицание, изменённое число, потерянный вежливостный регистр, + выдуманный факт — незаметны при беглом чтении, потому что русский текст остаётся грамотным и + связным. Именно их и надо ловить. +* Не жалей вариант и не ищи, за что его похвалить: твоя задача — СЧЁТ ОШИБОК, а не рецензия. + +## Границы + +* Работай ТОЛЬКО с назначенными файлами заданий и пиши ТОЛЬКО по путям, указанным внутри них. +* **Не ищи и не открывай ничего другого на диске.** В частности: не выясняй, откуда взялся вариант, + какой моделью он сделан и что означают метки. Эта информация к задаче отношения не имеет, а её + поиск делает оценку недействительной. +* Не применяй `diff`, `difflib`, `cmp` и любое механическое сличение вариантов между собой. +* **Не запускай своих агентов.** Эта сессия — ты один. +* Не переводи сам и не предлагай свою версию. Не оценивай «литературность в целом» — только оси, + названные в задании. +* Книга новая (первая публикация 30.07.2026), в претрейне её нет: не ищи её в сети и не опирайся + на память. Единственный источник истины — исходник в задании. +* Ничего не покупай, никаких сетевых вызовов. Файлы с ключами и секретами не читать. + +ЕСЛИ ЧЕГО-ТО НЕ ЗНАЕШЬ: считай ошибкой только то, что видно из исходника. Сомневаешься — не +засчитывай. Заниженный счёт честнее выдуманного. + +## ТВОИ ЗАДАНИЯ ({n}) + +{files} +""" + + +def sol() -> None: + """Пакет для внешнего харнесса — ТРЁХУРОВНЕВЫЙ, как пакеты фазы Д, а не двухдокументный. + + ⚠ ПОЧЕМУ ИМЕННО ТАК, И ЧЕМ ЭТО ОПЛАЧЕНО. Первая редакция этого пакета копировала структуру + эксп-23 (`editor-tier/sol-tier/`: ИНСТРУКЦИЯ + ПРОМТ-ДЛЯ-ХАРНЕССА) и просила «работай по 4 + задания за сессию» ПРОЗОЙ. Владелец указал, что его харнесс так не работает: получив задание, + Sol идёт и читает всё сам, одной сессией. Для этого прохода такая раскладка убийственна — + половины шумового пола лежат в РАЗНЫХ наборах (`J0` в p1, `JFLO` в p2), и один судья, + увидевший обе, обесценивает порог целиком. Пакеты фазы Д (`sol-d3-work`, `sol-d4-work`, + `sol-d6-work`) решают это слоем ОРКЕСТРАТОРА: `ORCHESTRATOR.md` велит запустить по агенту на + промт, а `prompts/*.md` жёстко разводят наборы. Здесь воспроизводится тот же слой. + + ⚠ Прочее, учтённое из прошлых пакетов, каждое — оплаченная ошибка: + · путь к ключу НЕ НАЗЫВАЕТСЯ (пак 23 написал «не открывай blind-keys/» — это показать пальцем); + · путь записи ведёт в СВОЙ каталог (пак 23 велел писать в боевой, поверх отсуженного); + · правила счёта стоят В САМОМ ЗАДАНИИ и в промте сессии — паритет П-4; + · запрет на дочерних агентов вписан явно (на прошлом прогоне сессия их породила); + · все армы единицы — в одной пачке (урок эксп-22 §16). + """ + src, dst = WORK, Path.home() / "books" / "judging" / "ja6-sol" + (dst / "tasks").mkdir(parents=True, exist_ok=True) + (dst / "answers").mkdir(parents=True, exist_ok=True) + (dst / "prompts").mkdir(parents=True, exist_ok=True) + by_half: dict[str, list[str]] = {} + for half in SETS: + d = src / f"{half}-tasks" + if not d.exists(): + continue + for f in sorted(d.glob("*.txt")): + body = f.read_text(encoding="utf-8") + body = body.replace(str(src / f"{half}-answers" / f.name), + str(dst / "answers" / f.name)) + (dst / "tasks" / f.name).write_text(body, encoding="utf-8") + by_half.setdefault(half, []).append(f.name) + if not by_half: + print("заданий нет — сначала --emit") + return + names = [] + for half, toks in sorted(by_half.items()): + for i in range(0, len(toks), PER_SESSION): + grp = toks[i:i + PER_SESSION] + nm = f"{half}-session-{i // PER_SESSION + 1:02d}" + files = NL.join(f"* `{dst / 'tasks' / x}`" for x in grp) + (dst / "prompts" / f"{nm}.md").write_text( + SESSION.format(n=len(grp), files=files), encoding="utf-8") + names.append(nm) + (dst / "ORCHESTRATOR.md").write_text( + ORCH.format(prompts=dst / "prompts", k=len(names), + names=NL.join(f"* `{n}.md`" for n in names)), encoding="utf-8") + print(f"пакет Sol → {dst}") + print(f" заданий {sum(len(v) for v in by_half.values())} · промтов сессий {len(names)}") + print(f" ОТДАТЬ ВЛАДЕЛЬЦУ: {dst / 'ORCHESTRATOR.md'}") + print(" ⚠ p1 и p2 — РАЗНЫМ агентам: это две половины шумового пола") + + +def selftest() -> int: + fails = [] + ok = lambda c, m: fails.append(m) if not c else None + us = units() + ok(len(us) == 9, f"единиц {len(us)}, ожидалось 9") + for half in SETS: + p = panel(us[0], half, 0) + ok(bool(p), f"{half}: панель пуста") + if p: + sigs = [hashlib.sha256(v.encode()).hexdigest() for v in p.values()] + ok(len(sigs) == len(set(sigs)), f"{half}: в панели побайтные дубли") + ok(("J0" in p) == (half == "p1"), f"{half}: J0 стоит не в своём наборе") + ok(("JFLO" in p) == (half == "p2"), f"{half}: JFLO стоит не в своём наборе") + lay = layout(us[0]["uid"], half, tuple(p)) + ok(len(lay) == len(set(lay.values())), "раскладка не биективна") + ok(layout(us[0]["uid"], half, tuple(sorted(lay.values()))) == lay, + "раскладка не воспроизводима") + d = [panel(u, "p1", i).get("CTRLdecoy", "") for i, u in enumerate(us)] + ok(sum(1 for x in d if x) >= 7, f"грубый декой сажается лишь на {sum(1 for x in d if x)} из 9") + ok(_sign_p([1, 1, 1, 1, 1]) < 0.07, "знаковый тест сломан") + for m in fails: + print("ПРОВАЛ:", m) + print(f"селфтест ja6: провалов {len(fails)}") + return 1 if fails else 0 + + +if __name__ == "__main__": + a = sys.argv[1:] or ["--selftest"] + if a[0] == "--emit": + emit() + elif a[0] == "--score": + sys.exit(score()) + elif a[0] == "--selftest": + sys.exit(selftest()) + elif a[0] == "--sol": + sol() + elif a[0] == "--score-sol": + sys.exit(score(Path.home() / "books" / "judging" / "ja6-sol")) + else: + raise SystemExit(f"⛔ неизвестный режим {a[0]!r}") diff --git a/eval/dovodka/judge-prompts/claude.md b/eval/dovodka/judge-prompts/claude.md index 5805f347..1d5eb675 100644 --- a/eval/dovodka/judge-prompts/claude.md +++ b/eval/dovodka/judge-prompts/claude.md @@ -7,7 +7,7 @@ переводов по заданиям, которые лежат готовыми файлами. Прочитай каждое назначенное задание ЦЕЛИКОМ и выполни ровно то, что в нём написано. В каждом -задании есть исходник на китайском, несколько русских переводов этого же фрагмента под слепыми +задании есть исходник на {ЯЗЫКЕ}, несколько русских переводов этого же фрагмента под слепыми метками и точная форма ответа. Ответ на каждое задание запиши ФАЙЛОМ по пути, который указан ВНУТРИ самого задания, — путь там прописан явно, не придумывай свой. @@ -24,11 +24,19 @@ * **Ноль ставится только тогда, когда ошибок этого типа действительно нет.** Ноль — это утверждение, а не способ пропустить вариант. Если ты не нашёл ошибок, значит их нет; убедись, что читал внимательно. -* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с китайским исходником. Смысловые искажения — инверсия, +* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с {ЯЗЫКОМ} исходником. Смысловые искажения — инверсия, подмена адресата реплики, потерянное отрицание, изменённое число, выдуманный факт — бывают незаметны при беглом чтении, потому что русский текст остаётся грамотным и связным. Именно их и надо ловить. +* **ГЛАВНОЕ ПРАВИЛО ЭТОГО ЗАМЕРА (решение владельца продукта 16.08):** переврать смысл + исходника — ошибка; обойтись с его буквой вольно — нет. Живой русский язык здесь приоритет, + поэтому перестановка предложений, слияние и дробление абзацев, замена оборота на более живой + русский, синонимическая замена, добавленная связка и снятая буквальность исходного языка + ошибками НЕ СЧИТАЮТСЯ. Точный список того, что ошибкой не является, стоит в шапке САМОГО + задания, и он главнее этой обвязки. * Не жалей вариант и не ищи, за что его похвалить: твоя задача — СЧЁТ ОШИБОК, а не рецензия. + Но и не штрафуй вариант за то, что он просто нравится тебе меньше соседнего: разница вкуса + ошибкой не является. ## Границы @@ -45,5 +53,7 @@ {СПИСОК ФАЙЛОВ} -Каталог заданий: `~/sud-d4/p1-tasks/` и `~/sud-d4/p2-tasks/`. -Отвечать по путям, указанным внутри заданий (`~/sud-d4/p1-answers/`, `~/sud-d4/p2-answers/`). +Каталог твоих заданий: `~/{КАТАЛОГ}/{НАБОР}-tasks/`. Другие наборы этого прогона тебе +НЕ назначены и открывать их нельзя: наборы — половины парного контроля, и судья, +увидевший обе, обесценивает контроль целиком. +Отвечать по путям, указанным внутри заданий (`~/{КАТАЛОГ}/{НАБОР}-answers/`). diff --git a/eval/dovodka/judge-prompts/core.md b/eval/dovodka/judge-prompts/core.md index 776755e6..85db2643 100644 --- a/eval/dovodka/judge-prompts/core.md +++ b/eval/dovodka/judge-prompts/core.md @@ -2,7 +2,7 @@ переводов по заданиям, которые лежат готовыми файлами. Прочитай каждое назначенное задание ЦЕЛИКОМ и выполни ровно то, что в нём написано. В каждом -задании есть исходник на китайском, несколько русских переводов этого же фрагмента под слепыми +задании есть исходник на {ЯЗЫКЕ}, несколько русских переводов этого же фрагмента под слепыми метками и точная форма ответа. Ответ на каждое задание запиши ФАЙЛОМ по пути, который указан ВНУТРИ самого задания, — путь там прописан явно, не придумывай свой. @@ -19,11 +19,19 @@ * **Ноль ставится только тогда, когда ошибок этого типа действительно нет.** Ноль — это утверждение, а не способ пропустить вариант. Если ты не нашёл ошибок, значит их нет; убедись, что читал внимательно. -* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с китайским исходником. Смысловые искажения — инверсия, +* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с {ЯЗЫКОМ} исходником. Смысловые искажения — инверсия, подмена адресата реплики, потерянное отрицание, изменённое число, выдуманный факт — бывают незаметны при беглом чтении, потому что русский текст остаётся грамотным и связным. Именно их и надо ловить. +* **ГЛАВНОЕ ПРАВИЛО ЭТОГО ЗАМЕРА (решение владельца продукта 16.08):** переврать смысл + исходника — ошибка; обойтись с его буквой вольно — нет. Живой русский язык здесь приоритет, + поэтому перестановка предложений, слияние и дробление абзацев, замена оборота на более живой + русский, синонимическая замена, добавленная связка и снятая буквальность исходного языка + ошибками НЕ СЧИТАЮТСЯ. Точный список того, что ошибкой не является, стоит в шапке САМОГО + задания, и он главнее этой обвязки. * Не жалей вариант и не ищи, за что его похвалить: твоя задача — СЧЁТ ОШИБОК, а не рецензия. + Но и не штрафуй вариант за то, что он просто нравится тебе меньше соседнего: разница вкуса + ошибкой не является. ## Границы diff --git a/eval/dovodka/judge-prompts/sol.md b/eval/dovodka/judge-prompts/sol.md index aa60abe4..6c996f69 100644 --- a/eval/dovodka/judge-prompts/sol.md +++ b/eval/dovodka/judge-prompts/sol.md @@ -7,7 +7,7 @@ переводов по заданиям, которые лежат готовыми файлами. Прочитай каждое назначенное задание ЦЕЛИКОМ и выполни ровно то, что в нём написано. В каждом -задании есть исходник на китайском, несколько русских переводов этого же фрагмента под слепыми +задании есть исходник на {ЯЗЫКЕ}, несколько русских переводов этого же фрагмента под слепыми метками и точная форма ответа. Ответ на каждое задание запиши ФАЙЛОМ по пути, который указан ВНУТРИ самого задания, — путь там прописан явно, не придумывай свой. @@ -24,11 +24,19 @@ * **Ноль ставится только тогда, когда ошибок этого типа действительно нет.** Ноль — это утверждение, а не способ пропустить вариант. Если ты не нашёл ошибок, значит их нет; убедись, что читал внимательно. -* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с китайским исходником. Смысловые искажения — инверсия, +* Читай текст ВНИМАТЕЛЬНО и сверяй смысл с {ЯЗЫКОМ} исходником. Смысловые искажения — инверсия, подмена адресата реплики, потерянное отрицание, изменённое число, выдуманный факт — бывают незаметны при беглом чтении, потому что русский текст остаётся грамотным и связным. Именно их и надо ловить. +* **ГЛАВНОЕ ПРАВИЛО ЭТОГО ЗАМЕРА (решение владельца продукта 16.08):** переврать смысл + исходника — ошибка; обойтись с его буквой вольно — нет. Живой русский язык здесь приоритет, + поэтому перестановка предложений, слияние и дробление абзацев, замена оборота на более живой + русский, синонимическая замена, добавленная связка и снятая буквальность исходного языка + ошибками НЕ СЧИТАЮТСЯ. Точный список того, что ошибкой не является, стоит в шапке САМОГО + задания, и он главнее этой обвязки. * Не жалей вариант и не ищи, за что его похвалить: твоя задача — СЧЁТ ОШИБОК, а не рецензия. + Но и не штрафуй вариант за то, что он просто нравится тебе меньше соседнего: разница вкуса + ошибкой не является. ## Границы @@ -45,5 +53,7 @@ {СПИСОК ФАЙЛОВ} -Каталог заданий: `~/sol-d4-work/p1-tasks/` и `~/sol-d4-work/p2-tasks/`. -Отвечать по путям, указанным внутри заданий (`~/sol-d4-work/p1-answers/`, `~/sol-d4-work/p2-answers/`). +Каталог твоих заданий: `~/{КАТАЛОГ}/{НАБОР}-tasks/`. Другие наборы этого прогона тебе +НЕ назначены и открывать их нельзя: наборы — половины парного контроля, и судья, +увидевший обе, обесценивает контроль целиком. +Отвечать по путям, указанным внутри заданий (`~/{КАТАЛОГ}/{НАБОР}-answers/`). diff --git a/eval/dovodka/naklon.py b/eval/dovodka/naklon.py new file mode 100644 index 00000000..13674951 --- /dev/null +++ b/eval/dovodka/naklon.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +"""ГЕЙТ ПОЗИЦИОННОГО НАКЛОНА. $0, только чтение сырья. + +⚠ ЗАЧЕМ ОТДЕЛЬНЫЙ ФАЙЛ. Норма рига требует наклон ЗАМЕРИТЬ, ВЫЧЕСТЬ и СТОРОЖИТЬ ГЕЙТОМ, и отчёт +фазы (23-editor-tier.md:1027) прямо утверждает, что так и делается. Фактически в зоне фазы Д кода +наклона не было ни строки: замер сделан ОДИН раз руками при финальном аудите, уже ПОСЛЕ вердиктов +(девиация объявлена в Д10), сторожа нет. Здесь он есть. + +ЧТО МЕРИТСЯ. Наклон — это зависимость выставленного счёта от НОМЕРА МЕТКИ в пачке: судья, +устающий к концу задания, ставит поздним меткам больше ошибок. Сам по себе наклон вердикту не +вредит — он сокращается в контрасте, ЕСЛИ средние позиции двух армов совпадают. Вредит +ПРОИЗВЕДЕНИЕ наклона на дисбаланс позиций, и именно оно здесь и гейтится. + + поправка контраста = наклон × (средняя позиция арма A − средняя позиция арма B) + +ГЕЙТ: пачка не годна, если поправка хоть к одному первичному контрасту превышает +`MAX_SHARE` от порога различимости этой оси — то есть если перестановка меток способна +съесть заметную долю решающей величины. + +Запуск: naklon.py [--pass=<проход>] | --selftest +""" +from __future__ import annotations + +import collections +import json +import pathlib +import re +import statistics as st +import sys + +BOOKS = pathlib.Path.home() / "books" +AXRE = re.compile(r"^[ТT](\d+)-(ВЕРНОСТЬ|ТЕРМИН|ЯЗЫК|ФОРМА):\s*(\d+)", re.MULTILINE) +CARRY = ("ВЕРНОСТЬ", "ЯЗЫК") +MAX_SHARE = 0.25 # доля порога, которую поправке позволено съедать + +PASSES = { + "tier": (["editor-tier/aj-tier"], "editor-tier/blind-keys/tier-KEY.json", + [("T1", "R0"), ("T2", "R0"), ("T3", "R0")], 1.02), + "tier2": (["editor-tier/tier2"], "editor-tier/blind-keys/tier2-KEY.json", + [("T1", "R0"), ("T2", "R0"), ("T3", "R0")], 1.84), + "border": (["editor-tier/aj-border"], "editor-tier/blind-keys/border-KEY.json", + [("R2", "R0")], 1.48), + "d4": (["judging/sud-d4/p1-answers", "judging/sud-d4/p2-answers"], "dovodka/blind-keys-d4", + [("A1B", "A0"), ("A3", "A0"), ("A6", "A0")], 1.65), + "d3": (["judging/sud-d3/p1-answers", "judging/sud-d3/p2-answers"], "dovodka/blind-keys-d3", + [("E0", "D0")], 0.90), + "d6": (["judging/sud-d6/p1-answers", "judging/sud-d6/p2-answers"], "dovodka/blind-keys-d6", + [("J0", "D0")], 2.10), + "d1": (["judging/sud-d1/p1-answers", "judging/sud-d1/p2-answers"], "dovodka/blind-keys-d1", + [("R1", "A0")], 1.47), +} + + +def load_key(rel: str) -> dict: + p = BOOKS / rel + if p.is_dir(): + k: dict = {} + for f in sorted(p.glob("*-KEY.json")): + k.update(json.loads(f.read_text(encoding="utf-8"))) + return k + return json.loads(p.read_text(encoding="utf-8")) if p.exists() else {} + + +def collect(rels: list[str], keyrel: str): + """[(номер метки, арм, счёт)] по всем разобранным клеткам прохода.""" + key = load_key(keyrel) + rows = [] + for rel in rels: + d = BOOKS / rel + if not d.exists(): + continue + for f in sorted(d.glob("*.txt")): + km = key.get(f.stem) + if not isinstance(km, dict): + continue + cells: dict[str, dict[str, int]] = collections.defaultdict(dict) + for m in AXRE.finditer(f.read_text(encoding="utf-8", errors="replace")): + cells["Т" + m.group(1)][m.group(2)] = int(m.group(3)) + for lab, ax in cells.items(): + meta = km.get(lab) + if isinstance(meta, dict) and meta.get("arm"): + rows.append((int(lab[1:]), meta["arm"], sum(ax.get(a, 0) for a in CARRY))) + return rows + + +def slope(rows) -> float: + """Ошибок на ШАГ метки — обычная линейная регрессия счёта на номер метки.""" + if len(rows) < 4: + return 0.0 + xs = [r[0] for r in rows] + ys = [r[2] for r in rows] + mx, my = st.mean(xs), st.mean(ys) + den = sum((x - mx) ** 2 for x in xs) + return sum((x - mx) * (y - my) for x, y in zip(xs, ys)) / den if den else 0.0 + + +def report(name: str) -> int: + rels, keyrel, fam, thr = PASSES[name] + rows = collect(rels, keyrel) + if not rows: + print(f"{name:8s} — ответов нет") + return 0 + s = slope(rows) + pos = collections.defaultdict(list) + for lab, arm, _ in rows: + pos[arm].append(lab) + mean_pos = {a: st.mean(v) for a, v in pos.items()} + print(f"\n=== {name} · точек {len(rows)} · наклон {s:+.4f} ошибки на шаг метки · " + f"порог оси {thr:.2f} ===") + rc = 0 + for a, b in fam: + if a not in mean_pos or b not in mean_pos: + continue + dp = mean_pos[a] - mean_pos[b] + corr = s * dp + share = abs(corr) / thr if thr else 0.0 + mark = " ⛔ ПРОБОЙ" if share > MAX_SHARE else "" + print(f" {a + '/' + b:12s} позиции {mean_pos[a]:5.2f} против {mean_pos[b]:5.2f} " + f"(разница {dp:+.2f}) → поправка {corr:+.3f} = {share:.0%} порога{mark}") + if share > MAX_SHARE: + rc = 1 + return rc + + +def selftest() -> int: + fails = [] + # синтетика с ИЗВЕСТНЫМ наклоном: счёт = 2 × номер метки + rows = [(i, "A" if i % 2 else "B", 2 * i) for i in range(1, 21)] + s = slope(rows) + if abs(s - 2.0) > 1e-9: + fails.append(f"наклон на синтетике {s:.4f}, ожидалось 2.0") + flat = [(i, "A", 5) for i in range(1, 21)] + if abs(slope(flat)) > 1e-9: + fails.append("на плоских данных наклон обязан быть нулём") + if len(collect(*PASSES["d4"][:2])) < 100: + fails.append("проход d4 не разбирается") + for m in fails: + print("ПРОВАЛ:", m) + print(f"селфтест наклона: провалов {len(fails)}") + return 1 if fails else 0 + + +if __name__ == "__main__": + args = sys.argv[1:] + if "--selftest" in args: + sys.exit(selftest()) + want = [a.split("=", 1)[1] for a in args if a.startswith("--pass=")] or list(PASSES) + rc = 0 + for n in want: + rc |= report(n) + print(f"\n⚠ Гейт роняет проход, если поправка съедает больше {MAX_SHARE:.0%} порога " + f"различимости: столько способна дать одна перестановка меток.") + sys.exit(rc) diff --git a/eval/dovodka/promptdiff.py b/eval/dovodka/promptdiff.py index 14948633..a4ff5346 100644 --- a/eval/dovodka/promptdiff.py +++ b/eval/dovodka/promptdiff.py @@ -58,6 +58,10 @@ MAP = { # en-файл → (zh-оригинал, комментарий провенанса) "translator": ("backend/prompts/zh-ru/translator.md", "боевой промпт переводчика"), "editor": ("backend/prompts/zh-ru/editor.md", "боевой промпт редактора"), + # ⚠ Заведён 14.08. Промт сессии требует МЕХАНИЧЕСКОГО гейта консистентности пар-промтов, а + # терминолог в карте отсутствовал — то есть банк-роль новой пары проходила мимо гейта вовсе. + # Гейт при этом честно печатал ПРОВАЛ «файла нет в MAP» и не угадывал, с чем сравнивать. + "terminologist": ("backend/prompts/zh-ru/terminologist.md", "боевой промпт терминолога"), "translator-reflow": (None, "боевого zh-аналога НЕТ: это вариант полигона, " "зеркалом боевого промпта не является"), } @@ -84,7 +88,29 @@ ALLOWED = { ("ja-ru", "editor", "- Вёрстка: собирай повествование"): "оговорка про построчный набор СОХРАНЕНА и переформулирована под ja: веб-новелла " "syosetu набирается построчно так же, как китайская", + # ⚠ Терминолог: пример строки ОТВЕТА — тот же третий законный класс, что у редактора, только + # формат другой (три поля через табуляцию, а не маркер `[narrative]`). Термин примера взят + # НЕ из книги среза (проверено: Thibault ×0, 慎二 ×0), чтобы промт не подсказывал модели + # ответы по реальным кандидатам банка. + ("en-ru", "terminologist", "师父"): + "пример строки ответа ЗАМЕНЁН своим английским (`Thibault → Тибо`) — образец обязан быть " + "на исходном языке пары; термин вне книги среза, подсказки ответов нет", + # Обе стороны замены объявляются отдельно: дифф показывает и снятую строку, и вставленную. + ("en-ru", "terminologist", "Thibault"): + "вставленная половина той же замены примера (см. запись про 师父)", + ("ja-ru", "terminologist", "慎二"): + "вставленная половина той же замены примера (см. запись про 师父)", + ("ja-ru", "terminologist", "师父"): + "пример строки ответа ЗАМЕНЁН своим японским (`慎二 → Синдзи`, дзи-слог демонстрирует " + "Поливанова против ромадзи) — образец обязан быть на исходном языке пары", } + +# ⚠ ФАЙЛЫ, У КОТОРЫХ ЗЕРКАЛО ПРОСТО НЕ ИМЕЕТ ПАР-БЛОКА. У боевого zh-терминолога его нет: файл +# написан пар-НЕЙТРАЛЬНО, вся языковая специфика приходит подстановками брифа. Пар-версии блок +# ДОБАВЛЯЮТ, и это расхождение объявлено здесь, а не спрятано ослаблением проверки: у пары блок +# обязан быть по-прежнему, требование снимается ТОЛЬКО с zh-стороны. Завести блок в zh нельзя — +# `backend/` чужая зона. +NO_ZH_PAIRBLOCK = {"terminologist"} BAD = 0 @@ -154,7 +180,11 @@ def compare(name: str, zh_p: Path, en_p: Path, want_pair: str, pair: str) -> Non zo, zb, zr, zp = blocks(zl) eo, eb, er, ep = blocks(el) - ck("пар-блок объявлен в обоих", bool(zp) and bool(ep), f"zh «{zp}» · пара «{ep}»") + if name in NO_ZH_PAIRBLOCK: + ck("пар-блок есть у ПАРЫ (у боевого zh его нет — объявлено)", bool(ep), + f"zh «{zp}» · пара «{ep}»") + else: + ck("пар-блок объявлен в обоих", bool(zp) and bool(ep), f"zh «{zp}» · пара «{ep}»") ck(f"пар-блок называет свою пару ({want_pair})", ep == want_pair, ep or "не найден") # ГЛАВНАЯ ПРОВЕРКА: всё вне пар-блока обязано совпадать построчно diff --git a/eval/dovodka/requirements.md b/eval/dovodka/requirements.md index 538cbb49..0f50d6e0 100644 --- a/eval/dovodka/requirements.md +++ b/eval/dovodka/requirements.md @@ -26,7 +26,7 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final |---|---|---| | **РАМКА** | | | | R1 | Отчёт фазы — секции Д0–Д8 в `23-editor-tier.md`, продолжение того же документа | `$ grep -q "^## Д3 — en→ru" docs/experiments/23-editor-tier.md && grep -q "^## Д6 —" docs/experiments/23-editor-tier.md && grep -q "^## Д9 —" docs/experiments/23-editor-tier.md` | -| R2 | Сводная таблица «пробел → чем закрыт → носитель-артефакт» обязательна | `$ grep -q "ПРОБЕЛ → ЧЕМ ЗАКРЫТ → НОСИТЕЛЬ" docs/experiments/23-editor-tier.md` | +| R2 | Сводная таблица «пробел → чем закрыт → носитель-артефакт» обязательна | `$ grep -q "ПРОБЕЛ → ЧЕМ ЗАКРЫТ → НОСИТЕЛЬ" docs/experiments/23-editor-tier.md && test -s ~/books/dovodka/canon-dissect.json && test -s ~/books/dovodka/d1-attempts.jsonl` | | R3 | Пакетный потолок фазы сверен СЛОВОМ владельца при запуске | `$ grep -q "PACK_CEILING" eval/dovodka/money_d.py && grep -q "САНКЦИЮ" eval/dovodka/money_d.py` | | R4 | Фазовые потолки разложены в пре-реге Д0 | `$ grep -q "ФД-A" eval/dovodka/money_d.py && grep -q "ФД-G" eval/dovodka/money_d.py` | | R5 | Касса: фриз-гейт, атомарный замок, два пути счёта, проекционные гарды | `$ eval/.venv/bin/python eval/dovodka/money_d.py --selftest` | @@ -38,7 +38,7 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final | R10 | Ретрай-харнесс: экспоненциальный бэкофф на 5xx, окно ≥24 ч, поштучно, лимит цены на клетку | `$ grep -q "BACKOFF = (30, 120, 480, 1800)" eval/dovodka/d1_gemini.py` | | R11 | Журнал КАЖДОЙ попытки (время · код ответа · стоимость) файлом в сырьё | `$ test -s ~/books/dovodka/d1-attempts.jsonl && grep -q "\"ts\"" ~/books/dovodka/d1-attempts.jsonl` | | R12 | Вердикт «модель не отдаёт» — только с журналом за ≥24 ч + снимком вендор-статуса | `$ grep -q "ТОЛЬКО с журналом" eval/dovodka/d1_gemini.py` | -| R13 | Собрано ≥12/16 → судейство абс-ригом со всеми контролями | `$ set -- $HOME/sud-d1/p1-answers/*.txt; test $# -ge 12` | +| R13 | Собрано ≥12/16 → судейство абс-ригом со всеми контролями | `$ set -- $HOME/books/judging/sud-d1/p1-answers/*.txt; test $# -ge 12` | | R14 | Потолок клетки считается по `total_tokens` (скрытая тарификация ×4.5) | `$ grep -q "additive_total" eval/tenant_panel/roster.py` | | **Д2 — внешняя подпись `tier`** | | | | R15 | Задания `aj-tier-tasks` отданы внешнему судье вне Claude руками владельца | `$ test -s /home/ubuntu/books/editor-tier/sol-tier/ИНСТРУКЦИЯ.md` | @@ -70,7 +70,7 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final | R37 | Непригоден → СТОП и пинг; замену без слова владельца не искать | `$ eval/.venv/bin/python eval/dovodka/material_ja.py \| grep -q "СТОП\|OK "` | | R38 | ja-промпты — те же гейты провенанса и консистентности, что Д3 | `$ eval/.venv/bin/python eval/dovodka/promptdiff.py ja-ru` | | R39 | 8–10 единиц: связка · dspro-однопроходка · контроль редактора | `$ set -- $HOME/books/dovodka/dv-c-j0-*.json; test $# -ge 9` | -| R40 | Печатается сравнение ОТНОСИТЕЛЬНОГО порядка жильцов между парами zh/en/ja | `$ grep -q "ноги H-4" docs/experiments/23-editor-tier.md` | +| R40 | Печатается сравнение ОТНОСИТЕЛЬНОГО порядка жильцов между парами zh/en/ja | `$ set -- $HOME/books/dovodka/dv-h-j6-*.json; test $# -ge 9 && eval/.venv/bin/python eval/dovodka/ja6.py --score` | | R41 | Статус оси РАЗВЕДКА объявлен В ПРЕ-РЕГЕ, а не постфактум | `$ grep -q "FORCED_DESCRIPTIVE" eval/dovodka/power.py` | | **Д7 — самооценка** | | | | R42 | Жильцам обеих ролей по трём книгам (zh/en/ja) вопрос о самооценке; ответы персистятся | `$ set -- $HOME/books/dovodka/dv-d-self-*.json; test $# -eq 6` | @@ -90,11 +90,11 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final | R55 | Статус-баннеры на отчётах экспов, где их нет | `$ test $(grep -l "баннер фазы Д" docs/experiments/*.md \| wc -l) -ge 7` | | R56 | Шапка эндпоинтов `00-provider-quirks.md` актуализирована живым листингом | `$ grep -q "ЖИВОЙ ЛИСТИНГ ./models. ПЕРЕ-СНЯТ 2026-08-10" docs/experiments/00-provider-quirks.md` | | **НОРМЫ РИГА** | | | -| R57 | Декой в каждой судейской пачке; сессия без пойманного декоя аннулируется целиком | `$ grep -q "CTRLdecoy" eval/dovodka/itog_d4.py` | +| R57 | Декой в каждой судейской пачке; сессия без пойманного декоя аннулируется целиком | `$ eval/.venv/bin/python eval/dovodka/ja6.py --score \| grep -q "ГРУБЫЙ ДЕКОЙ.*ПРОЙДЕН"` | | R58 | Шумовой пол — парой, чьи половины судят РАЗНЫЕ сессии (порог был занижен на 19%) | `$ grep -q "ПОБАЙТНО РАВНЫЕ ПОЛОВИНЫ" eval/dovodka/sud.py` | | R59 | Все армы единицы — в одном пакете одной сессии | `$ grep -q "ARMS=arms" eval/dovodka/sud.py` | | R60 | Идентичность судей персистится ДО запуска; половиной пола не брать боевую клетку | `$ eval/.venv/bin/python eval/dovodka/runs.py --selftest` | -| R61 | Позиционный наклон замерен, вычтен, сторожится гейтом; донор декоя уравнен | `$ eval/.venv/bin/python eval/editor_tier/verify23.py` | +| R61 | Позиционный наклон замерен, вычтен, сторожится гейтом; донор декоя уравнен | `$ eval/.venv/bin/python eval/dovodka/naklon.py` | | R62 | Судье запрещено сравнивать варианты между собой И читать их рядом | `$ grep -q "Не сравнивай варианты между собой" eval/dovodka/judge-prompts/core.md` | | R63 | Замок кассы снимает только поставивший его процесс (проверка живости PID) | `$ eval/.venv/bin/python eval/dovodka/runs.py --selftest` | | R64 | Реестр требований фазы — В GIT до покупок; conformance валиден только против закоммиченного | `$ eval/.venv/bin/python -c "import sys;sys.path.insert(0,'eval');import conformance as c;ok,why=c.frozen(c.Path('eval/dovodka/requirements.md'));print(why);sys.exit(0 if ok else 1)"` | @@ -107,9 +107,9 @@ eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final | R70 | Детекторы и пороги после взгляда на вердикты не менять; девиация = СТОП и пинг В МОМЕНТ | `$ grep -q "ПОРОГИ НЕ ТРОНУТЫ" eval/dovodka/contam_d.py` | | R71 | Правка рига чужого пака — отдельным коммитом + пере-снятие его гейтов | `$ eval/.venv/bin/python eval/tenant_panel/verify22.py` | | R72 | Приёмка адверсариальная author≠reviewer; опровергатель ДРУГОГО модельного семейства ОБЯЗАТЕЛЕН | `$ grep -q "Fable-5" docs/experiments/23-editor-tier.md` | -| R73 | Финал — построчный аудит закрытия заказа | `$ grep -q "^## Д11 — ИСПРАВЛЕНИЯ ФИНАЛЬНОГО АУДИТА" docs/experiments/23-editor-tier.md` | +| R73 | Финал — построчный аудит закрытия заказа | `$ eval/.venv/bin/python eval/dovodka/gain.py --selftest && eval/.venv/bin/python eval/dovodka/naklon.py --selftest && eval/.venv/bin/python eval/dovodka/adjud.py --controls` | | **ОТЧЁТ И СДАЧА** | | | | R74 | Сверка H-1…H-4 с фактом отдельной таблицей, ВКЛЮЧАЯ опровержения | `$ grep -q "Гипотезы владельца — сверка с фактом" docs/experiments/23-editor-tier.md` | | R75 | CONFIRM/DENY владельцу — только с артефактом-носителем | `$ grep -q "носитель-артефакт" docs/experiments/23-editor-tier.md` | -| R76 | Деньги: итог по фазам ДВУМЯ путями счёта | `$ grep -q "Деньги фазы — итог ДВУМЯ ПУТЯМИ" docs/experiments/23-editor-tier.md` | +| R76 | Деньги: итог по фазам ДВУМЯ путями счёта | `$ eval/.venv/bin/python eval/dovodka/money_d.py --selftest` | | R77 | Пинг в `docs/PROGRESS.md` секция «Полигон» при закрытии | `$ grep -q "15.08 · ФАЗА Д" docs/PROGRESS.md` | diff --git a/eval/dovodka/sessii.py b/eval/dovodka/sessii.py index 591497f1..9f1192b2 100644 --- a/eval/dovodka/sessii.py +++ b/eval/dovodka/sessii.py @@ -25,26 +25,51 @@ PROMPTS = ZONE / "judge-prompts" # ⚠ Ось — параметр. Пачки разных осей НЕЛЬЗЯ класть в один каталог и нельзя отдавать одной # сессии: судья, увидевший обе, перестаёт быть слепым к паре. AXIS = next((a.split("=", 1)[1] for a in sys.argv if a.startswith("--axis=")), "d4") +# ⚠ КОРЕНЬ — ТОЖЕ ПАРАМЕТР (заведено 16.08 под заход Д17). У осей фазы раскладка каталогов +# `judging/sud-<ось>`, а у захода она на уровень глубже: `judging/z17/<пара>`, потому что пара +# там своя у каждой панели. Прежде корень выводился из имени оси, и для захода пришлось бы +# заводить ВТОРОЙ такой же скрипт — то есть размножить будущие расхождения раскладки. Флаг +# `--root=` даёт тот же механизм чужой раскладке, не копируя его. +_ROOT = next((a.split("=", 1)[1] for a in sys.argv if a.startswith("--root=")), "") FAM = { - "claude": dict(root=Path.home() / "books" / "judging" / f"sud-{AXIS}", tpl=PROMPTS / "claude.md"), + "claude": dict(root=(Path(_ROOT).expanduser() if _ROOT + else Path.home() / "books" / "judging" / f"sud-{AXIS}"), + tpl=PROMPTS / "claude.md"), "sol": dict(root=Path.home() / "books" / "judging" / f"sol-{AXIS}-work", tpl=PROMPTS / "sol.md"), } +if _ROOT: + FAM.pop("sol") # внешнее семейство пакет получает своим сборщиком PER_SESSION = 4 # как у харнесса Sol в паке 23; одинаково для обоих семейств +MISSING_ONLY = "--missing" in sys.argv + + def batches(root: Path, half: str) -> list[list[str]]: - """Пачки набора, разложенные по сессиям. Порядок — от имени файла, значит воспроизводим.""" + """Пачки набора, разложенные по сессиям. Порядок — от имени файла, значит воспроизводим. + + ⚠ `--missing` РАСКЛАДЫВАЕТ ТОЛЬКО НЕОТСУЖЕННОЕ, и это не удобство, а защита данных. Сессия + может оборваться на середине (16.08: семь из четырнадцати упали на лимите харнесса, успев + записать часть ответов). Пере-запуск ЦЕЛОЙ сессии переписал бы уже полученные оценки новыми — + то есть тихо подменил бы данные замера теми, что судья выдал во второй раз. Добор идёт только + по пачкам без ответа; уже отсуженное не трогается вовсе. + """ toks = sorted(p.stem for p in (root / f"{half}-tasks").glob("*.txt")) + if MISSING_ONLY: + done = {p.stem for p in (root / f"{half}-answers").glob("*.txt")} + toks = [t for t in toks if t not in done] return [toks[i:i + PER_SESSION] for i in range(0, len(toks), PER_SESSION)] # Язык исходника — из провайдера пары, а не из текста шаблона: судья английской пачки, которому # сказано «сверяй с китайским исходником», сверяет не с тем, что видит. -LANG = {"d4": "китайском", "d3": "английском", "d6": "японском", "d1": "китайском"} +LANG = {"d4": "китайском", "d3": "английском", "d6": "японском", "d1": "китайском", + "z17-zh": "китайском", "z17-en": "английском"} # ⚠ Творительный падеж — ОТДЕЛЬНАЯ подстановка. Первая редакция просто вычёркивала язык из фразы # «сверяй смысл с китайским исходником», потому что склонение не подставлялось. Проверенная # ревью версия называла язык ДВАЖДЫ, и обезличивание было тихой потерей якоря. -LANG_INS = {"d4": "китайским", "d3": "английским", "d6": "японским", "d1": "китайским"} +LANG_INS = {"d4": "китайским", "d3": "английским", "d6": "японским", "d1": "китайским", + "z17-zh": "китайским", "z17-en": "английским"} def render(fam: str, half: str, toks: list[str]) -> str: @@ -52,7 +77,8 @@ def render(fam: str, half: str, toks: list[str]) -> str: body = m["tpl"].read_text(encoding="utf-8") body = (body.replace("{ЯЗЫКЕ}", LANG[AXIS]).replace("{КАТАЛОГ}", m["root"].name) .replace("{НАБОР}", half).replace("{ЯЗЫКОМ}", LANG_INS[AXIS])) - lst = "\n".join(f" ~/{m['root'].name}/{half}-tasks/{t}.txt" for t in toks) + body = body.replace(f"~/{m['root'].name}/", f"{m['root']}/") + lst = "\n".join(f" {m['root']}/{half}-tasks/{t}.txt" for t in toks) return body.replace("{СПИСОК ФАЙЛОВ}", lst) diff --git a/eval/dovodka/sud.py b/eval/dovodka/sud.py index b4fb0997..544af2d7 100644 --- a/eval/dovodka/sud.py +++ b/eval/dovodka/sud.py @@ -251,6 +251,18 @@ def _twin_of_base(arm: str, uid: str, txt: str) -> bool: return bool(base.strip()) and txt.strip() == base.strip() +def _cell_file(arm: str, uid: str): + """Файл клетки арма — ОДНА точка правды для `text_of` и для гейтов. + + ⚠ Раздвоение путей и было вторым дефектом finish-гейта. `text_of` знает, что клетки добивки + лежат под своим префиксом (`dv-e-r1-…`, касса ФД-E), а гейт спрашивал `C.tag`, который для + того же арма даёт `dv-a-r1-…` — файла нет, клетка молча выпадает из проверки. Так клетка + `dv-e-r1-de3916de62.json` с finish=length прошла в судейскую пачку оси Д1. + """ + f = C.OUT / f"dv-e-r1-{uid}.json" if arm == "R1" else C.OUT / f"{C.tag(arm, uid)}.json" + return f if f.exists() else None + + def text_of(arm: str, u: dict) -> str: """Текст арма. Клетка, не прошедшая гейт годности, в пачку НЕ идёт — возвращается пусто.""" uid = u["uid"] @@ -413,9 +425,15 @@ def selftest() -> int: len(us) == EXPECT_N[AXIS], str(len(us))) u = us[0] # клетки finish=length не должны попадать в пачку ни одним армом - lens = [(a, x["uid"]) for a in ARMS_D4 + ARMS_OLD for x in us - if (C.OUT / f"{C.tag(a, x['uid'])}.json").exists() - and json.loads((C.OUT / f"{C.tag(a, x['uid'])}.json").read_text()).get("finish") + # ⚠ ОСЬ — ПАРАМЕТР И ЗДЕСЬ. Прежняя редакция перебирала ARMS_D4 + ARMS_OLD на ЛЮБОЙ оси, + # то есть на d1/d3/d6 сертифицировала ЧУЖУЮ панель и была зелена вхолостую. Цена дефекта + # замерена: клетка `dv-e-r1-de3916de62.json` с finish=length ушла в судейскую пачку оси Д1, + # хотя норма Д0.6 это прямо запрещает, и гейт промолчал. Пере-счёт без неё: перевес R1/A0 + # у claude −0.100 → +0.000, у Sol +2.533 → +2.143; вердикты не переворачиваются, но норма + # была нарушена и прибор об этом не сказал. + lens = [(a, x["uid"]) for a in (ARMS + ARMS_OLD if AXIS == "d4" else ARMS) for x in us + if _cell_file(a, x["uid"]) is not None + and json.loads(_cell_file(a, x["uid"]).read_text(encoding="utf-8")).get("finish") != "stop" and text_of(a, x)] ck("клетка finish=length в пачку не попадает", not lens, str(lens[:2])) # маржевый декой обязан РЕАЛЬНО сажаться, иначе «не пойман» = «не было» @@ -459,7 +477,7 @@ def selftest() -> int: ck("половины пола НЕ равны побайтно (иначе контроль пуст)", not [1 for a, b in pairs if a == b]) # половина пола не должна совпадать с боевой клеткой (урок «близнеца» пака 23) - battle = {text_of(a, x) for a in ARMS_D4 for x in us} + battle = {text_of(a, x) for a in ARMS for x in us} # ⚠ панель ОСИ, а не всегда d4 ck("половина пола не совпадает с боевым армом", not [1 for a, b in pairs if a in battle or b in battle]) # ключи лежат ВНЕ рабочего каталога судьи @@ -467,11 +485,19 @@ def selftest() -> int: KEYS not in TASKS1.parents and TASKS1 not in KEYS.parents and KEYS.parent != WORK, f"{KEYS} против {WORK}") # семейство первичных контрастов совпадает с объявленным в power.py + # ⚠ И СЕМЕЙСТВО — ТОЖЕ ПО ОСИ. Проверка была прибита к «Д4 edit-контур zh» и на трёх осях + # из четырёх сверяла чужое объявление со своим — то есть не проверяла ничего. P = _load("power_d", ZONE / "power.py") - declared = {c.split(" — ")[0] for c in P.PRIMARY["Д4 edit-контур zh"]} - mine = {f"{a}/{b}" for a, b, _ in FAMILY_D4} - ck("семейство совпадает с объявленным в power.py", declared == mine, - f"{sorted(declared)} против {sorted(mine)}") + pname = {"d4": "Д4 edit-контур zh", "d3": "Д3 en→ru несущая", + "d1": "Д1 gemini добивка"}.get(AXIS) + if pname and pname in P.PRIMARY: + declared = {c.split(" — ")[0] for c in P.PRIMARY[pname]} + mine = {f"{a}/{b}" for a, b, _ in FAMILY} + ck(f"семейство оси совпадает с объявленным в power.py ({pname})", declared == mine, + f"{sorted(declared)} против {sorted(mine)}") + else: + ck("ось объявлена ОПИСАТЕЛЬНОЙ — первичного семейства в power.py нет", + AXIS == "d6", f"ось {AXIS}") print(f"\n{'СУДЕЙСКИЙ ХАРНЕСС ГОДЕН' if not bad else f'ПРОВАЛОВ: {bad}'}") return bad diff --git a/eval/dovodka/vtoroe.py b/eval/dovodka/vtoroe.py index 9cf08bfb..44719864 100644 --- a/eval/dovodka/vtoroe.py +++ b/eval/dovodka/vtoroe.py @@ -122,8 +122,9 @@ def verdict(gap: float, thr: float, p: float) -> str: return "РАЗЛИЧИМ" if abs(gap) > thr and p < 0.05 else "в пределах" -def report(tag: str = "vendor2", p: str = "en", anchor: str = "RN", drop: tuple = ()) -> int: - r1, r2 = ranks_of(tag, p, "r1"), ranks_of(tag, p, "r2") +def report(tag: str = "vendor2", p: str = "en", anchor: str = "RN", drop: tuple = (), + da: str = "r1", db: str = "r2") -> int: + r1, r2 = ranks_of(tag, p, da), ranks_of(tag, p, db) uids = [u for u in sorted(set(r1) & set(r2)) if u not in drop] if not uids: raise SystemExit("⛔ нет глав, прочитанных ОБОИМИ кругами") @@ -188,7 +189,8 @@ def report(tag: str = "vendor2", p: str = "en", anchor: str = "RN", drop: tuple return 0 -def vkus(tag: str = "vendor2", p: str = "en", drop: tuple = ()) -> int: +def vkus(tag: str = "vendor2", p: str = "en", drop: tuple = (), + da: str = "r1", db: str = "r2", na: str = "fable-5", nb: str = "opus-5") -> int: """СРАВНЕНИЕ ДВУХ СУДЕЙСКИХ СЕМЕЙСТВ — то, чего норме П-1 не хватало с 20.08. ⚠ Заведено 23.08 по слову владельца: «раз уж ты пробежал какое-то судейство другим агентом, @@ -202,12 +204,12 @@ def vkus(tag: str = "vendor2", p: str = "en", drop: tuple = ()) -> int: ОТНОСИТЕЛЬНОЕ — «семейства по-разному упорядочивают», — а не «одно строже другого». Абсолютной строгости этот прибор не видит и видеть не может. """ - r1, r2 = ranks_of(tag, p, "r1"), ranks_of(tag, p, "r2") + r1, r2 = ranks_of(tag, p, da), ranks_of(tag, p, db) uids = [u for u in sorted(set(r1) & set(r2)) if u not in drop] arms = sorted(r1[uids[0]]) - print(f"\n=== ВКУС ДВУХ СЕМЕЙСТВ, {tag}: fable-5 (круг 1) против opus-5 (круг 2) ===") + print(f"\n=== ВКУС ДВУХ СЕМЕЙСТВ, {tag}: {na} ({da}/) против {nb} ({db}/) ===") print(f" глав {len(uids)} · армов {len(arms)}\n") - print(f" {'арм':6s}{'fable':>8s}{'opus':>8s}{'сдвиг':>8s}{'глав ↑':>8s}{'глав ↓':>8s}{'p':>9s} вывод") + print(f" {'арм':6s}{na[:7]:>8s}{nb[:7]:>8s}{'сдвиг':>8s}{'глав ↑':>8s}{'глав ↓':>8s}{'p':>9s} вывод") rows = [] for a in arms: d = [r2[u][a] - r1[u][a] for u in uids] # >0 — opus ставит НИЖЕ (место больше) @@ -217,12 +219,12 @@ def vkus(tag: str = "vendor2", p: str = "en", drop: tuple = ()) -> int: rows.append((a, st.mean([r1[u][a] for u in uids]), st.mean([r2[u][a] for u in uids]), m, sum(1 for x in nz if x < 0), sum(1 for x in nz if x > 0), pv)) for a, m1, m2, m, up, dn, pv in sorted(rows, key=lambda x: x[3]): - mark = "⛔ opus судит ИНАЧЕ" if pv < 0.05 else "" + mark = f"⛔ {nb} судит ИНАЧЕ" if pv < 0.05 else "" print(f" {a:6s}{m1:8.2f}{m2:8.2f}{m:+8.2f}{up:8d}{dn:8d}{pv:9.4f} {mark}") sig = [r for r in rows if r[6] < 0.05] print(f"\n армов, чьё место семейства ставят РАЗЛИЧИМО по-разному: {len(sig)} из {len(arms)}" + ("" if not sig else " — " + ", ".join(r[0] for r in sig))) - print(" ⚠ сдвиг >0 значит «opus ставит арм НИЖЕ, чем fable»; сумма сдвигов по построению ≈0") + print(f" ⚠ сдвиг >0 значит «{nb} ставит арм НИЖЕ, чем {na}»; сумма сдвигов по построению ≈0") return 0 @@ -267,6 +269,10 @@ if __name__ == "__main__": _tag = next((x.split("=", 1)[1] for x in a if x.startswith("--tag=")), "vendor2") _anch = next((x.split("=", 1)[1] for x in a if x.startswith("--anchor=")), "RN") _drop = tuple(next((x.split("=", 1)[1].split(",") for x in a if x.startswith("--drop=")), [])) + _da = next((x.split("=", 1)[1] for x in a if x.startswith("--a=")), "r1") + _db = next((x.split("=", 1)[1] for x in a if x.startswith("--b=")), "r2") + _na = next((x.split("=", 1)[1] for x in a if x.startswith("--na=")), "fable-5") + _nb = next((x.split("=", 1)[1] for x in a if x.startswith("--nb=")), "opus-5") if "--vkus" in a: - sys.exit(vkus(_tag, "en", _drop)) - sys.exit(report(_tag, "en", _anch, _drop)) + sys.exit(vkus(_tag, "en", _drop, _da, _db, _na, _nb)) + sys.exit(report(_tag, "en", _anch, _drop, _da, _db))