Close orchestrator ten: hand the unaccepted effort-handle pack to the successor, trim the bank research, open six tracker holes as rows and raise Go lint conventions to the owner

This commit is contained in:
Claude (backend session) 2026-08-02 19:00:23 +03:00
parent 8be1e41ee1
commit bead5cc612
3 changed files with 477 additions and 9 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
# Журнал решений оркестратора — контракт D1D39.89 (развязки 04.07 · пакеты 0910.07 · приёмка/качество-первым/пивот/эмпирика 1112.07 · арх-ресет+стройка пере-прогонного стека 1319.07)
# Журнал решений оркестратора — контракт D1D39.90 (развязки 04.07 · пакеты 0910.07 · приёмка/качество-первым/пивот/эмпирика 1112.07 · арх-ресет+стройка пере-прогонного стека 1319.07)
> **КАРТА АКТУАЛЬНОСТИ (ревизия D31, продлена до D38.2 [12.07]; исторические записи ниже НЕ переписываются — дисциплина D23.3).** Читая контракт целиком, держи под рукой, что чем перекрыто:
> ⚠ **Навигация (актуализация 01.08):** карта ниже детально покрывает D1D39.28; решения D39.29+ живут хронологически в теле файла, **свежая голова — С ХВОСТА** (новые ноты аппендятся вниз). Сводка текущей головы и очередь — CURRENT-STATE в `../PROGRESS.md`.
@ -1303,3 +1303,23 @@ API-529-долг закрыт: 8-осевой refute-by-default воркфлоу
**Промт выдан — `docs/BANK_ARBITRATION_RESEARCH_SESSION_PROMPT.md`** (ресёрч+полигон сессия; зона: `docs/research/24-bank-arbitration.md` + `eval/bank_arbitration/` + секция PROGRESS; НЕ коммитит). Четыре работы: **A** критика текущего алгоритма в коде ($0, вердикт-тройки, дефекты классами сломано/хрупко/дорого/слепо) · **B** арбитраж (B1 голд — гейт остального; ядро уже есть: `guzhenren-seed-v2.yaml` 58 записей / 53 `approved` + дистилл-фикстура BANK-FULL 150 поверхностей + банк coldrun-a, смещение выборки назвать честно; B2 калибровка уверенности ≤$0.60 — разделяющая способность, числа против слов, устойчивость к повтору, **сравнимость МЕЖДУ моделями**, канал отказа, и обязательный БАР против бесплатного базиса §C2-3; B3 формы консилиума ≤$0.80 с контролем «одна модель N раз» — ответ на Q2) · **C** инжект/фетч ≤$0.60 (мерить ПОСЛУШАНИЕ и ВРЕД, не recall) · **D** рекомендованные алгоритмы тремя корзинами (строить сейчас · измерить · закрыть по нужности; последняя обязана быть непустой). Потолок **$2.00** с суб-потолками и пре-регистрацией критерия до каждой пробы. **Легитимные исходы, объявленные заранее:** «консилиум не бьёт базис — закрыть» · «уверенность не разделяет — закрыть» · «голда мало, вот точный запрос владельцу» · «рычаг в инжекте, а не в арбитраже». Мандат ревизии посылок (D39.68) и правило №4 (D39.47) вшиты.
**Гигиена выдачи:** промт холодно аудирован автором перед выдачей по `file:line`/D-номерам; исправлена одна мис-атрибуция (фактура премеров — **D39.50**, отчёт `archive/reports/POLYGON_PREMEASURE_2026-07-26.md`, а не D39.71). ⚠ **Коллизия зон названа в промте:** `backend/` ЖИВАЯ (сессия строк 104/46/16), `coldrun-a` — замороженный эталон только на чтение, `coldrun-b` — стенд живой сессии; пробы гонять СВОИМ харнессом против API, не через движок. Валидный базлайн качества банка — **coldrun-a**, НЕ coldrun-b (там терминолог сломан транспортом: 1 терм из 81, D39.86) — сравнение с поломкой даст фальшивую победу консилиума и будет отклонено приёмкой.
## D39.90 — ЗАКРЫТИЕ СЕССИИ ОРКЕСТРАТОРА №10: пак ручки эффорта отработан и ЖДЁТ ПРИЁМКИ преемника, банк-ресёрч подрезан, дыры трекера заведены строками, форма архитектурных гардов и конвенции Go-репо подняты владельцем в решение (02.08). ✅
**Повод — слово владельца: «эту сессию мы завершаем, дела передаёшь; главное важную инфу не потерять».** Нота собирает состояние на момент передачи; вода и хроника опущены намеренно.
**1. Бэкенд-пак строк 104/46/16 ОТРАБОТАН, НЕ ПРИНЯТ, НЕ ЗАЛЕНДЕН.** Отчёт `archive/reports/EFFORT_HANDLE_2026-08-02.md` закоммичен с ⚠-баннером «коммит ≠ ратификация» — только чтобы артефакт пережил смену сессий. **Код лежит в дереве НЕЗАКОММИЧЕННЫМ** (новые `internal/config/internal_call.go`, `internal_call_test.go`, `pipeline/synthetic_stage_seam_test.go`; правлены `models.go`/`pipeline.go`/`capability.go`/`escalation.go`/`repair.go`/`runner.go`/`stagerun.go`/`terminologist.go`/`live_reprobe_test.go`/`capability_test.go`) — **дерево не чистить, history-rewrite и `checkout` поверх него запрещены.** Заявления сессии, которые приёмка обязана пере-проверить исполнением: работа A = ДВА шва (`config.InternalCall{…}.Stage()` + `Runner.attemptRequest`), рукописных `config.Stage{}` не осталось ни одного, все четыре сборки `RequestHash(Request{…})` схлопнуты в одну, ловушка хеш-оси закрыта в обе стороны на обоих путях; **перекупки нет** (`gates.terminology` не фолдится в снапшот, кортеж воспроизведён побайтно — `8e4495006b978ed4` в обе стороны, голден PASS без пере-захвата); гранулярность решена ЗАМЕРОМ (парная выборка N=5: `low` 4/5 против `high` 5/5 неразличимо при цене `high` ×2.30 ⇒ вторая ручка `classify_reasoning`, построенная на n=1, СНЯТА самой сессией); `Temperature` зафиксирована нулём осознанно.
**2. Проба банка на `low` ЗЕЛЁНАЯ и это главный технический результат дня** (строка 104, §3.4 промта; хост — свой проект `reprobe/bank-low/`, эталон `coldrun-a` только на чтение): терминолог **4/5 батчей `stop`, consolidated 72, unanswered 18, $0.005750**; классификатор 5/5, reclassified 14, $0.004286; черновая волна 20/20 ok ($0.031639 flash + $0.010350 pro, 2 эскалации вылечили эхо); **весь заход $0.045095 из $0.10, леджер ≡ независимому пересчёту `usage`×цены до нуля** (строки 78 в этом заходе не было). Против вендор-дефолта `high` — 1/5 батчей и 1 терм из 81. **Оба молчащих пути исключены предъявлением** (терминолог 11.5% суб-бюджета, классификатор 21.4%, книга 45.1%, ноль WARN-строк). **Провод вскрыт: 35 тел, `reasoning_effort:"low"` во всех 35, ключей `thinking` ноль, `reasoning_content` непустой везде ⇒ гардрейл «thinking у DeepSeek не выключать» доказан ПРОВОДОМ, а не рассуждением.** ⚠ Несущий нюанс для будущего: `low` стену НЕ убирает, а разрежает — упавший батч вскрыт сырьём (`content=""`, `finish=length`, `completion=7999`, `reasoning=7999`), то есть тот же тяжёлый хвост, 1/5 вместо 4/5, и это стохастика вызова, а не размер входа. ⚠ На стенде пробы `escalation.budget_usd` сознательно понижен 0.10→0.015 ⇒ наблюдение эскалаций НЕПОЛНОЕ. ⚠ Боевой `coldrun-b/pipeline.yaml` НЕ тронут — там по-прежнему no-op `reasoning: "off"` на черновике.
**3. Работа B (строка 46) — ДИЗАЙН ПОДАН, КОДА НЕТ, стоит на решении владельца.** Сессия исполнила букву промта («вердикт-двигающее ⇒ сначала дизайн и пинг»). Три поправки к посылке, все замером, и они МЕНЯЮТ масштаб проблемы: **боевой гейт уже ловит 18 из 20** off-target событий (`sourceScriptShare > 0.15`, 0 ложных на 1055 здоровых, запас 13×) ⇒ остаточная дыра **0.18%** (2 чанка из ~1100, оба английские; японский ловится долей ханьцзы); **второй рубеж существует, но стоит не там** — санитайзер поймал бы те байты, однако `chunkrun.go:52` требует `isFinal`, а в шиппинговой двухстадийной топологии черновик НИКОГДА не финальный, и тот же `isFinal` глушит на черновике латинный линт, флаггер голоса и глоссарный пост-чек; здоровый минимум на тексте, который РЕАЛЬНО видит гейт (после среза банкноты), = 0.9767 при n=1055, а не 0.9510 при 1306 (сессия поправила собственное число). **Развилка владельцу: (а)** предикат в `classify()` + бамп `classifierVersion` — двигает снапшот у всех книг, голден пере-захват один раз, на свежем стенде $0; **(б)** отдельный опциональный гейт — снапшот не двигает, но это второй способ сказать то же самое и он выключен по умолчанию; **(в)** языковой детектор. Выбор сессии — **(а)**. Носитель — `lang.LangScripts(Book.TargetLang)`, НОВЫЙ go:embed-файл не заводить (`EmbeddedVersion` фолдится безусловно = перекупка всех книг всех пар). Три мины наивной реализации названы (новый флаг провалился бы мимо `escalatable()`/`retryable()` ⇒ переиспользовать `FlagCJKArtifact`; классификатор флагался бы ложно 16/16; экран слеп к ПОДМНОЖЕСТВАМ письменности — чисто китайский выход для →ja наберёт 1.000).
**4. Работа C (строка 16) НЕ НАЧАТА, закрыта владельцем как неуспешная на этом этапе; деньги фазы C не тронуты.** Редакторская волна по-прежнему не гонялась НИ РАЗУ ⇒ оси голоса (13б/24) без данных, полная цена холодного старта невыведена.
**5. Банк-ресёрч (строка 5): заход запущен и ДОСРОЧНО ЗАКРЫТ владельцем — «требуются фиксы в бэкенд». Артефакта в дереве НЕТ** (ни `docs/research/24-*`, ни `eval/bank_arbitration/`, ни свежих файлов в `~/books` — проверено) ⇒ **находки не сохранены; если они ценны, их надо вынуть из истории той сессии, иначе работа повторяется с нуля.** Диспозиция пере-запуска — ПОДРЕЗАТЬ: держать §3-A (критика кода, $0 — она и вскрывает нужные бэкенд-фиксы), B1 голд (гейт всего) и B3 формы консилиума с контролем «одна модель N раз»; свернуть B2 (калибровка уверенности) до под-пробы внутри B3 — как отдельная работа она дорога, а приоры D39.46 (судья = катастроф-экран, не ранжировщик) и D39.50 (канал отказа нестабилен, размах 0.375) уже сильно против неё; отложить §5-C (инжект/фетч) до бэкенд-доработок — мерить послушание инъекции без движка нечем. Потолок $2.00 → $1.20. **Да, после ресёрча последуют бэкенд-работы** — их предмет и есть деливерабл §3-A и §6-D промта.
**6. Дыры трекера закрыты строками (сверка по репозиторию, не по памяти).** Заведены: **106** слой 3 — контракт редактора остаётся full-regen при целевом diff-контракте (`09:121-126`), строки не было вовсе · **107** конвенции Go-репо (см. п.7) · **108** реестр загейченных триггеров — шесть реопенов жили только в телах D-нот, и триггер «появление Go-кода платформы» УЖЕ наступил (`platform/go.mod` заведён) · **109** требования без носителя (ПТ-6 · ПТ-7 · ПТ-22-интейк · ПТ-10-механизм) + гигиена перекрёстных ссылок реестра · **110** детектор повторов лемм / защита авторских рефренов · **111** `parseCategoryRows` молча last-wins (фикс дубль-ключа накрыл только `parseDCCheckers`). **Снято расхождение трекера:** строка 74 объявляла строку 44 закрытой — верно лишь для половины «поднять флор»; живой остаток обратный (число флора влияет на цену ретрая, не на проходимость). Уточнён гейт строки 93 (`分之`-часть гейчена полигон-разметкой, F4-хвост свободен). **Четыре пункта, поданные черновиком свода как долг, СНЯТЫ как уже закрытые решениями:** резолвер слоя 7 построен (`chunkrun.go:30-35`, D39.2 T3) · «санитайзер = чистый детектор» исполнен (D39.2 T2; остаточная мутация `StripCosmetic` — сама ратифицированная диспозиция D38.3 п.2а) · 36б — не бэкенд-стройка, а замер (D39.69) · полярность/ранг отменены D38.4, строка 2 и есть их загейченный носитель.
**7. Вопрос владельца, поднятый в решение (строка 107): форма архитектурных гардов и конвенции репозитория.** Предмет — `backend/internal/pipeline/synthetic_stage_seam_test.go` (395 строк): гард ходит по дереву через `go/parser` из обычного `_test.go`, резолвит пакет по ПУТИ импорта, несёт пол на число разобранных файлов. **Намерение верное и уже окупилось** (поймано 7 обходов: байтовая версия пробивалась пятью способами, AST-версия — ещё двумя, алиасом импорта и `[]config.Stage{{…}}`; сессия честно записала, что первая версия говорила `ok` на посаженном пятом сайте). **Дом неверный:** без типовой информации (`go/packages`+`types.Info` вместо эвристики по путям), в тестовом пакете, и каждый следующий инвариант породит ещё один такой обход. Индустриальная форма — analyzer на `golang.org/x/tools/go/analysis` (+`multichecker`, `go vet -vettool=`, CI), где алиас и дот-импорт закрываются ПО ПОСТРОЕНИЮ. Вторая половина вопроса: **в репозитории нет ни `.golangci.yml`, ни `Makefile`, ни CI-воркфлоу** (проверено) — батарея гоняется руками каждой сессией. Опасение владельца про хвост техдолга обоснованно ⇒ **первый шаг обязан быть $0-ЗАМЕРОМ** (набор в report-only, находки по правилам), и только потом состав + режим внедрения (baseline «только новый код» против big-bang). Решение — за владельцем, исполнение — следующей сессией оркестратора.
**8. Состояние зон на момент передачи.** `backend/` — незакоммиченный пак (п.1), сессия отработала и закрыта. `frontend/` — сессия ЖИВАЯ и коммитит сама по D39.88 (за день ушли `d8437d6`, `8d6b3f7`, `804dbc9`, `8be1e41`); правило pathspec-коммита до неё доехало только релеем владельца. `platform/` — кода нет, `go.mod` заведён. `eval/` — пусто по новому. **Очередь на момент передачи (пересчитано скриптом, не на глаз): 93 строки · зона бэкенд 45 строго / 59 широко · блокеров очереди 0 (104 переведена в «на приёмке») · «скоро» 11 (2·4·5·18·46·49·55·95·107·108·109).**

