From 371f15ff2bf834b4c5f899ab0eeab93dbdb6cd24 Mon Sep 17 00:00:00 2001 From: heaven Date: Thu, 27 Aug 2026 14:50:35 +0300 Subject: [PATCH] Act on the senior review of the ratification: correct four facts on the bank ontology page, split its defect class in two, and ratify the disputed-row practice into the register rule itself --- docs/PROGRESS.md | 18 +++++----- docs/architecture/05-decisions-log.md | 2 ++ docs/architecture/09-target-architecture.md | 4 +-- docs/architecture/17-seam-inbound-law.md | 2 +- docs/architecture/18-bank-ontology.md | 39 ++++++++++++++++----- docs/glossary.md | 18 ++++++++++ platform/docs/DEFECT_REGISTER.md | 10 ++++-- 7 files changed, 71 insertions(+), 22 deletions(-) diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 92cff263..add099e8 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -32,7 +32,7 @@ | 12 | **`postcheck_gate` в hard-gate** — требует пере-замера recall (recall чекеров измерен и низок); ⚠ «fn=0» демоции #10 класс-вакуумен на labels (ноль одно-Han-позитивов, D39.78) — пере-замер обязан добрать одно-Han-позитивы, на «fn=0» не опираться; одно-Han-класс при gate-on вне гейта (Unverified+Demoted); тем же решением строится HARD-value-детектор 成/万 (Q3 §6 — честный hard-gate требует value-чека; либо раньше при ненулевом потоке value-дефектов добора, D39.79; **$0-прототип получен эксп-21: `battery.check_numbers`, симметричный разбор, синтетика 16/16 — D39.117**) · recall существующих гейтов печатал арм C эксп-21 на контаминированном срезе (D39.108/D39.117) | владелец | когда-нибудь (после фазы 2) | отдельное решение | D39.34(2), D24.4+D28.1, D39.39, D39.78, D39.79 | | 13 | **Включение платной петли ремонта + per-class исходы ремонта** (схемное решение принимается вместе) — вернуться при расширении классов детекторов и ненулевом остатке; ⚠ при включении: репэйр-гард №5 после #10 слеп на одно-Han-дифференциал (демоция в обоих прогонах ⇒ 0>0 — репэйр, стёрший верно отрендеренный одно-Han-термин, не ветируется; D39.78-ревью) | владелец | когда-нибудь | отдельное решение | D39.38, D39.39(а), D39.78, CURRENT-STATE | | 13а | Веса рубрики K1–K12 не выбраны (Q1: «по данным фазы B»; фаза B прошла на BWS без весов — выбор нужен к агрегатной приёмке качества книги) | владелец | когда-нибудь (к пилоту) | Ф2.5 / отдельное решение | D39.44 Q1, D39.46 | -| 13б | **`speech-cue.txt` zh НЕ шипить до замера голос-флаггера; замера НЕТ (D39.86): флаггер живёт на ФИНАЛЬНОЙ волне, редакторская волна не гонялась ⇒ оси не считались НИ РАЗУ.** Гейт: первый прогон добора идеала С редакторской волной (строка 24) → решение владельца о шипе. ⚠ **ФАКТ 22.08, делающий строку жёстче: флаггер выключен НЕ только тем, что волна не гонялась — `gates.voice` не встречается НИ В ОДНОМ шиппинг-конфиге** (`grep -c voice backend/configs/pipeline-c1.yaml` = 0, то же в арм-конфигах), при том что сам гейт в схеме есть (`internal/config/pipeline.go` `VoiceGate`, пак-19/D39.55). То есть даже прогон С редактурой осей голоса НЕ посчитает, пока гейт не включён конфигом — отдельное касание, и его надо сделать ПЕРЕД покупкой прогона, иначе замер куплен впустую. ⚠ `rules=` в отчёте = «гейт СКОНФИГУРИРОВАН», не «отработал» — до фикса 105 читать по знаменателям `replies=`. Археология дословно — `architecture/13-tech-debt-anchors.md` §Б-13б. ⚠ **ПОПРАВКА 23.08 (№19):** блокер — ПУСТОЙ РЕЕСТР профилей, а не выключенный гейт: заполнить `voices:`/`addresses:` может только подпись владельца (`internal/seed/seed.go:20-21`=`Voices and Addresses are the two D21 record types` — «no miner emits them»), а при пустом реестре `RunVoiceChecks` выходит на `!reg.HasData()` ДО ВСЕХ осей, поэтому нулями отдаются и оси, и знаменатели — «ноль флагов» неотличим от «оси не считались». ⚠ Отдельно: `gates.voice` отсутствует в `backend/configs/`, НО в конфигах реальных прогонов (вне git) он ВКЛЮЧЁН — `~/books/gu-zhenren/coldrun-a/pipeline.yaml:68` и `coldrun-b:71`, `enabled: true`; дыра не «оператор забыл включить», а «шаблоны репозитория гейт не несут» | владелец | отложено-до-замера | первый прогон добора идеала С РЕДАКТОРСКОЙ ВОЛНОЙ (оси голоса, строка 24) → отдельное решение | D39.56, PACK19_BUILD §7.2, чат 31.07, D39.86 | +| 13б | **`speech-cue.txt` zh НЕ шипить до замера голос-флаггера; замера НЕТ (D39.86): флаггер живёт на ФИНАЛЬНОЙ волне, редакторская волна не гонялась ⇒ оси не считались НИ РАЗУ.** Гейт: первый прогон добора идеала С редакторской волной (строка 24) → решение владельца о шипе. ⚠ **ФАКТ 22.08, делающий строку жёстче: флаггер выключен НЕ только тем, что волна не гонялась — `gates.voice` не встречается НИ В ОДНОМ шиппинг-конфиге** (`grep -c voice backend/configs/pipeline-c1.yaml` = 0, то же в арм-конфигах), при том что сам гейт в схеме есть (`internal/config/pipeline.go` `VoiceGate`, пак-19/D39.55). То есть даже прогон С редактурой осей голоса НЕ посчитает, пока гейт не включён конфигом — отдельное касание, и его надо сделать ПЕРЕД покупкой прогона, иначе замер куплен впустую. ⚠ `rules=` в отчёте = «гейт СКОНФИГУРИРОВАН», не «отработал» — до фикса 105 читать по знаменателям `replies=`. Археология дословно — `architecture/13-tech-debt-anchors.md` §Б-13б. ⚠ **ПОПРАВКА 23.08 (№19):** блокер — ПУСТОЙ РЕЕСТР профилей, а не выключенный гейт: заполнить `voices:`/`addresses:` может только подпись владельца (`internal/seed/seed.go:23`=`Voices and Addresses are the two D21 record types` — «no miner emits them»), а при пустом реестре `RunVoiceChecks` выходит на `!reg.HasData()` ДО ВСЕХ осей, поэтому нулями отдаются и оси, и знаменатели — «ноль флагов» неотличим от «оси не считались». ⚠ Отдельно: `gates.voice` отсутствует в `backend/configs/`, НО в конфигах реальных прогонов (вне git) он ВКЛЮЧЁН — `~/books/gu-zhenren/coldrun-a/pipeline.yaml:68` и `coldrun-b:71`, `enabled: true`; дыра не «оператор забыл включить», а «шаблоны репозитория гейт не несут» | владелец | отложено-до-замера | первый прогон добора идеала С РЕДАКТОРСКОЙ ВОЛНОЙ (оси голоса, строка 24) → отдельное решение | D39.56, PACK19_BUILD §7.2, чат 31.07, D39.86 | | 14 | **Четыре data-инженерных флага фазы-2 общности, каждый с рекомендованным дизайном** (§5 отчёта): ё-фолд → target-данные (`char_fold`; смена сигнатуры core-примитива через вердикт-несущий банк, сдвиг `memory_version`) · segmentation-фолбэк громким (ломает контракт corridor-фолбэка `pipeline.go:839-848` + голден-фикстура на нём; лечение — объявить блок в голден-yaml) · `Fertility` per-script map (калибровка ждёт боевого токенизатора) · `source-encoding.txt` + Go-реестр декодеров | владелец | когда-нибудь (следующий пак общности) | отдельное решение | D39.64, GENERALITY_PHASE2 §5.1–5.4 | | 86 | **Мелкие открытые решения одним списком** (свип D39.66): echo-семантика дропнутого c-lite-члена (D39.18 — оставить pre-c-lite?) · magnitude-кап тяжёлой CJK-утечки (D38.3 п.4в; сейчас strip+flag) | владелец | когда-нибудь | отдельные решения | D39.66, D39.69(feed_cap закрыт §1) | | **— ХВОСТЫ ХОЛОДНОГО ПРОГОНА (исполнен достаточно, D39.58; Р7 владельца) —** | | | | | | @@ -138,25 +138,25 @@ | 196 | **Остаточный класс строки 176: неперехватываемые `fatal error` Go** (конкурентная запись в map, OOM, стек-оверфлоу) по-прежнему дают exit 2, то есть читаются платформой как «завершено с флагами» — `recover` их не ловит по устройству языка (подтверждено линзой шва исполнением). Строка 176 закрыта для ПАНИК, не для fatal. Лечение — вне exit-контракта: сторож процесса у платформы либо маркер живости в артефакте прогона | бэкенд/платформа | когда-нибудь | отдельное решение | приёмка №18, линза шва | | 197 | **Фикс-лист приёмки пака честности ФЧ-1…ФЧ-8** (D39.149; тело — [archive/PROGRESS-2026-08-17.md](archive/PROGRESS-2026-08-17.md), запись приёмки №18): три НЕПРИБИТЫЕ гарантии (снятие фильтра по книге в `CheckpointUsageForBook` не валит ни один тест — воспроизведено оркестратором · ключ ячейки без `chunk_idx` съедает деньги соседнего чанка · ветка «строка не пере-оценена» не ловится на продовом пути) · два докстринга `reprice.go` описывают НЕ то, что делает код · 187 исполнен наполовину (тот же снятый закон в докстринге теста и `backend/docs/D15.2-*.md:317`) · `HistoricalRows` не доезжает до `status`/`--json`, второй операторский текст (`--accept-rebill`) без basis · **ФЧ-5 РЕШЁН владельцем 17.08 (D39.150): прайсить по модели, которую стадия резолвит СЕЙЧАС, + округление вверх и оговорка в тексте согласия** · `Status()` жёстко падает при ошибке репрайсера против соседней политики деградации · паника поверх ceiling-стопа даёт `failed` вместо `paused` (PD-113) · три стейл-докстринга. Носителя-сессии НЕТ — по слову владельца 17.08 пока только записано | бэкенд | скоро (следующее касание денежного пути; ФЧ-5 обязателен до первого пере-прогона со сменой модели) | дофикс-промт ЛИБО попутно ближайшим паком | **D39.149**, **D39.150** | | 198 | **Апгрейд движка стирает замечания и счётчики книги БЕЗВОЗВРАТНО — композиция двух половин, каждая известна по отдельности** (приёмка P7, линза «вне карты», обе половины пере-прочитаны оркестратором). Платформа при смене `manifest_key` сносит ВСЕ `unit_resolutions` книги (`platform/internal/pgstore/readmodel.go:164-165`), рассчитывая, что поток их пере-наполнит; движок анонсирует юнит РОВНО ОДИН РАЗ за жизнь книги — ключ `unit::::` БЕЗ метки нарезки (`backend/internal/pipeline/events.go:399-400`), а леджер анонсов переживает прогоны (`backend/internal/store/outbox.go:96` «already announced… by one that ran before it») ⇒ совпавшие координаты не переанонсируются НИКОГДА. Обостряется порядком: долг на материализацию ставится только на ГРАНИЦАХ работы, поэтому первая зачистка после апгрейда случается в КОНЦЕ первого пост-апгрейдного прогона и сносит замечания ТОГО ЖЕ прогона, за который заплачено. Следствия на проводе: «0 из N» на переведённой книге навсегда · шкала снова предлагает купить переведённое · замечания читателя исчезают. **Лечится с обеих сторон:** движковая половина — метка нарезки в ключе анонса (решение зоны движка), платформенная — не сносить вслепую либо восстанавливать из экспорта. ⚠ Гейт холодного прогона: смысл упражнения — гонять книгу против МЕНЯЮЩЕГОСЯ движка, то есть первый же апгрейд обнулит библиотеку | бэкенд + платформа | **скоро (до первого реального пользователя И до холодного прогона с апгрейдами)** | связка: решение по ключу анонса в движке → правка платформы | приёмка P7 (D39.153) | -| 199 | **Канал доставки правок банка в движок НЕ построен — цепь «поправить/добавить термин → перегенерация» рвётся на шве платформа→движок** (расследование 20.08 по слову владельца; грепы пере-проверены оркестратором). ⚠ **ВЕРХНЕГО КРАЯ У ЦЕПИ БОЛЬШЕ НЕТ (испр. 22.08 при лендинге P8-FIX, D39.154).** Прежняя редакция строки опиралась на ручку `submitBankDecisions` с полем `dst`, куда пользователь мог впечатать исправленный перевод, — **она СНЯТА вместе со всей пер-термной моделью подписи** (`PD-370`, ратификация D39.144). То есть сегодня разорван не один конец, а оба: снизу движок умеет точечную ре-редактуру по вхождениям за $0 на незатронутых юнитах со сметой и порогом согласия (`stagerun.go:88-107`, `repin.go`/`rebill.go`), а сверху ручки «поправить/добавить термин» нет НИ В ЗОНЕ, НИ В КАНОНЕ — на 17 путей канона запись в банк была ровно одна, и она ушла. Это ужесточает строку, а не смягчает: раньше не хватало трубы посередине, теперь не хватает ещё и входа. ⚠ **И третья половина, вскрытая сквозной трассировкой 22.08 — «снизу движок готов» верно ЛИШЬ НАПОЛОВИНУ, сверено кодом:** (а) **правка существующего термина** доедет до РЕДАКТОРА, но не до ЧЕРНОВИКА — `loadMinedDelta` принудительно ставит `Source:"mined"` (`backend/internal/pipeline/mining.go`), а базовый банк черновой волны mined-строки ИСКЛЮЧАЕТ (`seeding.go:143-153`, `if row.Source == "mined" { continue }`). Чтобы правка формировала сам черновик, она должна ехать через `glossary_seed`, а это сдвиг БАЗОВОГО снапшота ⇒ пере-оплата всей черновой волны. То есть «дёшево» работает только для редакторского слоя; (б) **вход в пере-генерацию после прочтения закрыт двумя флагами, которых платформа не передаёт**: `--resnapshot` и `--accept-rebill` сверх порога. `platform/internal/runner/engine.go` `TranslateArgs` собирает РОВНО `translate --config <...> [--verify-bank] [ceiling]` и больше ничего; (в) команды, превращающей карту подписи в подписанный `mined_delta`, у движка НЕТ — в `tmctl` девять подкоманд, среди них ни `sign`, ни `promote`, а банк-экспорт объявлен «проекция, никогда не источник». Писателя файла надо НАЗВАТЬ, и это ровно развилка (а) выше. ⚠⚠ **И ТРИ МИНЫ В САМОМ ФОРМАТЕ ФАЙЛА, которые обязана знать сессия, которая его напишет (сверено кодом 22.08):** **(1) копи-паст карты подписи НЕ РАБОТАЕТ.** Карта пишется вся со `Status: "auto"` (`internal/miner/miner_emit.go`, док прямо: «Every term stays status:auto — that is the load-bearing line», и это запинено). А `unsignedEngineSurfaces` выбрасывает `Source=="mined" && Status!="approved"` (`mining.go:710-719`) ⇒ механически скопированные строки отфильтруются как неподписанные и стоп НЕ снимется — бесконечная петля. Писать надо ЯВНЫЙ `status: approved`. ⚠ Тонкость в другую сторону: ОПУЩЕННЫЙ статус в сид-загрузчике дефолтится в `approved` (`membank/memseed.go:71-74`=`status = "approved"`), то есть файл БЕЗ поля статуса работает, а файл со скопированным `auto` — нет. **(2) терм БЕЗ `dst` промотить нельзя вообще:** approved/draft с пустым `dst` — ПАДЕНИЕ загрузки сида, то есть срыв всего прогона (`memseed.go:83-86`). А `dst` есть не у всех — часть термов WHICH-only (`mining.go` считает `withDst` именно поэтому). Для них единственный очищающий глагол — ОТКАЗ (`mined_rejects`, берёт только `src`). **(3) хвост реверс-секции ломает «один ОК» на больших книгах:** `reverseSectionTerms` режет кап 200 ПОСЛЕ фильтров, и движок сам предупреждает — «the tail is NOT in this signature map and re-proposes on the next run». При >200 banknote-only поверхностей подпись всей карты стоп НЕ снимет: приедет свежая двухсотка, и актов подписи понадобится ceil(N/200). Носителя между ними нет: колонки `action`/`dst` не читает НИ ОДИН SELECT (единственная выборка `readmodel.go:336` проверяет лишь факт наличия строки) · `mined_delta|mined_rejects` по Go платформы = **0** вхождений · воркер, который должен переносить решения в эти файлы перед resume, описан в комментарии её же миграции (`00002_readmodel.sql:172-174`) и НЕ построен. **Делится надвое:** (а) **труба** — платформа пишет решения в два файла, которые движок читает. ⚠ **«Контракт файлов существует, проектировать нечего» — ОПРОВЕРГНУТО КОДОМ 21.08** (опровергатель промта, пере-проверено оркестратором построчно): движок читает `mined_delta`/`mined_rejects`, ТОЛЬКО если их путь объявлен в `book.yaml` (`backend/internal/pipeline/mining.go:855-858`=`if r.Book.MinedDelta == ""`), а объявленный ключ с НЕсуществующим файлом валит загрузку конфига целиком (`backend/internal/config/book.go:199-207`=`is not readable`). Ключей нет ни в шаблоне оператора, ни в дев-стенде (`backend/example/book.yaml` — ноль вхождений), и добавить их платформа не может молча: она пишет `book.yaml` ОДИН раз под `O_EXCL` и «никогда не перезаписывает» (`platform/internal/books/render.go:106-109`=`writes the file once and never again`), а схему движка знать не должна — это ратифицированная форма Б (D39.110 §2б). **Значит развилка есть и она упирается в ратифицированную границу владения `book.yaml`:** объявляет ли ключи платформа при рождении книги (и создаёт ли два пустых файла сразу, иначе движок не стартует) · несёт ли их шаблон оператора · двигается ли граница владения. Решать её — ратификацией, не паком. Закрывает и честность `decline` (PD-277 — его текст сегодня относит работу к отложенной 192, пере-диспозиция нужна тем же решением); (б) **отложенное владельцем** — правки ПОСЛЕ прочтения уже переведённой книги: там правка банка двигает снапшот, движок падает громко без явного согласия на пере-оплату, а `resnapshot|accept-rebill` по Go платформы = 0 ⇒ прогон не перегенерирует, а УПАДЁТ (сегодня не кусает только потому, что до движка не доезжает ничего). Половина (б) — территория строки **192**. ⚠ **ПОПРАВКИ 23.08 (№19, консилиум шва):** (1) формулировка «ни на одном конце» СНЯТА — принимающий конец ДВИЖКА построен (`pipeline/mining.go:830,856`=`if r.Book.MinedRejects == ""`), не построен ПЛАТФОРМЕННЫЙ путь целиком; (2) тело развилки с четырьмя вариантами и ценой лежит ТОЛЬКО в `platform/docs/archive/platform-PROGRESS-P8.md:176-205`=`Двигается ратифицированная граница владения`; (3) есть СЕДЬМОЙ вариант, которым развилка не решается, а РАСТВОРЯЕТСЯ: движок дефолтит `mined_delta`/`mined_rejects` по конвенции каталога (как уже делает с `project_db` — `config/book.go:163-167`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")` — и как уже читает `.auto-bank.yaml`, `mining.go:644`), писателем становится движковый $0-глагол ⇒ ключей никто не объявляет, файлов заранее никто не создаёт; (4) зона дополнена ВЛАДЕЛЬЦЕМ: канон зовёт половину (а) блокером на нём; (5) ⚠ **ГРАНИЦА, которую труба НЕ снимает и которая до 23.08 жила только внутри чужого списка ошибок:** правка, доставленная через `mined_delta`, доезжает до РЕДАКТОРСКОЙ волны, но НЕ формирует черновик — черновая волна выбирает по банку БЕЗ mined-строк (`backend/internal/pipeline/seeding.go:140`=`The DRAFT wave selects over a BASE-scoped bank`). Для читателя это не дефект: отгружается выход редактора, а юниты с термином пере-редактируются, незатронутые пере-пиниваются за $0 (`backend/internal/pipeline/stagerun.go:88-107`=`POINTWISE RE-EDIT`). Настоящая цена тоньше и НЕ ИЗМЕРЕНА: редактор латает черновик, построенный вокруг старого термина (согласование, род, игра слов). Правка, формирующая сам черновик, — это `glossary_seed`, то есть сдвиг базового снапшота с пере-оплатой; территория строки 192, глубину решает владелец ПОСЛЕ фазы Д | владелец + платформа + бэкенд | (а) РАСТВОРЕНА D39.156 · (б) гейчена 192 | (а) движковый пак (дефолты путей + глагол приёма правок) и платформенный (воркер решений); ⚠ прежняя запись звала «дофикс-пак платформы» — такого носителя в репозитории нет ни одного, имя мёртвое, испр. 23.08 · (б) с циклом чтения | расследование 20.08, D39.153 | +| 199 | **Канал доставки правок банка в движок НЕ построен — цепь «поправить/добавить термин → перегенерация» рвётся на шве платформа→движок** (расследование 20.08 по слову владельца; грепы пере-проверены оркестратором). ⚠ **ВЕРХНЕГО КРАЯ У ЦЕПИ БОЛЬШЕ НЕТ (испр. 22.08 при лендинге P8-FIX, D39.154).** Прежняя редакция строки опиралась на ручку `submitBankDecisions` с полем `dst`, куда пользователь мог впечатать исправленный перевод, — **она СНЯТА вместе со всей пер-термной моделью подписи** (`PD-370`, ратификация D39.144). То есть сегодня разорван не один конец, а оба: снизу движок умеет точечную ре-редактуру по вхождениям за $0 на незатронутых юнитах со сметой и порогом согласия (`stagerun.go:88-107`, `repin.go`/`rebill.go`), а сверху ручки «поправить/добавить термин» нет НИ В ЗОНЕ, НИ В КАНОНЕ — на 17 путей канона запись в банк была ровно одна, и она ушла. Это ужесточает строку, а не смягчает: раньше не хватало трубы посередине, теперь не хватает ещё и входа. ⚠ **И третья половина, вскрытая сквозной трассировкой 22.08 — «снизу движок готов» верно ЛИШЬ НАПОЛОВИНУ, сверено кодом:** (а) **правка существующего термина** доедет до РЕДАКТОРА, но не до ЧЕРНОВИКА — `loadMinedDelta` принудительно ставит `Source:"mined"` (`backend/internal/pipeline/mining.go`), а базовый банк черновой волны mined-строки ИСКЛЮЧАЕТ (`seeding.go:143-153`, `if row.Source == "mined" { continue }`). Чтобы правка формировала сам черновик, она должна ехать через `glossary_seed`, а это сдвиг БАЗОВОГО снапшота ⇒ пере-оплата всей черновой волны. То есть «дёшево» работает только для редакторского слоя; (б) **вход в пере-генерацию после прочтения закрыт двумя флагами, которых платформа не передаёт**: `--resnapshot` и `--accept-rebill` сверх порога. `platform/internal/runner/engine.go` `TranslateArgs` собирает РОВНО `translate --config <...> [--verify-bank] [ceiling]` и больше ничего; (в) команды, превращающей карту подписи в подписанный `mined_delta`, у движка НЕТ — в `tmctl` девять подкоманд, среди них ни `sign`, ни `promote`, а банк-экспорт объявлен «проекция, никогда не источник». Писателя файла надо НАЗВАТЬ, и это ровно развилка (а) выше. ⚠⚠ **И ТРИ МИНЫ В САМОМ ФОРМАТЕ ФАЙЛА, которые обязана знать сессия, которая его напишет (сверено кодом 22.08):** **(1) копи-паст карты подписи НЕ РАБОТАЕТ.** Карта пишется вся со `Status: "auto"` (`internal/miner/miner_emit.go`, док прямо: «Every term stays status:auto — that is the load-bearing line», и это запинено). А `unsignedEngineSurfaces` выбрасывает `Source=="mined" && Status!="approved"` (`mining.go:710-719`) ⇒ механически скопированные строки отфильтруются как неподписанные и стоп НЕ снимется — бесконечная петля. Писать надо ЯВНЫЙ `status: approved`. ⚠ Тонкость в другую сторону: ОПУЩЕННЫЙ статус в сид-загрузчике дефолтится в `approved` (`membank/memseed.go:96`=`status = "approved"`), то есть файл БЕЗ поля статуса работает, а файл со скопированным `auto` — нет. **(2) терм БЕЗ `dst` промотить нельзя вообще:** approved/draft с пустым `dst` — ПАДЕНИЕ загрузки сида, то есть срыв всего прогона (`memseed.go:83-86`). А `dst` есть не у всех — часть термов WHICH-only (`mining.go` считает `withDst` именно поэтому). Для них единственный очищающий глагол — ОТКАЗ (`mined_rejects`, берёт только `src`). **(3) хвост реверс-секции ломает «один ОК» на больших книгах:** `reverseSectionTerms` режет кап 200 ПОСЛЕ фильтров, и движок сам предупреждает — «the tail is NOT in this signature map and re-proposes on the next run». При >200 banknote-only поверхностей подпись всей карты стоп НЕ снимет: приедет свежая двухсотка, и актов подписи понадобится ceil(N/200). Носителя между ними нет: колонки `action`/`dst` не читает НИ ОДИН SELECT (единственная выборка `readmodel.go:336` проверяет лишь факт наличия строки) · `mined_delta|mined_rejects` по Go платформы = **0** вхождений · воркер, который должен переносить решения в эти файлы перед resume, описан в комментарии её же миграции (`00002_readmodel.sql:172-174`) и НЕ построен. **Делится надвое:** (а) **труба** — платформа пишет решения в два файла, которые движок читает. ⚠ **«Контракт файлов существует, проектировать нечего» — ОПРОВЕРГНУТО КОДОМ 21.08** (опровергатель промта, пере-проверено оркестратором построчно): движок читает `mined_delta`/`mined_rejects`, ТОЛЬКО если их путь объявлен в `book.yaml` (`backend/internal/pipeline/mining.go:822`=`if r.Book.MinedDelta == ""`), а объявленный ключ с НЕсуществующим файлом валит загрузку конфига целиком (`backend/internal/config/book.go:254`=`is not readable`). Ключей нет ни в шаблоне оператора, ни в дев-стенде (`backend/example/book.yaml` — ноль вхождений), и добавить их платформа не может молча: она пишет `book.yaml` ОДИН раз под `O_EXCL` и «никогда не перезаписывает» (`platform/internal/books/render.go:106-109`=`writes the file once and never again`), а схему движка знать не должна — это ратифицированная форма Б (D39.110 §2б). **Значит развилка есть и она упирается в ратифицированную границу владения `book.yaml`:** объявляет ли ключи платформа при рождении книги (и создаёт ли два пустых файла сразу, иначе движок не стартует) · несёт ли их шаблон оператора · двигается ли граница владения. Решать её — ратификацией, не паком. Закрывает и честность `decline` (PD-277 — его текст сегодня относит работу к отложенной 192, пере-диспозиция нужна тем же решением); (б) **отложенное владельцем** — правки ПОСЛЕ прочтения уже переведённой книги: там правка банка двигает снапшот, движок падает громко без явного согласия на пере-оплату, а `resnapshot|accept-rebill` по Go платформы = 0 ⇒ прогон не перегенерирует, а УПАДЁТ (сегодня не кусает только потому, что до движка не доезжает ничего). Половина (б) — территория строки **192**. ⚠ **ПОПРАВКИ 23.08 (№19, консилиум шва):** (1) формулировка «ни на одном конце» СНЯТА — принимающий конец ДВИЖКА построен (`pipeline/mining.go:784`=`if r.Book.MinedRejects == ""`), не построен ПЛАТФОРМЕННЫЙ путь целиком; (2) тело развилки с четырьмя вариантами и ценой лежит ТОЛЬКО в `platform/docs/archive/platform-PROGRESS-P8.md:176-205`=`Двигается ратифицированная граница владения`; (3) есть СЕДЬМОЙ вариант, которым развилка не решается, а РАСТВОРЯЕТСЯ: движок дефолтит `mined_delta`/`mined_rejects` по конвенции каталога (как уже делает с `project_db` — `config/book.go:189`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")` — и как уже читает `.auto-bank.yaml`, `mining.go:644`), писателем становится движковый $0-глагол ⇒ ключей никто не объявляет, файлов заранее никто не создаёт; (4) зона дополнена ВЛАДЕЛЬЦЕМ: канон зовёт половину (а) блокером на нём; (5) ⚠ **ГРАНИЦА, которую труба НЕ снимает и которая до 23.08 жила только внутри чужого списка ошибок:** правка, доставленная через `mined_delta`, доезжает до РЕДАКТОРСКОЙ волны, но НЕ формирует черновик — черновая волна выбирает по банку БЕЗ mined-строк (`backend/internal/pipeline/seeding.go:140`=`The DRAFT wave selects over a BASE-scoped bank`). Для читателя это не дефект: отгружается выход редактора, а юниты с термином пере-редактируются, незатронутые пере-пиниваются за $0 (`backend/internal/pipeline/stagerun.go:88-107`=`POINTWISE RE-EDIT`). Настоящая цена тоньше и НЕ ИЗМЕРЕНА: редактор латает черновик, построенный вокруг старого термина (согласование, род, игра слов). Правка, формирующая сам черновик, — это `glossary_seed`, то есть сдвиг базового снапшота с пере-оплатой; территория строки 192, глубину решает владелец ПОСЛЕ фазы Д | владелец + платформа + бэкенд | (а) РАСТВОРЕНА D39.156 · (б) гейчена 192 | (а) движковый пак (дефолты путей + глагол приёма правок) и платформенный (воркер решений); ⚠ прежняя запись звала «дофикс-пак платформы» — такого носителя в репозитории нет ни одного, имя мёртвое, испр. 23.08 · (б) с циклом чтения | расследование 20.08, D39.153 | | 200 | **Сквозная полоса прогресса вместо пофазной — требование владельца 20.08, сформулировано и ждёт исполнения.** Сегодня прогресс считается ПО ОТРЕЗКАМ и при снятии стопа начинается с нуля (канон §Progress: «When a stop is cleared the counter starts again from zero») ⇒ пользователь видит 100% после черновика и обнуление — владелец назвал это дефектом подписанного, не вкусовщиной. Форма решения: ОДНА доля на всю работу прогона, считает СЕРВЕР (иначе клиент снова начнёт знать про фазы), плюс подпись «что делается сейчас» и неопределённый индикатор там, где доли в главах нет. ⚠ Веса «условно 10/40/40» НЕ нужны для переводческой части: движок объявляет знаменатель каждого прохода и ноль для прохода, которого на деплое нет (`backend/internal/pipeline/events.go:297-323`) ⇒ доля = сделано по всем проходам ÷ запланировано, без подобранных констант, и на деплое без редактора честно доходит до 100%. Догадка остаётся только у банка: поток прогресса несёт лишь два переводческих прохода, майнинг знаменателя не объявляет — **вопрос движку: может ли майнинг объявлять знаменатель** (он идёт по главам). Цена: правит канон §Progress + `Book.chapters_done`, то есть новый минор; пока фронт заморожен, минор бесплатен | контракт → платформа + бэкенд | скоро (следующий минор контракта) | контрактная сессия + платформенная половина; вопрос движку о знаменателе майнинга | слово владельца 20.08, D39.153 | | 201 | **Движковое «Глава N» доезжает читателю ВНУТРИ текста, обходя дисциплину `heading: null`** (линза шва P7, пере-прочитано оркестратором): `backend/internal/pipeline/export.go:230` приклеивает детерминированный порядковый к экспортному тексту первой юнит-главы (`ce.FinalText = chunk.ApplyHeading(...)`), колонка `Source` при этом остаётся heading-stripped. Платформа переносит обе как есть и честно отдаёт `heading: null` — то есть клиент нарисует СВОЙ порядковый на языке своего интерфейса над абзацем, который уже начинается с русского «Глава N», а исходная колонка соответствующего маркера не несёт: пара визуально рассинхронизирована на каждой первой главе. Конкретное следствие открытого К-2 контракта; родня движковой строки **160** (глава без заголовка) | бэкенд | когда-нибудь (с 160) | отдельное решение | приёмка P7 (D39.153) | | 202 | **Живой перевод книги НАСКВОЗЬ через API платформы не гонялся НИ РАЗУ** (obstacle сессии P7, подтверждён приёмкой): всё после «прогон стартовал и остановился» — подпись банка, halt на потолке, резюм после пополнения — покрыто батареей против живого Postgres, но с движком-заглушкой; настоящий манифест, сайдкар банка и причины флагов будут ПЕРВЫМИ данными, форму которых платформа не сочинила сама. Форма проверки (решена): только через API, как ходил бы клиент — интейк → старт → живой движок с ключами провайдера → поток событий → подпись и продолжение → чтение глав/юнитов/замечаний/банка и сверка с тем, что движок реально произвёл; качество перевода НЕ предмет (полигон). Цена ≈ $0.05 на десятиглавую книгу. **Решение владельца 20.08: ПОСЛЕ холодного прогона движка** — гонять одну неготовую систему против другой смысла нет | платформа | скоро (гейт: холодный прогон движка) | короткая сессия по промту оркестратора | слово владельца 20.08, D39.153 | -| 203 | **Хвосты контракта после синка 0.4.0 — ОТКРЫТА ЧЕТВЕРТЬ, остальное исполнено** (тела релеев — `platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md` §7; сюда переписаны НЕ тела, а статус — один носитель на факт). ⚠ Сверено грепом при лендинге, потому что первая редакция этой строки уже разошлась с деревом: **(б) ИСПОЛНЕН** — срезанная фраза компаньона §2.14 стоит на месте (`14-api-contract/README.md:510`=`различать причины отказа клиент не может по замыслу`) · **(д) ИСПОЛНЕН** — клетка приложения А заполнена `unspecified` (`README.md:1376` — ⚠ первая редакция писала `:1391`, это строка ДРУГОГО приложения; испр. 20.08) · **(г) наполовину** — `unspecified` ратифицирован в каноне (`openapi.yaml:1784-1787`), открыта только ГРАНИЦА ступеней замечаний (две ступени при девяти рангах движка) → это строка **148** · **(и)** закрыт полем `stop_requested` (D39.152) · (л)(м)(н)(о) закрыты контрактной сессией. **ОТКРЫТО РЕАЛЬНО:** (а) канон-половина про `/auth` — компаньон поверхность описывает (§2.14), а канон говорит только «Signing in is not part of this surface» (`openapi.yaml:35-36`) и НЕ предупреждает, что конверт вне `/v0` может прийти БЕЗ обязательного `code`; сгенерированный по канону клиент об это спотыкается · (з) ⚠ **СНЯТО КАК РАТИФИКАЦИЯ 22.08 — конфликта моделей НЕТ, я нёс причину и следствие наоборот.** Прежняя редакция писала: «D39.144 ратифицировал подпись = ОДИН акт, движок держит пер-термный гейт полноты, одно из двух должно уступить». Сквозная трассировка это опровергла КОДОМ: гейт проверяет полноту не ПОДПИСЕЙ, а ФАЙЛА решений, и ОДИН акт его снимает — `loadMinedDelta` штампует только `Source`, статус не трогает (`mining.go:855-867`) → опущенный статус дефолтится в `approved` (`membank/memseed.go:71-74`=`status = "approved"`) → `unsignedEngineSurfaces` выбрасывает лишь `Source=="mined" && Status!="approved"` (`mining.go:710-719`) ⇒ промоутнутые строки остаются сид-поверхностями, дельта пустеет, стоп снимается. Шапка файла говорит это прямым текстом. **Один файл со всем банком = один ОК.** Обход в платформе (`--verify-bank` не передаётся на resume) — костыль под ОТСУТСТВУЮЩУЮ ДОСТАВКУ, а не под расхождение моделей: как только доставка появится, флаг можно просто оставить включённым. Настоящий и единственный блокер темы — владение `book.yaml`, строка **199(а)**. Ратификации здесь НЕ нужно; остаётся снять обход одной боевой строкой (`platform/internal/runs/spawn.go`) и семью строками ставшего ложным обоснования — работа платформенного пака, не решение владельца · (к) ключ `project_db` в `book.yaml`: договорить, кто им владеет — шаблон оператора его не содержит, движок делает необязательным. ⚠ **Следствие «на штатном деплое банк не читается» ОПРОВЕРГНУТО живым кодом** (проверено при лендинге): акт 5 внёс фолбэк `.db` и назвал прежний отказ багом (`platform/internal/runner/artifacts.go:82-90`=`The key is OPTIONAL to the engine`); первая редакция этой строки повторила клейм хендоффа зоны, не сверив с деревом — остаток чисто договорной · **(л) остаток закрытой строки 148 — Приложение А компаньона.** ⚠ Сама 148 СНЯТА 21.08 по слову владельца: она держала мир, которого больше нет — «карта потеряна инцидентом 08.08, реконструкция у оркестратора №15, решить, брать ли черновиком». Реконструкция брать нечего: прежняя карта вела «причина движка → ФРАЗА», то есть предполагала, что фразу рисует СЕРВЕР, а батч 0.3.0 эту политику ОТМЕНИЛ — `Note` несёт машинный `code`, фразу рисует клиент, `Note.message` с провода снят (`14-api-contract/README.md:1332`=`Карта «причина → КОД контракта`). Реально пуста другая клетка: соответствие причин движка кодам замечаний, и правила её заполнения в компаньоне уже написаны (фраза по ДОККОММЕНТУ `disposition.go`, а не по имени константы — инверсия уже стоила двух фраз; класс 2 схлопывается в ОДИН код). Исполнитель — контрактная сессия, не оркестратор ⚠ **ПЕРЕ-ДИСПОЗИЦИЯ 27.08 (D39.158 п.7):** хвост (з) закрыт — «ратификации не нужно» отвечало на вопрос про один файл решений, не про флажок; движковый гейт полноты снят, не обойдён.| контракт/доки | скоро (следующее касание контракта, вместе с 200) | контрактная сессия | приёмка P7 (D39.153) | +| 203 | **Хвосты контракта после синка 0.4.0 — ОТКРЫТА ЧЕТВЕРТЬ, остальное исполнено** (тела релеев — `platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md` §7; сюда переписаны НЕ тела, а статус — один носитель на факт). ⚠ Сверено грепом при лендинге, потому что первая редакция этой строки уже разошлась с деревом: **(б) ИСПОЛНЕН** — срезанная фраза компаньона §2.14 стоит на месте (`14-api-contract/README.md:510`=`различать причины отказа клиент не может по замыслу`) · **(д) ИСПОЛНЕН** — клетка приложения А заполнена `unspecified` (`README.md:1376` — ⚠ первая редакция писала `:1391`, это строка ДРУГОГО приложения; испр. 20.08) · **(г) наполовину** — `unspecified` ратифицирован в каноне (`openapi.yaml:1784-1787`), открыта только ГРАНИЦА ступеней замечаний (две ступени при девяти рангах движка) → это строка **148** · **(и)** закрыт полем `stop_requested` (D39.152) · (л)(м)(н)(о) закрыты контрактной сессией. **ОТКРЫТО РЕАЛЬНО:** (а) канон-половина про `/auth` — компаньон поверхность описывает (§2.14), а канон говорит только «Signing in is not part of this surface» (`openapi.yaml:35-36`) и НЕ предупреждает, что конверт вне `/v0` может прийти БЕЗ обязательного `code`; сгенерированный по канону клиент об это спотыкается · (з) ⚠ **СНЯТО КАК РАТИФИКАЦИЯ 22.08 — конфликта моделей НЕТ, я нёс причину и следствие наоборот.** Прежняя редакция писала: «D39.144 ратифицировал подпись = ОДИН акт, движок держит пер-термный гейт полноты, одно из двух должно уступить». Сквозная трассировка это опровергла КОДОМ: гейт проверяет полноту не ПОДПИСЕЙ, а ФАЙЛА решений, и ОДИН акт его снимает — `loadMinedDelta` штампует только `Source`, статус не трогает (`mining.go:855-867`) → опущенный статус дефолтится в `approved` (`membank/memseed.go:96`=`status = "approved"`) → `unsignedEngineSurfaces` выбрасывает лишь `Source=="mined" && Status!="approved"` (`mining.go:710-719`) ⇒ промоутнутые строки остаются сид-поверхностями, дельта пустеет, стоп снимается. Шапка файла говорит это прямым текстом. **Один файл со всем банком = один ОК.** Обход в платформе (`--verify-bank` не передаётся на resume) — костыль под ОТСУТСТВУЮЩУЮ ДОСТАВКУ, а не под расхождение моделей: как только доставка появится, флаг можно просто оставить включённым. Настоящий и единственный блокер темы — владение `book.yaml`, строка **199(а)**. Ратификации здесь НЕ нужно; остаётся снять обход одной боевой строкой (`platform/internal/runs/spawn.go`) и семью строками ставшего ложным обоснования — работа платформенного пака, не решение владельца · (к) ключ `project_db` в `book.yaml`: договорить, кто им владеет — шаблон оператора его не содержит, движок делает необязательным. ⚠ **Следствие «на штатном деплое банк не читается» ОПРОВЕРГНУТО живым кодом** (проверено при лендинге): акт 5 внёс фолбэк `.db` и назвал прежний отказ багом (`platform/internal/runner/artifacts.go:82-90`=`The key is OPTIONAL to the engine`); первая редакция этой строки повторила клейм хендоффа зоны, не сверив с деревом — остаток чисто договорной · **(л) остаток закрытой строки 148 — Приложение А компаньона.** ⚠ Сама 148 СНЯТА 21.08 по слову владельца: она держала мир, которого больше нет — «карта потеряна инцидентом 08.08, реконструкция у оркестратора №15, решить, брать ли черновиком». Реконструкция брать нечего: прежняя карта вела «причина движка → ФРАЗА», то есть предполагала, что фразу рисует СЕРВЕР, а батч 0.3.0 эту политику ОТМЕНИЛ — `Note` несёт машинный `code`, фразу рисует клиент, `Note.message` с провода снят (`14-api-contract/README.md:1332`=`Карта «причина → КОД контракта`). Реально пуста другая клетка: соответствие причин движка кодам замечаний, и правила её заполнения в компаньоне уже написаны (фраза по ДОККОММЕНТУ `disposition.go`, а не по имени константы — инверсия уже стоила двух фраз; класс 2 схлопывается в ОДИН код). Исполнитель — контрактная сессия, не оркестратор ⚠ **ПЕРЕ-ДИСПОЗИЦИЯ 27.08 (D39.158 п.7):** хвост (з) закрыт — «ратификации не нужно» отвечало на вопрос про один файл решений, не про флажок; движковый гейт полноты снят, не обойдён.| контракт/доки | скоро (следующее касание контракта, вместе с 200) | контрактная сессия | приёмка P7 (D39.153) | | 204 | **Движок не публикует причины флагов ДАННЫМИ — карта причин у платформы рукописная и расходится молча** (релей §7(в) хендоффа P7; зона платформы в чужой бэклог не пишет и просила строку сюда — `platform/docs/archive/platform-PROGRESS-P7.md:206`). Сегодня `platform/internal/ingest/notes.go` держит рукописную копию закрытого словаря ЧУЖОЙ зоны: 15 причин движка → коды замечаний контракта, сверено побайтно на 20.08 и верно СЕЙЧАС. Импортировать движок платформе нельзя (D39.85 — разные модули), поэтому расхождение появится молча в тот день, когда движок добавит причину: платформа отдаст `unspecified` и напишет ERROR в лог, то есть деградация честная, но карта протухнет незаметно. Лечение: движок публикует свой словарь причин артефактом-данными (тем же классом, что манифест и сайдкары банка), платформа читает его вместо копии. ⚠ Носителем был ТОЛЬКО регистр платформы (PD-246) — то есть обязательство к зоне движка жило внутри зоны автора; поймано ревьюером полноты выгрузки при лендинге | бэкенд | когда-нибудь (со следующим касанием эмиссии флагов) | отдельное решение | приёмка P7 (D39.153), релей §7(в) | | 205 | **Гейт якорей: ИСПОЛНЕН 21.08 — сверка по содержимому, двусторонняя дельта, объявленная слепота.** Был: проверял только существование файла и переполнение номера, поэтому дал ЗЕЛЁНЫЙ на четыре якоря, сломанных архивацией — а оркестратор на эту зелень опёрся вместо ручной сверки. Стало (`docs/scripts/counts.py`): якорь может нести ОПТ-ИН токен ожидания `` `путь:12-14`=`подстрока` ``, и такой якорь сверяется по содержимому; область расширена с `docs/` на `platform/docs` и `frontend/docs` (98 доков); из хука убран быстрый выход по «коммит не трогает docs/» — он ампутировал ЦЕЛЕВУЮ сторону, а цели у нас в основном КОД, то есть главный двигатель протухания приезжал ровно тем коммитом, на котором хук молчал (проверено ТЕСТ-КОММИТОМ, не ручным прогоном — норма D39.148). ⚠ **Разбор с внешним ревьюером снял три моих ошибки дизайна:** (1) у дельты ДВЕ роли, и путать их нельзя — ТРЕБОВАТЬ токен можно только на затронутых строках (иначе долг блокирует всех), а ПРОВЕРЯТЬ уже написанные токены дельта ограничивать не должна вовсе: токен был истинным при записи, значит упавший — гниль по построению; corpus-wide проверка схлопывает «пропущенное окно» (коммит без хука, `--no-verify`, свежий клон, проигнорированный warn) из «навсегда» в «до следующего прогона»; (2) окно диапазона `80-82,243` считалось как `min..max` — 164 строки, и токен, случайно живущий внутри, красил зелёным; теперь ОБЪЕДИНЕНИЕ отрезков; (3) литеральная сверка токена была машиной ложных тревог — фраза, разорванная переносом строки в цели, непроверяема в принципе, а будущая пере-вёрстка абзаца кричала бы «цель уехала» на невиновного; теперь ПРОБЕЛЬНАЯ нормализация (детерминированная и симметричная), разметка остаётся литеральной. ⚠ Мой довод «неудобство и есть механизм» был неверен: мой собственный случай был FALSE POSITIVE самого гейта — токен был истинным, цель не двигалась. **ОБЪЯВЛЕННАЯ СЛЕПОТА (печатается гейтом каждый прогон):** якорь БЕЗ токена — только существование и длина · подмена цели файлом с тем же путём ловится только токеном · цели читаются из ДЕРЕВА, поэтому чужой незакоммиченный WIP может транзиентно и уронить токен, и спасти · токен доказывает ссылочную свежесть, НЕ истинность фразы дока о цели · в ручном прогоне «дельта» = всё незакоммиченное кем угодно, поэтому ТРЕБОВАНИЕ токена живёт только под `--from-index`. **ОСТАТОК:** миграция корпуса на токен-форму органическая — токен требуется на ЗАТРОНУТЫХ строках, поэтому счёт растёт сам с каждым касанием. Числа здесь НЕ держим (первая редакция держала «4 из ~580» и протухла за сутки): текущий счёт токенов, якорей и доков гейт печатает КАЖДЫЙ прогон — `python3 docs/scripts/counts.py --lint` | оркестратор/доки | сделано 21.08, миграция органическая | исполнено; остаток гасится касаниями | аудит работы №18 + разбор с внешним ревьюером, 21.08 | | 206 | **Канон 0.4.0 объявляет ручку, которой зона больше не отдаёт — ломающий минор 0.4.0 → 0.5.0** (контрактная половина `PD-370`, лендинг P8-FIX D39.154). `D39.144` отменил пер-термную подпись 16.08, зона сняла write-путь 22.08, а словарь канона его держит: путь `POST /books/{bookId}/bank/decisions`, глагол и три схемы — **~17 мест в `openapi.yaml` + ~8 в компаньоне**, поимённый разбор (6 мест прямой лжи, 3 самопротиворечия) — `platform/docs/archive/platform-PROGRESS-P8.md`, раздел «Насколько страдает контракт». ⚠ **ОБЪЁМ ШИРЕ ОДНОЙ РУЧКИ — сверено аудитом доков 22.08 против кода платформы, и это меняет форму работы:** (1) пер-термный механизм решений живёт ещё в ПЯТИ местах ЖИВЫХ, обслуживаемых схем — после удаления пути канон продолжит учить механизму, которого нет; (2) §2.9 компаньона («Подпись — набор решений») стоит на отменённой модели ЦЕЛИКОМ и просит поле под таблицу, в которую больше ничего не пишется; (3) таблица §3 компаньона — единственное место, где ведётся статус построенного, — объявляет НЕ ПОСТРОЕННЫМИ шесть поверхностей, которые P7 построил, а P8 сохранил, и держит снятую ручку строкой «НЕ ПОСТРОЕНО, вход P7», то есть как отложенную работу; (4) провенанс-якоря компаньона на `httpapi/v0.go` сдвинулись примерно на +200 строк и указывают в посторонний код. **И дыра, которую спил вскрыл:** `approve`+`dst` был ЕДИНСТВЕННЫМ живым носителем того, что D39.144 оставила пер-термным — «поправить термин», — а «добавить свой термин» модель называет, и в контракте его нет вовсе. То есть у собственной фразы канона «correct what they care to, then release it with ONE resume» ручки не осталось. До правки деплой обслуживает на одну ручку меньше, чем объявляет — цена, названная заранее, а не найденная приёмкой. ⚠ **БАТЧ РАЗДЕЛЁН 22.08 по итогам сквозной трассировки — и довод, которым я его склеивал, снят.** Прежняя редакция строки везла 206 ОДНИМ минором со строкой 200 и хвостами 203 «ради одной перегенерации фронта вместо трёх» (D39.152 п.7). Довод сегодня почти ничего не весит: зеркало фронта ратифицированно стоит на 0.2.3 и при разморозке платит переход ОДНИМ куском в любом случае. А блокеры у этих работ РАЗНЫЕ, и склейка подчиняла незаблокированное заблокированному: **206 не заблокирована ничем**; ручка правки термина заблокирована ратификацией 199(а); **строка 200 заблокирована ответом ДВИЖКА** на вопрос «может ли майнинг объявлять знаменатель»; из хвостов 203 в минор едет одна текстовая правка (а), а (з) и (к) — ратификации, а не правки. **Порядок:** 0.5.0 СЕЙЧАС и только гигиена — она снимает живую ложь (деплой обслуживает на одну ручку меньше, чем объявляет) и не ждёт ничьих решений; новая ручка, сквозной прогресс и хвосты — 0.6.0 после ратификаций. Объём гигиены назван: ~14 мест в `openapi.yaml` (4 блока / ~105 строк на снос плюс проза в 10 живых схемах) + ~7 живых мест в компаньоне + таблица §3 (уже пере-снята 22.08) + одна строка в платформе (`ContractVersion`, иначе краснеет гейт `gates/contract_test.go`). ⚠ **ДОБОР СОСТАВА аудитом 22–23.08 — с ЧЕСТНОЙ атрибуцией, потому что это находки оркестратора №19, а не ратифицированные факты.** **Пере-проверено мной лично:** (а) строки :672/:673 таблицы §3 компаньона объявляют отсутствующей платформенную проекцию банка и замечаний, а она ПОСТРОЕНА — маршруты `platform/internal/httpapi/v0.go:79-80`=`GET", path: "/books/{bookId}/bank` и хендлеры `platform/internal/httpapi/reading.go:193`=`func (h *v0) listBank`; (б) зеркало фронта стоит на 0.2.3 против канона 0.4.0, и сверка канон↔зеркало существует ТОЛЬКО прозой. **СО СЛОВ панели, мной НЕ пере-проверено — исполняющая сессия проверяет сама:** баннер таблицы §3 объявляет её пере-снятой при трёх строках против кода · ≈19 голых провенанс-якорей компаньона на код платформы протухли. ⚠ **ВОЗВРАЩЕНО В СОСТАВ 23.08 — моё прежнее опровержение стояло на ЛОЖНОМ основании.** Я написал «слова „пять“ в строке нет вовсе» — оно есть, тремя предложениями выше: «пер-термный механизм решений живёт ещё в ПЯТИ местах ЖИВЫХ, обслуживаемых схем», и рядом стоит «проза в 10 живых схемах». Две цифры об одном множестве сосуществуют и противоречат друг другу; клейм панели «ярлыки перепутаны» я НЕ разрешил и снимать его было нельзя. **Машинный ПОЛ для исполняющей сессии, с командой и с явной границей пола:** `grep -cE 'BankDecision|bank/decisions' docs/architecture/14-api-contract/openapi.yaml` → **9**, и раскладка их такая — **восемь внутри четырёх сносимых тел** (путь `docs/architecture/14-api-contract/openapi.yaml:469`=`/books/{bookId}/bank/decisions:` плюс три схемы на строках 1939, 1980, 1992) **и одно прозаическое** в живой схеме `BankTerm` (строка 1830). ⚠ **Граница пола названа честно: СЕМАНТИЧЕСКАЯ проза о пер-термной модели этим токеном НЕ ловится** — пример на строках 442–446, и её свип, как и разъятие «пять мест» против «десяти схем», остаётся работой минора: пол даёт нижнюю границу, а не ответ | оркестратор/контракт | скоро (следующим минором) | ратифицировано D39.154 п.8 | D39.154, `PD-370` | | 207 | **Пол самосогласованности манифеста стоит только у материализатора, интейк тот же документ ПРИНИМАЕТ — и по такой книге можно ОПЛАТИТЬ прогон** (`PD-367`, вторая волна ревью P8-FIX). Манифест `{ChaptersTotal: 120, UnitsTotal: 400}` с пустым списком глав `Whole()` отвергает, а `books.Parse` заводит книгу `not_started` с `chapter_count=120` и пустым деревом; потолок считается ОТ `chapter_count`. **Очевидное лекарство опровергнуто исполнением самой зоной:** применить `Whole()` на интейке нельзя — вся батарея интейка ездит на манифестах без списка глав, контракт интейка это только счётчики. Значит решение не зонное: либо контракт интейка меняется, либо пол ставится в другом месте цепи | оркестратор → платформа | скоро | нужно решение (лекарство ломает запиненный контракт интейка) | D39.154 п.10, `PD-367` | | 208 | **Эскалационный хоп на Gemini ТИХО ТЕРЯЕТ инъекцию банка памяти — книга едет без канона имён** (живая проба полигона 22.08, подтверждена вендор-докой и пере-проверена оркестратором по коду). OpenAI-совместимый слой Gemini принимает РОВНО ОДНО системное сообщение, лишние выбрасывает МОЛЧА — без ошибки, при `finish=stop` и внешне правильном переводе. Замер полигона на одном и том же системном тексте 7970 знаков: одним сообщением → `prompt_tokens` 2994 и маркерная инструкция ИСПОЛНЕНА, двумя → 535 токенов и инструкция НЕ исполнена. Механизм вендорский, поэтому уход на нативный API НЕ лечит: `systemInstruction` там — один объект `Content`. Наша сторона: `backend/internal/pipeline/render.go` штатно строит ВТОРОЕ системное сообщение (инъекция глоссария/STM), `backend/internal/llm/httpllm.go` `toOpenAIMessages` копирует сообщения дословно, склейки под Gemini нет нигде — а `gemini-3.1-pro-preview` стоит в ратифицированной цепочке эскалации ВО ВСЕХ четырёх арм-конфигах. ⇒ любой хоп на Gemini при непустой инъекции теряет глоссарий, и **это невидимо гейтам годности**. ⚠ Класс в проекте УЖЕ решён рядом и не обобщён: `provider_anthropic.go` сворачивает ведущий system-префикс в одно поле и падает громко на system посреди диалога. Оговорка D22.3 «экспозиция Ф1 нулевая» держится на МАРШРУТЕ (до Gemini в дефолте не доходят), а не на коде — то есть на одном включённом `budget_usd` от дефекта в проде. Полигон у себя починил склейкой по вендору (`eval/dovodka/rol.py`), в движок не лез; квирк записан в `00-provider-quirks.md` | бэкенд | **скоро (до первого платного прогона с ненулевым `budget_usd` эскалации)** | склейка системных сообщений для Gemini в транспорте + пин | пинг полигона №19 (22.08), вендор-дока `ai.google.dev/api/generate-content` | | 209 | **Риг живых проб не может залогировать тела запросов ни при каких env** (находка бэкенд-пака честности 17.08, оставшаяся без носителя до аудита доков 22.08): `LOG_LLM_BODIES=1`+`LOG_LEVEL=debug` объявлены носителем тел, но ключ читается из `obs.ReqInfo` контекста, а `live_reprobe_test.go` строит ctx БЕЗ `WithReqInfo` — гейт `obs/logging.go` не пропускает. Обход, которым пользовались: ручной дамп сообщений в файл. Цена — каждая живая проба, которой нужно прочитать провод глазами, платит за обход заново | бэкенд | когда-нибудь (следующее касание рига живых проб) | малое касание (`WithReqInfo` в риге) + пин | пак честности 17.08, вынесено аудитом доков 22.08 | -| 210 | **Род НЕ ПРОИЗВОДИТСЯ авто-путём вовсе — механизм доставки построен, а данных для него нет** (найдено трассировкой цепи банка 22.08, сверено кодом оркестратором; полигон вскрыл то же независимо: «в банке НЕТ ПОЛЕЙ ПОЛА»). Доставка рода на провод ПОСТРОЕНА и работает: `membank/memory.go` кладёт `genderConstraintNote` в черновую инъекцию — но ТОЛЬКО для строк со статусом `Confirmed`, по прямой директиве владельца D39.21 «род должен доезжать». Данных ей никто не даёт: промпт терминолога возвращает РОВНО ТРИ поля (`backend/prompts/zh-ru/terminologist.md`: термин · перевод · уверенность 0–100) — поля рода в ответе НЕТ; майнер берёт `gender` исключительно из СИДА (`internal/miner/miner_emit.go` `seedMeta`, `meta.gender = e.Gender`), эмитируемые кандидаты его не несут. ⇒ **на любой книге без ручного сида ось рода мертва по построению**, и «плывущий род» — это не дефект рендера, а отсутствие входных данных. Прямо бьёт по цели №1: реестр памяти C3 называет русский глагол прош. вр. при скрытом роде «механическим спойлер-каналом» и «жалобой №1 читателей MTL» (строка 82). **Развилка, которую надо назвать до стройки:** добавлять ли поле рода в ответ терминолога (двигает провод ⇒ ТОЛЬКО в вайр-батч 182) ЛИБО выводить род отдельным дешёвым проходом, ЛИБО признать род сид-только свойством и гейтить им качество явно. Родня 82 (морфо-гейт рода) и 178 (калибровка `sense`). ⚠ **ДОПОЛНЕНО 23.08 (№19):** замка ДВА и они независимы — род рендерится только при `Disp == Confirmed` (`membank/memory.go:705-713,823-827`=`Mirrors the editor block's confirmed-only gender`), а `mining.go:676` запрещает авто-банку статус `approved`. Род НЕ единственное осиротевшее поле: `Sense`/`Speech`/`Decl` тоже приходят только из сида, авто-продюсер есть лишь у `Dst` (терминолог) и `Type` (классификатор). ⚠ Поле `Gender` в схеме сида УЖЕ ЕСТЬ (`internal/seed/seed.go:68`=`Gender string`) — ось мертва в ПИСАТЕЛЯХ, не в формате. ⚠ **И тесты слепы ПО ПОСТРОЕНИЮ** (пере-проверено 23.08): все gender-кейсы конструируют род РУКАМИ, то есть зелёная батарея не отличает «производитель работает» от «производителя нет»; гейта на дрейф рода в движке нет | бэкенд (+полигон на замер) | **скоро (цель №1; правка провода — вайр-батчем 182)** | развилка → решение → правка промпта/прохода + замер на прогоне с редактурой | трассировка цепи банка 22.08, D39.21, C3 реестра памяти | -| 211 | **⛔ Движок на SaaS НЕ ПОЛУЧАЕТ провайдерских ключей — блокер живого прогона (202).** Прод-путь `systemd-run --user` (`platform/internal/runner/runner.go:136,149`=`"systemd-run", args...`), окружение юнита = окружение systemd-менеджера + `--setenv` из `s.Env`, а платформа кладёт туда РОВНО `TM_TRACE_ID` (`runs/spawn.go:168`=`Env: engineEnv(engineStreamID(`, `engineEnv` `:268`). Замысел записан комментарием `:265-267` («движок читает ключи из .env рядом со своим book.yaml»), движок так и делает (`backend/cmd/tmctl/main.go:184-185`=`loadDotEnv(filepath.Join(filepath.Dir(inv.cfgPath), ".env")`; `cmd.Dir = workdir` — `runner/engine.go:176`=`cmd.Dir = workdir`, оба пути в ОДИН файл), **а кладёт туда `.env` НИКТО**: в каталоге книги платформа создаёт только `book.yaml` (`books/render.go:109`=`os.O_CREATE|os.O_EXCL`, O_EXCL) и исходник (`books/books.go:259-264`=`os.O_CREATE|os.O_EXCL`); писателя `.env` в `platform/internal/` ноль. ⚠ **Почему пережило все батареи — ловушка паритета dev/prod:** дев-супервизор запускает движок обычным процессом с `Env=nil` и наследует окружение ПЛАТФОРМЫ, что обещает его же докстринг (`ingest/supervisor.go:35-36,65-67`=`Provider keys reach the engine through it`) — стенд работает, прод голодает, гейты гоняют дев-путь. **Форма фикса (консилиум 22.08):** путь к файлу ключей аргументом у `translate` (читающим $0-глаголам ключи запрещены — `dotenv.go:53-55`), деплой-файл ПЕРВЫМ в цепочке без перекрытия, дев-супервизор на тот же механизм. Довод формы: ключи обязаны покинуть каталог пользовательского контента — сегодня бэкап/экспорт каталога книги несёт секреты. ⚠ Ключи ОБЩИЕ на всех пользователей (слово владельца 22.08); BYOK не исключён позже и ложится в ту же форму пер-книжным путём | бэкенд + платформа | **блокер строки 202** | движковый + платформенный пак | консилиум шва 22.08, пере-проверено оркестратором №19 | -| 212 | **Загрузчик сида НЕстрогий — опечатка в имени поля теряется молча.** `backend/internal/membank/memseed.go:41`=`yaml.Unmarshal(raw, &sf)` — голый `yaml.Unmarshal` без `KnownFields`; `gendr:` вместо `gender:` выбрасывается ДО валидации, а `seed-lint` этого не видит (валидирует значения, не ключи). Тот же класс тихой подмены, против которого пак-16 покупал строгость `book.yaml` (`config/book.go:143`=`dec.KnownFields(true)`) — и живёт он в файлах, которые владелец правит РУКАМИ чаще всего. Ратификации послабления не найдено: пак-16 покупал строгость только для `book.yaml` | бэкенд | скоро (в движковый пак) | одна строка + тест на unknown-key | консилиум шва 22.08 | -| 213 | **Платформа дублирует движковую конвенцию пути и знает суффикс чужого артефакта.** `platform/internal/runner/artifacts.go:65-95`=`neither project_db nor book_id` парсит `book.yaml` нестрогим декодером ради двух ключей и при пустом `project_db` САМА вычисляет `filepath.Join(workdir, book_id+".db")`, повторяя `backend/internal/config/book.go:163-167`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")`; суффикс `.bank.json` зашит на `:37`. Смена дефолта в движке тихо уводит чтение банка платформой на несуществующий путь. ⚠ Свип ВСЕЙ границы движок↔платформа (консилиум 22.08) дал ровно ОДНО такое место плюс тривиальность: дев-супервизор хардкодит `"book.yaml"` мимо константы `ConfigFile` (`ingest/supervisor.go:140`=`"status", "--config"`). Прочее знание платформы о движке — ратифицированные каналы, не гниль. **Фикс:** движок отдаёт путь артефакта аддитивно (`status --json` либо ответ нового глагола), платформа выкидывает свой `projectDB()`. ⚠ Пере-именование банк-экспорта в фикс-имя рядом с `events.jsonl` — ЛОМАЮЩЕЕ, его место в окне строки **161**, НЕ здесь | бэкенд + платформа | скоро (в те же паки) | движковый + платформенный пак | консилиум шва 22.08 | +| 210 | **Род НЕ ПРОИЗВОДИТСЯ авто-путём вовсе — механизм доставки построен, а данных для него нет** (найдено трассировкой цепи банка 22.08, сверено кодом оркестратором; полигон вскрыл то же независимо: «в банке НЕТ ПОЛЕЙ ПОЛА»). Доставка рода на провод ПОСТРОЕНА и работает: `membank/memory.go` кладёт `genderConstraintNote` в черновую инъекцию — но ТОЛЬКО для строк со статусом `Confirmed`, по прямой директиве владельца D39.21 «род должен доезжать». Данных ей никто не даёт: промпт терминолога возвращает РОВНО ТРИ поля (`backend/prompts/zh-ru/terminologist.md`: термин · перевод · уверенность 0–100) — поля рода в ответе НЕТ; майнер берёт `gender` исключительно из СИДА (`internal/miner/miner_emit.go` `seedMeta`, `meta.gender = e.Gender`), эмитируемые кандидаты его не несут. ⇒ **на любой книге без ручного сида ось рода мертва по построению**, и «плывущий род» — это не дефект рендера, а отсутствие входных данных. Прямо бьёт по цели №1: реестр памяти C3 называет русский глагол прош. вр. при скрытом роде «механическим спойлер-каналом» и «жалобой №1 читателей MTL» (строка 82). **Развилка, которую надо назвать до стройки:** добавлять ли поле рода в ответ терминолога (двигает провод ⇒ ТОЛЬКО в вайр-батч 182) ЛИБО выводить род отдельным дешёвым проходом, ЛИБО признать род сид-только свойством и гейтить им качество явно. Родня 82 (морфо-гейт рода) и 178 (калибровка `sense`). ⚠ **ДОПОЛНЕНО 23.08 (№19):** замка ДВА и они независимы — род рендерится только при `Disp == Confirmed` (`membank/memory.go:705-713,823-827`=`Mirrors the editor block's confirmed-only gender`), а `mining.go:676` запрещает авто-банку статус `approved`. Род НЕ единственное осиротевшее поле: `Sense`/`Speech`/`Decl` тоже приходят только из сида, авто-продюсер есть лишь у `Dst` (терминолог) и `Type` (классификатор). ⚠ Поле `Gender` в схеме сида УЖЕ ЕСТЬ (`internal/seed/seed.go:71`=`Gender string`) — ось мертва в ПИСАТЕЛЯХ, не в формате. ⚠ **И тесты слепы ПО ПОСТРОЕНИЮ** (пере-проверено 23.08): все gender-кейсы конструируют род РУКАМИ, то есть зелёная батарея не отличает «производитель работает» от «производителя нет»; гейта на дрейф рода в движке нет | бэкенд (+полигон на замер) | **скоро (цель №1; правка провода — вайр-батчем 182)** | развилка → решение → правка промпта/прохода + замер на прогоне с редактурой | трассировка цепи банка 22.08, D39.21, C3 реестра памяти | +| 211 | **⛔ Движок на SaaS НЕ ПОЛУЧАЕТ провайдерских ключей — блокер живого прогона (202).** Прод-путь `systemd-run --user` (`platform/internal/runner/runner.go:136,149`=`"systemd-run", args...`), окружение юнита = окружение systemd-менеджера + `--setenv` из `s.Env`, а платформа кладёт туда РОВНО `TM_TRACE_ID` (`runs/spawn.go:168`=`Env: engineEnv(engineStreamID(`, `engineEnv` `:268`). Замысел записан комментарием `:265-267` («движок читает ключи из .env рядом со своим book.yaml»), движок так и делает (`backend/cmd/tmctl/main.go:202`=`loadDotEnv(filepath.Join(filepath.Dir(inv.cfgPath), ".env")`; `cmd.Dir = workdir` — `runner/engine.go:176`=`cmd.Dir = workdir`, оба пути в ОДИН файл), **а кладёт туда `.env` НИКТО**: в каталоге книги платформа создаёт только `book.yaml` (`books/render.go:109`=`os.O_CREATE|os.O_EXCL`, O_EXCL) и исходник (`books/books.go:259-264`=`os.O_CREATE|os.O_EXCL`); писателя `.env` в `platform/internal/` ноль. ⚠ **Почему пережило все батареи — ловушка паритета dev/prod:** дев-супервизор запускает движок обычным процессом с `Env=nil` и наследует окружение ПЛАТФОРМЫ, что обещает его же докстринг (`ingest/supervisor.go:35-36,65-67`=`Provider keys reach the engine through it`) — стенд работает, прод голодает, гейты гоняют дев-путь. **Форма фикса (консилиум 22.08):** путь к файлу ключей аргументом у `translate` (читающим $0-глаголам ключи запрещены — `dotenv.go:53-55`), деплой-файл ПЕРВЫМ в цепочке без перекрытия, дев-супервизор на тот же механизм. Довод формы: ключи обязаны покинуть каталог пользовательского контента — сегодня бэкап/экспорт каталога книги несёт секреты. ⚠ Ключи ОБЩИЕ на всех пользователей (слово владельца 22.08); BYOK не исключён позже и ложится в ту же форму пер-книжным путём | бэкенд + платформа | **блокер строки 202** | движковый + платформенный пак | консилиум шва 22.08, пере-проверено оркестратором №19 | +| 212 | **Загрузчик сида НЕстрогий — опечатка в имени поля теряется молча.** `backend/internal/membank/memseed.go:51`=`yaml.Unmarshal(raw, &sf)` — голый `yaml.Unmarshal` без `KnownFields`; `gendr:` вместо `gender:` выбрасывается ДО валидации, а `seed-lint` этого не видит (валидирует значения, не ключи). Тот же класс тихой подмены, против которого пак-16 покупал строгость `book.yaml` (`config/book.go:163`=`dec.KnownFields(true)`) — и живёт он в файлах, которые владелец правит РУКАМИ чаще всего. Ратификации послабления не найдено: пак-16 покупал строгость только для `book.yaml` | бэкенд | скоро (в движковый пак) | одна строка + тест на unknown-key | консилиум шва 22.08 | +| 213 | **Платформа дублирует движковую конвенцию пути и знает суффикс чужого артефакта.** `platform/internal/runner/artifacts.go:65-95`=`neither project_db nor book_id` парсит `book.yaml` нестрогим декодером ради двух ключей и при пустом `project_db` САМА вычисляет `filepath.Join(workdir, book_id+".db")`, повторяя `backend/internal/config/book.go:189`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")`; суффикс `.bank.json` зашит на `:37`. Смена дефолта в движке тихо уводит чтение банка платформой на несуществующий путь. ⚠ Свип ВСЕЙ границы движок↔платформа (консилиум 22.08) дал ровно ОДНО такое место плюс тривиальность: дев-супервизор хардкодит `"book.yaml"` мимо константы `ConfigFile` (`ingest/supervisor.go:140`=`"status", "--config"`). Прочее знание платформы о движке — ратифицированные каналы, не гниль. **Фикс:** движок отдаёт путь артефакта аддитивно (`status --json` либо ответ нового глагола), платформа выкидывает свой `projectDB()`. ⚠ Пере-именование банк-экспорта в фикс-имя рядом с `events.jsonl` — ЛОМАЮЩЕЕ, его место в окне строки **161**, НЕ здесь | бэкенд + платформа | скоро (в те же паки) | движковый + платформенный пак | консилиум шва 22.08 | | 214 | **Подпись банка не оставляет на банке НИ ОДНОГО следа.** Единственная долговечная запись акта подписи — булев `bank_released` на прогоне (`platform/internal/pgstore/runs.go:844`=`bank_released = bank_released or $2`): кто и когда подписал, что именно вошло в подписанный набор — не хранится нигде. Следствие: пост-фактум ответить «почему в книге этот перевод термина» нельзя, а при пере-нарезке или пере-прогоне восстановить намерение человека не из чего. Всплыло трассировкой цепи банка 22.08, подтверждено пере-проверкой | платформа + бэкенд | когда-нибудь (с трубой доставки правок, D39.156) | отдельное решение о провенансе подписи | трассировка цепи банка 22.08, пере-проверено №19 | | 215 | **$0-точечный репин движка работает ТОЛЬКО под `--resnapshot`, а платформа этого флага не передаёт.** Механизм пере-привязки незатронутых юнитов при сдвиге банка построен (`backend/internal/pipeline/stagerun.go:88-107`=`POINTWISE RE-EDIT`) и он и делает подпись термина доступной по цене; но с платформы он недостижим — она зовёт `translate` без флагов сверх потолка и `--verify-bank`. ⇒ на SaaS правка термина сегодня стоила бы полной пере-редактуры, а не дельты. Родня строки 199(б) (нулевой набор consent-флагов) | платформа + бэкенд | когда-нибудь (с платформенным паком шва) | отдельное решение | трассировка цепи банка 22.08, пере-проверено №19 | | 216 | **Подпись банка, halt на потолке и резюм после пополнения не проверялись против ЖИВОГО движка ни разу.** Проверки этих путей в батарее платформы стоят на фейках; единственный живой прогон движка в батарее — не по этим маршрутам. То есть три несущих сценария продукта держатся на договорённости о поведении чужой стороны, а не на замере. ⚠ Гейтится тем же, чем и живой прогон книги (строка 202): холодный прогон движка | платформа | когда-нибудь (с 202) | живой прогон по трём маршрутам | трассировка цепи банка 22.08, пере-проверено №19 | -| 217 | **Скрипты полигона захардкодили АБСОЛЮТНЫЕ пути двух корней и не работают на этой машине вовсе.** ДВА РАЗНЫХ слома. **Корень репо** — смена пользователя: литерал в 121 файле, из них 104 дословно `REPO = Path("/home/ubuntu/projects/textmachine")`, портабельных форм всего 5. **Корень книг** — оба события порознь: литерал `/home/ubuntu/books` 86 файлов (пользователь), портабельный `Path.home()/"books"` 89 файлов (переезд каталога; на прежних машинах работал). Объединение 173 файла. Пользователь теперь `ubuntu-26`, книги переехали в `<репозиторий>/books` ⇒ ломается и то и другое, включая ЖИВОЙ гейт фазы Д `eval/conformance.py:48`=`REPO = Path("/home/ubuntu/projects/textmachine")`. Живых носителей 26 по корню книг (среди них портабельная форма преобладает 20:6) плюс корневые гейты и сборщики корпусов; остальное — скрипты закрытых эксп-12–16, их владелец велел ВЫБРОСИТЬ, а не чинить (D39.157 п.4а). Форма правки — за зоной; приор оркестратора: один модуль путей, корень резолвится МАРКЕРОМ вверх по дереву (движок так и делает — `backend/internal/miner/miner_parity_test.go:56-57`=`finds the repository root by MARKER`), книги от него, обе ручки перекрываются env | полигон | скоро (блокирует любой прогон зоны) | пак путей полигона | переезд машины 24.08, №19 | +| 217 | **Скрипты полигона захардкодили АБСОЛЮТНЫЕ пути двух корней и не работают на этой машине вовсе.** ДВА РАЗНЫХ слома. **Корень репо** — смена пользователя: литерал в 121 файле, из них 104 дословно `REPO = Path("/home/ubuntu/projects/textmachine")`, портабельных форм всего 5. **Корень книг** — оба события порознь: литерал `/home/ubuntu/books` 86 файлов (пользователь), портабельный `Path.home()/"books"` 89 файлов (переезд каталога; на прежних машинах работал). Объединение 173 файла. Пользователь теперь `ubuntu-26`, книги переехали в `<репозиторий>/books` ⇒ ломается и то и другое, включая ЖИВОЙ гейт фазы Д `eval/conformance.py:48`=`REPO = Path("/home/ubuntu/projects/textmachine")`. Живых носителей 26 по корню книг (среди них портабельная форма преобладает 20:6) плюс корневые гейты и сборщики корпусов; остальное — скрипты закрытых эксп-12–16, их владелец велел ВЫБРОСИТЬ, а не чинить (D39.157 п.4а). Форма правки — за зоной; приор оркестратора: один модуль путей, корень резолвится МАРКЕРОМ вверх по дереву (движок так и делает — `backend/internal/miner/miner_parity_test.go:29`=`finds the repository root by MARKER`), книги от него, обе ручки перекрываются env | полигон | скоро (блокирует любой прогон зоны) | пак путей полигона | переезд машины 24.08, №19 | | 218 | **Три тест-хелпера движка резолвят корпус от `$HOME/books`, которого больше нет.** `internal/miner/miner_parity_test.go:101-106`=`func standFile(parts ...string) string` · `internal/checks/labelharness_test.go:58-63`=`func standLabelsDir() string` · `internal/membank/labelharness_test.go:38`=`func memStandLabelsDir() string`. Env-переопределения в коде УЖЕ есть и с ними корпус-батарея зелена (проверено №19: `TM_CHECKER_LABELS_DIR`/`TM_MINER_PARITY_{RECORDS,SEED}` на новый путь ⇒ красен только `TestMinerFullBookParity`, и только из-за отсутствующего jieba — строка 123). Чинится ДЕФОЛТ: тот же маркерный резолв, что уже применён к `repoFile` в том же файле | бэкенд | скоро (дешёвая, 3 функции) | ПОВЕШЕНА на движковый пак шва §4.7 (24.08) | переезд машины 24.08, №19 | | 220 | **Репо-широкий гейт доков бутстрапится пакетным менеджером ОДНОЙ зоны, и та заморожена.** Диспетчер `.git/hooks/pre-commit` зоно-нейтрален по построению (`for hook in */scripts/githooks/pre-commit`, `#!/bin/sh`, node не нужен) и судит `CLAUDE.md` + `docs/**` + `platform/docs/**` + `frontend/docs/**` (`docs/scripts/counts.py:436-440`=`ROOT / "CLAUDE.md"`), но единственный его УСТАНОВЩИК — `frontend/scripts/githooks/install.mjs`, вызываемый ключом `prepare` из `frontend/package.json`. Охват и бутстрап не совпадают ⇒ инструмент решает свою задачу не в полной мере. Проверено исполнением 24.08: на этой машине `.git/hooks/` содержал только `*.sample`, гейт МОЛЧАЛ, ничего не проверив, и поймал первые четыре дефекта только после ручной установки; сессия бэкенда или доков `npm install` не делает никогда. Приор: зоно-нейтральный установщик в `docs/scripts/githooks/` как ЕДИНСТВЕННЫЙ писатель диспетчера, `prepare` фронта зовёт его — фронтовая половина выписана пингом №19 в зонный журнал фронта. ⚠ Ручная установка — временная мера, пока строка открыта; записывать её как порядок работы ЗАПРЕЩЕНО (это ровно тот обход, цену которого называет шапка `CLAUDE.md`) | доки + фронт | скоро | зоно-нейтральный бутстрап + правка `prepare` при разморозке | аудит переезда 24.08, №19 | | 49 | **Этапы Б+В спеки D15.2** (content-addressed resume / `guard_hash` — D39.31 сознательно не строил) ; этап В = tmctl export-контракт D29.1 (annot-v1 · цвет-мап+rollup · политика красных fail-closed) + операторский протокол-документ релиза гендер-твиста (D5.1): сам механизм УЖЕ построен — until_ch-правка → точечный re-edit со сметой и $0-пере-пином (`repin.go`/`rebill.go`, D39.42 п.5/D39.45) | бэкенд | скоро (ПОДТЯНУТА D39.81: annot-v1 = источник замечаний для фронта — критический путь подключения) | отдельное решение (annot-половина — по контракту 14, D39.99) | D39.34(4), D33 п.5, D39.81 | @@ -205,7 +205,7 @@ | 123 | **Полигон: `jieba==0.42.1` — build-time зависимость артефакта контраста `eval/exp16/data/jieba_dict_general_zh.txt`** (пере-собран бэкенд-сессией 03.08, полный sha256 сошёлся с пином `16-bank-mining.md`); дописать в манифест окружения полигона с пометкой «build-time, не runtime» | полигон | когда-нибудь | строка манифеста | D39.96 п.6, отчёт §7-4/§6 | | 124 | **Калибровка снапшота (вопрос владельца 04.08, разбор D39.97 п.5): нацеленность, не агрессивность.** ⚠ **Осей теперь ЧЕТЫРЕ — дописка D39.151:** (г) **идентичность оплаченной работы** («строка диспозиции ↔ вызовы, которые её оплатили») — переселена сюда из строки 195 решением владельца 17.08; родня оси (в), потому что массовое кросс-снапшотное переиспользование подрывает наивную атрибуцию по равенству снапшота; смежный носитель — §7 спеки D15.2. Прежние три оси одним дизайн-паком: (а) недо-чувствительность — вердикт-несущие вызовы x/text не свёрнуты (вход: строка 119); (б) пере-охват — `EmbeddedVersion` фолдит все пары разом → пер-парный хеш (родня 115/120); (в) дешёвая перекупка: `tmctl resnapshot --dry-run` — байт-сверка тел по чекпойнтам (`msgsContentHash`), идентичные юниты пере-привязываются без пере-вызова, платятся только изменившиеся. «Поведенческий отпечаток вместо строки версии» отложен (пол ≠ доказательство) | бэкенд | когда-нибудь (перед МАСШТАБОМ — когда перекупки станут реальными деньгами) | дизайн-пак по D39.97 п.5 | D39.97 | | 221 | **Джойн «предложено × решено × нерешено» живёт ТОЛЬКО в движке.** Читаемый банк несёт итоги без предложений, карта подписи — предложения без решений; семантика «решено ли» (`ownerHandled`) существует в двух файлах движка и больше нигде (греп по трём зонам). Экран подписи при разморозке фронта заставит платформу пере-реализовать движковый закон у себя — дубль, запрещённый п.6 закона шва. **Фикс:** движок публикует нерешённость (секцией банк-экспорта или отдельной проекцией) — но ТОЛЬКО когда экран закажут: рефакторить под несуществующий экран не надо. Сейчас — ноль кода | бэкенд | гейчена разморозкой фронта | аддитивное поле проекции | приёмка двери 27.08, D39.158 | -| 222 | **Межкнижный остаток оси арбитра: второй конфиг ТОЙ ЖЕ книги с другим `project_db` пишет ту же пару файлов решений под ДРУГИМ локом**, мимо флока живого прогона. Достижимо по построению: пути решений считаются от каталога `book.yaml` и `book_id`, лок — от `project_db`. Рукотворная конфигурация оператора (две базы одной книге), лечение трогает ратифицированный п.2 закона (второй арбитр на идентичности файлов решений либо отказ при загрузке). ⚠ Межкнижная версия ЗАКРЫТА снятием объявляемых ключей (D39.158 п.5) | бэкенд | когда-нибудь (непропорционально: правка ратифицированного закона ради операторского футгана) | второй арбитр ЛИБО отказ при загрузке | приёмка двери 27.08, D39.158 | +| 222 | **ВНУТРИкнижный остаток оси арбитра (заголовок исправлен 27.08 — прежний говорил «межкнижный» и инвертировал собственное тело: межкнижную гонку конвенция как раз сняла): второй конфиг ТОЙ ЖЕ книги с другим `project_db` пишет ту же пару файлов решений под ДРУГИМ локом**, мимо флока живого прогона. Достижимо по построению: пути решений считаются от каталога `book.yaml` и `book_id`, лок — от `project_db`. Рукотворная конфигурация оператора (две базы одной книге), лечение трогает ратифицированный п.2 закона (второй арбитр на идентичности файлов решений либо отказ при загрузке). ⚠ Межкнижная версия ЗАКРЫТА снятием объявляемых ключей (D39.158 п.5) | бэкенд | когда-нибудь (непропорционально: правка ратифицированного закона ради операторского футгана) | второй арбитр ЛИБО отказ при загрузке | приёмка двери 27.08, D39.158 | | 223 | **Майнерский хвост за потолком структурно недостижим.** Потолок эмиссии (200) применяется ДО фильтров пригодности, сида и отказов, а детектор кандидатов сида и отказов не видит вовсе — репозиторий пишет это про себя дословно. Замер: 2110 ранжированных → 442 годных во всём алфавите → 117 внутри окна потолка → **325 невидимы постоянно**; отклонение всех 117 не подняло ни одного. ⚠ Решения владельца слотов НЕ освобождают, и это верно СЕГОДНЯ, а не следствие флажка; цитата «пере-предлагается once these are signed or declined» относится только к ОБРАТНОЙ секции. Двигать потолок — трогать фрозен-паритет, отдельное решение. Якорь-пин: `internal/miner/emissioncap_test.go` | бэкенд | когда-нибудь | решение о потолке, не правка | приёмка двери 27.08, D39.158 | | 224 | **Банк-экспорт на стопе ПУСТ** — проекция, наиболее пустая ровно в момент, когда её читает экран подписи. Замерено: стоп с двумя предложенными термами даёт `total=0`, авто-банк на стопе отсутствует, потому что он пишется только в ветке авто-продолжения; при этом комментарий рядом называет стоп «границей, которую читает экран подписи», а платформа читает именно `.bank.json`. Член класса «проекция без контракта потребителя» (`architecture/18-bank-ontology.md`) | бэкенд + платформа | скоро (держит экран подписи вместе с 221) | публиковать предложенное на стопе | приёмка двери 27.08, D39.158 | | 225 | **Гейт «строка `open`, а лекарство уже в дереве».** При лендинге пересекать ПОЛНЫЕ пути застейдженных файлов с якорями открытых строк регистра и бэклога; совпадение — вопрос лендеру, а не находка следующего пака. Замерено прежде решения: по именам файлов 22 совпадения, почти все ложные; по полным путям 2, оба настоящие, и один из них (`PD-157`) убил мой же лендинг `d1eb8a9`. Норма-половина уже стоит в `platform/docs/ENGINEERING_STANDARDS.md` §3 п.8; здесь — машинная половина, режимом `counts.py` | оркестратор | скоро (окупился до постройки) | режим `counts.py`, берущий список путей и печатающий открытые строки, чьи якоря в них целят | приёмка пака P8-REVIEW 27.08, D39.159 п.7 | diff --git a/docs/architecture/05-decisions-log.md b/docs/architecture/05-decisions-log.md index 8b0c71bd..e4b3a442 100644 --- a/docs/architecture/05-decisions-log.md +++ b/docs/architecture/05-decisions-log.md @@ -11,6 +11,8 @@ > ⚠ **Эррата 16.08-г (D39.138 п.2а, часть «TermStatus снять с провода»):** ОПРОКИНУТА пост-ревью АВТОРА отчёта research/28 (по заказу владельца, речек оркестратора уликами): обоснование Б-0 «ни один экран их не рисует» подменяло «экрана подписи ещё НЕТ» — ось статуса ПРОДУКТОВАЯ (спека `:1070-1072`: «главный фильтр работы» S5; шов клиента уже потребляет — `frontend/src/api/vocabulary.ts:159-163`). Читать так: ось `TermStatus` ОСТАВИТЬ, движковые слова значений переименовать в продуктовые; «`TermOrigin` снять/переименовать» (`ruby` — паро-специфика) — В СИЛЕ. Контекст метода: Б-0/Б-11а/Б-23 добавлены в отчёт ПОСЛЕ раунда адверсариального судейства сессии — их основания проверять строже (помечено в ревью-шапке отчёта). > ⚠ **Эррата 20.08 (D39.153 п.7б):** D39.144 п.2 атрибутирует пер-термный гейт полноты банка «изобретению КОНТРАКТНОЙ линии (Ф-14 → спека → 0.3.0)» — верно про ГЕЙТ и **неверно про ФОРМУЛУ**. Формула «стоп очищается, когда каждый терм промоутнут ИЛИ отклонён» родилась 19.07 В ДВИЖКЕ как лечение ливлока R1-FL-B (отклонённый терм переспрашивался каждый прогон — тело D39.19, слайс `../archive/architecture/05-decisions-D39-arch-reset.md:22`) и живёт там до сих пор (`backend/internal/pipeline/mining.go:80-82,243`); контрактная линия 04.08 переписала её дословно и вывела из неё ЭКРАННЫЙ гейт — счётчик «решено N из M» и запрет продолжить (коммит `16e20e5`, `frontend/docs/API_CONTRACT_INPUT.md:258-264`). **Следствие, из-за которого эррата несущая:** снимая гейт, D39.144 адресовала только контракт, поэтому движковая половина осталась БЕЗ явной отмены — она сегодня ОБОЙДЕНА платформой (флаг не передаётся на resume), а не устранена; носитель — строка 191(б), развилка — строка 203(з). > ⚠ **Эррата 27.08 (D39.158 п.7):** D39.153 п.7б объявляет «ОСТАТКА-РАТИФИКАЦИИ НЕТ… гейт проверяет полноту ФАЙЛА решений, а не подписей, и ОДИН файл со всем банком его снимает — конфликта с моделью владельца нет». Читать так: диспозиция отвечала на вопрос «снимает ли гейт один файл» (снимает) и верифицировала СОСТАВНОЕ поведение — платформа снимает флаг, движок уходит по авто-ветке. Вопрос «работает ли флажок БЕЗ обхода» ею не закрывался, и заказанное той же строкой 191(б) «ослабить границу самой паузы» исполнено не было. Исполнено D39.158: движковая половина УСТРАНЕНА, не обойдена. +> ⚠ **Эррата 27.08-б (D39.158, страница `18-bank-ontology.md`):** названный там класс дефекта «проекция без потребителя и без якоря свежести» на момент ратификации имел ОДИН ярус и не судил носитель с ДВУМЯ писателями и нулём читателей. Дописан второй ярус: расхождение писателей ничем не судится, пока читателя нет, а в день его появления СТАНЕТ семантикой, разрешаемой задним числом по накопленной истории. Найдено платформенной сессией при вычитке приёмки D39.159 (живой член — `PD-396`); правило «у источника РОВНО один писатель» с этого дня читается в обе стороны. +> ⚠ **Эррата 27.08-в (D39.158 п.1):** «движок исполняет продуктовую модель сам» сказано без ратифицированной ОГОВОРКИ — правка ОТДЕЛЬНОГО термина через дверь доступна только для mined-термов; правка СИД-терма дверью отклоняется по имени (`internal/membank/decisions.go:359`), потому что это перенос базового снапшота и пере-оплата черновой волны — другое решение (щель ратифицирована, строка бэклога 192). Ещё три места, где модель шире кода, названы строками: окно глав одним решением не правится (named-узость v1) · банк-экспорт на стопе пуст (224) · «полный останов» — это граница ПЕРЕД редакторской волной, черновая довершается (S16). Найдено вычиткой старшего при ревью D39.159. > ⚠ **Навигация (актуализация 07.08, эра D39.1xx):** append-only-дисциплина (D23.3) означает, что > НЕВЕРНЫЙ ФАКТ внутри старой ноты не переписывается, а получает эрратау — и тогда он опасен ровно diff --git a/docs/architecture/09-target-architecture.md b/docs/architecture/09-target-architecture.md index 093fb487..f6dff5cb 100644 --- a/docs/architecture/09-target-architecture.md +++ b/docs/architecture/09-target-architecture.md @@ -7,13 +7,13 @@ > > Это карта «куда и почему», а не пошаговая спека. Спеки — в хендофф-промтах сессий; контракт — в D-логе. -> **⟶ СТАТУС СТРОЙКИ (освежён 23.08 оркестратором №19: **РАТИФИЦИРОВАН ЗАКОН ВХОДНОЙ ДВЕРИ ШВА** — D39.156, тело `17-seam-inbound-law.md`. Половина контракта движок↔платформа, которой не было: наружу поток богатый, внутрь не шло ничего, кроме запуска с флагами, поэтому данные ПОЛЬЗОВАТЕЛЯ движок принимать не умел. Ратифицирована ДИСЦИПЛИНА из семи пунктов, не каталог команд ⇒ двери встают паками без нового решения владельца. Развилка владения `book.yaml` (строка 199а) РАСТВОРЕНА: конвенционные дефолты путей + движковый глагол-писатель. Свип ВСЕЙ границы дал ОДНО гнилое место (строка 213) при шести ратифицированных каналах — широкого рефакторинга шва не требуется. Заведены строки 211 (движок на SaaS не получает провайдерских ключей — БЛОКЕР живого прогона 202), 212, 213; выдан движковый пак `BACKEND_SEAM_PACK_SESSION_PROMPT.md`. Ранее 22.08 оркестратором №18: **пак платформы P8-FIX ПРИНЯТ и ЗАЛЕНДЕН** — голодание свипа `PD-169` закрыто четырьмя механизмами (фазы прохода · отсрочка с бэкоффом · порог `StalledAfter` с гейджем и CLI · терминальный вердикт оператора `run abandon`), оба бюджета стали операторскими ручками, снят пер-термный write-путь банка по D39.144 — открытой осталась контрактная половина `PD-370` (строка 206). Ранее 20.08: **пак P7 платформы ПРИНЯТ и ЗАЛЕНДЕН** — читающая поверхность построена целиком (главы · юниты · замечания · проекция банка · SSE · модель ошибок · условные чтения · идемпотентность), D39.153; **канон контракта — 0.4.0**, ратифицирован D39.152; пак движка «честные числа и статусы» принят с фикс-листом, D39.149. Предыдущий срез — 16.08 оркестратором №17: контракт-ревью ПРИНЯТО (research/28, D39.138), ломающий батч 0.3.0 ИСПОЛНЕН и принят с дофиксом (D39.142–145: конвейер снят с провода, машинная модель ошибок, /capabilities, поток на книгу, подпись банка = один ОК D39.144; жанр из продукта ВЫКИНУТ — строка 184); P7 платформы ЗАПУЩЕН по финальному канону. Ранее 15.08: эмиттер шва ПОСТРОЕН И ПРИНЯТ — `events.jsonl` StreamVersion 1.1, exit 4/5 + полоса отказов 10–19 (D39.131); платформа P6 + дофикс приняты — потребительская половина шва, интейк формы Б, дев-стенд, PD-113/PD-196 закрыты (D39.132); `tmctl migrate` принят — exit 13 финализирован, деадлок деплоя v15 разорван (D39.134); фронт S4 принят — путь «файл → загрузка → запуск → прогресс» в мире моков, контракт **0.2.3** в каноне (D39.135). Ранее 14.08 №16: платформа достроена до интейка книг P5 — `POST /books` с сентинелом хранилища, стоп/резюм с намерением, Prometheus, Go-floor 1.26.6 (D39.130); фронт — S3.5–S3.7 приняты (оболочка · слой данных · i18n; D39.127/129). Ранее 09.08, пост-D39.125: шов движок↔платформа ратифицирован — транзиентный systemd-юнит на прогон + `events.jsonl`-outbox в каталоге книги, платформа НЕ родитель (D39.106); платформа достроена до раннера P4 (D39.109/114/123 — River-очередь, холд-до-спавна, реконсилятор; дефекты — `platform/docs/DEFECT_REGISTER.md`); фронт — слой данных S3, контракт 0.2.0→0.2.1 (D39.115/123); движок отдал блокеры контракта — `tm-manifest-v2`, `unit.id` с тегом разреза, пофазные счётчики на главу, `--ceiling-usd` (D39.122); слой 4 добит фикс-паком банка — семьи термов со-батчатся юнитами, bankdata-плоскость (D39.118). Ранее 04.08, пост-D39.105: слои 4/5 добраны паками банка/чекеров D39.69/78/82; репо на стандартах — `make battery`, tmvet/archguard, D39.96/97; контракт API v0 = `14-api-contract/` (D39.99), платформа = зона `platform/`, открыта D39.100. Баннер — не переписывание, тело ниже = замысел арх-ресета 13.07).** Карта «слой → статус → пак/D»: +> **⟶ СТАТУС СТРОЙКИ (освежён 27.08 оркестратором №19: **ВХОДНАЯ ДВЕРЬ ШВА ПОСТРОЕНА** — D39.158, четыре пака зоны бэкенда приняты и заленджены одним деревом; закон двери D39.156 тем самым исполнен, а не только ратифицирован. Онтология банка вынесена страницей `18-bank-ontology.md`. Прежняя редакция шапки от 23.08: **РАТИФИЦИРОВАН ЗАКОН ВХОДНОЙ ДВЕРИ ШВА** — D39.156, тело `17-seam-inbound-law.md`. Половина контракта движок↔платформа, которой не было: наружу поток богатый, внутрь не шло ничего, кроме запуска с флагами, поэтому данные ПОЛЬЗОВАТЕЛЯ движок принимать не умел. Ратифицирована ДИСЦИПЛИНА из семи пунктов, не каталог команд ⇒ двери встают паками без нового решения владельца. Развилка владения `book.yaml` (строка 199а) РАСТВОРЕНА: конвенционные дефолты путей + движковый глагол-писатель. Свип ВСЕЙ границы дал ОДНО гнилое место (строка 213) при шести ратифицированных каналах — широкого рефакторинга шва не требуется. Заведены строки 211 (движок на SaaS не получает провайдерских ключей — БЛОКЕР живого прогона 202), 212, 213; выдан движковый пак `BACKEND_SEAM_PACK_SESSION_PROMPT.md`. Ранее 22.08 оркестратором №18: **пак платформы P8-FIX ПРИНЯТ и ЗАЛЕНДЕН** — голодание свипа `PD-169` закрыто четырьмя механизмами (фазы прохода · отсрочка с бэкоффом · порог `StalledAfter` с гейджем и CLI · терминальный вердикт оператора `run abandon`), оба бюджета стали операторскими ручками, снят пер-термный write-путь банка по D39.144 — открытой осталась контрактная половина `PD-370` (строка 206). Ранее 20.08: **пак P7 платформы ПРИНЯТ и ЗАЛЕНДЕН** — читающая поверхность построена целиком (главы · юниты · замечания · проекция банка · SSE · модель ошибок · условные чтения · идемпотентность), D39.153; **канон контракта — 0.4.0**, ратифицирован D39.152; пак движка «честные числа и статусы» принят с фикс-листом, D39.149. Предыдущий срез — 16.08 оркестратором №17: контракт-ревью ПРИНЯТО (research/28, D39.138), ломающий батч 0.3.0 ИСПОЛНЕН и принят с дофиксом (D39.142–145: конвейер снят с провода, машинная модель ошибок, /capabilities, поток на книгу, подпись банка = один ОК D39.144; жанр из продукта ВЫКИНУТ — строка 184); P7 платформы ЗАПУЩЕН по финальному канону. Ранее 15.08: эмиттер шва ПОСТРОЕН И ПРИНЯТ — `events.jsonl` StreamVersion 1.1, exit 4/5 + полоса отказов 10–19 (D39.131); платформа P6 + дофикс приняты — потребительская половина шва, интейк формы Б, дев-стенд, PD-113/PD-196 закрыты (D39.132); `tmctl migrate` принят — exit 13 финализирован, деадлок деплоя v15 разорван (D39.134); фронт S4 принят — путь «файл → загрузка → запуск → прогресс» в мире моков, контракт **0.2.3** в каноне (D39.135). Ранее 14.08 №16: платформа достроена до интейка книг P5 — `POST /books` с сентинелом хранилища, стоп/резюм с намерением, Prometheus, Go-floor 1.26.6 (D39.130); фронт — S3.5–S3.7 приняты (оболочка · слой данных · i18n; D39.127/129). Ранее 09.08, пост-D39.125: шов движок↔платформа ратифицирован — транзиентный systemd-юнит на прогон + `events.jsonl`-outbox в каталоге книги, платформа НЕ родитель (D39.106); платформа достроена до раннера P4 (D39.109/114/123 — River-очередь, холд-до-спавна, реконсилятор; дефекты — `platform/docs/DEFECT_REGISTER.md`); фронт — слой данных S3, контракт 0.2.0→0.2.1 (D39.115/123); движок отдал блокеры контракта — `tm-manifest-v2`, `unit.id` с тегом разреза, пофазные счётчики на главу, `--ceiling-usd` (D39.122); слой 4 добит фикс-паком банка — семьи термов со-батчатся юнитами, bankdata-плоскость (D39.118). Ранее 04.08, пост-D39.105: слои 4/5 добраны паками банка/чекеров D39.69/78/82; репо на стандартах — `make battery`, tmvet/archguard, D39.96/97; контракт API v0 = `14-api-contract/` (D39.99), платформа = зона `platform/`, открыта D39.100. Баннер — не переписывание, тело ниже = замысел арх-ресета 13.07).** Карта «слой → статус → пак/D»: > | Слой | Статус | Пак / D | > |---|---|---| > | 1 Сегментация | ✅ ПОСТРОЕН: бюджет в выходных токенах снапшот-folded (`chunk.SegBudget`), edit-единицы декаплены; когезия/carryover — НЕ строится (D39.7/8: эффекты под floor 0.126; кнобы `STMDepth`/`OverlapTokens` УДАЛЕНЫ, WS2 §2а), граница `logical` — не рычаг (exp15), greedy остался | пак-11 (D39.13 WS2 · D39.17 волны · D39.18 c-lite) | > | 2 Пары | ✅ СЕАМ ПОСТРОЕН: `internal/lang`+langpacks (embedded · книго-overlay `langpack_extend`) · пар-агностичные чекеры · `configs/pairs/` · промпт-конвенция `prompts/<пара>/<роль>.md`; открыто: извлечение дискурс-норм/few-shot из ТЕЛА промтов (форсинг = ja→ru) | D39.16 · пак-14 · пак-15 (D39.23г) | > | 3 Контракт t/e | ✅ ПОСТРОЕН ЗА `enabled:false`: `checks.RepairCandidates` (позиционная детекция) · read-only скан остатка · машинерия ремонта §15.2; **включение платного пути = решение владельца по замеру остатка**; ⚠ эмпирика контракта — экспы 19/20 (D39.108): гейт Q4b — диффы ОТЛОЖЕНЫ (D21.4/D21.10 закрыты); итог эксп-21 (D39.117): full-regen ПОДТВЕРЖДЁН (точечная починка как замена редактора отклонена; принята формой «механический пре-гейт ПЕРЕД редактором» — строка 142; строка 106 понижена) | пак-16 (D39.24), **D39.108**, **D39.117** | -> | 4 Память | ✅ ПОСТРОЕН + ДОСТРОЕН паком-20 ⚠ **но ВХОДНОЙ двери у него нет: правку термина пользователем положить некуда — писателя файлов решений не существует ни на одной стороне; лечится движковым паком по D39.156** — disposition-gated suppressor · **ТЕРМИНОЛОГ** (роль пар-пака: смердженный банк + KWIC → консолидированный dst, durable-деньги) · флаговый стоп `--verify-bank` (дефолт=авто) · ДВУХСЕКЦИОННАЯ редакторская инъекция (CONFIRMED + ⟨проверить⟩; все строки фолдятся в версию; ⚠ D39.104: схлопывается в ЕДИНЫЙ закон-блок, маркер с провода снимается — внесение = строка 134 ПОСЛЕ пробы 18) · точечная ре-редактура (bank-only → пере-пин $0) · банкнота отдельным шиппинг-файлом · языковой экран `target_script` · дисциплина банкноты (кавычки/агрегация); жанровый словарь ОТМЕНЁН (D39.47) | пак-1 · пак-11 · **пак-20 (D39.41–45/47/50–53)** | +> | 4 Память | ✅ ПОСТРОЕН + ДОСТРОЕН паком-20 ✅ **ВХОДНАЯ ДВЕРЬ ПОСТРОЕНА И ЗАЛЕНДЖЕНА 27.08 (D39.158, лендинг `d1eb8a9`)**: $0-глагол `tmctl bank-apply` (проекция `--dry-run`, всё-или-ничего, байтовый no-op идемпотентен), писатель решений `internal/membank/decisions.go`, стоп банка — ФЛАЖОК, который движок чтит сам (память предъявления `bank_stop_presented`, схема store v16), полоса отказов получила пол и класс 15 `write_incomplete`. Онтология слоя — `18-bank-ontology.md` — disposition-gated suppressor · **ТЕРМИНОЛОГ** (роль пар-пака: смердженный банк + KWIC → консолидированный dst, durable-деньги) · флаговый стоп `--verify-bank` (дефолт=авто) · ДВУХСЕКЦИОННАЯ редакторская инъекция (CONFIRMED + ⟨проверить⟩; все строки фолдятся в версию; ⚠ D39.104: схлопывается в ЕДИНЫЙ закон-блок, маркер с провода снимается — внесение = строка 134 ПОСЛЕ пробы 18) · точечная ре-редактура (bank-only → пере-пин $0) · банкнота отдельным шиппинг-файлом · языковой экран `target_script` · дисциплина банкноты (кавычки/агрегация); жанровый словарь ОТМЕНЁН (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 | ✅ ПОСТРОЕН: `checks.ExportNormalize` на каждом финале; санитайзер-классы strip+export | D39.2 · D38.3 | > | 7 Гигиена | ✅ ПОСТРОЕН: реестр role→инъекция · вынос `foldModelWire` · снос сирот/`gatesEnabled` · **target-aware гейты ПО ДАННЫМ — П1 «цель-шов» (фаза 2, D39.64: `isRuTarget` удалён, `TargetActive`/`TargetScriptNonLatin`, токенизатор цели из `word_script`); остаток — ё-фолд/segmentation за подписью (бэклог-строка 14)** | D39.2 · паки 11–16 · **фаза 2 (D39.64)** | diff --git a/docs/architecture/17-seam-inbound-law.md b/docs/architecture/17-seam-inbound-law.md index 5864e5b9..017ed0e7 100644 --- a/docs/architecture/17-seam-inbound-law.md +++ b/docs/architecture/17-seam-inbound-law.md @@ -1,6 +1,6 @@ # 17 — Закон входной двери шва движок ↔ платформа -> **СТАТУС: РАТИФИЦИРОВАНО D39.156 (слово владельца 23.08).** Это НОРМА, ссылаться на неё можно. Собрано оркестратором №19 по консилиуму +> **СТАТУС: РАТИФИЦИРОВАНО D39.156 (слово владельца 23.08).** Это НОРМА, ссылаться на неё можно. ⚠ **ИСПОЛНЕН 27.08: первая дверь по этому закону ПОСТРОЕНА и заленджена** — `tmctl bank-apply`, четыре пака зоны бэкенда, ратификация D39.158 (лендинг `d1eb8a9`). Закон с этого дня читается не как план, а как норма, у которой есть работающий образец; онтология слоя, который дверь обслуживает, — `18-bank-ontology.md`. Собрано оркестратором №19 по консилиуму > со сторонним архитектором 22–23.08.2026. Ратифицирована одной D-нотой; с этого момента двери встают паками без > возврата к владельцу — ровно так, как код выхода `13` встал в ратифицированную полосу 10–19 без > нового решения ВЛАДЕЛЬЦА (D39.131 п.2а → D39.132 п.2г → D39.134: код 13 прошёл через направление и diff --git a/docs/architecture/18-bank-ontology.md b/docs/architecture/18-bank-ontology.md index ea776e1b..5e93055f 100644 --- a/docs/architecture/18-bank-ontology.md +++ b/docs/architecture/18-bank-ontology.md @@ -4,26 +4,33 @@ > при конфликте побеждает журнал решений. Собрано оркестратором №19 по консилиуму со сторонним > архитектором при приёмке четырёх паков входной двери шва. > -> **Зачем страница.** Вокруг одного понятия — «состояние банка этой книги» — живут девять носителей, +> **Зачем страница.** Вокруг одного понятия — «состояние банка этой книги» — живут десять носителей, > выросших пак за паком. Форма при этом СТРОГАЯ: поток однонаправленный, у каждого источника > единственный писатель, вид пересобирается полностью на каждой границе прогона. Но эта строгость не > была записана нигде — каждый файл документировал свою роль, ни одно место не документировало поток. > Из-за этого приёмка сначала прочитала девять носителей как девять заплаток и едва не заказала > рефактор ради стройности. Страница дешевле рефактора и снимает причину, а не симптом. -## Три роли, и они не смешиваются +## Четыре роли, и они не смешиваются | Роль | Носители | Писатель | Момент фиксации | |---|---|---|---| -| **ИСТОЧНИКИ** — то, из чего банк собирается | `glossary_seed` · `.mined-delta.yaml` · `.mined-rejects.yaml` (слово ВЛАДЕЛЬЦА, рядом с `book.yaml`, едут с книгой в бэкапе и экспорте) · `.auto-bank.yaml` (слово ДВИЖКА, сидит при базе) · ruby-чтения в хранилище | у каждого РОВНО один; у трёх первых — `tmctl bank-apply` и рука владельца, у авто-банка — прогон | запись под эксклюзивным флоком проекта | +| **ИСТОЧНИКИ** — то, из чего банк собирается | `glossary_seed` · `.mined-delta.yaml` · `.mined-rejects.yaml` (слово ВЛАДЕЛЬЦА, рядом с `book.yaml`, едут с книгой в бэкапе и экспорте) · `.auto-bank.yaml` (слово ДВИЖКА, сидит при базе) · ruby-чтения в хранилище | у каждого РОВНО один. ⚠ Дверь `bank-apply` пишет ТОЛЬКО два файла решений: правка сид-терма ею ОТКЛОНЯЕТСЯ по имени (`internal/membank/decisions.go:359`), потому что это перенос базового снапшота и пере-оплата черновой волны — другое решение (щель ратифицирована, строка 192). `glossary_seed` — рука владельца · `auto-bank` — прогон · ruby — хранилище | запись двери — под эксклюзивным флоком проекта; рука владельца флока не берёт (остаток — строка 222) | | **ВИД** — банк, каким его видят роли | строки глоссария в хранилище | только `seedGlossary`, и только полным пересбором | каждая граница прогона; один загрузчик, все гарды | -| **ПРОЕКЦИИ** — то, что банк рассказывает наружу | `.bank.json` (единственный канал банка наружу) · карта подписи `.mined-signature.yaml` · человеческая таблица стопа `.bank-stop.txt` | прогон, на границе | после смены вида | +| **ПРОЕКЦИИ** — то, что банк рассказывает наружу | `.bank.json` (единственный канал банка наружу) · карта подписи `.mined-signature.yaml` · человеческая таблица стопа `.bank-stop.txt` | прогон, на границе | ⚠ не одинаков: `bank.json` — ПОСЛЕ смены вида; карта подписи и `.bank-stop.txt` — при вычислении стопа, ДО пере-сида, а на стоп-границе вид вообще не меняется (остановившийся прогон авто-банк не пишет) | | **ПАМЯТЬ ФЛАЖКА** — что стоп уже предъявлял | таблица `bank_stop_presented` | стоп | ПОСЛЕ карты, никогда до | **Поток однонаправленный: источники → вид → проекции.** Отсюда главное свойство, из-за которого рефактор не нужен: **расхождение источников с видом структурно невозможно дольше одной границы прогона**, потому что вид не патчится, а пересобирается целиком. +⚠ **Единственная петля — авто-провод, и она названа здесь нарочно:** прогон пишет собственный ИСТОЧНИК +(`auto-bank`) и пере-сидит вид в той же границе. Это первое, куда ткнёт проверяющий однонаправленности. +Петля реальна и защищена с двух сторон — `unsignedEngineSurfaces` держит границу подписанного, а +`loadAutoBank` отказывает строке со статусом `approved`, — то есть слово движка не может присвоить себе +слово владельца. Однонаправленность формулируется точно так: **петля есть, но она не может изменить +подписанное.** + ## Почему слово владельца и слово движка не сливаются Соблазн понятный: одобрения владельца и авто-строки движка — одинаковые по форме строки банка. Но @@ -48,10 +55,26 @@ карты не существует, то есть молчаливую потерю навсегда. Инвариант, живущий в комментарии, не держится ничем — держит его пин. -⚠ **Класс дефекта, названный этой страницей: проекция без контракта потребителя и без якоря свежести -в самой себе.** Три известных члена: снесённая таблица стопа · банк-экспорт до конверта · порядок -«карта → память» до пина. Четвёртый живёт строкой бэклога: банк-экспорт на стопе ПУСТ — проекция, -наиболее пустая ровно в тот момент, когда её читает экран подписи. +⚠ **КЛАСС A, названный этой страницей: проекция без контракта потребителя и без якоря свежести +в самой себе.** Два известных члена: снесённая таблица стопа · банк-экспорт до конверта. Третий живёт +строкой бэклога 224: банк-экспорт на стопе ПУСТ — проекция, наиболее пустая ровно в тот момент, когда +её читает экран подписи. + +⚠ **КЛАСС B — другой, и путать их нельзя: межносительный инвариант, живущий комментарием без пина.** +Член — порядок «карта → память» до фикс3. Он НЕ подпадает под класс A: там дефект в том, что документ +не несёт контракта, здесь — в том, что связь ДВУХ носителей держится только текстом, который ничего не +проверяет. Разделено 27.08 по вычитке старшего: в одном классе они были бы неподсудны — первый же +судья отклонил бы членство порядка, и вместе с ним ушёл бы весь класс. + +⚠ **Второй ярус КЛАССА A, дописан 27.08 по замечанию платформенной сессии при приёмке +`D39.159`: носитель с ДВУМЯ писателями и НОЛЬ читателей.** Здесь опасность не в отсутствии +потребителя — она в том, что расхождение писателей ничем не судится, пока читателя нет, а в день, +когда читатель появится, разница СТАНЕТ семантикой, и разрешать её придётся задним числом по уже +накопленной истории. Отсутствие потребителя из симптома делается отсрочкой платежа. Живой член — +`books.chunker_version` в платформе (`PD-396`); родня по эту сторону шва — мёртвые поля шва и +`ingest.StatusReport.UnsignedBankTerms`. Отсюда правило таблицы выше — «у каждого источника РОВНО +один писатель» — читается в обе стороны: **два писателя без читателя судятся так же строго, как +проекция без потребителя, и чинятся раньше, а не когда читатель придёт.** ## Чего эта форма НЕ несёт diff --git a/docs/glossary.md b/docs/glossary.md index 1d651845..7024fa9b 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -51,3 +51,21 @@ - **Глава** — единица структуры книги: детект `chunk/ingest.go` (txt: CJK-шапки/form-feed; epub: документ spine), нумерация плотная позиционная + титульная политика `chunk/chunker.go` (`stripHeading`/`ApplyHeading` по `langpacks/*/heading.txt`), идентичность = контент-хеш в манифесте (`pipeline/manifest.go`), деньги ключуются ПОЗИЦИЕЙ (сдвиг границы = перекупка хвоста). Дизайн будущего — `research/27`. - **Деньги платформы:** **холд** — резервация суммы прироста на счёте ДО спавна; **прирост** — купленные главы × ставка ($0.03, `platform/internal/pricing/pricing.go`); **аргумент потолка** — что уходит движку в `--ceiling-usd` = committed книги + прирост (PD-158; ЭТО НЕ ХОЛД — он больше холда на committed); **baseline** — счётчик книги на старте попытки, списание = разница; **settle/расчёт** — закрытие холда по фигуре движка; **leftover-reserved** — висячий reserved движка после краша (зануляется его write-open); **SpendBound** — верхняя граница отложенного расчёта по baseline следующей попытки (PD-159). - **Лексикон экспериментов:** **жилец** — модель на роли; **арм** — вариант в бейк-оффе; **однопроходка/второй проход** — топологии редактуры; **закон-блок** = доктрина инжекта (D39.104); **пол/floor** — нижний якорь шкалы судьи; **Холм** — поправка Холма на множественные сравнения; **порог различимости** — минимальная дельта, отличимая от шума риге. +- **Входная дверь шва** — половина контракта движок↔платформа, которой не было: наружу поток богатый, + внутрь не шло ничего, кроме запуска с флагами. Закон — D39.156 (`17-seam-inbound-law.md`), построена + и заленджена D39.158. +- **`tmctl bank-apply`** — $0-глагол входной двери: кладёт правки банка памяти, сделанные пользователем. + Проекция `--dry-run`, всё-или-ничего, байтовый no-op идемпотентен, лок проекта неблокирующий. +- **Стоп банка** — ФЛАЖОК, а не развилка: движок останавливается на границе майнинга, показывает банк + и ждёт «ОК» = продолжить. Подписывается БАНК целиком либо правится отдельный термин — пер-термной + масс-подписи НЕТ (модель владельца 16.08, D39.144). Предъявленное помнится (`bank_stop_presented`, + схема store v16), поэтому стоп срабатывает ОДИН раз на одно и то же. +- **Полоса отказов [10,19]** — коды выхода движка, отличающие «отказал ЗАКОННО» от «сломался»: + 10 конфиг · 11 источник · 12 лок · 13 схема · 14 решения отклонены · **15 `write_incomplete`** · 19 без имени. +- **Онтология банка** — `architecture/18-bank-ontology.md`: три роли носителей (источники · вид · + проекции · память флага), их писатели и моменты коммита. Названный там класс дефекта — «проекция + без названного потребителя и без якоря свежести в кадре». +- **`ОСПОРЕНО(PD-N)`** — грепаемый токен реестра дефектов платформы: строка стоит `fixed`, но пак + доказал, что её пин доказывает свойство СЛАБЕЕ, чем строка гласит. Статус НЕ меняется, живой пробел + несёт новая строка `PD-N`. Ратифицирован D39.159 при условии двусторонней ссылки. + diff --git a/platform/docs/DEFECT_REGISTER.md b/platform/docs/DEFECT_REGISTER.md index 3c1c31dc..7fc23656 100644 --- a/platform/docs/DEFECT_REGISTER.md +++ b/platform/docs/DEFECT_REGISTER.md @@ -7,7 +7,13 @@ > > Правила: каждая находка любой сессии/приёмки/аудита — строкой сюда ДО закрытия; ID стабилен навсегда; > закрытие — только с коммитом фикса и тестом, пинящим свойство (урок PD-1: свойство без пинящего теста -> считается НЕ закрытым). Класс: `vuln` — эксплуатируемо или ослабляет защиту · `bug` — неверное поведение · +> считается НЕ закрытым). ⚠ **Оговорка к PD-1, ратифицирована D39.159 и дописана 27.08 по вычитке +> старшего:** строка `fixed`, чей пин доказывает МЕНЬШЕ, чем строка гласит, **не пере-открывается**, +> когда описанный ею дефект из кода ушёл, — иначе регистр объявляет вернувшимся баг, который не +> воспроизводится, и считает один пробел двумя открытыми. Живой пробел несёт открытая строка-преемник +> с ДВУСТОРОННЕЙ ссылкой и токеном `ОСПОРЕНО(PD-N)` в оспоренной строке. Пере-открытие — только когда +> дефект ВОСПРОИЗВОДИТСЯ. Без этой оговорки практика и буква правила спорили бы в одном файле, и +> следующая приёмка пере-судила бы спор с нуля. Класс: `vuln` — эксплуатируемо или ослабляет защиту · `bug` — неверное поведение · > `hardening` — защита в глубину / латентное · `doc` — док лжёт о коде · `standards` — расхождение с > объявленной нормой зоны (введены приёмкой P2; словарь отставал от строк — испр. оркестратором №15). Статус: `open` · `fixed()` · `accepted-risk(<кем, когда>)`. @@ -57,7 +63,7 @@ | ID | Класс | Серьёзность | Где | Суть | Статус | Источник | |---|---|---|---|---|---|---| -| PD-398 | standards | info | `docs/scripts/counts.py` `malformed`, строки `PD-99` и `PD-197` этого файла | **Гейт формы регистра ловит только НЕДОСТАЧУ ячеек, а не избыток — и в файле уже есть две строки, которые он пропускает.** `malformed()` сравнивает `if n < shape`, поэтому строка с лишним символом вертикальной черты внутри инлайн-кода проходит молча: счёт ячеек через `awk` с разделителем-чертой даёт `PD-99` (11 ячеек) и `PD-197` (10), а `counts.py --check` печатает `битая форма: []`. Обе строки допаковые, обе сломаны греп-альтернацией и регекспом в тексте. ⚠ Что СМЯГЧАЕТ и почему это `info`, а не выше: `col()` считает колонки С КОНЦА, поэтому статус и вес таких строк читаются ВЕРНО, и сегодняшние числа зоны не врут — пере-проверено, `PD-99` и `PD-197` попадают в свои корзины правильно. Дыра латентная: лишняя черта в одной из ТРЁХ последних колонок сдвинет уже их, и гейт снова промолчит. Пак наткнулся на это ДВАЖДЫ собственной рукой, и второй раз — этой же строкой: первая её редакция цитировала команду счёта ВМЕСТЕ с разделителем-чертой и сломала себя ровно тем, что описывает. моя же правка `PD-396` внесла конвейер в ячейку, счётчик весов выдал мусорный ключ вместо `info`, а `битая форма` осталась пустой — то есть симптом виден в СЧЁТЕ, а не в проверке формы, которая для этого и написана. Лечение однострочное и не моё: `if n != shape` вместо `if n < shape` (зона `docs/`, решение оркестратора). Родня по классу — весь этот пак: гейт, доказывающий меньше, чем читается | open ⚠ **ПЕРЕ-ФОРМУЛИРОВАНА приёмкой (оркестратор №19, 27.08, D39.159 п.8): предложенная правка ОТКЛОНЕНА замером.** `n != shape` краснит СЕМЬ законных строк (пять бэклога, две регистра), потому что колонки читаются С КОНЦА НАМЕРЕННО — `counts.py:132` называет это прямо и приводит ровно этот случай (строка бэклога 127 несёт три слова через вертикальную черту внутри инлайн-кода). Избыток в ПЕРВОЙ содержательной ячейке легален, и числа из-за него не врут: проверено, статус и вес читаются из хвоста, `PD-99` и `PD-197` разбираются верно. **Настоящий остаточный риск — избыток в ХВОСТОВОЙ ячейке:** там лишняя вертикальная черта сдвинула бы именно статус или вес, и тихо. Ловится не счётом ячеек, а сверкой хвостового словаря (статус ∈ {open, fixed, accepted-risk}); сегодня у такой сверки два известных доброкачественных исключения — `PD-59` и `PD-273` несут статус прозой. Строка остаётся ОТКРЫТОЙ под эту формулировку | ревью-пак P8-REVIEW (наткнулся при правке собственной строки, подтверждено редакторским аудитом) | +| PD-398 | standards | info | `docs/scripts/counts.py` `malformed`, строки `PD-99` и `PD-197` этого файла | **Гейт формы регистра ловит только НЕДОСТАЧУ ячеек, а не избыток — и в файле уже есть две строки, которые он пропускает.** `malformed()` сравнивает `if n < shape`, поэтому строка с лишним символом вертикальной черты внутри инлайн-кода проходит молча: счёт ячеек через `awk` с разделителем-чертой даёт `PD-99` (11 ячеек) и `PD-197` (10), а `counts.py --check` печатает `битая форма: []`. Обе строки допаковые, обе сломаны греп-альтернацией и регекспом в тексте. ⚠ Что СМЯГЧАЕТ и почему это `info`, а не выше: `col()` считает колонки С КОНЦА, поэтому статус и вес таких строк читаются ВЕРНО, и сегодняшние числа зоны не врут — пере-проверено, `PD-99` и `PD-197` попадают в свои корзины правильно. Дыра латентная: лишняя черта в одной из ТРЁХ последних колонок сдвинет уже их, и гейт снова промолчит. Пак наткнулся на это ДВАЖДЫ собственной рукой, и второй раз — этой же строкой: первая её редакция цитировала команду счёта ВМЕСТЕ с разделителем-чертой и сломала себя ровно тем, что описывает. моя же правка `PD-396` внесла конвейер в ячейку, счётчик весов выдал мусорный ключ вместо `info`, а `битая форма` осталась пустой — то есть симптом виден в СЧЁТЕ, а не в проверке формы, которая для этого и написана. Лечение однострочное и не моё: `if n != shape` вместо `if n < shape` (зона `docs/`, решение оркестратора). Родня по классу — весь этот пак: гейт, доказывающий меньше, чем читается ⚠ **ПЕРЕ-ФОРМУЛИРОВАНА приёмкой (оркестратор №19, 27.08, D39.159 п.8): предложенная правка ОТКЛОНЕНА замером.** `n != shape` краснит СЕМЬ законных строк (пять бэклога, две регистра), потому что колонки читаются С КОНЦА НАМЕРЕННО — `counts.py:132` называет это прямо и приводит ровно этот случай (строка бэклога 127 несёт три слова через вертикальную черту внутри инлайн-кода). Избыток в ПЕРВОЙ содержательной ячейке легален, и числа из-за него не врут: проверено, статус и вес читаются из хвоста, `PD-99` и `PD-197` разбираются верно. **Настоящий остаточный риск — избыток в ХВОСТОВОЙ ячейке:** там лишняя вертикальная черта сдвинула бы именно статус или вес, и тихо. Ловится не счётом ячеек, а сверкой хвостового словаря (статус ∈ {open, fixed, accepted-risk}); сегодня у такой сверки два известных доброкачественных исключения — `PD-59` и `PD-273` несут статус прозой. Строка остаётся ОТКРЫТОЙ под эту формулировку | open | ревью-пак P8-REVIEW (наткнулся при правке собственной строки, подтверждено редакторским аудитом) | | PD-397 | hardening | info | `internal/pgstore/credits.go:52-53`=`A ledger row is never edited: the correction is another row`, `internal/pgstore/credits.go:354-356`=`The two are never written apart`, миграция `internal/pgstore/migrations/00007_credits.sql` | **Два самых сильных денежных инварианта объявлены ПРОЗОЙ и держатся ТОЛЬКО кодом — схема их не навязывает.** `Adjust` обещает «леджер не правится, коррекция это ещё одна строка, и именно это делает сумму воспроизводимой»; `appendLedger` обещает «кэш и леджер никогда не пишутся врозь, потому что отстающий кэш — это второй ответ про деньги». Проба прямым SQL по стенду показывает, что DDL допускает нарушение обоих: `UPDATE` и `DELETE` строки леджера ПРИНЯТЫ, кэш баланса выставляется ЛОЖЬЮ и ОТРИЦАТЕЛЬНЫМ тоже. Пере-проверено координатором пака независимо от агента — все четыре приняты, и откат пробы сам же оставил расхождение кэша с леджером в 1 микро-доллар, которое поймало только сведение двумя путями, а не база. ⚠ Что схема при этом ДЕРЖИТ и что находкой НЕ является (иначе строка читается как «денежных констрейнтов нет»): знак по каждому виду строки, обязательная нота у коррекции, закрытый словарь видов, непустые `source`/`source_id`, уникальность ключа идемпотентности в пределах аккаунта, положительность сумм резервации, согласованность состояния и времени закрытия, владение книгой через композитный внешний ключ, и переполнение bigint в кэше. То есть DDL закрывает ФОРМУ строки и не закрывает ИСТОРИЮ. Цена названа и она не про сегодняшний код: пути правки леджера в Go нет, поэтому эксплуатации нет — опасны миграция данных, операторский `psql` и будущий инструмент, каждый из которых по построению идёт мимо кода, а прозу в доккомментарии не читает. Лечится либо триггером на `update`/`delete` по `credit_ledger`, либо явной записью «append-only — дисциплина кода, не схемы» рядом с обещанием. ⚠ Заведено ЗАПОЗДАЛО и это отдельный факт: работа была сделана агентом оси 1 по прямому требованию промта («попробуй нарушить каждый прямым SQL; констрейнт, которого нет, это находка»), артефакт `docs/p8-review/axis1-money/constraint-probe.out` лежал в сдаче, а строки не имел — нашёл редакторский аудит полноты. Воспроизведение: `docs/p8-review/axis1-money/constraint-probe.sh` | open | ревью-пак P8-REVIEW, ось 1 (проба агента, пере-проверена координатором; строка заведена по аудиту полноты) | | PD-396 | standards | info | `internal/pgstore/books.go:306`=`chunker_version = $4, parse_started_at = null,`, `internal/pgstore/sink.go:127`=`update books set chunker_version = $2 where id = $1`, `internal/ingest/resync.go:30`=`UnsignedBankTerms int `json:"unsigned_bank_terms"`` | **Мёртвые поля шва: у `books.chunker_version` ДВА писателя и НОЛЬ читателей, и это второй экземпляр класса, первый назвал оркестратор.** Колонку пишет интейк из манифеста движка (`FinishParse`) и пишет тейлер из хендшейка потока (`effect`); ни одного `select` по ней в зоне нет — грепом ноль. Сегодня это безвредно, но следствие названо ЗАРАНЕЕ, потому что оно семантическое, а не техническое: в день, когда читатель появится, РАСХОЖДЕНИЕ двух писателей (чанкер прогона против чанкера разбора) станет значением, и решать, какой из них правда, придётся задним числом — по колонке, у которой уже накоплена история из обоих источников. Родня — п.4 пинга оркестратора №19: `ingest.StatusReport.UnsignedBankTerms` разбирается из ответа движка и не используется НИ ОДНОЙ строкой продакшн-кода (грепом — только объявление и его доккомментарий, где поле описано как опора экрана подписи). Формулировка оркестратора применима дословно к обоим: мёртвое поле в структуре шва читается как контракт. ⚠ Заведено ОТДЕЛЬНОЙ строкой по прямому указанию закрывающего ревью старшей модели и с его же доводом: этой фразе НЕ место в `PD-166` — та строка про потерю значения между двумя автокоммитами, и её свойство построено; держать дефект-строку открытой как плейсхолдер несуществующей фичи есть ровно та патология «open, а лекарство построено», против которой пак завёл шестнадцать дописок. Диспозиция — вопрос владельца, а не зоны: либо назначить владельца колонки (один писатель), либо записать расхождение как ожидаемое до появления читателя Воспроизведение — две команды без конвейера (символ вертикальной черты в ячейку регистра не влезает): `grep -rn chunker_version platform/internal --include=*.go` даёт два `update` и ни одного `select`, `grep -rn UnsignedBankTerms platform/internal platform/cmd --include=*.go` даёт только объявление и его доккомментарий | open | ревью-пак P8-REVIEW (побочная находка сверки реестра, оформлена по указанию закрывающего ревью) | | PD-378 | bug | info | `internal/pgstore/books.go:1053`=`u.RemainingPercent = int(balance * 100 / granted)`, `internal/httpapi/v0.go` `usageState`, канон `14-api-contract` `remaining_percent` | **`/v0/usage` отдаёт `remaining_percent` вне контрактных 0..100 и зажигает предупреждение «low» на полном счёте: `balance * 100` переполняет int64.** Порог измерен точно: баланс 92 233 720 368 547 758 микро ещё даёт 99%, следующий микро-доллар даёт минус 99. Ответ нарушает схему (`minimum: 0`, `maximum: 100`), и хуже того `usageState` видит отрицательное значение ниже порога `lowCredit` и отдаёт `state: "low"` — «денег почти нет» счёту на сто миллиардов. Замерено на проводе: до гранта `{"state":"ok","remaining_percent":96}`, после `grant --usd 100000000000` → `{"state":"low","remaining_percent":-84}`; соседняя арифметика (`pricing.Scale`, `balance`) при том же балансе отвечает верно, то есть переполнение локально именно в этой строке. ⚠ Рефутер сузил minor → info: чтобы туда попасть, оператор должен добавить на счёт не меньше 92.23 млрд долларов, ни одна пользовательская ручка кредит не пишет; прецедент веса — `PD-39`. ⚠ Оговорка рефутера в другую сторону: более правдоподобный носитель — не разовая команда, а конфиг `TM_PLATFORM_SIGNUP_GRANT_USD`, у которого верхней границы нет и значение НАМЕРЕННО не печатается в стартовый лог, так что промах в нём сломал бы `/usage` каждому новому аккаунту невидимо. Воспроизведение: `docs/p8-review/axis1-money/a1-usage-overflow.sh` (сам откатывает грант) | open | ревью-пак P8-REVIEW, ось 1 (живой провод, сужено рефутером с измеренным порогом) |