Finish the documentation sweep the P7 landing left half-done: closed working docs go to the zone archive, the P7 era is sliced out, and eight stale claims are corrected

This commit is contained in:
Claude (backend session) 2026-08-21 00:15:24 +03:00
parent 7f4d37ccd2
commit 4c94f7e8ff
14 changed files with 300 additions and 275 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -8,7 +8,7 @@
| **+ банкнота** (WHAT-канал драфта) | та же flash, промпт `translator-banknote.md` | как draft | ⚠ НЕ в shipping-c1 — ран-локальный конфиг книги: `~/books/gu-zhenren/coldrun-b/pipeline.yaml` (`gates.banknote` + `prompt_override`) | ⚠0731; включение в c1 = СТОП-решение владельца (строка 140: двигает RequestHash) | те же |
| **терминолог** | `deepseek-v4-flash` | thinking ON, `gates.terminology.reasoning:"low"` (одна ручка на обе бэнк-роли) | ран-локальный `~/books/gu-zhenren/coldrun-b/reprobe/bank-low/pipeline.yaml:68-78`; схема — `internal/config/pipeline.go:298` (TerminologyGate); в shipping-c1 гейта НЕТ (opt-in OFF) | боевой по факту прогонов; носитель — конфиг книги | quirks §0731 п.3а: пустой `reasoning` = вендор-`high` → 4/5 `length` с пустым телом; экран `target_script: Cyrillic` обязателен при гейте ON |
| **классификатор** (типы кандидатов) | flash (`classify_model` пуст ⇒ = model) | та же ручка | там же: `classify_types: true` + `classify_budget_usd` | проба/полигон; в shipping выключен ВЕЗДЕ | латентный мис-вердикт `cjk_artifact``13-tech-debt-anchors.md` §Б-105 |
| **editor** | `deepseek-v4-pro` **БИЛИНГВ** | thinking ON; `reasoning:"off"` = осознанный NO-OP (ReasoningNone); ручки эффорта у pro НЕТ (`low``high`) | `configs/pipeline-c1.yaml:61-85`; floor 16000 — `models.yaml:178` | боевой ИНТЕРИМ (D30.1/D39.22); D39.117: топология 2-прохода ПОДТВЕРЖДЕНА (`A/F3` +2.50 Holm 0.0004), жильцы dspro/glm-5 неразличимы ⇒ выбор = цена; закон-блок ОБЯЗАТЕЛЕН (строка 134) | quirks §0731 п.3а (маппинг pro), п.3б (замер `low` на pro ОТОЗВАННЕ УСТАНОВЛЕНО); вахта смены маппинга «early August 2026» — §Б-108 |
| **editor** | `deepseek-v4-pro` **БИЛИНГВ** | thinking ON; `reasoning:"off"` = осознанный NO-OP (ReasoningNone); ⚠ ручка эффорта у pro ПОЯВИЛАСЬ 13.08 (low/high/max — D39.137; прежнее «ручки нет» протухло, испр. 20.08), маппинг поведением пере-проверяет полигон | `configs/pipeline-c1.yaml:61-85`; floor 16000 — `models.yaml:178` | боевой ИНТЕРИМ (D30.1/D39.22); D39.117: топология 2-прохода ПОДТВЕРЖДЕНА (`A/F3` +2.50 Holm 0.0004), жильцы dspro/glm-5 неразличимы ⇒ выбор = цена; закон-блок ОБЯЗАТЕЛЕН (строка 134) | quirks §0731 п.3а (маппинг pro), п.3б (замер `low` на pro ОТОЗВАННЕ УСТАНОВЛЕНО); вахта смены маппинга «early August 2026» — §Б-108 |
| **editor-резерв** | `glm-5` | thinking OFF ЯВНО (`extra_body {thinking:{type:disabled}}`, иначе таймауты ×3) | `configs/pipeline-arm-glm.yaml:42` + `models.yaml:184-195` | резерв; флип = один арм-конфиг. mistral-арм есть, но mistral ИСКЛЮЧЁН как редактор (D39.20) | quirks thinking-таблица (glm-5); эхо-экспозиция билингва при thinking-OFF НЕ измерена (`sourceEchoExposure`) |
| **судья Ф2** | Gemini (нативный адаптер — Ф2, строка 33); в C2-скелете провизорно `glm-5` | Gemini: thinking mandatory | `models.yaml:95-108,240-255`; `pipeline-c2.yaml:33-44` (select; «DO NOT FLIP») | Ф2/полигон — в движке НЕ построен; C2 неисполняем | quirks: `thinking_budget:0` → HTTP 400; учёт `additive_total`; судья ЭРОТИКИ непригоден (PROHIBITED_CONTENT неконфигурируем, D22.6) → первичный судья 18+ = grok |
| **канал B (18+)** | `mistral-large-2512` (переводчик-дефолт, D19.1) + `grok-4.3` (эскалация reasoning-ON + судья 18+) | mistral: не-reasoning; grok: OFF только явным `reasoning_effort:"none"` | `models.yaml:118-131,257-273` (accepts_labels, rate_limit) · `:63-87,221-238` · c1 `chains.adult:[grok-4.3]` | данные заведены; полный wiring — Ф2; `content_policy` пуст во всех shipping (сознательно) | quirks §Транспорт: mistral rate-limiter (~48% retry-fails → `max_concurrency:2`); grok: вложенная форма эффорта молча глотается; additive-биллинг (think-ON только за резервом D6.2) |
@ -18,5 +18,5 @@
## Примечания
- **⚠0731 (D39.61):** flash пере-обучен под тем же слагом; `/models` молчал. Развилка решена вариантом (б): явный `reasoning_effort:"low"` черновику + ручка эффорта бэнк-ролям (код `553f1a3`). Версию модели проверять ПОВЕДЕНИЕМ.
- **Вахта эффорта pro:** реестр загейченных триггеров — `architecture/13-tech-debt-anchors.md` §Б-108; статус ручки `low` на pro = НЕ УСТАНОВЛЕНО (quirks §3б).
- **Цена:** ~$0.85/ранобэ (D30.4; тело — `archive/architecture/05-decisions-D1-D38.md`, карта — шапка D-лога); пере-калибровка при следующем платном прогоне (112 залендена, D39.97). Edit-волна не гонялась НИ РАЗУ (строка 16); ставка платформы $0.03/глава (D39.123 п.2г). Цены слагов — `models.yaml` (`prices_checked: 2026-07-10`; катовер 0731 цен не менял).
- **Цена:** ~$0.85/ранобэ (⚠ число ДО пере-пина цен 1315.08: таблица запинена ПИКОМ, счёт шиппинга = 100% DeepSeek ⇒ ×4.24.4 в пике и ×2.12.2 в долине — D39.137; пере-калибровать первым же платным прогоном. D30.4; тело — `archive/architecture/05-decisions-D1-D38.md`, карта — шапка D-лога); пере-калибровка при следующем платном прогоне (112 залендена, D39.97). Edit-волна не гонялась НИ РАЗУ (строка 16); ставка платформы $0.03/глава (D39.123 п.2г). Цены слагов — `models.yaml` (`prices_checked: 2026-07-10`; катовер 0731 цен не менял).
- **Ключи API:** список env-ключей — `docs/README.md` §«Доступные ключи» (7 шт.); резолв per-провайдер — `models.yaml api_key_env`. **`.env` НЕ читать** (гардрейл CLAUDE.md), `.env.example` можно.

View file

@ -519,9 +519,9 @@
**4. Почему срочности нет.** Под контрактом D39.150 («гарантируется НАПРАВЛЕНИЕ ошибки, не точность») ни один исход нынешнего механизма не занижает молча: нормальный случай точен, недостача несётся историческими деньгами С РАСКРЫТИЕМ, пойманный откат откатывается к устаревшей цене ВСЛУХ. Остаточные нарушения контракта чинятся БЕЗ схемы и уже имеют носителей — **197** (прибить гарантии тестами и довести раскрытие до `status`) и **194** (деньги банковых ролей в число согласия). ⚠ Уточнение цены 194, сверенное оркестратором: миграция ей НЕ нужна — `store.RoleSpentUSD` уже ключуется по РОЛИ чекпоинта, а не по колонке класса (`internal/store/ledger.go:105-118`, докстринг прямо: «a new call class needs no migration to become countable»). (17.08.2026, оркестратор №18) ✅
## D39.152 — КОНТРАКТ API v0 **0.4.0 РАТИФИЦИРОВАН** (синк с платформой, 14 пунктов; author≠ratifier соблюдён сессией); четыре текстовые правки внесены при ратификации; клейм «ломающая правка одна» верен для типов и неточен поведенчески (20.08). ✅
## D39.152 — КОНТРАКТ API v0 **0.4.0 РАТИФИЦИРОВАН** (лендинг `6e84574`) (синк с платформой, 14 пунктов; author≠ratifier соблюдён сессией); четыре текстовые правки внесены при ратификации; клейм «ломающая правка одна» верен для типов и неточен поведенчески (20.08). ✅
**1. Что ратифицируется.** `docs/architecture/14-api-contract/openapi.yaml` + компаньон, `info.version: 0.4.0`. Автор — ОТДЕЛЬНАЯ контрактная сессия (20.08) в ответ на записку зоны платформы `platform/docs/CONTRACT_SYNC_FROM_PLATFORM.md` (14 пунктов AN); разбор каждого — §6в компаньона. Сессия ратификацию за собой не записала и сдала текст оркестратору — норма author≠ratifier исполнена ею самой, отмечаю как правильное поведение. **0.3.0 действовал до этой ноты**; зависимость зоны (PD-327) закрывается ею.
**1. Что ратифицируется.** `docs/architecture/14-api-contract/openapi.yaml` + компаньон, `info.version: 0.4.0`. Автор — ОТДЕЛЬНАЯ контрактная сессия (20.08) в ответ на записку зоны платформы `platform/docs/archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md` (14 пунктов AN); разбор каждого — §6в компаньона. Сессия ратификацию за собой не записала и сдала текст оркестратору — норма author≠ratifier исполнена ею самой, отмечаю как правильное поведение. **0.3.0 действовал до этой ноты**; зависимость зоны (PD-327) закрывается ею.
**2. Рубеж перед ратификацией исполнен** (норма: тяжёлая ратификация — контракт — смотрится ревьюером другой модели, мнение владельцу ДО). Fable-5, чтение обеих версий ЦЕЛИКОМ: вердикт «ратифицировать с правками». Пере-проверено ИМ независимо дважды: структурный дифф схем и дифф генерённых типов дают ровно одну строку `stop_requested`. Корневой `ErrorCode` — 16 значений, байт-в-байт не тронут; все открытые уровни (`ErrorCause`, `ErrorItem.code`, `Note.code`, три reason-словаря) объявлены открытыми явно. Все 14 пунктов записки имеют диспозицию, остатков прежних формулировок ноль (грепы по снятым фразам = 0).
@ -535,11 +535,11 @@
**7. Почему следующий минор не втиснут сюда.** Требование сквозной полосы прогресса (строка 200, слово владельца 20.08) — новое обязательное поле, то есть ещё один минор и ещё одна перегенерация клиента. Пока фронт заморожен и не потребляет ничего, миноры бесплатны: оба уедут в ОДНУ перегенерацию при разморозке. Значит нет причины вскрывать уже принятый пак платформы и заставлять зону доделывать на ходу. Зеркало фронта отстаёт ратифицированно (D39.142 п.5), при разморозке сверяется `cmp`. (20.08.2026, оркестратор №18) ✅
## D39.153 — ПРИЁМКА И ЛЕНДИНГ P7 ПЛАТФОРМЫ (пять актов, читающая поверхность): **ПРИНЯТ С ФИКС-ЛИСТОМ**; шесть линз + собственные посадки, одна дыра пинов найдена оркестратором; новые строки 198203; сессия закрыта владельцем (20.08). ✅
## D39.153 — ПРИЁМКА И ЛЕНДИНГ P7 ПЛАТФОРМЫ (пять актов, читающая поверхность; лендинг `9b23e8c`, записи `6e84574`): **ПРИНЯТ С ФИКС-ЛИСТОМ**; шесть линз + собственные посадки, одна дыра пинов найдена оркестратором; новые строки 198203; сессия закрыта владельцем (20.08). ✅
**1. Приёмка исполнением.** Панель шести линз в изолированных копиях, каждой свой каталог, бэкап чужого незакоммиченного ДО запуска (D39.113): слепая (заказ+дифф ДО отчёта) · контракт-конформность (пофайловый дифф wire-структур против ОБЕИХ версий канона) · деньги (два независимых пути из сырого леджера + живой стенд) · шов (инвентарь ВСЕХ каналов движка чтением чужого кода) · вне карты · ревью канона 0.4.0 другой моделью. Ни одной REJECT, регрессов против HEAD нет. **Пере-проверено рукой оркестратора:** `make check` с обоими гейтами — 18 пакетов, EXIT=0, **скипов 0**, линтер 0 issues · реестр 327 строк / 65 открытых (13 minor, 52 info), major и BLOCKER нет · оба док-гейта зелёные · миграционный манифест сходится, released-миграции не тронуты · `tmplatformctl books --migratable` живой. ⚠ Тестовых функций **532**, не 533 (команда — `grep -rh '^func Test' platform --include='*_test.go' | wc -l`; норма «дифф `^func Test` исполнением, не памятью» — `docs/ORCHESTRATOR_SESSION_PROMPT.md` §кодовый промт, со ссылкой на D39.121; в теле самой D39.121 команды нет — испр. при проверке записей); удалённых имён семь — каждое проверено, у каждого есть преемник, и преемники СИЛЬНЕЕ либо перевёрнуты по ратифицированному решению (гейт подписи банка → `TestResumeLiftsABankStopWithTheDecisionsAsTheyStand` плюс НОВЫЙ встречный `TestTheReconcilerDoesNotLiftABankStopNobodySigned`).
**2. Замер, обосновавший разворот схемного решения ВНУТРИ пака (00016 → 00022), пере-выведен независимо.** Свой бенчмарк на трёх вариантах в копии дерева: джойн **14.6 мс** · счётчик **5.3 мс** · без счётчика **2.9 мс**. Порядок и вывод подтверждаются, разворот законен: 00016 снёс колонку с доводом «её никто не пишет», 00022 вернул её ВМЕСТЕ с писателем. ⚠ Процент назван ЧЕТЫРЬМЯ носителями (журнал зоны 83% · `platform/internal/pgstore/perf_test.go:13` 96% · миграция 00022 «16.8 против 3.4, джойн сам 9 мс» · регистр PD-306 повторяет 83% и 16.8/3.4) — распад нормы «один носитель на факт» (D39.112) внутри одного пака. ⚠ **Первая редакция этой ноты писала «96% не выводится ни из чего» — ЭТО НЕВЕРНО и снято проверкой записей при лендинге:** число выводится точно, просто из ДРУГОГО замера — `platform/docs/P7_ACT5_FIX_PLAN.md:439-441`, «636 мс против 24 мс» на холодном корпусе акта 4, (63624)/636 = 96.2%. То есть носители меряли разное (холодный корпус против вакуумированного) и оба честны; сводить их надо указанием УСЛОВИЙ замера, а не удалением одного как фантома — иначе следующая сессия снесёт верное число.
**2. Замер, обосновавший разворот схемного решения ВНУТРИ пака (00016 → 00022), пере-выведен независимо.** Свой бенчмарк на трёх вариантах в копии дерева: джойн **14.6 мс** · счётчик **5.3 мс** · без счётчика **2.9 мс**. Порядок и вывод подтверждаются, разворот законен: 00016 снёс колонку с доводом «её никто не пишет», 00022 вернул её ВМЕСТЕ с писателем. ⚠ Процент назван ЧЕТЫРЬМЯ носителями (журнал зоны 83% · `platform/internal/pgstore/perf_test.go:13` 96% · миграция 00022 «16.8 против 3.4, джойн сам 9 мс» · регистр PD-306 повторяет 83% и 16.8/3.4) — распад нормы «один носитель на факт» (D39.112) внутри одного пака. ⚠ **Первая редакция этой ноты писала «96% не выводится ни из чего» — ЭТО НЕВЕРНО и снято проверкой записей при лендинге:** число выводится точно, просто из ДРУГОГО замера — `platform/docs/archive/P7_ACT5_FIX_PLAN_2026-08-20.md:439-441`, «636 мс против 24 мс» на холодном корпусе акта 4, (63624)/636 = 96.2%. То есть носители меряли разное (холодный корпус против вакуумированного) и оба честны; сводить их надо указанием УСЛОВИЙ замера, а не удалением одного как фантома — иначе следующая сессия снесёт верное число.
**3. Собственные посадки ВНЕ списка автора — 8, поймано 7.** Долг не поставлен третьей концовкой · `edit_wave` не монотонен · снят `Vary` · HEAD лишён валидатора · `reopen` снимает стоп банка кому угодно · погашение долга без сверки метки · вынос метки долга из закрывающей транзакции во второй оператор (техника `xmin` из их §36 работает — проверено исполнением, а не принято на слово). **НЕ поймано:** снятие `structure_version` И `revision` из `emitFrame` проходит ВСЮ батарею — два поля, которые канон требует на КАЖДОМ кадре `EventBase`. По норме самой зоны (PD-1) свойство без пинящего теста считается незакрытым.
@ -553,7 +553,7 @@
**8. ВТОРОЙ РУБЕЖ — приёмка приёмки (слово владельца 20.08: «сначала приёмка твоей же работы, потом коммит»).** Четыре линзы прочитали не пак, а РАБОТУ ОРКЕСТРАТОРА: широкий инвентарь доков (другая модель, чтение всей документации четырёх зон целиком) · claim-fidelity обеих нот и всех сегодняшних записей · поиск того, что панель пропустила · полнота выгрузки долга и зонная дисциплина. Результат оправдал остановку коммита.
**(а) Пять овер-атрибуций в первой редакции ЭТИХ нот — снято, каждая проверена автором лично.** «96% не выводится ни из чего» — неверно: число выводится точно из ДРУГОГО замера зоны (`P7_ACT5_FIX_PLAN.md:439-441`, 636 мс против 24 мс на холодном корпусе), и удаление его как фантома снесло бы верное измерение · расхождений с 0.3.0 не четыре, а ПЯТЬ (пятое — семантика `410 Gone`, носитель PD-253) · «четыре рантайм-ошибки» проверяемого носителя не имеют, репозиторий несёт две · клейм «оговорка расширена на всю ручку» опережал текст канона — оговорка внесена только сейчас (D39.152 п.6) · «фикс-лист из восьми пунктов» устарел на собственной правке. **Класс один и тот же — верное утверждение приписано не тому источнику**, и это ровно тот провал, против которого написана дисциплина «эрудиция ≠ улика».
**(а) Пять овер-атрибуций в первой редакции ЭТИХ нот — снято, каждая проверена автором лично.** «96% не выводится ни из чего» — неверно: число выводится точно из ДРУГОГО замера зоны (`archive/P7_ACT5_FIX_PLAN_2026-08-20.md:439-441`, 636 мс против 24 мс на холодном корпусе), и удаление его как фантома снесло бы верное измерение · расхождений с 0.3.0 не четыре, а ПЯТЬ (пятое — семантика `410 Gone`, носитель PD-253) · «четыре рантайм-ошибки» проверяемого носителя не имеют, репозиторий несёт две · клейм «оговорка расширена на всю ручку» опережал текст канона — оговорка внесена только сейчас (D39.152 п.6) · «фикс-лист из восьми пунктов» устарел на собственной правке. **Класс один и тот же — верное утверждение приписано не тому источнику**, и это ровно тот провал, против которого написана дисциплина «эрудиция ≠ улика».
**(б) Долг, живший только внутри зоны автора, вынесен наружу.** Строка в бэклог ДВИЖКА про публикацию причин флагов данными (релей §7в) жила только в регистре платформы, который движок не читает — заведена строка **204** · вопрос платформы К ВЛАДЕЛЬЦУ про норму плотности комментариев (PD-255) был объявлен несуществующим её же хендоффом («вопросов на владельце нет») — вынесен в обязательства CURRENT-STATE · норма «жалуйся владельцу, не обходи молча» для фронтового фрагмента хука жила только в моей прозе — написан пинг в зонный журнал фронта. Плюс сняты три строки бэклога (**176/181/187**), закрытые ещё D39.149: очередь всё это время звала 181 свободной работой, то есть направляла сессию чинить построенное.

