diff --git a/docs/architecture/06-memory-risk-registry.md b/docs/architecture/06-memory-risk-registry.md index 9118e63e..c3812089 100644 --- a/docs/architecture/06-memory-risk-registry.md +++ b/docs/architecture/06-memory-risk-registry.md @@ -29,7 +29,7 @@ | # | Сценарий отказа (как ломает перевод) | Вер./Тяж. | Механизм защиты | Слой | Где в коде/схеме | Фаза | Статус | |---|---|---|---|---|---|---|---| -| A1 | **Пустой/слабый retrieval трактуется как ошибка** → пайплайн «добивает» бюджет ближайшими эмбеддинг-соседями или фоновыми записями → near-miss дистрактор роняет качество (до −67%, 2401.14887; NLI-фильтр спасает, 2310.01558) | С / 🟠🔇 | «Лучше ничего, чем мусор»: на пустом ключевом матче инъектить **только** активные sticky-записи; бюджет **допустимо недозаполнить**; фильтр по точности **перед** сортировкой по приоритету (не только `approved>auto`) | DET | сборщик контекста (`render.Messages`, инъекция между system и user, `render.go:133-136`) | 1 | ✅ ПОСТРОЕНО (⚠ испр. 02.09): точность ПЕРЕД приоритетом — `membank.priorityRank` (Confirmed → не-sticky → approved), пустая выборка не инъектит НИ ОДНОГО сообщения (`internal/membank/memory.go`, греп `better nothing than garbage`); в research/05 §5.2 бюджет заполняется по приоритету, не по точности | +| A1 | **Пустой/слабый retrieval трактуется как ошибка** → пайплайн «добивает» бюджет ближайшими эмбеддинг-соседями или фоновыми записями → near-miss дистрактор роняет качество (до −67%, 2401.14887; NLI-фильтр спасает, 2310.01558) | С / 🟠🔇 | «Лучше ничего, чем мусор»: на пустом ключевом матче инъектить **только** активные sticky-записи; бюджет **допустимо недозаполнить**; фильтр по точности **перед** сортировкой по приоритету (не только `approved>auto`) | DET | сборщик контекста (⚠ **адрес и имя испр. 10.09:** символа `render.Messages` в дереве нет — 0 хитов; инъекция между system и user живёт в `MessagesWithInjection`, `backend/internal/pipeline/render.go`, греп `MessagesWithInjection` — прежний адрес `render.go:133-136` указывал в чтение файла промпта) | 1 | ✅ ПОСТРОЕНО (⚠ испр. 02.09): точность ПЕРЕД приоритетом — `membank.priorityRank` (Confirmed → не-sticky → approved), пустая выборка не инъектит НИ ОДНОГО сообщения (`internal/membank/memory.go`, греп `better nothing than garbage`); в research/05 §5.2 бюджет заполняется по приоритету, не по точности | | A2 | **Ошибочная инъекция** (омограф/короткий алиас/низкорелевантный сосед) → модель послушно следует мягкой инструкции, уверенность растёт, точность падает (2510.00829) — **главный источник тихой деградации** | С / 🔴🔇 | Трёхстороннее явное решение **на каждую запись-кандидат**: `CONFIRMED` (точный ключ/лемма, `approved`, спойлер-валидна) → авторитетно; `AMBIGUOUS` (короткий/общий алиас, коллизия поверхности, `auto/draft`) → инъекция с явной пометкой «unverified — проверить» **+ принудительный post-check**; `REJECT` → выкинуть и залогировать. Паттерн CRAG (accept/verify/reject), но каждая ветка наблюдаема | DET | сборщик контекста + `disposition`-запись | 1 | ✅ ПОСТРОЕНО: disposition + `retrieval-state` в `internal/membank` (абзац «Гейты … ИСПОЛНЕНЫ» ниже); ⚠ AMBIGUOUS-пометку «проверить» читать через D39.104 в шапке — с провода она снята | | A3 | **CJK-подстрока срабатывает на чужом смысле** (阿Q, 送灶-обряд vs «送到灶», 炎=пламя vs имя 萧炎, 道/气/修 полисемия) → инъекция неверной записи | В / 🟠🔇 | Запрет одиночных Han/каны/латинской буквы как самостоятельного ключа (schema-констрейнт, не соглашение); longest-match; замена только целой сущности; условные `judgment word` (GalTransl post-dict). **Эмпирика (`retrieval_bench`): точный многосимвольный подстрочный матч — precision 1.0, 0/4 ложных срабатываний на этих ловушках; char-BM25/фаззи — все 4 ловушки в top-5** → фаззи/одиночные ключи на горячий путь не пускать | DET | матчер ключей; `glossary.min_key_len` per-язык; поле `sense` | 1 | ✅ ПОСТРОЕНО (⚠ испр. 02.09): пер-язычный пол ключа + явный escape-hatch — `internal/membank/memseed.go`, грепы `minKeyLenFor` и `AllowShort` | | A4 | **Молчаливо-пустой матч из-за орфографии** (упрощ./трад. 鲁/魯, полу/полноширинные, кандзи↔кана 鈴木/すずき, ruby-чтения) → ключ есть в тексте, но не в той форме → запись не инъектируется | В / 🟠🔇 | Обязательный слой **нормализации перед матчем** (NFKC + OpenCC упрощ./трад. + kana-folding + захват ruby-чтений), симметрично к ключам/алиасам и тексту чанка; версионированный тестируемый артефакт | DET | pre-match нормализация (связать с pre-replace GalTransl); поле `aliases` | 1 | ✅ ПОСТРОЕНО: `internal/text` + данные пары; «тихо пусто» остаётся самым вероятным багом этого слоя | diff --git a/docs/architecture/09-target-architecture.md b/docs/architecture/09-target-architecture.md index 94835d5d..0ba68e44 100644 --- a/docs/architecture/09-target-architecture.md +++ b/docs/architecture/09-target-architecture.md @@ -22,7 +22,7 @@ > | 3 Контракт t/e | ✅ ПОСТРОЕН ЗА `enabled:false`: `checks.RepairCandidates` (позиционная детекция) · read-only скан остатка · машинерия ремонта §15.2; **включение платного пути = решение владельца по замеру остатка**; эмпирика контракта: диффы ОТЛОЖЕНЫ (D39.108, экспы 19/20; D21.4/D21.10 закрыты), full-regen ПОДТВЕРЖДЁН, точечная починка как ЗАМЕНА редактора отклонена и принята формой «механический пре-гейт ПЕРЕД редактором» (D39.117; строка 142, строка 106 понижена) | пак-16 (D39.24), **D39.108**, **D39.117** | > | 4 Память | ✅ ПОСТРОЕН + ДОСТРОЕН паком-20 + ✅ **ВХОДНАЯ ДВЕРЬ ПОСТРОЕНА (D39.158)**: $0-глагол `tmctl bank-apply` (проекция `--dry-run`, всё-или-ничего, байтовый no-op идемпотентен), писатель решений `internal/membank/decisions.go`, стоп банка — ФЛАЖОК, который движок чтит сам (память предъявления `bank_stop_presented`, схема store v16), класс отказа 15 `write_incomplete`. Роли носителей и поток «источники → вид → проекции» — `18-bank-ontology.md`; состав пака-20 (ТЕРМИНОЛОГ · флаговый стоп `--verify-bank` · двухсекционная редакторская инъекция · точечная ре-редактура · банкнота отдельным шиппинг-файлом · языковой экран `target_script`) — D39.41–45/50–53; ⚠ D39.104 п.2 ВНЕСЁН (испр. 05.09): инъекция схлопнута в ЕДИНЫЙ закон-блок, маркер ⟨проверить⟩ с провода СНЯТ — отработано вайр-батчем, пак принят D39.199 (`74ffe4f`), строка 134 закрыта; жанровый словарь ОТМЕНЁН (D39.47) | пак-1 · пак-11 · **пак-20 (D39.41–45/47/50–53)** | > | 5 Качество | ✅ детерм. часть: `QualityReport` · число/omission-гард · DC/пак-13-чекеры · repair-остаток; семантический span-судья/полярность — НЕ строился (защита = editor-swap, D38.4) | D39.2 · D38.3 · пак-13 (`2f91b04`) · пак-16 | -> | 6 Export | ✅ **ПЛАТФОРМЕННАЯ половина ПОСТРОЕНА 06.09** — что именно и с какой ценой, ОДНИМ носителем: `15-money-path.md` §3 (акт `D39.208`). Здесь только статус, механика не дублируется. ✅ ДВИЖКОВАЯ половина ПОСТРОЕНА: `checks.ExportNormalize` на каждом финале; санитайзер-классы strip+export · **писатель книги: `internal/bookfile` + `tmctl build` (EPUB 3 + txt, D39.175)**; холодный прогон 31.08 предъявил собранный EPUB настоящего перевода, `epubcheck` 0/0/0/0. ✅ **ПЛАТФОРМЕННАЯ половина ПОСТРОЕНА 04.09, ЦИКЛ ЗАМКНУТ ЖИВЬЁМ** (`adf5e53`, D39.194): пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0. Дверь — три адреса (`createExport`/`getExport`/`downloadExport`), `202`+`Location`, поллинг с `Retry-After`, Range 206, TTL с уборкой, идемпотентность. Проверка вместо счёта: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` → НЕ пусто; операций в каноне 21, смонтировано **19** (испр. 05.09: `updateBook` смонтирован платформенным паком `7e2226a`). ⚠ **СТЕНА ДЕСЯТИ ГЛАВ СНЯТА 05.09, НО ПОЛ ОСТАЛСЯ — ДОЧИТАТЬ ДО КОНЦА ПО-ПРЕЖНЕМУ НЕЛЬЗЯ.** — движковый пак `81a89e9` (акт D39.206) снял стену: отказ резервации ЗАЩЁЛКИВАЕТ волну, а обязательный вызов ЖДЁТ settle; порог «короче десяти глав не переводится» СНЯТ, остаточный пол — одна резервация (~3 главы) и он ВЫВЕДЕН, не замерен. Разбор — `15-money-path.md`, греп `ЭРРАТА 05.09`. ⛔ **Инвариантный пол: даже при ОДНОМ воркере прирост потолка `2 × $0.03 = $0.06` меньше одной редакторской резервации (~$0.07), то есть ПОСЛЕДНИЕ ~2 ГЛАВЫ любой книги не покупаются** (`15-money-path.md`, греп `даже при ОДНОМ воркере`). Защёлка тут не помогает — она делает стоп штатным, а не финансирует последний вызов. Снимается ПЛАТФОРМЕННОЙ половиной (потолок ДЕНЬГАМИ, `D39.196`, строка 280), которая НЕ построена: `git grep -lE "StepMax|step_max" -- platform/` → пусто. ⚠ **Пол ВЫВЕДЕН, не замерен** (платного прогона не было — D39.206 п.6). ⇒ `PD-440` жив в ЭТОЙ половине: его волновая часть починена движком, денежная — нет | D39.2 · D38.3 · D39.175 · D39.178 | +> | 6 Export | ✅ **ПЛАТФОРМЕННАЯ половина ПОСТРОЕНА 06.09** — что именно и с какой ценой, ОДНИМ носителем: `15-money-path.md` §3 (акт `D39.208`). Здесь только статус, механика не дублируется. ✅ ДВИЖКОВАЯ половина ПОСТРОЕНА: `checks.ExportNormalize` на каждом финале; санитайзер-классы strip+export · **писатель книги: `internal/bookfile` + `tmctl build` (EPUB 3 + txt, D39.175)**; холодный прогон 31.08 предъявил собранный EPUB настоящего перевода, `epubcheck` 0/0/0/0. ✅ **ПЛАТФОРМЕННАЯ половина ПОСТРОЕНА 04.09, ЦИКЛ ЗАМКНУТ ЖИВЬЁМ** (`adf5e53`, D39.194): пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0. Дверь — три адреса (`createExport`/`getExport`/`downloadExport`), `202`+`Location`, поллинг с `Retry-After`, Range 206, TTL с уборкой, идемпотентность. Проверка вместо счёта: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` → НЕ пусто; операций в каноне 21, смонтировано **19** (испр. 05.09: `updateBook` смонтирован платформенным паком `7e2226a`). ⚠ **СТЕНА ДЕСЯТИ ГЛАВ СНЯТА 05.09, НО ПОЛ ОСТАЛСЯ — ДОЧИТАТЬ ДО КОНЦА ПО-ПРЕЖНЕМУ НЕЛЬЗЯ.** — движковый пак `81a89e9` (акт D39.206) снял стену: отказ резервации ЗАЩЁЛКИВАЕТ волну, а обязательный вызов ЖДЁТ settle; порог «короче десяти глав не переводится» СНЯТ, остаточный пол — одна резервация (~3 главы) и он ВЫВЕДЕН, не замерен. Разбор — `15-money-path.md`, греп `ЭРРАТА 05.09`. ⚠ **Испр. 10.09: здесь стоял ⛔-«инвариантный пол» (`2 × $0.03 = $0.06` против редакторской резервации ~$0.07 ⇒ «последние ~2 главы любой книги не покупаются») — СНЯТ.** Он цитировал ДО-ПАКОВУЮ АРХЕОЛОГИЮ: в `15-money-path.md` этот абзац стоит ПОД баннером «⛔⛔ ЭРРАТА 05.09 — всё ниже описывает движок ДО пака «денежный стоп»» (акт `D39.206`). И его арифметика стоит на ставке $0.03 за главу, которой в коде больше нет ни константой, ни ручкой (`15-money-path.md`, греп `СТАВКИ ЗА ГЛАВУ БОЛЬШЕ НЕ СУЩЕСТВУЕТ`). ⇒ пол, если он нужен, берётся из `pricing.Hold`, а не из удалённой константы. Защёлка тут не помогает — она делает стоп штатным, а не финансирует последний вызов. Снимается ПЛАТФОРМЕННОЙ половиной (потолок ДЕНЬГАМИ, `D39.196`, строка 280). ⚠ **Испр. 10.09: она ПОСТРОЕНА, и прежний текст противоречил началу этой же ячейки.** Та же команда сегодня: `git grep -lE "StepMax|step_max" -- platform/` → **10 файлов** (контроль: `git grep -lE "ceiling" -- platform/` → 119; зона отвечает). Поведение: `platform/internal/pricing/pricing.go` (греп `func (m Model) Hold`), боевой вызов `platform/internal/runs/runs.go`, колонка `platform/internal/pgstore/migrations/00033_order_and_price.sql` (греп `step_max_micro_usd`). Строка **280** закрыта актом `D39.208`. ⚠ **Пол ВЫВЕДЕН, не замерен** (платного прогона не было — D39.206 п.6). ⇒ `PD-440` жив в ЭТОЙ половине: его волновая часть починена движком, денежная — нет | D39.2 · D38.3 · D39.175 · D39.178 | > | 7 Гигиена | ✅ ПОСТРОЕН: реестр role→инъекция · вынос `foldModelWire` · снос сирот/`gatesEnabled` · **target-aware гейты ПО ДАННЫМ — П1 «цель-шов» (фаза 2, D39.64: `isRuTarget` удалён, `TargetActive`/`TargetScriptNonLatin`, токенизатор цели из `word_script`); остаток — ё-фолд/segmentation за подписью (бэклог-строка 14)** | D39.2 · паки 11–16 · **фаза 2 (D39.64)** | > > **Общность (фаза 2) ИСПОЛНЕНА И ПРИНЯТА — D39.64**, отчёт `../archive/reports/GENERALITY_PHASE2_2026-08-01.md`: П0 хеш эмбед-данных (`embedded_version` в снапшоте) · П1 цель-шов (слой 7) · П2 скрипт-шов источника (`lang-script.txt`) · П3 нарезка/оценка (один дом `DenseScript`) · П4 манифест-по-каналам (`packAlgoVersion` не бампался); голден вердикт-нейтрален (0/172); П5 майнер гейтится второй книгой; 9 флагов §5 — очередь владельца (бэклог-строка 14). **Карта языковой привязки ВСЕГО движка — `../archive/reports/GENERALITY_RESEARCH_2026-07-31.md` (D39.60)**; пак-19 «голос/состояние» — D39.54–56. @@ -125,7 +125,7 @@ | Слой | Несущие находки (HIGH, CONFIRMED) | |---|---| -| Процесс (к.5) | `H18-no-standing-findings-ledger-process` + незакрытые владельческие: `H6` running-summary, `H7` input-size-limit, `H8` abuse-prescreen, `H9` parallelism/speed (V4-п1), `H15` glossary-auto-build (V4-п4), `H10` user-stop, `H16` self-improvement | +| Процесс (к.5) | `H18-no-standing-findings-ledger-process` + незакрытые владельческие: `H6` running-summary, `H7` input-size-limit, `H8` abuse-prescreen, ⚠ **испр. 10.09 — три из этого списка больше не «незакрытые»:** `H9` parallelism/speed ПОСТРОЕН (волна веером: `backend/internal/pipeline/waverun.go`, греп `runWave fans`; кноб `workers` в `backend/internal/config/pipeline.go`); `H15` glossary-auto-build ПОСТРОЕН (пакет `backend/internal/terminology/`, греп `func AttachKWIC`; писатель решений `backend/internal/membank/decisions.go`; жанр-условие владелец отозвал, веб-фетч остаётся долгом ПТ-10); `H10` user-stop — ⛔ **термин РАЗДВОИЛСЯ** (`D39.234` п.1): остановок ратифицировано ДВЕ, жёсткая ПОСТРОЕНА (`backend/cmd/tmctl/main.go`, греп `signal.NotifyContext`), мягкой НЕТ — это КОД отстаёт от ратификации, носители — строки **381** · **382** · **377**, `H16` self-improvement | --- diff --git a/docs/architecture/10-prompt-architecture.md b/docs/architecture/10-prompt-architecture.md index 1f9d2148..6c56472d 100644 --- a/docs/architecture/10-prompt-architecture.md +++ b/docs/architecture/10-prompt-architecture.md @@ -19,7 +19,7 @@ | Файл | Роль | Что внутри | Пара | |---|---|---|---| -| `zh-ru/translator.md` | черновик (flash) | брифинг-переменные `{{source_lang/target_lang/audience/honorifics/transcription/venuti/footnotes}}` плюс `{{title}}`/`{{text}}` (снято ИСПОЛНЕНИЕМ: `grep -o '{{[^}]*}}' backend/prompts/zh-ru/translator.md`; ⚠ `{{genre}}` в списке БОЛЬШЕ НЕТ — жанр выкинут словом владельца 16.08, строка бэклога 184, а `{{lang}}` разошёлся на пару) + слабая вёрстка-однострочка (D30.2) | zh→ru | +| `zh-ru/translator.md` | черновик (flash) | брифинг-переменные `{{source_lang/target_lang/audience/honorifics/transcription/venuti/footnotes}}` плюс `{{title}}`/`{{text}}` (снято ИСПОЛНЕНИЕМ: `grep -o '{{[^}]*}}' backend/prompts/zh-ru/translator.md`; ⚠ `{{genre}}` в списке БОЛЬШЕ НЕТ — жанр выкинут словом владельца 16.08, ⚠ строка бэклога 184 **ЗАКРЫТА** (пометка 10.09; факт про жанр верен и проверяется грепом `genre` по `backend/prompts/zh-ru/translator.md` — 0 хитов), а `{{lang}}` разошёлся на пару) + слабая вёрстка-однострочка (D30.2) | zh→ru | | `zh-ru/editor.md` | редактор (deepseek-v4-pro БИЛИНГВ ИНТЕРИМ, D39.22; glm-5 резерв) — ⚠ **версию брать ФАЙЛОМ: `backend/configs/pipeline-c1.yaml`, ключ `prompt_version` стадии редактора** (испр. 05.09: здесь стояло `v3` при боевом `v4-discourse-reflow-nogenre`, а с D39.182 метка НЕСУЩАЯ — `TestPromptLabelsPinTheirBytes`; поверивший таблице бампнул бы уже-бампнутое и получил красный гейт) | 4 мандата + ДИСКУРС-ПЕРЕВЁРСТКА (Ван Цуй) + чэнъюй-атом + структурные few-shot (`---FEWSHOT---`, тумблер per-stage) | zh→ru | | `zh-ru/editor-mono.md` | референс-вариант D13.1 (не боевой) | узкий моно-мандат | — | | `zh-ru/judge-selector.md` | Ф2 (не исполняется) | — | — | diff --git a/docs/architecture/13-tech-debt-anchors.md b/docs/architecture/13-tech-debt-anchors.md index 7648b4ab..08c54a68 100644 --- a/docs/architecture/13-tech-debt-anchors.md +++ b/docs/architecture/13-tech-debt-anchors.md @@ -26,7 +26,7 @@ Bertalign: grep -rni bertalign по eval/ и backend/ — 0 вхождений ( ### [CONFIRMED_LOST] Дописка к строке 53 — внутрифайловый дубль ключа pair-файла → громкая ошибка → **частично ИСПОЛНЕНО D39.82 (putUniqueDC, строка 53 закрыта); остаток parseCategoryRows — строка 111** (PACK15 §Внутрифайловый) -`parseCategoryRows` (`backend/internal/lang/langpack.go`, греп `cats[f[0]][f[1]] = val`): повторный ключ ВНУТРИ одного файла молча перезаписывает предыдущий (last-wins). ГРОМКО коллизия ловится только на стыке уже слитого состояния и следующего источника — `unionStringMap` («key … is already defined … and the overlay redefines it»); внутрифайловые дубли схлопываются ДО того, как merge их увидит. Теста нет (grep duplicate/дубл/already defined по `langpack_test.go` — 0). Скоуп: мелкие хвосты качества одним паком; улика — `docs/archive/reports/PACK15_STRUCTURE_REPORT_2026-07-25.md`, греп `Дубль ключа ВНУТРИ`. +`parseCategoryRows` (`backend/internal/lang/langpack.go`, греп `cats[f[0]][f[1]] = val`): повторный ключ ВНУТРИ одного файла молча перезаписывает предыдущий (last-wins). ГРОМКО коллизия ловится только на стыке уже слитого состояния и следующего источника — `unionStringMap` («key … is already defined … and the overlay redefines it»); внутрифайловые дубли схлопываются ДО того, как merge их увидит. ⚠ **Отрицательный замер испр. 10.09: он ПРОТУХ, а вывод устоял по ДРУГОЙ причине.** Тот же греп по `backend/internal/lang/langpack_test.go` даёт сегодня **3** хита (контроль: файл жив, 619 строк) — но держат они `parseDCCheckers` (`TestParseDCCheckersRefusesIntraFileDupKey`), а предмет ячейки — `parseCategoryRows`, и он по-прежнему тихо last-wins: `backend/internal/lang/langpack.go`, греп `cats[f[0]][f[1]] = val` — проверки на существующий ключ там нет. Скоуп: мелкие хвосты качества одним паком; улика — `docs/archive/reports/PACK15_STRUCTURE_REPORT_2026-07-25.md`, греп `Дубль ключа ВНУТРИ`. ### [CONFIRMED_LOST] Дописка к строке 11 — судьба v1.1 quote-absorption при флипе coverage-гейта (D12-Q1) @@ -153,9 +153,11 @@ Go-гейт стоит на v1 без поглощения кавычек: backe **Флор — калиброванная константа без модели «thinking в completion»: МОДЕЛЬ ПОЛУЧЕНА замером (D39.86), сама строка сужена до выбора числа.** Замер боевой формы: при вендор-дефолте `high` размышление на плотном хане потолком НЕ ограничивается (8496 → 0/9 пригодных, 16992 → 1/5; вендорская норма вывода для `high` = 384K) ⇒ **флор не является рычагом против неограниченного думания — рычаг это `reasoning_effort`**; при `low` размышление 170–313 ток. и флор 8000 избыточен. Остаток строки: какое число ставить ПОСЛЕ развилки 74 (⚠ испр. 02.09: клейм «у `deepseek-v4-pro` эффорт не настраивается, `low→high`» БОЛЬШЕ НЕ ДЕЙСТВУЕТ — звено про `low` отозвано самим же квирком (`docs/experiments/00-provider-quirks.md`, греп `ЗВЕНО (б) ПРО`, п.3б: статус `low` «НЕ УСТАНОВЛЕНО»), а вендор-чейнджлог 13.08 дал V4-Pro уровни low/high/max — §Б-108, вахта D39.92). ⚠ **РАСХОЖДЕНИЕ ТРЕКЕРА СНЯТО (D39.90):** строка 74 объявляла 44 «закрытой этим замером» — это верно только для ПОЛОВИНЫ «поднять флор» (фальсифицирована). Живой остаток — обратный: **опустить/пере-калибровать число по числам пробы `bank-low`**, где стена на `low` не исчезла, а разредилась (упавший батч: `content=""`, `finish=length`, `completion=7999`, `reasoning=7999` — тот же тяжёлый хвост, 1/5 вместо 4/5, и это стохастика вызова: батч не самый большой по входу). ⚠ **Флор при `low` НЕ варьировался ни разу** — упавший батч умер ровно на потолке 8000, так что «сколько ставить» остаётся незамеренным вопросом, а не выводом -### Б-46 — тело строки 46 на 09.08.2026 +### Б-46 — тело строки 46 на 09.08.2026 → **D39.199, ОБЕ половины закрыты (пометка 10.09)** -**Экран целевого языка на выходе НЕ-ПОСЛЕДНЕЙ стадии — ЖИВОЙ НОСИТЕЛЬ ПОЛУЧЕН, класс поднят (D39.86).** Было: 38/40 дефектов латиницы живут в черновиках вне охвата гейтов (`waverun.go:486`). Стало: на ре-пробе 74 при `effort:low` **1 черновик из 18 пришёл ПОЛНЫМ связным переводом НА АНГЛИЙСКИЙ** и прошёл как `disposition=ok` — `classify` экранирует ИСХОДНУЮ письменность, пустоту, обрыв и отказ, а «не исходный и не целевой» не ловит ни один предикат (частично ловится лишь третий язык, ДЕЛЯЩИЙ письменность с источником, и ловится под ЛОЖНОЙ причиной `cjk_artifact`). ⚠ Последний рубеж УСЛОВЕН тройкой `isFinal && Gates.Sanitizer.Enabled && checkers.TargetActive()` (`chunkrun.go:52`), и **дыра на оси ЦЕЛИ двусторонняя**: (i) без данных цели слой 7 инертен ПО ОТСУТСТВИЮ (осознанный дизайн, `disposition.go:302-305`) ⇒ для не-русской цели рубежа нет вовсе; (ii) а с данными будущей ЛАТИНОПИСЬМЕННОЙ цели `detectLatinInsertion` (`checks/sanitizer.go:228`) сработает на ЧИСТОЙ целевой прозе и дропнет 100% чанков — внутри санитайзера он НЕ обусловлен `TargetScriptNonLatin()`, хотя доккоммент это объявляет (реальный гард только на репэйре — `quality.go:215`, `repair.go:208`), а частичный файл цели невозможен (`CompileCheckers` паникует). ⇒ **ревью-вопрос «заработает ли пара, которой в репо нет, без правки Go?» для →en/→de отвечается НЕТ**; кандидат фикса — тот же шов, что уже применён к репэйру. *(Контрпример «pipeline-c2.yaml — легальная сборка без санитайзера» проверен и СНЯТ: c2 неисполняем `CheckRunnable`, его санитайзер забукован строкой 33.)* Даже при включённом санитайзере худший исход — не потеря юнита, а ТИХО ОТГРУЖЕННЫЙ релейный перевод zh→en→ru, если билингв-редактор отрендерит русский с английского черновика. Область фикса — «выход любой НЕ-последней стадии», не «черновик» (`isFinal` позиционный, `stagerun.go:145`). ⚠ **ИСПР. 05.09: КОД ПОСТРОЕН — экран целевого языка внесён вайр-батчем (`backend/internal/langscreen/`), строка бэклога 46 ЗАКРЫТА и снята из таблицы (D39.199/D39.195).** Ниже — АРХЕОЛОГИЯ подачи дизайна, читать как историю. **ДИЗАЙН ПОДАН, КОДА НЕТ — СТОИТ НА РЕШЕНИИ ВЛАДЕЛЬЦА (D39.90).** Три поправки сессии к посылке, все замером: **(1) боевой гейт уже ловит 18 из 20** off-target событий (`sourceScriptShare > 0.15`, `disposition.go:224,275`; 0 ложных на 1055 здоровых, запас 13×) ⇒ остаточная дыра **2 чанка из ~1100 = 0.18%**, оба английские, японский ловится долей ханьцзы; **(2) второй рубеж существует, но стоит не там** — санитайзер поймал бы те самые байты («Latin insertion»), но `chunkrun.go:52` требует `isFinal`, а в двухстадийной топологии черновик НИКОГДА не финальный; тот же `isFinal` глушит на черновике латинный линт, флаггер голоса и глоссарный пост-чек; **(3)** здоровый минимум на тексте, который РЕАЛЬНО видит гейт (после среза банкноты), = 0.9767 при n=1055. **Развилка владельцу:** (а) предикат внутри `classify()` + бамп `classifierVersion` — двигает снапшот у всех книг, голден пере-захват один раз, на свежем стенде $0; (б) отдельный опциональный гейт вне `classify()` — снапшот не двигает, но это второй способ сказать «ответ непригоден», и он выключен по умолчанию; (в) языковой детектор. **Выбор сессии — (а).** Носитель данных — `lang.LangScripts(Book.TargetLang)`, НОВЫЙ go:embed-файл не заводить (`EmbeddedVersion` фолдится безусловно = перекупка всех книг всех пар); порог 0.50 не калиброван, а поставлен в середину пустого интервала. Три мины наивной реализации названы: новый флаг провалился бы мимо `escalatable()`/`retryable()` (⇒ переиспользовать `FlagCJKArtifact`), классификатор ложно флагался бы 16/16, экран слеп к ПОДМНОЖЕСТВАМ письменности (чисто китайский выход для →ja наберёт 1.000). Вторая половина дыры (`detectLatinInsertion` без `TargetScriptNonLatin()`) — чинить тем же диффом, для ru вердикты не двигаются. **Константы и хвосты дизайна, без которых подпись неполна:** порог **abstain 200 букв** (ниже экран воздерживается; самое короткое off-target событие 346 букв, ниже 200 падают лишь 5 из 1055 здоровых, все со счётом 1.0000) · ⚠ **опубликованная база «~1.5% off-target» НЕВЕРНА по знаменателю** — он пулит черновики с редакторскими выходами, а редактор такой отказ произвести не может; на ЧЕРНОВОЙ вызов база **3.02%**, то есть вдвое хуже · ⚠ **не проверено, что делает РЕДАКТОР с английским черновиком** — вытащит обратно в русский или отредактирует как английский; именно этот неизвестный решает, чем является остаточные 0.18%: потерей юнита или тихо отгруженным релейным zh→en→ru · английских черновика найдено ДВА (`fb51e135a395`, `4080ee8287af`), плюс японский в exp15 · **`cultivation` чинить в СИДЕ, а не порогом:** 86 из 1055 здоровых несут голое английское `cultivation` внутри беглого русского, и все 32 абзаца ниже 0.90 содержат именно его, ни один — ханьцзы; правка пар-данных двигает `LangpackVersion` с `omitempty` ⇒ перекупка ограничена книгами ЭТОЙ пары (чинить на плане, где перекупка уже) · Hunspell и GlotScript-эмбед — **не строить** · база замера = строка **113** · **02.08 вечер (D39.92): владелец пере-поставил задачу closed-set** («вернуло НЕ тот язык, что просили»; полноценный LID — только запасной флаггер вне пути вердикта; проверку изолировать отдельным слоем) — ресёрч §5.1–5.3 отвечает постановке, форма (а)+изолированный модуль подтверждена, слой-0 владельца (полоса len(out)/len(in)) добавить замером на фикстуре 113 +> ⚠ **Закрыто, тело оставлено археологией по норме этой секции.** Обе половины опровергнуты деревом: экран цели построен пакетом `backend/internal/langscreen/` (греп `func Screen`), вердикт заходит в классификатор (`backend/internal/pipeline/disposition.go`, греп `FlagOffTargetLang`); латинская вставка в санитайзере ОБУСЛОВЛЕНА `TargetScriptNonLatin()` (`backend/internal/checks/sanitizer.go`, греп `if c.TargetScriptNonLatin()`), и доккоммент над гардом называет эту самую ячейку. Строки 46 в живом трекере нет. + +**Экран целевого языка на выходе НЕ-ПОСЛЕДНЕЙ стадии — ЖИВОЙ НОСИТЕЛЬ ПОЛУЧЕН, класс поднят (D39.86).** Было: 38/40 дефектов латиницы живут в черновиках вне охвата гейтов (`waverun.go:486`). Стало: на ре-пробе 74 при `effort:low` **1 черновик из 18 пришёл ПОЛНЫМ связным переводом НА АНГЛИЙСКИЙ** и прошёл как `disposition=ok` — `classify` экранирует ИСХОДНУЮ письменность, пустоту, обрыв и отказ, а «не исходный и не целевой» не ловит ни один предикат (частично ловится лишь третий язык, ДЕЛЯЩИЙ письменность с источником, и ловится под ЛОЖНОЙ причиной `cjk_artifact`). ⚠ Последний рубеж УСЛОВЕН тройкой `isFinal && Gates.Sanitizer.Enabled && checkers.TargetActive()` (`chunkrun.go:52`), и **дыра на оси ЦЕЛИ двусторонняя**: (i) без данных цели слой 7 инертен ПО ОТСУТСТВИЮ (осознанный дизайн, `disposition.go:302-305`) ⇒ для не-русской цели рубежа нет вовсе; (ii) а с данными будущей ЛАТИНОПИСЬМЕННОЙ цели `detectLatinInsertion` (`checks/sanitizer.go:228`) сработает на ЧИСТОЙ целевой прозе и дропнет 100% чанков — внутри санитайзера он НЕ обусловлен `TargetScriptNonLatin()`, хотя доккоммент это объявляет (реальный гард только на репэйре — `quality.go:215`, `repair.go:208`), а частичный файл цели невозможен (`CompileCheckers` паникует). ⇒ **ревью-вопрос «заработает ли пара, которой в репо нет, без правки Go?» для →en/→de отвечается НЕТ**; кандидат фикса — тот же шов, что уже применён к репэйру. *(Контрпример «pipeline-c2.yaml — легальная сборка без санитайзера» проверен и СНЯТ: c2 неисполняем `CheckRunnable`, его санитайзер забукован строкой 33.)* Даже при включённом санитайзере худший исход — не потеря юнита, а ТИХО ОТГРУЖЕННЫЙ релейный перевод zh→en→ru, если билингв-редактор отрендерит русский с английского черновика. Область фикса — «выход любой НЕ-последней стадии», не «черновик» (`isFinal` позиционный, `stagerun.go:145`). ⚠ **ИСПР. 05.09: КОД ПОСТРОЕН — экран целевого языка внесён вайр-батчем (`backend/internal/langscreen/`), строка бэклога 46 ЗАКРЫТА и снята из таблицы (D39.199/D39.195).** Ниже — АРХЕОЛОГИЯ подачи дизайна, читать как историю. **ДИЗАЙН ПОДАН, КОДА НЕТ — СТОИТ НА РЕШЕНИИ ВЛАДЕЛЬЦА (D39.90).** Три поправки сессии к посылке, все замером: **(1) боевой гейт уже ловит 18 из 20** off-target событий (`sourceScriptShare > 0.15`, `disposition.go:224,275`; 0 ложных на 1055 здоровых, запас 13×) ⇒ остаточная дыра **2 чанка из ~1100 = 0.18%**, оба английские, японский ловится долей ханьцзы; **(2) второй рубеж существует, но стоит не там** — санитайзер поймал бы те самые байты («Latin insertion»), но `chunkrun.go:52` требует `isFinal`, а в двухстадийной топологии черновик НИКОГДА не финальный; тот же `isFinal` глушит на черновике латинный линт, флаггер голоса и глоссарный пост-чек; **(3)** здоровый минимум на тексте, который РЕАЛЬНО видит гейт (после среза банкноты), = 0.9767 при n=1055. **Развилка владельцу:** (а) предикат внутри `classify()` + бамп `classifierVersion` — двигает снапшот у всех книг, голден пере-захват один раз, на свежем стенде $0; (б) отдельный опциональный гейт вне `classify()` — снапшот не двигает, но это второй способ сказать «ответ непригоден», и он выключен по умолчанию; (в) языковой детектор. **Выбор сессии — (а).** Носитель данных — `lang.LangScripts(Book.TargetLang)`, НОВЫЙ go:embed-файл не заводить (`EmbeddedVersion` фолдится безусловно = перекупка всех книг всех пар); порог 0.50 не калиброван, а поставлен в середину пустого интервала. Три мины наивной реализации названы: новый флаг провалился бы мимо `escalatable()`/`retryable()` (⇒ переиспользовать `FlagCJKArtifact`), классификатор ложно флагался бы 16/16, экран слеп к ПОДМНОЖЕСТВАМ письменности (чисто китайский выход для →ja наберёт 1.000). Вторая половина дыры (`detectLatinInsertion` без `TargetScriptNonLatin()`) — чинить тем же диффом, для ru вердикты не двигаются. **Константы и хвосты дизайна, без которых подпись неполна:** порог **abstain 200 букв** (ниже экран воздерживается; самое короткое off-target событие 346 букв, ниже 200 падают лишь 5 из 1055 здоровых, все со счётом 1.0000) · ⚠ **опубликованная база «~1.5% off-target» НЕВЕРНА по знаменателю** — он пулит черновики с редакторскими выходами, а редактор такой отказ произвести не может; на ЧЕРНОВОЙ вызов база **3.02%**, то есть вдвое хуже · ⚠ **не проверено, что делает РЕДАКТОР с английским черновиком** — вытащит обратно в русский или отредактирует как английский; именно этот неизвестный решает, чем является остаточные 0.18%: потерей юнита или тихо отгруженным релейным zh→en→ru · английских черновика найдено ДВА (`fb51e135a395`, `4080ee8287af`), плюс японский в exp15 · **`cultivation` чинить в СИДЕ, а не порогом:** 86 из 1055 здоровых несут голое английское `cultivation` внутри беглого русского, и все 32 абзаца ниже 0.90 содержат именно его, ни один — ханьцзы; правка пар-данных двигает `LangpackVersion` с `omitempty` ⇒ перекупка ограничена книгами ЭТОЙ пары (чинить на плане, где перекупка уже) · Hunspell и GlotScript-эмбед — **не строить** · база замера = строка **113** (⚠ **закрыта вместе с 46**, `D39.199`; в живом трекере её нет) · **02.08 вечер (D39.92): владелец пере-поставил задачу closed-set** («вернуло НЕ тот язык, что просили»; полноценный LID — только запасной флаггер вне пути вердикта; проверку изолировать отдельным слоем) — ресёрч §5.1–5.3 отвечает постановке, форма (а)+изолированный модуль подтверждена, слой-0 владельца (полоса len(out)/len(in)) добавить замером на фикстуре 113 ### Б-105 — тело строки 105 на 09.08.2026 diff --git a/docs/architecture/14-api-contract/README.md b/docs/architecture/14-api-contract/README.md index 87cc06bf..9f065bf2 100644 --- a/docs/architecture/14-api-contract/README.md +++ b/docs/architecture/14-api-contract/README.md @@ -59,7 +59,7 @@ cmp-сверка обязательна (D39.138 п.3). > шестнадцатая причина замечания `off_target_lang` получает код `wrong_language` (Приложение А). > Провенанс — §2.23**. > Дом канона — этот каталог; -> `frontend/docs/api-contract/openapi.yaml` — байт-зеркало ⚠ НА ФРИЗЕ ЗОНЫ РАВЕНСТВО ПРИОСТАНОВЛЕНО (D39.142 п.5): зеркало 0.2.3, канон 0.10.0. +> `frontend/docs/api-contract/openapi.yaml` — байт-зеркало ⚠ НА ФРИЗЕ ЗОНЫ РАВЕНСТВО ПРИОСТАНОВЛЕНО (D39.142 п.5): зеркало 0.2.3, канон **0.13.0** (⚠ число испр. 10.09 — здесь стояло 0.10.0, отставшее на два минора: 0.12.0 — `D39.211`, 0.13.0 — `D39.221`; шапка файла и `openapi.yaml` всё это время несли верное). Прежний текст: канон 0.10.0. > > ⚠ **0.5.0 ломающий по построению (мажор `0`): снесены путь `POST …/bank/decisions` и три его > схемы, из `BankPage` и `EventBank` сняты `pending_decisions`/`complete` (§2.19-бис), в @@ -1275,7 +1275,7 @@ focus»), то есть цена платится на горячем пути книги. Тогда это настоящая дыра и контракт открывается заново. ⚠ **Попутно найдено и стоит знать зоне:** суточный потолок — НЕ единственный стоп, который деньги -не лечат. Их четыре класса (`cmd/tmctl/main.go:52-79`): подпись банка (exit 3), graceful stop +не лечат. Их четыре класса (⚠ **адрес испр. 10.09: словарь кодов 3/4/5 живёт не там** — `backend/cmd/tmctl/main.go`, греп `func exitCode`; по прежнему адресу `:52-79` лежит только полоса отказов 10–19): подпись банка (exit 3), graceful stop (exit 5), полоса отказов 10–19 и суточный потолок (exit 4, scope `day`). Просто три из четырёх уже имеют свою проекцию (`awaiting_bank`, `stopped`, `failed` + `RunFailureReason`), и «денег не хватает» из них не следует ни для одного. То есть класс закрыт, а дыра — только в четвёртом. diff --git a/docs/architecture/15-money-path.md b/docs/architecture/15-money-path.md index d304b080..932f0516 100644 --- a/docs/architecture/15-money-path.md +++ b/docs/architecture/15-money-path.md @@ -22,8 +22,8 @@ - **`--ceiling-usd` — КНИЖНЫЙ потолок, не бюджет прогона.** Дословно из флага: «the book USD ceiling in force for THIS RUN ONLY — it OVERRIDES book.yaml `ceilings.book_usd` and is never written back. It caps the book's CUMULATIVE committed+reserved spend, not this run's increment…» (`backend/cmd/tmctl/invocation.go:142`=`the book USD ceiling in force for THIS RUN ONLY`; ⚠-коммент `backend/cmd/tmctl/main.go:260`=`--ceiling-usd is NOT a per-run budget`; многоточие закрывает обрыв цитаты — во флаге дальше стоит «, and must be > 0»). Ратификация — D39.122 п.2(в): пересчёт «пользовательский прирост → абсолют» — обязанность ПЛАТФОРМЫ, вторая денежная ось не заводится. День-потолок флаг НЕ перекрывает (PD-157). Проводка внутри: `Ceilings.BookUSD` в `Reserve` и именование сработавшего потолка в ошибке — `backend/internal/pipeline/stagerun.go:571`=`BookUSD: r.bookCeilingUSD()` и `backend/internal/pipeline/stagerun.go:653`=`overrides the book's ceilings.book_usd` (стоп = `errReserveCeiling`, `backend/internal/pipeline/escalation.go:49`=`var errReserveCeiling`). - **Потолки — wiring, не семантика:** `Ceilings` намеренно исключены из `BriefHash` («Wiring fields (paths, ceilings, db) deliberately excluded», `backend/internal/config/book.go:345`=`Wiring fields (paths, ceilings, db) deliberately excluded`; канон — `backend/internal/config/book.go:369`=`canon := struct {`) ⇒ смена ДЕНЕЖНОГО потолка не двигает ни снапшот, ни ре-билл (D39.110 п.2б). ⚠ Верно ровно про ДЕНЬГИ: потолок СЕГМЕНТАЦИИ `edit_ceiling_out` в снапшот ВХОДИТ и меняет границы чанков (`backend/internal/pipeline/snapshot.go:34`=`EditCeilingOut int`). - **`status --json` отдаёт фигуры платформе:** `committed_usd`, `reserved_usd`, `book_ceiling_usd`, `ceiling_pct` = 100·(committed+reserved)/book_ceiling (`backend/internal/pipeline/status.go:324`=`book_ceiling_usd,omitempty`; арифметика `ceiling_pct` — `backend/internal/pipeline/status.go:918`=`100 * (committed + reserved)`). Дневной фигуры в status нет (PD-157 — `platform/docs/DEFECT_REGISTER.md`, греп `PD-157`). -- ✅ **Канал «леджер = нижняя граница» ЗАКРЫТ 10.09** (строка 78, акт `D39.232`). Прежде оплаченный `2xx` с нечитаемым телом в `request_log` не попадал: провайдер списал, а в леджере следа нет — живой замер `D39.86` дал 3 вызова из 14 и неизвестность $0.015111 при леджере $0.114378. Теперь такой вызов пишет СВОЮ строку и помечает её оценкой (`backend/internal/pipeline/cutcall.go:133`=`rl.Estimated, rl.EstTokens = cost > 0`, затем `r.Store.LogRequest`). ⚠ Нижней границей леджер быть НЕ перестал по другой причине: обрыв, за которым ретрай УСПЕЛ, по-прежнему исчезает бесследно — строка **377**. -- **ГРАНИЦА ДЕНЕГ ЗА ОБОРВАННЫЙ НАМИ ВЫЗОВ — ФАКТ ДОСТАВКИ, а не статус-строка** (пак строки 360, акт `D39.232`; слово владельца — `D39.230`). Деньги берутся, только когда провайдер подтвердил запрос ответным объектом: `backend/internal/llm/attemptcut.go:221`=`Billable: answered && tr.afterHeaders()`. Наш собственный дедлайн даёт свой класс `attempt_timeout` без ретрая; дедлайн вычисляется ИЗ БЮДЖЕТА вызова, а не назначается (`backend/internal/llm/attemptcut.go:269`=`func (p RetryProfile) deriveDeadline`), так что эскалация, удвоившая `max_tokens`, удваивает и время. Оплаченный обрыв получает чекпойнт, и резюм его НЕ перепокупает. ⛔ Пометка «это оценка» — УСЛОВИЕ слова владельца (`D39.230` п.1) — сегодня живёт только в `status --json` и по шву к платформе НЕ едет: строка **382**. +- **Леджер = НИЖНЯЯ граница; строка 78 снята с трекера 10.09, но её ЖИВОЙ ОСТАТОК несёт строка 377.** ⛔ **Испр. 10.09 — прежняя редакция этого пункта, написанная в тот же день, была НЕВЕРНА, и ошибка моя.** Она утверждала, будто оплаченный `2xx` с нечитаемым телом «прежде в `request_log` не попадал» и что это починил пак оборванных вызовов. Замер: ветвь `BilledDecodeError` писала свою строку с пометкой оценки ещё до пака — на `3f05fab^` это `stagerun.go:629`, введено коммитом `2f91b04` **24.07**, а пак этой ветви не касался вовсе (хитов `Estimated` в его диффе по `stagerun.go` — **0**). И носитель я процитировал ЧУЖОЙ: `backend/internal/pipeline/cutcall.go:133` обслуживает `*llm.AttemptCutError` (`backend/internal/pipeline/stagerun.go:741`), а речь шла о `*llm.BilledDecodeError` (`stagerun.go:709`) — это разные классы, и второй до `cutcall.go` не доходит. **Что пак действительно купил:** строку `request_log` за вызов, который оборвали МЫ (прежде он падал в ветвь «ничего не куплено» и получал $0-строку) — вот это `cutcall.go:133`. **Что остаётся правдой:** леджер — нижняя граница, потому что обрыв, за которым ретрай УСПЕЛ, исчезает бесследно (строка **377**). Живой замер `D39.86` — 3 вызова из 14, неизвестность $0.015111 при леджере $0.114378. +- **ГРАНИЦА ДЕНЕГ ЗА ОБОРВАННЫЙ НАМИ ВЫЗОВ — ФАКТ ДОСТАВКИ, а не статус-строка** (пак строки 360, акт `D39.232`; слово владельца — `D39.230`). Деньги берутся, только когда провайдер подтвердил запрос ответным объектом: `backend/internal/llm/attemptcut.go:221`=`Billable: answered && tr.afterHeaders()`. Наш собственный дедлайн даёт свой класс `attempt_timeout` без ретрая; дедлайн вычисляется ИЗ БЮДЖЕТА вызова, а не назначается (`backend/internal/llm/attemptcut.go:269`=`func (p RetryProfile) deriveDeadline`), так что эскалация, удвоившая `max_tokens`, удваивает и время. Оплаченный обрыв получает чекпойнт, и резюм его НЕ перепокупает. ⛔ Пометка «это оценка» — УСЛОВИЕ слова владельца (`D39.230` п.1) — сегодня в движке живёт ТРЕМЯ носителями (колонка `Estimated` в `request_log` — `backend/internal/store/requestlog.go:41`; пара `estimated_rows`/`estimated_usd` в `status --json` — `backend/internal/pipeline/status.go:911`; печать оператору — `backend/cmd/tmctl/render.go:402`), но НАРУЖУ не выходит ни одним каналом шва: строка **382**. ⚠ Формулировка испр. 10.09 — прежняя («живёт только в `status --json`») звала следующего чинить пометку, которая уже есть. - **Согласие на пере-оплату — ДВИЖКОВЫЙ гейт**. Прогон, который пере-покупает уже оплаченные юниты дороже порога, ОСТАНАВЛИВАЕТСЯ и требует явного согласия. Порог по умолчанию ратифицирован D20.2-Q2: `min($0.50, 5% × ProjectedBookUSD)`. Носитель — `backend/internal/pipeline/rebill.go`, греп `rebillConsentThreshold`; единственная ручка книги — `rebill_consent_usd` (валидатор `backend/internal/config/book.go`, греп `rebill_consent_usd`; 0 = ратифицированный дефолт). ⚠ Порог считается БЕЗ контура банк-ролей (D39.182 §3), то есть срабатывает позже, чем подсказывает полная проекция. @@ -116,7 +116,7 @@ 3. **Менять надо ТРАКТОВКУ ОТКАЗА**, а не наличие резервации: «не начинаем новое» вместо «гасим всё». ⛔⛔ **ЭРРАТА 05.09 — ВСЁ НИЖЕ ОПИСЫВАЕТ ДВИЖОК ДО ПАКА «ДЕНЕЖНЫЙ СТОП» (приёмочный `81a89e9` + дофикс `616a8e4`, акт приёмки **D39.206**; ⚠ испр.: раньше здесь стояла D39.203 — это нота ВЛАДЕЛЬЦА про «сколько добавить», а не акт, и назван был только дофикс-коммит). Порог «книга короче десяти глав не переводится» СНЯТ ЛЕНДИНГОМ.** Стена стояла на том, что отказ ОДНОГО воркера ронял ВСЮ волну ⇒ резервы всех четырёх обязаны были уместиться в потолок разом. Пак это и чинил (его промт `docs/archive/prompts/BACKEND_MONEYSTOP_SESSION_PROMPT_2026-09-05.md` называл порог целью): отказ ЗАЩЁЛКИВАЕТ волну — новое не стартует, допущенное дорабатывается, — а обязательный вызов ЖДЁТ settle, когда settle может помочь. Пины: `backend/internal/pipeline/moneystop_test.go`, греп `TestUnderACeilingTheWaveFinishesWhatItAdmitted` и `TestWaitingForHeadroomDeliversTheWholeBook` («a ceiling that admits one call AT A TIME must still deliver the book»). -⇒ **Честное состояние на 05.09:** движок больше НЕ требует, чтобы вся волна умещалась в потолок; короткая книга идёт по одному вызову. Остаточный пол — ОДНА резервация (`step_max_usd` ≈ $0.07 ⇒ ~3 главы по ставке $0.03; до ИСПОЛНЕНИЯ `D39.204` удвоение на ретрае поднимает это до ~5 — ⚠ решение владельца принято 05.09, но КОД ещё не тронут, работа заведена строкой 291). ⚠ **Новый порог ВЫВЕДЕН, НЕ ЗАМЕРЕН** — отчёт пака называет это честно (`backend/docs/MONEYSTOP_REPORT.md` §9: цена защёлки не замерена, числа проекции с фактом не сверены). Замер стоит одного $0.15-прогона пятиглавой книги и передан полигону. ⚠ Платформенная половина (потолок ДЕНЬГАМИ, D39.196) снимает и этот пол по построению — но НЕ ПОСТРОЕНА: `git grep -lE "StepMax|step_max" -- platform/` пусто. +⇒ **Честное состояние на 05.09:** движок больше НЕ требует, чтобы вся волна умещалась в потолок; короткая книга идёт по одному вызову. Остаточный пол — ОДНА резервация (`step_max_usd` ≈ $0.07 ⇒ ~3 главы по ставке $0.03; до ИСПОЛНЕНИЯ `D39.204` удвоение на ретрае поднимает это до ~5 — ⚠ решение владельца принято 05.09, но КОД ещё не тронут, работа заведена строкой 291). ⚠ **Новый порог ВЫВЕДЕН, НЕ ЗАМЕРЕН** — отчёт пака называет это честно (`backend/docs/MONEYSTOP_REPORT.md` §9: цена защёлки не замерена, числа проекции с фактом не сверены). Замер стоит одного $0.15-прогона пятиглавой книги и передан полигону. ⚠ **Испр. 10.09: платформенная половина (потолок ДЕНЬГАМИ, D39.196) ПОСТРОЕНА, и прежний отрицательный замер здесь протух.** Та же команда сегодня: `git grep -lE "StepMax|step_max" -- platform/` → **20 файлов** (контроль: несуществующий токен — 0; файлов под `platform/` в индексе — 339), среди них боевые `platform/internal/pricing/pricing.go` (греп `func (m Model) Hold`), `platform/internal/ingest/manifest.go`, `platform/internal/pgstore/books.go`. Холд считается ИМЕННО на этих числах. ⚠ Строка **63** этого же файла предупредила об этом ещё 07.09 («вторая пережила лендинг `81a89e9`»), а фраза стояла — то есть предупреждение в доке само по себе не чинит дока. ⚠ **Класс ошибки, ради которого эррата написана так подробно: оркестратор заленджил пак, чей промт заказывал снятие этой стены, принял его — и в тот же час предупредил владельца о стене как о действующей, читая ЭТОТ абзац.** Канон, не обновлённый лендингом, врёт громче, чем отсутствующий. **ЧИСЛА, В КОТОРЫХ ЧТЕНИЯ РАСХОДИЛИСЬ (ДО-ПАКОВАЯ АРХЕОЛОГИЯ) — и оба верны на своём числе воркеров.** Зона платформы: стена diff --git a/docs/architecture/16-events-emitter.md b/docs/architecture/16-events-emitter.md index ff45e478..e7bb4428 100644 --- a/docs/architecture/16-events-emitter.md +++ b/docs/architecture/16-events-emitter.md @@ -55,7 +55,7 @@ ⚠ **ИСПР. 05.09 — СОБЫТИЕ ПОТОЛКА ТЕПЕРЬ НЕСЁТ ЧИСЛО.** Минор потока **1.3** (лендинг `81a89e9`, акт D39.206) добавил `Ceiling.ShortfallMicroUSD` — недостающую сумму, и `Finished.Money` — денежный леджер прогона. Ратифицировано владельцем (`D39.203`): движку РАЗРЕШЕНО говорить «сколько добавить», потому что наружу идёт ДО-ВЫЗОВНАЯ ОЦЕНКА, а не СТОИМОСТЬ. ⚠ Читатель, знающий потолок и кумулятивный `spend`, восстанавливает из неё оценку отказанного вызова — это принято ОСОЗНАННО и предъявлено приёмкой живым прогоном. Ниже — редакция ДО минора 1.3, читать как историю. **Событие потолка — факт без цифр.** ⚠ (редакция до 1.3; словарь потока с минора 1.3: `Ceiling.shortfall_micro_usd` — недостающая сумма, и `Finished.Money` — денежный леджер прогона) `Ceiling{halted}`: «It carries the FACT and nothing else: money never reaches the platform's wire or its INFO logs (D39.84), and the stop is resumable, so it is not a failure» (`events.go:123-127`). -**Различимость стопа — ПОСТРОЕНА (D39.131), и это был весь смысл заказа.** До пака потолочный стоп был неотличим от инфраструктурного отказа (оба = exit 1), из-за чего платформа ставила `failed` там, где контракт требует `paused` — открытый major PD-113, закрыт с обеих сторон (движок D39.131, платформа D39.132). Действующий словарь кодов выхода — в коде, а не здесь: `backend/cmd/tmctl/main.go` (греп `exitProjectLocked`; 0 clean · 1 infra · 2 completed-with-flags · 3 signature-stop банка · **4 потолок** · **5 graceful stop** · полоса отказов **10–19** с классами `config_invalid`/`source_unreadable`/`project_locked`/`schema_mismatch`/`decisions_rejected`/`write_incomplete`/`book_incomplete`) и его зеркало-читатель `platform/internal/ingest/exit.go`. +**Различимость стопа — ПОСТРОЕНА (D39.131), и это был весь смысл заказа.** ⚠ **Пометка 10.09: слово «стоп» с тех пор РАЗДВОИЛОСЬ.** `D39.234` п.1 ратифицировал ДВЕ остановки человека — мягкую и жёсткую, — и обе остаются кодом выхода **5** (полоса заморожена, `D39.233` п.6): какая именно случилась, скажет ПРИЗНАК в кадре `finished`, а не код. Ниже по тексту «5 graceful stop» читать как «оба стопа»; в дереве второй остановки пока нет — строка **381**. До пака потолочный стоп был неотличим от инфраструктурного отказа (оба = exit 1), из-за чего платформа ставила `failed` там, где контракт требует `paused` — открытый major PD-113, закрыт с обеих сторон (движок D39.131, платформа D39.132). Действующий словарь кодов выхода — в коде, а не здесь: `backend/cmd/tmctl/main.go` (греп `exitProjectLocked`; 0 clean · 1 infra · 2 completed-with-flags · 3 signature-stop банка · **4 потолок** · **5 graceful stop** · полоса отказов **10–19** с классами `config_invalid`/`source_unreadable`/`project_locked`/`schema_mismatch`/`decisions_rejected`/`write_incomplete`/`book_incomplete`) и его зеркало-читатель `platform/internal/ingest/exit.go`. **Потолочная окрестность — не здесь.** Семантика `--ceiling-usd` (КНИЖНЫЙ потолок в силе, не бюджет прогона; D39.122 п.2в) и формула аргумента потолка `committed + прирост×ставка` БЕЗ reserved (D39.123 п.2б, PD-158) живут одним маршрутом в `15-money-path.md` §2–§3. Для эмиттера важно ОДНО следствие: платформа выставляет потолок впритык к холду, значит стоп по потолку = ровно исчерпание холда — потому событие потолка и различимый exit-код и перестали быть косметикой (D39.123 п.2ж). diff --git a/docs/architecture/17-seam-inbound-law.md b/docs/architecture/17-seam-inbound-law.md index 065f943a..48ab92a7 100644 --- a/docs/architecture/17-seam-inbound-law.md +++ b/docs/architecture/17-seam-inbound-law.md @@ -46,7 +46,7 @@ (`backend/internal/config/book.go:200`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")`) · конвенционное опциональное чтение `.auto-bank.yaml` (`backend/internal/pipeline/mining.go:674`=`.auto-bank.yaml`). -2. **Посреди прогона внутрь идёт ТОЛЬКО сигнал.** `SIGTERM` = «дочти чанк и отпусти лок»; всё +2. **Посреди прогона внутрь идёт ТОЛЬКО сигнал.** `SIGTERM` = «дочти чанк и отпусти лок» — ⚠ **это ратифицированная ЦЕЛЬ, а не сегодняшнее дерево (пометка 10.09).** Владелец ратифицировал две остановки (`D39.234` п.1а): первый сигнал — мягкий, он и означает «дочти и отпусти». В КОДЕ его ещё нет: `backend/cmd/tmctl/main.go:207` отменяет контекст прогона, летящий вызов рвётся, книжится по оценке и метится `cancelled`. ⇒ норму НЕ переписывать под дерево, дерево догоняет норму; носитель работы — строка бэклога **381**, где этот адрес назван третьим. Всё остальное — на границах процессов. Арбитр — эксклюзивный flock проекта на всю жизнь прогона (`backend/internal/store/store.go:55`=`EXCLUSIVE flock on`), мутирующий глагол берёт его неблокирующе и при занятости отказывает **ровно классом 12** (`platform/internal/ingest/exit.go:69`=`ExitProjectLocked = 12`), а НЕ 10: @@ -68,7 +68,7 @@ 5. **Глагол зовётся ТЕМ ЖЕ пиннутым бинарём, что и возобновляемая попытка** (`runs/spawn.go:250-258`=`engineBinary is the path an attempt is PINNED to`, строка бэклога 139). Иначе файл, записанный новым бинарём, читается старым — а при нестрогом - загрузчике сида (строка **212**) новое поле теряется МОЛЧА. + загрузчике сида (строка **212**) новое поле теряется МОЛЧА (⚠ **адрес испр. 10.09: строки 212 в живом трекере НЕТ** — она закрыта; факт оставлен как довод, номер снят, чтобы не звать читателя в пустоту). 6. **Словарь движка ↔ словарь контракта — одна таблица, два направления, один файл.** Сегодня перевод односторонний, на чтении (`platform/internal/ingest/bank.go:15-21`=`Every vocabulary crossing here is TRANSLATED and none is passed through`). Запись @@ -94,7 +94,7 @@ | дверь | откуда следует | |---|---| -| `resnapshot --dry-run` | строка **124(в)**, глагол поименован там дословно. ⚠ Родня строки **231**: та просит ту же способность («оценить, ничего не покупая») со стороны ШВА, и решаться они, скорее всего, одним глаголом | +| `resnapshot --dry-run` | строка **124(в)**, глагол поименован там дословно. ⚠ **Испр. 10.09: ссылка на «родню строки 231» СНЯТА — такой строки в живом трекере нет,** а обоснование стояло на ней и на слове «скорее всего», то есть на догадке без хозяина. Прежний текст: родня строки 231 просит ту же способность («оценить, ничего не покупая») со стороны ШВА, и решаться они, скорее всего, одним глаголом | | ✅ **И ПРОЧИТАНА платформой с 06.09** — механика ОДНИМ носителем: `15-money-path.md` §3 (акт `D39.208`). Прежняя редакция: ⚠ движковая оценка $/глава — **ПОСТРОЕНА и опубликована 05.09** (`expected_usd` · `book_once_usd` · `step_max_usd` в `manifest --json`/`status --json`, лендинг `81a89e9`, акт D39.206); непостроена ПЛАТФОРМЕННАЯ половина — числа никем не читаются (`git grep -lE "StepMax|step_max" -- platform/` пусто) | строка **166**. Приоритет поднят: D39.165 §1 ратифицировал цену ОТ ОБЪЁМА исходника, и оценка перестала быть удобством. ⚠ Числа цены главы и их пере-пин под цены с 16.08 — только `15-money-path.md` §3 п.3, здесь копии нет | | `tmctl init` (форма В) | строка **170**; приоритет ПАДАЕТ после двери 199(а) — та забирает её главный довод | @@ -105,7 +105,7 @@ (`backend/internal/pipeline/events.go:409`=`func unitOnceKey(bookID string, k unitWave) string`); направление фикса консилиум НЕ установил. **Из чего двери НЕ следует** (экономия паков): **255** (бывшая **202**, закрыта D39.200) — потребитель всего набора, своих команд не -несёт · **208** — фикс транспорта внутри движка · **210** — развилка данных/промпта, вайр-батч 182 · +несёт · ~~**208**~~ · ~~**210**~~ (⚠ **испр. 10.09: обеих строк в живом трекере НЕТ, закрыты; довод «дверь из них не следует» остаётся, номера сняты**) · вайр-батч 182 · **164** — не команда, а починка: смета уже едет в `status --json`. ## Чего НЕ строить