From 21e7099133feee8b4dd574a92566bde9695919fa Mon Sep 17 00:00:00 2001 From: "Claude (backend session)" Date: Sun, 26 Jul 2026 22:05:20 +0300 Subject: [PATCH] Accept D39.51 measurements as D39.52: format example fixes the language slip and joins the build, P1 consistency fails to separate --- docs/PROGRESS.md | 4 +- docs/architecture/05-decisions-log.md | 4 + .../reports/POLYGON_PREMEASURE_2026-07-26.md | 188 +++++++++++++++++- eval/pkg7/p1_consistency.py | 112 +++++++++++ 4 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 eval/pkg7/p1_consistency.py diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 554b6013..31b41e19 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1,6 +1,6 @@ # Журнал прогресса -> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-07-25, пак-17 закрыт, право по ToS подписано, пак-18 принят — долгов контракта нет, D39.26–D39.31). **Источник истины по РЕШЕНИЯМ — `architecture/05-decisions-log.md` (D1–D39.51); этот файл — ЖУРНАЛ.** +> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-07-25, пак-17 закрыт, право по ToS подписано, пак-18 принят — долгов контракта нет, D39.26–D39.31). **Источник истины по РЕШЕНИЯМ — `architecture/05-decisions-log.md` (D1–D39.52); этот файл — ЖУРНАЛ.** > - **Фазы/курс:** Ф0 ✅ · Ф1-инфра ✅ (D20–D28; golden = инвариант №8) · арка «качество-первым» D26→D38.5 закрыта · **арх-ресет D39 исполнен ЦЕЛИКОМ** (7 слоёв → программа D39.1–D39.10 → паки 11–16, D39.13–D39.24; статус слоёв — шапка-таблица `architecture/09-target-architecture.md`) · пере-прогон rerun2 прочитан (D39.20: операционка живьём, $0-резюм ×3 арма, $6.63<$15; планка ≤2 НЕ пройдена; анти-корреляция гладкость↔верность ×3 → mistral-редактор вон) · эксп ЗАКРЫТ (D39.22), итерация №2 отложена · **курс = разработка бэкенда: пак-17 «канал B вживую» ЗАКРЫТ ЦЕЛИКОМ (дизайн D39.26 · стройка D39.27 · дельта `allow` D39.28, 25.07); вокабуляр = ПАРА `violence`/`sexually-explicit`, остаток — ФАКТЫ ToS в зоне полигона (fail-closed до них)** (D39.25: в движке НЕТ понятия «18+» — generic content-labels × provider-capabilities; лейблы вне хешей · резолв до валидации · один хоп `chain[0]` · гранулярность = книга) → **КУРС АМЕНДИРОВАН ВЛАДЕЛЬЦЕМ 25.07 (D39.33): МАСШТАБ — ПОСЛЕДНИМ, после достройки алгоритмического идеала; доказательство эффективности крупных изменений — МИНИ-ПРОГОНЫ ~10 глав (детерминированные чекеры и скан остатка первыми, судья — только когда от него зависит решение).** **ОЧЕРЕДЬ ИСПРАВЛЕНА ЗАМЕРОМ (D39.35): $0-резюм драфта на стенде УЖЕ МЁРТВ** (снапшот разошёлся по трём осям — cheapgate v3→v4, renderfmt v2→v3, langpack 09974d6→a7d4be2), поэтому срочности «гнать, пока жив» НЕТ, а ниты перестали быть условными: **(1) микро-пак «предпрогонная гигиена» ($0-код): два нита общности + ОБЯЗАТЕЛЬНЫЙ флип `Gates.RegressionGuard.Enabled` (невыполненное обязательство D38.4 п.5) + опц. проекция пере-оплаты в `tmctl status`** · **(2) мини-прогон-замер ~10 глав** (драфт ≈$0.02, редактура ≈$0.08–0.15/арм, судья НЕ зовётся ⇒ один арм ≈$0.10–0.17, два ≈$0.20–0.32; перед прогоном бэкап `rerun2/*.db`, БД на схеме v9 против v10 бинаря) · **(3) параллельно $0 — сид/банк-дельта** (元 · 赤城 · 学堂家老 · 春秋蝉; два терма ждут подписи владельца) · **(4) пак-19 в урезанном скоупе** · (4) далее слой-2 извлечение из тел промтов · ru-target · native-Gemini судья. Затем — сид-дельта (元 · 赤城 · 学堂家老 · 春秋蝉), МАСШТАБ целой книги (7,78 млн символов, ~2284 раздела) и пилот Ф2.5. Хроника треков A/B, exp15/16 и стройки паков — архивы ниже + D-лог. > - **Стек:** draft deepseek-v4-flash (thinking ON) → **editor deepseek-v4-pro БИЛИНГВ ИНТЕРИМ (D39.22; glm-5 резерв, mistral вон D39.20)**, промпт v3-discourse (P1a+чэнъюй+few_shot-тумблер) → судья gemini-3.1-pro-preview; канал B Mistral+grok; 7 ключей; ~$0.85/ранобэ (D30.4). > - **ОТКРЫТЫЕ ПЕТЛИ (норма 25.07, перепись 180 инцидентов: класс NEVER_CLOSED больше не копим — каждая петля обязана получить диспозицию решено/отложено-с-записью/отклонено; ведёт оркестратор).** *Владелец:* ~~вокабуляр content-лейблов~~ **РЕШЕНО 25.07: ПАРА `violence`/`sexually-explicit`** (D39.27 п.5 — сохраняет D14 п.1 и dspro-редактора на violence) ⇒ следствие в работе: assert-only `action` (дельта пака-17) · ~~Q2 редактор под лейблом~~ **ОТЛОЖЕН С ЗАПИСЬЮ** (D39.27 п.7: при паре связывает только `sexually-explicit`-книги, которых нет; возвращается вместе с предусловием D22.7 перед первой explicit-книгой; кандидаты — grok-4.3 интерим, glm-5 только после сверки ToS Z.AI по первоисточнику, mistral отвергнут D39.20) · ~~ToS-факты + три решения по ним~~ **ЗАКРЫТЫ 25.07 (D39.29 факты + D39.30 подписи владельца):** лейбл `violence` НЕ заводится, интерпретация «нейтральное изображение ≠ пропаганда» зафиксирована письменно · риск Z.AI (плоский запрет «violent content»; экспозиция состоялась в rerun2 через glm-арм) ПРИНЯТ сознательно с условиями пересмотра · две строки `accepts_labels: [sexually-explicit]` (`xai`, `mistral`) ПОДПИСАНЫ и ПРИМЕНЕНЫ в `models.yaml`. **Остаток ToS-вопросов ЗАКРЫТ владельцем 25.07 (D39.32):** local = «политик нет, переводим всё» ⇒ строка применена · Gemini-оговорка читается СУЖЕНИЕМ ⇒ вердикт `FORBIDDEN`→`UNCLEAR`, но по доктрине это НЕ разрешение: строку Gemini не получает, **ждёт одного слова, если владелец хочет подписать интерпретацию как разрешение** · причину пустоты в конфиг НЕ выносим (живёт в quirks-доке) · **ре-чек политик стал ПРАВИЛОМ** (триггеры: Google ToS 30.07.2026 · квартал 25.10.2026 · любая правка `accepts_labels`) · дата аккаунта OpenAI — открыта, приоритет низкий (OpenAI вне цепочки). **Продуктовое (Ф3):** Gemini API Additional ToS запрещает клиентов, «likely to be accessed by individuals under 18» — обязательство на конечный продукт, независимо от лейблов · **промпт-слой под лейблом (НОВОЕ, вскрыто вопросом владельца): лейбл сегодня НЕ доезжает до промпта** (чистые данные маршрутизации + load-time гарантия); нужно ли, чтобы переводчик/редактор ЗНАЛИ о свойстве контента — отдельное решение промпт-слоя · ~~мини-каноны сид-дельты~~ ЗАКРЫТО 26.07: все четыре пункта решены, правок сида ноль; книжные канон-решения живут С КНИГОЙ (`books/gu-zhenren/CANON-NOTES.md`), НЕ в D-логе — урок владельца: движок не строится вокруг одной книги · ~~включение платной петли ремонта~~ **РЕШЕНО 26.07 (D39.38): НЕ включается** — замер 0 в её классах; вернуться при расширении классов и ненулевом остатке; **уточнение D39.39: «0» = ноль в охвате детекторов с измеренным теперь recall (латиница 0.50 боевой, битые формы 0.10) — решение в силе (петля видит теми же детекторами), но читать как «финал чист» нельзя** · **вопросы разметки Р1–Р4 (НОВОЕ, D39.39, пакет-6 §5, 21 спорная строка):** гипербола 千万 (довод: тот же автор в гл.2 даёт 数十万 о том же деянии) · что есть дефект в классе 成 — форма или значение (от ответа precision K5c 1.000 или 0.167) · сокращённая форма термина («гу» при dst «гу-тварь») · принципиально неразрешимая офлайн речь/мысль (если да — потолок recall K4b < 1.0 by construction, вписать в контракт правила) · ~~место фикс-пака «чекеры» в очереди~~ **РЕШЕНО 26.07 (D39.40): это ПАК-21, а перед ним мини-прогон ~10 глав на полном бэкенде (после лендинга 20 и 19)**; ответы Р1–Р4 нужны к дизайну пака-21 · ja→ru-реплика §B5 (тайминг) · старые Ф2.5-блокеры (билингв-якорь — D25 п.9 Q1 · контаминация корпуса — D27 п.4 · судья-дублёр D22.6 · планка запуска · publishable/waiver — D25 п.1 · FN-bound L3 — D25 п.4 · юр-пакет · провенанс 12-*-доков · xAI-ключ off перед продом D27 · residual-тачпойнты exp16, не влитые в сид-дельту: мини-голд алиасов · precision@30, `books/gu-zhenren/exp16/`). *Оркестратор:* ~~ратификация дизайна пака-16~~ РЕШЕНО D39.24 (25.07) · фантом-гард D-ссылок в чек-листе лендинга (действует) · ~~doc-sync pass~~ **ИСПОЛНЕН 25.07** (запись ниже: оба puml перерисованы под пост-пак-16 · баннеры на 01/02/03/04/06/07/08/09 · ресёрч-свип 18/18c/19/20/21/22 · README/POLYGON_PACKAGE4 освежены; residual — пинги полигону в его зоне) · ~~приёмка пака-17 фаза 1~~ **РЕШЕНО D39.26 (25.07: принято в редакции §14, три добора приёмки, фаза 2 размечена)** · **`escalation_model` = ОТВЕТИВШАЯ модель (НОВОЕ, вынесено из пака-17): вердикт-изменение** — golden пинит `esc_model` на ОТКАЗАВШЕМ хопе, маскировка не скрывает ⇒ отдельная ратификация с санкционированным пере-капчером · **`--accept-rebill[=usd]` с порогом `min($0.50, 5%×ProjectedBookUSD)` РАТИФИЦИРОВАН D20.2-Q2, но в коде/CLI ОТСУТСТВУЕТ (НОВОЕ)** — живёт только в спеке `backend/docs/D15.2-*.md`; `--resnapshot` пере-пиннит без суммы · **D22.7 (пер-чанковый L3-скрин) — предусловие первой erotica-книги в проде НЕ снято паком-17 (НОВОЕ, держать отдельным гейтом)** · фантом-гард D-ссылок в чек-листе лендинга (действует). *Бэкенд:* **ХВОСТЫ МИНИ-ПРОГОНА (D39.37, 26.07, по убыванию веса):** ~~(1) размеченный набор для чекеров~~ **ЗАКРЫТ 26.07 (полигон-пакет-6, D39.39)** ⇒ породил вход нового фикс-пака «чекеры»: 12 дефектов с file:line (супрессор K5c гасит юнит целиком · атрибуция K4b (23 безатрибутивных + 12 с `!?…`) · `inner_marker` на тире-строки · предохранитель K5a отсекает все реальные магнитуды · заглавные/гомоглифы K2 · 两 в `cheng_re` · U+0301 рвёт `TokenizeCyrillic` · мн.число в `decl.forms`-тулинге · однознаковые ключи пост-чека) — приёмка против набора за $0; **место РЕШЕНО (D39.40): ПАК-21, после мини-прогона на полном бэкенде; + переезд `translitInterjections` в данные (улов владельца)** · (2) **тест-пин джойна WHAT↔WHICH** (мутация оркестратора выжила — фикс-лист №1 следующего касания) · ~~(3) покрытие WHAT↔WHICH + флаговый майнинг-стоп~~ **ПАК-20 ЗАКРЫТ ЦЕЛИКОМ 26.07 (фаза 1 D39.41 · решения D39.42 · фаза 2 ПРИНЯТА И ЗАЛЕНДЕНА D39.45): терминолог + `--verify-bank` (дефолт=авто) + авто-провод с пометкой + точечная ре-редактура с $0-пере-пином + банкнота отдельным файлом; живьём на 10 главах, $0.0476, деньги сходятся тремя путями; заделы серии/импорта — контрактами.** Открытое по паку: `feed_cap` (рекомендация: малой дельтой) · жанровые метки словаря · подпись словаря после фазы B полигона · resnapshot стендовых книг с auto-строками; хвосты: `AttachKWIC` квадратичен · смета молчит о пере-покупке роли на подписи · S12 частичен · zh-worst-case в `bankTokenBudget` (утечка §0 признана) · gofmt `llm.go` (до-паковый) · **вариативность банкноты 37↔14 (n=2) → полигону в фазу B** · (4) `dialogue_dash` мерить только на невиданном тексте (подогнан под 蛊真人) · (5) генеральность нарезки: кана/ханьцзи один класс ⇒ ja `est_out` ~1.7× завышен МОЛЧА; `EstimateTokens` недосчитывает кириллицу (резервации занижены) · (6) golden второй фикстурой С langpack · (7) `prompt_override` — честная формулировка гарантии слоя-2 · (8) майнер/банк не-CJK (едет с ja→ru) · (9) `request_log` скоуп/ретеншен + `first_flag_reason` третье представление истории · (10) флор-16000 без модели · (11) `max_tokens`-упоры вернутся на en · ~~пак-17 (фазы 1–2 + дельта)~~ **ЗАКРЫТ ЦЕЛИКОМ 25.07 (D39.26/27/28)** · ~~ПАК-18 «долги контракта»~~ **ПРИНЯТ И ЗАЛЕНДЕН 25.07 (D39.31) — долгов контракта не осталось**; выдавался (`BACKEND_PACK18_DEBTS_SESSION_PROMPT.md`: `--accept-rebill` на снапшотной гранулярности — `guard_hash` в коде нет и не строим · `escalation_model` = авторитетная модель с САНКЦИОНИРОВАННЫМ пере-капчером golden под этот дифф · два нита общности условно-байт-нейтрально) · **ru-target долг (слой 7, `isRuTarget`×11 прод-сайтов · `TokenizeCyrillic`×6 · ключ «ru» в санитайзере) — ЖДЁТ СВОЕГО ДИЗАЙН-ПАКА** (связывает только не-русский таргет, по текущей карте zh/ja/en→ru не срочен) · **ПАК-19 «голос и состояние» ВЫДАН 25.07** (`BACKEND_PACK19_VOICE_STATE_SESSION_PROMPT.md`, двухфазный; ниты общности едут ЕГО лендингом — D39.31 п.5) · **проекция пере-оплаты в `tmctl status` (НОВОЕ, из отчёта пака-18): спека §9 предлагает заменить булев `ConfigDrift` числом «N чанков, ~$X» — дёшево, `omitempty`, не сделано сознательно** · **не-долги, следующие несущие:** масштаб целой книги (волны/каденс/потолки/ETA на сотнях глав — гоняли максимум 5) · Ф2-механизмы (голос/состояние D21 · native-Gemini судья) · извлечение дискурс-норм/few-shot из ТЕЛ промтов (слой 2, форсинг = ja→ru) · ~~пак-16 ф.2~~ ЗАЛЕНДЕНА 25.07 · per-class исходы ремонта — схемное решение (класс не durable), принимается ВМЕСТЕ с включением петли · ниты общности (владелец 25.07) — **ОТЛОЖЕНЫ ПО ЗАМЕРУ (D39.31 п.5): едут на ближайший ПОЛНЫЙ `--resnapshot`**; для Detail-строк байт-нейтральной формы не существует определительно (дефолт шаблона обязан рендерить текущую строку). Прежняя формулировка: только в байт-нейтральной форме с доказательством исполнением (иначе `CheapGateVersion`/`pack.Version()` двигают снапшот стендовых книг и убивают $0-резюм драфта под замер остатка пака-16): Detail-строки чекеров → шаблоны/цитаты из данных (статический 时辰-текст в generic-файле соврёт второй паре) · множитель ×2 `lintTimeUnits` (`checkers.go:174`) → ключ данных. *Полигон:* ~~пакет-6 (размеченный набор чекеров)~~ **ОТРАБОТАН И ПРИНЯТ 26.07 (D39.39): 3015 позиций, 6 прогонов, $0; metrics.json воспроизведён побайтно; набор на стенде `books/gu-zhenren/labels/` = постоянный измерительный стенд чекеров; ~~ПАКЕТ-7 (v2, ресёрч-программа)~~ **ЗАКРЫТ ЦЕЛИКОМ 26.07 (A: D39.43 · Z-решения: D39.44 · B+B′: D39.46; $0.45043 из коридора $2.60): ратифицировано «контексты — главный рычаг терминолога»; судья = только катастроф-экран с декоем; словарь инертен до расширения эмиссии на term; три процессных правила — в нормы промтов; ~~на владельце подписи Z-B1–B4~~ **АМЕНДИРОВАНО D39.47: жанровый словарь ОТМЕНЁН КАК КЛАСС (решение о переводе = подпись владельца на банк КНИГИ; Z-B1/B2/B6 сняты, Z-B4 сужен, Z-B3 остаётся — промпт уже в узкой форме); дельта размотки → бэкендерам, главный вопрос покрытия = расширение эмиссии на term + feed_cap (ждёт санкции)**; амендмент D39.42 п.1 (терминолог на полном банке = единицы долларов) и Q4→Р3 (сокращённая форма = поле записи, вход пака-21) — в силе; полигон-очередь ПУСТА, кандидаты: вариативность банкноты 37↔14 · ёфикатор/санитайзер-разметка · dialogue_dash на невиданном (оставшиеся кандидаты: ёфикатор/санитайзер-классы не размечены — §6.5 отчёта пакета-6; `dialogue_dash` на невиданном тексте) · ~~пакет 5 (факты ToS + 8 пингов)~~ **ОТРАБОТАН ЦЕЛИКОМ И ПРИНЯТ 25.07 (D39.29); все восемь пингов закрыты.** Исторический список закрытых пингов: (1) `00-provider-quirks.md:13,73` — катовер deepseek-chat 24.07 ПРОШЁЛ, пост-катовер факт (жив ли алиас, цены) не внесён — live-проба `/models` + вендор-дока; (2) `00-provider-quirks.md:69` — оговорка «mistral-editor-арм до guard» superseded D39.20 (mistral вон из редакторов; rate-limit-цифры оставить — нужны переводчику канала B пака-17); (3) `09-pilot-protocol.md:3` — ⚠-указатель застрял на D31/glm-5: продлить до пост-D39.22 (dspro-интерим · планка ≤2 не пройдена rerun2 · авто-QA слепыми метками D39.20 · стайл-каноны D39.21), тело не трогать; (4) `15-segmentation-empirics.md:6-7` — шапка до сих пор «НИ ОДНОГО платного вызова», а эксп залендён REV.2 (D39.7/D39.8, $12.79) — перевернуть статус-блок; (5) `16-bank-mining.md:3` — дописать финал «ИСПОЛНЕН И ЗАЛЕНДЁН (D39.10); gender-тачпойнт §7 закрыт (D39.19); продолжение — Go-майнер парити EXACT»; (6) `experiments/README.md` — индекс без строк exp15/16 (сверка застряла на D38.2); (7) `eval/README.md` — не тронут с 19.07 (пре-rerun2): освежить статус под пост-D39.22 (эксп закрыт · судейский риг-стандарт D39.7 · экстракция только `tmctl export`). @@ -151,6 +151,8 @@ **Вопросов владельцу нет; в `backend/` не писал, не коммитил.** Открытым остаётся холодный старт с пустым банком — единственная цифра, на которой стоит цена расширения, и она стоит денег. +**Добор §7 по D39.51 — два замера, $0.00960 (пакет-8 нарастающим итогом $0.12273).** Пре-регистрация §7.1–7.2 написана до трат и до счёта. **(а) Пример формата в промпте роли — критерий ВЫПОЛНЕН:** три строки `Пример строки ответа: 师父наставник` дают **0 не-целевых строк из 120** против **22 у контроля** (тот же арм 3×40 без якоря; фактор ровно один — доказано диффом собранных запросов, ключи и user-часть побайтно те же; пример-терм в срезе не встречается ни разу, эталон подсказать не может). Мощность названа ДО прогона и ограничивает вывод: срыв в контроле шёл 2 батча из 9, значит при нулевом эффекте шанс чистого прохода (7/9)⁹ = 0.104 — «согласуется с починкой», не доказательство; удвоение повторов уронило бы до ~0.011 за ≈$0.010. **Побочно (§7.4): пример НЕ чинит утечку алфавита и языковой экран её не поймает** — `甲等 → «категория A»` с ЛАТИНСКОЙ A против подписанного «класс А» с кириллической (то же на `乙等`/`丙等`, 7 строк в арме с примером, 3 в контроле; сравнение причинным считать нельзя — в контроле те же термы уехали целиком в английский). Ни один из 53 подписанных dst сида латиницы не содержит, то есть правило проверяемо. **(б) P1 consistency — НЕ разделяет, $0.** Разметка 74 поверхностей — **три независимых слепых разметчика** (согласие 0.973/0.986/0.986, единогласно 72 из 74, метка 37 TERM · 37 JUNK), им не давали ни частот, ни глав, ни самой метрики. Сработал заранее названный стоп: вырожденных (встречаются в ≤1 главе → P1 = 1.0 по построению) **54 из 73 = 0.740**, и среди них ровно **27 TERM и 27 JUNK** — максимум получают обе метки. Критерий ≥80%/≥60% не выполнен ни на полном наборе, ни на 19 невырожденных; AUC **P1 = 0.438** (ниже монетки) при **частоте 0.500** — арм-без-фактора провалился ровно как обязан, независимо подтвердив §5.1 на другом наборе и другой разметке. Отклонение «глава вместо чанка» (тексты боевых чанков не сохранены) названо заранее и его цена измерена: вырожденность 0.740 по главам против 0.730 по чанкам — мелкая единица не спасает. **Права на порядок подачи P1 не заработал.** Мерить его по-настоящему стоит на том же полном прогоне, что и холодный старт, отдельных денег не требуя. + **Добор §6 по вопросу владельца «хватает ли одной книги, движок ведь универсальный» ($0, после СТОПа).** Прогнал боевой майнер с того же пина по стенду. `en→ru` сегодня не запускается вовсе: пары `en`/`en-ru` в `configs/langpacks/` нет, загрузчик отказывает ЛОУДНО (D39.15 работает как задумано — недостающее это данные). Когда загрузчик удовлетворён зеркалом zh-пака, канал WHICH на латинице даёт **0 кандидатов на 814k знаков** обеих английских книг (Кристофф и Fifty Shades) — механизм в `hanRuns` (`miner_substrate.go:46-48`): кандидатный алфавит есть пробеги ханьских иероглифов, в латинице их нет. На японском (`異世界魔術師`, 134k) канал не молчит, а **ошибается: 10 строк, все типа `name`, все ложные** (`何故` «почему» 44×, `王国` «королевство» 35×, `马鹿` «дурак» 21× — китайские фамильные паттерны на японских composites); настоящий ja-пак ложняки уберёт и не добавит ничего, потому что имена этой книги в катакане, а `hanRuns` её не видит. **Вывод: второй КНИГИ мало — для латиницы нужен второй ДЕТЕКТОР** (прототип и замеры есть: `eval/pkg7/mine_nonhan.py`, каналы ORTHO/DISP/CONTRAST, достаёт `Ashdrinker`/`San Michon`/`Forever King`). Попутно найден дефект общности: **`kwic_width` меряется в РУНАХ** (`terminology.go:332-349`), дефолт 40 живёт в Go одной константой на все пары и означает 43–84 морфемы на zh против 13 слов с обрубленной фразой на en — место такой константы в пар-данных, а мой вердикт «3×40 достаточен» читается только как «43–84 морфемы достаточно». Ничего не строил и не запускал платного: §6.4 — предложение, санкции на него не было. ### Пакет-6 «размеченный набор для чекеров» ОТРАБОТАН И ПРИНЯТ (D39.39), 26.07 diff --git a/docs/architecture/05-decisions-log.md b/docs/architecture/05-decisions-log.md index 7354f219..64a3fcc9 100644 --- a/docs/architecture/05-decisions-log.md +++ b/docs/architecture/05-decisions-log.md @@ -1158,3 +1158,7 @@ API-529-долг закрыт: 8-осевой refute-by-default воркфлоу ## D39.51 — Дизайн «дисциплина банкноты + дыра языка ответа» РАТИФИЦИРОВАН, стройка санкционирована (владелец: «идём», 26.07): языковой экран на ВЫХОДЕ + счётчик OffLanguage + фильтр свёртки банкноты + нормализация кавычек в свёртке (Q1) + агрегация из чекпойнтов (A1) + kwic_width в пар-данные (файл zh-ru НЕ шипуем) + doc-хвост. Судья ОТЛОЖЕН; мини-прогон D39.40 — В ХОЛОДНОЙ конфигурации; allow_short — на нём (26.07, оркестратор №8). ✅ **Отчёт:** `docs/archive/reports/PACK20_BANKNOTE_DESIGN_2026-07-26.md` (ревью-шапка). Принципиальные решения: (1) **гарантия языка — на выходе, не на входе** (предикат offLanguage по `*unicode.RangeTable` через конфиг гейта `target_script`, обязателен при enabled, вне снапшота, $0; направление отказа объявлено: отвергнутый законный латинизм дешевле англо-канона); (2) **кавычки — в свёртке (Q1), не в парсере/NormalizeSourceKey** (Q2/Q3 платят волной за косметику; Q1 $0 и ретроактивен; NormVersion не двигается — тест-страж); (3) **агрегация банкноты — производное из чекпойнтов (A1), без новых таблиц**; инвариант «прогон без новых черновиков даёт байт-идентичную карту» — пином; предложения отвергнутых попыток входят (решение: предложение = улика, не канон); (4) **судья-с-декоем НЕ строится** (≈$0.025/книга — цена не аргумент; его положительное решение = решение о переводе за владельца, против D39.47; популяция неизвестна до холодного старта; сначала P1); (5) **три расхождения сессии с записками оркестратора приняты** (allow_short = базовая версия = перепокупка книги → на холодный прогон; kwic-файл zh-ru не шипуем; судья отложен). **Замеры полигону:** пример формата в промпте (≈$0.009, критерий до прогона: 0 не-целевых строк ×3 против 22/40) · P1 consistency ($0: Σchunks-предложивших / chunks-содержащих; порог засчитывается при ≥80% термов и ≥60% мусора; арм-без-фактора = голая частота обязана провалиться; положительный исход = право на ПОРЯДОК, не на выброс строк) · приёмка предиката на живом сырье 147 вызовов (живо, подтверждено: ровно 27 строк §3.4 и ни одной сверх). **Мини-прогон D39.40 = холодная конфигурация** (пустой сид, банкнота+терминолог ON, --verify-bank, ОТДЕЛЬНАЯ БД) — пять ответов за ≤$0.23: банкнота без цензуры · совместный recall (замер 1 состоится) · язык при пустом якоре (счётчик OffLanguage как прибор) · побочки allow_short · второй сэмпл для агрегации. Приёмка стройки: мутации M-Я1/M-Я2 + регресс 16 живых строк + пин общности (латинская цель переворачивает вердикты) + пин инварианта агрегации + сырьё-приёмка. + +## D39.52 — Замеры D39.51 закрыты ($0.00960; пакет-8 итого $0.12273): (а) строка примера формата ЧИНИТ срыв языка (22/120 → 0/120) — ИДЁТ В ПРОМПТ РОЛИ данными вместе со стройкой пп.1–7; (б) P1 consistency НЕ разделяет (вырожденность 0.740, критерий провален, частота — ноль) — права на порядок подачи нет, пере-замер на холодном прогоне $0 (26.07, оркестратор №8). ✅ + +Приёмка воспроизведением из сырья: (а) 0/120 не-целевых строк в арме с примером — независимый пересчёт; фактор один (дифф запросов — только три строки примера; пример-терм 师父 не входит ни в выборку, ни в якорь, ни в сид); мощность 0.104 названа до прогона, удвоение повторов не берём (страховка = экран + OffLanguage живьём). (б) ре-ран скорера: 54/73 вырождено, критерий ≥80/≥60 не выполнен нигде, частотный арм — независимое подтверждение §5.1 на другом наборе и другой разметке (три слепых модельных разметчика, согласие 0.973–0.986; оговорка «разметчики — модели» названа). **Хвост в пак-21: утечка алфавита** — «甲等 → категория A» (7 строк): языковой предикат пропускает (кириллица есть), подписанные dst латиницы не содержат вовсе; кандидаты — банк-линт латиницы в dst или строгая форма предиката (§2.2-Б дизайна). **Вопросы холодного мини-прогона пополнены:** + пере-замер P1 на популяции без цензуры ($0 из чекпойнтов). Стройка бэкенда: пп.1–7 §8 + п.8 «строка примера в промпте роли» — санкционирована, ждёт релея. diff --git a/docs/archive/reports/POLYGON_PREMEASURE_2026-07-26.md b/docs/archive/reports/POLYGON_PREMEASURE_2026-07-26.md index 8c03f313..5018e90a 100644 --- a/docs/archive/reports/POLYGON_PREMEASURE_2026-07-26.md +++ b/docs/archive/reports/POLYGON_PREMEASURE_2026-07-26.md @@ -783,4 +783,190 @@ Precision 0/10. **Оговорка, обязательная:** я подста 3. **Что стоит перепроверить на второй паре ДО расширения** (когда детектор будет): срыв языка ответа без якоря (§3.4) — на en→ru он опаснее всего; и `kwic_width` как пар-данные (§6.3). 4. **Чего второй книгой не купить:** цену расширения эмиссии. Она упирается в холодный старт с - пустым банком на ЛЮБОЙ паре, и это по-прежнему единственная неизмеренная цифра. \ No newline at end of file + пустым банком на ЛЮБОЙ паре, и это по-прежнему единственная неизмеренная цифра. +--- + +## §7 Добор по D39.51: два замера. ПРЕ-РЕГИСТРАЦИЯ (написана ДО трат и ДО счёта) + +Санкция D39.51 (26.07): (а) пример формата в промпте роли — арм «3×40 без якоря + строка примера на +целевом письме», 3 повтора, ≈$0.009; (б) P1 consistency — $0 из сохранённых артефактов. + +### §7.1 Замер (а): помогает ли строка примера удержать язык ответа + +**Фактор ровно один, проверено диффом, а не глазом.** Вариант промпта собран копией +`backend/prompts/zh-ru/terminologist.md` в скретчпад (репозиторий не тронут); единственное отличие — +три строки после спецификации формата: + +``` +Пример строки ответа: + +师父 наставник +``` + +Батчи, ключи и user-часть запроса **побайтно те же**, что у контрольного арма `gNOANCHOR` +(проверено сравнением построенных запросов). Пример-терм `师父` не входит ни в выборку, ни в якорь, +ни в сид и **не встречается в срезе ни разу** — подсказать эталон он не может по построению. + +**Арм без фактора** — существующий `gNOANCHOR` (3×40, без якоря, без примера, 3 повтора, 120 строк +ответа, из них 22 не на целевом письме). + +**Предикат «не-целевая строка»** (тот же, которым отвечал бэкендерам): в принятом парсером ответе нет +ни одной руны кириллицы. Смешанные строки (кириллица + латиница) считаются отдельно и в критерий не +входят — предикат их по построению пропускает. + +**Критерий, названный ДО прогона:** арм с примером проходит, если даёт **0 не-целевых строк** из 120. +Любая не-целевая строка = фактор не сработал. + +**Мощность — называю ДО прогона, потому что после будет поздно.** Срыв в контроле случался не в +каждом прогоне: 1 прогон из 3, **2 батча из 9**, 22 строки из 120. Если считать батч независимой +попыткой, то при НУЛЕВОМ эффекте вероятность получить 0 срывов на 9 батчах равна (7/9)⁹ = **0.104**. +То есть даже чистый проход — это «согласуется с починкой» с одним шансом из десяти на совпадение, а +не доказательство. Провал же (хоть одна не-целевая строка) — сильная улика против фактора. +Усиление до 6 повторов уронило бы ложный проход до ~0.011 и стоило бы ещё ≈$0.009; санкция названа +на 3 повтора, поэтому гоню 3 и цифру ложного прохода публикую рядом с результатом. + +### §7.2 Замер (б): P1 consistency как разделитель термов и мусора + +**Метрика.** Для каждой из 74 поверхностей банкноты: Σ единиц, где чтец объявил строку термином, +делить на Σ единиц, где строка физически встречается. + +**Отклонение от формулировки санкции, названное заранее.** Санкция говорит «чанков». Тексты боевых +чанков прогона не сохранены (в БД лежат хеши и вердикты, не исходник чанка), поэтому единицей взята +**ГЛАВА** — атрибуция главы у чекпойнта банкноты есть точная. Цена отклонения измерена, а не +оценена: доля поверхностей, встречающихся в ≤1 единице, составляет 0.743 по главам и 0.730 по +чанкам моей нарезки — то есть более мелкая единица метрику не спасает, а вырожденность есть +свойство десятиглавного среза, не единицы. + +**Разметка.** 74 поверхности размечены на TERM/JUNK **тремя независимыми разметчиками вслепую**: +им дана строка исходника, предложенный черновиком перевод и до двух контекстов — и НЕ даны ни +частота, ни главы, ни сама метрика (иначе метка коррелировала бы с арм-без-фактора). Метка — +большинство из трёх; согласие публикуется; расхождения решаю сам по контекстам и помечаю. +Это прямой ответ на слабость, записанную мной же в §2.5 как непокрытую («разметчик один»). + +**Критерий, названный ДО счёта (из санкции):** P1 засчитывается как разделитель, если существует +порог, при котором выше него оказывается **≥80% TERM**, а ниже — **≥60% JUNK**. + +**Арм без фактора:** голая частота на том же наборе и том же критерии. Она **обязана провалиться** — +иначе P1 не несёт информации сверх частоты, и вся конструкция беспредметна (§5.1 уже показал, что +частота термы и не-термы не разделяет; здесь это проверяется на ДРУГОМ наборе и другой разметке). + +**Заранее названный стоп.** Если вырожденных поверхностей (встречаются в ≤1 главе, поэтому P1 = 1.0 +по построению) окажется больше половины набора, критерий §7.2 объявляется **неприменимым на этих +артефактах**: на такой популяции «≥80% TERM выше порога» достигается тривиально и ничего не значит. +В этом случае пишется, чего стоит измерить P1 по-настоящему, а цифра не подгоняется. + +### §7.3 Результат (а): пример формата язык УДЕРЖИВАЕТ — критерий выполнен + +Факт **$0.00960** (смета ≤$0.0232, названная до трат), 9 вызовов, 120 принятых строк ответа. + +| Арм | Строк | НЕ на целевом письме | Смешанных (кир.+лат.) | +|---|---|---|---| +| 3×40 без якоря, **без примера** (контроль) | 120 | **22** | 3 | +| 3×40 без якоря, **со строкой примера** | 120 | **0** | 7 | + +**Критерий §7.1 (0 не-целевых строк) ВЫПОЛНЕН.** Три строки промпта — `Пример строки ответа: 师父 +наставник` — сняли полный срыв в английский там, где его не удержал ни якорь (его нет), ни объявление +`{{target_lang}}`, ни весь русскоязычный промпт. + +**Мощность, названная ДО прогона, и она же ограничение вывода.** Срыв в контроле происходил не в +каждом прогоне: 2 батча из 9. Значит при НУЛЕВОМ эффекте шанс получить 0 срывов на 9 батчах равен +(7/9)⁹ = **0.104**. Читать результат следует как «согласуется с починкой, один шанс из десяти на +совпадение», а не как доказательство. Удвоение повторов уронило бы эту цифру до ~0.011 и стоило бы +ещё ≈$0.010 — решение не моё, но цифра названа. + +### §7.4 Побочный результат (а): пример НЕ чинит утечку алфавита, и экран её не поймает + +Смешанных строк стало не меньше, а больше (7 против 3), и все они — одно и то же место: + +| Терм | Подпись владельца | Арм с примером | Контроль | +|---|---|---|---| +| `甲等` | «класс **А**» (кириллица) | «категория **A**» (латиница) | «Ранг **A**» | +| `乙等` | «класс **Б**» | «Класс **B**» · «уровень **B**» | «Категория **B**» | +| `丙等` | «класс **В**» | «Класс **C**» · «уровень **C**» | «Категория **C**» | + +Сравнение 7 против 3 **причинным считать нельзя**: в контроле те же самые термы в прогоне r2 уехали +целиком в английский («Grade C», «Grade B») и попали в колонку «не на целевом письме», а не +«смешанных». То есть поведение одно и то же в обоих армах — модель берёт буквенные обозначения из +ЛАТИНСКОГО алфавита, — а пример лишь перевёл окружающее слово на русский. + +Значение для стройки: **предикат «в ответе есть руны целевого письма» пропускает все семь.** +«категория A» с латинской `A` от подписанного «класс А» с кириллической `А` отличается байтом и не +сойдётся с каноном никогда, а языковой экран промолчит. Ни один подписанный dst сида (53 строки) +латинских букв не содержит вовсе — то есть эталон здесь однозначен, и правило «в целевой строке +латиница допустима только внутри латинского имени собственного» проверяемо. Это не возражение против +экрана: это второй дефект, который экран по построению не покрывает, и теперь он измерен дважды. + +### §7.5 Результат (б): P1 consistency НЕ разделяет термы и мусор + +**Разметка — три независимых слепых разметчика**, попарное согласие **0.973 · 0.986 · 0.986**, +единогласно 72 из 74; расхождения ровно два (`第三蛊`, `镇族蛊虫`), взято большинство. Метка: +**37 TERM · 37 JUNK** — набор сбалансирован сам собой, подгонки не потребовалось. + +**Заранее названный стоп сработал.** Вырожденных поверхностей (встречаются в ≤1 главе среза, поэтому +P1 = 1.0 по построению) — **54 из 73 = 0.740**, что больше половины. И вырожденность бесполезна не +«в среднем», а адресно: среди этих 54 ровно **27 TERM и 27 JUNK**. Обе метки получают одинаковый +максимум, разделять там нечем. + +| Набор | Метрика | Лучший порог | TERM выше | JUNK ниже | Критерий ≥0.80/≥0.60 | +|---|---|---|---|---|---| +| весь (73) | **P1** | — | 1.000 | 0.000 | **НЕ ВЫПОЛНЕН** | +| весь (73) | частота (арм-без-фактора) | >4 | 0.189 | 0.861 | НЕ ВЫПОЛНЕН | +| невырожденные (19) | **P1** | >0.5 | 0.200 | 1.000 | **НЕ ВЫПОЛНЕН** | +| невырожденные (19) | частота (арм-без-фактора) | >7 | 0.300 | 0.778 | НЕ ВЫПОЛНЕН | + +Порог — не единственный способ прочитать метрику, поэтому считаю и площадь под кривой (вероятность, +что случайный TERM получит балл выше случайного JUNK; 0.5 = ноль информации): + +| | Весь набор | Невырожденные (19) | +|---|---|---| +| **P1 consistency** | **0.438** | 0.583 | +| Частота (арм-без-фактора) | **0.500** | 0.433 | + +**P1 на полном наборе ниже случайного угадывания.** Арм-без-фактора отработал ровно как обязан был: +голая частота даёт 0.500 — ноль информации, независимое подтверждение §5.1 на ДРУГОМ наборе и ДРУГОЙ +разметке. На 19 невырожденных P1 даёт 0.583 — формально выше монетки, фактически 10 против 9 +объектов, и лучшее, чего метрика достигает, это 2 TERM из 10 выше порога. + +**Вердикт: положительного P1 нет.** Права на порядок подачи метрика не заработала. Внутри среза +видно и почему: два верхних места по P1 действительно заняты термами (`灵泉` 1.00, `学堂家老` 0.75), +но на самом дне рядом с `无` (0.10, JUNK) и `少年` (0.14, JUNK) стоит `魔道巨擘` (0.00, TERM), а вся +середина 0.50 — ровная смесь. + +**Отклонение от формулировки санкции, названное в §7.2 и подтверждённое замером.** Единица — глава, +а не чанк: тексты боевых чанков не сохранены. Цена отклонения измерена: доля вырожденных 0.740 по +главам против 0.730 по чанкам моей нарезки — более мелкая единица метрику не спасает. + +**Чего стоит измерить P1 по-настоящему:** данных банкноты существует только на 10 глав, а метрика +требует, чтобы поверхность встречалась во МНОГИХ единицах. Нужен прогон черновой волны с включённой +банкнотой на объёме, где типичная поверхность попадает в 5–10 единиц, — то есть та же полная книга, +что и для холодного старта. Отдельного замера P1 при этом не потребуется: он считается $0 из +чекпойнтов того же прогона. + +### §7.6 Деньги добора и критик полноты + +| Замер | Вызовов | $ | +|---|---|---| +| (а) арм с примером формата | 9 | **0.00960** | +| (б) P1 consistency | 0 | **0** | +| **Добор итого** | 9 | **0.00960** | +| **Пакет-8 нарастающим итогом** | 156 | **$0.12273** | + +| Проверка критика | Итог | +|---|---| +| Фактор в (а) ровно один | доказано диффом собранных запросов: ключи и user-часть побайтно те же, отличие — три строки | +| Пример не подсказывает эталон | `师父` нет ни в выборке, ни в каноне, ни в сиде, и не встречается в срезе ни разу | +| Мощность (а) названа ДО прогона | да, 0.104 при нулевом эффекте | +| Разметчик в (б) не один | три независимых, слепых, согласие 0.973–0.986 | +| Разметка не видела метрику | да: разметчикам не давали ни частоту, ни главы, ни P1 | +| Арм-без-фактора в (б) провалился, как обязан | да, AUC 0.500 | +| **НЕ покрыто** | (а) одна модель, 3 повтора, одна пара; разметчики (б) — модели, а не люди, и их согласие 0.98 может отражать общий системный вкус, а не истину | + +> **Ревью-приписка к §7 (оркестратор №8, 26.07): ОБА ЗАМЕРА ПРИНЯТЫ, D39.52.** Воспроизведено: (а) арм +> с примером — 0 из 120 строк не на целевом письме (независимый пересчёт по raw_example своим кодом); +> (б) ре-ран `p1_consistency.py` на сохранённых артефактах d51 — вырожденность 54/73 = 0.740, критерий +> не выполнен на полном и невырожденном наборах, частотный арм-без-фактора нулевой. Оговорка мощности +> (а) — 0.104 — принята как названная честно; удвоение повторов НЕ берём: гарантия — экран п.1, живой +> монитор — счётчик OffLanguage холодного прогона. Побочная находка «утечка алфавита» (7 строк вида +> «категория A»; предикат п.1 их пропускает, подписанные dst латиницы не содержат) — записана хвостом +> в пак-21 (кандидаты: банк-линт по латинице в dst / строгая форма §2.2-Б). + diff --git a/eval/pkg7/p1_consistency.py b/eval/pkg7/p1_consistency.py new file mode 100644 index 00000000..e887ca4f --- /dev/null +++ b/eval/pkg7/p1_consistency.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +"""Полигон, пакет-8 (добор D39.51-б): P1 consistency как разделитель термов и мусора. $0. + +Метрика: Σ единиц, где чтец объявил строку термином / Σ единиц, где строка встречается. Единица — +ГЛАВА (тексты боевых чанков прогона не сохранены; отклонение и его цена названы в §7.2 отчёта). + +Арм без фактора — голая частота на том же наборе и том же критерии: если частота проходит, P1 не +несёт информации сверх неё и вся конструкция беспредметна. + +Критерий из санкции, названный ДО счёта: существует порог, выше которого ≥80% TERM, а ниже — ≥60% +JUNK. Дополнительно печатается вырожденность (строки, встречающиеся в ≤1 единице, получают 1.0 по +построению и разделять не могут) — при её доле >0.5 критерий объявляется неприменимым. +""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +def best_threshold(scores: dict[str, float], labels: dict[str, str], want_term=0.80, want_junk=0.60): + """Ищем порог, дающий ≥want_term доли TERM выше и ≥want_junk доли JUNK ниже. Возвращаем лучший + по сумме двух долей — и признак, выполнен ли критерий хоть на одном пороге.""" + terms = [s for s, l in labels.items() if l == "TERM" and s in scores] + junk = [s for s, l in labels.items() if l == "JUNK" and s in scores] + if not terms or not junk: + return None + cands = sorted({scores[s] for s in scores}) + best = None + for t in cands: + # "выше порога" = строго больше или равно; пробуем оба, берём лучший — порог как таковой + # заранее не назывался, назывались только доли. + for mode in (">=", ">"): + hi = (lambda v: v >= t) if mode == ">=" else (lambda v: v > t) + tp = sum(1 for s in terms if hi(scores[s])) / len(terms) + tn = sum(1 for s in junk if not hi(scores[s])) / len(junk) + row = (tp, tn, t, mode, tp >= want_term and tn >= want_junk) + if best is None or (row[0] + row[1]) > (best[0] + best[1]): + best = row + return best + + +def report(name: str, scores: dict[str, float], labels: dict[str, str]) -> dict: + b = best_threshold(scores, labels) + if b is None: + print(f"{name}: нет обоих классов") + return {} + tp, tn, t, mode, ok = b + n_t = sum(1 for s, l in labels.items() if l == "TERM" and s in scores) + n_j = sum(1 for s, l in labels.items() if l == "JUNK" and s in scores) + print(f"{name}: лучший порог {mode}{t:.3f} → TERM выше {tp:.3f} ({n_t} шт) · JUNK ниже {tn:.3f} ({n_j} шт)" + f" критерий (≥0.80/≥0.60): {'ВЫПОЛНЕН' if ok else 'НЕ ВЫПОЛНЕН'}") + return {"tpr": tp, "tnr": tn, "threshold": t, "mode": mode, "passes": ok, "n_term": n_t, "n_junk": n_j} + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--surfaces", required=True, type=Path) + ap.add_argument("--labels", required=True, type=Path) + ap.add_argument("--chunks", required=True, type=Path) + ap.add_argument("--out", type=Path) + a = ap.parse_args() + + items = json.loads(a.surfaces.read_text(encoding="utf-8")) + labels = json.loads(a.labels.read_text(encoding="utf-8"))["majority"] + rows = [json.loads(l) for l in a.chunks.open(encoding="utf-8") if l.strip()] + by_ch: dict[int, str] = {} + for r in rows: + by_ch[r["chapter"]] = by_ch.get(r["chapter"], "") + "\n" + r["source"] + + p1, freq, degen = {}, {}, [] + for it in items: + occ = it["occ_chapters"] + prop = [c for c in it["proposed_chapters"] if c in occ] + if not occ: + continue # предложена там, где не встречается — вне определения метрики + p1[it["src"]] = len(prop) / len(occ) + freq[it["src"]] = sum(by_ch[c].count(it["src"]) for c in occ) + if len(occ) <= 1: + degen.append(it["src"]) + + n = len(p1) + print(f"поверхностей в счёте: {n} TERM={sum(1 for s in p1 if labels.get(s)=='TERM')} " + f"JUNK={sum(1 for s in p1 if labels.get(s)=='JUNK')}") + print(f"ВЫРОЖДЕННЫХ (встречаются в ≤1 главе → P1 = 1.0 по построению): {len(degen)} = {len(degen)/n:.3f}") + dt = sum(1 for s in degen if labels.get(s) == "TERM") + print(f" из них TERM {dt}, JUNK {len(degen)-dt} — обе метки получают одинаковый максимум") + + print("\n--- весь набор ---") + r_all = report("P1 consistency ", p1, labels) + r_fq = report("частота (арм-без-фактора)", freq, labels) + + live = {s: v for s, v in p1.items() if s not in degen} + live_f = {s: v for s, v in freq.items() if s not in degen} + print(f"\n--- только НЕвырожденные ({len(live)} поверхностей) ---") + r_live = report("P1 consistency ", live, labels) + r_lf = report("частота (арм-без-фактора)", live_f, labels) + + print("\nневырожденные поверхности (P1 · частота · метка):") + for s in sorted(live, key=lambda s: -live[s]): + print(f" {s}\t{live[s]:.2f}\t{live_f[s]:3d}\t{labels.get(s)}") + + if a.out: + a.out.write_text(json.dumps({"p1": p1, "freq": freq, "degenerate": degen, + "all": {"p1": r_all, "freq": r_fq}, + "live": {"p1": r_live, "freq": r_lf}}, + ensure_ascii=False, indent=1), encoding="utf-8") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())