Second cut pass in the backend zone: restore what the review found lost, and take the sediment the first pass left behind

This commit is contained in:
heaven 2026-09-02 11:49:08 +03:00
parent 4a1ecc3aa6
commit 1d84cff75e
5 changed files with 145 additions and 132 deletions

View file

@ -1,6 +1,6 @@
# backend/ — Go-бэкенд TextMachine
Зона сессии «Бэкенд». Контракт — `docs/architecture/05-decisions-log.md` (свежая голова — с хвоста файла); целевая архитектура (7 слоёв, инвариант общности §0.1 — любая книга/пара/структура; статус стройки — шапка-таблица) — `docs/architecture/09-target-architecture.md`; норматив общности — `12-go-style-notes.md`; **актуальный сессионный промт — список «Активные хендофф-промты» в `docs/README.md`**; контракты Фазы 0 — `03-implementation-notes.md` (через баннер); квирки провайдеров — `docs/experiments/00-provider-quirks.md` (читать ПЕРЕД правкой адаптеров/вызовами). **Язык-данные майнера — вне пайплайна: `internal/lang`+`configs/langpacks/` (D39.16); пар-калибровки — `configs/pairs/<пара>.yaml`, промпты — `prompts/<пара>/<роль>.md` по конвенции (D39.23).** **Подсистемы (`chunk`/`miner`/`checks`/`membank`) не импортируют друг друга и не импортируют `pipeline` — драйвер является композит-корнем (D39.23; проверяется `go list -deps`).** **`.env` не читать.**
Зона сессии «Бэкенд». Контракт — `docs/architecture/05-decisions-log.md` (свежая голова — с хвоста файла); целевая архитектура (7 слоёв, инвариант общности §0.1 — любая книга/пара/структура; статус стройки — шапка-таблица) — `docs/architecture/09-target-architecture.md`; норматив общности — `12-go-style-notes.md`; контракты Фазы 0 — `03-implementation-notes.md` (через баннер). **Язык-данные майнера — вне пайплайна: `internal/lang`+`configs/langpacks/` (D39.16); пар-калибровки — `configs/pairs/<пара>.yaml`, промпты — `prompts/<пара>/<роль>.md` по конвенции (D39.23).** **Подсистемы (`chunk`/`miner`/`checks`/`membank`) не импортируют друг друга и не импортируют `pipeline` — драйвер является композит-корнем (D39.23; проверяется `go list -deps`).** **`.env` не читать.**
*(Актуализировано оркестратором 01.08 по мандату владельца, факты сверены по коду.)*
@ -8,10 +8,10 @@
| Пакет | Что делает | Ключевые файлы |
|---|---|---|
| `cmd/tmctl` | CLI: `translate` / `status` (read-only N/M+паспорта глав+деньги, `--json`; работает ПРИ живом прогоне — store без flock) / `report` (request-log+леджер книги **+ per-run quality-report**: структурный KPI предл./нарратив-абзац, тире, cosmetic-strip/echo-rates, глоссарий-промахи, trust-gated — наблюдаемость, НЕ гейт; D39.2-T4) / `export` (**экспорт-поверхность для полигона**: final_hash→checkpoint→checks.ExportNormalize, manifest-join c pending/ghost-учётом, ConfigDrift-поле, `--plaintext`; D39.5) / `build` (**писатель книги**, строка 236: EPUB 3 + чистый txt из той же экспорт-проекции в `<project_db>.book.<fmt>` рядом с БД, пути — в `artifacts.book_files`; книга с ЛЮБОЙ дырой — pending/withheld/incomplete/ghost — по умолчанию отказ **exit 16** с перечнем, `--partial` пишет с пометкой на первой странице и маркером на каждой дыре; слова читателя — данные `configs/langpacks/<цель>/reader.txt`, вне снапшота; `--format epub,txt`, `--out`) / `redrive` (переатака флагнутых: `--chapter/--chunk/--reason/--dry-run`, D15.3) / `manifest` (**$0-производитель персиста манифеста глав/чанков**, строка 100: нужен ДО первого прогона — дерево глав разобранной, но не запущенной книги; `--json` печатает сам документ) / `migrate` (**$0-write-open без прогона** (D39.134): единственный путь, доводящий схему проекта до головы бинаря — read-only команды её НЕ мигрируют. Грузит ТОЛЬКО `book.yaml` — ни цен, ни ключей на $0-пути (строка 146); restore point берёт ПОД ЛОКОМ и только когда шаг реально применяется (имя `<метка>-pre-migrate.db` — вне секундного неймспейса платного пути, строка 173); несовпадение схемы у read-only путей — типизированный отказ **exit 13** с машинным токеном `schema_mismatch found=N expected=M`). **ДВА потолка прогона, и они ортогональны.** `--ceiling-usd` (строка 145) — ДЕНЬГИ: перекрывает книжный `ceilings.book_usd` ТОЛЬКО на этот прогон, в book.yaml не пишется, ноль/отрицательное = отказ запуска; каппит КУМУЛЯТИВНУЮ трату книги, а не приращение прогона. `--max-units` (D39.165 §1б) — ОБЪЁМ: не больше N ВЫХОДНЫХ ЮНИТОВ (гранулярность `units_total` манифеста, та самая, в которой платформа продаёт главы) будет ОПЛАЧЕНО этим прогоном. Юниты, которые прогон отдаёт за $0 (резюм, ре-пин), едут бесплатно и потолок не тратят; ретраи и эскалации — тоже нет, они внутри юнита. Принимает только `translate`. **Остановка по объёму — ЗАВЕРШЕНИЕ (exit 0), а не пауза:** словарь кодов выхода не расширялся, различение живёт в отчёте прогона и в логе. Отчёт различает ДОСТАВКУ (юнит, которого не было) и ПЕРЕ-ДЕЛКУ (уже доставленный юнит под сдвинутым снапшотом) — покупка, целиком ушедшая в переделку, обязана читаться как переделка.На книге, которая МАЙНИТ банк, вторая покупка требует `--resnapshot` (авто-банк растёт между покупками и двигает edit-снапшот) — прогон предупреждает об этом в логе. main — тонкая обвязка: разбор/exit-коды/.env/рендеры в тестируемых функциях | `main.go`, `invocation.go`, `render.go`, `dotenv.go` |
| `internal/llm` | OpenAI-совместимый транспорт + retry/backoff, **capability-слой** (budget_field/temperature/reasoning per-модель), провайдеры openai/local (no-proxy)/anthropic (DEPRECATED-референс); `failover.go` удалён паком-17 — маршрутизация лейблами живёт в config/pipeline (канал B, один хоп `chain[0]`, fail-closed) | `httpllm.go`, `capability.go`, `provider_*.go` |
| `cmd/tmctl` | CLI: `translate` / `status` (read-only N/M+паспорта глав+деньги, `--json`; работает ПРИ живом прогоне — store без flock) / `report` (request-log+леджер книги **+ per-run quality-report**: структурный KPI предл./нарратив-абзац, тире, cosmetic-strip/echo-rates, глоссарий-промахи, trust-gated — наблюдаемость, НЕ гейт; D39.2-T4) / `export` (**экспорт-поверхность для полигона**: final_hash→checkpoint→checks.ExportNormalize, manifest-join c pending/ghost-учётом, ConfigDrift-поле, `--plaintext`; D39.5) / `build` (**писатель книги**: EPUB 3 + чистый txt из той же экспорт-проекции в `<project_db>.book.<fmt>` рядом с БД, пути — в `artifacts.book_files`; книга с ЛЮБОЙ дырой — pending/withheld/incomplete/ghost — по умолчанию отказ **exit 16** с перечнем, `--partial` пишет с пометкой на первой странице и маркером на каждой дыре; слова читателя — данные `configs/langpacks/<цель>/reader.txt`, вне снапшота; `--format epub,txt`, `--out`) / `redrive` (переатака флагнутых: `--chapter/--chunk/--reason/--dry-run`, D15.3) / `manifest` (**$0-производитель персиста манифеста глав/чанков**: нужен ДО первого прогона — дерево глав разобранной, но не запущенной книги) / `migrate` (**$0-write-open без прогона** (D39.134): read-only команды схему НЕ мигрируют. Грузит ТОЛЬКО `book.yaml` — ни цен, ни ключей на $0-пути (строка 146); restore point берёт ПОД ЛОКОМ и только когда шаг реально применяется (имя `<метка>-pre-migrate.db` — вне секундного неймспейса платного пути, строка 173); несовпадение схемы у read-only путей — типизированный отказ **exit 13** с машинным токеном `schema_mismatch found=N expected=M`). **ДВА потолка прогона, и они ортогональны.** `--ceiling-usd` — ДЕНЬГИ: перекрывает книжный `ceilings.book_usd` ТОЛЬКО на этот прогон, в book.yaml не пишется, ноль/отрицательное = отказ запуска; каппит КУМУЛЯТИВНУЮ трату книги, а не приращение прогона. `--max-units` (D39.165 §1б; механика и денежный отчёт — D39.170) — ОБЪЁМ: не больше N ВЫХОДНЫХ ЮНИТОВ (гранулярность `units_total` манифеста, та самая, в которой платформа продаёт главы) будет ОПЛАЧЕНО этим прогоном. $0-юниты (резюм, ре-пин) потолок не тратят. Принимает только `translate`. **Остановка по объёму — ЗАВЕРШЕНИЕ (exit 0), а не пауза:** различение живёт в отчёте прогона и в логе. ⚠ На книге, которая МАЙНИТ банк, вторая покупка требует `--resnapshot` (авто-банк растёт между покупками и двигает edit-снапшот) — прогон предупреждает об этом в логе. main — тонкая обвязка: разбор/exit-коды/.env/рендеры в тестируемых функциях | `main.go`, `invocation.go`, `render.go`, `dotenv.go` |
| `internal/llm` | OpenAI-совместимый транспорт + retry/backoff, **capability-слой** (budget_field/temperature/reasoning per-модель), провайдеры openai/local (no-proxy)/anthropic (DEPRECATED-референс); маршрутизация лейблами живёт в config/pipeline (канал B, один хоп `chain[0]`, fail-closed; D4/D39.26) | `httpllm.go`, `capability.go`, `provider_*.go` |
| `internal/ledger` | Цены по usage (вкл. reasoning/cache-поля), `PriceForResponse` по фактической модели | `pricing.go` |
| `internal/store` | SQLite (modernc, CGO-free), цепочка миграций `schema_version`, reserve/settle+checkpoint, chunk_status, глоссарий (+подписной цикл терминолога), ruby, retrieval_state, request_log; `OpenReadOnly` — без flock/миграций/recovery для status/report/export (схема не совпала — типизированный `*store.SchemaMismatchError{Found,Expected}`, обе стороны); `Migrate` (+шов `beforeApply` под локом: restore point берётся там) — поверхность деплой-шага строки 174, `SchemaHead` — та самая `Expected`; write-open БД новее бинаря теперь ОТКАЗ, а не тихое открытие | `ledger.go`, `migrate.go`, `glossary.go`, `store.go` |
| `internal/store` | SQLite (modernc, CGO-free), цепочка миграций `schema_version`, reserve/settle+checkpoint, chunk_status, глоссарий (+подписной цикл терминолога), ruby, retrieval_state, request_log; `OpenReadOnly` — без flock/миграций/recovery для status/report/export (схема не совпала — типизированный `*store.SchemaMismatchError{Found,Expected}`, обе стороны); `Migrate` (+шов `beforeApply` под локом), `SchemaHead` — та самая `Expected`; write-open БД новее бинаря ОТКАЗ (D39.134) | `ledger.go`, `migrate.go`, `glossary.go`, `store.go` |
| `internal/config` | fail-fast загрузка models/pipeline/book; **эхо-мина-гейт** `echoMineViolation`; `CheckRunnable` блокирует неисполнимое (C2/fanout/judge); **пар-слой** (D39.23): `configs/pairs/<пара>.yaml` — калибровка нарезки + коридор длины пары; **промпт-резолв КОНВЕНЦИЕЙ** `prompts/<пара>/<роль>.md` из `Book.LangPair()`+роли, **fail-loud на пару без пакета** (ja-книга не едет молча через zh-промпт), осознанное исключение — `prompt_override` на стадии | `models.go`, `pipeline.go`, `pair.go` |
| `internal/text` | Нормализация и рун-примитивы, общие для банка/майнера/чекеров (script-agnostic слой, `lang` НЕ импортирует): `NormalizeSourceKey`/`NormalizeTargetForm` (версионированный артефакт `NormVersion()`, фолдится в снапшот), `NormalizeSource`, `TokenizeScript` (алфавит цели из данных, Mn-фолд U+0301; `TokenizeCyrillic` = ru-дефолт-обёртка), `DenseScript` — единый дом sizing-таксономии (D39.64) | `norm.go`, `runes.go`, `source.go` |
| `internal/seed` | Схема сид-YAML книги (`File`/`Term`/`Decl`/`Alias`) — общий артефакт: майнер её ПИШЕТ (mined-дельта), банк ЧИТАЕТ (`LoadGlossarySeed`); тип общий, поэтому эмиссия не может разъехаться с загрузчиком | `seed.go` |
@ -33,7 +33,7 @@
5. **Эскалация:** ровно 1 хоп, результат РЕ-гейтится, retry-бюджет не сбрасывается, editor pinned, канал adult — только permissive (fail-closed).
6. **Детерминизм:** рендер — чистая функция snapshot; сортировки перед записью; никакого map-order в выводе; length-prefixed хэши; сорс-чанкер lossless (fuzz).
7. **Нейтральность адаптера:** `internal/llm` не знает про перевод — контент-вердикты в раннере (disposition-слой, post-settle).
8. **Экспорт-контракт (D39 слой 6, D39.5):** `checks.ExportNormalize` — эфемерная проекция (НЕ входит в request_hash/чекпоинты/деньги), применяется к КАЖДОМУ финальному тексту на всех выдающих поверхностях; **любая внешняя экстракция (полигон!) обязана читать `tmctl export`**, не сырые чекпоинты store — иначе видит ненормализованные байты и молча-неполную книгу (export делает manifest-join + drift-гард, сырое чтение — нет).
8. **Экспорт-контракт (D39 слой 6, D39.5):** `checks.ExportNormalize` — эфемерная проекция (НЕ входит в request_hash/чекпоинты/деньги), применяется к КАЖДОМУ финальному тексту на всех выдающих поверхностях; **любая внешняя экстракция (полигон!) обязана читать `tmctl export`**, не сырые чекпоинты store — иначе видит ненормализованные байты и молча-неполную книгу.
## Подготовка ja-книги (чек-лист сида)
@ -44,9 +44,9 @@
## Как гонять
Стенд: WSL2 (localhost из-под прокси = 403 — для local-вызовов no-proxy транспорт). Книга-стенд: `/home/ubuntu/books/gu-zhenren/` (GB18030; тексты и производные ВНЕ git; парити-тесты майнера читают отсюда под `TM_MINER_PARITY=1`).
Стенд: WSL2 (localhost из-под прокси = 403 — для local-вызовов no-proxy транспорт). Книга-стенд: `/home/ubuntu/books/gu-zhenren/` (GB18030; тексты и производные ВНЕ git).
Батарея — ОДНА команда, `make` из `backend/` (Makefile; собирать её руками больше не нужно):
Батарея — ОДНА команда, `make` из `backend/` (Makefile):
```bash
make battery # build · vet (оба тег-набора, + архитектурные анализаторы) · gofmt · lint · test -race
@ -65,7 +65,7 @@ go run ./cmd/tmctl status --config example/book.yaml --json # $0, N/M+пасп
go run ./cmd/tmctl report --config example/book.yaml # $0, quality-report (KPI/rates)
go run ./cmd/tmctl export --config example/book.yaml # $0, экспорт-JSON для полигона (--plaintext для человека)
go run ./cmd/tmctl manifest --config example/book.yaml # $0, пере-строить персист манифеста глав/чанков (--json — сам документ)
go run ./cmd/tmctl migrate --config example/book.yaml # $0, довести схему проекта до головы бинаря (деплой-шаг: стоп прогонов → НОВЫЙ бинарь на место → migrate ИМ по каждой книге без открытых попыток → прогоны в работу)
go run ./cmd/tmctl migrate --config example/book.yaml # $0, довести схему проекта до головы бинаря (деплой-порядок — D39.134 §3)
go run ./cmd/tmctl translate --config example/book.yaml --ceiling-usd 0.5 # ДЕНЕЖНЫЙ потолок ТОЛЬКО на этот прогон, book.yaml не пишется
go run ./cmd/tmctl translate --config example/book.yaml --max-units 10 # ОБЪЁМНЫЙ потолок: оплатить не больше 10 выходных юнитов; стоп = завершение (exit 0)
# live-conformance (реальные провайдеры, платно, вне CI):
@ -79,15 +79,15 @@ set -a; . ./.env; set +a; TM_LIVE=1 go test -tags live -run TestLive -v ./intern
| `TestMinerFullBookParity` — единственная сверка Go-порта против Python-эталона на полной книге | `TM_MINER_PARITY=1` | jieba-словарь по внутрирепозиторному ПУТИ `eval/exp16/data/`, но ВНЕ git (`eval/.gitignore:4`) — клон его не получает, регенерируется из jieba 0.42.1 (SHA в `experiments/16-bank-mining.md:183`); плюс `~/books/gu-zhenren/{rerun/records.json, guzhenren-seed-v2.yaml}` |
| `TestCheckerLabelsBaseline`, `TestCheckerLabelsCandidates`, `TestK6LabelsBaseline` — единственный замер precision/recall $0-гейтов | `TM_CHECKER_LABELS=1` | `~/books/gu-zhenren/labels/` |
Флаг переводит отсутствие данных из тихого скипа в громкий `Fatal` — то есть «я это гонял» становится проверяемым. Пути дефолтятся ОТ `$HOME` и от корня репозитория (не от `/home/ubuntu`), так что клон под любым пользователем и по любому пути находит то, что есть; точечные переопределения — `TM_CHECKER_LABELS_DIR`, `TM_MINER_PARITY_{CONTRAST,RECORDS,SEED}`.
Пути дефолтятся ОТ `$HOME` и от корня репозитория (не от `/home/ubuntu`), так что клон под любым пользователем и по любому пути находит то, что есть; точечные переопределения — `TM_CHECKER_LABELS_DIR`, `TM_MINER_PARITY_{CONTRAST,RECORDS,SEED}`.
**Golden-гард детерминизма** (`golden_test.go` + `testdata/golden/`): пинит бит-в-бит snapshotID, request_hash, wire-тела, вердикты и resume-байты. Красный golden = wire/вердикты изменились = `--resnapshot` = переоплата. Обновлять ТОЛЬКО на ратифицированной смене поведения: `TM_UPDATE_GOLDEN=1 go test ./internal/pipeline/ -run TestGolden`; при re-capture — **маскированный структурный дифф** (хеши/версии → плейсхолдер) обязан быть пустым, если смена версий не меняла вердиктов.
**Golden-гард детерминизма** (`golden_test.go` + `testdata/golden/`): пинит бит-в-бит snapshotID, request_hash, wire-тела, вердикты и resume-байты. Обновлять ТОЛЬКО на ратифицированной смене поведения: `TM_UPDATE_GOLDEN=1 go test ./internal/pipeline/ -run TestGolden`; при re-capture — **маскированный структурный дифф** (хеши/версии → плейсхолдер) обязан быть пустым, если смена версий не меняла вердиктов.
## Известный техдолг (не трогать молча; ЕДИНЫЙ трекер — таблица «Бэклог» в `docs/PROGRESS.md`, номера строк = она)
- **D15.2:** READ-половина `tmctl export` построена (D39.5); annotation/override-половина и content-addressed resume отложены (строка 49). F3 at-most-once — после D15.2 (строка 50).
- **`Retries` НЕ фолдится в снапшот** (pre-existing, D39.4-LOW; сознательно так же для ручки `RegenerateEchoBeforeEscalate`, D39.64 — дефолт 0 = текущее поведение): в крэш-окне пониженный ретрай-бюджет молча бросает оплаченные OK-чекпоинты.
- ⚠ ALTER-шаги миграций v8+ НЕ идемпотентны вопреки шапке `migrate.go` — полу-применённая БД не сходится (строка 49а).
- Открытый остаток строки 79 — граница ПЛОТНОСТИ письма Go-константой `cjkScriptNames` (`internal/lang/script.go`); Cyrillic-хардкоды словесной границы DC-чекеров ПОГАШЕНЫ — она берётся из данных цели (`isTargetWordLetter`/`c.wordScript`, D39.39/D39.78).
- Открытый остаток строки 79 — граница ПЛОТНОСТИ письма Go-константой `cjkScriptNames` (`internal/lang/script.go`).
- `prompts/zh-ru/editor-mono.md` — DORMANT-референс D13.1 (не боевой, тестом закреплён); боевой = `prompts/zh-ru/editor.md` v3-discourse (БИЛИНГВ). Глосс-капы санитайзера (6 CJK / 24 лат-токен) — tunable-эвристики (D39.5). Флоры min_max_tokens = схема (D24.3).
- Хвост LOW/NOTE-находок адверсариала D39.4 — в ledger-очереди (строка 53).

View file

@ -186,7 +186,7 @@ about work»*. Асимметрия уже узаконена, и аргумен
Совершив необратимое — потратив деньги, записав файл, удалив файл, — движок обязан **сначала** выдать
отчёт об этом и **только потом** сообщать об ошибке, случившейся позже на том же пути. Ошибка ПОСЛЕ
необратимого действия не смеет ехать маршрутом, который отчёт подавляет.
*Экземпляры: A3а · A2.*
*Экземпляры: A3а.*
### Ст. 3. Расширение каналов — ЧЕТЫРЕ класса, и они РАЗНОЙ цены
@ -207,7 +207,7 @@ number up; a class this build has never heard of still lands inside the band and
фаза идёт после оплаченной черновой волны.
⛔ **ОГОВОРКА К ДЕШЕВИЗНЕ ПОЛОСЫ — она из находки A15 (§6), и без неё закон разрешает удешевлённо делать
то, что сам же называет опасным.** Противоречие поймал оркестратор №21 при чтении дизайна; формулировка моя.
то, что сам же называет опасным.**
Довод, которым полоса удешевлена, — «незнакомый номер поглощается ЧЛЕНСТВОМ и читается `refused`, а не
`failed`». Это верно **по оси НОМЕРА и только по ней.** A15 показала, что разошёлся не номер, а **ОБЕЩАНИЕ**
@ -253,18 +253,15 @@ number up; a class this build has never heard of still lands inside the band and
* ⚠ **И собственная ловушка среза, замеренная мной на живых деньгах:** `ok=0 AND cost_usd>0` **НЕ ЕСТЬ
«оплачено впустую»**. На `coldrun-v16` в этих 11 строках ($0.12316089) лежат три РАЗНЫЕ вещи:
`degraded=cjk_artifact` у роли **classifier** — 3 строки, $0.00918827, **мис-вердикт**, вызовы удались.
⚠ Это утверждение я пере-обосновала КОДОМ, а не строкой бэклога 105 (адверсариальный проход показал,
что предпосылка строки — «`classify_types` выключен во всех шиппинг-конфигах» — для ЭТОГО прогона
ложна: он его включал). Механизм прямой: `internal/pipeline/chunkrun.go:40`=`SourceEchoExpected: role == roleTerminologist`
⚠ Обоснование — КОДОМ, а не строкой бэклога 105: её предпосылка «`classify_types` выключен во всех
шиппинг-конфигах» для ЭТОГО прогона ЛОЖНА — он его включал. Механизм прямой: `internal/pipeline/chunkrun.go:40`=`SourceEchoExpected: role == roleTerminologist`
— исключение из эхо-правила выдано ТОЛЬКО терминологу, хотя формат ответа классификатора — та же
двуязычная таблица терминов, а докстринг самого поля объясняет, чем это кончается: *«Left on, every
healthy call of that role lands in request_log as ok=0/degraded=cjk_artifact — poisoning the one signal
that says a provider is misbehaving»*.
**И ПРЯМАЯ улика на сами эти три строки, а не только форма кода** — их ответы лежат в
`checkpoints.response_text` и читаются read-only: три ответа `finish=stop`, длиной 163/101/150 знаков,
и содержимое — здоровая таблица `<терм>\t<тип>`:
«三转蛊师\tterm / 丙等\ttitle / 元海\tterm / … / 方源\tname». Доля CJK в ней высока ПО СПЕЦИФИКАЦИИ формата.
⇒ вызовы удались, вердикт ложен. ⛔ **Чинить это здесь НЕЛЬЗЯ:** правка применимости эхо-правила двигает
**И ПРЯМАЯ улика на сами эти три строки, а не только форма кода:** их ответы лежат в
`checkpoints.response_text`, читаются read-only и здоровы — разбор с числами и содержимым
`MONEY_HONESTY_REPORT.md` §5.4. ⇒ вызовы удались, вердикт ложен. ⛔ **Чинить это здесь НЕЛЬЗЯ:** правка применимости эхо-правила двигает
`classifierVersion`, а он — поле снапшота (`snapshot.go`, греп `ClassifierVersion versions the intrinsic`)
⇒ ярус B (и ровно это записано ценой в строке бэклога 105); `degraded=sanitizer_stripped` у редактора — 1 строка, $0.01865424,
текст **ОТГРУЖЕН**; и лишь $0.09531838 действительно выброшено. **Наивная поверхность назвала бы оператору
@ -405,8 +402,7 @@ number up; a class this build has never heard of still lands inside the band and
**Что поднимать НЕЛЬЗЯ:** расхождение в свёртке банка между `status` и `export` объявлено
СОЗНАТЕЛЬНЫМ (`export.go`, греп `symmetry between two surfaces that answer different questions`).
Общим делается ТОЛЬКО правило осиротевшей стадии.
* ⛔ **ЛОВУШКА, из-за которой наивная правка A7 СОЗДАЁТ новый экземпляр §2.2** (снята адверсариальным
проходом, проверена мной по коду): `projectRebill` пропускает ровно те строки, на которые сработает
* ⛔ **ЛОВУШКА, из-за которой наивная правка A7 СОЗДАЁТ новый экземпляр §2.2:** `projectRebill` пропускает ровно те строки, на которые сработает
новое правило — `default: continue // a stage the current pipeline does not run is never re-billed`
(`internal/pipeline/rebill.go`). ⇒ поставив `config_drift=true`, поверхность оставит `rebill_units=0`, а
человеческий рендер статуса превращает этот флаг в денежное утверждение о пере-оплате, которой не
@ -422,7 +418,7 @@ number up; a class this build has never heard of still lands inside the band and
* **Нарушено:** §2.3 — метка есть базис без гарантии, и её равенство читается как равенство байтов.
* **Потребитель:** оператор/замер (сравнимость прогонов). Не платформа, не деньги (деньги защищены
`PromptSHA256` в снапшоте).
* ⚠ **И решающий довод, которого у меня в первой редакции не было: НОРМА УЖЕ ОБЪЯВЛЕНА И УЖЕ НАРУШЕНА.**
* ⚠ **И решающий довод: НОРМА УЖЕ ОБЪЯВЛЕНА И УЖЕ НАРУШЕНА.**
`backend/configs/pipeline-c2.yaml:56` дословно: «**лейбл обязан следовать за новым SHA файла**». Именно
это и произошло 01.08 с `prompts/zh-ru/editor.md` при неизменном `v3-discourse-reflow`. ⇒ гейт здесь —
не новый закон, а МАШИНА под уже написанным правилом, которое проза удержать не смогла. Планка
@ -453,14 +449,11 @@ number up; a class this build has never heard of still lands inside the band and
`projected_book_usd`.
* **Класс правки:** новое число (сессия вправе). ⚠ Замер этого прогона: терминологический контур —
$0.04980482 из $0.43610966 = **11.42% захода**, и ни цента из этого в проекцию не входило.
* ⛔ **ОСЬ, НАЗВАННАЯ В СТРОКЕ 194, ОПРОВЕРГНУТА ЖИВЫМИ ДАННЫМИ — и это самое важное здесь.** Строка
говорит: «на сдвиге снапшота они реально пере-покупаются». Пере-покупка наступает НЕ от сдвига
снапшота. Разложение прогона по покупкам (`request_log`, `read-only`):
П1 `f6bb59d4` — терминология $0.02056340 (классификатор 2 батча + терминолог 2 батча, всё свежее);
П2 `ce5990b3`**$0.02924141, ВСЕ батчи свежие** (`tm_hit=0`), при том что черновой снапшот не
двигался; П3 `a0958e53`**$0.00, ВСЕ батчи попали в чекпоинты** (`tm_hit=1`), при том что снапшот
как раз СДВИНУЛСЯ (в П3 сработал снапшот-гард). ⇒ ось пере-покупки — **БАЙТЫ БАТЧА**: батчи
пере-собираются, когда растёт множество кандидатов, а оно растёт от каждой новой дочерновленной главы.
* ⛔ **ОСЬ, НАЗВАННАЯ В СТРОКЕ 194 («на сдвиге снапшота они реально пере-покупаются»), ОПРОВЕРГНУТА
ЖИВЫМИ ДАННЫМИ — и это самое важное здесь.** Ось пере-покупки — **БАЙТЫ БАТЧА**: батчи пере-собираются,
когда растёт множество кандидатов, а оно растёт от каждой новой дочерновленной главы; на оси
терминологии снапшот за весь прогон не двигался ни разу. Разложение покупок П1/П2/П3 и прямая улика из
таблицы `jobs``MONEY_HONESTY_REPORT.md` §5.1 (опровержение ратифицировано D39.181 §3).
Чинить по названной в строке оси значит чинить не то.
* ⇒ **A10 и A9 — ОДИН дефект с двух сторон**, и §5 A9 ниже несёт его вторую половину.
@ -626,7 +619,7 @@ json-тегов его типа, и рефлексия читает его бе
исключений по образцу существующего `exempt` (по импорт-пути + базовому имени) и **последним по
очереди**: сначала Г1Г3, он дороже и слабее.
### ⛔ ГЛАВНОЕ ОГРАНИЧЕНИЕ ГЕЙТА, и первая редакция его не назвала
### ⛔ ГЛАВНОЕ ОГРАНИЧЕНИЕ ГЕЙТА
**Гейты покрывают НЕ ТЕ каналы, в которые закон маршрутизирует большинство корпуса** (сверка §5 против
§7): шесть строк из десяти закон отправляет в
@ -641,9 +634,9 @@ A3(а) → отчёт + WARN, A3(б) → строка сборки, A4 → те
Статус на 31.08: **Г1 не построен · Г2 не построен · Г3 построен точечно (A4, по снапшотной оси) ·
репо-гейт A8 построен · Г4 не построен.**
**машинная половина закона достаёт до четырёх строк из десяти.** Это не довод против закона — статьи
§2.1§2.5 остаются ревью-вопросами и без машины, — но это ЧЕСТНАЯ цена, и приёмка обязана её знать:
**закон, поданный как «спроектирован с гейтом», сегодня загейчен на 40%.** Дозакрыть прозаические каналы
**машинная половина закона достаёт до четырёх строк из десяти — закон загейчен на 40%.** Это не довод
против закона: статьи §2.1§2.5 остаются ревью-вопросами и без машины, — но это ЧЕСТНАЯ цена, и приёмка
обязана её знать. Дозакрыть прозаические каналы
можно только гейтом на ТЕКСТ (посадка на сообщение с проверкой красноты при подмене строки, D39.171), и
это отдельная работа, которую я НЕ проектировала.
@ -660,13 +653,9 @@ A3(а) → отчёт + WARN, A3(б) → строка сборки, A4 → те
## 8. Что закон ТРЕБОВАЛ на ратификацию — и что с этим стало
**ВСЕ ТРИ РАТИФИЦИРОВАНЫ 31.08 (D39.181) И ПОСТРОЕНЫ.** Список оставлен как он уехал, с исходом каждого:
1. **A5 — поля леджера доставки в кадре `finished`** → ратифицировано, построено. Признак едет ЧИСЛАМИ, и
присутствие объекта `volume` и есть признак; ни нового кода выхода, ни нового значения `Outcome`.
2. **A7/A12 — `config_drift_basis` в `status --json`** → ратифицировано, построено на ОБЕИХ поверхностях,
и `build` его слушает.
3. **Сам закон** → норма зоны.
**A10 оркестратор отнёс к пункту 1**, а не к новому полю: это исправление ДЕРИВАЦИИ существующей фигуры,
**Все три пункта ратифицированы 31.08 (D39.181) и построены** — перечень в шапке файла, разбор каждого
экземпляра — `MONEY_HONESTY_REPORT.md` §9.
**A10 оркестратор НЕ отнёс к классу «новое поле»**: это исправление ДЕРИВАЦИИ существующей фигуры,
что закон и предписывает. Условие — «ни одна фигура, которую читает платформа, не смеет сменить смысл» —
выполнено и предъявлено тестом (`committed_usd` остаётся `SUM(checkpoints)` до микро-доллара).
@ -674,6 +663,5 @@ A3(а) → отчёт + WARN, A3(б) → строка сборки, A4 → те
* **Не проверяла, согласится ли платформа** декодировать предложенные поля — чужая зона, вопрос уехал
пингом вместе с дизайном.
* **A13 и A14 помечены PLAUSIBLE**: они выведены из кода и комментариев, живьём не воспроизводились.
* Реестр Г1 оценён по составу документов, но **не написан**; цена «одна запись на поле» — оценка, а не
замер.

