diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md
index 61703d8d..e876810a 100644
--- a/docs/PROGRESS.md
+++ b/docs/PROGRESS.md
@@ -619,6 +619,8 @@
* ⭐⛔ **30.08, ПОСЛЕ КОМПАКТА: КЛЮЧИ ПОЧИНЕНЫ ВЛАДЕЛЬЦЕМ, ПРОБНАЯ КЛЕТКА КУПЛЕНА ($0.038276) — И ГЛАВНОЕ ОНА ОТДАЛА БЕСПЛАТНО (Д45.5, Д46).** Несущая улика — не размер размышления, а **входные токены**: при побайтно одинаковых `messages` (sha256 сверен исполнением) вызов без параметра дал `prompt_tokens` **3270**, вызов с `reasoning_effort:"low"` — **3191**, то есть **−79**. **Два контроля детерминизма:** замер 05.08 (дефолт и `low` вернули ОДИНАКОВЫЕ 2124) и пред-полётные пробы самого рига (`84 · 84 · 84 · 84` на одном тривиальном запросе, четвёртая куплена 30.08 — то есть **дефолтный счёт входа не сдвинулся через смену вендорского сервинга**). ⇒ **параметр `low` на `deepseek-v4-pro` ТЕПЕРЬ ДОХОДИТ ДО МОДЕЛИ**, статус квирк-бюллетеня «НЕ УСТАНОВЛЕНО» закрыт в части «доходит ли» (новая запись **3г**). ⛔ **ВЕЛИЧИНА среза — НЕТ, и это находка против моего же пре-рега:** `RN` куплен 21.08, `RNL` — 30.08, а между блоками лежит **документированная смена вендорского сервинга** (её же зафиксировала вахта Д44) — сравнение приписывает ручке всё, что вендор поменял за девять дней; дрейф из §3б — второй конфаунд и слабее (у него есть знак, и он работает против нуля). Раздел Д45.3 «чего замер не закроет» не назвал НИ ОДНОГО — **дефект найден пере-чтением ПОСЛЕ фриза `87dd124`, печатаю против себя**. ⇒ ⭐ **Лечение одно: оба арма в ОДНОМ блоке вперемежку — и такой замер КУПИТЬ МОЖНО, вчетверо дешевле замороженного:** дизайн Д (интерливинг `RN2` против `RNL` на восьми дешёвых главах) стоит **$0.078–0.133** при свободных $0.2472, n=8 пар, минимальный двусторонний p = 0.0078; гард кассы его пропускает (проверено по коду, гард не тронут). **Пре-регистрация — Д47, заморожена коммитом, ⛔ НЕ КУПЛЕНО: санкция владельца была на ДРУГОЙ дизайн, переносить её я не вправе.** Рекомендация: пятнадцать клеток по фризу Д45 (дизайн А) **не покупать ни при каком ответе**; Д47 — по его слову. ⚠ **Первая редакция Д46 утверждала обратное — «ни один неконфаундированный дизайн не влезает» — и это было НЕВЕРНО** (пропущена дешёвая восьмёрка, при том что бимодальность нашла та же секция); сломал адверсариальный проход, разбор и цена ошибки — **Д46.6**, там же единственная находка прохода, которую я ОТКЛОНИЛ (корреляция по порядку вызовов: у всех 16 клеток `RN` один mtime, порядка не существует). Боевая ступень редактора **не тронута** (`eval/tenant_panel/roster.py` — правлен только комментарий, который до сегодня объяснял выбор снятой посылкой; гейт пака пере-снят, расхождение то же единственное mtime-шное).
+* ⭐⭐ **30.08, Д47 ЗАПУЩЕН ПО САНКЦИИ ВЛАДЕЛЬЦА И ОСТАНОВЛЕН ГАРДОМ ПОСЛЕ ПЕРВОЙ ПАРЫ — А КУПЛЕННОЕ ОКАЗАЛОСЬ ЦЕННЕЕ ЭНДПОЙНТА (Д47.6).** Списано **$0.134804**, ФД-N 3.187585 из 3.30, потолок НЕ поднимался. **Побайтно один промт, одна глава `b065a92dc0`, `reasoning_effort` не слался НИ РАЗУ: 21.08 модель думала 466 токенов, 30.08 — 17 757. ×38.** Конфаунд, которым я двумя часами раньше обосновывал отказ от дизайна А, теперь ЗАМЕРЕН, а не выведен: пятнадцать клеток, сравнённых с августовским `RN`, дали бы число, целиком утонувшее в этом сдвиге. ⚠ При n=1 «сдвиг во времени» и «разброс розыгрыша на побайтно одном входе» (квирк п.2) неразличимы — и **второе чтение обвиняет уже МОЙ дизайн**: один розыгрыш на главу такого шума не переиграет. ⭐ Побочно Клейм 1 подтверждён на БОЕВОМ промте: `prompt_tokens` = **3256 и 21.08, и 30.08** (через девять дней и смену сервинга), а `low` двигает их ровно на **−79**; гейт провенанса пройден (Δpt=79, кириллица 1.000, латиница 0). **Эндпойнт по правилу: внутри блока ручка дала ×1.40** (меньше ×2.52, полученных через девять дней — конфаунд эффект РАЗДУВАЛ), годных пар 1 из 8, знаковый тест p=1.0 ⇒ ⛔ **«НЕ УСТАНОВЛЕНО»**, добор глав после чисел запрещён. Носитель — `eval/dovodka/stupen.py`, написан ДО прихода клеток, селфтест стережёт, что порог достижим только единогласием. ⛔ **Смета ошиблась на порядок, и это моя восьмая ошибка того же класса:** $0.078–0.133 объявлялись ценой ВОСЬМИ пар, одна пара стоила $0.1348 — **я оценил опыт по той самой базе, нестабильность которой опыт и должен был показать**. Доделать дизайн сегодня ≈$0.94 (подъём ФД-N на ~$0.85, резерв пака $1.09 покрывает) — **не рекомендую**, нужен пак с несколькими розыгрышами на главу. ⚠ Операционное для чужих циклов: отказ гарда возвращает код 1, но в ФОНОВОМ shell `set -e` цикл не остановил — семь итераций вхолостую, $0.
+
**📌 ПИНГ ПОЛИГОНУ от оркестратора №19 (28.08, D39.164) — вендор-вердикт по Gemini для `docs/experiments/00-provider-quirks.md`.**
Материал добыт бэкенд-сессией (curl по вендор-доке, не пересказ), но файл — ВАША зона, поэтому вписываете вы, а не я. Строка 157 сегодня
несёт три утверждения сильнее, чем даёт источник:
diff --git a/docs/experiments/00-provider-quirks.md b/docs/experiments/00-provider-quirks.md
index 9161014b..fc87630d 100644
--- a/docs/experiments/00-provider-quirks.md
+++ b/docs/experiments/00-provider-quirks.md
@@ -82,6 +82,8 @@
**3г. ⭐ ПОВЕДЕНЧЕСКАЯ ПРОБА 2026-08-30 (полигон, фаза Д, $0.038276): ПАРАМЕТР `low` НА `pro` ТЕПЕРЬ ДОХОДИТ ДО МОДЕЛИ.** Дока 3а требовала живой пробы перед денежным решением — вот она. **Несущая улика — не размер размышления, а `prompt_tokens`:** при **побайтно одинаковых** `messages` (sha256 `51e7f427940cae15`, проверено исполнением) вызов без параметра дал **3270** входных токенов, вызов с `reasoning_effort:"low"` — **3191**, то есть **−79**. Тело вызова различалось РОВНО одним полем (`eval/dovodka/rol.py:668`=`def call_kwargs`), исходник главы приколот манифестом с гейтом остановки (`eval/role_topology/pair_en.py:132`=`def units`), шаблон промта не менялся с 20.08, кэш смещения не даёт (две клетки с `cached_tokens=0` дали остатки −31.8 и +10.4 при разбросе ±20–44). **ДВА контроля детерминизма.** (1) Замер 05.08 ниже: при побайтно одинаковых `messages` дефолт и `low` вернули ОДИНАКОВЫЕ 2124 — провайдер повторяет `prompt_tokens` в точности, когда параметр не действует; ⚠ но он снят ДО смены сервинга и на другом промте. (2) **Межэпохальный контроль — пред-полётные пробы того же рига:** один и тот же тривиальный запрос БЕЗ ручки дал `prompt_tokens` = **84 · 84 · 84 · 84**, причём четвёртая куплена 30.08 за две минуты до испытуемого вызова ⇒ **дефолтный счёт входа не сдвинулся ни на токен через смену вендорского сервинга**, и −79 принадлежит параметру, а не эпохе. ⚠ **Регулярность без объяснения:** 2203 − 2124 = **79** (05.08, `xhigh` над дефолтом) и 3270 − 3191 = **79** (30.08, дефолт над `low`) на совершенно разных промтах — похоже на серверную вставку фиксированного размера, но это догадка. ⇒ **Статус `low` на `pro` закрыт В ЧАСТИ «доходит ли»: ДОХОДИТ.** ⛔ **ВЕЛИЧИНА среза размышления ПО-ПРЕЖНЕМУ НЕ УСТАНОВЛЕНА:** наблюдённые ×2.52 (19 559 → 7 748 токенов) сняты БЛОКАМИ, разнесёнными на 9 дней. ⛔ **Несущий конфаунд здесь — НЕ дрейф, а сама смена сервинга, зафиксированная выше в 3а:** вендор переписал маппинг между двумя блоками, и сравнение «арм 21.08 против арма 30.08» приписывает ручке всё, что он поменял. Дрейф (строка в 3б) — второй и более слабый: у него есть ЗНАК (все шесть розыгрышей монотонно РАСТУТ), и при его продолжении ×2.52 было бы нижней границей среза. Чтобы величина стала известной, нужен интерливинг обоих армов в ОДНОМ блоке; пре-регистрация и цены — `docs/experiments/23-editor-tier.md` §Д46.3 и §Д47 (дорогая восьмёрка $0.565–0.620, дешёвая — $0.078–0.133). ⚠ **Эхо на `low` у `pro`: одна английская клетка дала кириллицу 1.000 и ноль латиницы — это НЕ снимает предупреждение «Не бесплатно» ниже**, эхо-мина живёт на плотном CJK, а проба была на английской паре.
+ **3д. ⭐⭐ ЗАМЕРЕНО 30.08 ($0.134804, интерливинг Д47): ДЕФОЛТНЫЙ ПУТЬ `deepseek-v4-pro` СДВИНУЛСЯ ×38 ЗА ДЕВЯТЬ ДНЕЙ — ИЛИ РАЗБРОС РОЗЫГРЫША ТАКОВ.** Побайтно один промт (sha256 сверен), одна и та же глава, `reasoning_effort` НЕ слался ни разу: 21.08 модель думала **466** токенов, 30.08 — **17 757**. Цена той же клетки $0.005034 → $0.076893. ⚠ При n=1 «сдвиг во времени» и «разброс на побайтно одном входе» (п.2 выше) неразличимы — но для практики это одно и то же предупреждение: **любая смета DeepSeek, снятая с прошлого прогона, может ошибиться на порядок, и денежные планы на неё ставить нельзя**. Проверено на себе: пре-регистрация оценила восемь пар в $0.078–0.133, одна пара стоила $0.1348. ⭐ **Зато счёт ВХОДА через тот же разрыв стабилен до токена:** `prompt_tokens` = 3 256 и 21.08, и 30.08 при не-присланной ручке, а `low` сдвигает его ровно на −79 (3 177) — то есть провенанс параметра читается по входу даже там, где выход уехал ×38. ⚠ Наблюдённый ВНУТРИ ОДНОГО БЛОКА эффект ручки — ×1.40 (17 757 → 12 676), заметно меньше ×2.52, полученных сравнением через девять дней: конфаунд эффект РАЗДУВАЛ. n=1, знаковый тест p=1.0 ⇒ величина по-прежнему **НЕ УСТАНОВЛЕНА**; разбор — `docs/experiments/23-editor-tier.md` §Д47.6.
+
**3в. ВАХТА D39.92 ИСПОЛНЕНА ПОВТОРНО 2026-08-10 (полигон, фаза Д; $0, `curl` HTTP 200).**
Таблица маппинга 3а на `guides/thinking_mode` **побайтно та же** (снята разбором ячеек `
`:
`low`→low/high · `high`→high/high · `xhigh`→high/**max** · `max`→max/max), и сноска (3)
diff --git a/docs/experiments/23-editor-tier.md b/docs/experiments/23-editor-tier.md
index 3638234a..6511cf15 100644
--- a/docs/experiments/23-editor-tier.md
+++ b/docs/experiments/23-editor-tier.md
@@ -6184,7 +6184,7 @@ n = 16 английских единиц = 7.89 главы Гу · k = 5 арм
* **Находка 1 (декой) — диспозиция за приёмкой**, не за сессией. От неё зависит, считать ли Шаг 4
закрытым по букве или по цели.
* **Находка 11** — пометы «контраст условный» на телах Д23/Д24 не поставлены.
-* `docs/PROGRESS.md:673`=`ЖИВОЙ ДЕФЕКТ ДВИЖКА` — шапка пинга №19 теперь ложна дважды (дефект
+* `docs/PROGRESS.md:675`=`ЖИВОЙ ДЕФЕКТ ДВИЖКА` — шапка пинга №19 теперь ложна дважды (дефект
движка закрыт 28.08; терялся промт, а не глоссарий) и баннера не имеет. **Чужая зона, пинг дан.**
---
@@ -7080,3 +7080,55 @@ RN» переносила отношение с САМОЙ ДУМАЮЩЕЙ гл
⇒ **Даже полный успех этого замера НЕ разрешает менять боевую ступень редактора.** Он двигает
ровно одну клетку знания: «ручка режет» перестаёт быть вендорским заявлением и становится замером.
+
+### Д47.6 ⭐⭐ РЕЗУЛЬТАТ: КУПЛЕНА ОДНА ПАРА, ГАРД ОСТАНОВИЛ ОСТАЛЬНОЕ — И ЭТА ПАРА СТОИТ БОЛЬШЕ, ЧЕМ ЭНДПОЙНТ
+
+Санкция владельца 30.08 — дословно «Запускай». Прогон пошёл интерливингом, как заморожено: глава
+`b065a92dc0`, сначала контроль `RN2` (ручка не шлётся), сразу за ним `RNL` на `low`. **Списано
+$0.134804** (клетки $0.133588 + две пред-полётные пробы $0.001216). После первой же пары
+**гард кассы отказал** — `потрачено $3.187585 + ожидание $0.131016 > потолок $3.30` — и семь
+оставшихся пар не куплены. Потолок сессией не поднят.
+
+| клетка | когда | ручка | `prompt_tokens` | выход | **размышление** | цена | латентность |
+|---|---|---|---|---|---|---|---|
+| `RN` | 21.08 | не слалась | **3 256** | 1 052 | **466** | $0.005034 | 19.5 с |
+| `RN2` | 30.08 | не слалась | **3 256** | 18 332 | **17 757** | $0.076893 | 260.8 с |
+| `RNL` | 30.08 | `low` | **3 177** | 13 258 | **12 676** | $0.056695 | 173.0 с |
+
+⭐⭐ **ГЛАВНОЕ И НЕ ТО, ЗА ЧЕМ ШЛИ. Побайтно один промт, одна глава, ручка не слалась НИ РАЗУ —
+и размышление 466 против 17 757, то есть ×38.** Конфаунд, названный в Д46.2 рассуждением, теперь
+**замерен**, и он огромен. Это ретроспективно решает судьбу дизайна А: пятнадцать клеток `RNL`,
+сравнённых с `RN` от 21.08, дали бы число, целиком утонувшее в этом сдвиге.
+
+⚠ **И приписать сдвиг нечему: при n=1 «время» и «разброс розыгрыша» неразличимы.** Квирк-бюллетень
+п.2 давно печатает разброс на побайтно одном входе; сегодня он же мог сработать. **Оба чтения
+обвиняют дизайн А — но второе обвиняет и МОЙ дизайн Д:** один розыгрыш на главу не переиграет шум
+такого размера, каким бы ни было n.
+
+⭐ **Побочно — усиление Клейма 1 на БОЕВОМ промте, а не на тривиальной пробе.** `prompt_tokens` у
+`RN` (21.08) и `RN2` (30.08) — **3 256 и 3 256, до токена**, через девять дней и смену вендорского
+сервинга; а ручка `low` сдвигает их на **ровно 79** (3 177). Гейт провенанса Д47.3 пройден:
+Δpt = 79 внутри полосы [60,100], кириллица 1.000, латиница 0, `finish=stop` у обеих клеток.
+
+**Эндпойнт — по правилу, а не по желанию.** Внутри блока `RNL` думал меньше `RN2` в **×1.40**
+(17 757 → 12 676). Годных пар **1 из 8**, знаковый тест даёт **p = 1.0000** ⇒ ⛔ **«НЕ УСТАНОВЛЕНО»**,
+и добор глав после взгляда на числа запрещён (Д47.3). Носитель вывода — `eval/dovodka/stupen.py`,
+написан ДО прихода клеток. ⚠ Заметить стоит и то, что ×1.40 внутри блока **много меньше** ×2.52,
+полученных сравнением через девять дней: конфаунд не просто мешал — он РАЗДУВАЛ видимый эффект.
+
+⛔ **МОЯ СМЕТА ОШИБЛАСЬ НА ПОРЯДОК, И ИРОНИЯ ТОЧНАЯ: я оценил опыт по той самой базе, нестабильность
+которой опыт и должен был показать.** $0.078–0.133 объявлялись ценой ВОСЬМИ пар; одна пара стоила
+$0.1348. Класс ошибки тот же, что у семи прежних: число взято из прошлого замера без вопроса, живо
+ли оно.
+
+**Что стоило бы доделать дизайн сегодня:** семь пар по наблюдённой цене ≈ **$0.94**, то есть подъём
+ФД-N примерно на $0.85 (резерв пака $1.090316 это покрывает). ⛔ **Не рекомендую.** Наблюдённый
+внутриблочный эффект ×1.40 стоит против шума, который на соседней строке показал ×38; дизайн с
+одним розыгрышем на главу против такого шума недомощен, и восемь пар этого не чинят. Вопрос
+переносится в пак, который может позволить **несколько розыгрышей на главу на арм** — и цену
+которого теперь считают по СЕГОДНЯШНЕЙ базе, а не по августовской.
+
+⚠ **Операционная заметка, чтобы не повторить:** отказ гарда возвращает код 1 (проверено), но в
+фоновом shell `set -e` цикл не остановил — семь оставшихся итераций отработали вхолостую, печатая
+тот же отказ. Денег это не стоило ($0: гард отклонил и пред-полётную пробу), но цикл на сотню клеток
+в такой форме выглядел бы как прогон. Следующий цикл несёт `|| break` в теле, а не надежду на `set -e`.
diff --git a/eval/dovodka/SESSION-30-08.md b/eval/dovodka/SESSION-30-08.md
index 37a4e208..de10ea24 100644
--- a/eval/dovodka/SESSION-30-08.md
+++ b/eval/dovodka/SESSION-30-08.md
@@ -69,7 +69,21 @@ workaround, который канон запрещает («ошибка тих
3. ⭐ **Неконфаундированный замер КУПИТЬ МОЖНО, и он вчетверо дешевле замороженного** — дизайн Д:
интерливинг `RN2` против `RNL` на восьми дешёвых главах, **$0.078–0.133** при свободных $0.2472,
n=8, минимальный двусторонний p = 0.0078. Пре-регистрация — **Д47**, ⛔ **не куплено**.
-⇒ **Вопрос владельцу: запускать ли Д47.** Рекомендация: (а) пятнадцать клеток по фризу Д45 —
+⛔⛔ **Д47 ЗАПУЩЕН ПО СЛОВУ ВЛАДЕЛЬЦА («Запускай») И ОСТАНОВЛЕН ГАРДОМ ПОСЛЕ ПЕРВОЙ ЖЕ ПАРЫ.**
+Списано **$0.134804**, ФД-N теперь 3.187585 из 3.30 (свободно $0.1124), потолок НЕ поднимался.
+**Результат крупнее эндпойнта:** побайтно один промт, одна глава, ручка не слалась ни разу — 21.08
+модель думала **466** токенов, 30.08 — **17 757**, то есть **×38**. Конфаунд из Д46.2 замерен, а не
+выведен. ⚠ При n=1 «время» и «разброс розыгрыша» неразличимы, и второе чтение обвиняет мой же
+дизайн Д. Внутри блока ручка дала ×1.40 (меньше, чем ×2.52 через девять дней — конфаунд эффект
+РАЗДУВАЛ); годных пар 1 из 8, p=1.0 ⇒ **величина НЕ УСТАНОВЛЕНА**, добор глав запрещён. ⭐ Побочно:
+`prompt_tokens` = 3256 и 21.08, и 30.08, а `low` двигает их ровно на −79 — Клейм 1 подтверждён на
+боевом промте. Доделать дизайн стоило бы ≈$0.94 (подъём ФД-N на ~$0.85) — **не рекомендую**: эффект
+×1.40 против шума ×38, один розыгрыш на главу такого шума не переиграет. Разбор — **Д47.6**,
+носитель вывода — `eval/dovodka/stupen.py` (написан ДО клеток).
+⚠ **Операционное:** отказ гарда возвращает код 1, но в ФОНОВОМ shell `set -e` цикл не остановил —
+семь итераций отработали вхолостую ($0). Следующий цикл покупок несёт `|| break` в теле.
+
+⇒ **Прежняя развилка (закрыта):** (а) пятнадцать клеток по фризу Д45 —
**НЕ покупать** ни при каком ответе; (б) Д47 — по его слову. **Величина среза остаётся НЕ
УСТАНОВЛЕННОЙ, и так и записана.**
⚠ **Первая редакция Д46 утверждала «ни один неконфаундированный дизайн не влезает» — ЭТО БЫЛО
diff --git a/eval/dovodka/stupen.py b/eval/dovodka/stupen.py
new file mode 100644
index 00000000..ea971844
--- /dev/null
+++ b/eval/dovodka/stupen.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python3
+"""ЭНДПОЙНТ ДИЗАЙНА Д (пре-рег Д47): РЕЖЕТ ЛИ `reasoning_effort:"low"` РАЗМЫШЛЕНИЕ У `pro`. $0.
+
+⚠ ЗАЧЕМ ОТДЕЛЬНЫЙ ФАЙЛ, а не счёт в уме отчёта. Правило решения Д47.3 написано ДО чисел, и оно
+обязано применяться механически, иначе «правило до чисел» превращается в «правило под числа».
+Файл написан ДО прихода клеток и коммитится вместе с ними: кто угодно пере-снимает вывод одной
+командой и видит ровно то, что видел автор.
+
+ЧТО ЗДЕСЬ ЕСТЬ И ЧЕГО НЕТ:
+ * есть парный знаковый тест по восьми главам (`reasoning_tokens` у `RNL` против `RN2`);
+ * есть по-клеточный гейт годности В ТОЙ ФОРМЕ, В КАКОЙ ОН ЗАМОРОЖЕН, включая проверку
+ провенанса `Δprompt_tokens ∈ [60,100]` — она ловит клетку, до которой параметр НЕ доехал;
+ * НЕТ ни одной ручки, которой можно подвинуть порог: α, полосы и список глав — константы файла,
+ и селфтест стережёт, что они те же, что в отчёте.
+
+⛔ ОТНОШЕНИЯ (×N) ПЕЧАТАЮТСЯ КАК ОПИСАНИЕ, А НЕ КАК ВЫВОД. Дизайн Д стоит на восьми ДЕШЁВЫХ главах
+(10.2% цены панели) — переносить их величины на дорогую моду запрещено там же, где выбраны главы.
+
+Запуск: eval/.venv/bin/python eval/dovodka/stupen.py [--selftest]
+"""
+
+from __future__ import annotations
+
+import json
+import math
+import sys
+from pathlib import Path
+
+ZONE = Path(__file__).resolve().parent
+sys.path.insert(0, str(ZONE))
+
+import rol # noqa: E402
+
+OUT = Path.home() / "books" / "dovodka"
+
+# ── КОНСТАНТЫ ПРЕ-РЕГА Д47. Меняются только вместе с отчётом и только ДО покупок ───────────────
+CHAPTERS = ("b065a92dc0", "5a8f48d24a", "100b088f71", "49ca859c94",
+ "8e50448cea", "c3517980c7", "3bd6481182", "26c3bff1d4")
+ARM_HIGH, ARM_LOW = "RN2", "RNL" # контроль (ручка не шлётся) и испытуемый (`low`)
+ALPHA = 0.05 # двусторонний, Д47.3
+DPT_BAND = (60, 100) # провенанс параметра: ожидается 79 (Д46.1)
+CYR_MIN = 0.99 # доля кириллицы среди букв
+LAT_MAX = 0 # латиница-эхо
+
+
+def _letters(t: str) -> tuple[int, int, int]:
+ """(букв всего, кириллических, латинских)."""
+ al = [c for c in t if c.isalpha()]
+ cyr = sum(1 for c in al if "а" <= c.lower() <= "я" or c.lower() == "ё")
+ lat = sum(1 for c in al if "a" <= c.lower() <= "z")
+ return len(al), cyr, lat
+
+
+def _cell(arm: str, uid: str) -> dict | None:
+ f = OUT / f"{rol.tag('en', arm, uid)}.json"
+ return json.loads(f.read_text(encoding="utf-8")) if f.exists() else None
+
+
+def sign_p(k: int, n: int) -> float:
+ """Точный двусторонний знаковый тест при p=0.5. k — сколько пар легли в ожидаемую сторону."""
+ if n == 0:
+ return 1.0
+ tail = sum(math.comb(n, i) for i in range(k, n + 1)) / 2 ** n
+ return min(1.0, 2 * tail)
+
+
+def main() -> int:
+ rows, missing = [], []
+ for uid in CHAPTERS:
+ hi, lo = _cell(ARM_HIGH, uid), _cell(ARM_LOW, uid)
+ if not hi or not lo:
+ missing.append(uid)
+ continue
+ n_al, cyr, lat = _letters(lo.get("content") or "")
+ share = cyr / n_al if n_al else 0.0
+ dpt = hi["prompt_tokens"] - lo["prompt_tokens"]
+ bad = []
+ if hi.get("finish") != "stop" or lo.get("finish") != "stop":
+ bad.append("finish≠stop")
+ if share < CYR_MIN:
+ bad.append(f"кириллица {share:.3f}")
+ if lat > LAT_MAX:
+ bad.append(f"латиница {lat}")
+ if not DPT_BAND[0] <= dpt <= DPT_BAND[1]:
+ bad.append(f"Δpt {dpt} вне [{DPT_BAND[0]},{DPT_BAND[1]}] — параметр не доехал")
+ rows.append(dict(uid=uid, hi=hi["reasoning_tokens"], lo=lo["reasoning_tokens"],
+ dpt=dpt, cyr=share, lat=lat, bad=bad,
+ cost=hi["cost_usd"] + lo["cost_usd"]))
+
+ if missing:
+ print(f"⛔ клеток нет у глав: {' '.join(missing)} — эндпойнт неполон")
+ if not rows:
+ return 1
+
+ print(f"{'глава':12}{'RN2 дум':>9}{'RNL дум':>9}{'отнош':>8}{'Δpt':>6}{'кирилл':>8}"
+ f"{'лат':>5} гейт")
+ for r in rows:
+ ratio = r["hi"] / r["lo"] if r["lo"] else float("inf")
+ print(f"{r['uid']:12}{r['hi']:>9}{r['lo']:>9}{ratio:>8.2f}{r['dpt']:>6}"
+ f"{r['cyr']:>8.3f}{r['lat']:>5} "
+ f"{'OK' if not r['bad'] else '⛔ ' + ' · '.join(r['bad'])}")
+
+ good = [r for r in rows if not r["bad"]]
+ n = len(good)
+ k = sum(1 for r in good if r["lo"] < r["hi"])
+ p = sign_p(k, n)
+ ratios = sorted(r["hi"] / r["lo"] for r in good if r["lo"])
+ med = ratios[len(ratios) // 2] if ratios else float("nan")
+
+ print(f"\nгодных пар {n} из {len(CHAPTERS)} · размышление у `low` МЕНЬШЕ в {k} из {n}")
+ print(f"знаковый тест, двусторонний: p = {p:.4f} при пороге α = {ALPHA}")
+ print(f"цена прогона: ${sum(r['cost'] for r in rows):.6f}")
+
+ # ── ПРАВИЛО Д47.3. Ветки написаны до чисел; ни одна из них ничего не «дозакупает» ──────────
+ print()
+ if n < len(CHAPTERS):
+ print("⚠ ПАР МЕНЬШЕ ВОСЬМИ: правило Д47.3 писалось на n=8. Вывод печатается по факту "
+ "годных пар и объявляется НЕПОЛНЫМ — добор до восьми ПОСЛЕ взгляда на числа запрещён.")
+ if k == n and p < ALPHA:
+ print(f"⭐ ЕДИНОГЛАСНО И ЗНАЧИМО: ручка РЕЖЕТ размышление. Медиана отношения ×{med:.2f} — "
+ "ОПИСАНИЕ этих восьми дешёвых глав, не перенос на дорогую моду и не на пару zh.")
+ elif p >= ALPHA:
+ print(f"⛔ НЕ ЗНАЧИМО (p = {p:.4f} ≥ {ALPHA}): «не установлено». Это РЕЗУЛЬТАТ, а не повод "
+ "докупать девятую главу — добор до значимости после чисел запрещён (Д47.3).")
+ else:
+ print(f"⛔ ЗНАЧИМО В ОБРАТНУЮ СТОРОНУ (k={k} из {n}): ручка размышление НЕ режет.")
+ return 0
+
+
+def selftest() -> int:
+ bad = 0
+
+ def ck(ok: bool, what: str) -> None:
+ nonlocal bad
+ print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {what}")
+ bad += 0 if ok else 1
+
+ ck(len(CHAPTERS) == 8 and len(set(CHAPTERS)) == 8, "восемь глав пре-рега, без повторов")
+ rep = (ZONE.parents[1] / "docs" / "experiments" / "23-editor-tier.md").read_text(encoding="utf-8")
+ ck(all(u in rep for u in CHAPTERS), "все восемь глав названы в отчёте (пре-рег Д47.2)")
+ ck(ARM_HIGH in rol.ARMS and ARM_LOW in rol.ARMS, "оба арма есть в реестре покупок")
+ ck(rol.THINK_LEVEL.get(ARM_HIGH) is None and rol.THINK_LEVEL.get(ARM_LOW) == "low",
+ "контроль без ручки, испытуемый на `low`")
+ ck(rol.ARMS[ARM_HIGH][0] == rol.ARMS[ARM_LOW][0] == "deepseek-v4-pro", "модель у армов одна")
+ ck(rol.tag("en", ARM_HIGH, "x") != rol.tag("en", ARM_LOW, "x"), "теги армов не схлопываются")
+ # правило решения обязано быть НЕ достижимо ничем, кроме единогласия — это и есть цена n=8
+ ck(sign_p(8, 8) < ALPHA <= sign_p(7, 8), "при n=8 значимость даёт ТОЛЬКО 8 из 8")
+ ck(abs(sign_p(8, 8) - 0.0078125) < 1e-9, "минимальный двусторонний p посчитан верно")
+ ck(sign_p(4, 8) == 1.0, "полный ноль эффекта даёт p = 1")
+ print(f"\n{'ПРИБОР ГОДЕН' if not bad else f'ПРОВАЛОВ: {bad}'}")
+ return bad
+
+
+if __name__ == "__main__":
+ sys.exit(selftest() if "--selftest" in sys.argv else main())
|