View file

@ -7,7 +7,7 @@
>
> Это карта «куда и почему», а не пошаговая спека. Спеки — в хендофф-промтах сессий; контракт — в D-логе.
> **СТАТУС СТРОЙКИ (освежён 16.08 оркестратором №17: контракт-ревью ПРИНЯТО (research/28, D39.138), ломающий батч 0.3.0 ИСПОЛНЕН и принят с дофиксом (D39.142145: конвейер снят с провода, машинная модель ошибок, /capabilities, поток на книгу, подпись банка = один ОК D39.144; жанр из продукта ВЫКИНУТ — строка 184); P7 платформы ЗАПУЩЕН по финальному канону. Ранее 15.08: эмиттер шва ПОСТРОЕН И ПРИНЯТ — `events.jsonl` StreamVersion 1.1, exit 4/5 + полоса отказов 1019 (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.5S3.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»:
> **СТАТУС СТРОЙКИ (освежён 20.08 оркестратором №18: **пак P7 платформы ПРИНЯТ и ЗАЛЕНДЕН** — читающая поверхность построена целиком (главы · юниты · замечания · проекция банка · SSE · модель ошибок · условные чтения · идемпотентность), D39.153; **канон контракта — 0.4.0**, ратифицирован D39.152; пак движка «честные числа и статусы» принят с фикс-листом, D39.149. Предыдущий срез — 16.08 оркестратором №17: контракт-ревью ПРИНЯТО (research/28, D39.138), ломающий батч 0.3.0 ИСПОЛНЕН и принят с дофиксом (D39.142145: конвейер снят с провода, машинная модель ошибок, /capabilities, поток на книгу, подпись банка = один ОК D39.144; жанр из продукта ВЫКИНУТ — строка 184); P7 платформы ЗАПУЩЕН по финальному канону. Ранее 15.08: эмиттер шва ПОСТРОЕН И ПРИНЯТ — `events.jsonl` StreamVersion 1.1, exit 4/5 + полоса отказов 1019 (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.5S3.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) |

View file

@ -9,7 +9,7 @@ openapi.yaml РЯДОМ. ⚠ Зонная копия frontend/docs/api-contract/
первое касание зоны при разморозке; после него правило прежнее: расхождение = дефект лендинга.
Генерация типов фронта после этой ратификации идёт из ЭТОЙ копии.
20.08.2026: СИНК С ПЛАТФОРМОЙ (запрос platform/docs/CONTRACT_SYNC_FROM_PLATFORM.md, 14 пунктов) —
20.08.2026: СИНК С ПЛАТФОРМОЙ (запрос platform/docs/archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md, 14 пунктов) —
канон 0.3.0 → 0.4.0, разбор и диспозиция КАЖДОГО пункта в §6в, отдельным пунктом §6в п.0 — сверка
0.2.3 → 0.3.0 на молча срезанные при резке прозы правила (найдена одна, восстановлена).
Ратификацию этой редакции контрактная сессия за собой НЕ записывает — это акт оркестратора:
@ -893,7 +893,7 @@ the original request was never applied». Каждый `POST /books` созда
## 6в. Синк с платформой 20.08 → канон 0.4.0: что решено и почему
Вход — `platform/docs/CONTRACT_SYNC_FROM_PLATFORM.md`, четырнадцать мест, где провод сообщает
Вход — `platform/docs/archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md`, четырнадцать мест, где провод сообщает
состояние и не даёт клиенту действия. Записка честно разделяет ФАКТ / ⚠ МНЕНИЕ ПЛАТФОРМЫ / ГРАНИЦА,
и разбор шёл по этой границе: ФАКТы перепроверены исполнением, МНЕНИЯ проверялись на ПОСЫЛКУ, а не
только на предложенную форму. Два мнения посылку не выдержали (B, часть A), одно оказалось
@ -951,7 +951,7 @@ the original request was never applied». Каждый `POST /books` созда
`resumeRun`). `paused` до этой ветки не доходит — он отказан выше, поэтому молчаливый `202` живёт
ровно на `stopped` и `awaiting_bank`. Худший из двух — `awaiting_bank`: человек подписал банк,
нажал «продолжить», получил «принято», а `ReleaseBankStop` не вызывался (зона это записала сама,
`P7_ACCEPTANCE_HANDOFF.md` §7(и)).
`archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md` §7(и)).
**Номера строк в чужой зоне в этом разделе сняты намеренно** (находка ревью): дерево платформы
на 20.08 держит незакоммиченные правки, поэтому одна и та же строка в HEAD и в рабочем дереве
@ -1149,7 +1149,7 @@ announce-once-леджер: ключ `unit:<book>:<wave>:<chapter>:<unit>` не
### J. «Остановлено по вашей просьбе» — ПОЛЕ ЗАВЕДЕНО; форма отличается от предложенной зоной
Решение владельца 17.08 принято, форму делегировали контрактной сессии
(`P7_ACCEPTANCE_HANDOFF.md:452`). Заведено `Run.stop_requested` — **обязательное булево, а не
(`archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md:452`). Заведено `Run.stop_requested` — **обязательное булево, а не
необязательное.** Довод: функция тотальна — прогон, который никто не просил остановить, это `false`,
а не «поля нет»; необязательность дала бы два написания одного факта, и это ровно тот дефект, который
контракт уже чинил у `sense` (0.2.2), у `reject_reason` и у `kind`. Ценой стал ломающий минор —

View file

@ -8,7 +8,7 @@
- **Хендофф(-промт)** — передача роли новой сессии файлом-промтом; аддендумы к бегущей сессии доезжают только релеем владельца.
- **Владелец** — человек; ратифицирует деньги, скоуп-сдвиги, продукт, 18+; связной между сессиями.
- **Ф0Ф3** — фазы: Ф0 скелет · Ф1 инфраструктура · Ф2 качество (судья/гейты) · Ф2.5 пилот · Ф3 продукт (веб-фронт, ридер-IDE).
- **S0S7 / P0P7** — нумерация кодовых сессий зон: S-этапы фронта (S0S4 приняты, S4 — путь «файл→прогресс», D39.135; зона ЗАМОРОЖЕНА, и с 17.08 морозится ДАЛЬШЕ лендинга P7 — D39.147: P7 её не размораживает, S5 не выдаётся, разморозка отдельным словом владельца) · P-этапы платформы (P0 бутстрап D39.100 … P6 потребительская половина шва D39.132 · **P7 читающая поверхность — ЗАПУЩЕН 16.08**); не путать с фазами Ф0Ф3.
- **S0S7 / P0P7** — нумерация кодовых сессий зон: S-этапы фронта (S0S4 приняты, S4 — путь «файл→прогресс», D39.135; зона ЗАМОРОЖЕНА, и лендинг P7 её НЕ разморозил — D39.147: P7 её не размораживает, S5 не выдаётся, разморозка отдельным словом владельца) · P-этапы платформы (P0 бутстрап D39.100 … P6 потребительская половина шва D39.132 · **P7 читающая поверхность — ЗАПУЩЕН 16.08**); не путать с фазами Ф0Ф3.
- **Зонный журнал / зонный бэклог**`frontend/docs/frontend-PROGRESS.md` · `platform/docs/platform-PROGRESS.md` и BACKLOG зоны: прогресс/пинги фронта и платформы живут ТОЛЬКО там (D39.100); их строки единый бэклог не принимает (D39.84).
- **Полигон** — eval-зона платных/бесплатных экспериментов (`eval/`; отчёты — `docs/experiments/`, статус эксп-аего баннер/D-лог).
@ -26,7 +26,7 @@
- **Строка N** — стабильный ID строки единого бэклога (PROGRESS §Бэклог; не перенумеровывается); **вес** — блокер-очереди / скоро / когда-нибудь; **носитель** — артефакт, в котором обязательство живёт; **хвост** — незакрытый остаток; **петля** — вопрос, обязанный получить диспозицию; **слот** — зарезервированное место в будущем пакете/замере.
- **Гвоздь** — регрессионный тест, прибивающий контракт (негативный гвоздь — тест на ОТСУТСТВИЕ поведения).
- **Реестр 108 / «жилец реестра»** — строка 108 единого бэклога: реестр ЗАГЕЙЧЕННЫХ ТРИГГЕРОВ (отложенных реопенов с условием «когда вернуться»); «жилец» = такой реопен. «Каждое отложено получает строку бэклога или жильца реестра 108» — норма D39.66/D39.90.
- **Контракт 14 (API v0)** — ратифицированный контракт фронт↔платформа: `architecture/14-api-contract/` — нормативная спека OpenAPI 3.1 + компаньон-README; зонная копия `frontend/docs/api-contract/` — байт-зеркало (D39.99; ⚠ временно ОТСТАЁТ на 0.2.3 при каноне 0.3.0 — ратифицировано D39.142 п.5 на фриз фронта, синк при разморозке). **Провенанс ✓/◆/○** — классы утверждений компаньона: выведено из кода/ратификации · предложено (подтверждает названная зона) · открыто (К-вопросы). **spectral / openapi-typescript** — линт спеки и генерация типов фронта с дрифт-тестом (⚠ 3.1-условия генератор игнорирует — К-11).
- **Контракт 14 (API v0)** — ратифицированный контракт фронт↔платформа: `architecture/14-api-contract/` — нормативная спека OpenAPI 3.1 + компаньон-README; зонная копия `frontend/docs/api-contract/` — байт-зеркало (D39.99; ⚠ временно ОТСТАЁТ на 0.2.3 при каноне **0.4.0** (ратифицирован D39.152, 20.08) — ратифицировано D39.142 п.5 на фриз фронта, синк при разморозке). **Провенанс ✓/◆/○** — классы утверждений компаньона: выведено из кода/ратификации · предложено (подтверждает названная зона) · открыто (К-вопросы). **spectral / openapi-typescript** — линт спеки и генерация типов фронта с дрифт-тестом (⚠ 3.1-условия генератор игнорирует — К-11).
- **ПТ-N** — стабильный ID требования в `product-requirements.md`; не путать со строками единого бэклога. **paused / лимиты «как Claude Code»** — статус книги при стопе по потолку + страница статуса использования в настройках, сумм на экране нет (ПТ-35, D39.100).
## Пайплайн и код

View file

@ -364,7 +364,7 @@
| PD-270 | bug | info | `internal/httpapi/problem.go` `WriteProblem` | **`Problem` без `Code` уходил как 500 БЕЗ обязательного `code`.** Канон требует код на каждом ответе `/v0`, 500 включая; `omitempty` существует ради поверхности, которую канон не описывает, и протекал обратно. Живого вызова не было — латентная ловушка, бьющая по принципу самого файла («расходящаяся пара непредставима») — **закрыто:** пустой код → `internal_error`; пин `httpapi.TestAProblemWithNoCodeStillAnswersOne` | fixed(приёмка P7, дерево сессии) | приёмка P7 (воркфлоу 12 осей + кросс-модель) |
| PD-271 | bug | info | `internal/httpapi/conditional.go` `acceptsGzip`, `writeJSON` | **Договорённость о кодировании решалась по ПЕРВОМУ токену** (`*, gzip;q=0` читалось как согласие) и **HEAD не получал ни ETag, ни 304**, хотя маршрутизатор отдаёт ему тот же обработчик (Go 1.22+) — **закрыто:** именованное кодирование выигрывает у `*` в любом порядке; валидатор отвечает и на HEAD; пины `TestTheNamedCodingOutranksTheWildcardInEitherOrder`, `TestHeadCarriesTheSameValidatorAsGet` | fixed(приёмка P7, дерево сессии) | приёмка P7 (воркфлоу 12 осей + кросс-модель) |
| PD-272 | bug | info | `internal/pgstore/readmodel.go` `scope.tag`, `SubmitBankDecisions` | **Курсор не был привязан к КНИГЕ** (только к версии структуры) и принимался на другой книге; **решения банка** брали блокировки строк в порядке клиентского массива, и два пересекающихся сабмита взаимно блокировались — **закрыто:** книга входит в тег области; решения применяются в порядке `term_id`, отказ по-прежнему называет клиентский индекс; пин `pgstore.TestACursorMintedForOneBookIsRefusedOnAnother` | fixed(приёмка P7, дерево сессии) | приёмка P7 (воркфлоу 12 осей + кросс-модель) |
| PD-273 | standards | info | `internal/runs/reconcile.go` `outcome`, контракт §Run | **ЗАКРЫТ решением владельца 17.08: статус остаётся `awaiting_bank`, лечение — в КОНТРАКТЕ.** Разбор: нажать «стоп» на прогоне, УЖЕ стоящем в `awaiting_bank`, нельзя (`RequestStop` требует `finished_at is null`) ⇒ случай ровно один — гонка: стоп запрошен во время перевода, а движок в этом окне домайнил банк и вышел кодом 3. Оба состояния продолжаемы, но ярлык несёт ПРОВОДКУ: `ReleaseBankStop` вызывается только при `l.Status == "awaiting_bank"`, поэтому переименование в `stopped` оставило бы `bank_released = false`, вернуло бы `--verify-bank` на resume и **пере-открыло бы PD-277**. Плюс `awaiting_bank` информативнее: работа стоит на самой дешёвой точке — черновик и майнинг оплачены, редакторская волна не начата. **Настоящая дыра — ПРОВОД: `Run` не умеет сказать «остановлено по вашей просьбе»** (`status` + `stop_for_signing` = опция запуска, а не состояние), поэтому честную фразу клиент нарисовать не может. Строка бэклога для оркестратора — `P7_ACCEPTANCE_HANDOFF.md` §7(и) | closed (решение владельца 17.08) | приёмка P7 |
| PD-273 | standards | info | `internal/runs/reconcile.go` `outcome`, контракт §Run | **ЗАКРЫТ решением владельца 17.08: статус остаётся `awaiting_bank`, лечение — в КОНТРАКТЕ.** Разбор: нажать «стоп» на прогоне, УЖЕ стоящем в `awaiting_bank`, нельзя (`RequestStop` требует `finished_at is null`) ⇒ случай ровно один — гонка: стоп запрошен во время перевода, а движок в этом окне домайнил банк и вышел кодом 3. Оба состояния продолжаемы, но ярлык несёт ПРОВОДКУ: `ReleaseBankStop` вызывается только при `l.Status == "awaiting_bank"`, поэтому переименование в `stopped` оставило бы `bank_released = false`, вернуло бы `--verify-bank` на resume и **пере-открыло бы PD-277**. Плюс `awaiting_bank` информативнее: работа стоит на самой дешёвой точке — черновик и майнинг оплачены, редакторская волна не начата. **Настоящая дыра — ПРОВОД: `Run` не умеет сказать «остановлено по вашей просьбе»** (`status` + `stop_for_signing` = опция запуска, а не состояние), поэтому честную фразу клиент нарисовать не может. Строка бэклога для оркестратора — `archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md` §7(и) | closed (решение владельца 17.08) | приёмка P7 |
| PD-274 | doc | **major** | `deploy/README.md:80`, `docs/PLATFORM_DIRECTION.md:68` | **Деплой-нота включала обратно грант, который PD-104 выключил.** В копируемом блоке окружения стояло `TM_PLATFORM_SIGNUP_GRANT_USD=5`, то есть развёртывание по ноте давало каждой новой личности безлимитный самообслуживаемый грант; в `PLATFORM_DIRECTION.md` §2 «Дефолт $5» пережил правку первого абзаца. Плюс `TM_PLATFORM_LANGUAGE_PAIRS` не назван вовсе, а пустой список делает `/capabilities` и интейк противоположными — **закрыто:** строка снята с объяснением, пары внесены, оба текста актуализированы | fixed(приёмка P7, дерево сессии) | приёмка P7 (воркфлоу 12 осей + кросс-модель) |
| PD-275 | standards | minor | `internal/pgstore/readmodel_test.go`, `httpapi/reading_test.go`, `httpapi/problem_test.go` | **Три пина не проверяли того, что объявляли.** `TestAMissingBankReadOutSavesNothing` падал на шаг раньше (нет `book.yaml`) и в ветку `ErrNoBank` не входил; `TestALimitIsClampedOrIgnoredButNeverRefused` не мог наблюдать подрезку (фейк выбрасывал `limit`); `title(code) == ""` недостижимо ни для одного кода. Плюс подсистема `Idempotency-Key` целиком без тестов — **закрыто:** три пина переписаны на наблюдаемое свойство, подсистема ключей закрыта семью тестами, карта замечаний (`ingest/notes.go`) — тремя | fixed(приёмка P7, дерево сессии) | приёмка P7 (воркфлоу 12 осей + кросс-модель) |
| PD-276 | bug | info | `internal/books/parse.go` `Parse` | **Материализация на границе интейка шла на контексте задачи,** уже потраченном разбором: на большой книге `Refresh` (ещё два процесса движка) не укладывался, и дерево оставалось пустым, а свип этого не повторяет — **закрыто:** свой отвязанный бюджет, как на границе прогона. Бэкстоп-свип для книг с `chapter_count > 0` и пустым деревом **построен доработкой 20.08** (`books.materializeMissingTrees`, `pgstore.BooksWithNoTree`; пин `TestABookWhoseTreeNeverLandedIsMaterializedByTheSweep`) ⚠ **ЭРРАТА (акт 5): названный здесь бэкстоп и его пин БОЛЬШЕ НЕ СУЩЕСТВУЮТ.** `books.materializeMissingTrees`, `pgstore.BooksWithNoTree` и `TestABookWhoseTreeNeverLandedIsMaterializedByTheSweep` удалены вместе с механизмом, который их заменил: долг на материализацию стал колонкой (PD-302), и он покрывает не только «дерева нет вовсе», но и «дерево на границу старше». Свойство закрыто, доказательство переехало: `books.TestAParsedBookOwesAReadingSurfaceUntilOneIsMaterialized` (плюс проверка, что метка ставится ТОЙ ЖЕ транзакцией, что и конец разбора) | fixed(приёмка P7 + доработка 20.08, дерево сессии) | приёмка P7 (воркфлоу 12 осей + кросс-модель) |

View file

@ -424,6 +424,10 @@ export TM_PLATFORM_TEST_DSN='postgres://postgres@/postgres?host=/tmp&port=55432&
- **Красный `runner.TestARunIsBoundedByItsOwnCgroup` — обычно НЕ регрессия кода:** у `tm.slice`
опустел `cgroup.subtree_control`. Лечение без root: `echo "+memory +pids" >
/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/tm.slice/cgroup.subtree_control`.
- **Демон нельзя убивать `pkill -f <путь к бинарю>`** — шаблон совпадает с собственной командной
строкой оболочки, и она убивает сама себя (exit 144). Убивать по PID.
- **Postgres стенда переживает не всё:** сокет живёт в `/tmp`, и уборка `/tmp` (или smart shutdown)
роняет соединение — `pg_ctl … status` скажет «no server running». Поднимать заново по разделу выше.
- **Замер страницы библиотеки** (когда трогаете проекции книги): `go test ./internal/pgstore/
-bench LibraryPage -run xxx` — корпус 40 книг × 500 глав собирается сам, `vacuum analyze` внутри.

View file

@ -1,6 +1,6 @@
# Промт: контрактная сессия — синк с платформой по API v0
> ✅ **ОТВЕЧЕНА ЦЕЛИКОМ 20.08 контрактной сессией; канон стал 0.4.0 (в дереве контрактной зоны, НЕ ратифицирован).** Все 14 пунктов имеют диспозицию; работа, которая пришла зоне (A · E · G · J), исполнена актом 5. Документ ИСТОРИЧЕСКИЙ — это вход другой сессии, а не задача. Что из ответа стало кодом — `platform-PROGRESS.md`, шапка; зависимость от ратификации — PD-327.
> ✅ **ОТВЕЧЕНА ЦЕЛИКОМ 20.08 контрактной сессией; канон 0.4.0 РАТИФИЦИРОВАН 20.08 — D39.152** (испр. оркестратором №18 при архивации: баннер писался до ратификации). Все 14 пунктов имеют диспозицию; работа, которая пришла зоне (A · E · G · J), исполнена актом 5. Документ ИСТОРИЧЕСКИЙ — это вход другой сессии, а не задача. Что из ответа стало кодом — `platform-PROGRESS.md`, шапка; зависимость от ратификации — PD-327.
> Составила зона «Платформа» 20.08.2026 по слову владельца. Это ЗАПРОС, не проект правки: зона
> называет места, где провод не даёт клиенту сделать то, что нужно пользователю, и говорит, что она

View file

@ -1,5 +1,10 @@
# Пак P7 — приёмка, правки, приёмка правок, доработка. Хендофф
> ⚠ **АРХИВ (оркестратор №18, 20.08).** Пак P7 принят и заленден (D39.153, `9b23e8c`), сессия закрыта владельцем. Документ ИСТОРИЧЕСКИЙ — рабочий процесс приёмки актов 14; инструкции НЕ исполнять.
>
> Живое из него ВЫНЕСЕНО, чтобы не держать второй носитель: рецепт стенда и грабли (§2) живут в `platform/docs/STACK_DECISIONS.md` (две уникальные грабли дописаны туда при архивации) · релеи §7 — строка **203** единого бэклога, статус каждого сверен грепом и половина исполнена · строка про «остановлено по вашей просьбе» (§7и) закрыта полем `stop_requested` канона 0.4.0 · строка в бэклог движка (§7в) заведена как **204**. ⚠ §0 этого файла объявляет «вопросов на владельце нет» — это НЕВЕРНО и поймано ревью: PD-255 просит именно решение владельца, оно вынесено в CURRENT-STATE.
> ⚠ **РАБОЧИЙ ДОКУМЕНТ СЕССИИ, ОРКЕСТРАТОРУ ЧИТАТЬ НЕ НУЖНО.** Это мой процесс: как я шёл, что проверял, что передумал. Для лендинга нужны три вещи, и все они в других местах — что сделано и зачем: шапка `platform-PROGRESS.md` · статус каждого дефекта: `DEFECT_REGISTER.md` · правила, переживающие пак: `STACK_DECISIONS.md` §3336.
>
> Составлен 17.08.2026 сессией приёмки платформы; **акт 4 — доработка 20.08** (этот же документ).

View file

@ -1,5 +1,10 @@
# Пак P7, акт 5 — исполнение фиксов по ревью акта 4
> ⚠ **АРХИВ (оркестратор №18, 20.08).** Задача закрыта целиком: акт 5 исполнен, пак P7 принят с фикс-листом и заленден (D39.153, `9b23e8c`). Документ ИСТОРИЧЕСКИЙ — рабочий процесс сессии; инструкции НЕ исполнять.
>
> Живое из него переехало: замер «636 мс против 24 мс» (§ про `note_count`) — единственный носитель числа 96% в `perf_test.go`, разбор — D39.153 п.2; фикс-лист приёмки — пинг оркестратора №18 в `platform-PROGRESS.md`.
> ⚠ **РАБОЧИЙ ДОКУМЕНТ СЕССИИ, ОРКЕСТРАТОРУ ЧИТАТЬ НЕ НУЖНО.** Это мой процесс: как я шёл, что проверял, что передумал. Для лендинга нужны три вещи, и все они в других местах — что сделано и зачем: шапка `platform-PROGRESS.md` · статус каждого дефекта: `DEFECT_REGISTER.md` · правила, переживающие пак: `STACK_DECISIONS.md` §3336.
>
> Составлен 20.08.2026 сессией платформы ДЛЯ СЕБЯ ЖЕ после компакции контекста.

View file

@ -0,0 +1,250 @@
# Архив журнала зоны «Платформа» — эра пака P7 (1620.08.2026)
> ⚠ **СРЕЗ, выселен оркестратором №18 при лендинге P7 (20.08, D39.153)** — по образцу срезов `platform-PROGRESS-P0-P3.md` и `-P4-P6.md`. Эра ЗАКРЫТА: пак исполнен пятью актами, принят с фикс-листом и заленден (`9b23e8c`), сессия закрыта владельцем.
>
> **Не читать при онбординге.** Живое состояние зоны — шапка `platform-PROGRESS.md`; статус каждого дефекта — `DEFECT_REGISTER.md` (источник истины); правила, пережившие пак, — `STACK_DECISIONS.md` §3336; фикс-лист приёмки и то, что ушло в единый бэклог, — пинг оркестратора №18 там же в живом журнале.
## Сессия P7 (1617.08): читающая поверхность контракта 0.3.0
Промт — `PLATFORM_P7_SESSION_PROMPT.md`. Ниже: записка-план по §4 (каждый пункт с диспозицией),
команды, живые пробы, находки, obstacle.
### Записка-план: пункт → исполнено / отказ → улика
| § | Пункт | Статус | Улика |
|---|---|---|---|
| 0 | Рантбук деплоя живым `migrate` | **исполнен** | БД проекта отведена на v14 при бинаре v15 → `tmctl status --json` = exit 13, токен `schema_mismatch found=14 expected=15`; `tmctl migrate` → бэкап + v14→v15; тот же `status` = exit 0. Мёртвая цитата вычищена (`deploy/README.md`, П-1 бэклога), пример в `tmplatformctl/runs.go` переписан на ВЕРСИОНИРОВАННЫЙ путь |
| 0 | `tmplatformctl books --migratable``tmctl migrate` end-to-end | **исполнен** | 5 книг стенда, все `MIGRATE=yes`, цикл из деплой-ноты отработал |
| 1а | Читать `BookIntake.title` | **исполнен** | `books.Intake.Title`; пустая строка = «назови по файлу» (`books.title()`), непустая не перезаписывается разбором |
| 1б | Проецировать `reject_reason` | **исполнен** | `httpapi.contractRejectReason`; `parser_unavailable``processing_failed` (ФБ-9: имя значения — то, на что клиент вешает фразу) |
| 2 | PD-104: грант → 0 + тест | **исполнен** | `config.go` дефолт 0; пин `config.TestSignupGrantIsParsedNotGuessed` пинит ИМЕННО ноль; протухшие «$5» вычищены из `PLATFORM_DIRECTION.md` §2, `BACKLOG.md` П-7, регистра |
| 3 | Модель ошибок по карте А-2 | **исполнен** | `httpapi/problem.go`: 16 корневых кодов, статус ВЫВОДИТСЯ из кода (пара «код+статус» непредставима расходящейся), `cause` открытый, `errors[]`, `request_id` из `reqid`; `WWW-Authenticate` на 401; две причины 403 разведены в `cause` при одном корневом `forbidden`; класс 2 — один код без вариации |
| 4 | `listChapters` · `listUnits` (пер-главно, 410) · `listNotes` · `listBankTerms` · `submitBankDecisions` · дельты · SSE · `structure_version` · gzip · ETag/304 | **исполнен** | `httpapi/{reading,stream,conditional}.go`, `pgstore/{readmodel,events}.go`; ручки «вся книга юнитами» НЕТ; `Unit.notes` массивом; агрегаты банка на ПЕРВОЙ странице; дельты ВКЛЮЧИТЕЛЬНЫЕ; `version_too_old` и `cursor_invalid` — причинами |
| 4 | Ловушка `heading` | **исполнен** | поле не декодируется ВОВСЕ (`ingest.ManifestChapter`), колонка `chapters.heading` удалена миграцией, проекция отдаёт `null`; пин `readmodel.TestTheManifestsRenderedHeadingHasNowhereToLand` |
| 4 | Демонтаж построенного гейта полноты банка | **исполнен** | `runs/reconcile.go`: `awaiting_bank` продолжается при ЛЮБОМ состоянии решений; пин `TestResumeLiftsABankStopWithTheDecisionsAsTheyStand`. Граница паузы движка сверена чтением (`pipeline/mining.go`: авто-продолжение с неподписанным банком — дефолт) |
| 4 | Пофазность у главы НЕ строить | **соблюдён** | у главы один счётчик `units_done`; пин «фазовый счётчик не доехал до строки главы» |
| 4 | `Note.code` из ◆-карты | **исполнен с ОДНИМ отступлением** | `httpapi/notes.go`; отступление — незнакомая причина проецируется как `unspecified` (в карте колонка кода пуста, а поле обязательно). Заведена строка PD-246, ждёт ратификации вместе с фразами |
| 5 | `Progress` с книги на `Run`, одна полоса в ГЛАВАХ | **исполнен** | `pgstore.Progress{Done,Total,ETASeconds}`, знаменатель — `ceiling_chapters`; сегмент выбирается `verify_bank && !bank_released` |
| 5 | `Book`: +`revision`, +`chapters_done`, `progress`, `genre` | **исполнен** | `genre` снят и с ПРОВОДА, и из формы интейка, и из колонки: платформа его больше не знает, значение шаблона оператора остаётся его |
| 5 | `Run`: +`book_id`, +`failure_reason`, `verify_bank``stop_for_signing` | **исполнен** | словарь из трёх значений мапится с полосы 1019 и веток `outcome()` (`runs.failureReason`) |
| 5 | `Usage`: `halt_reason` со своим словарём | **исполнен** | `httpapi.contractHaltReason`; прогонная причина больше не может доехать до аккаунтного поля |
| 5 | `GET /capabilities` | **исполнен** | все шесть полей; пары — из конфигурации (`TM_PLATFORM_LANGUAGE_PAIRS`), не список в Go |
| 5 | `limit` подрезать, а не отвергать | **исполнен** | `pgstore.clampPage`; отвергается только значение, которое не является размером страницы |
| 5 | Трейлинг-части интейка → 400 | **исполнен, с находкой** | сначала книга ОСТАВАЛАСЬ при 400 (живая проба); теперь часть после файла ПРОВАЛИВАЕТ чтение, и интейк откатывает загрузку своим же путём |
| 5 | `Location` на 201 | **исполнен** | `/v0/books/<id>` |
| 5 | `Idempotency-Key` на `createBook` и `startRun` | **исполнен** | таблица 00017, повтор отдаёт ИСХОДНЫЙ ответ, другой запрос — 409 `key_reused`, гонка — `key_in_flight` + `Retry-After`, >255 → 400 |
| 5 | `blocked {code, book_id}` на run-options и 409 | **исполнен** | `pgstore.CreditHeldBy` + `runs.CreditHeldError` |
| 5 | Отказ по неподдерживаемой паре на интейке | **исполнен** | `books.ErrUnsupportedPair``errors[].code: unsupported_pair`; пустой список пар = деплой не объявляет ничего и пару не проверяет |
| 5 | `finalizing` вон из словарей | **исполнен** | Go-свитчи и CHECK-констрейнты обеих таблиц |
| 6 | PD-172/173/174/180/199 привести · PD-201 · PD-241 | **исполнен** | все закрыты либо сужены с уликой; PD-241 — перевёрнутый порядок в `outcome()` с пином |
| 6 | Кандидаты PD-219/217/162 | **отсуждены** | PD-217 — половина ЗАКРЫТА (paused больше не блокирует апгрейд движка); PD-219 и PD-162 — не брались, с названной причиной |
| 6 | Находки §9 research/28 | **отсуждены** | три взяты строками (PD-249/250/251/252), две сведены к существующим (PD-203, PD-162/244) |
| 6 | Инструменты: sqlc / oapi-codegen | **отказ с доводом** | оба НЕ взяты; довод — `PLATFORM_DIRECTION.md` §3, и он опирается на замер этого пака, а не на вкус |
### Команды (каждое число отчёта — этой командой)
```sh
# батарея, оба гейта, -race
export TM_PLATFORM_TEST_DSN='postgres://postgres@/postgres?host=/tmp&port=55433&sslmode=disable'
export TM_PLATFORM_TEST_ENGINE_BIN=<путь к tmctl> TM_PLATFORM_TEST_BOOK_TEMPLATE=<шаблон>
make check # EXIT=0, 18 пакетов, скипов 0, линтер 0 issues
grep -rh '^func Test' --include='*_test.go' . | wc -l # 458 (HEAD: 416)
git diff --stat platform | tail -1 # 46 файлов, +2329 646
python3 docs/scripts/counts.py # регистр: 256 строк, 63 открытых
```
⚠ Стенд Postgres на этой машине пришлось поднять с нуля (его не было): рецепт `STACK_DECISIONS.md`
§«Postgres без root», micromamba + `postgresql=18.4` в `~/.local/pgsql`, порт 55433 — отработал
дословно.
### Живые пробы на дев-стенде (демон + сид + настоящий `tmctl`)
Сид P6 отработал целиком: дев-вход → грант → загрузка через ЖИВОЙ `POST /v0/books` → разбор →
`not_started, 3 главы`. Дальше — курлом по новым ручкам:
- `GET /capabilities` → все шесть полей, `contract_version: 0.3.0`, пары `zh>ru available` +
`ja>ru unavailable`;
- `GET /books` → `revision · reject_reason: null · structure_version: 1 · chapters_done: 0 ·
character_count: 987`, **никакого `progress` и никакого `genre`**;
- `GET /books/{id}/chapters` → три главы, `heading: null` у каждой;
- `GET /books/{id}/chapters/{ch}/units` → пара с ИСХОДНЫМ ТЕКСТОМ (канал `tmctl export --pairs`
работает), `target: ""`, `state: pending`, `notes: []`;
- `GET /books/{id}/notes`, `/bank` → пустые с агрегатами и `structure_version`;
- SSE на книге в покое → `hello id=4``end id=4` → закрытие; реконнект с `Last-Event-ID: 6`**204**;
- `ETag``304` на `If-None-Match`; `Accept-Encoding: gzip``Content-Encoding: gzip` + `Vary`;
тот же заголовок на `/events`**не сжато**;
- ошибки: 404 → `code: not_found` + `request_id`; 401 → `WWW-Authenticate: Bearer realm="textmachine"`;
POST без `X-TM-Client` → 403 `forbidden` · `cause.code: client_header_missing`;
- интейк: пара `ja→ru` → 400 `unsupported_pair` на обоих полях; часть после файла → 400
`missing_or_late` **и книга не остаётся** (проверено счётом библиотеки до/после);
- `Idempotency-Key`: тот же ключ дважды → один и тот же `Location`; тот же ключ с другим `title`
409 `idempotency_conflict` · `cause.code: key_reused`.
### Что нашли ПРОБЫ, чего не нашли тесты
**Часть формы после файла оставляла книгу.** Отказ 400 приезжал (правило соблюдалось), но
`intake.Accept` к тому моменту уже создал строку и каталог — пользователь получал ошибку И книгу в
библиотеке. Починено не новым кодом отката, а ПРОВАЛОМ чтения: часть после файла возвращает ошибку
из ридера, и интейк отменяет загрузку тем же путём, которым отменяет оборванную. Пин
`httpapi.TestAPartAfterTheFileIsRefusedAndTheBookIsNotKept`, пере-проверено живьём.
### Адверсариальное селф-ревью финалом: три ревьюера, таблица находок
Механизм по §5 промта: три независимых субагента по ФИНАЛЬНОМУ диффу, author≠reviewer, **два из
трёх — другой модели** (кросс-семейная линза, а не вторая своя). Ноль находок не случился — и это
хорошо: две HIGH нашли ОБА контрактных ревьюера независимо, что и есть подтверждение.
| # | Линза | Находка | Вердикт | Что сделано |
|---|---|---|---|---|
| Р-1 | контракт (кросс-модель) · деньги/шов | **Кадр `note` уносил движковую причину** (`glossary_miss`, `hard_refusal`) и не нёс обязательных `severity`/`code`; `unit_id: null` вместо отсутствия | ПОДТВЕРЖДЕНА, HIGH | Карта переехала в `internal/ingest/notes.go` — один дом на оба потребителя; кадр строится через неё; `unit_id` опускается. Пин `TestTheNoteFrameCarriesTheContractsNoteAndNotTheEnginesReason` |
| Р-2 | контракт (кросс-модель) | **Кадр `status` уносил внутренние словари** (`daily_ceiling`, `parser_unavailable`): та же величина читалась `null` на карточке и внутренним словом в потоке | ПОДТВЕРЖДЕНА, HIGH | Переводы переехали в `internal/ingest/vocabulary.go`, кадр строит `emitStatus`; при ошибке чтения кадр НЕ шлётся (вместо `status: null` вне схемы). Пин `TestTheStatusFrameCarriesTheContractsVocabularyOrNothing` |
| Р-3 | деньги/шов | **DOWN-путь миграции неисполним при первой же строке банка** (воспроизведено живым PG): три словаря переписывались без перевода данных, а goose гоняет миграцию одной транзакцией | ПОДТВЕРЖДЕНА, HIGH | Порядок drop → перевод → add в ОБЕ стороны (UP тоже: он должен переживать пере-накат после отката). Пин `TestARollbackSurvivesTheDataTheNewVocabulariesWrote` — откат до документированного пола (версия 5) на базе С данными |
| Р-4 | деньги/шов | **Четыре писателя меняли статус без кадра** (`RestartRun`, `PauseRun`, `FinishUnspawnedStop`, `ReleaseBankStop`); двое из них ставят `finished_at`, то есть книга «в покое» — и следующий реконнект получает **204 «не переподключайся»** при устаревшем статусе у клиента | ПОДТВЕРЖДЕНА, HIGH | Кадр у всех трёх писателей статуса; четвёртый (`ReleaseBankStop`) перестал быть отдельным писателем — см. Р-5 |
| Р-5 | деньги/шов | **`ReleaseBankStop` коммитился ДО `reopen`**: резюм, который ничего не сделал (расчёт в полёте, бюджет исчерпан), всё равно переключал полосу с N/N на 0/N | ПОДТВЕРЖДЕНА, MED | Снятие стопа перенесено ПОСЛЕ успешного `reopen` |
| Р-6 | деньги/шов | **`Refresh` тратил бюджет прохода ПЕРЕД `settle`**: на большой книге два полных ре-чанка съедали проход, расчёт уходил в «отложено», холд висел свип | ПОДТВЕРЖДЕНА, MED | Деньги первыми: `settle` → затем `refreshReadModel` на СВОЁМ ограниченном контексте, ошибка громкая |
| Р-7 | деньги/шов | **`failureReason` игнорировал маркер**, который ему передавали: OOM и kill по стоп-таймауту приезжали `interrupted` («повтори») | ПОДТВЕРЖДЕНА, MED | Читает `m.Result`; пин `TestWhyARunFailedDecidesWhetherARetryIsWorthOffering` (7 веток) |
| Р-8 | контракт (кросс-модель) | **Квитанции `Run` несли нулевую полосу** (`202` на старт/стоп/резюм) со свежей ревизией — клиентский гард пропускал её и полоса откатывалась к 0/0 | ПОДТВЕРЖДЕНА, MED | `StartRun`/`RequestStop`/`ReadRun` считают ту же полосу, что карточка |
| Р-9 | контракт (кросс-модель) | **Полоса ВТОРОГО прогона считала главы всей книги**: дробь стартовала выше нуля и могла превысить единицу | ПОДТВЕРЖДЕНА, MED | Колонка `runs.chapters_before` — база сегмента, снимается в том же стейтменте, что создаёт прогон; `least(…, ceiling_chapters)` закрывает второй край. Пин `TestASecondRunsBarStartsAtZeroOverAHalfFinishedBook` |
| Р-10 | контракт (кросс-модель) | **`Last-Event-ID: 0` после подрезки буфера молча пропускал дыру** — а `0` это легальный id, который сервер сам выдаёт | ПОДТВЕРЖДЕНА, MED | Условие `last+1 < Oldest` вместо `last > 0 && last < Oldest` |
| Р-11 | контракт (кросс-модель) · скоуп | **Склейка кадров `chapter` шла по имени события**, теряя счётчик соседней главы внутри одного соединения | ПОДТВЕРЖДЕНА, MED | Склейка по (событие, id сущности) |
| Р-12 | контракт (кросс-модель) | **Порядок банка** был `(src, id)` — то есть по ХЕШУ, а канон объявляет «по поверхности, затем по окну» | ПОДТВЕРЖДЕНА, MED | Порядок и кейсет приведены к канону (`src, since, until, id`) |
| Р-13 | контракт (кросс-модель) | **`limit` всё ещё отвергался снизу** (0, отрицательный, нечисло), тогда как канон освобождает параметр от отказа | ПОДТВЕРЖДЕНА, MED | Никогда не отвергается: сверху подрезка, всё прочее — дефолт деплоя. Пин переписан |
| Р-14 | контракт (кросс-модель) | **Неверная пара языков и слишком длинное поле отказывали без `errors[]`** | ПОДТВЕРЖДЕНА, MED | `ErrMalformedLanguage` и `too_long` с указателем на поле |
| Р-15 | контракт (кросс-модель) | **`SweepIdempotency` не имел вызывающего**: ключи не забывались никогда | ПОДТВЕРЖДЕНА, LOW | Подключён в свип демона, окно 25 часов |
| Р-16 | контракт (кросс-модель) | **Курсор замечаний нёс движковое слово волны** в открытом виде | ПОДТВЕРЖДЕНА, LOW | Пакуется порядковым номером |
| Р-17 | скоуп (кросс-модель) | `acceptsGzip` игнорировал `*`; `pump` спал такт даже после полной пачки; `unknownTermPointer` парсил текст ошибки; алиас `itoa` | ПОДТВЕРЖДЕНЫ, LOW | Все четыре починены (типизированная `UnknownTermError` вместо парсинга строки) |
| Р-18 | скоуп (кросс-модель) | **`sqlc` был привязан к этому паку ратификацией**, а пак от него отказался — это отступление, а не решение зоны | СПРАВЕДЛИВО | Отказ с доводом записан в `PLATFORM_DIRECTION.md` §3 и вынесен ВОПРОСОМ оркестратору (ниже) |
| Р-19 | скоуп (кросс-модель) | **`unspecified` — изобретённый код** там, где промт требовал пинг | ЧАСТИЧНО: разбор с владельцем 17.08 показал, что находка верна по форме и неверна по существу | Значение отдано ветке, которую канон УЖЕ ратифицировал («нейтральная фраза для незнакомого кода»), а не заводит новое правило; настоящий предмет — рукописная копия чужого словаря без гейта, PD-246 пере-сформулирован, движку — пинг про публикацию причин данными |
| Р-20 | контракт (кросс-модель) | `410` отвечает и на главу, которой никогда не было: различение 410/404 без надгробий неисполнимо | ПРИНЯТО КАК ЕСТЬ | Лечение у обоих одно (перечитать дерево); строка PD-253 |
| Р-21 | скоуп (кросс-модель) | **Масса комментариев** выросла против нормы зоны (миграция на 250 строк — половина проза) | ЧАСТИЧНО ПРИНЯТО | Подрезано самое тяжёлое; остальное — предмет пинга, а не тихой правки |
| Р-22 | скоуп (кросс-модель) | **Два писателя ошибок** (`WriteProblem` + `WriteStatusProblem`/`codeForStatus`), три одинаковых структуры пары | НЕ БРАЛОСЬ | Второй писатель обслуживает НЕконтрактную поверхность (`/auth`, `/readyz`), где кода версии нет; сведение в один требует ратификации формы для 405/429 — вопрос оркестратору |
**Что ревьюеры подтвердили как ВЕРНОЕ** (важно не меньше находок): ни одной денежной величины на
проводе ни в каком виде · порядок блокировок не тронут, `emitFrame` не берёт НОВЫХ локов ни в одной
из 14 точек · кадр не может быть выпущен для откаченного состояния · демонтаж гейта банка
соответствует таблице канона дословно · словари шва (шесть значений банка, полоса отказов 1019,
все семь типов событий) сверены по коду движка и полны · `finalizing` снят безопасно · оба гейта
миграций пере-считаны и совпадают.
### Живые пробы ПОСЛЕ фикс-раунда (стенд пере-создан)
Стенд пришлось пере-создать, и это отдельная улика: миграция 00016 правилась ПОСЛЕ того, как была
применена, а goose ключуется номером — отредактированный файл не перезапускается никогда. На стенде
лечится сносом базы; в проде именно от этого стоит гейт `migrations.sha256` (обе строки пере-считаны).
- `limit=0 | -3 | abc | 100000`**200 во всех четырёх** (канон освобождает параметр от отказа);
- квитанция старта: `status: translating`, `progress {done:0, total:2}`, `book_id`, `failure_reason: null`
— полоса едет с квитанцией, а не только с карточкой;
- поток при ЖИВОМ прогоне: `hello id=5``status id=7` с `failure_reason: "service_error"` и
`paused_reason: null` — кадр несёт КОНТРАКТНЫЙ словарь (внутреннего слова в кадре нет);
- карточка после отказа: книга `failed`, прогон `failed` + `service_error` (на стенде нет ключей
провайдера — движок отказывает конфигурацией, и это ровно тот класс, который «повтор не лечит»);
- `resume` упавшего прогона → `run_not_resumable` без `cause` (канон: это не остановленный прогон);
- неверный код языка → `invalid_request` + `errors[]` на оба поля;
- `/usage``halt_reason: null` (свой словарь, не прогонный).
### Obstacle: что НЕ удалось и что не проверено
1. **Отложено в P8 явно** (заказ промта): `updateBook` · `deleteBook` · `getRun` · `createExport`
/`getExport` · эскроу (П-18).
2. **`Note.code` для незнакомой причины — отступление**, не решение: `unspecified` (PD-246).
3. **Ни одного живого ЗАМЕЧАНИЯ и ни одного термина банка через настоящий прогон не прошло**
прогон стоит денег, а сессия $0. Проекции замечаний и банка проверены тестами на живом PG и
ручными строками `unit_resolutions`, но не сквозным прогоном движка.
4. **SSE с ЖИВЫМ прогоном не гонялся** по той же причине: проверены книга в покое, реконнект, 204,
склейка и `resync_required` — тестами и на стенде, но не под настоящим потоком кадров.
5. **Стоимость материализации не замерена на большой книге** (PD-248): два полных ре-чанка на
границу работы; на демо-книге в 3 главы это миллисекунды, на 2283 главах — секунды CPU.
6. **`structure_version` на РЕАЛЬНОЙ пере-нарезке** проверен тестом (смена ключа манифеста), но не
настоящей сменой чанкера движка.
7. **Опрос потока — 2 запроса/с на соединение** (PD-247): `LISTEN/NOTIFY` не строился.
8. **Кадр `note` живьём не наблюдался**: он рождается только на помеченной единице настоящего
прогона. Проверен тестом на живом PG через настоящий `RunSink.Apply` (полный путь материализатора),
но не в потоке под браузером.
9. **Полоса под настоящей работой не наблюдалась**: `progress {0,2}` — это старт; движения счётчика
на стенде без ключей провайдера не бывает.
### Вопросы оркестратору (канал вопросов промта, НЕ тихая интерпретация)
1. **Словарь флаг-причин — рукописная копия чужой зоны без гейта** (PD-246; формулировка исправлена
после разбора с владельцем 17.08 — прежняя, «в карте контракта дыра», была неверной).
Проверено чтением движка: локализованных фраз там НЕТ вовсе — `refusal.go` и `status.go` несут
машинные классы, русских пользовательских строк в продовом коде движка ноль. То есть движок и
контракт построены на одном принципе, и переделывать под «гугловский словарик в движке» нечего:
фраза всегда на языке ИНТЕРФЕЙСА, которого не знает ни движок, ни платформа, а движок ОДИН на все
пары — русская строка в нём была бы таргет-спецификой в общем слое (тот же инвариант, что снял
`ruby` с провода).
**Проводу окно закрыто:** причина вне карты едет кодом `unspecified`, а канон уже предписывает
клиенту нейтральную фразу для незнакомого кода — значение отдано РАТИФИЦИРОВАННОЙ ветке, а не
изобретено правило. На подпись остаются две строки продуктового текста: фраза для `unspecified` и
граница ступеней `attention`/`glance` (сегодня — догадка платформы, колонка «Ступень» в
приложении А пуста у всех 15 строк). Это не блокер.
**Правка контракта — предлагаю, зона не моя (готовые формулировки, вставить как есть).**
*Обязательный минимум — КОМПАНЬОН, приложение А, последняя строка (`README.md:899`).* Пустая
клетка кода в ней и есть то, что заставило платформу выбирать значение самой:
> `| незнакомая причина | 8 (по умолчанию) | `unspecified` — стабильный плейсхолдер деплоя: не значение словаря, а ОБЯЗАННОСТЬ сервера ответить одним и тем же кодом на причину, которой его сборка карты не знает (носитель — PD-246 платформы) | ⬜ нейтральная, НЕ «ошибка» | ⬜ |`
*Желательное — ОДНА фраза в канон*, к описанию `Note.code` (`openapi.yaml:1693-1700`), после
абзаца про нейтральную фразу:
> A deployment that meets a reason its build of the map cannot name answers the stable placeholder `unspecified` rather than the producer's own word, so the branch above is exercised by a real value in ordinary operation and not only in theory.
Форма выбрана намеренно: это ОБЯЗАННОСТЬ СЕРВЕРА, а не запись словаря — канон отказывается
перечислять `Note.code` («freezing a list in the schema before the words exist would make it a
second copy»), и перечисление начинать нельзя. Ценность ровно одна и она процессная: без строки в
нормативном документе значение живёт только в Go платформы, и следующая сессия изобретёт своё.
**Бампа версии не требует, и вот почему точно:** `Note.code` объявлен `type: string`, не enum, —
ни один генерённый тип не сужается и не расширяется; а сама фраза описывает поведение, которое
ЕДИНСТВЕННЫЙ потребитель 0.3.0 (этот пак) уже реализует, то есть сломать клиента, сгенерированного
по 0.3.0, она не может. ⚠ Прецедент D39.144 (микро-правка канона без бампа) здесь опорой НЕ
служит: там довод был «спека ещё никем не потреблена», и он больше не верен. Цена, которую надо
назвать: описания компилируются в исходники клиента, значит фраза доедет до `schema.ts` — то есть
зеркало фронта и регенерация типов при разморозке. И пара «код нормативен, фраза ⬜» должна
остаться видимой, иначе следующий читатель решит, что карта опять сломана.
**⚠ ПИНГ ОРКЕСТРАТОРУ — строка в бэклог ДВИЖКА (сама зона в чужой бэклог не пишет):** публиковать
список флаг-причин ДАННЫМИ — артефакт рядом с манифестом либо `tmctl flag-reasons --json` ($0,
таблица `flagReasonSeverity` уже существует). Тогда тест платформы читает его и ПАДАЕТ, если в
карте нет строки, и класс «словарь разъехался тихо» закрывается насовсем. Со стороны платформы
гейт невозможен: импорт `backend/internal` запрещён ревью-гардом модулей (D39.85). Обратный
вариант — чтобы движок эмитил сразу контрактный код — отвергнут: это зеркальная утечка,
продуктовое слово поехало бы в движок, который о контракте знать не должен.
2. **`sqlc` — ОТЛОЖЕН СЛОВОМ ВЛАДЕЛЬЦА 17.08** («отложим»), строка PD-44 дополнена разбором.
Фиксирую честно, включая довод против себя: пак был обязан его взять (D39.132 п.2б), и sqlc
поймал бы ровно тот класс, который дважды укусил этот пак в рантайме (`stop_for_signing`,
`chapters_before`) — батарея его поймала, но на цикл позже компилятора. Что мешает в read-модели:
запросы собраны склейкой общих фрагментов (одна проекция книги на четыре пути), а sqlc требует
литерального SQL на запрос, и страница с её ревизией — транзакция, которую он не моделирует.
Предложение зоны: взять в P8 на отложенных однооператорных ручках, по итогам решить про
read-модель.
3. **Поверхность входа: РЕШЕНО контрактной сессией 17.08 (релей владельца), механизм исполнен.**
Вердикт — вариант A уточнённый: `/auth/*` отвечает тем же конвертом и **без машинного `code`
вовсе**; клиент показывает одну нейтральную фразу, а единственный пользовательски осмысленный
случай — `429` — уже машинный без всякого словаря, потому что лечение едет в `Retry-After`
(платформа его отдаёт: `login.go:173,233`, `dev.go:138`). Отвергнуты: расширение `ErrorCode`
(значения, недостижимые на поверхности, которую этот словарь описывает, ломают инвариант «код
называет свой статус») и собственный словарь в компаньоне (сделал бы НЕнормативный документ
нормативным с чёрного хода — канон в шапке объявляет нормативным для ФОРМЫ только себя).
**Исполнено в этом дереве:** `codeForStatus` и обратная функция удалены — писатель входа берёт
статус в точке вызова и кода не эмитит, поэтому инвертировать больше нечего. Пин
`httpapi.TestTheSignInSurfaceAnswersTheSameEnvelopeWithoutAVersionedCode` (шесть статусов: конверт
и `request_id` есть, `code` нет ни у одного). PD-254 закрыт.
**Остаётся зоне контракта (релей, не пере-проектирование):** две фразы — в канон рядом с
существующей про вход (`:36-37`): поверхность механики сессии отвечает тем же конвертом без
`code`, клиент диспетчеризует по статусу, особый случай `429` с `Retry-After`, серверный текст не
показывает никогда; и в компаньон §2.14 — восстановить срезанное правило и таблицу шести статусов.
Триггер пересмотра назван явно: второй пользовательски осмысленный случай на входе → своя
маленькая нормативная спека на `/auth/*`, а не рост таблицы в спутнике.
4. **⚠ НАХОДКА КОНТРАКТНОЙ СЕССИИ, которая дороже самого вопроса — передать оркестратору отдельно.**
Правило про вход было РАТИФИЦИРОВАНО и потеряно молча: в компаньоне 0.2.3 §2.14 стояло «Отказ
входа — problem+json, как везде; **различать причины отказа клиент не может по замыслу**», а в
сегодняшней редакции осталась только первая половина — вторую срезал коммит лендинга батча 0.3.0
(`8d82096`, проверено `git log -S`). То есть P7 не переоткрывал спор, а восстанавливал решение,
которое кто-то выкинул. **Класс потери структурно непроверяем существующей приёмкой:** сверка
батча шла по мультимножеству модальных глаголов (MUST/SHOULD/MAY), а в этой фразе модального
глагола нет. Стоит отдельного разбора приёмки — не как дефект контракта, а как дыра в МЕТОДЕ
проверки резки прозы.
5. **Плотность комментариев** (PD-255): пак вышел за норму «одна-две строки почему». Часть прозы
несущая, часть — эссе; прошу решение владельца о норме, а не тихую подрезку в следующем паке.

View file

@ -55,7 +55,7 @@
продолжить». Ломающая правка ровно одна — `Run.stop_requested` (0.3.0 такого поля не объявляет);
`cause.code: credit_unavailable` легален и по 0.3.0, второй уровень там открыт. Зависимость
записана строкой **PD-327** и закрывается лендингом канона, а не работой зоны. Если форма
сдвинется словом владельца — правки отделимы, карта в `P7_ACT5_FIX_PLAN.md` §5б.
сдвинется словом владельца — правки отделимы, карта в `archive/P7_ACT5_FIX_PLAN_2026-08-20.md` §5б.
- **Тесты на АТОМАРНОСТЬ сверяют `xmin`** (транзакцию, последней писавшей строку). Это не
экзотика ради экзотики: два пина акта проходили под мутацией «вынести метку из транзакции во
второй оператор» — конечное состояние она не меняет, меняет окно, и утверждать про окно иначе
@ -92,9 +92,9 @@
появление класса в паке, и он назван в плане §3 п.2 ещё до того, как повторился.
- **P7, акт 5 (начало) — исполнение фиксов по ревью акта 4 + ответ контрактной сессии.**
План и рабочий список — **`platform/docs/P7_ACT5_FIX_PLAN.md`** (читать первым; §5а — что пришло из
План и рабочий список — **`platform/docs/archive/P7_ACT5_FIX_PLAN_2026-08-20.md`** (читать первым; §5а — что пришло из
контракта).
⚠ **Контрактная сессия ответила 20.08 на записку зоны `CONTRACT_SYNC_FROM_PLATFORM.md`: канон стал
⚠ **Контрактная сессия ответила 20.08 на записку зоны `archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md`: канон стал
0.4.0** (лежит в дереве контрактной зоны, НЕ ратифицирован — до лендинга в силе 0.3.0). Все три
вопроса, висевшие на владельце, закрыты. Зоне пришла работа: `resume` исчерпанного прогона — 409 с
двумя РАЗНЫМИ причинами (`ceiling_reached` против нового `credit_unavailable`); «глава сделана» =
@ -117,8 +117,8 @@
регистронезависимо. Батарея: 18 пакетов, exit 0, скипов 0, тестов **503**.
- **P7, акт 4 — доработка 20.08: сверка ВСЕХ находок против дерева + триаж §8.2.**
(Пакет релеев `P7_ACCEPTANCE_HANDOFF.md` §7; три вопроса владельцу §8.1а; вопросы контракта
вынесены в `platform/docs/CONTRACT_SYNC_FROM_PLATFORM.md` для отдельной контрактной сессии.)
(Пакет релеев `archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md` §7; три вопроса владельцу §8.1а; вопросы контракта
вынесены в `platform/docs/archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md` для отдельной контрактной сессии.)
Все 74 позиции обеих приёмок сверены поштучно с ТЕКУЩИМ деревом — каждая с цитатой живого кода,
исход «не проверено» отдельно от «опровергнуто».
⚠ **Три места числились закрытыми и закрыты не были — правка не закрывала собственный сценарий:**
@ -144,7 +144,7 @@
**Осей, которых не смотрел никто:** деньги и леджер · вход/сессии/CSRF · очередь и джобы · метрики.
- **P7: приёмка → правки → приёмка правок (17.08).**
⚠ Полный хендофф под сессию без контекста — **`platform/docs/P7_ACCEPTANCE_HANDOFF.md`** (читать §0§3,
⚠ Полный хендофф под сессию без контекста — **`platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md`** (читать §0§3,
дальше по нужде). Там же: принципы приёмки, сверка с исходным промтом, техдолг, план следующей сессии.
Категорийное ревью по 12 осям с адверсариальной верификацией: 80 находок, **54 подтверждено, 16
правдоподобно, 10 опровергнуто**; после дедупа **46 уникальных дефектов** — 3 блокирующих класса,
@ -172,11 +172,11 @@
«стоп был запрошен» в контракте; готовая строка для единого бэклога — хендофф §7(и), вставляет
оркестратор (зона `docs/` не наша).
**Разбор, план, обязательства через оркестратора и команды воспроизведения —
`platform/docs/P7_ACCEPTANCE_HANDOFF.md`.**
`platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md`.**
⚠ Заявки раздела «Сессия P7» ниже приёмку НЕ прошли — читать их вместе с хендоффом, не вместо.
- **P7 ПОСТРОЕН — заявка САМОЙ сессии написания (17.08).****Приёмку эта заявка НЕ прошла** — см.
верхнюю запись и `P7_ACCEPTANCE_HANDOFF.md`: 46 уникальных дефектов, три блокирующих класса, из них
верхнюю запись и `archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md`: 46 уникальных дефектов, три блокирующих класса, из них
ни один батарея не видела. Читать ниже как исходную позицию, а не как состояние дерева.
Читающая поверхность контракта **0.3.0**:
главы · пары с текстом · замечания · банк и подпись · SSE на КНИГЕ · `GET /capabilities` ·
@ -295,250 +295,9 @@
*(Записи паков P4P6 с дофиксами (0815.08) — в [archive/platform-PROGRESS-P4-P6.md](archive/platform-PROGRESS-P4-P6.md); вынесено 16.08 по слову владельца.)*
## Сессия P7 (1617.08): читающая поверхность контракта 0.3.0
## Эра пака P7 (1620.08) — в архиве
Промт — `PLATFORM_P7_SESSION_PROMPT.md`. Ниже: записка-план по §4 (каждый пункт с диспозицией),
команды, живые пробы, находки, obstacle.
### Записка-план: пункт → исполнено / отказ → улика
| § | Пункт | Статус | Улика |
|---|---|---|---|
| 0 | Рантбук деплоя живым `migrate` | **исполнен** | БД проекта отведена на v14 при бинаре v15 → `tmctl status --json` = exit 13, токен `schema_mismatch found=14 expected=15`; `tmctl migrate` → бэкап + v14→v15; тот же `status` = exit 0. Мёртвая цитата вычищена (`deploy/README.md`, П-1 бэклога), пример в `tmplatformctl/runs.go` переписан на ВЕРСИОНИРОВАННЫЙ путь |
| 0 | `tmplatformctl books --migratable``tmctl migrate` end-to-end | **исполнен** | 5 книг стенда, все `MIGRATE=yes`, цикл из деплой-ноты отработал |
| 1а | Читать `BookIntake.title` | **исполнен** | `books.Intake.Title`; пустая строка = «назови по файлу» (`books.title()`), непустая не перезаписывается разбором |
| 1б | Проецировать `reject_reason` | **исполнен** | `httpapi.contractRejectReason`; `parser_unavailable``processing_failed` (ФБ-9: имя значения — то, на что клиент вешает фразу) |
| 2 | PD-104: грант → 0 + тест | **исполнен** | `config.go` дефолт 0; пин `config.TestSignupGrantIsParsedNotGuessed` пинит ИМЕННО ноль; протухшие «$5» вычищены из `PLATFORM_DIRECTION.md` §2, `BACKLOG.md` П-7, регистра |
| 3 | Модель ошибок по карте А-2 | **исполнен** | `httpapi/problem.go`: 16 корневых кодов, статус ВЫВОДИТСЯ из кода (пара «код+статус» непредставима расходящейся), `cause` открытый, `errors[]`, `request_id` из `reqid`; `WWW-Authenticate` на 401; две причины 403 разведены в `cause` при одном корневом `forbidden`; класс 2 — один код без вариации |
| 4 | `listChapters` · `listUnits` (пер-главно, 410) · `listNotes` · `listBankTerms` · `submitBankDecisions` · дельты · SSE · `structure_version` · gzip · ETag/304 | **исполнен** | `httpapi/{reading,stream,conditional}.go`, `pgstore/{readmodel,events}.go`; ручки «вся книга юнитами» НЕТ; `Unit.notes` массивом; агрегаты банка на ПЕРВОЙ странице; дельты ВКЛЮЧИТЕЛЬНЫЕ; `version_too_old` и `cursor_invalid` — причинами |
| 4 | Ловушка `heading` | **исполнен** | поле не декодируется ВОВСЕ (`ingest.ManifestChapter`), колонка `chapters.heading` удалена миграцией, проекция отдаёт `null`; пин `readmodel.TestTheManifestsRenderedHeadingHasNowhereToLand` |
| 4 | Демонтаж построенного гейта полноты банка | **исполнен** | `runs/reconcile.go`: `awaiting_bank` продолжается при ЛЮБОМ состоянии решений; пин `TestResumeLiftsABankStopWithTheDecisionsAsTheyStand`. Граница паузы движка сверена чтением (`pipeline/mining.go`: авто-продолжение с неподписанным банком — дефолт) |
| 4 | Пофазность у главы НЕ строить | **соблюдён** | у главы один счётчик `units_done`; пин «фазовый счётчик не доехал до строки главы» |
| 4 | `Note.code` из ◆-карты | **исполнен с ОДНИМ отступлением** | `httpapi/notes.go`; отступление — незнакомая причина проецируется как `unspecified` (в карте колонка кода пуста, а поле обязательно). Заведена строка PD-246, ждёт ратификации вместе с фразами |
| 5 | `Progress` с книги на `Run`, одна полоса в ГЛАВАХ | **исполнен** | `pgstore.Progress{Done,Total,ETASeconds}`, знаменатель — `ceiling_chapters`; сегмент выбирается `verify_bank && !bank_released` |
| 5 | `Book`: +`revision`, +`chapters_done`, `progress`, `genre` | **исполнен** | `genre` снят и с ПРОВОДА, и из формы интейка, и из колонки: платформа его больше не знает, значение шаблона оператора остаётся его |
| 5 | `Run`: +`book_id`, +`failure_reason`, `verify_bank``stop_for_signing` | **исполнен** | словарь из трёх значений мапится с полосы 1019 и веток `outcome()` (`runs.failureReason`) |
| 5 | `Usage`: `halt_reason` со своим словарём | **исполнен** | `httpapi.contractHaltReason`; прогонная причина больше не может доехать до аккаунтного поля |
| 5 | `GET /capabilities` | **исполнен** | все шесть полей; пары — из конфигурации (`TM_PLATFORM_LANGUAGE_PAIRS`), не список в Go |
| 5 | `limit` подрезать, а не отвергать | **исполнен** | `pgstore.clampPage`; отвергается только значение, которое не является размером страницы |
| 5 | Трейлинг-части интейка → 400 | **исполнен, с находкой** | сначала книга ОСТАВАЛАСЬ при 400 (живая проба); теперь часть после файла ПРОВАЛИВАЕТ чтение, и интейк откатывает загрузку своим же путём |
| 5 | `Location` на 201 | **исполнен** | `/v0/books/<id>` |
| 5 | `Idempotency-Key` на `createBook` и `startRun` | **исполнен** | таблица 00017, повтор отдаёт ИСХОДНЫЙ ответ, другой запрос — 409 `key_reused`, гонка — `key_in_flight` + `Retry-After`, >255 → 400 |
| 5 | `blocked {code, book_id}` на run-options и 409 | **исполнен** | `pgstore.CreditHeldBy` + `runs.CreditHeldError` |
| 5 | Отказ по неподдерживаемой паре на интейке | **исполнен** | `books.ErrUnsupportedPair``errors[].code: unsupported_pair`; пустой список пар = деплой не объявляет ничего и пару не проверяет |
| 5 | `finalizing` вон из словарей | **исполнен** | Go-свитчи и CHECK-констрейнты обеих таблиц |
| 6 | PD-172/173/174/180/199 привести · PD-201 · PD-241 | **исполнен** | все закрыты либо сужены с уликой; PD-241 — перевёрнутый порядок в `outcome()` с пином |
| 6 | Кандидаты PD-219/217/162 | **отсуждены** | PD-217 — половина ЗАКРЫТА (paused больше не блокирует апгрейд движка); PD-219 и PD-162 — не брались, с названной причиной |
| 6 | Находки §9 research/28 | **отсуждены** | три взяты строками (PD-249/250/251/252), две сведены к существующим (PD-203, PD-162/244) |
| 6 | Инструменты: sqlc / oapi-codegen | **отказ с доводом** | оба НЕ взяты; довод — `PLATFORM_DIRECTION.md` §3, и он опирается на замер этого пака, а не на вкус |
### Команды (каждое число отчёта — этой командой)
```sh
# батарея, оба гейта, -race
export TM_PLATFORM_TEST_DSN='postgres://postgres@/postgres?host=/tmp&port=55433&sslmode=disable'
export TM_PLATFORM_TEST_ENGINE_BIN=<путь к tmctl> TM_PLATFORM_TEST_BOOK_TEMPLATE=<шаблон>
make check # EXIT=0, 18 пакетов, скипов 0, линтер 0 issues
grep -rh '^func Test' --include='*_test.go' . | wc -l # 458 (HEAD: 416)
git diff --stat platform | tail -1 # 46 файлов, +2329 646
python3 docs/scripts/counts.py # регистр: 256 строк, 63 открытых
```
⚠ Стенд Postgres на этой машине пришлось поднять с нуля (его не было): рецепт `STACK_DECISIONS.md`
§«Postgres без root», micromamba + `postgresql=18.4` в `~/.local/pgsql`, порт 55433 — отработал
дословно.
### Живые пробы на дев-стенде (демон + сид + настоящий `tmctl`)
Сид P6 отработал целиком: дев-вход → грант → загрузка через ЖИВОЙ `POST /v0/books` → разбор →
`not_started, 3 главы`. Дальше — курлом по новым ручкам:
- `GET /capabilities` → все шесть полей, `contract_version: 0.3.0`, пары `zh>ru available` +
`ja>ru unavailable`;
- `GET /books` → `revision · reject_reason: null · structure_version: 1 · chapters_done: 0 ·
character_count: 987`, **никакого `progress` и никакого `genre`**;
- `GET /books/{id}/chapters` → три главы, `heading: null` у каждой;
- `GET /books/{id}/chapters/{ch}/units` → пара с ИСХОДНЫМ ТЕКСТОМ (канал `tmctl export --pairs`
работает), `target: ""`, `state: pending`, `notes: []`;
- `GET /books/{id}/notes`, `/bank` → пустые с агрегатами и `structure_version`;
- SSE на книге в покое → `hello id=4``end id=4` → закрытие; реконнект с `Last-Event-ID: 6`**204**;
- `ETag``304` на `If-None-Match`; `Accept-Encoding: gzip``Content-Encoding: gzip` + `Vary`;
тот же заголовок на `/events`**не сжато**;
- ошибки: 404 → `code: not_found` + `request_id`; 401 → `WWW-Authenticate: Bearer realm="textmachine"`;
POST без `X-TM-Client` → 403 `forbidden` · `cause.code: client_header_missing`;
- интейк: пара `ja→ru` → 400 `unsupported_pair` на обоих полях; часть после файла → 400
`missing_or_late` **и книга не остаётся** (проверено счётом библиотеки до/после);
- `Idempotency-Key`: тот же ключ дважды → один и тот же `Location`; тот же ключ с другим `title`
409 `idempotency_conflict` · `cause.code: key_reused`.
### Что нашли ПРОБЫ, чего не нашли тесты
**Часть формы после файла оставляла книгу.** Отказ 400 приезжал (правило соблюдалось), но
`intake.Accept` к тому моменту уже создал строку и каталог — пользователь получал ошибку И книгу в
библиотеке. Починено не новым кодом отката, а ПРОВАЛОМ чтения: часть после файла возвращает ошибку
из ридера, и интейк отменяет загрузку тем же путём, которым отменяет оборванную. Пин
`httpapi.TestAPartAfterTheFileIsRefusedAndTheBookIsNotKept`, пере-проверено живьём.
### Адверсариальное селф-ревью финалом: три ревьюера, таблица находок
Механизм по §5 промта: три независимых субагента по ФИНАЛЬНОМУ диффу, author≠reviewer, **два из
трёх — другой модели** (кросс-семейная линза, а не вторая своя). Ноль находок не случился — и это
хорошо: две HIGH нашли ОБА контрактных ревьюера независимо, что и есть подтверждение.
| # | Линза | Находка | Вердикт | Что сделано |
|---|---|---|---|---|
| Р-1 | контракт (кросс-модель) · деньги/шов | **Кадр `note` уносил движковую причину** (`glossary_miss`, `hard_refusal`) и не нёс обязательных `severity`/`code`; `unit_id: null` вместо отсутствия | ПОДТВЕРЖДЕНА, HIGH | Карта переехала в `internal/ingest/notes.go` — один дом на оба потребителя; кадр строится через неё; `unit_id` опускается. Пин `TestTheNoteFrameCarriesTheContractsNoteAndNotTheEnginesReason` |
| Р-2 | контракт (кросс-модель) | **Кадр `status` уносил внутренние словари** (`daily_ceiling`, `parser_unavailable`): та же величина читалась `null` на карточке и внутренним словом в потоке | ПОДТВЕРЖДЕНА, HIGH | Переводы переехали в `internal/ingest/vocabulary.go`, кадр строит `emitStatus`; при ошибке чтения кадр НЕ шлётся (вместо `status: null` вне схемы). Пин `TestTheStatusFrameCarriesTheContractsVocabularyOrNothing` |
| Р-3 | деньги/шов | **DOWN-путь миграции неисполним при первой же строке банка** (воспроизведено живым PG): три словаря переписывались без перевода данных, а goose гоняет миграцию одной транзакцией | ПОДТВЕРЖДЕНА, HIGH | Порядок drop → перевод → add в ОБЕ стороны (UP тоже: он должен переживать пере-накат после отката). Пин `TestARollbackSurvivesTheDataTheNewVocabulariesWrote` — откат до документированного пола (версия 5) на базе С данными |
| Р-4 | деньги/шов | **Четыре писателя меняли статус без кадра** (`RestartRun`, `PauseRun`, `FinishUnspawnedStop`, `ReleaseBankStop`); двое из них ставят `finished_at`, то есть книга «в покое» — и следующий реконнект получает **204 «не переподключайся»** при устаревшем статусе у клиента | ПОДТВЕРЖДЕНА, HIGH | Кадр у всех трёх писателей статуса; четвёртый (`ReleaseBankStop`) перестал быть отдельным писателем — см. Р-5 |
| Р-5 | деньги/шов | **`ReleaseBankStop` коммитился ДО `reopen`**: резюм, который ничего не сделал (расчёт в полёте, бюджет исчерпан), всё равно переключал полосу с N/N на 0/N | ПОДТВЕРЖДЕНА, MED | Снятие стопа перенесено ПОСЛЕ успешного `reopen` |
| Р-6 | деньги/шов | **`Refresh` тратил бюджет прохода ПЕРЕД `settle`**: на большой книге два полных ре-чанка съедали проход, расчёт уходил в «отложено», холд висел свип | ПОДТВЕРЖДЕНА, MED | Деньги первыми: `settle` → затем `refreshReadModel` на СВОЁМ ограниченном контексте, ошибка громкая |
| Р-7 | деньги/шов | **`failureReason` игнорировал маркер**, который ему передавали: OOM и kill по стоп-таймауту приезжали `interrupted` («повтори») | ПОДТВЕРЖДЕНА, MED | Читает `m.Result`; пин `TestWhyARunFailedDecidesWhetherARetryIsWorthOffering` (7 веток) |
| Р-8 | контракт (кросс-модель) | **Квитанции `Run` несли нулевую полосу** (`202` на старт/стоп/резюм) со свежей ревизией — клиентский гард пропускал её и полоса откатывалась к 0/0 | ПОДТВЕРЖДЕНА, MED | `StartRun`/`RequestStop`/`ReadRun` считают ту же полосу, что карточка |
| Р-9 | контракт (кросс-модель) | **Полоса ВТОРОГО прогона считала главы всей книги**: дробь стартовала выше нуля и могла превысить единицу | ПОДТВЕРЖДЕНА, MED | Колонка `runs.chapters_before` — база сегмента, снимается в том же стейтменте, что создаёт прогон; `least(…, ceiling_chapters)` закрывает второй край. Пин `TestASecondRunsBarStartsAtZeroOverAHalfFinishedBook` |
| Р-10 | контракт (кросс-модель) | **`Last-Event-ID: 0` после подрезки буфера молча пропускал дыру** — а `0` это легальный id, который сервер сам выдаёт | ПОДТВЕРЖДЕНА, MED | Условие `last+1 < Oldest` вместо `last > 0 && last < Oldest` |
| Р-11 | контракт (кросс-модель) · скоуп | **Склейка кадров `chapter` шла по имени события**, теряя счётчик соседней главы внутри одного соединения | ПОДТВЕРЖДЕНА, MED | Склейка по (событие, id сущности) |
| Р-12 | контракт (кросс-модель) | **Порядок банка** был `(src, id)` — то есть по ХЕШУ, а канон объявляет «по поверхности, затем по окну» | ПОДТВЕРЖДЕНА, MED | Порядок и кейсет приведены к канону (`src, since, until, id`) |
| Р-13 | контракт (кросс-модель) | **`limit` всё ещё отвергался снизу** (0, отрицательный, нечисло), тогда как канон освобождает параметр от отказа | ПОДТВЕРЖДЕНА, MED | Никогда не отвергается: сверху подрезка, всё прочее — дефолт деплоя. Пин переписан |
| Р-14 | контракт (кросс-модель) | **Неверная пара языков и слишком длинное поле отказывали без `errors[]`** | ПОДТВЕРЖДЕНА, MED | `ErrMalformedLanguage` и `too_long` с указателем на поле |
| Р-15 | контракт (кросс-модель) | **`SweepIdempotency` не имел вызывающего**: ключи не забывались никогда | ПОДТВЕРЖДЕНА, LOW | Подключён в свип демона, окно 25 часов |
| Р-16 | контракт (кросс-модель) | **Курсор замечаний нёс движковое слово волны** в открытом виде | ПОДТВЕРЖДЕНА, LOW | Пакуется порядковым номером |
| Р-17 | скоуп (кросс-модель) | `acceptsGzip` игнорировал `*`; `pump` спал такт даже после полной пачки; `unknownTermPointer` парсил текст ошибки; алиас `itoa` | ПОДТВЕРЖДЕНЫ, LOW | Все четыре починены (типизированная `UnknownTermError` вместо парсинга строки) |
| Р-18 | скоуп (кросс-модель) | **`sqlc` был привязан к этому паку ратификацией**, а пак от него отказался — это отступление, а не решение зоны | СПРАВЕДЛИВО | Отказ с доводом записан в `PLATFORM_DIRECTION.md` §3 и вынесен ВОПРОСОМ оркестратору (ниже) |
| Р-19 | скоуп (кросс-модель) | **`unspecified` — изобретённый код** там, где промт требовал пинг | ЧАСТИЧНО: разбор с владельцем 17.08 показал, что находка верна по форме и неверна по существу | Значение отдано ветке, которую канон УЖЕ ратифицировал («нейтральная фраза для незнакомого кода»), а не заводит новое правило; настоящий предмет — рукописная копия чужого словаря без гейта, PD-246 пере-сформулирован, движку — пинг про публикацию причин данными |
| Р-20 | контракт (кросс-модель) | `410` отвечает и на главу, которой никогда не было: различение 410/404 без надгробий неисполнимо | ПРИНЯТО КАК ЕСТЬ | Лечение у обоих одно (перечитать дерево); строка PD-253 |
| Р-21 | скоуп (кросс-модель) | **Масса комментариев** выросла против нормы зоны (миграция на 250 строк — половина проза) | ЧАСТИЧНО ПРИНЯТО | Подрезано самое тяжёлое; остальное — предмет пинга, а не тихой правки |
| Р-22 | скоуп (кросс-модель) | **Два писателя ошибок** (`WriteProblem` + `WriteStatusProblem`/`codeForStatus`), три одинаковых структуры пары | НЕ БРАЛОСЬ | Второй писатель обслуживает НЕконтрактную поверхность (`/auth`, `/readyz`), где кода версии нет; сведение в один требует ратификации формы для 405/429 — вопрос оркестратору |
**Что ревьюеры подтвердили как ВЕРНОЕ** (важно не меньше находок): ни одной денежной величины на
проводе ни в каком виде · порядок блокировок не тронут, `emitFrame` не берёт НОВЫХ локов ни в одной
из 14 точек · кадр не может быть выпущен для откаченного состояния · демонтаж гейта банка
соответствует таблице канона дословно · словари шва (шесть значений банка, полоса отказов 1019,
все семь типов событий) сверены по коду движка и полны · `finalizing` снят безопасно · оба гейта
миграций пере-считаны и совпадают.
### Живые пробы ПОСЛЕ фикс-раунда (стенд пере-создан)
Стенд пришлось пере-создать, и это отдельная улика: миграция 00016 правилась ПОСЛЕ того, как была
применена, а goose ключуется номером — отредактированный файл не перезапускается никогда. На стенде
лечится сносом базы; в проде именно от этого стоит гейт `migrations.sha256` (обе строки пере-считаны).
- `limit=0 | -3 | abc | 100000`**200 во всех четырёх** (канон освобождает параметр от отказа);
- квитанция старта: `status: translating`, `progress {done:0, total:2}`, `book_id`, `failure_reason: null`
— полоса едет с квитанцией, а не только с карточкой;
- поток при ЖИВОМ прогоне: `hello id=5``status id=7` с `failure_reason: "service_error"` и
`paused_reason: null` — кадр несёт КОНТРАКТНЫЙ словарь (внутреннего слова в кадре нет);
- карточка после отказа: книга `failed`, прогон `failed` + `service_error` (на стенде нет ключей
провайдера — движок отказывает конфигурацией, и это ровно тот класс, который «повтор не лечит»);
- `resume` упавшего прогона → `run_not_resumable` без `cause` (канон: это не остановленный прогон);
- неверный код языка → `invalid_request` + `errors[]` на оба поля;
- `/usage``halt_reason: null` (свой словарь, не прогонный).
### Obstacle: что НЕ удалось и что не проверено
1. **Отложено в P8 явно** (заказ промта): `updateBook` · `deleteBook` · `getRun` · `createExport`
/`getExport` · эскроу (П-18).
2. **`Note.code` для незнакомой причины — отступление**, не решение: `unspecified` (PD-246).
3. **Ни одного живого ЗАМЕЧАНИЯ и ни одного термина банка через настоящий прогон не прошло**
прогон стоит денег, а сессия $0. Проекции замечаний и банка проверены тестами на живом PG и
ручными строками `unit_resolutions`, но не сквозным прогоном движка.
4. **SSE с ЖИВЫМ прогоном не гонялся** по той же причине: проверены книга в покое, реконнект, 204,
склейка и `resync_required` — тестами и на стенде, но не под настоящим потоком кадров.
5. **Стоимость материализации не замерена на большой книге** (PD-248): два полных ре-чанка на
границу работы; на демо-книге в 3 главы это миллисекунды, на 2283 главах — секунды CPU.
6. **`structure_version` на РЕАЛЬНОЙ пере-нарезке** проверен тестом (смена ключа манифеста), но не
настоящей сменой чанкера движка.
7. **Опрос потока — 2 запроса/с на соединение** (PD-247): `LISTEN/NOTIFY` не строился.
8. **Кадр `note` живьём не наблюдался**: он рождается только на помеченной единице настоящего
прогона. Проверен тестом на живом PG через настоящий `RunSink.Apply` (полный путь материализатора),
но не в потоке под браузером.
9. **Полоса под настоящей работой не наблюдалась**: `progress {0,2}` — это старт; движения счётчика
на стенде без ключей провайдера не бывает.
### Вопросы оркестратору (канал вопросов промта, НЕ тихая интерпретация)
1. **Словарь флаг-причин — рукописная копия чужой зоны без гейта** (PD-246; формулировка исправлена
после разбора с владельцем 17.08 — прежняя, «в карте контракта дыра», была неверной).
Проверено чтением движка: локализованных фраз там НЕТ вовсе — `refusal.go` и `status.go` несут
машинные классы, русских пользовательских строк в продовом коде движка ноль. То есть движок и
контракт построены на одном принципе, и переделывать под «гугловский словарик в движке» нечего:
фраза всегда на языке ИНТЕРФЕЙСА, которого не знает ни движок, ни платформа, а движок ОДИН на все
пары — русская строка в нём была бы таргет-спецификой в общем слое (тот же инвариант, что снял
`ruby` с провода).
**Проводу окно закрыто:** причина вне карты едет кодом `unspecified`, а канон уже предписывает
клиенту нейтральную фразу для незнакомого кода — значение отдано РАТИФИЦИРОВАННОЙ ветке, а не
изобретено правило. На подпись остаются две строки продуктового текста: фраза для `unspecified` и
граница ступеней `attention`/`glance` (сегодня — догадка платформы, колонка «Ступень» в
приложении А пуста у всех 15 строк). Это не блокер.
**Правка контракта — предлагаю, зона не моя (готовые формулировки, вставить как есть).**
*Обязательный минимум — КОМПАНЬОН, приложение А, последняя строка (`README.md:899`).* Пустая
клетка кода в ней и есть то, что заставило платформу выбирать значение самой:
> `| незнакомая причина | 8 (по умолчанию) | `unspecified` — стабильный плейсхолдер деплоя: не значение словаря, а ОБЯЗАННОСТЬ сервера ответить одним и тем же кодом на причину, которой его сборка карты не знает (носитель — PD-246 платформы) | ⬜ нейтральная, НЕ «ошибка» | ⬜ |`
*Желательное — ОДНА фраза в канон*, к описанию `Note.code` (`openapi.yaml:1693-1700`), после
абзаца про нейтральную фразу:
> A deployment that meets a reason its build of the map cannot name answers the stable placeholder `unspecified` rather than the producer's own word, so the branch above is exercised by a real value in ordinary operation and not only in theory.
Форма выбрана намеренно: это ОБЯЗАННОСТЬ СЕРВЕРА, а не запись словаря — канон отказывается
перечислять `Note.code` («freezing a list in the schema before the words exist would make it a
second copy»), и перечисление начинать нельзя. Ценность ровно одна и она процессная: без строки в
нормативном документе значение живёт только в Go платформы, и следующая сессия изобретёт своё.
**Бампа версии не требует, и вот почему точно:** `Note.code` объявлен `type: string`, не enum, —
ни один генерённый тип не сужается и не расширяется; а сама фраза описывает поведение, которое
ЕДИНСТВЕННЫЙ потребитель 0.3.0 (этот пак) уже реализует, то есть сломать клиента, сгенерированного
по 0.3.0, она не может. ⚠ Прецедент D39.144 (микро-правка канона без бампа) здесь опорой НЕ
служит: там довод был «спека ещё никем не потреблена», и он больше не верен. Цена, которую надо
назвать: описания компилируются в исходники клиента, значит фраза доедет до `schema.ts` — то есть
зеркало фронта и регенерация типов при разморозке. И пара «код нормативен, фраза ⬜» должна
остаться видимой, иначе следующий читатель решит, что карта опять сломана.
**⚠ ПИНГ ОРКЕСТРАТОРУ — строка в бэклог ДВИЖКА (сама зона в чужой бэклог не пишет):** публиковать
список флаг-причин ДАННЫМИ — артефакт рядом с манифестом либо `tmctl flag-reasons --json` ($0,
таблица `flagReasonSeverity` уже существует). Тогда тест платформы читает его и ПАДАЕТ, если в
карте нет строки, и класс «словарь разъехался тихо» закрывается насовсем. Со стороны платформы
гейт невозможен: импорт `backend/internal` запрещён ревью-гардом модулей (D39.85). Обратный
вариант — чтобы движок эмитил сразу контрактный код — отвергнут: это зеркальная утечка,
продуктовое слово поехало бы в движок, который о контракте знать не должен.
2. **`sqlc` — ОТЛОЖЕН СЛОВОМ ВЛАДЕЛЬЦА 17.08** («отложим»), строка PD-44 дополнена разбором.
Фиксирую честно, включая довод против себя: пак был обязан его взять (D39.132 п.2б), и sqlc
поймал бы ровно тот класс, который дважды укусил этот пак в рантайме (`stop_for_signing`,
`chapters_before`) — батарея его поймала, но на цикл позже компилятора. Что мешает в read-модели:
запросы собраны склейкой общих фрагментов (одна проекция книги на четыре пути), а sqlc требует
литерального SQL на запрос, и страница с её ревизией — транзакция, которую он не моделирует.
Предложение зоны: взять в P8 на отложенных однооператорных ручках, по итогам решить про
read-модель.
3. **Поверхность входа: РЕШЕНО контрактной сессией 17.08 (релей владельца), механизм исполнен.**
Вердикт — вариант A уточнённый: `/auth/*` отвечает тем же конвертом и **без машинного `code`
вовсе**; клиент показывает одну нейтральную фразу, а единственный пользовательски осмысленный
случай — `429` — уже машинный без всякого словаря, потому что лечение едет в `Retry-After`
(платформа его отдаёт: `login.go:173,233`, `dev.go:138`). Отвергнуты: расширение `ErrorCode`
(значения, недостижимые на поверхности, которую этот словарь описывает, ломают инвариант «код
называет свой статус») и собственный словарь в компаньоне (сделал бы НЕнормативный документ
нормативным с чёрного хода — канон в шапке объявляет нормативным для ФОРМЫ только себя).
**Исполнено в этом дереве:** `codeForStatus` и обратная функция удалены — писатель входа берёт
статус в точке вызова и кода не эмитит, поэтому инвертировать больше нечего. Пин
`httpapi.TestTheSignInSurfaceAnswersTheSameEnvelopeWithoutAVersionedCode` (шесть статусов: конверт
и `request_id` есть, `code` нет ни у одного). PD-254 закрыт.
**Остаётся зоне контракта (релей, не пере-проектирование):** две фразы — в канон рядом с
существующей про вход (`:36-37`): поверхность механики сессии отвечает тем же конвертом без
`code`, клиент диспетчеризует по статусу, особый случай `429` с `Retry-After`, серверный текст не
показывает никогда; и в компаньон §2.14 — восстановить срезанное правило и таблицу шести статусов.
Триггер пересмотра назван явно: второй пользовательски осмысленный случай на входе → своя
маленькая нормативная спека на `/auth/*`, а не рост таблицы в спутнике.
4. **⚠ НАХОДКА КОНТРАКТНОЙ СЕССИИ, которая дороже самого вопроса — передать оркестратору отдельно.**
Правило про вход было РАТИФИЦИРОВАНО и потеряно молча: в компаньоне 0.2.3 §2.14 стояло «Отказ
входа — problem+json, как везде; **различать причины отказа клиент не может по замыслу**», а в
сегодняшней редакции осталась только первая половина — вторую срезал коммит лендинга батча 0.3.0
(`8d82096`, проверено `git log -S`). То есть P7 не переоткрывал спор, а восстанавливал решение,
которое кто-то выкинул. **Класс потери структурно непроверяем существующей приёмкой:** сверка
батча шла по мультимножеству модальных глаголов (MUST/SHOULD/MAY), а в этой фразе модального
глагола нет. Стоит отдельного разбора приёмки — не как дефект контракта, а как дыра в МЕТОДЕ
проверки резки прозы.
5. **Плотность комментариев** (PD-255): пак вышел за норму «одна-две строки почему». Часть прозы
несущая, часть — эссе; прошу решение владельца о норме, а не тихую подрезку в следующем паке.
Пять актов, приёмка, фикс-раунды и таблицы селф-ревью выселены срезом в [`archive/platform-PROGRESS-P7.md`](archive/platform-PROGRESS-P7.md) (норма: закрытая эра не живёт в журнале). Итог и то, что пережило пак, — шапка выше и пинг оркестратора №18 ниже.
## Закрытые эры P0P3 — в архиве
@ -609,7 +368,7 @@ python3 docs/scripts/counts.py # регистр: 256
6. **Труба доставки решений банка в движок** — единый бэклог, строка **199(а)**: перед resume писать решения в `mined_delta`/`mined_rejects`. Сегодня `action`/`dst` — write-only колонки (единственный SELECT `readmodel.go:336` проверяет лишь наличие строки), а воркер описан в комментарии вашей же миграции `00002_readmodel.sql:172-174` и не построен.
7. **Мусор:** пустой `platform/ru` (0 байт, обрубок редиректа) — в лендинг не взят, снесён оркестратором. `books.go:234-235` — задвоенная первая строка доккомментария, класс PD-310/326.
8. **Ревью-пак четырёх осей, которых не смотрел НИКТО** (ваш же obstacle): деньги и леджер целиком · вход/сессии/CSRF · очередь и джобы · метрики. Это не дофикс и не довесок — первый взгляд, отдельной работой.
9. **Один носитель на факт для замера 00016→00022 — но НЕ удалением числа.** Выигрыш назван ЧЕТЫРЬМЯ носителями: шапка этого журнала «83%» · `internal/pgstore/perf_test.go:13` «96%» · миграция 00022 «16.8 против 3.4, джойн сам 9 мс» · регистр PD-306 (повторяет 83% и 16.8/3.4). ⚠ Я сперва записал, что «96%» ни из чего не выводится — **это была моя ошибка, снята проверкой записей**: оно выводится точно из ВАШЕГО же замера в `P7_ACT5_FIX_PLAN.md:439-441` («636 мс против 24 мс», холодный корпус акта 4) = 96.2%, тогда как 83% и мой независимый ≈80% — с вакуумированного корпуса. То есть носители меряли РАЗНОЕ и все честны. Свести указанием УСЛОВИЙ замера при каждом числе, оставив нормативным один (миграция 00022 — она их и несёт); удалять «96%» как фантом НЕЛЬЗЯ.
9. **Один носитель на факт для замера 00016→00022 — но НЕ удалением числа.** Выигрыш назван ЧЕТЫРЬМЯ носителями: шапка этого журнала «83%» · `internal/pgstore/perf_test.go:13` «96%» · миграция 00022 «16.8 против 3.4, джойн сам 9 мс» · регистр PD-306 (повторяет 83% и 16.8/3.4). ⚠ Я сперва записал, что «96%» ни из чего не выводится — **это была моя ошибка, снята проверкой записей**: оно выводится точно из ВАШЕГО же замера в `archive/P7_ACT5_FIX_PLAN_2026-08-20.md:439-441` («636 мс против 24 мс», холодный корпус акта 4) = 96.2%, тогда как 83% и мой независимый ≈80% — с вакуумированного корпуса. То есть носители меряли РАЗНОЕ и все честны. Свести указанием УСЛОВИЙ замера при каждом числе, оставив нормативным один (миграция 00022 — она их и несёт); удалять «96%» как фантом НЕЛЬЗЯ.
11. **У материализатора нет ПОЛА на пустой манифест — латентная потеря всего текста книги.** `internal/readmodel/readmodel.go:215-232` строит `in.Chapters` только из `manifest.Chapters` и ни разу не сверяется со счётчиками того же документа (`ChaptersTotal`/`UnitsTotal` лежат рядом и печатаются в лог строкой ниже). Пустой список едет в `SaveStructure`, где `pgstore/readmodel.go:193-196` выполняет `delete from chapters where book_id = $1 and not (id = any($2))` — на пустом массиве предикат истинен для ВСЕХ глав, и каскад `chapters → units` сносит текст; при пустом `Key` вдобавок срабатывает `changed` и уходят все `unit_resolutions` (родня строки 198). Сегодняшним движком недостижимо (`buildManifest` всегда наполняет главы, файл пишется атомарно) ⇒ фикс-лист, не блокер. **Асимметрия и есть находка:** на ИНТЕЙКЕ ровно этот случай отловлен явно и прибит мутацией (`internal/books/parse.go:134-142`, `books_test.go:1296`) — «нет глав, но работа считается» там не признаётся правдой о книге. У материализатора такого пола нет, и теста на нулевой манифест в `readmodel_test.go` тоже нет. Лечится одной сверкой `len(manifest.Chapters)` против `manifest.ChaptersTotal`.
10. **`PD-327` в регистре стоит `open`, а канон 0.4.0 РАТИФИЦИРОВАН** (D39.152) — то есть условие её закрытия наступило. Закрыть строку регистра явно; сегодня расхождение видно скриптом (`counts.py --check` печатает PD-327 среди открытых), и это ровно тот класс, ради которого регистр объявлен источником истины по статусу.