Give both zones their next pack, name the read-timeout class that had no row, and correct three rows that carried the wrong mechanism or the wrong numbers

This commit is contained in:
heaven 2026-09-08 12:34:20 +03:00
parent 18644e4815
commit 69f548e2a9
8 changed files with 722 additions and 10 deletions

View file

@ -0,0 +1,325 @@
# Пак бэкенда: «ДВИЖОК УЖЕ ЗНАЕТ — ДОВЕЗИ ДО ЧИТАТЕЛЯ»
## 1. Какая проблема и что решит твой результат
Движок **вычисляет правду и пишет её себе в лог**, а тот, кто по этой правде принимает решение —
владелец на экране подписи банка и оператор, читающий траты, — смотрит в другое место. Это один класс, а
не три случая, и он сегодня стоит дороже всего остального в очереди.
- **Полнота банка.** `internal/pipeline/terminologist.go:99-103` считает `Consolidated` · `Declined` ·
`Unanswered` · `BatchesDropped` · `ClassifyBatchesDropped`; `:582` предупреждает — «this bank is
PARTIALLY consolidated — a budget cut a pass». В `Log`. А носитель, который читает платформа и с
которого строится экран подписи, — `internal/pipeline/bankexport.go` (`BankExport`: `bank_version`,
`book_id`, `total`, `signed`, `as_of`, `run_id`, `terms`, `proposed`) — полей полноты **не несёт вовсе**.
Наблюдение платформы за живым потоком **04.09** (H10/H11, строка 199): консолидировано 38,
**не спрошено ни разу 47**, срезано бюджетом 5. Владелец подписал бы такой банк как полный.
- **Деньги.** `internal/pipeline/waverun.go:218` складывает в итог волны только `CostUSD` рендер-паса;
`ClassifyCostUSD` (`terminologist.go:145`) в `TOTAL (this run)` не попадает. А разложение «за что
заплачено» относит классификатор в `superseded`, потому что обе банк-роли адресуются ОДНИМ именем стадии
и делят позицию `{глава, чанк, стадия}`. Замер прогона 08.09 по строкам леджера: терминологическая фаза
стоила **$0.013608** — классификатор `$0.009013` плюс два рендер-батча `$0.002775` и `$0.001820`; в итог
волны входит только рендерная половина, а разложение печатает `$0.004595 (2)` — ровно два последних.
Оператор читает о своих деньгах два числа, не сходящиеся с сырым леджером — ровно тогда, когда решает,
продолжать ли платить.
**Что покупает твой результат.** (1) Владелец видит на подписи, что банк неполон, ДО того как подписал.
(2) Числа отчёта сходятся с леджером. (3) Ни один байт снапшота, хеша запроса, ключа манифеста и версии
встроенных данных не двигается — пак **$0** и ничего не перекупает.
## 2. Зона и git
**Зона записи — `backend/`** плюс свой подзаголовок в секции «Бэкенд» `docs/PROGRESS.md`. Чужие зоны
(`platform/`, `frontend/`, `eval/`, `books/`) не трогаешь ни строкой; `docs/` кроме своей секции — тоже.
**Ты НЕ коммитишь**: дерево готовишь и передаёшь оркестратору (канон — `CLAUDE.md`
§«Git-координация мультисессий»). Никаких `git add -A`, `reset --hard`, переписывания истории.
⚠ Заведи подзаголовок `#### Пак «движок уже знает»` внутри секции «Бэкенд» и пиши только под ним: файл
коммичу я, и чужой текст уедет под моим сообщением. `.env` не читать никогда.
## 3. Карта чтения — ЗАКОН, дальше только по её ссылкам
1. `docs/BACKLOG.md` — строки **253** (обе половины) · **357** · **358** · **355** · **312**. Греп `^| 253 |`.
Строка — носитель заказа; расхождение строки с этим промтом — пинг, не интерпретация.
2. `docs/architecture/18-bank-ontology.md`**нормативная страница банковых проекций**: три правила
дисциплины проекции и два класса дефектов. Ты добавляешь поля в проекцию банка ⇒ читаешь ДО работы.
⚠ Там же эррата 07.09 про класс A, заведённый сознательно.
3. `docs/architecture/15-money-path.md` — денежный путь: что чем считается и где леджер есть нижняя граница.
4. `docs/architecture/12-go-style-notes.md` — норматив формы («least mechanism», размер не критерий сплита,
интерфейс объявляет потребитель, код английский).
5. `backend/cmd/tmmutate/main.go`**шапка целиком** (контракт мутационного инструмента) плюс
`backend/Makefile`, цели `battery` и `mutations`.
Ратифицированное, нужное по существу, вложено сюда и похода в журнал решений НЕ требует:
- **`D39.226`** — акт предыдущего пака этой зоны: что уже построено (узкий фильтр банка, пометка `NOT ASKED`
в текстовом сайдкаре, каталог мутаций 259/48).
- **`D39.225` п.10** — ⛔ **правило остановки, применённое к секции предложений сайдкара: четвёртый круг по
ней НЕ открывается** (строка 353 — гейт «появится читатель»).
- **`D39.104` п.1** — банк на проводе есть ЗАКОН для всех ролей независимо от статуса строки. Отсюда цена
неполного банка: владелец подписывает то, что станет законом для всей книги.
- **`D39.183`** — запрет править тесты «чтобы прошло» про МОТИВ; правка от ЗАКАЗАННОЙ смены поведения —
обслуживание, но объявляется в отчёте.
- **`D39.208` п.5** — фикстура, где две различные величины совпали, делает весь их класс невидимым.
- **`D39.216`** — где нынешняя форма не выдерживает заказ, её РАЗВЯЗЫВАЮТ или переписывают, а не обкладывают
заплатой; «дешевле подпереть» само по себе доводом НЕ является. Отступление законно, но объявляется с
ценой: почему заплата дешевле развязки и что именно она откладывает. ⚠ Второй носитель одного знания не
заводится: если правка требует держать две копии в согласии — развязка назрела. Это прямо про §4.1, где
форму полей выбираешь ты.
- ⛔ **Правило, купленное дважды подряд:** пин на УСЛОВНОЕ сообщение обязан иметь фикстуру, где оно ОБЯЗАНО
прозвучать. Тест, утверждающий только молчание, вакуумен ровно там, где сообщение ложно.
## 4. Состав и разметка свободы
### 4.1 Полнота банка доезжает до того, кто подписывает — **состав РОВНО так, форма полей — реши сам**
Три поверхности подписи, и правда есть только в логе:
- **машинный сайдкар** `BankExport` (`internal/pipeline/bankexport.go`) — полей полноты нет; его читает
платформа, и с него строится экран;
- **текстовый сайдкар** стоп-таблицы (`internal/pipeline/mining.go`) — несёт пометку `NOT ASKED(…)`
(1 хит), и это единственное место, где она есть;
- **экран стопа на stdout** (`cmd/tmctl/render.go`, `renderSignatureStop`) — про пропущенного кандидата не
знает: хитов `NOT ASKED` там **0**, при том что тот же рендерер УМЕЕТ печатать пер-строчные пометки
(контроль — `invented`).
**Заказ:** довези до ОБОИХ читателей то, что движок уже посчитал. Что именно нести — `Consolidated`,
`Declined`, `Unanswered`, `BatchesDropped`, `ClassifyBatchesDropped` — и в какой форме (плоские поля,
вложенный объект, одно суммарное «полон/неполон» плюс детали) **решаешь ты**; аргументируй в отчёте.
**Что не обсуждается:** (а) поля берутся из УЖЕ вычисленного — ПРОИЗВОДНАЯ от посчитанного (например
бит «полон») законна и приветствуется, запрещён ПЕРЕСЧЁТ полноты вторым механизмом;
(б) версию формата сайдкара (`tm-bank-v1`) НЕ бампать — платформа разбирает аллоулистом и новое поле
игнорирует, это ПИНГ ей, а не поломка; (в) на stdout-экране оператор узнаёт о пропущенных кандидатах. ⛔ **Но НЕ обязательно пер-строчной
пометкой, и вот почему — это ловушка вырожденной фикстуры.** `cmd/tmctl/render.go`: `bankStopStdoutCap =
20`, строки сортируются `reviewRank`, а у не-спрошенной строки `Dst` пуст ⇒ ранг **1000** ⇒ она уходит
ПОСЛЕДНЕЙ и при ≥20 строках проваливается под кап. Фикстура на ≤20 строк будет зелёной, а на боевом банке
оператор не увидит ничего. ⇒ **задано СВОЙСТВО** (оператор со стдаута узнаёт, сколько кандидатов не
спрашивали и почему), **форму выбираешь ты** — сводная строка над таблицей или пер-строчная пометка плюс
работа с капом; и фикстура обязана быть ШИРЕ капа;
(г) секцию предложений сайдкара не трогать — по ней применено правило остановки (`D39.225` п.10, строка 353).
**«ОБА ЧИТАТЕЛЯ» = машинный сайдкар и stdout-экран.** Текстовый сайдкар стоп-таблицы уже несёт `NOT
ASKED` и счётчиков полноты от тебя НЕ требует — трогать его в этом пункте не надо.
⛔⛔ **ПОСЧИТАННОЕ ДВИЖКОМ САМО НЕТОЧНО — не довози его буквально, иначе построишь ложную тревогу.**
Три факта, которые смешивать нельзя:
- **`BatchesDropped` (рендер-пас срезан)** — вот это и есть неполный БАНК: термины не консолидированы.
- **`ClassifyBatchesDropped` (классификатор срезан)** — банк ПОЛОН, не уточнены ТИПЫ. Другой факт.
- **`Unanswered`** — по собственному тексту предупреждения «counts them together with terms the role saw
and did not answer», то есть СЛИВАЕТ «не предложено бюджетом» и «предложено, роль не ответила».
⛔ И предупреждение `terminologist.go` кричит «PARTIALLY consolidated» при `BatchesDropped > 0 **||**
ClassifyBatchesDropped > 0`. На прогоне 08.09 было `batches_dropped=0`, `classify_batches_dropped=1`,
`consolidated=29`, `unanswered=0` — то есть рендер-пас ЦЕЛ, а движок всё равно говорит «неполон».
**вывод «полон/неполон» из `dropped > 0` даёт ЛОЖНУЮ ТРЕВОГУ ровно на этом прогоне** — то, что пункт
запрещает абзацем ниже. Полнота БАНКА выводится из рендер-паса; срез классификатора несётся ОТДЕЛЬНО.
⛔⛔ **ГЛАВНАЯ ЛОВУШКА ЭТОГО ПУНКТА, и наивная проекция в неё падает.** `exportBank` зовётся из ПЯТИ мест
(`bookrun.go:180` run-start/seeded · `bookrun.go:258` run-finished · `mining.go:249` auto-continue ·
`mining.go:269` signature-stop · `status.go:1125` redrive/re-seeded), а `r.lastTerminology` заполняется
РОВНО в одном (`mining.go:170`). Плюс `mining.go:66`: майнинг не сконфигурирован ⇒ терминология не
запускается вовсе. ⇒ на четырёх границах из пяти счётчики нулевые, и «`consolidated=0 unanswered=0`»
прочтётся как «неполноты нет» там, где её просто НЕ МЕРИЛИ. Это класс `D39.202` — прибор, отвечающий на
свой вопрос, — и ровно он куплен строкой 312 из §4.3 («`toc_unresolved = 0`, потому что целей НОЛЬ»).
**Заказ:** «не мерено» и «полно» обязаны быть РАЗЛИЧИМЫ на выходе; форма различения твоя. ⚠ Самый простой
путь — граница: поля осмысленны там, где пас ШЁЛ (`signature-stop`, `run-finished`), и на `run-start/seeded`
их быть не должно вовсе (`omitempty`), иначе «неполон» повиснет на проекции следующего прогона.
**Предъявление — не «поле добавлено», а «читатель видит»:** нужны ТРИ фикстуры, и третья — та, которую
легче всего забыть: (1) банк неполон ⇒ артефакт и экран это НЕСУТ; (2) банк полон ⇒ молчат, ложной тревоги
нет; (3) **терминология не работала вовсе** (граница run-start или майнинг не сконфигурирован) ⇒ выход
говорит «не мерено», а НЕ «полно».
**КЛАСС A ЗАВОДИТСЯ СОЗНАТЕЛЬНО, и это надо ОБЪЯВИТЬ.** `18-bank-ontology.md` требует у проекции
НАЗВАННОГО читателя. У новых полей его сегодня нет: `Consolidated`/`Unanswered`/`BatchesDropped` дают
**0 хитов в `platform/internal/` при 183 go-файлах** (контроль: слово `bank` знают 65 из них). Читатель
появится на платформенной стороне и заказан ПИНГОМ. ⇒ назови класс A в отчёте прямо, как это сделал
предыдущий пак зоны (`D39.225` п.4), а не оставляй читателя подразумеваемым.
### 4.2 Два числа о деньгах, не сходящиеся с леджером — **делай РОВНО так**
- **(а) Итог волны занижен.** `internal/pipeline/waverun.go:218``res.TotalUSD += r.lastTerminology.CostUSD`,
а `CostUSD` по собственному докстрингу — «what THIS run's RENDER phase paid»; классификаторская стоимость
лежит отдельным полем `ClassifyCostUSD` (`terminologist.go:145`, заполняется на `:406`) и в
`TOTAL (this run)` не попадает вовсе. **Замер прогона 08.09 по строкам леджера:** терминологическая фаза
стоила `$0.013608` = классификатор `$0.009013` + рендер-батчи `$0.002775` и `$0.001820`; в итог входит
только рендерная сумма `$0.004595`, то есть **две трети денег фазы до итога не доезжают**.
**ВКЛЮЧИТЬ.** ⚠ Я рассматривала второй вариант («объявить в строке, что классификатор считается
отдельно») и снимаю его: он оставляет `TOTAL` МЕНЬШЕ леджера, а этого пак запрещает ниже и требует §13.
Вариант один. ⚠ Числа выше — из отчёта зоны о том прогоне (`docs/PROGRESS.md`, секция «Бэкенд»), СЫРОГО
леджера того прогона в дереве НЕТ (замер: `.db` новее 07.09 — 0 при 163 в дереве) ⇒ предъявляй на СВОЕЙ
фикстуре, а эти числа бери как описание симптома, не как мишень для воспроизведения.
- **(б) Разложение относит классификатор в «купил ничего».** Обе банк-роли адресуются одним синтетическим
именем стадии (`terminologist.go:65` `terminologyStageName`, проставляется обоим), а разложение ключуется
`pos{Chapter, ChunkIdx, Stage}` без роли (`internal/pipeline/paidtail.go`), и более ранний из двух батчей
с одинаковой позицией уезжает в `SupersededUSD`. Улика живого прогона — три строки леджера
(`0 0 terminology classifier` · `0 0 terminology terminologist` · `0 1 terminology terminologist`) при
печатном «the book's TERMINOLOGY … (2)», где два — последние.
⇒ развести позиции банк-ролей в ключе разложения. Роли различимы уже сегодня (`roleTerminologist` /
`roleClassifier`), и колонка `role` в БД ЕСТЬ (`store/migrate.go`) — **миграция не нужна**.
⛔⛔ **ПРАВИТСЯ ЧИТАТЕЛЬ, НЕ ПИСАТЕЛЬ, и это денежная граница, а не вкус.** Соблазн дать классификатору
СВОЁ имя стадии есть, и он покупает перепокупку: чекпойнты адресуются `EnsureJob(bookID, 0,
terminologyStageName, snapID)`, новое имя стадии = новый джоб = старые чекпойнты не матчатся = **на
резюме классификатор оплачивается заново**. ⇒ имя стадии НЕ переименовывать; роль доезжает до разложения
отдельным полем.
**Но на ВХОДЕ разложения роли нет:** `store.CheckpointUsage` поля `Role` не несёт, и запрос её не
выбирает (`internal/store/ledger.go`, тип и его `SELECT`). ⇒ **правка `internal/store/ledger.go`
РАЗРЕШЕНА и входит в заказ** — это не выход за скоуп.
**И она делает ЛОЖНЫМ комментарий, стоящий в самом предмете:** `paidtail.go` про идентификацию
класса по стадии — «equivalent to reading `checkpoints.role` and needing no store change». Правя
механизм, снимай и это утверждение: держать два носителя одного знания в согласии — ровно то, что
запрещает `D39.216` п.3(б).
⛔ **ТЕСТ, КОТОРЫЙ ОБЯЗАН ОСТАТЬСЯ ЗЕЛЁНЫМ И НЕ ПОДЛЕЖИТ ПРАВКЕ:**
`TestAReboughtGlossaryBatchIsStillALoss` (`paidtail_test.go`) держит ДВА вызова в ОДНОЙ позиции и
требует, чтобы ранний уехал в `superseded`. Это ДРУГОЙ случай — перепокупка ОДНОЙ роли при росте
черновикового множества (строка 233), и она остаётся потерей. Твоя правка разводит РАЗНЫЕ роли, а не
амнистирует перепокупку одной. Покраснел он — ты сломала класс, а не починила свой.
⚠ И существующие фикстуры `paidtail_test.go` — ровно вырожденный класс `D39.208` п.5: коллизионного
случая (две РАЗНЫЕ роли в одной позиции) в них нет ни одного, поэтому батарея на этом дефекте зелёная.
Фикстуру, где роли РАЗЛИЧНЫ, заводишь ты.
**Обе половины предъявляются СВЕРКОЙ С ЛЕДЖЕРОМ**, а не чтением кода: тест берёт числа отчёта и числа
леджера на одной фикстуре и утверждает их отношение. ⚠ **Отношение назови точно, это два разных
утверждения:** для (а) — РАВЕНСТВО (итог обязан сойтись с суммой строк), для (б) — что классификаторская
строка попала в свой класс, а не в `superseded`. «Леджер есть нижняя граница» — про мир, а не про этот
тест: там, где движок не записал вызов, равенства нет и быть не может, и это отдельный класс (`PD-441`,
строка 331), в пак НЕ входящий.
⛔ **ГРАНИЦА ЭТОГО ПУНКТА, и она снята чтением ОБЕИХ сторон шва — не выводи её заново и не расширяй.**
Это правка ОТЧЁТА, а не денег: платформа биллит НЕ `TotalUSD`, а `committed_usd` — сумму по таблице
`spend` (`platform/internal/runs/reconcile.go` докстринг `attemptSpend`), а классификатор ходит тем же
учтённым путём, что и все стадии (`runBankAttempt``runAttempt`, `terminologist.go:783-796`), и его
деньги в `spend` УЖЕ лежат. ⇒ ни один счёт пользователя не двигается, согласия владельца пункт не требует.
Если тебе кажется, что нужно тронуть ЛЕДЖЕР, — это стоп и пинг: там будет двойной счёт, а не починка.
**СОСЕДНЯЯ МИНА — не в этом пункте, но в шаге от него, и я называю её ГРАНИЦЕЙ, а не заказом.** Твоя
правка `waverun.go` порога согласия НЕ касается: база порога — `projectBookUSD` в
`internal/pipeline/rebill.go`, другая сумма. ⛔ Но если работа поведёт тебя в ПРОЕКЦИЮ (`ProjectedBookUSD`,
`status`, добавка контура к `bookUSD`) — там порог СОГЛАСИЯ на пере-оплату считается БЕЗ банкового контура
НАМЕРЕННО («THE CONSENT THRESHOLD IS COMPUTED WITHOUT THE CONTOUR, and the two numbers part company HERE»),
и **первая версия той правки сложила контур ДО взятия порога и тихо ОСЛАБИЛА денежный гейт; поймала
приёмка.** ⇒ пошла в проекцию — это стоп и пинг, а не «заодно».
### 4.3 Третий член того же класса — **реши сам, бери если по пути**
Строка **312**, остаток Д-3: движок предупреждает о нечитаемом оглавлении в свой `Log.Warn`, а платформа
читает `events.jsonl` — то есть та же форма «знает один, смотрит другой». Проверь сам, тот ли это класс;
если да и если дёшево — закрой тем же движением. Если дорого или предмет другой — скажи в отчёте, оставлю
строкой.
### 4.4 Чего в паке НЕТ**не делай**, и это ОБЪЯВЛЕННЫЕ сужения
- **Секция предложений сайдкара банка** — по ней применено правило остановки (`D39.225` п.10), гейт
возврата назван строкой 353: появление первого ЧИТАТЕЛЯ секции. Четвёртый круг не открывается.
- **Платформенные половины** строк 253 и 224 — чужая зона. Твоё действие — ПИНГ в отчёте, не правка.
- **Всё, что двигает** снапшот, хеш запроса, ключ манифеста или версию встроенных данных. В этом паке
заказанных смен поведения ДВЕ, и обе — в путях отчётности и артефактов: §4.1 и §4.2.
- ⛔ **СТРОКА 360 — ТАЙМАУТ ЧТЕНИЯ, МАСКИРУЮЩИЙСЯ ПОД НЕЧИТАЕМОЕ ТЕЛО. НЕ БЕРЁШЬ, и я называю это прямо,
потому что она ДОРОЖЕ всего, что в этом паке.** Замер полигона: 23.834.5 % цены редакторской руки;
ошибка чтения выбрасывается в `_`, обрезок биллится по оценке и РЕТРАИТСЯ. Почему не здесь: (1) это
другой класс — не «движок знает, а читатель не видит», а «движок не знает, что оборвал сам»; (2) её
денежная половина требует слова владельца (платит ли пользователь за вызов, который оборвали МЫ) и
сцеплена со строкой **331**, где тот же предмет закрыт ПРОТИВОПОЛОЖНО — нулём вместо оценки. Идёт
отдельным паком. ⚠ Наткнёшься на неё по пути — **не чини, отдай наблюдением в отчёт**: молчаливая
починка здесь смешает два денежных класса в одном лендинге.
- **Строки 261 · 262 · 331** — члены того же класса «вызов оборвали мы»; уезжают вместе с 360.
- **Переписывание `Runner`, дробление `internal/pipeline`** — запрещены `D39.224` §7. ⚠ «Новый детектор
границ» там же — не запрет, а УСЛОВИЕ; к твоим задачам не относится, но силу пункта не путай.
- **Платные вызовы** — ноль, см. §Деньги.
## 5. Мандат самопроверки ИСПОЛНЕНИЕМ — «перечитал сам» не считается
1. **Каждая новая гарантия предъявляется МУТАЦИЕЙ**, и краснота читается ПО ТЕКСТУ падения: говорит ли он
про сломанное тобой. Правый вердикт по неправой причине — дыра, а не поимка.
2. **Мутации — ТОЛЬКО на копии дерева, и рецепт копии УЖЕ СТОИТ в цели `mutations` (`backend/Makefile`)**
своего однострочника не пиши, он станет вторым носителем и разойдётся. Гонишь батарейное подмножество —
`make mutations`. Гонишь одиночную посадку `-id`, то есть копию делаешь руками, — повтори цель ДОСЛОВНО:
распаковка в **подкаталог** `$tmp/backend` (а не в корень времянки), исключения `--exclude='./.env*'` и
`--exclude=./bin`, и **два** трапа: `EXIT` убирает, а `INT TERM` убирает И ВЫХОДИТ (`exit 130`) — иначе
прерванный прогон продолжает жить. Рабочее дерево не правится никогда, в том числе «на секунду».
3. **Мутируй и СВОИ новые фикстуры.** ⛔ И особая осторожность здесь: у тебя ДВА условных сообщения (неполный
банк · пропущенный кандидат). Пин на каждое обязан иметь фикстуру, где оно ОБЯЗАНО прозвучать, и вторую,
где оно обязано молчать. Предыдущий пак этой зоны выпустил ТРИ ложных операторских предупреждения подряд,
и все три были зелёными именно потому, что утверждалось только молчание.
4. **Новые записи — в каталог** `cmd/tmmutate/mutations.json`. Ключи СТРОЧНЫЕ: `id`, `package`, `why`,
`edits`, плюс необязательный `battery`. ⚠ `package` — пакет ПИНА, а не пакет правки: инструмент гоняет
один пакет. ⚠ `battery` ставится не всем: сегодня это **48 записей из 259** — гейтовое подмножество;
какие из твоих туда входят, решаешь ты и говоришь почему.
5. **BASELINE НА ВХОДЕ** (снят оркестратором 08.09): `make battery` → 19 `ok` · 0 `FAIL` · 4 «no test files» ·
4 скипа поимённо; `make mutations`**48 записей · 0 unexpected · `anchors swept: 0 of 259`**. Красное на
входе — находка и пинг, а не твоя работа.
6. **Субагенты РАЗРЕШЕНЫ ЯВНО** — и на охват, и на адверсариальный проход по ГОТОВОЙ работе. Направление,
что здесь уязвимее всего: (1) вторая половина утверждений про условные сообщения; (2) сверка отчёта с
леджером — легко заявить и трудно доказать; (3) фикстуры, где «полон» и «неполон» случайно совпали.
7. **Проверяемый артефакт** — таблица мутаций в отчёте: `мутация → где посажена → красный тест → ТЕКСТ
падения`, и отдельной строкой **выжившие**, если такие есть.
## 6. Оси ревью — характер «Код», плюс деньги (ось вправе заменить с аргументом)
Тест утверждает не то, что думает (`D39.171`): часы как способ синхронизации там, где предмет не время, и
ассерт по подстроке в буфере, куда пишет кто-то ещё. Обе формы дают и ложное красное, и **тихо-зелёное**
оно дороже: зелёное, не проверившее ничего, не перечитывает никто. Третья ось твоя: **деньги** — любое число
выводится из СЫРОГО леджера, и леджер есть нижняя граница, а не оценка.
## 7. Записка-план ДО работы
До первой правки — короткая записка в отчёт: что берёшь, в каком порядке, что считаешь рискованным. После
работы — сверка комплектности против §4 МЕХАНИЧЕСКИ (таблицей), не перечитыванием.
## 8. Заявление = команда
Каждое число и категорика отчёта — с командой, которой получены. Отрицательный замер печатается ВМЕСТЕ с
контрольной величиной, доказывающей, что вопрос задан существующему предмету. Сверх этого, про код:
- **перед отчётом сверь каждый клейм с результатом инструмента ЭТОЙ сессии**, а не с памятью и не с этим
промтом: адреса §4 сняты 08.09 и могли уехать;
- **дифф `^func Test` — исполнением**, ИМЕНАМИ через `comm`, а не разностью счётчиков;
- **последний абзац отчёта оказался планом или обещанием? сделай это СЕЙЧАС**;
- ⛔ править или удалять тест/голден/гейт, чтобы он прошёл, — НЕДОПУСТИМО; исключение — правка от заказанной
смены поведения (§4.1, §4.2), и она объявляется отдельным пунктом;
- сессия длинная — поставь себе интервальную самоверификацию субагентом против ЯВНЫХ критериев §13.
## 9. Эхо-протокол старта
ДО работы — ≤10 строк **СВОИМИ словами**: скоуп · инварианты, которые не имеешь права сломать · что НЕ
делаешь. Дословный пересказ промта подтверждает канал, но не понимание, и ошибку промта повторяет вместе с
ним. Отправь эхо оркестратору по адресу из прямого канала (§12); канала нет — эхо идёт первой секцией
отчёта, и работа продолжается.
## 10. Что НЕ удалось — обязательная секция отчёта
Что не вышло, что не проверено, где сомневаешься. Пустая секция при непустой работе — сама по себе находка;
«не проверено» и «опровергнуто» — РАЗНЫЕ исходы.
## 11. Канал вопросов и право сказать «этого делать не надо»
Конфликт промта с кодом или доками — **пинг, а не интерпретация**: промт мог устареть, и в этом проекте это
случается — за прошлые сутки в промтах и трекере нашли шесть ложных утверждений, три из них авторства
оркестратора. Считаешь пункт ненужным — скажи с доводом; такие возражения ценнее послушного исполнения.
## 12. Прямой канал
Механизм — `CLAUDE.md` §«Связь между сессиями». Впиши СВОЙ блок в `/tmp/textmachine-channel` первым делом,
чужие блоки не трогай. ⚠ Адрес из файла не доказывает, что сессия жива: сверься с `ListAgents`. Нужной роли
нет или имя не отвечает ⇒ **канала нет, и это НОРМА: не опрашивай сессии подряд** — вопрос секцией в отчёт.
## 13. Критерий завершённости — по нему тебя примут
Работу доводишь до конца САМ; приёмка идёт последней и вторым кругом твоей самопроверки не служит.
Готово — это когда:
- у КАЖДОГО пункта §4 есть исход: сделано · не делаю с доводом · пинг;
- круги СОШЛИСЬ: последний проход не дал НОВЫХ находок, прежние закрыты таблицей
«находка → что сделано → ЧЕМ ПРЕДЪЯВЛЕНО»;
- **каждый класс закрыт с ЗНАМЕНАТЕЛЕМ: «закрыт в N носителях из M»**, и M посчитан командой, а не на глаз;
- таблица мутаций полная, выжившие названы;
- **числа отчёта сверены с СЫРЫМ леджером**, а не с логом;
- числа сняты ПОСЛЕ последней правки, а не вспомнены;
- всё живое — в ДЕРЕВЕ, а не в письме;
- сказано явно: **«работа завершена, править не планирую»**. Без этой фразы пак считается идущим.
## Деньги
**Пак $0.** Платных вызовов ноль: всё, что нужно предъявить, предъявляется на ФЕЙК-провайдере и на
собственных фикстурах. ⚠ **Сырого леджера прогона 08.09 в дереве НЕТ** (замер: файлов `.db` новее 07.09 —
**0** при 163 в дереве, свежайший от 02.09) — числа этого прогона живут только описанием в отчёте зоны, и
воспроизводить их не надо. Если тебе кажется, что задача требует платного прогона, — это стоп и пинг, а не
вызов.
## Отчёт
Секция **«Бэкенд»** в `docs/PROGRESS.md`, подзаголовок `#### Пак «движок уже знает»`: исход по каждому
пункту §4 · таблица мутаций · таблица «находка → что сделано → чем предъявлено» · столбец «закрыт в N из M» ·
сверка отчёта с леджером числами · пинги в чужие зоны (платформенные половины 253 и 224) · §10 «что не
удалось» · вопросы оркестратору.

