diff --git a/docs/BACKEND_ENGINE_KNOWS_SESSION_PROMPT.md b/docs/BACKEND_ENGINE_KNOWS_SESSION_PROMPT.md new file mode 100644 index 00000000..fa08427d --- /dev/null +++ b/docs/BACKEND_ENGINE_KNOWS_SESSION_PROMPT.md @@ -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.8–34.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 «что не +удалось» · вопросы оркестратору. diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index d67e5d91..0ae9a7ab 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -3,7 +3,7 @@ > **Единственный трекер проекта.** Здесь живут строки, на которые доки, промты и D-ноты ссылаются словами «строка N» / «строка бэклога N»: **ID строки стабилен навсегда**, не перенумеровывается и не переиспользуется (D39.80). Правки — только через оркестратора; каждая петля обязана иметь диспозицию: решено / отложено-с-записью / отклонено. > ⚠ **Это НЕ бэклог зоны `docs/`, а бэклог ПРОЕКТА.** У зон свои, с другими неймспейсами ID, и единый их строк не принимает (D39.84): платформа — [../platform/BACKLOG.md](../platform/BACKLOG.md) (`П-N`) и её регистр дефектов `platform/docs/DEFECT_REGISTER.md` (`PD-N`); фронт — [../frontend/docs/BACKLOG.md](../frontend/docs/BACKLOG.md) (`Ф-N`). > ⚠ **Состояние проекта — не здесь.** Очередь, курс, CURRENT-STATE, состояние паков и живая хроника — шапка [PROGRESS.md](PROGRESS.md). Здесь только долг и его диспозиции. -> - **СЧЁТ ОЧЕРЕДИ на 07.09 (вечер) (скриптом по таблице — `python3 docs/scripts/counts.py`; обновлять при каждом лендинге):** всего **255** строк · зона бэкенд **123** строго / **170** широко (⚠ колонки счётчик читает С КОНЦА — испр. D39.116) · **блокеров очереди 0** (236 закрыта D39.175), платные прогоны разблокированы · «скоро» **113** (перечень — грепом по таблице, рукописный список снят D39.126) · гейт-строка эксп-22: 153 (плюс 150 — руки владельца; ⚠ 55 из гейтов СНЯТА — D39.198 п.5); строка 5 — остаток гейчен ре-пробой 74, носитель события теперь строка 188 (аудит D39.140); остальное «когда-нибудь». ⚠ Счёт — НИЖНЯЯ граница долга, не потолок (разбор — легенда таблицы ниже). +> - **СЧЁТ ОЧЕРЕДИ на 08.09 (скриптом по таблице — `python3 docs/scripts/counts.py`; обновлять при каждом лендинге):** всего **256** строк · зона бэкенд **124** строго / **171** широко (⚠ колонки счётчик читает С КОНЦА — испр. D39.116) · **блокеров очереди 1** (236 закрыта D39.175), платные прогоны разблокированы · «скоро» **113** (перечень — грепом по таблице, рукописный список снят D39.126) · гейт-строка эксп-22: 153 (плюс 150 — руки владельца; ⚠ 55 из гейтов СНЯТА — D39.198 п.5); строка 5 — остаток гейчен ре-пробой 74, носитель события теперь строка 188 (аудит D39.140); остальное «когда-нибудь». ⚠ Счёт — НИЖНЯЯ граница долга, не потолок (разбор — легенда таблицы ниже). > - ⚠ **Переезд 06.09 (D39.218):** таблица вынесена из `PROGRESS.md` ДОСЛОВНО и ни один ID не сдвинут — **с единственным исключением: две ячейки (строки 220 и 306) тронуты РОВНО в номере якоря `docs/scripts/counts.py:566`→`:569` и `:592`→`:595`**, потому что цель сдвинул этот же коммит; текст ячеек не менялся, длины равны. В `PROGRESS.md` на прежнем месте оставлен заголовок-указатель, поэтому ссылки «секция „Бэклог“» доезжают в один хоп. ## Бэклог (ЕДИНЫЙ, собран 26.07, актуализация 04.08 D39.99/D39.101; правки — только через оркестратора) @@ -239,7 +239,7 @@ | 302 | **РЕЗКА xhtml ПО ЯКОРЯМ `nav`: текст цел, ГРАНУЛЯРНОСТЬ ТЕРЯЕТСЯ.** Массовая форма EPUB — несколько глав в ОДНОМ документе, разделённых якорями, и `nav` объявляет на них отдельные цели. Структурный пак 1 такие цели СХЛОПЫВАЕТ: одна глава вместо трёх, текст не теряется (формулировка бэкенд-сессии 05.09: «текст цел, гранулярность потеряна»). ⚠ **Ложь при этом уже не едет** — тем же паком ратифицировано: схлопнулась хоть одна цель ⇒ книга НЕ `declared`, а `delimited`, плюс счёт `targets collapsed: N` в WARN. То есть провенанс честен, а разрез груб. ⇒ заказ: резать документ по якорям целей. ⛔ **Почему НЕ сделано паком 1 и почему это дорого:** это единственное место, трогающее `extractXHTML`, у которого **четыре пина байт-паритета** (`ingest_test.go:323, 359, 423, 467` — void-теги, несбалансированный ruby, raw-text-элементы, голый амперсанд), и ошибка там ТИХАЯ. Заходить в него в конце пака, ради гранулярности, при живом гейте разреза — плохой размен. ⚠ Строкой закрывается ГРАНУЛЯРНОСТЬ, а не провенанс: продать такую книгу по главам платформа и так не предложит | бэкенд | когда-нибудь (после пака 2) | книга с тремя якорями в одном документе даёт три главы; четыре пина байт-паритета целы | **D39.207** | | 303 | **НЕ-CJK ПУТЬ ПОСТРОЕН НАПОЛОВИНУ: чанкер безъюнитную грамматику УЖЕ УМЕЕТ, ингест по ней НЕ РЕЖЕТ.** `detectChapterUnit` итерирует `UnitOrdered` и без юнита возвращает 0 ⇒ грамматика вида «Kapitel 12» / «Chapter 12» (маркер + номер, БЕЗ юнита) книгу не режет; при этом `matchHeaderLine` чанкера юнита не требует и на это есть живой тест (`TestStripHeadingGenericLatinMarker`). То есть заголовок такой книги движок умеет СНЯТЬ, но не умеет по нему РАЗРЕЗАТЬ. ⇒ ревью-вопрос проекта «заработает ли пара, которой в репо НЕТ, без правки Go» закрыт для языков С ЮНИТОМ (доказано сквозным тестом `TestANewLanguageCutsFromItsDataFileAlone`: без файла корейская книга — одна глава, с `ko/structure.txt` — три, разница только в данных) и **ОТКРЫТ для языков без юнита**. Это названный ВХОД в пак 2, а не открытие по ходу. ⚠⚠ **ГРАНИЦА ЗАМЕРА, названная самой зоной и записанная здесь в её настоящей силе:** утверждение проверено **ПО КОДУ и ПО ТЕСТУ НА ФИКСТУРЕ**; **живая не-CJK книга через ингест НЕ прогонялась**. «На живой книге» под этой строкой НЕ стоит, и ссылаться на неё как на замер живого поведения нельзя | бэкенд | скоро (вход пака 2) | безъюнитная грамматика режет книгу; замер на живой не-CJK книге | **D39.207** | | 304 | ⚠ **РИСК NULLABLE ДЕНЕЖНЫХ КОЛОНОК — НЕ ТЕОРЕТИЧЕСКИЙ, А ОТЛОЖЕННЫЙ, и это вскрыла ЛОЖНАЯ ПРИЧИНА в отчёте зоны.** Платформенная зона объяснила чистый `sqlc diff` тем, что новые колонки лежат «в таблицах, для которых sqlc не держит запросов» — **догадка, и неверная**: `queries/credits.sql` и `queries/exports.sql` читают `books`, `chapters` и `runs`. Верная причина: ни один из этих запросов не выбирает `*` и не называет новых колонок, поэтому сгенерированный код не двигается (греп по `*.sql.go` — ноль вхождений). ⇒ **`books` ЛЕЖИТ в мире sqlc**, значит риск NULLABLE денежных колонок наступит на ПЕРВОМ же запросе, который их выберет, — не «никогда», а «пока не спросили». ⚠ Нашла это зона САМА, четвёртым заходом по прямому вопросу владельца «нет ли чего, что ты умолчала»; ни один гейт поймать это не мог — сторожат поведение и форму, а не то, что отчёт о себе ГОВОРИТ (`D39.202` п.9) | платформа | скоро | первый запрос, выбирающий денежные колонки, не роняет генерацию | **D39.208** | -| 306 | ⛔ **СЕМЬСОТ ЯКОРЕЙ БЕЗ ТОКЕНА НЕПРОВЕРЯЕМЫ, И 59% ИЗ НИХ УЖЕ НЕ УКАЗЫВАЮТ ТУДА, КУДА УКАЗЫВАЛИ.** Замер Fable 5 06.09 (выборка 80, seed фиксирован, метод — blame-триаж: коммит последней правки СТРОКИ ДОКА → цель на том коммите → сравнение): цела **31** · уехала **25** · переписана либо исчезла **22** · не судится 2. ⇒ **59% (±11 п.п.)**. ⚠ Линт сверяет по токену 202 и честно печатает «без токена — несверяемы: 780», но ЗАГОЛОВОК «0 проблемных якорей» читается как «всё цело» — прибор отвечает на свой вопрос, а читают его как ответ на другой (`D39.202`). ⚠ **Дрейф сосредоточен там, где он ОЖИДАЕМ:** 34 из 47 сдвинувшихся — в ЗАМОРОЖЕННЫХ отчётах, чьи тела улика по `D23.3` и обязаны описывать дерево НА ДАТУ; их чинить НЕЛЬЗЯ, им нужен штамп HEAD в шапке. **Опасны 13 в ЖИВЫХ носителях**, и часть из них — в ШАПКЕ журнала, то есть в первом, что читает сессия. ⇒ заказ, тремя шагами и без велосипедов: **(1)** заголовок линта печатает ПОКРЫТИЕ («сверено N · несверяемы M · из них с изменившейся целью — K»), чтобы гейт перестал врать формой; **(2)** ОДНОРАЗОВАЯ механическая миграция тем же скриптом: «цела» → дописать токен автоматически, «уехала» с единственным попаданием → пере-навести, «исчезла» → список на руки; замороженным отчётам вместо пере-наводки штамп коммита и исключение из линта; **(3)** правило «токен при касании» остаётся удержанием линии; **(4)** ⛔ **грамматика УЛИКИ — `path:line@`**, которую линт сверяет через `git show :path`, а НЕ против HEAD (~20 строк в `counts.py`). Без неё автор, цитирующий мёртвый указатель как ПРЕДМЕТ записи, вынужден снимать форму якоря руками — я так и сделал 06.09 и объявил это в тексте, но это подгонка ЗАПИСИ под модель прибора, а не починка модели: **законно как последнее исключение, незаконно как привычка**. Та же форма закрывает и ЗАМОРОЖЕННЫЕ отчёты, которым сегодня штамп HEAD стоит прозой (`research/31`/`32`). ⚠ Сегодня без (2) правка любого дока превращается в игру «бей крота»: каждое касание вскрывает соседний голый якорь. ⚠⚠ **ПЕРЕ-ЗАМЕР 06.09 ГРАММАТИКОЙ САМОГО ЛИНТЕРА — МАСШТАБ ОКАЗАЛСЯ В РАЗЫ МЕНЬШЕ, И ЭТО МЕНЯЕТ ЗАКАЗ.** По 117 живым докам: **214 якорей с токеном** (это и есть те, про кого «0 проблемных») и **1776 без токена**; за конец файла целят 66, однозначно разрешаются 9, и семь из девяти — ВНЕШНИЕ цитаты (стандартная библиотека Go, чужой фронтовый код). ⇒ **настоящий внутрирепозиторный протухший якорь ОДИН** (испр. 06.09 автором замера): `docs/architecture/05-decisions-log.md:21`=`Эррата 28.08-и` — **в ШАПКЕ журнала, которую сессия читает первой**; цель нашлась `git log -S` по тексту якоря и пере-навёдена с токеном. ⚠ **Второй кандидат (`PROGRESS.md:71` → `README.md:95`) оказался АРТЕФАКТОМ РАЗРЕШЕНИЯ ИМЕНИ, а не дефектом** — и он же служит уликой к правилу (2) строки 299. ⭐ **Дешёвый метод для ведра «исчезла», исполненный, а не описанный:** `git log -S'<текст якоря>' -- <док> | tail -1` даёт коммит, ВВЕДШИЙ якорь; `git show <коммит>:<цель>` — что стояло в ту минуту ДОСЛОВНО; `git grep` — сегодняшний адрес. Угадывания нет ни на одном шаге. ⛔ **ОПИСАНИЕ ЧУЖОГО МЕТОДА В ЭТОЙ СТРОКЕ БЫЛО ЛОЖНЫМ — СНЯТО 06.09.** Абзац писал, что «59 % на выборке 80» получены наивным резолвингом (голое `mining.go:40` из подкаталога ищут от корня, не находят, объявляют мёртвым). **Наивный резолвинг — метод АГЕНТСКИХ чисел** («42 %», «19 из 32 мертвы»), и опровергнуты им именно они; тем же способом автор пере-замера получил «31 мёртвый из 36» и не понёс их, потому что стал сверять расхождение. Замер Fable 5 устроен иначе и так же описан в ГОЛОВЕ этой строки: берутся только ЗОННО-ПРЕФИКСНЫЕ пути, `git blame` даёт коммит последней правки СТРОКИ ДОКА, `git show <тот коммит>:<цель>` — цель на ту минуту, и сравнивается СОДЕРЖИМОЕ с HEAD. ⇒ строка противоречила сама себе: голова описывала blame-триаж, хвост объявлял его наивным резолвингом. **Класс строки 299, только внутри бэклога.** ⚠ **ДВА ЧИСЛА ОТВЕЧАЮТ НА РАЗНЫЕ ВОПРОСЫ, и потому ни одно не опровергает другое.** Пере-замер грамматикой линтера спрашивает «указывает ли якорь в СУЩЕСТВУЮЩУЮ строку HEAD и совпадает ли токен» — ответ: 214 с токеном, 1776 без, за конец файла 66, однозначно разрешаются 9, настоящий внутрирепозиторный протухший ОДИН. Замер blame спрашивает «изменилось ли СОДЕРЖИМОЕ цели с той минуты, когда строка дока была написана» — ответ: ~59 % на выборке 80. Первый вопрос про HEAD, второй про ДРЕЙФ; строка обязана нести оба, а прежняя редакция вычитала одно из другого. ⚠⚠ **И вопрос HEAD слеп ПО ПОСТРОЕНИЮ к тому самому якорю, ради которого строка заведена:** якорь шапки журнала был написан голым — `seeding.go:143-153`, без слэша, — а `docs/scripts/counts.py:595`=`"~" in rel or "/" not in rel` относит такую форму к «неразбираемым» и пропускает МОЛЧА, ДО всякой проверки файла. ⚠ **Испр. 06.09 по ревью Fable 5: файл НЕ переименовывали** — `seeding.go` жив и сегодня (128 строк), а `bankmaterialize.go` ДОБАВЛЕН `6ec9f8a` 29.08, и в него ПЕРЕЕХАЛ код. Прежняя редакция этой строки писала «переименовали», и следствие «ветвь «файла нет» не выстрелила» было лишним: голое имя до той ветви не доходит по построению. Прибор не видел ровно того случая, с которого начался заказ, — и «0 проблемных» был честен про свой вопрос и слеп про этот. ⚠ И одно слово в этой строке названо неточно: пере-замер сделан ГРАММАТИКОЙ линта (его правила разрешения путей и целей), но не самим линтом и не сверкой токенов — у голых якорей токенов нет — этот якорь линт не видит по построению, и два абзаца строки описывают РАЗНЫЕ приборы одним словом. ⚠ И у метода есть ложные срабатывания: две находки этого же прохода я проверил руками — одна («`STACK.md:11` исчезло») оказалась ЛОЖНОЙ, цель жива и точна | оркестратор | скоро | линт печатает покрытие; голых якорей в ЖИВЫХ доках нет | **D39.202** | +| 306 | ⛔ **СЕМЬСОТ ЯКОРЕЙ БЕЗ ТОКЕНА НЕПРОВЕРЯЕМЫ, И 59% ИЗ НИХ УЖЕ НЕ УКАЗЫВАЮТ ТУДА, КУДА УКАЗЫВАЛИ.** Замер Fable 5 06.09 (выборка 80, seed фиксирован, метод — blame-триаж: коммит последней правки СТРОКИ ДОКА → цель на том коммите → сравнение): цела **31** · уехала **25** · переписана либо исчезла **22** · не судится 2. ⇒ **59% (±11 п.п.)**. ⚠ Линт сверяет по токену 202 и честно печатает «без токена — несверяемы: 780», но ЗАГОЛОВОК «0 проблемных якорей» читается как «всё цело» — прибор отвечает на свой вопрос, а читают его как ответ на другой (`D39.202`). ⚠ **Дрейф сосредоточен там, где он ОЖИДАЕМ:** 34 из 47 сдвинувшихся — в ЗАМОРОЖЕННЫХ отчётах, чьи тела улика по `D23.3` и обязаны описывать дерево НА ДАТУ; их чинить НЕЛЬЗЯ, им нужен штамп HEAD в шапке. **Опасны 13 в ЖИВЫХ носителях**, и часть из них — в ШАПКЕ журнала, то есть в первом, что читает сессия. ⇒ заказ, тремя шагами и без велосипедов: **(1)** заголовок линта печатает ПОКРЫТИЕ («сверено N · несверяемы M · из них с изменившейся целью — K»), чтобы гейт перестал врать формой; **(2)** ОДНОРАЗОВАЯ механическая миграция тем же скриптом: «цела» → дописать токен автоматически, «уехала» с единственным попаданием → пере-навести, «исчезла» → список на руки; замороженным отчётам вместо пере-наводки штамп коммита и исключение из линта; **(3)** правило «токен при касании» остаётся удержанием линии; **(4)** ⛔ **грамматика УЛИКИ — `path:line@`**, которую линт сверяет через `git show :path`, а НЕ против HEAD (~20 строк в `counts.py`). Без неё автор, цитирующий мёртвый указатель как ПРЕДМЕТ записи, вынужден снимать форму якоря руками — я так и сделал 06.09 и объявил это в тексте, но это подгонка ЗАПИСИ под модель прибора, а не починка модели: **законно как последнее исключение, незаконно как привычка**. Та же форма закрывает и ЗАМОРОЖЕННЫЕ отчёты, которым сегодня штамп HEAD стоит прозой (`research/31`/`32`). ⚠ Сегодня без (2) правка любого дока превращается в игру «бей крота»: каждое касание вскрывает соседний голый якорь. ⚠⚠ **ПЕРЕ-ЗАМЕР 06.09 ГРАММАТИКОЙ САМОГО ЛИНТЕРА — МАСШТАБ ОКАЗАЛСЯ В РАЗЫ МЕНЬШЕ, И ЭТО МЕНЯЕТ ЗАКАЗ.** По 117 живым докам: **214 якорей с токеном** (это и есть те, про кого «0 проблемных») и **1776 без токена**; за конец файла целят 66, однозначно разрешаются 9, и семь из девяти — ВНЕШНИЕ цитаты (стандартная библиотека Go, чужой фронтовый код). ⇒ **настоящий внутрирепозиторный протухший якорь ОДИН** (испр. 06.09 автором замера): `docs/architecture/05-decisions-log.md:29`=`Эррата 28.08-и` — **в ШАПКЕ журнала, которую сессия читает первой**; цель нашлась `git log -S` по тексту якоря и пере-навёдена с токеном. ⚠ **Второй кандидат (`PROGRESS.md:71` → `README.md:95`) оказался АРТЕФАКТОМ РАЗРЕШЕНИЯ ИМЕНИ, а не дефектом** — и он же служит уликой к правилу (2) строки 299. ⭐ **Дешёвый метод для ведра «исчезла», исполненный, а не описанный:** `git log -S'<текст якоря>' -- <док> | tail -1` даёт коммит, ВВЕДШИЙ якорь; `git show <коммит>:<цель>` — что стояло в ту минуту ДОСЛОВНО; `git grep` — сегодняшний адрес. Угадывания нет ни на одном шаге. ⛔ **ОПИСАНИЕ ЧУЖОГО МЕТОДА В ЭТОЙ СТРОКЕ БЫЛО ЛОЖНЫМ — СНЯТО 06.09.** Абзац писал, что «59 % на выборке 80» получены наивным резолвингом (голое `mining.go:40` из подкаталога ищут от корня, не находят, объявляют мёртвым). **Наивный резолвинг — метод АГЕНТСКИХ чисел** («42 %», «19 из 32 мертвы»), и опровергнуты им именно они; тем же способом автор пере-замера получил «31 мёртвый из 36» и не понёс их, потому что стал сверять расхождение. Замер Fable 5 устроен иначе и так же описан в ГОЛОВЕ этой строки: берутся только ЗОННО-ПРЕФИКСНЫЕ пути, `git blame` даёт коммит последней правки СТРОКИ ДОКА, `git show <тот коммит>:<цель>` — цель на ту минуту, и сравнивается СОДЕРЖИМОЕ с HEAD. ⇒ строка противоречила сама себе: голова описывала blame-триаж, хвост объявлял его наивным резолвингом. **Класс строки 299, только внутри бэклога.** ⚠ **ДВА ЧИСЛА ОТВЕЧАЮТ НА РАЗНЫЕ ВОПРОСЫ, и потому ни одно не опровергает другое.** Пере-замер грамматикой линтера спрашивает «указывает ли якорь в СУЩЕСТВУЮЩУЮ строку HEAD и совпадает ли токен» — ответ: 214 с токеном, 1776 без, за конец файла 66, однозначно разрешаются 9, настоящий внутрирепозиторный протухший ОДИН. Замер blame спрашивает «изменилось ли СОДЕРЖИМОЕ цели с той минуты, когда строка дока была написана» — ответ: ~59 % на выборке 80. Первый вопрос про HEAD, второй про ДРЕЙФ; строка обязана нести оба, а прежняя редакция вычитала одно из другого. ⚠⚠ **И вопрос HEAD слеп ПО ПОСТРОЕНИЮ к тому самому якорю, ради которого строка заведена:** якорь шапки журнала был написан голым — `seeding.go:143-153`, без слэша, — а `docs/scripts/counts.py:595`=`"~" in rel or "/" not in rel` относит такую форму к «неразбираемым» и пропускает МОЛЧА, ДО всякой проверки файла. ⚠ **Испр. 06.09 по ревью Fable 5: файл НЕ переименовывали** — `seeding.go` жив и сегодня (128 строк), а `bankmaterialize.go` ДОБАВЛЕН `6ec9f8a` 29.08, и в него ПЕРЕЕХАЛ код. Прежняя редакция этой строки писала «переименовали», и следствие «ветвь «файла нет» не выстрелила» было лишним: голое имя до той ветви не доходит по построению. Прибор не видел ровно того случая, с которого начался заказ, — и «0 проблемных» был честен про свой вопрос и слеп про этот. ⚠ И одно слово в этой строке названо неточно: пере-замер сделан ГРАММАТИКОЙ линта (его правила разрешения путей и целей), но не самим линтом и не сверкой токенов — у голых якорей токенов нет — этот якорь линт не видит по построению, и два абзаца строки описывают РАЗНЫЕ приборы одним словом. ⚠ И у метода есть ложные срабатывания: две находки этого же прохода я проверил руками — одна («`STACK.md:11` исчезло») оказалась ЛОЖНОЙ, цель жива и точна | оркестратор | скоро | линт печатает покрытие; голых якорей в ЖИВЫХ доках нет | **D39.202** | | 307 | ⛔ **СПАН ПОД ПОДПИСЬЮ «СКОЛЬКО ЗАКАЗАНО» — ЛОЖЬ ЯРЛЫКОМ, И ЧЕСТНАЯ ФОРМА ДРУГАЯ.** Для заказа, выраженного ЗНАКАМИ, `Run.ordered_chapters` несёт не заказанное, а **СПАН** — сколько глав заказ ЗАТРАГИВАЕТ, считая главу затронутой независимо от того, куплена ли она целиком (`pricing.QuoteUnits` ставит `Chapters: max(chapters, 1)`, вызывающий передаёт `chaptersSpanning`, и сам код называет это «coarser of the two figures»). ⚠ **Замер зоны:** заказ на ОДИН юнит из четырёх в первой главе даёт `1` — **сто процентов завышения на самом дешёвом заказе**, то есть на том, которым сервис пробуют впервые. ⇒ число под именем «ordered chapters», превышающее заказанное, — ложь ярлыком, сколь угодно последовательная. ⭐ **Разбор, снявший спор:** нужда, которую зона защищала («видно, докуда дошли деньги»), настоящая, а носитель неверный — «докуда» есть **ПОЗИЦИЯ** («достигает главы N»), а не **КОЛИЧЕСТВО** («2 главы»), и позиция у платформы УЖЕ ЕСТЬ (`Quote.ThroughChapter`, `BookOrder.ThroughUnitID`). ⚠ Спан ≠ позиция для заказа-продолжения: спан считает затронутые от текущей точки, позиция — ординал. ⇒ **честная форма:** `ordered_chapters: null` для символьного заказа (зеркало `delivered_chapters: null` — «заказано не в главах») + аддитивно `ordered_characters` (эхо запроса) + позиция ординалом, если экрану нужно «докуда». Это смена формы (поле становится нуллабельным) ⇒ минор. ⚠ **Цена смены почти пуста и это ЗАМЕРЕНО:** `ordered_chapters` вошёл в канон только `75ae6f0`, до него в файле НОЛЬ вхождений — клиент 0.10.0 поля не видел никогда, а `ceiling_chapters` тот же минор уже отвергает с 400. ⚠ Поведение менять НЕ требуется: политика «заказ выражен в знаках и меряется в них насквозь» верна и остаётся. До лендинга формы канон описывает ДЕФЕКТ с ⛔-пометкой, а не дизайн | бэкенд+платформа | скоро | `ordered_chapters` нуллабелен, размер заказа эхом, позиция отдельно | **D39.208** | | 308 | **ВИД ПРОГОНА ЗАКОДИРОВАН НУЛ�ём, И ЭТО ЕДИНСТВЕННАЯ ЕГО МЕТКА ВО ВСЕЙ ПЛАТФОРМЕ.** Явного признака ре-прохода у `LiveRun` НЕТ: `Resnapshot` не годится — он ставится и ОБЫЧНОМУ продолжению после правки банка, а валидация `OrderedChapters == 0 && !Resnapshot` читается «ноль допустим только под пере-снапшот», то есть ре-проход ⇒ resnapshot, а не наоборот. ⇒ **вид прогона различается ЗНАЧЕНИЕМ денежного поля**, и держится это на двух вызовах `max(n, 1)` (`pricing.QuoteUnits`, `runs.chaptersSpanning`). ⚠ **Опасность растёт после лендинга формы заказа** (строка 307): снаружи символьный заказ станет `null`, и единственное место, где `max(n,1)` что-то значит, уходит ВНУТРЬ — следующий читатель колонки (отчёт, CLI) напечатает спан под старым словом и не узнает об этом. ⇒ заказ: **явный признак вида** (`RePass`) вместо кодирования вида нулём. ⛔ **Рантайм-пояса `== 0 && OrderedUnits == nil` ставить НЕЛЬЗЯ** — символьный заказ не может записать ноль ни при каких данных, а гейт от несуществующего состояния завтра прочтут как свидетельство, что состояние бывает. Честная защита до рефакторинга — **ПИН инварианта** «символьный заказ на один юнит пишет в колонку 1, никогда 0» (замер у зоны уже есть — сто процентов завышения на самом дешёвом заказе; норма `D39.209` требует превратить замер в пин) плюс одно предложение в комментарий колонки. ⚠ Это рефакторинг в ДЕНЕЖНОМ коде без дефекта сегодня — отдельным паком, не минором формы заказа | платформа | когда-нибудь (после строки 307) | вид прогона читается признаком, а не значением денежного поля | **D39.208** | | 309 | ⛔ **ГЕЙТ ВЕРСИИ КОНТРАКТА СВЕРЯЕТ ЧИСЛА, А НЕ ФОРМЫ — И ПОТОМУ НЕ ВИДИТ ГЛАВНОГО РАСХОЖДЕНИЯ.** `TestTheAnnouncedContractVersionIsTheOneTheCanonRatified` сверяет константу сборки с `info.version` канона. Пока эти два числа равны, гейт ЗЕЛЁН — **даже если провод и документ описывают РАЗНЫЕ формы**. Замерено 06.09 трижды за сутки: канон 0.11.0 объявлял полосу «in chapters», а сборка слала юниты; обещал `ordered_chapters: 0` для символьного заказа, а приезжал спан 2; перечислял три значения словаря, а движок слал четыре. **Ни одно из трёх гейт не покраснил**, потому что число совпадало. ⇒ заказ: сверять ПОЛЯ схемы с проводом, а не только версию. ⭐ **Дешёвая форма названа Fable 5 и уже наполовину оплачена нормой:** тест платформы пишет РЕАЛЬНЫЕ ответы в `examples/*.json` (по действующему правилу «фикстуры снимать реальным бинарём»), канон несёт их в `examples:`, линт сравнивает — тогда фраза «both report 0» умерла бы о файл примера, а не о верификатора через сутки. Прозу это не ловит, ЗНАЧЕНИЯ — ловит. ⚠ Вторая половина той же нормы — **авторство**: утверждение канона о ЗНАЧЕНИИ пишет ЗОНА из своих пинов и называет пин в сообщении коммита; оркестратор ратифицирует текст, а не сочиняет его. Три сегодняшние лжи в каноне написаны тем, у кого не было прогона | оркестратор+платформа | скоро | поля схемы сверяются с проводом порождёнными примерами | **D39.208** | @@ -272,7 +272,7 @@ | 329 | ⚠ **ОСТАТОК АУДИТА ПАКОВ И ДОКОВ 06.09: 77 находок, закрыто лендингом `e4097cb` только то, что било по двум выданным пакам.** Доказательная база — `docs/archive/reports/PROMPT_AND_DOCS_AUDIT_2026-09-06.md`. ⛔ **Вес и формулировки там принадлежат АГЕНТАМ: веер остановлен владельцем на логической точке (расход), и опровергатель прошёл НЕ по всем** ⇒ перед исполнением каждая пере-снимается по дереву. Названные, но не разобранные кандидаты: `backend/docs/components.puml` и `pipeline.puml` (глаголов 11 против 6, `.bank-stop.json` снесён D39.158, «интерим» пака-17 закрыт) · `backend/docs/MONEYSTOP_REPORT.md:3` · `docs/architecture/12-go-style-notes.md:17` (построчный маркер снят с провода, сам ⟨проверить⟩ едет) · `docs/architecture/15-money-path.md:62` и строка бэклога **166** (платформенная половина названа дырой, а построена — 19 файлов зоны) · `platform/BACKLOG.md:25` (П-10, четыре ложных утверждения) · `platform/docs/DEFECT_REGISTER.md` `PD-458` (статус-жертва: лекарство легло тем же коммитом). ⚠ Правки в ЧУЖИХ зонах — пинги владельцам зон, не своя правка | доки | скоро | пере-снятие каждой находки по дереву + правки в своей зоне + пинги в чужие | аудит промтов 06.09 | | 330 | ⛔ **ЧТО ДВИЖОК ДЕЛАЕТ С ПРОТИВОРЕЧИЕМ В БАНКЕ — РЕШЕНИЕ ВЛАДЕЛЬЦА, И ОНО ПРО ДЕНЬГИ.** Слово владельца (строка 267): «у неподписанного термина тоже обязан быть ОДИН перевод». Сегодня движок противоречие в лучшем случае ВИДИТ: `ApprovedSharedKeyCollisions` роняет прогон на approved×approved, `UnverifiedKeyConflicts` варнит approved×unsigned, детектор схлопывания (пак 06.09) добавит репорт. **Ни один не делает ОДИН перевод.** Два выхода требуют слова владельца, потому что оба стоят денег: (а) «варнинг→стоп» для approved×unsigned — роняет ОПЛАЧЕННЫЙ прогон, довод против записан в коде (`backend/internal/membank/memseed.go:685-688`: самострел); (б) отказ схлопывания отдавать противоречащую форму — термин остаётся несхлопнутым, банк держит прежнюю форму; это меняет СОДЕРЖИМОЕ банка ⇒ двигает `memory_version` и снапшот редакторской волны ⇒ **пере-покупка на затронутых книгах**. ⚠ Разводить (а) и (б) по разным пакам нельзя — это ОДИН вопрос «что ДЕЛАЕМ», в отличие от «видим ли» (`D39.216`). ⇒ вопрос владельцу подаётся ВМЕСТЕ с ценой, и она ЗАМЕРЕНА 06.09 бэкенд-зоной ($0, read-only, прибор в дереве — `backend/internal/pipeline/bankconflict_corpus_test.go`, opt-in по `TM_CORPUS`): **обошёл 162 файла БД, 161 с таблицей глоссария, 142 книги с непустым банком, прочитано 13 350 строк; книг с ЛЮБЫМ конфликтом — 0**. ⛔ **Ноль ЧЕСТНЫЙ, и вот три величины, которые делают его читаемым:** популяция огромна (12 257 из 13 350 строк неподписанные — approved 1093 · draft 11399 · auto 858, то есть недостающее правило покрывает 92 % банка, а не экзотику) · форма столкновения в корпусе не возникает, потому что все существующие двери роняют дубль по ПОЛНОМУ ключу `(src,sense,since_ch,until_ch)`, а разные ОКНА через них проходят · подсаженная противоречащая строка ловится на **142 из 142** книг, значит прибор мерит. ⭐ **И решающее для владельца: СТАРОЕ правило (approved × unsigned) на том же корпусе тоже даёт 0** — то есть обе половины одинаково НЕ НАБЛЮДЕНЫ, и решать он будет про класс риска, а не про частоту | владелец | скоро | решение владельца по (а) и (б) одним ответом + цена в книгах | пинги П1/П2 бэкенд-сессии 06.09, пак «закон банка честно» | | 331 | ⛔ **ДВИЖКОВАЯ ПОЛОВИНА `PD-441`: ОТМЕНЁННЫЙ В ПОЛЁТЕ ВЫЗОВ УХОДИТ В ЛЕДЖЕР НУЛЁМ, И ПЛАТФОРМА НЕ МОЖЕТ СКАЗАТЬ БОЛЬШЕ, ЧЕМ «≥».** Формулировка платформенной зоны 06.09 (её слова, приняты оркестратором): (1) `backend/internal/pipeline/stagerun.go`, ветвь с комментарием `No 2xx ever arrived: nothing was billed` — сеттлить ОЦЕНКУ резервации, а не ноль, когда отменённый вызов УЖЕ УШЁЛ; различитель — факт ухода, и порог обязан быть ЗАМЕРЕН, а не назначен (замер `PD-441`: отмена до ухода 20–31 мс против ушедших 120756/156469/156470 мс); (2) ⭐ без второй половины платформа умеет только «≥», но никогда «≥ X, до Y»: `tmctl status --json` обязан публиковать РЯДОМ с `committed_usd` число и сумму строк, чья цена ОЦЕНОЧНАЯ или неизвестна (движок уже печатает `estimated-cost rows: N`, строка **78**). Первое чинит счёт, второе делает маржу ИЗМЕРИМОЙ. ⚠ Пользовательской половины у этого НЕТ и не проектируется: недо-счёт бьёт по деплою, баланс пользователя завышен в его же пользу (решение оркестратора 06.09, `D39.196` п.2а) | бэкенд | скоро | сеттл оценки при отмене ушедшего вызова + публикация оценочных строк в `status --json` | `PD-441`, записка-план платформы 06.09 | -| 332 | ⛔ **СИНХРОННЫЙ РАЗРЕЗ НА ПРИЁМЕ ПРИНЯТ НАПОЛОВИНУ: ПОСТРОЕН, НО НЕ ГОТОВ — сработало правило остановки.** Из одиннадцати major пака девять легли в этот один механизм, и каждая починка открывала новую площадь ⇒ зона прекратила его чинить по решению оркестратора, а не по усталости. **ДЕСЯТЬ незакрытых пунктов — поимённо в зонном журнале** `platform/docs/platform-PROGRESS.md`, греп `СРАБОТАЛО ПРАВИЛО ОСТАНОВКИ`; здесь копии НЕТ (один носитель). Первый из них определяет форму: **граница бюджета хвоста обязана ВЫВОДИТЬСЯ ИЗ кода пути, а не перечисляться руками** — счёт выводили трижды и трижды неверно. ⚠ Ложный инвариант бюджета заландён осознанно: окно лжи 8 секунд ширины (дедлайн 26m21s–26m29s) при дефолте 10m0s, последствие — теряется идемпотентность (повтор даёт вторую книгу), а не целостность; ряд регистра несёт числа. Три пункта дописаны приёмкой: потерянный ограничитель параллелизма (`MaxWorkers: 4` был у очереди, теперь разрез живёт в HTTP-обработчике без лимитера) · маршрут молчит до 210–220 с после последнего байта, промежуточный прокси не спрошен · отказ уходит наружу только МАШИННЫМ кодом (`Detail` не заполняется) ⇒ «честная причина человеку» из заказа сегодня НЕ доезжает | платформа | скоро | пак, забирающий разрез: вывод границы из кода + десять пунктов журнала | приёмка пака «деньги и правда» 07.09 | +| 332 | ⛔ **СИНХРОННЫЙ РАЗРЕЗ НА ПРИЁМЕ ПРИНЯТ НАПОЛОВИНУ: ПОСТРОЕН, НО НЕ ГОТОВ — сработало правило остановки.** Из одиннадцати major пака девять легли в этот один механизм, и каждая починка открывала новую площадь ⇒ зона прекратила его чинить по решению оркестратора, а не по усталости. **ДЕСЯТЬ незакрытых пунктов — поимённо в зонном журнале** `platform/docs/platform-PROGRESS.md`, греп `СРАБОТАЛО ПРАВИЛО ОСТАНОВКИ`; здесь копии НЕТ (один носитель). Первый из них определяет форму: **граница бюджета хвоста обязана ВЫВОДИТЬСЯ ИЗ кода пути, а не перечисляться руками** — счёт выводили трижды и трижды неверно. ⚠ Ложный инвариант бюджета заландён осознанно: окно лжи 8 секунд ширины (дедлайн 26m21s–26m29s) при дефолте 10m0s, последствие — теряется идемпотентность (повтор даёт вторую книгу), а не целостность; ряд регистра несёт числа. Три пункта дописаны приёмкой: потерянный ограничитель параллелизма (`MaxWorkers: 4` был у очереди, теперь разрез живёт в HTTP-обработчике без лимитера) · маршрут молчит до 210–220 с после последнего байта, промежуточный прокси не спрошен · отказ уходит наружу только МАШИННЫМ кодом. ⚠ **испр. 08.09: прежняя редакция называла дефектом незаполненный `Detail` — это НЕВЕРНО и ведёт чинить запрещённым инструментом.** Канон дословно: `title` и `detail` пишутся для РАЗРАБОТЧИКА и лога, клиент их пользователю показывать НЕ ДОЛЖЕН; единственное исключение `localized` не несёт ни одна строка кода. При этом словарь `errors[]` ОТКРЫТ по словам канона, и обе причины отказа разреза уже едут перечислимыми (`no_book`, `no_chapter_structure` — `platform/internal/httpapi/problem.go`), а расширяется полоса через НЕ закрытый `cause.code`, тогда как новый КОРНЕВОЙ `code` есть ломающее изменение и минор. ⇒ вопрос не «как довезти», а «какая причина ещё не перечислена и рисует ли клиент фразу» | платформа | скоро | пак, забирающий разрез: вывод границы из кода + десять пунктов журнала | приёмка пака «деньги и правда» 07.09 | | 339 | **РОЛИ НЕ ПОКАЗЫВАЮТ СУЩЕСТВУЮЩУЮ ФОРМУ ПОВЕРХНОСТИ — якорная половина канон-проверок.** `terminology.go:873` и `:941`: обе проверки пропускают пару с РАВНЫМИ источниками; проверочная половина закрыта паком 06.09, якорная — нет. ⚠ Лечение МЕНЯЕТ ЗАПРОС к модели ⇒ деньги и снапшот, поэтому отдельным решением, а не прицепом | бэкенд | скоро | показать роли существующую форму + цена в снапшоте | §5а №2 + предложение №2 зоны | | 340 | ⛔ **СУЖЕНИЕ ВХОДА ДЕЙСТВУЕТ ТОЛЬКО НА СИНХРОННОЙ ВЕТВИ: при деплойном классе книга одним полотном попадает в библиотеку МОЛЧА.** Отказ по числу глав (`D39.221`) стоит на пути, где движок ответил; когда он недоступен, книга уходит в очередь прежним путём и сужение её не видит. ⛔ Закрыть нечем: словарь `RejectReason` закрыт четырьмя значениями, а ближайшее по смыслу `source_unreadable` ТЕРМИНАЛЬНО и УДАЛЯЕТ исходник — то есть уничтожило бы файл пользователя из-за НАШЕГО ограничения. ⇒ развилка владельцу: пятая причина отказа минором канона ЛИБО осознанное «асинхронная ветвь остаётся проницаемой до готовности структуры глав». Рекомендация оркестратора — второе: беты нет, впуск ручной, а лишнее значение в закрытом словаре переживёт надобность | владелец | скоро | слово владельца + минор канона или записанный риск | `PD-463`, вопросы платформы 07.09 | | 341 | **ПРЕДЛОЖЕНИЕ ДВИЖКА ПРОВЕСТИ `--ceiling-usd` В `status` ВИСИТ БЕЗ ДИСПОЗИЦИИ С 09.08** (пинг №15). `backend/internal/pipeline/status.go` отвечает «a read path never carries a run-scoped override» — то есть у движка есть позиция, а решения нет ни за, ни против. ⇒ принять или отклонить, но не оставлять третий месяц | бэкенд | когда-нибудь | диспозиция оркестратора одной строкой | вопросы платформы 07.09 | @@ -285,9 +285,10 @@ | 351 | **ЛИНТ СЛОВАРЯ КЛАССИФИКАТОРА ПИНИТ ОДНО НАПРАВЛЕНИЕ: слово, УБРАННОЕ из Go, не ловит ничто.** Гейт `TestEveryClassifierPromptSpeaksTheEnginesVocabulary` (лендинг `D39.225`) утверждает «каждое слово из `terminology.Types`/`Genders` названо в промте пары» — и потому сжимается вместе со своим предметом: выкинутый класс `title` оставляет батарею зелёной (замер 07.09 на копии: 19 ok / 0 FAIL). Вырожденный предел закрыт стражем `len(classifierVocabulary()) == 0`, общий случай — нет. Настоящий дом — пин САМОГО словаря, которого в репозитории нет (research/33 Д1: 0 тестов на `terminology.Types`). ⚠ Граница названа зоной самостоятельно, в заказ пака не входила | бэкенд | когда-нибудь | пин словаря (голден или рефлексивный тест) | `D39.225`, research/33 Д1 | | 352 | **ПЯТЬ РУКОПИСНЫХ СПИСКОВ АРМ-КОНФИГОВ ЖИВУТ РЯДОМ С ДВУМЯ ГЕЙТАМИ, КОТОРЫЕ ТЕПЕРЬ ГЛОБЯТ.** `pipeline/promptlabel_test.go:45` · `config/echoregen_shipping_test.go:35` · `config/content_labels_test.go:529` и `:575` · `config/prompt_pack_test.go:239` перечисляют армы поимённо; новый арм автоматически попадает в `TestAnArmIsTheProductionConfigWithADifferentEditor` и `TestSwapArmConfigs` и проходит мимо всех пяти. Найдено приёмкой `D39.225`, вне заказа пака; предсуществующее | бэкенд | когда-нибудь (со следующим касанием армов) | свести пять списков к глобу | `D39.225` | | 353 | **КОНТРАКТ СЕКЦИИ ПРЕДЛОЖЕНИЙ В САЙДКАРЕ БАНКА ЗАПИНЕН ПО ПОЛЯМ И ПОРЯДКУ, НО НЕ ИСЧЕРПЫВАЮЩЕ — строка заведена ПРАВИЛОМ ОСТАНОВКИ, а не находкой.** Три круга приёмки `D39.225` находили в одной новой секции всё более узкие дыры: сперва семь полей без утверждений, потом `Freq` и своп списков, потом ранжирование и класс пустого `Dst`. Каждая была настоящей и предъявлена посадкой; каждый следующий круг стоил больше и покупал меньше. Сегодня запинены: все решающие поля против стоп-строки · порядок против стоп-СТРОК на двух уровнях · рефлективный страж «ни одно поле проекции не нулевое» (ловит поле, добавленное завтра и не заполненное). ⚠ **Дописка 08.09 (приёмка `D39.226`):** у поля `SettledByBank` стоп-строки нет свидетеля в списке «полей, которых сайдкар намеренно НЕ несёт» (`bankexport_test.go`) — то есть его сознательное отсутствие в проекции не утверждено ничем; закрывать вместе с первым читателем секции. ⚠ Границу называет сам гейт: поле, которое отрастил `BankStopRow` и которое СЛЕДОВАЛО бы проецировать, механически не ловится — это суждение того, кто поле добавляет. ⇒ вернуться к исчерпывающему контракту, когда у секции появится ЧИТАТЕЛЬ (платформенная половина строки **224**): пока читателя нет, расхождение ничем не судится, а в день его появления разница станет семантикой задним числом — второй ярус класса A `18-bank-ontology.md` | бэкенд + платформа | когда-нибудь (гейт: появление читателя секции, строка 224) | исчерпывающий пин контракта секции вместе с её первым потребителем | `D39.225` п.10 | -| 354 | ⛔ **ОПЛАЧЕННЫЙ `2xx` С ОБОРВАННЫМ ТЕЛОМ — КЛАСС, СТОИВШИЙ 32 % ЖИВОГО ПРОГОНА, И СТРАНИЦА КВИРКОВ О НЁМ МОЛЧИТ.** Живой прогон 08.09 (10 глав, `committed=$1.259217`): два вызова редактора вернулись `2xx` с нечитаемым JSON и были ОПЛАЧЕНЫ — `$0.407606`, то есть **32 % бюджета прогона**. Движок класс ЗНАЕТ и обрабатывает штатно: `backend/internal/pipeline/disposition.go:79` `FlagDecodeError` — «2xx with an unreadable body — billed, conservatively settled, flagged». ⛔ Но `docs/experiments/00-provider-quirks.md` — страница, которую канон велит читать ПЕРЕД правкой адаптеров и вызовов, — о нём не несёт НИ СТРОКИ: греп по `decode`/`unexpected end of JSON`/«оборван» даёт **0 при 250 строках файла** (контроль: слово `provider` там встречается 3 раза, то есть спрошено существующее). ⇒ следующая сессия, читающая квирки перед платным прогоном, не узнает о поведении, которое съедает треть бюджета. ⚠ Зона — ПОЛИГОН (`docs/experiments/` по зонной таблице), живой сессии полигона нет; строка заведена оркестратором как носитель, писать в файл полигону | полигон | скоро (перед следующим платным прогоном любой зоны) | строка в `00-provider-quirks.md` с частотой, стоимостью и тем, что движок делает с классом | живой прогон 08.09, пак «правда оператору и деньги» | +| 354 | ⛔ **ОПЛАЧЕННЫЙ `2xx` С ОБОРВАННЫМ ТЕЛОМ — КЛАСС, СТОИВШИЙ 32 % ЖИВОГО ПРОГОНА, И СТРАНИЦА КВИРКОВ О НЁМ МОЛЧИТ.** Живой прогон 08.09 (10 глав, `committed=$1.259217`): два вызова редактора вернулись `2xx` с нечитаемым JSON и были ОПЛАЧЕНЫ — `$0.407606`, то есть **32 % бюджета прогона**. Движок класс ЗНАЕТ и обрабатывает штатно: `backend/internal/pipeline/disposition.go:79` `FlagDecodeError` — «2xx with an unreadable body — billed, conservatively settled, flagged». ⛔ Но `docs/experiments/00-provider-quirks.md` — страница, которую канон велит читать ПЕРЕД правкой адаптеров и вызовов, — о нём не несёт НИ СТРОКИ: греп по `decode`/`unexpected end of JSON`/«оборван» даёт **0 при 250 строках файла** (контроль: слово `provider` там встречается 3 раза, то есть спрошено существующее). ⇒ следующая сессия, читающая квирки перед платным прогоном, не узнает о поведении, которое съедает треть бюджета. ⛔ **испр. 08.09: МЕХАНИЗМ НАЗВАН ЗДЕСЬ НЕВЕРНО — это не свойство провайдера, а дефект движка, и носитель у него теперь строка 360.** Полигон замером exp-23 п.5 показал, что за подписью «нечитаемое тело» стоит ТАЙМАУТ ЧТЕНИЯ по `attempt_s`, который движок сам же выбрасывает в `_`. ⇒ эта строка остаётся ТОЛЬКО про пробел страницы квирков: она обязана предупреждать, ЧТО оператор увидит и почему, со ссылкой на 360, а не приписывать поведение вендору. ⚠ Зона — ПОЛИГОН (`docs/experiments/` по зонной таблице), живой сессии полигона нет; строка заведена оркестратором как носитель, писать в файл полигону | полигон | скоро (перед следующим платным прогоном любой зоны) | строка в `00-provider-quirks.md` с частотой, стоимостью и тем, что движок делает с классом | живой прогон 08.09, пак «правда оператору и деньги» | | 355 | **РАЗЛОЖЕНИЕ «ЗА ЧТО ЗАПЛАЧЕНО» ОТНОСИТ КЛАССИФИКАТОР В `superseded`, ПОТОМУ ЧТО БЭНК-РОЛИ ДЕЛЯТ ОДНУ ПОЗИЦИЮ.** Бэнк-роли адресуются синтетическим чанком (глава 0, `chunk_idx` = номер батча), а разложение ключуется `{глава, чанк, стадия}` (`backend/internal/pipeline/paidtail.go:120-123`: `p := pos{u.Chapter, u.ChunkIdx, u.Stage}`, затем `case lastPaid[p] != i` → `SupersededUSD`). Классификаторский батч 0 и терминологический батч 0 занимают ОДНУ позицию `{0,0,terminology}` ⇒ более ранний уезжает в «superseded», то есть в «купил ничего». Улика живого прогона 08.09 — три строки леджера (`0 0 terminology classifier $0.009013` · `0 0 terminology terminologist $0.002775` · `0 1 terminology terminologist $0.001820`) при печатном «the book's TERMINOLOGY $0.004595 (2)», где два — ровно последние. ⚠ На стенде цена копеечная, на боевом `classify_budget_usd: 1.00` доля может быть большой: оператор читает, что классификатор куплен зря, тогда как он куплен и использован. Найдено зонной сессией на прогоне, ею НЕ чинилось — вне её заказа | бэкенд | скоро (до следующего платного прогона, где смотрят разложение) | разведение позиций бэнк-ролей в ключе разложения + пин на три строки леджера | живой прогон 08.09 | | 356 | **КНИГА У СВОЕГО ПОЖИЗНЕННОГО ПОТОЛКА РОЛИ НАВСЕГДА ОСТАЁТСЯ БЕЗ КОНСОЛИДИРОВАННОГО БАНКА, и ни одно сообщение не говорит именно этого.** `store/ledger.go` `RoleSpentUSD` суммирует чекпойнты роли за ВСЮ жизнь книги, предполётный срез сеет `probe := spent` и обрывается на `probe+want > budgetUSD`; при `fits == 0` цикл попыток не запускается вовсе. ⚠ Оператор ПРИ ЭТОМ извещён — `terminologist.go:936` печатает `batches_planned=N batches_running=0 batches_dropped=N spent_usd budget_usd` ДО первого вызова (сверено оркестратором 08.09), поэтому «тихо» здесь неверно, и находка приёмки в этой части ОТКЛОНЕНА доводом зоны. Верен остаток: сообщение говорит про ЭТОТ прогон, а не про то, что книга исчерпала пожизненный бюджет роли и консолидации не получит НИКОГДА, сколько её ни перезапускай. Свойство `budget_usd`, а не фильтра пака «правда оператору и деньги»: тот на решение бюджета не влияет. ⇒ решить, что показывать оператору и владельцу: отдельное сообщение «книга у пожизненного потолка роли», подъём потолка как явный акт, или сознательное молчание с записью | бэкенд | когда-нибудь (гейт: первая книга, упёршаяся в пожизненный потолок роли) | сообщение об исчерпании пожизненного бюджета либо ратифицированное молчание | приёмка `D39.226`, линза «вне карты» | | 357 | **ПОМЕТКА «БАНК УЖЕ РЕНДЕРИТ ЭТУ ПОВЕРХНОСТЬ» ЕСТЬ ТОЛЬКО В ОДНОЙ ИЗ ТРЁХ ПОВЕРХНОСТЕЙ ПОДПИСИ — а первой владелец читает НЕ её.** Пак `D39.226` довёл пометку `NOT ASKED(the bank already renders this surface and every draft agreed — nothing to decide)` до ТЕКСТОВОГО сайдкара стоп-таблицы, и там она предъявлена исполнением. Остаются две: **(а)** экран стопа на stdout (`cmd/tmctl/render.go`, то, что печатает `renderSignatureStop` первым) о пропущенном кандидате не знает — 0 ссылок на признак, и приёмка предъявила контролем, что этот же рендерер УМЕЕТ печатать пер-строчные пометки (`invented`): пропущенная строка и неотвеченная выглядят одинаково (`方源 — … —` против `花家 — … —`). Это НЕ ложь о решении (решать там действительно нечего), это отсутствующее объяснение на экране, который читают первым. **(б)** машинный сайдкар `BankExportProposal` факта не несёт — но это ОБЪЯВЛЕНО (`mining.go:412`) и упирается в строку **353**, чью секцию заказ пака трогать запрещал. ⇒ (а) — дофикс одной пометки в рендерере; (б) — вместе с первым читателем секции, то есть со строкой 224 | бэкенд | скоро (до первой подписи банка внешним пользователем) | пометка в stdout-рендерере + решение по (б) вместе со строкой 353 | приёмка `D39.226`, линза «вне карты» | -| 358 | **ИТОГ ТРАТ ПРОГОНА ЗАНИЖЕН: `TOTAL (this run)` не включает стоимость классификатора.** `backend/internal/pipeline/waverun.go:218` — замер живого прогона 08.09: сырой леджер даёт `$0.007280`, отчёт печатает `$0.005460`, разница ровно классификаторская. Предсуществующее, паком `D39.226` не вносилось и им не чинилось. ⚠ Класс тот же, что у строки 355: оператор читает о СВОИХ деньгах число, которое не сходится с леджером, и не может этого заметить без второго чтения. ⇒ либо включить, либо назвать в самой строке, что классификатор считается отдельно | бэкенд | скоро (до следующего платного прогона, где смотрят итог) | итог сходится с леджером либо объявляет своё исключение + пин | приёмка `D39.226`, живой прогон 08.09 | -| 359 | **ТРИ ЯКОРЯ В ЧУЖИХ ЗОНАХ ПРОТУХЛИ ОТ ДВУХ ДВИЖКОВЫХ ЛЕНДИНГОВ — адреса уже найдены, править владельцам зон.** Линтер `counts.py --lint` даёт 6 проблемных якорей при 119 живых доках; три из них — фронтовые и пингнуты в `frontend/docs/frontend-PROGRESS.md` (зона ЗАМОРОЖЕНА). Остальные три, с новыми адресами, снятыми оркестратором 08.09: **(а) полигон** — `docs/experiments/23-editor-tier.md:7739` и `:7740` ссылаются на «×2 дешевле glm» в `configs/pipeline-c1.yaml:95` и `pipeline-arm-glm.yaml:6`; ⚠ цель ИСЧЕЗЛА, а не сдвинулась (греп даёт 0): долларовые числа сняты из шапки арма приёмкой `D39.226`, потому что стояли на опровергнутом ценовом базисе — значит якорь надо не пере-навести, а пере-написать по факту. **(б) бэкенд** — `backend/docs/DISCLOSURE_LAW_DESIGN.md:40` ссылается на `internal/pipeline/quality.go:233`, токен уехал на `:235`. ⚠ Оркестратор в чужие зоны не пишет; строка — носитель, чтобы адреса не искали заново | полигон + бэкенд | когда-нибудь (попутно с любым касанием этих файлов) | пере-наведённый якорь у бэкенда, пере-написанный по факту — у полигона | `counts.py --lint` 08.09 | +| 358 | **ИТОГ ТРАТ ПРОГОНА ЗАНИЖЕН: `TOTAL (this run)` не включает стоимость классификатора.** `backend/internal/pipeline/waverun.go:218` — ⚠ **испр. 08.09: прежняя редакция несла числа `$0.007280`/`$0.005460` — они АВГУСТОВСКИЕ, из соседнего файла, к этому прогону отношения не имеют (моя ошибка при заведении ряда).** Замер прогона 08.09 по строкам леджера: терминологическая фаза стоила `$0.013608` = классификатор `$0.009013` + рендер-батчи `$0.002775` и `$0.001820`; в итог волны входит только рендерная сумма `$0.004595`, то есть две трети денег фазы до итога не доезжают. Предсуществующее, паком `D39.226` не вносилось и им не чинилось. ⚠ Класс тот же, что у строки 355: оператор читает о СВОИХ деньгах число, которое не сходится с леджером, и не может этого заметить без второго чтения. ⇒ либо включить, либо назвать в самой строке, что классификатор считается отдельно | бэкенд | скоро (до следующего платного прогона, где смотрят итог) | итог сходится с леджером либо объявляет своё исключение + пин | приёмка `D39.226`, живой прогон 08.09 | +| 359 | **ТРИ ЯКОРЯ В ЧУЖИХ ЗОНАХ ПРОТУХЛИ ОТ ДВУХ ДВИЖКОВЫХ ЛЕНДИНГОВ — адреса уже найдены, править владельцам зон.** Линтер `counts.py --lint` даёт 6 проблемных якорей при 121 живом доке; три из них — фронтовые и пингнуты в `frontend/docs/frontend-PROGRESS.md` (зона ЗАМОРОЖЕНА). Остальные три, с новыми адресами, снятыми оркестратором 08.09: **(а) полигон** — `docs/experiments/23-editor-tier.md:7739` и `:7740` ссылаются на «×2 дешевле glm» в `configs/pipeline-c1.yaml:95` и `pipeline-arm-glm.yaml:6`; ⚠ цель ИСЧЕЗЛА, а не сдвинулась (греп даёт 0): долларовые числа сняты из шапки арма приёмкой `D39.226`, потому что стояли на опровергнутом ценовом базисе — значит якорь надо не пере-навести, а пере-написать по факту. **(б) бэкенд** — `backend/docs/DISCLOSURE_LAW_DESIGN.md:40` ссылается на `internal/pipeline/quality.go:233`, токен уехал на `:235`. ⚠ Оркестратор в чужие зоны не пишет; строка — носитель, чтобы адреса не искали заново | полигон + бэкенд | когда-нибудь (попутно с любым касанием этих файлов) | пере-наведённый якорь у бэкенда, пере-написанный по факту — у полигона | `counts.py --lint` 08.09 | +| 360 | ⛔⛔ **ТАЙМАУТ ЧТЕНИЯ МАСКИРУЕТСЯ ПОД НЕЧИТАЕМОЕ ТЕЛО, РЕТРАИТСЯ И БИЛЛИТСЯ ПО ОЦЕНКЕ — 23–34 % ЦЕНЫ РЕДАКТОРСКОГО ПРОГОНА, И НОСИТЕЛЯ У КЛАССА НЕ БЫЛО.** Механизм доказан кодом: `backend/internal/llm/httpllm.go` в чтении ответа делает `data, _ := io.ReadAll(io.LimitReader(...))` — **ошибка чтения выбрасывается**, а обрыв распознаётся по РАЗМЕРУ (читают на байт дальше лимита), поэтому тело, оборванное дедлайном `attempt_s` посреди генерации, от целого НЕ ОТЛИЧИМО. Обрезок идёт в `json.Unmarshal` → `unexpected end of JSON input` → `BilledDecodeError` («2xx body decode failed (call IS billed)»), и возвращается он с флагом retryable **`true`** ⇒ вызов повторяется до `max_attempts`, и КАЖДАЯ попытка сеттлится ПО ОЦЕНКЕ. ⛔ Замер полигона (`docs/experiments/23-editor-tier.md`, п.5 «ЧЕТЫРЕ ТАЙМАУТА»): латентности `480390 / 480505 / 480577 / 480528` мс = **ровно 2 × `attempt_s: 240`**, класс назван `decode_error`, цена — **$0.475893 = 23.8 % руки строками, $0.689215 = 34.5 % юнитами**; полигон квалифицировал это как дефект ДВИЖКА и пингнул оркестратору («не наша зона»). ⚠ **Носителя не было: греп `attempt_s` и «таймаут чтения» по живому бэклогу — 0 хитов при 255 рядах** (контроль: `decode` — 3 хита), то есть спрошено существующее. ⛔ **И это ТА ЖЕ физика, что строка 331**, где вызов, оборванный потолком, уезжает в леджер НУЛЁМ: один предмет — «вызов оборвали МЫ, ответа нет» — движок сегодня закрывает ПРОТИВОПОЛОЖНО, оценкой в одном случае и нулём в другом. ⚠ **Граница честности:** что два вызова прогона 08.09 ($0.407606 = 32 % бюджета, «unexpected end of JSON input») были именно таймаутами — ВЫВЕДЕНО по совпадению подписи с замером полигона, а НЕ измерено: сырого леджера того прогона в дереве нет (`.db` новее 07.09 — 0 при 163). ⇒ инженерная половина (не выбрасывать ошибку чтения, отличать таймаут от нечитаемого тела, не ретраить вызов, оборванный СВОИМ дедлайном) — заказ без развилок; ДЕНЕЖНАЯ половина требует слова владельца: платит ли пользователь за вызов, который оборвали мы, и если да — по оценке или по нулю. Пока ответа нет, платформа биллит `committed_usd`, куда оценка уже попала | бэкенд | блокер-очереди для ПЛАТНЫХ прогонов (класс съедает четверть-треть бюджета; $0-работа им не блокируется) | ошибка чтения не выбрасывается · таймаут отличён от нечитаемого тела и НЕ ретраится · денежная диспозиция ратифицирована владельцем · строка 331 закрыта тем же решением | замер полигона exp-23 п.5 + разбор Fable 5 08.09, пере-проверено оркестратором по коду | diff --git a/docs/ORCHESTRATOR_SESSION_PROMPT.md b/docs/ORCHESTRATOR_SESSION_PROMPT.md index 6f08304a..4e80df84 100644 --- a/docs/ORCHESTRATOR_SESSION_PROMPT.md +++ b/docs/ORCHESTRATOR_SESSION_PROMPT.md @@ -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/` при приёмке); вне репозитория — только diff --git a/docs/PLATFORM_INTAKE_TRUTH_SESSION_PROMPT.md b/docs/PLATFORM_INTAKE_TRUTH_SESSION_PROMPT.md new file mode 100644 index 00000000..e2a303ed --- /dev/null +++ b/docs/PLATFORM_INTAKE_TRUTH_SESSION_PROMPT.md @@ -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 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 «что не удалось» · вопросы +оркестратору. diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 802df4e0..a30b3922 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -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б) планы доработок в бэкенд и платформу — исполняются ПАКАМИ, за 07–08.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б) планы доработок в бэкенд и платформу — исполняются ПАКАМИ, за 07–08.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 — самый дорогой движковый класс (23–34 % цены редакторской руки), носителя у него не было;** её ДЕНЕЖНАЯ половина ждёт слова владельца (платит ли пользователь за вызов, оборванный нами) и сцеплена со строкой 331. До ответа пак по 360 не пишется. > **(4) ДИЗАЙН-ПАК 161 — ✅ НАПИСАН 07.09** (`docs/CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md`, `7f84c8d`), прошёл оба > рубежа. ⚠ **НЕ ВЫДАН: новый пак идёт НОВОЙ сессии, обе ночные отработали** — запуск за владельцем. > **(5) ТЕХДОЛГ БЭКЕНДА — разбор `research/33` в трекер: ✅ ЗАЛЕНДЕН 07.09, акт `D39.224`.** Заведены строки diff --git a/docs/README.md b/docs/README.md index 4a0465bf..32dbc68a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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** (строки 333–338); живое — 353–359 | | Бэкенд · ПРАВДА ОПЕРАТОРУ И ДЕНЬГИ | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 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 — зона ОРКЕСТРАТОРА, не платформы: правило игнора общее для всех рабочих деревьев клона) | diff --git a/docs/architecture/05-decisions-index.md b/docs/architecture/05-decisions-index.md index 519201a3..f92ab52e 100644 --- a/docs/architecture/05-decisions-index.md +++ b/docs/architecture/05-decisions-index.md @@ -1,4 +1,4 @@ -# Реестр D-нот — карта актуальности v2 (D1–D39.226; титул — носитель головы, бампать при каждом аппенде) +# Реестр D-нот — карта актуальности v2 (D1–D39.227; титул — носитель головы, бампать при каждом аппенде) > ⚠ **Колонку «тело» `counts.py --check` НЕ сторожит по устройству:** он сверяет полноту НОМЕРОВ, а не > место тела, поэтому колонка держится дисциплиной лендинга. Не нашёл тело по колонке — иди в слайсы, @@ -285,3 +285,4 @@ | D39.224 | 07.09 | **РАЗБОР research/33 В ТРЕКЕР**: заведены строки **344–350** (шов заголовка · словари классификатора · правило заголовка и `第零章` · каналы 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`). Закрыты строки 333–338; остатки — 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 % кандидатов, и фаза платится раз на книгу. Два блокера закрыты корнем, третий круг нашёл мёртвый страж | жив | ЖИВОЕ: строки 354–358 и остаток 353; три ошибки ЗАКАЗЧИКА названы в п.8 | движок деньги банк оператор приёмка | +| D39.227 | 08.09 | **ТРИАЖ И ВЫДАЧА ДВУХ ПАКОВ** («движок уже знает» + «разрез приёма и правда о себе», оба $0). Заведена строка **360** — таймаут чтения маскируется под нечитаемое тело, ретраится и биллится по оценке (23–34 % цены редакторской руки), носителя у класса не было; строка **354** называла механизм свойством провайдера и исправлена. Пред-решение оркестратора «новый корневой `code`» ОТОЗВАНО: корневой закрыт, открыт `cause.code`. Фантом-гард спущен до номера ПУНКТА. Вопрос владельцу: платит ли пользователь за вызов, оборванный нами | триаж · паки · деньги · контракт | diff --git a/docs/architecture/05-decisions-log.md b/docs/architecture/05-decisions-log.md index c6073fcf..ced24e03 100644 --- a/docs/architecture/05-decisions-log.md +++ b/docs/architecture/05-decisions-log.md @@ -1,4 +1,4 @@ -# Журнал решений оркестратора — контракт D1–D39.226 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`) +# Журнал решений оркестратора — контракт D1–D39.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, исполнением не воспроизводился.