Final documentation pass in the backend zone: keep what a session needs to work, drop the account of how we got there, and correct the stale claims about code
| `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` |
| `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`) / `bank-apply` (**входная дверь шва**, D39.156/D39.158: приём документа решений владельца по банку, $0; отчёт v2 на stdout, отказ по существу — **exit 14**, незавершённая запись — **exit 15**) / `backup` (`VACUUM INTO` + `integrity_check`, он же pre-flight боевого прогона) / `seed-lint` ($0-проверка сида и дельты владельца). **ДВА потолка прогона, и они ортогональны.**`--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/рендеры в тестируемых функциях (полный перечень глаголов — `dispatchCommands` в `cmd/tmctl/main.go`) | `main.go`, `invocation.go`, `render.go`, `dotenv.go`, `bankapply.go`, `backup.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` под локом), `SchemaHead` — та самая `Expected`; write-open БД новее бинаря — ОТКАЗ (D39.134) | `ledger.go`, `migrate.go`, `glossary.go`, `store.go` |
@ -21,7 +21,10 @@
| `internal/membank` | Банк памяти v2, memmatch-v4: Aho-Corasick, спойлер-окна, disposition, **trust-gated longest-match** (draft-длиннее НЕ съедает вложенный approved; отказ = громкая телеметрия — терм-дрейф закрыт в КОДЕ, D39.2-T1), post-check, рендереры инъекции, загрузка/валидация сида (`SeedLint`) | `memory.go`, `memseed.go`, `mempostcheck.go` |
| `internal/miner` | Детерминированный оффлайн-майнер банка (WS3, $0): `MineBank` над нормализованными чанками + контраст-корпус → `[]Term`, `DeltaYAML` — сид-дельта на подпись владельца; все пар-данные приходят `*lang.Pack` | `miner.go`, `miner_*.go` |
| `internal/checks` | Детерминированные $0-вердикты над текстом чанка, **target-aware ПО ДАННЫМ (D39.64): `Checkers` компилируется из цель-данных, `TargetActive()`/`TargetScriptNonLatin()` гейтят всё — цель без данных инертна, Go-веток по паре нет**: санитайзер v7 (fold-first, CJK/Hangul-leak, CJK-глосс-whitelist) + `ExportNormalize` (**export-contract слой 6**, no-op без цель-данных), cheap-гейты и DC-чекеры (пар-данные из пака), coverage-гейт эксцизии, regression-guard. Флаг-вокабуляр НЕ импортирует: возвращает факт, диспозицию назначает драйвер | `sanitizer.go`, `cheapgates.go`, `checkers.go`, `coverage.go` |
| `internal/pipeline` | Композит-корень (драйвер): сетап `runner.go` → снапшот `snapshot.go` → сид `seeding.go` → цикл книги `bookrun.go` → волны `waverun.go` → петля чанка `chunkrun.go` → стадия `stagerun.go` → эскалация сре-гейтом `escalation.go` → resume `resume.go`; classify/disposition; **реестр `roleInjectionRenderers`** (новая роль = данные+рендерер, не правка switch); `export.go`/`quality.go`/`status.go` — read-only проекции (общая derivation member-drop = `memberDrops`); **read-out файлы для платформы** (D39.85 — приватный SQLite движка ей закрыт): `manifest.go` (структура глав/чанков + стабильный id главы, строка 100), `bankexport.go` (весь банк тремя статусами, 125); оба пишутся через `artifact.go` (write-then-rename — их читают ПОКА идёт прогон) | `bookrun.go`, `waverun.go`, `stagerun.go`, `export.go`, `manifest.go`, `disposition.go` |
| `internal/pipeline` | Композит-корень (драйвер): сетап `runner.go` → снапшот `snapshot.go` → сид `seeding.go` → цикл книги `bookrun.go` → волны `waverun.go` → петля чанка `chunkrun.go` → стадия `stagerun.go` → эскалация сре-гейтом `escalation.go` → resume `resume.go`; classify/disposition; **реестр `roleInjectionRenderers`** (новая роль = данные+рендерер, не правка switch); `export.go`/`quality.go`/`status.go` — read-only проекции (общая derivation member-drop = `memberDrops`); **read-out файлы для платформы** (D39.85 — приватный SQLite движка ей закрыт): `manifest.go` (структура глав/чанков + стабильный id главы, строка 100), `bankexport.go` (весь банк тремя статусами, 125); вместе с картой подписи и авто-банком в `mining.go` (`signatureMapPath` · `autoBankPath`) — все ЧЕТЫРЕ пишутся через `artifact.go` (write-then-rename — их читают ПОКА идёт прогон); человеческая стоп-таблица `.bank-stop.txt` — обычным `os.WriteFile`, БЕЗ этой гарантии, а машинной `.bank-stop.json` больше нет (снесена D39.158) | `bookrun.go`, `waverun.go`, `stagerun.go`, `export.go`, `manifest.go`, `disposition.go` |
| `internal/bookfile` | Писатель книги: EPUB 3 (контейнер/OPF/навигация) и чистый txt из экспорт-проекции; формат знает только он, `tmctl build` даёт ему данные | `epub.go`, `txt.go`, `container.go`, `model.go` |
| `internal/runevents` | ДВИЖКОВАЯ сторона шва кадров прогона (строка 103): конверт и payload'ы NDJSON-потока `events.jsonl` + append-only журнал; словарь исходов `finished`. Форма — здесь, КОГДА событие случилось — у драйвера, долговечная нумерация — у`store.events_outbox` | `runevents.go`, `journal.go` |
| `internal/standdata` | Резолв путей корпусных данных ОТ КОРНЯ РЕПОЗИТОРИЯ по маркеру (`<репозиторий>/books`, D39.157 п.2); `$HOME` не читается вовсе, env-переопределения `TM_*` старше дефолтов | `standdata.go` |
## Инварианты — ЛОМАТЬ НЕЛЬЗЯ (каждый закреплён тестами)
@ -37,14 +40,14 @@
## Подготовка ja-книги (чек-лист сида)
- **Kana-написания имён = matchable.** Ruby-чтения захватываются на инжесте и авто-подцепляются как alias ручной записи (D16.4, `attachRubyAliasesToManual`). Имя, встречающееся ТОЛЬКО каной, внеси в `aliases:` руками — иначе «тихо пусто».
- **Kana-написания имён = matchable.** Ruby-чтения захватываются на инжесте и авто-подцепляются как alias ручной записи (D16.4, `membank.AttachRubyAliasesToManual`). Имя, встречающееся ТОЛЬКО каной, внеси в `aliases:` руками — иначе «тихо пусто».
- **Двойные чтения (強敵→とも) — под флагом:** длинное двойное чтение может дать CONFIRMED-ложь — при подозрении убери из сида. Полная дизамбигуация — Ф2 (B6).
- **Kana-precision ≥4 не замерена** (minKeyLenPhonetic=3): кана-ключи не проверяются на границу слова (нет сегментации) — задача полигона + токенизатор B6.
- ⚠ **ja-книга требует каталога `prompts/ja-ru/` (по файлу на роль)** — промпт-резолв конвенцией fail-loud'ится без него, называя ожидаемый путь (пример: закомментированный ja-блок в `example/book.yaml`). Контент пакета = данные под реальную книгу (инвариант общности §0.1), не сочинять.
## Как гонять
Стенд: WSL2 (localhost из-под прокси = 403 — для local-вызовов no-proxy транспорт). Книга-стенд: `/home/ubuntu/books/gu-zhenren/` (GB18030; тексты и производные ВНЕ git).
Стенд: WSL2 (localhost из-под прокси = 403 — для local-вызовов no-proxy транспорт). Книга-стенд: `<репозиторий>/books/gu-zhenren/` (адрес по D39.157 п.2; GB18030; тексты и производные версионируются ОТДЕЛЬНЫМ git-репозиторием).
Батарея — ОДНА команда, `make` из `backend/` (Makefile):
@ -76,10 +79,10 @@ 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}` |
| `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}` |
Пути дефолтятся ОТ`$HOME` и от корня репозитория (не от `/home/ubuntu`), так что клон под любым пользователем и по любому пути находит то, что есть; точечные переопределения — `TM_CHECKER_LABELS_DIR`, `TM_MINER_PARITY_{CONTRAST,RECORDS,SEED}`.
Пути дефолтятся от КОРНЯ РЕПОЗИТОРИЯ, найденного по маркеру (`internal/standdata`; `$HOME` не читается вовсе — D39.157 п.2), так что клон под любым пользователем и по любому пути находит то, что есть; точечные переопределения — `TM_CHECKER_LABELS_DIR`, `TM_MINER_PARITY_{CONTRAST,RECORDS,SEED}`.
**Golden-гард детерминизма** (`golden_test.go` + `testdata/golden/`): пинит бит-в-бит snapshotID, request_hash, wire-тела, вердикты и resume-байты. Обновлять ТОЛЬКО на ратифицированной смене поведения: `TM_UPDATE_GOLDEN=1 go test ./internal/pipeline/ -run TestGolden`; при re-capture — **маскированный структурный дифф** (хеши/версии → плейсхолдер) обязан быть пустым, если смена версий не меняла вердиктов.
@ -90,4 +93,4 @@ set -a; . ./.env; set +a; TM_LIVE=1 go test -tags live -run TestLive -v ./intern
- ⚠ ALTER-шаги миграций v8+ НЕ идемпотентны вопреки шапке `migrate.go` — полу-применённая БД не сходится (строка 49а).
- Открытый остаток строки 79 — граница ПЛОТНОСТИ письма Go-константой `cjkScriptNames` (`internal/lang/script.go`).
2. **Гейт `TestDispatchCommandsCoversTheSwitch` слеп к `case` с КОНСТАНТОЙ** — считает только
`*ast.BasicLit`, поэтому команда, объявленная константой, невидима гейту, чей смысл в том, что дефект
@ -208,47 +222,19 @@ file or directory` — та же причина, по которой красе
изоляции ⇒ нагрузка»), затем другой одиночный прогон дал ровно обратное. Решает только чередующийся
A/B (§10 п.7).
## 12. Попунктный чек промта
## 12. Чек заказа и дифф тестов — сняты
| пункт | статус |
|---|---|
| §2 зона записи только `backend/` + секция «Бэкенд» в `docs/PROGRESS.md`; не коммитить; чужое не трогать | ✅ `git status` подтверждает |
| §4.1 дефолты пустых ключей; гард ЯВНОГО пути не ослаблен; отсутствующий дефолт = «решений нет» | ✅ на момент пака — M1 + `TestDeclaredAndUnreadableDecisionPathStillRefuses`. ⚠ **Механизм СНЯТ фикс2:** объявляемых ключей больше нет, путь — чистая функция каталога и `book_id` (D39.158 п.5), оба пина сняты вместе с предметом (§40) |
| §4.2 глагол $0; байтовый no-op; отзыв = ЗАМЕНА; всё-или-ничего; два решения по терму = отказ; конфликт с сидом С ИМЕНЕМ; проекция БЕЗ мутации; занятый лок РОВНО 12; flock неблокирующе; версия в ответе; `depth` явным полем | ✅ M2–M8 + `TestDeclineWritesTheRejectAndWithdrawsTheApproval`, `TestOneCallDecidesATermOnce`, `TestDecisionOnASignedSeedTermIsRefusedByName`, `TestBankApplyDryRunWritesNothing`, `TestBankApplyDeclineThenApproveConverges`; §3, §4 |
| §4.2 НЕ проектировать «глубину до черновика» | ✅ `depth: edit_wave`; `TestTheRunReadsTheConventionalDelta` доказывает `Source:"mined"` |
| §4.3 `--keys-file` только у`translate`; деплой-файл ПЕРВЫМ; нечитаемый = отказ конфигом; ничего «на вырост» | ✅ M6 + `TestKeysFileWinsOverTheConventionalEnv` + `TestAnUnreadableKeysFileRefusesWithTheConfigClass` |
| §4.4 строгость сид-загрузчика + тест на unknown-key | ✅ 2 кейса в `TestSeedLintCatchesDefects` (`gendr`, `termz`) |
| §4.5 путь артефакта аддитивно + версия документа; НЕ тащить пере-именование банк-экспорта (ЛОМАЮЩЕЕ, окно — строка 161) | ✅ `artifacts{}` + `status_version`; пин `rep.Artifacts.BankExport == r.bankExportPath()` |
| §4.6 дефолт корпуса маркерным резолвом, env-переопределения старше | ✅ §1 |
| §4.7 платных вызовов НОЛЬ; `--keys-file` фейковыми ключами; вердикт PLAUSIBLE | ✅ sha256 базы (§4) + `TestTheKeysFileActuallyReachesTheProvider` (сверяет `Authorization` на fake-провайдере, и БЕЗ флага тот же прогон отказывает конфигом — проверка не вакуумна); §7 |
| §5 батарея после каждой правки; красный тест не подгонять; посадки ВНЕ списка промта; посадка называет ПАКЕТ; живая проба в КОПИИ; артефакт-находок | ✅ 16 посадок, 8 сверх списка; флейки охарактеризованы и НЕ тронуты; §2, §3 |
| §5.1 последний абзац — план или обещание? сделать СЕЙЧАС | ✅ всё найденное ревью закрыто в этом дереве; единственное отложенное — §8, вне моих возможностей по деньгам |
| §5.1 дифф `^func Test` исполнением; интервальная самоверификация субагентом | ✅ §13; ревью гонялось в СЕРЕДИНЕ пака против явных критериев |
| §6 оси: шов (инвентарь ЧУЖОГО кода) · общность · деньги | ✅ читал `artifacts.go`/`bank.go`/`engine.go`/`spawn.go`/`resync.go`/`exit.go` сам; проверил, что ни один декодер платформы не строгий ⇒ аддитивные поля безопасны; Go не ветвится по паре/книге; деньги — sha256 базы |
| §8 число с командой · §9 эхо до работы · §10 «что НЕ удалось» · §11 развилки пингом | ✅ §1–§4 · выдано · §7 · §10 |
## 13. Дифф `^func Test` — исполнением
**Удалено: НОЛЬ** — сводный счёт по трём пакам и обе команды в §40. Новых тест-функций пака шесть файлов
несут в себе, плюс `TestTheRefusalTableIsTotal` в существующем `cmd/tmctl/exitcontract_test.go`.
**Тронуто в существующих файлах — шесть, ни у одной сигнатура не изменилась:**
- `exitcontract_test.go` — в список полосы добавлены `exitSchemaMismatch` и `exitDecisionsRejected`
(иначе гейт молча перестал бы покрывать новый код) + тест тотальности;
- `invocation_test.go` — frozen-строка usage расширена именем `bank-apply`. Объявленное расширение
контракта тем же механизмом, которым строка росла пять раз до меня (её собственный комментарий их
перечисляет), и второй половине того же теста («usage обязана называть каждую dispatch-команду»)
пункту — §15–§25 выше; сводный счёт тестов и переименования — §40; каталог посадок —
`backend/cmd/tmmutate/mutations.json`.
| пункт | статус | чем закрыт |
|---|---|---|
| **4.1** ⛔ блокер: round-trip на ОБОИХ документах | ✅ **сделано ШИРЕ заказа** | нормализация документа + запроса, рендер ДОКАЗЫВАЕТ равенство (не только разбираемость — заказанная форма пропустила бы две тихие потери), байты проверены один раз и один раз записаны, фаззер пути ДВЕРИ, характеризационный тест на библиотеку. §15, §20; мутации I/J/K; живая проба §19 |
| **4.2** пред-существующие чинятся по одной | ✅ сделано; **дважды углублено по ревью** | вердикт загрузчика разложен по СУБЪЕКТАМ (`membank/problems.go`), ключ сравнения — субъект, который НЕ несёт рендеринга (§29 п.2), и проверки ПОД загрузчиком выполняются по обе стороны (§30 п.1). Мутации T, Y, AD (⚠ X снята вместе с правилом маски, каталог её больше не несёт); пины `TestRepairingOneOfTwoBrokenTermsIsAccepted`, `TestAFaultTheDoorHasNoChannelForCannotDeadlockIt`, `TestRepairingAnUnloadableDocumentIsNotRefusedOverWhatItUNMASKS`; живая проба §19 |
| **4.3** два ключа на один путь | ✅ **сделано ШИРЕ на одну проверку** | попарно различны четыре движковых пути (`project_db`, `glossary_seed`, `mined_delta`, `mined_rejects`) — `project_db == mined_delta` означало бы YAML поверх sqlite. Проверено на всех `book.yaml` репозитория и стенда (`find . -name book.yaml \| wc -l` → **22**): нарушителей ноль. Мутация M |
| **4.4** гард флага на ПРИСУТСТВИЕ | ✅ сделано | `fs.Visit` (единственное, что отвечает на этот вопрос), оба гарда, плюс пустое значение у присутствующего флага — тоже отказ. Мутация N |
| **4.5** кап документа + строка отказа полосой | ✅ сделано, ПЕРЕСМОТРЕНО по ревью | ДВА капа: 1 МиБ на чтение + **5 000 решений** на применение. Первая редакция капила только байты и утверждала, что этим ограничено ВРЕМЯ — опровергнуто замером (§29). Класс 14 у обоих. Хвост про `exit 2` — пингом. Мутации R, W |
| **4.6** конверт путей потребителю | ✅ сделано, компромисс назван | `manifest --json` несёт тот же `artifacts{}`; ВЫВОДИТСЯ на каждом чтении, поэтому версия документа не двигалась. Компромисс вслух: манифест семантически про дерево глав, путь артефакта там гость. Мутация V |
| **4.7** алиас судится по НАБОРУ | ✅ сделано; **углублено по §30 п.2** | проверка перенесена ПОСЛЕ свёртки и судит РЕЗУЛЬТАТ — именно поэтому два отказа (терм + его алиас) остаются законными: к концу вызова алиасом никто не владеет. ⚠ Побега «своя поверхность проверяется по ВХОДНОМУ документу» в коде НЕТ — он был заведён и снят как вредный (§30 п.2), заводить заново не надо. Мутации P, AB |
| **4.8** перевёрнутое окно глав | ✅ сделано | отказ с цитатой окна; открытая верхняя граница законна. Мутация O |
| **4.9** честность про непогасший стоп | ✅ **исполнено фактом, не предсказанием** | `signature{map,surfaces,undecided}` по правилу самого прогона; `undecided>0` — факт, `undecided==0` — явно не обещание. Аргумент в §22. Мутация U |
| **4.10** SIGTERM | ✅ сделано; сомнение записки СНЯТО замером; **довод в комментарии исправлен по ревью** | ctx проверяется дважды, стоп не пишет ничего, exit 5. Несущий довод — НЕ длина непрерываемого участка (первая редакция назвала её «≈20 с, два порядка внутри stop grace»; замер §29 п.1 дал **308.2 с** на 28 900 минимальных `decline`, которые байтовый кап пускает, — оценка неверна в 15 раз), а то, что записи идут последними и атомарно: убитый где угодно до них процесс не записал ничего. Мутация S (пин целится именно во вторую проверку) |
| **4.11** десять посадок, пин на каждую | ✅ 9 из 10 + доказательство по десятой | A–H, E1–E3 краснеют своими пинами; **G недостижима за первым гейтом — доказано, не предположено** (§17), вместо поведенческого пина запинен контракт функции |
| **4.12** цепь в репозитории | ✅ сделано | `internal/pipeline/bankchain_test.go`, $0 на фейковом провайдере, ОДИН проект от стопа до погасшего стопа; форма фикстуры (2 терма = 1 approve + 1 decline) проверяется как ПРЕМИСА; ограничение синтетики названо в комментарии; карта подписи после погасшего стопа зафиксирована явно |
| **4.13** девять мелочей | ✅ по каждой решение и довод | §21: починено 3, отклонено с аргументом 5, пингом 1 |
| **4.14** довод отказа `--keys-file` | ✅ сделано | текст называет `redrive` (платный, свой `.env`), `backup`/`migrate` (деплой-шаги) и $0-читающие поимённо. Развилку про канал ключей `redrive`НЕ решал |
| **4.15** три тавтологичных пина | ✅ сделано | вердикт `Preexisting` исполняется всеми четырьмя проверками (`TestAPreExistingFaultOfEveryKindStaysReportedAndUnblocking`); четвёртый оракул фаззера снят с тавтологии и стал НЕПОДВИЖНОЙ ТОЧКОЙ; `TestDecisionByBankID` сверяется с НЕЗАВИСИМО посчитанными константами + отдельный пин на инъективность, ради которой в `TermID` сделана длина-префикс |
| **4.16** платных вызовов ноль | ✅ | `sha256` базы стенда не сдвинулся; рантайм по платному пути — PLAUSIBLE, как и ожидалось |
| §5 батарея после каждой правки · посадки СВЕРХ списка · посадка называет ПАКЕТ · живая проба в КОПИИ · артефакт-находок | ✅ | 36 посадок (26 сверх списка §4.11), пакет пина ≠ пакет правки записан в каталоге, проба в копии, эта секция |
| §5.1 последний абзац — план или обещание? · дифф `^func Test` исполнением · интервальная самоверификация субагентом · каждый клейм — с результатом инструмента ЭТОЙ сессии | ✅ | обещаний в отчёте нет: всё найденное закрыто в дереве; §27; ревью гонялось в СЕРЕДИНЕ работы против явных критериев; каждое число выше — с командой |
| §6 три оси | ✅ | round-trip формата (§15, §20) · слепота гейта (§16, §17) · общность: в Go, которую я тронул, нет ни одного литерала пары/письменности/орфографии (проверено грепом по девяти файлам); нормализация — `strings.TrimSpace`, определённый Юникодом, а не скриптом |
| §8 заявление = команда · §9 эхо до работы · §10 «что НЕ удалось» · §11 развилки | ✅ | §14–§27 · выдано до кода · §24 · §21–§23 |
## 27. Дифф `^func Test` — исполнением
**Удалено: НОЛЬ** — сводный счёт и обе команды в §40; поимённая половина проверки прошла по семнадцати
пинам, которые отчёт пака называет по имени (каждый существует ровно один раз).
**Тронуто (тело изменено, сигнатура — нет), и вот почему это законно:**
- `cmd/tmctl/exitcontract_test.go` — `TestTheRefusalTableIsTotal` перестал держать ручной список и
читает классы из ИСХОДНИКА обходом `GenDecl`/`ValueSpec` (⚠ не по `*ast.BasicLit` в `case`, как
соседний гейт диспетча: значения `RefusalClass` объявлены КОНСТАНТАМИ, и копирование той техники
воспроизвело бы её собственную слепоту — это её же пинг);
- `internal/membank/decisions_test.go` — `TestDecisionByBankID` сверяется с НЕЗАВИСИМО посчитанными
константами; `decisions_fuzz_test.go` — оракул 4 переписан с тавтологии на неподвижную точку;
⚠ D39.158 называет «вход §26» — читатель, пришедший по этой ссылке, попал верно: адреса выше.
## 28. Состав дофикса
@ -646,7 +575,8 @@ YAML не накладывает ничего. Новый гоняет прои
Ревью запрашивалось по решениям, где заказ оставлял форму на сессии. **Каждую находку сессия
воспроизвела сама прежде, чем чинить** — чужой результат, взятый на веру, ровно так же становится
нормой. У двух подтверждённых решений ревью дало довод СИЛЬНЕЕ, и довод заменён в комментариях кода: у конверта путей на манифесте — что бамп версии выбросил бы все сохранённые сайдкары
и пере-нарезал каждую книгу ради аддитивного поля; у SIGTERM — атомарность и порядок записей вместо
и пере-нарезал каждую книгу ради аддитивного поля (компромисс назван вслух: манифест семантически про
дерево глав, путь артефакта там гость); у SIGTERM — атомарность и порядок записей вместо
длины непрерываемого участка.
### 1. Кап в БАЙТАХ не ограничивает ВРЕМЯ — мой довод был неверен
@ -794,6 +724,10 @@ off-by-one · после второй проверки контекста нич
`backend/internal/membank/decisions_test.go:348=Computed outside Go, from the spec in TermID's own comment`) ·
механически: `grep -cE '^### 4\.' <промт>` → 13); решения с доводами и названные остатки — §37; замеры —
§38; живые пробы — §39; дифф тестов и переименования — §40; что НЕ удалось — §41; сужения воркфлоу-ревью
— §45.
| пункт | статус | чем закрыт |
|---|---|---|
| **4.1** память «предъявленного стопом» | ✅ | store v16 `bank_stop_presented` (плоское множество поверхностей кластеров — Src+Aliases, `INSERT OR IGNORE`, без обрезки); предикат `hasUnpresentedCluster` — пересечение кластера с памятью, домен — ИТОГОВАЯ карта после потолков (обе секции); двусторонний остаток записан пином. Цена миграции названа (§37). Пины: цепь Б, `stopflag_test.go`, `stopmemory_test.go`, реверс-новизна; мутации FX1–FX3 |
| **4.2** порядок и момент записи | ✅ | карта (атомарно) → память, в момент СТОПА; отказ памяти НЕ валит стоп (ERROR-строка, названо §37); «подтверди карту» НЕ заведено; draft-only ветка названа. Развилка карта/SQLite-транзакция решена явно (направление потерь — доброкачественный повторный стоп) |
| **4.3** авто-провод при поднятом флаге | ✅ | «останавливаться ли» и «проводить ли» — два решения; авто-провод на ЛЮБОМ неостановившемся прогоне; вторая половина названа: остановившийся прогон авто-банк НЕ пишет (решение с доводом, §37). Мутация FX4; пины в WHATSurvives и цепи Б |
| **4.4** хвост за потолком | ✅ не лечился | пере-замер своей командой (2354/209/11/198, §38) — числа промта без первоисточника не цитируются; фильтр карты НЕ заведён; пинг в журнале |
| **4.5** единая точка фиксации | ✅ | вердикт (`decisionVerdict`) / исход (`finishReport` — единственный конструктор, свитч по `applyOutcome`, шесть исходов); «записано» — пост-состояние ПЕРЕЧИТАННЫХ файлов; пин `TestEveryOutcomeStampsItsOwnMode` + греп-инвариант (присваиваний вне конструктора нет — подтверждено интервальным ревью). Вердиктная половина сохранена (сильная форма отвергнута по букве промта) |
| **4.6**`signature.undecided` | ✅ | пере-документирован: НЕ отвечает на вопрос о стопе (в обе стороны), информационный счётчик для экрана подписи; память дверь НЕ читает (решение с доводом: ответ требовал бы следующего намайненного набора, которого нет ни у кого за $0); поле не удалено |
| **4.7** полоса в два яруса | ✅ | пол «нет отката, повтор безопасен»; клауза «ничего не произошло» — у пяти исходных классов; гардрейл «разрушительное — только на классе» буквой; класс `write_incomplete` (exit 15) один на оба исхода, различие — `written_*`; отказ по обоим капам печатает отчёт (контракт сведён к одному носителю). Мутации FX18–FX20 |
| **4.8** подготовка обоих файлов | ✅ | stage обоих (write+sync+chmod) → два rename → fsync каталога; заявлено НЕ шире сделанного (атомарность против среды, не против SIGKILL — довод в `writeDecisionFiles`, остаток в §41); отказ синка — класс 15 с оговоркой долговечности. Мутации FX16–FX17, пины staged/unsyncable |
| **4.9** Clean + конвенция | ✅ | (а) `filepath.Clean` абсолютных в resolve, репро приёмки закрыто пином; (б) оба ключа СНЯТЫ retired-отказом с лечением в тексте (прецедент `adult:`), пути — чистая функция каталога и book_id: МЕЖКНИЖНАЯ гонка (⛔-пинг прошлого пака) снята; воркфлоу-ревью показало ОСТАТОК той же оси — второй конфиг ТОЙ ЖЕ книги (один каталог, один book_id, другой объявленный project_db) пишет те же файлы под другим локом; остаток — пинг, не решение пака (§45). Мутации FX12–FX13 |
| **4.11** слепые пятна | ✅ | все шесть строк таблицы закрыты посадкой+пином (normText-класс · субъект-класс · субъект-окно · абсолютность путей отчёта двери · границы обоих капов · граница окна); пин семантики §4.1 — цепь Б + два переписанных харнесс-теста (возврат старой формулы краснит ТРИ теста, вывод tmmutate по FX2 поимённо — §40). Вакуумный пин пойман посадкой ДО записи (§38) |
| **4.12** наследие — списком | ✅ | шапка mining.go · комментарий у loadMinedRejects · unsignedEngineSurfaces · docstring runBankMiningStop · сообщение стопа · renderSignatureStop + два стейл-хинта render.go · help `--verify-bank` · WaveSignatureStop/waverun · комментарий v16; новое состояние «дельта непустая, нового нет» — явная WARN-строка; цепной тест переписан (замена названа: `TestTheStopIsAFlagNotAGateOnDecisions`); пинги с ПОРЯДКОМ и миграционная нота — в журнале |
| **4.13** платных вызовов ноль | ✅ | фейковый провайдер + харнесс + копия стенда; sha256 стендовой базы не сдвинулся; рантайм по платному пути — PLAUSIBLE; «выигрыш по чекпоинтам» НЕ заявлен (механизма нет, §41) |
| §5 батарея/каталог/проба/артефакт | ✅ | батарея после каждой правки; `make battery` EXIT=0; стенд-батарея красна ровно на jieba-парити; каталог 36→56, `NOTHING` роняет гейт (свойство не тронуто); живая проба §39; эта база |
| §6 три оси | ✅ | семантика флажка — прогонами движка (цепи, харнесс), без кода платформы · точка фиксации — конструктор + греп + пин исходов · слепота гейта — 56 посадок / 0 неожиданных |
| §7–§11 | ✅ | план до кода (`SEAM_FIX2_PLAN.md`, механическая сверка 13) · эхо до работы · «что НЕ удалось» §41 · развилки решались по букве промта, интерпретаций против него нет |
⚠ D39.158 называет «вход §43» — читатель, пришедший по этой ссылке, попал верно: адреса выше.
## 44. Проверка ЗАКРЫТИЯ — отдельным агентом, не чинившим (§5.1)
Прогнана по презумпции «каждое заявленное закрытие ложно, пока не увидено исполнением»: все 13 строк §43
и все 4 диспозиции §42 подтверждены в дереве (13 точечных тестовых прогонов + 3 полных пакета — зелёные;
Прогнана по презумпции «каждое заявленное закрытие ложно, пока не увидено исполнением»: все 13 пунктов
заказа фикс2 (§36–§41, §45) и все 4 диспозиции §42 подтверждены в дереве (13 точечных тестовых прогонов + 3 полных пакета — зелёные;
носители по file:line; реверс-пин признан невакуумным с названным механизмом падения). Расхождения нашлись
и здесь (⚠-вывод §35 держится и на этой сессии), все закрыты по месту — правки видны в §38, §40 и §42.
Одно стоит того, чтобы его запомнили: пере-замер §4.4 жил только в скратчпаде — то есть был «числом без