File diff suppressed because one or more lines are too long

View file

@ -70,6 +70,13 @@
4. Разметка свободы: каждый пункт — «делай РОВНО так» (отступление = пинг) ЛИБО «реши сам и
аргументируй». Неразмеченная середина — главный источник миссимплементации. Советы — приорами
с пометкой «опровергается аргументом/замером».
**«ИНСТРУМЕНТ ЗАДАН, НЕ ВЫБИРАЙ САМ» — САМАЯ ОПАСНАЯ СТРОКА ПАКА, и потому самая проверяемая.**
Она снимает суждение сессии, значит ошибка в ней не ловится ничем ниже. Прежде чем её писать:
(а) прочитай, что канон называет ШТАТНЫМ механизмом для этого класса — расширяемость обычно живёт не
там, где кажется (08.09: я задала «новый корневой `code`», тогда как корневой закрыт и его рост —
ломающий минор, а открыт `cause.code` и словарь `errors[]`); (б) проверь, не ПОСТРОЕНО ли уже
заказываемое (там же: обе причины отказа уже ехали перечислимыми); (в) запрет «не изобретай третий
путь» законен только после (а) и (б).
5. Мандат самопроверки ИСПОЛНЕНИЕМ: НАЗВАННЫЙ механизм + явное разрешение субагентов (иначе
дефолт-запрет харнесса тихо побеждает) + проверяемый артефакт-находки. Тест на вырожденность:
если букву удовлетворяет пустое исполнение («перечитал сам») — дефектна строка промта.
@ -97,7 +104,10 @@
Плюс к любому промту: построчная сверка инструкций с путями/каналами ВНЕ зоны сессии против зонной
таблицы CLAUDE.md (D39.99 п.4) · состав против ВСЕХ листов владельца (реестр требований · зонные
журналы · его явные просьбы; пропуск подписывается пропуском) · фантом-гард: каждая цитируемая
D-ссылка грепается по D-логу · mid-flight аддендумы доезжают ТОЛЬКО релеем — владелец пере-передаёт
D-ссылка грепается по D-логу — ⛔ **и до НОМЕРА ПУНКТА, а не до тела ноты:** тело есть почти всегда, и
проверка «`^## D<номер>` нашёлся» зеленеет на ссылке `D39.226 п.10` у ноты, где пунктов восемь (08.09,
поймал опровергатель, не я; правило остановки жило в `D39.225` п.10). Команда:
`awk` по телу ноты + `grep -E '^\*\*<пункт>\.'` · mid-flight аддендумы доезжают ТОЛЬКО релеем — владелец пере-передаёт
правку сессии отдельным сообщением, сессия эхо-подтверждает — и
отдельным пунктом отчёта · артефакты сессий: **что должно пережить рестарт и жить дальше — в
РЕПОЗИТОРИЙ** (доказательная база пака — `archive/reports/` при приёмке); вне репозитория — только

