From 84886d5b344146818841b1e767e6fc032438e810 Mon Sep 17 00:00:00 2001 From: heaven Date: Sat, 5 Sep 2026 14:08:13 +0300 Subject: [PATCH] Accept the platform pack, record the shift's norms as their own note, and take the owner's word that the engine may say how much to add --- docs/PROGRESS.md | 8 +- docs/architecture/05-decisions-index.md | 5 +- docs/architecture/05-decisions-log.md | 95 ++++++++++++++++++++- docs/architecture/14-api-contract/README.md | 2 +- docs/architecture/15-money-path.md | 2 +- 5 files changed, 104 insertions(+), 8 deletions(-) diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index c91cc88c..13b8b470 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1,6 +1,6 @@ # Журнал прогресса -> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-05, голова D39.200 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №22** (единственный носитель — здесь; роль передана 01.09, №21 закрыт нотой передачи D39.184). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ПЕРВЫМ:** (0а) **ревизия документации на ПРОТУХШЕЕ** — по всем зонам, не только тронутым; (0б) **планы доработок в БЭКЕНД и ПЛАТФОРМУ** по остатку бэклога и регистра; (0в) **вынос неактуального в АРХИВ** (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **90** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 107 (major 5), всего рядов 453 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном). +> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-05, голова D39.203 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №22** (единственный носитель — здесь; роль передана 01.09, №21 закрыт нотой передачи D39.184). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ПЕРВЫМ:** (0а) **ревизия документации на ПРОТУХШЕЕ** — по всем зонам, не только тронутым; (0б) **планы доработок в БЭКЕНД и ПЛАТФОРМУ** по остатку бэклога и регистра; (0в) **вынос неактуального в АРХИВ** (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **90** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 107 (major 5), всего рядов 453 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном). > ⛔⛔ **НО КНИГУ ЧЕРЕЗ API НЕЛЬЗЯ ДОВЕСТИ ДО КОНЦА — найдено тем же прогоном 04.09 и воспроизведено дважды.** Прирост потолка на прогон = `chaptersLeft × $0.03` (ставка `DefaultPerChapter`, занижена ×4.47 — D39.179 п.1), и после первой готовой главы это $0.27, тогда как движок при `waves.workers: 4` резервирует под редакторский шаг ~$0.28 ⇒ **отказ резервации, прогон встаёт, книга не двигается**. Два прогона подряд дали одно и то же: `committed` не сдвинулся, холд вернулся. ⇒ **занижение ставки — не «шкала короче», а СТОП ПРОДУКТА: с какого-то момента каждая следующая покупка не покупает ничего.** Лечить придётся согласованно двумя сторонами (ставка платформы + резерв движка под шаг с параллельными воркерами), носитель — ряд регистра зоны, вес `major` **за недостижимость продукта, а не за деньги**: сам стоп НЕ ЖЖЁТ ничего — замер платформы 04.09 показал отмену ДО ухода запроса (латентность 20–31 мс, `model_actual` пуст, токенов ноль), и вся книга сходится до микро-доллара по трём источникам (`request_log` 0.278319 · `spend.committed_usd` 0.278318752 · платформенный `committed` 278319 микро). > ⛔ **ЗАТО HALT НА ПРОГОНЕ, КОТОРЫЙ ПРОДВИНУЛСЯ, ЖЖЁТ — отдельная находка того же прогона и свежая датированная точка строки 78 («леджер = НИЖНЯЯ граница»).** Четыре редакторских вызова ушли параллельно; один дошёл ($0.063404) и своим коммитом сорвал потолок, три других отменены в тот же миг, **отработав 120756, 156469 и 156470 мс** — и легли в леджер нулём. Незаписанное ≈ **$0.05–0.19 на ОДИН halt** против $0.278 за всю книгу, то есть механизм, защищающий от перерасхода, сам создаёт неучтённый расход. ⚠ Списание провайдером НЕ доказано (биллинга у нас нет) — утверждается ровно наблюдаемое: вызовы шли 2–2.6 минуты, их цены в леджере нет. > ⇒ **Дыра НЕ в принципе, а в покрытии, и это делает её заказом, а не наблюдением** (разбор оркестратора 04.09 по коду): консервативная оценка уже построена и ратифицирована — `backend/internal/pipeline/stagerun.go`, греп `settling the reservation estimate to keep the ceiling honest` (pack-13 point-9), — но её условие требует ОТВЕТА провайдера (`resp.Usage`), а отменённый в полёте вызов ответа не получает и уходит другим путём. Направление лечения: сеттлить оценку при отмене вызова, который успел уйти; различитель — факт ухода (латентность кандидат, порог обязан быть обоснован замером, а не назначен). Половина ДВИЖКОВАЯ. ⚠ Прежняя формулировка пункта — ниже, она о состоянии ДО 04.09. Движок пишет файл с 30.08 (`tmctl build` — EPUB 3 + чистый txt, D39.175), и холодный прогон 31.08 предъявил это живьём: собранный EPUB настоящего перевода прошёл `epubcheck` 0/0/0/0 и передан владельцу. ⚠ **ЭТОТ АБЗАЦ ЗАКРЫТ 04.09 и оставлен как история:** дверь выдачи ПОСТРОЕНА и заленджена (`adf5e53`, D39.194), смонтировано **18 операций из 21** (три адреса двери: `createExport`/`getExport`/`downloadExport`), долг `П-17` зона пере-сняла. Прежняя проверка «`grep -rn 'createExport\|getExport' platform/internal/` — пусто» БОЛЬШЕ НЕ ПУСТА и как гейт не работает. Не смонтированы по-прежнему `getRun`, `updateBook`, `deleteBook` — `updateBook` несёт продуктовое следствие, строка бэклога 274 (имя книги в выданном файле не чинится ничем). **Слово «отдаёт результат» ложно по букве ТОЛЬКО платформенной половиной** (п.4) · **(2) ~~холодный прогон движка~~ ОТРАБОТАЛ 31.08, закрыт РЕШЕНИЕМ ВЛАДЕЛЬЦА «не доводить»** (отчёт — `archive/reports/COLDRUN_V16_REPORT_2026-08-31.md`, числа относятся к `065d8ac`). Потрачено **$0.436110** из санкции $0.80. ПРЕДЪЯВЛЕНО живьём: объёмный потолок · D39.158 ОБЕИМИ половинами (стоп и не-стоп) · exit 16 · строки 154/198/239/240. ⚠ ~~**Причина большой доли редактора НЕ УСТАНОВЛЕНА**~~ — **УСТАНОВЛЕНА пробой полигона 02.09 по четырём осям: РЕДАКТОР ДУМАЕТ, А НЕ ПИШЕТ.** Размышление — **77.9%** цены редактора без ручки и 45.7% с `low`; у `glm-5` с выключенным thinking — 0.0%. Замер куплен и закончен ($6.17 из $9.50). ⚠ `research/30` §2.4 того же полигона повторяет «причина не установлена» — это про ДРУГОЙ вопрос (доля роли в прогоне против доли размышления в цене роли), и различение обязано попасть в ревью-шапку 30. Доля редактора идёт с ДВУМЯ знаменателями: **49.3%** от всего прогона ($0.214865 из $0.436110) и **81.3%** внутри порции П1 (⚠ ходившее «73%» — фантом, в отчёте его нет ни разу). Следующий шаг — контролируемая проба по ЧЕТЫРЁМ осям (роли осей и три требования, купленные ошибками этого прогона, — §14 отчёта; ось `stale` НЕДОСТИЖИМА — §10 там же). ⚠ **Цена: слово владельца 31.08 — ≈$0.60, потолок $0.80, ДЕСЯТЬ глав ПОЛНОЙ цепью включая редакторскую волну.** ⚠ **КЛЮЧИ: гейт снят словом владельца 30.08, разбор двух путей — D39.176 п.6.** Живое отсюда: конвенционного `.env` НЕТ НИ У ОДНОЙ книги корпуса (заполнены только `projects/textmachine/backend/.env` и `.../eval/.env`), поэтому прогон движка обязан передавать `--keys-file` абсолютным путём и только на `translate`; для п.3 нужен ДРУГОЙ файл — `TM_PLATFORM_ENGINE_KEYS_PATH`, тоже АБСОЛЮТНЫМ путём (`platform/internal/config/config.go`, греп `must be an absolute path`). Лечение операционное и $0 — направить переменную на уже заполненный файл; проверять ПРОГОНОМ, не чтением (гардрейл `.env`) · **(3) живой сквозной прогон через API платформы** — форма манифеста, сайдкара банка и карта причин флагов сочинены платформой САМОСТОЯТЕЛЬНО. ⚠ Уточнено 01.09: гейт СНЯТ холодным прогоном, и настоящие артефакты движка теперь ЕСТЬ (каталог `books/gu-zhenren/coldrun-v16/`), но сверка вышла ТОНКОЙ: на шов реально вышло **одно значение `flag_reason` из пятнадцати** (`sanitizer_stripped`), остальные четырнадцать по-прежнему только чтением кода, а манифест против `platform/internal/ingest/manifest.go` не сверялся вовсе · **(4) дверь выдачи** — строго ПОСЛЕ (3) (⚠ слово владельца 30.08: дверь ВСЕГДА строит файл — отказ юзеру не отдаётся, неполная книга уходит с пометкой, т.е. дверь зовёт `tmctl build --partial`; отказ по умолчанию остаётся операторской ручкой CLI; D29.1(б) «fail-closed» читать как «никогда молча» — честность несут пометки) · **(5) проводка `--max-units`** — снятие гейта, а не стройка: механизм в движке стоит (D39.170), живое основание гейта — `PD-422` · **(6) разморозка фронта** — ПОСЛЕ (4): зеркало отстало на **ВОСЕМЬ** миноров (`frontend/docs/api-contract/openapi.yaml` — `0.2.3` против канона `0.10.0`, и первый из них — ЛОМАЮЩИЙ 0.3.0), **16 операций против 21**; зеркало последний раз двигали 15.08 — ⚠ ходившее «30.08» было взято из текста коммита, а не из `git log` (испр. 05.09), то есть расхождение старше на две недели; отставание ратифицировано D39.142 п.5 на время фриза и две ручки разъехались, размораживать раньше значит перегенерировать типы дважды. ⚠ **ДВА ЗАГЕЙЧЕННЫХ ТРИГГЕРА НАЖАТЫ И НЕ ОТРАБОТАНЫ — строка бэклога 237** (`go.work` · вендор-сверка эффорта `deepseek-v4-pro`; реестр `13-tech-debt-anchors.md` §Б-108). ⚠ **Состояние git — снимать КОМАНДАМИ, не чтением** (ветка `polygon` ушла вперёд, коммит здесь не цитирую): работа полигона лежит на ней и в `main` НЕ влита, а `git status -- eval/` в main-дереве пуст ПО ПОСТРОЕНИЮ и уводит в ложный вывод «работы нет»; ⚠ **«развязка git с origin» — арбитр ПЕРЕ-СНЯТ 04.09 и прежний вердикт БОЛЬШЕ НЕ ВЕРЕН:** `git merge-base --is-ancestor origin/main main` → **rc 1**, `git log main..origin/main` → `afa7700 temp` (коммит владельца 04.09, которым он передавал работу зон). Локальная линия ушла вперёд на семнадцать коммитов и содержит ВСЁ содержимое `afa7700` — оно снято `reset --soft` и заленджено раздельными зонными коммитами, — но формально ветки разошлись. Лечение — force-push рукой владельца; **не пуллить** (его же правило). ⚠ Пункт «развязка git с origin» в листе владельца ниже держится ТОЛЬКО как вопрос про force-push его рукой; утверждением о расхождении дерева он не является — арбитр этой пары именно здесь. @@ -93,7 +93,7 @@ **7. ⚠ СЕМЬ ПРОТУХШИХ ТОКЕН-ЯКОРЕЙ В `backend/docs/` — и до сегодня их не видел никто.** Корень `backend/docs` в линтере ОТСУТСТВОВАЛ с самого его заведения; добавлен 04.09 (`docs/scripts/counts.py`, -греп `backend/docs` в `lint_anchors`). Сегодня краснеют: `DISCLOSURE_LAW_DESIGN.md:40-44` (пять целей +греп `backend/docs` в `lint_anchors`). ⚠ **Испр. 05.09, ЗАМЕР на чистом дереве: починены ДВА из семи, пять живы** (`DISCLOSURE_LAW_DESIGN.md:40-44` целят в `quality.go` · `status.go` ×3 · `bookbuild.go` — зона бэкенда, пинг в её секции). ⚠ Прежняя редакция этой строки говорила «семь пере-навешены» — **ложь, выведенная из ожидания, а не из прогона линта**; поймана внешним ревьюером. Починенные два не съехали, а УМЕРЛИ: целили в (`DISCLOSURE_LAW_DESIGN.md:264` и `MONEY_HONESTY_REPORT.md:285`) не съехали, а УМЕРЛИ: целили в `chunkrun.go`=`SourceEchoExpected: role == roleTerminologist`, подстроки которой на HEAD нет вовсе — её съел переход на `isBankRole`. Прежняя редакция этой строки перечисляла краснеющие цели и была верна ровно до починки. Прежде краснели: `DISCLOSURE_LAW_DESIGN.md:40-44` (пять целей подряд — `quality.go`, `status.go` ×3, `bookbuild.go`), `:264` и `MONEY_HONESTY_REPORT.md:285` (`chunkrun.go:40`). ⚠ Эррата D15.2 от 02.09 УЖЕ указывала мимо — гейт не мог этого увидеть по построению. Команда: `python3 docs/scripts/counts.py --lint`. @@ -235,7 +235,7 @@ | 197 | **Фикс-лист приёмки пака честности ФЧ-1…ФЧ-8** — восемь пунктов класса «заявленное не прибито» и «текст обещает больше числа», ни один не блокировал лендинг. **Тело списка дословно, с весами и с секцией «что НЕ проверено», — [archive/PROGRESS-2026-08-17.md](archive/PROGRESS-2026-08-17.md), запись приёмки №18** (D39.149). Здесь держим только ратифицированное: **ФЧ-5 РЕШЁН владельцем 17.08 (D39.150): прайсить по модели, которую стадия резолвит СЕЙЧАС, + округление вверх и оговорка в тексте согласия.** Носителя-сессии НЕТ — по слову владельца 17.08 пока только записано | бэкенд | скоро (следующее касание денежного пути; ФЧ-5 обязателен до первого пере-прогона со сменой модели) ⚠ **СОСТАВ ПЕРЕ-СКОУПЛЕН 04.09:** ФЧ-1/ФЧ-4/ФЧ-5 закрыты паком «число согласия» (D39.187); **ФЧ-6 по коду выглядит ЗАКРЫТЫМ** — отказ проекции репортится базисом, отчёт не валится (`backend/internal/pipeline/status.go`, греп `RebillBasisFailed`); **ФЧ-7 отдан проверкой** в вайр-батч (воспроизвести панику поверх пойманного потолка, чинить только если воспроизводится); **ФЧ-2/ФЧ-3/ФЧ-8 НЕ ПРОВЕРЕНЫ никем** и остаются живым остатком строки. | дофикс-промт ЛИБО попутно ближайшим паком | **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) | | 201 | **Движковое «Глава N» доезжает читателю ВНУТРИ текста, обходя дисциплину `heading: null`** (линза шва P7, пере-прочитано оркестратором): `backend/internal/pipeline/export.go:58`=`ApplyHeading` (испр. оркестратором №20 30.08: якорь уезжал с `:230` и с `:266` — код растёт) приклеивает детерминированный порядковый к экспортному тексту первой юнит-главы (`ce.FinalText = chunk.ApplyHeading(...)`), колонка `Source` при этом остаётся heading-stripped. Платформа переносит обе как есть и честно отдаёт `heading: null` — то есть клиент нарисует СВОЙ порядковый на языке своего интерфейса над абзацем, который уже начинается с русского «Глава N», а исходная колонка соответствующего маркера не несёт: пара визуально рассинхронизирована на каждой первой главе. Конкретное следствие открытого К-2 контракта; родня движковой строки **160** (глава без заголовка) | бэкенд | когда-нибудь (с 160) | отдельное решение | приёмка P7 (D39.153) | -| 203 | **Хвосты контракта после синка 0.4.0 — ОТКРЫТ ОДИН ПУНКТ, остальное исполнено** (тела релеев — `platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md` §7; сюда переписан НЕ текст, а статус — один носитель на факт). **ИСПОЛНЕНЫ и сверены грепом при лендинге:** (а) минором 0.5.0 (D39.161) · (б) · (д) · (и) поле `stop_requested` (D39.152) · (л)(м)(н)(о) контрактной сессией — остаток по фразам Приложения А несёт строка **204** и лист владельца. **(г) наполовину** — `unspecified` ратифицирован в каноне (`docs/architecture/14-api-contract/openapi.yaml:1975`=`is reserved and is the server`), открыта только ГРАНИЦА ступеней замечаний (две ступени при девяти рангах движка); ⚠ её прежний носитель — строка 148 — СНЯТ 21.08 по слову владельца. **(з) СНЯТ как ратификация 22.08 сквозной трассировкой (разбор, на который ссылается строка 191):** конфликта моделей НЕТ — гейт движка проверяет полноту не ПОДПИСЕЙ, а ФАЙЛА решений, и ОДИН файл со всем банком его снимает (`loadMinedDelta` штампует только `Source`, `mining.go:855-867` → опущенный статус дефолтится в `approved`, `membank/memseed.go:96`=`status = "approved"` → `unsignedEngineSurfaces` выбрасывает лишь `Source=="mined" && Status!="approved"`, `mining.go:710-719`). Пере-диспозиция 27.08 (D39.158 п.7): движковый гейт полноты УСТРАНЁН, не обойдён; остаётся снять обход в платформе — одна боевая строка (`platform/internal/runs/spawn.go`, `--verify-bank` не передаётся на resume) плюс семь строк ставшего ложным обоснования, работа платформенного пака. **ОТКРЫТО РЕАЛЬНО — (к):** ключ `project_db` в `book.yaml`, договорить, кто им владеет: шаблон оператора его не содержит, движок делает необязательным. ⚠ Следствие «на штатном деплое банк не читается» ОПРОВЕРГНУТО живым кодом: путь публикует ДВИЖОК конвертом артефактов, пустой путь — ГРОМКИЙ отказ (`platform/internal/runner/artifacts.go:39`=`the engine published no bank read-out path`); остаток (к) чисто договорной| контракт/доки | скоро (следующее касание контракта) | контрактная сессия | приёмка P7 (D39.153) | +| 203 | **Хвосты контракта после синка 0.4.0 — ОТКРЫТ ОДИН ПУНКТ, остальное исполнено** (тела релеев — `platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md` §7; сюда переписан НЕ текст, а статус — один носитель на факт). **ИСПОЛНЕНЫ и сверены грепом при лендинге:** (а) минором 0.5.0 (D39.161) · (б) · (д) · (и) поле `stop_requested` (D39.152) · (л)(м)(н)(о) контрактной сессией — остаток по фразам Приложения А несёт строка **204** и лист владельца. **(г) наполовину** — `unspecified` ратифицирован в каноне (`docs/architecture/14-api-contract/openapi.yaml:1975`=`is reserved and is the server`), открыта только ГРАНИЦА ступеней замечаний (две ступени при девяти рангах движка); ⚠ её прежний носитель — строка 148 — СНЯТ 21.08 по слову владельца. **(з) СНЯТ как ратификация 22.08 сквозной трассировкой (разбор, на который ссылается строка 191):** конфликта моделей НЕТ — гейт движка проверяет полноту не ПОДПИСЕЙ, а ФАЙЛА решений, и ОДИН файл со всем банком его снимает (`loadMinedDelta` штампует только `Source`, `mining.go:855-867` → опущенный статус дефолтится в `approved`, `membank/memseed.go:130`=`status = "approved"` → `unsignedEngineSurfaces` выбрасывает лишь `Source=="mined" && Status!="approved"`, `mining.go:710-719`). Пере-диспозиция 27.08 (D39.158 п.7): движковый гейт полноты УСТРАНЁН, не обойдён; остаётся снять обход в платформе — одна боевая строка (`platform/internal/runs/spawn.go`, `--verify-bank` не передаётся на resume) плюс семь строк ставшего ложным обоснования, работа платформенного пака. **ОТКРЫТО РЕАЛЬНО — (к):** ключ `project_db` в `book.yaml`, договорить, кто им владеет: шаблон оператора его не содержит, движок делает необязательным. ⚠ Следствие «на штатном деплое банк не читается» ОПРОВЕРГНУТО живым кодом: путь публикует ДВИЖОК конвертом артефактов, пустой путь — ГРОМКИЙ отказ (`platform/internal/runner/artifacts.go:39`=`the engine published no bank read-out path`); остаток (к) чисто договорной| контракт/доки | скоро (следующее касание контракта) | контрактная сессия | приёмка P7 (D39.153) | | 204 | **Движок не публикует причины флагов ДАННЫМИ — карта причин у платформы рукописная и расходится молча** (релей §7(в) хендоффа P7; зона платформы в чужой бэклог не пишет и просила строку сюда — `platform/docs/archive/platform-PROGRESS-P7.md:206`). Сегодня `platform/internal/ingest/notes.go` держит рукописную копию закрытого словаря ЧУЖОЙ зоны: причин движка **16** (испр. 05.09 — шестнадцатой приехала `off_target_lang` → `wrong_language` с минором 0.10.0; прежняя редакция говорила 15 и это ровно то расхождение, которое строка предсказывала) → коды замечаний контракта. Импортировать движок платформе нельзя (D39.85 — разные модули), поэтому расхождение появится молча в тот день, когда движок добавит причину: платформа отдаст `unspecified` и напишет ERROR в лог, то есть деградация честная, но карта протухнет незаметно. ⚠ **Правила заполнения контрактных фраз по этим причинам УЖЕ НАПИСАНЫ** и выводить их заново не надо — компаньон `14-api-contract/README.md`, греп `по ДОККОММЕНТУ` (фраза пишется по доккомменту `disposition.go`, а не по имени константы; класс 2 схлопывается в ОДИН код). Лечение: движок публикует свой словарь причин артефактом-данными (тем же классом, что манифест и сайдкары банка), платформа читает его вместо копии. ⚠ Класс: носителем был ТОЛЬКО регистр платформы (PD-246) — обязательство к ЧУЖОЙ зоне жило внутри зоны автора | бэкенд | когда-нибудь (со следующим касанием эмиссии флагов) | отдельное решение | приёмка P7 (D39.153), релей §7(в) | | 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` | | 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 | @@ -335,7 +335,7 @@ | 275 | ⚠ **ПЕРЕ-СНЯТА 05.09 СЛОВОМ ВЛАДЕЛЬЦА — прежняя формулировка «КАЧЕСТВО НЕ МОЖЕТ СТАРТОВАТЬ ВООБЩЕ» БЫЛА НЕВЕРНА (ошибка оркестратора).** Она называла четыре гейта; из них **три сняты словом владельца, четвёртый гейтом не был** (разбор — `D39.198`): **13а** веса рубрики НЕ НУЖНЫ — веса меряют РАЗНЫЕ узлы (консистентность — банк памяти, выдумки — редактор, художественность — редактор И черновик), единого веса быть не может, а одно взвешенное число измеряет, но не ДИАГНОСТИРУЕТ · **24** профили голоса — приоритет №3, подписи владельца не требуют («зачем мне это подписывать») · **149** разморозка панели не нужна: пока идут доработки, менять жильцов ролей незачем · **55** прогоняем на ТЕХ ЖЕ текстах (мастер Гу, Жан-Кристоф), оговорка про претрейн остаётся ОГРАНИЧЕНИЕМ ВЫВОДА, а не требованием новой книги. **ЧТО ОСТАЁТСЯ ЖИВЫМ В ЭТОЙ СТРОКЕ:** привязать каждый класс дефекта к УЗЛУ и мерить по узлам; поставить порог остановки для приоритетов №1 и №2 (разъехавшийся термин — стоп, выдумка — стоп, сухая фраза — не стоп). ⚠ Консистентность проверяема ДЕТЕРМИНИРОВАННО за $0 — это ГЕЙТ, а не строка рубрики; ближайший её носитель — строка **267** | полигон+бэкенд | скоро | привязка классов к узлам + пороги остановки для №1 и №2 | решение владельца 05.09, `D39.198` | | 276 | ⭐ **ФОРМА ЗАКАЗА ПЕРЕВОДА — решение владельца 05.09, ратифицировано D39.196.** На аккаунте **баланс в ДЕНЬГАХ** и он виден. Человек стартует перевод; хочет задать потолок — тянет ползунок **в деньгах**; рядом ответ на его настоящий вопрос: **хватает на полную книгу или нет**. Хочет задать объёмом — второй ползунок: **ГЛАВЫ когда книга распозналась**, **ЗНАКИ когда нет** (о нераспознавании говорим ЯВНО). Старт = **холд с баланса**. Отдельная кнопка **«перевести книгу целиком»** — за целую книгу оценка точнее, чем за произвольный кусок. ⚠ **Две ручки связаны проекцией:** объём — ЗАКАЗ (главная), деньги — ЛИМИТ (вторая); тронутая последней ограничивает, другая пересчитывается и показывается производной («5 глав ≈ $0.55, зарезервируем до $0.8»); минимум денежного ползунка = неделимый шаг движка, чтобы ползунком нельзя было выразить невозможный заказ. Не тронута ни одна ⇒ заказ = вся книга. ⚠ **Цифра на экране — ПАРА, а не точка:** «ожидаемо ≈ $1.1, зарезервируем до $1.6, спишем по факту», и закрытие фактом после прогона — проекция имеет дисперсию (редакторские вызовы 25–90 % worst-case), точечное число превратится в «сказали 1.14, списали 1.31». Главный ответ — фраза, цифра рядом и мельче. ⚠ **Запрет «денег в UI» ОТОЗВАН владельцем** (D39.196 п.2): виден баланс, потолок, холд; цены моделей и стадий — нет. «Страница» из контракта убирается совсем — владелец назвал ЗНАКИ, форматирование «≈67 стр.» это локаль фронта. Исполнение — 277–283 | движок+платформа+фронт | скоро | решение принято и ратифицировано D39.196; исполняется 277–283 | решение владельца 05.09 + сведение с внешним рецензентом | | 277 | **ШАГ 1, ГЕЙТ ДЛЯ ВСЕГО ОСТАЛЬНОГО: отказ резервации = «не начинать новое», а не авария книги.** Сегодня отказ ОДНОГО из N параллельных резервов зовёт `cancel()` производного контекста и гасит уже допущенные вызовы (`waverun.go`, общий путь ошибки; `stagerun.go`, ветка `ReserveDeniedBook`). Чинится: защёлка на уровне кормильца (перестать раздавать индексы по первому отказу), доработка допущенного, штатное завершение с леджером; при непустом полёте — ждать settle, потому что settle ≤ оценки и запас растёт; поздняя отмена сеттлится ОЦЕНКОЙ по уже ратифицированному пути. ⚠ Без этого ЛЮБОЙ заказ упирается в ту же стену, поэтому строка идёт ПЕРВОЙ. Прецедент внутри пакета: объёмный потолок уже построен так («THE STOP IS A COMPLETION, NOT A HALT», `backend/internal/pipeline/volume.go`), и три места уже деградируют по `errReserveCeiling` вместо аборта. ⚠ **НЕ закрывает `PD-441` целиком — испр. 05.09 по ревью:** эта строка снимает ОТМЕНУ допущенного, но структурный корень невидимого расхода живёт в строке **261**, а сверх-утверждение комментария — в **262**. `PD-441` закрывается составом 277+261+262, брать ОДНИМ паком. Пять рисков реализации названы зоной — в промт дословно | бэкенд | скоро | защёлка + отказ гасить допущенное + посадка «под потолком волна погасила допущенный вызов» красной | разбор зоны движка + старшей модели 04.09 | -| 278 | **ШАГ 2: движок публикует ПРОЕКЦИЮ ЦЕНЫ и говорит, сколько не хватило.** В `manifest --json` и `status --json` — `book_once_usd` (книжное слагаемое, терминология: расход КНИГИ, а не юнита — иначе короткая книга получит заниженную оценку там, где ей больнее), по главам `expected_usd`, и `step_max_usd` (самый крупный неделимый резерв). Считается из СВОИХ цен и фертильности, БЕЗ константы. Плюс `shortfall_micro_usd` в событие `ceiling` — сегодня «сколько не хватило» умирает в stderr транзиентного юнита, хотя движок это число печатает. ⚠ **Имя поля пере-снято 05.09 под построенное:** строка заказывала `next_step_micro_usd`, но это имя называет ЦЕНУ ОДНОГО ВЫЗОВА — ровно тот класс числа, который следующая же фраза строки запрещает как ответ на «сколько не хватило»; носитель обязан называть построенное. Плюс денежный леджер в `finished` по образцу объёмного. ⚠ Всё аддитивно, версию манифеста не двигает. Даёт форму строке **166**. ⚠ **ВТОРАЯ РЕДАКЦИЯ (04.09): манифест получает по ЮНИТУ `source_chars` (руны ингестированного текста, ВКЛЮЧАЯ пробелы — так, чтобы человек мог пересчитать в редакторе) плюс раздельно ПЛОТНЫЕ/РАЗРЕЖЕННЫЕ знаки** (движок уже умеет: `backend/internal/text/runes.go`, Han/кана/хангыль ≈ 1 токен, прочее ≈ ⅓), **и `structure: detected|declared|none`** — признак доверия к нарезке, без которого вид заказа `through_chapter` предлагать нельзя. Всё аддитивно, версию манифеста не двигает | бэкенд | скоро | три аддитивных поля + событие | согласование 04.09; требование «отказ обязан называть недостающую сумму» — зона платформы | +| 278 | **ШАГ 2: движок публикует ПРОЕКЦИЮ ЦЕНЫ и говорит, сколько не хватило.** В `manifest --json` и `status --json` — `book_once_usd` (книжное слагаемое, терминология: расход КНИГИ, а не юнита — иначе короткая книга получит заниженную оценку там, где ей больнее), по главам `expected_usd`, и `step_max_usd` (самый крупный неделимый резерв). Считается из СВОИХ цен и фертильности, БЕЗ константы. Плюс `shortfall_micro_usd` в событие `ceiling` — сегодня «сколько не хватило» умирает в stderr транзиентного юнита, хотя движок это число печатает. ⚠ **Имя поля пере-снято 05.09 под построенное `shortfall_micro_usd`.** Довод переименования был: `next_step_micro_usd` называет ЦЕНУ ОДНОГО ВЫЗОВА — запрещённый класс. ⛔ **Испр. 05.09 приёмкой: довод оказался НЕПОЛОН, и это дефект оркестратора.** Недостача ПОЗВОЛЯЕТ восстановить оценку отказанного вызова — `estimate = shortfall + ceiling − committed − reserved`, где потолок потребитель поставил сам, а `committed` идёт кадром `spend`; на обычном терминальном отказе `reserved` пуст по построению. Предъявлено живым прогоном верификатора: `shortfall + ceiling` сошлось с собственным текстом отказа движка до микродоллара. ⚠ И вторая половина довода («цена вызова и так выходит через `step_max_usd`») ЛОЖНА: `git grep 'StepMax|step_max' HEAD -- platform/` пусто, в `runevents` его нет ⇒ **недостача сегодня — ЕДИНСТВЕННЫЙ канал поцикловой цены на проводе.** Переименование при этом остаётся верным: имя обязано называть построенное. Раскрытие принято ОСОЗНАННО как исключение на внутреннем проводе; граница — путь `settleCannotHelp` с непустым небом, где `reserved` неизвестен и выходит граница, а не цена. Плюс денежный леджер в `finished` по образцу объёмного. ⚠ Всё аддитивно, версию манифеста не двигает. Даёт форму строке **166**. ⚠ **ВТОРАЯ РЕДАКЦИЯ (04.09): манифест получает по ЮНИТУ `source_chars` (руны ингестированного текста, ВКЛЮЧАЯ пробелы — так, чтобы человек мог пересчитать в редакторе) плюс раздельно ПЛОТНЫЕ/РАЗРЕЖЕННЫЕ знаки** (движок уже умеет: `backend/internal/text/runes.go`, Han/кана/хангыль ≈ 1 токен, прочее ≈ ⅓), **и `structure: detected|declared|none`** — признак доверия к нарезке, без которого вид заказа `through_chapter` предлагать нельзя. Всё аддитивно, версию манифеста не двигает | бэкенд | скоро | три аддитивных поля + событие | согласование 04.09; требование «отказ обязан называть недостающую сумму» — зона платформы | | 279 | **ШАГ 3, КОНТРАКТ (минор, полоса ломающих правок мажора 0 законна): заказ объёмом.** `ceiling_chapters` → `chapters` со смыслом ОБЪЁМА (`null` = вся книга) · `CeilingBounds{min,max,default}` → `OrderOptions{chapters_left, affordable_chapters, verdict: covers_all|covers_part|covers_none, blocked}` · `PausedReason` += `run_limit_reached` (и `credit_exhausted` СТАНОВИТСЯ правдой — сегодня лжёт при 34% баланса, наблюдение H14) · `Run` += `ordered_chapters`/`delivered_chapters`. ⚠ Фразы — ДАННЫМИ по коду причины и локали, не литералом. ⚠ **ПЕРЕ-СНЯТО 05.09 под D39.196:** наружу ВЫХОДЯТ баланс, потолок заказа и холд — деньгами; контракт получает `balance`, `estimate{expected, hold}` под выбранный заказ, границы денежного ползунка `{min: неделимый шаг, max: баланс}` и `shortfall` у паузы; процент остатка остаётся СИГНАЛОМ «мало/пусто». НЕ выходят: цены моделей, стоимость стадий и вызовов, структура НАШИХ расходов. Плюс `OrderOptions` обязан нести ТРИ вида заказа (вся книга · знаки · до главы N), а не только главы. Ремедиа-таблица `resumeRun` упрощается: «Продолжить» — новый прогон ТОГО ЖЕ заказа | контракт | скоро | минор контракта одним актом с платформенной половиной | согласование 04.09 | | 280 | **ШАГ 4, ПЛАТФОРМА: заказ живёт на КНИГЕ, холд считается от проекции, `--max-units` наконец едет в argv.** `books.ordered_through_chapter` (null = вся книга) — сегодня интент живёт на ПРОГОНЕ, оттого и неловкость «поднять лимит = НОВЫЙ прогон с бо́льшим `ceiling_chapters`»; с заказом на книге «Продолжить» после пополнения тривиален, и дверь пере-прохода встаёт рядом вторым типом заказа. Холд = `min(баланс, проекция × k)`; `bookCap = committed + холд` (`PD-158` цел). В argv добавляется `--max-units` = сумма юнитов заказанных глав минус доставленные. Удаляются `DefaultPerChapter`, `Scale` и `draftWork`: план движка становится РАВЕН заказу, и «черновики на весь потолок при нуле готовых глав» исчезает структурно. Закрывает `PD-410`, `PD-440`, `PD-446`. ⚠ **ПРЕДУСЛОВИЕ, названное отдельно: `PD-422`** — `--resnapshot` передаётся УСЛОВНО, а условие ставит только правка банка; рост авто-банка от майнинга флага не ставит. Частичный заказ на майнящей книге растит банк ⇒ следующий заказ без `--resnapshot` умрёт на снапшот-гарде, холд взят, попытка сожжена. Взять В ТОТ ЖЕ пак | платформа | скоро | заказ на книге + холд от проекции + проводка `--max-units` + PD-422 | согласование 04.09 | | 281 | **ЗАМЕР: сколько наших денег стоит тысяча ПЛОТНЫХ и тысяча РАЗРЕЖЕННЫХ знаков по стадиям и парам.** Цель — НЕ константа (D39.165 п.1г прямо запрещает ставку константой), а **дисперсия проекции**: коэффициент k для холда и честность вердикта «хватит ли». Снимать есть из чего и это $0: `find -L books` даёт 162 базы прогонов, в `request_log` лежат `chapter · chunk_idx · stage · prompt/completion_tokens · cost_usd · tm_hit`, исходник по юниту отдаёт `tmctl export --pairs` бесплатно. Метод: по юниту плотные/разреженные знаки против суммы `cost_usd` при `tm_hit=0`, по стадиям; хвост ретраев и эскалаций считать ОТДЕЛЬНО; базы до 16.08 пере-прайсить по токенам. ⚠ Гейт для коэффициента холда в строке 280 | полигон | скоро (гейт строки 280) | замер по 162 базам, $0 | согласование 04.09, вторая редакция | diff --git a/docs/architecture/05-decisions-index.md b/docs/architecture/05-decisions-index.md index 61ef19b8..a91d7763 100644 --- a/docs/architecture/05-decisions-index.md +++ b/docs/architecture/05-decisions-index.md @@ -1,4 +1,4 @@ -# Реестр D-нот — карта актуальности v2 (D1–D39.200; титул — носитель головы, бампать при каждом аппенде) +# Реестр D-нот — карта актуальности v2 (D1–D39.203; титул — носитель головы, бампать при каждом аппенде) > ⚠ **Колонку «тело» `counts.py --check` НЕ сторожит по устройству:** он сверяет полноту НОМЕРОВ, а не > место тела, поэтому колонка держится дисциплиной лендинга. Не нашёл тело по колонке — иди в слайсы, @@ -259,3 +259,6 @@ | D39.198 | 05.09 | **Приоритет качества назван владельцем: (1) 100% консистентность, никаких разнопереведённых терминов · (2) никаких выдуманных сюжетных линий и смыслов · (3) художественность.** ⛔ Тем же словом СНЯТ вопрос «веса рубрики K1–K12»: веса меряют РАЗНЫЕ УЗЛЫ (консистентность — банк памяти; выдумки — редактор; художественность — редактор И черновик), поэтому единого веса быть не может, а одно взвешенное число измеряет, но НЕ ДИАГНОСТИРУЕТ — чинить надо узел. Мерить по узлам; приоритет говорит, чей отказ ОСТАНАВЛИВАЕТ выпуск. ⚠ Консистентность проверяема детерминированно за $0 — это ГЕЙТ, не строка рубрики. Сняты как блокеры «добора идеала»: 13а · 24 · 149 · 55 ⇒ формулировка оркестратора «качество не может стартовать» была НЕВЕРНА, строка 275 пере-снята | жив | ЖИВОЕ: метрика, сливающая узлы в одно число, измеряет, но не диагностирует | качество приоритет рубрика узлы банк редактор черновик | | D39.199 | 05.09 | **АКТ: движковый пак ПРИНЯТ С ДОФИКСОМ, приёмочный коммит `74ffe4f`; эррата 04.09-д «пак НЕ принят» ИСЧЕРПАНА.** Каталог 158 — 156 RED · 0 SURVIVED · 0 ROTTED · 2 `survives`; батарея чистая, тесты 1173→1178. Приёмка по обязательным нормам: дерево названо КОММИТОМ, №1 судил дифф НЕ ЧИТАЯ отчёт (вердикт «достигает»), №2 искал ВНЕ карты. ⚠ Граница: №2 прогнал 5 записей из 19, остальные на слово. Находки вне карты — строки 287·288·289·290; закрыта 264. ⚠ Остаток зоны: последние шесть починок чужими глазами НЕ ревьюились | жив | ЖИВОЕ: ось с читателем и писателем прибивается С ОБЕИХ сторон — «прибил читателя» без писателя не находка, а половина | приёмка движок акт мутации верификаторы | | D39.200 | 05.09 | **Вторая ревизия документации: десять строк бэклога закрыты (169·191·202·216·234·105·13а·149·167·205, счёт 220→210), снесены ТРИ фантомных противоречия, восстановлена норма языка кода, которую снесла первая ревизия; батч Б16 был закрыт при красной собственной приёмке; отзыв запрета «денег в UI» доехал прозой до всех носителей** | ратифицировано | живое тело | доки · бэклог · контракт · ревизия | +| D39.201 | 05.09 | **АКТ: платформенный пак «пустить внутрь можно» ПРИНЯТ С ДОФИКСОМ** (`7e2226a` + `3e84716`; 20 находок, 5 снято, 15 устояло, 5 major — ТРИ в самом механизме бэкапа: книга в интейке не попадала в точку ни одним файлом при `complete: true`, ложная посылка дискриминатора вешала гейдж возраста в `+Inf` навсегда, рецепт восстановления не останавливает прогоны). Ратификации прозой: `bearerToken` минтится вне полосы, `character_count` — признак точности рядом с числом. Строки 269 · 270 · 274 · 282 закрыты; заведена 293 | ратифицировано | живое тело | приёмка · платформа · бэкап · контракт | +| D39.202 | 05.09 | **НОРМЫ СМЕНЫ: один корень у восьми случаев — прибор отвечает на свой вопрос, а читают его как ответ на свой.** Четыре вида неразличимого зелёного отчёта · девять форм ложной зелени (включая ГЕЙТ, ЗАЩИЩАЮЩИЙ ДЕФЕКТ) · правила посадок, указателей, замеров · «замер, попавший в посылку заказа, — стоп и вопрос владельцу» · норма о нормах: правило, требующее бдительности, исполняется настолько, насколько она есть | ратифицировано | живое тело | нормы · приёмка · инструменты | +| D39.203 | 05.09 | **Владелец: движку МОЖНО говорить «сколько добавить»** — раскрытие оценки отказанного вызова легитимно (стена и есть цена вызова, формой не обходится). ⚠ Запрет ПТ-35 на СТОИМОСТЬ цел: наружу идёт до-вызовная ОЦЕНКА, не сеттлмент. Тензия жила внутри заказа (п.3 против п.4: `step_max_usd` — тоже оценка вызова). Создаёт работу: величина `max(shortfall, step_max − headroom)` + ПЕРЕИМЕНОВАНИЕ поля + минор `StreamVersion` 1.3→1.4; пак лендится как построен | ратифицировано | живое тело | деньги · шов · контракт · продукт | diff --git a/docs/architecture/05-decisions-log.md b/docs/architecture/05-decisions-log.md index d9198073..c54c170e 100644 --- a/docs/architecture/05-decisions-log.md +++ b/docs/architecture/05-decisions-log.md @@ -1,4 +1,4 @@ -# Журнал решений оркестратора — контракт D1–D39.200 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`) +# Журнал решений оркестратора — контракт D1–D39.203 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`) > **⟶ КАРТА АКТУАЛЬНОСТИ (ревизия D31, продлена до D38.2 [12.07]; исторические записи ниже НЕ переписываются — дисциплина D23.3).** Работая с контрактом (греп номера: живой файл → слайсы, целиком НЕ читать — D39.125), держи под рукой, что чем перекрыто: > ⚠ **Эррата 09.08 (D39.125):** D39.111 п.1 предписывал промту S3 «максимум = баланс МИНУС открытые холды» — формула ОШИБОЧНА (вычитание дважды), исправлена D39.115 п.2(а): максимум = Balance КАК ЕСТЬ; тело D39.111 — в слайсе `../archive/architecture/05-decisions-D39-106-123.md` (испр. 05.09: прежнее «живёт ниже в этом файле» протухло подрезкой D39.139) (голова D39.106+). @@ -2353,3 +2353,96 @@ Gemini как построенный (его в `internal/llm/` нет) · дв бестокенных якорей не сверялись, и линт их тоже не судит. ⇒ **класс «строка посылает строить построенное» среди непроверенных строк мог остаться, и следующая ревизия начинает с них** (05.09.2026, оркестратор №22). ✅ + +## D39.201 — АКТ ПРИЁМКИ ПЛАТФОРМЕННОГО ПАКА «ПУСТИТЬ ВНУТРЬ МОЖНО»: ПРИНЯТ С ДОФИКСОМ, `7e2226a` + `3e84716`; три дыры были в самом бэкапе (05.09). ✅ + +**1. Пак ПРИНЯТ.** Лендинги: приёмочный **`7e2226a`** (32 файла, +5307/−72) · дофикс **`3e84716`** (10 файлов, +427/−95) · зонный журнал `4230b5e`; последним касанием — секция норм в зонном журнале (+95/−4), заказанная оркестратором после того, как зона предъявила: её же залёнженный журнал учил проверять якоря НЕГОДНЫМ способом — фильтром по своей зоне. Приёмка по обязательным нормам: два верификатора с асимметричными мандатами (№1 судил ЗАКАЗ и ДИФФ, НЕ ЧИТАЯ отчёт; №2 строил карту отчёта и искал ВНЕ неё), каждая находка атакована скептиком с заданием СНЯТЬ её. **20 находок · 5 снято с предъявленным основанием · 15 устояло · 5 major.** + +**2. ⛔ ТРИ ИЗ ПЯТИ MAJOR — В САМОМ МЕХАНИЗМЕ БЭКАПА, то есть в том, ради чего пак и заказывался.** (а) Книга, не дошедшая до реза, не попадала в точку НИ ОДНИМ файлом — включая загруженный пользователем исходник, который сам пакет называет незаменимым, — **а точка при этом `complete: true`**; статус `parsing` при сломанном шаблоне книги не терминален НИКОГДА, то есть окно не минутное. (б) Дискриминатор стоял на ложной посылке «каталог отвергнутой книги уходит вместе с исходником»: каталог сносится лишь при одной причине из двух, книга проваливается между ветвями, и **гейдж возраста бэкапа залипает в `+Inf` навсегда** — единственный сигнал настоящей потери обесценивается, а защита `prune` «не выбрасывать самую свежую ПОЛНУЮ точку» становится холостой. (в) Рецепт восстановления гасит демона, но **прогоны демону НЕ дети** (транзиентные юниты, переживающие деплой по построению — тот же документ объясняет это семьюстами строками выше), и `mv` подсунул бы файл под открытым дескриптором живого оплаченного прогона; PLAUSIBLE — живым прогоном не воспроизведено, шаг добавлен, но на стенде зоны неисполним (`Failed to connect to bus`), проверит только боевой хост. + +**3. Четвёртый major — «поправил читателя, оставил писателя» на паре писателей ОДНОЙ колонки.** Гард на управляющие символы в титуле получила только дверь переименования; интейк шёл мимо, `NUL` давал 500 и ERROR-строку в журнале за ошибку вызывающего. **Отчёт называл этот дефект починенным.** Починка развела поведение осознанно: интейк ЧИСТИТ (тело мультипарта уже принято, ронять десятки мегабайт из-за одной руны нельзя), дверь переименования по-прежнему ОТКАЗЫВАЕТ с указателем на `/title`. + +**4. Пятый major нашли ОБА верификатора независимо: абзац рантбука о «тихом денежном» симптоме ИНВЕРТИРОВАН.** Потерянный КЛЮЧ `mining.contrast_path` валит загрузку конфига ГРОМКО на всех путях, включая $0-читающие (`backend/internal/config/pipeline.go`, греп `mining.contrast_path` is not set — `bad()` фатален); тихо ломается отсутствие ФАЙЛА. Проверено оркестратором самостоятельно и живым прогоном верификатора (ключ убран → `exit 10`, манифеста нет вовсе; файл убран → `exit 0`, `book_once_usd: 2`). Заменено ТАБЛИЦЕЙ ЗАМЕРА, а не новой прозой. ⚠ Зона написала оркестратору «симптом пере-проверен по коду с замером обеих веток» — померена была одна; она же назвала это своим классом «записано точнее, чем проверено». + +**5. Ратификации канона — ПРОЗОЙ, схема впереди кода не едет** (дефект, за который в тот же день написана эррата 04.09-в). (а) `bearerToken`: абзац «Nothing issues such a token today» стал ложным в момент лендинга — токен минтится ВНЕ ПОЛОСЫ оператором (`tmplatformctl token issue`), сессия обычная, гасится `POST /auth/logout`; **эндпоинта для выдачи нет и по умолчанию не будет — дверь, выдающая себе учётные данные по HTTP, есть другая модель угроз и ратифицируется отдельно**, иначе следующий прочитает его отсутствие как недоделку. Компаньон приведён к канону тем же актом (сутки они противоречили друг другу). (б) `character_count`: ратифицирована форма **признак точности РЯДОМ с числом**, а НЕ смена семантики `null` — довод решающий: `null` уже значит «ещё грузится», живой клиент рисует его как `0`, перегрузка сломала бы развёрнутого читателя. Число точно только для UTF-8 текста: для EPUB это руны ZIP-КОНТЕЙНЕРА. **Поле приезжает с паком формы заказа**, который и делает это число ЦЕНОЙ. + +**6. ⚠ ЧЕСТНЫЙ ОСТАТОК ПРИЁМКИ, названный поимённо.** Из тринадцати починок дофикса оркестратор проверил ТРИ (разделение копирования и дискриминатора · таблица замера в рантбуке · пересылка DSN целиком минус пароль); **остальные десять приняты по пинам зоны и зелёным гейтам и оркестратором НЕ ПРОВЕРЕНЫ** — это записано так, а не «всё подтверждено», ровно потому что смена дважды заплатила за обобщение выборки. Восстановление рехёрсено зоной пять раз, последний — на дофикснутом коде с настоящим `drop database` до нуля таблиц. Платного прогона в паке не было; денег не потрачено. + +**7. Диспозиции строк бэклога:** **269** (бэкапов нет ни у оплаченной работы, ни у денежного реестра) — ЗАКРЫТА лендингом · **270** (у продукта ноль дверей: контракт объявляет `bearerToken`, выдать нечем) — ЗАКРЫТА · **274** (имя книги) — ЗАКРЫТА платформенной половиной; ⚠ доставка титула В ФАЙЛ остаётся и уходит к строке **284**: форма описана (`tmctl build --title` аргументом при спавне, прецедент `--ceiling-usd`), **но патч НЕ проверен** — движок отвергает незнакомый флаг по построению · **282** (ложная подпись «Знаков») — состав минора внесён прозой, поле за паком формы заказа. Новая строка **293** — файл под любым каталогом `books/` невидим для git. + +**8. Норма, купленная этим паком и стоящая дороже находок.** Зона сама свела итог: **четыре из пяти major — не новые дефекты, а её собственные ПОСЫЛКИ, записанные как факты** («каталог уходит с исходником» · «у книги в интейке терять нечего» · «граница одна на две двери» · «замер обеих веток»). ⇒ **Верификатор не нашёл кода, который зона не додумала, — он нашёл предложения, которые она не проверила.** Это меняет ФОРМУ приёмки, а не только список находок: искать надо не «где код неверен», а **«какое утверждение отчёта никто не исполнил»** (05.09.2026, оркестратор №22). ✅ + +## D39.202 — НОРМЫ СМЕНЫ 05.09: ОДИН КОРЕНЬ У ВОСЬМИ СЛУЧАЕВ — ПРИБОР ОТВЕЧАЕТ НА СВОЙ ВОПРОС, А ЧИТАЮТ ЕГО КАК ОТВЕТ НА СВОЙ (05.09). ✅ + +**0. Зачем отдельной нотой.** За смену три сессии и два акта приёмки родили полтора десятка правил, и в актах им не место: акт говорит, ЧТО принято и с каким остатком, а не читает мораль (форма D-ноты — `ORCHESTRATOR_SESSION_PROMPT.md` §«Ратифицируешь?»). Здесь — только правила и их ГРАНИЦЫ; кто и на чём их купил, живёт в актах `D39.201` и следующем за ним. ⚠ Восемь случаев ниже выглядели РАЗНЫМИ дефектами и оказались одним. + +**1. ⛔ КОРЕНЬ: вывод инструмента отвечает на вопрос, который инструмент умеет задать, а читают его как ответ на СВОЙ.** Восемь случаев одной смены: длина списка посадок прочитана как ПОКРЫТИЕ · `SURVIVED` прочитан как ОТСУТСТВИЕ находки · зелёный линт — как «носители целы» (а он видит лишь то, у чего ЕСТЬ номер строки) · линт с фильтром по своей зоне — как «якоря зоны целы» (а он мерит только ИСХОДЯЩИЕ) · `--stat` прочитан как столбец вставок (а это ЗАТРОНУТЫЕ строки) · зелёная батарея — как «починка покрыта» (а посадку без теста ловит только полный каталог) · пустой выход упавшей утилиты — как «файлы идентичны» · греп `A|B` без `-E` — как отрицание. + +**2. Четыре вида ЗЕЛЁНОГО ОТЧЁТА, неразличимые чтением.** Отчёт зелен одинаково, когда: (а) проверено МАЛО (выборка, предъявленная как доказательство, доказывает ровно себя) · (б) проверено НЕ ТО (посылка истинна не по той причине, по которой её проверяли) · (в) записано ТОЧНЕЕ, чем проверено (число звучит как замер, замером не являясь) · (г) утверждение ИСТИННО, но отвечает не на тот вопрос, который решает приёмку («каталог прогнан целиком» ≠ «прогнан и СОШЁЛСЯ»). **Различает их только исполнение** ⇒ «проверено» без предъявленной команды и её вывода утверждением не является. + +**3. Формы ЛОЖНОЙ ЗЕЛЕНИ, найденные за смену** (реестр, а не рассказ): упавшая сборка · обрезанный вывод · **эквивалентная посадка** · **отказ, принявший форму таймаута или тишины** (восемь случаев за смену — свойство инструмента, не невезение) · **вырожденное утверждение** (предмет отсутствует в фикстуре) · **триггер, существующий только в ПОЧИНЕННОМ мире** · **посадка с нерезолвящимся якорем** (запись есть, не утверждает ничего, а ДЛИНА списка читается как покрытие) · **`SURVIVED` как сообщение о НЕДОСТИЖИМОСТИ** (единственная форма, которая приносит дефект, а не прячет: так найден мёртвый код) · ⛔ **гейт, ЗАЩИЩАЮЩИЙ дефект** — тест ПИНИТ неверное поведение, починить не покраснев нельзя, и ревизия по зелени проходит мимо; самая устойчивая из всех. + +**4. Посадки и починки.** Посадка, чей отказ выглядит ТАЙМАУТОМ, посадкой не является (`t.Fatalf` из порождённой горутины — `runtime.Goexit` не на той горутине). **Починка из двух частей требует посадки из двух частей** (одинокая половина воспроизводит третий мир, которого не было). **Починка, у которой проверена только одна половина, — половина починки** (громкая смерть, ставшая тихим исчезновением, — класс из шапки `CLAUDE.md`, допущенный В ПОЧИНКЕ). **Починка с посадкой, но без теста, выглядит покрытой ровно до полного прогона каталога.** Проверка «якорь резолвится ровно один раз» гоняется ПОСЛЕ КАЖДОГО касания кода, а не перед сдачей. + +**5. Указатели.** Носитель БЕЗ якоря не проверяется инструментом, который ищет якоря (комментарий в Go пережил линт и нёс снятое различение как действующее). Ноту адресуют НОМЕРОМ: строка журнала подрезку не переживает. **Номер строки протухает от акта СВОЕЙ ЖЕ записи, если запись стоит выше цели в том же файле** ⇒ форма указателя: сначала ТОКЕН, потом номер с оговоркой «на момент этой редакции». **Протухший ЧУЖОЙ указатель ПОМЕЧАЮТ, а не переписывают.** Правка файла, на который целятся якоря, требует линта БЕЗ фильтра по своей зоне: «поправил читателя, оставил писателя» бывает и через НУМЕРАЦИЮ — правка, законная в своей зоне, ломает чужую по механике файла; худший случай — замороженная зона, у которой нет своего линта в цикле. + +**6. Замеры и приборы.** `--stat` даёт ЗАТРОНУТЫЕ строки; «сколько добавлено» — только `--numstat`. Инструмент, чей отказ даёт ПУСТОЙ выход, обязан печатать РАЗМЕР рядом с вердиктом. Отрицание сопровождается КОМАНДОЙ, воспроизводимой другим (греп с `|` требует `-E`). **Сходимость «чистая дельта файла = сдвиг самого нижнего якоря» — ТРЕВОЖКА, а не прибор:** импликация односторонняя в обе стороны (сошлось ⇒ про число ханков ничего; РАЗНЫЕ сдвиги ⇒ ханков ≥2, доказательно), выход всегда «иди мерь», никогда «вот причина»; мерит по-настоящему перегреп токена. **«Покрыть нечем» — ЗАМЕР, а не мнение, и стоит одной попытки.** **Линт якорей в чужие доки — гейт ЛЕНДИНГА, а не сессии:** на грязном чужом дереве у вопроса нет определённого ответа, и называть результат («транзиент» либо «долг») значит выдавать неопределённость за замер. + +**7. Ратификация и передача довода.** Замер, попавший в ПОСЫЛКУ ЗАКАЗА, — стоп и вопрос владельцу, а НЕ артефакт под подпись (нарушено оркестратором 05.09 и отозвано в тот же день). **Ратификация, приносящая зоне готовый довод, обязана нести и КОМАНДУ, которой он проверяется**; зона обязана эту команду прогнать, кем бы довод ни был принесён. ⚠ **Норма О НОРМАХ, объясняющая, почему половина написанного здесь не сработает: правило, требующее БДИТЕЛЬНОСТИ, исполняется ровно настолько, насколько бдительность есть; правило, снижающее ЦЕНУ проверки, исполняется.** По этому критерию из всего списка выживают немногие — сходимость дельты, «ратификация несёт команду», «якорь резолвится ровно один раз», «размер рядом с вердиктом»: все они дают КОМАНДУ, а не призыв. + +**8. Приёмка как механизм.** Промт, называющий верификатору КОММИТ (D39.172), обязан выдавать ему ГОТОВЫЙ ПУТЬ отдельного рабочего дерева (`git worktree add <путь> <коммит>`), а не только запрет `checkout`: запрет уже стоял (`ORCHESTRATOR_SESSION_PROMPT.md`, греп `state-changing git`) и не сработал на 22 агентах. ⚠ 05.09 общее дерево оказалось отцеплено на судимый коммит, следующий коммит оркестратора ушёл мимо ветки, и заметила это ЧУЖАЯ зона; **актор не установлен, и запись «переключил верификатор» снята как вывод, поданный за замер.** Симптом был виден за полчаса — верификатор доложил «появились чужие незакоммиченные правки трёх файлов `docs/`», и это отпечаток «HEAD на коммите, файлы на ветке»; отпечаток не был опознан. **Дешёвый механизм, которого не было: одна строка в pre-commit — `git symbolic-ref -q HEAD >/dev/null || exit 1`**. + +**9. ⛔ ГЛАВНЫЙ ПРОБЕЛ СМЕНЫ, У КОТОРОГО СЕГОДНЯ НЕТ МЕХАНИЗМА (назван зоной движка).** Все ЧЕТЫРЕ major +её приёмки оказались **утверждениями, а не поведением**: код делал то, что должен, а врали про него +слова — доккоммент контрактного типа, шапка поля, обоснование фенса, подпись числа. То же на платформе: +четыре из пяти major — её собственные ПОСЫЛКИ, записанные как факты. ⇒ **Батарея сторожит поведение, +каталог мутаций сторожит поведение, линтер сторожит форму — и НИ ОДИН из них не сторожит того, что код +о себе ГОВОРИТ.** Единственное, что поймало все восемь, — чтение вслух ЧУЖИМИ ГЛАЗАМИ: приёмка и внешний +ревьюер. У следующей смены их может не быть, и тогда слова разъедутся с кодом МОЛЧА. +⚠ **Направление, а не решение (кандидат в заказ, НЕ ратифицирован).** Механизм у проекта уже есть в +доках — опт-ин токен-якорь `` `путь:12-14`=`подстрока` ``, сверяемый ПО СОДЕРЖИМОМУ (`docs/scripts/counts.py`, +греп `def lint_anchors`), с разведёнными ролями дельты: ТРЕБОВАТЬ токен — на тронутых строках, ПРОВЕРЯТЬ +написанные — по всему корпусу каждый прогон. Перенос в КОД: **комментарий, дающий ГАРАНТИЮ, называет тест, +который её пинит.** Триггер обязан быть ЯВНЫМ маркером, а не эвристикой по прозе: механизм, применяемый +по суждению, — не механизм. +**Замер зоны на её ПЯТИ major (а не на здравом смысле):** два поймано СТРУКТУРНО (`Finished.Money` +«present only on `outcome: ceiling`» и `status.go` «absent when there is no current manifest» — оба +заявляют правило присутствия ⇒ тест пишется ⇒ падает); один вскрыт ЧАСТИЧНО и самым интересным образом +(`runevents.go` «discloses nothing about what any one call cost» — теста быть не может, это отрицательное +свойство над деривацией, и попытка назвать тест обнажила бы, что фраза АРГУМЕНТ, а не гарантия); два +ПРОМАХ (история фенса и подпись числа — не гарантии о поведении). +⛔ **ВОЗРАЖЕНИЕ, УБИВАЮЩЕЕ ПРОСТУЮ ФОРМУ: указатель на ПУСТОЙ тест хуже отсутствия указателя.** За одну +смену у зоны было ЧЕТЫРЕ зелёных теста, не доказывавших ничего: `TestWaitingForHeadroomDeliversTheWholeBook` +(строка ожидания не появлялась ни разу — починен `waits > 0`) · `TestAFutileRefusalDoesNotHoldTheWaveOpen` +(проходил по неверной причине, `attempt_timeout` — удалён) · фолд-тест, сравнивавший два РАЗНЫХ набора +рядов · тест `bookOnceUSD`, ПИНИВШИЙ неверное число. Под линтом в форме «назови тест» все четыре стали бы +машинно-проверенными указателями от гарантии к тесту, который её не держит ⇒ **отмывание уверенности ровно +там, где замер говорит, что уверенность была неверна.** +⭐ **ПОПРАВКА, СОБИРАЕМАЯ ИЗ УЖЕ ПОСТРОЕННОГО: названный тест обязан быть тем, про который КАТАЛОГ доказал, +что он умеет ПАДАТЬ.** `tmmutate` уже печатает по каждой посадке имена покрасневших тестов и имеет режим +«keep only the failing test names» (`backend/cmd/tmmutate/main.go`, греп `-logs`) — величина вычисляется +сегодня и просто не сохраняется; добавка — писать эти списки файлом. Тогда линт проверяет ТРИ машинных +условия: тест существует · тест НАЗВАН красным хотя бы одной посадкой · посадка про тот же предмет (через +`why`, глазами один раз при заведении). ⇒ «указатель» превращается в «указатель, про который ПОКАЗАНО, что +он краснеет, когда гарантию ломают» — единственная версия, которая сильнее заменяемого ею чтения. Побочно +проект получает долговременную запись, КАКИЕ ТЕСТЫ НЕСУЩИЕ. +⚠ **ПРЕДЕЛ, записанный РЯДОМ, а не вместо:** кандидат НЕ берёт класс, стоивший сегодня дороже всего — три +major из пяти были предложениями, которые НЕ гарантии (история · подпись числа · довод). Для них +инструмент другой и дешевле: **утверждение, ссылающееся на замер, обязано цитировать КОМАНДУ** («$1.00 из +$2.00» не мерил никто), **а утверждение о ПРОШЛОМ обязано быть ДАТИРОВАНО** («раньше это было невозможно» +звало доверять не той границе). Оба — правила письма, проверяемые тем же опт-ин токеном +(05.09.2026, оркестратор №22). ✅ + +## D39.203 — ВЛАДЕЛЕЦ РАЗРЕШИЛ ДВИЖКУ ГОВОРИТЬ «СКОЛЬКО ДОБАВИТЬ»: раскрытие оценки вызова легитимно, поле переименовывается, запрет на СТОИМОСТЬ цел (05.09). ✅ + +**1. Решение владельца 05.09, дословно: «Можно, в форме „сколько добавить“».** Снимает `pending owner` с `Ceiling.ShortfallMicroUSD` и закрывает вопрос, поставленный после того, как приёмка предъявила ЖИВЫМ ПРОГОНОМ: из публикуемого числа и потолка, который потребитель поставил сам, восстанавливается оценка отказанного вызова с точностью до микродоллара (`shortfall + ceiling − committed`, где `committed` идёт кадром `spend`). + +**2. ⚠ Что именно разрешено, и чего решение НЕ отменяет.** Разрешено движку называть платформе сумму, которой не хватает, ЗНАЯ, что она ограничивает цену вызова: **стена и ЕСТЬ цена вызова, поэтому обойти это формой невозможно — можно только решить, секрет ли это.** Владелец решил, что нет. ⛔ **Запрет ПТ-35 остаётся в силе и НЕ сужен:** «цены моделей, стоимость стадий и вызовов». Различение, на котором стоит решение и которое обязано пережить эту ноту: **наружу идёт ДО-ВЫЗОВНАЯ ОЦЕНКА (резервация, которую движок просит ПЕРЕД звонком), а не СТОИМОСТЬ (что списано по факту).** Оценка есть потолок, стоимость есть факт; запрет написан про факт. Сеттлмент по-прежнему не выходит. + +**3. Тензия жила ВНУТРИ ЗАКАЗА, а не в одном поле — найдено зоной движка.** Пункт (3) промта `BACKEND_MONEYSTOP_SESSION_PROMPT.md` запрещал цене вызова выходить наружу; пункт (4) ТРЕБОВАЛ публиковать `step_max_usd` — «the largest single INDIVISIBLE reservation… the number the wall was made of» (`backend/internal/pipeline/manifest.go`, греп `StepMaxUSD`), то есть тоже оценку ОДНОГО вызова. ⇒ Снятие недостачи вопроса не закрыло бы. Решение владельца закрывает ОБЕ половины разом. + +**4. ⛔ Ошибка оркестратора, которую это решение исправляет, — процедурная, и она названа прямо.** Замер приёмки попал не в работу зоны, а в ПОСЫЛКУ ЗАКАЗА, который писал оркестратор. Норма на этот случай («замер бьёт по посылке — стоп и вопрос владельцу, не артефакт под подпись») была нарушена: оркестратор **ратифицировал сам, объявив внутренний провод исключением, и спросил владельца ПОСЛЕ**. Ярлык «внутренний провод» придуман после замера — собственный промт применял запрет к ПРОВОДУ («наружу»), а не к интерфейсу. Ратификация отозвана в тот же час, до слова владельца поведение не менялось. ⚠ Сопутствующая ошибка того же захода: сужение объявлялось «ратификацией фразы D39.106 §2» — такой фразы в журнале НЕТ, она жила только в комментарии теста ⇒ это было ОБСЛУЖИВАНИЕ по D39.183, а овер-атрибуция скрыла настоящий конфликт (ПТ-35). Обе поправки — от внешнего ревьюера, проверены оркестратором командами. + +**5. ЧТО ЭТО СОЗДАЁТ КАК РАБОТУ (не дофикс сданного пака — НОВЫЙ заказ).** Построенное поле легитимно и НЕ ложно: оно называет «сколько не хватило на ЭТОТ вызов» и именно это и считает. Выбранная владельцем форма СИЛЬНЕЕ: «сколько добавить, чтобы прошёл ЛЮБОЙ следующий вызов» = `max(shortfall, step_max − headroom)`. Разница для покупателя практическая: сегодняшняя недостача закрывает текущий вызов, а следующий может быть крупнее, и человек доплатит дважды. ⇒ Состав новой работы, названный зоной и принятый: **(а)** новая величина; **(б)** **ПЕРЕИМЕНОВАНИЕ поля** — `shortfall_micro_usd` станет ЛОЖНЫМ ИМЕНЕМ, кандидат `top_up_micro_usd`; **(в)** минорный бамп `StreamVersion` (1.3 → 1.4) и правка зеркала словаря на стороне платформы; **(г)** пере-снятие пина белого списка кадра `ceiling`. ⚠ **Это НЕ однострочник, и объявлять его дешёвым было бы третьей ошибкой того же дня.** + +**6. Диспозиция: пак движка лендится КАК ПОСТРОЕН, форма (б) уходит строкой бэклога.** Довод: построенное не ложно, а уже — оно слабее выбранной формы, но честно называет то, что считает; принудить зону к третьему полному прогону каталога (~2 часа) ради работы, которая по составу тянет на свой пак, — плата не за качество, а за темп. ⚠ Это НЕ случай эрраты 04.09-д («найденное и не починенное не превращается в долг решением о темпе»): здесь не найденный дефект откладывается, а СОЗДАЁТСЯ новая работа решением владельца. Шапка `pending owner` в `Ceiling.ShortfallMicroUSD` заменяется указателем на эту ноту при ближайшем касании зоны (05.09.2026, оркестратор №22). ✅ diff --git a/docs/architecture/14-api-contract/README.md b/docs/architecture/14-api-contract/README.md index 85afb275..10bb196d 100644 --- a/docs/architecture/14-api-contract/README.md +++ b/docs/architecture/14-api-contract/README.md @@ -1010,7 +1010,7 @@ $1.6, спишем по факту», и закрытие после прого | `POST`/`GET /exports` | движок: `tmctl build [--format epub,txt] [--out path] [--partial]` — EPUB 3 + чистый txt (`backend/internal/bookfile`), конверт `tm-build-v1`, пути в `StatusArtifacts.book_files`, отказ exit 16 `book_incomplete` | ⚠ **ПЕРЕ-СНЯТО 05.09: ОБЕ ПОЛОВИНЫ ПОСТРОЕНЫ.** Движковая — D39.175 (`8adcb86`); ПЛАТФОРМЕННАЯ дверь заленджена `adf5e53` (D39.194) и предъявлена живьём платным EPUB через API. Прежнее «открыта только дверь платформы» протухло на сутки. Дверь СТРОИТ через `tmctl build` (не читать файл у БД: там копия прежней сборки) и сверять `BuildReport` (`config_drift`/`stale_unknown`). ⚠ Это про ФАЙЛ КНИГИ; редакторский `tmctl export`/annot-v1 (D29.1а) — ОТДЕЛЬНАЯ, не закрытая работа | движковая половина — D39.175; дверь — пинг зоне платформы (её журнал, греп `Движок отдаёт книгу файлом`); annot-v1 — строка 49 / D29.1 | | Условные чтения (`ETag`/304), сжатие | — | **ПОСТРОЕНО P7** — `httpapi/conditional.go`; валидатор считается от БАЙТ ответа. Остаток строки 186 — шаги 3–5 (скоуп кадра, дельта-чтение, `staleTime`) | шаги 1–2 закрыты D39.153 | | `Idempotency-Key` | — | **ПОСТРОЕНО P7** — `httpapi/idempotency.go` + `pgstore/idempotency.go`, на ТРЁХ создающих вызовах (третий — дверь выдачи, `httpapi/exports.go`; было два до 04.09); ⚠ живой дефект под конкуренцией — `PD-369` | закрыто D39.153 | -| `bearerToken` — чем ВЫДАЁТСЯ токен | вход только ставит HttpOnly-куку (`login.go:338`) | сервер токен ПРИНИМАЕТ, выдать его нечем | носитель: research/28 §2 (Б-15) + **строка бэклога 270** (⚠ испр. 05.09: прежнее «строки нет» ложно); развилка «выдать либо снять обещание из канона» заказана платформенным паком 05.09 | +| `bearerToken` — чем ВЫДАЁТСЯ токен | вход ставит HttpOnly-куку (`login.go:338`); токен минтится ВНЕ ПОЛОСЫ оператором | ✅ **ЗАКРЫТО 05.09 платформенным паком:** `tmplatformctl token issue --user … --client …`, сессия обычная — видна в журнале входов под операторским провайдером и гасится `POST /auth/logout`. ⚠ Эндпоинта для выдачи НЕТ и по умолчанию не будет: дверь, выдающая себе учётные данные по HTTP, — другая модель угроз и ратифицируется отдельно. Прежняя редакция («выдать его нечем») стала ложной в момент лендинга и сутки противоречила самому канону (`openapi.yaml`, греп `minted OUT OF BAND`) | носитель: research/28 §2 (Б-15) + **строка бэклога 270** (⚠ испр. 05.09: прежнее «строки нет» ложно); развилка «выдать либо снять обещание из канона» заказана платформенным паком 05.09 | | `Problem.localized` | — | объявлено, ни одним кодом не используется | носитель: research/28 §8 п.4 | | `Note.code` как enum спеки | карта приложения А | не enum, пока не написаны фразы | ⚠ **прежний носитель «строка 148» МЁРТВ** (`a023e39`, 21.08; разбор — приложение А, п.3); живой носитель — строки бэклога **203 + 204** (словарь фраз) | | Настоящие названия глав (`Chapter.heading` ≠ null) | парсер структуры | НЕ ПОСТРОЕНО | строка 160 (Этап 0) | diff --git a/docs/architecture/15-money-path.md b/docs/architecture/15-money-path.md index 685dc231..5b776c6a 100644 --- a/docs/architecture/15-money-path.md +++ b/docs/architecture/15-money-path.md @@ -21,7 +21,7 @@ - **`--max-units` — ОБЪЁМНЫЙ потолок прогона, ортогональный денежному** (D39.165 §1б, принят D39.170). Ограничивает не деньги, а РАБОТУ: не больше N выходных ЮНИТОВ (гранулярность `units_total` манифеста — та же, в которой платформа продаёт главы) возьмут СЛОТ гранта в этом прогоне; юниты, отданные за $0 (резюм, ре-пин), ретраи и эскалации внутри юнита потолок не тратят. ⚠ **«Слот» ≠ «оплата», и с 03.09 это РАЗНЫЕ числа** (пак «число согласия», строка бэклога 232): юнит, который прежний прогон НАЧАЛ и не отгрузил, дописывается ВНЕ гранта, поэтому грант N оплачивает ДО 2N выходных юнитов — замерено приёмкой на живом раннере (грант 2 → `Paid()=4`, шесть вызовов провайдера). Единственный денежный бонд здесь — `--ceiling-usd`. ⛔ **Семантика переноса НЕ ратифицирована — слово владельца 03.09 «подумаем на этот счёт»**, и до его решения читать это как замеренный факт, а не как норму. Принимает только `translate`. **Остановка по объёму — ЗАВЕРШЕНИЕ (exit 0), не пауза:** словарь кодов выхода не расширялся и нового значения `Finished.Outcome` тоже нет — ⚠ **но с 31.08 признак едет ЧИСЛАМИ в кадре `finished` шва, а не только прозой** (D39.181 п.2, закон раскрытия: прозаическая строка отчёта до потребителя потока не доезжала): носитель — `Finished.Volume`, леджер доставки. Различение при этом живёт и в отчёте прогона, и в логе; отчёт разводит ДОСТАВКУ и ПЕРЕ-ДЕЛКУ. Носитель — `backend/internal/pipeline/volume.go:13`=`the VOLUME ceiling — the run's second stop`; словарь флага дословно — `backend/cmd/tmctl/invocation.go:143`=`Stopping on it is a COMPLETION (exit 0), not a pause`. ⚠ Проводка в платформу ГЕЙЧЕНА (`PD-422`): единственный писатель признака движения банка — дверь правок, рост АВТО-банка от майнинга флага не ставит. - **`--ceiling-usd` — КНИЖНЫЙ потолок, не бюджет прогона.** Дословно из флага: «the book USD ceiling in force for THIS RUN ONLY — it OVERRIDES book.yaml `ceilings.book_usd` and is never written back. It caps the book's CUMULATIVE committed+reserved spend, not this run's increment…» (`backend/cmd/tmctl/invocation.go:142`=`the book USD ceiling in force for THIS RUN ONLY`; ⚠-коммент `backend/cmd/tmctl/main.go:260`=`--ceiling-usd is NOT a per-run budget`; многоточие закрывает обрыв цитаты — во флаге дальше стоит «, and must be > 0»). Ратификация — D39.122 п.2(в): пересчёт «пользовательский прирост → абсолют» — обязанность ПЛАТФОРМЫ, вторая денежная ось не заводится. День-потолок флаг НЕ перекрывает (PD-157). Проводка внутри: `Ceilings.BookUSD` в `Reserve` и именование сработавшего потолка в ошибке — `backend/internal/pipeline/stagerun.go:495`=`BookUSD: r.bookCeilingUSD()` и `backend/internal/pipeline/stagerun.go:559`=`overrides the book's ceilings.book_usd` (стоп = `errReserveCeiling`, `backend/internal/pipeline/escalation.go:49`=`var errReserveCeiling`). - **Потолки — wiring, не семантика:** `Ceilings` намеренно исключены из `BriefHash` («Wiring fields (paths, ceilings, db) deliberately excluded», `backend/internal/config/book.go:345`=`Wiring fields (paths, ceilings, db) deliberately excluded`; канон — `backend/internal/config/book.go:369`=`canon := struct {`) ⇒ смена ДЕНЕЖНОГО потолка не двигает ни снапшот, ни ре-билл (D39.110 п.2б). ⚠ Верно ровно про ДЕНЬГИ: потолок СЕГМЕНТАЦИИ `edit_ceiling_out` в снапшот ВХОДИТ и меняет границы чанков (`backend/internal/pipeline/snapshot.go:34`=`EditCeilingOut int`). -- **`status --json` отдаёт фигуры платформе:** `committed_usd`, `reserved_usd`, `book_ceiling_usd`, `ceiling_pct` = 100·(committed+reserved)/book_ceiling (`backend/internal/pipeline/status.go:311`=`book_ceiling_usd,omitempty`; арифметика `ceiling_pct` — `backend/internal/pipeline/status.go:858`=`100 * (committed + reserved)`). Дневной фигуры в status нет (PD-157 — `platform/docs/DEFECT_REGISTER.md`, греп `PD-157`). +- **`status --json` отдаёт фигуры платформе:** `committed_usd`, `reserved_usd`, `book_ceiling_usd`, `ceiling_pct` = 100·(committed+reserved)/book_ceiling (`backend/internal/pipeline/status.go:311`=`book_ceiling_usd,omitempty`; арифметика `ceiling_pct` — `backend/internal/pipeline/status.go:869`=`100 * (committed + reserved)`). Дневной фигуры в status нет (PD-157 — `platform/docs/DEFECT_REGISTER.md`, греп `PD-157`). - **Леджер = НИЖНЯЯ граница** (строка 78 бэклога, `docs/PROGRESS.md`, греп `Леджер денег = НИЖНЯЯ граница`): «2xx body decode failed (call IS billed)» — провайдер списал, попытка в `request_log` не попадает; живой замер D39.86 — 3 вызова из 14, неизвестность $0.015111 при леджере $0.114378; движок сеттлит оценку и печатает `estimated-cost rows: 3`. Канал виден, фикс — money-паком (дизайн GENERALITY_PHASE2 §5.6). - **Согласие на пере-оплату — ДВИЖКОВЫЙ гейт**. Прогон, который пере-покупает уже оплаченные юниты дороже порога, ОСТАНАВЛИВАЕТСЯ и требует явного согласия. Порог по умолчанию ратифицирован D20.2-Q2: `min($0.50, 5% × ProjectedBookUSD)`. Носитель — `backend/internal/pipeline/rebill.go`, греп `rebillConsentThreshold`; единственная ручка книги — `rebill_consent_usd` (валидатор `backend/internal/config/book.go`, греп `rebill_consent_usd`; 0 = ратифицированный дефолт). ⚠ Порог считается БЕЗ контура банк-ролей (D39.182 §3), то есть срабатывает позже, чем подсказывает полная проекция.