325 lines
40 KiB
Markdown
325 lines
40 KiB
Markdown
# Пак бэкенда: «ДВИЖОК УЖЕ ЗНАЕТ — ДОВЕЗИ ДО ЧИТАТЕЛЯ»
|
||
|
||
## 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 «что не
|
||
удалось» · вопросы оркестратору.
|