View file

@ -0,0 +1,354 @@
# Пак платформы: «РАЗРЕЗ ПРИЁМА ДО ГОТОВНОСТИ И ПРАВДА О СЕБЕ»
## 1. Какая проблема и что решит твой результат
Синхронный разрез книги на приёме заландён с формулировкой **«ПОСТРОЕН, НО НЕ ГОТОВ»**: из одиннадцати
major прошлого пака девять легли в этот один механизм, каждая починка открывала новую площадь, и зона
прекратила его чинить по правилу остановки. Десять пунктов остались, все проверены живыми сегодня, ни один
не починен молча — их и надо забрать.
Острее всего не аккуратность, а **доступность хоста**, и точная формулировка тут важнее лозунга.
`parseClaimed` — ОДНО тело, порождающее процесс движка, и у него **ДВА входа**
(`platform/internal/books/parse.go`, докстринг: «the body of a parse, shared by the queue worker and the
intake»). Вход ЧЕРЕЗ ОЧЕРЕДЬ ограничен: River жив, `parseWorker` зарегистрирован, `MaxWorkers` берётся из
конфига с дефолтом **4**. Вход СИНХРОННЫЙ, из HTTP-обработчика приёма, не ограничен **ничем** — именно его
разрез и завёл, перестав ставить задание (пункт 8 списка остановки: «разрез потерял единственный
ограничитель параллелизма»). ⇒ N одновременных загрузок = N процессов на одной VM без потолка, при том что
рядом стоит РАБОТАЮЩИЙ потолок для второго входа.
Второе: **платформа неверно рассказывает о себе самой**, и с этого рассказа онбордится следующая сессия
зоны. Шапка зонного журнала называет последним заландённым паком «форму заказа» с каноном `0.12.0` и держит
«деньги и правду» как незакоммиченный пак в дереве — а тот заландён `fda0679`, коммитов в `platform/` после
него **0**, канон сегодня `0.13.0`. Три ряда регистра стоят `open`, будучи заландёнными, и проект
отчитывается владельцу «major 3» при фактически одном.
**Что покупает твой результат.** (1) Приём перестаёт быть местом, где один пользователь может положить хост.
(2) Оператор и прокси знают, сколько маршрут молчит. (3) Следующая сессия зоны начинает с правды, а не с
картины недельной давности.
## 2. Зона и git
**Зона записи — `platform/`** плюс свой зонный журнал `platform/docs/platform-PROGRESS.md` (решение
владельца: платформа пишет ТОЛЬКО туда, в `docs/PROGRESS.md` не пишет). Чужие зоны (`backend/`, `frontend/`,
`eval/`, `books/`) не трогаешь ни строкой. `docs/` — зона оркестратора, правки туда просишь пингом.
**Ты НЕ коммитишь**: дерево готовишь и передаёшь оркестратору (канон — `CLAUDE.md`
§«Git-координация мультисессий»). Никаких `git add -A`, `reset --hard`, переписывания истории. `.env` не
читать никогда.
## 3. Карта чтения — ЗАКОН, дальше только по её ссылкам
⛔ **Обязательное пре-чтение сверх карты, для КАЖДОГО платформенного пака:**
`platform/docs/ENGINEERING_STANDARDS.md` — норматив зоны, **§3 DoD — закон**, отступление от любого его
пункта = пинг, а не решение сессии. Оттуда же берётся форма твоей самопроверки (§5 ниже её не
переписывает, а указывает на неё) и правило копии под мутации: **копия несёт КАНОН**
(`cp -a --parents platform docs/architecture/14-api-contract <куда>/`), иначе гейт версии контракта
постоянно красный и МАСКИРУЕТ дельту — мутация выходит «выжившей» и даёт ложную находку.
Направление зоны — `platform/docs/PLATFORM_DIRECTION.md` (ратифицировано).
1. `docs/BACKLOG.md` — строка **332**, твой основной заказ. Греп `^| 332 |`.
**Копии десяти пунктов в строке НЕТ — она указывает на носитель:** список живёт в твоём зонном
журнале, греп `СРАБОТАЛО ПРАВИЛО ОСТАНОВКИ`. **Открой его и сверяйся с ним, а не с §4 этого промта:**
§4 берёт СЕМЬ из десяти, три вынесены в §4.8 с доводами. Расхождение списка с промтом — пинг.
2. `platform/docs/DEFECT_REGISTER.md` — открытые ряды. ⚠ Числа брать прибором: `python3 docs/scripts/counts.py`
(он читает колонки С КОНЦА — наивный разбор слева даёт ложное «open»).
3. `platform/docs/platform-PROGRESS.md` — свой журнал. ⚠ Его ШАПКА протухла (см. §4.5) — читай её как
предмет работы, а не как факт.
4. `docs/architecture/14-api-contract/` — канон API: `openapi.yaml` + `README.md`. Версию брать ФАЙЛОМ
(`grep '^ version:' openapi.yaml`), не из доков.
5. `platform/deploy/README.md` — рантбук выката: то, что читает человек, поднимающий систему.
Ратифицированное, нужное по существу, вложено сюда и похода в журнал решений НЕ требует:
- ⛔ **`title` и `detail` проблемы НИКОГДА не показываются пользователю** — канон, `openapi.yaml`: «written
for a DEVELOPER and a log, and a client MUST NOT show either to a user». Фразу человеку рисует КЛИЕНТ по
`code`. Инструмент серверной фразы — `localized`, и его не несёт **0 строк кода при 197 `.go`**.
- **`D39.176` п.1** — продуктовых КВОТ и фри-тир-лимитов нет и не проектируется. Из гигиены интейка законны
только инженерные вещи: ретеншен, свип сирот, потолок диска.
- **`D39.216`** — где форма не тянет, её переписывают; «дешевле подпереть» доводом не является.
- **`D39.183`** — запрет править тесты «чтобы прошло» про МОТИВ; правка от заказанной смены поведения —
обслуживание, но объявляется в отчёте.
- ⛔ **`D39.208` п.5 — норму купила ТВОЯ ЖЕ зона, и она прямо про §4.4.** Три круга ревью нашли девять
вещей при ЗЕЛЁНОЙ батарее на всех девяти, потому что фикстуры зоны делали два РАЗНЫХ числа ОДНИМ.
**Фикстура, в которой две различные величины совпадают, делает весь их класс расхождений невидимым, и
никакой батареей это не ловится.** Лечение — не тест, а ФИКСТУРА, где величины РАЗЛИЧНЫ.
- ⛔ **Правило, купленное движковой зоной дважды подряд:** пин на УСЛОВНОЕ сообщение обязан иметь фикстуру,
где оно ОБЯЗАНО прозвучать. Тест, утверждающий только молчание, вакуумен ровно там, где сообщение ложно.
## 4. Состав и разметка свободы
### 4.1 Ограничитель параллелизма на пути порождения процессов — **делай РОВНО так по составу, форму реши сам**
Сегодня синхронный вход не ограничен ничем (замер в §1). **Заказ:** потолок одновременных разборов, за
которым запрос либо ждёт (см. приор ниже), либо получает честный отказ.
**Где ставить и почему это единственная граница.** Вызывающих у порождения ТРИ: интейк (без потолка),
воркеры River (`MaxWorkers` 4), свип (последовательный). Ставить надо на ОБЩЕЙ точке порождения
(`Engine.Manifest` / `s.manifest`), а не в маршруте, иначе два других входа останутся мимо.
⛔ И довод к тому, чтобы дефолт был скромным: разбор идёт **голым `exec.CommandContext`**
(`internal/runner/engine.go`, `readEngine`) — **без `systemd-run`, без cgroup, без `MemoryMax`**, в отличие
от ПРОГОНОВ, которые обёрнуты. ⇒ твой потолок — единственная граница по памяти, какая у приёма есть. Форму (семафор · очередь с ёмкостью · лимит на уровне HTTP) выбираешь ты и
аргументируешь; ⛔ но потолок обязан быть **конфигурируемым** и **наблюдаемым** — оператор должен видеть,
что упёрлись, а не догадываться по латентности.
⚠ Отказ при упоре — это `code` для клиента, НЕ `detail` (см. §4.6).
**Этот пункт и §4.2 связаны, и связь видна только из кода — не чини один, тихо ломая другой.** Если
потолок реализован ОЖИДАНИЕМ, время молчания маршрута после последнего байта растёт на время ожидания, и
граница §4.2 перестаёт быть границей, а число в рантбуке §4.3 становится ложным. ⇒ либо ожидание ОГРАНИЧЕНО
и его потолок входит в вывод §4.2 слагаемым, либо при упоре отказ немедленный. Третьего («подождём сколько
получится») в паке нет: неограниченное ожидание — это та же недоступность, только выглядящая как работа.
**ПРИОР ЗАКАЗЧИКА, опровергается аргументом или замером — но опровергать придётся ЯВНО.** Я написала выше
«потолок ожидания входит слагаемым в §4.2» и, проверив код, снимаю это: **ожидание внутри уже стоящего
бюджета не добавляет к `UploadSettle` НИЧЕГО.** `cutNow` держит разрез под
`context.WithTimeout(context.WithoutCancel(ctx), CutBudget)` (`books/books.go`), и `parseClaimed → manifest`
идёт под ним. Простоял в очереди семафора дольше бюджета ⇒ `errNotConclusive``201 parsing`, книгу
доделывает очередь (`MaxWorkers` 4) — ровно та штатная деградация, ради которой `CutBudget` и существует.
⛔ И тогда же снимается требование «потолок конфигурируем»: `CutBudget` сделан КОНСТАНТОЙ намеренно —
«a knob here would let an operator push the tail of an upload past the window in which its key can still be
replayed» (`books/parse.go`, над `CutBudget`; гейт — `config/config.go`, сравнение
`in.UploadDeadline+books.UploadSettle` с `min(UploadGrace, ClaimStale)`). ⇒ **конфигурируемым обязан быть
ПОТОЛОК ПАРАЛЛЕЛИЗМА (число), а не время ожидания.**
**И если ты всё же выбираешь немедленный отказ — минор НЕ нужен, проверено:** `service_unavailable` уже
стоит в закрытом словаре канона, `cause.code` открыт, `Retry-After` каноном предусмотрен. Не выбирай
ожидание из страха перед контрактом.
### 4.2 Бюджет приёма выводится ИЗ кода, а не ставится рукой — **делай РОВНО так**
`platform/internal/books/parse.go``UploadSettle` объявлен как «худший случай всего, что загрузка ещё
делает после прихода тела», и собран формулой `CutBudget + 4*writeBudget`. Пере-счёт по коду даёт другое
число: шесть шагов, каждый на своём `WithoutCancel`, ни один не вложен.
**Значение не пинит ничто:** `UploadSettle` встречается в тестах **3 раза в 2 файлах**, и два из трёх
сравнивают константу с самой собой (`config/effective_test.go`), третий — строка в имени подтеста.
**Заказ — СВОЙСТВО «РОВНО так», ФОРМА твоя, и обе формы названы с ценой.**
Свойство: **хвост после последнего байта ≤ `UploadSettle` при ЛЮБОМ числе шагов**, и это проверяемо.
- Форма A (перечень): пере-перечислить шаги и их бюджеты. ⚠ Это ровно та форма, что подвела ТРИЖДЫ, и
`D39.216` называет её заплатой; выбрав её, объясни, чем твой четвёртый перечень надёжнее трёх прежних.
- Форма Б (структурная): ОДИН отсоединённый контекст хвоста с дедлайном `UploadSettle`, все шаги берут
`min(шаг, остаток)`, `WithoutCancel` на пути ровно один. Тогда добавленный шаг границу не двигает
ПО ПОСТРОЕНИЮ.
**И от выбора зависит §5.3:** «добавь шаг — пин обязан покраснеть» осмысленно при форме A; при форме Б
пин обязан утверждать САМО свойство (хвост укладывается в дедлайн), потому что шаг его не двигает. ⚠ Границу выводили трижды и
трижды неверно, каждый раз по новой причине (носитель — `PD-464` и твой журнал, не нота), поэтому «поднять
руками» здесь запрещено мандатом чистоты `D39.216`.
### 4.3 Рантбук не знает, что маршрут молчит после последнего байта — **делай РОВНО так**
`platform/deploy/README.md` про таймаут ОТВЕТА прокси не говорит ничего (греп по теме — **0 хитов**).
Единственное указание про прокси — размер тела и таймаут ЧТЕНИЯ запроса. Прокси с дефолтным таймаутом
ответа вернёт человеку ошибку на книге, которая на самом деле **принята**. **Заказ:** рантбук называет
величину молчания числом, выведенным в §4.2, и требование к прокси; и это единственная правка рантбука в
паке — остальное про выкат не трогай.
### 4.4 Три места приёма, где сбой неотличим от нормы — **состав РОВНО так, лечение реши сам**
- `platform/internal/books/books.go``ClaimParse` в `cutNow` не различает **гонку** и **сбой БД** и
молчит; книга остаётся `parsing` без задания до свипа (окно — `claimGrace`).
- `platform/internal/pgstore/books.go` — не запинена **ВЕТКА** `RowsAffected()==0 ⇒ задание НЕ ставить` в
`ReleaseParseClaim`. ⚠ Уточняю улику, чтобы ты не сняла пункт как ложный: сама функция ИСПОЛНЯЕТСЯ
(`failfast_test.go`, `TestACutWithNoVerdictGivesTheClaimBackAndEnqueuesTheJob`), по имени в тестах её
**0 хитов при 110 тестовых файлах** — но предмет не «функция не вызвана», а «ветка не различена».
Предъявление — мутация: переверни условие, тест обязан покраснеть по ТЕКСТУ про задание.
- Свип `StuckIntake` — третий претендент на ту же строку, и он считает от `coalesce(parse_started_at,
added_at)`, где `added_at` штампуется ДО прихода тела.
Все три — про то, что «застряло» и «сломалось» выглядят одинаково. Лечение твоё; предъявление —
исполнением, и на каждое условное сообщение нужна фикстура, где оно ОБЯЗАНО прозвучать.
### 4.5 Правда о собственном состоянии — **делай РОВНО так, это $0 и это предусловие всего остального**
- **Шапка твоего журнала лжёт о том, где вы находитесь:** называет последним заландённым паком «форму
заказа» с каноном `0.12.0` и держит «деньги и правду» как незакоммиченный пак в дереве. Замер: пак
заландён `fda0679`, коммитов в `platform/` после него **0**, канон `0.13.0` (файл-носитель). Приведи к
правде — с этой шапки онбордится следующая сессия зоны.
- **Три ряда регистра несут диспозицию «статус флипает лендинг», лендинг состоялся, а статус у всех трёх
так и стоит `open`.** Замер: `grep -c 'статус флипает лендинг' platform/docs/DEFECT_REGISTER.md` → **3**
(`PD-424`, `PD-441`, `PD-438`), у каждого колонка статуса `open`, при 465 рядах в файле.
**И маркер ВЕРЕН только на двух из трёх — не исполняй его механически.** `PD-441` двухзонный: его
ячейка сама говорит «платформенная половина закрыта… **движковая половина названа поимённо и ушла
строкой бэклога**» (строка **331** единого трекера, чужая зона) ⇒ ряд остаётся `open`, а неверен его
собственный маркер. Переведи `PD-424` и `PD-438`; у `PD-441` почини МАРКЕР, а не статус.
Во всех трёх ячейках диспозиция называет пак «в дереве» — он заландён (`fda0679`), и эта фраза
протухла тем же движением. Правя ячейку, снимай и её.
⛔⛔ **И ЭТО СДЕЛАЕТ ГЕЙТ ЧИСЕЛ КРАСНЫМ — так и должно быть, это НЕ твоя поломка.** `docs/PROGRESS.md`
держит под гардом `counts.py --check` литералы «открытых рядов регистра платформы — 112 (major 3)», и
после твоего флипа они станут `110` и `major 1`. ⛔ **`docs/PROGRESS.md`НЕ твоя зона**
(`CLAUDE.md`: платформа «в `docs/PROGRESS.md` не пишет»), и лезть туда НЕЛЬЗЯ даже ради зелени.
⇒ правильное поведение: **назови новые числа в отчёте явно** («после флипа: open 110, major 1») и
скажи, что гард красный ОЖИДАЕМО. Литерал двигаю я тем же актом, что и лендинг. ⚠ И это единственное
место пака, где красный гейт на выходе — норма; во всех остальных красное есть находка.
- **Три ряда несут незаэкранированный `|` — гигиена, и НЕ пинг.** В КОЛОНКАХ, как их печатает сам прибор
(`n-2`, шапка регистра — 7): `PD-375` → 9, `PD-422` → 11, `PD-197` → 8 при норме **7**; рядов с
НЕДОСТАЧЕЙ — 0, всего рядов 465. ⚠ Сырой `split` даёт на два больше (пустые края) — если считаешь сама,
считай тем же прибором, иначе решишь, что пак ошибся. Заэкранируй `\|` в своей зоне.
**И не заводи по этому поводу находку о гейте: она была бы неверна, я проверила.** Счёт ячеек ловить
избыток НЕ МОЖЕТ по построению — `|` внутри содержательной ячейки легален и намеренен, — а опасный
случай (черта, съехавшая в одну из трёх ПОСЛЕДНИХ колонок, где молча сдвигаются статус и вес) гейт уже
судит СЛОВАРЁМ хвоста, `tail_vocab()` со своим селф-тестом. Все три нынешних ряда доброкачественны:
лишнее легло до хвоста, статус и вес читаются верно.
### 4.6 «Честная причина человеку» — **делай РОВНО так, и начни с проверки, не построено ли это уже**
Пункт 10 списка остановки: «отказ уходит наружу только машинным кодом (`Detail` не заполняется) ⇒ честная
причина человеку НЕ доезжает». ⚠ **Формулировка пункта вводит в заблуждение, и я это заявляю как заказчик:
лечение НЕ в заполнении `Detail`.**
**Что запрещено:** канон дословно — `title` и `detail` «are written for a DEVELOPER and a log, and a
client MUST NOT show either to a user». Единственное исключение — `localized`, и его не несёт **0 строк
кода** (замер по `platform/**.go`). Значит `Detail` не заполняется ПРАВИЛЬНО, и это не дефект.
**Что уже построено — проверь ПЕРВЫМ делом, прежде чем строить.** Словарь `errors[]` по словам самого
канона ОТКРЫТ, и обе причины отказа разреза уже едут наружу перечислимыми:
`httpapi/problem.go``ItemNoBook = "no_book"` и `ItemNoChapterStructure = "no_chapter_structure"`,
ставятся в `httpapi/v0.go` на `/file`. То есть машина причину уже называет.
**Инструмент расширения — НЕ корневой `code`.** Канон: корневой `code` «stable, **closed** for this
version», а `cause.code` — «its vocabulary is **NOT closed** and grows without a minor bump». Компаньон
контракта прямо называет это механизмом расширяемости. ⇒ новый КОРНЕВОЙ код = ломающее изменение и минор
(мой акт, пинг мне); новая частная причина в `cause.code` или новый член `errors[]` — аддитивно и минора
НЕ требует. Это твой путь по умолчанию.
⛔ **И скорее всего пункт закрыт ПО ПОСТРОЕНИЮ — начни с попытки это опровергнуть, а не с работы.**
Таблица «код → русская фраза» у клиента уже есть (`14-api-contract/README.md`, и фронт её рисует), обе
причины перечислены, словарь открыт. Неисполнена только КЛИЕНТСКАЯ половина, а фронт ЗАМОРОЖЕН — то есть
это не твоя работа и не работа этого пака.
**Заказ, отсюда:** (а) проверь и объяви: закрыт по построению — или назови причину отказа приёма, которой
сегодня НЕТ в `errors[]`/`cause`; (б) нашлась такая — добери её ОТКРЫТЫМ словарём (`errors[].code` /
`cause.code`), это аддитивно; (в) нашлась причина, не выразимая кодом в принципе, — вот это пинг мне (там
`localized` и минор), с примерами, а не с тезисом.
⚠ Граница зоны: РИСУЕТ фразу клиент. Твоё дело — чтобы у него было по чему её нарисовать.
### 4.7 Живой гейт — **делай РОВНО так: проверь причину исполнением, прежде чем её повторять**
Твой журнал объясняет, почему разрез не предъявлен настоящим `tmctl`: гейт требует `$0`-пайплайна рядом с
`backend/prompts/`, то есть **записи в чужую зону ИЛИ полной копии дерева**. ⚠ Зона про копию ЗНАЛА и её не
сделала — я не утверждаю, что довод выдуман, я утверждаю, что он не проверен исполнением. А **35 строками
ниже** в том же журнале лежит рецепт снапшота через `git archive <commit> backend/configs backend/prompts`,
то есть путь без записи в чужую зону. Итог: новый механизм в платном пути ни разу не встречался с живым
движком, а единственная причина этого — непроверенное предположение.
**Заказ:** проверь рецепт исполнением. Работает — предъяви разрез настоящим движком; не работает — назови
ЧЕМ именно, и это пинг мне, а не строка «не получилось».
### 4.8 Чего в паке НЕТ**не делай**, и это ОБЪЯВЛЕННЫЕ сужения
- **Квоты интейка и фри-тир-лимиты** — сняты словом владельца (`D39.176` п.1). Из гигиены законны только
ретеншен, свип каталогов-сирот и потолок диска — и они НЕ в этом паке.
- **Пятая причина отказа** (строка 340) — ждёт слова владельца, рекомендация оркестратора уже записана.
- **Читатель секции предложений банка** (строка 224) — ждёт КЛЮЧА у движковой стороны: у строки предложения
нет ни `id`, ни полного кортежа, а дверь правок требует одного из двух. Строить раньше ключа = адресовать
правку по угаданному ключу.
- **`deleteBook`** — предмет вырос: каскад по строкам есть, по файлам нет; без ретеншена решается вполсилы.
- **Зеркало контракта фронта** — зона заморожена, отставание ратифицировано.
- **Строка 342 (восстановление из бэкапа на масштабе)** — предмет РАЗНЫЙ: это авария и большие данные, а
пак про приём. Не бери; и не считай её пропуском — она названа здесь.
- ⛔ **ТРИ ПУНКТА ИЗ ДЕСЯТИ, взятые сознательно и названные поимённо** (иначе они выпадут молча, а §1
обещает все десять):
- **п. 2 — комментарий в `cutNow` («the queue job is already enqueued») противоречит починке**, ради
которой функцию правили: на этом пути задание ставит сам `ReleaseParseClaim`. **БЕРЁШЬ, если правишь
`cutNow` по §4.4** — правка механизма обязана снять ложное утверждение о нём тем же движением
(`D39.216` п.3б); отдельной работой не заводи.
- **п. 4 — параметр `enqueue` у `StartParsing` в бою мёртв** (движок настроен всегда), а его doc
описывает его штатным путём; решение «кто ставит задание» размазано по двум местам на одном
предикате. **БЕРЁШЬ вместе с §4.1**: ты всё равно трогаешь вопрос «кто ставит задание», и развязка
этого предиката — часть ответа. Форма — твоя.
- **п. 6 — «ВСЕГДА» в дельте контракта есть гарантия по ТАЙМИНГУ, не по построению** (свип
материализатора теоретически успевает между `FinishParse` и `ReadBook`). ⛔ **НЕ берёшь:** текст
контракта — зона оркестратора, и лечение тут либо структурная гарантия, либо оговорка в каноне.
Твоё действие — **пинг мне с тем, какую из двух ты считаешь верной**, и почему.
- **Миграции** — ни один пункт пака их не требует ПО ЗАМЫСЛУ. ⚠ Известное исключение: у `StuckIntake`
(§4.4) прямое лечение просится колонкой «тело пришло / `StartParsing` отработал», которой в таблице нет
(замер: `body_received` — 0 хитов при 34 миграциях). ⇒ упрёшься в это — **пинг, а не самовольная
миграция и не полумера молча**; назови, какую полумеру видишь и что она недодаёт.
## 5. Мандат самопроверки ИСПОЛНЕНИЕМ — «перечитал сам» не считается
1. **Каждая новая гарантия предъявляется ИСПОЛНЕНИЕМ**, а условное сообщение — фикстурой, где оно ОБЯЗАНО
прозвучать, И фикстурой, где обязано молчать. Тест, утверждающий только молчание, вакуумен.
2. **Ограничитель параллелизма (§4.1) предъявляется НАГРУЗКОЙ**, а не чтением: N одновременных приёмов при
потолке M дают ровно M одновременных процессов, остальные ждут или получают названный отказ.
3. **Бюджет (§4.2) предъявляется МУТАЦИЕЙ:** добавь шаг в путь — пин обязан покраснеть.
4. **Субагенты РАЗРЕШЕНЫ ЯВНО** — и на охват, и на адверсариальный проход по ГОТОВОЙ работе. Направление:
(1) фикстуры, где «застряло» и «сломалось» совпали; (2) числа, взятые из соседнего файла — за прошлые
сутки это стоило проекту четырёх неверных чисел; (3) утверждения о чужой зоне, сделанные по памяти.
5. **Проверяемый артефакт** — таблица «находка → что сделано → ЧЕМ ПРЕДЪЯВЛЕНО» плюс, для каждого класса,
столбец **«закрыт в N носителях из M»**, где M посчитан командой.
6. **BASELINE НА ВХОДЕ сними САМ** первой командой и сверь с моим: гейт чисел `python3 docs/scripts/counts.py
--check` обязан говорить «Литералы сходятся»; батарея — `make check` по DoD §3 п.1. ⛔ **Число скипов и
их условия бери ТОЛЬКО из `platform/docs/STACK_DECISIONS.md` §«Гейты батареи»** — это единственный
носитель, и он заведён потому, что число жило в трёх файлах, они разошлись, и сессия, ЧЕСТНО исполнившая
этот пункт по устаревшей копии, объявила приёмку выполненной при красном тесте. Невыполненное на хосте
условие НАЗЫВАЙ вместе с числом скипов. Красное на входе — находка и пинг, а не твоя работа.
**И сам этот носитель уже протух — почини его ПЕРВЫМ движением, он твой:** там стоит «19 пакетов», а
`go list ./...` даёт **20**, и эррата `06.09-е` двадцать уже установила. Сессия, честно исполнившая этот
пункт, получит расхождение первой же командой и потратит круг на выяснение, чья это ошибка.
## 6. Оси ревью — характер «Код», плюс шов и деньги (ось вправе заменить с аргументом)
Тест утверждает не то, что думает (`D39.171`): часы как способ синхронизации там, где предмет не время, и
ассерт по подстроке в общем буфере. Обе формы дают и ложное красное, и **тихо-зелёное** — оно дороже.
Вторая ось: **шов** — инвентарь ВСЕХ каналов другой стороны чтением ЧУЖОГО кода, не по памяти. Третья:
**деньги** — любое число из сырого носителя, а не из агрегата.
**Четвёртая, и она про §4.1 прямо: сверка с индустриальным первоисточником ПРЕЖДЕ велосипеда.** Форму
ограничителя ты выбираешь сама — значит `x/sync/semaphore`, `errgroup.SetLimit`, `netutil.LimitListener` и
уже стоящий рядом `MaxWorkers` очереди обязаны быть РАССМОТРЕНЫ и отвергнуты с доводом, а не пропущены.
Самописный счётчик там, где есть stdlib или устоявшаяся библиотека, — находка приёмки, а не решение.
## 7. Записка-план ДО работы
До первой правки — короткая записка в журнал: что берёшь, в каком порядке, что считаешь рискованным. После
работы — сверка комплектности против §4 МЕХАНИЧЕСКИ (таблицей), не перечитыванием.
## 8. Заявление = команда
Каждое число и категорика — с командой, которой получены. Отрицательный замер печатается ВМЕСТЕ с
контрольной величиной («0 хитов при 197 go-файлах»). Сверх этого:
- **перед отчётом сверь каждый клейм с результатом инструмента ЭТОЙ сессии**, а не с памятью и не с этим
промтом: адреса §4 сняты 08.09 и могли уехать;
- **последний абзац отчёта оказался планом или обещанием? сделай это СЕЙЧАС**;
- ⛔ править тест/гейт, чтобы он прошёл, — НЕДОПУСТИМО; исключение — правка от заказанной смены поведения,
и она объявляется отдельным пунктом;
- сессия длинная — поставь себе интервальную самоверификацию субагентом против ЯВНЫХ критериев §13.
## 9. Эхо-протокол старта
ДО работы — ≤10 строк **СВОИМИ словами**: скоуп · инварианты · что НЕ делаешь. Дословный пересказ промта
подтверждает канал, но не понимание, и ошибку промта повторяет вместе с ним. Эхо — оркестратору по адресу
из прямого канала (§12); канала нет — эхо первой секцией журнала, работа продолжается.
## 10. Что НЕ удалось — обязательная секция отчёта
Что не вышло, что не проверено, где сомневаешься. «Не проверено» и «опровергнуто» — РАЗНЫЕ исходы.
## 11. Канал вопросов и право сказать «этого делать не надо»
Конфликт промта с кодом или доками — **пинг, а не интерпретация**: за прошлые сутки в промтах и трекере
нашли шесть ложных утверждений, три из них авторства оркестратора. Считаешь пункт ненужным — скажи с
доводом. Решение владельца требуется, если работа упрётся в деньги, скоуп или продукт.
## 12. Прямой канал
Механизм — `CLAUDE.md` §«Связь между сессиями». Впиши СВОЙ блок в `/tmp/textmachine-channel` первым делом,
чужие блоки не трогай. ⚠ Адрес из файла не доказывает, что сессия жива: сверься с `ListAgents`. Нужной роли
нет или имя не отвечает ⇒ **канала нет, и это НОРМАЛЬНЫЙ случай: НЕ опрашивай сессии подряд** — вопрос
секцией в журнал, работа продолжается.
## 13. Критерий завершённости — по нему тебя примут
Работу доводишь до конца САМ; приёмка идёт последней и вторым кругом твоей самопроверки не служит.
Готово — это когда:
- у КАЖДОГО пункта §4 есть исход: сделано · не делаю с доводом · пинг;
- круги СОШЛИСЬ: последний проход не дал НОВЫХ находок, прежние закрыты таблицей
«находка → что сделано → ЧЕМ ПРЕДЪЯВЛЕНО»;
- **каждый класс закрыт со ЗНАМЕНАТЕЛЕМ «N из M»**, и M посчитан командой;
- ограничитель §4.1 предъявлен НАГРУЗКОЙ, бюджет §4.2 — мутацией;
- числа сняты ПОСЛЕ последней правки, а не вспомнены;
- всё живое — в ДЕРЕВЕ, а не в письме;
- сказано явно: **«работа завершена, править не планирую»**. Без этой фразы пак считается идущим.
## Деньги
**Пак $0.** Платных вызовов провайдерам ноль: разбор книги — `$0`-глагол движка, живой гейт гоняет его же.
Если тебе кажется, что задача требует платного прогона, — это стоп и пинг, а не вызов.
## Отчёт
Зонный журнал `platform/docs/platform-PROGRESS.md`: исход по каждому пункту §4 · таблица «находка → что
сделано → чем предъявлено» · столбец «закрыт в N из M» · пере-снятая шапка журнала · пинги (форма гейта
рядов — оркестратору; `PD-441` — движку; всё, что упёрлось в контракт) · §10 «что не удалось» · вопросы
оркестратору.