View file

@ -4,6 +4,15 @@
(оркестратор №21, 31.08). Написана **ДО первой правки** дерева; ниже — состояние на момент написания,
она не переписывается задним числом (дополнения помечаются датой и словом «дописано»).
> ⚠ **Дописано 02.09 — указатели и исходы; тело записки НЕ переписывается (это пре-регистрация).**
> Промт переехал: `docs/archive/prompts/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md`. Прожитый дамп пака из
> секции «Бэкенд» `docs/PROGRESS.md` снят 02.09 — исходы живут в `MONEY_HONESTY_REPORT.md`.
> Пак ЗАЛЕНДЖЕН `bb541a8` и принят **D39.182**; закон раскрытия ратифицирован **D39.181**.
> ⚠ Два места ниже опровергнуты собственной же работой пака, и оба разобраны в отчёте:
> «22.6%» (§4) — ответ на ДРУГОЙ вопрос, величина завышения = **29.2%** (отчёт §5.4); пред-регистрированная
> посадка A9 (§2, «воспроизведение перерасхода фикстурой + посадка») НЕ написана — долг назван прямо
> в отчёте §8 п.1.
**Личность старта.** HEAD `9d27c0b`, дерево ЧИСТО (`git status --short` → пусто).
Go-модуль `backend/`. Пак **$0**: платных вызовов не содержит и не планирует.