View file

@ -0,0 +1,435 @@
# Отчёт бэкенд-сессии: ручка эффорта · экран целевого языка · холодный прогон (строки 104 · 46 · 16)
> ⚠⚠ **ОТЧЁТ НЕ ПРИНЯТ. Коммит этого файла ≠ ратификация (оркестратор №10, 02.08, D39.90).** Файл закоммичен ТОЛЬКО чтобы артефакт не потерялся при смене сессий — приёмки не было: батарея независимо не пере-прогонялась, деньги вторым путём не пере-выводились, адверсариальный проход по правкам не гонялся. **Код сессии лежит в дереве НЕЗАКОММИЧЕННЫМ** (`backend/internal/config/internal_call.go` + `internal_call_test.go` + `pipeline/synthetic_stage_seam_test.go` — новые; `models.go`/`pipeline.go`/`capability.go`/`escalation.go`/`repair.go`/`runner.go`/`stagerun.go`/`terminologist.go`/`live_reprobe_test.go`/`capability_test.go` — правленые). **Дерево НЕ чистить, `reset --hard`/`checkout` поверх него НЕ делать.** Приёмка — задача №1 преемника: пере-прогнать батарею с фрозен-числами, пере-вывести $0.045095 вторым путём из сырья, адверсариально проверить несущие клеймы (два шва · отсутствие перекупки · гарды · парная выборка N=5), затем лендить. До приёмки числа отчёта — заявления сессии, а не ратифицированные факты.
**Промт:** `docs/BACKEND_EFFORT_HANDLE_SESSION_PROMPT.md` (оркестратор №10, D39.87).
**Дата:** 02.08.2026. **Не коммичено** — лендит оркестратор.
**ИСХОД ЗАХОДА:** работа A **ЗАКРЫТА**; §3.4 (платная проба банка) **ЗЕЛЁНАЯ**; работа B — **дизайн подан, кода нет**, стоит на решении владельца; работа C **НЕ НАЧАТА** и закрыта владельцем как неуспешная на этом этапе, возвращена оркестратору. Деньги: **$0.045095** из $0.10 (проба §3.4), прогон C — $0.
---
## §0. Эхо-шапка
1. Скоуп: A — ручка эффорта ролям без неё; B — экран целевого языка; C — добивка холодного прогона.
2. A: один шов деривации синтетической стадии на все 4 места + обе расщеплённые хеш-пары.
3. §3.4: своя проба `reprobe/bank-low/`, потолок $0.10, вердикт «низкий эффорт не ломает банк» — только предъявив, что ни один из двух молчащих путей не сработал.
4. B: вердикт-двигающее ⇒ дизайн и пинг ДО кода.
5. C: только после заморозки A и B.
6. Деньги: A $0 · §3.4 ≤$0.10 · B $0 · C ≤$5 (не потрачено).
7. Вне скоупа: сид · судья · DC7 · `models.yaml` capabilities/price · thinking-off · `optNoThink` · чужие файлы · коммиты.
---
## §1. Работа A — ручка эффорта (строка 104). ЗАКРЫТА
### 1.1. Форма шва
**Корень дефекта — не «забыли ключ», а форма кода.** Четыре рукописных `config.Stage{...}` по движку, каждый молча терял поля родителя. Починка четырёх мест копипастой тиражирует дефект на пятое.
Построено **два шва**:
| Шов | Файл | Что закрывает |
|---|---|---|
| `config.InternalCall{...}.Stage()` | `internal/config/internal_call.go` (новый) | единственная деривация стадии для внутренних вызовов движка |
| `Runner.attemptRequest(...)` | `internal/pipeline/stagerun.go:406` | единственная сборка кортежа идентичности запроса |
Рукописных `config.Stage{}` в движке **не осталось ни одного**. Все четыре места (`terminologist.go:515`, `:568`, `repair.go:384`, `live_reprobe_test.go:98`) едут через шов. Все четыре сборки `RequestHash(Request{...})` схлопнулись в одну.
### 1.2. Деньги правки: перекупки НЕТ. Проверено хешами
Ключевой вопрос любого шва, трогающего идентичность запроса: не осиротели ли уже оплаченные чекпойнты.
- **`gates.terminology` НЕ фолдится в снапшот** (в `snapshot.go` ссылки на `Terminology` нет — это осознанное решение, см. доккоммент `config.TerminologyGate`). ⇒ добавление ключа `reasoning` **не двигает `snapshotID` и не пере-биллит ни одной волны**.
- **Кортеж идентичности для всех предсуществующих путей воспроизведён побайтно.** `RequestHash` длина-префиксует `req.Reasoning`, а опущенное поле в композитном литерале И ЕСТЬ `""` ⇒ старая и новая формы совпадают **по построению, а не по везению**. Бэнк-пробник с незаданным ключом даёт `8e4495006b978ed4` в обе стороны; эскалационный хоп — равенство; draft/edit — тот же список полей, перенесённый дословно.
- **Голден НЕ пере-захватывался** и не должен был: `classifierVersion` не бампнут (работа B не начата), `CheapGateVersion`/`SanitizerVersion` не тронуты. `TestGoldenDeterminism` PASS на исходных фикстурах — это и есть доказательство, что вердикты и провод не сдвинулись.
- **Единственное, что двигало бы хеш, — наследование эффорта у repair, и оно снято** (§1.6).
### 1.3. Ответы на четыре приёмочных вопроса §0 промта
**(1) Новый механизм или использование существующего?** Существующего. `Stage.Reasoning` было с самого начала, проброс на провод был, капабилити-слой различает провайдеров данными. Добавлен ОДИН конфиг-ключ `gates.terminology.reasoning` и ОДИН `gates.repair.reasoning`; новых механизмов нет.
**(2) Появится ПЯТАЯ синтетическая стадия — унаследует сама или забудут?** Унаследует, и это **механика, а не обещание** — три гарда:
| Гард | Что физически не даёт сделать | Проверено исполнением |
|---|---|---|
| `TestSyntheticStageSeamIsSingle` | построить `config.Stage` мимо шва | 6 посадок, все CAUGHT |
| `TestRequestIdentitySeamIsSingle` | пересобрать кортеж идентичности руками | посадка CAUGHT |
| `TestInternalCallDecidesEveryStageField` | добавить в `Stage` поле, не решив, что с ним делают внутренние вызовы | добавил поле → FAIL с именем поля |
**Первая версия гардов была дырявой, и это найдено ревью, а не мной.** Байтовый скан пробивался пятью способами; после переписи на AST — ещё двумя (алиас импорта `cfg "…/internal/config"` и `[]config.Stage{{…}}` с опущенным типом элемента). Обе дыры я **перепроверил на живом дереве** — гард говорил `ok`. Причина: сравнивал имя пакета вместо пути импорта. Итоговая версия резолвит путь, разворачивает `[]T`/`map[K]T`/`*T`, исключения полными путями (а не именами файлов, которые расползались на одноимённые файлы по всему репо), и несёт **пол на число разобранных файлов** — обход, не распарсивший ничего, выглядел как чистое дерево.
**(3) Заработает ли пара, которой нет в репо, без правки Go?** Да. Ключ пар-слепой, валидируется общим `ValidReasoningEffort`, Go не ветвится ни по паре, ни по книге. Вторая книга той же пары — без новых флагов (ключ ран-скоупный, в `book.yaml` и пар-пак не заходит).
**(4) Не появилось ли второго способа сказать то же самое?** Проверено и вычищено в трёх местах: enum эффорта — один валидатор на стадии и оба гейта; `bankRolePlan.model` снят (был вторым источником правды, читался только логом); идентичность запроса — одна сборка. **Остаточный долг признаю:** в репо теперь два гарда РАЗНОЙ формы — мой на AST и давний `TestProviderEgressSeamIsSingle` на байтовом регэкспе, с другим корнем обхода и своим словарём исключений. Свести в один обход — правильно, но это правка чужого давно живущего теста; вынесено предложением (§6, Д7).
### 1.4. Гранулярность ручки — решено ЗАМЕРОМ, не заранее
Приор был «одна ручка на бэнк-блок; вторая появится по замеру». Замер парный, N=5 на уровень, через боевой путь:
| Уровень | 6/6 достигнуто | completion-токены | цена 5 вызовов |
|---|---|---|---|
| `low` | 4 из 5 | 911278 | $0.001016 |
| `high` | 5 из 5 | 9203104 | $0.002335 |
4/5 против 5/5 на n=5 **неразличимо**, а `high` стоит 2.3×. ⚠ **Самопоправка:** первый вызов дал 3/6, я на этом основании построил вторую ручку `classify_reasoning` — и **снял её**, когда выборка не подтвердила разницу. Единственный промах — вызов на 91 токене, то есть модель просто не подумала; уровень тут ни при чём.
**Итог: одна ручка на обе бэнк-роли.** Классификатор сохраняет собственную МОДЕЛЬ (`classify_model`), но не собственный эффорт.
**Честная граница самих чисел 6/6** (унаследована от D39.86, повторяю, чтобы не читалась сильнее, чем есть): 元石 и 灵泉 стоят ДОСЛОВНО в `prompts/zh-ru/classifier.md`, то есть два из шести — припоминание, а не суждение. Дискриминирующее подмножество — 元海/蛊室/池塘/酒肆, и падение на `low` было ровно по нему (1/4 против 4/4). Читать как «4 решено + 2 припомнено».
**Точки шва в коде после правок:** `attemptRequest``stagerun.go:406`; `bankRoleSettings``terminologist.go:534`; `bankStage``terminologist.go:553`; `repairStage``repair.go:363`. (Номера `terminologist.go:515,568`, `repair.go:384`, `live_reprobe_test.go:98` в тексте — ИСХОДНЫЕ места литералов, как их называет D39.87.)
### 1.5. `Temperature` — зафиксирована нулём осознанно
Ответ бэнк-роли и repair — разбираемая движком СТРУКТУРА (таблица терминов, тип, спан-замена), где разброс сэмплинга — чистый риск; температура стадии это СТИЛЕВОЙ выбор про прозу и здесь бессмысленна. Ноль — то же значение, при котором куплены все существующие бэнк-чекпойнты, так что решение ещё и не двигает хеш. Причина первая, совпадение второе.
### 1.6. Repair — ключ СВОЙ, наследование СНЯТО
Первая версия наследовала эффорт от чинимой стадии (так предлагал промт и D39.87). **Ревью это опровергло, я перепроверил и согласился:**
- **Деньги:** `reasoning: "off"` стоит на финальной стадии у **всех** шиппинг-пайплайнов (`pipeline-c1.yaml:77`, `c2:51`, `arm-glm:43`, `arm-deepseek-pro:43`, стенд coldrun-b:44). Наследование двигало хеш ⇒ перекупка всех уже оплаченных repair-вызовов.
- **Семантика, и это хуже:** `reasoning` — не уровень, а значение, чьё ЗНАЧЕНИЕ зависит от control модели. Финальная стадия `deepseek-v4-pro` с `reasoning: "off"` — задокументированный no-op; `gates.repair.model: glm-5` — и тот же «off» становится живым `thinking:{type:disabled}`, то есть эхо-зоной. Родительская стадия и repair — РАЗНЫЕ модели.
Решение: `gates.repair.reasoning` — собственный ключ. Не задан ⇒ `""` ⇒ побайтно прежнее поведение.
### 1.7. Мульти-провайдерность — и найденная при этом дыра
Промт требовал закрыть режим `extra_body_disable` (glm), где ПУСТОЙ эффорт означает не «дефолт провайдера», а thinking ВЫКЛЮЧЕН. Расширил `sourceEchoExposure` на внутренние вызовы движка — и **ревью показало, что этого мало**: предикат `ThinksOnWire` для этого control возвращал `reasoning != "off"`, то есть на ПУСТОМ значении отвечал «думает», расходясь с проводом, который в этом же случае мержит disable.
Проверено исполнением:
```
applyToBody effort="" -> wire={"max_tokens":…, "thinking":{"type":"disabled"}}
ThinksOnWire(glm-подобная, "") = true <-- врал
```
Починено, запинено `TestThinksOnWireMirrorsTheWireForEachControl` (8 кейсов на три control). Проверил, что на старом коде тест падает.
⚠ Это была **предсуществующая** дыра (она так же молчала про СТАДИИ на glm без ключа `reasoning`), но мой пак первым на этот предикат оперся, поэтому чиню здесь.
### 1.8. Наш enum против вендорского — решение и явный отказ
`ValidReasoningEffort` принимает `"" | off | low | medium | high` — НАШ провайдер-слепой набор. У DeepSeek документированы `low/high/max` (+`xhigh`): `medium` там неопределённое поведение, `max`/`xhigh` нашим конфигом недостижимы. **Валидацию против капабилити резолвнутой модели НЕ строил** — это потребовало бы пер-модельной таблицы enum'ов в `models.yaml`, которой нет, то есть НОВЫЙ механизм (нарушение §0 п.1). Дыра предсуществующая и одинаковая для `stages[].reasoning`. Записано находкой (§6, Д5), не закрыто молча.
### 1.9. Доккомменты эхо-гейта — обязанность D39.86 п.2 / D39.87
Приведены в соответствие `config/models.go` (`thinkingControlExtraKeys`) и `llm/capability.go` (`ReasoningNone`). Ключевая формулировка: **запрещён не ПРЕДМЕТ, а сырой канал** — уровень эффорта через `stages[].reasoning`/`gates.*.reasoning` легален и идёт через `Capability.applyToBody`, который знает, что «off» значит для этого control; `model.extra_body` мержится дословно и потому может ВЫКЛЮЧИТЬ thinking, что и есть мина. Гейт **не ослаблен**.
---
## §2. §3.4 — платная проба банка на `low`. ЗЕЛЁНАЯ
**Хост:** `~/books/gu-zhenren/coldrun-b/reprobe/bank-low/` — свой `book_id`, своя БД, свой леджер. Эталон `coldrun-a` только на ЧТЕНИЕ (срез глав 110), не тронут.
### 2.1. Числа
```
терминолог 4/5 батчей finish=stop · consolidated=72 · unanswered=18
bad_lines=0 · off_language=0 · canon_conflicts=0 · $0.005750
классификатор 5/5 stop · reclassified=14 · $0.004286
черновая волна 20/20 чанков ok · 25 свежих вызовов · 2 эскалации вылечили эхо
$0.031639 (flash) + $0.010350 (pro)
```
Против вендор-дефолта `high` (замер D39.86): **1 батч из 5 и 1 терм из 81**.
### 2.2. Оба молчащих пути ИСКЛЮЧЕНЫ предъявлением
Промт требовал: вердикт «работает/не работает» имеет право быть вынесен только предъявив, что ни ролевой суб-бюджет, ни книжный потолок не сработали.
| | потрачено | бюджет | % |
|---|---|---|---|
| терминолог | $0.005750 | $0.05 | 11.5% |
| классификатор | $0.004286 | $0.02 | 21.4% |
| книга | $0.045095 | $0.10 | 45.1% |
И в логе **ноль** строк `budget would be exceeded by the next batch` / `denied by a USD ceiling`.
### 2.3. Провод вскрыт
35 тел (25 translator + 5 terminologist + 5 classifier), ключи ровно `[max_tokens, messages, model, reasoning_effort, stream, temperature]`, `reasoning_effort:"low"` **во всех 35**, ключей `thinking`**0**, `reasoning_content` непустой везде. **Гардрейл «thinking у DeepSeek не выключать» цел и доказан проводом, а не рассуждением.**
Упавший батч вскрыт сырьём (требование промта — прошлая сессия этого не сделала): `content:""`, `finish=length`, `completion_tokens=7999`, `reasoning_tokens=7999`. То есть **тот же тяжёлый хвост, что при `high`, просто на 1/5 вместо 4/5**`low` стену не убирает, а разрежает. Батч #2 не самый большой по входу (5909 симв. против 5890/5806), то есть стохастика вызова, согласуется с квирками п.6.
### 2.4. Деньги двумя путями
```
леджер (Σ чекпойнтов) $0.045095
независимый пересчёт usage×вендор-цены $0.045095
дельта $0.000000
разбивка: основной заход $0.041675
риг 6/6 (11 вызовов: 1 + 5 на low + 5 на high) $0.003420
```
Расхождения строки 78 (2xx с битым декодом) в этом заходе НЕ было — 0 строк `decode_error`.
### 2.5. Правки стенда, объявленные построчно
`~/books/gu-zhenren/coldrun-b/reprobe/bank-low/pipeline.yaml` — тело ран-конфига coldrun-b с ТРЕМЯ отличиями:
1. `stages[draft].reasoning: "low"` вместо no-op `"off"` — иначе волна упирается в стену и до банка проба не доезжает.
2. `gates.terminology.reasoning: "low"` — НОВАЯ ручка, предмет замера.
3. `escalation.budget_usd: 0.015` вместо `0.10`**понижен сознательно**: эскалации идут ДО банка, при $0.10 они могли съесть книжный потолок раньше первого вызова терминолога, и проба ответила бы «банк не работает» на деньгах. ⚠ Это делает наблюдение эскалаций НЕПОЛНЫМ; предмет пробы не они.
Боевой `coldrun-b/pipeline.yaml` **не тронут** (там по-прежнему `reasoning: "off"` на черновике — правка под работу C, которая не стартовала).
---
## §3. Работа B — экран целевого языка (строка 46). ДИЗАЙН ПОДАН, КОДА НЕТ
Промт: «вердикт-двигающее ⇒ сначала дизайн и пинг, потом код». Пинг подан владельцу, решения нет ⇒ кода нет. Это легитимный исход по букве промта.
### 3.1. Живая улика, добытая в этом заходе
**Глава 8, чанк 1 пробы bank-low:** полный связный черновик **на английском**, `finish=stop`, `disposition=ok`, и его хеш стоит в `final_hash` чанка. Хеш `fb51e135a395`, 5990 симв., 4690 букв, латиница 100%, кириллица 0.000. Второй такой же в `optB` (`4080ee8287af`, 4811 букв). Плюс полный **японский** черновик в `exp15/anchors/oracle_30.db` (`94905a7e443f`).
### 3.2. ⚠ ПОПРАВКА К МОИМ ЖЕ ЧИСЛАМ
Я подал владельцу «здоровый русский 0.95101.000, n=1306». **Это измерено на СЫРОМ тексте чекпойнта.** Движок классифицирует ПОСЛЕ среза банкноты (`stagerun.go:632` режет, `:636` классифицирует; срез безусловен для роли translator, `banknote.go:320`), то есть гейт видит другой текст. Пере-мерил его же функциями (`splitBanknote` + `checks.StripThink`):
| | я подал | на тексте, который видит гейт |
|---|---|---|
| здоровый минимум | 0.9510 | **0.9767** |
| n | 1306 | **1055** (дедуп по хешу; 1306 двоил бэкап-копии БД) |
| off-target выходов | 26 строк | **20 событий** |
Решения это не меняет (оба числа далеко от любого порога), но число я называл — поправляю.
### 3.2а. Поправка к БАЗЕ отказа
Публиковалось «~1.5% off-target». Знаменатель неверен: он пулит черновики с РЕДАКТОРСКИМИ выходами, а редактор этот отказ произвести не может (он не переводит с исходника). На черновой вызов база **≈3.0%**. Оба числа — одна книга, один жанр, по сути одна модель.
### 3.3. ⚠ ГЛАВНАЯ ПОПРАВКА: боевой гейт уже ловит 18 из 20
Ни ресерч, ни я не измеряли ДО этого, что делает **уже развёрнутый** гейт. Измерил:
```
sourceScriptShare > 0.15 (disposition.go:224,275)
поймано: 18 из 20 off-target событий
ложных: 0 из 1055 здоровых
запас: худший здоровый 0.0117 против порога 0.15 — 13×
промах: ровно 2, оба английские, 5990 и 6078 симв.
```
То есть движок **не открыт настежь**. Остаточная дыра — **2 чанка из ~1100 = 0.18%**, оба английские. Японский ловится (доля ханьцзы 0.332, японский делит письменность с китайским).
**И второй рубеж существует, но стоит не там.** Прогнал боевой санитайзер по тем самым байтам английского черновика: `total=1, cosmeticOnly=false, "Latin insertion"` — он бы его поймал. Не ловит потому, что `chunkrun.go:52` требует `isFinal`, а `isFinal := stageIdx == len(Stages)-1` (`stagerun.go:145`) ⇒ **в шиппинговой двухстадийной топологии черновик переводчика НИКОГДА не финальный**. Тот же `isFinal`-гейт глушит на черновике латинный лint, флаггер голоса и глоссарный пост-чек. Плюс он опционален (`gates.sanitizer.enabled`) и требует данных цели.
⇒ Формулировка «ни один предикат не ловит» верна **только для черновой стадии**. На финале для русской цели с включённым санитайзером ущерб = потеря юнита + оплаченный впустую редактор, а не тихая отгрузка. Для любой другой цели слой 7 инертен по отсутствию данных и рубежа нет вовсе.
### 3.4. Дизайн, поданный на решение
**Носитель:** `lang.LangScripts(Book.TargetLang)` — тот же дата-план, на котором стоит эхо-детектор, тот же `data/lang-script.txt`, где `ru→cyrillic` уже объявлено. **Новый файл в `go:embed` НЕ добавляется** — это критично: `EmbeddedVersion` фолдится в снапшот БЕЗУСЛОВНО, то есть новый эмбед-файл перекупает все книги всех пар.
`gates.terminology.target_script` как носитель **отвергнут**, но по причине сильнее промтовой: он ДУБЛИРУЕТ то, что данные уже знают, и потому это уже существующий «второй способ сказать то же самое». Промт снимал его за «нет в шиппинг-конфигах» — на деле ключ ОБЯЗАТЕЛЕН при включённом гейте (`pipeline.go:1075`).
**Порог 0.50** — не калиброван, а сознательно консервативен: пустой интервал между 0.0000 (все 20 событий) и 0.9767 (худший здоровый) шириной 0.9767, порог поставлен в его середину. Калибровать по Нейману–Пирсону на этом корпусе нельзя (§5.2).
**Развилка, которую промт требовал НАЗВАТЬ, а не предрешать** — подана владельцу трёхчастной:
| | Что | Цена |
|---|---|---|
| (а) | предикат внутри `classify()` + бамп `classifierVersion` | снапшот двигается у всех книг; на свежем стенде $0, голден пере-захват один раз |
| (б) | отдельный опциональный гейт вне `classify()` | снапшот не двигает, но это второй способ сказать «ответ непригоден», и он выключен по умолчанию |
| (в) | сразу языковой детектор | см. §5 |
Мой выбор — **(а)**.
### 3.5. Вторая половина дыры — решение
`detectLatinInsertion` (`checks/sanitizer.go:343`) внутри санитайзера НЕ обусловлен `TargetScriptNonLatin()`, хотя доккоммент `disposition.go:302-305` объявляет именно эту защиту (реальный гард стоит только на репэйр-пути). Следствие: первая же ЛАТИНОПИСЬМЕННАЯ цель дропнет 100% чанков чистой прозы.
**Решение: чинить здесь же, тем же диффом** — это одна работа с первой половиной. Для русской цели вердикты не меняются ни на байт (`TargetScriptNonLatin()` для ru истинно), то есть бампа `SanitizerVersion` не требуется. **Не сделано** — работа B не начата.
### 3.6. Что бы сломалось, если бы я сделал B наивно
Ревью и аудит нашли три мины, в которые я бы въехал:
1. **Новый флаг провалился бы в дыру.** `escalatable()` — закрытый белый список, `retryable()` — только `{length, empty}`. Новая причина не попадает ни туда, ни туда ⇒ пойманный чанк **молча терялся бы вместо перекупки**. Правильное решение — переиспользовать `FlagCJKArtifact` (он уже escalatable и не retryable), а не заводить новую константу.
2. **Классификатор ложно флагается 16 из 16 уже сегодня** (`SourceEchoExpected: role == roleTerminologist` не покрывает `roleClassifier`). Мой экран флагнул бы его вторым способом. Экран обязан читать не роль-строку, а факт формы ответа, проставленный тем же швом работы A.
3. **Экран слеп к ПОДМНОЖЕСТВАМ письменности:** у японского набор {ханьцзы, хирагана, катакана} ⇒ чисто китайский выход для →ja книги наберёт 1.000 и пройдёт. То же для →ko.
---
## §4. Работа C — НЕ НАЧАТА
Причина по букве промта: «идёт только после того, как работы A и B ЗАКРЫТЫ И ЗАМОРОЖЕНЫ». Работа B застряла на дизайн-пинге — промт прямо называет это легитимным исходом захода. Дополнительно владелец закрыл прогон как неуспешный на данном этапе и вернул его оркестратору.
**Стенд `~/books/gu-zhenren/coldrun-b/` стоит нетронутым:** свежая БД, сид не подключён, `pipeline.yaml` побайтно равен эталону coldrun-a кроме шапки, черновик по-прежнему на `reasoning: "off"` (правка под C не внесена — вносить её без прогона значило бы оставить стенд в промежуточном состоянии).
Строка 16 (полная цена холодного старта включая редактуру) **НЕ закрыта**. Строка 13б (оси голоса) **без данных** — редакторская волна снова не гонялась.
---
## §5. Ресерч-задел для следующих сессий
Два холодных исследования, каждое с независимыми скептиками (author≠reviewer). Полные тела — в транскриптах воркфлоу; ниже то, что должно пережить сессию.
### 5.1. Off-target детекция: закрыта ли задача
**ЧАСТИЧНО, и нерешённая часть меньше, чем казалось.**
**Устоялось (не пере-открывать):**
- У отказа есть имя и метрика: *off-target translation* (Zhang et al., ACL 2020, arXiv 2004.11867), *language confusion* LPR/WPR (Marchisio et al., EMNLP 2024, arXiv 2406.20052). Рецепт у всех один и он у нас уже реализован: детектор на выходе → сравнение с запрошенной целью → mismatch = отказ. **Более умного serving-time метода не упущено.**
- Английский — доминирующий аттрактор. Подтверждено дважды независимо (Cohere; Guerreiro et al., TACL 2023: >90% off-target галлюцинаций при переводе С английского). Совпадает с нашими 2/20 английских.
- Четыре режима отказа — практика поля, не новизна: Guerreiro использует РАЗНЫЙ детектор на режим (ALTI+ для detached, top-n-gram для oscillatory, LID только для off-target). Гнать всё через один детектор — ошибка проектирования, и мнение из обсуждения тут право.
- Abstain + пол по длине — опубликованная практика (Cohere `compute_metrics.py`: строка оценивается только при `len(tokens) >= 5`).
- Reference-free QE эту задачу не делает, и поле пишет об этом прямо.
**Открыто у всех:**
- Близкие кириллические соседи. OpenLID-v3 на реальном пользовательском тексте: боснийский 37.21% precision, сербский-латиница 30.85% recall; 19.9% ошибок — «полная неоднозначность». Трёх девяток тут не достигает никто.
- FPR детектора на настоящей ЦЕЛЕВОЙ прозе длиной в чанк не опубликован нигде.
- Дрейф в середине генерации: нет устоявшейся статистики, гранулярности и правила агрегации.
- Ничего в этой литературе не про длинную художественную прозу. Caswell et al. 2020: >90% held-out F1 соответствовал ~5% точности по человеческой оценке «в дикой природе».
### 5.2. Почему обещанной гарантии FPR ≤ 0.1% здесь не купить
Правило трёх: ноль отказов на n независимых испытаний покупает `1 0.05^(1/n)`. Для ≤0.1% нужно n ≥ 2995. У нас **114 различных позиций (глава, чанк)** — одна книга, один жанр, одна модель — это покупает **FPR ≤ 2.594%**, в 26 раз слабее обещанного. Даже если считать все 1055 выходов независимыми — ≤0.283%.
Плюс процедура двоит данные: «калибруй так, чтобы ложное отклонение ≤0.1%» ставит порог в эмпирическую 0.001-квантиль ТОЙ ЖЕ выборки, по которой потом заявляется граница. При n≈1000 порог определяется вторым наименьшим наблюдением.
И третье: Нейман–Пирсон ограничивает ошибку I рода и **ничего не говорит про мощность**. На сиблингах распределения нулевой и альтернативной гипотез по доле целевого письма СОВПАДАЮТ (болгарский даёт ровно 1.0000). Односторонний срез при α=0.001 даёт мощность ≈0.1%: гарантия есть, ловли нет.
⇒ **Ставить руками консервативную константу в широком пустом интервале и версионировать её как контракт, а не как тюнинг.**
### 5.3. Что НЕ строить (и почему) — самая ценная часть
1. **Никакую статистическую LID-библиотеку в путь вердикта.** Проверено запуском, не по README: `lingua-go` — НЕдетерминистична (6/23 образцов на 20 000 повторов; `GOMAXPROCS=1` не лечит — причина в обходе map при суммировании float64), +126 МиБ эмбеда безусловно, 1012 мс/чанк против бюджета 168 мкс, и вендор документирует вердикт-двигающие апгрейды. `langid-go` — на 644 реальных сегментах русской Википедии даёт P(ru)<0.5 на **22.2%** сегментов длиной 2040 рун; плюс FMA-зависимость от GOARCH. `whatlanggo` 46 из 9 настоящих русских образцов принимает за болгарский/македонский. `getlang` argmax флипается. `gocld3`/fastText CGo, вердикт становится функцией тулчейна, то есть уезжает ВНЕ снапшот-хеша.
2. **Посегментный worst-of (L4 из мнения).** Умножает FPR на число сегментов ПО ПОСТРОЕНИЮ, и — решающее — **не имеет ни одного положительного примера**: самая длинная не-кириллическая цепочка в здоровом выходе 23 символа, между 0.0000 и 0.9767 не лежит ничего. На замеренных точках стоило бы 74290 дропнутых чанков на книгу ради нуля дополнительных поимок. Цена (269 мкс) никогда не была препятствием — препятствие в калибровке и пустом классе положительных.
3. **Замыкание алфавита (L2).** Против болгарского доказуемо ноль (его алфавит — собственное подмножество русского), в остальных случаях избыточно с L1.
4. **Функционально-словный экран.** Здоровый русский p01 = 0.2433 на выход и 0.0455 на абзац — зазор крошечный, а русский закрытый класс во многом и болгарский. На этой машине нет ни одного сиблинг-текста, чтобы померить.
5. **Эхо через расстояние редактирования / n-граммное сходство.** `sourceScriptShare > 0.15` уже даёт 18/20 при 0 FP и 13× запасе.
6. **Любой новый файл в `go:embed`-манифесте** ради этой фичи. `EmbeddedVersion` фолдится БЕЗУСЛОВНО ⇒ перекупка всех книг всех пар.
7. **Hunspell в любом виде** и **GlotScript-эмбед** (stdlib `unicode` уже несёт ISO-15924-совместимые таблицы, движок к ним уже ходит).
### 5.4. Пошаговый план для следующей сессии
Принцип порядка: единственный необратимый шаг — бамп `classifierVersion`, он последний. Шаги 13 независимы.
1. **Корпус-фикстура в репо** (~20 off-target + ~40 здоровых, с движковой предобработкой). Снапшот НЕ двигает. СТОП, если не воспроизводит 18/20 при 0 FP.
2. **Записать реальный запас боевого гейта регресс-тестом** (худший здоровый source-share 0.0117 против 0.15). Это база, которую обязано побить любое предложение, и её никто не записывал. СТОП, если запас меньше 3×.
3. **Поправить два дока-дефекта** — опубликованный диапазон 0.9510 и база 1.5% (правильно: 0.9767 на 1055 движково-чистых, и база 3.02% на ЧЕРНОВОЙ вызов, а не на все выходы, потому что редактор этот отказ произвести не может). Зона оркестратора.
4. **`targetScriptShare` чистой функцией, НЕ подключённой.** Снапшот НЕ двигает — в этом и смысл, дорогое отложено. СТОП, если хоть один здоровый ниже 0.90.
5. **Прогнать предлагаемый вердикт офлайн по всем сохранённым чекпойнтам** и сдиффить диспозиции. Ожидание: ровно **2 строки** меняются. **СТОП+эскалация, если изменилась хоть одна здоровая.**
6. **Подключить и бампнуть `classifierVersion`** — ДЕНЕЖНЫЙ шаг, только с подписью владельца. Переиспользовать `FlagCJKArtifact` (уже escalatable). Порог abstain — **200 букв** (ниже него экран воздерживается; самое короткое off-target событие — 346 букв, ниже 200 падают лишь 5 из 1055 здоровых, все со счётом 1.0000).
7. **Починить `cultivation` в сиде, а не порогом.** 86 из 1055 здоровых выходов содержат голое английское `cultivation` внутри беглого русского; ВСЕ 32 абзаца, набравшие ниже 0.90, содержат его, и НИ ОДИН не содержит ханьцзы. Это дефект глоссария, не калибровочная константа. Правка в пар-данных двигает `LangpackVersion`, который фолдится с `omitempty` ⇒ перекупка ограничена книгами ЭТОЙ пары. Это самый переносимый вывод всей цепочки: **чинить на том плане, где перекупка уже.**
8. **Записать сиблинг-угрозу непостроенной строкой с триггером.** Ни одного события не наблюдалось. Если появится — дискриминатор это плотность РУССКО-ЭКСКЛЮЗИВНЫХ букв (ы/э/ё) НИЖЕ ожидания, а не замыкание алфавита.
### 5.5. Ресерч про механизм гардов (побочный, но применён)
Форма «архитектурное правило как обычный тест» — **стандартная**: `go/build/deps_test.go` в самой stdlib держит так граф зависимостей («*It is a statement of policy. DO NOT CHANGE THIS DATA TO FIX BUILDS*»), `go/types/errorcalls_test.go` — буквально `ast.Inspect` по своим исходникам. Аналог в Java — ArchUnit.
Альтернативы проверены запуском и **не проходят наше ограничение «падать на обычной батарее»**: `go test -vet=<свой анализатор>`*«-vet argument must be a supported analyzer»*; `GOFLAGS=-vettool` при `go test` игнорируется; штатный `composites` вообще не входит в набор `go test`. `golangci-lint` — 38.7 МБ бинарь и первый в проекте `.golangci.yml`. `x/tools` в тесте тянет `x/net` 0.26.0 → 0.54.0, а `x/net/http2` — наш транспорт к провайдерам.
`exhaustruct` — инструмент ровно под исходный класс бага, но на нашем коде даёт 16 находок, из которых 15 в тестах и одна — сам конструктор.
---
## §6. Дефекты: найдено / починено / отложено
### Починено с тестами (в этом заходе)
| № | Дефект | Тест |
|---|---|---|
| П1 | Синтетические стадии теряли `Reasoning`/`Temperature` — 4 места | `TestSyntheticStageSeamIsSingle` + `TestInternalCallDecidesEveryStageField` |
| П2 | `bankCheckpointExists`/`repairCheckpointExists` строили кортеж БЕЗ `Temperature`/`Reasoning`, расходясь с `runAttempt` в ОБЕ стороны (ложно-false режет резюм бюджетом; ложно-true обходит ролевой суб-бюджет) | `TestRequestIdentitySeamIsSingle`, `TestBankProbeAndAttemptAddressOneCheckpoint` (3 уровня эффорта, боевой путь `Reserve``SettleWithCheckpoint`) |
| П3 | `ThinksOnWire` на `extra_body_disable` врал про пустой эффорт ⇒ бэнк-роль на glm уезжала в thinking-off МОЛЧА | `TestThinksOnWireMirrorsTheWireForEachControl` |
| П4 | `sourceEchoExposure` не видел внутренние вызовы движка | расширен; детерминизм порядка — `sort.Strings` |
| П5 | Гарды пробивались 7 способами (5 байтовых + алиас импорта + опущенный тип элемента) | переписаны на AST, все 7 посадок CAUGHT |
| П6 | Поведенческие тесты были вакуумны (сравнение вызова с самим собой; смена двух полей вместо одного) | переписаны на боевой денежный путь |
| П7 | `bankRoleSettings` молча отдавал бы настройки терминолога неизвестной роли | явная паника с именем роли |
| П8 | `bankRolePlan.model` — второй источник правды, читался только логом | снят |
| П9 | Риг с N=1 по умолчанию при комментарии, осуждающем n=1 | дефолт 5 |
### Найдено, НЕ починено — с носителем
| № | Дефект | Носитель |
|---|---|---|
| Д1 | Экран целевого языка (работа B) | решение владельца по развилке §3.4 |
| Д2 | `detectLatinInsertion` не обусловлен `TargetScriptNonLatin()` ⇒ первая латинописьменная цель дропнет 100% чанков | тем же диффом, что Д1 |
| Д3 | `SourceEchoExpected` не покрывает `roleClassifier` ⇒ 16/16 вызовов классификатора логируются `ok=0 degraded=cjk_artifact`. Данные НЕ теряются (`run.texts[i] = att.text` безусловен, `reclassified=14` доказывает), но отравлен ровно тот сигнал, который говорит «провайдер плохеет» | тем же диффом, что Д1 |
| Д4 | `gates.terminology.target_script` не сверяется с `book.target_lang`: `target_lang: ru` + `target_script: Latin` грузится чисто и **инвертирует** банковский экран | бэкенд, отдельный пак; вне снапшота ⇒ дёшево |
| Д5 | Наш enum эффорта провайдер-слеп: `medium` на DeepSeek — неопределённое поведение, `max`/`xhigh` недостижимы | решение оркестратора: нужна ли пер-модельная таблица |
| Д6 | `//go:embed` в `internal/lang/embedded.go:22` — ЯВНЫЙ список файлов, не `data/*`. Новая цель ⇒ правка Go. `CompileCheckers` паникует при неполном файле цели. На ревью-вопрос «заработает ли пара, которой нет в репо» ответ сегодня **НЕТ** — и это не про язык, а про строку эмбеда | архитектура, оркестратор |
| Д7 | Два гарда шва разной формы (AST против байтового регэкспа), с разным корнем обхода и разными словарями исключений | бэкенд; свести в один обход |
| Д8 | Ратифицированный порог классификатора «6/6» определён для ОДНОГО вызова, не для выборки. На `low` — 4/5. Риг сейчас красный на рекомендованном уровне, и это заявлено в тексте падения, а не смягчено | владелец/оркестратор |
| Д9 | Аддитивные провайдеры отказываются для бэнк- и repair-гейтов ИМЕННО потому, что блок не несёт `reasoning_max_tokens`. Новая ручка эту дыру НЕ закрывает | зафиксировано комментарием; вскрывать под отдельное решение |
| Д10 | **Шиппинговые конфиги репо всё ещё несут `reasoning: "off"` на ЧЕРНОВОЙ стадии** (`configs/pipeline-c1.yaml:49`, `c2:26,39`, `arm-mistral:30`, `arm-deepseek-pro:34`, `arm-glm:34`) — тот самый no-op, который отправил прогон в стену 0/9 при вендор-дефолте `high`. Санкция (б) по D39.87 касается и их, но правка **двигает снапшот всем, кто этими конфигами пользуется** ⇒ молча не менял | владелец/оркестратор: применять ли (б) к шиппинг-конфигам и когда платить перекупку |
| Д11 | Область правки `ThinksOnWire` — уточнение, чтобы её не переоценили: **ЯВНЫЙ `"off"` на glm предупреждался и до правки, и после**`pipeline-arm-glm.yaml:43` он стоит осознанно и с измеренной причиной — «таймауты ×3»). Правка меняет ровно один случай: **ключ ОТСУТСТВУЕТ**. Там предикат отвечал «думает», а провод мержил disable. Дыра предсуществующая и одинаковая для стадий и для гейтов | закрыто; строка оставлена, чтобы приёмка не искала регресса в армах |
---
## §6а. Батарея числами (после ВСЕХ правок, включая пост-ревью)
```
go build ./... exit 0
go vet ./... exit 0, пусто
go vet -tags live ./internal/pipeline/ exit 0, пусто
gofmt -l . пусто
go test -race -count=1 ./... все 14 пакетов ok (pipeline 82.0s, store 15.9s)
TestGoldenDeterminism PASS (голден НЕ пере-захватывался — нечему двигаться)
майнер-парити EXACT n=13618 catastrophe{方源:0 蛊:1 蛊师:2 古月:22} recall@proposed=0.9655 (56/58 GT)
labels-фриз k4b 10/1/36/99 · k4_inverse 3/0/1/71 · k2 34/1/6/51 · k4a 0/0/0/348
K6 (membank) tp=1 fp=6 fn=0 tn=251
```
Все пять фрозен-чисел совпали с ратифицированными. Батарея гонялась **трижды**: после работы A, после снятия второй ручки, после пост-ревью фиксов.
## §7. Заявление = команда
| Число | Команда |
|---|---|
| батарея (build/vet/vet-live/race/gofmt) | `go build ./... && go vet ./... && go vet -tags live ./internal/pipeline/ && gofmt -l . && go test -race -count=1 ./...` |
| голден PASS | `go test ./internal/pipeline/ -run TestGoldenDeterminism -count=1` |
| парити EXACT `n=13618 catastrophe{方源:0 蛊:1 蛊师:2 古月:22} recall 0.9655` | `TM_MINER_PARITY=1 go test ./internal/miner/ -run Parity -count=1 -v` |
| labels-фриз k4b 10/1/36/99 · k4_inverse 3/0/1/71 · k2 34/1/6/51 · k4a 0/0/0/348 | `TM_CHECKER_LABELS=1 go test ./internal/checks/ -run Label -count=1 -v` |
| K6 1/6/0/251 | `TM_CHECKER_LABELS=1 go test ./internal/membank/ -run K6 -count=1 -v` |
| проба §3.4, все числа стадий | `LOG_LEVEL=debug LOG_LLM_BODIES=1 ./bin/tmctl translate --config ~/books/gu-zhenren/coldrun-b/reprobe/bank-low/book.yaml --verify-bank` |
| терминолог 72/18, off_language=0 | `grep "terminology finished" ~/books/gu-zhenren/coldrun-b/reprobe/bank-low/run-01.stderr.log` |
| леджер $0.045095 | `sqlite3 "file:…/guzhenren-bank-low.db?mode=ro" "SELECT SUM(cost_usd) FROM checkpoints;"` |
| независимый пересчёт $0.045095 | `SELECT SUM((prompt_tokens-cached_tokens)*0.14/1e6 + cached_tokens*0.0028/1e6 + completion_tokens*0.28/1e6) FROM request_log WHERE tm_hit=0 AND model_actual LIKE 'deepseek-v4-flash%'` + pro вручную по `models.yaml:161` |
| 35 тел с `reasoning_effort:"low"`, 0 ключей `thinking` | `grep -o 'reasoning_effort[^,}]*' run-01.stderr.log \| sort \| uniq -c; grep -c '"thinking"' run-01.stderr.log` |
| классификатор 4/5 и 5/5 парно | `TM_LIVE=1 TM_CLASSIFY6_N=5 TM_CLASSIFY6_CONFIG=…/bank-low/book.yaml go test -tags live -run TestLiveClassifierHarmSet -count=1 -v ./internal/pipeline/` (сырьё: `classifier-6of6-{low,high}.json`) |
| боевой гейт 18/20, FP 0/1055, запас 13× | временный тест в пакете pipeline через `splitBanknote`+`StripThink`+`sourceScriptShare`; удалён после снятия |
| санитайзер поймал бы английский черновик | временный тест: `CompileCheckers(nil, lang.TargetChecksFor("ru")).SanitizeOutput(…)``total=1 cosmeticOnly=false "Latin insertion"`; удалён |
| валидация гейта эффорта громкая | `./bin/tmctl status --config <копия с reasoning: "sorta">``gates.terminology.reasoning must be off\|low\|medium\|high` |
| 7 обходов гарда пойманы | посадки в `waverun.go` + `go test ./internal/pipeline/ -run SeamIsSingle`, дерево восстановлено (`git diff --stat` пуст) |
---
## §8. Критик полноты против себя
- **Качество прозы не судил никто, ни в одной пробе.** `low` мерился детерминированными осями. Черновик без глубокого размышления может быть заметно хуже как ТЕКСТ, проходя все $0-гейты. Это работа судьи Ф2, он не гонялся.
- **`low` вернул волну к жизни, но стену не убрал** — 1 батч из 5 по-прежнему упирается. При другом размере книги/батча доля может быть иной; n=5 батчей.
- **Редакторская волна не гонялась НИ РАЗУ** — ни до смены весов, ни после. Класс отказа, убивший черновик и терминолога, на редакторе не проверен, а рычага там нет (маппинг pro `low→high`). Строка 13б без данных.
- **Проба bank-low понизила `escalation.budget_usd`** — наблюдение эскалаций неполное.
- **Работа B не начата**, а её улика (английский черновик) воспроизвелась в этом же заходе. Дыра открыта.
- **Числа off-target — одна книга, один жанр, по сути одна модель.** Сиблинг-угроза не наблюдалась ни разу; всё, что про неё сказано, — арифметика алфавитов и авторские образцы, не корпус.
- **Первые версии моих гардов и тестов были дефектны**, и нашёл это ревью, а не я. Это второй за заход случай, когда я подал вывод, который не выдержал проверки (первый — 3/6 классификатора на n=1). Оба поправлены до сдачи; оба записаны здесь, а не сглажены.
- **Не проверял**, что происходит с английским черновиком в РЕДАКТУРЕ — вытащит ли редактор его обратно в русский или отредактирует как английский. Проба остановилась на банк-паузе.
## §9. Адверсариальный проход по своим правкам
Запущен независимым агентом (author≠reviewer, мандат CLAUDE.md 12.07) по всему диффу с явным заданием «считать сломанным, пока не доказано обратное». Вернул **10 находок**: 1 blocker, 3 major, 5 minor, 1 nit. Все разобраны; топ-2 я **перепроверил кодом сам** до принятия (`ThinksOnWire` — исполнением `applyToBody`; наследование repair — по всем шиппинг-конфигам). Проверенные им и НЕ нашедшие дефектов области: идентичность кортежа для всех предсуществующих путей (хеши совпали побайтно, бэнк-пробник с незаданным ключом → `8e4495006b978ed4` в обе стороны), полнота шва по всем 17 полям `Stage`, конкурентность (`-race` чист), общность (ветвлений по языку/паре/книге нет).
---
## §10. Тронутые файлы (только своё)
```
M backend/internal/config/models.go ThinksOnWire + доккоммент эхо-гейта
M backend/internal/config/pipeline.go gates.terminology.reasoning, gates.repair.reasoning, валидация
M backend/internal/config/capability_test.go +TestThinksOnWireMirrorsTheWireForEachControl
M backend/internal/llm/capability.go доккоммент ReasoningNone
M backend/internal/pipeline/stagerun.go attemptRequest
M backend/internal/pipeline/escalation.go хоп через attemptRequest
M backend/internal/pipeline/terminologist.go bankStage/bankRoleSettings, пробник, смета, plan без model
M backend/internal/pipeline/repair.go repairStage, пробник, доккоммент
M backend/internal/pipeline/runner.go sourceEchoExposure на внутренние вызовы
M backend/internal/pipeline/live_reprobe_test.go N-выборка, шов, порог
?? backend/internal/config/internal_call.go ШОВ
?? backend/internal/config/internal_call_test.go полнота полей
?? backend/internal/pipeline/synthetic_stage_seam_test.go гарды швов
M docs/PROGRESS.md секция «Бэкенд», допись СВЕРХУ
?? docs/archive/reports/EFFORT_HANDLE_2026-08-02.md этот отчёт
```
Вне git (артефакты): `~/books/gu-zhenren/coldrun-b/reprobe/bank-low/` — проект, БД, `run-01.stderr.log`, `wire-terminologist-batches.json`, `classifier-6of6-{low,high}.json`.
Чужого не тронуто: `START_PROMT.MD`, `frontend/`, `platform/`, `.gitignore`, `docs/` кроме этого отчёта и §Бэкенд в `PROGRESS.md`. Коммитов нет.
**СТОП — приёмка оркестратора.**