View file

@ -1,6 +1,6 @@
# Журнал прогресса
> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-07, голова D39.226КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №23** (единственный носитель — здесь; роль передана 06.09, №22 закрыт нотой передачи D39.217; ⚠ испр. 06.09: коммит передачи `c992ee5` бампнул голову и НЕ тронул номер — гейта на номер очереди нет вовсе, `counts.py` сверяет только голову, поэтому носитель разошёлся молча). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ИСПОЛНЕН, испр. 08.09 (висел как «первым» неделю после исполнения):** (0а) ревизия документации на протухшее ОТРАБОТАНА 01.09 воркфлоу `docs-staleness-revision-A` (15 срезов), провенанс находок — `D39.185`; (0б) планы доработок в бэкенд и платформу — исполняются ПАКАМИ, за 0708.09 закрыты два движковых (`D39.225`, `D39.226`); (0в) вынос неактуального в архив идёт батчами `DOC_CLEANUP_PLAN.md` (Б14/Б15/Б17 живы). ⇒ строка ниже — не заказ, а история: (0а) ревизия документации на ПРОТУХШЕЕ — по всем зонам; (0б) планы доработок в БЭКЕНД и ПЛАТФОРМУ; (0в) вынос неактуального в АРХИВ (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **94** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 112 (major 3), всего рядов 465 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном).
> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-08, голова D39.227КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №23** (единственный носитель — здесь; роль передана 06.09, №22 закрыт нотой передачи D39.217; ⚠ испр. 06.09: коммит передачи `c992ee5` бампнул голову и НЕ тронул номер — гейта на номер очереди нет вовсе, `counts.py` сверяет только голову, поэтому носитель разошёлся молча). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ИСПОЛНЕН, испр. 08.09 (висел как «первым» неделю после исполнения):** (0а) ревизия документации на протухшее ОТРАБОТАНА 01.09 воркфлоу `docs-staleness-revision-A` (15 срезов), провенанс находок — `D39.185`; (0б) планы доработок в бэкенд и платформу — исполняются ПАКАМИ, за 0708.09 закрыты два движковых (`D39.225`, `D39.226`); (0в) вынос неактуального в архив идёт батчами `DOC_CLEANUP_PLAN.md` (Б14/Б15/Б17 живы). ⇒ строка ниже — не заказ, а история: (0а) ревизия документации на ПРОТУХШЕЕ — по всем зонам; (0б) планы доработок в БЭКЕНД и ПЛАТФОРМУ; (0в) вынос неактуального в АРХИВ (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **94** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 112 (major 3), всего рядов 465 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном).
> ⛔ **ПЯТЬ ПОТОКОВ РАБОТЫ — состояние на 07.09 (пере-снято лендингами смены №23).**
> **(1) ПОЛИГОН — «ремонт прибора»** (`docs/POLYGON_INSTRUMENT_REPAIR_SESSION_PROMPT.md`, строки 319 · 300 · 265 · 143):
> ⚠ **ЕДИНСТВЕННЫЙ ПОТОК, НЕ СДВИНУВШИЙСЯ ЗА СМЕНУ — сессия по промту так и не стартовала.** Блокирующая линза
@ -13,6 +13,7 @@
> `3d90943`). Строки 285 · 305 · 325 закрыты. ⚠ **Синхронный разрез приёма принят ПОСТРОЕННЫМ, НО НЕ ГОТОВЫМ**
> сработало правило остановки (9 major из 11 в одном механизме), десять пунктов несёт строка **332**; ложный
> инвариант бюджета заландён осознанно (окно лжи 8 секунд при дефолте в 16 минутах, ломается идемпотентность).
> **(6) ВЫДАНЫ ДВА ПАКА 08.09, акт `D39.227`, оба $0.** Движковый «ДВИЖОК УЖЕ ЗНАЕТ» (`BACKEND_ENGINE_KNOWS_SESSION_PROMPT.md`: строки 253 · 357 · 358 · 355, попутно 312) и платформенный «РАЗРЕЗ ПРИЁМА И ПРАВДА О СЕБЕ» (`PLATFORM_INTAKE_TRUTH_SESSION_PROMPT.md`: строка 332, семь пунктов из десяти). Оба прошли опровергателя и разбор Fable 5. ⛔ **Заведена строка 360 — самый дорогой движковый класс (2334 % цены редакторской руки), носителя у него не было;** её ДЕНЕЖНАЯ половина ждёт слова владельца (платит ли пользователь за вызов, оборванный нами) и сцеплена со строкой 331. До ответа пак по 360 не пишется.
> **(4) ДИЗАЙН-ПАК 161 — ✅ НАПИСАН 07.09** (`docs/CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md`, `7f84c8d`), прошёл оба
> рубежа. ⚠ **НЕ ВЫДАН: новый пак идёт НОВОЙ сессии, обе ночные отработали** — запуск за владельцем.
> **(5) ТЕХДОЛГ БЭКЕНДА — разбор `research/33` в трекер: ✅ ЗАЛЕНДЕН 07.09, акт `D39.224`.** Заведены строки

