From 1d84cff75ea3334163fb703444a1763260f257b4 Mon Sep 17 00:00:00 2001 From: heaven Date: Wed, 2 Sep 2026 11:49:08 +0300 Subject: [PATCH] Second cut pass in the backend zone: restore what the review found lost, and take the sediment the first pass left behind --- backend/README.md | 22 ++-- backend/docs/DISCLOSURE_LAW_DESIGN.md | 54 ++++----- backend/docs/MONEY_HONESTY_PLAN-NOTE.md | 9 ++ backend/docs/MONEY_HONESTY_REPORT.md | 43 +++++-- backend/docs/SEAM_PACK_FINDINGS.md | 149 ++++++++++++------------ 5 files changed, 145 insertions(+), 132 deletions(-) diff --git a/backend/README.md b/backend/README.md index 0099dfa1..f4f9482a 100644 --- a/backend/README.md +++ b/backend/README.md @@ -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 из той же экспорт-проекции в `.book.` рядом с БД, пути — в `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 из той же экспорт-проекции в `.book.` рядом с БД, пути — в `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). diff --git a/backend/docs/DISCLOSURE_LAW_DESIGN.md b/backend/docs/DISCLOSURE_LAW_DESIGN.md index 813f7c36..c1971293 100644 --- a/backend/docs/DISCLOSURE_LAW_DESIGN.md +++ b/backend/docs/DISCLOSURE_LAW_DESIGN.md @@ -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 оценён по составу документов, но **не написан**; цена «одна запись на поле» — оценка, а не замер. diff --git a/backend/docs/MONEY_HONESTY_PLAN-NOTE.md b/backend/docs/MONEY_HONESTY_PLAN-NOTE.md index df474f6f..b619f09c 100644 --- a/backend/docs/MONEY_HONESTY_PLAN-NOTE.md +++ b/backend/docs/MONEY_HONESTY_PLAN-NOTE.md @@ -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**: платных вызовов не содержит и не планирует. diff --git a/backend/docs/MONEY_HONESTY_REPORT.md b/backend/docs/MONEY_HONESTY_REPORT.md index fd97778a..e90b801f 100644 --- a/backend/docs/MONEY_HONESTY_REPORT.md +++ b/backend/docs/MONEY_HONESTY_REPORT.md @@ -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). diff --git a/backend/docs/SEAM_PACK_FINDINGS.md b/backend/docs/SEAM_PACK_FINDINGS.md index e9acf524..579b11e2 100644 --- a/backend/docs/SEAM_PACK_FINDINGS.md +++ b/backend/docs/SEAM_PACK_FINDINGS.md @@ -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`. Секции 0–13 выше — улика приёмки, не тронуты. **Каждое число ниже — с -командой, которой получено;** команды из `backend/`. +записка-план — `SEAM_FIX_PLAN.md`. Секции 0–13 выше — улика приёмки, не тронуты. ## 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), обе закрыты правкой + пином + посадкой:**