View file

@ -131,6 +131,12 @@ A0, и свёртку трогать не нужно. Свёртку я не т
| `TestShippingPipelinesRegenerateEchoBeforeEscalating` | снять ключ из `pipeline-arm-glm.yaml` | `--- FAIL … pipeline-arm-glm.yaml: … = 0, want 1` |
| `TestEchoRegenFiresONLYForEcho` | снять проверку ПРИЧИНЫ, оставив `attempt < echoRegen` | `--- FAIL … primary calls = 2, want 1` |
⚠ **Граница, которую §6 промта требует различать, а одна строка «проверены краснотой» стирает:**
буквально «красная на состоянии ДО правки» здесь ОДНА — `TestShippingPipelinesRegenerateEchoBeforeEscalating`
(правка A1 есть ДАННЫЕ, и он краснеет на конфигах HEAD пятью строками, по одной на файл). Остальные три
на состоянии ДО правки ЗЕЛЁНЫЕ: они держат МЕХАНИЗМ, которым правка пользуется, и краснеют на мутациях
кода. Это не подгонка, а разные предметы — но отчёт обязан их различать.
**Ручка была БЕЗ ЕДИНОГО ТЕСТА** до этого пака (`grep -rn RegenerateEchoBeforeEscalate --include=*_test.go` → пусто).
Включать в шиппинг ручку на денежном пути, которую держит только чтение кода, я не стала.
@ -293,6 +299,27 @@ WARN'а, процитированного промтом. Следующий б
* **Ярус B** — не тронут ничем: эффорт редактора, потолок вывода, свёртка ре-генов, метка-из-хеша.
* **`platform/`** — только чтение. Найденное там (A15) уехало пингом, файл не тронут.
## 6.2 Адверсариальный проход по СВОЕЙ ГОТОВОЙ работе (промт §7.5/§7.8) — обязательный артефакт
**Два веера, 29 агентов.** Веер 1 — грунтовка корпуса (22: два картографа каналов + десять читателей по
строке корпуса + десять адверсариальных верификаторов ПОВЕРХ отчёта каждого читателя; вход — код обеих
зон read-only, БД прогона `?mode=ro`, текст промта КАК ПРИОР). Веер 2 — проход по ГОТОВОЙ работе (7:
шесть линз — посадки · каждое число · закон против своего корпуса · код-дифф · заказ против выдачи ·
решение по A9 — плюс седьмая, механическая сверка с заказом по трёхисходной шкале §6, то есть
интервальная сверка субагенту с явными критериями). Мандат дословно: «найди, где она выдаёт
непроверенное за проверенное». Линзе посадок было велено мутировать КОПИЮ дерева в `/tmp`; реальное
дерево агенты не трогали (проверено ими же по md5).
**Вердикты шести линз:** 2 × `MATERIALLY_WRONG` (закон против корпуса; код-дифф), 4 ×
`SOUND_WITH_CORRECTIONS`; линз, не нашедших ничего, НОЛЬ. **25 находок, из них 7 блокеров** — они
переписали §3.3, добавили три посадки, которых без них не было (`TestAUnitWhoseEDITFlaggedIsStillNewBook`,
`TestEchoRegenFiresONLYForEcho`, `TestDeliveryIsReadFromTheSHIPPINGWaveOnADraftOnlyPipeline`) и сняли
мой же довод §5.3 п.3 как неверный.
Диспозиции вплетены в §3§9 по месту.
**Отступление, которое называю сама: модель агентам я ЯВНО НЕ ЗАДАВАЛА** — они унаследовали модель
сессии (Opus 5), тогда как промт §7 велит задавать её ИМЕНЕМ.
---
## 7. ПИНГИ оркестратору №21 — доставлены; открытыми остались ДВА
@ -418,7 +445,7 @@ consolidated» — потому что `unanswered` в одиночку чита
а не `null`).
⚠ Новое поле кадра бампает МИНОР потока: `StreamVersion` = `1.2` (`internal/runevents/runevents.go`) —
правило записано в комментарии над самой константой, и первая редакция правки его нарушила. Платформенное
правило записано в комментарии над самой константой. Платформенное
зеркало не тронуто: мажор совпадает.
### 9.6 A6 — деньги слепы к правке исходника на месте
@ -494,11 +521,8 @@ was taken`). Посадка `TestTheContourDoesNotWeakenTheConsentGate`: мут
Я не стала печатать старую фразу рядом с новой ради совпадения грепа: это было бы вводить в заблуждение
ради зелени, то есть нарушать закон, который я же пишу. Предпосылка переведена с подстроки на ФАКТ
(`lastTerminology.BatchesDropped == 0`) — строго сильнее и ровно то, что предписывает D39.171.
Оркестратор правку ОСТАВИЛ, проверив дифф сам (удалены ровно две строки предпосылки, утверждение стоит
контекстной строкой): запрет канона бьёт по УТВЕРЖДЕНИЮ теста, а сменён был МЕХАНИЗМ ПРЕДПОСЫЛКИ — с
механизма, который проект ратифицированно считает дефектным (D39.171), на структурный факт. ⚠ Общей нормы
«правка предпосылки ≠ правка утверждения» он НЕ устанавливает — она унесена владельцу вопросом (D39.182
§2). ⚠ Порядок был нарушен: правка, потом пинг, а правило говорит «пинг, НЕ правка».
Оркестратор правку ОСТАВИЛ, проверив дифф сам; условия и разбор всех трёх касаний — **D39.182 §2**.
⚠ Порядок был нарушен: правка, потом пинг, а правило говорит «пинг, НЕ правка».
⚠ **ВТОРАЯ ПОЛОВИНА ТОГО ЖЕ ТЕСТА ОСТАВЛЕНА НА ТОЙ ЖЕ БОЛЕЗНИ — СОЗНАТЕЛЬНО, и это строка для следующего.**
Сохранённое утверждение `strings.Contains(out, "EMPTY completion")` стоит на ТОМ ЖЕ механизме, который я
@ -542,8 +566,5 @@ D39.182 §2): ПРЕДПОСЫЛКА `bankfixpack_test.go` (§9.10) · УТВЕ
## 11. Состояние на закрытии пака
Обе фазы отработаны. Закон ратифицирован (D39.181) и применён к десяти экземплярам; корпус вырос до
четырнадцати, и четыре лишних нашла мерка, а не заказ. Тридцать три посадки, каждая красная под своей
мутацией; батарея зелёная с кодом, снятым с самого `make`, и тремя названными скипами. Открытое оставлено
НАЗВАННЫМ, а не закрытым: долг по посадке A9 (§8 п.1) и вторая половина чужого теста на дефектном
механизме (§9.10). Пак принят и заленджен 31.08 — `bb541a8`, нота D39.182.
Обе фазы отработаны, пак принят и заленджен (§0, шапка). **Открытое оставлено НАЗВАННЫМ, а не
закрытым:** долг по посадке A9 (§8 п.1) и вторая половина чужого теста на дефектном механизме (§9.10).

View file

@ -190,19 +190,17 @@ file or directory` — та же причина, по которой красе
«пак улучшает»**: на таком числе событий это шум (Фишер p≈0.5); решающее и достаточное — что флейк
живёт на дереве, которого пак не касался. Окончательная атрибуция — 5-раундовый A/B в §31.
## 11. Три правила протокола, вынесенные инцидентами сессии
## 11. Два правила протокола, вынесенные инцидентами сессии
1. **Харнесс мутаций обязан фингерпринтить цели.** Прогон `mutate.py` был убит таймаутом инструмента,
`finally` не отработал, и посадка M4 (снятый гейт байтового no-op) просидела в
`internal/pipeline/bankdecisions.go` ~40 минут, выглядя продакшн-кодом; сторонний ревьюер снял с
этого дерева замеры и доложил «дефект гейта идемпотентности». Поймал посадку пин
`TestBankApplyRepeatIsAByteNoOp` на первом прогоне после. Харнесс починен (sha256 целей до и после).
Пин усилен по следу: повтор проверяется над файлом с РУЧНЫМ комментарием в шапке — это то, что
пере-рендер уничтожает и чего не заметила бы проверка содержимого — плюс что `changed:false` не врёт.
2. **Снимай отпечаток дерева ПЕРЕД сборкой бинаря замера.** Симметричная половина того же класса:
ревьюер собрал `tmctl` из незакоммиченного дерева, не сняв отпечаток, и инструмент унаследовал
посадку. За сессию на это наступили обе стороны.
3. **Одиночный прогон — не замер.** Атрибуцию флейков я сперва вывел из одного прогона («зелено в
1. **Эталон самопроверки снимается ДО первого изменения, и харнесс мутаций обязан фингерпринтить цели.**
Незавершённый прогон `mutate.py` оставил посадку M4 в дереве ~40 минут, выглядящей продакшн-кодом, и
сторонний ревьюер снял с этого дерева замеры (он же собрал `tmctl` из незакоммиченного дерева, не
сняв отпечаток, — на класс наступили обе стороны). Посадку поймал и правило теперь держит пин
`TestBankApplyRepeatIsAByteNoOp`: он же документирует и форму инцидента, и довод (инод плюс
рукописный комментарий в шапке — то, что пере-рендер уничтожает, а проверка содержимого не заметила
бы). ⚠ Класс повторился следующей сессией ВНУТРИ самого харнесса — §25; отпечаток целей и каталога у
инструмента сегодня свой — §16.
2. **Одиночный прогон — не замер.** Атрибуцию флейков я сперва вывел из одного прогона («зелено в
изоляции ⇒ нагрузка»), затем другой одиночный прогон дал ровно обратное. Решает только чередующийся
A/B (§10 п.7).
@ -227,10 +225,8 @@ file or directory` — та же причина, по которой красе
## 13. Дифф `^func Test` — исполнением
**Удалено: НОЛЬ.** Добавлено 59 тест-функций в шести НОВЫХ файлах + `TestTheRefusalTableIsTotal` в
существующем `cmd/tmctl/exitcontract_test.go` + 2 фаззера — снято командами
`git diff -U0 -- backend | grep -E "^[+-]func Test"` и `git ls-files --others --exclude-standard --
backend | grep '_test.go$' | xargs grep -h '^func Test' | wc -l`.
**Удалено: НОЛЬ** — сводный счёт по трём пакам и обе команды в §40. Новых тест-функций пака шесть файлов
несут в себе, плюс `TestTheRefusalTableIsTotal` в существующем `cmd/tmctl/exitcontract_test.go`.
**Тронуто в существующих файлах — шесть, ни у одной сигнатура не изменилась:**
- `exitcontract_test.go` — в список полосы добавлены `exitSchemaMismatch` и `exitDecisionsRejected`
@ -248,8 +244,7 @@ backend | grep '_test.go$' | xargs grep -h '^func Test' | wc -l`.
# Дофикс той же двери (сессия 25.08.2026)
Промт (16 пунктов §4) архивирован — `docs/archive/prompts/BACKEND_SEAM_FIX_SESSION_PROMPT_2026-08-27.md`;
записка-план — `SEAM_FIX_PLAN.md`. Секции 013 выше — улика приёмки, не тронуты. **Каждое число ниже — с
командой, которой получено;** команды из `backend/`.
записка-план — `SEAM_FIX_PLAN.md`. Секции 013 выше — улика приёмки, не тронуты.
## 14. Батареи
@ -566,12 +561,12 @@ YAML не накладывает ничего. Новый гоняет прои
## 25. Правило протокола, вынесенное инцидентом ЭТОЙ сессии
**Инструмент, который сторожит от подсадки, сам подсадил.** Первая версия `tmmutate`
читала-правила-писала файл ОДИН РАЗ НА ПРАВКУ. У мутации B две правки в ОДНОМ файле — и «нетронутая»
копия второй правки была снята с уже пропатченного файла; восстановление вернуло пропатченное содержимое,
а sha256-самопроверка сравнила его с пропатченным же хешем и **прошла**. Дерево копии осталось
мутированным, и **два полных раунда измерений после этого шли по мутированному коду**: несколько пинов
отчитались как поймавшие мутации, к которым не имели отношения. Вскрыто ровно тем, чему учит §11 п.3:
один подозрительный «RED», пере-проверенный изолированным прогоном, не воспроизвёлся.
читала-правила-писала файл ОДИН РАЗ НА ПРАВКУ: у мутации B две правки в ОДНОМ файле, «нетронутая» копия
второй снималась с уже пропатченного файла, и sha256-самопроверка сравнивала пропатченное с пропатченным
же хешем — **проходила**. ⚠ Цена: дерево копии осталось мутированным, и **два полных раунда измерений
после этого шли по мутированному коду** — несколько пинов отчитались как поймавшие мутации, к которым не
имели отношения. Вскрыто ровно тем, чему учит §11 п.2: один подозрительный «RED», пере-проверенный
изолированным прогоном, не воспроизвёлся.
**Починено дважды:** одно чтение и одна запись на файл (порядок объявлен контрактом в комментарии), плюс
второй независимый рубеж — отпечаток ВСЕХ файлов каталога, снятый на заведомо чистом дереве и
@ -611,9 +606,8 @@ YAML не накладывает ничего. Новый гоняет прои
## 27. Дифф `^func Test` — исполнением
**Удалено: НОЛЬ** — `git diff -U0 -- backend | grep -cE "^-func (Test|Fuzz)"` → 0, и это проверено
дважды: грепом по диффу и поимённо по семнадцати пинам, которые отчёт пака называет по имени (каждый
существует ровно один раз). Дофикс добавил 38 тест-функций и 1 фаззер поверх 59 + 2 фаззера пака.
**Удалено: НОЛЬ** — сводный счёт и обе команды в §40; поимённая половина проверки прошла по семнадцати
пинам, которые отчёт пака называет по имени (каждый существует ровно один раз).
**Тронуто (тело изменено, сигнатура — нет), и вот почему это законно:**
- `cmd/tmctl/exitcontract_test.go``TestTheRefusalTableIsTotal` перестал держать ручной список и
@ -630,7 +624,8 @@ YAML не накладывает ничего. Новый гоняет прои
## 28. Состав дофикса
**Новое:** `internal/seed/normalize.go` (канонический вид документа) · `internal/membank/problems.go`
(вердикт загрузчика по субъектам) · `cmd/tmmutate/` (харнесс мутаций + каталог посадок, `python3 -c "import json;print(len(json.load(open('cmd/tmmutate/mutations.json'))))"`**36**) ·
(вердикт загрузчика по субъектам) · `cmd/tmmutate/` (харнесс мутаций + каталог посадок; счёт записей на
дату дофикса и команда актуального счёта — §16) ·
`internal/pipeline/bankchain_test.go`.
**Тронуто:** `internal/membank/decisions.go` (доказывающий рендер, нормализация запроса, набор-осознанный
алиас, перевёрнутое окно, байты результата, `note` не стирается) · `memseed.go`, `memvoice.go` (вердикт
@ -644,11 +639,9 @@ YAML не накладывает ничего. Новый гоняет прои
## 29. Ревью старшей моделью: три дефекта, каждый воспроизведён МНОЙ и починен
Ревью запрашивалось по семи решениям, где заказ оставлял форму на сессии. Оно согласилось с пятью, не
согласилось с одним и нашло три вещи, которых сессия не видела. **Каждую сессия воспроизвела сама
прежде, чем чинить** — не потому что не доверяет, а потому что чужой результат, взятый на веру, ровно
так же становится нормой. У двух подтверждённых решений ревью дало довод СИЛЬНЕЕ, и довод заменён в
комментариях кода: у конверта путей на манифесте — что бамп версии выбросил бы все сохранённые сайдкары
Ревью запрашивалось по решениям, где заказ оставлял форму на сессии. **Каждую находку сессия
воспроизвела сама прежде, чем чинить** — чужой результат, взятый на веру, ровно так же становится
нормой. У двух подтверждённых решений ревью дало довод СИЛЬНЕЕ, и довод заменён в комментариях кода: у конверта путей на манифесте — что бамп версии выбросил бы все сохранённые сайдкары
и пере-нарезал каждую книгу ради аддитивного поля; у SIGTERM — атомарность и порядок записей вместо
длины непрерываемого участка.
@ -721,9 +714,9 @@ nil, …)` → true и **записала бы файл нулевой длин
## 30. Интервальная самоверификация субагентом: ещё пять дефектов, все воспроизведены и починены
Ревью гонялось В СЕРЕДИНЕ работы против семи ЯВНЫХ критериев, read-only, в копии. Дерево двигалось под
ним трижды, поэтому его вердикты пере-исполнены на финальном снимке. Каждый дефект сессия воспроизвела
своим прогоном прежде, чем чинить; воспроизведение каждого теперь живёт в его пине, а не здесь.
Ревью гонялось В СЕРЕДИНЕ работы против явных критериев, read-only, в копии; дерево под ним двигалось,
поэтому его вердикты пере-исполнены на финальном снимке. Каждый дефект сессия воспроизвела своим
прогоном прежде, чем чинить; воспроизведение каждого теперь живёт в его пине, а не здесь.
### 1. ⛔ Правило маски проглатывало ливлок, который вносил САМ вызов
@ -890,7 +883,10 @@ ApplyBankDecisions → openDecisionRequest · readBookState · decisionReport
**Считать длину функции надо по ТЕЛУ** (от `^func` до первой строки ровно `}`), а не от `^func` до
следующего `^func`: второе правило на методах даёт мусор, и именно им была получена несуществующая
«норма репозитория в 75 строк» — такой нормы в проекте НЕТ.
«норма репозитория в 75 строк» — такой нормы в проекте НЕТ. **Замерено по ТЕЛУ:** две точки входа — **92**
и **90** строк против **42** у следующей по длине функции тех же двух файлов; максимумы пакетов совсем
другие — **218** (`Status`, `internal/pipeline`) и **105** (`loadVoiceSections`, `internal/membank`);
после разбора самая длинная функция обоих файлов — **44** (`signatureState`).
**И вот чем доказательство эквивалентности ОКАЗАЛОСЬ слабее, чем здесь было написано.** Первая
редакция утверждала «весь каталог пере-прогнан, как записано». На деле рефакторинг сломал ДВЕ записи
@ -898,32 +894,26 @@ ApplyBankDecisions → openDecisionRequest · readBookState · decisionReport
провал СБОРКИ красным, то есть две записи не проверяли ничего, а отчёт называл тесты, которые не могли
отработать. Нашло воркфлоу-ревью; обе записи пере-выражены, у инструмента появился третий вердикт — §34.
## 34. Воркфлоу-ревью: шесть осей, 50 находок, 26 переживших скептиков
## 34. Воркфлоу-ревью дофикса: шесть осей, все находки воспроизведены и закрыты
Третье и последнее ревью дофикса — **воркфлоу**, а не одиночный агент: шесть независимых полос читают
дерево параллельно, каждая находка отдаётся отдельному скептику с заданием ЕЁ ОПРОВЕРГНУТЬ, выживает
только то, что он не смог. Полосы выбраны по тому, чего не касались два предыдущих ревью: гонка двух
`bank-apply` · последствия новых полей для платформы (read-only) · эквивалентность только что сделанного
рефакторинга · сам `cmd/tmmutate` (он теперь в репозитории, значит продакшн-код) · честность
хендофф-документов · общность и безопасность данных. **50 сырых находок, 26 пережили скептиков.** Каждая
перепроверена своим прогоном прежде, чем чинить; правки доводов в комментариях и семь правок в самом этом
документе живут по месту, здесь — только то, что осталось фактом.
Третье и последнее ревью дофикса — **воркфлоу**, а не одиночный агент (форма — §45). Полосы выбраны по
тому, чего не касались два предыдущих ревью: гонка двух `bank-apply` · последствия новых полей для
платформы (read-only) · эквивалентность только что сделанного рефакторинга · сам `cmd/tmmutate` (он
теперь в репозитории, значит продакшн-код) · честность хендофф-документов · общность и безопасность
данных. Каждая находка перепроверена своим прогоном прежде, чем чинить; правки доводов в комментариях и
правки в самом этом документе живут по месту, здесь — только то, что осталось фактом.
### ⛔ Главное: две посадки каталога НЕ КОМПИЛИРОВАЛИСЬ — и это внёс мой же рефакторинг
`P-alias-judged-on-the-set` и `AB-alias-escape` патчат тело `refuseInertDeclines`, ссылаясь на `in.Delta`.
Рефакторинг §33 убрал `in` из области видимости этой функции. **Проверено:** прогон всех посадок с
`go build` после каждой — ровно эти две не собираются (`undefined: in`). А инструмент считал их КРАСНЫМИ,
потому что `red` был буквально `terr != nil`, и провал СБОРКИ выглядел так же, как поймавший пин.
`P-alias-judged-on-the-set` и `AB-alias-escape` патчат тело `refuseInertDeclines`, ссылаясь на `in.Delta`,
которое рефакторинг §33 убрал из области видимости, — а инструмент считал их КРАСНЫМИ, потому что `red`
был буквально `terr != nil`, и провал СБОРКИ выглядел так же, как поймавший пин.
**Починено на двух уровнях, и это живое свойство инструмента.**
(1) Обе записи пере-выражены под новую сигнатуру и краснеют на своих пинах поимённо.
(2) У инструмента появился ТРЕТИЙ вердикт. `RED` теперь требует, чтобы хоть один тест назвал себя
(`--- FAIL:`); прогон, в котором не отчитался ни один тест, — это `NOTHING`, и он **роняет гейт**. Плюс
перед первой посадкой каждый пакет прогоняется НЕТРОНУТЫМ и обязан быть зелёным: без базовой линии «этот
пин ловит эту мутацию» опирается на код возврата, который выдают также сборка, флейк, таймаут и опечатка
в имени пакета. **Негативная проба:** посадка, которая заведомо не компилируется, даёт
`NOTHING … 1 unexpected outcome(s)` и exit 1.
**Починено на двух уровнях.** Обе записи пере-выражены под новую сигнатуру и краснеют на своих пинах
поимённо; у инструмента появился ТРЕТИЙ вердикт (`NOTHING` — ни один тест не назвал себя, гейт падает)
плюс обязательная зелёная базовая линия пакета ДО первой посадки. ⚠ Оба механизма и провенанс инцидента
живут в самом инструменте — `cmd/tmmutate/main.go`, комментарии «THE PACKAGE MUST BE GREEN BEFORE
ANYTHING IS PLANTED» и «NOT a red …»; второй копии этот отчёт не держит.
### Продуктовые дефекты — все воспроизведены мной
@ -946,11 +936,11 @@ ApplyBankDecisions → openDecisionRequest · readBookState · decisionReport
## 35. Проверка ЗАКРЫТИЯ — отдельным воркфлоу, и она нашла, что закрыто не всё
На вопрос «все замечания исправлены?» сессия не отвечала по памяти: четыре независимых агента прошли по
всем 26 пережившим находкам §34 и по живому дереву решали КАЖДУЮ — закрыта / не закрыта / частично /
отклонена осознанно. **Из 28 проверенных пунктов закрытыми оказались 16, осознанно отклонёнными 2,
частично 7 и НЕ закрытыми 3.** Все остатки доделаны, и их результат живёт не здесь, а в исправленных по
месту секциях (§15, §16, §18, §26, §27, §31, §33) — второй копии этот отчёт намеренно не держит.
На вопрос «все замечания исправлены?» сессия не отвечала по памяти: независимые агенты прошли по всем
пережившим находкам §34 и по живому дереву решали КАЖДУЮ — закрыта / не закрыта / частично / отклонена
осознанно. Закрыто оказалось не всё; все остатки доделаны, и их результат живёт не здесь, а в
исправленных по месту секциях (§15, §16, §18, §26, §27, §31, §33) — второй копии этот отчёт намеренно не
держит.
**Два остатка были признаны вне полномочий пака, и оба закрылись позже:** межкнижная гонка за один
`mined_delta` — снятием объявляемых ключей (D39.158 п.5; остаток «второй конфиг ТОЙ ЖЕ книги» заведён
@ -958,9 +948,10 @@ ApplyBankDecisions → openDecisionRequest · readBookState · decisionReport
(§43 4.10).
**Вывод, который стоит унести дальше самого пака.** Это было ТРЕТЬЕ ревью подряд, и каждое находило
настоящие дефекты, которых не видели предыдущие: одиночный старший ревьюер — три, интервальное — пять,
воркфлоу — двадцать шесть, проверка закрытия — ещё десять остатков. **Ни одно «все замечания исправлены»
в этой сессии не оказалось верным при проверке.** Вывод не «ревью бесполезны», а обратный: закрытие надо
настоящие дефекты, которых не видели предыдущие: одиночный старший ревьюер — три (§29), интервальное —
пять (§30), воркфлоу — двадцать шесть, проверка закрытия — ещё десять остатков (из 28 её пунктов: 16
закрыто, 2 отклонено осознанно, 7 частично, 3 не закрыто). **Ни одно «все замечания исправлены» в этой
сессии не оказалось верным при проверке.** Вывод не «ревью бесполезны», а обратный: закрытие надо
ПРОВЕРЯТЬ отдельно от починки, и не тем, кто чинил.
---
@ -1067,6 +1058,9 @@ $ git diff -U0 -- . | grep -cE "^-func (Test|Fuzz)" → 0
$ git ls-files --others --exclude-standard -- . | grep '_test.go$' \
| xargs grep -h '^func Test' | wc -l → 117 # было 97: +20 тест-функций (фаззеров по-прежнему 3)
```
**Сводно по трём пакам:** пак оставил 59 тест-функций + 2 фаззера, дофикс добавил 38 + 1, фикс2 — ещё 20;
**удалено за все три пака НОЛЬ**, и это проверено дважды — грепом по диффу и поимённо по пинам, которые
отчёты паков называют по имени.
Переименовано (не удалено): `TestTheStopClearsWhenTheOwnerDecidesTheWholeSignatureMap`
`TestTheChainOwnerDecidesTheWholeMapAndTheRunGoesOn` (замена по заказу §4.12, новая семантика; вторая цепь —
`TestTheStopIsAFlagNotAGateOnDecisions` — и есть пин старой формулы) · `TestDecisionPathsDefaultByConventionOnAnEmptyKey`
@ -1102,12 +1096,15 @@ $ git ls-files --others --exclude-standard -- . | grep '_test.go$' \
- **Гипотеза §4.13 о пере-оплате терминолога при подписании** механизмом не подтверждена и выигрышем не
заявляется: память предъявленного не трогает ни состав кандидатов, ни ключ чекпоинта (хеш
отрендеренных сообщений); `TestTerminologistResumeIsFree` держит $0-резюм в обеих семантиках.
- **Поимённой разметки новизны в карте подписи НЕТ**: оператор видит РАЗМЕР карты, а не число новых
термов (баннер стопа больше не выдаёт одно за другое — `renderSignatureStop`, §45). Пер-термная
новизна — продуктовая поверхность, не этот пак.
## 42. Интервальная самоверификация субагентом (§5.1): четыре находки, все minor
Ревью шло против десяти явных критериев, адверсариально, с исполнением (точечные тесты + посадки в своей
копии). Код по всем десяти сошёлся; находки — класс «довод/покрытие», ровно тот, что предыдущая сессия
назвала своим худшим. Три из них — неверные доводы в докстроках при верном коде (отменённая механика
Ревью шло против явных критериев, адверсариально, с исполнением (точечные тесты + посадки в своей
копии). Код сошёлся по всем; находки — класс «довод/покрытие», ровно тот, что предыдущая сессия назвала
своим худшим. Три из них — неверные доводы в докстроках при верном коде (отменённая механика
«промотируй/отклони, потом re-run» у `renderSignatureStop`; предикат v16 в `migrate.go`, читавшийся как
«любая поверхность вне памяти», тогда как код стреляет только на кластере, чья ВСЯ поверхность вне;
описание стоп-окна в `bankapply.go`), все переписаны по месту. ⚠ Третья правка сама внесла ложь про
@ -1153,11 +1150,10 @@ retired-ключи ловятся полями, а не голым strict-decode
Прогнана по презумпции «каждое заявленное закрытие ложно, пока не увидено исполнением»: все 13 строк §43
и все 4 диспозиции §42 подтверждены в дереве (13 точечных тестовых прогонов + 3 полных пакета — зелёные;
носители по file:line; реверс-пин признан невакуумным с названным механизмом падения). Найдено ПЯТЬ
расхождений — традиция «ни одно "всё исправлено" не выдерживает проверки» держится и на этой сессии.
Все пять закрыты по месту (правки видны в §38, §40 и §42), и одно стоит того, чтобы его запомнили:
пере-замер §4.4 жил только в скратчпаде — то есть был «числом без первоисточника» ровно того класса, за
который заказ корил чужие числа; первоисточник положен в дерево постоянным якорем-пином
носители по file:line; реверс-пин признан невакуумным с названным механизмом падения). Расхождения нашлись
и здесь (⚠-вывод §35 держится и на этой сессии), все закрыты по месту — правки видны в §38, §40 и §42.
Одно стоит того, чтобы его запомнили: пере-замер §4.4 жил только в скратчпаде — то есть был «числом без
первоисточника» ровно того класса, за который заказ корил чужие числа; первоисточник положен в дерево постоянным якорем-пином
`internal/miner/emissioncap_test.go`.
## 45. Воркфлоу-ревью фикс2: две кодовые находки и три остатка, которые оно сузило
@ -1165,9 +1161,8 @@ retired-ключи ловятся полями, а не голым strict-decode
Форма: шесть параллельных полос-искателей под антипаттерны проекта (неверные доводы при верном коде ·
вакуумные пины · семантика флажка глубоким чтением · дверь/отчёт глубоким чтением · общность движка ·
шов/контракт), каждая находка — через трёх адверсариальных скептиков с разными линзами (корректность
трассой · достижимость · «не принятый ли уже остаток»), выживание — большинство. 23 сырых находки,
20 выживших, 16 уникальных, все закрыты. Правки доводов в комментариях и документах живут по месту;
здесь — то, что осталось фактом.
трассой · достижимость · «не принятый ли уже остаток»), выживание — большинство. Все выжившие находки
закрыты; правки доводов в комментариях и документах живут по месту, здесь — то, что осталось фактом.
**Кодовые (2), обе закрыты правкой + пином + посадкой:**