View file

@ -23,12 +23,14 @@
| Роль | Активный промт | Статус |
|---|---|---|
| Оркестратор | [ORCHESTRATOR_SESSION_PROMPT.md](ORCHESTRATOR_SESSION_PROMPT.md) | роль и нормы; счётчик роли — CURRENT-STATE |
| **Бэкенд · ДВИЖОК УЖЕ ЗНАЕТ** | [BACKEND_ENGINE_KNOWS_SESSION_PROMPT.md](BACKEND_ENGINE_KNOWS_SESSION_PROMPT.md) | ⚠ **ВЫДАН 08.09, $0 и снапшот-нейтрально.** Движок вычисляет правду и пишет её в лог, а подписывающий и оператор смотрят в другое место: полнота банка не доезжает до листа подписи (строки **253**, **357**) · итог трат не включает классификатор и разложение относит его в «купил ничего» (**358**, **355**) · попутно **312** Д-3. ⚠ Пак прошёл оба рубежа и разбор Fable 5; три блокера и шесть существенных находок закрыты ДО выдачи |
| Бэкенд · КОНСИСТЕНТНОСТЬ | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 07.09** — акт **D39.222**; промт в `archive/prompts/` с баннером исхода. Строки 267 и 318 закрыты, у 320 исполнена половина (а). Живой остаток зоны ЗАКРЫТ актом **D39.226** (строки 333338); живое — 353359 |
| Бэкенд · ПРАВДА ОПЕРАТОРУ И ДЕНЬГИ | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 08.09** — акт **D39.226**, живой платный прогон; промт в `archive/prompts/` с баннером-исходом |
| Бэкенд · ГЕЙТ ВМЕСТО ПРОЗЫ | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 07.09** — акт **D39.225**; промт в `archive/prompts/` с баннером-исходом |
| **Бэкенд · СТРУКТУРА ГЛАВ (ДИЗАЙН)** | [CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md](CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md) | ⚠ **ПРОЕКТИРОВАНИЕ, НЕ СТРОЙКА** (`D39.136` п.3): дизайн окна большой пере-нарезки — чем адресуется глава и развязка чанкера (161) · карта осей сдвига · банк-окна на chapter-ID · контент-адресуемый resume `D15.2` · IR/адаптеры/индуктор · правило заголовка и не-CJK путь (303) · гранулярность EPUB (302) и выдача (283). Снимает временный отказ интейка книге из одной главы (`D39.221`). Пак 1 «форматы и честный словарь» ОТРАБОТАН и ПРИНЯТ 06.09 — акт **D39.210**, промт в `archive/prompts/`. ⚠ **ПАК 2 (лексиконы не-CJK, корпус, замер оракулом) — НЕ НАПИСАН и ждёт файлов владельца** |
| Бэкенд · отозванный | активного НЕТ | пак КАЧЕСТВО написан и ОТОЗВАН до выдачи 05.09 (`archive/prompts/…_WITHDRAWN.md`): предмет дешевле пака — точность чекера уже измерена, пере-снять её стоит одной команды. Следующий пак — СТРУКТУРА ГЛАВ (решение владельца 05.09 «любая книга любого формата»), пишется |
| Бэкенд · прежний | активного НЕТ | пак «денежный стоп» ОТРАБОТАН и ПРИНЯТ С ДОФИКСОМ 05.09 — акт **D39.206** (`81a89e9` + `616a8e4`), промт в `archive/prompts/` с баннером исхода. Следующая работа зоны — строки **291** (флаг вместо клина, `D39.204`) · **294** (форма «сколько добавить», `D39.203`) · **296** (точечная перегенерация по промаху глоссария, после 295) |
| **Платформа · РАЗРЕЗ ПРИЁМА И ПРАВДА О СЕБЕ** | [PLATFORM_INTAKE_TRUTH_SESSION_PROMPT.md](PLATFORM_INTAKE_TRUTH_SESSION_PROMPT.md) | ⚠ **ВЫДАН 08.09, $0.** Забирает синхронный разрез приёма до готовности (строка **332**, семь пунктов из десяти — три названы исключениями) плюс правду зоны о себе: ограничитель параллелизма у СИНХРОННОГО входа (у очередного он есть) · граница бюджета хвоста выводится из кода · рантбук про молчание маршрута · протухшая шапка зонного журнала и три ряда регистра. ⚠ Прошёл оба рубежа и разбор Fable 5; пять блокеров закрыто ДО выдачи, в том числе развёрнутое наоборот пред-решение оркестратора по инструменту отказа |
| Платформа · ДЕНЬГИ И ПРАВДА | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 07.09** — акт **D39.221**, контрактный минор **0.13.0**; промт в `archive/prompts/` с баннером исхода. ⚠ **Синхронный разрез приёма остановлен правилом остановки и заландён построенным, но НЕ готовым** — десять пунктов несёт строка бэклога **332**, и она же заказ следующему паку зоны; ложный инвариант бюджета заландён осознанно (окно 8 с при дефолте в 16 минутах) |
| Платформа · ФОРМА ЗАКАЗА | активного НЕТ | пак ОТРАБОТАН, ПРИНЯТ С ДОФИКСОМ и ЗАЛЕНДЕН 06.09 — акт **D39.208**, код + канон **0.11.0**; промт уведён в `archive/prompts/ORDER_FORM_SESSION_PROMPT_2026-09-05.md` с баннером исхода. ⚠ **НЕ построено, хотя заказывалось:** денежный ползунок на проводе объявлен, а отправить его нечем, и оценки под ВЫБРАННЫЙ заказ нет; «упрощение резюма» не сделано — обе позиции живут строками бэклога, брать их как новый пак СЛЕДУЮЩЕЙ сессии, не читать промт как задание |
| Платформа · прежний | активного НЕТ | пак «пустить внутрь можно» ОТРАБОТАН и ПРИНЯТ С ДОФИКСОМ 05.09 — акт **D39.201** (`7e2226a` + `3e84716`), промт в `archive/prompts/` с баннером исхода. Следующая работа зоны — строки **294** (зеркало словаря под минор потока) ⚠ (строка 293 — зона ОРКЕСТРАТОРА, не платформы: правило игнора общее для всех рабочих деревьев клона) |

View file

@ -1,4 +1,4 @@
# Реестр D-нот — карта актуальности v2 (D1D39.226; титул — носитель головы, бампать при каждом аппенде)
# Реестр D-нот — карта актуальности v2 (D1D39.227; титул — носитель головы, бампать при каждом аппенде)
> ⚠ **Колонку «тело» `counts.py --check` НЕ сторожит по устройству:** он сверяет полноту НОМЕРОВ, а не
> место тела, поэтому колонка держится дисциплиной лендинга. Не нашёл тело по колонке — иди в слайсы,
@ -285,3 +285,4 @@
| D39.224 | 07.09 | **РАЗБОР research/33 В ТРЕКЕР**: заведены строки **344350** (шов заголовка · словари классификатора · правило заголовка и `第零章` · каналы langpack · арм-шапки · остатки · рефакторинг по санкции владельца), дописки в 86/114/119/182/224; «переписать» не получил ни один из 11 долгов. Порядок: шаг 0 ПАРАЛЛЕЛЬНО дизайну 161 → шаги 2+3 одним окном перенарезки (wire-очередь 182 ложится в него же) → шаг 4 аддитивно; цена «дважды» условна (D39.190 п.4), ограничение — время до интейка. Запреты только под ратифицированной базой, остальное — условия с механизмом | жив | ЖИВОЕ: место строки 160 относительно стоп-мира перекроя — вопрос дизайну (п.6); критерий шага 1 (п.8) — в дизайн-пак; четыре ошибки разбора названы в п.9 | техдолг трекер порядок деньги |
| D39.225 | 07.09 | **АКТ: движковый пак «гейт вместо прозы» ПРИНЯТ С ДОФИКСОМ** (`7ffec51` + `4188ba5`). Пять швов, державшихся прозой, держат гейты; закрыты строки 344/345/348/349, половинами 224/313/346/350, остатки — 351/352. Числа пере-сняты приёмкой: батарея 19/0/4, мутационный гейт 14 записей и 0 unexpected со свипом якорей `0 of 225`. СМЕН ПОВЕДЕНИЯ ДВЕ: секция предложений в сайдкаре стопа (аддитивно) и ДЕНЕЖНАЯ — прогон по арму теперь покупает банковый контур. Класс A онтологии банка заведён сознательно: у секции ноль читателей, носитель — платформенная половина 253 | жив | ЖИВОЕ: платформенная половина 224/253 (читатель секции) · вопрос `pending_decisions` в контракте 14 · строки 351/352 | движок гейты банк деньги приёмка |
| D39.226 | 08.09 | **АКТ: движковый пак «правда оператору и деньги» ПРИНЯТ С ДОФИКСОМ** (`e48bca2`). Закрыты строки 333338; остатки — 353(+)/354/355/356/357/358. Числа приёмки: батарея 19/0/4, мутации 48 записей и 0 unexpected, свип `0 of 259`, каталог 232→259 и 21→48 в гейте, тесты +18. ЖИВОЙ ПЛАТНЫЙ ПРОГОН (санкция владельца, потолок машинным гейтом): `committed=$1.259217` против сметы `$0.350075`**`tmctl manifest` есть нижняя граница, не прогноз**; 64.6 % «купило ничего»; оплаченный `2xx` с оборванным телом — 32 % бюджета. Строка 335 опровергнута своим же замером: не «прямые деньги», а 4.1 % кандидатов, и фаза платится раз на книгу. Два блокера закрыты корнем, третий круг нашёл мёртвый страж | жив | ЖИВОЕ: строки 354358 и остаток 353; три ошибки ЗАКАЗЧИКА названы в п.8 | движок деньги банк оператор приёмка |
| D39.227 | 08.09 | **ТРИАЖ И ВЫДАЧА ДВУХ ПАКОВ** («движок уже знает» + «разрез приёма и правда о себе», оба $0). Заведена строка **360** — таймаут чтения маскируется под нечитаемое тело, ретраится и биллится по оценке (2334 % цены редакторской руки), носителя у класса не было; строка **354** называла механизм свойством провайдера и исправлена. Пред-решение оркестратора «новый корневой `code`» ОТОЗВАНО: корневой закрыт, открыт `cause.code`. Фантом-гард спущен до номера ПУНКТА. Вопрос владельцу: платит ли пользователь за вызов, оборванный нами | триаж · паки · деньги · контракт |

View file

@ -1,4 +1,4 @@
# Журнал решений оркестратора — контракт D1D39.226 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`)
# Журнал решений оркестратора — контракт D1D39.227 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`)
> **КАРТА АКТУАЛЬНОСТИ (ревизия D31, продлена до D38.2 [12.07]; исторические записи ниже НЕ переписываются — дисциплина D23.3).** Работая с контрактом (греп номера: живой файл → слайсы, целиком НЕ читать — D39.125), держи под рукой, что чем перекрыто:
> ⚠ **Эррата 09.08 (D39.125):** D39.111 п.1 предписывал промту S3 «максимум = баланс МИНУС открытые холды» — формула ОШИБОЧНА (вычитание дважды), исправлена D39.115 п.2(а): максимум = Balance КАК ЕСТЬ; тело D39.111 — в слайсе `../archive/architecture/05-decisions-D39-106-123.md` (испр. 05.09: прежнее «живёт ниже в этом файле» протухло подрезкой D39.139) (голова D39.106+).
@ -3139,3 +3139,21 @@ bought NOTHING». ⇒ **`tmctl manifest` есть НИЖНЯЯ граница,
здесь: отчёт принадлежит ЗОНЕ, коммит `bdf9123` его унёс по ошибке оркестратора**; историю не переписывали
(две параллельные сессии). Признак поимки — расхождение объёма коммита с объёмом своей правки — унесён в
промт нормой.
## D39.227 — ТРИАЖ И ВЫДАЧА ДВУХ ПАКОВ; заведён носитель самого дорогого движкового класса; пред-решение оркестратора по инструменту отказа ОТОЗВАНО (08.09, оркестратор №23) ✅
**1. Выданы два пака, оба $0.** Движковый «ДВИЖОК УЖЕ ЗНАЕТ — ДОВЕЗИ ДО ЧИТАТЕЛЯ» (`docs/BACKEND_ENGINE_KNOWS_SESSION_PROMPT.md`): строки **253** · **357** · **358** · **355**, попутно **312** Д-3. Платформенный «РАЗРЕЗ ПРИЁМА ДО ГОТОВНОСТИ И ПРАВДА О СЕБЕ» (`docs/PLATFORM_INTAKE_TRUTH_SESSION_PROMPT.md`): строка **332**, семь пунктов из десяти — три названы исключениями поимённо. Оба прошли механическую сверку 13 блоков, опровергателя и разбор Fable 5; таблица активных промтов обновлена тем же коммитом (`D39.80`).
**2. ⛔ ЗАВЕДЕНА СТРОКА 360 — КЛАСС, КОТОРЫЙ ДОРОЖЕ ОБОИХ ПАКОВ И НЕ ИМЕЛ НОСИТЕЛЯ ВОВСЕ.** Таймаут чтения ответа маскируется под нечитаемое тело: `backend/internal/llm/httpllm.go` выбрасывает ошибку `io.ReadAll` в `_`, а обрыв распознаёт по РАЗМЕРУ, поэтому тело, оборванное дедлайном `attempt_s`, от целого неотличимо; обрезок даёт `BilledDecodeError` с флагом retryable **`true`** ⇒ вызов ретраится, и каждая попытка сеттлится ПО ОЦЕНКЕ. Замер полигона (`docs/experiments/23-editor-tier.md` п.5): латентности ровно `2 × attempt_s`, цена **$0.475893 = 23.8 % руки строками, 34.5 % юнитами**; полигон квалифицировал это дефектом ДВИЖКА и пингнул оркестратору. **Носителя не было: греп `attempt_s` по живому бэклогу — 0 хитов при 255 рядах** (контроль `decode` — 3). Нашёл Fable 5 по прямому вопросу «не взяла ли я аккуратное вместо важного»; пере-проверено оркестратором по коду и по телу замера.
**3. Строка 354 называла механизм НЕВЕРНО и исправлена.** Она отправляла полигон записать класс в страницу квирков как свойство ПРОВАЙДЕРА — то есть закрепить в каноне неверную причину. Теперь она про пробел страницы квирков и указывает на 360; механизм живёт в 360.
**4. Класс 360 сцеплен со строкой 331, и это делает его вопросом ВЛАДЕЛЬЦУ, а не заказом.** Один предмет — «вызов оборвали МЫ, ответа нет» — движок сегодня закрывает противоположно: по таймауту оценкой, по отмене потолком нулём. Инженерная половина развилок не имеет (не выбрасывать ошибку чтения · отличать таймаут от нечитаемого тела · не ретраить вызов, оборванный своим дедлайном). Денежная требует слова: **платит ли пользователь за вызов, который оборвали мы, и если да — по оценке или по нулю.** Вопрос вынесен владельцу; до ответа пак не пишется, класс объявлен исключением в движковом паке поимённо.
**5. ⛔ МОЁ ПРЕД-РЕШЕНИЕ ПО ИНСТРУМЕНТУ ОТКАЗА БЫЛО РАЗВЁРНУТО НАОБОРОТ — ОТОЗВАНО.** Я задала платформенному паку «перечислимая причина ⇒ новый `code`» и запретила искать третий путь. Канон говорит обратное: корневой `code` «stable, **closed** for this version» (его рост — ломающее изменение и минор), а `cause.code` «its vocabulary is **NOT closed** and grows without a minor bump», и компаньон прямо называет это механизмом расширяемости; словарь `errors[]` открыт, и обе причины отказа приёма УЖЕ едут перечислимыми (`no_book`, `no_chapter_structure`). ⇒ я заказала ломающий инструмент и запретила штатный. **Следствие для роли, внесено в промт:** «инструмент задан, не выбирай сам» — самая опасная строка пака, потому что снимает суждение сессии, и ошибка в ней не ловится ничем ниже; писать её можно только после того, как прочитано, что канон называет ШТАТНЫМ механизмом, и проверено, не построено ли заказываемое.
**6. Фантом-гард не спускался до НОМЕРА ПУНКТА — дыра закрыта.** Ссылка `D39.226 п.10` прошла мою проверку, потому что та искала только тело `^## D<номер>`; у ноты восемь пунктов, а правило остановки живёт в `D39.225` п.10. Норма и команда — в ролевом промте.
**7. Что я назвала неверно и исправила в ратифицированных носителях.** Строка **358** несла числа `$0.007280`/`$0.005460` как «замер живого прогона 08.09» — они АВГУСТОВСКИЕ, из соседнего архивного файла. Правда по строкам леджера: терминологическая фаза `$0.013608` = классификатор `$0.009013` + рендер-батчи `$0.002775` и `$0.001820`, в итог волны входит только `$0.004595`. Строка **332** подавала незаполненный `Detail` дефектом — канон запрещает показывать `title`/`detail` пользователю дословно, и `Detail` не заполняется ПРАВИЛЬНО. Обе исправлены с пометкой.
**8. Границы вердикта — честно.** Батарей не гонял никто из троих проверяющих: опровергатель движкового пака прогнал `go test ./... -count=1` (19 `ok` · 0 `FAIL` · 4 без тестов), платформенную не гонял никто (нет DSN и движкового бинаря на хосте) — вход платформенной зоны сессия снимает сама. Что два вызова прогона 08.09 были именно таймаутами — ВЫВЕДЕНО по совпадению подписи с замером полигона, не измерено: сырого леджера того прогона в дереве нет (`.db` новее 07.09 — 0 при 163). Механизм 360 доказан кодом и телом замера exp-23, исполнением не воспроизводился.