diff --git a/docs/DOC_CLEANUP_PLAN.md b/docs/DOC_CLEANUP_PLAN.md index 470b538b..f8b0be99 100644 --- a/docs/DOC_CLEANUP_PLAN.md +++ b/docs/DOC_CLEANUP_PLAN.md @@ -1,380 +1,200 @@ # ПЛАН РАЗБОРА ТЕХДОЛГА ДОКУМЕНТАЦИИ — заказ владельца 01.09.2026 -> **⚠ ВРЕМЕННЫЙ РАБОЧИЙ ДОКУМЕНТ.** Живёт, пока идёт разбор; по закрытию последнего батча -> уезжает в `archive/` вместе с реестром находок. Держать его в живых доках после закрытия — -> значит сделать ровно то, против чего заказан сам разбор. +> **⚠ РЕДАКЦИЯ 2. Редакция 1 ОТКЛОНЕНА контролёром (Fable 5.1) и переписана.** Что он нашёл и что +> исправлено — раздел «Почему ред.1 отклонена». Главное: реестр находок был собран с дефектом +> атрибуции, и план исполнялся бы по чужим поправкам. > -> **Заказ владельца дословно:** свести находки в план и идти по нему · привести документацию -> в хороший порядок · **с бэклогом ПООСТОРОЖНЕЕ** · спилить лишнее, актуализировать · -> **старое и откровенно лишнее выкинуть, актуальное оставить и НЕ ПОТЕРЯТЬ** · старые решения -> и мусор спиливать, чтобы не забивать контекст сессиям · **касается и фронтовой, и -> платформенной, и бэкенд-документации** (прямая санкция на чужие зоны, выданная 01.09). +> **⚠ ВРЕМЕННЫЙ РАБОЧИЙ ДОКУМЕНТ.** По закрытию последнего батча уезжает в `archive/` вместе с +> реестром. Держать его в живых доках после закрытия — сделать ровно то, против чего он заказан. > -> **Контроль исполнения — отдельный агент модели Fable 5** (решение владельца 01.09): после -> КАЖДОГО коммита я предъявляю ему коммит, он сверяется с реестром находок и выносит, верно ли -> закрыто. Пинг-понг идёт, пока обе стороны не скажут «техдолг закрыт». Мой самоотчёт -> закрытием НЕ считается. +> **Заказ владельца дословно:** свести находки в план и идти по нему · привести документацию в +> хороший порядок · **с бэклогом ПООСТОРОЖНЕЕ** · спилить лишнее, актуализировать · **старое и +> откровенно лишнее выкинуть, актуальное оставить и НЕ ПОТЕРЯТЬ** · старые решения и мусор +> спиливать, чтобы не забивать контекст сессиям · **касается фронтовой, платформенной и +> бэкенд-документации** (прямая санкция на чужие зоны, 01.09). +> +> **Контроль исполнения — агент модели Fable 5.1**, поднят по оригинальному воркфлоу ревью +> (получил скрипт воркфлоу и сырой журнал 30 агентов). Контекст у него сквозной и не стирается. +> После КАЖДОГО коммита я предъявляю коммит, он сверяет с реестром и выносит вердикт. +> **Мой самоотчёт закрытием не считается.** Пинг-понг идёт, пока ОБЕ стороны не скажут «закрыто». + +## Почему ред.1 отклонена (находки контролёра, все проверены моей командой) + +1. **Реестр собран с дефектом атрибуции.** Вердикты опровергателей присваивались по паре + `(файл, строка)`, а на одной строке сидит до ШЕСТИ находок (`docs/PROGRESS.md:3` — шесть). + Все они получали поправку ПОСЛЕДНЕЙ. Затронуто 10 находок. Цена, если бы пошёл по ред.1: + переписал бы `docs/README.md:29`, которую ДВА опровергателя независимо велели не трогать; + снял бы с листа владельца живое обязательство (N076); искал бы в `docs/` файл, который уже + в архиве (N075). **Исправлено:** матч по дословной цитате, 82 вердикта на 82 разные находки, + дублей 0. +2. **N015 носила чужой вердикт «ПОДТВЕРЖДЕНО»**, не выносившись на пере-суд. Исправлено: honest + «НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ». +3. **Улики были обрезаны на ~700 знаках**, и в хвостах терялись именно положительные контроли; + разбор опровергателей (`reasoning`, 82 вердикта с уликами) в реестр не попадал вовсе. + Исправлено: реестр без капа, с разбором. +4. **Б13 ред.1 был батчем на ПОТЕРЮ:** три позиции из четырёх противоречили выводам ревизии + (`platform/docs/p8-review/` цитируется боевым тестом `spendbound_test.go`; `backend/docs/SEAM_*` + — носитель D39.158 ссылается на них пятью местами; `MONEY_HONESTY_REPORT` назначен носителем + живого остатка строки 232). Осталась ОДНА законная позиция — переезд отработавшего промта. +5. **Б9 «баннеры ИЛИ архив» — «или архив» снят:** 01/04/06 цитирует Go-код десятком мест. +6. **«Планы доработок в бэкенд и платформу» вынесены из плана:** это заказ (0б), а не техдолг + доков; держать его критерием «техдолг закрыт» нельзя — он бесконечен по определению. ## Источники -- **Реестр находок** — [`DOC_REVISION_FINDINGS.md`](DOC_REVISION_FINDINGS.md): 130 живых находок - с уликами (команда + вывод), по одной секции `N001`…`N130`. -- **Снятые опровергателем** — [`DOC_REVISION_REFUTED.md`](DOC_REVISION_REFUTED.md): 7 находок, - которые адверсариальный проход ОПРОВЕРГ. ⚠ Их чинить НЕЛЬЗЯ: «починка» вернёт дефект. +- [`DOC_REVISION_FINDINGS.md`](DOC_REVISION_FINDINGS.md) — **реестр ред.2**, 130 находок с уликами. +- [`DOC_REVISION_REFUTED.md`](DOC_REVISION_REFUTED.md) — 7 снятых. ⚠ **Чинить нельзя**: починка + вернёт дефект. -## Как добыты находки (чтобы вес был понятен) +## Дисциплина разбора (жёсткая) -Воркфлоу `docs-staleness-revision-A`, 15 срезов × (ревизор + опровергатель), 30 агентов, 0 отказов, -1689 вызовов инструментов. **1432 утверждения проверены ИСПОЛНЕНИЕМ**, 115 положительных контролей -(норма: отрицательный результат предъявляется вместе с доказательством, что инструмент в этой -области способен хоть что-то найти). Найдено 137 → адверсариальный пере-суд снял 7 → **осталось 130**. - -⚠ **Вес находки не одинаков.** 82 разрушающие и блокерные прошли пере-суд (75 ПОДТВЕРЖДЕНО). -Остальные проверены ОДНИМ агентом — они наряд на перепроверку, а не истина: исполнять их на -доверии запрещено, каждую верифицирую своей командой перед правкой. - -## Дисциплина разбора (жёсткая, из заказа владельца) - -1. **Не потерять.** Прежде чем удалить абзац/строку/док — грепнуть его отличительный токен по - всему дереву и назвать, куда уехало содержимое. Удаление без адреса = потеря. -2. **Бэклог — осторожно.** Строку закрываем ТОЛЬКО когда лекарство предъявлено в коде своей - командой. ID строк стабильны навсегда и на них ссылаются доки — закрытая строка уезжает в - D-ноту с номером решения, а не стирается молча. -3. **Чужие зоны.** Санкция владельца выдана на ДОКУМЕНТАЦИЮ. Код чужих зон не трогаю. - Каждая правка чужого дока объявляется в зонном журнале, чтобы зона увидела. -4. **Гейты перед каждым коммитом:** `counts.py --check` и `--lint`, `git status` на чужое, +1. **Поправка опровергателя ПЕРВИЧНА.** Где в находке стоит «⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ» — + исполняется она, а не «Предложенное действие». Поправка есть у **67 из 130**, и часть + ПРОТИВОПОЛОЖНА исходному действию. +2. **Не вписывать новые голые `file:line`** — сгниют так же. Форма: токен-якорь + `` файл:строка=`подстрока` `` либо ссылка на символ. Прямо требуют десять поправок. +3. **Вес находки не одинаков.** «НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ» = проверена одним агентом = наряд на + перепроверку. Верифицировать своей командой ДО правки. +4. **Не потерять.** Перед удалением абзаца/строки/дока — грепнуть отличительный токен и назвать + адрес, куда уехало содержимое. Удаление без адреса = потеря. +5. **Бэклог — осторожно.** Строка закрывается ТОЛЬКО с предъявленным в коде лекарством. ID строк + стабильны навсегда, на них ссылаются доки: закрытая строка уезжает в D-ноту с номером решения. +6. **Чужие зоны.** Санкция владельца — на ДОКУМЕНТАЦИЮ; код чужих зон не трогаю. + **Объявление = строка в зонном журнале ТЕМ ЖЕ коммитом** (иначе зона не увидит). +7. **Гейты перед каждым коммитом:** `counts.py --check` и `--lint`, `git status` на чужое, коммит только явным списком путей. -5. **Соседний worktree `/home/ubuntu/projects/textmachine` — НЕ ТРОГАТЬ** (указание владельца - 01.09): ни чтением, ни записью, ни git-командой. Каталог `books/` — симлинк туда же. +8. ⛔ **Соседний worktree `/home/ubuntu/projects/textmachine` — НЕ ТРОГАТЬ ВООБЩЕ** (указание + владельца 01.09): ни чтением, ни `git -C`. `books/` — симлинк туда же. + +## Порядок и зависимости (требование контролёра) + +- **Б0 идёт первым:** факт «канон 0.9.0» размазан по пяти батчам; пять коммитов дали бы четыре + промежуточных состояния, где часть носителей врёт. +- **Б2 (переезд промта) — до Б4:** N075 даёт развилку «перенести файл ИЛИ переписать текст». + Если Б4 выберет текст, а Б2 потом перенесёт файл — текст снова ложь. +- **Б1 — одна нота на четыре строки** (194/238/239/240): это одно событие, лендинги 31.08. +- **Б6 после Б4:** якоря реестра 13 ссылаются на строки бэклога. ## Батчи -Порядок — по убыванию цены ошибки. Каждый батч = один коммит = одна сверка контролёра. - -| Батч | Предмет | Находок | бл / maj / min | Статус | +| Батч | Предмет | Находок | бл/maj/min | Статус | |---|---|---|---|---| -| **Б1** | Блокеры бэклога: строки, закрытые кодом, но открытые в таблице | 3 | 3 / 0 / 0 | ⬜ не начат | -| **Б2** | Денежный путь `15-money-path.md` | 7 | 2 / 3 / 2 | ⬜ не начат | -| **Б3** | Шапка журнала и остальной PROGRESS | 17 | 0 / 9 / 8 | ⬜ не начат | -| **Б4** | Контракт API 14 (README + openapi) | 10 | 0 / 5 / 5 | ⬜ не начат | -| **Б5** | Реестр загейченных триггеров 13 | 8 | 0 / 5 / 3 | ⬜ не начат | -| **Б6** | Шов и эмиттер: 16, 17 | 11 | 0 / 8 / 3 | ⬜ не начат | -| **Б7** | Движковые арх-доки: 03, 09, 10, 12 | 17 | 0 / 5 / 12 | ⬜ не начат | -| **Б8** | Корневые и онбординг: CLAUDE.md, README, STACK, glossary, product-requirements | 17 | 0 / 6 / 11 | ⬜ не начат | -| **Б9** | Ранние арх-доки 01/04/06 — баннеры или архив | 6 | 0 / 1 / 5 | ⬜ не начат | -| **Б10** | ЧУЖАЯ ЗОНА (санкция владельца 01.09): бэкенд-доки | 10 | 0 / 6 / 4 | ⬜ не начат | -| **Б11** | ЧУЖАЯ ЗОНА (санкция владельца 01.09): фронт-доки | 16 | 0 / 9 / 7 | ⬜ не начат | -| **Б12** | ЧУЖАЯ ЗОНА (санкция владельца 01.09): платформенные доки | 8 | 1 / 4 / 3 | ⬜ не начат | +| **Б0** | СКВОЗНОЙ: «канон 0.9.0» одним касанием — требование контролёра 2.2 | 7 | 0/6/1 | ⬜ | +| **Б1** | Блокеры бэклога + строка 240: ОДНА нота на четыре строки | 4 | 3/1/0 | ⬜ | +| **Б2** | Переезд ОТРАБОТАВШЕГО промта (git mv) — раньше Б4, т.к. N075 от него зависит | 1 | 0/0/1 | ⬜ | +| **Б3** | Денежный путь 15-money-path.md (+ парная правка 17:101 по N004) | 7 | 2/3/2 | ⬜ | +| **Б4** | Шапка журнала и остальной бэклог | 14 | 0/7/7 | ⬜ | +| **Б5** | Контракт API 14 | 8 | 0/4/4 | ⬜ | +| **Б6** | Реестр загейченных триггеров 13 | 8 | 0/5/3 | ⬜ | +| **Б7** | Шов и эмиттер 16, 17 | 11 | 0/8/3 | ⬜ | +| **Б8** | Движковые арх-доки 03, 09, 10, 12 | 16 | 0/4/12 | ⬜ | +| **Б9** | Корневые и онбординг: CLAUDE.md, README, STACK, glossary, product-requirements | 15 | 0/4/11 | ⬜ | +| **Б10** | Ранние арх-доки 01/04/06 — ТОЛЬКО баннеры, архив СНЯТ (контролёр 4.2) | 6 | 0/1/5 | ⬜ | +| **Б11** | ЧУЖАЯ ЗОНА: бэкенд-доки — баннеры, НЕ архив (N074 corrected) | 10 | 0/6/4 | ⬜ | +| **Б12** | ЧУЖАЯ ЗОНА: фронт-доки (три неприкасаемых) | 15 | 0/8/7 | ⬜ | +| **Б13** | ЧУЖАЯ ЗОНА: платформенные доки | 8 | 1/4/3 | ⬜ | +| **Б14** | Углы вне ОБЕИХ сеток: `PROGRESS` 19–56 и 273–483 · `ORCHESTRATOR_SESSION_PROMPT.md` (32 КБ, не аудирован никем) · `05-decisions-index.md` статусы · шапка D-лога · `platform/BACKLOG.md` · `platform-PROGRESS.md` (415 КБ) · `DEFECT_REGISTER.md` (782 КБ) · `docs/POLYGON_*` | — | — | ⬜ | +| **Б15** | Явный мусор: байт-дубль промта P0 в двух архивах · `frontend/docs/API_CONTRACT_{DRAFT,INPUT}.md` (78 КБ с баннером «не читать» в ЖИВОМ каталоге) · `16-events-emitter` §5–§6 · `14-README` §6б/6в | — | — | ⬜ | +| **Б16** | Шапки статуса у `research/**` и `experiments/**` (60 файлов): тела НЕ трогать (D23.3), только датированная строка статуса там, где её нет | — | — | ⬜ | +| **Б17** | Пинги зонам по МЁРТВЫМ УКАЗАТЕЛЯМ В КОДЕ (не доки, правит зона): `artifact.go:11` · `backup.go:28` · `pgstore/books.go:776` · `languages.ts:3-8` · `tmctl/main.go:72` · `store/ledger.go:172` · `cheapgates.go:34` · `pipeline.puml:72` | — | — | ⬜ | -### Состав батчей поимённо +### Критерии приёмки по батчам -**Б1 — Блокеры бэклога: строки, закрытые кодом, но открытые в таблице** +**Б0 — СКВОЗНОЙ: «канон 0.9.0» одним касанием — требование контролёра 2.2** +Находки: N013, N022, N026, N034, N053, N056, N108 +**Приёмка:** `grep -rn '0\.8\.0' docs/ CLAUDE.md` вне исторических записей = 0; **`docs/README.md:29` НЕ тронута** (два опровергателя независимо: это корректная историческая запись о D39.169); `README:26` уже несёт 0.9.0 — не дублировать. -- `N001` блокер · `docs/PROGRESS.md:176` · C-исполненное-как-открытое — Строка 194 ЗАКРЫТА лендингом 31.08 (`bb541a8`, экземпляр A10 закона раскрытия): `projectBookUSD(...) + r.bankRoleCommittedUSD()` уже стоит в `status.g -- `N002` блокер · `docs/PROGRESS.md:200` · C-исполненное-как-открытое — Строка 238 ЗАКРЫТА тем же лендингом 31.08 (`bb541a8`, экземпляр A6 «проекция видит правку исходника»). Посадка `rebillsource_test.go` называет строку +**Б1 — Блокеры бэклога + строка 240: ОДНА нота на четыре строки** +Находки: N001, N002, N003, N021 +**Приёмка:** `grep -c '^| 194 \|^| 238 \|^| 239 \|^| 240 ' docs/PROGRESS.md` = 0; тело ноты несёт ВСЕ четыре номера, оба опровержения N001 (ось «сдвиг снапшота» И «не входят в число согласия»), экземпляры A6, A7+A12, обе половины улики 240 с оговоркой про `local`-заглушку; `docs/PROGRESS.md:282` больше не предлагает 238/239 как свободную работу; литералы очереди пере-считаны **скриптом** (`counts.py`), не руками; `counts.py --check` зелёный (голова сдвинется — сверить все три носителя). -- `N003` блокер · `docs/PROGRESS.md:201` · C-исполненное-как-открытое — Строка 239 ЗАКРЫТА лендингом 31.08 (`bb541a8`, экземпляр A7+A12). Правило `exportConfigDrift` не «перенесено», а сведено в ОДНО общее определение `orp +**Б2 — Переезд ОТРАБОТАВШЕГО промта (git mv) — раньше Б4, т.к. N075 от него зависит** +Находки: N075 +**Приёмка:** `git show --stat` = ровно rename + `docs/README.md`; после — `grep -rn 'docs/BACKEND_COLDRUN_V16'` даёт только исторические носители (архивный отчёт, D-лог, реестр нот), `COLDRUN_V16_REPORT:51` НЕ тронут (тело архивного отчёта); обе протухшие половины строки `README.md:25` поправлены («развилка НА ВЛАДЕЛЬЦЕ» → закрыта 31.08; «отчёт ждёт лендинга» → заленджен); ⚠ литерал «86 живых доков» в CURRENT-STATE уменьшится — `counts.py` его НЕ сторожит, править руками или снять. -**Б2 — Денежный путь `15-money-path.md`** -- `N004` блокер · `docs/architecture/15-money-path.md:31` · D-противоречие-ратифицированному — Числа взяты из D39.165 §1 (28.08) и меряны на леджерах ДО ценового перелома. D39.179 п.1 (31.08) ратифицировал: DeepSeek пере-пинен под цены, вступивш +**Б3 — Денежный путь 15-money-path.md (+ парная правка 17:101 по N004)** +Находки: N004, N005, N039, N040, N041, N113, N114 +**Приёмка:** N002/N003 — обе ветви `run abandon` описаны; провенанс чисел заменён с июльских на замер холодного прогона с явной датой; **N004 требует парной правки `17-seam-inbound-law.md:101` ТЕМ ЖЕ коммитом**, иначе класс E воспроизводится. -- `N005` блокер · `docs/architecture/15-money-path.md:35` · A-дрейф — Абзац описывает ТОЛЬКО живую ветвь `run abandon`. С пака P11 (PD-385, коммит e548e5a от 29.08) есть вторая ветвь — PHASE=settling: к ней отказ «юнит/б -- `N039` major · `docs/architecture/15-money-path.md:6` · B-мёртвый-указатель — Норму «чистый ФАЙЛ» ратифицировал D39.179 п.4, который сам говорит: «уточняет D39.177 §2в … Ось «зона» была НЕВЕРНА … Носитель нормы — шапка 15-money- +**Б4 — Шапка журнала и остальной бэклог** +Находки: N014, N015, N016, N017, N018, N019, N020, N076, N077, N078, N079, N080, N081, N082 +**Приёмка:** diff строго строк 1–18 и затронутых `| N |`; **строка 11 (лист владельца) неизменна**; N076 — исполняется ТОЛЬКО первая половина, снятие пункта с листа владельца ЗАПРЕЩЕНО; N016 — НЕ закрывать, переписать (живой остаток — цена доведённой книги); N017 — уровень ноты, не строки; N019 — писать связку $0.436110 / проекция $1.1377 / отток ≈$1.44; N020 — СУЗИТЬ строку 232, не закрывать; на каждую снятую фразу — адрес, куда уехала. -- `N040` major · `docs/architecture/15-money-path.md:19` · A-дрейф — Перечень движковых суб-бюджетов читается как исчерпывающий (репэйр + эскалация) и не знает третьего семейства — книго-широких бюджетов банк-ролей `gat -- `N041` major · `docs/architecture/15-money-path.md:21` · D-противоречие-ратифицированному — «Различение живёт в отчёте прогона и в логе» перестало быть верным 31.08. D39.181 п.2 ратифицировал закон раскрытия: «признак остановки по объёму обяз +**Б5 — Контракт API 14** +Находки: N035, N036, N037, N038, N109, N110, N111, N112 +**Приёмка:** N036 — команду убрать целиком (не менять «0 → 26»), хвост §7 тем же коммитом; N035 — первую половину 981 не трогать, §6в только ⚠-указателем; греп отличительных токенов снесённых абзацев в цели указателя. -- `N113` minor · `docs/architecture/15-money-path.md:6` · C-исполненное-как-открытое — Пак контракта 0.9.0 ЗАЛЕНДЖЕН 31.08 коммитом 0a680a3 (D39.180), файл в дереве чист, версия в нём — 0.9.0. Настоящее время «двигается прямо сейчас» лож -- `N114` minor · `docs/architecture/15-money-path.md:14` · A-дрейф — Из трёх опор канона, названных здесь, у двух в доке есть носители (телеметрия — леджер/status в §2, потолки — §2/§3), а у третьей — «явное согласие на +**Б6 — Реестр загейченных триггеров 13** +Находки: N029, N030, N031, N032, N033, N105, N106, N107 +**Приёмка:** `counts.py --lint` до/после: проблем 0, счётчик «сверено ПО СОДЕРЖИМОМУ» вырос ровно на число мигрированных, **число якорей в файле не уменьшилось**; N029 — стрелка «→ ЗАКРЫТО D39.82, см. строку 16», сводку свипа НЕ переписывать; N030 и N107 — строки 72 и 98–104 одним касанием. -**Б3 — Шапка журнала и остальной PROGRESS** +**Б7 — Шов и эмиттер 16, 17** +Находки: N042, N043, N044, N045, N046, N047, N048, N049, N115, N116, N117 +**Приёмка:** N032 — гвоздь НЕ строить, он построен; §5–§6 дока 16 сам себя баннирует как отработанный хендофф, внешних ссылок ноль — кандидат на вынос в `archive/reports/` с указателем (решение объявить, не делать молча). -- `N013` major · `docs/PROGRESS.md:3` · A-дрейф — Канон уже НЕ 0.8.0: в `docs/architecture/14-api-contract/openapi.yaml` стоит 0.9.0 — минор уехал с лендингом P12 31.08, о котором сообщает ЭТА ЖЕ стро -- `N014` major · `docs/PROGRESS.md:3` · A-дрейф — Числа «73%» в цитируемом отчёте холодного прогона НЕТ вовсе. Отчёт даёт долю редактора с ДВУМЯ знаменателями: 49.3% от всего прогона ($0.214865 из $0. +**Б8 — Движковые арх-доки 03, 09, 10, 12** +Находки: N023, N024, N027, N028, N092, N093, N095, N096, N097, N098, N099, N100, N101, N102, N103, N104 +**Приёмка:** 09 §1/§2/§4 — тела под ⚠-баннерами, анти-оверфлаг: не лезть, кроме случаев, где содержимое расходится с шапкой; N063 — §8 целиком НЕ сносить. -- `N015` major · `docs/PROGRESS.md:3` · E-второй-носитель — Шапка объявляет себя ЕДИНСТВЕННЫМ носителем состояния очереди, но не несёт двух лендингов 31.08, которые несёт `docs/README.md`: бэкенд-пак «ДЕНЬГИ И -- `N016` major · `docs/PROGRESS.md:80` · C-исполненное-как-открытое — Довод «⇒ строка жива» опирается на прогоны coldrun-a/b, но 31.08 отработал НОВЫЙ холодный прогон (coldrun-v16), в котором редакторская волна гонялась +**Б9 — Корневые и онбординг: CLAUDE.md, README, STACK, glossary, product-requirements** +Находки: N050, N051, N052, N054, N068, N069, N070, N083, N084, N085, N086, N087, N118, N119, N120 +**Приёмка:** CLAUDE.md — правка канона владельца: каждая формулировка предъявляется владельцу до коммита; `docs/README.md:29` не трогать (см. Б0). -- `N017` major · `docs/PROGRESS.md:143` · A-дрейф — Гейт назначен на событие, которое УЖЕ произошло (первый холодный прогон, 31.08), и прогон показал, что назначенный замер этим путём недостижим: манифе -- `N018` major · `docs/PROGRESS.md:157` · A-дрейф — Файла `.bank-stop.json` больше НЕ существует: он снесён вместе с писателем и четырьмя тестами при постройке входной двери шва (D39.158) — в Go-коде дв +**Б10 — Ранние арх-доки 01/04/06 — ТОЛЬКО баннеры, архив СНЯТ (контролёр 4.2)** +Находки: N025, N088, N089, N090, N091, N094 +**Приёмка:** ⚠ **«или архив» СНЯТ контролёром**: ни 01, ни 04, ни 06 не кандидаты — их цитирует Go-код (`ingest.go`, `ruby.go`, `migrate.go`, `cheapgates.go`, `coverage.go`, `book.go`, `memory.go`, `memseed.go`), а 01 назначен носителем Р5/Р8 самим D-логом. Только ⚠-баннеры на месте. -- `N019` major · `docs/PROGRESS.md:182` · D-противоречие-ратифицированному — Дважды протухла. (1) Цифра «≈$0.05» СНЯТА ратифицированной нотой D39.179 п.1 как протухшая: пере-пин DeepSeek 15.08 даёт множитель 4.47, а замер 31.08 -- `N020` major · `docs/PROGRESS.md:196` · C-исполненное-как-открытое — Строка 232 ЗАКРЫТА лендингом 31.08 (`bb541a8`, экземпляр A11). Читающий метод `store.AnnouncedOnceKeys()` построен, его докстринг называет строку 232 +**Б11 — ЧУЖАЯ ЗОНА: бэкенд-доки — баннеры, НЕ архив (N074 corrected)** +Находки: N007, N008, N009, N010, N011, N012, N071, N072, N073, N074 +**Приёмка:** N074 corrected: SEAM_* НЕ переносить — `SEAM_PACK_FINDINGS.md` (носитель D39.158) ссылается на них в пяти местах; MONEY_HONESTY_REPORT §4.5 назначен носителем живого остатка строки 232 — не архивировать; лечение = датированные ⚠-баннеры. N008/N009 — часть лекарства в коде (`artifact.go`, `backup.go`, `pipeline.puml`) ⇒ ПИНГ зоне, не касание. Строка в `docs/PROGRESS.md` секции «Бэкенд» тем же коммитом. -- `N021` major · `docs/PROGRESS.md:202` · C-исполненное-как-открытое — Строка 240 исполнена лендингом P12 31.08 (`0a680a3`, D39.180) — обеими ветвями своей же диспозиции. Гард `LiftBankStop` СНЯТ (в `platform/` осталось д -- `N075` minor · `docs/PROGRESS.md:3` · B-мёртвый-указатель — Промт НЕ в `docs/archive/prompts/` — он до сих пор лежит в корне `docs/`. В архиве промтов лежат два ДРУГИХ холодно-прогонных промта (`BACKEND_COLDRUN +**Б12 — ЧУЖАЯ ЗОНА: фронт-доки (три неприкасаемых)** +Находки: N055, N057, N058, N059, N060, N061, N062, N063, N121, N122, N123, N124, N125, N126, N127 +**Приёмка:** ⛔ НЕ трогать: зеркало `frontend/docs/api-contract/openapi.yaml` (отставание РАТИФИЦИРОВАНО D39.142 п.5 на время фриза), `src/api/schema.ts` (генерируемый), `src/mock/handlers.ts` (код). N057 не переписывать в «закрыта»; N122 не менять пример на живой; N124/N048 — хронику зонного журнала задним числом НЕ править. Объявление = строка в `frontend/docs/frontend-PROGRESS.md` ТЕМ ЖЕ коммитом. -- `N076` minor · `docs/PROGRESS.md:3` · A-дрейф — Сегодня `main` опережает `origin/main` на 48 коммитов. Несущий вывод («развязки нет») выжил — `origin/main` остаётся ПРЕДКОМ `main`, расхождения по-пр -- `N077` minor · `docs/PROGRESS.md:3` · A-дрейф — «Пять» было верно до 31.08 (P8-FIX · P8-REVIEW · P9 · P10 · P11). После P7, где долг выдачи был отложен в P8, по реестру нот легло ШЕСТЬ P-нумерованны +**Б13 — ЧУЖАЯ ЗОНА: платформенные доки** +Находки: N006, N064, N065, N066, N067, N128, N129, N130 +**Приёмка:** N065 — абзац 251–253 снести, оставив ОДНУ отсылку вверх (не второй носитель); 255–258 не сносить, только зачин; N006 — формулировка «атомарно ПОФАЙЛОВО, пара не транзакционна»; N129 — координаты пере-грепнуть (6 из 7 в реестре промахиваются). Объявление = строка в `platform/docs/platform-PROGRESS.md` ТЕМ ЖЕ коммитом. -- `N078` minor · `docs/PROGRESS.md:15` · C-исполненное-как-открытое — Условие, по которому число обещано пере-калибровать, ИСПОЛНИЛОСЬ: платный прогон отработал 31.08 и дал собственную оценку движка $1.1377277707 на деся -- `N079` minor · `docs/PROGRESS.md:146` · C-исполненное-как-открытое — Гейт пал: первый холодный прогон отработал 31.08 и выдал ровно то число, на котором строка условила решение — `families_held=0` (строка требует ослабл +## Карта ДЫР самой ревизии — записываю честно, а не молчу -- `N080` minor · `docs/PROGRESS.md:183` · B-мёртвый-указатель — Оба line-якоря строки 203 уехали после контрактного минора 0.9.0 (P12, `0a680a3`, 31.08). `openapi.yaml:1784-1787` сегодня показывает поля `id`/`units +Ревизоры сами назвали, чего не проверили. План это НЕ закрывает; пункты числятся как +«не проверено» (что не равно «опровергнуто»): -- `N081` minor · `docs/PROGRESS.md:193` · B-мёртвый-указатель — Якорь уехал на 28 строк после контрактного минора 0.9.0 (P12, 31.08): цитируемая фраза стоит на `:2079`, а `:2051` показывает заголовок совсем другой +- **30 строк бэклога веса «скоро» не проверены** ни одним агентом: 2, 4, 18, 46, 109, 113, 114, + 119, 120, 121, 134, 136, 140, 142, 143, 150, 167, 172, 182, 183, 188, 191, 207, 210, 216, 219, + 224, 225, 49, 55. Плюс ~120 строк «когда-нибудь» проверены выборочно (16 из них). +- 18 пунктов строки 243 не проверены. +- В реестре триггеров 13 проверено 59 якорей из 100+. +- `14-api-contract/README.md` прочитан частями; `openapi.yaml` — только грепами. +- `SEAM_PACK_FINDINGS.md` — НЕ_УСПЕЛ. `D15.2` — сверено два якоря из десятков. +- `FRONTEND_PLAN.md` §1–4, §5.2–5.5 не открывались. +- **Классы, которых воркфлоу не заказывал по построению:** дубли БЕЗ расхождения · вес контекста + в байтах на маршруте онбординга · сироты (входящие ссылки спрашивались только как гард) · + забаннированное историческое (анти-оверфлаг объявил его легитимным — а это самый большой класс + кандидатов на вынос) · весь `archive/` (213 файлов) · не-`.md` носители (puml, комментарии кода + и yaml) · зонные трекеры. -- `N082` minor · `docs/PROGRESS.md:207` · C-исполненное-как-открытое — Строка исполнена НАПОЛОВИНУ, и строка об этом не знает: 31.08 коммитом `94381bb` в `docs/scripts/githooks/pre-commit` добавлен warn-блок, который пере +## Что вынесено ИЗ плана +**Планы доработок в бэкенд и платформу (заказ 0б)** — отдельная работа, не критерий закрытия +техдолга доков. Носитель — CURRENT-STATE. -**Б4 — Контракт API 14 (README + openapi)** - -- `N034` major · `docs/architecture/14-api-contract/README.md:8` · A-дрейф — Канон стоит на 0.9.0 с 31.08 (минор заленджен D39.180 вместе с паком P12), а ревью-шапка всё ещё называет базой 0.8.0. Верно: зеркало 0.2.3 против кан - -- `N035` major · `docs/architecture/14-api-contract/README.md:981` · C-исполненное-как-открытое — Расхождение ЗАКРЫТО: `Resume` больше не отвечает 202 на исчерпанный прогон — вердикт `ceilingSpent` возвращает `ErrCeilingReached`, и `httpapi/v0.go:8 - -- `N036` major · `docs/architecture/14-api-contract/README.md:1637` · A-дрейф — Собственная команда дока, исполненная сегодня, даёт 26 хитов, а не ноль: платформа сжимает САМА, в Go, в `platform/internal/httpapi/conditional.go` (` - -- `N037` major · `docs/architecture/14-api-contract/README.md:1718` · B-мёртвый-указатель — Вся колонка «Откуда взят (платформа)» Приложения А-2 съехала: реальные производители лежат на 240–270 строк ниже названных, а на названных номерах сто - -- `N038` major · `docs/architecture/14-api-contract/openapi.yaml:1414` · E-второй-носитель — Деплой отвечает ТРЕМЯ значениями: `runStage` (platform/internal/pgstore/readmodel.go:576) выдаёт `re_pass` для всего пере-прохода, помимо `drafting`/` - -- `N108` minor · `docs/architecture/14-api-contract/README.md:6` · B-мёртвый-указатель — D39.169 ратифицировала минор 0.8.0, а не 0.9.0; 0.9.0 принят и заленджен нотой D39.180 (31.08). Тело того же дока (строка 69) описывает 0.9.0 вовсе БЕ - -- `N109` minor · `docs/architecture/14-api-contract/README.md:60` · B-мёртвый-указатель — Файла по этому пути нет: отчёт вынесен в архив коммитом 31b96d1 (27.08) и лежит как `docs/archive/reports/CONTRACT_MINOR_REPORT_2026-08-27.md`. Ссылка - -- `N110` minor · `docs/architecture/14-api-contract/README.md:990` · C-исполненное-как-открытое — Носитель «вход P7» израсходован — пак P7 принят D39.153, и ⚠-абзац НАД самой таблицей это уже пишет («Носитель израсходован: пак P7 принят D39.153»). - -- `N111` minor · `docs/architecture/14-api-contract/README.md:998` · B-мёртвый-указатель — Строки 148 в едином бэклоге НЕТ — её удалил `a023e39` решением владельца 21.08. Хуже: ТОТ ЖЕ документ на строке 1670 уже это знает и пишет «⚠ Прежний - -- `N112` minor · `docs/architecture/14-api-contract/README.md:1002` · B-мёртвый-указатель — Промт вынесен в архив: живой путь — `docs/archive/prompts/BACKEND_SILENT_HARM_SESSION_PROMPT_2026-08-28.md`. Дополнительно: половина того, на что ссыл - - -**Б5 — Реестр загейченных триггеров 13** - -- `N029` major · `docs/architecture/13-tech-debt-anchors.md:52` · E-второй-носитель — Это ВТОРОЙ (и единственный не-баннированный) носитель статуса строки F4 реестра памяти, и он разошёлся с кодом: `tmctl backup` существует (cmd/tmctl/m - -- `N030` major · `docs/architecture/13-tech-debt-anchors.md:72` · D-противоречие-ратифицированному — Обе половины ложны и противоречат ратифицированной D39.69 §3 («строки 27/84 ЗАКРЫТЫ … neuter end-to-end (enum · gender_neuter в injection-данных · See - -- `N031` major · `docs/architecture/13-tech-debt-anchors.md:100` · C-исполненное-как-открытое — feed_cap ЗАКРЫТ ратифицированной нотой D39.69 §1 («value-порядок отреза бюджета (частота-desc — feed_cap ЗАКРЫТ этим, из строки 86 изъят)»), и код это - -- `N032` major · `docs/architecture/13-tech-debt-anchors.md:134` · B-мёртвый-указатель — Единственная объявленная функция файла — быть справочником file:line-якорей («Этот файл — справочные file:line-якоря … Актуализируется лендингами пако - -- `N033` major · `docs/architecture/13-tech-debt-anchors.md:180` · C-исполненное-как-открытое — Гейт по письму источника ПОСТРОЕН и стоит внутри самой lintNumberMagnitude; код называет заказ по имени в комментарии — «SOURCE-script gate (строка 79 - -- `N105` minor · `docs/architecture/13-tech-debt-anchors.md:12` · E-второй-носитель — Один и тот же якорь ja-препа живёт в двух живых носителях, и копии разошлись: строка 81 бэклога уже исправлена на «migrate.go:187/194», а справочник — - -- `N106` minor · `docs/architecture/13-tech-debt-anchors.md:24` · B-мёртвый-указатель — Якоря реестра техдолга в ДВА дока моего среза сдвинулись ровно на +6 строк и не были актуализированы: коммит d2cd30c (22.08) дописал ⚠-поправки в 02-m - -- `N107` minor · `docs/architecture/13-tech-debt-anchors.md:104` · E-второй-носитель — Факт про neuter лежит в этом файле ДВАЖДЫ, и копии разошлись ВНУТРИ одного документа: тело пункта (строки 70-72) несёт пере-сверку 23.08 «ЧЕТЫРЕ пункт - - -**Б6 — Шов и эмиттер: 16, 17** - -- `N042` major · `docs/architecture/16-events-emitter.md:11` · B-мёртвый-указатель — Якорь ведёт не туда И цель больше не существует: `docs/PROGRESS.md:149` — это строка бэклога 42, а строк 103 и 165 в таблице нет вовсе (закрыты D39.13 - -- `N043` major · `docs/architecture/16-events-emitter.md:17` · B-мёртвый-указатель — Три якоря §1 («что уже есть со стороны движка», раздел БЕЗ ⚠-баннера) уехали: `cmd/tmctl/main.go:67-70` — это константы полосы отказов (`refusalLast = - -- `N044` major · `docs/architecture/16-events-emitter.md:38` · A-дрейф — Дословная цитата, которую док обещает в шапке («дословные цитаты — в кавычках-ёлочках»), в дереве больше не существует: `events.go:5-6` теперь читаетс - -- `N045` major · `docs/architecture/16-events-emitter.md:66` · B-мёртвый-указатель — ВСЕ двенадцать якорей в `platform/docs/DEFECT_REGISTER.md` мертвы — ни один не попадает в свою строку. Фактические номера: PD-60→343, PD-61→344, PD-79 - -- `N046` major · `docs/architecture/17-seam-inbound-law.md:56` · B-мёртвый-указатель — Якорь родился верным и сгнил дрейфом — и уехал на строку, ВЫГЛЯДЯЩУЮ так же. На 23.08 (коммит `4f56160`, тот же, что написал якорь) `mining.go:221` бы - -- `N047` major · `docs/architecture/17-seam-inbound-law.md:71` · C-исполненное-как-открытое — Загрузчик сида СТРОГИЙ с пака входной двери: `backend/internal/seed/decode.go` называет строку 212 своим именем и ставит `dec.KnownFields(true)` — неи - -- `N048` major · `docs/architecture/17-seam-inbound-law.md:97` · C-исполненное-как-открытое — Первая строка таблицы «Двери, УЖЕ подразумеваемые живыми строками» описывает как ПРЕДСТОЯЩУЮ ту самую дверь, которую собственная шапка дока объявляет - -- `N049` major · `docs/architecture/17-seam-inbound-law.md:100` · C-исполненное-как-открытое — Строка 231 закрыта паком «деньги» (D39.170, 29.08) и снята с бэклога — сам PROGRESS.md говорит это в лоб. Док цитирует её ДВАЖДЫ как живую: на строке - -- `N115` minor · `docs/architecture/16-events-emitter.md:3` · E-второй-носитель — Движок с 31.08 (`bb541a8`) пишет `StreamVersion = "1.2"` — минор поднят под `Finished.Volume` (D39.181 п.2). 1.1 сегодня — это версия ЧИТАТЕЛЯ платфор - -- `N116` minor · `docs/architecture/16-events-emitter.md:90` · C-исполненное-как-открытое — PD-60 (строка 88, «открыт») и PD-61 (строка 90, «открыт») оба стоят в регистре как `fixed(P6, дерево сессии)`. Шапка дока баннирует только «PD-113 отк - -- `N117` minor · `docs/architecture/16-events-emitter.md:114` · B-мёртвый-указатель — Тела D39.119/122/123 уехали в слайс `docs/archive/architecture/05-decisions-D39-106-123.md`, где всего 210 строк — номера 394/416/428/430 там не сущес - - -**Б7 — Движковые арх-доки: 03, 09, 10, 12** - -- `N023` major · `docs/architecture/03-implementation-notes.md:64` · A-дрейф — Шапка строки 3 объявляет §3-контракты «живым референсом бэкенда», а формула §3.1 не совпадает с кодом ни одним полем-разделителем: реальный RequestHas - -- `N024` major · `docs/architecture/03-implementation-notes.md:262` · D-противоречие-ратифицированному — D30 п.1 ратифицировал ПРЯМО ОБРАТНОЕ и с явным supersede: «Флип D1: редактор — БИЛИНГВАЛЬНЫЙ (черновик + исходник), новый дефолт». Реестр решений поме - -- `N026` major · `docs/architecture/09-target-architecture.md:12` · A-дрейф — Канон стоит на 0.9.0, а не на 0.8.0: минор 0.9.0 залендён 31.08 паком платформы P12 (D39.180), и ОБА названных в этой же строке носителя показывают 0. - -- `N027` major · `docs/architecture/10-prompt-architecture.md:51` · C-исполненное-как-открытое — Вопрос закрыт и ПОСТРОЕН: WS2 пака-11 отрендерил редакторский констрейнт-блок именно как src→dst-мэппинг, и D39.13 ратифицировал это дословно как «зак - -- `N028` major · `docs/architecture/10-prompt-architecture.md:52` · C-исполненное-как-открытое — Q4a ИЗМЕРЕН 18.07 и ратифицирован D39.9 («верность НЕ покупается на translate-стадии»), а топологию ролей решил эксп-21 (D39.117: full-regen, починка - -- `N092` minor · `docs/architecture/03-implementation-notes.md:212` · B-мёртвый-указатель — Ни `dc6ea8e`, ни `a32ec2e` в репозитории не существуют — `git cat-file -t` отвечает «Not a valid object name» на оба, при том что тот же вызов на реал - -- `N093` minor · `docs/architecture/03-implementation-notes.md:267` · C-исполненное-как-открытое — Запрошенный «структурный запрет» построен и ратифицирован дважды: (1) на загрузке — `config/pipeline.go:731` отказывает конфигу, где модель провайдера - -- `N095` minor · `docs/architecture/09-target-architecture.md:12` · A-дрейф — Механизмов, которых таблица не знает, уже не два: 31.08 (после последней правки дока) залендён пак D39.181/182 «закон раскрытия», добавивший в шов ДВА - -- `N096` minor · `docs/architecture/09-target-architecture.md:178` · B-мёртвый-указатель — Токен `H14-inversion-guard` в ледджере, на который §3 явно ссылается заголовком раздела, не встречается ни разу: реальный идентификатор — `H14-D38.4-t - -- `N097` minor · `docs/architecture/10-prompt-architecture.md:8` · B-мёртвый-указатель — `08-sync-audit-ledger.md` вынесен в `docs/archive/architecture/` 25.07 как отработанный (санкция владельца, `docs/archive/README.md:5`), и рядом с `09 - -- `N098` minor · `docs/architecture/10-prompt-architecture.md:17` · A-дрейф — Механизм, названный здесь как построенный, СНЯТ паком-15: пар-ключевая карта `prompts:` в конфиге сегодня не просто не используется — она отвергается - -- `N099` minor · `docs/architecture/10-prompt-architecture.md:18` · B-мёртвый-указатель — Строка 366 живого D-лога сегодня пустая и лежит внутри эры D39.135 (приёмка фронта S4, 15.08) — то есть на 250 строк мимо цели и в чужой эре. Цитируем - -- `N100` minor · `docs/architecture/10-prompt-architecture.md:33` · A-дрейф — Инвентарь пар-специфики в телах промтов неполон с 24.07: паком-13 в ОБА боевых промпта вписан блок «Единицы и приёмы (общие для zh→ru)» с зашитыми кит - -- `N101` minor · `docs/architecture/10-prompt-architecture.md:41` · C-исполненное-как-открытое — exp15 как гейт ПАЛ: эксперимент исполнен и залендён 25.07 в редакции REV.2 (D39.7), потрачено $12.79. §4 п.2 того же дока это уже фиксирует («exp15 ЗА - -- `N102` minor · `docs/architecture/12-go-style-notes.md:8` · C-исполненное-как-открытое — Норма как норма жива, но её единственный пример — мёртвый указатель, и он указывает ровно в обратную сторону. `backend/internal/pipeline/checkers_zh_r - -- `N103` minor · `docs/architecture/12-go-style-notes.md:16` · B-мёртвый-указатель — Идентификатора `glossaryBlockHeader` в дереве нет: константа с русским заголовком жила в `internal/pipeline/memory.go:504` и удалена тем же паком-14 ( - -- `N104` minor · `docs/architecture/12-go-style-notes.md:17` · A-дрейф — Фактический хвост нормы («в код … не возвращаются») сегодня ложен: в НЕтестовом Go живут 8 упоминаний, все в комментариях — `cmd/tmctl/main.go:313`, ` - - -**Б8 — Корневые и онбординг: CLAUDE.md, README, STACK, glossary, product-requirements** - -- `N022` major · `docs/README.md:13` · A-дрейф — Канон контракта уехал до 0.9.0 (заленджен 31.08, D39.180), а зонная копия стоит на 0.2.3 от 15.08 — 2999 строк против 1444. «Байт-зеркало» сегодня ЛОЖ - -- `N050` major · `docs/glossary.md:11` · C-исполненное-как-открытое — P12 принят и залёнджен 31.08 нотой D39.180 (коммит 0a680a3), обязательство D39.158 закрыто целиком, регистр: major 2 против 7. Глоссарий лежит в онбор - -- `N051` major · `docs/glossary.md:22` · E-второй-носитель — В START_PROMT.MD есть секция V6 от 18.08.2026 (идея переделки архитектуры пайплайна), разобранная в реестр 23.08 строками ПТ-37..ПТ-43. Парный носител - -- `N052` major · `docs/product-requirements.md:32` · E-второй-носитель — Число — июльские деньги ДО пере-пина цен DeepSeek 13–15.08. Два других живых носителя того же числа несут ⚠-оговорку про множитель ×4.2–4.4 (STACK.md) - -- `N053` major · `docs/product-requirements.md:56` · A-дрейф — P12 залёнджен 31.08 (0a680a3) вместе с минором, канон в дереве стоит на 0.9.0. Верно: канон 0.9.0 с 31.08 (D39.180), зеркало фронта отстаёт уже на СЕМ - -- `N054` major · `docs/product-requirements.md:63` · D-противоречие-ратифицированному — D39.147 (слово владельца 17.08) АМЕНДИРОВАЛ условие разморозки: «лендинг P7 зону БОЛЬШЕ НЕ размораживает», S5-промт не выдаётся, разморозка — отдельны - -- `N068` minor · `CLAUDE.md:8` · B-мёртвый-указатель — Путь написан от `docs/`, а файл — корневой: от корня репозитория `architecture/09-target-architecture.md` не существует; реальный путь — `docs/archite - -- `N069` minor · `CLAUDE.md:21` · B-мёртвый-указатель — Второй уцелевший docs-относительный путь, битый ДВАЖДЫ: нет префикса `docs/` И нет расширения `.md`. Реальный носитель — `docs/architecture/12-go-styl - -- `N070` minor · `CLAUDE.md:93` · A-дрейф — Число родилось верным и сгнило дрейфом. На 24.08 (ff03dca — коммит, вписавший фразу) голова, как её определяет титул D-лога («голова D39.124+ (подрезк - -- `N083` minor · `docs/README.md:20` · A-дрейф — Половина фразы верна, половина протухла. `--check` действительно гейчен составом коммита (`docs/scripts/githooks/pre-commit:74`). `--lint` НЕ гейчен н - -- `N084` minor · `docs/README.md:31` · A-дрейф — Правило не исполнено ровно для одного файла — и это тот файл, который холодная бэкенд-сессия увидит первым в `ls docs/`. `BACKEND_COLDRUN_V16_SESSION_ - -- `N085` minor · `docs/STACK.md:7` · B-мёртвый-указатель — Блок стадии draft лежит на строках 60–84 (`stages:` — 59, `- name: draft` — 60, следующая стадия `- name: edit` — 85). На заявленных 36–60 сегодня бло - -- `N086` minor · `docs/STACK.md:15` · B-мёртвый-указатель — Блок `escalation` лежит на строках 142–159 (`escalation:` — 142, `chains:` — 143, `default:` — 153, `adult:` — 154, `budget_usd: 0` — 159). На заявлен - -- `N087` minor · `docs/STACK.md:16` · B-мёртвый-указатель — Указатель РОДИЛСЯ ВЕРНЫМ 09.08 (085dbb9): строка 6 `docs/PROGRESS.md` тогда читалась «**Открыто на владельце:** прогон Sol-пакетов руками (150) · кред - -- `N118` minor · `docs/glossary.md:7` · B-мёртвый-указатель — В файле docs/ORCHESTRATOR_SESSION_PROMPT.md (240 строк) символа «№» нет НИ РАЗУ: его шапка намеренно номер не держит — «Счётчик ролей ОДИН, номер выда - -- `N119` minor · `docs/product-requirements.md:50` · B-мёртвый-указатель — Сам факт ЖИВ (проверено кодом, см. positive_control), но указатель «очередь №20» умер дважды: роль передана №20→№21 (D39.177, 31.08) и №21→№22 (D39.18 - -- `N120` minor · `docs/product-requirements.md:57` · B-мёртвый-указатель — Якорь РОДИЛСЯ верным 02.08 (fbefe33: тогда 89-93 были ровно комментарием «the --pairs column aligned byte-for-byte to how the edit wave concatenated t - - -**Б9 — Ранние арх-доки 01/04/06 — баннеры или архив** - -- `N025` major · `docs/architecture/06-memory-risk-registry.md:81` · B-мёртвый-указатель — Колонка «Где в коде/схеме» реестра указывает шесть file:line-якорей, и пять из шести уехали: реальные адреса — MessagesWithInjection render.go:246 (а - -- `N088` minor · `docs/architecture/01-decisions.md:19` · A-дрейф — Каталога `pkg/` в бэкенде нет вовсе; план исполнен под `internal/`: internal/llm, internal/ledger, internal/obs, internal/store. Имена совпали, префик - -- `N089` minor · `docs/architecture/01-decisions.md:39` · A-дрейф — FTS5 в движке нет НИ на горячем, ни на холодном пути: во всём backend/ ноль виртуальных таблиц и единственное упоминание FTS5 — отрицание в комментари - -- `N090` minor · `docs/architecture/01-decisions.md:55` · B-мёртвый-указатель — Обоих коммитов в истории репозитория НЕ существует (`git cat-file -t c7f8a95` → «Not a valid object name»). Реальные коммиты того же дня и того же сод - -- `N091` minor · `docs/architecture/01-decisions.md:93` · A-дрейф — Отдельного файла БД под банк памяти НЕТ и не было построено: у книги ровно ОДНА база `.db` (config/book.go:189), и банк памяти живёт в ней же - -- `N094` minor · `docs/architecture/04-unhappy-paths.md:120` · C-исполненное-как-открытое — Паспорт качества главы ПОСТРОЕН и отдаётся `tmctl status`: тип `pipeline.ChapterPassport` (internal/pipeline/status.go:157-159, «the per-chapter quali - - -**Б10 — ЧУЖАЯ ЗОНА (санкция владельца 01.09): бэкенд-доки** - -- `N007` major · `backend/README.md:11` · A-дрейф — Перечень команд читается исчерпывающим и отстал на ТРИ верба из одиннадцати: `backup`, `seed-lint`, `bank-apply`. `bank-apply` — это ратифицированная - -- `N008` major · `backend/README.md:24` · A-дрейф — Машинной стоп-таблицы (`.bank-stop.json`) в движке НЕТ — её снёс пак «входная дверь шва» (d1eb8a9, ратифицирован D39.158); уцелевшая `.bank-stop.txt` - -- `N009` major · `backend/README.md:82` · D-противоречие-ратифицированному — От `$HOME` НЕ дефолтится НИЧЕГО. `internal/standdata` резолвит корпус ТОЛЬКО от корня репозитория (`/books`), и его собственная шапка говорит пр - -- `N010` major · `backend/README.md:99` · C-исполненное-как-открытое — Долг ПОГАШЕН и это записано в двух носителях. Код по обоим якорям сегодня говорит обратное README: repair.go:131 — «The boundary is the TARGET's decla - -- `N011` major · `backend/README.md:101` · B-мёртвый-указатель — Строка 53 в едином трекере не существует: она закрыта D39.82 (носитель этого сказан в самой таблице — строка 111: «D39.82 закрыл строку 53 через `putU - -- `N012` major · `backend/docs/D15.2-content-addressed-resume-spec.md:529` · A-дрейф — Голова схемы сегодня v16, а не v7, и номер v8 давно занят чужой миграцией. Спека при этом ЖИВА: строка бэклога 49 («Этапы Б+В спеки D15.2») открыта, и - -- `N071` minor · `backend/README.md:20` · B-мёртвый-указатель — Экспортированной `chunk.Ingest` не существует — в пакете живёт неэкспортированная `ingest(p string)`, обёртка над `IngestEncoded`. Родилась ошибка в Т - -- `N072` minor · `backend/README.md:94` · B-мёртвый-указатель — README объявляет, что все его «строка N» разрешаются в таблицу «Бэклог», и цитирует тринадцать номеров. Семь из них в таблице отсутствуют — закрыты и - -- `N073` minor · `backend/docs/MONEY_HONESTY_REPORT.md:787` · C-исполненное-как-открытое — Заленджено — коммитом bb541a8 (31.08) и принято нотой D39.182. Фраза стоит в секции, которая сама называет себя «§11. Последний абзац — СОСТОЯНИЕ, не - -- `N074` minor · `backend/docs/SEAM_FIX3_PLAN.md:1` · F-целиком-историческое — Четыре записки-плана паков шва — чистые списки намерений отработавших сессий. Паки приняты и заленджены (D39.158: «Четыре пака зоны бэкенда приняты и - - -**Б11 — ЧУЖАЯ ЗОНА (санкция владельца 01.09): фронт-доки** - -- `N055` major · `frontend/README.md:7` · A-дрейф — S4 исполнен 14.08 и ПРИНЯТ 15.08 (D39.135, `267aa35`): библиотека, интейк, форма запуска прогона, настройки, читалка — построенные экраны. Перечислени - -- `N056` major · `frontend/README.md:66` · D-противоречие-ратифицированному — Зеркало НЕ байт-равно: 0.2.3/16 операций против канона 0.9.0/20 операций, разные md5. И правило «зеркало = байт-копия» ратифицированно ПРИОСТАНОВЛЕНО - -- `N057` major · `frontend/docs/BACKLOG.md:50` · D-противоречие-ратифицированному — D39.144 (16.08) снял гейт полноты. Канон: `awaiting_bank` с ЛЮБЫМ состоянием решений → 202. Якорь `mining.go:201` тоже уехал: там сегодня стоит прямая - -- `N058` major · `frontend/docs/BACKLOG.md:51` · B-мёртвый-указатель — В каноне 0.9.0 нет ни схемы `BankDecision`, ни значения `promote`, ни пути `POST …/bank/decisions`, ни «частичного накопления»: канон 0.5.0 снёс их и - -- `N059` major · `frontend/docs/BACKLOG.md:62` · C-исполненное-как-открытое — Канон 0.9.0 перевесил поток на КНИГУ: `GET /books/{bookId}/events`, и его описание прямо называет причину — «a book is received and cut into chapters - -- `N060` major · `frontend/docs/BACKLOG.md:63` · C-исполненное-как-открытое — Ручка построена: канон 0.9.0 даёт `GET /capabilities` с обязательным `language_pairs` («Every pair the deployment knows about, unavailable ones includ - -- `N061` major · `frontend/docs/BACKLOG.md:68` · C-исполненное-как-открытое — Канон 0.9.0 несёт и `PATCH /books/{bookId}` (merge patch, только `title`, 409 при живом прогоне), и `DELETE /books/{bookId}` (204, 409 при живом прого - -- `N062` major · `frontend/docs/FRONTEND_PLAN.md:692` · D-противоречие-ратифицированному — Ратифицированный канон 0.9.0 кладёт на провод ОДНУ пару {done,total}, считаемую в ГЛАВАХ по всем проходам, плюс открытую строку `stage`. Клиент, напис - -- `N063` major · `frontend/docs/STACK_DECISIONS.md:245` · C-исполненное-как-открытое — Все ЧЕТЫРЕ пункта («пофазный прогресс», «персист манифеста глав и чанков», «JSON-выход таблицы подписи банка», «приём внешнего TRACEPARENT в cmd/tmctl - -- `N121` minor · `frontend/README.md:21` · A-дрейф — Маршрутов ВОСЕМЬ: S4 завела `/intake`, и он есть в обоих замкнутых тестом списках. Приёмка S4 в том же дереве печатает «8 маршрутов, 11 сценариев». - -- `N122` minor · `frontend/docs/BACKLOG.md:3` · B-мёртвый-указатель — Строк 99–102 в живом `docs/PROGRESS.md` больше нет — все четыре закрыты и по норме журнала («закрытые строки здесь НЕ хранятся») ушли в D-лог. Шапка у - -- `N123` minor · `frontend/docs/BACKLOG.md:23` · B-мёртвый-указатель — Токена `--color-hint` в зоне больше нет вовсе — ни в `tokens.css`, ни в тестах, ни в коде; строка держит хвост на несуществующем токене. Вторая полови - -- `N124` minor · `frontend/docs/FRONTEND_SESSION_PROMPT.md:2` · B-мёртвый-указатель — `S3_SESSION_PROMPT.md` в зоне не существует: промты S3/S3.5/S3.7 архивированы 14.08 в `docs/archive/prompts/FRONTEND_S3*`, зонные копии сняты. Довод б - -- `N125` minor · `frontend/docs/STACK_DECISIONS.md:111` · B-мёртвый-указатель — Зона зависит от пакета `playwright` 1.62.1, а не от `@playwright/test`: последнего нет ни в `package.json`, ни в дереве `node_modules`. Сессия, постав - -- `N126` minor · `frontend/docs/STACK_DECISIONS.md:116` · E-второй-носитель — Шагов у `check` шесть, и spectral — ЧЕТВЁРТЫЙ. Тот же файл на строке 148 уже утверждает «шесть шагов», то есть носитель противоречит сам себе; вторая - -- `N127` minor · `frontend/docs/frontend-PROGRESS.md:221` · C-исполненное-как-открытое — Приёмка S4 состоялась 15.08 и записана В ЭТОМ ЖЕ ФАЙЛЕ строкой 465 («ПРИНЯТО и залендено `267aa35`, D39.135»). Два места одного файла говорят разное. - - -**Б12 — ЧУЖАЯ ЗОНА (санкция владельца 01.09): платформенные доки** - -- `N006` блокер · `platform/docs/STACK_DECISIONS.md:414` · D-противоречие-ратифицированному — Строка (и парная ей 415 про `mined_rejects`) неверна в четырёх местах сразу. (1) `mined_delta` БОЛЬШЕ НЕ «путь из book.yaml»: ключ РЕТИРОВАН — `Retire - -- `N064` major · `platform/README.md:10` · A-дрейф — Перечень устарел с лендингом пака sqlc 29.08: `check: build vet fmt lint sqlc-check` — пятый шаг `sqlc-check` в списке отсутствует. Хуже перечня — умо - -- `N065` major · `platform/deploy/README.md:252` · C-исполненное-как-открытое — Глагол `tmctl migrate` существует и заленджен (`backend/cmd/tmctl/migrate.go`, в диспетчере `main.go:184/:245`, свой код выхода `exitSchemaMismatch = - -- `N066` major · `platform/docs/PLATFORM_DIRECTION.md:159` · C-исполненное-как-открытое — sqlc ВЗЯТ и заленджен 29.08 (`D39.172`: «Пак `sqlc` принят и заленджен: 40 запросов пяти файлов `pgstore` на типизированный слой, пин 1.31.1, `sqlc di - -- `N067` major · `platform/docs/STACK_DECISIONS.md:621` · B-мёртвый-указатель — Лечебная команда «самой частой грабли стенда» указывает на путь, которого не существует: `tm-runs.slice` по конвенции имён systemd вложен в `tm.slice` - -- `N128` minor · `platform/deploy/README.md:428` · C-исполненное-как-открытое — Воркер существует с пака P4: прогон спавнится в `internal/runs/spawn.go`, который задаёт `Workdir: l.Workdir` транзиентному юниту, а корень УЖЕ настро - -- `N129` minor · `platform/docs/PLATFORM_DIRECTION.md:101` · B-мёртвый-указатель — Указатели на строки единого бэклога, розданные по всему срезу, в шести случаях целят в строки, которых в живой таблице БОЛЬШЕ НЕТ: правило бэклога — « - -- `N130` minor · `platform/docs/STACK_DECISIONS.md:566` · E-второй-носитель — Перечень админ-команд отстал от CLI на семь глаголов. В диспетчере `cmd/tmplatformctl/main.go` сегодня: grant · adjust · balance · logins · revoke · b - - -## Сверх находок — работы заказа, которых в реестре нет - -Реестр отвечает на «что протухло». Заказ владельца шире, и эти пункты в него не попали: - -- **Б13. Архивация отработавшего (0в).** Промт `BACKEND_COLDRUN_V16_SESSION_PROMPT.md` лежит в - живом `docs/`, при этом ДВА носителя (таблица `README.md` и CURRENT-STATE) уже утверждают, что - он в `archive/prompts/`. Плюс исполненные план-доки зон: `backend/docs/SEAM_*`, - `MONEY_HONESTY_*`, `platform/docs/p8-review/`, `platform/docs/p9/`. -- **Б14. Планы доработок в бэкенд и платформу (0б).** По остатку единого бэклога (189 строк, - 59 «скоро») и регистра платформы (435 строк, 97 открытых, major 2). -- **Б15. Хвост лендинга ресёрча.** Очередь п.5 велит новой смене лендить файл, лендинг которого - владелец 01.09 откатил (`162ab9b`). Пока формулировка стоит, следующая смена повторит откат. - **Формулировку задаёт владелец — сам не переписываю.** +**Хвост лендинга ресёрча.** Очередь п.5 велит новой смене лендить файл, лендинг которого владелец +01.09 откатил (`162ab9b`). Пока формулировка стоит, следующая смена повторит откат. +**Формулировку задаёт владелец — сам не переписываю.** diff --git a/docs/DOC_REVISION_FINDINGS.md b/docs/DOC_REVISION_FINDINGS.md index a616e83d..b6509493 100644 --- a/docs/DOC_REVISION_FINDINGS.md +++ b/docs/DOC_REVISION_FINDINGS.md @@ -1,134 +1,274 @@ # РЕЕСТР НАХОДОК РЕВИЗИИ ДОКОВ — 01.09.2026, оркестратор №22 -Источник: воркфлоу `docs-staleness-revision-A`, 15 срезов, 30 агентов, 1432 клейма проверены -исполнением, 115 положительных контролей. Найдено 137, адверсариальный пере-суд снял 7. -**Живых находок: 130** — блокеров 6, major 61, minor 63. -Колонка ВЕРДИКТ: ПОДТВЕРЖДЕНО = прошло адверсариальный пере-суд; (пусто) = не выносилось на пере-суд -(не разрушающее и не блокер) — такая находка проверена ОДНИМ агентом и на доверии не исполняется. + +> **⚠ РЕДАКЦИЯ 2. Первая была собрана с дефектом и отозвана.** Вердикты опровергателей +> присваивались по паре `(файл, строка)`, а на одной строке сидит до ШЕСТИ находок +> (`docs/PROGRESS.md:3` — шесть), поэтому все они получали поправку ПОСЛЕДНЕЙ. Нашёл контролёр +> Fable 5.1, проверено моей командой: 10 находок стояли на неоднозначных ключах. Здесь матч идёт +> **по дословной цитате**; 82 вердикта присвоены 82 разным находкам, дублей 0. +> +> **ГЛАВНОЕ ПРАВИЛО ИСПОЛНЕНИЯ, которого в редакции 1 не было:** +> **где есть «⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ» — исполняется ОНА, а не «Предложенное действие».** +> Поправка есть у **67 находок из 130**, и часть поправок ПРОТИВОПОЛОЖНА +> исходному действию («не закрывать, а сузить» · «не переносить, а забаннерить» · «не строить — +> уже построено» · «этот носитель НЕ ТРОГАТЬ»). Исполнение по «Предложенному действию» там, где +> стоит поправка, — дефект. +> +> **ВТОРОЕ ПРАВИЛО:** не вписывать в доки НОВЫЕ голые `file:line` — они сгниют так же. +> Форма — токен-якорь `` файл:строка=`подстрока` `` либо ссылка на символ. + +## Как читать вес находки + +- **«Вердикт: ПОДТВЕРЖДЕНО»** — находка прошла адверсариальный пере-суд отдельным агентом. + Таких 75. +- **«Вердикт: НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ»** — проверена ОДНИМ агентом (правило воркфлоу: на + пере-суд шли только разрушающие, блокеры и класс F). Таких 55. + ⚠ Это **наряд на перепроверку, а не истина**: перед правкой верифицировать своей командой. + +Добыто воркфлоу `docs-staleness-revision-A`: 15 срезов × (ревизор + опровергатель), 30 агентов, +0 отказов, **1432 утверждения проверены исполнением**, 115 положительных контролей. +Найдено 137 → пере-суд снял 7 → **живых 130** (блокеров 6, +major 61, minor 63). +Снятые семь — `DOC_REVISION_REFUTED.md`, **их чинить нельзя**. --- ## N001 · БЛОКЕР · `docs/PROGRESS.md:176` · C-исполненное-как-открытое -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** их деньги не входят НИ в число согласия, НИ в `projected_book_usd`, при этом на сдвиге снапшота они реально пере-покупаются +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> их деньги не входят НИ в число согласия, НИ в `projected_book_usd`, при этом на сдвиге снапшота они реально пере-покупаются + **Почему протухло:** Строка 194 ЗАКРЫТА лендингом 31.08 (`bb541a8`, экземпляр A10 закона раскрытия): `projectBookUSD(...) + r.bankRoleCommittedUSD()` уже стоит в `status.go:801`, а посадочный тест `bankrolemoney_test.go` в первой же строке комментария называет себя «backlog row 194». Вдобавок ОСЬ строки опровергнута живыми данными: пере-покупка терминолога наступает не «на сдвиге снапшота», а от БАЙТОВ БАТЧА (пинг №2 той же сессии, `docs/PROGRESS.md:344`). -**Улика — команда:** `grep -rn 'bankRoleCommittedUSD' backend --include=*.go; sed -n '13,18p' backend/internal/pipeline/bankrolemoney_test.go` -**Улика — вывод:** backend/internal/pipeline/rebill.go:527:func (r *Runner) bankRoleCommittedUSD() float64 { + +**Улика — команда:** +``` +grep -rn 'bankRoleCommittedUSD' backend --include=*.go; sed -n '13,18p' backend/internal/pipeline/bankrolemoney_test.go +``` +**Улика — вывод:** +``` +backend/internal/pipeline/rebill.go:527:func (r *Runner) bankRoleCommittedUSD() float64 { backend/internal/pipeline/status.go:801: rep.ProjectedBookUSD = projectBookUSD(units, byChunk, len(draftStages), len(editStages), rp) + r.bankRoleCommittedUSD() backend/internal/pipeline/bankrolemoney_test.go:30:// Mutation this catches: drop the `+ r.bankRoleCommittedUSD()` addend and the projection stops covering --- // bankrolemoney_test.go: backlog row 194 — the money of the BANK ROLES was outside every projection an // operator decides on. +``` **Другие носители того же факта:** docs/PROGRESS.md:306 (секция «Бэкенд» той же сессии: «A10 (контур банк-ролей в проекции, `committed_usd` не сдвинулся — предъявлено тестом)»), docs/PROGRESS.md:344 (пинг №2: ось строки 194 опровергнута), backend/docs/DISCLOSURE_LAW_DESIGN.md:447 («A10 — деньги банковых ролей вне контура согласия (строка 194)»), backend/docs/MONEY_HONESTY_REPORT.md -**Предложенное действие:** Закрыть строку 194 нотой лендинга пака «деньги и честность» (экземпляр A10) и убрать её из таблицы; в теле ноты сохранить опровержение оси — пере-покупка терминолога идёт от байтов батча, а не от сдвига снапшота. + +**Предложенное действие (автора находки):** Закрыть строку 194 нотой лендинга пака «деньги и честность» (экземпляр A10) и убрать её из таблицы; в теле ноты сохранить опровержение оси — пере-покупка терминолога идёт от байтов батча, а не от сдвига снапшота. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Закрыть строку 194 — верно, но в ноту закрытия обязаны войти ДВА опровержения её текста, а не одно. Кроме оси «сдвиг снапшота» (опровергнута пингом №2, `docs/PROGRESS.md:344` — пере-покупка идёт от БАЙТОВ БАТЧА) ЛОЖНА и первая половина клейма «не входят НИ в число согласия»: тест утверждает раздельно, что `committed_usd` = `SUM(checkpoints)` НЕ сдвинулся, потому что деньги банк-ролей были в нём ВСЕГДА (spend и checkpoints пишутся одной транзакцией) — слепа была только ПРОЕКЦИЯ. Плюс назвать честную границу из §9.9: в проекцию добавлено УЖЕ ПОТРАЧЕННОЕ, а не прогноз контура, то есть проекция стала НИЖНЕЙ границей. + +**Разбор опровергателя:** Пытался опровергнуть тремя путями и не смог. (1) Код в РАБОЧЕМ ДЕРЕВЕ: слагаемое `r.bankRoleCommittedUSD()` стоит в `status.go:801`, посадка `bankrolemoney_test.go` называет строку 194 первой строкой файла. (2) Тест РЕАЛЬНО ЗЕЛЁНЫЙ — я его прогнал, а не поверил комментарию. (3) Лендинг ратифицирован: `bb541a8` в `git log`, нота D39.182 в живом журнале решений. Диспозиция строки («расширение проекции на банковые роли + тест») исполнена буквально — отчёт `backend/docs/MONEY_HONESTY_REPORT.md:107` подписывает «A10 строка 194 | предъявлено», §9.9 называет обе посадки. Строка при этом до сих пор стоит в таблице со статусом «скоро». Класс C подтверждён. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Закрыть строку 194 — верно, но в ноту закрытия обязаны войти ДВА опровержения её текста, а не одно. Кроме оси «сдвиг снапшота» (опровергнута пингом №2, `docs/PROGRESS.md:344` — пере-покупка идёт от БАЙТОВ БАТЧА) ЛОЖНА и первая половина клейма «не входят НИ в число согласия»: тест утверждает раздельно, что `committed_usd` = `SUM(checkpoints)` НЕ сдвинулся, потому что деньги банк-ролей были в нём ВСЕГДА (spend и checkpoints пишутся одной транзакцией) — слепа была только ПРОЕКЦИЯ. Плюс назвать честную границу из §9.9: в проекцию добавлено УЖЕ ПОТРАЧЕННОЕ, а не прогноз контура, то есть проекция стала НИЖНЕЙ границей. --- ## N002 · БЛОКЕР · `docs/PROGRESS.md:200` · C-исполненное-как-открытое -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** при том же снапшоте строка идёт как «$0 resume» без сверки — `translate` на такой книге перекупает юниты БЕЗ предупреждения в смете +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> при том же снапшоте строка идёт как «$0 resume» без сверки — `translate` на такой книге перекупает юниты БЕЗ предупреждения в смете + **Почему протухло:** Строка 238 ЗАКРЫТА тем же лендингом 31.08 (`bb541a8`, экземпляр A6 «проекция видит правку исходника»). Посадка `rebillsource_test.go` называет строку по номеру и описывает ровно её сценарий; мутация, которую тест ловит, — возврат голого `if cs.SnapshotID == cur { continue }`, то есть именно того кода, о слепоте которого писала строка. -**Улика — команда:** `sed -n '13,27p' backend/internal/pipeline/rebillsource_test.go` -**Улика — вывод:** // rebillsource_test.go: backlog row 238 — the re-payment projection was blind to an in-place SOURCE edit. + +**Улика — команда:** +``` +sed -n '13,27p' backend/internal/pipeline/rebillsource_test.go +``` +**Улика — вывод:** +``` +// rebillsource_test.go: backlog row 238 — the re-payment projection was blind to an in-place SOURCE edit. // The cold run of 31.08 did not run this probe on purpose ... so the defect stood as a code reading. This is that reading turned into a fixture. // ... the projection's same-snapshot branch used to wave them through as «$0 resume» ... // TestTheProjectionSeesAnInPlaceSourceEdit is the landing. // Mutation this catches: restore the bare `if cs.SnapshotID == cur { continue }` and RebillUnits drops to zero while `translate` still charges +``` **Другие носители того же факта:** docs/PROGRESS.md:282 (шапка секции «Бэкенд»: «Свободная работа зоны — строками бэклога: 238 … · 239 …»), docs/PROGRESS.md:306 (та же секция: «A6 (проекция видит правку исходника)»), backend/docs/MONEY_HONESTY_REPORT.md (таблица касаний, `internal/pipeline/rebill.go` — A6) -**Предложенное действие:** Закрыть строку 238 нотой лендинга (A6) и убрать её из таблицы; одновременно снять её из списка «свободной работы зоны» в шапке секции «Бэкенд» (docs/PROGRESS.md:282), где она предлагается как незанятая. + +**Предложенное действие (автора находки):** Закрыть строку 238 нотой лендинга (A6) и убрать её из таблицы; одновременно снять её из списка «свободной работы зоны» в шапке секции «Бэкенд» (docs/PROGRESS.md:282), где она предлагается как незанятая. + +**Разбор опровергателя:** Опровергнуть не удалось. Тест `TestTheProjectionSeesAnInPlaceSourceEdit` есть в рабочем дереве и ЗЕЛЁНЫЙ у меня в руках, а не по комментарию. Отчёт пака подписывает A6 «предъявлено» с двумя посадками (§9.6: ветка совпавшего снапшота теперь сверяет контент-хеш тем же предикатом, что применяет сам прогон, плюс дешёвый зонд валидности манифеста). Приёмка нашла в самой починке блокер 2 (зонд был мёртв на денежной поверхности) и он ЗАКРЫТ до лендинга — это в теле D39.182 п.(б) дословно. Второй носитель тоже верен: строки 238 и 239 действительно предлагаются как «свободная работа зоны» на `docs/PROGRESS.md:282`, то есть протухли ДВА места, а не одно. + **Разрушающее:** True --- ## N003 · БЛОКЕР · `docs/PROGRESS.md:201` · C-исполненное-как-открытое -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** `Status.checkWave` не видит исчезнувшую из конфига стадию +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> `Status.checkWave` не видит исчезнувшую из конфига стадию + **Почему протухло:** Строка 239 ЗАКРЫТА лендингом 31.08 (`bb541a8`, экземпляр A7+A12). Правило `exportConfigDrift` не «перенесено», а сведено в ОДНО общее определение `orphanStageRows`, и `status.go` зовёт его прямо рядом с `checkWave`, комментарием называя строку 239 по номеру. Тест-половина заказа тоже есть (пак сдал 33 посадки, `driftbasis_test.go`). -**Улика — команда:** `sed -n '735,741p' backend/internal/pipeline/status.go; grep -rn 'orphanStageRows' backend --include=*.go | head -4` -**Улика — вывод:** checkWave(draftSnaps, waveDraft) + +**Улика — команда:** +``` +sed -n '735,741p' backend/internal/pipeline/status.go; grep -rn 'orphanStageRows' backend --include=*.go | head -4 +``` +**Улика — вывод:** +``` + checkWave(draftSnaps, waveDraft) checkWave(editSnaps, waveEdit) // A stored row for a stage the current pipeline does not run — the rule `export` has had all // along and this surface did not, reproduced verbatim on the cold run (backlog row 239). ONE // definition, shared: orphanStageRows. if stage, orphan := orphanStageRows(statuses, draftStageNames, editStageNames); orphan { rep.ConfigDrift = true +``` **Другие носители того же факта:** docs/PROGRESS.md:282 (список свободной работы зоны), docs/PROGRESS.md:306 (той же секцией объявлено исполненным), backend/docs/MONEY_HONESTY_REPORT.md (строки таблицы `internal/pipeline/status.go` и `export.go` — A7+A12) -**Предложенное действие:** Закрыть строку 239 нотой лендинга (A7+A12) и убрать её из таблицы и из списка свободной работы зоны в docs/PROGRESS.md:282. + +**Предложенное действие (автора находки):** Закрыть строку 239 нотой лендинга (A7+A12) и убрать её из таблицы и из списка свободной работы зоны в docs/PROGRESS.md:282. + +**Разбор опровергателя:** Опроверг только ОДНУ деталь формулировки находки и она не меняет вердикта: правило не «перенесено», а сведено в общее определение `orphanStageRows` (`driftbasis.go:83`), которое зовут ОБЕ поверхности; при этом символ `exportConfigDrift`, названный в самой строке бэклога, жив — то есть мёртвого указателя тут нет. По существу же строка исполнена: `status.go:740` зовёт правило прямо в блоке `checkWave` и комментарием называет строку 239 по номеру, посадки есть (`driftbasis_test.go` + §9.7 отчёта: 4 посадки плюс две в `cmd/tmctl`), тесты у меня зелёные. D39.182 в примечаниях сам называет строку 239 «болезнью, воспроизведённой в лекарстве от неё» и говорит, что она сведена к общей предпосылке `driftCheckable` — то есть лендинг про эту строку знает, а таблица нет. + **Разрушающее:** True --- ## N004 · БЛОКЕР · `docs/architecture/15-money-path.md:31` · D-противоречие-ратифицированному -**Срез:** Денежный путь — docs/architecture/15-money-path.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** реальная цена главы замерена по движковым леджерам — средняя **$0.0115–$0.0190**, максимум одной главы $0.0375, то есть константа завышена в **1.6–2.6×** … и «купить 10 глав» отдаёт движку $0.30, покупающий ~16–26 глав +**Срез:** Денежный путь — docs/architecture/15-money-path.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> реальная цена главы замерена по движковым леджерам — средняя **$0.0115–$0.0190**, максимум одной главы $0.0375, то есть константа завышена в **1.6–2.6×** … и «купить 10 глав» отдаёт движку $0.30, покупающий ~16–26 глав + **Почему протухло:** Числа взяты из D39.165 §1 (28.08) и меряны на леджерах ДО ценового перелома. D39.179 п.1 (31.08) ратифицировал: DeepSeek пере-пинен под цены, вступившие 16.08 16:00 UTC, множитель к июльским деньгам ×4.47. Мой замер по леджеру coldrun-v16 (прогон 31.08, сегодняшние цены, $0, только чтение): полная цепь главы 1 = $0.092934, главы 2 = $0.150865, а ТОЛЬКО черновик главы 8 = $0.076785 — то есть даже черновик вдвое дороже названного в доке АБСОЛЮТНОГО максимума $0.0375. Знак утверждения перевернулся: константа $0.03 сегодня ЗАНИЖЕНА примерно втрое, $0.30 покупает ~3 главы, а не 16–26, и ⛔ «константу по сегодняшним числам не калибровать» защищает ставку НИЖЕ себестоимости. ‘Исторический’ баннер над провенансом не спасает: вывод про ручку «главы» стоит в НАСТОЯЩЕМ времени как живое описание дефекта PD-410. -**Улика — команда:** `sqlite3 -header "file:/home/ubuntu/projects/textmachine-main/books/gu-zhenren/coldrun-v16/guzhenren-coldrun-v16.db?mode=ro" "select j.chapter, round(sum(c.cost_usd),6) usd, group_concat(distinct c.stage) stages from checkpoints c join jobs j on j.id=c.job_id where j.chapter in (1,2,8) group by j.chapter;"` -**Улика — вывод:** chapter|usd|stages + +**Улика — команда:** +``` +sqlite3 -header "file:/home/ubuntu/projects/textmachine-main/books/gu-zhenren/coldrun-v16/guzhenren-coldrun-v16.db?mode=ro" "select j.chapter, round(sum(c.cost_usd),6) usd, group_concat(distinct c.stage) stages from checkpoints c join jobs j on j.id=c.job_id where j.chapter in (1,2,8) group by j.chapter;" +``` +**Улика — вывод:** +``` +chapter|usd|stages 1|0.092934|draft,edit 2|0.150865|draft,edit 8|0.076785|draft Ратифицированная эррата (docs/architecture/05-decisions-log.md:1667, D39.179 п.1): «Прежняя цифра очереди «≈$0.05» СНЯТА как протухшая: DeepSeek пере-пинен коммитом 15.08 (76049bb, D39.137) под цены, вступившие в силу 16.08 16:00 UTC; заход bank-low ($0.045095 в июльских деньгах) по ним стоит $0.2017 — множитель 4.47» +``` **Другие носители того же факта:** docs/architecture/05-decisions-log.md:1018 и :1020 — тело D39.165 §1, первоисточник чисел (по диете D-лога тела не переписываются, эррата живёт в D39.179 — править там НЕ надо), platform/docs/DEFECT_REGISTER.md:28 — PD-410, пере-диспозиционирована паком P12 по тем же июльским числам (чужая зона — пингом) -**Предложенное действие:** Заменить баннер провенанса на: «⚠ Числа D39.165 §1 — ИЮЛЬСКИЕ ДЕНЬГИ и с 16.08 не действуют: D39.179 п.1 ратифицировал множитель ×4.47 к ценам, вступившим 16.08 16:00 UTC. Пере-замер по леджеру coldrun-v16 (31.08, $0): полная цепь главы $0.093–$0.151, один черновик главы до $0.077. Значит константа $0.03 сегодня ЗАНИЖЕНА ~втрое, а не завышена в 1.6–2.6×, и «купить 10 глав» = $0.30 покупает ~3 главы, а не 16–26. Направление лечения (цена от объёма исходника + объёмный потолок в движке) не меняется, но ЗНАК дефекта PD-410 обратен записанному: ручка не пере-продаёт, а НЕДО-продаёт.» Добавить D39.179 в карту §6 и отпинговать зоне платформы PD-410. + +**Предложенное действие (автора находки):** Заменить баннер провенанса на: «⚠ Числа D39.165 §1 — ИЮЛЬСКИЕ ДЕНЬГИ и с 16.08 не действуют: D39.179 п.1 ратифицировал множитель ×4.47 к ценам, вступившим 16.08 16:00 UTC. Пере-замер по леджеру coldrun-v16 (31.08, $0): полная цепь главы $0.093–$0.151, один черновик главы до $0.077. Значит константа $0.03 сегодня ЗАНИЖЕНА ~втрое, а не завышена в 1.6–2.6×, и «купить 10 глав» = $0.30 покупает ~3 главы, а не 16–26. Направление лечения (цена от объёма исходника + объёмный потолок в движке) не меняется, но ЗНАК дефекта PD-410 обратен записанному: ручка не пере-продаёт, а НЕДО-продаёт.» Добавить D39.179 в карту §6 и отпинговать зоне платформы PD-410. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Находка верна, предложенный текст — нет, он ставит под удар ровно то, чем прошлая смена уже обожглась. Три правки. (1) НЕ объявлять $0.093–$0.151 ценой главы: это n=2 главы из десяти, прогон остановлен снапшот-гардом (редакторская волна 3/14), и сам отчёт запрещает такое употребление дословно — «Чего замер НЕ должен делать: калибровать ставку платформы. Она гейчена строкой 202 и требует ЗАВЕРШЁННОЙ цепи» (COLDRUN_V16_REPORT_2026-08-31.md:642). Прогон вдобавок нёс объявленные девиации (черновик шёл БАНКНОТНЫМ промптом вместо конвенционного `translator.md`; `escalation.budget_usd` поднят 0.015 → 0.08), спред цены на юнит ×7.6, а причина, по которой редактор съел 73% захода, НЕ УСТАНОВЛЕНА. (2) Несущим доводом сделать РАТИФИЦИРОВАННУЮ арифметику, которая ни от чего этого не зависит: «Числа D39.165 §1 — ИЮЛЬСКИЕ (сама нота, ⛔г: прогоны 10–11.07, редактор другой моделью). D39.179 п.1 ратифицировал ×4.47 к ценам с 16.08 16:00 UTC ⇒ та же средняя сегодня $0.051–$0.085, тот же максимум $0.168. Константа $0.03 НЕ завышена — она ниже даже пере-считанной средней, и «купить 10 глав» = $0.30 покупает единицы глав, а не 16–26.» Замер холодного прогона добавить ОДНОЙ строкой как иллюстрацию с оговоркой n=2: движковая проекция полной цепи тех же десяти глав `projected_book_usd = 1.1377277707` (≈$0.114/глава, `docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:70`) и консервативный пол по ok-вызовам $0.0716/$0.0783. (3) Автор пропустил ВТОРОЙ ЖИВОЙ НОСИТЕЛЬ, и без него класс E разъедется снова: `docs/architecture/17-seam-inbound-law.md:101` несёт те же июльские числа («цена главы измерена ($0.0115–0.0190 против константы продажи $0.03)») — чинить в одной правке. Строка реестра `docs/architecture/05-decisions-index.md:229` их тоже повторяет, но она зеркало ноты: там достаточно указателя на D39.179. И уточнить формулировку про PD-410: строку регистра я прочёл — она несёт только (а)(б)(в)(г) и НЕ содержит ни «16–26 глав», ни «завышена в 1.6–2.6×», поэтому «знак дефекта PD-410 обратен записанному» — перебор; перевёрнуто СЛЕДСТВИЕ в D39.165 §1 и его копия в доке. Пинг зоне платформы послать стоит, но как новую информацию («бета сегодня НЕДО-берёт»), а не как претензию к ряду. ⛔(г) при этом остаётся в силе: правка описывает знак, калибровку по-прежнему держит строка бэклога 202. + +**Разбор опровергателя:** Пытался опровергнуть тремя путями, ни один не сработал. (1) Сначала проверил, не сложены ли деньги дважды: `checkpoints.cost_usd` — по одной строке на запрос (PK `request_hash`), сумма чекпойнтов сходится с `request_log` и с `spend.committed_usd` = 0.436109664 копейка в копейку. Замер автора воспроизводится. (2) Затем проверил, не куплен ли вывод хвостом брака: исключив оплаченные НЕуспешные вызовы, полная цепь главы всё равно $0.071563 и $0.078336 — 2.4–2.6× константы $0.03, знак тот же. (3) Затем — самый важный ход: вывод НЕ зависит от тонкого замера автора вовсе, он следует из одной ратифицированной арифметики. D39.165 ⛔(г) сам объявляет свои прогоны июльскими (10–11.07, «редактор был другой моделью»), а D39.179 п.1 ратифицировал множитель ×4.47 к ценам, вступившим 16.08 16:00 UTC. $0.0115–0.0190 × 4.47 = $0.051–$0.085; названный доком АБСОЛЮТНЫЙ максимум $0.0375 × 4.47 = $0.168. Обе цифры выше $0.03 ⇒ «завышена в 1.6–2.6×» и «$0.30 покупает ~16–26 глав» перевёрнуты уже чистой ратификацией, без единого нового замера. (4) Независимое подтверждение из ЗАЛЕНДЖЕННОГО носителя: движок сам печатает `projected_book_usd = 1.1377277707` на полной цепи тех же десяти глав (docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:70/294) — ≈$0.114/глава. Утверждение дока стоит в НАСТОЯЩЕМ времени как живое описание дефекта PD-410, и баннер «провенанс ниже — ИСТОРИЯ» его не спасает: он снимает обоснование ставки, но не переворачивает знак следствия, который читатель уносит с собой. + **Разрушающее:** False -**⚠ Опровергатель поправил действие:** Находка верна, предложенный текст — нет, он ставит под удар ровно то, чем прошлая смена уже обожглась. Три правки. (1) НЕ объявлять $0.093–$0.151 ценой главы: это n=2 главы из десяти, прогон остановлен снапшот-гардом (редакторская волна 3/14), и сам отчёт запрещает такое употребление дословно — «Чего замер НЕ должен делать: калибровать ставку платформы. Она гейчена строкой 202 и требует ЗАВЕРШЁННОЙ цепи» (COLDRUN_V16_REPORT_2026-08-31.md:642). Прогон вдобавок нёс объявленные девиации (черновик шёл БАНКНОТНЫМ промптом вместо конвенционного `translator.md`; `escalation.budget_usd` поднят 0.015 → 0.08), спред цены на юнит ×7.6, а причина, по которой редактор съел 73% захода, НЕ УСТАНОВЛЕНА. (2) Несущим доводом сделать РАТИФИЦИРОВАННУЮ арифметику, которая ни от чего этого не зависит: «Числа D39.165 §1 — ИЮЛЬСКИЕ (сама нота, ⛔г: прогоны 10–11.07, редактор другой моделью). D39.179 п.1 ратифицировал ×4.47 к ценам с 16.08 16:00 UTC ⇒ та же средняя сегодня $0.051–$0.085, тот же максимум $0.168. Константа $0.03 НЕ завышена — она ниже даже пере-считанной средней, и «купить 10 глав» = $0.30 покупает единицы глав, а не 16–26.» Замер холодного прогона добавить ОДНОЙ строкой как иллюстрацию с оговоркой n=2: движковая проекция полной цепи тех же десяти глав `projected_book_usd = 1.1377277707` (≈$0.114/глава, `docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:70`) и консервативный пол по ok-вызовам $0.0716/$0.0783. (3) Автор пропустил ВТОРОЙ ЖИВОЙ НОСИТЕЛЬ, и без него класс E разъедется снова: `docs/architecture/17-seam-inbound-law.md:101` несёт те же июльские числа («цена главы измерена ($0.0115–0.0190 против константы продажи $0.03)») — чинить в одной правке. Строка реестра `docs/architecture/05-decisions-index.md:229` их тоже повторяет, но она зеркало ноты: там достаточно указателя на D39.179. И уточнить формулировку про PD-410: строку регистра я прочёл — она несёт только (а)(б)(в)(г) и НЕ содержит ни «16–26 глав», ни «завышена в 1.6–2.6×», поэтому «знак дефекта PD-410 обратен записанному» — перебор; перевёрнуто СЛЕДСТВИЕ в D39.165 §1 и его копия в доке. Пинг зоне платформы послать стоит, но как новую информацию («бета сегодня НЕДО-берёт»), а не как претензию к ряду. ⛔(г) при этом остаётся в силе: правка описывает знак, калибровку по-прежнему держит строка бэклога 202. --- ## N005 · БЛОКЕР · `docs/architecture/15-money-path.md:35` · A-дрейф -**Срез:** Денежный путь — docs/architecture/15-money-path.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Команда ОТКАЗЫВАЕТ, если попытка ещё называет юнит или несёт базовую линию траты — над живым движком прогон не закрывают. Холд возвращается ЦЕЛИКОМ в обоих случаях (к abandon допускается только попытка, не дошедшая до движка, значит она ничего не потратила), а флаг решает лишь КОГДА +**Срез:** Денежный путь — docs/architecture/15-money-path.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Команда ОТКАЗЫВАЕТ, если попытка ещё называет юнит или несёт базовую линию траты — над живым движком прогон не закрывают. Холд возвращается ЦЕЛИКОМ в обоих случаях (к abandon допускается только попытка, не дошедшая до движка, значит она ничего не потратила), а флаг решает лишь КОГДА + **Почему протухло:** Абзац описывает ТОЛЬКО живую ветвь `run abandon`. С пака P11 (PD-385, коммит e548e5a от 29.08) есть вторая ветвь — PHASE=settling: к ней отказ «юнит/базовая линия» НЕ применяется (попытка уже кончилась), она принимает попытку, которая ДОШЛА до движка и потратила, возвращает холд ЦЕЛИКОМ (единственная защита — порог `abandonAfter` неудач расчёта, отказ `ErrSettlementNotStuck`), а флаг `--release-hold` там ИГНОРИРУЕТСЯ: деньги всегда закрываются inline. Все три утверждения абзаца в settling-ветви ложны, и холд возвращается целиком за работу, которую движок реально оплатил. Абзац вошёл в док 22.08 (0e31a8b) и был верен; аудит указателей 31.08 его смысла не касался. -**Улика — команда:** `sed -n '292,300p;307p' /home/ubuntu/projects/textmachine-main/platform/cmd/tmplatformctl/runs.go` -**Улика — вывод:** // ⚠ THE PARAGRAPH ABOVE IS THE LIVE HALF ONLY, since PD-385. On a run whose PHASE is `settling` — + +**Улика — команда:** +``` +sed -n '292,300p;307p' /home/ubuntu/projects/textmachine-main/platform/cmd/tmplatformctl/runs.go +``` +**Улика — вывод:** +``` +// ⚠ THE PARAGRAPH ABOVE IS THE LIVE HALF ONLY, since PD-385. On a run whose PHASE is `settling` — // finished, with a settlement that has failed and will not close — there is no sweep left to hand // the money to, so the command closes it inline whatever the flag says, and the refusal above does // not apply because the attempt has already ended. What DOES apply there is a different refusal: a // settlement that has not failed even once is turned away, because this command returns the hold // WHOLE and that one is about to be settled correctly for what it really spent. ... -release := fs.Bool("release-hold", false, "... ; ignored for a PHASE=settling run, whose money is always closed inline +release := fs.Bool("release-hold", false, "... ; ignored for a PHASE=settling run, whose money is always closed inline because no sweep will ever come for it") +``` **Другие носители того же факта:** platform/deploy/README.md:288-291 — тот же абзац дословно, НО там он починен отдельной секцией «**Строка PHASE = settling — та же команда, другой исход.**» (deploy/README.md:311-318), которой в 15-money-path нет, platform/docs/DEFECT_REGISTER.md:536 (PD-385) и :60 (PD-418) — регистровые носители ветвления -**Предложенное действие:** Дописать в §3 п.7 вторую ветвь: «⚠ С P11 (`PD-385`) ветвей ДВЕ, и абзац выше — только ЖИВАЯ. У прогона с `runs.finished_at is not null` (`PHASE=settling`) отказ по юниту/базовой линии не действует, к abandon допускается попытка, которая ДОШЛА до движка и потратила, холд всё равно возвращается ЦЕЛИКОМ (сдерживает только порог `abandonAfter` неудач расчёта — отказ `ErrSettlementNotStuck`), а `--release-hold` игнорируется: деньги закрываются в той же транзакции (`platform/internal/pgstore/runs.go`, греп `func abandonSettlement(`).» Живой образец формулировки — секция «Строка PHASE = settling» в platform/deploy/README.md. + +**Предложенное действие (автора находки):** Дописать в §3 п.7 вторую ветвь: «⚠ С P11 (`PD-385`) ветвей ДВЕ, и абзац выше — только ЖИВАЯ. У прогона с `runs.finished_at is not null` (`PHASE=settling`) отказ по юниту/базовой линии не действует, к abandon допускается попытка, которая ДОШЛА до движка и потратила, холд всё равно возвращается ЦЕЛИКОМ (сдерживает только порог `abandonAfter` неудач расчёта — отказ `ErrSettlementNotStuck`), а `--release-hold` игнорируется: деньги закрываются в той же транзакции (`platform/internal/pgstore/runs.go`, греп `func abandonSettlement(`).» Живой образец формулировки — секция «Строка PHASE = settling» в platform/deploy/README.md. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие в целом верно (дописать вторую ветвь в §3 п.7, образец — секция «Строка PHASE = settling» в platform/deploy/README.md), но добавить в него ДВЕ оговорки, без которых новый абзац сам станет протухшим: (а) ветвление идёт по `runs.finished_at`, а НЕ по наличию осиротевшей попытки — это ратифицированная оговорка `PD-418`, дословно стоящая в platform/deploy/README.md рядом с образцом; (б) ЖИВОЙ прогон с намертво заблокированной расплатой уходит в ЖИВУЮ ветвь и отказом по юниту закрыт до сих пор — `PD-424` в platform/docs/DEFECT_REGISTER.md стоит `open` именно на этой половине («терминальная РУЧКА»), и абзац, обещавший бы лечение settling-ветвью на живом прогоне, соврал бы. Плюс мелочь фактуры: холд возвращается по КАЖДОЙ осиротевшей попытке прогона, а не по одной (цикл `for _, o := range orphans`). Указатели — греп-формой, как и предложено (`platform/internal/pgstore/runs.go` сегодня чист, но зона под паками движется). + +**Разбор опровергателя:** Воспроизвёл своей командой, не поверив автору. (1) Ветвление стоит в `AbandonRun` (platform/internal/pgstore/runs.go:566-569): `if finished != nil { verdict = AbandonedSettlement; return abandonSettlement(...) }` — ДО чтения попытки и ДО отказа `if unit != "" || baseline != nil` (строка ~592). Значит для прогона с `finished_at is not null` названный доком отказ по юниту/базовой линии физически недостижим. (2) `giveTheHoldBack` в `abandonSettlement` НЕ передаётся ни на одном из двух вызовов (569, 587), а сама функция (732) закрывает резервацию и возвращает холд `closeReservation(..., "released", now)` в СВОЕЙ транзакции — то есть `--release-hold` в этой ветви игнорируется, деньги всегда закрываются inline. Флаг это и объявляет: `"...; ignored for a PHASE=settling run"` (platform/cmd/tmplatformctl/runs.go:307). (3) Единственный сдерживатель — порог `const abandonAfter = 5` (runs.go:730) с отказом `ErrSettlementNotStuck`, и комментарий над функцией прямо пишет, почему: «this command REFUNDS THE WHOLE HOLD», а замер приёмки назван числом ($1.234567 потрачено, эта команда за тик раньше списала бы $0.000000). Значит к abandon в этой ветви допускается именно попытка, которая ДОШЛА до движка и потратила. (4) Положительный контроль на пустоту: `grep -c settling` даёт 4 в platform/deploy/README.md и 0 в 15-money-path.md — инструмент в этой области ищет, а в доке слова нет. (5) Датировка автора точна: абзац вошёл 22.08 (0e31a8b), settling-ветвь и её описание — 29.08 (e548e5a) и только в зонный README. Абзац дока с тех пор не правился по смыслу. + **Разрушающее:** False -**⚠ Опровергатель поправил действие:** Действие в целом верно (дописать вторую ветвь в §3 п.7, образец — секция «Строка PHASE = settling» в platform/deploy/README.md), но добавить в него ДВЕ оговорки, без которых новый абзац сам станет протухшим: (а) ветвление идёт по `runs.finished_at`, а НЕ по наличию осиротевшей попытки — это ратифицированная оговорка `PD-418`, дословно стоящая в platform/deploy/README.md рядом с образцом; (б) ЖИВОЙ прогон с намертво заблокированной расплатой уходит в ЖИВУЮ ветвь и отказом по юниту закрыт до сих пор — `PD-424` в platform/docs/DEFECT_REGISTER.md стоит `open` именно на этой половине («терминальная РУЧКА»), и абзац, обещавший бы лечение settling-ветвью на живом прогоне, соврал бы. Плюс мелочь фактуры: холд возвращается по КАЖДОЙ осиротевшей попытке прогона, а не по одной (цикл `for _, o := range orphans`). Указатели — греп-формой, как и предложено (`platform/internal/pgstore/runs.go` сегодня чист, но зона под паками движется). --- ## N006 · БЛОКЕР · `platform/docs/STACK_DECISIONS.md:414` · D-противоречие-ратифицированному -**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** | `mined_delta` (путь из `book.yaml`) | **писателя в движке НЕТ** — только читатель `loadMinedDelta` +**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> | `mined_delta` (путь из `book.yaml`) | **писателя в движке НЕТ** — только читатель `loadMinedDelta` + **Почему протухло:** Строка (и парная ей 415 про `mined_rejects`) неверна в четырёх местах сразу. (1) `mined_delta` БОЛЬШЕ НЕ «путь из book.yaml»: ключ РЕТИРОВАН — `RetiredMinedDelta string `yaml:"mined_delta"`` ловит его, чтобы конфиг эры объявляемых путей ПАДАЛ, а сам путь выводится из book_id (`b.MinedDelta = filepath.Join(dir, b.BookID+MinedDeltaSuffix)`). (2) «писателя в движке НЕТ» — ложь: писатель есть, это глагол `bank-apply` (`stageFileAtomic(book.MinedDelta, res.DeltaBytes)`), тот самый СЕДЬМОЙ канал, который дописан в эту же таблицу 31.08 четырьмя строками ниже. (3) Колонка «Атомарность: —» неверна: запись стейджится атомарно + `syncDir`. (4) «развилка ждёт ратификации» — развилка ЗАКРЫТА ратифицированной D39.156 п.3 (23.08, слово владельца): «Развилка 199(а) закрывается РАСТВОРЕНИЕМ… движок дефолтит `mined_delta`/`mined_rejects` по конвенции каталога… писателем становится движковый $0-глагол ⇒ ключей никто не объявляет». Строка 199 единого бэклога закрыта 28.08 (D39.166), её преемник 231 съеден паком «деньги». Цена ошибки не теоретическая: этот же документ на 424 предупреждает, что расширение набора ключей платформой — ратификация, а декодер движка строгий; сессия, прочитавшая «путь из book.yaml», может вписать `mined_delta:` в деплой-шаблон, и тогда НЕ ЗАГРУЗИТСЯ КАЖДАЯ новая книга. -**Улика — команда:** `grep -n 'RetiredMinedDelta string' backend/internal/config/book.go; grep -n 'stageFileAtomic(book.MinedDelta' backend/internal/pipeline/bankdecisions.go; grep -n 'b.MinedDelta = filepath.Join' backend/internal/config/book.go; sed -n '670p' docs/architecture/05-decisions-log.md | cut -c1-260; for n in 199 175; do printf 'row %s in live backlog: %s\n' $n $(grep -c "^| $n " docs/PROGRESS.md); done` -**Улика — вывод:** 115: RetiredMinedDelta string `yaml:"mined_delta"` + +**Улика — команда:** +``` +grep -n 'RetiredMinedDelta string' backend/internal/config/book.go; grep -n 'stageFileAtomic(book.MinedDelta' backend/internal/pipeline/bankdecisions.go; grep -n 'b.MinedDelta = filepath.Join' backend/internal/config/book.go; sed -n '670p' docs/architecture/05-decisions-log.md | cut -c1-260; for n in 199 175; do printf 'row %s in live backlog: %s\n' $n $(grep -c "^| $n " docs/PROGRESS.md); done +``` +**Улика — вывод:** +``` +115: RetiredMinedDelta string `yaml:"mined_delta"` 455: if deltaStage, err = stageFileAtomic(book.MinedDelta, res.DeltaBytes); err != nil { 199: b.MinedDelta = filepath.Join(dir, b.BookID+MinedDeltaSuffix) **3. Развилка 199(а) закрывается РАСТВОРЕНИЕМ, а не выбором. Ни один из четырёх вариантов зоны: движок дефолтит `mined_delta`/`mined_rejects` по конвенции каталога … писателем становится движковый $0-глагол ⇒ ключей никто не объявляет** (это тело D39.156, ратифицировано 23.08) row 199 in live backlog: 0 row 175 in live backlog: 1 -**Положительный контроль:** grep -c "^| 175 " docs/PROGRESS.md → 1 (шаблон грепа строк бэклога РАБОТАЕТ и находит живые строки 175/202/224), при этом для 199 он даёт 0; правило удаления закрытых строк — docs/PROGRESS.md:59 «Закрытые строки здесь НЕ хранятся — они уходят в D-лог с номером решения» +``` +**Положительный контроль:** +``` +grep -c "^| 175 " docs/PROGRESS.md → 1 (шаблон грепа строк бэклога РАБОТАЕТ и находит живые строки 175/202/224), при этом для 199 он даёт 0; правило удаления закрытых строк — docs/PROGRESS.md:59 «Закрытые строки здесь НЕ хранятся — они уходят в D-лог с номером решения» +``` **Другие носители того же факта:** platform/docs/STACK_DECISIONS.md:415 (парная строка `mined_rejects`, «❌ тот же разрыв»), platform/docs/STACK_DECISIONS.md:424 (хвост строки `book.yaml`: «Прямо относится к развилке 199(а)»), platform/docs/p9/door-live-probe.md:157 (тот же факт — но ВЕРНО: «правка термина через контрактную дверь доезжает до движковых файлов решений»), docs/architecture/17-seam-inbound-law.md:97 -**Предложенное действие:** Пере-написать обе строки таблицы: путь ВЫВОДИТСЯ из book_id (ключи `mined_delta`/`mined_rejects` в book.yaml ретированы и валят конфиг); писатель — движковый глагол `bank-apply`; атомарность — стейдж+rename+syncDir; колонка «Согласовано?» → ✅ с ссылкой на D39.156 п.3 вместо «❌ разрыв… ждёт ратификации». Хвост на 424 «Прямо относится к развилке 199(а)» снять. + +**Предложенное действие (автора находки):** Пере-написать обе строки таблицы: путь ВЫВОДИТСЯ из book_id (ключи `mined_delta`/`mined_rejects` в book.yaml ретированы и валят конфиг); писатель — движковый глагол `bank-apply`; атомарность — стейдж+rename+syncDir; колонка «Согласовано?» → ✅ с ссылкой на D39.156 п.3 вместо «❌ разрыв… ждёт ратификации». Хвост на 424 «Прямо относится к развилке 199(а)» снять. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие принимается как есть. Единственное уточнение к формулировке замены: писателем является не «глагол bank-apply» вообще, а функция `writeDecisionFiles` (`backend/internal/pipeline/bankdecisions.go:450`), и порядок переименований в ней НЕ произвольный (rejects первым — аргумент в шапке функции); если таблица описывает атомарность, честнее написать «атомарно ПОФАЙЛОВО (stage+rename+syncDir), но пара файлов НЕ транзакционна — половинное состояние сходится на пере-посылке», иначе колонка «атомарно» пообещает больше, чем код даёт. + +**Разбор опровергателя:** Все четыре под-утверждения находки воспроизвелись моими командами на рабочем дереве. (1) Ключ РЕТИРОВАН: поле `MinedDelta` помечено `yaml:"-"`, путь выводится в `book.go:199` из `BookID+MinedDeltaSuffix`, а `RetiredMinedDelta yaml:"mined_delta"` существует ровно затем, чтобы объявленный ключ ВАЛИЛ конфиг (`bad(...)` в 263–265, с прямой ссылкой на D39.156 п.3). Значит цена ошибки, названная находкой, реальна: вписанный в деплой-шаблон `mined_delta:` уронит загрузку каждой книги. (2) Писатель ЕСТЬ — `writeDecisionFiles` в `pipeline/bankdecisions.go` стейджит оба файла и вызывается глаголом `bank-apply`, который стоит в диспетчере `main.go:185/:249`. (3) Атомарность не «—»: `stageFileAtomic` + commit(rename) + `syncDir` на каталоге книги. (4) Развилка не «ждёт ратификации» — D39.156 п.3 (23.08) закрыла её растворением, а индекс D39.166 пишет «строки 199/215 закрыты». Положительный контроль грепа бэклога состоятелен: тот же шаблон даёт 1 на живых 175/202/224 и на надгробии 96, и 0 на 199/231. Находка верна и предложенное действие верно. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие принимается как есть. Единственное уточнение к формулировке замены: писателем является не «глагол bank-apply» вообще, а функция `writeDecisionFiles` (`backend/internal/pipeline/bankdecisions.go:450`), и порядок переименований в ней НЕ произвольный (rejects первым — аргумент в шапке функции); если таблица описывает атомарность, честнее написать «атомарно ПОФАЙЛОВО (stage+rename+syncDir), но пара файлов НЕ транзакционна — половинное состояние сходится на пере-посылке», иначе колонка «атомарно» пообещает больше, чем код даёт. --- ## N007 · MAJOR · `backend/README.md:11` · A-дрейф -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** | `cmd/tmctl` | CLI: `translate` / `status` … / `redrive` … / `manifest` … / `migrate` … +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> | `cmd/tmctl` | CLI: `translate` / `status` … / `redrive` … / `manifest` … / `migrate` … + **Почему протухло:** Перечень команд читается исчерпывающим и отстал на ТРИ верба из одиннадцати: `backup`, `seed-lint`, `bank-apply`. `bank-apply` — это ратифицированная ВХОДНАЯ ДВЕРЬ ШВА (D39.158), то есть единственный способ платформы вернуть движку решения владельца по банку; сессия, читающая карту пакетов, о её существовании не узнаёт. Тем же дефектом страдает «Карта пакетов»: из 18 пакетов `internal/` в ней нет `bookfile`, `runevents`, `standdata` — причём `internal/bookfile` завёл ТОТ ЖЕ коммит 8adcb86, которым строка 11 последний раз переписывалась. -**Улика — команда:** `cd backend && sed -n '183,186p' cmd/tmctl/main.go; ls internal/; for t in bookfile runevents standdata backup seed-lint bank-apply; do printf '%-12s ' $t; grep -q -- "$t" README.md && echo IN_README || echo 'NOT IN README'; done` -**Улика — вывод:** var dispatchCommands = []string{ + +**Улика — команда:** +``` +cd backend && sed -n '183,186p' cmd/tmctl/main.go; ls internal/; for t in bookfile runevents standdata backup seed-lint bank-apply; do printf '%-12s ' $t; grep -q -- "$t" README.md && echo IN_README || echo 'NOT IN README'; done +``` +**Улика — вывод:** +``` +var dispatchCommands = []string{ "translate", "report", "status", "export", "build", "redrive", "manifest", "backup", "migrate", "seed-lint", "bank-apply", } @@ -141,18 +281,31 @@ standdata NOT IN README backup NOT IN README seed-lint NOT IN README bank-apply NOT IN README -**Предложенное действие:** Дописать в ячейку `cmd/tmctl`: `bank-apply` (входная дверь шва — приём документа решений владельца по банку, exit 14 «отклонено» / 15 «запись не завершилась», D39.156/D39.158) · `backup` (VACUUM INTO + integrity_check, он же pre-flight боевого прогона, строка 83) · `seed-lint`. В карту пакетов добавить строки `internal/bookfile` (EPUB 3/txt писатель), `internal/runevents` (кадры журнала прогона + словарь исходов), `internal/standdata` (резолв корпусных путей от корня репо). +``` + +**Предложенное действие (автора находки):** Дописать в ячейку `cmd/tmctl`: `bank-apply` (входная дверь шва — приём документа решений владельца по банку, exit 14 «отклонено» / 15 «запись не завершилась», D39.156/D39.158) · `backup` (VACUUM INTO + integrity_check, он же pre-flight боевого прогона, строка 83) · `seed-lint`. В карту пакетов добавить строки `internal/bookfile` (EPUB 3/txt писатель), `internal/runevents` (кадры журнала прогона + словарь исходов), `internal/standdata` (резолв корпусных путей от корня репо). + **Разрушающее:** False --- ## N008 · MAJOR · `backend/README.md:24` · A-дрейф -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** машинная стоп-таблица в `mining.go` (101); все три пишутся через `artifact.go` (write-then-rename — их читают ПОКА идёт прогон) +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> машинная стоп-таблица в `mining.go` (101); все три пишутся через `artifact.go` (write-then-rename — их читают ПОКА идёт прогон) + **Почему протухло:** Машинной стоп-таблицы (`.bank-stop.json`) в движке НЕТ — её снёс пак «входная дверь шва» (d1eb8a9, ратифицирован D39.158); уцелевшая `.bank-stop.txt` пишется ГОЛЫМ `os.WriteFile` (mining.go:198), то есть НЕ через artifact.go и БЕЗ write-then-rename. Реальные четыре клиента writeFileAtomic — bankexport, manifest, карта подписи и авто-банк. Строка родилась верной в 0e69bc1 (09.08, коммит, который машинную таблицу и завёл) и сгнила 27.08. -**Улика — команда:** `cd backend && grep -rn 'writeFileAtomic(' internal/pipeline/*.go | grep -v _test | grep -v 'func writeFileAtomic'; grep -n 'bankStopTablePath()' internal/pipeline/mining.go | head -3; git log --oneline --all -S'TableJSONPath' | cat` -**Улика — вывод:** internal/pipeline/bankexport.go:152: if err := writeFileAtomic(r.bankExportPath(), ... + +**Улика — команда:** +``` +cd backend && grep -rn 'writeFileAtomic(' internal/pipeline/*.go | grep -v _test | grep -v 'func writeFileAtomic'; grep -n 'bankStopTablePath()' internal/pipeline/mining.go | head -3; git log --oneline --all -S'TableJSONPath' | cat +``` +**Улика — вывод:** +``` +internal/pipeline/bankexport.go:152: if err := writeFileAtomic(r.bankExportPath(), ... internal/pipeline/manifest.go:401: if err := writeFileAtomic(r.manifestPath(), ... internal/pipeline/mining.go:191: if err := writeFileAtomic(r.signatureMapPath(), mapBytes); err != nil { internal/pipeline/mining.go:722: if err := writeFileAtomic(r.autoBankPath(), []byte(body)); err != nil { @@ -161,20 +314,38 @@ internal/pipeline/mining.go:722: if err := writeFileAtomic(r.autoBankPath(), []b --- git -S TableJSONPath --- d1eb8a9 Land the seam inbound door: ... (removal) 0e69bc1 Land the contract-blockers pack: ... machine bank-stop table (introduction) +``` **Другие носители того же факта:** backend/internal/pipeline/artifact.go:11 — та же протухшая опись клиентов: «the machine bank-stop table (row 101)» перечислена, а карта подписи (mining.go:191) и авто-банк (mining.go:722) — нет -**Предложенное действие:** Заменить хвост ячейки на: «`manifest.go` (структура глав/чанков, строка 100), `bankexport.go` (весь банк тремя статусами, 125), карта подписи и авто-банк в `mining.go` — все четыре через `artifact.go` (write-then-rename); человеческая стоп-таблица `.bank-stop.txt` пишется обычным `os.WriteFile` (машинная `.bank-stop.json` снесена D39.158)». Заодно поправить шапку artifact.go. + +**Предложенное действие (автора находки):** Заменить хвост ячейки на: «`manifest.go` (структура глав/чанков, строка 100), `bankexport.go` (весь банк тремя статусами, 125), карта подписи и авто-банк в `mining.go` — все четыре через `artifact.go` (write-then-rename); человеческая стоп-таблица `.bank-stop.txt` пишется обычным `os.WriteFile` (машинная `.bank-stop.json` снесена D39.158)». Заодно поправить шапку artifact.go. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верное по сути, но опись носителей в находке НЕТОЧНА и предложенная форма сама сгниёт. (1) `artifact.go:11-13` авто-банк УЖЕ перечисляет («and the engine's auto-bank (row 231 …)») — из четырёх реальных клиентов в его шапке не хватает ТОЛЬКО карты подписи (mining.go:191), а лишняя — машинная таблица (row 101). (2) Находка пропустила третий носитель: `backend/docs/pipeline.puml:72` («.bank-stop.json — машине (строка 101): ПОЛНАЯ таблица») — он вне .md-среза, но правится тем же ходом, и `docs/README.md:34` объявляет puml домом диаграмм рядом с кодом. (3) Номера строк бэклога 100/101/125/231 в этой ячейке ВСЕ закрыты (грепом по `^| N |` в docs/PROGRESS.md ноль при 189 живых строках) — это исторические ссылки-заказчики, не указатели в трекер; менять их не нужно, но и добавлять новых номеров в замену не надо. (4) В самом тексте вместо якорей-строк называй символы (`bankExportPath` · `manifestPath` · `signatureMapPath` · `autoBankPath`): именно line-якоря в этом README и протухают быстрее всего. + +**Разбор опровергателя:** Воспроизвёл своей командой полностью. Из «трёх» read-out файлов один не существует, а уцелевшая человеческая `.bank-stop.txt` пишется голым `os.WriteFile` — то есть ровно БЕЗ той гарантии, ради которой README и написал «их читают ПОКА идёт прогон». Автор был прав в 0e69bc1 и сгнил на d1eb8a9. Класс A, major — подтверждаю. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верное по сути, но опись носителей в находке НЕТОЧНА и предложенная форма сама сгниёт. (1) `artifact.go:11-13` авто-банк УЖЕ перечисляет («and the engine's auto-bank (row 231 …)») — из четырёх реальных клиентов в его шапке не хватает ТОЛЬКО карты подписи (mining.go:191), а лишняя — машинная таблица (row 101). (2) Находка пропустила третий носитель: `backend/docs/pipeline.puml:72` («.bank-stop.json — машине (строка 101): ПОЛНАЯ таблица») — он вне .md-среза, но правится тем же ходом, и `docs/README.md:34` объявляет puml домом диаграмм рядом с кодом. (3) Номера строк бэклога 100/101/125/231 в этой ячейке ВСЕ закрыты (грепом по `^| N |` в docs/PROGRESS.md ноль при 189 живых строках) — это исторические ссылки-заказчики, не указатели в трекер; менять их не нужно, но и добавлять новых номеров в замену не надо. (4) В самом тексте вместо якорей-строк называй символы (`bankExportPath` · `manifestPath` · `signatureMapPath` · `autoBankPath`): именно line-якоря в этом README и протухают быстрее всего. --- ## N009 · MAJOR · `backend/README.md:82` · D-противоречие-ратифицированному -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Пути дефолтятся ОТ `$HOME` и от корня репозитория (не от `/home/ubuntu`), так что клон под любым пользователем и по любому пути находит то, что есть +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Пути дефолтятся ОТ `$HOME` и от корня репозитория (не от `/home/ubuntu`), так что клон под любым пользователем и по любому пути находит то, что есть + **Почему протухло:** От `$HOME` НЕ дефолтится НИЧЕГО. `internal/standdata` резолвит корпус ТОЛЬКО от корня репозитория (`/books`), и его собственная шапка говорит прямо: «never taken from $HOME … the previous address $HOME/books is dead (D39.157 п.2, moved there on 24.08)». Переезд ратифицирован D39.157 п.2. Строки 47/79/80 того же README дают мёртвые адреса `/home/ubuntu/books/gu-zhenren/` и `~/books/...`; на этой машине они «работают» только потому, что /home/ubuntu/books — симлинк в соседний чекаут. -**Улика — команда:** `sed -n '1,14p;76,79p' backend/internal/standdata/standdata.go; awk '/^## D39\.157/,/^## D39\.158/' docs/architecture/05-decisions-log.md | grep -n 'Корень книг'; ls -la /home/ubuntu/books` -**Улика — вывод:** // - The STAND CORPUS — book derivatives that are versioned by their own git repository under + +**Улика — команда:** +``` +sed -n '1,14p;76,79p' backend/internal/standdata/standdata.go; awk '/^## D39\.157/,/^## D39\.158/' docs/architecture/05-decisions-log.md | grep -n 'Корень книг'; ls -la /home/ubuntu/books +``` +**Улика — вывод:** +``` +// - The STAND CORPUS — book derivatives that are versioned by their own git repository under // /books (D39.157 п.2, moved there on 24.08; the previous address $HOME/books is dead). // Both are derived from the repository root found by MARKER, never counted in ".." and never taken from // $HOME: a default that reads a home directory is wrong for every machine but the one it was written on @@ -184,74 +355,144 @@ const standRoot = "books" 5:**2. Корень книг — `<репозиторий>/books`, отдельный git.** Каталог перенесён владельцем внутрь дерева --- lrwxrwxrwx /home/ubuntu/books -> /home/ubuntu/projects/textmachine/books +``` **Другие носители того же факта:** backend/README.md:47 — «Книга-стенд: `/home/ubuntu/books/gu-zhenren/`», backend/README.md:79 — «`~/books/gu-zhenren/{rerun/records.json, guzhenren-seed-v2.yaml}`», backend/README.md:80 — «`~/books/gu-zhenren/labels/`», backend/cmd/tmctl/backup.go:28 — «the stand book lands under ~/books//…» -**Предложенное действие:** Строку 82 переписать: «Пути дефолтятся ОТ КОРНЯ РЕПОЗИТОРИЯ, найденного по маркеру `backend/go.mod` (`internal/standdata`); корпус — `<репозиторий>/books` (D39.157 п.2). $HOME не читается вовсе.» В строках 47/79/80 заменить `/home/ubuntu/books` и `~/books` на `<репозиторий>/books`. Отдельной строкой — backup.go:28. ⚠ Строка бэклога 217 покрывает только СКРИПТЫ полигона, носителя для этого README-дрейфа в трекере нет. + +**Предложенное действие (автора находки):** Строку 82 переписать: «Пути дефолтятся ОТ КОРНЯ РЕПОЗИТОРИЯ, найденного по маркеру `backend/go.mod` (`internal/standdata`); корпус — `<репозиторий>/books` (D39.157 п.2). $HOME не читается вовсе.» В строках 47/79/80 заменить `/home/ubuntu/books` и `~/books` на `<репозиторий>/books`. Отдельной строкой — backup.go:28. ⚠ Строка бэклога 217 покрывает только СКРИПТЫ полигона, носителя для этого README-дрейфа в трекере нет. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верное; уточняю две вещи. (1) Половина фразы («и от корня репозитория», «не от `/home/ubuntu`») ВЕРНА — переписывать надо не всю строку, а снять только «ОТ `$HOME` и»; предложенная редакция это делает, но не должна выглядеть как опровержение всей строки. (2) По строкам 47/79/80: замена на `<репозиторий>/books` правильна, но проверь, что имелось в виду — на ЭТОЙ машине оба адреса резолвятся (`/home/ubuntu/books` → `/home/ubuntu/projects/textmachine/books`, и `books/` внутри самого чекаута — тоже симлинк туда же), так что «сломано» тут не наблюдаемо и правка чисто анти-дрейфовая: пиши её как «адрес по D39.157 п.2», а не как «путь не работает» — иначе следующая сессия проверит `ls` и решит, что ты ошибся. (3) `backend/cmd/tmctl/backup.go:28` — код бэкенд-зоны, не док: это ПИНГ бэкенд-сессии, а не правка оркестратора. + +**Разбор опровергателя:** Воспроизведено. `$HOME` в дефолтах не читается вовсе и это прямо ратифицировано D39.157 п.2 плюс записано в шапке пакета. Классический «родилось верным — сгнило дрейфом», и носителя в трекере под него больше нет (218 закрыта). Класс D/A, major — подтверждаю. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верное; уточняю две вещи. (1) Половина фразы («и от корня репозитория», «не от `/home/ubuntu`») ВЕРНА — переписывать надо не всю строку, а снять только «ОТ `$HOME` и»; предложенная редакция это делает, но не должна выглядеть как опровержение всей строки. (2) По строкам 47/79/80: замена на `<репозиторий>/books` правильна, но проверь, что имелось в виду — на ЭТОЙ машине оба адреса резолвятся (`/home/ubuntu/books` → `/home/ubuntu/projects/textmachine/books`, и `books/` внутри самого чекаута — тоже симлинк туда же), так что «сломано» тут не наблюдаемо и правка чисто анти-дрейфовая: пиши её как «адрес по D39.157 п.2», а не как «путь не работает» — иначе следующая сессия проверит `ls` и решит, что ты ошибся. (3) `backend/cmd/tmctl/backup.go:28` — код бэкенд-зоны, не док: это ПИНГ бэкенд-сессии, а не правка оркестратора. --- ## N010 · MAJOR · `backend/README.md:99` · C-исполненное-как-открытое -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Остаточные Cyrillic-хардкоды пар-гейтед DC-чекеров (`checks/repair.go:131`, `checks/checkers.go:457,474`) — инертны без dc-данных пары; кандидат на `c.wordScript` (строка 79). +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Остаточные Cyrillic-хардкоды пар-гейтед DC-чекеров (`checks/repair.go:131`, `checks/checkers.go:457,474`) — инертны без dc-данных пары; кандидат на `c.wordScript` (строка 79). + **Почему протухло:** Долг ПОГАШЕН и это записано в двух носителях. Код по обоим якорям сегодня говорит обратное README: repair.go:131 — «The boundary is the TARGET's declared word script, not a hardcoded Cyrillic predicate (строка 79, D39.39)», repair.go:190 — «TARGET-SCRIPT DEBT REPAID (D39.24 §15.3 amended by D39.62/П1)», checkers.go:494 — «data-driven off c.wordScript instead of a hardcoded Cyrillic predicate». Сама строка бэклога 79 фиксирует: «Первые два компонента строки (isCyrLetter→`isTargetWordLetter` · magnitude `SourceScripts`-гейт) исполнены пакетом-чекеров, D39.78». Плюс мёртвые line-якоря: в checkers.go по 457/474 лежит `lintRegisterLexicon`, а нужный комментарий — на 494. -**Улика — команда:** `cd backend && sed -n '131p;190,191p' internal/checks/repair.go; sed -n '494p' internal/checks/checkers.go; grep -nE '^\| *\*{0,2}79\*{0,2} *\|' ../docs/PROGRESS.md | cut -c1-260` -**Улика — вывод:** // TARGET's declared word script, not a hardcoded Cyrillic predicate (строка 79, D39.39). + +**Улика — команда:** +``` +cd backend && sed -n '131p;190,191p' internal/checks/repair.go; sed -n '494p' internal/checks/checkers.go; grep -nE '^\| *\*{0,2}79\*{0,2} *\|' ../docs/PROGRESS.md | cut -c1-260 +``` +**Улика — вывод:** +``` + // TARGET's declared word script, not a hardcoded Cyrillic predicate (строка 79, D39.39). // TARGET-SCRIPT DEBT REPAID (D39.24 §15.3 amended by D39.62/П1): the word boundary was unicode.Cyrillic in // BOTH the lint tokenizer and here; it is now the target's DECLARED word script (data), so a non-Cyrillic // word boundary, data-driven off c.wordScript instead of a hardcoded Cyrillic predicate (строка 79, D39.39). --- | 79 | **Граница ПЛОТНОСТИ письма для серий**: `enabled` через Go-константу `cjkScriptNames` ... Первые два компонента строки (isCyrLetter→`isTargetWordLetter` · magnitude `SourceScripts`-гейт) исполнены пакетом-чекеров, D39.78 | -**Предложенное действие:** Снести строку 99 целиком и заменить одной: «Остаток строки 79 — граница ПЛОТНОСТИ письма (`cjkScriptNames` Go-константой); Cyrillic-хардкоды DC-чекеров ПОГАШЕНЫ пакетом-чекеров, D39.78 (`checks/repair.go:190`, `checks/checkers.go:494`).» +``` + +**Предложенное действие (автора находки):** Снести строку 99 целиком и заменить одной: «Остаток строки 79 — граница ПЛОТНОСТИ письма (`cjkScriptNames` Go-константой); Cyrillic-хардкоды DC-чекеров ПОГАШЕНЫ пакетом-чекеров, D39.78 (`checks/repair.go:190`, `checks/checkers.go:494`).» + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верное по существу, но предложенная замена воспроизводит ту же болезнь — снова номера строк (`repair.go:190`, `checkers.go:494`), которые уедут при следующей же правке пакета; ровно так и протухли 457/474. Пиши через СИМВОЛЫ: «Cyrillic-хардкоды словесной границы чекеров ПОГАШЕНЫ (`Checkers.isTargetWordLetter` · `brokenWordCandidates`); открытый остаток строки 79 — только граница ПЛОТНОСТИ письма (`cjkScriptNames` Go-константой)». Атрибуцию тоже уточни: код погашение приписывает D39.24 §15.3 с поправкой D39.62/П1, а строка 79 — пакету-чекеров D39.78; называй строку 79 и оба номера, а не один D39.78. + +**Разбор опровергателя:** Воспроизведено дважды — и грепом по предмету, и положительным контролем в соседнем файле того же пакета. Долг погашен, код по обоим якорям говорит README прямо в лицо, а сами якоря вдобавок уехали (457/474 → 494). Класс C + B, major — подтверждаю. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верное по существу, но предложенная замена воспроизводит ту же болезнь — снова номера строк (`repair.go:190`, `checkers.go:494`), которые уедут при следующей же правке пакета; ровно так и протухли 457/474. Пиши через СИМВОЛЫ: «Cyrillic-хардкоды словесной границы чекеров ПОГАШЕНЫ (`Checkers.isTargetWordLetter` · `brokenWordCandidates`); открытый остаток строки 79 — только граница ПЛОТНОСТИ письма (`cjkScriptNames` Go-константой)». Атрибуцию тоже уточни: код погашение приписывает D39.24 §15.3 с поправкой D39.62/П1, а строка 79 — пакету-чекеров D39.78; называй строку 79 и оба номера, а не один D39.78. --- ## N011 · MAJOR · `backend/README.md:101` · B-мёртвый-указатель -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Хвост LOW/NOTE-находок адверсариала D39.4 — в ledger-очереди (строка 53). +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Хвост LOW/NOTE-находок адверсариала D39.4 — в ledger-очереди (строка 53). + **Почему протухло:** Строка 53 в едином трекере не существует: она закрыта D39.82 (носитель этого сказан в самой таблице — строка 111: «D39.82 закрыл строку 53 через `putUniqueDC`»), а закрытые строки таблица по своему же правилу не хранит. Хвост D39.4 получил ЯВНУЮ диспозицию «не чинить — легитимно отложено, носитель = D39.4/D39.5 (пересекается с живыми строками 43/78)». README же подаёт это как открытый техдолг в секции, чья шапка гласит «ЕДИНЫЙ трекер — таблица «Бэклог» в `docs/PROGRESS.md`, номера строк = она» — читатель грепает 53 и не находит ничего. -**Улика — команда:** `grep -nE '^\| *\*{0,2}53\*{0,2} *\|' docs/PROGRESS.md; echo "rc=$? (пусто = строки нет)"; grep -n 'закрыл строку 53' docs/PROGRESS.md | cut -c1-160; grep -n 'D39.4 ledger-очередь LOW/NOTE' docs/archive/reports/SMALLPACK_TAILS_2026-08-02.md | cut -c1-300` -**Улика — вывод:** (пустой вывод по '^| 53 |') + +**Улика — команда:** +``` +grep -nE '^\| *\*{0,2}53\*{0,2} *\|' docs/PROGRESS.md; echo "rc=$? (пусто = строки нет)"; grep -n 'закрыл строку 53' docs/PROGRESS.md | cut -c1-160; grep -n 'D39.4 ledger-очередь LOW/NOTE' docs/archive/reports/SMALLPACK_TAILS_2026-08-02.md | cut -c1-300 +``` +**Улика — вывод:** +``` +(пустой вывод по '^| 53 |') rc=1 251:| 111 | **Дубль ключа пар-файла: фикс накрыл половину.** D39.82 закрыл строку 53 через `putUniqueDC` в `parseDCCheckers`... 90: - *D39.4 ledger-очередь LOW/NOTE:* припаркованная 14 LOW + 14 NOTE хвост пака-1.5 адверсариала (Retries-снапшот · pair-fail-loud · trustGateEvent · golden-глосс-ячейка · пунктуационные огрызки); **не чинить** — легитимно отложено, носитель = D39.4/D39.5 (пересекается с живыми строками 43/78) -**Положительный контроль:** Инструмент в этой области находит: тем же паттерном по таблице извлекается 189 существующих ID — `grep -oE '^\| *\*{0,2}[0-9]+[а-я]?\*{0,2} *\|' docs/PROGRESS.md | wc -l` → 189, среди них 49, 49а, 50, 79, 146, 173, которые README цитирует рядом и которые ЖИВЫ. -**Предложенное действие:** Заменить на: «Хвост LOW/NOTE-находок адверсариала D39.4 — диспозиция «не чинить» (D39.82, строка 53 закрыта); живые пересечения — строки 43/78.» +``` +**Положительный контроль:** +``` +Инструмент в этой области находит: тем же паттерном по таблице извлекается 189 существующих ID — `grep -oE '^\| *\*{0,2}[0-9]+[а-я]?\*{0,2} *\|' docs/PROGRESS.md | wc -l` → 189, среди них 49, 49а, 50, 79, 146, 173, которые README цитирует рядом и которые ЖИВЫ. +``` + +**Предложенное действие (автора находки):** Заменить на: «Хвост LOW/NOTE-находок адверсариала D39.4 — диспозиция «не чинить» (D39.82, строка 53 закрыта); живые пересечения — строки 43/78.» + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верное, но одну деталь предложенной редакции надо снять: «строка 53 закрыта» D39.82 закрыл СВОЙ предмет (дубль ключа пар-файла через `putUniqueDC`), а хвост LOW/NOTE адверсариала — это ДРУГАЯ сущность, чья диспозиция «не чинить» стоит в docs/archive/reports/SMALLPACK_TAILS_2026-08-02.md:90 с носителем D39.4/D39.5. Склеивать их одной фразой значит завести новый неверный факт вместо старого. Пиши так: «Хвост LOW/NOTE-находок адверсариала D39.4 (14 LOW + 14 NOTE) — диспозиция «не чинить», носитель D39.4/D39.5; живые пересечения — строки 43/78 (прежний указатель на строку 53 мёртв: она закрыта D39.82)». И раз ссылка уходит в docs/archive/reports/, назови файл явно — иначе следующая сессия снова не найдёт, откуда взялась диспозиция. + +**Разбор опровергателя:** Воспроизведено с собственным положительным контролем (189 строк / 3 из 3 соседних номеров). Указатель мёртв, и мёртв он под шапкой, которая явно обещает, что номер резолвится в таблицу. Класс B, major — подтверждаю. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верное, но одну деталь предложенной редакции надо снять: «строка 53 закрыта» D39.82 закрыл СВОЙ предмет (дубль ключа пар-файла через `putUniqueDC`), а хвост LOW/NOTE адверсариала — это ДРУГАЯ сущность, чья диспозиция «не чинить» стоит в docs/archive/reports/SMALLPACK_TAILS_2026-08-02.md:90 с носителем D39.4/D39.5. Склеивать их одной фразой значит завести новый неверный факт вместо старого. Пиши так: «Хвост LOW/NOTE-находок адверсариала D39.4 (14 LOW + 14 NOTE) — диспозиция «не чинить», носитель D39.4/D39.5; живые пересечения — строки 43/78 (прежний указатель на строку 53 мёртв: она закрыта D39.82)». И раз ссылка уходит в docs/archive/reports/, назови файл явно — иначе следующая сессия снова не найдёт, откуда взялась диспозиция. --- ## N012 · MAJOR · `backend/docs/D15.2-content-addressed-resume-spec.md:529` · A-дрейф -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **Схема**: миграция **v8** (текущий максимум — **v7** после cheap-gate миграции этой сессии: `migrate.go` v7 = `ALTER TABLE retrieval_state ADD COLUMN n_style_flags/style_detail`) +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **Схема**: миграция **v8** (текущий максимум — **v7** после cheap-gate миграции этой сессии: `migrate.go` v7 = `ALTER TABLE retrieval_state ADD COLUMN n_style_flags/style_detail`) + **Почему протухло:** Голова схемы сегодня v16, а не v7, и номер v8 давно занят чужой миграцией. Спека при этом ЖИВА: строка бэклога 49 («Этапы Б+В спеки D15.2») открыта, и README:96 отсылает к спеке как к дизайну-оф-рекорд. Сессия, реализующая §11/§12 по букве, заведёт коллизию номера. Рядом — мёртвый line-якорь `render.go:212`: литерал `tm-request-v2`, о котором спека говорит, лежит на render.go:306, а на 212 — тело подстановщика плейсхолдеров. -**Улика — команда:** `cd backend && grep -n '^\t// v1[0-6]' internal/store/migrate.go | tail -3; grep -n 'func SchemaHead' internal/store/migrate.go; grep -rn 'tm-request-v' --include=*.go internal/ | grep -v _test; sed -n '212p' internal/pipeline/render.go` -**Улика — вывод:** // v14 (pack-19): the per-chunk observability of the two new $0 flaggers, riding the same + +**Улика — команда:** +``` +cd backend && grep -n '^\t// v1[0-6]' internal/store/migrate.go | tail -3; grep -n 'func SchemaHead' internal/store/migrate.go; grep -rn 'tm-request-v' --include=*.go internal/ | grep -v _test; sed -n '212p' internal/pipeline/render.go +``` +**Улика — вывод:** +``` + // v14 (pack-19): the per-chunk observability of the two new $0 flaggers, riding the same // v15 (row 103): the run-event OUTBOX. A line of `events.jsonl` is a projection of a row committed // v16 — the bank-stop memory (the D39.144 flag model): every normalized surface a --verify-bank stop 485:func SchemaHead() int { return len(migrations) } internal/pipeline/render.go:306: w("tm-request-v2", req.BookID, strconv.Itoa(req.Chapter), ... --- (render.go:212) return "", fmt.Errorf("pipeline: unknown placeholder {{%s}} in template", name) +``` **Другие носители того же факта:** backend/docs/D15.2-content-addressed-resume-spec.md:560 — «**Миграция v8** (§11): колонки `guard_hash`, `verdict_snapshot`» -**Предложенное действие:** Дописать в ⚠-шапку спеки одну строку-эррату (тело §11/§12 по норме не переписывать): «⚠ Эррата: с момента письма голова схемы уехала v7→v16, номер v8 занят; при реализации взять СЛЕДУЮЩИЙ свободный (`store.SchemaHead()+1`). Якорь `render.go:212` съехал — литерал `tm-request-v2` на `render.go:306`.» + +**Предложенное действие (автора находки):** Дописать в ⚠-шапку спеки одну строку-эррату (тело §11/§12 по норме не переписывать): «⚠ Эррата: с момента письма голова схемы уехала v7→v16, номер v8 занят; при реализации взять СЛЕДУЮЩИЙ свободный (`store.SchemaHead()+1`). Якорь `render.go:212` съехал — литерал `tm-request-v2` на `render.go:306`.» + **Разрушающее:** False --- ## N013 · MAJOR · `docs/PROGRESS.md:3` · A-дрейф -**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** зеркало отстало на ШЕСТЬ миноров (`frontend/docs/api-contract/openapi.yaml` — `0.2.3` против канона `0.8.0`, и первый из них — ЛОМАЮЩИЙ 0.3.0), 16 операций против 20 +**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> зеркало отстало на ШЕСТЬ миноров (`frontend/docs/api-contract/openapi.yaml` — `0.2.3` против канона `0.8.0`, и первый из них — ЛОМАЮЩИЙ 0.3.0), 16 операций против 20 + **Почему протухло:** Канон уже НЕ 0.8.0: в `docs/architecture/14-api-contract/openapi.yaml` стоит 0.9.0 — минор уехал с лендингом P12 31.08, о котором сообщает ЭТА ЖЕ строка шапки. Значит и отставание зеркала теперь СЕМЬ миноров, а не шесть. Число операций (16 против 20) проверено и верно. -**Улика — команда:** `grep -rn '^ version:' docs/architecture/14-api-contract/*.yaml; grep -n '^ version:' frontend/docs/api-contract/openapi.yaml; echo -n 'canon ops: '; grep -c 'operationId:' docs/architecture/14-api-contract/openapi.yaml; echo -n 'mirror ops: '; grep -c 'operationId:' frontend/docs/api-contract/openapi.yaml` -**Улика — вывод:** docs/architecture/14-api-contract/openapi.yaml:5: version: 0.9.0 + +**Улика — команда:** +``` +grep -rn '^ version:' docs/architecture/14-api-contract/*.yaml; grep -n '^ version:' frontend/docs/api-contract/openapi.yaml; echo -n 'canon ops: '; grep -c 'operationId:' docs/architecture/14-api-contract/openapi.yaml; echo -n 'mirror ops: '; grep -c 'operationId:' frontend/docs/api-contract/openapi.yaml +``` +**Улика — вывод:** +``` +docs/architecture/14-api-contract/openapi.yaml:5: version: 0.9.0 5: version: 0.2.3 canon ops: 20 mirror ops: 16 @@ -259,39 +500,74 @@ mirror ops: 16 (перепроверено на текущем HEAD 162ab9b: canon version: 0.9.0 / mirror version: 0.2.3) docs/README.md:26 знает верное: «пак **P12 ... ПРИНЯТ И ЗАЛЕНДЖЕН 31.08** (**D39.180**) вместе с контрактным минором **0.9.0**» +``` **Другие носители того же факта:** docs/PROGRESS.md:10 — «зеркало на 0.2.3, канон на 0.8.0 и уйдёт на 0.9.0 с лендингом P12» (лендинг уже был 31.08), frontend/docs/frontend-PROGRESS.md:9 — «Состояние зеркала контракта — 0.2.3 против канона 0.8.0 (и 0.9.0 придёт с лендингом P12)», docs/README.md:29 (строка «Контракт» таблицы активных промтов) — «минор **0.8.0** ПРИНЯТ и заленджен 29.08», про 0.9.0 в этой строке нет ни слова, хотя соседняя строка «Платформа» (README:26) его называет -**Предложенное действие:** В обеих строках шапки заменить «канона `0.8.0`» → «канона `0.9.0`» и «ШЕСТЬ миноров» → «СЕМЬ миноров»; в строке 10 снять будущее время «уйдёт на 0.9.0 с лендингом P12» на «ушёл на 0.9.0 лендингом P12 31.08, D39.180». Тем же заходом поправить frontend/docs/frontend-PROGRESS.md:9 и строку «Контракт» в docs/README.md:29 — иначе разморозка фронта будет спланирована на неверное число перегенераций. + +**Предложенное действие (автора находки):** В обеих строках шапки заменить «канона `0.8.0`» → «канона `0.9.0`» и «ШЕСТЬ миноров» → «СЕМЬ миноров»; в строке 10 снять будущее время «уйдёт на 0.9.0 с лендингом P12» на «ушёл на 0.9.0 лендингом P12 31.08, D39.180». Тем же заходом поправить frontend/docs/frontend-PROGRESS.md:9 и строку «Контракт» в docs/README.md:29 — иначе разморозка фронта будет спланирована на неверное число перегенераций. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Само действие верно, но один из перечисленных «носителей» я снимаю: docs/README.md:29 (строка «Контракт») говорит «минор 0.8.0 ПРИНЯТ и заленджен 29.08 (D39.169) вместе с паком P11» — это ИСТОРИЧЕСКИ ВЕРНОЕ утверждение о контрактном паке контрактной роли, а не заявление «канон сегодня 0.8.0»; 0.9.0 приехал платформенным паком и назван в своей строке (README:26). Правку туда вносить не надо — максимум добавить ссылку «далее 0.9.0 — см. строку Платформа». Править надо ровно три места: docs/PROGRESS.md:3, docs/PROGRESS.md:10, frontend/docs/frontend-PROGRESS.md:9. + +**Разбор опровергателя:** Опровергнуть не удалось: канон в рабочем дереве — 0.9.0, зеркало — 0.2.3, и все семь промежуточных миноров подтверждены историей самого файла (не выведены арифметикой из двух чисел). Счёт операций 20/16 воспроизведён. Носители-копии (PROGRESS:10, frontend-PROGRESS:9) существуют дословно. Разрушительность действия проверена: правка не сносит текста, а меняет два числа — риск низкий. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, и лучше исполнить именно вторую, критериальную его половину: заменить ручной счёт на «ни один пак платформы после P7 этот долг не взял (проверка: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` — пусто)», чтобы число не гнило при каждом следующем лендинге. Если счёт всё-таки оставлять — «шесть паков (P8-FIX · P8-REVIEW · P9 · P10 · P11 · P12), плюс непронумерованный `sqlc`». --- ## N014 · MAJOR · `docs/PROGRESS.md:3` · A-дрейф -**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** ⚠ **Причина, по которой редактор съел 73% захода, НЕ УСТАНОВЛЕНА** — три каузальные истории построены и две сняты; следующий шаг — контролируемая проба по ЧЕТЫРЁМ осям (§14 отчёта) +**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> ⚠ **Причина, по которой редактор съел 73% захода, НЕ УСТАНОВЛЕНА** — три каузальные истории построены и две сняты; следующий шаг — контролируемая проба по ЧЕТЫРЁМ осям (§14 отчёта) + **Почему протухло:** Числа «73%» в цитируемом отчёте холодного прогона НЕТ вовсе. Отчёт даёт долю редактора с ДВУМЯ знаменателями: 49.3% от всего прогона ($0.214865 из $0.436110) и 81.3% внутри порции П1. Более того, адверсариальный проход самого отчёта отдельной строкой ПРИНЯЛ находку «редактор — 81% без знаменателя». Шапка воспроизводит третье число, не принадлежащее ни одному знаменателю, и именно оно формулирует предмет следующей ПЛАТНОЙ пробы. -**Улика — команда:** `grep -n '73%' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md; sed -n '211,213p;572p' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md` -**Улика — вывод:** (grep '73%' — НОЛЬ строк) + +**Улика — команда:** +``` +grep -n '73%' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md; sed -n '211,213p;572p' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md +``` +**Улика — вывод:** +``` +(grep '73%' — НОЛЬ строк) 211:**Доля редактора — со знаменателем, потому что их два:** 212:**49.3%** от всего прогона ($0.214865 из $0.436110) и **81.3%** внутри П1, куда целиком уместилась 213:редакторская волна. Для сравнения: у minirun редактор — 80.6% его захода. 572:| «редактор — 81%» без знаменателя (49.3% от прогона) | **ПРИНЯТО** (§4.2) | -**Положительный контроль:** grep -c '%' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md → 14 (знак процента в файле есть и грепается); grep -rn '49\.3%\|81\.3%' → docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:212 и :572. Инструмент в этой области находит ровно то, что там лежит. -**Предложенное действие:** Заменить «73% захода» на числа отчёта со знаменателем: «49.3% всего прогона ($0.214865 из $0.436110) / 81.3% внутри П1». Проверить заодно, не считана ли «73%» из какого-то третьего места — в живых доках это число встречается только в несвязанных контекстах (research/20 точность майнинга, exp01 Batch-надбавка, research/18 DocRepair-предпочтение). +``` +**Положительный контроль:** +``` +grep -c '%' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md → 14 (знак процента в файле есть и грепается); grep -rn '49\.3%\|81\.3%' → docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:212 и :572. Инструмент в этой области находит ровно то, что там лежит. +``` + +**Предложенное действие (автора находки):** Заменить «73% захода» на числа отчёта со знаменателем: «49.3% всего прогона ($0.214865 из $0.436110) / 81.3% внутри П1». Проверить заодно, не считана ли «73%» из какого-то третьего места — в живых доках это число встречается только в несвязанных контекстах (research/20 точность майнинга, exp01 Batch-надбавка, research/18 DocRepair-предпочтение). + +**Разбор опровергателя:** Опровергнуть не удалось, и находка оказалась даже сильнее заявленного: «73%» противоречит не только двум знаменателям отчёта, но и ДОСЛОВНОМУ слову владельца в шапке того же отчёта («половину захода»), а адверсариальный проход отчёта отдельной строкой уже принял находку «81% без знаменателя». Прямой пересчёт из таблицы 4.2 даёт 49.27% — 73% не получается ни при каком знаменателе отчёта. Число формулирует предмет следующей ПЛАТНОЙ пробы, поэтому severity major оправдан. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, и лучше исполнить именно вторую, критериальную его половину: заменить ручной счёт на «ни один пак платформы после P7 этот долг не взял (проверка: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` — пусто)», чтобы число не гнило при каждом следующем лендинге. Если счёт всё-таки оставлять — «шесть паков (P8-FIX · P8-REVIEW · P9 · P10 · P11 · P12), плюс непронумерованный `sqlc`». --- ## N015 · MAJOR · `docs/PROGRESS.md:3` · E-второй-носитель -**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-01, голова D39.184 — КУРС: движок и платформа до «работает и отдаёт результат» ... **ОЧЕРЕДЬ №22** (единственный носитель — здесь; роль передана 01.09, №21 закрыт нотой передачи D39.184) +**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-01, голова D39.184 — КУРС: движок и платформа до «работает и отдаёт результат» ... **ОЧЕРЕДЬ №22** (единственный носитель — здесь; роль передана 01.09, №21 закрыт нотой передачи D39.184) + **Почему протухло:** Шапка объявляет себя ЕДИНСТВЕННЫМ носителем состояния очереди, но не несёт двух лендингов 31.08, которые несёт `docs/README.md`: бэкенд-пак «ДЕНЬГИ И ЧЕСТНОСТЬ ВЫДАЧИ» (D39.182) и ратифицированный владельцем ЗАКОН РАСКРЫТИЯ движка (D39.181, «честная цена: закон загейчен на 40%», живое — применение к десяти экземплярам фазой 2). Из номеров D39.18x шапка называет только D39.180 и D39.184. Следствие для заказа владельца (0б) «планы доработок в БЭКЕНД»: читатель очереди не узнаёт ни о свежем лендинге зоны, ни о заявленном остатке ратифицированного закона. -**Улика — команда:** `sed -n '1,18p' docs/PROGRESS.md | grep -o 'D39\.18[0-9]' | sort -u; echo '--- где D39.182 в PROGRESS.md ---'; grep -n 'D39\.182' docs/PROGRESS.md | cut -c1-90; echo '--- README ---'; grep -n 'ДЕНЬГИ И ЧЕСТНОСТЬ' docs/README.md | cut -c1-200` -**Улика — вывод:** D39.180 + +**Улика — команда:** +``` +sed -n '1,18p' docs/PROGRESS.md | grep -o 'D39\.18[0-9]' | sort -u; echo '--- где D39.182 в PROGRESS.md ---'; grep -n 'D39\.182' docs/PROGRESS.md | cut -c1-90; echo '--- README ---'; grep -n 'ДЕНЬГИ И ЧЕСТНОСТЬ' docs/README.md | cut -c1-200 +``` +**Улика — вывод:** +``` +D39.180 D39.184 --- где D39.182 в PROGRESS.md --- 209:| 247 | **§2.2 закона раскрытия опровергает себя следующей же строкой — эррата зонного @@ -299,110 +575,221 @@ D39.184 25: | Бэкенд | активного НЕТ | пак **«ДЕНЬГИ И ЧЕСТНОСТЬ ВЫДАЧИ» ПРИНЯТ И ЗАЛЕНДЖЕН 31.08** (**D39.182**): закон раскрытия (**D39.181**, ратифицирован владельцем (D39.182 во всём PROGRESS.md встречается РОВНО один раз — внутри строки бэклога 247, то есть только как ссылка долга, не как запись состояния) -**Положительный контроль:** sed -n '1,18p' docs/PROGRESS.md | grep -o 'D39\.1[0-9][0-9]' | sort -u → 43 РАЗЛИЧНЫХ номера, включая соседние D39.180 и D39.184. Шапка насыщена D-ссылками и грепается; пустой результат по D39.181/D39.182 — отсутствие факта, а не слепота инструмента. +``` +**Положительный контроль:** +``` +sed -n '1,18p' docs/PROGRESS.md | grep -o 'D39\.1[0-9][0-9]' | sort -u → 43 РАЗЛИЧНЫХ номера, включая соседние D39.180 и D39.184. Шапка насыщена D-ссылками и грепается; пустой результат по D39.181/D39.182 — отсутствие факта, а не слепота инструмента. +``` **Другие носители того же факта:** docs/README.md:25 (строка «Бэкенд» таблицы активных промтов) — несёт и лендинг D39.182, и ратификацию D39.181, docs/architecture/05-decisions-index.md:245–246 — реестровые строки D39.181/D39.182 со столбцом «ЖИВОЕ» (применение закона к десяти экземплярам — фаза 2; строка бэклога 246), docs/PROGRESS.md:302 — упоминание D39.181 в зонной записи бэкенда, ниже шапки -**Предложенное действие:** Дописать в шапку одну фразу вида: «31.08 в зоне бэкенда лёг пак «деньги и честность выдачи» (D39.182) и РАТИФИЦИРОВАН закон раскрытия движка (D39.181); живой остаток — фаза 2 (применение к десяти экземплярам) и строка бэклога 246». Заодно продлить провенанс-перечень строки 16 до 248: строки 244–248 заведены 31.08 и в перечне обрываются на 243 (сами счётчики 189/88/125/59/0 при этом сходятся с `counts.py`). + +**Предложенное действие (автора находки):** Дописать в шапку одну фразу вида: «31.08 в зоне бэкенда лёг пак «деньги и честность выдачи» (D39.182) и РАТИФИЦИРОВАН закон раскрытия движка (D39.181); живой остаток — фаза 2 (применение к десяти экземплярам) и строка бэклога 246». Заодно продлить провенанс-перечень строки 16 до 248: строки 244–248 заведены 31.08 и в перечне обрываются на 243 (сами счётчики 189/88/125/59/0 при этом сходятся с `counts.py`). + **Разрушающее:** False -**⚠ Опровергатель поправил действие:** Действие верно, и лучше исполнить именно вторую, критериальную его половину: заменить ручной счёт на «ни один пак платформы после P7 этот долг не взял (проверка: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` — пусто)», чтобы число не гнило при каждом следующем лендинге. Если счёт всё-таки оставлять — «шесть паков (P8-FIX · P8-REVIEW · P9 · P10 · P11 · P12), плюс непронумерованный `sqlc`». --- ## N016 · MAJOR · `docs/PROGRESS.md:80` · C-исполненное-как-открытое -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Редакторская волна на ЭТОМ прогоне не гонялась (coldrun-a/b: ноль редакторских вызовов) ⇒ строка жива +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Редакторская волна на ЭТОМ прогоне не гонялась (coldrun-a/b: ноль редакторских вызовов) ⇒ строка жива + **Почему протухло:** Довод «⇒ строка жива» опирается на прогоны coldrun-a/b, но 31.08 отработал НОВЫЙ холодный прогон (coldrun-v16), в котором редакторская волна гонялась и оплачена: 4 оплаченных чекпойнта `edit/editor/deepseek-v4-pro` на $0.214865 — 49.3% всего захода $0.436110. То есть «полная цена холодного старта с редактурой» больше не «не измерена»: она измерена частично (доставлено 3 юнита из 14) и имеет собственную проекцию $1.1377. Числа самой строки ($0.031639 драфт-стадия, $0.045095 весь заход) остаются ИЮЛЬСКИМИ и по D39.179 п.1 множатся на 4.47 в сегодняшних ценах — в строке об этом ни слова. -**Улика — команда:** `sqlite3 'file:/home/ubuntu/projects/textmachine/books/gu-zhenren/coldrun-v16/guzhenren-coldrun-v16.db?mode=ro' "select stage,role,model_actual,count(*),round(sum(cost_usd),6) from checkpoints where cost_usd>0 group by 1,2,3 order by 5 desc;"` -**Улика — вывод:** edit|editor|deepseek-v4-pro|4|0.214865 + +**Улика — команда:** +``` +sqlite3 'file:/home/ubuntu/projects/textmachine/books/gu-zhenren/coldrun-v16/guzhenren-coldrun-v16.db?mode=ro' "select stage,role,model_actual,count(*),round(sum(cost_usd),6) from checkpoints where cost_usd>0 group by 1,2,3 order by 5 desc;" +``` +**Улика — вывод:** +``` +edit|editor|deepseek-v4-pro|4|0.214865 draft|translator|deepseek-v4-pro|5|0.103305 draft|translator|deepseek-v4-flash|20|0.068135 terminology|terminologist|deepseek-v4-flash|6|0.040617 terminology|classifier|deepseek-v4-flash|3|0.009188 (доля edit от всей суммы — 49.27%) +``` **Другие носители того же факта:** docs/PROGRESS.md:3 (CURRENT-STATE п.2 — числа холодного прогона), docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md §4.2/§4.4 -**Предложенное действие:** Переписать хвост строки 16: назвать coldrun-v16 (31.08) как первый холодный прогон С редакторской волной, вписать $0.214865 edit / $0.436110 заход / проекция $1.1377 при 3 доставленных юнитах из 14, и явно пометить прежние июльские числа ценовой эрой (множитель 4.47, D39.179 п.1). Живым остатком оставить только то, что действительно не измерено, — цена ДОВЕДЁННОЙ до конца книги. + +**Предложенное действие (автора находки):** Переписать хвост строки 16: назвать coldrun-v16 (31.08) как первый холодный прогон С редакторской волной, вписать $0.214865 edit / $0.436110 заход / проекция $1.1377 при 3 доставленных юнитах из 14, и явно пометить прежние июльские числа ценовой эрой (множитель 4.47, D39.179 п.1). Живым остатком оставить только то, что действительно не измерено, — цена ДОВЕДЁННОЙ до конца книги. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно и строку НЕ закрывать — только переписать. Живым остатком оставить ровно то, что находка и называет: цена ДОВЕДЁННОЙ до конца книги (прогон отдал 3 юнита из 14). При переписывании привести в согласие и колонку гейта: сегодня она говорит «первый прогон добора идеала», а CURRENT-STATE числит строку 16 среди снятых холодным прогоном — оставить одну версию, иначе класс E воспроизведётся внутри одного файла. + +**Разбор опровергателя:** Историческая часть цитаты («на coldrun-a/b ноль редакторских вызовов») ВЕРНА и я её не оспариваю — протух ВЫВОД «⇒ строка жива» в прежнем объёме. 31.08 отработал coldrun-v16, и в его собственной БД четыре оплаченных редакторских чекпойнта на $0.214865 — почти половина захода. То есть «полная цена холодного старта с редактурой» перешла из «не измерена» в «измерена частично». Отдельно подтверждаю вторую половину находки: июльские числа строки ($0.031639 / $0.045095) по ратифицированной D39.179 п.1 множатся на 4.47 в сегодняшних деньгах, и строка об этом молчит, то есть вводит читателя в смету, ошибочную вчетверо. Третий носитель, который находка не назвала: CURRENT-STATE (`docs/PROGRESS.md:3`) числит строку 16 в списке «гейт строк 16/154/157/160/198/202/216», снятом холодным прогоном, а колонка гейта самой строки говорит «первый прогон добора идеала — после решения по 46» — то есть шапка и таблица расходятся и в этом месте. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно и строку НЕ закрывать — только переписать. Живым остатком оставить ровно то, что находка и называет: цена ДОВЕДЁННОЙ до конца книги (прогон отдал 3 юнита из 14). При переписывании привести в согласие и колонку гейта: сегодня она говорит «первый прогон добора идеала», а CURRENT-STATE числит строку 16 среди снятых холодным прогоном — оставить одну версию, иначе класс E воспроизведётся внутри одного файла. --- ## N017 · MAJOR · `docs/PROGRESS.md:143` · A-дрейф -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** скоро (гейт: замер размеров юнитов на первом холодном прогоне, вместе со 157) +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> скоро (гейт: замер размеров юнитов на первом холодном прогоне, вместе со 157) + **Почему протухло:** Гейт назначен на событие, которое УЖЕ произошло (первый холодный прогон, 31.08), и прогон показал, что назначенный замер этим путём недостижим: манифест не публикует размеров юнитов В ЗНАКАХ вовсе — юнит несёт только `id`, `first_chunk_idx`, `chunk_count`, `edit_unit_id`. Отчёт прогона говорит прямо: «Заказ в этой части выполнить нечем: носителя нет». ⚠ Дополнительная ловушка для принимающего: отчёт подписал числа семейств под «строкой 154», а размеры юнитов — под «строкой 157», то есть КРЕСТ-НАКРЕСТ относительно таблицы. -**Улика — команда:** `python3 -c "import json;d=json.load(open('/home/ubuntu/projects/textmachine/books/gu-zhenren/coldrun-v16/guzhenren-coldrun-v16.db.manifest.json'));ch=d['chapters'][0];print(list(ch.keys()));print(json.dumps(ch['units'][0]))"` -**Улика — вывод:** ['id', 'number', 'heading', 'units_total', 'chunks_total', 'units'] + +**Улика — команда:** +``` +python3 -c "import json;d=json.load(open('/home/ubuntu/projects/textmachine/books/gu-zhenren/coldrun-v16/guzhenren-coldrun-v16.db.manifest.json'));ch=d['chapters'][0];print(list(ch.keys()));print(json.dumps(ch['units'][0]))" +``` +**Улика — вывод:** +``` +['id', 'number', 'heading', 'units_total', 'chunks_total', 'units'] {"id": "c3aa4cbbb53de134:cb440937:0", "first_chunk_idx": 0, "chunk_count": 1, "edit_unit_id": 0} (отчёт §9: «Распределения размеров юнитов В ЗНАКАХ манифест не публикует вовсе — ни `--json`, ни персист-сайдкар символьных размеров не несут. Заказ в этой части выполнить нечем: носителя нет.») -**Положительный контроль:** тот же python3-вызов печатает НЕПУСТОЙ список ключей главы и юнита — инструмент манифест читает и поля видит, значит отсутствие размеров в знаках — факт, а не пустой хит. +``` +**Положительный контроль:** +``` +тот же python3-вызов печатает НЕПУСТОЙ список ключей главы и юнита — инструмент манифест читает и поля видит, значит отсутствие размеров в знаках — факт, а не пустой хит. +``` **Другие носители того же факта:** docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md §9 (подписано «Строка 157 — ЧАСТИЧНО», хотя предмет — гейт 154) -**Предложенное действие:** Пере-написать гейт строки 154: событие «первый холодный прогон» наступило 31.08 и замер НЕ дало — назначить настоящее предусловие (публикация размеров юнитов в знаках в манифесте/сайдкаре) отдельным носителем, либо признать замер невозможным и решать по косвенной мере (входные токены черновых вызовов, 1557–1863 на чанк). Тем же касанием снять крест-накрест подпись отчёта §9 (154↔157). + +**Предложенное действие (автора находки):** Пере-написать гейт строки 154: событие «первый холодный прогон» наступило 31.08 и замер НЕ дало — назначить настоящее предусловие (публикация размеров юнитов в знаках в манифесте/сайдкаре) отдельным носителем, либо признать замер невозможным и решать по косвенной мере (входные токены черновых вызовов, 1557–1863 на чанк). Тем же касанием снять крест-накрест подпись отчёта §9 (154↔157). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно, но одна деталь его формулировки опасна: «назначить настоящее предусловие … отдельным носителем» надо делать НЕ молча — публикация размеров юнитов в знаках это новое поле контракта манифеста (`manifest_version`), то есть решение уровня ноты, а не строки бэклога. Плюс уточнение по косвенной мере: 1557–1863 — это входные токены на ЧАНК, а юнит по этому же прогону несёт от 1 до 2 чанков (медиана 1), поэтому пере-считывать в цену юнита надо через `chunk_count`, а не приравнивать юнит чанку. + +**Разбор опровергателя:** Положительный контроль внутри самой команды: python печатает НЕПУСТЫЕ списки ключей книги, главы и юнита — манифест читается, поля видны, значит отсутствие символьных размеров это факт, а не пустой хит. Гейт назначен на событие, которое произошло 31.08, и событие ответа не дало. Крест-накрест подтверждаю СВОИМ прочтением: предмет строки 154 — «цена в размере ЮНИТОВ не замерена», и раздел отчёта про размеры юнитов подписан «Строка 157»; предмет строки 157 — `family_max_members`/`families_held`, и раздел с числами семейств подписан «Строка 154». Промт §5.7 обе строки гейтит одним прогоном и подписей не путает — путаница родилась в отчёте. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, но одна деталь его формулировки опасна: «назначить настоящее предусловие … отдельным носителем» надо делать НЕ молча — публикация размеров юнитов в знаках это новое поле контракта манифеста (`manifest_version`), то есть решение уровня ноты, а не строки бэклога. Плюс уточнение по косвенной мере: 1557–1863 — это входные токены на ЧАНК, а юнит по этому же прогону несёт от 1 до 2 чанков (медиана 1), поэтому пере-считывать в цену юнита надо через `chunk_count`, а не приравнивать юнит чанку. --- ## N018 · MAJOR · `docs/PROGRESS.md:157` · A-дрейф -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** рядом `bank-stop.json` — полная таблица подписи для S5 +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> рядом `bank-stop.json` — полная таблица подписи для S5 + **Почему протухло:** Файла `.bank-stop.json` больше НЕ существует: он снесён вместе с писателем и четырьмя тестами при постройке входной двери шва (D39.158) — в Go-коде движка нет ни одного вхождения, а холодный прогон 31.08 подтвердил это на диске (в каталоге прогона лежит `bank-stop.txt`, `bank-stop.json` нет). Строка обещает платформе/фронту машинную таблицу подписи для экрана S5, которой нет ни на диске, ни в коде. -**Улика — команда:** `grep -rn 'bank-stop\.json' backend --include=*.go; ls -L books/gu-zhenren/coldrun-v16/ | grep bank-stop` -**Улика — вывод:** (греп по *.go — НИ ОДНОГО вхождения) + +**Улика — команда:** +``` +grep -rn 'bank-stop\.json' backend --include=*.go; ls -L books/gu-zhenren/coldrun-v16/ | grep bank-stop +``` +**Улика — вывод:** +``` +(греп по *.go — НИ ОДНОГО вхождения) --- guzhenren-coldrun-v16.db.bank-stop.txt -**Положительный контроль:** grep -rn 'bank-stop\.txt|bank\.json' backend/internal backend/cmd → backend/internal/pipeline/mining.go:371: return r.Book.ProjectDB + ".bank-stop.txt"; backend/internal/pipeline/bankexport.go:85: func (r *Runner) bankExportPath() string { return r.Book.ProjectDB + ".bank.json" } — инструмент имена сайдкаров в этом коде находит, значит пустота именно у `bank-stop.json`. +``` +**Положительный контроль:** +``` +grep -rn 'bank-stop\.txt|bank\.json' backend/internal backend/cmd → backend/internal/pipeline/mining.go:371: return r.Book.ProjectDB + ".bank-stop.txt"; backend/internal/pipeline/bankexport.go:85: func (r *Runner) bankExportPath() string { return r.Book.ProjectDB + ".bank.json" } — инструмент имена сайдкаров в этом коде находит, значит пустота именно у `bank-stop.json`. +``` **Другие носители того же факта:** platform/docs/STACK_DECISIONS.md:410 (`.bank-stop.json` в таблице каналов: «⚠ канал существует и…»), backend/docs/pipeline.puml:72 («.bank-stop.json — машине (строка 101): ПОЛНАЯ таблица»), frontend/docs/frontend-PROGRESS.md:499 («сайдкар `.bank.json` + `bank-stop.json`»), platform/docs/platform-PROGRESS.md:2695 и :2771 (перечень сайдкаров и их атомарности), docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md:363 — ЕДИНСТВЕННЫЙ носитель с верным текстом («Файла `bank-stop.json` не существует») -**Предложенное действие:** Снять из строки 169 упоминание `bank-stop.json` (заменить на: «машинная таблица `.bank-stop.json` СНЕСЕНА D39.158; оператору остался `.bank-stop.txt`, машине — `.bank.json`»), и тем же касанием пройти прочих живых носителей, где файл всё ещё числится существующим каналом. + +**Предложенное действие (автора находки):** Снять из строки 169 упоминание `bank-stop.json` (заменить на: «машинная таблица `.bank-stop.json` СНЕСЕНА D39.158; оператору остался `.bank-stop.txt`, машине — `.bank.json`»), и тем же касанием пройти прочих живых носителей, где файл всё ещё числится существующим каналом. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно; список носителей уточняю по исполнению. `docs/architecture/18-bank-ontology.md:45` ПРАВИТЬ НЕ НАДО — там файл назван в ПРОШЕДШЕМ времени как урок («Машинная таблица стопа `.bank-stop.json` была…»), это легитимная история, а не дрейф; так же и `backend/docs/SEAM_FIX3_PLAN.md:14` / `SEAM_PACK_FINDINGS.md:1415-1424`, которые описывают САМ снос. Протухли ровно три живых носителя, где файл числится СУЩЕСТВУЮЩИМ каналом: `platform/docs/STACK_DECISIONS.md:410` (строка таблицы каналов «⚠ канал существует и не потребляется» — прочитал, там же ложное «атомарно»), `backend/docs/pipeline.puml:72` и `frontend/docs/frontend-PROGRESS.md:499`. Два из них — чужие зоны: правка пингом, не касанием. + +**Разбор опровергателя:** Это самый крепкий из десяти. Отрицательный результат снабжён живым положительным контролем: тем же грепом в тех же файлах находятся `.bank-stop.txt` и `.bank.json`, значит пустота у `bank-stop.json` — факт. `git log -S` показывает и рождение символа (`0e69bc1`), и его смерть (`d1eb8a9`, входная дверь шва D39.158) — то есть это не опечатка и не мой промах с симлинком: каталог прогона я читал через `ls -L`, и там лежит только `.bank-stop.txt`. Зона платформы независимо пишет ровно то же самое и прямо предупреждает чужие доки. Строка 169 обещает фронту машинную таблицу для экрана S5, которой нет. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно; список носителей уточняю по исполнению. `docs/architecture/18-bank-ontology.md:45` ПРАВИТЬ НЕ НАДО — там файл назван в ПРОШЕДШЕМ времени как урок («Машинная таблица стопа `.bank-stop.json` была…»), это легитимная история, а не дрейф; так же и `backend/docs/SEAM_FIX3_PLAN.md:14` / `SEAM_PACK_FINDINGS.md:1415-1424`, которые описывают САМ снос. Протухли ровно три живых носителя, где файл числится СУЩЕСТВУЮЩИМ каналом: `platform/docs/STACK_DECISIONS.md:410` (строка таблицы каналов «⚠ канал существует и не потребляется» — прочитал, там же ложное «атомарно»), `backend/docs/pipeline.puml:72` и `frontend/docs/frontend-PROGRESS.md:499`. Два из них — чужие зоны: правка пингом, не касанием. --- ## N019 · MAJOR · `docs/PROGRESS.md:182` · D-противоречие-ратифицированному -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Цена ≈ $0.05 на десятиглавую книгу. +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Цена ≈ $0.05 на десятиглавую книгу. + **Почему протухло:** Дважды протухла. (1) Цифра «≈$0.05» СНЯТА ратифицированной нотой D39.179 п.1 как протухшая: пере-пин DeepSeek 15.08 даёт множитель 4.47, а замер 31.08 на ровно такой десятиглавой книге дал $0.436110 при собственной проекции движка $1.1377 — то есть строка обещает платформе смету, ошибочную примерно в 9–23 раза. (2) Гейт в колонке веса — «скоро (гейт: холодный прогон движка)» — УЖЕ ПАЛ: холодный прогон отработал 31.08; настоящий блокер строки сместился на ключи провайдера (журнал платформы). Шапка CURRENT-STATE это знает, таблица — нет. -**Улика — команда:** `awk '/^## D39\.179/,/^## D39\.180/' docs/architecture/05-decisions-log.md | sed -n '3p'; sqlite3 'file:/home/ubuntu/projects/textmachine/books/gu-zhenren/coldrun-v16/guzhenren-coldrun-v16.db?mode=ro' 'select round(sum(cost_usd),6) from checkpoints;'` -**Улика — вывод:** 1. **Санкция владельца 31.08: ≈$0.60, потолок $0.80, ДЕСЯТЬ глав ПОЛНОЙ ЦЕПЬЮ включая редакторскую волну.** … **Прежняя цифра очереди «≈$0.05» СНЯТА как протухшая:** DeepSeek пере-пинен коммитом 15.08 (`76049bb`, D39.137) под цены, вступившие в силу 16.08 16:00 UTC; заход `bank-low` ($0.045095 в июльских деньгах) по ним стоит $0.2017 — множитель 4.47, пере-считан из его же токенов. + +**Улика — команда:** +``` +awk '/^## D39\.179/,/^## D39\.180/' docs/architecture/05-decisions-log.md | sed -n '3p'; sqlite3 'file:/home/ubuntu/projects/textmachine/books/gu-zhenren/coldrun-v16/guzhenren-coldrun-v16.db?mode=ro' 'select round(sum(cost_usd),6) from checkpoints;' +``` +**Улика — вывод:** +``` +1. **Санкция владельца 31.08: ≈$0.60, потолок $0.80, ДЕСЯТЬ глав ПОЛНОЙ ЦЕПЬЮ включая редакторскую волну.** … **Прежняя цифра очереди «≈$0.05» СНЯТА как протухшая:** DeepSeek пере-пинен коммитом 15.08 (`76049bb`, D39.137) под цены, вступившие в силу 16.08 16:00 UTC; заход `bank-low` ($0.045095 в июльских деньгах) по ним стоит $0.2017 — множитель 4.47, пере-считан из его же токенов. --- 0.43611 +``` **Другие носители того же факта:** docs/PROGRESS.md:3 (CURRENT-STATE п.3: «гейт СНЯТ холодным прогоном» — та же строка 202 в том же файле описана иначе), platform/docs/platform-PROGRESS.md:2166 («Строка 202 «книга насквозь по-настоящему» упирается теперь ровно в ключи») -**Предложенное действие:** Заменить «Цена ≈ $0.05 на десятиглавую книгу» на замеренное: холодный прогон 31.08 купил $0.436110 на 10 главах при доставленных 3 юнитах из 14 и проекции движка $1.1377; ссылка — D39.179 п.1. Колонку веса переписать: гейт «холодный прогон движка» ПАЛ 31.08, живой блокер — заполненный файл ключей для `TM_PLATFORM_ENGINE_KEYS_PATH` (obstacle P12). + +**Предложенное действие (автора находки):** Заменить «Цена ≈ $0.05 на десятиглавую книгу» на замеренное: холодный прогон 31.08 купил $0.436110 на 10 главах при доставленных 3 юнитах из 14 и проекции движка $1.1377; ссылка — D39.179 п.1. Колонку веса переписать: гейт «холодный прогон движка» ПАЛ 31.08, живой блокер — заполненный файл ключей для `TM_PLATFORM_ENGINE_KEYS_PATH` (obstacle P12). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно; добавить два уточнения, чтобы новая цифра не протухла так же. Первое: $0.436110 — цена НЕПОЛНОГО захода (доставлено 3 юнита из 14), поэтому в строку надо писать не её одну, а связку «куплено $0.436110 при собственной проекции движка `projected_book_usd = 1.1377277707` и полном оттоке кассы ≈$1.44 по §4 отчёта» — иначе платформа снова возьмёт в смету число меньше правды. Второе: указатель на ключи ставить греп-указателем (`TM_PLATFORM_ENGINE_KEYS_PATH` в `platform/internal/config/config.go`), без номера строки — файл в живой зоне платформы. + +**Разбор опровергателя:** Опровергнуть не вышло по обеим половинам. (1) D39.179 п.1 — ЖИВАЯ ратифицированная нота, и она снимает «≈$0.05» ИМЕННО как протухшую цифру, с механизмом (пере-пин 15.08, множитель 4.47). Строка бэклога спорит с ратифицированным — класс D по норме приоритета «журнал решений бьёт всё». (2) Гейт «холодный прогон движка» действительно ПАЛ: `git log` держит `244f808` (лендинг отчёта холодного прогона), а сумма по БД прогона — $0.436110 на 50 чекпойнтах, то есть на порядок выше обещанных строкой $0.05. Расхождение с собственной шапкой файла тоже реально: CURRENT-STATE (`docs/PROGRESS.md:3`) пишет «гейт СНЯТ холодным прогоном», таблица на строке 182 — «скоро (гейт: холодный прогон движка)». Живой блокер подтверждён зонным журналом: ключи провайдера. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно; добавить два уточнения, чтобы новая цифра не протухла так же. Первое: $0.436110 — цена НЕПОЛНОГО захода (доставлено 3 юнита из 14), поэтому в строку надо писать не её одну, а связку «куплено $0.436110 при собственной проекции движка `projected_book_usd = 1.1377277707` и полном оттоке кассы ≈$1.44 по §4 отчёта» — иначе платформа снова возьмёт в смету число меньше правды. Второе: указатель на ключи ставить греп-указателем (`TM_PLATFORM_ENGINE_KEYS_PATH` в `platform/internal/config/config.go`), без номера строки — файл в живой зоне платформы. --- ## N020 · MAJOR · `docs/PROGRESS.md:196` · C-исполненное-как-открытое -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Не хватает ЧИТАЮЩЕГО метода поверх готовой константы +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Не хватает ЧИТАЮЩЕГО метода поверх готовой константы + **Почему протухло:** Строка 232 ЗАКРЫТА лендингом 31.08 (`bb541a8`, экземпляр A11). Читающий метод `store.AnnouncedOnceKeys()` построен, его докстринг называет строку 232 по номеру и воспроизводит её же довод («invites its buyer to purchase it a second time»), а ось доставки в `volume.go:490,539` теперь выводится из анонс-леджера, а не из полноты строк — то есть исполнена и развилка, которую строка требовала назвать. -**Улика — команда:** `grep -rn 'AnnouncedOnceKeys' backend --include=*.go; sed -n '39,42p' backend/internal/store/outbox.go` -**Улика — вывод:** backend/internal/pipeline/volume.go:490:// announce-once ledger (store.AnnouncedOnceKeys) rather than re-derived from rows. + +**Улика — команда:** +``` +grep -rn 'AnnouncedOnceKeys' backend --include=*.go; sed -n '39,42p' backend/internal/store/outbox.go +``` +**Улика — вывод:** +``` +backend/internal/pipeline/volume.go:490:// announce-once ledger (store.AnnouncedOnceKeys) rather than re-derived from rows. backend/internal/pipeline/volume.go:539: announced, err := r.Store.AnnouncedOnceKeys() backend/internal/store/outbox.go:51:func (s *Store) AnnouncedOnceKeys() (map[string]bool, error) { --- // A projection that asks the first question calls an already-delivered book "never delivered" the day a // stage is added to the config, and invites its buyer to purchase it a second time (unified backlog row // 232). +``` **Другие носители того же факта:** docs/PROGRESS.md:305 («A11 (строка 232) — `store.AnnouncedOnceKeys()` + ось доставки по анонс-леджеру. Две посадки.»), backend/docs/MONEY_HONESTY_REPORT.md:769 («A11: `AnnouncedOnceKeys()` — читающий метод строки 232») -**Предложенное действие:** Закрыть строку 232 нотой лендинга (A11) и убрать из таблицы. ⚠ Якорь `backend/internal/store/outbox.go:31` внутри строки ПРОВЕРЕН и остался верным (`const onceKeyLookup`) — при закрытии не «чинить». + +**Предложенное действие (автора находки):** Закрыть строку 232 нотой лендинга (A11) и убрать из таблицы. ⚠ Якорь `backend/internal/store/outbox.go:31` внутри строки ПРОВЕРЕН и остался верным (`const onceKeyLookup`) — при закрытии не «чинить». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +НЕ закрывать и НЕ убирать строку 232. СУЗИТЬ её: снять первую половину (ложное «NEVER delivered» после добавления стадии) как исполненную A11 — `store.AnnouncedOnceKeys()` + предикат `delivered[key] && unitShipped(rows)`; оставить живой ВТОРУЮ половину — юнит, прерванный между волнами, повторно тратит слот гранта — со ссылкой на `backend/docs/MONEY_HONESTY_REPORT.md` §4.5, где эта граница названа автором лекарства явно. Якорь `backend/internal/store/outbox.go:31` (`const onceKeyLookup`) не трогать. + +**Разбор опровергателя:** ЦИТАТА протухла — это подтверждаю: `store.AnnouncedOnceKeys()` построен (`outbox.go:51`), ось доставки в `volume.go:490,539` выведена из анонс-леджера, отчёт подписывает A11 «предъявлено», 4 посадки. НО предложенное ДЕЙСТВИЕ находки неверно, и это ловушка, в которую находка попала: сам пак §4.5 ЯВНО объявляет, что строка 232 несёт ДВЕ половины и вторую он НЕ закрывает. Убрать строку из таблицы — значит потерять живой открытый долг (учёт слота гранта у юнита, прерванного между волнами; замер строки — 4 купленных юнита на 2 главы), который автор лекарства сам назвал незакрытым. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** НЕ закрывать и НЕ убирать строку 232. СУЗИТЬ её: снять первую половину (ложное «NEVER delivered» после добавления стадии) как исполненную A11 — `store.AnnouncedOnceKeys()` + предикат `delivered[key] && unitShipped(rows)`; оставить живой ВТОРУЮ половину — юнит, прерванный между волнами, повторно тратит слот гранта — со ссылкой на `backend/docs/MONEY_HONESTY_REPORT.md` §4.5, где эта граница названа автором лекарства явно. Якорь `backend/internal/store/outbox.go:31` (`const onceKeyLookup`) не трогать. --- ## N021 · MAJOR · `docs/PROGRESS.md:202` · C-исполненное-как-открытое -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** скоро (с §3.7 пака P12) | снять гард вместе с обходом либо доказать, что память v16 делает его недостижимым — с живой пробой на стенде +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> скоро (с §3.7 пака P12) | снять гард вместе с обходом либо доказать, что память v16 делает его недостижимым — с живой пробой на стенде + **Почему протухло:** Строка 240 исполнена лендингом P12 31.08 (`0a680a3`, D39.180) — обеими ветвями своей же диспозиции. Гард `LiftBankStop` СНЯТ (в `platform/` осталось два комментария, оба явно говорят «removed with the workaround»), колонка `bank_released` снесена миграцией 00029, а защита переехала на уровень выше: ветка `awaiting_bank` в `reconcile.go` теперь закрывает прогон, а не рестартует сквозь оплаченный стоп, и комментарий там ЯВНО называет строку 240. Движковая половина улики — отчёт холодного прогона §7 (`DELETE`/`DROP`/`TRUNCATE` у `bank_stop_presented` нет нигде; память пережила границу процесса). -**Улика — команда:** `grep -rn 'LiftBankStop' platform --include=*.go; sed -n '605,619p' platform/internal/runs/reconcile.go; ls platform/internal/pgstore/migrations/ | grep bank_released` -**Улика — вывод:** platform/internal/runs/control_test.go:652:// (`LiftBankStop`), and that guard was written against an engine that re-halted on everything still + +**Улика — команда:** +``` +grep -rn 'LiftBankStop' platform --include=*.go; sed -n '605,619p' platform/internal/runs/reconcile.go; ls platform/internal/pgstore/migrations/ | grep bank_released +``` +**Улика — вывод:** +``` +platform/internal/runs/control_test.go:652:// (`LiftBankStop`), and that guard was written against an engine that re-halted on everything still platform/internal/runs/reconcile.go:615:// (`LiftBankStop`, removed with the workaround) only decided which BIT the restart wrote, and --- if l.Status == "awaiting_bank" { @@ -410,21 +797,42 @@ platform/internal/runs/reconcile.go:615:// (`LiftBankStop`, removed with the wor // ⚠ This is what protects the paid stop now; the platform's old guard did not. --- 00029_drop_bank_released.sql -**Положительный контроль:** grep -rn 'BankStop' platform --include=*.go | head -4 → platform/internal/runs/reconcile.go:645 (TypeBankStop), :927 (ingest.OutcomeBankStop), control_test.go:436 TestResumeLiftsABankStop… — инструмент в этой области символы находит, значит пустота именно у `LiftBankStop`. +``` +**Положительный контроль:** +``` +grep -rn 'BankStop' platform --include=*.go | head -4 → platform/internal/runs/reconcile.go:645 (TypeBankStop), :927 (ingest.OutcomeBankStop), control_test.go:436 TestResumeLiftsABankStop… — инструмент в этой области символы находит, значит пустота именно у `LiftBankStop`. +``` **Другие носители того же факта:** platform/docs/platform-PROGRESS.md:283 («Пинившийся гард (`LiftBankStop`) с памятью v16 не защищал НИЧЕГО; защита переехала на уровень выше»), platform/docs/platform-PROGRESS.md:2009 (строка Д3 регистра с якорем `reconcile.go:1122-1126`), platform/internal/runs/reconcile.go:615 (код называет строку 240 по номеру) -**Предложенное действие:** Закрыть строку 240 при лендинге/приёмке P12 нотой, назвав ОБЕ половины улики (снятый гард + перенос защиты на решение о рестарте, и §7 отчёта холодного прогона по движковой половине); ⚠ честно записать, что «живая проба на стенде» из диспозиции сделана на `local`-заглушке, а не на облачном провайдере (obstacle P12). + +**Предложенное действие (автора находки):** Закрыть строку 240 при лендинге/приёмке P12 нотой, назвав ОБЕ половины улики (снятый гард + перенос защиты на решение о рестарте, и §7 отчёта холодного прогона по движковой половине); ⚠ честно записать, что «живая проба на стенде» из диспозиции сделана на `local`-заглушке, а не на облачном провайдере (obstacle P12). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие в целом верно, но его ФОРМУЛИРОВКА протухла на сутки: закрывать «при лендинге/приёмке P12» нечего — P12 ПРИНЯТ И ЗАЛЕНДЖЕН 31.08 (`0a680a3`, ратификация D39.180), так что закрытие делается СЕЙЧАС нотой со ссылкой на D39.180, а не в будущем лендинге. Тем же касанием обязательно чинить второй носитель, который находка назвала, но не пометила протухшим: в `platform/docs/platform-PROGRESS.md:2009` ряд Д3 регистра держит якорь `reconcile.go:1122-1126` на снятый гард — я прочёл эти строки, там `ReleaseUnspawned`/`MarkSettled` (файл 1621 строка), то есть указатель МЁРТВ и по норме D39.179 п.4 в живом файле зоны обязан быть греп-указателем без номера. Оговорку находки про пробу принимаю и подтверждаю: obstacle пака говорит «Живой пробой на ОБЛАЧНОМ провайдере не удался … Цепь пробита на `local`-заглушке». + +**Разбор опровергателя:** Проверял именно на пустой положительный контроль — он НЕ пустой: греп по `BankStop` в `platform/` даёт шесть живых символов и три теста, значит отсутствие объявления `LiftBankStop` — факт, а не опечатка. Гард снят, колонка `bank_released` снесена миграцией 00029, защита переехала в ветку `awaiting_bank` реконсилера, которая закрывает прогон вместо рестарта и называет строку 240 по номеру в двух местах (`:612`, `:971`). Зонный журнал подписывает §3.7/строку 240 как «да». Лендинг ратифицирован D39.180. Строка в таблице по-прежнему «скоро». + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие в целом верно, но его ФОРМУЛИРОВКА протухла на сутки: закрывать «при лендинге/приёмке P12» нечего — P12 ПРИНЯТ И ЗАЛЕНДЖЕН 31.08 (`0a680a3`, ратификация D39.180), так что закрытие делается СЕЙЧАС нотой со ссылкой на D39.180, а не в будущем лендинге. Тем же касанием обязательно чинить второй носитель, который находка назвала, но не пометила протухшим: в `platform/docs/platform-PROGRESS.md:2009` ряд Д3 регистра держит якорь `reconcile.go:1122-1126` на снятый гард — я прочёл эти строки, там `ReleaseUnspawned`/`MarkSettled` (файл 1621 строка), то есть указатель МЁРТВ и по норме D39.179 п.4 в живом файле зоны обязан быть греп-указателем без номера. Оговорку находки про пробу принимаю и подтверждаю: obstacle пака говорит «Живой пробой на ОБЛАЧНОМ провайдере не удался … Цепь пробита на `local`-заглушке». --- ## N022 · MAJOR · `docs/README.md:13` · A-дрейф -**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** зонная копия `frontend/docs/api-contract/` — байт-зеркало, сверять при лендинге +**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> зонная копия `frontend/docs/api-contract/` — байт-зеркало, сверять при лендинге + **Почему протухло:** Канон контракта уехал до 0.9.0 (заленджен 31.08, D39.180), а зонная копия стоит на 0.2.3 от 15.08 — 2999 строк против 1444. «Байт-зеркало» сегодня ЛОЖНО: копии разошлись шестью минорами (0.3.0…0.9.0). Верно: копия ОТСТАЛА и зеркалом не является; дисциплина «сверять при лендинге» не исполнялась ни разу с заморозки фронта. -**Улика — команда:** `cmp docs/architecture/14-api-contract/openapi.yaml frontend/docs/api-contract/openapi.yaml; grep -n 'version:' <оба> | head -1; wc -l <оба>; md5sum <оба>` -**Улика — вывод:** docs/architecture/14-api-contract/openapi.yaml frontend/docs/api-contract/openapi.yaml differ: byte 61, line 5 + +**Улика — команда:** +``` +cmp docs/architecture/14-api-contract/openapi.yaml frontend/docs/api-contract/openapi.yaml; grep -n 'version:' <оба> | head -1; wc -l <оба>; md5sum <оба> +``` +**Улика — вывод:** +``` +docs/architecture/14-api-contract/openapi.yaml frontend/docs/api-contract/openapi.yaml differ: byte 61, line 5 cmp exit=1 канон 5: version: 0.9.0 зеркало 5: version: 0.2.3 @@ -432,19 +840,32 @@ cmp exit=1 1444 frontend/docs/api-contract/openapi.yaml 34bb4b6d0496f4377633597cd12bc73a docs/architecture/14-api-contract/openapi.yaml 4e4ab5e6d313bd56d395032a718ab18a frontend/docs/api-contract/openapi.yaml -**Другие носители того же факта:** docs/architecture/14-api-contract/openapi.yaml:9, docs/architecture/14-api-contract/README.md:73, docs/glossary.md:31, docs/ORCHESTRATOR_SESSION_PROMPT.md:173, docs/research/28-contract-review.md:1589, frontend/README.md:66, frontend/docs/API_CONTRACT_DRAFT.md:16, frontend/docs/frontend-PROGRESS.md:48 -**Предложенное действие:** Заменить «— байт-зеркало, сверять при лендинге» на факт: «зонная копия `frontend/docs/api-contract/` ОТСТАЛА на 0.2.3 (канон 0.9.0) — зона заморожена, синхронизация при разморозке; читать ТОЛЬКО канон». Завести строку единого бэклога «зонное зеркало контракта отстало на шесть миноров». Правку самого зеркала не делать — это чужая (замороженная) зона. +``` +**Другие носители того же факта:** docs/architecture/14-api-contract/openapi.yaml:9, docs/architecture/14-api-contract/README.md:73, docs/glossary.md:31, docs/ORCHESTRATOR_SESSION_PROMPT.md:173, docs/research/28-contract-review.md:1589, frontend/README.md:66, frontend/docs/API_CONTRACT_DRAFT.md:16, frontend/docs/frontend-PROGRESS.md:48, frontend/docs/api-contract/openapi.yaml:10 + +**Предложенное действие (автора находки):** Заменить «— байт-зеркало, сверять при лендинге» на факт: «зонная копия `frontend/docs/api-contract/` ОТСТАЛА на 0.2.3 (канон 0.9.0) — зона заморожена, синхронизация при разморозке; читать ТОЛЬКО канон». Завести строку единого бэклога «зонное зеркало контракта отстало на шесть миноров». Правку самого зеркала не делать — это чужая (замороженная) зона. + **Разрушающее:** False --- ## N023 · MAJOR · `docs/architecture/03-implementation-notes.md:64` · A-дрейф -**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** `request_hash = sha256(book_id | chapter | chunk_index | stage | role | model+sampling | snapshot_id | sha256(rendered_messages))`. +**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> `request_hash = sha256(book_id | chapter | chunk_index | stage | role | model+sampling | snapshot_id | sha256(rendered_messages))`. + **Почему протухло:** Шапка строки 3 объявляет §3-контракты «живым референсом бэкенда», а формула §3.1 не совпадает с кодом ни одним полем-разделителем: реальный RequestHash фолдит версионный тег `tm-request-v2`, ДОБАВЛЯЕТ `attempt`, разворачивает «model+sampling» в пять отдельных полей (model · temperature · reasoning · json_only · max_tokens) и фолдит сообщения НАПРЯМУЮ с length-prefix и явным счётчиком, а НЕ как вложенный `sha256(rendered_messages)`. Сессия, реализовавшая ключ по букве §3.1, получила бы чужие чекпоинты (attempt отсутствует ⇒ ре-генерация садится на только что провалившийся чекпоинт — ровно тот дефект, который код в комментарии называет). -**Улика — команда:** `sed -n '292,316p' /home/ubuntu/projects/textmachine-main/backend/internal/pipeline/render.go` -**Улика — вывод:** func RequestHash(req Request) string { + +**Улика — команда:** +``` +sed -n '292,316p' /home/ubuntu/projects/textmachine-main/backend/internal/pipeline/render.go +``` +**Улика — вывод:** +``` +func RequestHash(req Request) string { h := sha256.New() // Each field is length-prefixed (8 bytes LE) instead of a NUL separator ... Version v2 pins the hash-format change. var lb [8]byte @@ -456,34 +877,60 @@ cmp exit=1 // Explicit message count: the list length is also part of the key. binary.LittleEndian.PutUint64(lb[:], uint64(len(req.Messages))) h.Write(lb[:]) - for _, m := range req.Me -**Предложенное действие:** Сноской по образцу уже стоящей в §3.4 «ПОПРАВКА»: «⚠ Формула устарела — действующий ключ см. `backend/internal/pipeline/render.go:292` (`RequestHash`): версионный тег `tm-request-v2`, поля length-prefixed, `attempt` В КЛЮЧЕ, sampling развёрнут (temperature·reasoning·json_only·max_tokens), сообщения фолдятся напрямую со счётчиком, а не вложенным sha256. Изменение зафиксировано волнами §6 (`attempt` в ключе и схеме, length-prefix + счётчик сообщений), но §3.1 не был обновлён». + for _, m := range req.Messages { w(m.Role, m.Content, strconv.FormatBool(m.CacheBoundary)) } +``` + +**Предложенное действие (автора находки):** Сноской по образцу уже стоящей в §3.4 «ПОПРАВКА»: «⚠ Формула устарела — действующий ключ см. `backend/internal/pipeline/render.go:292` (`RequestHash`): версионный тег `tm-request-v2`, поля length-prefixed, `attempt` В КЛЮЧЕ, sampling развёрнут (temperature·reasoning·json_only·max_tokens), сообщения фолдятся напрямую со счётчиком, а не вложенным sha256. Изменение зафиксировано волнами §6 (`attempt` в ключе и схеме, length-prefix + счётчик сообщений), но §3.1 не был обновлён». + **Разрушающее:** False --- ## N024 · MAJOR · `docs/architecture/03-implementation-notes.md:262` · D-противоречие-ратифицированному -**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **D1** — редактор C1 видит исходник (`{{text}}` в editor.md); для Фазы 1+ сделать редактора монолингвальным (только `{{draft}}`), верность → coverage-гейт + билингвальный судья ... но `{{text}}` убрать, когда они приедут. +**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **D1** — редактор C1 видит исходник (`{{text}}` в editor.md); для Фазы 1+ сделать редактора монолингвальным (только `{{draft}}`), верность → coverage-гейт + билингвальный судья ... но `{{text}}` убрать, когда они приедут. + **Почему протухло:** D30 п.1 ратифицировал ПРЯМО ОБРАТНОЕ и с явным supersede: «Флип D1: редактор — БИЛИНГВАЛЬНЫЙ (черновик + исходник), новый дефолт». Реестр решений помечает саму формулировку доку («{{text}} убрать из editor.md») как SUPERSEDED → D17 → D30.1. Док не знает об этом и продолжает предписывать имплементеру Фазы 1+ снять `{{text}}` — исполнение этой строки развернуло бы ратифицированный контракт и двинуло prompt_version → resnapshot. Боевой конфиг сегодня явно ссылается на D30.1 как на основание билингва. -**Улика — команда:** `grep -n '| D1 |' docs/architecture/05-decisions-index.md; sed -n '115,115p' docs/architecture/05-decisions-log.md | cut -c1-260; grep -n '{{text}}' backend/prompts/zh-ru/editor.md; grep -n 'D30.1' backend/configs/pipeline-c1.yaml` -**Улика — вывод:** docs/architecture/05-decisions-index.md:17:| D1 | | Редактор C1 — монолингвальный: видит только черновик + approved-глоссарий, без исходника; {{text}} убрать из editor.md, поднять edit prompt_version | SUPERSEDED → D17 → D30.1 (редактор БИЛИНГВ) | слайс D1-D38 | модели промпт | + +**Улика — команда:** +``` +grep -n '| D1 |' docs/architecture/05-decisions-index.md; sed -n '115,115p' docs/architecture/05-decisions-log.md | cut -c1-260; grep -n '{{text}}' backend/prompts/zh-ru/editor.md; grep -n 'D30.1' backend/configs/pipeline-c1.yaml +``` +**Улика — вывод:** +``` +docs/architecture/05-decisions-index.md:17:| D1 | | Редактор C1 — монолингвальный: видит только черновик + approved-глоссарий, без исходника; {{text}} убрать из editor.md, поднять edit prompt_version | SUPERSEDED → D17 → D30.1 (редактор БИЛИНГВ) | слайс D1-D38 | модели промпт | 05-decisions-log.md:115: 1. **Флип D1: редактор — БИЛИНГВАЛЬНЫЙ (черновик + исходник), новый дефолт. Ратифицирован с явным supersede D17.в** backend/prompts/zh-ru/editor.md:37:{{text}} backend/configs/pipeline-c1.yaml:96: # против D30.1 — редактор БИЛИНГВ и видит плотный CJK-исходник (prompts/zh-ru/editor.md, {{text}}). -**Предложенное действие:** Сноской сразу за пунктом D1: «⚠ ОПРОВЕРГНУТО D30 п.1 (12.07): редактор БИЛИНГВАЛЬНЫЙ — новый дефолт, с явным supersede D17.в; реестр помечает D1 как SUPERSEDED → D17 → D30.1. `{{text}}` из `prompts/zh-ru/editor.md` НЕ снимать. Моно-вариант живёт как `editor-mono.md` — дормант-референс (D33.4a), не план». Текст пункта не сносить — он объясняет, откуда взялась нота D1. +``` + +**Предложенное действие (автора находки):** Сноской сразу за пунктом D1: «⚠ ОПРОВЕРГНУТО D30 п.1 (12.07): редактор БИЛИНГВАЛЬНЫЙ — новый дефолт, с явным supersede D17.в; реестр помечает D1 как SUPERSEDED → D17 → D30.1. `{{text}}` из `prompts/zh-ru/editor.md` НЕ снимать. Моно-вариант живёт как `editor-mono.md` — дормант-референс (D33.4a), не план». Текст пункта не сносить — он объясняет, откуда взялась нота D1. + **Разрушающее:** False --- ## N025 · MAJOR · `docs/architecture/06-memory-risk-registry.md:81` · B-мёртвый-указатель -**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** `snapshotID` (`snapshot.go:82 (до пакета №4 — runner.go:145-163)`) хэширует brief/chunker/план-стадий/сэмплинг ... `RequestHash` (`render.go:158`) ... `render.go:23-29` ... инъектит память в `msgs` (`render.go:133-136`) ... resnapshot-гейт (`bookrun.go:108/stagerun.go:77`) +**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> `snapshotID` (`snapshot.go:82 (до пакета №4 — runner.go:145-163)`) хэширует brief/chunker/план-стадий/сэмплинг ... `RequestHash` (`render.go:158`) ... `render.go:23-29` ... инъектит память в `msgs` (`render.go:133-136`) ... resnapshot-гейт (`bookrun.go:108/stagerun.go:77`) + **Почему протухло:** Колонка «Где в коде/схеме» реестра указывает шесть file:line-якорей, и пять из шести уехали: реальные адреса — MessagesWithInjection render.go:246 (а не render.go:133-136, где сегодня разбор разделителя промпта), RequestHash render.go:292 (а не :158 = fewShotEnabled), snapshotID() snapshot.go:209 (а не :82), resnapshot-гейт целиком в stagerun.go:52-65 (в bookrun.go его НЕТ вовсе, :108 — пустая строка перед CompletedWithFlags), memoryVersion() snapshot.go:190 (а не :175). Плюс имя `render.Messages` больше не место инъекции: Messages() на render.go:229 делегирует в MessagesWithInjection с ПУСТОЙ инъекцией. -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main/backend && for a in render.go:133 render.go:158 snapshot.go:82 bookrun.go:108 stagerun.go:77 snapshot.go:175; do f=${a%%:*}; n=${a##*:}; printf '%-16s -> ' "$a"; sed -n "${n}p" internal/pipeline/$f | cut -c1-95; done; grep -n '^func MessagesWithInjection\|^func RequestHash\|^func (r \*Runner) snapshotID\|^func (r \*Runner) memoryVersion' internal/pipeline/render.go internal/pipeline/snapshot.go; grep -n 'was started under snapshot' internal/pipeline/stagerun.go` -**Улика — вывод:** render.go:133 -> parts := strings.SplitN(canon, userSeparator, 2) + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main/backend && for a in render.go:133 render.go:158 snapshot.go:82 bookrun.go:108 stagerun.go:77 snapshot.go:175; do f=${a%%:*}; n=${a##*:}; printf '%-16s -> ' "$a"; sed -n "${n}p" internal/pipeline/$f | cut -c1-95; done; grep -n '^func MessagesWithInjection\|^func RequestHash\|^func (r \*Runner) snapshotID\|^func (r \*Runner) memoryVersion' internal/pipeline/render.go internal/pipeline/snapshot.go; grep -n 'was started under snapshot' internal/pipeline/stagerun.go +``` +**Улика — вывод:** +``` +render.go:133 -> parts := strings.SplitN(canon, userSeparator, 2) render.go:158 -> // fewShotEnabled resolves a stage's few_shot toggle: absent (nil) defaults to ON, so snapshot.go:82 -> } bookrun.go:108 -> @@ -493,84 +940,159 @@ snapshot.go:175 -> s.ModelWire = wire.capability internal/pipeline/snapshot.go:190:func (r *Runner) memoryVersion() string { internal/pipeline/snapshot.go:209:func (r *Runner) snapshotID() (id, payload string, err error) { internal/pipeline/render.go:246:func MessagesWithInjection(tpl *PromptTemplate, v RenderVars, injection string) ([]llm.Message, error) { -internal/pipeline/render.go:292:func RequestHash(req Req -**Положительный контроль:** Тот же sed по якорю, который НЕ уехал, попадает точно: `sed -n '22,28p' internal/pipeline/render.go` → «// render.go enforces the determinism invariant (03-implementation-notes §3.1): the rendered prompt is a PURE function of (snapshot, committed prior outputs, chunk)» — т.е. якорь «render.go:23-29» из того же дока резолвится, инструмент в этой области работает. +internal/pipeline/render.go:292:func RequestHash(req Request) string { +stagerun.go:59: return nil, fmt.Errorf("pipeline: job %s/ch%d/%s was started under snapshot %.12s ... --resnapshot ...") +``` +**Положительный контроль:** +``` +Тот же sed по якорю, который НЕ уехал, попадает точно: `sed -n '22,28p' internal/pipeline/render.go` → «// render.go enforces the determinism invariant (03-implementation-notes §3.1): the rendered prompt is a PURE function of (snapshot, committed prior outputs, chunk)» — т.е. якорь «render.go:23-29» из того же дока резолвится, инструмент в этой области работает. +``` **Другие носители того же факта:** docs/architecture/06-memory-risk-registry.md:10 (шапка 25.07 несёт тот же уехавший «snapshot.go:175»), docs/architecture/06-memory-risk-registry.md:32 (A1: «render.Messages … render.go:133-136»), docs/architecture/06-memory-risk-registry.md:37 (A6: «раскладка render.Messages»), docs/architecture/06-memory-risk-registry.md:100 («impl-notes §3.6 (строки 123-124)» — цитируемая фраза про FTS5 живёт на 03-implementation-notes.md:127) -**Предложенное действие:** Пере-нацелить якоря одной правкой: render.go:133-136 → `pipeline.MessagesWithInjection` (render.go:246); render.go:158 → render.go:292; snapshot.go:82 → snapshot.go:209; «bookrun.go:108/stagerun.go:77» → stagerun.go:52-65; snapshot.go:175 → snapshot.go:190; «impl-notes §3.6 (строки 123-124)» → 03-implementation-notes.md:127. Имя `render.Messages` в A1/A6 заменить на `render.MessagesWithInjection` (Messages() передаёт пустую инъекцию). Устойчивее — заменить номера строк на ИМЕНА функций, они пережили уже два рефакторинга. + +**Предложенное действие (автора находки):** Пере-нацелить якоря одной правкой: render.go:133-136 → `pipeline.MessagesWithInjection` (render.go:246); render.go:158 → render.go:292; snapshot.go:82 → snapshot.go:209; «bookrun.go:108/stagerun.go:77» → stagerun.go:52-65; snapshot.go:175 → snapshot.go:190; «impl-notes §3.6 (строки 123-124)» → 03-implementation-notes.md:127. Имя `render.Messages` в A1/A6 заменить на `render.MessagesWithInjection` (Messages() передаёт пустую инъекцию). Устойчивее — заменить номера строк на ИМЕНА функций, они пережили уже два рефакторинга. + **Разрушающее:** False --- ## N026 · MAJOR · `docs/architecture/09-target-architecture.md:12` · A-дрейф -**Срез:** Целевая архитектура — docs/architecture/09-target-architecture.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** ⚠ **Канон контракта — 0.8.0**, не 0.4.0 и не 0.7.0: пять миноров за трое суток (0.5.0 — D39.161, 0.6.0 — D39.162/163, 0.7.0 — D39.166, **0.8.0 — D39.169**, кадр `session_ended`). Носители — `14-api-contract/openapi.yaml` и `platform/internal/httpapi/capabilities.go`, они сходятся гейтом. +**Срез:** Целевая архитектура — docs/architecture/09-target-architecture.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> ⚠ **Канон контракта — 0.8.0**, не 0.4.0 и не 0.7.0: пять миноров за трое суток (0.5.0 — D39.161, 0.6.0 — D39.162/163, 0.7.0 — D39.166, **0.8.0 — D39.169**, кадр `session_ended`). Носители — `14-api-contract/openapi.yaml` и `platform/internal/httpapi/capabilities.go`, они сходятся гейтом. + **Почему протухло:** Канон стоит на 0.9.0, а не на 0.8.0: минор 0.9.0 залендён 31.08 паком платформы P12 (D39.180), и ОБА названных в этой же строке носителя показывают 0.9.0. Строка сама называет носителей и сама им противоречит — то есть читатель, послушавшийся её вместо грепа, получит ложное число. -**Улика — команда:** `grep -n '^ version:' docs/architecture/14-api-contract/openapi.yaml; grep -n 'ContractVersion = ' platform/internal/httpapi/capabilities.go; grep -n '| D39.180 |' docs/architecture/05-decisions-index.md | cut -c1-150` -**Улика — вывод:** 5: version: 0.9.0 + +**Улика — команда:** +``` +grep -n '^ version:' docs/architecture/14-api-contract/openapi.yaml; grep -n 'ContractVersion = ' platform/internal/httpapi/capabilities.go; grep -n '| D39.180 |' docs/architecture/05-decisions-index.md | cut -c1-150 +``` +**Улика — вывод:** +``` +5: version: 0.9.0 13:const ContractVersion = "0.9.0" 244:| D39.180 | 31.08 | **Пак платформы P12 «долги под ногами» ПРИНЯТ И ЗАЛЕНДЖЕН** вместе с кон +``` **Другие носители того же факта:** docs/README.md:29, docs/product-requirements.md:56, docs/PROGRESS.md:10 -**Предложенное действие:** Заменить «Канон контракта — 0.8.0» на «Канон контракта — 0.9.0 (D39.180, лендинг пака P12 31.08)» и дописать 0.9.0 в перечень миноров. Те же три носителя (README §активные промты, product-requirements ПТ-20, CURRENT-STATE в PROGRESS) правятся тем же заходом — иначе класс E воспроизведётся: product-requirements сам предупреждает «версия здесь живёт хуже всего». + +**Предложенное действие (автора находки):** Заменить «Канон контракта — 0.8.0» на «Канон контракта — 0.9.0 (D39.180, лендинг пака P12 31.08)» и дописать 0.9.0 в перечень миноров. Те же три носителя (README §активные промты, product-requirements ПТ-20, CURRENT-STATE в PROGRESS) правятся тем же заходом — иначе класс E воспроизведётся: product-requirements сам предупреждает «версия здесь живёт хуже всего». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие автора верно по сути, но перечень носителей уточняю: docs/README.md:29 НЕ ложна — это корректное историческое утверждение о D39.169, а README:26 (строка «Платформа») уже несёт 0.9.0; ей нужно дописать 0.9.0 в ряд «Контракт», а не переписывать. Обязательны к правке ТРИ носителя: 09-target-architecture.md:12, product-requirements.md:56 (её же собственный «урок ячейки» требует править версию первой), PROGRESS.md:10. + +**Разбор опровергателя:** Воспроизвёл своей командой, попыток опровергнуть не нашёл. ОБА носителя, названные самой строкой, показывают 0.9.0; D39.180 (31.08) ратифицирует и лендит минор 0.9.0 вместе с паком P12. Blame: строка 12 написана 30.08 живой правкой шапки (e1e48ac), то есть это НЕ 13.07-текст под общим баннером «тело ниже = замысел 13.07» (он стоит в конце строки 14 и покрывает только тело НИЖЕ шапки) — это прескриптивное утверждение сегодняшнего дня, и оно ложно. Класс E тоже подтверждён: product-requirements.md:56 пишет «он придёт с лендингом пака P12, до него канон 0.8.0», PROGRESS.md:10 — «канон на 0.8.0 и уйдёт на 0.9.0 с лендингом P12»; P12 залендён 31.08, обе копии протухли тем же событием. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно; предпочтительна названная автором «лучшая» ветка — внести объёмный потолок, гейт схемы и закон раскрытия В САМУ таблицу слоёв и снять оговорку целиком, иначе числительное будет протухать при каждом следующем лендинге. Если правится только текст — не заменять «двух» на «трёх» (то же протухнет), а писать «ряда» + ссылку на реестр нот. --- ## N027 · MAJOR · `docs/architecture/10-prompt-architecture.md:51` · C-исполненное-как-открытое -**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** 4. **Билингв-редактору src→dst вместо голых dst-форм?** — открытый дизайн-вопрос D30.1, после exp15. +**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> 4. **Билингв-редактору src→dst вместо голых dst-форм?** — открытый дизайн-вопрос D30.1, после exp15. + **Почему протухло:** Вопрос закрыт и ПОСТРОЕН: WS2 пака-11 отрендерил редакторский констрейнт-блок именно как src→dst-мэппинг, и D39.13 ратифицировал это дословно как «закрытие D30.1» (19.07 — через три дня после рождения дока). В коде это несущая константа снапшота RenderFormatVersion = renderfmt-v3-draft-gender+editor-src2dst+dc3-gender, а доккоммент RenderEditorConstraintBlock прямо говорит «WS2 §2в resolves the D30.1-open question». -**Улика — команда:** `grep -n 'RenderFormatVersion = ' backend/internal/membank/memory.go; sed -n '745,747p' backend/internal/membank/memory.go; grep -n '^| D39.13 ' docs/architecture/05-decisions-index.md; sed -n '212p' docs/archive/architecture/05-decisions-D39-arch-reset.md | grep -o 'src→dst констрейнт-блок редактора.\{0,30\}'` -**Улика — вывод:** memory.go:738: const RenderFormatVersion = "renderfmt-v3-draft-gender+editor-src2dst+dc3-gender" + +**Улика — команда:** +``` +grep -n 'RenderFormatVersion = ' backend/internal/membank/memory.go; sed -n '745,747p' backend/internal/membank/memory.go; grep -n '^| D39.13 ' docs/architecture/05-decisions-index.md; sed -n '212p' docs/archive/architecture/05-decisions-D39-arch-reset.md | grep -o 'src→dst констрейнт-блок редактора.\{0,30\}' +``` +**Улика — вывод:** +``` +memory.go:738: const RenderFormatVersion = "renderfmt-v3-draft-gender+editor-src2dst+dc3-gender" memory.go:745-747: // RenderEditorConstraintBlock serializes the selected records' CONFIRMED renderings into the // editor's injection as a src→dst MAPPING (WS2 §2в resolves the D30.1-open question): "源термин → // «dst»", like the translator block, NOT bare canonical Russian. 05-decisions-index.md:75: | D39.13 | 19.07 | Приёмка пака-11 Block A: WS2 целиком (бюджет в выходных токенах, edit-единицы, src→dst-блок редактора = закрытие D30.1) + WS1a/b... 05-decisions-D39-arch-reset.md:212: **src→dst констрейнт-блок редактора** [закрытие D30.1] +``` **Другие носители того же факта:** docs/architecture/09-target-architecture.md:144-145 — «Убрать устаревшую D1-моно-рационализацию из renderEditorConstraintBlock (редактор теперь БИЛИНГВ, D30.1); открытый вопрос — давать ли билингв-редактору src→dst-мэппинг, а не голые dst-формы» (живой арх-док, тот же протухший открытый вопрос; заодно имя функции сегодня экспортное — RenderEditorConstraintBlock), docs/architecture/09-target-architecture.md:77 — «инъекция per-role (переводчику src→dst, редактору CONFIRMED-dst-констрейнты)»: половина верна (CONFIRMED-only), половина протухла — редактору сегодня едут src→dst-пары, а не голые dst-формы, docs/research/20-bank-mining.md:780 — «Открытый вопрос D30.1-редактора (src→dst вместо голых dst-форм, memory.go:505-511)»: тело ресёрча, по D23.3 не переписывается, но указатель memory.go:505-511 туда больше не ведёт -**Предложенное действие:** Заменить п.4 на закрытый: «**Билингв-редактору src→dst вместо голых dst-форм — ЗАКРЫТО** (D39.13, пак-11 WS2): констрейнт-блок редактора рендерится как src→dst-мэппинг, CONFIRMED-only; форма фолдится в снапшот через `RenderFormatVersion` (`memory.go:738`). Довод — дизамбигуация омонимичного dst.» Тем же касанием оркестратору: 09-target-architecture.md:144-145 и :77 несут ту же протухшую формулировку. + +**Предложенное действие (автора находки):** Заменить п.4 на закрытый: «**Билингв-редактору src→dst вместо голых dst-форм — ЗАКРЫТО** (D39.13, пак-11 WS2): констрейнт-блок редактора рендерится как src→dst-мэппинг, CONFIRMED-only; форма фолдится в снапшот через `RenderFormatVersion` (`memory.go:738`). Довод — дизамбигуация омонимичного dst.» Тем же касанием оркестратору: 09-target-architecture.md:144-145 и :77 несут ту же протухшую формулировку. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верное по существу, две поправки. (1) Не вешать в правку сырой номер строки `memory.go:738` — именно такие якоря и гниют дрейфом (в этом же доке §2 уже несёт ⚠ «пути таблицы устарели механически»); назвать символ: константа `RenderFormatVersion` в `internal/membank/memory.go`, проводка — `internal/pipeline/chunkrun.go:102`. (2) В 09-target-architecture.md:144-145 протух НЕ только хвост про открытый вопрос: первая половина буллета («убрать устаревшую D1-моно-рационализацию») тоже ИСПОЛНЕНА — доккоммент сегодня читается «the editor is BILINGUAL (editor.md feeds it the source)», — а имя функции в буллете строчное (`renderEditorConstraintBlock`), тогда как в коде символ экспортный `RenderEditorConstraintBlock` (`memory.go:769`; строчного варианта в backend/*.go нет вовсе — он остался только в backend/docs/D15.2-спеке и в телах экспериментов). То есть буллет сносится целиком, а не правится хвостом. + +**Разбор опровергателя:** Опровергнуть не удалось — находка воспроизводится тремя независимыми носителями. (1) Ратификация: D39.13 дословно называет src→dst-блок редактора «закрытием D30.1» — и в реестре, и в теле архивного слайса; тело прочитано целиком, реопена нет. (2) Код: форма src→dst не просто написана, а зафолжена в снапшот константой RenderFormatVersion (подстрока editor-src2dst), доккоммент прямо говорит «resolves the D30.1-open question». (3) Потребитель есть — проверил положительным контролем, которого у автора не было: RenderEditorConstraintBlock подключён боевой проводкой pipeline/chunkrun.go:102 как рендерер роли редактора, плюс четыре тест-файла пинуют формат. Вопрос, который §4 п.5 держит открытым, ратифицирован 19.07 — через три дня после рождения дока. Оба со-носителя тоже проверены и протухли: 09-target-architecture.md:144-145 и :77 существуют дословно. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верное по существу, две поправки. (1) Не вешать в правку сырой номер строки `memory.go:738` — именно такие якоря и гниют дрейфом (в этом же доке §2 уже несёт ⚠ «пути таблицы устарели механически»); назвать символ: константа `RenderFormatVersion` в `internal/membank/memory.go`, проводка — `internal/pipeline/chunkrun.go:102`. (2) В 09-target-architecture.md:144-145 протух НЕ только хвост про открытый вопрос: первая половина буллета («убрать устаревшую D1-моно-рационализацию») тоже ИСПОЛНЕНА — доккоммент сегодня читается «the editor is BILINGUAL (editor.md feeds it the source)», — а имя функции в буллете строчное (`renderEditorConstraintBlock`), тогда как в коде символ экспортный `RenderEditorConstraintBlock` (`memory.go:769`; строчного варианта в backend/*.go нет вовсе — он остался только в backend/docs/D15.2-спеке и в телах экспериментов). То есть буллет сносится целиком, а не правится хвостом. --- ## N028 · MAJOR · `docs/architecture/10-prompt-architecture.md:52` · C-исполненное-как-открытое -**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** 5. **Q4a/Q4c exp15** — где жить структуре (переводчик/редактор/отдельный пасс) — решится эмпирикой. +**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> 5. **Q4a/Q4c exp15** — где жить структуре (переводчик/редактор/отдельный пасс) — решится эмпирикой. + **Почему протухло:** Q4a ИЗМЕРЕН 18.07 и ратифицирован D39.9 («верность НЕ покупается на translate-стадии»), а топологию ролей решил эксп-21 (D39.117: full-regen, починка = пре-гейт ПЕРЕД редактором). Открытым остался только Q4c. Собственная §1 того же дока (испр. 22.08) это уже знает и говорит ровно противоположное — §4 п.5 не был поправлен тем же касанием, и читатель §4 уходит с выводом, что структура «решится эмпирикой», хотя эмпирика получена и ратифицирована. -**Улика — команда:** `grep -n '^| D39.9 \|^| D39.117 ' docs/architecture/05-decisions-index.md; sed -n '391p' docs/experiments/19-editor-contract-q4b.md; sed -n '18p;52p' docs/architecture/10-prompt-architecture.md | cut -c1-160` -**Улика — вывод:** index:71: | D39.9 | 18.07 | Q4a «сильный переводчик»: верность НЕ покупается на translate-стадии (flash уже верен, pro ×3.67 не ратифицирован); смысл-катастрофы рождает glm-РЕДАКТОР → свап-арм приоритетен | закрыт + +**Улика — команда:** +``` +grep -n '^| D39.9 \|^| D39.117 ' docs/architecture/05-decisions-index.md; sed -n '391p' docs/experiments/19-editor-contract-q4b.md; sed -n '18p;52p' docs/architecture/10-prompt-architecture.md | cut -c1-160 +``` +**Улика — вывод:** +``` +index:71: | D39.9 | 18.07 | Q4a «сильный переводчик»: верность НЕ покупается на translate-стадии (flash уже верен, pro ×3.67 не ратифицирован); смысл-катастрофы рождает glm-РЕДАКТОР → свап-арм приоритетен | закрыт index:181: | D39.117 | 08.08 | Эксп-21 принят: строка 65 закрыта; починка = пре-гейт перед редактором; опровергнут вывод D39.108 | ЖИВОЕ: диспозиции 142/65/106/55 exp19:391: Reflow-проход (Q4c) не гонялся — проза и переверстка НЕ сравнивались doc10:18: ... Q4a ИЗМЕРЕН (D39.9: верность на translate-стадии не покупается) ... Не гонялся только Q4c doc10:52: 5. **Q4a/Q4c exp15** — где жить структуре ... решится эмпирикой. -**Предложенное действие:** Заменить п.5 на: «**Q4c** (reflow отдельным пассом) — единственный не прогнанный арм exp15 (`19-editor-contract-q4b.md:391`). Q4a измерен и закрыт D39.9; топология ролей решена эксп-21 (D39.117: full-regen, починка — пре-гейтом ПЕРЕД редактором, строка 142).» +``` + +**Предложенное действие (автора находки):** Заменить п.5 на: «**Q4c** (reflow отдельным пассом) — единственный не прогнанный арм exp15 (`19-editor-contract-q4b.md:391`). Q4a измерен и закрыт D39.9; топология ролей решена эксп-21 (D39.117: full-regen, починка — пре-гейтом ПЕРЕД редактором, строка 142).» + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верное. Добавить в тот же обход одну проверку, которую автор не сделал: 09-target-architecture.md:138 несёт «⚠ Арм «переводчик отдаёт структуру» никогда не тестировался», что тем же D39.9 выглядит протухшим — но осторожно: D39.9 мерил ВЕРНОСТЬ черновика, а не структуру, так что это ЛИД для отдельной сверки, а не готовая правка. Ссылку на Q4c лучше дать якорем секции (`19-editor-contract-q4b.md` §2.10 «Границы»), а не только номером строки. + +**Разбор опровергателя:** Воспроизводится. Q4a и Q4c по research/19:730/732 — это ровно оси «структура у переводчика» и «где живёт reflow», то есть п.5 действительно про них. Q4a измерен и ратифицирован D39.9 (реестр: «закрыт»), топология ролей решена эксп-21 — тело D39.117 прочитано, п.3 дословно принимает форму «механический пре-гейт ПЕРЕД редактором» и подтверждает full-regen. Не гонялся только Q4c, и это подтверждено телом эксп-19 §2.10. Решающая улика — сам док: §1 (строка 18, испр. 22.08) уже несёт «Q4a ИЗМЕРЕН (D39.9)» и «Не гонялся только Q4c», то есть п.5 в §4 остался непоправленным тем же касанием и прямо противоречит §1 через 34 строки. Класс C верен. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верное. Добавить в тот же обход одну проверку, которую автор не сделал: 09-target-architecture.md:138 несёт «⚠ Арм «переводчик отдаёт структуру» никогда не тестировался», что тем же D39.9 выглядит протухшим — но осторожно: D39.9 мерил ВЕРНОСТЬ черновика, а не структуру, так что это ЛИД для отдельной сверки, а не готовая правка. Ссылку на Q4c лучше дать якорем секции (`19-editor-contract-q4b.md` §2.10 «Границы»), а не только номером строки. --- ## N029 · MAJOR · `docs/architecture/13-tech-debt-anchors.md:52` · E-второй-носитель -**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** F4-бэкап (83): 0 вхождений VACUUM/integrity_check/backup во всём backend/, tmctl без такой подкоманды (main.go:73-83). +**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> F4-бэкап (83): 0 вхождений VACUUM/integrity_check/backup во всём backend/, tmctl без такой подкоманды (main.go:73-83). + **Почему протухло:** Это ВТОРОЙ (и единственный не-баннированный) носитель статуса строки F4 реестра памяти, и он разошёлся с кодом: `tmctl backup` существует (cmd/tmctl/main.go:243 `case "backup"`, в списке команд main.go:184), `store.IntegrityCheck` с `PRAGMA integrity_check` — internal/store/backup.go:18,79, пре-флайт-бэкап зовётся из translate (main.go:221,236). Всё это приехало коммитом 9f60746 (D39.82) ПОСЛЕ того, как строка сводки была написана (036dc78, D39.67). Живой журнал уже знает правду: docs/PROGRESS.md:93 говорит про «F4-хвост» (т.е. механизм есть, остались доработки), а не про отсутствие. Заодно уехал и якорь: main.go:73-83 сегодня — словарь exit-кодов, а не список подкоманд. -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main && grep -rniI 'VACUUM|integrity_check|backup' -E --include='*.go' backend/ | grep -v _test | head -6; grep -n 'case "backup"' backend/cmd/tmctl/main.go; git log --oneline --diff-filter=A -1 -- backend/internal/store/backup.go; git log --oneline -1 -S 'F4-бэкап (83)' -- docs/architecture/13-tech-debt-anchors.md` -**Улика — вывод:** backend/cmd/tmctl/main.go:184: "translate", "report", "status", "export", "build", "redrive", "manifest", "backup", "migrate", "seed-lint", + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main && grep -rniI 'VACUUM|integrity_check|backup' -E --include='*.go' backend/ | grep -v _test | head -6; grep -n 'case "backup"' backend/cmd/tmctl/main.go; git log --oneline --diff-filter=A -1 -- backend/internal/store/backup.go; git log --oneline -1 -S 'F4-бэкап (83)' -- docs/architecture/13-tech-debt-anchors.md +``` +**Улика — вывод:** +``` +backend/cmd/tmctl/main.go:184: "translate", "report", "status", "export", "build", "redrive", "manifest", "backup", "migrate", "seed-lint", backend/cmd/tmctl/main.go:221: if err := preflightBackup(inv.cfgPath, os.Stdout); err != nil { backend/cmd/tmctl/main.go:243: case "backup": backend/internal/store/backup.go:18:// IntegrityCheck runs `PRAGMA integrity_check` against the SQLite file at dbPath … backend/internal/store/backup.go:79:// integrityCheck runs the pragma over db and demands the single canonical "ok" row … 243: case "backup" 9f60746 Land smallpack tails as D39.82: … tmctl backup with production preflight … -036dc78 Ratify D39.67: … debt anchors moved to living architecture referen +036dc78 Ratify D39.67: … debt anchors moved to living architecture reference +``` **Другие носители того же факта:** docs/architecture/06-memory-risk-registry.md:88 (строка F4 со статусом «🔶 нет строки бэкапа/интеграла» — покрыта ⚠-баннером 25.07 «статусы ❌/🔶 датированы 04.07», потому НЕ находка), docs/PROGRESS.md:93 (актуальный носитель: «F4-хвост» — механизм есть, остались доработки) -**Предложенное действие:** Переписать пункт сводки: «F4-бэкап: ЗАКРЫТ D39.82 (9f60746) — `tmctl backup` (main.go:243), `store.IntegrityCheck` + PRAGMA integrity_check (store/backup.go:18,79), пре-флайт в translate (main.go:221,236); остаток — строка 93 PROGRESS (dry-run обходит пре-флайт)». Якорь main.go:73-83 заменить на main.go:184 (список команд). -**Разрушающее:** True -**⚠ Опровергатель поправил действие:** Находка верна, но предложенное действие бьёт мимо в трёх местах. + +**Предложенное действие (автора находки):** Переписать пункт сводки: «F4-бэкап: ЗАКРЫТ D39.82 (9f60746) — `tmctl backup` (main.go:243), `store.IntegrityCheck` + PRAGMA integrity_check (store/backup.go:18,79), пре-флайт в translate (main.go:221,236); остаток — строка 93 PROGRESS (dry-run обходит пре-флайт)». Якорь main.go:73-83 заменить на main.go:184 (список команд). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Находка верна, но предложенное действие бьёт мимо в трёх местах. 1) НЕ переписывать сводку заново своим текстом. Строка 16 того же файла уже несёт ратифицированную формулировку закрытия («→ ИСПОЛНЕНО D39.82: `tmctl backup` + pre-flight (`backend/cmd/tmctl/backup.go`), строка 83 закрыта; остаток F4-хвоста — строка 93»). Минимальная и достаточная правка — привести сводку в соответствие с ней тем же механизмом, который объявлен баннером дока: вставить в предложение о F4 стрелку «→ ЗАКРЫТО D39.82 (9f60746), см. строку 16» и не трогать саму запись свипа. Сводка — часть протокола верификационного свипа D39.66 (author≠reviewer, evidence-документ); переписывать её вердикт задним числом — то же, что править тело ресёрча по норме D23.3. Помечать закрытие — можно и нужно, стирать улику свипа — нет. @@ -580,15 +1102,43 @@ backend/internal/store/backup.go:79:// integrityCheck runs the pragma over db an 4) Второй носитель в отчёте назвать точно: docs/architecture/06-memory-risk-registry.md:84 (не :88), статус «🔶 нет строки бэкапа», покрыт ⚠-баннером строки 11 — НЕ находка, флагать не надо. +**Разбор опровергателя:** Опровергнуть находку не вышло — она воспроизводится четырьмя независимыми уликами. + +1) Отрицательное утверждение находки я проверял С ПОЛОЖИТЕЛЬНЫМ КОНТРОЛЕМ в обе стороны: греп по backend/ находит (func main → main.go:25), греп по бэклогу находит строку 93 и не находит 83. Пустого-грепа-по-опечатке здесь нет. + +2) Факт строки 52 сегодня ЛОЖЕН по существу: `case "backup"` (main.go:243), `PRAGMA integrity_check` и `VACUUM INTO` (store/backup.go:18,31,56,82), пре-флайт в двух точках платного пути (main.go:221,236). Утверждение «0 вхождений во всём backend/» опровергается кодом. + +3) Якорь main.go:73-83 — классический дрейф, а не ошибка автора: `git show 036dc78:…` показывает, что в момент написания там действительно стоял dispatch-switch (case "translate" … case "seed-lint"), а сегодня это словарь exit-кодов. Это ровно тот паттерн «родился верным, сгнил дрейфом», о котором предупреждал заказ. + +4) Проверил главную линию защиты — «это баннированное историческое, а значит легитимно». Не проходит: баннер файла (строка 3, D39.101) сам обещает механизм «закрытия помечены стрелками «→» ПО МЕСТУ». Стрелка проставлена в заголовке среза (строка 16: «→ ИСПОЛНЕНО D39.82»), а в сводке строки 52 — нет. То есть док противоречит САМ СЕБЕ через 36 строк, и обещанный баннером маркер в этом месте отсутствует. Это не «ощущение устарелости», а невыполненный контракт собственного баннера. + +5) Проверил «не дефект ли это ратифицированного контракта»: наоборот — реестр D39.82 (05-decisions-index.md:146, тело в слайсе docs/archive/architecture/05-decisions-D39-59-90.md) прямо ратифицирует «tmctl backup + pre-flight (F4) … строки 89/53/83 закрыты», и строка 83 из бэклога PROGRESS физически снята. Ратифицированное на стороне находки. + +6) Проверил, не читал ли автор устаревшую копию: все цитаты сверены по рабочему дереву (git status по docs/architecture/13-*.md чист; изменён только docs/research/15-voice-and-state.md, к предмету отношения не имеет). + +Неточности находки, на вердикт не влияющие: (а) второй носитель F4 в реестре памяти — строка 84, а не 88 (баннированность автор определил верно, флагать её не стал — правильно); (б) формулировка «единственный не-баннированный носитель» неточна: не-баннированный носитель есть ещё и в строке 16 того же файла, но он КОРРЕКТЕН — что усиливает класс E (копии разошлись внутри одного дока), а не ослабляет. + +**Разрушающее:** True + --- ## N030 · MAJOR · `docs/architecture/13-tech-debt-anchors.md:72` · D-противоречие-ратифицированному -**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** сегодня `gender: neuter` в сиде — ТИХИЙ no-op без линта, т.е. дыра хуже «невыразимо» — при стройке носителя добавить и seed-lint-отбраковку неизвестных значений рода +**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> сегодня `gender: neuter` в сиде — ТИХИЙ no-op без линта, т.е. дыра хуже «невыразимо» — при стройке носителя добавить и seed-lint-отбраковку неизвестных значений рода + **Почему протухло:** Обе половины ложны и противоречат ратифицированной D39.69 §3 («строки 27/84 ЗАКРЫТЫ … neuter end-to-end (enum · gender_neuter в injection-данных · SeedLint громко отбраковывает неизвестный род)»). В коде: `genderConstraintNote` отдаёт `tx.GenderNeuter` на `case "neuter", "n"` — значит это НЕ no-op; `GenderVocabViolations` вызывается из сид-линта (`memseed.go:533`) — значит линт ЕСТЬ, и его собственный докстринг говорит о neuter в прошедшем времени: «the row-84 class where gender:neuter (now valid) was an unnoticed no-op». Строка 84 в таблице бэклога отсутствует (закрыта), а тело пункта выше (пере-сверка 23.08) уже это признаёт — не обновлён только абзац «Фикс/скоуп», то есть предписание к действию. -**Улика — команда:** `sed -n '846,850p' backend/internal/membank/memory.go; sed -n '531,534p;540,552p' backend/internal/membank/memseed.go; grep -nE '^\| 84 \|' docs/PROGRESS.md; grep -nE '^\| (85|86) \|' docs/PROGRESS.md | cut -c1-40` -**Улика — вывод:** case "neuter", "n": + +**Улика — команда:** +``` +sed -n '846,850p' backend/internal/membank/memory.go; sed -n '531,534p;540,552p' backend/internal/membank/memseed.go; grep -nE '^\| 84 \|' docs/PROGRESS.md; grep -nE '^\| (85|86) \|' docs/PROGRESS.md | cut -c1-40 +``` +**Улика — вывод:** +``` + case "neuter", "n": return tx.GenderNeuter --- if bad := GenderVocabViolations(entries); len(bad) > 0 { @@ -602,20 +1152,38 @@ var knownGenders = map[string]bool{ (grep '^| 84 |' — пусто: строки 84 в бэклоге НЕТ) 78:| 86 | **Мелкие открытые 91:| 85 | **Пилот-преп добор** +``` **Другие носители того же факта:** docs/architecture/13-tech-debt-anchors.md:104 (Сводка того же среза — вторая копия того же протухшего утверждения), docs/archive/architecture/05-decisions-D39-59-90.md (архивный носитель, легитимен как история) -**Предложенное действие:** Заменить абзац «Фикс/скоуп» пункта на: «→ **ЗАКРЫТО D39.69 §3 (строки 27/84): neuter end-to-end построен** — `genderConstraintNote` `case "neuter","n"` (`memory.go:846-848`), `GenderNeuter` в `lang/embedded.go:398/456`, seed-lint `GenderVocabViolations` (`memseed.go:533/546`), ось = scoped `neuter-directive-v1`-фолд в `memory_version`. **ЖИВОЙ ОСТАТОК ОДИН — косметика:** комментарий схемы `store/migrate.go:184` всё ещё перечисляет `male|female|hidden|''` без neuter.» Пункт (б) про `decl.invariant` оставить. + +**Предложенное действие (автора находки):** Заменить абзац «Фикс/скоуп» пункта на: «→ **ЗАКРЫТО D39.69 §3 (строки 27/84): neuter end-to-end построен** — `genderConstraintNote` `case "neuter","n"` (`memory.go:846-848`), `GenderNeuter` в `lang/embedded.go:398/456`, seed-lint `GenderVocabViolations` (`memseed.go:533/546`), ось = scoped `neuter-directive-v1`-фолд в `memory_version`. **ЖИВОЙ ОСТАТОК ОДИН — косметика:** комментарий схемы `store/migrate.go:184` всё ещё перечисляет `male|female|hidden|''` без neuter.» Пункт (б) про `decl.invariant` оставить. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие в целом верно и его якоря я проверил (memory.go:848-849 внутри предложенного 846-848 ✓; embedded.go:398 `GenderNeuter string` и :456 `case "gender_neuter":` ✓; memseed.go:533 вызов и :546 докстринг ✓; живой остаток migrate.go:184 `-- male|female|hidden|'' (C2 …)` без neuter ✓). ДВЕ правки к нему. (1) Не вписывать НОВЫЕ голые номера строк: этот файл гниёт именно ими (см. вердикт по строке 134), а в репозитории есть гейт, который сверяет якорь по содержимому ТОЛЬКО в опт-ин токен-форме `` `путь:848`=`case "neuter", "n":` `` (docs/scripts/counts.py, ратифицировано D39.126, бэклог-строка 205). Тело этого же пункта на строке 70 УЖЕ написано в токен-форме — новый абзац обязан быть в ней же, иначе закрытие протухнет третьим кругом. (2) Правку строки 72 нельзя делать в отрыве от строки 104: там ВТОРАЯ, противоположная копия того же факта (находка №2), и починка одной половины оставит документ противоречащим самому себе. + +**Разбор опровергателя:** Опровергнуть не удалось: обе половины цитаты ложны замером. (1) `gender: neuter` не no-op — `genderConstraintNote` отдаёт `tx.GenderNeuter` (memory.go:848-849), а `ComputeVersionScopedIn` складывает scoped-тег `neuter-directive-v1` (memory.go:461-462); (2) линт есть — `GenderVocabViolations` вызывается из `SeedLint` (memseed.go:533), т.е. ровно та «seed-lint-отбраковка неизвестных значений рода», которую абзац предписывает ПОСТРОИТЬ. Это прямое противоречие ратифицированному D39.69 §3, который называет строку 84 ЗАКРЫТОЙ, — и строки 84 в единственном трекере (таблица «Бэклог» docs/PROGRESS.md) уже нет. Абзац не защищён ⚠-баннером и в отличие от соседей (строки 60, 78, 82 — «→ Закрыто D39.69 … (Исторически: …)») не помечен как исторический: он читается как живое предписание к действию. Тело пункта выше (строка 70) это уже признаёт — не обновлён ровно абзац «Фикс/скоуп». Находка верна, класс D верен, severity major верен. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие в целом верно и его якоря я проверил (memory.go:848-849 внутри предложенного 846-848 ✓; embedded.go:398 `GenderNeuter string` и :456 `case "gender_neuter":` ✓; memseed.go:533 вызов и :546 докстринг ✓; живой остаток migrate.go:184 `-- male|female|hidden|'' (C2 …)` без neuter ✓). ДВЕ правки к нему. (1) Не вписывать НОВЫЕ голые номера строк: этот файл гниёт именно ими (см. вердикт по строке 134), а в репозитории есть гейт, который сверяет якорь по содержимому ТОЛЬКО в опт-ин токен-форме `` `путь:848`=`case "neuter", "n":` `` (docs/scripts/counts.py, ратифицировано D39.126, бэклог-строка 205). Тело этого же пункта на строке 70 УЖЕ написано в токен-форме — новый абзац обязан быть в ней же, иначе закрытие протухнет третьим кругом. (2) Правку строки 72 нельзя делать в отрыве от строки 104: там ВТОРАЯ, противоположная копия того же факта (находка №2), и починка одной половины оставит документ противоречащим самому себе. --- ## N031 · MAJOR · `docs/architecture/13-tech-debt-anchors.md:100` · C-исполненное-как-открытое -**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** feed_cap НЕ построен: grep feed_cap/FeedCap по backend/ = 0 +**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> feed_cap НЕ построен: grep feed_cap/FeedCap по backend/ = 0 + **Почему протухло:** feed_cap ЗАКРЫТ ратифицированной нотой D39.69 §1 («value-порядок отреза бюджета (частота-desc — feed_cap ЗАКРЫТ этим, из строки 86 изъят)»), и код это несёт: terminology.Batch упорядочивает батчи по УБЫВАНИЮ частоты именно чтобы бюджетный потолок резал наименее ценное, а не лексикографический хвост; греп, объявленный нулевым, сегодня даёт два хита. Пункт стоит без стрелки закрытия, вопреки собственной норме файла «исполненный пункт помечать ссылкой на D-номер»; та же ложь повторена в «Сводке среза» (строка 104: «feed_cap: не построен (grep=0)»). Трекер уже исправлен — строка 86 бэклога несёт «D39.69(feed_cap закрыт §1)». -**Улика — команда:** `grep -rn 'feed_cap' backend/ | head; sed -n '1162,1165p' backend/internal/terminology/terminology.go; cd backend && go test ./internal/terminology/ -run TestBatchValueOrder -count=1 -v` -**Улика — вывод:** backend/internal/terminology/series_test.go:199:// (feed_cap) rather than the lexicographic tail. + +**Улика — команда:** +``` +grep -rn 'feed_cap' backend/ | head; sed -n '1162,1165p' backend/internal/terminology/terminology.go; cd backend && go test ./internal/terminology/ -run TestBatchValueOrder -count=1 -v +``` +**Улика — вывод:** +``` +backend/internal/terminology/series_test.go:199:// (feed_cap) rather than the lexicographic tail. backend/internal/terminology/terminology.go:1164:// (feed_cap, §7). --- // The batches are then ordered by descending source frequency, so a budget ceiling drops the least-frequent terms instead of the lexicographic tail @@ -625,39 +1193,70 @@ backend/internal/terminology/terminology.go:1164:// (feed_cap, §7). --- PASS: TestBatchValueOrder (0.00s) PASS ok textmachine/backend/internal/terminology 0.002s +``` **Другие носители того же факта:** docs/PROGRESS.md:78 (строка бэклога 86 — уже несёт закрытие «D39.69(feed_cap закрыт §1)»), docs/architecture/05-decisions-log.md:194 (тело D39.69 — носитель закрытия), docs/architecture/05-decisions-log.md:182 (тело D39.45 — историческая констатация ДО закрытия), docs/architecture/13-tech-debt-anchors.md:104 (Сводка среза — вторая копия протухшего утверждения в том же файле) -**Предложенное действие:** Дописать по месту стрелку, как велит норма файла: «→ **ЗАКРЫТО D39.69 §1: value-порядок отреза бюджета (частота-desc) построен — `terminology.Batch`, гвоздь `TestBatchValueOrder`; из строки 86 изъят.**» Строку 102 «Занесено правильно … формулировка точна» заменить на «формулировка БЫЛА точна на 01.08; опровергнута лендингом D39.69». В «Сводке среза» (строка 104) убрать фрагмент «feed_cap: не построен (grep=0), и претензия … подтверждена кодом — батчи в лексикографическом порядке src». + +**Предложенное действие (автора находки):** Дописать по месту стрелку, как велит норма файла: «→ **ЗАКРЫТО D39.69 §1: value-порядок отреза бюджета (частота-desc) построен — `terminology.Batch`, гвоздь `TestBatchValueOrder`; из строки 86 изъят.**» Строку 102 «Занесено правильно … формулировка точна» заменить на «формулировка БЫЛА точна на 01.08; опровергнута лендингом D39.69». В «Сводке среза» (строка 104) убрать фрагмент «feed_cap: не построен (grep=0), и претензия … подтверждена кодом — батчи в лексикографическом порядке src». + **Разрушающее:** False --- ## N032 · MAJOR · `docs/architecture/13-tech-debt-anchors.md:134` · B-мёртвый-указатель -**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** membank/mempostcheck.go:12 — пост-чек стоит на посылке «SRC actually fired in THIS chunk»; «сработал» = Aho-Corasick ПОДСТРОКА (memory.go:532). Han-ключи намеренно НЕ проверяются на границы: memory.go:1071-1072 +**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> membank/mempostcheck.go:12 — пост-чек стоит на посылке «SRC actually fired in THIS chunk»; «сработал» = Aho-Corasick ПОДСТРОКА (memory.go:532). Han-ключи намеренно НЕ проверяются на границы: memory.go:1071-1072 + **Почему протухло:** Единственная объявленная функция файла — быть справочником file:line-якорей («Этот файл — справочные file:line-якоря … Актуализируется лендингами паков»). Механизм не работает: сплошной проверкой 59 якорей (по ДОСЛОВНО процитированному в доке токену) 29 не указывают на то, что цитируют. Дрейф не косметический: `status.go:561-582` (RedriveSelector) → 907 (+346); `render.go:308` («incl. dropped c-lite members») → 471; `cheapgates.go:414-416` (гейт magnitudeStem) → 549; `terminologist.go:603` → 380 (−223); `terminology.go:917-919` (Batch) → 1172; `book.go:250` (валидатор ceilings) → 321; `archguard.go:79-82` — цель `touchesPkg` вообще исчезла (переписана в `reaches`/`fromPkg` дофиксом D39.97). Особый случай: якорь KANA/HAN уже правился один раз в этом же файле (1048→1071) и уехал СНОВА — фактически 1077, то есть обе редакции мертвы одновременно. -**Улика — команда:** `grep -n 'KANA and HAN are deliberately NOT boundary-checked' backend/internal/membank/memory.go; grep -n 'type RedriveSelector struct' backend/internal/pipeline/status.go; grep -n 'incl. dropped c-lite members' backend/cmd/tmctl/render.go; grep -n 'len(c.magnitudeStem) == 0' backend/internal/checks/cheapgates.go; grep -n 'run.texts\[i\]' backend/internal/pipeline/terminologist.go | head -1; grep -c 'touchesPkg' backend/internal/archguard/archguard.go` -**Улика — вывод:** memory.go:1077 (док: 1048-1051 и, в исправленной редакции, 1071-1072) + +**Улика — команда:** +``` +grep -n 'KANA and HAN are deliberately NOT boundary-checked' backend/internal/membank/memory.go; grep -n 'type RedriveSelector struct' backend/internal/pipeline/status.go; grep -n 'incl. dropped c-lite members' backend/cmd/tmctl/render.go; grep -n 'len(c.magnitudeStem) == 0' backend/internal/checks/cheapgates.go; grep -n 'run.texts\[i\]' backend/internal/pipeline/terminologist.go | head -1; grep -c 'touchesPkg' backend/internal/archguard/archguard.go +``` +**Улика — вывод:** +``` +memory.go:1077 (док: 1048-1051 и, в исправленной редакции, 1071-1072) status.go:907 (док: 561-582) render.go:471 (док: 308) cheapgates.go:549 (док: 414-416) terminologist.go:380 (док: 603) archguard.go: touchesPkg = 0 хитов (при 17 живых func в файле — цель переименована дофиксом D39.97) Сплошной прогон 59 якорей: == OK=30 DRIFT/GONE=29 == +``` **Другие носители того же факта:** docs/PROGRESS.md:89 (строка бэклога 81 — несёт ИСПРАВЛЕННЫЙ якорь migrate.go:187/194, тогда как справочник на строке 12 всё ещё пишет :186), docs/PROGRESS.md:251 (строка 111 — свой якорь langpack.go:806-826, тоже мимо: cats[f[0]][f[1]] на 829), docs/PROGRESS.md:255-256 (строки 121/122 явно отсылают за file:line именно в §Б-121/§Б-122 этого файла) -**Предложенное действие:** Не переписывать 29 чисел руками — это третий круг того же гниения (якорь memory.go правился и уехал снова). Завести $0-гвоздь: тест/скрипт, который читает пары «путь:строка + дословная цитата» из этого файла и краснеет, когда цитата не лежит на названной строке (форма уже отработана в репо — `TestMemnormXTextVersionPin` пинит константу против go.mod). До гвоздя — заменить голые номера строк на ЦИТАТУ+имя символа (`membank.InjectivityCollisions`, `RedriveSelector`), которые греп находит без номера, и поставить в шапку файла предупреждение «номера строк — снимок лендинга, сверяйся грепом токена». + +**Предложенное действие (автора находки):** Не переписывать 29 чисел руками — это третий круг того же гниения (якорь memory.go правился и уехал снова). Завести $0-гвоздь: тест/скрипт, который читает пары «путь:строка + дословная цитата» из этого файла и краснеет, когда цитата не лежит на названной строке (форма уже отработана в репо — `TestMemnormXTextVersionPin` пинит константу против go.mod). До гвоздя — заменить голые номера строк на ЦИТАТУ+имя символа (`membank.InjectivityCollisions`, `RedriveSelector`), которые греп находит без номера, и поставить в шапку файла предупреждение «номера строк — снимок лендинга, сверяйся грепом токена». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Находка верна, ПРЕДПИСАНИЕ НЕВЕРНО в главном: «завести $0-гвоздь» — предложение построить то, что УЖЕ ПОСТРОЕНО И РАТИФИЦИРОВАНО. Гейт якорей живёт в `docs/scripts/counts.py` (D39.126; исполнен 21.08, носитель — бэклог-строка 205 в docs/PROGRESS.md:185), сверяет якорь ПО СОДЕРЖИМОМУ через опт-ин токен ожидания `` `путь:123`=`подстрока` `` (регексп ANCHOR, counts.py:344-347) и вплетён в pre-commit хук (D39.148, warn-only). Я его исполнил ($0): `python3 docs/scripts/counts.py --lint` → «0 проблемных якорей в 100 живых доках · сверено ПО СОДЕРЖИМОМУ (токен): 137 · **без токена — несверяемы: 708**». Вот и объяснение зелени при 14 уехавших якорях: в самом 13-tech-debt-anchors.md токен-форму несут ВСЕГО ШЕСТЬ якорей (`grep -c '=`' = 4 строки), остальные ~130 гейт не видит по построению. Поэтому правильное действие: не строить второй гвоздь, а МИГРИРОВАТЬ якоря этого файла в токен-форму — ровно тот «органический остаток», который строка 205 уже объявила («токен требуется на ЗАТРОНУТЫХ строках, поэтому счёт растёт сам с каждым касанием»); этот док — концентрат якорей, и на нём органика не сработала, значит он законный кандидат на разовую пакетную миграцию, а не на новый механизм. Совет «поставить в шапку предупреждение о снапшоте» верен и дёшев (форма уже отработана на строке 162 и 94 этого же файла). Две поправки к фактуре самой находки: (1) цель якоря `terminologist.go:603` — это `run.texts[i] = att.text` на строке 817, а не 380 (на 380 другое чтение `run.texts[i]`); (2) при планировании учесть бэклог-строку 244 (docs/PROGRESS.md:206) — известный дефект гейта: хук судит ЗАКОММИЧЕННЫЙ док против РАБОЧЕГО кода и врёт в обе стороны. + +**Разбор опровергателя:** Опровергнуть не удалось — наоборот, мой независимый прогон дал ХУДШУЮ картину, чем у автора (14/18 против его 29/59). Все семь поимённо названных им якорей воспроизведены, включая особый случай: якорь KANA/HAN правился один раз (1048→1071, о чём файл сам пишет на строке 134) и уехал СНОВА на 1077 — обе редакции мертвы одновременно, причём голая 1048 всё ещё живёт на строках 12, 14 и 52 этого же файла и в бэклог-строке 81. `touchesPkg` предъявлен с корректным положительным контролем (15 функций в файле, найдена замена `reaches`/`fromPkg`) — пустой греп не от опечатки. Класс B верен, severity major верна: файл существует РОВНО чтобы быть носителем file:line, и две живые строки бэклога (docs/PROGRESS.md:255-256, строки 121/122) отсылают за якорями именно сюда. Заявленный механизм актуализации (строка 4) демонстративно не работает. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Находка верна, ПРЕДПИСАНИЕ НЕВЕРНО в главном: «завести $0-гвоздь» — предложение построить то, что УЖЕ ПОСТРОЕНО И РАТИФИЦИРОВАНО. Гейт якорей живёт в `docs/scripts/counts.py` (D39.126; исполнен 21.08, носитель — бэклог-строка 205 в docs/PROGRESS.md:185), сверяет якорь ПО СОДЕРЖИМОМУ через опт-ин токен ожидания `` `путь:123`=`подстрока` `` (регексп ANCHOR, counts.py:344-347) и вплетён в pre-commit хук (D39.148, warn-only). Я его исполнил ($0): `python3 docs/scripts/counts.py --lint` → «0 проблемных якорей в 100 живых доках · сверено ПО СОДЕРЖИМОМУ (токен): 137 · **без токена — несверяемы: 708**». Вот и объяснение зелени при 14 уехавших якорях: в самом 13-tech-debt-anchors.md токен-форму несут ВСЕГО ШЕСТЬ якорей (`grep -c '=`' = 4 строки), остальные ~130 гейт не видит по построению. Поэтому правильное действие: не строить второй гвоздь, а МИГРИРОВАТЬ якоря этого файла в токен-форму — ровно тот «органический остаток», который строка 205 уже объявила («токен требуется на ЗАТРОНУТЫХ строках, поэтому счёт растёт сам с каждым касанием»); этот док — концентрат якорей, и на нём органика не сработала, значит он законный кандидат на разовую пакетную миграцию, а не на новый механизм. Совет «поставить в шапку предупреждение о снапшоте» верен и дёшев (форма уже отработана на строке 162 и 94 этого же файла). Две поправки к фактуре самой находки: (1) цель якоря `terminologist.go:603` — это `run.texts[i] = att.text` на строке 817, а не 380 (на 380 другое чтение `run.texts[i]`); (2) при планировании учесть бэклог-строку 244 (docs/PROGRESS.md:206) — известный дефект гейта: хук судит ЗАКОММИЧЕННЫЙ док против РАБОЧЕГО кода и врёт в обе стороны. --- ## N033 · MAJOR · `docs/architecture/13-tech-debt-anchors.md:180` · C-исполненное-как-открытое -**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** grep sourceScripts/SourceScripts по cheapgates.go — 0 вхождений: механизм SetSourceScripts существует (checkers.go:104-125) и используется другими путями, но lintNumberMagnitude его НЕ читает +**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> grep sourceScripts/SourceScripts по cheapgates.go — 0 вхождений: механизм SetSourceScripts существует (checkers.go:104-125) и используется другими путями, но lintNumberMagnitude его НЕ читает + **Почему протухло:** Гейт по письму источника ПОСТРОЕН и стоит внутри самой lintNumberMagnitude; код называет заказ по имени в комментарии — «SOURCE-script gate (строка 79, D39.39)». Греп, объявленный нулевым, даёт хит cheapgates.go:555. Есть гвоздь `TestNumberMagnitudeSourceScriptGate`, который проверяет ровно три случая (zh → файрит, en → инертен, ja → файрит). Вердикт строки 182 «Не требуется — дописка 79 точна» сегодня ложен, и это самый дорогой класс: сессия, читающая справочник, возьмётся строить уже построенное. Трекер снова впереди — строка 79 бэклога уже пишет, что этот компонент исполнен. -**Улика — команда:** `grep -ni 'sourcescripts' backend/internal/checks/cheapgates.go; sed -n '552,557p' backend/internal/checks/cheapgates.go; cd backend && go test ./internal/checks/ -run TestNumberMagnitudeSourceScriptGate -count=1 -v` -**Улика — вывод:** 555: if len(c.sourceScripts) > 0 && !c.sourceHasDenseScript() { + +**Улика — команда:** +``` +grep -ni 'sourcescripts' backend/internal/checks/cheapgates.go; sed -n '552,557p' backend/internal/checks/cheapgates.go; cd backend && go test ./internal/checks/ -run TestNumberMagnitudeSourceScriptGate -count=1 -v +``` +**Улика — вывод:** +``` +555: if len(c.sourceScripts) > 0 && !c.sourceHasDenseScript() { --- // SOURCE-script gate (строка 79, D39.39): the 万/億/兆 markers are Han, so a DECLARED non-dense source // cannot carry a magnitude — stay inert rather than firing on a stray Han rune quoted inside e.g. an @@ -670,35 +1269,61 @@ archguard.go: touchesPkg = 0 хитов (при 17 живых func в файле --- PASS: TestNumberMagnitudeSourceScriptGate (0.00s) PASS ok textmachine/backend/internal/checks 0.011s +``` **Другие носители того же факта:** docs/PROGRESS.md:85 (строка бэклога 79 — уже несёт «Первые два компонента строки (isCyrLetter→isTargetWordLetter · magnitude SourceScripts-гейт) исполнены пакетом-чекеров») -**Предложенное действие:** Пометить пункт стрелкой: «→ **ИСПОЛНЕНО пакетом-чекеров: SOURCE-script-гейт стоит в `lintNumberMagnitude` (`cheapgates.go:555`, `sourceHasDenseScript` — `checkers.go:507`), гвоздь `TestNumberMagnitudeSourceScriptGate` (zh файрит · en инертен · ja файрит); в строке 79 бэклога компонент уже числится исполненным.**» Строку 182 «Не требуется — дописка 79 точна» заменить на констатацию исполнения. Остаток строки 79 (граница плотности через Go-константу `cjkScriptNames`) НЕ трогать — он жив. + +**Предложенное действие (автора находки):** Пометить пункт стрелкой: «→ **ИСПОЛНЕНО пакетом-чекеров: SOURCE-script-гейт стоит в `lintNumberMagnitude` (`cheapgates.go:555`, `sourceHasDenseScript` — `checkers.go:507`), гвоздь `TestNumberMagnitudeSourceScriptGate` (zh файрит · en инертен · ja файрит); в строке 79 бэклога компонент уже числится исполненным.**» Строку 182 «Не требуется — дописка 79 точна» заменить на констатацию исполнения. Остаток строки 79 (граница плотности через Go-константу `cjkScriptNames`) НЕ трогать — он жив. + **Разрушающее:** False --- ## N034 · MAJOR · `docs/architecture/14-api-contract/README.md:8` · A-дрейф -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** ⚠ Зонная копия frontend/docs/api-contract/ ВРЕМЕННО ОТСТАЁТ (0.2.3 при каноне +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> ⚠ Зонная копия frontend/docs/api-contract/ ВРЕМЕННО ОТСТАЁТ (0.2.3 при каноне 0.8.0) — ратифицировано D39.142 п.5 на время фриза фронта + **Почему протухло:** Канон стоит на 0.9.0 с 31.08 (минор заленджен D39.180 вместе с паком P12), а ревью-шапка всё ещё называет базой 0.8.0. Верно: зеркало 0.2.3 против канона 0.9.0 — СЕМЬ миноров, а не шесть, и цена разморозки зоны в шапке занижена на один ломающий шаг. -**Улика — команда:** `grep -n 'version:' docs/architecture/14-api-contract/openapi.yaml | head -1; grep -n 'version:' frontend/docs/api-contract/openapi.yaml | head -1; grep -n '^## D39.180' docs/architecture/05-decisions-log.md` -**Улика — вывод:** docs/architecture/14-api-contract/openapi.yaml:5: version: 0.9.0 + +**Улика — команда:** +``` +grep -n 'version:' docs/architecture/14-api-contract/openapi.yaml | head -1; grep -n 'version:' frontend/docs/api-contract/openapi.yaml | head -1; grep -n '^## D39.180' docs/architecture/05-decisions-log.md +``` +**Улика — вывод:** +``` +docs/architecture/14-api-contract/openapi.yaml:5: version: 0.9.0 frontend/docs/api-contract/openapi.yaml:5: version: 0.2.3 1677:## D39.180 — ПАК ПЛАТФОРМЫ P12 «ДОЛГИ ПОД НОГАМИ» ПРИНЯТ И ЗАЛЕНДЖЕН вместе с контрактным минором 0.9.0 (31.08, оркестратор №21). ✅ +``` **Другие носители того же факта:** docs/PROGRESS.md:3 — «зеркало отстало на ШЕСТЬ миноров (`frontend/docs/api-contract/openapi.yaml` — `0.2.3` против канона `0.8.0`, и первый из них — ЛОМАЮЩИЙ 0.3.0), 16 операций против 20», frontend/docs/frontend-PROGRESS.md:9 — «Состояние зеркала контракта — 0.2.3 против канона 0.8.0 (и 0.9.0 придёт с лендингом P12): шесть миноров» — P12 уже лёг, скобка протухла вместе с числом -**Предложенное действие:** В шапке README заменить «(0.2.3 при каноне 0.8.0)» на «(0.2.3 при каноне 0.9.0)»; тем же проходом поправить оба других носителя: в docs/PROGRESS.md:3 и frontend/docs/frontend-PROGRESS.md:9 — «ШЕСТЬ миноров» → «СЕМЬ миноров», «против канона 0.8.0» → «против канона 0.9.0», снять скобку «(и 0.9.0 придёт с лендингом P12)». + +**Предложенное действие (автора находки):** В шапке README заменить «(0.2.3 при каноне 0.8.0)» на «(0.2.3 при каноне 0.9.0)»; тем же проходом поправить оба других носителя: в docs/PROGRESS.md:3 и frontend/docs/frontend-PROGRESS.md:9 — «ШЕСТЬ миноров» → «СЕМЬ миноров», «против канона 0.8.0» → «против канона 0.9.0», снять скобку «(и 0.9.0 придёт с лендингом P12)». + **Разрушающее:** False --- ## N035 · MAJOR · `docs/architecture/14-api-contract/README.md:981` · C-исполненное-как-открытое -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Исчерпанность: платформа отвечает `202` и возвращает прогон в то же состояние (`reconcile.go`, оба выхода `exhausted` из `reopen` → ветка `exhausted` в `Resume` → `httpapi/v0.go`, хендлер `resumeRun`), а контракт теперь требует `409` `run_not_resumable` · `cause.code: ceiling_reached` +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Исчерпанность: платформа отвечает `202` и возвращает прогон в то же состояние (`reconcile.go`, оба выхода `exhausted` из `reopen` → ветка `exhausted` в `Resume` → `httpapi/v0.go`, хендлер `resumeRun`), а контракт теперь требует `409` `run_not_resumable` · `cause.code: ceiling_reached` + **Почему протухло:** Расхождение ЗАКРЫТО: `Resume` больше не отвечает 202 на исчерпанный прогон — вердикт `ceilingSpent` возвращает `ErrCeilingReached`, и `httpapi/v0.go:807` кладёт его в 409 `run_not_resumable` · `cause: ceiling_reached`. Регистр платформы держит PD-282 как `fixed(акт 5)` с пином теста. Ветки `exhausted` в `reopen`/`Resume`, на которую указывает док, в коде НЕТ вовсе — она пере-названа в `ceilingSpent`/`creditUnavailable`. -**Улика — команда:** `grep -n 'ceilingSpent\|ErrCeilingReached' platform/internal/runs/reconcile.go platform/internal/httpapi/v0.go | grep -v _test; grep -n 'exhausted' platform/internal/runs/reconcile.go; grep -o 'PD-282[^|]*| fixed[^|]*|' platform/docs/DEFECT_REGISTER.md` -**Улика — вывод:** platform/internal/runs/reconcile.go:1325: ceilingSpent + +**Улика — команда:** +``` +grep -n 'ceilingSpent\|ErrCeilingReached' platform/internal/runs/reconcile.go platform/internal/httpapi/v0.go | grep -v _test; grep -n 'exhausted' platform/internal/runs/reconcile.go; grep -o 'PD-282[^|]*| fixed[^|]*|' platform/docs/DEFECT_REGISTER.md +``` +**Улика — вывод:** +``` +platform/internal/runs/reconcile.go:1325: ceilingSpent platform/internal/runs/reconcile.go:1371: return pgstore.LiveRun{}, ceilingSpent, nil platform/internal/runs/reconcile.go:1570: case ceilingSpent: platform/internal/runs/reconcile.go:1578: return pgstore.Run{}, ErrCeilingReached @@ -706,42 +1331,83 @@ platform/internal/httpapi/v0.go:807: case errors.Is(err, runs.ErrCeilingReached) --- grep 'exhausted' (положительный контроль: греп В ЭТОМ ФАЙЛЕ находит) --- 950: // The reason it is not cosmetic: a ceiling reason of `credit_exhausted` lights the ACCOUNT-level 980: // this way. Guessing `credit_exhausted` would light the account-level halted flag -1277: // the account's reason is what made it look like an exha -**Положительный контроль:** grep -n 'exhausted' platform/internal/runs/reconcile.go → 3 хита (950, 980, 1277). Инструмент в этом файле находит слово; отсутствует именно ВЕТКА `case "exhausted"`, а не способность грепа её увидеть. +1277: // the account's reason is what made it look like an exhausted account. +--- реестр --- +PD-282 ... | fixed(акт 5) | (комментарий кода на reconcile.go:1575: «It used to answer 202 with the run unchanged ... (PD-282)») +``` +**Положительный контроль:** +``` +grep -n 'exhausted' platform/internal/runs/reconcile.go → 3 хита (950, 980, 1277). Инструмент в этом файле находит слово; отсутствует именно ВЕТКА `case "exhausted"`, а не способность грепа её увидеть. +``` **Другие носители того же факта:** docs/architecture/14-api-contract/README.md:1724 — Приложение А-2, строка `run_not_resumable`: «где платформа сегодня молча отвечает `202`» с тем же мёртвым указателем «ветка `exhausted` функции `reopen`», platform/docs/DEFECT_REGISTER.md:446 — PD-282, уже помечен fixed (носитель актуален, менять нечего) -**Предложенное действие:** В §3 (строка 981) заменить «платформа отвечает `202` и возвращает прогон в то же состояние (…ветка `exhausted`…)» на «СОШЛОСЬ: платформа отвечает `409` `run_not_resumable` · `cause: ceiling_reached` (`runs/reconcile.go`, вердикт `ceilingSpent` в `Resume` → `httpapi/v0.go:807`); PD-282 закрыт, пин `runs.TestResumeOfARunWithNothingLeftIsRefusedWithTheCeilingReached`». То же — в Приложении А-2, строка 1724: снять «где платформа сегодня молча отвечает `202`» и указатель на ветку `exhausted`, добавить второй вердикт `creditUnavailable` → `cause: credit_unavailable`. + +**Предложенное действие (автора находки):** В §3 (строка 981) заменить «платформа отвечает `202` и возвращает прогон в то же состояние (…ветка `exhausted`…)» на «СОШЛОСЬ: платформа отвечает `409` `run_not_resumable` · `cause: ceiling_reached` (`runs/reconcile.go`, вердикт `ceilingSpent` в `Resume` → `httpapi/v0.go:807`); PD-282 закрыт, пин `runs.TestResumeOfARunWithNothingLeftIsRefusedWithTheCeilingReached`». То же — в Приложении А-2, строка 1724: снять «где платформа сегодня молча отвечает `202`» и указатель на ветку `exhausted`, добавить второй вердикт `creditUnavailable` → `cause: credit_unavailable`. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Предложенное действие верно по сути; добавить четыре вещи. (1) НЕ трогать первую половину строки 981 про паузу — она подтверждена кодом (reconcile.go:1537-1544). (2) В обеих правках назвать ОБА вердикта, а не один: `ceilingSpent` → `cause: ceiling_reached` и `creditUnavailable` → `cause: credit_unavailable` (reconcile.go:1581, v0.go:813) — прежняя редакция дока схлопывала их в один `exhausted`, и разведение как раз и есть суть закрытия. (3) Пин цитировать МОЖНО, но с оговоркой: `TestResumeOfARunWithNothingLeftIsRefusedWithTheCeilingReached` без `TM_PLATFORM_TEST_DSN` уходит в SKIP, поэтому «зелёный тест» доказательством закрытия НЕ является — доказательство это reconcile.go:1570-1578 + v0.go:807-813. (4) Находка назвала не все носители внутри самого дока: третий — README:1265+ (§6в «A. Прогон с исчерпанным бюджетом (PD-282)») повторяет «`Resume` отвечает на оба одинаково: `202` + неизменённый `Run`» и «`reopen` даёт `exhausted` в ДВУХ местах». Это ДАТИРОВАННАЯ запись решения («Синк с платформой 20.08 → канон 0.4.0: что решено и почему»), то есть улика-обоснование, а не живое утверждение: её ТЕЛО переписывать не надо, ей нужен ⚠-указатель «закрыто, PD-282 fixed(акт 5), см. §3». Живой переписи подлежат только 981 и 1724. + +**Разбор опровергателя:** Опровергнуть не удалось ни по одному из трёх каналов. Указатель мёртв: вердиктов `exhausted` в коде нет, они разведены на `ceilingSpent`/`creditUnavailable` — и это не пустой греп, положительный контроль в том же файле даёт три хита на то же слово. Поведение сменилось: 409 `run_not_resumable` · `cause: ceiling_reached` идёт из `Resume` через `fail()` (v0.go:807/810) при статусе 409 из problem.go:120. Носитель дефекта закрыт: PD-282 = fixed(акт 5). Проверил и обратную гипотезу «автор читал устаревшую копию» — нет, он читал ОДНОВРЕМЕННУЮ: лендинг кода и лендинг дока разделяют 10 секунд, доковая половина писалась под код ДО фикса и не была пере-снята. Проверил и вторую половину той же строки — «Пауза: платформа уже отвечает 409 ceiling_reached (ветка case "paused" в Resume) — совпало»: она ВЕРНА до сих пор (reconcile.go:1537 `case "paused":` → :1544 `ErrCeilingReached`), сносить её нельзя. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Предложенное действие верно по сути; добавить четыре вещи. (1) НЕ трогать первую половину строки 981 про паузу — она подтверждена кодом (reconcile.go:1537-1544). (2) В обеих правках назвать ОБА вердикта, а не один: `ceilingSpent` → `cause: ceiling_reached` и `creditUnavailable` → `cause: credit_unavailable` (reconcile.go:1581, v0.go:813) — прежняя редакция дока схлопывала их в один `exhausted`, и разведение как раз и есть суть закрытия. (3) Пин цитировать МОЖНО, но с оговоркой: `TestResumeOfARunWithNothingLeftIsRefusedWithTheCeilingReached` без `TM_PLATFORM_TEST_DSN` уходит в SKIP, поэтому «зелёный тест» доказательством закрытия НЕ является — доказательство это reconcile.go:1570-1578 + v0.go:807-813. (4) Находка назвала не все носители внутри самого дока: третий — README:1265+ (§6в «A. Прогон с исчерпанным бюджетом (PD-282)») повторяет «`Resume` отвечает на оба одинаково: `202` + неизменённый `Run`» и «`reopen` даёт `exhausted` в ДВУХ местах». Это ДАТИРОВАННАЯ запись решения («Синк с платформой 20.08 → канон 0.4.0: что решено и почему»), то есть улика-обоснование, а не живое утверждение: её ТЕЛО переписывать не надо, ей нужен ⚠-указатель «закрыто, PD-282 fixed(акт 5), см. §3». Живой переписи подлежат только 981 и 1724. --- ## N036 · MAJOR · `docs/architecture/14-api-contract/README.md:1637` · A-дрейф -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** - **Чем и на каком слое сжимать** (middleware, обратный прокси, CDN). Go stdlib не сжимает, +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> - **Чем и на каком слое сжимать** (middleware, обратный прокси, CDN). Go stdlib не сжимает, edge-конфига в репозитории нет, `grep -rn "gzip|Content-Encoding" platform --include=*.go` → ноль. + **Почему протухло:** Собственная команда дока, исполненная сегодня, даёт 26 хитов, а не ноль: платформа сжимает САМА, в Go, в `platform/internal/httpapi/conditional.go` (`gzipFloor`, `acceptsGzip`, `head.Set("Content-Encoding","gzip")`). Файл приехал с паком P7 (коммит 9b23e8c), то есть абзац родился верным и сгнил лендингом. Он же противоречит §3 ТОГО ЖЕ дока (строка 994: «Условные чтения (`ETag`/304), сжатие | ПОСТРОЕНО P7 — `httpapi/conditional.go`») и последнему пункту §7 («шаги 1–2 — конфигурация»). -**Улика — команда:** `grep -rnE "gzip|Content-Encoding" /home/ubuntu/projects/textmachine-main/platform --include=*.go | wc -l; grep -rnE "gzip|Content-Encoding" /home/ubuntu/projects/textmachine-main/platform --include=*.go | grep -v _test | head -5; git log --oneline -1 --diff-filter=A -- platform/internal/httpapi/conditional.go` -**Улика — вывод:** 26 + +**Улика — команда:** +``` +grep -rnE "gzip|Content-Encoding" /home/ubuntu/projects/textmachine-main/platform --include=*.go | wc -l; grep -rnE "gzip|Content-Encoding" /home/ubuntu/projects/textmachine-main/platform --include=*.go | grep -v _test | head -5; git log --oneline -1 --diff-filter=A -- platform/internal/httpapi/conditional.go +``` +**Улика — вывод:** +``` +26 platform/internal/httpapi/conditional.go:5: "compress/gzip" platform/internal/httpapi/conditional.go:26:// gzipFloor is the size below which compressing costs more than it saves platform/internal/httpapi/conditional.go:28:const gzipFloor = 1 << 10 platform/internal/httpapi/conditional.go:64: if acceptsGzip(r) && len(buf) >= gzipFloor { platform/internal/httpapi/conditional.go:69: head.Set("Content-Encoding", "gzip") 9b23e8c Land the platform reading surface: chapters, units, notes, bank and the event stream, with a materialization debt that survives a restart and account-level halts -**Предложенное действие:** Снести обещание «грепом ноль» и переписать пункт по факту: сжатие ПОСТРОЕНО в приложении (`platform/internal/httpapi/conditional.go`, порог `gzipFloor` 1 КиБ, поток `text/event-stream` не сжимается по построению — регистрируется вне слоя); в эксплуатационном примечании оставить только то, что клиент действительно не наблюдает — не сжимать ВТОРОЙ раз на прокси/CDN. Заодно поправить хвост §7: «шаги 1–2 — конфигурация» на «шаги 1–2 построены P7». +``` + +**Предложенное действие (автора находки):** Снести обещание «грепом ноль» и переписать пункт по факту: сжатие ПОСТРОЕНО в приложении (`platform/internal/httpapi/conditional.go`, порог `gzipFloor` 1 КиБ, поток `text/event-stream` не сжимается по построению — регистрируется вне слоя); в эксплуатационном примечании оставить только то, что клиент действительно не наблюдает — не сжимать ВТОРОЙ раз на прокси/CDN. Заодно поправить хвост §7: «шаги 1–2 — конфигурация» на «шаги 1–2 построены P7». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие в целом верное, но с тремя правками. (1) НЕ подменять число в команде («26 вместо ноля»): команду из абзаца убрать целиком — она BRE-формы и её ноль ничего не значит; вместо ссылки на греп поставить file:line факта (`platform/internal/httpapi/conditional.go:64-70`, порог `gzipFloor` = 1<<10, `Vary: Accept-Encoding` на всяком согласуемом представлении, `acceptsGzip` по RFC 9110 §12.5.3). (2) Клаузу «Go stdlib не сжимает» сохранить как верную, снести только вывод «edge-конфига нет ⇒ деплой обязан устроить сжатие сам»: в эксплуатационном примечании остаётся ровно одно — не жать ВТОРОЙ раз на прокси/CDN и не жать `text/event-stream` (поток по построению не проходит через writeJSON — комментарий conditional.go:20-24 и README:991). (3) Хвост §7 «шаги 1–2 — конфигурация» править обязательно и в ОДНОМ коммите с абзацем 1637 — иначе останется третий расходящийся носитель внутри одного файла (994 говорит «шаги 1–2 закрыты D39.153»). + +**Разбор опровергателя:** Опровергнуть не удалось: сжатие ПОСТРОЕНО в приложении, в Go, и это видно из тела conditional.go (writeJSON буферизует, ставит Vary: Accept-Encoding, при acceptsGzip и размере ≥1 КиБ жмёт и ставит Content-Encoding: gzip). Хронология подтверждает диагноз «родилось верным, сгнило лендингом», и положительный контроль на историческом коммите чист. Уточнение, которое находка проглядела и которое НАДО передать принимающему: дословная команда, вписанная в док (`grep -rn "gzip|Content-Encoding"`, БЕЗ -E), даёт 0 и СЕГОДНЯ — basic grep читает `|` буквально. То есть в доке лежит команда с пустым положительным контролем: её ноль неотличим от отсутствия факта. Это усиливает находку, а не ослабляет: вписанная в док «улика» никогда ничего не доказывала. Отдельно: клауза «Go stdlib не сжимает» сама по себе ВЕРНА (net/http не жмёт автоматически) — ложен вывод «в репозитории сжатия нет». + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие в целом верное, но с тремя правками. (1) НЕ подменять число в команде («26 вместо ноля»): команду из абзаца убрать целиком — она BRE-формы и её ноль ничего не значит; вместо ссылки на греп поставить file:line факта (`platform/internal/httpapi/conditional.go:64-70`, порог `gzipFloor` = 1<<10, `Vary: Accept-Encoding` на всяком согласуемом представлении, `acceptsGzip` по RFC 9110 §12.5.3). (2) Клаузу «Go stdlib не сжимает» сохранить как верную, снести только вывод «edge-конфига нет ⇒ деплой обязан устроить сжатие сам»: в эксплуатационном примечании остаётся ровно одно — не жать ВТОРОЙ раз на прокси/CDN и не жать `text/event-stream` (поток по построению не проходит через writeJSON — комментарий conditional.go:20-24 и README:991). (3) Хвост §7 «шаги 1–2 — конфигурация» править обязательно и в ОДНОМ коммите с абзацем 1637 — иначе останется третий расходящийся носитель внутри одного файла (994 говорит «шаги 1–2 закрыты D39.153»). --- ## N037 · MAJOR · `docs/architecture/14-api-contract/README.md:1718` · B-мёртвый-указатель -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** | `gone` | 410 | `pgstore.ErrNoChapter` → `v0.go:761` … | `run_in_flight` | 409 | `pgstore.ErrRunInFlight` (`v0.go:548`) | … | `payload_too_large` | 413 | `*http.MaxBytesError` → `v0.go:409` | +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> | `gone` | 410 | `pgstore.ErrNoChapter` → `v0.go:761` … | `run_in_flight` | 409 | `pgstore.ErrRunInFlight` (`v0.go:548`) | … | `payload_too_large` | 413 | `*http.MaxBytesError` → `v0.go:409` | + **Почему протухло:** Вся колонка «Откуда взят (платформа)» Приложения А-2 съехала: реальные производители лежат на 240–270 строк ниже названных, а на названных номерах стоят комментарии и скобки. Это провенанс-карта словаря ошибок — единственное место, откуда следующая сессия узнаёт, где в платформе рождается каждый код, и сегодня она ведёт мимо. Линтер якорей (`python3 docs/scripts/counts.py --lint`) эти якоря НЕ ловит: они «без токена», проверяется только существование файла и длина. -**Улика — команда:** `for l in 409 417 548 550 554 558 562 761; do printf '%4d: ' $l; sed -n "${l}p" platform/internal/httpapi/v0.go | cut -c1-90; done; echo '--- РЕАЛЬНЫЕ ---'; grep -n 'MaxBytesError\|ErrDeadlineExceeded\|ErrNoChapter\|ErrRunInFlight\|ErrBookNotReady\|ErrNotStoppable\|ErrCeilingOutOfBounds\|ErrCeilingNotWired\|func readField' platform/internal/httpapi/v0.go | grep -v _test` -**Улика — вывод:** 409: ) + +**Улика — команда:** +``` +for l in 409 417 548 550 554 558 562 761; do printf '%4d: ' $l; sed -n "${l}p" platform/internal/httpapi/v0.go | cut -c1-90; done; echo '--- РЕАЛЬНЫЕ ---'; grep -n 'MaxBytesError\|ErrDeadlineExceeded\|ErrNoChapter\|ErrRunInFlight\|ErrBookNotReady\|ErrNotStoppable\|ErrCeilingOutOfBounds\|ErrCeilingNotWired\|func readField' platform/internal/httpapi/v0.go | grep -v _test +``` +**Улика — вывод:** +``` + 409: ) 417: // 548: in.File = io.MultiReader(in.File, trailing) 550: // that is what the deferred release does for every failure below. @@ -754,75 +1420,146 @@ platform/internal/httpapi/conditional.go:69: head.Set("Content-Encoding", "gzi 651: var tooLarge *http.MaxBytesError [док: v0.go:409] 655: case errors.Is(err, os.ErrDeadlineExceeded): [док: v0.go:417] 788: case errors.Is(err, pgstore.ErrNoChapter): [док: v0.go:761] -799: case er -**Положительный контроль:** Тем же методом (sed по номеру) проверены якоря, которые ЖИВЫ: `middleware.go:68` → `Fail(w, r, CodeInternalError)` (в точку), `problem.go:42` → `CodeContentRefused Code = "content_refused"` (в точку), `v0.go:92-96` → строки таблицы `contractSurface` (в точку). Значит метод находит верные цели, и пустота на 409/548/761 — свойство указателей, а не инструмента. +799: case errors.Is(err, pgstore.ErrRunInFlight): [док: v0.go:548] +801: case errors.Is(err, runs.ErrBookNotReady): [док: v0.go:550] +805: case errors.Is(err, runs.ErrNotStoppable): [док: v0.go:554] +820: case errors.Is(err, runs.ErrCeilingOutOfBounds) [док: v0.go:558] +828: case errors.Is(err, runner.ErrCeilingNotWired) [док: v0.go:562] +server.go: CodeForbidden на 108-109 [док: server.go:99 = комментарий], catch-all CodeNotFound на 144 [док: server.go:134 = комментарий], CodeUnauthenticated в server.go ОТСУТСТВУЕТ вовсе — он объявлен в problem.go:30/111 [док: server.go:109-110] +``` +**Положительный контроль:** +``` +Тем же методом (sed по номеру) проверены якоря, которые ЖИВЫ: `middleware.go:68` → `Fail(w, r, CodeInternalError)` (в точку), `problem.go:42` → `CodeContentRefused Code = "content_refused"` (в точку), `v0.go:92-96` → строки таблицы `contractSurface` (в точку). Значит метод находит верные цели, и пустота на 409/548/761 — свойство указателей, а не инструмента. +``` **Другие носители того же факта:** docs/architecture/14-api-contract/README.md:141 — «класс 1 выведен перечислением реальных ветвей `platform/internal/httpapi/v0.go:333-427,542-574`» — тот же съезд, тот же файл, docs/architecture/14-api-contract/README.md:914 — `backend/internal/pipeline/status.go:733-744` («его собственный комментарий предупреждает…»): на 733-744 лежит `orphanStageRows`/`ConfigDrift`, а названный комментарий про `bank-apply` — на status.go:872, docs/architecture/14-api-contract/README.md:604 — `runs/runs.go:187-188` для `book.HasLiveRun` (реально runs.go:295), README:1020 — `runs/runs.go:227-231` для аллоулиста `readyToTranslate` (реально runs.go:382-389) -**Предложенное действие:** Пере-снять всю колонку «Откуда взят» Приложения А-2 и три якоря-спутника (README:141, :604, :914, :1020) против дерева ОДНИМ проходом, ПОСЛЕДНИМ шагом после того как код замер (норма D39.169). Точечно: 400→`v0.go:793-797` + `readField` `v0.go:634-640`, 401→`problem.go:30,111` (в `server.go` производителя нет), 403→`server.go:108-109`, 404 catch-all→`server.go:144`, 408→`v0.go:655`, 410→`v0.go:788-792`, 413→`v0.go:651`, 409-ветки→`v0.go:799/801/805/820`, 503→`v0.go:828`, `books.ErrBadIntake`→`books.go:102-117`. Отдельно завести строку долга: линтер `counts.py --lint` этот класс не покрывает — якорь без токена проверяется только на существование файла. + +**Предложенное действие (автора находки):** Пере-снять всю колонку «Откуда взят» Приложения А-2 и три якоря-спутника (README:141, :604, :914, :1020) против дерева ОДНИМ проходом, ПОСЛЕДНИМ шагом после того как код замер (норма D39.169). Точечно: 400→`v0.go:793-797` + `readField` `v0.go:634-640`, 401→`problem.go:30,111` (в `server.go` производителя нет), 403→`server.go:108-109`, 404 catch-all→`server.go:144`, 408→`v0.go:655`, 410→`v0.go:788-792`, 413→`v0.go:651`, 409-ветки→`v0.go:799/801/805/820`, 503→`v0.go:828`, `books.ErrBadIntake`→`books.go:102-117`. Отдельно завести строку долга: линтер `counts.py --lint` этот класс не покрывает — якорь без токена проверяется только на существование файла. + **Разрушающее:** False --- ## N038 · MAJOR · `docs/architecture/14-api-contract/openapi.yaml:1414` · E-второй-носитель -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Today's deployments answer `drafting` or `editing`; other values are not a breaking +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Today's deployments answer `drafting` or `editing`; other values are not a breaking change and do not raise this version. + **Почему протухло:** Деплой отвечает ТРЕМЯ значениями: `runStage` (platform/internal/pgstore/readmodel.go:576) выдаёт `re_pass` для всего пере-прохода, помимо `drafting`/`editing`, и значение доезжает на провод (project.go:53 → v0.go:155 `Stage`). Фраза родилась верной в 0.6.0 (58bae30) и сгнила пере-проходом 0.7.0 (7d45e24). Ломающим это не делает (словарь объявлен открытым), но нормативная проза компилируется в JSDoc генерённых типов — клиент учится по ней неправде. -**Улика — команда:** `sed -n '570,579p' platform/internal/pgstore/readmodel.go; grep -n 'Stage' platform/internal/httpapi/project.go platform/internal/httpapi/v0.go | grep -v test; git log --oneline -S"'re_pass'" -- platform/internal/pgstore/readmodel.go` -**Улика — вывод:** 576: runStage = `(case when r.ceiling_chapters = 0 then 're_pass' + +**Улика — команда:** +``` +sed -n '570,579p' platform/internal/pgstore/readmodel.go; grep -n 'Stage' platform/internal/httpapi/project.go platform/internal/httpapi/v0.go | grep -v test; git log --oneline -S"'re_pass'" -- platform/internal/pgstore/readmodel.go +``` +**Улика — вывод:** +``` +576: runStage = `(case when r.ceiling_chapters = 0 then 're_pass' 577: when ` + editWave + ` and ` + draftBar + ` >= ` + draftWork + ` 578: then 'editing' else 'drafting' end)` (комментарий 574-575: «always `drafting` where drafting is the only pass there is, and `re_pass` for the whole of a re-pass run, which is neither») platform/internal/httpapi/project.go:53: Done: r.Progress.Done, Total: r.Progress.Total, Stage: r.Progress.Stage, platform/internal/httpapi/v0.go:155: Stage string `json:"stage"` 7d45e24 Land the pack that lets a bank correction reach translated text: the run no longer dies on the snapshot guard and a re-pass can be bought +``` **Другие носители того же факта:** docs/architecture/14-api-contract/README.md:883 — «Сегодняшние значения — `drafting` и `editing`.» (§2.20, тот же факт, та же протухшая пара) -**Предложенное действие:** В openapi.yaml:1414 заменить на «Today's deployments answer `drafting`, `editing` or `re_pass`; other values are not a breaking change and do not raise this version.» и синхронно поправить README:883 («Сегодняшние значения — `drafting`, `editing` и `re_pass` (последнее — на всём пере-проходе).»). ⚠ Правка НОРМАТИВНОГО файла: минор поднимать не надо (словарь открыт по объявлению), но лендинг обязан пройти cmp-сверку и гейт `platform/internal/gates/contract_test.go`. + +**Предложенное действие (автора находки):** В openapi.yaml:1414 заменить на «Today's deployments answer `drafting`, `editing` or `re_pass`; other values are not a breaking change and do not raise this version.» и синхронно поправить README:883 («Сегодняшние значения — `drafting`, `editing` и `re_pass` (последнее — на всём пере-проходе).»). ⚠ Правка НОРМАТИВНОГО файла: минор поднимать не надо (словарь открыт по объявлению), но лендинг обязан пройти cmp-сверку и гейт `platform/internal/gates/contract_test.go`. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Текст правки верен, обоснование и процедура — нет; передать принимающему три поправки. (1) НОСИТЕЛЕЙ ТРИ, а не два: к openapi.yaml:1414 и README:883 добавить комментарий кода `platform/internal/pgstore/books.go:776-777` — он утверждает ту же неправду над самим полем `Stage` и относится к зоне ПЛАТФОРМЫ, значит уходит не правкой оркестратора, а пингом в `platform/docs/platform-PROGRESS.md`. (2) «Гейт `platform/internal/gates/contract_test.go`» страховкой этой правки НЕ является: он читает из канона одну строку `info.version` регэкспом и сверяет с `httpapi.ContractVersion` (contract_test.go:34-40) — нормативной прозы не касается вовсе, а `info.version` эта правка не двигает, так что гейт к ней просто безразличен. (3) «cmp-сверка» невыполнима и её нельзя ставить условием лендинга: байт-зеркало, объявленное самим каноном (openapi.yaml:8-9 «`frontend/docs/api-contract/` is a byte-mirror and a divergence is a defect of one of the two»), СЕГОДНЯ разошлось катастрофически — `frontend/docs/api-contract/openapi.yaml` держит version 0.2.3 и 61 189 байт против канонных 0.9.0 и 147 925 байт (`cmp` расходится уже на строке 5), поля `stage` там нет вовсе. Это отдельный, гораздо более крупный дефект, и правку `stage` на него вешать нельзя — но и рационале находки надо поправить: генерённые типы (`frontend/package.json:19` → `openapi-typescript docs/api-contract/openapi.yaml -o src/api/schema.ts`) компилируются из ЗЕРКАЛА, а не из канона, и в `frontend/src/api/schema.ts` слова `drafting`/`editing`/`re_pass` не встречаются ни разу. Значит «клиент учится по этой прозе неправде» сегодня неверно — клиент вообще не видит этой прозы; вред от протухшей фразы чисто канонический (человек-читатель нормы), и заявлять его надо так. + +**Разбор опровергателя:** Опровергнуть не удалось: третье значение и существует, и доезжает до клиента. Проверил самое уязвимое место находки — «а точно ли `ceiling_chapters = 0` это пере-проход, а не какой-то вырожденный прогон»: да, единственное место, где ноль ставится в потолок, — ветка `in.RePass` в runs.go, и она же берёт холд на всю книгу. Проверил обратную гипотезу «автор читал устаревшую копию» — нет: правка `re_pass` и фраза лендились в один день, фраза первой. Класс E назван корректно: README:883 несёт ровно ту же протухшую пару. Но носителей БОЛЬШЕ, чем два, и это меняет объём работы, а не вердикт. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Текст правки верен, обоснование и процедура — нет; передать принимающему три поправки. (1) НОСИТЕЛЕЙ ТРИ, а не два: к openapi.yaml:1414 и README:883 добавить комментарий кода `platform/internal/pgstore/books.go:776-777` — он утверждает ту же неправду над самим полем `Stage` и относится к зоне ПЛАТФОРМЫ, значит уходит не правкой оркестратора, а пингом в `platform/docs/platform-PROGRESS.md`. (2) «Гейт `platform/internal/gates/contract_test.go`» страховкой этой правки НЕ является: он читает из канона одну строку `info.version` регэкспом и сверяет с `httpapi.ContractVersion` (contract_test.go:34-40) — нормативной прозы не касается вовсе, а `info.version` эта правка не двигает, так что гейт к ней просто безразличен. (3) «cmp-сверка» невыполнима и её нельзя ставить условием лендинга: байт-зеркало, объявленное самим каноном (openapi.yaml:8-9 «`frontend/docs/api-contract/` is a byte-mirror and a divergence is a defect of one of the two»), СЕГОДНЯ разошлось катастрофически — `frontend/docs/api-contract/openapi.yaml` держит version 0.2.3 и 61 189 байт против канонных 0.9.0 и 147 925 байт (`cmp` расходится уже на строке 5), поля `stage` там нет вовсе. Это отдельный, гораздо более крупный дефект, и правку `stage` на него вешать нельзя — но и рационале находки надо поправить: генерённые типы (`frontend/package.json:19` → `openapi-typescript docs/api-contract/openapi.yaml -o src/api/schema.ts`) компилируются из ЗЕРКАЛА, а не из канона, и в `frontend/src/api/schema.ts` слова `drafting`/`editing`/`re_pass` не встречаются ни разу. Значит «клиент учится по этой прозе неправде» сегодня неверно — клиент вообще не видит этой прозы; вред от протухшей фразы чисто канонический (человек-читатель нормы), и заявлять его надо так. --- ## N039 · MAJOR · `docs/architecture/15-money-path.md:6` · B-мёртвый-указатель -**Срез:** Денежный путь — docs/architecture/15-money-path.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **Стабильна не ЗОНА, а ЧИСТЫЙ ФАЙЛ.** Номер строки ставится только туда, где `git status <цель>` показывает файл чистым … Ось именно такая (D39.177 §2в), и зонная ось её НЕ заменяет +**Срез:** Денежный путь — docs/architecture/15-money-path.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **Стабильна не ЗОНА, а ЧИСТЫЙ ФАЙЛ.** Номер строки ставится только туда, где `git status <цель>` показывает файл чистым … Ось именно такая (D39.177 §2в), и зонная ось её НЕ заменяет + **Почему протухло:** Норму «чистый ФАЙЛ» ратифицировал D39.179 п.4, который сам говорит: «уточняет D39.177 §2в … Ось «зона» была НЕВЕРНА … Носитель нормы — шапка 15-money-path.md». А D39.177 §2(в), на который шапка ссылается, несёт как раз ЗОННУЮ/«живая сессия» ось. То есть единственный указатель на ратификацию ведёт в ноту, которая эту норму ОПРОВЕРГАЕТ, а ратифицирующая нота D39.179 в доке не названа НИ РАЗУ. Читатель, грепнувший D39.177, увидит шапку противоречащей своему же основанию и либо починит её обратно в зонную ось, либо решит, что норма не ратифицирована. -**Улика — команда:** `grep -n 'Норма формы указателей заострена' docs/architecture/05-decisions-log.md; grep -c 'D39\.179' docs/architecture/15-money-path.md` -**Улика — вывод:** 1673:4. **Норма формы указателей заострена (уточняет D39.177 §2в): стабильна не ЗОНА, а ЧИСТЫЙ ФАЙЛ.** … Ось «зона» была НЕВЕРНА и поймана опровергателем на моей же правке … Носитель нормы — шапка `15-money-path.md`. + +**Улика — команда:** +``` +grep -n 'Норма формы указателей заострена' docs/architecture/05-decisions-log.md; grep -c 'D39\.179' docs/architecture/15-money-path.md +``` +**Улика — вывод:** +``` +1673:4. **Норма формы указателей заострена (уточняет D39.177 §2в): стабильна не ЗОНА, а ЧИСТЫЙ ФАЙЛ.** … Ось «зона» была НЕВЕРНА и поймана опровергателем на моей же правке … Носитель нормы — шапка `15-money-path.md`. 0 Цель ссылки дока (D39.177 §2в): «(в) Якорь по номеру строки в файл, который правит ЖИВАЯ сессия, — заведомо мёртвый … Это касается зонных журналов, канона под минором и кода зоны в работе.» -**Положительный контроль:** grep -o 'D39\.[0-9]*' docs/architecture/15-money-path.md | sort -u → D39.100 D39.110 D39.112 D39.115 D39.122 D39.123 D39.125 D39.131 D39.132 D39.134 D39.154 D39.161 D39.165 D39.167 D39.170 D39.177 D39.84 D39.86 (18 номеров найдено — инструмент в этой области работает, то есть ноль по D39.179 не артефакт грепа) -**Предложенное действие:** Заменить «Ось именно такая (D39.177 §2в)» на «Ось именно такая — ратифицировано D39.179 п.4, который уточняет D39.177 §2в и прямо объявляет зонную ось НЕВЕРНОЙ (там же: «Носитель нормы — шапка 15-money-path.md»)», и внести D39.179 в карту носителей §6. +``` +**Положительный контроль:** +``` +grep -o 'D39\.[0-9]*' docs/architecture/15-money-path.md | sort -u → D39.100 D39.110 D39.112 D39.115 D39.122 D39.123 D39.125 D39.131 D39.132 D39.134 D39.154 D39.161 D39.165 D39.167 D39.170 D39.177 D39.84 D39.86 (18 номеров найдено — инструмент в этой области работает, то есть ноль по D39.179 не артефакт грепа) +``` + +**Предложенное действие (автора находки):** Заменить «Ось именно такая (D39.177 §2в)» на «Ось именно такая — ратифицировано D39.179 п.4, который уточняет D39.177 §2в и прямо объявляет зонную ось НЕВЕРНОЙ (там же: «Носитель нормы — шапка 15-money-path.md»)», и внести D39.179 в карту носителей §6. + **Разрушающее:** False --- ## N040 · MAJOR · `docs/architecture/15-money-path.md:19` · A-дрейф -**Срез:** Денежный путь — docs/architecture/15-money-path.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** На границе юнита сидел РЕПЭЙР-суб-бюджет — ужесточён до пер-вызовного с ценой вызова (строка 135 закрыта D39.131); эскалационный кап хоп НЕ прицениваает — перелёт ≤1 хопа, задокументирован и запинен +**Срез:** Денежный путь — docs/architecture/15-money-path.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> На границе юнита сидел РЕПЭЙР-суб-бюджет — ужесточён до пер-вызовного с ценой вызова (строка 135 закрыта D39.131); эскалационный кап хоп НЕ прицениваает — перелёт ≤1 хопа, задокументирован и запинен + **Почему протухло:** Перечень движковых суб-бюджетов читается как исчерпывающий (репэйр + эскалация) и не знает третьего семейства — книго-широких бюджетов банк-ролей `gates.terminology.budget_usd` и `classify_budget_usd`. Именно оно резало ОПЛАЧЕННУЮ работу на холодном прогоне 31.08 (D39.182 §4: «инцидент холодного прогона был на классификаторе»; комментарий в terminologist.go: «its $0.02 was exhausted … three of four classify batches were never bought»), и именно его поведение ИЗМЕНИЛОСЬ в тот же день: до bb541a8 проход обрывался ПОСЕРЕДИНЕ, после — план режется ЦЕНОЙ ПАРТИИ ДО первого вызова, и срез отчитывается новыми носителями `BatchesDropped`/`ClassifyBatchesDropped`. Денежный вход этого не несёт ни строкой, а калибровка этих цифр ($0.05/$0.02/$0.08 в coldrun-v16) — июльская. ⚠ ПРОВЕРЕНО И ОБРАТНОЕ: утверждение «перелёт ≤1 хопа» ДЕРЖИТСЯ и правки НЕ требует. -**Улика — команда:** `grep -n 'probe+want > plan.budgetUSD' backend/internal/pipeline/terminologist.go; git show bb541a8^:backend/internal/pipeline/terminologist.go | grep -n 'spent+want > plan.budgetUSD'; grep -n 'budget_usd' books/gu-zhenren/coldrun-v16/pipeline.yaml` -**Улика — вывод:** 782: if probe+want > plan.budgetUSD { <- СЕГОДНЯ: план режется ДО первого вызова + +**Улика — команда:** +``` +grep -n 'probe+want > plan.budgetUSD' backend/internal/pipeline/terminologist.go; git show bb541a8^:backend/internal/pipeline/terminologist.go | grep -n 'spent+want > plan.budgetUSD'; grep -n 'budget_usd' books/gu-zhenren/coldrun-v16/pipeline.yaml +``` +**Улика — вывод:** +``` +782: if probe+want > plan.budgetUSD { <- СЕГОДНЯ: план режется ДО первого вызова 725: if want := r.bankCallEstimateUSD(st, msgsPer[i]); !paid && spent+want > plan.budgetUSD { <- ДО bb541a8: обрыв ПОСЕРЕДИНЕ прохода 97: budget_usd: 0.05 101: classify_budget_usd: 0.02 110: budget_usd: 0.08 -**Положительный контроль:** ОТРИЦАТЕЛЬНОЕ: grep -in 'classify_budget|terminolog|банк-рол|gates.terminology' docs/architecture/15-money-path.md → 0 хитов (rc=1). ПОЛОЖИТЕЛЬНЫЙ КОНТРОЛЬ в той же области: grep -on 'суб-бюджет|бюджет' docs/architecture/15-money-path.md → 19:суб-бюджет и 22:бюджет — греп по русской теме бюджетов в этом файле работает, значит ноль не артефакт инструмента. -**Предложенное действие:** Дописать в тот же буллет третий пункт: «Банк-роли несут СОБСТВЕННЫЕ книго-широкие бюджеты — `gates.terminology.budget_usd` и `gates.terminology.classify_budget_usd`; с 31.08 (D39.182) план прохода режется ценой партии ДО первого вызова, а не обрывается посередине, и срез отчитывается полями `BatchesDropped`/`ClassifyBatchesDropped` (`backend/internal/pipeline/terminologist.go`, греп `probe+want > plan.budgetUSD`). Это УСЕЧЕНИЕ, а не отказ: проход идёт внутри банк-стопа, после оплаченной черновой волны. ⚠ Цифры этих суб-бюджетов в книжных конфигах калиброваны под ИЮЛЬСКИЕ цены (D39.179 п.1).» И внести `pipeline/terminologist.go` в карту §6. +``` +**Положительный контроль:** +``` +ОТРИЦАТЕЛЬНОЕ: grep -in 'classify_budget|terminolog|банк-рол|gates.terminology' docs/architecture/15-money-path.md → 0 хитов (rc=1). ПОЛОЖИТЕЛЬНЫЙ КОНТРОЛЬ в той же области: grep -on 'суб-бюджет|бюджет' docs/architecture/15-money-path.md → 19:суб-бюджет и 22:бюджет — греп по русской теме бюджетов в этом файле работает, значит ноль не артефакт инструмента. +``` + +**Предложенное действие (автора находки):** Дописать в тот же буллет третий пункт: «Банк-роли несут СОБСТВЕННЫЕ книго-широкие бюджеты — `gates.terminology.budget_usd` и `gates.terminology.classify_budget_usd`; с 31.08 (D39.182) план прохода режется ценой партии ДО первого вызова, а не обрывается посередине, и срез отчитывается полями `BatchesDropped`/`ClassifyBatchesDropped` (`backend/internal/pipeline/terminologist.go`, греп `probe+want > plan.budgetUSD`). Это УСЕЧЕНИЕ, а не отказ: проход идёт внутри банк-стопа, после оплаченной черновой волны. ⚠ Цифры этих суб-бюджетов в книжных конфигах калиброваны под ИЮЛЬСКИЕ цены (D39.179 п.1).» И внести `pipeline/terminologist.go` в карту §6. + **Разрушающее:** False --- ## N041 · MAJOR · `docs/architecture/15-money-path.md:21` · D-противоречие-ратифицированному -**Срез:** Денежный путь — docs/architecture/15-money-path.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **Остановка по объёму — ЗАВЕРШЕНИЕ (exit 0), не пауза:** словарь кодов выхода не расширялся, различение живёт в отчёте прогона и в логе; отчёт разводит ДОСТАВКУ и ПЕРЕ-ДЕЛКУ. +**Срез:** Денежный путь — docs/architecture/15-money-path.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **Остановка по объёму — ЗАВЕРШЕНИЕ (exit 0), не пауза:** словарь кодов выхода не расширялся, различение живёт в отчёте прогона и в логе; отчёт разводит ДОСТАВКУ и ПЕРЕ-ДЕЛКУ. + **Почему протухло:** «Различение живёт в отчёте прогона и в логе» перестало быть верным 31.08. D39.181 п.2 ратифицировал закон раскрытия: «признак остановки по объёму обязан ехать ЧИСЛОМ» в кадре `finished` шва, и обоснование ноты ровно в том, что прозаическая строка отчёта до потребителя потока не доезжала («said it in ONE PROSE LINE that no consumer of this stream reads»). Поле построено И проведено на обоих исходах. Док остался в редакции ДО закона и посылает читателя-платформу в тот самый канал, который закон объявил недостаточным. -**Улика — команда:** `sed -n '200,206p' backend/internal/runevents/runevents.go; grep -n 'Volume: volumeLedger(res)' backend/internal/pipeline/events.go` -**Улика — вывод:** // run: the line would say `volume` while OutcomeOf(0) said `clean`. … Numbers have no vocabulary to + +**Улика — команда:** +``` +sed -n '200,206p' backend/internal/runevents/runevents.go; grep -n 'Volume: volumeLedger(res)' backend/internal/pipeline/events.go +``` +**Улика — вывод:** +``` + // run: the line would say `volume` while OutcomeOf(0) said `clean`. … Numbers have no vocabulary to // contradict, so the fact travels as numbers, and its PRESENCE is the boolean … // // Ratified with the disclosure law, D39.181 п.2. @@ -831,36 +1568,65 @@ platform/internal/httpapi/v0.go:155: Stage string `json:"stage"` backend/internal/pipeline/events.go:440: e.emit(runevents.TypeFinished, runevents.Finished{Outcome: runevents.OutcomeFlagged, Volume: volumeLedger(res)}) backend/internal/pipeline/events.go:442: e.emit(runevents.TypeFinished, runevents.Finished{Outcome: runevents.OutcomeClean, Volume: volumeLedger(res)}) -**Предложенное действие:** Заменить хвост предложения на: «…словарь кодов выхода не расширялся и нового значения `Finished.Outcome` тоже нет — признак едет ЧИСЛАМИ в кадре `finished` шва: `Finished.Volume` — леджер доставки (max_units/delivered/reworked/flagged/free/left_fresh/left_rework), и его ПРИСУТСТВИЕ и есть булев признак остановки по объёму (ратифицировано D39.181 п.2; `backend/internal/runevents/runevents.go`, греп `Volume *VolumeLedger`). Отчёт прогона и лог — вторая копия того же факта, разводящая ДОСТАВКУ и ПЕРЕ-ДЕЛКУ.» +``` + +**Предложенное действие (автора находки):** Заменить хвост предложения на: «…словарь кодов выхода не расширялся и нового значения `Finished.Outcome` тоже нет — признак едет ЧИСЛАМИ в кадре `finished` шва: `Finished.Volume` — леджер доставки (max_units/delivered/reworked/flagged/free/left_fresh/left_rework), и его ПРИСУТСТВИЕ и есть булев признак остановки по объёму (ратифицировано D39.181 п.2; `backend/internal/runevents/runevents.go`, греп `Volume *VolumeLedger`). Отчёт прогона и лог — вторая копия того же факта, разводящая ДОСТАВКУ и ПЕРЕ-ДЕЛКУ.» + **Разрушающее:** False --- ## N042 · MAJOR · `docs/architecture/16-events-emitter.md:11` · B-мёртвый-указатель -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **Что.** Строка 103 бэклога (`docs/PROGRESS.md:149`), дословно ядро ячейки: +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **Что.** Строка 103 бэклога (`docs/PROGRESS.md:149`), дословно ядро ячейки: + **Почему протухло:** Якорь ведёт не туда И цель больше не существует: `docs/PROGRESS.md:149` — это строка бэклога 42, а строк 103 и 165 в таблице нет вовсе (закрыты D39.131 «строки 103+135+165+102 закрыты»). Тот же дубль в §6 строка 123 (`docs/PROGRESS.md:149`, `:113`) — там `:113` показывает строку 160. -**Улика — команда:** `sed -n '149p;113p' docs/PROGRESS.md | cut -c1-70; for n in 100 103 160 165; do printf "row %s -> %s\n" $n "$(grep -c "^| $n |" docs/PROGRESS.md)"; done` -**Улика — вывод:** | 42 | `prompt_override` — легальный обход гарантии слоя-2, сформул + +**Улика — команда:** +``` +sed -n '149p;113p' docs/PROGRESS.md | cut -c1-70; for n in 100 103 160 165; do printf "row %s -> %s\n" $n "$(grep -c "^| $n |" docs/PROGRESS.md)"; done +``` +**Улика — вывод:** +``` +| 42 | `prompt_override` — легальный обход гарантии слоя-2, сформул | 160 | **Структура глав, Этап 0** ($0, wire-нейтрально): `titleRaw` row 100 -> 0 row 103 -> 0 row 160 -> 1 row 165 -> 0 -**Положительный контроль:** Тот же счётчик по соседним номерам находит живые строки: `for n in $(seq 204 216); do printf "%s:%s " $n $(grep -c "^| $n |" docs/PROGRESS.md); done` → `204:1 205:1 206:0 207:1 208:0 209:1 210:1 211:0 212:0 213:1 214:1 215:0 216:1` — инструмент в этой таблице находит строки, значит 103/165 отсутствуют по факту, а не из-за формы грепа. -**Предложенное действие:** Заменить оба якоря на адресацию БЕЗ номера строки и с признанием закрытия: «Строка 103 бэклога (ЗАКРЫТА D39.131; тело — `docs/archive/PROGRESS-*.md` либо цитата ниже)». Номер строки в живом PROGRESS.md для закрытой ячейки не восстановим — её там больше нет. +``` +**Положительный контроль:** +``` +Тот же счётчик по соседним номерам находит живые строки: `for n in $(seq 204 216); do printf "%s:%s " $n $(grep -c "^| $n |" docs/PROGRESS.md); done` → `204:1 205:1 206:0 207:1 208:0 209:1 210:1 211:0 212:0 213:1 214:1 215:0 216:1` — инструмент в этой таблице находит строки, значит 103/165 отсутствуют по факту, а не из-за формы грепа. +``` + +**Предложенное действие (автора находки):** Заменить оба якоря на адресацию БЕЗ номера строки и с признанием закрытия: «Строка 103 бэклога (ЗАКРЫТА D39.131; тело — `docs/archive/PROGRESS-*.md` либо цитата ниже)». Номер строки в живом PROGRESS.md для закрытой ячейки не восстановим — её там больше нет. + **Разрушающее:** False --- ## N043 · MAJOR · `docs/architecture/16-events-emitter.md:17` · B-мёртвый-указатель -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** Trace id чеканится на каждый вызов в `cmd/tmctl/main.go:67-70` — это сегодняшний кандидат в `engine_run_id` (так и записано платформой, `platform/internal/ingest/events.go:75`). +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> Trace id чеканится на каждый вызов в `cmd/tmctl/main.go:67-70` — это сегодняшний кандидат в `engine_run_id` (так и записано платформой, `platform/internal/ingest/events.go:75`). + **Почему протухло:** Три якоря §1 («что уже есть со стороны движка», раздел БЕЗ ⚠-баннера) уехали: `cmd/tmctl/main.go:67-70` — это константы полосы отказов (`refusalLast = 19`, `exitSourceUnreadable = 11`), а `traceID()` живёт на `:149`/`:162`; `platform/internal/ingest/events.go:75` — поле `Seq`, а не `engine_run_id` (он в `Hello` около `:83`); процитированные строки денег стоят не на `stagerun.go:489-513`, а на `:517`, `:527`, `:529`. -**Улика — команда:** `sed -n '67p;70p;149p;162p' backend/cmd/tmctl/main.go | cut -c1-70; sed -n '75p' platform/internal/ingest/events.go; grep -n "USD ceiling reached\|denied estimate" backend/internal/pipeline/stagerun.go` -**Улика — вывод:** refusalLast = 19 + +**Улика — команда:** +``` +sed -n '67p;70p;149p;162p' backend/cmd/tmctl/main.go | cut -c1-70; sed -n '75p' platform/internal/ingest/events.go; grep -n "USD ceiling reached\|denied estimate" backend/internal/pipeline/stagerun.go +``` +**Улика — вывод:** +``` + refusalLast = 19 exitSourceUnreadable = 11 // the BOOK's source cannot be read // traceID is the identity of this invocation: its log axis, its req func traceID() string { @@ -868,18 +1634,31 @@ func traceID() string { stagerun.go:517: money = fmt.Sprintf(" (committed=$%.6f reserved=$%.6f, denied estimate=$%.6f, ch%d/chunk%d/%s)", stagerun.go:527: return att, &CeilingHalt{... "pipeline: book USD ceiling reached ($%g)%s — %s or stop: %w" ...} stagerun.go:529: return att, &CeilingHalt{... "pipeline: daily USD ceiling reached ($%g)%s: %w" ...} -**Предложенное действие:** Пере-нацелить: `cmd/tmctl/main.go:149-162`=`func traceID() string`, `platform/internal/ingest/events.go:83`=`StreamVersion string`/`Hello`, `backend/internal/pipeline/stagerun.go:517-529`=`USD ceiling reached`. Форма с токеном — чтобы гейт их судил. +``` + +**Предложенное действие (автора находки):** Пере-нацелить: `cmd/tmctl/main.go:149-162`=`func traceID() string`, `platform/internal/ingest/events.go:83`=`StreamVersion string`/`Hello`, `backend/internal/pipeline/stagerun.go:517-529`=`USD ceiling reached`. Форма с токеном — чтобы гейт их судил. + **Разрушающее:** False --- ## N044 · MAJOR · `docs/architecture/16-events-emitter.md:38` · A-дрейф -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** «The vocabulary below is therefore the platform's PROPOSAL, written as code so the engine zone can answer it with a diff» (`events.go:5-6`). Состав (`events.go:37-147`): `StreamVersion = "1.0"` +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> «The vocabulary below is therefore the platform's PROPOSAL, written as code so the engine zone can answer it with a diff» (`events.go:5-6`). Состав (`events.go:37-147`): `StreamVersion = "1.0"` + **Почему протухло:** Дословная цитата, которую док обещает в шапке («дословные цитаты — в кавычках-ёлочках»), в дереве больше не существует: `events.go:5-6` теперь читается «The vocabulary below STARTED AS the platform's PROPOSAL … It DID (D39.131…): the emitter EXISTS». Вместе с ней протух и состав: `StreamVersion` в этом файле = `"1.1"` (не 1.0), `Ceiling` несёт ещё и `Scope`, а `Finished.outcome` — шесть значений (добавлены `ceiling` и `stopped`), не четыре. -**Улика — команда:** `grep -rn "is therefore the platform's PROPOSAL" platform/ backend/; echo '--- positive control ---'; sed -n '5,6p' platform/internal/ingest/events.go; grep -n 'const StreamVersion' platform/internal/ingest/events.go; sed -n '/^type Ceiling struct/,/^}/p' platform/internal/ingest/events.go; grep -n 'Outcome[A-Z][a-z]* *=' backend/internal/runevents/runevents.go` -**Улика — вывод:** (греп по цитате пуст) + +**Улика — команда:** +``` +grep -rn "is therefore the platform's PROPOSAL" platform/ backend/; echo '--- positive control ---'; sed -n '5,6p' platform/internal/ingest/events.go; grep -n 'const StreamVersion' platform/internal/ingest/events.go; sed -n '/^type Ceiling struct/,/^}/p' platform/internal/ingest/events.go; grep -n 'Outcome[A-Z][a-z]* *=' backend/internal/runevents/runevents.go +``` +**Улика — вывод:** +``` +(греп по цитате пуст) --- positive control --- // The vocabulary below started as the platform's PROPOSAL, written as code so the engine zone could // answer it with a diff. It did (D39.131, engine commit 9cfe080): the emitter EXISTS, it writes @@ -889,20 +1668,36 @@ type Ceiling struct { Scope string `json:"scope,omitempty"` } 239: OutcomeClean="clean" 240:OutcomeFlagged 241:OutcomeBankStop 242:OutcomeCeiling 243:OutcomeStopped 244:OutcomeFailed -**Положительный контроль:** Греп по цитате целиком пуст, а по её выжившей половине — находит: `grep -rn "the platform's PROPOSAL" platform/internal/ingest/events.go` → `5:// The vocabulary below started as the platform's PROPOSAL, …`. Инструмент в файле работает; изменилась именно формулировка. +``` +**Положительный контроль:** +``` +Греп по цитате целиком пуст, а по её выжившей половине — находит: `grep -rn "the platform's PROPOSAL" platform/internal/ingest/events.go` → `5:// The vocabulary below started as the platform's PROPOSAL, …`. Инструмент в файле работает; изменилась именно формулировка. +``` **Другие носители того же факта:** docs/PROGRESS.md:205 (строка бэклога 243, пункт arch-10: «`16-events-emitter.md`: словарь и `StreamVersion` разошлись с кодом обеих зон» — находка удержана аудитом 30–31.08 и НЕ применена) -**Предложенное действие:** Заменить цитату на действующую и пометить абзац ⚠-снимком: «состав §2 — снимок предложения платформы на 10.08; действующий словарь — `backend/internal/runevents/runevents.go` (StreamVersion 1.2, шесть outcome, `Ceiling.Scope`) и `platform/internal/ingest/events.go` (1.1)». Это ровно строка бэклога 243 / находка `arch-10`. + +**Предложенное действие (автора находки):** Заменить цитату на действующую и пометить абзац ⚠-снимком: «состав §2 — снимок предложения платформы на 10.08; действующий словарь — `backend/internal/runevents/runevents.go` (StreamVersion 1.2, шесть outcome, `Ceiling.Scope`) и `platform/internal/ingest/events.go` (1.1)». Это ровно строка бэклога 243 / находка `arch-10`. + **Разрушающее:** False --- ## N045 · MAJOR · `docs/architecture/16-events-emitter.md:66` · B-мёртвый-указатель -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **PD-113 (открытый major, закрывается этим паком)** — текущий текст `DEFECT_REGISTER.md:18`: +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **PD-113 (открытый major, закрывается этим паком)** — текущий текст `DEFECT_REGISTER.md:18`: + **Почему протухло:** ВСЕ двенадцать якорей в `platform/docs/DEFECT_REGISTER.md` мертвы — ни один не попадает в свою строку. Фактические номера: PD-60→343, PD-61→344, PD-79→233, PD-95→239, PD-99→251, PD-105→252, PD-107→97, PD-113→345. Хуже: док несёт ДВА взаимно противоречивых набора для одних и тех же PD (§4 против чек-листа §6): PD-79 `:90` против `:174`, PD-99 `:110` против `:92`, PD-107 `:118` против `:56`, PD-61 `:72` против `:46`. Оба набора неверны. -**Улика — команда:** `for p in PD-60 PD-61 PD-79 PD-95 PD-99 PD-105 PD-107 PD-113; do printf "%-7s -> " $p; grep -n "^| $p |" platform/docs/DEFECT_REGISTER.md | cut -d: -f1; done; for l in 18 24 46 56 72 90 92 110 118 174 180 192; do printf "%-4s| " $l; sed -n "${l}p" platform/docs/DEFECT_REGISTER.md | cut -c1-46; done` -**Улика — вывод:** PD-60 -> 343 / PD-61 -> 344 / PD-79 -> 233 / PD-95 -> 239 / PD-99 -> 251 / PD-105 -> 252 / PD-107 -> 97 / PD-113 -> 345 + +**Улика — команда:** +``` +for p in PD-60 PD-61 PD-79 PD-95 PD-99 PD-105 PD-107 PD-113; do printf "%-7s -> " $p; grep -n "^| $p |" platform/docs/DEFECT_REGISTER.md | cut -d: -f1; done; for l in 18 24 46 56 72 90 92 110 118 174 180 192; do printf "%-4s| " $l; sed -n "${l}p" platform/docs/DEFECT_REGISTER.md | cut -c1-46; done +``` +**Улика — вывод:** +``` +PD-60 -> 343 / PD-61 -> 344 / PD-79 -> 233 / PD-95 -> 239 / PD-99 -> 251 / PD-105 -> 252 / PD-107 -> 97 / PD-113 -> 345 18 | > объявленной нормой зоны (введены приёмкой P2 24 | Несущий путь или контрактно видимое поведение 46 | | PD-102 | doc | minor | ... @@ -915,260 +1710,521 @@ type Ceiling struct { 174 | | PD-19 | bug | info | ... 180 | | PD-27 | bug | minor | ... 192 | | PD-39 | bug | info | ... -**Предложенное действие:** Пере-нацелить все двенадцать по СОДЕРЖИМОМУ (номера выше) и свести два набора в один. Лучше — перевести их в форму с токеном (`DEFECT_REGISTER.md:345`=`PD-113`), которую `docs/scripts/counts.py --lint` реально судит: сегодня гейт эти якоря пропускает как «без токена — несверяемы» (708 из 845). +``` + +**Предложенное действие (автора находки):** Пере-нацелить все двенадцать по СОДЕРЖИМОМУ (номера выше) и свести два набора в один. Лучше — перевести их в форму с токеном (`DEFECT_REGISTER.md:345`=`PD-113`), которую `docs/scripts/counts.py --lint` реально судит: сегодня гейт эти якоря пропускает как «без токена — несверяемы» (708 из 845). + **Разрушающее:** False --- ## N046 · MAJOR · `docs/architecture/17-seam-inbound-law.md:56` · B-мёртвый-указатель -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** *Почему жёстко:* движок пере-читает сид ПОСРЕДИ прогона (`mining.go:221`), запись в живой прогон +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> *Почему жёстко:* движок пере-читает сид ПОСРЕДИ прогона (`mining.go:221`), запись в живой прогон + **Почему протухло:** Якорь родился верным и сгнил дрейфом — и уехал на строку, ВЫГЛЯДЯЩУЮ так же. На 23.08 (коммит `4f56160`, тот же, что написал якорь) `mining.go:221` был `if err := r.seedGlossary(ctx); err != nil {` — тот самый пере-сид. Сегодня 221 — это `if !stopping {` ВНУТРИ стоп-ветки (первое из двух одинаковых условий файла), а пере-сид уехал на `:243`. Читатель, проверяющий довод ратифицированного закона п.2, попадает не в тот механизм и не заметит подмены. -**Улика — команда:** `git show 4f56160:backend/internal/pipeline/mining.go | sed -n '221p'; sed -n '221p;243,244p' backend/internal/pipeline/mining.go; grep -n 'seedGlossary(ctx)' backend/internal/pipeline/mining.go` -**Улика — вывод:** if err := r.seedGlossary(ctx); err != nil { <- было на :221 в 4f56160 + +**Улика — команда:** +``` +git show 4f56160:backend/internal/pipeline/mining.go | sed -n '221p'; sed -n '221p;243,244p' backend/internal/pipeline/mining.go; grep -n 'seedGlossary(ctx)' backend/internal/pipeline/mining.go +``` +**Улика — вывод:** +``` + if err := r.seedGlossary(ctx); err != nil { <- было на :221 в 4f56160 if !stopping { <- стало на :221 сегодня if err := r.seedGlossary(ctx); err != nil { return false, fmt.Errorf("pipeline: re-seed the bank with the auto rows: %w", err) 243: if err := r.seedGlossary(ctx); err != nil { -**Предложенное действие:** Заменить на форму с токеном, которую судит гейт: `backend/internal/pipeline/mining.go:243`=`return false, fmt.Errorf("pipeline: re-seed the bank with the auto rows` (токен уникален и переживёт сдвиг лучше номера). +``` + +**Предложенное действие (автора находки):** Заменить на форму с токеном, которую судит гейт: `backend/internal/pipeline/mining.go:243`=`return false, fmt.Errorf("pipeline: re-seed the bank with the auto rows` (токен уникален и переживёт сдвиг лучше номера). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Находка верна, предложенный ЯКОРЬ неверен и провалит гейт. counts.py:anchor_window даёт для `:243` окно ровно [243,243], а предложенный токен `return false, fmt.Errorf("pipeline: re-seed the bank with the auto rows` физически лежит на строке 244 — гейт напечатает «ожидаемого ... в цели НЕТ (цель уехала)». Верная форма: `backend/internal/pipeline/mining.go:243-244`=`re-seed the bank with the auto rows` (диапазон объединяется, токен попадает в окно). Полный путь от корня ОБЯЗАТЕЛЕН: голое `mining.go:221` гейт считает внешней ссылкой и не проверяет, поэтому починка без пути оставит якорь ровно так же беззащитным. Правка одного якоря в ратифицированном доке — обслуживание, не пере-ратификация. + +**Разбор опровергателя:** Воспроизвёл СВОЕЙ командой полностью. 4f56160 — действительно тот коммит, что создал 17-seam-inbound-law.md (110 добавленных строк), и на нём mining.go:221 был `if err := r.seedGlossary(ctx)` — тот самый пере-сид. В рабочем дереве 221 — это `if !stopping {`, ПЕРВОЕ из двух одинаковых условий файла (второе на 233), и оно живёт в ветке стопа --verify-bank, то есть в другом механизме; пере-сид уехал на 243. Сам ФАКТ дока («движок пере-читает сид посреди прогона») остался верным — сгнил только указатель, ровно класс B. Проверил и почему гейт это не поймал: линт даёт 0 проблем, потому что якорь без токена несверяем (708 таких), а форма `mining.go:221` без корневого префикса вообще классифицируется как внешняя ссылка (REPO_TOPS={backend,docs,eval,frontend,platform}, counts.py:384) и не судится никогда. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Находка верна, предложенный ЯКОРЬ неверен и провалит гейт. counts.py:anchor_window даёт для `:243` окно ровно [243,243], а предложенный токен `return false, fmt.Errorf("pipeline: re-seed the bank with the auto rows` физически лежит на строке 244 — гейт напечатает «ожидаемого ... в цели НЕТ (цель уехала)». Верная форма: `backend/internal/pipeline/mining.go:243-244`=`re-seed the bank with the auto rows` (диапазон объединяется, токен попадает в окно). Полный путь от корня ОБЯЗАТЕЛЕН: голое `mining.go:221` гейт считает внешней ссылкой и не проверяет, поэтому починка без пути оставит якорь ровно так же беззащитным. Правка одного якоря в ратифицированном доке — обслуживание, не пере-ратификация. --- ## N047 · MAJOR · `docs/architecture/17-seam-inbound-law.md:71` · C-исполненное-как-открытое -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** загрузчике сида (строка **212**) новое поле теряется МОЛЧА. +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> загрузчике сида (строка **212**) новое поле теряется МОЛЧА. + **Почему протухло:** Загрузчик сида СТРОГИЙ с пака входной двери: `backend/internal/seed/decode.go` называет строку 212 своим именем и ставит `dec.KnownFields(true)` — неизвестный ключ теперь громкая ошибка с номером строки, а не тихая потеря. Строка 212 закрыта и снята с бэклога. Довод п.5 («иначе новое поле теряется МОЛЧА») сегодня опирается на несуществующий дефект. -**Улика — команда:** `grep -n 'backlog row 212' backend/internal/seed/decode.go; grep -n 'KnownFields(true)' backend/internal/seed/decode.go; for n in $(seq 209 216); do printf "%s:%s " $n $(grep -c "^| $n |" docs/PROGRESS.md); done` -**Улика — вывод:** 16:// struct does not have is a typo, and yaml.v3 DROPS one silently, so `gendr:` for `gender:` reads as + +**Улика — команда:** +``` +grep -n 'backlog row 212' backend/internal/seed/decode.go; grep -n 'KnownFields(true)' backend/internal/seed/decode.go; for n in $(seq 209 216); do printf "%s:%s " $n $(grep -c "^| $n |" docs/PROGRESS.md); done +``` +**Улика — вывод:** +``` +16:// struct does not have is a typo, and yaml.v3 DROPS one silently, so `gendr:` for `gender:` reads as 17:// "not set" and the run looks normal (backlog row 212). The same class the strict `book.yaml` decoder 61: dec.KnownFields(true) 209:1 210:1 211:0 212:0 213:1 214:1 215:0 216:1 -**Положительный контроль:** Тот же счётчик по соседям возвращает единицы (209, 210, 213, 214, 216) — таблица читается, и ноль у 212 означает отсутствие строки, а не промах грепа. -**Предложенное действие:** Заменить довод: «…а строгий с D39.158 загрузчик сида (`backend/internal/seed/decode.go:61`=`dec.KnownFields(true)`, строка 212 ЗАКРЫТА) роняет неизвестный ключ ГРОМКО — именно поэтому пиннинг бинаря остаётся единственной защитой от чтения нового файла старым кодом». Правка ратифицированного носителя. +``` +**Положительный контроль:** +``` +Тот же счётчик по соседям возвращает единицы (209, 210, 213, 214, 216) — таблица читается, и ноль у 212 означает отсутствие строки, а не промах грепа. +``` + +**Предложенное действие (автора находки):** Заменить довод: «…а строгий с D39.158 загрузчик сида (`backend/internal/seed/decode.go:61`=`dec.KnownFields(true)`, строка 212 ЗАКРЫТА) роняет неизвестный ключ ГРОМКО — именно поэтому пиннинг бинаря остаётся единственной защитой от чтения нового файла старым кодом». Правка ратифицированного носителя. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Находка верна, атрибуция в предложенной формулировке неточна. Строгость КОДА приехала лендингом d1eb8a9 (пак D39.158), но строку 212 закрыла и сняла с таблицы ЭРРАТА 27.08-д = D39.159 п.7 — на неё и надо ссылаться, иначе следующий читатель пойдёт грепать 212 в теле D39.158 и не найдёт (я проверил: в теле D39.158 номера 212 нет вовсе). Форма якоря: `backend/internal/seed/decode.go:61`=`dec.KnownFields(true)` — корректна и гейт её судит. ⚠ Заодно, при той же правке абзаца: п.5 ссылается ещё и на «строку бэклога 139», а `grep -c '^| 139 |' docs/PROGRESS.md` = 0 при живых соседях — этот номер надо пере-судить тем же заходом, иначе абзац починят наполовину. + +**Разбор опровергателя:** Воспроизвёл и усилил. Загрузчик сида строгий: decodeStrict ставит dec.KnownFields(true) (decode.go:61), а единственный вход в него — seed.DecodeFile, который и зовёт memseed.go:51, то есть боевой путь glossary_seed. Отрицательный вывод «нестрогого загрузчика больше нет» подпёрт положительным контролем в обе стороны: греп yaml.Unmarshal по backend/ в НЕ-тестовом коде пуст (тот же греп находит строку-упоминание в комментарии, значит инструмент работает), а счётчик строк бэклога даёт единицы у соседей 209/210/213/214/216 и ноль у 212. Сверх авторской улики нашёл ПРЯМУЮ ратификацию: шапка журнала решений, эррата 27.08-д (D39.159 п.7), дословно «212 ЗАКРЫТА и снята с таблицы: голый yaml.Unmarshal заменён строгим seed.DecodeFile». Довод п.5 закона опирается на дефект, которого нет. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Находка верна, атрибуция в предложенной формулировке неточна. Строгость КОДА приехала лендингом d1eb8a9 (пак D39.158), но строку 212 закрыла и сняла с таблицы ЭРРАТА 27.08-д = D39.159 п.7 — на неё и надо ссылаться, иначе следующий читатель пойдёт грепать 212 в теле D39.158 и не найдёт (я проверил: в теле D39.158 номера 212 нет вовсе). Форма якоря: `backend/internal/seed/decode.go:61`=`dec.KnownFields(true)` — корректна и гейт её судит. ⚠ Заодно, при той же правке абзаца: п.5 ссылается ещё и на «строку бэклога 139», а `grep -c '^| 139 |' docs/PROGRESS.md` = 0 при живых соседях — этот номер надо пере-судить тем же заходом, иначе абзац починят наполовину. --- ## N048 · MAJOR · `docs/architecture/17-seam-inbound-law.md:97` · C-исполненное-как-открытое -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** | приём правок банка движковым глаголом | строка **199(а)** — развилка владения `book.yaml` растворяется этим | +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> | приём правок банка движковым глаголом | строка **199(а)** — развилка владения `book.yaml` растворяется этим | + **Почему протухло:** Первая строка таблицы «Двери, УЖЕ подразумеваемые живыми строками» описывает как ПРЕДСТОЯЩУЮ ту самую дверь, которую собственная шапка дока объявляет построенной («⚠ ИСПОЛНЕН 27.08: первая дверь по этому закону ПОСТРОЕНА и заленджена — `tmctl bank-apply`»). Три соседние строки таблицы получили зачёркивание и пометку **ПОСТРОЕН**, эта — нет. Строка 199 закрыта (D39.156 п.3 «растворением», D39.158 п.5) и снята с бэклога; D39.166 отдельно фиксирует «Бэклог держал ДВЕ ЗАКРЫТЫЕ строки (199, 215)». -**Улика — команда:** `grep -n 'bank-apply' backend/cmd/tmctl/main.go | head -3; for n in $(seq 195 203); do printf "%s:%s " $n $(grep -c "^| $n |" docs/PROGRESS.md); done; grep -n 'Бэклог держал ДВЕ ЗАКРЫТЫЕ строки' docs/architecture/05-decisions-log.md | cut -c1-90` -**Улика — вывод:** backend/cmd/tmctl/main.go:2:// backup / migrate / seed-lint / bank-apply (dispatchCommands). + +**Улика — команда:** +``` +grep -n 'bank-apply' backend/cmd/tmctl/main.go | head -3; for n in $(seq 195 203); do printf "%s:%s " $n $(grep -c "^| $n |" docs/PROGRESS.md); done; grep -n 'Бэклог держал ДВЕ ЗАКРЫТЫЕ строки' docs/architecture/05-decisions-log.md | cut -c1-90 +``` +**Улика — вывод:** +``` +backend/cmd/tmctl/main.go:2:// backup / migrate / seed-lint / bank-apply (dispatchCommands). backend/cmd/tmctl/main.go:185: "bank-apply", backend/cmd/tmctl/main.go:249: case "bank-apply": 195:1 196:1 197:1 198:1 199:0 200:0 201:1 202:1 203:1 1121:**3. Бэклог держал ДВЕ ЗАКРЫТЫЕ строки** (199, 215) вопреки собственному правилу -**Положительный контроль:** Соседи 195–198 и 201–203 возвращают по единице — таблица бэклога читается грепом, и ноль у 199 не артефакт формы. +``` +**Положительный контроль:** +``` +Соседи 195–198 и 201–203 возвращают по единице — таблица бэклога читается грепом, и ноль у 199 не артефакт формы. +``` **Другие носители того же факта:** platform/docs/platform-PROGRESS.md:2750 (исторический фикс-лист приёмки: «Труба доставки решений банка в движок — единый бэклог, строка **199(а)**» — тоже описывает дверь как предстоящую) -**Предложенное действие:** Привести строку к форме соседей: «~~приём правок банка движковым глаголом~~ **ПОСТРОЕН** 27.08 (`tmctl bank-apply`, D39.158); строка **199(а)** ЗАКРЫТА растворением (D39.156 п.3) и снята с бэклога». Заодно перечитать строку 103 таблицы («приоритет ПАДАЕТ после двери 199(а)») — падение уже произошло. + +**Предложенное действие (автора находки):** Привести строку к форме соседей: «~~приём правок банка движковым глаголом~~ **ПОСТРОЕН** 27.08 (`tmctl bank-apply`, D39.158); строка **199(а)** ЗАКРЫТА растворением (D39.156 п.3) и снята с бэклога». Заодно перечитать строку 103 таблицы («приоритет ПАДАЕТ после двери 199(а)») — падение уже произошло. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верное, но чинить надо ОБЕ строки одним заходом, а не одну: строка 103 той же таблицы («`tmctl init` (форма В) … приоритет ПАДАЕТ после двери 199(а) — та забирает её главный довод») стоит в будущем времени о двери, которая построена, — падение уже произошло, и её надо переписать в прошедшее. Атрибуцию брать точную: дверь — D39.158 (лендинг d1eb8a9), развилка 199(а) растворена D39.156 п.3, сама строка снята с бэклога сверткой D39.166 п.3. ⚠ ВТОРОЙ НОСИТЕЛЬ из находки трогать НЕ НАДО: platform/docs/platform-PROGRESS.md:2750 сидит внутри раздела «### Фикс-лист приёмки — завести строками СВОЕГО бэклога» (:2738) — это ХРОНИКА чужой зоны, запись о том, что было верно на своей дате, а не живой носитель факта; правка чужого журнала задним числом стирает след, а не чинит его. + +**Разбор опровергателя:** Подтверждено четырьмя независимыми уликами. (1) Глагол существует: `bank-apply` в списке команд tmctl (:185) и в диспетчере (:249). (2) Собственная шапка дока на строке 3 объявляет эту дверь ПОСТРОЕННОЙ. (3) Строка 199 снята с бэклога — счётчик даёт ноль при единицах у всех соседей 195-198 и 201-203 (положительный контроль на форме таблицы), а D39.166 п.3 фиксирует «Бэклог держал ДВЕ ЗАКРЫТЫЕ строки (199, 215)»; развилку 199(а) закрыл ещё D39.156 п.3 «РАСТВОРЕНИЕМ». (4) Решающее: я поднял диффом коммит 2f97a3a от 30.08 («two seam doors were still listed as forthcoming») — он в ЭТОЙ ЖЕ таблице пере-написал строки 211 и 213 в форму «~~…~~ ПОСТРОЕН», а строку 199(а) прошёл мимо. Значит это не осознанное решение прежней сессии, а пропуск актуализации: соседи получили зачёркивание, эта строка осталась в прошедшем времени. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верное, но чинить надо ОБЕ строки одним заходом, а не одну: строка 103 той же таблицы («`tmctl init` (форма В) … приоритет ПАДАЕТ после двери 199(а) — та забирает её главный довод») стоит в будущем времени о двери, которая построена, — падение уже произошло, и её надо переписать в прошедшее. Атрибуцию брать точную: дверь — D39.158 (лендинг d1eb8a9), развилка 199(а) растворена D39.156 п.3, сама строка снята с бэклога сверткой D39.166 п.3. ⚠ ВТОРОЙ НОСИТЕЛЬ из находки трогать НЕ НАДО: platform/docs/platform-PROGRESS.md:2750 сидит внутри раздела «### Фикс-лист приёмки — завести строками СВОЕГО бэклога» (:2738) — это ХРОНИКА чужой зоны, запись о том, что было верно на своей дате, а не живой носитель факта; правка чужого журнала задним числом стирает след, а не чинит его. --- ## N049 · MAJOR · `docs/architecture/17-seam-inbound-law.md:100` · C-исполненное-как-открытое -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** ⚠ Родня строки **231**: та просит ту же способность («оценить, ничего не покупая») со стороны ШВА, и решаться они, скорее всего, одним глаголом +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> ⚠ Родня строки **231**: та просит ту же способность («оценить, ничего не покупая») со стороны ШВА, и решаться они, скорее всего, одним глаголом + **Почему протухло:** Строка 231 закрыта паком «деньги» (D39.170, 29.08) и снята с бэклога — сам PROGRESS.md говорит это в лоб. Док цитирует её ДВАЖДЫ как живую: на строке 100 («родня», решится одним глаголом) и на строке 101 («Родня — строка 231 … смету пере-прохода до покупки показать нечем, и это названная цена минора 0.7.0»). Второе утверждение прямо опровергнуто телом D39.170: слепое окно закрыто, `foldMemoryForRead` стал первым ответом читающего пути. -**Улика — команда:** `for n in $(seq 228 234); do printf "%s:%s " $n $(grep -c "^| $n |" docs/PROGRESS.md); done; grep -n 'строк 231 и 236 в таблице БОЛЬШЕ НЕТ' docs/PROGRESS.md; grep -n 'строка бэклога$' -A1 docs/architecture/05-decisions-log.md | sed -n '1,2p'; sed -n '1271,1273p' docs/architecture/05-decisions-log.md | cut -c1-160` -**Улика — вывод:** 228:1 229:1 230:1 231:0 232:1 233:1 234:1 + +**Улика — команда:** +``` +for n in $(seq 228 234); do printf "%s:%s " $n $(grep -c "^| $n |" docs/PROGRESS.md); done; grep -n 'строк 231 и 236 в таблице БОЛЬШЕ НЕТ' docs/PROGRESS.md; grep -n 'строка бэклога$' -A1 docs/architecture/05-decisions-log.md | sed -n '1,2p'; sed -n '1271,1273p' docs/architecture/05-decisions-log.md | cut -c1-160 +``` +**Улика — вывод:** +``` +228:1 229:1 230:1 231:0 232:1 233:1 234:1 docs/PROGRESS.md:285:> (⚠ строк 231 и 236 в таблице БОЛЬШЕ НЕТ — 231 съел пак «деньги», 236 сужена D39.175; прежняя редакция (D39.170) «Слепое окно закрыто. Следствия проведены этим же лендингом: строка бэклога **231** закрывается, комментарий платформы получает строку в её регистр, а проводка полей через шов становится ВОЗМОЖНОЙ» -**Положительный контроль:** Соседние номера 228–230 и 232–234 дают по единице — счётчик строк работает; ноль у 231 совпадает с явной запиской PROGRESS.md:285 и с телом D39.170. +``` +**Положительный контроль:** +``` +Соседние номера 228–230 и 232–234 дают по единице — счётчик строк работает; ноль у 231 совпадает с явной запиской PROGRESS.md:285 и с телом D39.170. +``` **Другие носители того же факта:** docs/PROGRESS.md:285 (несёт ВЕРНУЮ версию факта — «строк 231 и 236 в таблице БОЛЬШЕ НЕТ»; расхождение именно между ним и доком 17) -**Предложенное действие:** Снять обе ссылки на 231: в строке 100 оставить только 124(в); в строке 101 заменить хвост на «⚠ испр.: строка 231 ЗАКРЫТА D39.170 (29.08) — смета пере-прохода доезжает до покупателя; движковая оценка $/глава (строка 166) остаётся открытой САМА ПО СЕБЕ, без родни». Правка ратифицированного носителя. + +**Предложенное действие (автора находки):** Снять обе ссылки на 231: в строке 100 оставить только 124(в); в строке 101 заменить хвост на «⚠ испр.: строка 231 ЗАКРЫТА D39.170 (29.08) — смета пере-прохода доезжает до покупателя; движковая оценка $/глава (строка 166) остаётся открытой САМА ПО СЕБЕ, без родни». Правка ратифицированного носителя. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие в целом верное, но формулировку для строки 101 надо взять точнее, иначе док качнётся в противоположную ложь. «Смету показать нечем» неверно, однако смета до сих пор НЕ доезжает до покупателя через шов: платформа сознательно НЕ берёт `rebill_units`/`rebill_usd`, и это записано её же кодом (`platform/internal/ingest/resync.go:37-39`=`The engine's re-bill projection (rebill_units/rebill_usd) is deliberately NOT taken`, регистровая строка PD-427), а D39.170 отдельно оговаривает, что проводка полей «становится ВОЗМОЖНОЙ — но не выполняется». Верная замена хвоста: «⚠ испр.: строка 231 ЗАКРЫТА D39.170 (29.08) — движок оценивает пере-проход $0-глаголом `status`, свернув банк; открытым остаётся не ГЛАГОЛ, а проводка полей через шов (PD-427 платформы). Движковая оценка $/глава (строка 166) остаётся открытой сама по себе, без родни». Правка ратифицированного носителя ⇒ нотой оркестратора. + +**Разбор опровергателя:** Подтверждено, причём тремя носителями сразу и с рабочим положительным контролем (соседи 228-230 и 232-234 дают по единице, 231 — ноль). Тело D39.170 говорит прямо: «Слепое окно закрыто… строка бэклога 231 закрывается». Сам PROGRESS.md:285 несёт верную версию явной запиской «строк 231 и 236 в таблице БОЛЬШЕ НЕТ — 231 съел пак „деньги“». Сверх авторской улики я проверил ЖИВОЙ КОД чужой зоны: platform/internal/ingest/resync.go:41-47 своим комментарием подтверждает «That blind window is CLOSED» и «`status` will price a re-pass BEFORE it is bought». Значит и ссылка на 231 как на живую родню (:100), и категорическое «смету пере-прохода до покупки показать нечем» (:101) сегодня ложны. При этом соседние номера в тех же двух строках — 124 и 166 — живы (по единице), так что правка адресная, а не снос строк. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие в целом верное, но формулировку для строки 101 надо взять точнее, иначе док качнётся в противоположную ложь. «Смету показать нечем» неверно, однако смета до сих пор НЕ доезжает до покупателя через шов: платформа сознательно НЕ берёт `rebill_units`/`rebill_usd`, и это записано её же кодом (`platform/internal/ingest/resync.go:37-39`=`The engine's re-bill projection (rebill_units/rebill_usd) is deliberately NOT taken`, регистровая строка PD-427), а D39.170 отдельно оговаривает, что проводка полей «становится ВОЗМОЖНОЙ — но не выполняется». Верная замена хвоста: «⚠ испр.: строка 231 ЗАКРЫТА D39.170 (29.08) — движок оценивает пере-проход $0-глаголом `status`, свернув банк; открытым остаётся не ГЛАГОЛ, а проводка полей через шов (PD-427 платформы). Движковая оценка $/глава (строка 166) остаётся открытой сама по себе, без родни». Правка ратифицированного носителя ⇒ нотой оркестратора. --- ## N050 · MAJOR · `docs/glossary.md:11` · C-исполненное-как-открытое -**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** **P12** «долги под ногами» — семь открытых major + снятие обхода `--verify-bank`, ВЫДАН 30.08, на приёмке. +**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> **P12** «долги под ногами» — семь открытых major + снятие обхода `--verify-bank`, ВЫДАН 30.08, на приёмке. + **Почему протухло:** P12 принят и залёнджен 31.08 нотой D39.180 (коммит 0a680a3), обязательство D39.158 закрыто целиком, регистр: major 2 против 7. Глоссарий лежит в онбординг-пути и сообщает свежей сессии, что пак ещё на приёмке. -**Улика — команда:** `git log --oneline -1 0a680a3; git log -1 --format='%h %ad' --date=short -- docs/glossary.md; grep -m1 '| D39.180 |' docs/architecture/05-decisions-index.md | cut -c1-200` -**Улика — вывод:** 0a680a3 Land platform pack P12 with contract minor 0.9.0: the bank-stop bypass is gone, a chapter counts by the pass current for it, and the boundary now judges prose too + +**Улика — команда:** +``` +git log --oneline -1 0a680a3; git log -1 --format='%h %ad' --date=short -- docs/glossary.md; grep -m1 '| D39.180 |' docs/architecture/05-decisions-index.md | cut -c1-200 +``` +**Улика — вывод:** +``` +0a680a3 Land platform pack P12 with contract minor 0.9.0: the bank-stop bypass is gone, a chapter counts by the pass current for it, and the boundary now judges prose too df4b064 2026-08-30 | D39.180 | 31.08 | **Пак платформы P12 «долги под ногами» ПРИНЯТ И ЗАЛЕНДЖЕН** вместе с контрактным минором **0.9.0**. Приёмка — 5 линз, 6 major / 16 minor / 0 блокеров, 8 дофиксов исполнены. Обязательство D39.158 ... закрыто целиком +``` **Другие носители того же факта:** docs/PROGRESS.md:37 — «1. Приёмка пака P12 — платформа, сессия textmachine-main-b5, работает» в блоке «ОТКРЫТО НА ОРКЕСТРАТОРЕ №21 (снимок 31.08)»; блок помечен как снимок, но пункты 1–3 уже исполнены -**Предложенное действие:** Заменить хвост строки на «**P12** «долги под ногами» — семь открытых major + снятие обхода `--verify-bank`, ПРИНЯТ и ЗАЛЕНДЖЕН 31.08, D39.180, вместе с минором 0.9.0». + +**Предложенное действие (автора находки):** Заменить хвост строки на «**P12** «долги под ногами» — семь открытых major + снятие обхода `--verify-bank`, ПРИНЯТ и ЗАЛЕНДЖЕН 31.08, D39.180, вместе с минором 0.9.0». + +**Разбор опровергателя:** Тело D39.180 (не только строка реестра) читается как «ПРИНЯТ И ЗАЛЕНДЖЕН… Обязательство D39.158 (снятие обхода `--verify-bank`) закрыто целиком», коммит лендинга существует и содержит ровно это. Глоссарий последний раз тронут 30.08 (d7ccc50) — за сутки до лендинга, то есть классическое протухание, а не ошибка автора. Побочный клейм находки про «major 2 против 7» тоже подтверждён: CURRENT-STATE считает открытых рядов регистра 97 при major 2. Глоссарий лежит в онбординг-пути, так что свежая сессия получает ложное «на приёмке». + **Разрушающее:** True --- ## N051 · MAJOR · `docs/glossary.md:22` · E-второй-носитель -**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** - **V0–V5** — секции идей владельца в `START_PROMT.MD` (живой бриф; разобраны в `product-requirements.md`). +**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> - **V0–V5** — секции идей владельца в `START_PROMT.MD` (живой бриф; разобраны в `product-requirements.md`). + **Почему протухло:** В START_PROMT.MD есть секция V6 от 18.08.2026 (идея переделки архитектуры пайплайна), разобранная в реестр 23.08 строками ПТ-37..ПТ-43. Парный носитель — шапка product-requirements.md — уже говорит «идеи V0–**V6**». Диапазон в глоссарии не обновили, и он занижает список секций владельца ровно на ту, что предлагает переделку пайплайна. -**Улика — команда:** `grep -nE 'Иде[ия].*V[0-9]' START_PROMT.MD; grep -o 'идеи V0–\*\*V6\*\*' docs/product-requirements.md` -**Улика — вывод:** 52:Идеи которые возникли постфактум V1, 06.07.2025 + +**Улика — команда:** +``` +grep -nE 'Иде[ия].*V[0-9]' START_PROMT.MD; grep -o 'идеи V0–\*\*V6\*\*' docs/product-requirements.md +``` +**Улика — вывод:** +``` +52:Идеи которые возникли постфактум V1, 06.07.2025 66:Идеи V2 не касающиеся самого перевода 72:Идеи V3 пайплайна перевода 84:Идеи проекта V4, 12.07.2026 92:Идея проекта V5, 26.07.2026 97:Идеи проекта V6, 18.08.2026 идеи V0–**V6** +``` **Другие носители того же факта:** docs/product-requirements.md:3 — «Источники: `../START_PROMT.MD` (ЖИВОЙ бриф, идеи V0–**V6**...)» — ВЕРНЫЙ носитель -**Предложенное действие:** Заменить на «**V0–V6** — секции идей владельца в `START_PROMT.MD` (живой бриф; разобраны в `product-requirements.md`; V6 от 18.08 — ПТ-37..ПТ-43)». Впредь диапазон в глоссарии двигать тем же коммитом, что разбирает новую V-секцию. + +**Предложенное действие (автора находки):** Заменить на «**V0–V6** — секции идей владельца в `START_PROMT.MD` (живой бриф; разобраны в `product-requirements.md`; V6 от 18.08 — ПТ-37..ПТ-43)». Впредь диапазон в глоссарии двигать тем же коммитом, что разбирает новую V-секцию. + +**Разбор опровергателя:** Опровергнуть не удалось: секция V6 физически есть в живом брифе (START_PROMT.MD:97, файл 102 строки, то есть V6 — последняя и не обрезана), а парный носитель — шапка docs/product-requirements.md:3 — уже пишет «идеи V0–**V6**» и несёт разбор строками ПТ-37…ПТ-43 под заголовком §80 «идеи V6». Значит две копии одного факта разошлись, и занижена ровно та секция, которая предлагает переделку архитектуры пайплайна. Класс E назван верно. + **Разрушающее:** True --- ## N052 · MAJOR · `docs/product-requirements.md:32` · E-второй-носитель -**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** $0.85/ранобэ (D30.4); ре-проба исполнена и строка 74 закрыта (замер D39.86/91, закрытие D39.95); пере-калибровка цены — при следующем платном прогоне +**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> $0.85/ранобэ (D30.4); ре-проба исполнена и строка 74 закрыта (замер D39.86/91, закрытие D39.95); пере-калибровка цены — при следующем платном прогоне + **Почему протухло:** Число — июльские деньги ДО пере-пина цен DeepSeek 13–15.08. Два других живых носителя того же числа несут ⚠-оговорку про множитель ×4.2–4.4 (STACK.md) и ратифицированный ×4.47 (D39.179 п.1); ПТ-12 её НЕ несёт и стоит со статусом ✅. Плюс триггер сработал: платный прогон 31.08 БЫЛ, и он замерил ≈$1.14 за ДЕСЯТЬ глав — то есть десять глав сегодня дороже, чем весь «ранобэ» по этой ячейке. -**Улика — команда:** `grep -n '0\.85/ранобэ' docs/STACK.md docs/PROGRESS.md docs/product-requirements.md | cut -c1-120; sed -n '69p' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md` -**Улика — вывод:** docs/STACK.md:21:- **Цена:** ~$0.85/ранобэ (⚠ число ДО пере-пина цен 13–15.08: таблица запинена ПИКОМ, счёт шип... + +**Улика — команда:** +``` +grep -n '0\.85/ранобэ' docs/STACK.md docs/PROGRESS.md docs/product-requirements.md | cut -c1-120; sed -n '69p' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md +``` +**Улика — вывод:** +``` +docs/STACK.md:21:- **Цена:** ~$0.85/ранобэ (⚠ число ДО пере-пина цен 13–15.08: таблица запинена ПИКОМ, счёт шип... docs/product-requirements.md:32:| ПТ-12 | Низкий COGS ... | ✅ | ~$0.85/ранобэ (D30.4)... docs/PROGRESS.md:15:> - **Стек ...** ~$0.85/ранобэ (D30.4, пере-калибровка при следующем платном прогоне) Полная цепь на десяти главах в СЕГОДНЯШНИХ ценах стоит **≈$1.14 по собственной оценке движка** +``` **Другие носители того же факта:** docs/STACK.md:21 — то же число С ⚠-оговоркой про пере-пин, docs/PROGRESS.md:15 — то же число, оговорка про пере-пин стоит рядом отдельным блоком, docs/architecture/02-mvp-plan.md:40 — «~$0.7/ранобэ ... после D30.1 ~$0.85 — D30.4», без оговорки, docs/architecture/01-decisions.md:5 — «COGS ~$0.85/ранобэ после флипа D1 (D30.4)», но док под ⚠-баннером «проигрывает D-логу» — легитимно, docs/architecture/05-decisions-index.md:46 — строка реестра D30 -**Предложенное действие:** Дописать в носитель ПТ-12 ту же ⚠-оговорку, что несут STACK.md и PROGRESS: «⚠ число ДО пере-пина цен 13–15.08 (×4.47 на том же заходе — D39.179 п.1); платный прогон 31.08 состоялся и дал ≈$1.14 за 10 глав (COLDRUN_V16_REPORT), пере-калибровка НЕ исполнена». Статус ✅ вынести оркестратору отдельным вопросом — понижать его я права не имею. + +**Предложенное действие (автора находки):** Дописать в носитель ПТ-12 ту же ⚠-оговорку, что несут STACK.md и PROGRESS: «⚠ число ДО пере-пина цен 13–15.08 (×4.47 на том же заходе — D39.179 п.1); платный прогон 31.08 состоялся и дал ≈$1.14 за 10 глав (COLDRUN_V16_REPORT), пере-калибровка НЕ исполнена». Статус ✅ вынести оркестратору отдельным вопросом — понижать его я права не имею. + **Разрушающее:** False --- ## N053 · MAJOR · `docs/product-requirements.md:56` · A-дрейф -**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** ⚠ 30.08 РАТИФИЦИРОВАН минор **0.9.0** ... — он придёт с лендингом пака P12, до него канон 0.8.0 +**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> ⚠ 30.08 РАТИФИЦИРОВАН минор **0.9.0** ... — он придёт с лендингом пака P12, до него канон 0.8.0 + **Почему протухло:** P12 залёнджен 31.08 (0a680a3) вместе с минором, канон в дереве стоит на 0.9.0. Верно: канон 0.9.0 с 31.08 (D39.180), зеркало фронта отстаёт уже на СЕМЬ миноров, а не шесть. Ячейка сама предупреждает «версия здесь живёт хуже всего — правится ПЕРВОЙ»; минор прошёл, ячейка нет. -**Улика — команда:** `grep -n '^ version:' docs/architecture/14-api-contract/openapi.yaml; git log -1 --format='%h %ad' --date=short -- docs/architecture/14-api-contract/openapi.yaml; grep -m1 '| D39.180 |' docs/architecture/05-decisions-index.md | cut -c1-120` -**Улика — вывод:** 5: version: 0.9.0 + +**Улика — команда:** +``` +grep -n '^ version:' docs/architecture/14-api-contract/openapi.yaml; git log -1 --format='%h %ad' --date=short -- docs/architecture/14-api-contract/openapi.yaml; grep -m1 '| D39.180 |' docs/architecture/05-decisions-index.md | cut -c1-120 +``` +**Улика — вывод:** +``` +5: version: 0.9.0 0a680a3 2026-08-31 | D39.180 | 31.08 | **Пак платформы P12 «долги под ногами» ПРИНЯТ И ЗАЛЕНДЖЕН** вместе с контрактным минором **0.9.0** +``` **Другие носители того же факта:** docs/architecture/09-target-architecture.md:12 — «⚠ Канон контракта — 0.8.0, не 0.4.0 и не 0.7.0» (протухло так же), docs/architecture/14-api-contract/README.md:8-9 — «0.2.3 при каноне 0.8.0» (протухло; зона уже запинговала — platform/docs/platform-PROGRESS.md:346), docs/PROGRESS.md:3 — «зеркало отстало на ШЕСТЬ миноров (0.2.3 против канона 0.8.0)» (протухло: миноров семь), docs/README.md:29 — «минор 0.8.0 ПРИНЯТ и заленджен 29.08 (D39.169)» (фраза верна, но читается как последний минор) -**Предложенное действие:** В ПТ-20 заменить хвост на: «⚠ 30.08 ратифицирован и 31.08 ЗАЛЕНДЖЕН вместе с паком P12 минор **0.9.0** (D39.180, `0a680a3`) — канон стоит на 0.9.0; зеркало фронта отстаёт на СЕМЬ миноров». Тем же проходом поправить четыре носителя из other_carriers (они не в моём срезе). + +**Предложенное действие (автора находки):** В ПТ-20 заменить хвост на: «⚠ 30.08 ратифицирован и 31.08 ЗАЛЕНДЖЕН вместе с паком P12 минор **0.9.0** (D39.180, `0a680a3`) — канон стоит на 0.9.0; зеркало фронта отстаёт на СЕМЬ миноров». Тем же проходом поправить четыре носителя из other_carriers (они не в моём срезе). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие в целом верное, но одну его половину надо адресовать точнее: фразы «отстаёт на СЕМЬ миноров» в самой ПТ-20 НЕТ — ячейка говорит только «зеркало ФРОНТА ратифицированно отстаёт на 0.2.3», и это утверждение ОСТАЁТСЯ ВЕРНЫМ (frontend/docs/api-contract/openapi.yaml = 0.2.3). Счёт «шесть миноров» живёт в docs/PROGRESS.md:3 — правку числа шесть→семь вносить ТУДА, а в ПТ-20 менять только хвост про 0.9.0/P12. Ячейку 0.9.0 при этом писать без нового номера строки openapi.yaml (сама ячейка предупреждает, что номер двигается). + +**Разбор опровергателя:** Пытался опровергнуть — не смог. Нормативная спека В ДЕРЕВЕ несёт version: 0.9.0, и внесена она коммитом 0a680a3 от 31.08, чьё сообщение прямо называет лендинг P12 вместе с минором; D39.180 (реестр, строка 244) это ратифицирует. Значит условие «он придёт с лендингом пака P12, до него канон 0.8.0» наступило и обе фразы ячейки про 0.8.0 сегодня ложны. Носители из other_carriers проверены и тоже протухли: docs/architecture/09-target-architecture.md:12 «Канон контракта — 0.8.0»; docs/architecture/14-api-contract/README.md:8-9 «0.2.3 при каноне 0.8.0»; docs/PROGRESS.md:3 «зеркало отстало на ШЕСТЬ миноров (0.2.3 против канона 0.8.0)» — при каноне 0.9.0 миноров действительно семь (0.3–0.9). + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие в целом верное, но одну его половину надо адресовать точнее: фразы «отстаёт на СЕМЬ миноров» в самой ПТ-20 НЕТ — ячейка говорит только «зеркало ФРОНТА ратифицированно отстаёт на 0.2.3», и это утверждение ОСТАЁТСЯ ВЕРНЫМ (frontend/docs/api-contract/openapi.yaml = 0.2.3). Счёт «шесть миноров» живёт в docs/PROGRESS.md:3 — правку числа шесть→семь вносить ТУДА, а в ПТ-20 менять только хвост про 0.9.0/P12. Ячейку 0.9.0 при этом писать без нового номера строки openapi.yaml (сама ячейка предупреждает, что номер двигается). --- ## N054 · MAJOR · `docs/product-requirements.md:63` · D-противоречие-ратифицированному -**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** остаток: страница лимитов и оповещение на фронте (после P7) +**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> остаток: страница лимитов и оповещение на фронте (после P7) + **Почему протухло:** D39.147 (слово владельца 17.08) АМЕНДИРОВАЛ условие разморозки: «лендинг P7 зону БОЛЬШЕ НЕ размораживает», S5-промт не выдаётся, разморозка — отдельным словом владельца. P7 залёнджен 20.08 (D39.153), а остаток ПТ-35 по-прежнему адресован «после P7» — гейт, который прошёл, ничего не открыв. Тот же коммит D39.147 п.3 синхронизировал ПТ-20 и ПТ-35 не тронул. -**Улика — команда:** `sed -n '500p' docs/architecture/05-decisions-log.md | grep -o 'лендинг P7 зону БОЛЬШЕ НЕ размораживает.\{0,90\}'; grep -m1 '| D39.153 |' docs/architecture/05-decisions-index.md | cut -c1-90` -**Улика — вывод:** лендинг P7 зону БОЛЬШЕ НЕ размораживает**, S5-промт не выдаётся; разморозка — отдельным словом владельца по достижении сквозного пути + +**Улика — команда:** +``` +sed -n '500p' docs/architecture/05-decisions-log.md | grep -o 'лендинг P7 зону БОЛЬШЕ НЕ размораживает.\{0,90\}'; grep -m1 '| D39.153 |' docs/architecture/05-decisions-index.md | cut -c1-90 +``` +**Улика — вывод:** +``` +лендинг P7 зону БОЛЬШЕ НЕ размораживает**, S5-промт не выдаётся; разморозка — отдельным словом владельца по достижении сквозного пути | D39.153 | 20.08 | Приёмка и лендинг P7 платформы (пять актов, читающая поверхность): **ПРИН +``` **Другие носители того же факта:** docs/glossary.md:11 — верный носитель: «зона ЗАМОРОЖЕНА, и лендинг P7 её НЕ разморозил — D39.147», docs/product-requirements.md:56 (ПТ-20) — верный носитель того же факта в том же доке -**Предложенное действие:** Заменить «(после P7)» на «(после РАЗМОРОЗКИ фронта — D39.147: лендинг P7 её не размораживает, разморозка отдельным словом владельца)». + +**Предложенное действие (автора находки):** Заменить «(после P7)» на «(после РАЗМОРОЗКИ фронта — D39.147: лендинг P7 её не размораживает, разморозка отдельным словом владельца)». + +**Разбор опровергателя:** Тело D39.147 п.1 прочитано целиком и говорит буквально то, что заявлено: условие разморозки амендировано, лендинг P7 зону больше не размораживает. P7 залёнджен 20.08 (D39.153), то есть названный в ПТ-35 гейт прошёл и ничего не открыл — остаток по-прежнему недостижим, но адресован пройденной вехе. Дополнительно я проверил происхождение: «после P7» впервые появилось 15.08 (33b4aa6), за двое суток ДО D39.147, — то есть якорь родился верным при D39.136 п.2 и сгнил амендментом. Парный носитель в том же доке (ПТ-20) и docs/glossary.md:11 факт про D39.147 несут правильно, так что док спорит сам с собой. + **Разрушающее:** True --- ## N055 · MAJOR · `frontend/README.md:7` · A-дрейф -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Продуктовых экранов +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Продуктовых экранов ещё нет — они на S4–S7 (`docs/BACKLOG.md`, Ф-1). + **Почему протухло:** S4 исполнен 14.08 и ПРИНЯТ 15.08 (D39.135, `267aa35`): библиотека, интейк, форма запуска прогона, настройки, читалка — построенные экраны. Перечисление пройденных этапов в шапке обрывается на S3.7. -**Улика — команда:** `ls frontend/src/showcase/ | grep -E 'Library|AddBook|RunStart|Settings|Reader'; grep -n '15.08 — приёмка S4' frontend/docs/frontend-PROGRESS.md` -**Улика — вывод:** AddBook.module.css / AddBook.tsx / Library.module.css / Library.tsx / Reader.module.css / Reader.tsx / RunStart.module.css / RunStart.tsx / Settings.module.css / Settings.tsx + +**Улика — команда:** +``` +ls frontend/src/showcase/ | grep -E 'Library|AddBook|RunStart|Settings|Reader'; grep -n '15.08 — приёмка S4' frontend/docs/frontend-PROGRESS.md +``` +**Улика — вывод:** +``` +AddBook.module.css / AddBook.tsx / Library.module.css / Library.tsx / Reader.module.css / Reader.tsx / RunStart.module.css / RunStart.tsx / Settings.module.css / Settings.tsx 465:### 15.08 — приёмка S4 оркестратором №17: ПРИНЯТО и залендено (`267aa35`); контракт 0.2.3 в каноне (D39.135) +``` **Другие носители того же факта:** frontend/docs/BACKLOG.md:7 (Ф-1, столбец «Вес»: «по очереди сессий (S0/S1 — текущая)») -**Предложенное действие:** В шапке дописать «…и S4 (библиотека, загрузка книги, разбор, запуск прогона; принят D39.135 15.08)»; фразу «Продуктовых экранов ещё нет» заменить на «Построены экраны S4; S5–S7 — впереди, зона ЗАМОРОЖЕНА (D39.136 п.2 + D39.147)». + +**Предложенное действие (автора находки):** В шапке дописать «…и S4 (библиотека, загрузка книги, разбор, запуск прогона; принят D39.135 15.08)»; фразу «Продуктовых экранов ещё нет» заменить на «Построены экраны S4; S5–S7 — впереди, зона ЗАМОРОЖЕНА (D39.136 п.2 + D39.147)». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Формулировка предложенной замены верна, но названный «второй носитель» — ОШИБКА: строка Ф-1 стоит на `frontend/docs/BACKLOG.md:7`, а не на 7 в смысле находки… проверено: Ф-1 действительно на строке 7, и её столбец «Вес» гласит «по очереди сессий (S0/S1 — текущая)» — это тоже протухло (текущая не S0/S1), так что носитель назван верно, но его правка — отдельный пункт, а не «тот же факт». ⚠ И тут, и в Ф-1 исполнитель — фронт-сессия: `frontend/README.md` в зоне фронта, зона заморожена (D39.147). Оркестратору доступна только запись в `frontend/docs/frontend-PROGRESS.md` («шапка README зоны отстала на S4 — поправить первым касанием»). + +**Разбор опровергателя:** Опровергнуть не удалось: экраны не «фикстуры», а продуктовые компоненты на слое данных (`useQuery(libraryQuery())`, `bookQuery`, `chaptersQuery`, `bankQuery` внутри `Showcase.tsx`), S4 залендена коммитом `267aa35` и принята D39.135 15.08. Перечисление пройденных этапов в шапке обрывается на S3.7 при том, что S4 в том же дереве. Утверждение «продуктовых экранов ещё нет» ложно. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Формулировка предложенной замены верна, но названный «второй носитель» — ОШИБКА: строка Ф-1 стоит на `frontend/docs/BACKLOG.md:7`, а не на 7 в смысле находки… проверено: Ф-1 действительно на строке 7, и её столбец «Вес» гласит «по очереди сессий (S0/S1 — текущая)» — это тоже протухло (текущая не S0/S1), так что носитель назван верно, но его правка — отдельный пункт, а не «тот же факт». ⚠ И тут, и в Ф-1 исполнитель — фронт-сессия: `frontend/README.md` в зоне фронта, зона заморожена (D39.147). Оркестратору доступна только запись в `frontend/docs/frontend-PROGRESS.md` («шапка README зоны отстала на S4 — поправить первым касанием»). --- ## N056 · MAJOR · `frontend/README.md:66` · D-противоречие-ратифицированному -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** эта копия — байт-зеркало канона `docs/architecture/14-api-contract/`; правит её +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> эта копия — байт-зеркало канона `docs/architecture/14-api-contract/`; правит её фронт-сессия, переносит в канон диффом оркестратор при лендинге (прецедент Ф-47/0.2.2). + **Почему протухло:** Зеркало НЕ байт-равно: 0.2.3/16 операций против канона 0.9.0/20 операций, разные md5. И правило «зеркало = байт-копия» ратифицированно ПРИОСТАНОВЛЕНО на время фриза (D39.142 п.5) — нормативен только канон. README — точка входа зоны и учит обратному. -**Улика — команда:** `md5sum frontend/docs/api-contract/openapi.yaml docs/architecture/14-api-contract/openapi.yaml; grep -m1 'version:' в обоих; awk-счёт операций` -**Улика — вывод:** 4e4ab5e6d313bd56d395032a718ab18a frontend/docs/api-contract/openapi.yaml + +**Улика — команда:** +``` +md5sum frontend/docs/api-contract/openapi.yaml docs/architecture/14-api-contract/openapi.yaml; grep -m1 'version:' в обоих; awk-счёт операций +``` +**Улика — вывод:** +``` +4e4ab5e6d313bd56d395032a718ab18a frontend/docs/api-contract/openapi.yaml 34bb4b6d0496f4377633597cd12bc73a docs/architecture/14-api-contract/openapi.yaml зеркало: version: 0.2.3, 15 путей, 16 операций канон: version: 0.9.0, 17 путей, 20 операций (добавлены /capabilities, /books/{bookId}/events, /runs/{runId}; bank/decisions → bank/corrections; снят /runs/{runId}/events) +``` **Другие носители того же факта:** frontend/docs/api-contract/openapi.yaml (info.description: «byte-mirror of it, and a divergence is a defect»), frontend/docs/frontend-PROGRESS.md:47-48, frontend/docs/frontend-PROGRESS.md:9 (пинг №20 п.4 — называет 0.8.0, канон уже 0.9.0) -**Предложенное действие:** Дописать в этот буллет: «⚠ С фриза зеркало отстаёт РАТИФИЦИРОВАННО (D39.142 п.5): нормативен только канон, зеркало на 0.2.3. Синк байт-в-байт (`cmp`) + перегенерация типов — первое касание зоны при разморозке». + +**Предложенное действие (автора находки):** Дописать в этот буллет: «⚠ С фриза зеркало отстаёт РАТИФИЦИРОВАННО (D39.142 п.5): нормативен только канон, зеркало на 0.2.3. Синк байт-в-байт (`cmp`) + перегенерация типов — первое касание зоны при разморозке». + **Разрушающее:** False --- ## N057 · MAJOR · `frontend/docs/BACKLOG.md:50` · D-противоречие-ратифицированному -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Модель владельца и движка совпадают: подписывается банк ЦЕЛИКОМ, стоп снимается только когда решён каждый термин (`pipeline/mining.go:201`), в контракте это `POST /runs/{id}/resume` с `409` при неполном наборе. +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Модель владельца и движка совпадают: подписывается банк ЦЕЛИКОМ, стоп снимается только когда решён каждый термин (`pipeline/mining.go:201`), в контракте это `POST /runs/{id}/resume` с `409` при неполном наборе. + **Почему протухло:** D39.144 (16.08) снял гейт полноты. Канон: `awaiting_bank` с ЛЮБЫМ состоянием решений → 202. Якорь `mining.go:201` тоже уехал: там сегодня стоит прямая ссылка на D39.144 и слова владельца, что марш по пер-термным решениям — НЕ продукт. -**Улика — команда:** `sed -n "$(grep -n '/runs/{runId}/resume:' docs/architecture/14-api-contract/openapi.yaml | cut -d: -f1),+20p" docs/architecture/14-api-contract/openapi.yaml; sed -n '202,206p' backend/internal/pipeline/mining.go` -**Улика — вывод:** | the run stands in | this call answers | + +**Улика — команда:** +``` +sed -n "$(grep -n '/runs/{runId}/resume:' docs/architecture/14-api-contract/openapi.yaml | cut -d: -f1),+20p" docs/architecture/14-api-contract/openapi.yaml; sed -n '202,206p' backend/internal/pipeline/mining.go +``` +**Улика — вывод:** +``` +| the run stands in | this call answers | | `awaiting_bank` — with ANY state of decisions | continues it — `202` | | `paused` — ANY reason, `null` included | `409` `run_not_resumable`, `cause.code: ceiling_reached` | --- mining.go:202: // THE FLAG (D39.42 п.5 · D39.144, owner's words 26.08: «стоп на банке памяти — это просто флажок»). mining.go:205-207: «only then: a flag that re-stopped on everything still undecided would turn «остановиться один раз» into a march over per-term decisions the product does not have» +``` **Другие носители того же факта:** frontend/docs/BACKLOG.md:16 (Ф-14: «стоп снимается ТОЛЬКО полным набором (mining.go:200-202)»), frontend/docs/frontend-PROGRESS.md:508 (пинг №17 — модель названа, строки не поправлены) -**Предложенное действие:** Переписать хвост Ф-44 по D39.144: «подписывается ВЕСЬ банк одним ОК (= `resume`); гейт полноты снят, `pending_decisions`/`complete` информационные; пер-термная масс-подпись НЕ строится». Якорь `mining.go:201` заменить на `mining.go:202-207`. + +**Предложенное действие (автора находки):** Переписать хвост Ф-44 по D39.144: «подписывается ВЕСЬ банк одним ОК (= `resume`); гейт полноты снят, `pending_decisions`/`complete` информационные; пер-термная масс-подпись НЕ строится». Якорь `mining.go:201` заменить на `mining.go:202-207`. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Содержание правки верно; уточнить якорь: тело флага занимает `mining.go:202-207` (строка 202 — заголовок коммента с ссылкой на D39.144), а не «201». ⚠ Не переписывать Ф-44 в «строка закрыта»: пинг №17 (`frontend-PROGRESS.md:508`) ратифицирует не закрытие, а ПЕРЕЧТЕНИЕ — «строки Ф-44/Ф-45/Ф-46 при разморозке читать через эту модель: действие подписи = один ОК + опциональные точечные правки». Хвост «экран действия строить при разморозке» остаётся живым. Исполнитель — фронт-сессия (зона заморожена). + +**Разбор опровергателя:** Опровергнуть не удалось ни по одной из трёх половин. (1) Гейт полноты снят D39.144 — это прямое слово владельца, ратифицированное. (2) Канон 0.9.0 в таблице resume отвечает `202` на `awaiting_bank` «with ANY state of decisions» — `409` там ТОЛЬКО про потолок/кредит/нестоповые состояния. (3) Якорь `mining.go:201` уехал буквально: строка 201 пуста, тело стоит на 202-207 и цитирует D39.144 против той самой модели, которую утверждает Ф-44. Второй носитель Ф-14 (`BACKLOG.md:16`, «стоп снимается ТОЛЬКО полным набором (mining.go:200-202)») назван верно — я его прочитал. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Содержание правки верно; уточнить якорь: тело флага занимает `mining.go:202-207` (строка 202 — заголовок коммента с ссылкой на D39.144), а не «201». ⚠ Не переписывать Ф-44 в «строка закрыта»: пинг №17 (`frontend-PROGRESS.md:508`) ратифицирует не закрытие, а ПЕРЕЧТЕНИЕ — «строки Ф-44/Ф-45/Ф-46 при разморозке читать через эту модель: действие подписи = один ОК + опциональные точечные правки». Хвост «экран действия строить при разморозке» остаётся живым. Исполнитель — фронт-сессия (зона заморожена). --- ## N058 · MAJOR · `frontend/docs/BACKLOG.md:51` · B-мёртвый-указатель -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Контракт это выражает (`BankDecision`: `promote` с непустым `dst` либо `decline`, частичное накопление на сервере), то есть редактирование строки — это отправка решения, а не PATCH. +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Контракт это выражает (`BankDecision`: `promote` с непустым `dst` либо `decline`, частичное накопление на сервере), то есть редактирование строки — это отправка решения, а не PATCH. + **Почему протухло:** В каноне 0.9.0 нет ни схемы `BankDecision`, ни значения `promote`, ни пути `POST …/bank/decisions`, ни «частичного накопления»: канон 0.5.0 снёс их и завёл `POST /books/{bookId}/bank/corrections` со словарём `approve`/`decline`, применяемый ВСЁ-ИЛИ-НИЧЕГО. -**Улика — команда:** `grep -c 'promote\|BankDecision' docs/architecture/14-api-contract/openapi.yaml; grep -n 'bank/corrections' -A 12 docs/architecture/14-api-contract/openapi.yaml | head -14; grep -n 'снесены путь' docs/architecture/14-api-contract/README.md` -**Улика — вывод:** 0 (промоут/BankDecision в каноне отсутствуют) + +**Улика — команда:** +``` +grep -c 'promote\|BankDecision' docs/architecture/14-api-contract/openapi.yaml; grep -n 'bank/corrections' -A 12 docs/architecture/14-api-contract/openapi.yaml | head -14; grep -n 'снесены путь' docs/architecture/14-api-contract/README.md +``` +**Улика — вывод:** +``` +0 (промоут/BankDecision в каноне отсутствуют) /books/{bookId}/bank/corrections: post: operationId: applyBankCorrections «The per-term half of the bank model: signing is ONE act over the whole bank (`resumeRun`), and what exists per term is a CORRECTION — `approve` a rendering, or `decline` a surface … The whole document is applied as one act, **all or nothing**» README.md:75: ⚠ 0.5.0 ломающий по построению: снесены путь `POST …/bank/decisions` и схема `BankDecision` -**Положительный контроль:** grep -c 'decline' docs/architecture/14-api-contract/openapi.yaml → 17 (инструмент в этой области находит словарь решений; ноль по `promote`/`BankDecision` — не опечатка грепа) +``` +**Положительный контроль:** +``` +grep -c 'decline' docs/architecture/14-api-contract/openapi.yaml → 17 (инструмент в этой области находит словарь решений; ноль по `promote`/`BankDecision` — не опечатка грепа) +``` **Другие носители того же факта:** frontend/docs/api-contract/openapi.yaml:1149-1169 (зеркало 0.2.3, отстаёт ратифицированно), frontend/src/api/schema.ts:853-864, frontend/src/mock/handlers.ts:195 -**Предложенное действие:** Заменить в Ф-45 «`BankDecision`: `promote`/`decline`, частичное накопление» на «`POST /books/{bookId}/bank/corrections`, `BankCorrection`: `approve`/`decline`, всё-или-ничего, превью перед сохранением, эффект — на СЛЕДУЮЩЕМ прогоне». + +**Предложенное действие (автора находки):** Заменить в Ф-45 «`BankDecision`: `promote`/`decline`, частичное накопление» на «`POST /books/{bookId}/bank/corrections`, `BankCorrection`: `approve`/`decline`, всё-или-ничего, превью перед сохранением, эффект — на СЛЕДУЮЩЕМ прогоне». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Текст замены верен, но ОСТАЛЬНЫЕ НОСИТЕЛИ ПРАВИТЬ НЕЛЬЗЯ, и это важнее самой правки: (1) `frontend/docs/api-contract/openapi.yaml` — не «отстающий док», а РАТИФИЦИРОВАННО замороженное зеркало (D39.142 п.5; `14-api-contract/README.md:102`), синк 0.2.3→0.9.0 — первое касание зоны при разморозке, одним актом с перегенерацией типов; (2) `frontend/src/api/schema.ts` — ГЕНЕРИРУЕМЫЙ файл (`openapi-typescript` из зеркала, `FRONTEND_PLAN.md:125`): править руками = завести четвёртую точку истины, он переедет сам вместе с зеркалом; (3) `frontend/src/mock/handlers.ts` — код замороженной зоны. Единственное, что стоит сделать сейчас: строка в `frontend/docs/frontend-PROGRESS.md`, что Ф-45 читается через `bank/corrections` (`approve`/`decline`, всё-или-ничего, превью). + +**Разбор опровергателя:** Положительный контроль автора воспроизведён и усилен: `decline` в каноне 17 вхождений, `bank/corrections` — живой путь со схемой `BankCorrection`, а `promote`/`BankDecision`/`bank/decisions` — ровно ноль. Это не опечатка грепа. Зеркало 0.2.3 в том же дереве несёт `/books/{bookId}/bank/decisions` — значит утверждение Ф-45 родилось верным и убито ломающим 0.5.0 (README:75). Класс B назван правильно. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Текст замены верен, но ОСТАЛЬНЫЕ НОСИТЕЛИ ПРАВИТЬ НЕЛЬЗЯ, и это важнее самой правки: (1) `frontend/docs/api-contract/openapi.yaml` — не «отстающий док», а РАТИФИЦИРОВАННО замороженное зеркало (D39.142 п.5; `14-api-contract/README.md:102`), синк 0.2.3→0.9.0 — первое касание зоны при разморозке, одним актом с перегенерацией типов; (2) `frontend/src/api/schema.ts` — ГЕНЕРИРУЕМЫЙ файл (`openapi-typescript` из зеркала, `FRONTEND_PLAN.md:125`): править руками = завести четвёртую точку истины, он переедет сам вместе с зеркалом; (3) `frontend/src/mock/handlers.ts` — код замороженной зоны. Единственное, что стоит сделать сейчас: строка в `frontend/docs/frontend-PROGRESS.md`, что Ф-45 читается через `bank/corrections` (`approve`/`decline`, всё-или-ничего, превью). --- ## N059 · MAJOR · `frontend/docs/BACKLOG.md:62` · C-исполненное-как-открытое -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Живой канал принадлежит ПРОГОНУ (`GET /runs/{runId}/events`), а книга в `uploading`/`parsing` прогона не имеет вовсе: значит «разбор закончился» не приходит ничем. +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Живой канал принадлежит ПРОГОНУ (`GET /runs/{runId}/events`), а книга в `uploading`/`parsing` прогона не имеет вовсе: значит «разбор закончился» не приходит ничем. + **Почему протухло:** Канон 0.9.0 перевесил поток на КНИГУ: `GET /books/{bookId}/events`, и его описание прямо называет причину — «a book is received and cut into chapters before any run exists, and those minutes are what a user watches». Пути `/runs/{runId}/events` в каноне больше нет. -**Улика — команда:** `grep -n '^ /runs/{runId}/events:\|^ /books/{bookId}/events:' docs/architecture/14-api-contract/openapi.yaml; sed -n '619,634p' docs/architecture/14-api-contract/openapi.yaml` -**Улика — вывод:** 619: /books/{bookId}/events: (пути /runs/{runId}/events в каноне НЕТ) + +**Улика — команда:** +``` +grep -n '^ /runs/{runId}/events:\|^ /books/{bookId}/events:' docs/architecture/14-api-contract/openapi.yaml; sed -n '619,634p' docs/architecture/14-api-contract/openapi.yaml +``` +**Улика — вывод:** +``` +619: /books/{bookId}/events: (пути /runs/{runId}/events в каноне НЕТ) 623: get: 625: operationId: streamBookEvents 626: summary: Live events of a book (SSE). 628: `text/event-stream`, on the BOOK and not on a run: a book is received and cut into chapters 629: before any run exists, and those minutes are what a user watches. -**Положительный контроль:** grep -n '^ /runs/{runId}' канона → /runs/{runId}: · /runs/{runId}/stop: · /runs/{runId}/resume: (греп по путям /runs/ в этом файле находит три штуки — отсутствие /runs/{runId}/events не артефакт шаблона) +``` +**Положительный контроль:** +``` +grep -n '^ /runs/{runId}' канона → /runs/{runId}: · /runs/{runId}/stop: · /runs/{runId}/resume: (греп по путям /runs/ в этом файле находит три штуки — отсутствие /runs/{runId}/events не артефакт шаблона) +``` **Другие носители того же факта:** frontend/docs/frontend-PROGRESS.md:495 (пинг №17 п.1) -**Предложенное действие:** Пере-диспозиционировать Ф-56: опрос `intakePollMs = 3000` в `src/api/queries.ts` + `useIntakeEnd.ts` СНОСЯТСЯ при разморозке, подписка переезжает на `GET /books/{bookId}/events`. + +**Предложенное действие (автора находки):** Пере-диспозиционировать Ф-56: опрос `intakePollMs = 3000` в `src/api/queries.ts` + `useIntakeEnd.ts` СНОСЯТСЯ при разморозке, подписка переезжает на `GET /books/{bookId}/events`. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно, но формулировка «опрос СНОСИТСЯ» слишком категорична для строки бэклога: `intakePollMs` обслуживает ДВА чтения (`queries.ts:171` библиотека и `:178` карточка книги), и снос — не одна строка, а переезд обоих на подписку плюс `useIntakeEnd.ts`. Записывать так: «Ф-56 ОТВЕЧЕНА каноном (Б-6а, `GET /books/{bookId}/events`); опрос остаётся легитимным лечением до синка зеркала; при разморозке — переезд обоих чтений (`queries.ts:171,178`) и `useIntakeEnd.ts` на поток». Исполнитель — фронт-сессия. + +**Разбор опровергателя:** Отрицательный вывод предъявлен с положительным контролем и он воспроизводится: в каноне три пути `/runs/{runId}*`, и `events` среди них нет; вместо него `/books/{bookId}/events`, чьё описание называет ровно ту причину, которую строка Ф-56 держит как нерешённую («a book is received and cut into chapters before any run exists»). В зеркале 0.2.3 путь на прогоне ещё стоит — строка родилась верной. Диспозиция «оркестратор → контракт» исполнена. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, но формулировка «опрос СНОСИТСЯ» слишком категорична для строки бэклога: `intakePollMs` обслуживает ДВА чтения (`queries.ts:171` библиотека и `:178` карточка книги), и снос — не одна строка, а переезд обоих на подписку плюс `useIntakeEnd.ts`. Записывать так: «Ф-56 ОТВЕЧЕНА каноном (Б-6а, `GET /books/{bookId}/events`); опрос остаётся легитимным лечением до синка зеркала; при разморозке — переезд обоих чтений (`queries.ts:171,178`) и `useIntakeEnd.ts` на поток». Исполнитель — фронт-сессия. --- ## N060 · MAJOR · `frontend/docs/BACKLOG.md:63` · C-исполненное-как-открытое -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Кода на замену немного (одна ручка + чтение вместо константы), но ручки в контракте нет: `POST /books` принимает любой `LangCode`, а отказ по неподдерживаемой паре не выражен ни одним классом ответа. +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Кода на замену немного (одна ручка + чтение вместо константы), но ручки в контракте нет: `POST /books` принимает любой `LangCode`, а отказ по неподдерживаемой паре не выражен ни одним классом ответа. + **Почему протухло:** Ручка построена: канон 0.9.0 даёт `GET /capabilities` с обязательным `language_pairs` («Every pair the deployment knows about, unavailable ones included»). Ф-57 держит как открытую развилку то, что контракт уже решил. -**Улика — команда:** `awk '/^ Capabilities:/{f=1} f{print NR": "$0; c++} c>26{exit}' docs/architecture/14-api-contract/openapi.yaml` -**Улика — вывод:** 1286: Capabilities: + +**Улика — команда:** +``` +awk '/^ Capabilities:/{f=1} f{print NR": "$0; c++} c>26{exit}' docs/architecture/14-api-contract/openapi.yaml +``` +**Улика — вывод:** +``` +1286: Capabilities: 1290: required: 1291: - contract_version 1292: - language_pairs @@ -1179,20 +2235,38 @@ README.md:75: ⚠ 0.5.0 ломающий по построению: снесен 1297: - page_size_default 1306: language_pairs: 1309: Every pair the deployment knows about, unavailable ones included: "absent" and "listed as unavailable" are different facts to a user waiting for one. +``` **Другие носители того же факта:** frontend/docs/frontend-PROGRESS.md:496 (пинг №17 п.2 — предсказано, носитель не поправлен) -**Предложенное действие:** Пере-диспозиционировать Ф-57 в работу первого касания: «`src/showcase/languages.ts` перестаёт быть константой и становится чтением `GET /capabilities.language_pairs`» — продуктовой развилки больше нет. + +**Предложенное действие (автора находки):** Пере-диспозиционировать Ф-57 в работу первого касания: «`src/showcase/languages.ts` перестаёт быть константой и становится чтением `GET /capabilities.language_pairs`» — продуктовой развилки больше нет. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Находка верна, но список носителей НЕПОЛОН — есть ТРЕТИЙ, и он в коде: докблок `frontend/src/showcase/languages.ts:3-8` дословно повторяет ту же протухшую посылку («the contract has no handle for "what this deployment can translate" … the gap is written down rather than papered over (BACKLOG Ф-57: a handle in the contract)»). Комментарий переедет вместе с заменой константы на чтение `GET /capabilities`, но в диспозицию его назвать надо, иначе при разморозке поправят строку бэклога и оставят ложь в коде. Исполнитель — фронт-сессия первого касания. + +**Разбор опровергателя:** Опровергнуть не удалось. Ручка есть в каноне 0.9.0 с ОБЯЗАТЕЛЬНЫМ `language_pairs`; в зеркале 0.2.3 её нет вовсе — то есть Ф-57 родилась верной и стала ложной с батчем. Пинг №17 п.2 в том же дереве это уже зафиксировал; носитель не поправлен. Класс C верен: продуктовая развилка («обещать пары списком или отвечать отказом») контрактом РЕШЕНА в пользу списка со статусом. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Находка верна, но список носителей НЕПОЛОН — есть ТРЕТИЙ, и он в коде: докблок `frontend/src/showcase/languages.ts:3-8` дословно повторяет ту же протухшую посылку («the contract has no handle for "what this deployment can translate" … the gap is written down rather than papered over (BACKLOG Ф-57: a handle in the contract)»). Комментарий переедет вместе с заменой константы на чтение `GET /capabilities`, но в диспозицию его назвать надо, иначе при разморозке поправят строку бэклога и оставят ложь в коде. Исполнитель — фронт-сессия первого касания. --- ## N061 · MAJOR · `frontend/docs/BACKLOG.md:68` · C-исполненное-как-открытое -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** `POST /books` кладёт название один раз (рукой или из имени файла, 0.2.3), дальше только чтения: `GET /books/{bookId}` и ни одного `PATCH`/`PUT`. … Пока её нет, ошибку в названии лечит только удаление и повторная загрузка, а ручки удаления в контракте тоже нет. +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> `POST /books` кладёт название один раз (рукой или из имени файла, 0.2.3), дальше только чтения: `GET /books/{bookId}` и ни одного `PATCH`/`PUT`. … Пока её нет, ошибку в названии лечит только удаление и повторная загрузка, а ручки удаления в контракте тоже нет. + **Почему протухло:** Канон 0.9.0 несёт и `PATCH /books/{bookId}` (merge patch, только `title`, 409 при живом прогоне), и `DELETE /books/{bookId}` (204, 409 при живом прогоне). Обе половины утверждения ложны. -**Улика — команда:** `sed -n '281,325p' docs/architecture/14-api-contract/openapi.yaml | grep -n 'patch:\|delete:\|operationId\|summary'` -**Улика — вывод:** patch: + +**Улика — команда:** +``` +sed -n '281,325p' docs/architecture/14-api-contract/openapi.yaml | grep -n 'patch:\|delete:\|operationId\|summary' +``` +**Улика — вывод:** +``` + patch: operationId: updateBook summary: Rename a book. «Merge patch (RFC 7386). **Only `title` may be changed.** … Accepted while a run is live» @@ -1200,22 +2274,40 @@ README.md:75: ⚠ 0.5.0 ломающий по построению: снесен operationId: deleteBook summary: Delete a book. «Removes the book and everything derived from it … `409` while a run is live — stop it first.» +``` **Другие носители того же факта:** frontend/docs/frontend-PROGRESS.md:498 (пинг №17 п.4) -**Предложенное действие:** Закрыть Ф-62 как «отвечена каноном (Б-5/0.4.0+)», оставив ЗОННЫЙ хвост: построить экраны переименования и удаления при разморозке; жанр из формы снять (Б-23). + +**Предложенное действие (автора находки):** Закрыть Ф-62 как «отвечена каноном (Б-5/0.4.0+)», оставив ЗОННЫЙ хвост: построить экраны переименования и удаления при разморозке; жанр из формы снять (Б-23). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно по существу. Одно уточнение: «жанр из формы снять (Б-23)» — это НЕ хвост Ф-62, а отдельное слово владельца (D39.136 / пинг №17 п.4: «жанр из продукта УХОДИТ совсем»), и вешать его хвостом на строку про переименование значит спрятать самостоятельное решение внутри чужой строки — завести отдельной зонной строкой при разморозке. Исполнитель — фронт-сессия (зона заморожена, D39.147). + +**Разбор опровергателя:** Обе половины утверждения сегодня ложны и обе были верны на 0.2.3 (в зеркале у `/books/{bookId}` действительно только `get`). Продуктовый вопрос, который строка держит открытым («нужна ли правка метаданных книги и в какой форме»), отвечен владельцем 16.08 и ратифицирован — сам зонный журнал это уже записал строкой 498, носитель не поправлен. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно по существу. Одно уточнение: «жанр из формы снять (Б-23)» — это НЕ хвост Ф-62, а отдельное слово владельца (D39.136 / пинг №17 п.4: «жанр из продукта УХОДИТ совсем»), и вешать его хвостом на строку про переименование значит спрятать самостоятельное решение внутри чужой строки — завести отдельной зонной строкой при разморозке. Исполнитель — фронт-сессия (зона заморожена, D39.147). --- ## N062 · MAJOR · `frontend/docs/FRONTEND_PLAN.md:692` · D-противоречие-ратифицированному -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** 1. **Прогресс — пофазный, не «готово N из M».** Юнит становится `done` только когда есть и +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> 1. **Прогресс — пофазный, не «готово N из M».** Юнит становится `done` только когда есть и черновые строки всех членов, и строка редактуры … Тип прогресса — две пары `{ done, total }` (черновик ∥ редактура), а не одно число. + **Почему протухло:** Ратифицированный канон 0.9.0 кладёт на провод ОДНУ пару {done,total}, считаемую в ГЛАВАХ по всем проходам, плюс открытую строку `stage`. Клиент, написанный по этому пункту, спроектирует тип, которого контракт не отдаёт. -**Улика — команда:** `sed -n '1396,1414p' docs/architecture/14-api-contract/openapi.yaml` -**Улика — вывод:** required: [done, total, stage, eta_seconds] + +**Улика — команда:** +``` +sed -n '1396,1414p' docs/architecture/14-api-contract/openapi.yaml +``` +**Улика — вывод:** +``` + required: [done, total, stage, eta_seconds] properties: done: description: Chapters of this run's own work that are finished. @@ -1225,41 +2317,77 @@ README.md:75: ⚠ 0.5.0 ломающий по построению: снесен description: | What the run is doing NOW — the caption beside the bar, phrased by the client. … Today's deployments answer `drafting` or `editing` +``` **Другие носители того же факта:** frontend/docs/STACK_DECISIONS.md:249, frontend/docs/FRONTEND_SESSION_PROMPT.md:437 -**Предложенное действие:** Во всех ТРЁХ носителях заменить «две пары {done,total} (черновик ∥ редактура)» на «одна пара {done,total} в ГЛАВАХ за все проходы + открытая строка `stage`; фазы с провода сняты (Б-0/D39.138, канон ≥0.3.0)». + +**Предложенное действие (автора находки):** Во всех ТРЁХ носителях заменить «две пары {done,total} (черновик ∥ редактура)» на «одна пара {done,total} в ГЛАВАХ за все проходы + открытая строка `stage`; фазы с провода сняты (Б-0/D39.138, канон ≥0.3.0)». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Формулировка замены верна по существу, но: (а) третий носитель `FRONTEND_SESSION_PROMPT.md` §9 стоит под ⚠-баннером, который перечисляет ЖИВЫЕ секции — §9 в их числе НЕ назван; правку туда вносить можно, но не как в живое задание, а строкой-эрратой под тем же баннером; (б) ⚠ ЗЕРКАЛО `frontend/docs/api-contract/openapi.yaml` НЕ ТРОГАТЬ: его отставание на 0.2.3 РАТИФИЦИРОВАНО на время фриза (D39.142 п.5; `docs/architecture/14-api-contract/README.md:102` — «зонное зеркало фронта стоит на 0.2.3: при разморозке фронт платит переход целиком»), и правка зеркала врозь с каноном = вторая точка правки; (в) исполнитель — фронт-сессия первого касания (зона заморожена, D39.147), заказ уже стоит в пинге №17 аддендум п.1. + +**Разбор опровергателя:** Подтверждено И замером, И ратификацией. Пункт РОДИЛСЯ верным (зеркало 0.2.3 в том же дереве до сих пор несёт `required: [draft, edit]`) и сгнил дрейфом канона: 0.9.0 кладёт на провод одну пару в ГЛАВАХ + открытую строку `stage`; `phases`/`draft_done`/`edit_done` в каноне ноль вхождений при том, что схема `Progress` грепается по имени — инструмент в этой области видит. D39.138 ратифицирует именно это. Носителей действительно три, и все три названы правильно (я сверил построчно: PLAN:692, STACK:249, PROMPT:437). + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Формулировка замены верна по существу, но: (а) третий носитель `FRONTEND_SESSION_PROMPT.md` §9 стоит под ⚠-баннером, который перечисляет ЖИВЫЕ секции — §9 в их числе НЕ назван; правку туда вносить можно, но не как в живое задание, а строкой-эрратой под тем же баннером; (б) ⚠ ЗЕРКАЛО `frontend/docs/api-contract/openapi.yaml` НЕ ТРОГАТЬ: его отставание на 0.2.3 РАТИФИЦИРОВАНО на время фриза (D39.142 п.5; `docs/architecture/14-api-contract/README.md:102` — «зонное зеркало фронта стоит на 0.2.3: при разморозке фронт платит переход целиком»), и правка зеркала врозь с каноном = вторая точка правки; (в) исполнитель — фронт-сессия первого касания (зона заморожена, D39.147), заказ уже стоит в пинге №17 аддендум п.1. --- ## N063 · MAJOR · `frontend/docs/STACK_DECISIONS.md:245` · C-исполненное-как-открытое -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** ## 8. Что придётся достроить в движке +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> ## 8. Что придётся достроить в движке Через оркестратора, в зону `backend/`, в порядке критичности. Обосновано разбором кода. + **Почему протухло:** Все ЧЕТЫРЕ пункта («пофазный прогресс», «персист манифеста глав и чанков», «JSON-выход таблицы подписи банка», «приём внешнего TRACEPARENT в cmd/tmctl») в движке ПОСТРОЕНЫ. Раздел учит сессию, что этих поверхностей нет и их надо заказывать через оркестратора. -**Улика — команда:** `sed -n '9,13p' backend/internal/pipeline/artifact.go; sed -n '17,18p' backend/internal/pipeline/contractblockers_test.go; grep -n 'TM_TRACE_ID' backend/cmd/tmctl/main.go | head -2` -**Улика — вывод:** artifact.go:9-11: «the sidecars beside the project DB … the chapter/chunk manifest (row 100), the machine bank-stop table (row 101), the bank export (row 125)» + +**Улика — команда:** +``` +sed -n '9,13p' backend/internal/pipeline/artifact.go; sed -n '17,18p' backend/internal/pipeline/contractblockers_test.go; grep -n 'TM_TRACE_ID' backend/cmd/tmctl/main.go | head -2 +``` +**Улика — вывод:** +``` +artifact.go:9-11: «the sidecars beside the project DB … the chapter/chunk manifest (row 100), the machine bank-stop table (row 101), the bank export (row 125)» contractblockers_test.go:17-18: «the five engine surfaces the API contract is blocked on — per-phase progress (backlog row 99), the persisted chapter/chunk manifest (100), the machine bank-stop table (101), the bank export (125)…» + TestPhaseProgressSplitsTheWaves main.go:163: v := os.Getenv("TM_TRACE_ID") -**Предложенное действие:** Заменить §8 целиком на строку-указатель: «все четыре поверхности построены (манифест/bank-stop/bank-export — D39.122, `pipeline/artifact.go`; трасса — TM_TRACE_ID, `cmd/tmctl/main.go:163`); актуальный список долгов движка — единый бэклог `docs/PROGRESS.md`». ⚠ Уже названо пингом №17 (frontend-PROGRESS.md:504 п.2) — носитель не поправлен. -**Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие «заменить §8 ЦЕЛИКОМ» НЕВЕРНО в двух местах. +``` + +**Предложенное действие (автора находки):** Заменить §8 целиком на строку-указатель: «все четыре поверхности построены (манифест/bank-stop/bank-export — D39.122, `pipeline/artifact.go`; трасса — TM_TRACE_ID, `cmd/tmctl/main.go:163`); актуальный список долгов движка — единый бэклог `docs/PROGRESS.md`». ⚠ Уже названо пингом №17 (frontend-PROGRESS.md:504 п.2) — носитель не поправлен. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие «заменить §8 ЦЕЛИКОМ» НЕВЕРНО в двух местах. (1) §8 содержит не только четыре пункта: ниже них лежат ⚠-абзацы «Подпись термина — это НЕ UPDATE строки» (`ReplaceBank` жив — `backend/internal/store/glossary.go:129`; `seedGlossary` жив — `backend/internal/membank/memseed.go:21`), «Выравнивание колонок достраивать не нужно», «Спаны внутри текста не делаем (решение владельца 02.08)» и «После апгрейда бинарника движка…». Снос всего §8 унёс бы живые ратифицированные предупреждения. Правится только НУМЕРОВАННЫЙ список из четырёх пунктов. (2) Предложенный текст-замена сам ставит МЁРТВЫЙ указатель: «bank-stop — D39.122, `pipeline/artifact.go`». Машинная (JSON) таблица подписи БЫЛА построена D39.122 (коммит `0e69bc1`) и СНЕСЕНА 27.08 паком шва (`d1eb8a9`, D39.158) как не имеющая читателя ни в одной зоне — см. `backend/docs/SEAM_PACK_FINDINGS.md:1415` («Контрольный греп `bank-stop.json|TableJSONPath|…` по `*.go` → 0»). Сегодня существует только человеческий `.bank-stop.txt` (`mining.go:370`), и он пишется ОБЫЧНЫМ `os.WriteFile` (`mining.go:198`), т.е. в список `writeFileAtomic` не входит вовсе. Комментарий `artifact.go:11`, которым опровергаемая находка доказывает существование поверхности, сам протух — это отдельная находка ЗОНЫ БЭКЕНДА, а не улика для фронта. Верная замена п.3: «машинная таблица подписи была построена (row 101, D39.122) и снесена как беспотребительская (D39.158); фронту её заменяет проекция платформы `GET /books/{bookId}/bank` + движковый экспорт банка `.bank.json` (row 125, `pipeline/bankexport.go:85`)». (3) ⚠ Зона `frontend/` ЗАМОРОЖЕНА (D39.147, пинг №18) и не является зоной записи оркестратора (CLAUDE.md, таблица ролей: оркестратор пишет `docs/` + корневые доки + зонный журнал `frontend/docs/frontend-PROGRESS.md`). Правка САМОГО `STACK_DECISIONS.md` — работа фронт-сессии первого касания, и она УЖЕ заказана пингом №17 «при разморозке обновить все три». Единственное, что здесь принадлежит оркестратору сегодня, — ⚠-баннер/запись в зонном журнале. +**Разбор опровергателя:** Раздел действительно протух, и это не мнение, а уже РАТИФИЦИРОВАННЫЙ вывод: аддендум к пингу №17 (frontend-PROGRESS.md:504 п.2) дословно говорит «`STACK_DECISIONS.md` §8 протух целиком: все четыре пункта ПОСТРОЕНЫ». Мой независимый замер: п.1 (пофазный прогресс) = `PhaseProgress` row 99; п.2 (манифест) = row 100, писатель `manifest.go:401`; п.3 = row 101; п.4 (трасса) = row 102, `TM_TRACE_ID` принимается средой (`main.go:152`). Все пять строк отсутствуют в живом бэклоге при положительном контроле на четырёх живых номерах, и закрыты нотами D39.122 / D39.131. + +**Разрушающее:** True + --- ## N064 · MAJOR · `platform/README.md:10` · A-дрейф -**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** Батарея зоны: `make check` (build · vet · fmt · lint · test -race). `make vuln` и `make fuzz` — +**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> Батарея зоны: `make check` (build · vet · fmt · lint · test -race). `make vuln` и `make fuzz` — + **Почему протухло:** Перечень устарел с лендингом пака sqlc 29.08: `check: build vet fmt lint sqlc-check` — пятый шаг `sqlc-check` в списке отсутствует. Хуже перечня — умолчание о ТУЛЕ: `build` и `lint` зависят от `tools-check`, который ЖЁСТКО ОТКАЗЫВАЕТ без `sqlc v1.31.1` на хосте. Ни рецепт стенда в `STACK_DECISIONS` («Собрать бинари и шаблон книги» — три `go build` и `sed`), ни `deploy/README.md`, ни `ENGINEERING_STANDARDS` §3.1 нигде не называют установку sqlc: слово «sqlc» встречается во всём срезе ровно ОДИН раз — в таблице пинов `STACK_DECISIONS:27`. Сессия, собравшая стенд по рецепту зоны, получает не «зелёную батарею со скипами», а `make: *** [Makefile:62: tools-check] Error 1` — и это выглядит как поломка дерева, а не как недостающий инструмент. Прямо ломает критерий приёмки §3.1 «`make check` зелёный офлайн». -**Улика — команда:** `cd platform && which sqlc; make tools-check 2>&1 | head -3; grep -n 'check: build' Makefile; grep -c sqlc README.md docs/ENGINEERING_STANDARDS.md deploy/README.md; awk 'NR>=430 && NR<=520 && /sqlc/' docs/STACK_DECISIONS.md | wc -l` -**Улика — вывод:** (which sqlc: пусто — не установлен) + +**Улика — команда:** +``` +cd platform && which sqlc; make tools-check 2>&1 | head -3; grep -n 'check: build' Makefile; grep -c sqlc README.md docs/ENGINEERING_STANDARDS.md deploy/README.md; awk 'NR>=430 && NR<=520 && /sqlc/' docs/STACK_DECISIONS.md | wc -l +``` +**Улика — вывод:** +``` +(which sqlc: пусто — не установлен) sqlc v1.31.1 required (the generated query layer is committed, so the version decides the diff); got: none install: go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.31.1 make: *** [Makefile:62: tools-check] Error 1 @@ -1268,133 +2396,251 @@ README.md:0 docs/ENGINEERING_STANDARDS.md:0 deploy/README.md:0 0 (в разделе «Стенд разработчика» слова sqlc нет вовсе) -**Положительный контроль:** `golangci-lint --version` → «has version 2.12.2» — тот же `tools-check` ВИДИТ установленный пиненный тул и на нём не падает; падение происходит именно на sqlc, то есть проверка различает установленное и отсутствующее, а не отказывает всегда. +``` +**Положительный контроль:** +``` +`golangci-lint --version` → «has version 2.12.2» — тот же `tools-check` ВИДИТ установленный пиненный тул и на нём не падает; падение происходит именно на sqlc, то есть проверка различает установленное и отсутствующее, а не отказывает всегда. +``` **Другие носители того же факта:** platform/docs/STACK_DECISIONS.md:49 (§6, тот же перечень «build · vet · fmt · lint · test -race»), platform/docs/STACK_DECISIONS.md:439-455 (рецепт стенда: три go build + шаблон, установки sqlc нет), platform/docs/ENGINEERING_STANDARDS.md:63 («`make check` зелёный офлайн» — недостижимо без тула) -**Предложенное действие:** Дописать в перечень `sqlc-check` (README:10 и STACK_DECISIONS:49) и добавить в рецепт стенда строку `go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.31.1` рядом со сборкой бинарей, с пометкой «без него `make check` не доходит до тестов вовсе — это НЕ скип, а отказ tools-check». + +**Предложенное действие (автора находки):** Дописать в перечень `sqlc-check` (README:10 и STACK_DECISIONS:49) и добавить в рецепт стенда строку `go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.31.1` рядом со сборкой бинарей, с пометкой «без него `make check` не доходит до тестов вовсе — это НЕ скип, а отказ tools-check». + **Разрушающее:** False --- ## N065 · MAJOR · `platform/deploy/README.md:252` · C-исполненное-как-открытое -**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** стенде (P6), но сам шаг `tmctl migrate` не гонялся: движковой команды ещё нет. Пометить сделанным +**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> стенде (P6), но сам шаг `tmctl migrate` не гонялся: движковой команды ещё нет. Пометить сделанным + **Почему протухло:** Глагол `tmctl migrate` существует и заленджен (`backend/cmd/tmctl/migrate.go`, в диспетчере `main.go:184/:245`, свой код выхода `exitSchemaMismatch = 13`), а сам шаг ПРОГНАН на стенде — это записано двумя абзацами выше в ЭТОМ ЖЕ файле («⚠ Проверено исполнением на стенде (P7): … `tmctl migrate --config ` делает пред-миграционный бэкап и переводит v14 → v15») и подтверждено строкой регистра PD-201 («ПОЛОВИНА ЗАКРЫТА P7 (проверка исполнением): рантбук деплоя прогнан end-to-end на стенде живым `migrate`»). Документ спорит сам с собой на протяжении 60 строк, и стоящая последней — ложная редакция. Оператор, читающий рантбук сверху вниз, получает вывод «порядок апгрейда движка не проверен и проверить нечем», хотя проверен. Тем же протухли следующие три строки («Хвост, который НЕ построен… движковый `migrate` ПРИДЁТ с машиноразличимой ошибкой») — форма ошибки УЖЕ финализирована (`schema_mismatch found=N expected=M`, D39.134) и названа в этом же файле; не построен только автомат самолечения, что и есть живой остаток PD-201. -**Улика — команда:** `ls backend/cmd/tmctl/migrate.go && grep -n '"migrate"' backend/cmd/tmctl/main.go | head -2 && grep -n 'exitSchemaMismatch = 13' backend/cmd/tmctl/main.go; awk -F'|' '/^\| PD-201 /{print $(NF-3)}' platform/docs/DEFECT_REGISTER.md | grep -o 'ПОЛОВИНА ЗАКРЫТА P7.\{0,120\}'` -**Улика — вывод:** backend/cmd/tmctl/migrate.go + +**Улика — команда:** +``` +ls backend/cmd/tmctl/migrate.go && grep -n '"migrate"' backend/cmd/tmctl/main.go | head -2 && grep -n 'exitSchemaMismatch = 13' backend/cmd/tmctl/main.go; awk -F'|' '/^\| PD-201 /{print $(NF-3)}' platform/docs/DEFECT_REGISTER.md | grep -o 'ПОЛОВИНА ЗАКРЫТА P7.\{0,120\}' +``` +**Улика — вывод:** +``` +backend/cmd/tmctl/migrate.go 184: "translate", "report", "status", "export", "build", "redrive", "manifest", "backup", "migrate", "seed-lint", 245: case "migrate": 72: exitSchemaMismatch = 13 // this project's schema is not this binary's: run `tmctl migrate` (row 174) ПОЛОВИНА ЗАКРЫТА P7 (проверка исполнением):** рантбук деплоя прогнан end-to-end на стенде живым `migrate` — проектная БД, отведённая на схему v14 при бинаре v15 +``` **Другие носители того же факта:** platform/docs/archive/platform-PROGRESS-P4-P6.md:175 (архив, под баннером — легитимно) -**Предложенное действие:** Снести абзац 251–253 целиком (его содержимое опровергнуто абзацем 215–217 того же файла) и заменить на: «Проверка исполнением ИСПОЛНЕНА паком P7: v14→v15 прогнан на стенде, exit 13 + `schema_mismatch found=14 expected=15` наблюдён живьём». Абзац 255–258 пере-формулировать: форма ошибки известна, НЕ построен только автомат самолечения (PD-201, живой остаток). + +**Предложенное действие (автора находки):** Снести абзац 251–253 целиком (его содержимое опровергнуто абзацем 215–217 того же файла) и заменить на: «Проверка исполнением ИСПОЛНЕНА паком P7: v14→v15 прогнан на стенде, exit 13 + `schema_mismatch found=14 expected=15` наблюдён живьём». Абзац 255–258 пере-формулировать: форма ошибки известна, НЕ построен только автомат самолечения (PD-201, живой остаток). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Не «заменить абзац 251–253 на новую формулировку про P7» — это завело бы ВТОРОЙ носитель того же факта в одном файле, ровно тот класс, из-за которого он и разошёлся. Абзац 251–253 снести и на его месте оставить одну отсылку вверх («проверка исполнением — см. врезку P7 выше»), а сам факт держать в единственном месте (строки 187–189). Абзац 255–258 не сносить: его живой остаток — автомат самолечения — по регистру PD-201 действительно open; пере-написать только зачин «движковый `migrate` ПРИДЁТ с машиноразличимой ошибкой» на «форма ошибки финализирована (D39.134, `schema_mismatch found=N expected=M`); не построен автомат». + +**Разбор опровергателя:** Опровергнуть не удалось: файл противоречит сам себе внутри одного раздела. Строки 187–195 того же файла говорят «Проверено исполнением на стенде (P7)» и «УСЛОВИЕ ВЫПОЛНЕНО — правка 29.08, глагол существует и заленджен», а строки 251–253 через 60 строк утверждают обратное — «движковой команды ещё нет». Дерево на стороне первой редакции: `backend/cmd/tmctl/migrate.go` лежит, стоит в списке команд `main.go:184` и в `case` на 245, и у отказа есть свой код 13. Регистр PD-201 независимо подтверждает прогон на стенде. Первый абзац раздела (182) при этом фиксирует, что ПРЕЖНЯЯ цитата ошибки в этом же доке была мёртвой — то есть чистку 29.08 делали, но до хвоста не дошли. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Не «заменить абзац 251–253 на новую формулировку про P7» — это завело бы ВТОРОЙ носитель того же факта в одном файле, ровно тот класс, из-за которого он и разошёлся. Абзац 251–253 снести и на его месте оставить одну отсылку вверх («проверка исполнением — см. врезку P7 выше»), а сам факт держать в единственном месте (строки 187–189). Абзац 255–258 не сносить: его живой остаток — автомат самолечения — по регистру PD-201 действительно open; пере-написать только зачин «движковый `migrate` ПРИДЁТ с машиноразличимой ошибкой» на «форма ошибки финализирована (D39.134, `schema_mismatch found=N expected=M`); не построен автомат». --- ## N066 · MAJOR · `platform/docs/PLATFORM_DIRECTION.md:159` · C-исполненное-как-открытое -**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** ⚠ **НЕ ИСПОЛНЕНО на 14.08**, срок «до первого хендлера» пройден — см. баннер статуса выше (PD-44) +**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> ⚠ **НЕ ИСПОЛНЕНО на 14.08**, срок «до первого хендлера» пройден — см. баннер статуса выше (PD-44) + **Почему протухло:** sqlc ВЗЯТ и заленджен 29.08 (`D39.172`: «Пак `sqlc` принят и заленджен: 40 запросов пяти файлов `pgstore` на типизированный слой, пин 1.31.1, `sqlc diff` в `make check`»; коммит 63fcee5). В дереве лежат `platform/sqlc.yaml`, `internal/pgstore/queries/{credits,identity,idempotency,sessions}.sql` (ровно 40 `-- name:`), генерённые `*.sql.go`, пин `SQLC_VERSION := 1.31.1` в `tools-check` и цель `sqlc-check` внутри `check`. Ратифицированный документ направления при этом объявляет инструмент неисполненным, а его верхний баннер («ЧИТАТЬ ПРЕЖДЕ ВСЕГО») — работой, ожидающей отдельной сессии («Носитель работы — `BACKLOG.md` П-19»). Побочно устарела и граница взятия в баннере: «пять файлов… `idempotency.go`(7) · `sessions.go`(5) · `observe.go`(1) = **41 запрос**» — в дереве ЧЕТЫРЕ файла запросов, idempotency 6, sessions 6, observe не конвертирован вовсе, итог 40 (`D39.172` и П-19 бэклога уже исправлены на 40, направление — нет). Документ обязателен к ссылке в КАЖДОМ промте платформы, поэтому следующая платформенная сессия читает «инструмент не взят» первым делом. -**Улика — команда:** `git log -1 --format='%h %ad %s' --date=short -- platform/sqlc.yaml | cut -c1-90; ls platform/internal/pgstore/queries/; grep -rh '^-- name:' platform/internal/pgstore/queries/*.sql | wc -l; grep -n 'sqlc-check\|SQLC_VERSION' platform/Makefile | head -4` -**Улика — вывод:** 63fcee5 2026-08-29 Land the sqlc pack: the coverage answer said almost nothing… + +**Улика — команда:** +``` +git log -1 --format='%h %ad %s' --date=short -- platform/sqlc.yaml | cut -c1-90; ls platform/internal/pgstore/queries/; grep -rh '^-- name:' platform/internal/pgstore/queries/*.sql | wc -l; grep -n 'sqlc-check\|SQLC_VERSION' platform/Makefile | head -4 +``` +**Улика — вывод:** +``` +63fcee5 2026-08-29 Land the sqlc pack: the coverage answer said almost nothing… credits.sql idempotency.sql identity.sql sessions.sql 40 26:SQLC_VERSION := 1.31.1 30:.PHONY: build vet fmt lint test check tools-check version-check vuln fuzz sqlc-check sqlc-generate 78:sqlc-check: tools-check 99:check: build vet fmt lint sqlc-check +``` **Другие носители того же факта:** platform/docs/PLATFORM_DIRECTION.md:106-116 (верхний баннер: «БЕРЁМ… отдельной сессией», «= 41 запрос», «Носитель работы — BACKLOG.md П-19»), platform/docs/PLATFORM_DIRECTION.md:145 («`sqlc` («до первого хендлера»): НЕ ВЗЯТ»), platform/docs/STACK_DECISIONS.md:27 (ВЕРНАЯ редакция: «Взят по решению владельца 20.08», 40 запросов), platform/BACKLOG.md:31 (П-19, уже исправлен D39.172 на «40 КОНВЕРТИРУЕМЫХ») -**Предложенное действие:** Клетку таблицы 159 пере-подписать: «ВЗЯТ и заленджен 29.08 (D39.172, 63fcee5): 40 запросов четырёх файлов `pgstore/queries/`, пин 1.31.1 в `tools-check`, `sqlc diff` в `make check`». Баннер 106–116 свернуть в исторический (с датой) и поправить счёт 41→40; строку 145 «НЕ ВЗЯТ» пометить как историю P7. П-19 в BACKLOG.md — закрыть/пере-скоупить (зона платформы, не мой ход). + +**Предложенное действие (автора находки):** Клетку таблицы 159 пере-подписать: «ВЗЯТ и заленджен 29.08 (D39.172, 63fcee5): 40 запросов четырёх файлов `pgstore/queries/`, пин 1.31.1 в `tools-check`, `sqlc diff` в `make check`». Баннер 106–116 свернуть в исторический (с датой) и поправить счёт 41→40; строку 145 «НЕ ВЗЯТ» пометить как историю P7. П-19 в BACKLOG.md — закрыть/пере-скоупить (зона платформы, не мой ход). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно, но формулировку замены поправить, чтобы она не спорила с ратифицированной нотой: писать «40 запросов, вынесенных в ЧЕТЫРЕ файла `pgstore/queries/`» (D39.172 говорит «сорок запросов пяти файлов pgstore» — это счёт по исходным Go-файлам, и переписывать его в «четырёх файлов» нельзя, номер бьёт док). Строку 145 «НЕ ВЗЯТ» и блок 136–147 не сносить: это записанный ДОВОД отказа P7, улика решения; пометить датой как историю пака. П-19 в `platform/BACKLOG.md` — зона платформы, не ход оркестратора: пинг, а не правка. + +**Разбор опровергателя:** Опровергнуть не вышло — всё воспроизвелось. sqlc взят и заленджен: `platform/sqlc.yaml`, четыре файла запросов, генерённые `*.sql.go`, пин `SQLC_VERSION := 1.31.1` и `sqlc-check` пререквизитом `check` в Makefile, ратифицировано D39.172. Клетка 159 при этом объявляет инструмент неисполненным, а верхний баннер (106–116, «ЧИТАТЬ ПРЕЖДЕ ВСЕГО») — работой, ждущей отдельной сессии. Побочное утверждение находки о счёте тоже подтвердилось точно: 15+6+13+6 = 40 в четырёх файлах, `observe.go` не конвертирован (нет ни `observe.sql`, ни `observe.sql.go`), тогда как баннер держит «пять файлов … idempotency(7) · sessions(5) · observe(1) = 41 запрос». Важная оговорка для исполнителя: сама D39.172 пишет «Сорок запросов ПЯТИ файлов `pgstore`» — то есть ратифицированная нота считает по исходным Go-файлам, а не по `.sql`; путать эти два счёта нельзя. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, но формулировку замены поправить, чтобы она не спорила с ратифицированной нотой: писать «40 запросов, вынесенных в ЧЕТЫРЕ файла `pgstore/queries/`» (D39.172 говорит «сорок запросов пяти файлов pgstore» — это счёт по исходным Go-файлам, и переписывать его в «четырёх файлов» нельзя, номер бьёт док). Строку 145 «НЕ ВЗЯТ» и блок 136–147 не сносить: это записанный ДОВОД отказа P7, улика решения; пометить датой как историю пака. П-19 в `platform/BACKLOG.md` — зона платформы, не ход оркестратора: пинг, а не правка. --- ## N067 · MAJOR · `platform/docs/STACK_DECISIONS.md:621` · B-мёртвый-указатель -**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/tm-runs.slice/cgroup.subtree_control`. +**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/tm-runs.slice/cgroup.subtree_control`. + **Почему протухло:** Лечебная команда «самой частой грабли стенда» указывает на путь, которого не существует: `tm-runs.slice` по конвенции имён systemd вложен в `tm.slice` (срез `X-Y.slice` живёт в `X.slice/X-Y.slice`), поэтому реальный путь — `…/user@1000.service/`**`tm.slice/`**`tm-runs.slice/cgroup.subtree_control`. Оператор получает «No such file or directory» — ровно тот симптом, ради устранения которого правка 29.08 и делалась: тогда `tm.slice` заменили на `tm-runs.slice`, но нужны ОБА уровня. Зонный журнал уже замерил верную вложенность и записал её (`platform/docs/platform-PROGRESS.md:599`: «Сам `tm-runs.slice` лежит глубже, чем его ищут: `user@1000.service/`**`tm.slice`**`/tm-runs.slice`»), но рецепт с ней не сверили. Цена: `TestARunIsBoundedByItsOwnCgroup` остаётся красным, а сессия, честно исполнившая рецепт, ищет дефект в своём диффе. -**Улика — команда:** `B=/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service; test -e $B/tm-runs.slice/cgroup.subtree_control && echo 'РЕЦЕПТНЫЙ ПУТЬ ЕСТЬ' || echo 'РЕЦЕПТНЫЙ ПУТЬ ОТСУТСТВУЕТ'; test -e $B/tm.slice/tm-runs.slice/cgroup.subtree_control && echo 'РЕАЛЬНЫЙ ПУТЬ (через tm.slice) ЕСТЬ'; cat $B/tm.slice/cgroup.subtree_control; find /sys/fs/cgroup -maxdepth 3 -name '*-*.slice' -type d | head -3` -**Улика — вывод:** РЕЦЕПТНЫЙ ПУТЬ ОТСУТСТВУЕТ + +**Улика — команда:** +``` +B=/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service; test -e $B/tm-runs.slice/cgroup.subtree_control && echo 'РЕЦЕПТНЫЙ ПУТЬ ЕСТЬ' || echo 'РЕЦЕПТНЫЙ ПУТЬ ОТСУТСТВУЕТ'; test -e $B/tm.slice/tm-runs.slice/cgroup.subtree_control && echo 'РЕАЛЬНЫЙ ПУТЬ (через tm.slice) ЕСТЬ'; cat $B/tm.slice/cgroup.subtree_control; find /sys/fs/cgroup -maxdepth 3 -name '*-*.slice' -type d | head -3 +``` +**Улика — вывод:** +``` +РЕЦЕПТНЫЙ ПУТЬ ОТСУТСТВУЕТ РЕАЛЬНЫЙ ПУТЬ (через tm.slice) ЕСТЬ memory pids /sys/fs/cgroup/user.slice/user-1000.slice /sys/fs/cgroup/system.slice/system-modprobe.slice /sys/fs/cgroup/system.slice/system-getty.slice -**Положительный контроль:** Тот же find на этом хосте находит РЕАЛЬНЫЕ вложенные срезы (`user.slice/user-1000.slice`, `system.slice/system-modprobe.slice`, `system.slice/system-getty.slice`) — инструмент видит срезы; и `.../tm.slice/tm-runs.slice/cgroup.subtree_control` существует, то есть отсутствует именно рецептный путь, а не каталог cgroup вообще. +``` +**Положительный контроль:** +``` +Тот же find на этом хосте находит РЕАЛЬНЫЕ вложенные срезы (`user.slice/user-1000.slice`, `system.slice/system-modprobe.slice`, `system.slice/system-getty.slice`) — инструмент видит срезы; и `.../tm.slice/tm-runs.slice/cgroup.subtree_control` существует, то есть отсутствует именно рецептный путь, а не каталог cgroup вообще. +``` **Другие носители того же факта:** platform/docs/p8-review/README.md:23 («опустевший `cgroup.subtree_control` у `tm.slice`, лечится строкой из рецепта») — называет другой уровень, чем рецепт, на который ссылается, platform/docs/STACK_DECISIONS.md:485 (кандидат-замена условия — «состояние `cgroup.subtree_control` среза `tm-runs.slice`», тот же недоуказанный путь) -**Предложенное действие:** В строке 621 вставить недостающий уровень: `…/user@1000.service/tm.slice/tm-runs.slice/cgroup.subtree_control`. Заодно свериться, что `tm.slice/cgroup.subtree_control` тоже несёт `+memory +pids` (на этом хосте несёт) — без родителя лист контроллеров не получит. + +**Предложенное действие (автора находки):** В строке 621 вставить недостающий уровень: `…/user@1000.service/tm.slice/tm-runs.slice/cgroup.subtree_control`. Заодно свериться, что `tm.slice/cgroup.subtree_control` тоже несёт `+memory +pids` (на этом хосте несёт) — без родителя лист контроллеров не получит. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно, но одного вставленного уровня мало: рецепт обязан лечить ОБА уровня, иначе он останется ложно-успокаивающим на свежей машине. Правильная форма — `echo "+memory +pids" > …/user@1000.service/tm.slice/cgroup.subtree_control` (если пуст), затем то же в `…/tm.slice/tm-runs.slice/cgroup.subtree_control`: лист не получит контроллеров, которых не раздал родитель. Заодно поправить `p8-review/README.md:23` — там назван `tm.slice` там, где симптом у `tm-runs.slice`, и это второй разошедшийся носитель одного факта. + +**Разбор опровергателя:** Проверено исполнением на живом хосте: путь из рецепта ОТСУТСТВУЕТ, а путь через `tm.slice` СУЩЕСТВУЕТ — это правило именования systemd (срез `X-Y.slice` живёт внутри `X.slice`), а не особенность машины. Положительный контроль сильный: тот же `find` находит настоящие вложенные срезы (`user.slice/user-1000.slice`, `system.slice/system-*.slice`), то есть инструмент видит cgroup-дерево; отсутствует именно рецептный путь. Больше того, замер поймал ровно ту болезнь, ради которой рецепт писан: `tm-runs.slice/cgroup.subtree_control` СЕЙЧАС пуст, а родительский `tm.slice` несёт `memory pids` — то есть исполнить рецепт нужно, и в нынешней записи он даст ENOENT. Имя среза в коде подтверждено (`runner.go:54`). Второй носитель тоже реален и назван неточно: `p8-review/README.md:23` говорит про опустевший контрол у `tm.slice`, тогда как чинить надо у `tm-runs.slice` — два дока называют разные уровни. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, но одного вставленного уровня мало: рецепт обязан лечить ОБА уровня, иначе он останется ложно-успокаивающим на свежей машине. Правильная форма — `echo "+memory +pids" > …/user@1000.service/tm.slice/cgroup.subtree_control` (если пуст), затем то же в `…/tm.slice/tm-runs.slice/cgroup.subtree_control`: лист не получит контроллеров, которых не раздал родитель. Заодно поправить `p8-review/README.md:23` — там назван `tm.slice` там, где симптом у `tm-runs.slice`, и это второй разошедшийся носитель одного факта. --- ## N068 · MINOR · `CLAUDE.md:8` · B-мёртвый-указатель -**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** «бэкенд только под русский» — объявленная АНТИЦЕЛЬ (`architecture/09-target-architecture.md` §0.1) +**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> «бэкенд только под русский» — объявленная АНТИЦЕЛЬ (`architecture/09-target-architecture.md` §0.1) + **Почему протухло:** Путь написан от `docs/`, а файл — корневой: от корня репозитория `architecture/09-target-architecture.md` не существует; реальный путь — `docs/architecture/09-target-architecture.md`. Ровно этот дефект CLAUDE.md:94 объявляет ИСПРАВЛЕННЫМ своими же словами: «(пути от КОРНЯ репозитория — прежняя редакция писала их от `docs/`, и от корня они не резолвились)» — правка прошла только по секции онбординга и до шапки не дошла. ⚠ Сам якорь §0.1 ЖИВ — битый только префикс пути. -**Улика — команда:** `for p in architecture/09-target-architecture.md docs/architecture/09-target-architecture.md; do [ -e "$p" ] && echo "$p EXISTS" || echo "$p MISSING"; done; grep -n '^## 0.1' docs/architecture/09-target-architecture.md` -**Улика — вывод:** architecture/09-target-architecture.md : MISSING + +**Улика — команда:** +``` +for p in architecture/09-target-architecture.md docs/architecture/09-target-architecture.md; do [ -e "$p" ] && echo "$p EXISTS" || echo "$p MISSING"; done; grep -n '^## 0.1' docs/architecture/09-target-architecture.md +``` +**Улика — вывод:** +``` +architecture/09-target-architecture.md : MISSING --- POSITIVE CONTROL --- docs/architecture/09-target-architecture.md : EXISTS (44039 байт, Aug 30 19:22) --- якорь жив --- 44:## 0.1 Инвариант общности (поправка владельца, 2026-07-13) — НЕСУЩИЙ -**Положительный контроль:** Тот же `[ -e ]` в том же дереве отвечает EXISTS на `docs/architecture/09-target-architecture.md` и MISSING на тот же путь без префикса `docs/`; файл читается и в нём находится §0.1. Значит отрицание — по факту, а не по опечатке в имени. -**Предложенное действие:** Дописать префикс: `docs/architecture/09-target-architecture.md` §0.1 — привести шапку к норме, которую CLAUDE.md:94 уже объявила действующей. +``` +**Положительный контроль:** +``` +Тот же `[ -e ]` в том же дереве отвечает EXISTS на `docs/architecture/09-target-architecture.md` и MISSING на тот же путь без префикса `docs/`; файл читается и в нём находится §0.1. Значит отрицание — по факту, а не по опечатке в имени. +``` + +**Предложенное действие (автора находки):** Дописать префикс: `docs/architecture/09-target-architecture.md` §0.1 — привести шапку к норме, которую CLAUDE.md:94 уже объявила действующей. + **Разрушающее:** False --- ## N069 · MINOR · `CLAUDE.md:21` · B-мёртвый-указатель -**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** код и комментарии — по-английски (`architecture/12-go-style-notes` §1) +**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> код и комментарии — по-английски (`architecture/12-go-style-notes` §1) + **Почему протухло:** Второй уцелевший docs-относительный путь, битый ДВАЖДЫ: нет префикса `docs/` И нет расширения `.md`. Реальный носитель — `docs/architecture/12-go-style-notes.md`, и правильную форму эта же CLAUDE.md печатает на строке 70 (гардрейл общности). Как и на строке 8, объявленная на строке 94 правка «пути от КОРНЯ» сюда не дошла. ⚠ Якорь §1 ЖИВ — битый только путь. -**Улика — команда:** `for p in architecture/12-go-style-notes architecture/12-go-style-notes.md docs/architecture/12-go-style-notes.md; do [ -e "$p" ] && echo "$p EXISTS" || echo "$p MISSING"; done; grep -n '^## §' docs/architecture/12-go-style-notes.md | head -3` -**Улика — вывод:** architecture/12-go-style-notes : MISSING + +**Улика — команда:** +``` +for p in architecture/12-go-style-notes architecture/12-go-style-notes.md docs/architecture/12-go-style-notes.md; do [ -e "$p" ] && echo "$p EXISTS" || echo "$p MISSING"; done; grep -n '^## §' docs/architecture/12-go-style-notes.md | head -3 +``` +**Улика — вывод:** +``` +architecture/12-go-style-notes : MISSING architecture/12-go-style-notes.md : MISSING --- POSITIVE CONTROL --- docs/architecture/12-go-style-notes.md : EXISTS (11134 байт, Aug 30 23:44) --- якорь жив --- 5:## §0. Общность движка (директива владельца 24.07 — жёсткая) 14:## §1. Go-ответы (только то, о чём реально спорим) -**Положительный контроль:** Тот же `[ -e ]` в том же дереве отвечает EXISTS на `docs/architecture/12-go-style-notes.md`, и grep в нём находит §1 на строке 14. Отрицание по двум коротким формам получено тем же инструментом. +``` +**Положительный контроль:** +``` +Тот же `[ -e ]` в том же дереве отвечает EXISTS на `docs/architecture/12-go-style-notes.md`, и grep в нём находит §1 на строке 14. Отрицание по двум коротким формам получено тем же инструментом. +``` **Другие носители того же факта:** CLAUDE.md:70 — та же цель, форма ПРАВИЛЬНАЯ (`docs/architecture/12-go-style-notes.md`) -**Предложенное действие:** Привести к форме строки 70: `docs/architecture/12-go-style-notes.md` §1. + +**Предложенное действие (автора находки):** Привести к форме строки 70: `docs/architecture/12-go-style-notes.md` §1. + **Разрушающее:** False --- ## N070 · MINOR · `CLAUDE.md:93` · A-дрейф -**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** ⚠ «Живая голова» целиком в обязательное чтение НЕ входит: это 33 ноты +**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> ⚠ «Живая голова» целиком в обязательное чтение НЕ входит: это 33 ноты + **Почему протухло:** Число родилось верным и сгнило дрейфом. На 24.08 (ff03dca — коммит, вписавший фразу) голова, как её определяет титул D-лога («голова D39.124+ (подрезка D39.139)»), была D39.124…D39.156 — РОВНО 33 ноты. Сегодня голова — D39.124…D39.184, то есть 61 нота (а всех живых тел в файле 87 — сверх головы ещё D22, D25, D29, D30 и два десятка D39.25…D39.106). Число занижает объём почти вдвое; вывод «целиком не читать» от этого крепче, но сессия, планирующая бюджет чтения по этому числу, ошибётся вдвое. -**Улика — команда:** `grep -c '^## D' docs/architecture/05-decisions-log.md; grep -o '^## D39\.[0-9]*' docs/architecture/05-decisions-log.md | sed 's/^## D39\.//' | awk '$1+0>=124' | wc -l; git show ff03dca:docs/architecture/05-decisions-log.md | grep -o '^## D39\.[0-9]*' | sed 's/^## D39\.//' | awk '$1+0>=124' | wc -l` -**Улика — вывод:** === TODAY: total ## D headings === + +**Улика — команда:** +``` +grep -c '^## D' docs/architecture/05-decisions-log.md; grep -o '^## D39\.[0-9]*' docs/architecture/05-decisions-log.md | sed 's/^## D39\.//' | awk '$1+0>=124' | wc -l; git show ff03dca:docs/architecture/05-decisions-log.md | grep -o '^## D39\.[0-9]*' | sed 's/^## D39\.//' | awk '$1+0>=124' | wc -l +``` +**Улика — вывод:** +``` +=== TODAY: total ## D headings === 87 === TODAY: head D39.124+ === 61 === на ff03dca (2026-08-24 15:54, коммит фразы) === 33 --- титул D-лога определяет голову одинаково тогда и сейчас: «голова D39.124+ (подрезка D39.139)» --- -**Предложенное действие:** Заменить «это 33 ноты» на форму, которая не гниёт: «это ВСЯ голова D39.124+, сегодня шестой десяток нот». Если число всё же нужно — поставить 61 И завести его шестой сверкой в `docs/scripts/counts.py --check` (скрипт уже считает голову по трём носителям, но литерал в CLAUDE.md не сторожит — именно поэтому он и уехал на 28 нот). +``` + +**Предложенное действие (автора находки):** Заменить «это 33 ноты» на форму, которая не гниёт: «это ВСЯ голова D39.124+, сегодня шестой десяток нот». Если число всё же нужно — поставить 61 И завести его шестой сверкой в `docs/scripts/counts.py --check` (скрипт уже считает голову по трём носителям, но литерал в CLAUDE.md не сторожит — именно поэтому он и уехал на 28 нот). + **Разрушающее:** False --- ## N071 · MINOR · `backend/README.md:20` · B-мёртвый-указатель -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Ингест (txt/epub, кодировки, ruby) + чанкер: `Ingest`/`IngestEncoded` → `Document` +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Ингест (txt/epub, кодировки, ruby) + чанкер: `Ingest`/`IngestEncoded` → `Document` + **Почему протухло:** Экспортированной `chunk.Ingest` не существует — в пакете живёт неэкспортированная `ingest(p string)`, обёртка над `IngestEncoded`. Родилась ошибка в ТОМ ЖЕ коммите e33e3be (25.07), который и разэкспортировал функцию, и написал эту строку README: сессия, грепающая `chunk.Ingest`, получает ноль. -**Улика — команда:** `cd backend && grep -rn '^func [Ii]ngest' internal/chunk/*.go; grep -rn 'chunk\.Ingest' --include=*.go . | head -3; git blame -L 20,20 --date=short README.md | cut -c1-60` -**Улика — вывод:** internal/chunk/ingest.go:67:func ingest(p string) (*Document, error) { + +**Улика — команда:** +``` +cd backend && grep -rn '^func [Ii]ngest' internal/chunk/*.go; grep -rn 'chunk\.Ingest' --include=*.go . | head -3; git blame -L 20,20 --date=short README.md | cut -c1-60 +``` +**Улика — вывод:** +``` +internal/chunk/ingest.go:67:func ingest(p string) (*Document, error) { internal/chunk/ingest.go:77:func IngestEncoded(p, encoding, sourceLang string) (*Document, error) { --- internal/pipeline/events.go:577: doc, err := chunk.IngestEncoded(r.Book.SourceFile, ...) @@ -1402,20 +2648,41 @@ internal/pipeline/contractblockers_test.go:381: doc, err := chunk.IngestEncoded( internal/pipeline/bookbuild_test.go:29: doc, err := chunk.IngestEncoded(epubPath, "", "") --- e33e3be2 (Claude (backend session) 2026-07-25 20) -**Положительный контроль:** Тот же греп в той же области находит живой символ: `grep -rn 'chunk\.Ingest' --include=*.go .` → три вызова `chunk.IngestEncoded` (events.go:577, contractblockers_test.go:381, bookbuild_test.go:29). Пустота относится именно к `Ingest`, а не к инструменту. -**Предложенное действие:** Заменить «`Ingest`/`IngestEncoded`» на «`IngestEncoded` (единственный экспорт; `ingest` — внутренняя терсная обёртка)». +``` +**Положительный контроль:** +``` +Тот же греп в той же области находит живой символ: `grep -rn 'chunk\.Ingest' --include=*.go .` → три вызова `chunk.IngestEncoded` (events.go:577, contractblockers_test.go:381, bookbuild_test.go:29). Пустота относится именно к `Ingest`, а не к инструменту. +``` + +**Предложенное действие (автора находки):** Заменить «`Ingest`/`IngestEncoded`» на «`IngestEncoded` (единственный экспорт; `ingest` — внутренняя терсная обёртка)». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие в целом верно, но формулировка неточна: `ingest` — не «терсная обёртка», а точка входа по расширению (она разветвляется на `ingestTXT`/`ingestEPUB`), и главное — она ТЕСТОВАЯ: вне пакета недостижима, вызывается только из `internal/chunk/ingest_test.go`. Точнее: «`IngestEncoded` → `Document` (единственный экспорт пакета; неэкспортированная `ingest` — вход по умолчанию для тестов пакета)». + +**Разбор опровергателя:** Воспроизведено с положительным контролем. Указатель на несуществующий экспорт, родившийся ошибкой в момент написания. Класс B — подтверждаю, но severity автор завысил: это minor и он сам так его и пометил; на поведение не влияет, стоит одного пустого грепа читателя. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие в целом верно, но формулировка неточна: `ingest` — не «терсная обёртка», а точка входа по расширению (она разветвляется на `ingestTXT`/`ingestEPUB`), и главное — она ТЕСТОВАЯ: вне пакета недостижима, вызывается только из `internal/chunk/ingest_test.go`. Точнее: «`IngestEncoded` → `Document` (единственный экспорт пакета; неэкспортированная `ingest` — вход по умолчанию для тестов пакета)». --- ## N072 · MINOR · `backend/README.md:94` · B-мёртвый-указатель -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** Известный техдолг (не трогать молча; ЕДИНЫЙ трекер — таблица «Бэклог» в `docs/PROGRESS.md`, номера строк = она) +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> Известный техдолг (не трогать молча; ЕДИНЫЙ трекер — таблица «Бэклог» в `docs/PROGRESS.md`, номера строк = она) + **Почему протухло:** README объявляет, что все его «строка N» разрешаются в таблицу «Бэклог», и цитирует тринадцать номеров. Семь из них в таблице отсутствуют — закрыты и по правилу таблицы вынесены: 53, 100, 101, 125, 145, 174, 236 (236 закрыта D39.175, 174 — паком migrate, 100/101/125 — контракт-блокерами). Живы только 49, 49а, 50, 79, 146, 173. Для номеров-провенансов это не техдолг, но обещание разрешимости README даёт всем. -**Улика — команда:** `for id in 49 49а 50 53 79 100 101 125 145 146 173 174 236; do printf '%-5s: ' "$id"; grep -qE "^\| *\*{0,2}${id}\*{0,2} *\|" docs/PROGRESS.md && echo ЖИВА || echo 'НЕТ В ТАБЛИЦЕ'; done` -**Улика — вывод:** 49 : ЖИВА + +**Улика — команда:** +``` +for id in 49 49а 50 53 79 100 101 125 145 146 173 174 236; do printf '%-5s: ' "$id"; grep -qE "^\| *\*{0,2}${id}\*{0,2} *\|" docs/PROGRESS.md && echo ЖИВА || echo 'НЕТ В ТАБЛИЦЕ'; done +``` +**Улика — вывод:** +``` +49 : ЖИВА 49а : ЖИВА 50 : ЖИВА 53 : НЕТ В ТАБЛИЦЕ @@ -1428,151 +2695,292 @@ e33e3be2 (Claude (backend session) 2026-07-25 20) 173 : ЖИВА 174 : НЕТ В ТАБЛИЦЕ 236 : НЕТ В ТАБЛИЦЕ -**Положительный контроль:** Тот же паттерн в той же области находит 189 существующих ID таблицы (`grep -oE '^\| *\*{0,2}[0-9]+[а-я]?\*{0,2} *\|' docs/PROGRESS.md | wc -l` → 189), в том числе шесть из тринадцати цитируемых README — значит пустота по остальным семи не артефакт грепа. -**Предложенное действие:** Добавить в шапку секции одну фразу: «номер, которого в таблице нет, — ЗАКРЫТАЯ строка: её тело ушло в D-ноту, ищи грепом номер в `05-decisions-log.md`». Это дешевле, чем переписывать 7 провенансов, и лечит класс, а не случай. +``` +**Положительный контроль:** +``` +Тот же паттерн в той же области находит 189 существующих ID таблицы (`grep -oE '^\| *\*{0,2}[0-9]+[а-я]?\*{0,2} *\|' docs/PROGRESS.md | wc -l` → 189), в том числе шесть из тринадцати цитируемых README — значит пустота по остальным семи не артефакт грепа. +``` + +**Предложенное действие (автора находки):** Добавить в шапку секции одну фразу: «номер, которого в таблице нет, — ЗАКРЫТАЯ строка: её тело ушло в D-ноту, ищи грепом номер в `05-decisions-log.md`». Это дешевле, чем переписывать 7 провенансов, и лечит класс, а не случай. + **Разрушающее:** False --- ## N073 · MINOR · `backend/docs/MONEY_HONESTY_REPORT.md:787` · C-исполненное-как-открытое -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** Ничего не закоммичено; лендинг за оркестратором. +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> Ничего не закоммичено; лендинг за оркестратором. + **Почему протухло:** Заленджено — коммитом bb541a8 (31.08) и принято нотой D39.182. Фраза стоит в секции, которая сама называет себя «§11. Последний абзац — СОСТОЯНИЕ, не план», а `docs/PROGRESS.md:299-300` отправляет читателя в этот файл как в долговременный носитель — значит абзац читается как текущее состояние. Тот же файл (строка 2) и записка-план (строка 3) указывают на `docs/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md`, которого в `docs/` нет: промт переехал в `docs/archive/prompts/`. -**Улика — команда:** `git log --format='%h %ad %s' --date=short -1 -- backend/docs/MONEY_HONESTY_REPORT.md | cat; grep -n '^## D39.182' docs/architecture/05-decisions-log.md | cut -c1-120; ls docs/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md; ls docs/archive/prompts/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md` -**Улика — вывод:** bb541a8 2026-08-31 Land the disclosure law and its eleven instances: two of the three acceptance blockers were defects inside the cure itself + +**Улика — команда:** +``` +git log --format='%h %ad %s' --date=short -1 -- backend/docs/MONEY_HONESTY_REPORT.md | cat; grep -n '^## D39.182' docs/architecture/05-decisions-log.md | cut -c1-120; ls docs/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md; ls docs/archive/prompts/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md +``` +**Улика — вывод:** +``` +bb541a8 2026-08-31 Land the disclosure law and its eleven instances: two of the three acceptance blockers were defects inside the cure itself 1733:## D39.182 — БЭКЕНД-ПАК «ДЕНЬГИ И ЧЕСТНОСТЬ ВЫДАЧИ» ПРИНЯТ И ЗАЛЕНДЖЕН ls: cannot access 'docs/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md': No such file or directory docs/archive/prompts/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md -**Положительный контроль:** Указатель проверен на живом образце той же формы: `ls docs/ORCHESTRATOR_SESSION_PROMPT.md` резолвится (32182 байта), то есть отсутствие относится к файлу, а не к каталогу или команде. +``` +**Положительный контроль:** +``` +Указатель проверен на живом образце той же формы: `ls docs/ORCHESTRATOR_SESSION_PROMPT.md` резолвится (32182 байта), то есть отсутствие относится к файлу, а не к каталогу или команде. +``` **Другие носители того же факта:** backend/docs/MONEY_HONESTY_PLAN-NOTE.md:3 — тот же мёртвый указатель на промт, backend/docs/MONEY_HONESTY_REPORT.md:2 — тот же указатель, docs/PROGRESS.md:298 — тот же указатель (зона оркестратора) -**Предложенное действие:** Добавить в шапку отчёта (тело по D23.3 не переписывать) дату-баннер: «⚠ Дописано 01.09: пак ЗАЛЕНДЖЕН bb541a8 и принят D39.182; §11 «ничего не закоммичено» — состояние на момент сдачи. Промт переехал в `docs/archive/prompts/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md`.» То же — в шапку записки-плана. + +**Предложенное действие (автора находки):** Добавить в шапку отчёта (тело по D23.3 не переписывать) дату-баннер: «⚠ Дописано 01.09: пак ЗАЛЕНДЖЕН bb541a8 и принят D39.182; §11 «ничего не закоммичено» — состояние на момент сдачи. Промт переехал в `docs/archive/prompts/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md`.» То же — в шапку записки-плана. + **Разрушающее:** False --- ## N074 · MINOR · `backend/docs/SEAM_FIX3_PLAN.md:1` · F-целиком-историческое -**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** # План фикс3 (промт BACKEND_SEAM_FIX3_SESSION_PROMPT.md) +**Срез:** Доки зоны бэкенда: backend/README.md + backend/docs/*.md (9 файлов) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> # План фикс3 (промт BACKEND_SEAM_FIX3_SESSION_PROMPT.md) > Комплектность: `grep -cE '^### 4\.' docs/BACKEND_SEAM_FIX3_SESSION_PROMPT.md` → **6**. + **Почему протухло:** Четыре записки-плана паков шва — чистые списки намерений отработавших сессий. Паки приняты и заленджены (D39.158: «Четыре пака зоны бэкенда приняты и заленджены одним деревом»), а их промты архивированы коммитом 31b96d1 — так что первая строка каждой записки и её команда комплектности указывают в несуществующий файл. Ни один ЖИВОЙ док и ни одна строка кода на эти четыре записки не ссылается: единственные ссылки — из `SEAM_PACK_FINDINGS.md` и из уже архивированных промтов. ⚠ ВАЖНОЕ ИСКЛЮЧЕНИЕ: `SEAM_PACK_FINDINGS.md` в архив НЕ кандидат — на неё ссылается ратифицированная нота D39.158 как на доказательную базу. -**Улика — команда:** `ls docs/BACKEND_SEAM_*; ls docs/archive/prompts/ | grep -i seam; grep -rn 'SEAM_FIX3_PLAN\|SEAM_FIX2_PLAN\|SEAM_FIX_PLAN\|SEAM_PACK_PLAN' --include='*.md' --include='*.go' . | grep -v '^./backend/docs/SEAM' | grep -v '^./docs/archive/'; sed -n '698p' docs/architecture/05-decisions-log.md | cut -c1-160` -**Улика — вывод:** ls: cannot access 'docs/BACKEND_SEAM_*': No such file or directory + +**Улика — команда:** +``` +ls docs/BACKEND_SEAM_*; ls docs/archive/prompts/ | grep -i seam; grep -rn 'SEAM_FIX3_PLAN\|SEAM_FIX2_PLAN\|SEAM_FIX_PLAN\|SEAM_PACK_PLAN' --include='*.md' --include='*.go' . | grep -v '^./backend/docs/SEAM' | grep -v '^./docs/archive/'; sed -n '698p' docs/architecture/05-decisions-log.md | cut -c1-160 +``` +**Улика — вывод:** +``` +ls: cannot access 'docs/BACKEND_SEAM_*': No such file or directory BACKEND_SEAM_FIX2_SESSION_PROMPT_2026-08-27.md BACKEND_SEAM_FIX3_SESSION_PROMPT_2026-08-27.md BACKEND_SEAM_FIX_SESSION_PROMPT_2026-08-27.md BACKEND_SEAM_PACK_SESSION_PROMPT_2026-08-27.md --- ссылки из живых доков/кода: (пусто) --- Четыре пака зоны бэкенда приняты и заленджены одним деревом. Механика и доказательная база — `backend/docs/SEAM_PACK_FINDINGS.md` (вход §26 и §43) -**Положительный контроль:** Инструмент в этой области ссылки находит: тем же грепом видны хиты из `SEAM_PACK_FINDINGS.md` и из `docs/archive/prompts/*`, а `ls docs/ORCHESTRATOR_SESSION_PROMPT.md` подтверждает, что живые промты в `docs/` резолвятся — то есть ноль по `docs/BACKEND_SEAM_*` относится к файлам, а не к команде. +``` +**Положительный контроль:** +``` +Инструмент в этой области ссылки находит: тем же грепом видны хиты из `SEAM_PACK_FINDINGS.md` и из `docs/archive/prompts/*`, а `ls docs/ORCHESTRATOR_SESSION_PROMPT.md` подтверждает, что живые промты в `docs/` резолвятся — то есть ноль по `docs/BACKEND_SEAM_*` относится к файлам, а не к команде. +``` **Другие носители того же факта:** backend/docs/SEAM_FIX_PLAN.md:1 — тот же мёртвый указатель на `docs/BACKEND_SEAM_FIX_SESSION_PROMPT.md`, backend/docs/SEAM_FIX2_PLAN.md:1,3 — на `BACKEND_SEAM_FIX2_SESSION_PROMPT.md`, backend/docs/SEAM_PACK_PLAN.md:3 — на `docs/BACKEND_SEAM_PACK_SESSION_PROMPT.md` -**Предложенное действие:** Вынести ЧЕТЫРЕ записки (SEAM_PACK_PLAN · SEAM_FIX_PLAN · SEAM_FIX2_PLAN · SEAM_FIX3_PLAN) в `docs/archive/` рядом с их промтами, с ⚠-баннером «план отработавшего пака, принят D39.158». `SEAM_PACK_FINDINGS.md` ОСТАВИТЬ на месте — на неё смотрит D39.158; если её всё же двигать, сначала править ноту. + +**Предложенное действие (автора находки):** Вынести ЧЕТЫРЕ записки (SEAM_PACK_PLAN · SEAM_FIX_PLAN · SEAM_FIX2_PLAN · SEAM_FIX3_PLAN) в `docs/archive/` рядом с их промтами, с ⚠-баннером «план отработавшего пака, принят D39.158». `SEAM_PACK_FINDINGS.md` ОСТАВИТЬ на месте — на неё смотрит D39.158; если её всё же двигать, сначала править ноту. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Находка верна, ДЕЙСТВИЕ — нет, в двух местах. (1) Автор объявил SEAM_PACK_FINDINGS.md неприкосновенной, но не заметил, что она же держит ссылки на переносимые файлы: строки 679-680 прямо говорят «Едут вместе с деревом и без них его нельзя принять: `backend/docs/SEAM_FIX_PLAN.md` … `backend/docs/SEAM_PACK_PLAN.md`», плюс :3, :270, :1327. Перенос без правки этих пяти ссылок ПОРОДИТ ровно тот класс дефекта, который находка чинит, — и породит его в доке, куда ратифицированная D39.158 посылает читателя. Значит: либо переносить вместе с обновлением пяти указателей в FINDINGS, либо не переносить вовсе, а поставить каждой записке ⚠-баннер «план отработавшего пака, принят D39.158; промт — `docs/archive/prompts/BACKEND_SEAM_*_2026-08-27.md`» — второе дешевле и по правилам проекта достаточно: баннер И ЕСТЬ механизм легитимности исторического. (2) Зоны: `backend/docs/` — зона БЭКЕНДА, `docs/archive/` — зона оркестратора; перенос файлов из чужой зоны оркестратор в одиночку не делает. Это либо пинг бэкенд-сессии, либо явное согласование. Рекомендую минимальный ход: ⚠-баннеры на месте (правит бэкенд-зона), FINDINGS не трогать вовсе. + +**Разбор опровергателя:** Самый опасный класс проверил жёстче прочих: git grep по всему трекаемому дереву (а не по --include=*.md/*.go, как у автора) даёт тот же результат — живых читателей у четырёх записок нет. Факт «протухла» подтверждаю: шапки указывают в несуществующие файлы, паки приняты D39.158. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Находка верна, ДЕЙСТВИЕ — нет, в двух местах. (1) Автор объявил SEAM_PACK_FINDINGS.md неприкосновенной, но не заметил, что она же держит ссылки на переносимые файлы: строки 679-680 прямо говорят «Едут вместе с деревом и без них его нельзя принять: `backend/docs/SEAM_FIX_PLAN.md` … `backend/docs/SEAM_PACK_PLAN.md`», плюс :3, :270, :1327. Перенос без правки этих пяти ссылок ПОРОДИТ ровно тот класс дефекта, который находка чинит, — и породит его в доке, куда ратифицированная D39.158 посылает читателя. Значит: либо переносить вместе с обновлением пяти указателей в FINDINGS, либо не переносить вовсе, а поставить каждой записке ⚠-баннер «план отработавшего пака, принят D39.158; промт — `docs/archive/prompts/BACKEND_SEAM_*_2026-08-27.md`» — второе дешевле и по правилам проекта достаточно: баннер И ЕСТЬ механизм легитимности исторического. (2) Зоны: `backend/docs/` — зона БЭКЕНДА, `docs/archive/` — зона оркестратора; перенос файлов из чужой зоны оркестратор в одиночку не делает. Это либо пинг бэкенд-сессии, либо явное согласование. Рекомендую минимальный ход: ⚠-баннеры на месте (правит бэкенд-зона), FINDINGS не трогать вовсе. --- ## N075 · MINOR · `docs/PROGRESS.md:3` · B-мёртвый-указатель -**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Контекст заказа (промт `BACKEND_COLDRUN_V16_SESSION_PROMPT.md` отработан, в `archive/prompts/`) +**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Контекст заказа (промт `BACKEND_COLDRUN_V16_SESSION_PROMPT.md` отработан, в `archive/prompts/`) + **Почему протухло:** Промт НЕ в `docs/archive/prompts/` — он до сих пор лежит в корне `docs/`. В архиве промтов лежат два ДРУГИХ холодно-прогонных промта (`BACKEND_COLDRUN_SESSION_PROMPT.md`, `BACKEND_COLDRUN_DEBUG_SESSION_PROMPT.md`), с которыми V16 легко спутать. Тот же дефект у второго отработанного промта: `docs/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md` (пак принят 31.08, D39.182) тоже остался в корне, хотя норма зоны — выносить отработанный промт в архив (ср. `archive/prompts/BACKEND_MONEY_PACK_SESSION_PROMPT_2026-08-29.md`, `.../BACKEND_BOOKWRITER_PACK_SESSION_PROMPT_2026-08-30.md`). -**Улика — команда:** `find . -L -name 'BACKEND_COLDRUN*' -not -path './books/*'; ls docs/BACKEND_*.md` -**Улика — вывод:** ./docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md + +**Улика — команда:** +``` +find . -L -name 'BACKEND_COLDRUN*' -not -path './books/*'; ls docs/BACKEND_*.md +``` +**Улика — вывод:** +``` +./docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md ./docs/archive/prompts/BACKEND_COLDRUN_DEBUG_SESSION_PROMPT.md ./docs/archive/prompts/BACKEND_COLDRUN_SESSION_PROMPT.md ls docs/BACKEND_*.md → docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md docs/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md -**Положительный контроль:** ls docs/archive/prompts/ | grep -i 'v16\|COLDRUN' → BACKEND_COLDRUN_DEBUG_SESSION_PROMPT.md · BACKEND_COLDRUN_SESSION_PROMPT.md · ORCHESTRATOR_SESSION_PROMPT_v16_2026-08-16.md. Каталог существует, содержит и «COLDRUN», и «v16» — пустой результат по V16-промту означает отсутствие файла, а не опечатку в шаблоне. -**Предложенное действие:** Одно из двух, но одно обязательно: либо вынести оба отработанных промта (`docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md` и `docs/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md`) в `docs/archive/prompts/` с датой в имени, как принято, и оставить указатель шапки как есть; либо оставить файлы на месте и исправить текст на «промт отработан, лежит в `docs/`». +``` +**Положительный контроль:** +``` +ls docs/archive/prompts/ | grep -i 'v16\|COLDRUN' → BACKEND_COLDRUN_DEBUG_SESSION_PROMPT.md · BACKEND_COLDRUN_SESSION_PROMPT.md · ORCHESTRATOR_SESSION_PROMPT_v16_2026-08-16.md. Каталог существует, содержит и «COLDRUN», и «v16» — пустой результат по V16-промту означает отсутствие файла, а не опечатку в шаблоне. +``` + +**Предложенное действие (автора находки):** Одно из двух, но одно обязательно: либо вынести оба отработанных промта (`docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md` и `docs/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md`) в `docs/archive/prompts/` с датой в имени, как принято, и оставить указатель шапки как есть; либо оставить файлы на месте и исправить текст на «промт отработан, лежит в `docs/`». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Исполнять только по ОДНОМУ файлу. Действие: либо перенести docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md в docs/archive/prompts/BACKEND_COLDRUN_V16_SESSION_PROMPT_2026-08-31.md (дата — по отчёту COLDRUN_V16_REPORT_2026-08-31.md) и оставить текст шапки как есть, либо оставить файл на месте и написать «промт отработан, лежит в `docs/`». Про BACKEND_MONEY_HONESTY_SESSION_PROMPT.md НИЧЕГО не делать — он уже в docs/archive/prompts/ (bb541a8, 31.08); попытка «вынести его в архив» ничего не найдёт. Также сверить строку промта в docs/README.md, если она называет путь: перенос сделает её мёртвым указателем. + +**Разбор опровергателя:** Ядро находки воспроизведено: указатель шапки «в archive/prompts/» ведёт в пустоту, файл лежит в docs/, и положительный контроль доказывает, что каталог архива грепается и содержит однокоренные имена (спутать V16 с двумя другими COLDRUN-промтами действительно легко). Но ВТОРАЯ половина находки опровергнута деревом: BACKEND_MONEY_HONESTY_SESSION_PROMPT.md в корне docs/ отсутствует с 31.08 (коммит bb541a8) — автор находки читал устаревшее состояние, ровно ловушка «истина — рабочее дерево». + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, и лучше исполнить именно вторую, критериальную его половину: заменить ручной счёт на «ни один пак платформы после P7 этот долг не взял (проверка: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` — пусто)», чтобы число не гнило при каждом следующем лендинге. Если счёт всё-таки оставлять — «шесть паков (P8-FIX · P8-REVIEW · P9 · P10 · P11 · P12), плюс непронумерованный `sqlc`». --- ## N076 · MINOR · `docs/PROGRESS.md:3` · A-дрейф -**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** «развязка git с origin» ОПРОВЕРГНУТА — `main` и `origin/main` на одном коммите. +**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> «развязка git с origin» ОПРОВЕРГНУТА — `main` и `origin/main` на одном коммите. + **Почему протухло:** Сегодня `main` опережает `origin/main` на 48 коммитов. Несущий вывод («развязки нет») выжил — `origin/main` остаётся ПРЕДКОМ `main`, расхождения по-прежнему нет, — но предъявленная улика «на одном коммите» ложна, и читатель шапки заключает, что всё запушено. Замер помечен «проверено деревом 30.08»: с тех пор легло 48 коммитов, ни один не запушен. -**Улика — команда:** `git rev-parse --short main origin/main; git rev-list --left-right --count main...origin/main; git merge-base --is-ancestor origin/main main && echo 'origin/main — ПРЕДОК main'` -**Улика — вывод:** 162ab9b + +**Улика — команда:** +``` +git rev-parse --short main origin/main; git rev-list --left-right --count main...origin/main; git merge-base --is-ancestor origin/main main && echo 'origin/main — ПРЕДОК main' +``` +**Улика — вывод:** +``` +162ab9b 8977001 48 0 origin/main — ПРЕДОК main git log -1 --date=short --format='%h %ad %s' origin/main → 8977001 2026-08-30 Hand the orchestrator role over with the shift's seven own errors named in full +``` **Другие носители того же факта:** docs/PROGRESS.md:11 — в листе владельца тот же предмет стоит ОТКРЫТЫМ: «**развязка git с origin** (локальная линия ИСТИННА, force-push его рукой; не пуллить)». Две половины шапки говорят о развязке разное: строка 3 объявляет её опровергнутой, строка 11 держит её на владельце. -**Предложенное действие:** Заменить улику на измеримую и датированную: «`origin/main` — ПРЕДОК `main`, расхождения нет; локально не запушено N коммитов (замер <дата>, `git rev-list --left-right --count main...origin/main`)». Одновременно свести со строкой 11: либо снять пункт с листа владельца, либо переписать вывод строки 3 как «развязки нет, но линия не запушена». + +**Предложенное действие (автора находки):** Заменить улику на измеримую и датированную: «`origin/main` — ПРЕДОК `main`, расхождения нет; локально не запушено N коммитов (замер <дата>, `git rev-list --left-right --count main...origin/main`)». Одновременно свести со строкой 11: либо снять пункт с листа владельца, либо переписать вывод строки 3 как «развязки нет, но линия не запушена». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Первую половину предложенного действия принять: заменить улику на «origin/main — ПРЕДОК main, расхождения нет; не запушено 48 коммитов (замер 01.09, `git rev-list --left-right --count main...origin/main`)». ВТОРУЮ половину — «либо снять пункт с листа владельца» — НЕ исполнять: противоречия между строками 3 и 11 нет. Строка 3 говорит «расхождения (развилки) нет», строка 11 держит на владельце ДЕЙСТВИЕ (force-push его рукой, не пуллить), и замер это действие подтверждает как всё ещё нужное — 48 неопубликованных коммитов. Снятие пункта с листа владельца потеряло бы живое обязательство. + +**Разбор опровергателя:** Опровергнуть не удалось, и я закрыл единственную лазейку, о которой автор не подумал: «origin/main» мог быть протухшим tracking-ref'ом — сходил ls-remote (сетевое чтение, $0), живой удалённый main тоже 8977001. Улика шапки «на одном коммите» сегодня ложна: расхождение 48/0. Несущий вывод («развязки нет») выжил — origin/main остаётся предком main. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, и лучше исполнить именно вторую, критериальную его половину: заменить ручной счёт на «ни один пак платформы после P7 этот долг не взял (проверка: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` — пусто)», чтобы число не гнило при каждом следующем лендинге. Если счёт всё-таки оставлять — «шесть паков (P8-FIX · P8-REVIEW · P9 · P10 · P11 · P12), плюс непронумерованный `sqlc`». --- ## N077 · MINOR · `docs/PROGRESS.md:3` · A-дрейф -**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Долг числится с P8 (`platform/BACKLOG.md` П-17), с тех пор легло пять паков и ни один его не взял. +**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Долг числится с P8 (`platform/BACKLOG.md` П-17), с тех пор легло пять паков и ни один его не взял. + **Почему протухло:** «Пять» было верно до 31.08 (P8-FIX · P8-REVIEW · P9 · P10 · P11). После P7, где долг выдачи был отложен в P8, по реестру нот легло ШЕСТЬ P-нумерованных паков платформы (добавился P12, D39.180, 31.08) плюс непронумерованный пак `sqlc` (D39.172) — семь лендингов. Лендинг P12 объявлен В ЭТОЙ ЖЕ строке шапки, а счётчик паков рядом не пересчитан. Сам указатель на П-17 при этом ВЕРЕН — его выходная ячейка дословно несёт «НЕ вошло и отложено в P8: `updateBook`/`deleteBook`/`getRun`, экспорт (`createExport`/`getExport`), эскроу (П-18)». -**Улика — команда:** `grep -nE '\| D39\.(15[0-9]|16[0-9]|17[0-9]|18[0-9]) \|' docs/architecture/05-decisions-index.md | grep -iE 'пак P|P8|P9|P10|P11|P12|sqlc'` -**Улика — вывод:** 218:| D39.154 | 22.08 | Приёмка и лендинг P8-FIX платформы ... + +**Улика — команда:** +``` +grep -nE '\| D39\.(15[0-9]|16[0-9]|17[0-9]|18[0-9]) \|' docs/architecture/05-decisions-index.md | grep -iE 'пак P|P8|P9|P10|P11|P12|sqlc' +``` +**Улика — вывод:** +``` +218:| D39.154 | 22.08 | Приёмка и лендинг P8-FIX платформы ... 223:| D39.159 | 27.08 | Пак P8-REVIEW принят ... 226:| D39.162 | 28.08 | **Платформенный пак P9 принят и заленджен + контрактный минор 0.6.0** ... 230:| D39.166 | 28.08 | **Пак P10 принят и заленджен + контрактный минор 0.7.0** ... 233:| D39.169 | 29.08 | **Платформенный пак P11 принят и заленджен + контрактный минор 0.8.0** ... 236:| D39.172 | 29.08 | **Пак `sqlc` принят и заленджен** ... 244:| D39.180 | 31.08 | **Пак платформы P12 «долги под ногами» ПРИНЯТ И ЗАЛЕНДЖЕН** ... -**Предложенное действие:** Заменить «пять паков» на «шесть паков (P8-FIX · P8-REVIEW · P9 · P10 · P11 · P12), плюс непронумерованный `sqlc`». Ещё лучше — снять счёт руками и написать критерий («ни один пак платформы после P7 его не взял»), чтобы число не гнило при каждом лендинге. +``` + +**Предложенное действие (автора находки):** Заменить «пять паков» на «шесть паков (P8-FIX · P8-REVIEW · P9 · P10 · P11 · P12), плюс непронумерованный `sqlc`». Ещё лучше — снять счёт руками и написать критерий («ни один пак платформы после P7 его не взял»), чтобы число не гнило при каждом лендинге. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно, и лучше исполнить именно вторую, критериальную его половину: заменить ручной счёт на «ни один пак платформы после P7 этот долг не взял (проверка: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` — пусто)», чтобы число не гнило при каждом следующем лендинге. Если счёт всё-таки оставлять — «шесть паков (P8-FIX · P8-REVIEW · P9 · P10 · P11 · P12), плюс непронумерованный `sqlc`». + +**Разбор опровергателя:** Опровергнуть не удалось. «Пять» было верно ровно до 31.08; лендинг P12 объявлен В ТОЙ ЖЕ строке шапки, а счётчик рядом не пересчитан — классический дрейф от ручного счёта. Заодно я проверил то, что автор находки принял на веру: указатель П-17 жив и несёт названный текст, а вывод «ни один его не взял» подтверждён кодом при непустом положительном контроле. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, и лучше исполнить именно вторую, критериальную его половину: заменить ручной счёт на «ни один пак платформы после P7 этот долг не взял (проверка: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` — пусто)», чтобы число не гнило при каждом следующем лендинге. Если счёт всё-таки оставлять — «шесть паков (P8-FIX · P8-REVIEW · P9 · P10 · P11 · P12), плюс непронумерованный `sqlc`». --- ## N078 · MINOR · `docs/PROGRESS.md:15` · C-исполненное-как-открытое -**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** ~$0.85/ранобэ (D30.4, пере-калибровка при следующем платном прогоне) +**Срез:** ГОЛОВА журнала: CURRENT-STATE и очередь — docs/PROGRESS.md, строки 1–18 (шапка ⟶ ТЕКУЩЕЕ СОСТОЯНИЕ, ~34 КБ) +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> ~$0.85/ранобэ (D30.4, пере-калибровка при следующем платном прогоне) + **Почему протухло:** Условие, по которому число обещано пере-калибровать, ИСПОЛНИЛОСЬ: платный прогон отработал 31.08 и дал собственную оценку движка $1.1377277707 на десяти главах при куплённых $0.436110 (об этом сообщает та же шапка строкой 3). Триггер нажат, пере-калибровка не сделана и нигде не отмечена, а формулировка «при следующем платном прогоне» отсылает читателя в будущее, которое уже прошло. Само число $0.85 я НЕ объявляю ложным: единица измерения («ранобэ» целиком) и десять глав прогона несопоставимы напрямую — опровергнут именно статус ожидания. -**Улика — команда:** `sed -n '15p' docs/PROGRESS.md | grep -o '~\$0\.85/ранобэ ([^)]*)'; sed -n '69,72p' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md` -**Улика — вывод:** ~$0.85/ранобэ (D30.4, пере-калибровка при следующем платном прогоне) + +**Улика — команда:** +``` +sed -n '15p' docs/PROGRESS.md | grep -o '~\$0\.85/ранобэ ([^)]*)'; sed -n '69,72p' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md +``` +**Улика — вывод:** +``` +~$0.85/ранобэ (D30.4, пере-калибровка при следующем платном прогоне) Полная цепь на десяти главах в СЕГОДНЯШНИХ ценах стоит **≈$1.14 по собственной оценке движка** (`projected_book_usd = 1.1377277707`) против приора ≈$0.58 и санкции $0.80. Прогон остановлен **не потолком, а снапшот-гардом**, потребовавшим `--resnapshot` после покупки двумя порциями. Куплено **$0.436110**, доставлено **3 юнита из 14** (черновая волна 14/14, редакторская 3/14). +``` **Другие носители того же факта:** docs/STACK.md:21 — «~$0.85/ранобэ (⚠ число ДО пере-пина цен 13–15.08 ... пере-калибровать первым же платным прогоном ... пере-калибровка при следующем платном прогоне (112 залендена, D39.97))» — тот же просроченный триггер, названный ДВАЖДЫ в одной ячейке, docs/product-requirements.md:32 — ПТ-12 «Низкий COGS» со статусом ✅ и тем же числом ~$0.85/ранобэ -**Предложенное действие:** Во всех трёх носителях заменить обещание на факт: «платный прогон отработал 31.08 (`archive/reports/COLDRUN_V16_REPORT_2026-08-31.md`), собственная оценка движка — $1.1377 на десяти главах gu-zhenren при куплённых $0.436110; пере-калибровка $/ранобэ НЕ сделана — завести строку бэклога». Число $0.85 не трогать, пока не будет замера в тех же единицах. + +**Предложенное действие (автора находки):** Во всех трёх носителях заменить обещание на факт: «платный прогон отработал 31.08 (`archive/reports/COLDRUN_V16_REPORT_2026-08-31.md`), собственная оценка движка — $1.1377 на десяти главах gu-zhenren при куплённых $0.436110; пере-калибровка $/ранобэ НЕ сделана — завести строку бэклога». Число $0.85 не трогать, пока не будет замера в тех же единицах. + **Разрушающее:** False --- ## N079 · MINOR · `docs/PROGRESS.md:146` · C-исполненное-как-открытое -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** скоро (гейт: первый холодный прогон, §6 отчёта) +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> скоро (гейт: первый холодный прогон, §6 отчёта) + **Почему протухло:** Гейт пал: первый холодный прогон отработал 31.08 и выдал ровно то число, на котором строка условила решение — `families_held=0` (строка требует ослаблять гард «если families_held велик»). Ответ получен и он отрицательный: ослаблять нечего. Открытым может остаться только пере-подтверждение `family_max_members=24` на ДЛИННОЙ книге — прогон был десятиглавным. -**Улика — команда:** `grep -n 'families=11' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md` -**Улика — вывод:** 448:families=11 families_refused=0 families_held=0`. ⚠ Есть ВТОРАЯ строка про семейства с ДРУГИМИ именами + +**Улика — команда:** +``` +grep -n 'families=11' docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md +``` +**Улика — вывод:** +``` +448:families=11 families_refused=0 families_held=0`. ⚠ Есть ВТОРАЯ строка про семейства с ДРУГИМИ именами 449:полей (`refused_by_cap`/`held_by_series`) — греп по `families_refused` её не найдёт; здесь не печаталась. +``` **Другие носители того же факта:** docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md §9 (подписано «Строка 154 — ЗАКРЫТА числами» — крест-накрест с таблицей) -**Предложенное действие:** Сузить строку 157: записать замер `families=11 · families_refused=0 · families_held=0` (10 глав, 31.08) и вывод «ослаблять гард не нужно»; живым остатком оставить только пере-подтверждение `family_max_members=24` на длинной книге, с новым гейтом вместо отработавшего. + +**Предложенное действие (автора находки):** Сузить строку 157: записать замер `families=11 · families_refused=0 · families_held=0` (10 глав, 31.08) и вывод «ослаблять гард не нужно»; живым остатком оставить только пере-подтверждение `family_max_members=24` на длинной книге, с новым гейтом вместо отработавшего. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно, но сужать надо аккуратнее, чем написано: `families_held=0` замерен на ДЕСЯТИГЛАВНОЙ книге, а строка требует пере-подтверждения на ДЛИННОЙ — значит вывод «ослаблять гард не нужно» пишется с явной оговоркой «на 10 главах», иначе он сам протухнет на первой длинной книге. Тем же касанием починить указатель гейта «§6 отчёта»: в отчёте холодного прогона §6 — это «Две ловушки `build`», числа семейств в §9; ставить греп-указатель (`families_held` по отчёту), а не номер секции. + +**Разбор опровергателя:** Числа я взял НЕ из отчёта, а из собственного stderr прогона на диске (через симлинк `books/`, с `-h` по логам) — `families=11 families_refused=0 families_held=0`, и эмиттер этих полей найден в коде (`terminologist.go:479`), то есть имена не выдуманы. Гейт «первый холодный прогон» пал, ответ получен и он отрицательный: ослаблять гард не от чего. Дополнительно нашёл то, чего находка не назвала: указатель «§6 отчёта» в колонке гейта при чтении через отчёт холодного прогона МЁРТВ — §6 там «Две ловушки `build`», а числа семейств лежат в §9. Крест-накрест подписи (§9 зовёт эти числа «Строкой 154») подтверждаю. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, но сужать надо аккуратнее, чем написано: `families_held=0` замерен на ДЕСЯТИГЛАВНОЙ книге, а строка требует пере-подтверждения на ДЛИННОЙ — значит вывод «ослаблять гард не нужно» пишется с явной оговоркой «на 10 главах», иначе он сам протухнет на первой длинной книге. Тем же касанием починить указатель гейта «§6 отчёта»: в отчёте холодного прогона §6 — это «Две ловушки `build`», числа семейств в §9; ставить греп-указатель (`families_held` по отчёту), а не номер секции. --- ## N080 · MINOR · `docs/PROGRESS.md:183` · B-мёртвый-указатель -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** `unspecified` ратифицирован в каноне (`openapi.yaml:1784-1787`) +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> `unspecified` ратифицирован в каноне (`openapi.yaml:1784-1787`) + **Почему протухло:** Оба line-якоря строки 203 уехали после контрактного минора 0.9.0 (P12, `0a680a3`, 31.08). `openapi.yaml:1784-1787` сегодня показывает поля `id`/`units_done` схемы прогресса; ратификация `unspecified` живёт на 1903–1906. Соседний якорь `README.md:1376` («клетка приложения А заполнена `unspecified`») показывает текст про идемпотентность; настоящие цели — `README.md:1361` (ратификация) и `:1700` (клетка таблицы). ⚠ Этот же якорь строка уже чинила 20.08 («первая редакция писала `:1391`») — гниёт вторично. Третий якорь строки, `README.md:559`, ПРОВЕРЕН и верен. -**Улика — команда:** `sed -n '1784,1787p' docs/architecture/14-api-contract/openapi.yaml; grep -n 'unspecified' docs/architecture/14-api-contract/openapi.yaml | head -3; sed -n '1376p' docs/architecture/14-api-contract/README.md; grep -n 'unspecified' docs/architecture/14-api-contract/README.md | head -3` -**Улика — вывод:** id: { $ref: '#/components/schemas/Id' } + +**Улика — команда:** +``` +sed -n '1784,1787p' docs/architecture/14-api-contract/openapi.yaml; grep -n 'unspecified' docs/architecture/14-api-contract/openapi.yaml | head -3; sed -n '1376p' docs/architecture/14-api-contract/README.md; grep -n 'unspecified' docs/architecture/14-api-contract/README.md | head -3 +``` +**Улика — вывод:** +``` + id: { $ref: '#/components/schemas/Id' } units_done: type: integer minimum: 0 @@ -1584,49 +2992,91 @@ git log -1 --date=short --format='%h %ad %s' origin/main → --- 1361:сервер обязан прислать значение, которого словарь не содержит. `unspecified` ратифицирован как 1700:| незнакомая причина | 8 (по умолчанию) | **`unspecified`** (обязанность сервера, п.5; клиент рисует ту же нейтральную фразу, что и на незнакомый код) | -**Предложенное действие:** Пере-нацелить два якоря строки 203: `openapi.yaml:1784-1787` → `:1903-1906`, `README.md:1376` → `:1361` (ратификация) или `:1700` (клетка приложения А). ⚠ Лучше сразу на форму без номера (греп отличительного токена `unspecified is reserved`), иначе следующий контрактный минор убьёт их в третий раз. +``` + +**Предложенное действие (автора находки):** Пере-нацелить два якоря строки 203: `openapi.yaml:1784-1787` → `:1903-1906`, `README.md:1376` → `:1361` (ратификация) или `:1700` (клетка приложения А). ⚠ Лучше сразу на форму без номера (греп отличительного токена `unspecified is reserved`), иначе следующий контрактный минор убьёт их в третий раз. + **Разрушающее:** False --- ## N081 · MINOR · `docs/PROGRESS.md:193` · B-мёртвый-указатель -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** «declining a surface removes EVERY window of that surface» (`docs/architecture/14-api-contract/openapi.yaml:2051`) +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> «declining a surface removes EVERY window of that surface» (`docs/architecture/14-api-contract/openapi.yaml:2051`) + **Почему протухло:** Якорь уехал на 28 строк после контрактного минора 0.9.0 (P12, 31.08): цитируемая фраза стоит на `:2079`, а `:2051` показывает заголовок совсем другой схемы `BankCorrection:`. Цитата верна, адрес — нет. -**Улика — команда:** `sed -n '2051p' docs/architecture/14-api-contract/openapi.yaml; grep -n 'removes EVERY window of that surface' docs/architecture/14-api-contract/openapi.yaml` -**Улика — вывод:** BankCorrection: + +**Улика — команда:** +``` +sed -n '2051p' docs/architecture/14-api-contract/openapi.yaml; grep -n 'removes EVERY window of that surface' docs/architecture/14-api-contract/openapi.yaml +``` +**Улика — вывод:** +``` + BankCorrection: --- 2079: the old one standing, and declining a surface removes EVERY window of that surface. -**Предложенное действие:** Пере-нацелить якорь строки 228 с `openapi.yaml:2051` на `:2079`, а лучше — на греп-форму по самой цитате, поскольку канон движется каждым минором. +``` + +**Предложенное действие (автора находки):** Пере-нацелить якорь строки 228 с `openapi.yaml:2051` на `:2079`, а лучше — на греп-форму по самой цитате, поскольку канон движется каждым минором. + **Разрушающее:** False --- ## N082 · MINOR · `docs/PROGRESS.md:207` · C-исполненное-как-открытое -**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** новые файлы стейджить явно и проверять состав `git show --name-only`, а не доверять форме +**Срез:** ЕДИНЫЙ БЭКЛОГ docs/PROGRESS.md — таблица (строки 57–272 файла), проверка на класс C «исполненное как открытое» +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> новые файлы стейджить явно и проверять состав `git show --name-only`, а не доверять форме + **Почему протухло:** Строка исполнена НАПОЛОВИНУ, и строка об этом не знает: 31.08 коммитом `94381bb` в `docs/scripts/githooks/pre-commit` добавлен warn-блок, который перед каждым коммитом печатает неотслеживаемые файлы в каталогах коммита и дословно советует ту самую норму («git add -- <путь>, затем проверь состав: git show --name-only»). Открытой осталась ровно одна половина — строка в `CLAUDE.md` §«Git-координация мультисессий», которой там действительно нет. -**Улика — команда:** `grep -n 'неотслеживаемые файлы в каталогах ЭТОГО коммита\|git show --name-only' docs/scripts/githooks/pre-commit; grep -n 'неотслеж\|untracked\|show --name-only' CLAUDE.md` -**Улика — вывод:** 115: echo "pre-commit ⚠ неотслеживаемые файлы в каталогах ЭТОГО коммита — pathspec-форма их РОНЯЕТ:" >&2 + +**Улика — команда:** +``` +grep -n 'неотслеживаемые файлы в каталогах ЭТОГО коммита\|git show --name-only' docs/scripts/githooks/pre-commit; grep -n 'неотслеж\|untracked\|show --name-only' CLAUDE.md +``` +**Улика — вывод:** +``` +115: echo "pre-commit ⚠ неотслеживаемые файлы в каталогах ЭТОГО коммита — pathspec-форма их РОНЯЕТ:" >&2 117: echo ' Нужны в коммите? git add -- <путь>, затем проверь состав: git show --name-only' >&2 --- (по CLAUDE.md — НИ ОДНОГО вхождения) -**Положительный контроль:** grep -n 'pathspec' CLAUDE.md → 80: «…снимай не чужое из индекса, а свою задачу — коммить pathspec-формой.» — греп по CLAUDE.md в этой области содержимое находит, значит пустота по «неотслеживаемым» — факт. +``` +**Положительный контроль:** +``` +grep -n 'pathspec' CLAUDE.md → 80: «…снимай не чужое из индекса, а свою задачу — коммить pathspec-формой.» — греп по CLAUDE.md в этой области содержимое находит, значит пустота по «неотслеживаемым» — факт. +``` **Другие носители того же факта:** docs/scripts/githooks/pre-commit:99-121 (построенная половина), CLAUDE.md §«Git-координация мультисессий» (носитель незакрытой половины) -**Предложенное действие:** Дописать в строку 245, что машинная половина УЖЕ стоит (`docs/scripts/githooks/pre-commit`, `94381bb`, warn-only), и сузить остаток до одной фразы в `CLAUDE.md`, которую вписывает владелец; иначе следующая сессия построит хук второй раз. + +**Предложенное действие (автора находки):** Дописать в строку 245, что машинная половина УЖЕ стоит (`docs/scripts/githooks/pre-commit`, `94381bb`, warn-only), и сузить остаток до одной фразы в `CLAUDE.md`, которую вписывает владелец; иначе следующая сессия построит хук второй раз. + **Разрушающее:** False --- ## N083 · MINOR · `docs/README.md:20` · A-дрейф -**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **`--lint` якорей — при коммите, задевающем ЛЮБОЙ док или `CLAUDE.md`; `--check` чисел и головы — при коммите с D-логом или PROGRESS** +**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **`--lint` якорей — при коммите, задевающем ЛЮБОЙ док или `CLAUDE.md`; `--check` чисел и головы — при коммите с D-логом или PROGRESS** + **Почему протухло:** Половина фразы верна, половина протухла. `--check` действительно гейчен составом коммита (`docs/scripts/githooks/pre-commit:74`). `--lint` НЕ гейчен ничем: блок 3 (строки 89–97) исполняется при ЛЮБОМ непустом индексе — единственный ранний выход хука это `[ -z "$staged" ] && exit 0` на строке 37. Сам хук объясняет отмену гейта на строках 42–47. Следствие уже живёт строкой бэклога 244 («гейт якорей ломается всякий раз, когда зона правит КОД…»): то есть проект знает поведение, а карта всё ещё обещает старое. -**Улика — команда:** `grep -n 'printf .%s\\n. "$staged" | grep' docs/scripts/githooks/pre-commit; sed -n '86,97p' docs/scripts/githooks/pre-commit; sed -n '42,44p' docs/scripts/githooks/pre-commit` -**Улика — вывод:** 52:if printf '%s\n' "$staged" | grep -qx "$dlog"; then + +**Улика — команда:** +``` +grep -n 'printf .%s\\n. "$staged" | grep' docs/scripts/githooks/pre-commit; sed -n '86,97p' docs/scripts/githooks/pre-commit; sed -n '42,44p' docs/scripts/githooks/pre-commit +``` +**Улика — вывод:** +``` +52:if printf '%s\n' "$staged" | grep -qx "$dlog"; then 74:if printf '%s\n' "$staged" | grep -qE "^($dlog|$prog)$"; then --- блок 3, строки 86-90 (ГЕЙТА ПО СОСТАВУ НЕТ) --- # 3) Якоря file:line живых доков (D39.148 …) @@ -1634,20 +3084,36 @@ if command -v python3 >/dev/null 2>&1 && [ -f docs/scripts/counts.py ]; then if ! out=$(python3 docs/scripts/counts.py --lint --from-index 2>&1); then --- строки 42-44 --- # ⚠ БЫСТРОГО ВЫХОДА ПО «коммит не задевает docs/» БОЛЬШЕ НЕТ (21.08, разбор с внешним ревьюером). -**Положительный контроль:** grep по условной форме `printf '%s\n' "$staged" | grep` находит ДВА гейта — строки 52 и 74 (блоки «голова» и «числа»). Значит инструмент такие условия в этом файле видит, и их отсутствие перед блоком 3 — реальное, а не промах грепа. +``` +**Положительный контроль:** +``` +grep по условной форме `printf '%s\n' "$staged" | grep` находит ДВА гейта — строки 52 и 74 (блоки «голова» и «числа»). Значит инструмент такие условия в этом файле видит, и их отсутствие перед блоком 3 — реальное, а не промах грепа. +``` **Другие носители того же факта:** docs/scripts/githooks/pre-commit:6 (собственная шапка хука несёт то же устаревшее описание: «при коммите, задевающем ЛЮБОЙ док, проверяет file:line-якоря») -**Предложенное действие:** Заменить на: «`--lint` якорей — при ЛЮБОМ коммите с непустым индексом, включая коммиты чистого кода (гейт по составу снят 21.08 — цели якорей живут в коде, и именно кодовый коммит их двигает; шумовое следствие — строка бэклога 244); `--check` чисел и головы — только при коммите с D-логом или PROGRESS». Ту же поправку внести в шапку-комментарий `docs/scripts/githooks/pre-commit:6`. + +**Предложенное действие (автора находки):** Заменить на: «`--lint` якорей — при ЛЮБОМ коммите с непустым индексом, включая коммиты чистого кода (гейт по составу снят 21.08 — цели якорей живут в коде, и именно кодовый коммит их двигает; шумовое следствие — строка бэклога 244); `--check` чисел и головы — только при коммите с D-логом или PROGRESS». Ту же поправку внести в шапку-комментарий `docs/scripts/githooks/pre-commit:6`. + **Разрушающее:** False --- ## N084 · MINOR · `docs/README.md:31` · A-дрейф -**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Отработанные промты — `archive/prompts/`, отчёты с ревью-шапками — `archive/reports/`. +**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Отработанные промты — `archive/prompts/`, отчёты с ревью-шапками — `archive/reports/`. + **Почему протухло:** Правило не исполнено ровно для одного файла — и это тот файл, который холодная бэкенд-сессия увидит первым в `ls docs/`. `BACKEND_COLDRUN_V16_SESSION_PROMPT.md` объявлен отработавшим в этой же таблице (строка 25: «Прежний пак — холодный прогон v16 (**D39.179**): отработал…»), его отчёт уже лежит в `docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md`, а сам промт остался в `docs/` и в `archive/prompts/` не переезжал ни разу. То есть расхождение РОВНО ОДНО и только в эту сторону: все четыре промта, названные таблицей активными, физически лежат в `docs/`, а ни один файл из `archive/prompts/` активным не объявлен. -**Улика — команда:** `ls docs/*.md; ls docs/archive/prompts/ | grep -ci COLDRUN_V16; ls docs/archive/prompts/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md docs/archive/prompts/PLATFORM_P12_SESSION_PROMPT.md; git log --oneline --follow -- docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md` -**Улика — вывод:** docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md (55493 байт, Aug 31 01:28) + +**Улика — команда:** +``` +ls docs/*.md; ls docs/archive/prompts/ | grep -ci COLDRUN_V16; ls docs/archive/prompts/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md docs/archive/prompts/PLATFORM_P12_SESSION_PROMPT.md; git log --oneline --follow -- docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md +``` +**Улика — вывод:** +``` +docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md (55493 байт, Aug 31 01:28) --- в archive/prompts его нет --- 0 --- POSITIVE CONTROL: промты, отработавшие в тот же день 31.08, переехали --- @@ -1655,21 +3121,42 @@ docs/archive/prompts/BACKEND_MONEY_HONESTY_SESSION_PROMPT.md docs/archive/prompts/PLATFORM_P12_SESSION_PROMPT.md --- история файла: один коммит (выдача), переезда нет --- 8e475fa Issue the cold-run prompt under the owner's sanctioned budget and repair the money doc … -**Положительный контроль:** `ls docs/archive/prompts/ | grep -i coldrun` находит ДВА других coldrun-промта (BACKEND_COLDRUN_DEBUG_SESSION_PROMPT.md, BACKEND_COLDRUN_SESSION_PROMPT.md), а промты обоих паков 31.08 лежат там же. Значит каталог грепается и переезды в него реально происходят — ноль по COLDRUN_V16 не артефакт грепа. +``` +**Положительный контроль:** +``` +`ls docs/archive/prompts/ | grep -i coldrun` находит ДВА других coldrun-промта (BACKEND_COLDRUN_DEBUG_SESSION_PROMPT.md, BACKEND_COLDRUN_SESSION_PROMPT.md), а промты обоих паков 31.08 лежат там же. Значит каталог грепается и переезды в него реально происходят — ноль по COLDRUN_V16 не артефакт грепа. +``` **Другие носители того же факта:** docs/README.md:25 — та же таблица объявляет пак v16 отработавшим, docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:51 — адресует промт по нынешнему пути `docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md` -**Предложенное действие:** Вынести `docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md` в `docs/archive/prompts/` ОДНИМ коммитом и тем же коммитом поправить ссылку `docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:51`. ⚠ Прежде чем двигать — сверить, закрыта ли развилка владельца «довести ≈$1.00 / остановиться» (README:25 говорит — НА ВЛАДЕЛЬЦЕ): если не закрыта, переезд НЕ делать, а вместо этого дописать в строке 25 явное «промт отработал, но ФИЗИЧЕСКИ ещё в `docs/` — ждёт слова владельца по развилке», чтобы холодная сессия не приняла его за свой. + +**Предложенное действие (автора находки):** Вынести `docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md` в `docs/archive/prompts/` ОДНИМ коммитом и тем же коммитом поправить ссылку `docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:51`. ⚠ Прежде чем двигать — сверить, закрыта ли развилка владельца «довести ≈$1.00 / остановиться» (README:25 говорит — НА ВЛАДЕЛЬЦЕ): если не закрыта, переезд НЕ делать, а вместо этого дописать в строке 25 явное «промт отработал, но ФИЗИЧЕСКИ ещё в `docs/` — ждёт слова владельца по развилке», чтобы холодная сессия не приняла его за свой. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Переезд ДЕЛАТЬ — оговорка «если развилка не закрыта, не двигать» снята замером: закрыта. Одним коммитом pathspec-формой: (1) `git mv` промта в docs/archive/prompts/ — проверено, что переезд link-safe: внутри промта НОЛЬ относительных markdown-ссылок (`grep -cE '\]\(\.\.?/'` → 0, тот же ноль у уже переехавшего money-промта), все пути — репо-корневые в бэктиках, так что норма пере-углубления D39.80 здесь не срабатывает; (2) тем же коммитом починить README:25, где ДВЕ протухшие половины, а не ноль: «развилка … НА ВЛАДЕЛЬЦЕ» → «закрыта словом владельца «не доводить» 31.08, $0.436110 из $0.80 (D39.184)» и «отчёт ждёт лендинга» → «отчёт заленджен, `archive/reports/COLDRUN_V16_REPORT_2026-08-31.md`». (3) docs/PROGRESS.md:3 правки НЕ требует — переезд делает его утверждение истинным; после коммита сверить это грепом. (4) А вот предложенную находкой правку docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:51 я бы НЕ делал в том же движении: это ТЕЛО архивного отчёта с ревью-шапкой, и оно фиксирует, где промт лежал на момент прогона; конвенция в каталоге и так смешанная (MINIRUN_REPORT_2026-07-25.md:5 цитирует архивный путь, COLDRUN_V16 — docs/). Если оркестратор хочет живой указатель — правка допустима, но она отдельное решение, а не часть переезда. + +**Разбор опровергателя:** Воспроизвёл своими командами и подтверждаю с усилением. Файл лежит в docs/, в archive/prompts его нет; позитивный контроль честный — каталог грепается (в нём два ДРУГИХ coldrun-промта), и переезды реально случаются (у money-промта --follow даёт три коммита, включая лендинг-переезд, у V16 — ровно один, коммит выдачи 8e475fa). Находка при этом НЕДОСЧИТАЛА носителей и потому недооценила severity: docs/PROGRESS.md:3 — CURRENT-STATE, объявленный ЕДИНСТВЕННЫМ носителем состояния — прямо утверждает «промт `BACKEND_COLDRUN_V16_SESSION_PROMPT.md` отработан, в `archive/prompts/`». Это не норма, а УКАЗАТЕЛЬ, и он сегодня ложен: класс B поверх класса A, и врёт он ровно тому читателю, который по нему пойдёт. Зато КАВЕАТ находки опровергнут: развилка «довести / остановиться» НЕ висит на владельце — она закрыта его словом «не доводить» (05-decisions-log.md:1786, нота передачи D39.184; то же в PROGRESS.md:3 п.2), потрачено $0.436110 из $0.80. Заодно вскрылось, что стала протухшей и сама строка README:25, на которую находка опиралась как на основание осторожности: «отчёт ждёт лендинга» ложно — отчёт заленджен 244f808 (31.08 21:21, предок HEAD) и лежит в docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Переезд ДЕЛАТЬ — оговорка «если развилка не закрыта, не двигать» снята замером: закрыта. Одним коммитом pathspec-формой: (1) `git mv` промта в docs/archive/prompts/ — проверено, что переезд link-safe: внутри промта НОЛЬ относительных markdown-ссылок (`grep -cE '\]\(\.\.?/'` → 0, тот же ноль у уже переехавшего money-промта), все пути — репо-корневые в бэктиках, так что норма пере-углубления D39.80 здесь не срабатывает; (2) тем же коммитом починить README:25, где ДВЕ протухшие половины, а не ноль: «развилка … НА ВЛАДЕЛЬЦЕ» → «закрыта словом владельца «не доводить» 31.08, $0.436110 из $0.80 (D39.184)» и «отчёт ждёт лендинга» → «отчёт заленджен, `archive/reports/COLDRUN_V16_REPORT_2026-08-31.md`». (3) docs/PROGRESS.md:3 правки НЕ требует — переезд делает его утверждение истинным; после коммита сверить это грепом. (4) А вот предложенную находкой правку docs/archive/reports/COLDRUN_V16_REPORT_2026-08-31.md:51 я бы НЕ делал в том же движении: это ТЕЛО архивного отчёта с ревью-шапкой, и оно фиксирует, где промт лежал на момент прогона; конвенция в каталоге и так смешанная (MINIRUN_REPORT_2026-07-25.md:5 цитирует архивный путь, COLDRUN_V16 — docs/). Если оркестратор хочет живой указатель — правка допустима, но она отдельное решение, а не часть переезда. --- ## N085 · MINOR · `docs/STACK.md:7` · B-мёртвый-указатель -**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** `configs/pipeline-c1.yaml:36-60` +**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> `configs/pipeline-c1.yaml:36-60` + **Почему протухло:** Блок стадии draft лежит на строках 60–84 (`stages:` — 59, `- name: draft` — 60, следующая стадия `- name: edit` — 85). На заявленных 36–60 сегодня блок `retries` (включая `regenerate_echo_before_escalate`, вписанный паком «деньги и честность выдачи» 31.08) и ровно одна строка `stages:`. Указатель уехал вниз на ~24 строки. ⚠ Шапка ЭТОГО ЖЕ дока (строка 3) запрещает адресовать носители номером строки именно из-за этого класса («испр. 22.08 аудитом доков: номера уехали и вели в чужие блоки») — исключение выжило в таблице под ней и уже сгнило. Линтер `counts.py --lint` этот якорь НЕ ловит — он в его классе «без токена — несверяемые: 708». -**Улика — команда:** `grep -n -E '^\s*(stages:|- name:|escalation:|gates:|chains:)' backend/configs/pipeline-c1.yaml; awk 'NR>=36&&NR<=38' backend/configs/pipeline-c1.yaml` -**Улика — вывод:** 59:stages: + +**Улика — команда:** +``` +grep -n -E '^\s*(stages:|- name:|escalation:|gates:|chains:)' backend/configs/pipeline-c1.yaml; awk 'NR>=36&&NR<=38' backend/configs/pipeline-c1.yaml +``` +**Улика — вывод:** +``` +59:stages: 60: - name: draft 61: role: translator 62: model: deepseek-v4-flash # интерим — переводчик по exp13 … @@ -1682,18 +3169,31 @@ docs/archive/prompts/PLATFORM_P12_SESSION_PROMPT.md # • budget_usd > 0 (книга, готовая к боевому прогону …) # самого ключа): ре-ген ЗАМЕНЯЕТ хоп … # (`coldrun-v16`, книжный конфиг с budget_usd: 0.08) … -**Предложенное действие:** Убрать номер строки и адресовать КЛЮЧОМ, как требует шапка самого дока: «`configs/pipeline-c1.yaml`, стадия `draft`». Содержание строки (thinking ON, явный `reasoning:"low"`, модель `deepseek-v4-flash`) пере-проверено и ВЕРНО — битый только номер. +``` + +**Предложенное действие (автора находки):** Убрать номер строки и адресовать КЛЮЧОМ, как требует шапка самого дока: «`configs/pipeline-c1.yaml`, стадия `draft`». Содержание строки (thinking ON, явный `reasoning:"low"`, модель `deepseek-v4-flash`) пере-проверено и ВЕРНО — битый только номер. + **Разрушающее:** False --- ## N086 · MINOR · `docs/STACK.md:15` · B-мёртвый-указатель -**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** `configs/pipeline-c1.yaml:118-135` +**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> `configs/pipeline-c1.yaml:118-135` + **Почему протухло:** Блок `escalation` лежит на строках 142–159 (`escalation:` — 142, `chains:` — 143, `default:` — 153, `adult:` — 154, `budget_usd: 0` — 159). На заявленных 118–135 сегодня СОВСЕМ ДРУГИЕ гейты — `sanitizer:` (118) и `regression_guard:` (128). Указатель ведёт не в пустоту, а в ЧУЖОЙ блок, то есть даёт читателю неверный ответ — самый дорогой вид промаха и ровно тот, от которого предостерегает шапка дока (строка 3). -**Улика — команда:** `awk 'NR>=118&&NR<=129{printf "%d: %s\n",NR,$0}' backend/configs/pipeline-c1.yaml | head -4; grep -n 'escalation:\|chains:\|budget_usd: 0\|default: \[\|adult: \[' backend/configs/pipeline-c1.yaml` -**Улика — вывод:** 118: sanitizer: + +**Улика — команда:** +``` +awk 'NR>=118&&NR<=129{printf "%d: %s\n",NR,$0}' backend/configs/pipeline-c1.yaml | head -4; grep -n 'escalation:\|chains:\|budget_usd: 0\|default: \[\|adult: \[' backend/configs/pipeline-c1.yaml +``` +**Улика — вывод:** +``` +118: sanitizer: 119: # Output-санитайзер (D30.3): детерминированный вердикт-гейт «мгновенной 128: regression_guard: 129: # Пост-reflow регресс-гард (D38-инфра, research/18 §C#5) … @@ -1703,18 +3203,31 @@ docs/archive/prompts/PLATFORM_P12_SESSION_PROMPT.md 153: default: [deepseek-v4-pro, glm-5.1, gemini-3.1-pro-preview] 154: adult: [grok-4.3] 159: budget_usd: 0 -**Предложенное действие:** Адресовать ключом: «`configs/pipeline-c1.yaml`, блок `escalation` (`chains.default` / `chains.adult` / `budget_usd`)». Содержательные утверждения строки (цепочка из трёх, `budget_usd: 0` в шиппинге ⇒ эскалация не исполняется, слаг ИМЕННО `-preview`) пере-проверены и ВЕРНЫ — трогать их не надо. +``` + +**Предложенное действие (автора находки):** Адресовать ключом: «`configs/pipeline-c1.yaml`, блок `escalation` (`chains.default` / `chains.adult` / `budget_usd`)». Содержательные утверждения строки (цепочка из трёх, `budget_usd: 0` в шиппинге ⇒ эскалация не исполняется, слаг ИМЕННО `-preview`) пере-проверены и ВЕРНЫ — трогать их не надо. + **Разрушающее:** False --- ## N087 · MINOR · `docs/STACK.md:16` · B-мёртвый-указатель -**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** кредиты xAI — лист владельца (PROGRESS строка 6) +**Срез:** Корневые онбординг-доки: CLAUDE.md · docs/README.md · docs/STACK.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> кредиты xAI — лист владельца (PROGRESS строка 6) + **Почему протухло:** Указатель РОДИЛСЯ ВЕРНЫМ 09.08 (085dbb9): строка 6 `docs/PROGRESS.md` тогда читалась «**Открыто на владельце:** прогон Sol-пакетов руками (150) · кредиты xAI (арбитр grok + судья 18+) …» — то есть автор не ошибался, сгнил механизм. Сегодня строка 6 — про выданный 27.08 промт контрактного минора, блок «Открыто на владельце» уехал на строку 11 и кредитов xAI не называет вовсе; во всём живом PROGRESS.md слово «xAI» осталось только в строках бэклога 144 и 67, ни одна из которых не «лист владельца». -**Улика — команда:** `git show 085dbb9:docs/PROGRESS.md | sed -n '6p'; sed -n '6p' docs/PROGRESS.md; grep -n 'xAI' docs/PROGRESS.md; grep -n 'Открыто на владельце' docs/PROGRESS.md` -**Улика — вывод:** --- на 085dbb9 (09.08, коммит-автор указателя) строка 6 --- + +**Улика — команда:** +``` +git show 085dbb9:docs/PROGRESS.md | sed -n '6p'; sed -n '6p' docs/PROGRESS.md; grep -n 'xAI' docs/PROGRESS.md; grep -n 'Открыто на владельце' docs/PROGRESS.md +``` +**Улика — вывод:** +``` +--- на 085dbb9 (09.08, коммит-автор указателя) строка 6 --- > - **Открыто на владельце:** прогон Sol-пакетов руками (150) · кредиты xAI (арбитр grok + судья 18+) · Приложение А контракта (148 … --- сегодня строка 6 --- > - ⚠ **27.08: ВЫДАН ПРОМТ КОНТРАКТНОГО МИНОРА** (`CONTRACT_MINOR_SESSION_PROMPT.md`, пункт 2б очереди D39.156) … @@ -1723,19 +3236,35 @@ docs/archive/prompts/PLATFORM_P12_SESSION_PROMPT.md 233:| 67 | **Прод-гигиена: … xAI-ключ off перед продом** (D27) … --- лист владельца сегодня --- 11:> - **Открыто на владельце:** … лестница D25.1 … лейбл главы без заголовка … -**Положительный контроль:** grep -n 'xAI' docs/PROGRESS.md сегодня возвращает ДВА попадания (строки 134 и 233) — инструмент слово «xAI» в этом файле находит, значит его отсутствие на строке 6 и в блоке «Открыто на владельце» (строка 11) — реальный факт, а не пустой греп. Контроль второго рода: тот же `sed -n '6p'` на историческом дереве НАХОДИТ кредиты xAI — значит и чтение строки по номеру работает. -**Предложенное действие:** Снять номер строки файла (он в PROGRESS.md нестабилен по построению — шапка пере-собирается каждым лендингом). Либо (а) заменить на «кредиты xAI — на владельце; носителя в CURRENT-STATE СЕЙЧАС НЕТ — был на 09.08, вымыт пере-сборкой шапки», либо (б) завести строку единого бэклога под кредиты xAI и ссылаться на её НОМЕР СТРОКИ БЭКЛОГА — он стабилен, и соседний указатель той же ячейки («строка 150 бэклога») пере-проверен и ЖИВ. +``` +**Положительный контроль:** +``` +grep -n 'xAI' docs/PROGRESS.md сегодня возвращает ДВА попадания (строки 134 и 233) — инструмент слово «xAI» в этом файле находит, значит его отсутствие на строке 6 и в блоке «Открыто на владельце» (строка 11) — реальный факт, а не пустой греп. Контроль второго рода: тот же `sed -n '6p'` на историческом дереве НАХОДИТ кредиты xAI — значит и чтение строки по номеру работает. +``` + +**Предложенное действие (автора находки):** Снять номер строки файла (он в PROGRESS.md нестабилен по построению — шапка пере-собирается каждым лендингом). Либо (а) заменить на «кредиты xAI — на владельце; носителя в CURRENT-STATE СЕЙЧАС НЕТ — был на 09.08, вымыт пере-сборкой шапки», либо (б) завести строку единого бэклога под кредиты xAI и ссылаться на её НОМЕР СТРОКИ БЭКЛОГА — он стабилен, и соседний указатель той же ячейки («строка 150 бэклога») пере-проверен и ЖИВ. + **Разрушающее:** False --- ## N088 · MINOR · `docs/architecture/01-decisions.md:19` · A-дрейф -**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** План выделения пакетов: `pkg/llm` (llm.go + httpllm.go + адаптеры + failover), `pkg/ledger` (pricing + reserve/settle), `pkg/obs` (trace + logging), `pkg/store` (миграции). +**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> План выделения пакетов: `pkg/llm` (llm.go + httpllm.go + адаптеры + failover), `pkg/ledger` (pricing + reserve/settle), `pkg/obs` (trace + logging), `pkg/store` (миграции). + **Почему протухло:** Каталога `pkg/` в бэкенде нет вовсе; план исполнен под `internal/`: internal/llm, internal/ledger, internal/obs, internal/store. Имена совпали, префикс — нет, и живые доки (PROGRESS, 13-tech-debt-anchors, 05-decisions-log, research/21) ссылаются уже на `internal/...`. Читатель, идущий по 01-decisions за адаптером, ищет несуществующий путь. -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main/backend && ls pkg/ ; ls internal/ | head -20; cd /home/ubuntu/projects/textmachine-main && grep -rln 'internal/llm' --include='*.md' docs/ | grep -v archive` -**Улика — вывод:** ls: cannot access 'pkg/': No such file or directory + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main/backend && ls pkg/ ; ls internal/ | head -20; cd /home/ubuntu/projects/textmachine-main && grep -rln 'internal/llm' --include='*.md' docs/ | grep -v archive +``` +**Улика — вывод:** +``` +ls: cannot access 'pkg/': No such file or directory archguard bookfile checks @@ -1760,39 +3289,71 @@ docs/experiments/00-provider-quirks.md docs/architecture/13-tech-debt-anchors.md docs/research/21-llm-transport-survey.md docs/architecture/05-decisions-log.md -**Положительный контроль:** Отрицательный результат («pkg/ нет») предъявлен вместе с положительным: тот же `ls` по соседнему пути печатает 18 реальных пакетов, а грепом по живым докам находятся пять файлов, называющих `internal/llm` — инструмент в этой области находит и каталоги, и упоминания. +``` +**Положительный контроль:** +``` +Отрицательный результат («pkg/ нет») предъявлен вместе с положительным: тот же `ls` по соседнему пути печатает 18 реальных пакетов, а грепом по живым докам находятся пять файлов, называющих `internal/llm` — инструмент в этой области находит и каталоги, и упоминания. +``` **Другие носители того же факта:** docs/research/10-vojo-ai-bot-review.md:145 (первоисточник плана `pkg/llm`/`pkg/ledger`/`pkg/obs`/`pkg/store`; тело ресёрча по D23.3 не переписывается) -**Предложенное действие:** В строке 19 заменить `pkg/*` на `internal/*` (internal/llm · internal/ledger · internal/obs · internal/store) с пометкой «план исполнен под internal/». Ресёрч 10-vojo-ai-bot-review.md не трогать — он улика того, как план выглядел. + +**Предложенное действие (автора находки):** В строке 19 заменить `pkg/*` на `internal/*` (internal/llm · internal/ledger · internal/obs · internal/store) с пометкой «план исполнен под internal/». Ресёрч 10-vojo-ai-bot-review.md не трогать — он улика того, как план выглядел. + **Разрушающее:** False --- ## N089 · MINOR · `docs/architecture/01-decisions.md:39` · A-дрейф -**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** Хранилище: **SQLite (один файл на книгу) + FTS5** (FTS5 — НЕ в горячем пути: unicode61 не сегментирует CJK → молча вернёт ничего). +**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> Хранилище: **SQLite (один файл на книгу) + FTS5** (FTS5 — НЕ в горячем пути: unicode61 не сегментирует CJK → молча вернёт ничего). + **Почему протухло:** FTS5 в движке нет НИ на горячем, ни на холодном пути: во всём backend/ ноль виртуальных таблиц и единственное упоминание FTS5 — отрицание в комментарии горячего пути («$0, no LLM, no embeddings, no FTS5», membank/memory.go:32). Драйвер FTS5 умеет (03-implementation-notes.md:21), но индекс никогда не создавался. Р3 читается как «хранилище состоит из SQLite и FTS5», хотя вторая половина — ненаступившее. -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main/backend && grep -rniI 'fts5|fts4|CREATE VIRTUAL TABLE' -E --include='*.go' --include='*.yaml' . ; echo '--- positive control ---'; grep -rniI 'CREATE TABLE' --include='*.go' . | wc -l; grep -rniI 'PRAGMA' --include='*.go' internal/store/*.go | grep -v _test | head -3` -**Улика — вывод:** internal/membank/memory.go:32:// Contract / research/13 Q3). $0, no LLM, no embeddings, no FTS5 — a multi-pattern + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main/backend && grep -rniI 'fts5|fts4|CREATE VIRTUAL TABLE' -E --include='*.go' --include='*.yaml' . ; echo '--- positive control ---'; grep -rniI 'CREATE TABLE' --include='*.go' . | wc -l; grep -rniI 'PRAGMA' --include='*.go' internal/store/*.go | grep -v _test | head -3 +``` +**Улика — вывод:** +``` +internal/membank/memory.go:32:// Contract / research/13 Q3). $0, no LLM, no embeddings, no FTS5 — a multi-pattern --- positive control --- 19 internal/store/backup.go:18:// IntegrityCheck runs `PRAGMA integrity_check` against the SQLite file at dbPath and returns a loud error internal/store/backup.go:67: v.Add("_pragma", "busy_timeout(5000)") internal/store/backup.go:69: v.Add("_pragma", "query_only(1)") -**Положительный контроль:** Тот же грепом по тем же файлам находятся 19 объявлений `CREATE TABLE` и три места с `PRAGMA` — т.е. инструмент видит DDL и SQLite-фичи в этом дереве; ноль по FTS5/VIRTUAL TABLE — это отсутствие факта, а не отсутствие поиска. +``` +**Положительный контроль:** +``` +Тот же грепом по тем же файлам находятся 19 объявлений `CREATE TABLE` и три места с `PRAGMA` — т.е. инструмент видит DDL и SQLite-фичи в этом дереве; ноль по FTS5/VIRTUAL TABLE — это отсутствие факта, а не отсутствие поиска. +``` **Другие носители того же факта:** docs/architecture/06-memory-risk-registry.md:100 («Горячий путь — НЕ FTS5», согласуется с кодом), docs/architecture/03-implementation-notes.md:21 и :127 (FTS5 в драйвере есть; в горячий путь не тащить — согласуется), docs/research/05-memory-glossary.md:179 (первоисточник «SQLite + FTS5 + sqlite-vec»; тело ресёрча не переписывается) -**Предложенное действие:** Снять «+ FTS5» из формулы хранилища в Р3, оставив факт: «SQLite (один файл на книгу); FTS5 драйвер умеет, но НЕ используется — ни на горячем пути (CJK не сегментируется), ни на холодном; горячий путь — Aho-Corasick по нормализованному чанку (internal/membank/memory.go)». + +**Предложенное действие (автора находки):** Снять «+ FTS5» из формулы хранилища в Р3, оставив факт: «SQLite (один файл на книгу); FTS5 драйвер умеет, но НЕ используется — ни на горячем пути (CJK не сегментируется), ни на холодном; горячий путь — Aho-Corasick по нормализованному чанку (internal/membank/memory.go)». + **Разрушающее:** False --- ## N090 · MINOR · `docs/architecture/01-decisions.md:55` · B-мёртвый-указатель -**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **Решение владельца (04.07): удалён только Anthropic — за ДОРОГОВИЗНУ** (git `c7f8a95`, `85f24fb`), не за недоступность +**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **Решение владельца (04.07): удалён только Anthropic — за ДОРОГОВИЗНУ** (git `c7f8a95`, `85f24fb`), не за недоступность + **Почему протухло:** Обоих коммитов в истории репозитория НЕ существует (`git cat-file -t c7f8a95` → «Not a valid object name»). Реальные коммиты того же дня и того же содержания — `c4676d5` «Drop Anthropic from the model stack per owner decision» и `c504cfe` «Mark native Anthropic adapter deprecated (kept as reference for Phase-2 Gemini adapter)», оба 2026-07-04. История не усечена и не переписана: 924 коммита от 9bb00d4 (04.07), `.git/shallow` нет. Само решение верно и живо (адаптер на месте — internal/llm/provider_anthropic.go), протухла только квитанция. -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main && git cat-file -t c7f8a95; git log --all --oneline --grep='nthropic' | head -5; git log -1 --format='%h %ad %s' --date=short c4676d5; git rev-list --count HEAD; ls .git/shallow` -**Улика — вывод:** fatal: Not a valid object name c7f8a95 + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main && git cat-file -t c7f8a95; git log --all --oneline --grep='nthropic' | head -5; git log -1 --format='%h %ad %s' --date=short c4676d5; git rev-list --count HEAD; ls .git/shallow +``` +**Улика — вывод:** +``` +fatal: Not a valid object name c7f8a95 210b38e Ratify D39.121: post-review self-plan ... c504cfe Mark native Anthropic adapter deprecated (kept as reference for Phase-2 Gemini adapter); record real DeepSeek cache-hit confirmation c4676d5 Drop Anthropic from the model stack per owner decision: remove provider and Claude models, repoint escalation and C2 judge to non-Anthropic @@ -1800,20 +3361,36 @@ c4676d5 Drop Anthropic from the model stack per owner decision: remove provider c4676d5 2026-07-04 Drop Anthropic from the model stack per owner decision ... 924 ls: cannot access '.git/shallow': Not a directory -**Положительный контроль:** Та же команда на хэшах из соседних доков резолвит: `git log --oneline -1 2f91b04` → «2f91b04 Land pack-13 release QA: … percent and latin and broken-word checkers …» (цитируется шапкой 04-unhappy-paths.md:3), `git log --oneline -1 636919c` → «636919c Add per-model capability wire-shape layer (D3.1) and fold context-assembly, memory-version, and resolved capability into the job snapshot (D5.2)» (ци +``` +**Положительный контроль:** +``` +Та же команда на хэшах из соседних доков резолвит: `git log --oneline -1 2f91b04` → «2f91b04 Land pack-13 release QA: … percent and latin and broken-word checkers …» (цитируется шапкой 04-unhappy-paths.md:3), `git log --oneline -1 636919c` → «636919c Add per-model capability wire-shape layer (D3.1) and fold context-assembly, memory-version, and resolved capability into the job snapshot (D5.2)» (цитируется 06-memory-risk-registry.md F1). Т.е. git находит существующие короткие хэши той же длины. +``` **Другие носители того же факта:** docs/architecture/01-decisions.md:19 (второе вхождение `c7f8a95` в том же доке, Р1), docs/research/11-gap-2.md:7 (⚠-шапка ресёрча: «Anthropic выброшен из стека целиком (Р4/D3, `c7f8a95`)» — тело ресёрча по D23.3 не переписывается), docs/archive/research/11-gap-1.md:12 (архив, под ⚠-баннером) -**Предложенное действие:** В 01-decisions.md заменить обе цитаты на реальные: строка 19 — git `c4676d5` (выброс) / `c504cfe` (пометка DEPRECATED); строка 55 — git `c4676d5`, `c504cfe`. Живой носитель ресёрча 11-gap-2.md:7 не переписывать (тело — улика), но при следующем касании его шапки — там же. + +**Предложенное действие (автора находки):** В 01-decisions.md заменить обе цитаты на реальные: строка 19 — git `c4676d5` (выброс) / `c504cfe` (пометка DEPRECATED); строка 55 — git `c4676d5`, `c504cfe`. Живой носитель ресёрча 11-gap-2.md:7 не переписывать (тело — улика), но при следующем касании его шапки — там же. + **Разрушающее:** False --- ## N091 · MINOR · `docs/architecture/01-decisions.md:93` · A-дрейф -**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** Локальный режим MVP: SQLite (modernc.org/sqlite, CGO-free, без нативных расширений — см. Р3) для проектных данных; отдельный файл БД на книгу для банка памяти. +**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> Локальный режим MVP: SQLite (modernc.org/sqlite, CGO-free, без нативных расширений — см. Р3) для проектных данных; отдельный файл БД на книгу для банка памяти. + **Почему протухло:** Отдельного файла БД под банк памяти НЕТ и не было построено: у книги ровно ОДНА база `.db` (config/book.go:189), и банк памяти живёт в ней же — один и тот же мигратор internal/store/migrate.go создаёт и jobs/checkpoints (проектные данные), и glossary/glossary_aliases/glossary_revisions/retrieval_state/ruby_readings (банк). Соседний живой док читает раскладку правильно — 03-implementation-notes.md:234 говорит «раскладка «файл БД на книгу» из Р3/Р6», т.е. ОДИН файл; расходится только формулировка Р6. -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main/backend && grep -n 'b.ProjectDB = filepath.Join' internal/config/book.go && grep -n 'CREATE TABLE IF NOT EXISTS \(jobs\|checkpoints\|glossary\|glossary_aliases\|retrieval_state\|ruby_readings\)' internal/store/migrate.go && grep -rn 'sql.Open("sqlite"' --include='*.go' . | grep -v _test.go` -**Улика — вывод:** 189: b.ProjectDB = filepath.Join(dir, b.BookID+".db") + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main/backend && grep -n 'b.ProjectDB = filepath.Join' internal/config/book.go && grep -n 'CREATE TABLE IF NOT EXISTS \(jobs\|checkpoints\|glossary\|glossary_aliases\|retrieval_state\|ruby_readings\)' internal/store/migrate.go && grep -rn 'sql.Open("sqlite"' --include='*.go' . | grep -v _test.go +``` +**Улика — вывод:** +``` +189: b.ProjectDB = filepath.Join(dir, b.BookID+".db") 29: CREATE TABLE IF NOT EXISTS jobs ( 46: CREATE TABLE IF NOT EXISTS checkpoints ( 154: CREATE TABLE IF NOT EXISTS ruby_readings ( @@ -1824,190 +3401,380 @@ ls: cannot access '.git/shallow': Not a directory --- internal/store/backup.go:71: db, err := sql.Open("sqlite", "file:"+dbPath+...) internal/store/store.go:80/89/142 (Open writer, Open reader, OpenReadOnly) -**Положительный контроль:** Тот же грепом ищутся ВСЕ открыватели БД в дереве и находятся четыре — все они открывают ОДИН путь (store.Open/OpenReadOnly + бэкап того же файла). Т.е. грепу есть что находить: если бы второй файл существовал, его открыватель попал бы в этот же список. +``` +**Положительный контроль:** +``` +Тот же грепом ищутся ВСЕ открыватели БД в дереве и находятся четыре — все они открывают ОДИН путь (store.Open/OpenReadOnly + бэкап того же файла). Т.е. грепу есть что находить: если бы второй файл существовал, его открыватель попал бы в этот же список. +``` **Другие носители того же факта:** docs/architecture/01-decisions.md:39 (Р3 «SQLite (один файл на книгу)» — эта формулировка ВЕРНА и коду соответствует), docs/architecture/03-implementation-notes.md:234 («раскладка «файл БД на книгу» из Р3/Р6» — верно) -**Предложенное действие:** Заменить в Р6 «отдельный файл БД на книгу для банка памяти» на «ОДИН файл `.db` на книгу — и проектные данные, и банк памяти в нём (internal/store/migrate.go)», чтобы Р6 перестал расходиться с Р3 и с 03-implementation-notes.md:234. + +**Предложенное действие (автора находки):** Заменить в Р6 «отдельный файл БД на книгу для банка памяти» на «ОДИН файл `.db` на книгу — и проектные данные, и банк памяти в нём (internal/store/migrate.go)», чтобы Р6 перестал расходиться с Р3 и с 03-implementation-notes.md:234. + **Разрушающее:** False --- ## N092 · MINOR · `docs/architecture/03-implementation-notes.md:212` · B-мёртвый-указатель -**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Подтверждённые исправлены **двумя волнами** (коммиты `dc6ea8e` → `a32ec2e` → фиксы v2) +**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Подтверждённые исправлены **двумя волнами** (коммиты `dc6ea8e` → `a32ec2e` → фиксы v2) + **Почему протухло:** Ни `dc6ea8e`, ни `a32ec2e` в репозитории не существуют — `git cat-file -t` отвечает «Not a valid object name» на оба, при том что тот же вызов на реальных хэшах возвращает `commit`. Реальные коммиты двух волн селфревью Фазы 0 — `e861541` (волна 1: shared retry engine, billed-decode settle, empty-completion fail-loud, snapshot pinning с `--resnapshot`, process file lock, local model pricing, single-pass render, attempt hash dimension — дословно перечень §6 волны 1) и `214e005` (волна 2: snapshot coverage, response-price fallback, usage clamp, job-status lifecycle, config guards, source normalization, hash robustness — дословно перечень волны 2). Строка вдобавок противоречит себе: здесь порядок `dc6ea8e → a32ec2e`, а ниже «Волна 1 (коммит a32ec2e)». -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main && for c in dc6ea8e a32ec2e; do echo -n "$c: "; git cat-file -t $c 2>&1|head -1; done; echo POSCTRL; git cat-file -t 7954325; git log --oneline | grep -iE 'self-review|review fixes|Harden Phase 0'` -**Улика — вывод:** dc6ea8e: fatal: Not a valid object name dc6ea8e + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main && for c in dc6ea8e a32ec2e; do echo -n "$c: "; git cat-file -t $c 2>&1|head -1; done; echo POSCTRL; git cat-file -t 7954325; git log --oneline | grep -iE 'self-review|review fixes|Harden Phase 0' +``` +**Улика — вывод:** +``` +dc6ea8e: fatal: Not a valid object name dc6ea8e a32ec2e: fatal: Not a valid object name a32ec2e POSCTRL commit 9d78385 Apply external review fixes: snapshot local overrides, gate-enabled guard, TM-key doc note, db-lock gitignore, techdebt disposition 214e005 Harden Phase 0 after self-review: snapshot coverage, response-price fallback, usage clamp, job-status lifecycle, config guards, source normalization, hash robustness e861541 Fix review findings: shared retry engine, billed-decode settle, empty-completion fail-loud, snapshot pinning with resnapshot flag, process file lock, local model pricing, single-pass render, attempt hash dimension -**Положительный контроль:** git cat-file -t 7954325 → commit (тот же инструмент на реальном коротком хэше отвечает; значит пустой ответ на dc6ea8e/a32ec2e — отсутствие объекта, а не поломка команды) -**Предложенное действие:** Заменить в строке 212 `dc6ea8e` → `e861541`, `a32ec2e` → `214e005`; ниже в подзаголовке «Волна 1 (коммит a32ec2e)» → «Волна 1 (коммит `e861541`)», «Волна 2 (эта)» → «Волна 2 (коммит `214e005`)». Внешнее ревью §6 приземлено `9d78385`/`56670e3` — можно добавить туда же. +``` +**Положительный контроль:** +``` +git cat-file -t 7954325 → commit (тот же инструмент на реальном коротком хэше отвечает; значит пустой ответ на dc6ea8e/a32ec2e — отсутствие объекта, а не поломка команды) +``` + +**Предложенное действие (автора находки):** Заменить в строке 212 `dc6ea8e` → `e861541`, `a32ec2e` → `214e005`; ниже в подзаголовке «Волна 1 (коммит a32ec2e)» → «Волна 1 (коммит `e861541`)», «Волна 2 (эта)» → «Волна 2 (коммит `214e005`)». Внешнее ревью §6 приземлено `9d78385`/`56670e3` — можно добавить туда же. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Правка нужна, но НЕ той подстановкой, что предложена: находка в одной строке отображает a32ec2e→214e005, а строкой ниже тот же токен a32ec2e→e861541 — это несогласованно и спорит с архивным носителем. Верное отображение по СОДЕРЖАНИЮ коммитов: dc6ea8e = каркас `7954325` («Add Phase 0 backend skeleton…»), a32ec2e = волна 1 `e861541` (subject дословно повторяет перечень §6 волны 1), волна 2 = `214e005` (blame: этим коммитом строка и написана, его же подзаголовок «Волна 2 (эта)»). Внешнее ревью — `56670e3`/`9d78385`. Архивную копию `docs/archive/PROGRESS-2026-07-04-10.md:164-165` НЕ править (замороженная хроника); если её мёртвые хэши мешают — только пометка, не переписывание. Рекомендую в §6 добавить полсловом «(исходные хэши в отчёте были указаны ошибочно; в истории их нет)», чтобы следующий ревизор не искал переписанную историю. + +**Разбор опровергателя:** Оба хэша мертвы — воспроизвёл своей командой в ОБОИХ чекаутах, при работающем положительном контроле на трёх реальных коротких хэшах и при полном сканировании 1032 объектов. Уточнение к находке: они не «сгнили», а были ЛОЖНЫ ПРИ РОЖДЕНИИ — автор писал строку внутри 214e005, когда 7954325 и e861541 уже несли сегодняшние хэши, а совпадение author/committer-дат по всей цепи 04.07 исключает переписывание истории. Второй носитель, который находка не назвала (archive/PROGRESS-2026-07-04-10.md:164-165), одновременно РАЗРЕШАЕТ заявленное «самопротиворечие»: там прямо написано, что dc6ea8e — это КАРКАС, а a32ec2e — волна 1, то есть строка 212 не противоречит подзаголовку «Волна 1 (коммит a32ec2e)», она лишь неаккуратно ставит каркас в перечень «двух волн». + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Правка нужна, но НЕ той подстановкой, что предложена: находка в одной строке отображает a32ec2e→214e005, а строкой ниже тот же токен a32ec2e→e861541 — это несогласованно и спорит с архивным носителем. Верное отображение по СОДЕРЖАНИЮ коммитов: dc6ea8e = каркас `7954325` («Add Phase 0 backend skeleton…»), a32ec2e = волна 1 `e861541` (subject дословно повторяет перечень §6 волны 1), волна 2 = `214e005` (blame: этим коммитом строка и написана, его же подзаголовок «Волна 2 (эта)»). Внешнее ревью — `56670e3`/`9d78385`. Архивную копию `docs/archive/PROGRESS-2026-07-04-10.md:164-165` НЕ править (замороженная хроника); если её мёртвые хэши мешают — только пометка, не переписывание. Рекомендую в §6 добавить полсловом «(исходные хэши в отчёте были указаны ошибочно; в истории их нет)», чтобы следующий ревизор не искал переписанную историю. --- ## N093 · MINOR · `docs/architecture/03-implementation-notes.md:267` · C-исполненное-как-открытое -**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **D3** — изоляция канала B в failover не принуждается кодом (пара primary/fallback задаётся вызывающим); при подключении failover (Фаза 2) нужен структурный запрет Anthropic в канале B, иначе пустой ответ local-abliterated молча уйдёт в Anthropic. Все три — Фаза 1–2, не блокеры Ф0. +**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **D3** — изоляция канала B в failover не принуждается кодом (пара primary/fallback задаётся вызывающим); при подключении failover (Фаза 2) нужен структурный запрет Anthropic в канале B, иначе пустой ответ local-abliterated молча уйдёт в Anthropic. Все три — Фаза 1–2, не блокеры Ф0. + **Почему протухло:** Запрошенный «структурный запрет» построен и ратифицирован дважды: (1) на загрузке — `config/pipeline.go:731` отказывает конфигу, где модель провайдера без нужного `accepts_labels` резолвится под лейблом книги; (2) на вызове — `Runner.clientFor` физически не выдаёт клиента без заявленного набора лейблов, и комментарий называет это «the compile-enforced half of the routing guarantee, D39.26 point 8». Вдобавок сам объект запрета отсутствует: Anthropic убран из стека решением владельца, в `configs/models.yaml` провайдера `anthropic` и моделей claude-* нет. Пункт читается как открытая задача Фазы 2, тогда как он закрыт паком-17 (D39.25/D39.26). -**Улика — команда:** `grep -n 'may not receive content label' backend/internal/config/pipeline.go; grep -n 'refusing to send content labelled\|compile-enforced half' backend/internal/pipeline/runner.go; grep -in 'provider: anthropic\|claude-' backend/configs/models.yaml; grep -n 'provider:' backend/configs/models.yaml | awk -F': ' '{print $3}' | sort -u` -**Улика — вывод:** backend/internal/config/pipeline.go:731: contentBad("model %q (provider %s) may not receive content label(s) %v — this book declares %v...") + +**Улика — команда:** +``` +grep -n 'may not receive content label' backend/internal/config/pipeline.go; grep -n 'refusing to send content labelled\|compile-enforced half' backend/internal/pipeline/runner.go; grep -in 'provider: anthropic\|claude-' backend/configs/models.yaml; grep -n 'provider:' backend/configs/models.yaml | awk -F': ' '{print $3}' | sort -u +``` +**Улика — вывод:** +``` +backend/internal/config/pipeline.go:731: contentBad("model %q (provider %s) may not receive content label(s) %v — this book declares %v...") backend/internal/pipeline/runner.go:573:// cannot reach a provider without stating what it is sending (the compile-enforced half of the routing guarantee, D39.26 point 8). backend/internal/pipeline/runner.go:578: return nil, fmt.Errorf("pipeline: refusing to send content labelled %v to model %q (provider %s) — it does not accept %v ... (D39.25)") -[grep -in 'provider: anthropic|claude-' configs/models.yaml → ни одной строки объявления; только комментарии 104-108/325: «Anthropic убран из стека по решению владельца (04.07)», «claude-sonnet-5 / claude-op -**Положительный контроль:** grep -n 'provider:' backend/configs/models.yaml | awk -F': ' '{print $3}' | sort -u → deepseek, gemini, kimi, local, mistral, openai, xai, zai (восемь провайдеров реально находятся тем же грепом; пустота по `anthropic` — отсутствие провайдера, не поломка команды) -**Предложенное действие:** Дописать в конец пункта: «⚠ ЗАКРЫТО паком-17 (D39.25/D39.26): маршрутизация по лейблам запрещает выдачу клиента структурно — отказ на загрузке (`config/pipeline.go:731`) и на вызове (`pipeline.clientFor`, `runner.go:573-578`); Anthropic из стека убран 04.07 (`configs/models.yaml`), объект запрета отсутствует». Тот же абзац: D2 (per-chunk skip+flag) тоже исполнен — дисп-классификатор `DispOK`/`FlagReason` + exit 2 «completed-with-flags». +[grep -in 'provider: anthropic|claude-' configs/models.yaml → ни одной строки объявления; только комментарии 104-108/325: «Anthropic убран из стека по решению владельца (04.07)», «claude-sonnet-5 / claude-opus-4-8 удалены вместе с провайдером anthropic»] +provider: deepseek | gemini | kimi | local | mistral | openai | xai | zai +``` +**Положительный контроль:** +``` +grep -n 'provider:' backend/configs/models.yaml | awk -F': ' '{print $3}' | sort -u → deepseek, gemini, kimi, local, mistral, openai, xai, zai (восемь провайдеров реально находятся тем же грепом; пустота по `anthropic` — отсутствие провайдера, не поломка команды) +``` + +**Предложенное действие (автора находки):** Дописать в конец пункта: «⚠ ЗАКРЫТО паком-17 (D39.25/D39.26): маршрутизация по лейблам запрещает выдачу клиента структурно — отказ на загрузке (`config/pipeline.go:731`) и на вызове (`pipeline.clientFor`, `runner.go:573-578`); Anthropic из стека убран 04.07 (`configs/models.yaml`), объект запрета отсутствует». Тот же абзац: D2 (per-chunk skip+flag) тоже исполнен — дисп-классификатор `DispOK`/`FlagReason` + exit 2 «completed-with-flags». + **Разрушающее:** False --- ## N094 · MINOR · `docs/architecture/04-unhappy-paths.md:120` · C-исполненное-как-открытое -**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** равномерные гейты по всем главам + **«паспорт качества главы»** в отчёте — продуктовая фича доверия. → в бэклог продукта. +**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> равномерные гейты по всем главам + **«паспорт качества главы»** в отчёте — продуктовая фича доверия. → в бэклог продукта. + **Почему протухло:** Паспорт качества главы ПОСТРОЕН и отдаётся `tmctl status`: тип `pipeline.ChapterPassport` (internal/pipeline/status.go:157-159, «the per-chapter quality passport (D12): unit counts, the worst flag reason, a pass|attention|fail verdict (exp07 chapter rule) and the chapter's spend»), поле `Chapters []ChapterPassport` в отчёте (status.go:313), CLI-дашборд описан в main.go:389. §10 отправляет его «в бэклог продукта», хотя это уже поставленный механизм. Ни один из двух ⚠-баннеров дока это не покрывает: они оговаривают колонки «Фаза/Статус» ТАБЛИЦ, а §10 — прозаические буллеты без колонок. -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main && sed -n '120p' docs/architecture/04-unhappy-paths.md | cut -c1-160 && sed -n '157,159p' backend/internal/pipeline/status.go && grep -n 'per-chapter quality passports' backend/cmd/tmctl/main.go && grep -n 'Chapters \[\]ChapterPassport' backend/internal/pipeline/status.go && grep -n 'паспорта глав' backend/README.md | cut -c1-90` -**Улика — вывод:** - **«Проклятие 300-й главы»** … + **«паспорт качества главы»** в отчёте — продуктовая фича доверия. → в бэклог продукта. + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main && sed -n '120p' docs/architecture/04-unhappy-paths.md | cut -c1-160 && sed -n '157,159p' backend/internal/pipeline/status.go && grep -n 'per-chapter quality passports' backend/cmd/tmctl/main.go && grep -n 'Chapters \[\]ChapterPassport' backend/internal/pipeline/status.go && grep -n 'паспорта глав' backend/README.md | cut -c1-90 +``` +**Улика — вывод:** +``` +- **«Проклятие 300-й главы»** … + **«паспорт качества главы»** в отчёте — продуктовая фича доверия. → в бэклог продукта. 157:// ChapterPassport is the per-chapter quality passport (D12): unit counts, the worst flag 158:// reason, a pass|attention|fail verdict (exp07 chapter rule) and the chapter's spend. 159:type ChapterPassport struct { 389:// dashboard (unit counts, per-chapter quality passports, money, secondary ETA). BOTH modes exit 2 313: Chapters []ChapterPassport `json:"chapters"` 11:| `cmd/tmctl` | CLI: `translate` / `status` (read-only N/M+паспорта глав+деньги, `--json` … +``` **Другие носители того же факта:** docs/architecture/02-mvp-plan.md:34 (тот же факт как ПОСТРОЕННЫЙ: «`tmctl export` … паспорт-rollup» в составе D29.1) — то есть два ранних дока уже расходятся в статусе одного механизма -**Предложенное действие:** В §10 заменить «→ в бэклог продукта» на «→ ПОСТРОЕНО: `pipeline.ChapterPassport` (status.go:157), поверхность `tmctl status --json` + rollup в `tmctl export` (D29.1)». Строку бэклога не заводить — предмет закрыт. + +**Предложенное действие (автора находки):** В §10 заменить «→ в бэклог продукта» на «→ ПОСТРОЕНО: `pipeline.ChapterPassport` (status.go:157), поверхность `tmctl status --json` + rollup в `tmctl export` (D29.1)». Строку бэклога не заводить — предмет закрыт. + **Разрушающее:** False --- ## N095 · MINOR · `docs/architecture/09-target-architecture.md:12` · A-дрейф -**Срез:** Целевая архитектура — docs/architecture/09-target-architecture.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** ⚠ **С 29–30.08 шапка ниже НЕ знает ещё двух построенных механизмов, и до их внесения в таблицу слоёв читать её как полную нельзя:** объёмный потолок оплаченной работы в движке (`--max-units`, `VolumeStop`, `backend/internal/pipeline/volume.go` — D39.170 …) и гейт схемы движка (`backend/internal/store/sqlgate_test.go` — D39.173 +**Срез:** Целевая архитектура — docs/architecture/09-target-architecture.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> ⚠ **С 29–30.08 шапка ниже НЕ знает ещё двух построенных механизмов, и до их внесения в таблицу слоёв читать её как полную нельзя:** объёмный потолок оплаченной работы в движке (`--max-units`, `VolumeStop`, `backend/internal/pipeline/volume.go` — D39.170 …) и гейт схемы движка (`backend/internal/store/sqlgate_test.go` — D39.173 + **Почему протухло:** Механизмов, которых таблица не знает, уже не два: 31.08 (после последней правки дока) залендён пак D39.181/182 «закон раскрытия», добавивший в шов ДВА новых поля контракта — леджер объёмного стопа в кадре `finished` (`runevents.VolumeLedger`) и `config_drift_basis` в `status --json` (`pipeline.ConfigDriftBasis`). Точное числительное «двух» превращает предупреждение в ложную гарантию полноты остального. -**Улика — команда:** `git log -1 --format='doc last edit: %h %ad' -- docs/architecture/09-target-architecture.md; git log -1 --format='landed: %h %ad' -S 'ConfigDriftBasis' -- backend/; grep -n 'Volume \*VolumeLedger' backend/internal/runevents/runevents.go; grep -n 'ConfigDriftBasis == pipeline' backend/cmd/tmctl/render.go` -**Улика — вывод:** doc last edit: 69ef3e9 Sun Aug 30 20:20:02 2026 +0300 + +**Улика — команда:** +``` +git log -1 --format='doc last edit: %h %ad' -- docs/architecture/09-target-architecture.md; git log -1 --format='landed: %h %ad' -S 'ConfigDriftBasis' -- backend/; grep -n 'Volume \*VolumeLedger' backend/internal/runevents/runevents.go; grep -n 'ConfigDriftBasis == pipeline' backend/cmd/tmctl/render.go +``` +**Улика — вывод:** +``` +doc last edit: 69ef3e9 Sun Aug 30 20:20:02 2026 +0300 landed: bb541a8 Mon Aug 31 21:03:04 2026 +0300 206: Volume *VolumeLedger `json:"volume,omitempty"` 727: if rep.ConfigDriftBasis == pipeline.DriftBasisUnknown { -**Предложенное действие:** Снять числительное: «шапка ниже НЕ знает ряда построенных механизмов» + дописать третьим пунктом закон раскрытия движка (D39.181) и два его контрактных поля (`finished.volume`, `config_drift_basis`, лендинг `bb541a8`). Лучшее лечение — внести объёмный потолок, гейт схемы и закон раскрытия В САМУ таблицу слоёв и снять оговорку целиком. +``` + +**Предложенное действие (автора находки):** Снять числительное: «шапка ниже НЕ знает ряда построенных механизмов» + дописать третьим пунктом закон раскрытия движка (D39.181) и два его контрактных поля (`finished.volume`, `config_drift_basis`, лендинг `bb541a8`). Лучшее лечение — внести объёмный потолок, гейт схемы и закон раскрытия В САМУ таблицу слоёв и снять оговорку целиком. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно; предпочтительна названная автором «лучшая» ветка — внести объёмный потолок, гейт схемы и закон раскрытия В САМУ таблицу слоёв и снять оговорку целиком, иначе числительное будет протухать при каждом следующем лендинге. Если правится только текст — не заменять «двух» на «трёх» (то же протухнет), а писать «ряда» + ссылку на реестр нот. + +**Разбор опровергателя:** Опровергнуть не удалось. Док в последний раз правился 30.08 20:20; закон раскрытия (D39.181) и его применение (D39.182) залендены 31.08 21:03 коммитом bb541a8, оба новых поля живут в коде (runevents.go:206, export.go:100). Положительный контроль на пустой греп: 'volume.go|sqlgate' в доке даёт ОДИН хит (строка 12) — инструмент в этом файле такие токены находит; ноль по 'config_drift_basis|VolumeLedger' — отсутствие факта, а не опечатка. Точное числительное «двух» стало ложной гарантией полноты: непокрытых механизмов теперь минимум три. Замечу отдельно: доп-греп показал, что док не знает НИ ОДНОЙ ноты из D39.176–184 (0 упоминаний), так что дефект шире названного. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно; предпочтительна названная автором «лучшая» ветка — внести объёмный потолок, гейт схемы и закон раскрытия В САМУ таблицу слоёв и снять оговорку целиком, иначе числительное будет протухать при каждом следующем лендинге. Если правится только текст — не заменять «двух» на «трёх» (то же протухнет), а писать «ряда» + ссылку на реестр нот. --- ## N096 · MINOR · `docs/architecture/09-target-architecture.md:178` · B-мёртвый-указатель -**Срез:** Целевая архитектура — docs/architecture/09-target-architecture.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** | 5 Качество | `H5-no-in-loop-quality-signal`, `H14-inversion-guard` (D37 §2г), `H12-cross-boundary-coherence-untested` | +**Срез:** Целевая архитектура — docs/architecture/09-target-architecture.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> | 5 Качество | `H5-no-in-loop-quality-signal`, `H14-inversion-guard` (D37 §2г), `H12-cross-boundary-coherence-untested` | + **Почему протухло:** Токен `H14-inversion-guard` в ледджере, на который §3 явно ссылается заголовком раздела, не встречается ни разу: реальный идентификатор — `H14-D38.4-tail-inversion-guard`. Из восемнадцати проверенных ID таблицы §3 это ЕДИНСТВЕННЫЙ, который не грепается; остальные семнадцать резолвятся точно. -**Улика — команда:** `grep -c 'H14-inversion-guard' docs/archive/architecture/08-sync-audit-ledger.md; grep -n '^### `H14' docs/archive/architecture/08-sync-audit-ledger.md | cut -c1-110; grep -c 'H12-cross-boundary-coherence-untested' docs/archive/architecture/08-sync-audit-ledger.md` -**Улика — вывод:** 0 + +**Улика — команда:** +``` +grep -c 'H14-inversion-guard' docs/archive/architecture/08-sync-audit-ledger.md; grep -n '^### `H14' docs/archive/architecture/08-sync-audit-ledger.md | cut -c1-110; grep -c 'H12-cross-boundary-coherence-untested' docs/archive/architecture/08-sync-audit-ledger.md +``` +**Улика — вывод:** +``` +0 459:### `H14-D38.4-tail-inversion-guard` — **WORKAROUND_NOT_ROOT** / MEDIUM · верификатор: CONF 1 -**Положительный контроль:** grep -c 'H12-cross-boundary-coherence-untested' → 1 в том же файле: греп по ледджеру работает и соседние ID из той же строки таблицы находит; ноль по H14-inversion-guard — отсутствие цели, не поломка инструмента. -**Предложенное действие:** Заменить `H14-inversion-guard` на `H14-D38.4-tail-inversion-guard` (точный заголовок ледджера, строка 459). +``` +**Положительный контроль:** +``` +grep -c 'H12-cross-boundary-coherence-untested' → 1 в том же файле: греп по ледджеру работает и соседние ID из той же строки таблицы находит; ноль по H14-inversion-guard — отсутствие цели, не поломка инструмента. +``` + +**Предложенное действие (автора находки):** Заменить `H14-inversion-guard` на `H14-D38.4-tail-inversion-guard` (точный заголовок ледджера, строка 459). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно и минимально-разрушительно (замена одного токена). Уточнение к сопроводительной записи: писать не «цель переименована», а «ссылка была битой с рождения (оба написания пришли коммитом 225abc0)» — иначе следующая сессия пойдёт искать несуществующее переименование. + +**Разбор опровергателя:** Опровергнуть не удалось, и положительный контроль здесь исключительно сильный: 27 из 28 ID таблицы §3 грепаются в ледджере ровно по одному разу, ноль даёт ТОЛЬКО `H14-inversion-guard` — значит инструмент в этом файле работает, и ноль есть отсутствие цели. Реальный заголовок — `H14-D38.4-tail-inversion-guard` (строка 459). Токен `H14-inversion-guard` не встречается больше нигде в docs/ backend/ platform/ eval/ frontend/ CLAUDE.md. Уточнение к формулировке автора: указатель не «уехал» — `git log -S` показывает, что ОБА написания появились одним коммитом 225abc0, то есть ссылка РОДИЛАСЬ битой 13.07 и такой прожила. На класс B это не влияет, но точность цели правки повышает: чинится опечатка автора, а не последствие переименования. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно и минимально-разрушительно (замена одного токена). Уточнение к сопроводительной записи: писать не «цель переименована», а «ссылка была битой с рождения (оба написания пришли коммитом 225abc0)» — иначе следующая сессия пойдёт искать несуществующее переименование. --- ## N097 · MINOR · `docs/architecture/10-prompt-architecture.md:8` · B-мёртвый-указатель -**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** арх-ресета была размазана по `08-sync-audit-ledger` (дорожка L4), `09-target-architecture` (§0.1, §2 слой 2), D-логу (D30.2, D39 п.7, D39.1–39.2) и research/18–19. +**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> арх-ресета была размазана по `08-sync-audit-ledger` (дорожка L4), `09-target-architecture` (§0.1, §2 слой 2), D-логу (D30.2, D39 п.7, D39.1–39.2) и research/18–19. + **Почему протухло:** `08-sync-audit-ledger.md` вынесен в `docs/archive/architecture/` 25.07 как отработанный (санкция владельца, `docs/archive/README.md:5`), и рядом с `09-target-architecture` он больше не лежит. Док зовёт его голым именем-соседом, а колонка «Где живёт» трижды адресует его находки по идентификаторам (`L4-prompt-language-always-russian` — строка 16, `L1-*`/`H3` — строка 18, `H2` — строка 19) без пути, так что читателю не за что зацепиться. Соседний живой док уже исправлен: 09-target-architecture ссылается через `../archive/architecture/08-sync-audit-ledger.md` (строки 5 и 170). -**Улика — команда:** `find -L . -name '08-sync-audit*' -not -path '*/node_modules/*'; grep -n '08-sync-audit' docs/architecture/09-target-architecture.md; grep -rn 'L4-prompt-language-always-russian' --include='*.md' . | cut -c1-120` -**Улика — вывод:** find -L → ./docs/archive/architecture/08-sync-audit-ledger.md (единственный экземпляр; в docs/architecture/ его нет) + +**Улика — команда:** +``` +find -L . -name '08-sync-audit*' -not -path '*/node_modules/*'; grep -n '08-sync-audit' docs/architecture/09-target-architecture.md; grep -rn 'L4-prompt-language-always-russian' --include='*.md' . | cut -c1-120 +``` +**Улика — вывод:** +``` +find -L → ./docs/archive/architecture/08-sync-audit-ledger.md (единственный экземпляр; в docs/architecture/ его нет) 09-target-architecture.md:5: (`../archive/architecture/08-sync-audit-ledger.md`, 65 находок, адверсариально верифицированы; архивирован 25.07 как отработанный) 09-target-architecture.md:170: ## 3. Карта находок → слои (полный ледджер — `../archive/architecture/08-sync-audit-ledger.md`) grep L4-prompt-language-always-russian → docs/archive/architecture/08-sync-audit-ledger.md:209 · docs/architecture/09-target-architecture.md:175 · docs/architecture/10-prompt-architecture.md:16 -**Положительный контроль:** Отрицательный вывод («в docs/architecture/ файла нет») подстрахован: find -L docs -name '08*' находит три других 08-дока (docs/experiments/08-cost-model-v2.md, docs/research/08-legal.md, docs/archive/architecture/08-sync-audit-ledger.md) — инструмент в этой области файлы видит, симлинки раскрывает, ноль по нужному пути содержателен. +``` +**Положительный контроль:** +``` +Отрицательный вывод («в docs/architecture/ файла нет») подстрахован: find -L docs -name '08*' находит три других 08-дока (docs/experiments/08-cost-model-v2.md, docs/research/08-legal.md, docs/archive/architecture/08-sync-audit-ledger.md) — инструмент в этой области файлы видит, симлинки раскрывает, ноль по нужному пути содержателен. +``` **Другие носители того же факта:** docs/architecture/09-target-architecture.md:175 — цитирует те же аудит-идентификаторы (`L4-prompt-language-always-russian`, `H2`), но путь до ледджера у него верный (строки 5 и 170), поэтому носитель здоров и служит образцом правки -**Предложенное действие:** В строке 8 заменить `08-sync-audit-ledger` на `../archive/architecture/08-sync-audit-ledger.md` (архивирован 25.07 как отработанный) и один раз в §1 — при первом идентификаторе (строка 16) — дать тот же путь, чтобы `L1-*`/`H3`/`H2` резолвились одним хопом. + +**Предложенное действие (автора находки):** В строке 8 заменить `08-sync-audit-ledger` на `../archive/architecture/08-sync-audit-ledger.md` (архивирован 25.07 как отработанный) и один раз в §1 — при первом идентификаторе (строка 16) — дать тот же путь, чтобы `L1-*`/`H3`/`H2` резолвились одним хопом. + **Разрушающее:** False --- ## N098 · MINOR · `docs/architecture/10-prompt-architecture.md:17` · A-дрейф -**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** СЕАМ ПОСТРОЕН (пак-1, D39.2): конфиг pair-keyed `prompts: {zh-ru: …}` + `Book.LangPair()` + fail-loud на чужую пару. +**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> СЕАМ ПОСТРОЕН (пак-1, D39.2): конфиг pair-keyed `prompts: {zh-ru: …}` + `Book.LangPair()` + fail-loud на чужую пару. + **Почему протухло:** Механизм, названный здесь как построенный, СНЯТ паком-15: пар-ключевая карта `prompts:` в конфиге сегодня не просто не используется — она отвергается загрузчиком как миграционная ошибка («the pair-keyed `prompts:` map is retired»), промпт резолвится конвенцией `//.md`, а калибровка пары уехала в `configs/pairs/.yaml`. Шапка дока (строка 5) объявляет конвенционный резолв построенным, но §1 продолжает называть носителем сеама именно ретайрнутую карту, и читатель уносит форму конфига, которая сегодня уронит загрузку. `Book.LangPair()` и fail-loud — проверены, живы. -**Улика — команда:** `sed -n '938,940p' backend/internal/config/pipeline.go; grep -n 'func .*LangPair' backend/internal/config/book.go; grep -rn '^\s*prompts:' backend/configs/*.yaml` -**Улика — вывод:** pipeline.go:938-940: if len(st.LegacyPrompts) > 0 { + +**Улика — команда:** +``` +sed -n '938,940p' backend/internal/config/pipeline.go; grep -n 'func .*LangPair' backend/internal/config/book.go; grep -rn '^\s*prompts:' backend/configs/*.yaml +``` +**Улика — вывод:** +``` +pipeline.go:938-940: if len(st.LegacyPrompts) > 0 { bad("stage %q: the pair-keyed `prompts:` map is retired — a stage's prompt is resolved by convention as //.md (pack-15), so a pair is a DIRECTORY, not a config entry. Drop the map (put the pair calibration in configs/pairs/.yaml); set `prompt_override:` only if this stage deliberately runs a variant", st.Name) book.go:378: func (b *Book) LangPair() string { grep '^ *prompts:' backend/configs/*.yaml → пусто (ни один боевой конфиг карту не несёт) -**Положительный контроль:** Пустой грep `^\s*prompts:` по backend/configs/*.yaml подстрахован положительным контролем в том же наборе файлов: grep -rn 'prompt_override' backend/configs/*.yaml → configs/pipeline-c1.yaml:67, configs/pipeline-c2.yaml:42, configs/pipeline-c2.yaml:43 — грep по этим yaml находит промпт-ключи, значит пустота по `prompts:` содержательна. -**Предложенное действие:** В ячейке §1 заменить «конфиг pair-keyed `prompts: {zh-ru: …}`» на «пар-каталог `prompts/<пара>/<роль>.md` (конвенционный резолв, пак-15/D39.23; карта `prompts:` в конфиге РЕТАЙРНУТА и падает громко — `internal/config/pipeline.go:939`) + калибровка пары `configs/pairs/<пара>.yaml`», сохранив «`Book.LangPair()` + fail-loud на чужую пару» — обе половины проверены и живы. +``` +**Положительный контроль:** +``` +Пустой грep `^\s*prompts:` по backend/configs/*.yaml подстрахован положительным контролем в том же наборе файлов: grep -rn 'prompt_override' backend/configs/*.yaml → configs/pipeline-c1.yaml:67, configs/pipeline-c2.yaml:42, configs/pipeline-c2.yaml:43 — грep по этим yaml находит промпт-ключи, значит пустота по `prompts:` содержательна. +``` + +**Предложенное действие (автора находки):** В ячейке §1 заменить «конфиг pair-keyed `prompts: {zh-ru: …}`» на «пар-каталог `prompts/<пара>/<роль>.md` (конвенционный резолв, пак-15/D39.23; карта `prompts:` в конфиге РЕТАЙРНУТА и падает громко — `internal/config/pipeline.go:939`) + калибровка пары `configs/pairs/<пара>.yaml`», сохранив «`Book.LangPair()` + fail-loud на чужую пару» — обе половины проверены и живы. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Находка верна фактически, но действие СЛИШКОМ КРУПНОЕ, и это важно: шапка того же дока (строка 5, ⚠ СТАТУС-АПДЕЙТ 25.07) УЖЕ несёт правку дословно — «Pair-keyed промпты → конвенционный резолв `prompts/<пара>/<роль>.md` + конфиг-слой пары `configs/pairs/` (пак-15, D39.23)», — а по норме «баннер прежде содержимого» читатель её видит первой. Полный перепис ячейки создаёт ТРЕТИЙ носитель того же факта в одном доке (шапка + §1 + §4 п.2), а это ровно тот механизм, из которого рождается следующее расхождение. Минимальная правка: пометить в ячейке форму как историю пака-1 и сослаться на шапку — в стиле, который док уже применяет к §2 («⚠ Пути таблицы устарели механически…»), например «(форма пака-1; РЕТАЙРНУТА паком-15 — см. шапку)». Файл-лайн `pipeline.go:939` в док не вносить: анкер сгниёт; при нужде цитировать текст ошибки «the pair-keyed `prompts:` map is retired». + +**Разбор опровергателя:** Опровергнуть не удалось. Ключ `prompts:` жив в схеме ровно затем, чтобы быть ОТВЕРГНУТЫМ при загрузке (комментарий pipeline.go:194 + ветка 938-939), ни один боевой конфиг его не несёт, а положительный контроль тем же грепом по тем же файлам находит `prompt_override` — значит пустота не артефакт инструмента. Резолв сегодня конвенционный (`promptConventionPath`), калибровка пары — `configs/pairs/<пара>.yaml` (три файла на диске). Обе половины, которые автор объявил живыми, действительно живы: `Book.LangPair()` (book.go:378) и fail-loud на чужую пару (pipeline.go:955, «never silently substitute another pair's conventions»). То есть цитата описывает форму конфига, которая сегодня роняет загрузку. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Находка верна фактически, но действие СЛИШКОМ КРУПНОЕ, и это важно: шапка того же дока (строка 5, ⚠ СТАТУС-АПДЕЙТ 25.07) УЖЕ несёт правку дословно — «Pair-keyed промпты → конвенционный резолв `prompts/<пара>/<роль>.md` + конфиг-слой пары `configs/pairs/` (пак-15, D39.23)», — а по норме «баннер прежде содержимого» читатель её видит первой. Полный перепис ячейки создаёт ТРЕТИЙ носитель того же факта в одном доке (шапка + §1 + §4 п.2), а это ровно тот механизм, из которого рождается следующее расхождение. Минимальная правка: пометить в ячейке форму как историю пака-1 и сослаться на шапку — в стиле, который док уже применяет к §2 («⚠ Пути таблицы устарели механически…»), например «(форма пака-1; РЕТАЙРНУТА паком-15 — см. шапку)». Файл-лайн `pipeline.go:939` в док не вносить: анкер сгниёт; при нужде цитировать текст ошибки «the pair-keyed `prompts:` map is retired». --- ## N099 · MINOR · `docs/architecture/10-prompt-architecture.md:18` · B-мёртвый-указатель -**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** | D-лог D30.2 (`05:366` — «механизм реверстки — открытый вопрос») · аудит `L1-*`/`H3` · research/19 §C · exp15 Q4 | +**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> | D-лог D30.2 (`05:366` — «механизм реверстки — открытый вопрос») · аудит `L1-*`/`H3` · research/19 §C · exp15 Q4 | + **Почему протухло:** Строка 366 живого D-лога сегодня пустая и лежит внутри эры D39.135 (приёмка фронта S4, 15.08) — то есть на 250 строк мимо цели и в чужой эре. Цитируемый текст живёт на 05:116, в теле D30 п.2. Указатель дрейфанул от роста файла (1823 строки), автор не ошибался. -**Улика — команда:** `sed -n '366p' docs/architecture/05-decisions-log.md; grep -n 'реверстк' docs/architecture/05-decisions-log.md; grep -n '^## D30\.' docs/architecture/05-decisions-log.md | head -1; grep -n '^## D30\b' docs/architecture/05-decisions-log.md` -**Улика — вывод:** sed 366p → (пустая строка; ближайший заголовок выше — 05:365 «## D39.135 — ПРИЁМКА ФРОНТА S4 ПРИНЯТА И ЗАЛЕНДЕНА (267aa35) … (15.08)») + +**Улика — команда:** +``` +sed -n '366p' docs/architecture/05-decisions-log.md; grep -n 'реверстк' docs/architecture/05-decisions-log.md; grep -n '^## D30\.' docs/architecture/05-decisions-log.md | head -1; grep -n '^## D30\b' docs/architecture/05-decisions-log.md +``` +**Улика — вывод:** +``` +sed 366p → (пустая строка; ближайший заголовок выше — 05:365 «## D39.135 — ПРИЁМКА ФРОНТА S4 ПРИНЯТА И ЗАЛЕНДЕНА (267aa35) … (15.08)») grep реверстк → 116: 2. **Reflow-вёрстка:** снять «Сохраняй разбивку на абзацы» из zh→ru промптов (translator.md:9, editor.md:8 — дословно подтверждено) … Механизм реверстки (в промпте редактора vs отдельный пасс) — открытый вопрос пере-прогона grep '^## D30' → 111: ## D30. Приёмка exp12 + флип D1 (моно→билингв) … -**Положительный контроль:** Отрицательный результат («на 366 цели нет») предъявлен вместе с положительным: тот же grep по отличительному токену «реверстк» в ТОМ ЖЕ файле даёт хит на строке 116 — инструмент в этой области находит искомое, пустота на 366 не артефакт грепа. -**Предложенное действие:** Заменить `05:366` на `05:116` (тело D30, п.2). Ещё устойчивее — снять номер строки совсем и оставить «D-лог D30 п.2», как предписывает дисциплина чтения журнала (номер грепается: `grep -n '^## D30' `): подномер D30.2 собственного тела не имеет, а номер строки будет дрейфовать и дальше. +``` +**Положительный контроль:** +``` +Отрицательный результат («на 366 цели нет») предъявлен вместе с положительным: тот же grep по отличительному токену «реверстк» в ТОМ ЖЕ файле даёт хит на строке 116 — инструмент в этой области находит искомое, пустота на 366 не артефакт грепа. +``` + +**Предложенное действие (автора находки):** Заменить `05:366` на `05:116` (тело D30, п.2). Ещё устойчивее — снять номер строки совсем и оставить «D-лог D30 п.2», как предписывает дисциплина чтения журнала (номер грепается: `grep -n '^## D30' `): подномер D30.2 собственного тела не имеет, а номер строки будет дрейфовать и дальше. + **Разрушающее:** False --- ## N100 · MINOR · `docs/architecture/10-prompt-architecture.md:33` · A-дрейф -**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** Правила, сидящие В ТЕЛЕ промтов (кандидаты на извлечение в пакет пары после exp15): русское тире-оформление диалога; «пиши живым русским»; паратаксис→гипотаксис-блок; чэнъюй-политика; few-shot-примеры (структурные, по-русски). +**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> Правила, сидящие В ТЕЛЕ промтов (кандидаты на извлечение в пакет пары после exp15): русское тире-оформление диалога; «пиши живым русским»; паратаксис→гипотаксис-блок; чэнъюй-политика; few-shot-примеры (структурные, по-русски). + **Почему протухло:** Инвентарь пар-специфики в телах промтов неполон с 24.07: паком-13 в ОБА боевых промпта вписан блок «Единицы и приёмы (общие для zh→ru)» с зашитыми китайскими токенами и конверсионными таблицами (时辰 = 2 часа, 六成 = 60%, стихи — по смыслу), а 01.08 в editor.md добавлено правило авторского рефрена. Это самая явно данно-образная пар-специфика из всех перечисленных (числовые таблицы конверсии), и она уже имеет носителя-данные на детекторной стороне — langpack `configs/langpacks/zh-ru/dc-checkers.txt` — и отдельные repair-промпты `repair/dc1_time_units.md` / `repair/dc1_fractional.md`. В списке кандидатов на извлечение её нет, значит план извлечения недосчитывает свой главный лот. -**Улика — команда:** `sed -n '12,15p' backend/prompts/zh-ru/translator.md; sed -n '10p' backend/prompts/zh-ru/editor.md; git log -S'时辰' --format='%h %ad %s' --date=short -- backend/prompts/zh-ru/translator.md backend/prompts/translator.md; grep -rln '时辰' backend/configs backend/prompts` -**Улика — вывод:** translator.md:12-15: Единицы и приёмы (общие для zh→ru): + +**Улика — команда:** +``` +sed -n '12,15p' backend/prompts/zh-ru/translator.md; sed -n '10p' backend/prompts/zh-ru/editor.md; git log -S'时辰' --format='%h %ad %s' --date=short -- backend/prompts/zh-ru/translator.md backend/prompts/translator.md; grep -rln '时辰' backend/configs backend/prompts +``` +**Улика — вывод:** +``` +translator.md:12-15: Единицы и приёмы (общие для zh→ru): - МЕРЫ переводи в привычные читателю единицы: китайский 时辰 = 2 часа (三个时辰 ≈ шесть часов, 半个时辰 ≈ час) … Доля 成 — это десятые (六成 = 60%, 六成六 = 66%), а НЕ десятичная дробь «6,6». - СТИХИ и названия классики передавай ПО СМЫСЛУ … editor.md:10: - Единицы и приёмы (общие для zh→ru): МЕРЫ приводи к привычным читателю единицам (时辰 = 2 часа …); СТИХИ и названия классики — по смыслу … git log -S时辰 → 2f91b04 2026-07-24 Land pack-13 release QA … -grep -rln 时辰 → backend/configs/langpacks/zh-ru/dc-checkers.txt, backend/prompts/zh-ru/translator.md, backend/prompts/zh-ru/editor.md, backend/prompts/zh-ru/translator-banknote.md, backend/prompts/zh-ru/rep +grep -rln 时辰 → backend/configs/langpacks/zh-ru/dc-checkers.txt, backend/prompts/zh-ru/translator.md, backend/prompts/zh-ru/editor.md, backend/prompts/zh-ru/translator-banknote.md, backend/prompts/zh-ru/repair/dc1_time_units.md, backend/prompts/zh-ru/repair/dc1_fractional.md +``` **Другие носители того же факта:** docs/architecture/09-target-architecture.md:73-76 — описывает содержимое тех же двух промптов («подстрочник + слабая однострочка про вёрстку» / «4 мандатами сразу — верность, стиль, глоссарий, дискурс-reflow») и блок единиц/мер тоже не знает: тот же пробел в живом арх-доке -**Предложенное действие:** Дописать в список (аддитивно, без сноса существующих пунктов): «блок «Единицы и приёмы (общие для zh→ru)» — конверсии мер 时辰/成 и правило «стихи по смыслу», сидит В ОБОИХ боевых промптах (`translator.md:12-15`, `editor.md:10`, пак-13 от 24.07); у детекторной половины этого класса носитель-данные УЖЕ есть — `configs/langpacks/zh-ru/dc-checkers.txt` + `prompts/zh-ru/repair/dc1_time_units.md`/`dc1_fractional.md`, что делает его первым кандидатом на извлечение; авторский рефрен (`editor.md:6`, 01.08)». + +**Предложенное действие (автора находки):** Дописать в список (аддитивно, без сноса существующих пунктов): «блок «Единицы и приёмы (общие для zh→ru)» — конверсии мер 时辰/成 и правило «стихи по смыслу», сидит В ОБОИХ боевых промптах (`translator.md:12-15`, `editor.md:10`, пак-13 от 24.07); у детекторной половины этого класса носитель-данные УЖЕ есть — `configs/langpacks/zh-ru/dc-checkers.txt` + `prompts/zh-ru/repair/dc1_time_units.md`/`dc1_fractional.md`, что делает его первым кандидатом на извлечение; авторский рефрен (`editor.md:6`, 01.08)». + **Разрушающее:** False --- ## N101 · MINOR · `docs/architecture/10-prompt-architecture.md:41` · C-исполненное-как-открытое -**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Гейты на стройку: exp15 (какие правила реально несут) + research/18 §D-3 (форма пакета). +**Срез:** Архитектура промптов — docs/architecture/10-prompt-architecture.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Гейты на стройку: exp15 (какие правила реально несут) + research/18 §D-3 (форма пакета). + **Почему протухло:** exp15 как гейт ПАЛ: эксперимент исполнен и залендён 25.07 в редакции REV.2 (D39.7), потрачено $12.79. §4 п.2 того же дока это уже фиксирует («exp15 ЗАВЕРШЁН») и называет новую форсинг-функцию — ja→ru-реплику; §1 (строка 17) и §3 (строка 41) остались с прежней рамкой и держат построенный гейт как несданный. Док спорит сам с собой в трёх строках. -**Улика — команда:** `sed -n '6,8p' docs/experiments/15-segmentation-empirics.md; sed -n '17p;41p;45p' docs/architecture/10-prompt-architecture.md | cut -c1-190` -**Улика — вывод:** exp15:6-8: > **СТАТУС (перевёрнут полигоном, пакет-5, 25.07.2026): ЭКСПЕРИМЕНТ ИСПОЛНЕН И ЗАЛЕНДЁН в редакции REV.2 (D39.7).** Прежний статус-блок … описывал состояние ДО прогона и завис — снят. Факт: прогон состоялся, потрачено **$12.79 из капа $15** + +**Улика — команда:** +``` +sed -n '6,8p' docs/experiments/15-segmentation-empirics.md; sed -n '17p;41p;45p' docs/architecture/10-prompt-architecture.md | cut -c1-190 +``` +**Улика — вывод:** +``` +exp15:6-8: > **СТАТУС (перевёрнут полигоном, пакет-5, 25.07.2026): ЭКСПЕРИМЕНТ ИСПОЛНЕН И ЗАЛЕНДЁН в редакции REV.2 (D39.7).** Прежний статус-блок … описывал состояние ДО прогона и завис — снят. Факт: прогон состоялся, потрачено **$12.79 из капа $15** doc10:17: … извлечение в структурированный пакет гейтится exp15 + research/18 §D-3 («форма пакета не разрешена») doc10:41: Гейты на стройку: exp15 (какие правила реально несут) + research/18 §D-3 (форма пакета). doc10:45: 2. **Извлечение правил в пакет** — exp15 ЗАВЕРШЁН; **данные-часть майнера ИЗВЛЕЧЕНА (D39.16 … -**Предложенное действие:** В §3 и в ячейке §1 (строка 17) снять exp15 из списка невзятых гейтов и оставить единственный живой: «Гейт на стройку — research/18 §D-3 (форма пакета не разрешена); exp15 отработан 25.07 (D39.7), форсинг извлечения сегодня — ja→ru-реплика (§4 п.2)». Тем же касанием привести строку 33 («кандидаты на извлечение ПОСЛЕ exp15») к той же рамке. +``` + +**Предложенное действие (автора находки):** В §3 и в ячейке §1 (строка 17) снять exp15 из списка невзятых гейтов и оставить единственный живой: «Гейт на стройку — research/18 §D-3 (форма пакета не разрешена); exp15 отработан 25.07 (D39.7), форсинг извлечения сегодня — ja→ru-реплика (§4 п.2)». Тем же касанием привести строку 33 («кандидаты на извлечение ПОСЛЕ exp15») к той же рамке. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верное, добавить один якорь для принимающего: §D-3 — это `docs/research/18-quality-levers.md:273` (в репо два файла `18-*`: `18-quality-levers.md` и `18-quality-levers-chatgpt.md`; §D живёт в первом, во втором такой секции нет). Правку строки 33 автор назвал сам, она подтверждена (строка существует дословно). + +**Разбор опровергателя:** Воспроизводится, и вторым независимым носителем: exp15 объявлен исполненным не только своей шапкой, но и живым 09-target-architecture.md:26 («трек B исполнен, программа ЗАВЕРШЕНА, D39.7/D39.10») и реестром D39.7. Док спорит сам с собой: строки 33 и 41 держат exp15 несданным гейтом, строка 45 в том же доке уже пишет «exp15 ЗАВЕРШЁН». Отдельно проверил то, чего автор не проверял и что могло бы убить его же действие: указатель research/18 §D-3 ЖИВОЙ — это пункт 3 §D по строке 273, дословно «Размер/форма пакета конвенций… не решён», — так что оставление его единственным гейтом не заводит мёртвую ссылку. Класс C верен, severity minor справедлив. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верное, добавить один якорь для принимающего: §D-3 — это `docs/research/18-quality-levers.md:273` (в репо два файла `18-*`: `18-quality-levers.md` и `18-quality-levers-chatgpt.md`; §D живёт в первом, во втором такой секции нет). Правку строки 33 автор назвал сам, она подтверждена (строка существует дословно). --- ## N102 · MINOR · `docs/architecture/12-go-style-notes.md:8` · C-исполненное-как-открытое -**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** **Пар-скоупнутые модули** (checkers_zh_ru и т.п.) легитимны как код за pair-гейтом, но их ДАННЫЕ (словники, руны, таблицы) подлежат выносу в langpack. +**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> **Пар-скоупнутые модули** (checkers_zh_ru и т.п.) легитимны как код за pair-гейтом, но их ДАННЫЕ (словники, руны, таблицы) подлежат выносу в langpack. + **Почему протухло:** Норма как норма жива, но её единственный пример — мёртвый указатель, и он указывает ровно в обратную сторону. `backend/internal/pipeline/checkers_zh_ru.go` (295 строк) УДАЛЁН коммитом `952469b` 24.07.2026 — в тот же день, которым датирован сам норматив; данные ушли в `configs/langpacks/zh-ru/dc-checkers.txt`, а чекеры стали пар-агностичными. То есть `checkers_zh_ru` — эталон ЗАВЕРШЁННОГО выноса, а док предъявляет его как открытый долг. Тот же класс указателя уже вычищали из кода: ресёрч 31.07 нашёл 6 живых ссылок на удалённый файл и назвал их вредными («рекламируют пар-модуль там, где код уже generic»), 02.08 их сняли («grep пуст») — а седьмая, докова, осталась. -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main && git show --stat --format='%h %ad' --date=short 952469b -- '*checkers_zh_ru*' | cat; git ls-tree -r --name-only HEAD | grep checkers_zh_ru; wc -l backend/configs/langpacks/zh-ru/dc-checkers.txt` -**Улика — вывод:** 952469b 2026-07-24 + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main && git show --stat --format='%h %ad' --date=short 952469b -- '*checkers_zh_ru*' | cat; git ls-tree -r --name-only HEAD | grep checkers_zh_ru; wc -l backend/configs/langpacks/zh-ru/dc-checkers.txt +``` +**Улика — вывод:** +``` +952469b 2026-07-24 backend/internal/pipeline/checkers_zh_ru.go | 295 ----------------------- backend/internal/pipeline/checkers_zh_ru_test.go | 25 +- 2 files changed, 19 insertions(+), 301 deletions(-) @@ -2016,77 +3783,158 @@ git ls-tree HEAD | grep checkers_zh_ru → backend/internal/checks/checkers_zh_r 80 backend/configs/langpacks/zh-ru/dc-checkers.txt docs/archive/reports/GENERALITY_RESEARCH_2026-07-31.md:144: «6 живых указателей на checkers_zh_ru.go — файла нет с 952469b (24.07, 7 дней назад…). Вредны именно для этой инвентаризации: рекламируют пар-модуль там, где код уже generic» -docs/archive/reports/SMALLPACK_TAILS_2026-08-02.md:65: «6 указателей на checkers_zh_ru.go | с -**Положительный контроль:** git ls-tree -r --name-only HEAD | grep checkers_zh_ru → backend/internal/checks/checkers_zh_ru_test.go (греп по этому токену в дереве HEAD что-то находит; значит отсутствие `.go`-файла — факт, а не опечатка в шаблоне) -**Предложенное действие:** Заменить пример на живой: «**Пар-скоупнутые модули** легитимны как код за pair-гейтом, но их ДАННЫЕ (словники, руны, таблицы) подлежат выносу в langpack. ⚠ Прежний пример `checkers_zh_ru.go` снят: файл удалён `952469b` (24.07), детект-паттерны живут в `configs/langpacks/zh-ru/dc-checkers.txt`, чекеры пар-агностичны. Живой остаток долга — строка 79 бэклога (остаточные Cyrillic-хардкоды пар-гейтед DC-чекеров).» +docs/archive/reports/SMALLPACK_TAILS_2026-08-02.md:65: «6 указателей на checkers_zh_ru.go | сделано (D39.78): grep пуст.» +``` +**Положительный контроль:** +``` +git ls-tree -r --name-only HEAD | grep checkers_zh_ru → backend/internal/checks/checkers_zh_ru_test.go (греп по этому токену в дереве HEAD что-то находит; значит отсутствие `.go`-файла — факт, а не опечатка в шаблоне) +``` + +**Предложенное действие (автора находки):** Заменить пример на живой: «**Пар-скоупнутые модули** легитимны как код за pair-гейтом, но их ДАННЫЕ (словники, руны, таблицы) подлежат выносу в langpack. ⚠ Прежний пример `checkers_zh_ru.go` снят: файл удалён `952469b` (24.07), детект-паттерны живут в `configs/langpacks/zh-ru/dc-checkers.txt`, чекеры пар-агностичны. Живой остаток долга — строка 79 бэклога (остаточные Cyrillic-хардкоды пар-гейтед DC-чекеров).» + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Правку делать, но без двух ошибок предложенного текста. (1) Не оформлять как «исполненное как открытое» — норма в силе; менять только пример: назвать `checkers_zh_ru.go` СНЯТЫМ образцом завершённого выноса (удалён `952469b` 24.07, данные — `configs/langpacks/zh-ru/dc-checkers.txt`, 80 строк) и, если нужен живой пример, брать пар-гейченные DC-чекеры в `internal/checks`. (2) НЕ писать «живой остаток долга — строка 79 бэклога (остаточные Cyrillic-хардкоды)»: строка 79 — про Go-константу `cjkScriptNames` (плотность письма для серий), и её кириллические компоненты в ней же помечены ИСПОЛНЕННЫМИ (D39.78); носителем чистки мёртвых указателей на checkers_zh_ru.go была строка 89 (D39.72), и она ЗАКРЫТА — то есть остатка долга по этому предмету в трекере нет вовсе, доковый пример — последний хвост. + +**Разбор опровергателя:** Факт воспроизведён: прод-модуль, названный единственным примером нормы, удалён 24.07, а сама норма написана тем же днём на 19.5 часов раньше — классический «родилось верным, сгнило дрейфом», а не впечатление. Смягчение, которое находка не заметила: токен `checkers_zh_ru` (без `.go`) в дереве всё ещё РЕЗОЛВИТСЯ — в `internal/checks/checkers_zh_ru_test.go`, то есть греп даёт не ноль, и указатель «полумёртвый», а не стерильно мёртвый. Класс тоже назван неточно: сама НОРМА («данные пар-модулей — в langpack») жива и ничего не объявляет открытым — протух только иллюстративный пример, это B, а не C. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Правку делать, но без двух ошибок предложенного текста. (1) Не оформлять как «исполненное как открытое» — норма в силе; менять только пример: назвать `checkers_zh_ru.go` СНЯТЫМ образцом завершённого выноса (удалён `952469b` 24.07, данные — `configs/langpacks/zh-ru/dc-checkers.txt`, 80 строк) и, если нужен живой пример, брать пар-гейченные DC-чекеры в `internal/checks`. (2) НЕ писать «живой остаток долга — строка 79 бэклога (остаточные Cyrillic-хардкоды)»: строка 79 — про Go-константу `cjkScriptNames` (плотность письма для серий), и её кириллические компоненты в ней же помечены ИСПОЛНЕННЫМИ (D39.78); носителем чистки мёртвых указателей на checkers_zh_ru.go была строка 89 (D39.72), и она ЗАКРЫТА — то есть остатка долга по этому предмету в трекере нет вовсе, доковый пример — последний хвост. --- ## N103 · MINOR · `docs/architecture/12-go-style-notes.md:16` · B-мёртвый-указатель -**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** **Русским (target-языком) ОСТАЮТСЯ и не трогаются:** wire/инъекц-промпты, которые читает модель (`glossaryBlockHeader`, маркер `⟨проверить⟩`, gender-аннотации; …) +**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> **Русским (target-языком) ОСТАЮТСЯ и не трогаются:** wire/инъекц-промпты, которые читает модель (`glossaryBlockHeader`, маркер `⟨проверить⟩`, gender-аннотации; …) + **Почему протухло:** Идентификатора `glossaryBlockHeader` в дереве нет: константа с русским заголовком жила в `internal/pipeline/memory.go:504` и удалена тем же паком-14 (`952469b`, 24.07) — русский wire-текст перестал быть Go-идентификатором и стал СТРОКОЙ ДАННЫХ `ru\tglossary_header\t…` в `internal/lang/data/injection.txt`; в Go осталось только поле `InjectionTexts.GlossaryHeader` (`internal/lang/embedded.go:387`), которое само по себе никакого русского не несёт. Правило неперевода wire-строк в силе, но названный носитель уехал: сессия, ищущая «где нельзя трогать русский», грепает `glossaryBlockHeader` и получает ноль, а настоящий охраняемый объект — `injection.txt`. -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main && grep -rn 'glossaryBlockHeader' --include=*.go backend/ ; echo '--- was ---'; git grep -n 'glossaryBlockHeader' 952469b^ -- backend/ | head -3; echo '--- now ---'; grep -n 'glossary_header' backend/internal/lang/data/injection.txt` -**Улика — вывод:** grep -rn 'glossaryBlockHeader' --include=*.go backend/ → (пусто) + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main && grep -rn 'glossaryBlockHeader' --include=*.go backend/ ; echo '--- was ---'; git grep -n 'glossaryBlockHeader' 952469b^ -- backend/ | head -3; echo '--- now ---'; grep -n 'glossary_header' backend/internal/lang/data/injection.txt +``` +**Улика — вывод:** +``` +grep -rn 'glossaryBlockHeader' --include=*.go backend/ → (пусто) --- was --- 952469b^:backend/internal/pipeline/memory.go:501:// glossaryBlockHeader introduces the injected glossary block. … 952469b^:backend/internal/pipeline/memory.go:504:const glossaryBlockHeader = "ГЛОССАРИЙ (используй эти утверждённые переводы имён и терминов последовательно; строки с пометкой ⟨проверить⟩ — неподтверждённые кандидаты):" --- now --- backend/internal/lang/data/injection.txt:6:ru\tglossary_header\tГЛОССАРИЙ (используй эти утверждённые переводы имён и терминов последовательно; строки с пометкой ⟨проверить⟩ — неподтверждённые кандидаты): -**Положительный контроль:** grep -rno '[a-zA-Z]*[Gg]lossary[A-Za-z]*' --include=*.go backend/ | sort -u → RenderGlossaryBlock, GlossaryHeader, GlossaryInjection, glossaryLineTokens, … (37 идентификаторов). Инструмент в этой области находит десятки соседних имён; пустой результат по `glossaryBlockHeader` — отсутствие символа, а не промах грепа. -**Предложенное действие:** Заменить `glossaryBlockHeader` на действующие носители: «…которые читает модель (target-данные инъекции `internal/lang/data/injection.txt`: `glossary_header` / `editor_header` / `editor_unverified_header` / `unverified_marker` / `gender_*`; в Go им отвечает `lang.InjectionTexts`, `internal/lang/embedded.go:387`)». Так правило начинает указывать на файл, правка которого действительно двигает провод. +``` +**Положительный контроль:** +``` +grep -rno '[a-zA-Z]*[Gg]lossary[A-Za-z]*' --include=*.go backend/ | sort -u → RenderGlossaryBlock, GlossaryHeader, GlossaryInjection, glossaryLineTokens, … (37 идентификаторов). Инструмент в этой области находит десятки соседних имён; пустой результат по `glossaryBlockHeader` — отсутствие символа, а не промах грепа. +``` + +**Предложенное действие (автора находки):** Заменить `glossaryBlockHeader` на действующие носители: «…которые читает модель (target-данные инъекции `internal/lang/data/injection.txt`: `glossary_header` / `editor_header` / `editor_unverified_header` / `unverified_marker` / `gender_*`; в Go им отвечает `lang.InjectionTexts`, `internal/lang/embedded.go:387`)». Так правило начинает указывать на файл, правка которого действительно двигает провод. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Замену делать, но без предложенного якоря `internal/lang/embedded.go:387`: 387 — это ПОЛЕ `GlossaryHeader`, тип `InjectionTexts` объявлен на 386, и главное — file:line-якоря живых доков линтуются `docs/scripts/counts.py --lint` и гниют при каждой правке файла. Писать без номера строки: «…target-данные инъекции `internal/lang/data/injection.txt` (`glossary_header` · `editor_header` · `editor_unverified_header` · `unverified_marker` · `gender_*`); в Go им отвечает тип `lang.InjectionTexts` (`internal/lang/embedded.go`)». Оговорку про санкционированное снятие маркера (строка 134 / D39.104) сохранить — она к идентификатору не относится. + +**Разбор опровергателя:** Отрицательный вывод получен с честным положительным контролем (51 соседний идентификатор), поэтому пустой греп — отсутствие символа, а не промах шаблона; историю подтвердил обеими сторонами коммита 952469b. Указатель мёртв дважды: он не резолвится ни в дереве, ни в подсказке «где нельзя трогать русский» — настоящий охраняемый объект переехал в ДАННЫЕ (injection.txt), правка которых двигает `pack.Version()`/снапшот. Два других примера в той же скобке (`⟨проверить⟩`, gender-аннотации) живы, так что протух ровно один из трёх — минорность находки подтверждаю. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Замену делать, но без предложенного якоря `internal/lang/embedded.go:387`: 387 — это ПОЛЕ `GlossaryHeader`, тип `InjectionTexts` объявлен на 386, и главное — file:line-якоря живых доков линтуются `docs/scripts/counts.py --lint` и гниют при каждой правке файла. Писать без номера строки: «…target-данные инъекции `internal/lang/data/injection.txt` (`glossary_header` · `editor_header` · `editor_unverified_header` · `unverified_marker` · `gender_*`); в Go им отвечает тип `lang.InjectionTexts` (`internal/lang/embedded.go`)». Оговорку про санкционированное снятие маркера (строка 134 / D39.104) сохранить — она к идентификатору не относится. --- ## N104 · MINOR · `docs/architecture/12-go-style-notes.md:17` · A-дрейф -**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** ни `W0`/`W1`/`W1.5`/`W2` в идентификаторах, логах и комментариях — пишем `waveDraft`/`waveEdit` … в код и в операторский вывод они не возвращаются. +**Срез:** Заметки реализации и Go-нормы — docs/architecture/03-implementation-notes.md · docs/architecture/12-go-style-notes.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> ни `W0`/`W1`/`W1.5`/`W2` в идентификаторах, логах и комментариях — пишем `waveDraft`/`waveEdit` … в код и в операторский вывод они не возвращаются. + **Почему протухло:** Фактический хвост нормы («в код … не возвращаются») сегодня ложен: в НЕтестовом Go живут 8 упоминаний, все в комментариях — `cmd/tmctl/main.go:313`, `internal/config/pipeline.go:90,94,101,103,108`, `internal/config/book.go:84,85`. Норма запрещает аббревиатуры именно «в идентификаторах, логах и КОММЕНТАРИЯХ», так что это её прямое нарушение, а не серая зона. Все восемь датированы 19.07 — днём самой директивы — и с тех пор ни разу не вычищены. В идентификаторах и в операторском выводе аббревиатур действительно нет (проверено): протух только хвост про «код целиком». -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main/backend && grep -rnE '\bW0\b|\bW1\b|\bW1\.5\b|\bW2\b' --include=*.go . | grep -v _test.go; git blame -L 313,313 --date=short -- cmd/tmctl/main.go | cat` -**Улика — вывод:** cmd/tmctl/main.go:313:// pre-flight the operator runs on the mined delta before the W1.5 reseed. Prints "OK" on a clean seed. + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main/backend && grep -rnE '\bW0\b|\bW1\b|\bW1\.5\b|\bW2\b' --include=*.go . | grep -v _test.go; git blame -L 313,313 --date=short -- cmd/tmctl/main.go | cat +``` +**Улика — вывод:** +``` +cmd/tmctl/main.go:313:// pre-flight the operator runs on the mined delta before the W1.5 reseed. Prints "OK" on a clean seed. internal/config/pipeline.go:90:// over the draft chunks (W1) and edit units (W2). It is a TRANSPORT axis … internal/config/pipeline.go:94:// to 1: a wave-STRUCTURED but sequential run (W1 all drafts → W1.5 → W2 all units), deterministic and safe; internal/config/pipeline.go:101:// Mining configures the W1.5 bank-mining stop (WS3 / R1): … internal/config/pipeline.go:103:// langpack_root): the miner then runs at the W1.5 boundary over the W1 drafts … internal/config/pipeline.go:108:// … Empty ContrastPath ⇒ W1.5 auto-continues. -internal/config/book.go:84:// … Empty ⇒ no +internal/config/book.go:84:// … Empty ⇒ no pack (the miner is inert / W1.5 auto-continues); +internal/config/book.go:85:// set ⇒ the runner loads the pack in W0, failing LOUD … + +git blame → 13c4375e (Claude (backend session) 2026-07-19 313) +``` **Другие носители того же факта:** docs/glossary.md:39 — «Волны — waveDraft (черновик) → терминолог → waveEdit (редактура); имена только полные, аббревиатуры W0/W1 запрещены (решение владельца 19.07)». Носитель ВЕРЕН: несёт норму без ложного фактического хвоста, править его не нужно. -**Предложенное действие:** Снять недоказуемый хвост и заменить его на состояние с ценой: «… — пишем `waveDraft`/`waveEdit`, «draft wave started». В идентификаторах и операторском выводе аббревиатур нет (проверено грепом); ⚠ в КОММЕНТАРИЯХ остались 8 хвостов от 19.07 — `cmd/tmctl/main.go:313`, `internal/config/pipeline.go:90,94,101,103,108`, `internal/config/book.go:84,85`; чистка комментария поведенчески нейтральна и голден не двигает — снимать попутно при правке этих файлов.» + +**Предложенное действие (автора находки):** Снять недоказуемый хвост и заменить его на состояние с ценой: «… — пишем `waveDraft`/`waveEdit`, «draft wave started». В идентификаторах и операторском выводе аббревиатур нет (проверено грепом); ⚠ в КОММЕНТАРИЯХ остались 8 хвостов от 19.07 — `cmd/tmctl/main.go:313`, `internal/config/pipeline.go:90,94,101,103,108`, `internal/config/book.go:84,85`; чистка комментария поведенчески нейтральна и голден не двигает — снимать попутно при правке этих файлов.» + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Не вписывать в норматив предложенный список из восьми file:line — он неполон уже при рождении (пропущены `internal/pipeline/bankmaterialize.go:98` и комментарии `configs/pipeline-arm-mistral.yaml:3`, `configs/pipeline-arm-deepseek-pro.yaml:3`) и попадает под линт якорей `counts.py --lint`, то есть гниёт с первой же правкой этих файлов. Правильное лечение двухчастное: (1) в доке снять недоказуемый описательный хвост, оставив НОРМУ и одну датированную фразу без якорей — «⚠ в комментариях остались хвосты от 19.07; чистка поведенчески нейтральна и голден не двигает — снимать попутно»; (2) сама чистка — зона БЭКЕНДА, значит нужна строка бэклога на свип комментариев (греп вести токеном `W[012](\.5)?` с учётом подчёркиваний, иначе `snapshot_W2` снова уедет). Второй носитель `docs/glossary.md:39` находка оценила верно — он несёт норму без ложного хвоста и правки не требует. + +**Разбор опровергателя:** Факт воспроизведён моей командой: аббревиатуры волн живут в НЕтестовых комментариях, а норма запрещает их именно «в идентификаторах, логах и комментариях», поэтому хвост «в код … не возвращаются» как ОПИСАНИЕ сегодня ложен (и был ложен уже в момент переноса нормы в док 21.08). Половина про операторский вывод и идентификаторы, наоборот, подтверждается — там чисто. Отмечаю, что перечень находки НЕПОЛОН: её регекс `\bW2\b` не ловит `snapshot_W2` (подчёркивание — словесный символ), и девятый нетестовый комментарий плюс два конфига остались за кадром. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Не вписывать в норматив предложенный список из восьми file:line — он неполон уже при рождении (пропущены `internal/pipeline/bankmaterialize.go:98` и комментарии `configs/pipeline-arm-mistral.yaml:3`, `configs/pipeline-arm-deepseek-pro.yaml:3`) и попадает под линт якорей `counts.py --lint`, то есть гниёт с первой же правкой этих файлов. Правильное лечение двухчастное: (1) в доке снять недоказуемый описательный хвост, оставив НОРМУ и одну датированную фразу без якорей — «⚠ в комментариях остались хвосты от 19.07; чистка поведенчески нейтральна и голден не двигает — снимать попутно»; (2) сама чистка — зона БЭКЕНДА, значит нужна строка бэклога на свип комментариев (греп вести токеном `W[012](\.5)?` с учётом подчёркиваний, иначе `snapshot_W2` снова уедет). Второй носитель `docs/glossary.md:39` находка оценила верно — он несёт норму без ложного хвоста и правки не требует. --- ## N105 · MINOR · `docs/architecture/13-tech-debt-anchors.md:12` · E-второй-носитель -**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** backend/internal/store/migrate.go:186 «translit_policy … field only in v1; validator = Phase 2» и :194 «ruby_reading … bridge to Polivanov dst validation, B6 Phase 2 — NOT a dst» +**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> backend/internal/store/migrate.go:186 «translit_policy … field only in v1; validator = Phase 2» и :194 «ruby_reading … bridge to Polivanov dst validation, B6 Phase 2 — NOT a dst» + **Почему протухло:** Один и тот же якорь ja-препа живёт в двух живых носителях, и копии разошлись: строка 81 бэклога уже исправлена на «migrate.go:187/194», а справочник — тот самый файл, куда бэклог отсылает за якорями, — застрял на :186. Реальные позиции: 187 и 195, то есть в справочнике ошибочны ОБА числа, а в трекере — одно. Это прямой контрпример объявленной норме файла «Актуализируется лендингами паков, которые трогают эти строки»: лендинг поправил трекер и не поправил справочник, ради которого трекер и держат тонким. -**Улика — команда:** `grep -n 'translit_policy TEXT' backend/internal/store/migrate.go; grep -n 'ruby_reading TEXT' backend/internal/store/migrate.go; grep -nE '^\| 81 \|' docs/PROGRESS.md | grep -o 'migrate.go:[0-9/]*'` -**Улика — вывод:** 187: translit_policy TEXT NOT NULL DEFAULT '', -- B5 western-name-via-katakana (field only in v1; validator = Phase 2) + +**Улика — команда:** +``` +grep -n 'translit_policy TEXT' backend/internal/store/migrate.go; grep -n 'ruby_reading TEXT' backend/internal/store/migrate.go; grep -nE '^\| 81 \|' docs/PROGRESS.md | grep -o 'migrate.go:[0-9/]*' +``` +**Улика — вывод:** +``` +187: translit_policy TEXT NOT NULL DEFAULT '', -- B5 western-name-via-katakana (field only in v1; validator = Phase 2) 195: ruby_reading TEXT NOT NULL DEFAULT '', -- ruby-seeded rows: the furigana reading (bridge to Polivanov dst validation, B6 Phase 2 — NOT a dst) --- docs/PROGRESS.md строка 81: migrate.go:187/194 +``` **Другие носители того же факта:** docs/PROGRESS.md:89 (строка бэклога 81 — вторая, частично исправленная копия якоря) -**Предложенное действие:** Свести к одному носителю: в строке 81 бэклога оставить ссылку «якоря — §Срез 81/83/85 справочника», а числа держать только в справочнике (и там поправить 186→187, 194→195). Пока живут обе копии — при каждом лендинге, трогающем ja-преп, обязана обновляться ТА, куда отсылает вторая, иначе расхождение воспроизведётся. + +**Предложенное действие (автора находки):** Свести к одному носителю: в строке 81 бэклога оставить ссылку «якоря — §Срез 81/83/85 справочника», а числа держать только в справочнике (и там поправить 186→187, 194→195). Пока живут обе копии — при каждом лендинге, трогающем ja-преп, обязана обновляться ТА, куда отсылает вторая, иначе расхождение воспроизведётся. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Диагноз верен, лекарство — нет. Автор предлагает «числа держать только в справочнике», то есть сконцентрировать голые номера в одном носителе, — а именно голые номера и есть механизм гниения: в этом же файле 14 из 18 сверенных мной якорей уехали, и якорь `memory.go:1048` из ЭТОЙ ЖЕ строки 81 уехал дважды (сегодня цель на 1077), причём автор его не заметил, хотя он лежит в тех же двух носителях. Правильное действие — не переносить копию, а сделать ОБЕ копии машинно-проверяемыми: гейт `docs/scripts/counts.py --lint` (D39.126, бэклог-строка 205) сверяет якорь по содержимому, если он записан в токен-форме, поэтому в справочнике и в строке 81 писать `` `backend/internal/store/migrate.go:187`=`translit_policy TEXT` ``, `` `…:195`=`ruby_reading TEXT` `` и `` `backend/internal/membank/memory.go:1077`=`KANA and HAN are deliberately NOT boundary-checked` ``. Тогда расхождение копий перестаёт быть вопросом дисциплины лендинга: следующий сдвиг кода покраснеет в обеих. Дедупликацию носителей (ссылка вместо чисел) делать можно, но она вторична и без токенов не спасает — сегодняшний единственный носитель тоже протух. + +**Разбор опровергателя:** Опровергнуть не удалось. Оба числа справочника ошибочны (186→187, 194→195), в трекере одно из двух уже поправлено (187 ✓, 194 ✗) — то есть копии одного факта в двух ЖИВЫХ носителях действительно разошлись, класс E верен. Проверил и обратную гипотезу «автор читал устаревшую копию»: сверка сделана по рабочему дереву на HEAD 162ab9b, дерево по docs/ чистое (единственная незакоммиченная правка — docs/research/15-voice-and-state.md, к делу не относится). Заявленный автором вывод «прямой контрпример норме файла» подтверждается: лендинг, поправивший трекер, справочник не тронул. Severity minor согласна — обе цели существуют, ошибка в 1-2 строки, читатель дойдёт грепом. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Диагноз верен, лекарство — нет. Автор предлагает «числа держать только в справочнике», то есть сконцентрировать голые номера в одном носителе, — а именно голые номера и есть механизм гниения: в этом же файле 14 из 18 сверенных мной якорей уехали, и якорь `memory.go:1048` из ЭТОЙ ЖЕ строки 81 уехал дважды (сегодня цель на 1077), причём автор его не заметил, хотя он лежит в тех же двух носителях. Правильное действие — не переносить копию, а сделать ОБЕ копии машинно-проверяемыми: гейт `docs/scripts/counts.py --lint` (D39.126, бэклог-строка 205) сверяет якорь по содержимому, если он записан в токен-форме, поэтому в справочнике и в строке 81 писать `` `backend/internal/store/migrate.go:187`=`translit_policy TEXT` ``, `` `…:195`=`ruby_reading TEXT` `` и `` `backend/internal/membank/memory.go:1077`=`KANA and HAN are deliberately NOT boundary-checked` ``. Тогда расхождение копий перестаёт быть вопросом дисциплины лендинга: следующий сдвиг кода покраснеет в обеих. Дедупликацию носителей (ссылка вместо чисел) делать можно, но она вторична и без токенов не спасает — сегодняшний единственный носитель тоже протух. --- ## N106 · MINOR · `docs/architecture/13-tech-debt-anchors.md:24` · B-мёртвый-указатель -**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** 02-mvp-plan.md:34 «пере-съём exp08 v3» как открытый +**Срез:** Ранние арх-доки (кандидаты в архив): docs/architecture/01-decisions.md · 02-mvp-plan.md · 04-unhappy-paths.md · 06-memory-risk-registry.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> 02-mvp-plan.md:34 «пере-съём exp08 v3» как открытый + **Почему протухло:** Якоря реестра техдолга в ДВА дока моего среза сдвинулись ровно на +6 строк и не были актуализированы: коммит d2cd30c (22.08) дописал ⚠-поправки в 02-mvp-plan.md (+7/−1 = +6) и 06-memory-risk-registry.md (+6). Сегодня: «пере-съём exp08 v3» — 02-mvp-plan.md:40 (на :34 стоит строка про `tmctl export`); A7 — 06:38 (на :32 стоит A1), D2 — 06:67 (на :61 пустая строка), «Отложено в v1.1/Фазу 2 … морфо-гейт глагольного рода (C3)» — 06:111 (на :105 стоит «Обязательно закрыть/учесть до заморозки схемы (шаг 4):»). -**Улика — команда:** `cd /home/ubuntu/projects/textmachine-main && git show --numstat d2cd30c -- docs/architecture/02-mvp-plan.md docs/architecture/06-memory-risk-registry.md | tail -2; for n in 34 40; do printf '02:%s -> ' $n; sed -n "${n}p" docs/architecture/02-mvp-plan.md | cut -c1-60; done; for n in 32 38 61 67 105 111; do printf '06:%s -> ' $n; sed -n "${n}p" docs/architecture/06-memory-risk-registry.md | cut -c1-55; done` -**Улика — вывод:** 7 1 docs/architecture/02-mvp-plan.md + +**Улика — команда:** +``` +cd /home/ubuntu/projects/textmachine-main && git show --numstat d2cd30c -- docs/architecture/02-mvp-plan.md docs/architecture/06-memory-risk-registry.md | tail -2; for n in 34 40; do printf '02:%s -> ' $n; sed -n "${n}p" docs/architecture/02-mvp-plan.md | cut -c1-60; done; for n in 32 38 61 67 105 111; do printf '06:%s -> ' $n; sed -n "${n}p" docs/architecture/06-memory-risk-registry.md | cut -c1-55; done +``` +**Улика — вывод:** +``` +7 1 docs/architecture/02-mvp-plan.md 6 0 docs/architecture/06-memory-risk-registry.md 02:34 -> - Экспорт txt/epub + отчёт — конкретизирован D29.1: `tmctl export` 02:40 -> 2. **Деньги:** телеметрия точна … мягкий sanity-якорь **~$0.7/ранобэ (до-флиповый exp08; … пере-съём exp08 v3)** @@ -2096,71 +3944,131 @@ docs/PROGRESS.md строка 81: migrate.go:187/194 06:67 -> | D2 | **Whole-book RAG по резюме — недоказанное расширение** 06:105 -> Обязательно закрыть/учесть до заморозки схемы (шаг 4): 06:111 -> 5. **Отложено в v1.1/Фазу 2 (не блокирует v1-схему, но заложить поля/место):** +``` **Другие носители того же факта:** docs/architecture/13-tech-debt-anchors.md:64 («06-memory-risk-registry.md:105» — тот же +6), docs/architecture/13-tech-debt-anchors.md:88 («06-memory-risk-registry.md:32,61,105» — тот же +6) -**Предложенное действие:** Одна арифметическая правка: во всех трёх местах 13-tech-debt-anchors.md прибавить 6 — 02-mvp-plan.md:34→:40; 06:32→:38, 06:61→:67, 06:105→:111. Урок на будущее: якоря в ⚠-баннируемые доки держать по цитате/секции, а не по номеру строки — любой новый баннер их снова сдвинет. + +**Предложенное действие (автора находки):** Одна арифметическая правка: во всех трёх местах 13-tech-debt-anchors.md прибавить 6 — 02-mvp-plan.md:34→:40; 06:32→:38, 06:61→:67, 06:105→:111. Урок на будущее: якоря в ⚠-баннируемые доки держать по цитате/секции, а не по номеру строки — любой новый баннер их снова сдвинет. + **Разрушающее:** False --- ## N107 · MINOR · `docs/architecture/13-tech-debt-anchors.md:104` · E-второй-носитель -**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** 84 (neuter отсутствует в enum memory.go:815-823 и в инъекционных текстах embedded.go:395-397; `gender: neuter` в сиде — тихий no-op без линта) +**Срез:** Реестр загейченных триггеров — docs/architecture/13-tech-debt-anchors.md (весь реестр, §Б-108 первым) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> 84 (neuter отсутствует в enum memory.go:815-823 и в инъекционных текстах embedded.go:395-397; `gender: neuter` в сиде — тихий no-op без линта) + **Почему протухло:** Факт про neuter лежит в этом файле ДВАЖДЫ, и копии разошлись ВНУТРИ одного документа: тело пункта (строки 70-72) несёт пере-сверку 23.08 «ЧЕТЫРЕ пункта из пяти ПРОТУХЛИ — носитель в коде ПОСТРОЕН», а «Сводка среза» тремя абзацами ниже по-прежнему утверждает обратное — «neuter отсутствует в enum … тихий no-op без линта». Оба цитируемых сводкой якоря мертвы: `case "neuter"` живёт на memory.go:848 (не 815-823), `GenderNeuter` — на embedded.go:398 (не 395-397). Читатель, который дойдёт только до сводки (а сводка — то, что читают быстро), унесёт опровергнутый факт. -**Улика — команда:** `sed -n '848p' backend/internal/membank/memory.go; sed -n '815,823p' backend/internal/membank/memory.go | head -3; sed -n '398p' backend/internal/lang/embedded.go; sed -n '395,397p' backend/internal/lang/embedded.go` -**Улика — вывод:** 848: case "neuter", "n": + +**Улика — команда:** +``` +sed -n '848p' backend/internal/membank/memory.go; sed -n '815,823p' backend/internal/membank/memory.go | head -3; sed -n '398p' backend/internal/lang/embedded.go; sed -n '395,397p' backend/internal/lang/embedded.go +``` +**Улика — вывод:** +``` +848: case "neuter", "n": --- memory.go:815-823 (то, что там на самом деле): --- // containsWholeWord reports whether s contains sub as a whole word ... --- embedded.go:398: --- GenderNeuter string // " (ср. — …)" — a neuter entity/creature (row 84); "" for a target wi --- embedded.go:395-397: соседние поля, не GenderNeuter --- +``` **Другие носители того же факта:** docs/architecture/13-tech-debt-anchors.md:70-72 (тело пункта «Строка 84» — противоположная, верная редакция) -**Предложенное действие:** В «Сводке среза» (строка 104) заменить фрагмент про 84 на «84 — ЗАКРЫТА D39.69 §3 (neuter end-to-end построен; живой остаток — комментарий схемы `migrate.go:184`)» и убрать фрагмент «feed_cap: не построен (grep=0) … батчи в лексикографическом порядке src». Общее правило по месту: сводки среза не должны нести собственную фактуру — только ссылку на пункты, иначе они гниют независимо от тел, как здесь. + +**Предложенное действие (автора находки):** В «Сводке среза» (строка 104) заменить фрагмент про 84 на «84 — ЗАКРЫТА D39.69 §3 (neuter end-to-end построен; живой остаток — комментарий схемы `migrate.go:184`)» и убрать фрагмент «feed_cap: не построен (grep=0) … батчи в лексикографическом порядке src». Общее правило по месту: сводки среза не должны нести собственную фактуру — только ссылку на пункты, иначе они гниют независимо от тел, как здесь. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно, но НЕПОЛНО, и в неполном виде оставит документ наполовину ложным. Автор чинит feed_cap только в СВОДКЕ, а тот же протухший факт несёт ТЕЛО пункта на строках 98-102: «feed_cap НЕ построен … кандидаты сортированы ЛЕКСИКОГРАФИЧЕСКИ по src (terminologist.go:191; terminology.go:212), а Batch сохраняет этот порядок (terminology.go:917-919)». Замером: `Batch` теперь упорядочивает батчи по УБЫВАНИЮ частоты (terminology.go:1161-1164, гвоздь TestBatchValueOrder в series_test.go:199), а ратифицированная D39.69 §1 говорит дословно «value-порядок отреза бюджета (частота-desc — **feed_cap ЗАКРЫТ этим**, из строки 86 изъят)». Сверх того путь `terminology.go` в теле мёртв как ПУТЬ: `backend/internal/pipeline/terminology.go` не существует, пакет живёт в `backend/internal/terminology/`. Поэтому: править строки 98-102 и 104 ОДНИМ касанием — тело пометить «→ ЗАКРЫТО D39.69 §1 (value-порядок частота-desc, `backend/internal/terminology/terminology.go`, гвоздь TestBatchValueOrder)», сводку свести к ссылке на пункты. И общее правило автора («сводки не должны нести собственную фактуру») верно, но реализуемо тут дешевле: строка 162 показывает работающую альтернативу — ⚠-баннер о том, что якоря сводки не актуализируются. + +**Разбор опровергателя:** Опровергнуть не удалось. Оба якоря сводки мертвы (815-823 — дедуп-цикл, 395-397 — соседние поля), а фактическое утверждение «тихий no-op без линта» опровергнуто тем же замером, что и находка №1. Класс E назван верно и подтверждён буквально: один факт лежит в ОДНОМ файле дважды (тело пункта, строки 68-72, и сводка, строка 104), и копии разошлись до прямой противоположности — тело говорит «носитель ПОСТРОЕН», сводка говорит «отсутствует». Ключевая проверка на анти-оверфлаг пройдена: ⚠-баннера над этой сводкой нет, тогда как соседняя сводка на строке 162 такой баннер несёт — значит здесь это не легитимное баннированное историческое, а незакрытая гниль. Severity minor занижена относительно риска (сводку читают быстро, и именно она уносится читателем), но как формальная оценка приемлема. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, но НЕПОЛНО, и в неполном виде оставит документ наполовину ложным. Автор чинит feed_cap только в СВОДКЕ, а тот же протухший факт несёт ТЕЛО пункта на строках 98-102: «feed_cap НЕ построен … кандидаты сортированы ЛЕКСИКОГРАФИЧЕСКИ по src (terminologist.go:191; terminology.go:212), а Batch сохраняет этот порядок (terminology.go:917-919)». Замером: `Batch` теперь упорядочивает батчи по УБЫВАНИЮ частоты (terminology.go:1161-1164, гвоздь TestBatchValueOrder в series_test.go:199), а ратифицированная D39.69 §1 говорит дословно «value-порядок отреза бюджета (частота-desc — **feed_cap ЗАКРЫТ этим**, из строки 86 изъят)». Сверх того путь `terminology.go` в теле мёртв как ПУТЬ: `backend/internal/pipeline/terminology.go` не существует, пакет живёт в `backend/internal/terminology/`. Поэтому: править строки 98-102 и 104 ОДНИМ касанием — тело пометить «→ ЗАКРЫТО D39.69 §1 (value-порядок частота-desc, `backend/internal/terminology/terminology.go`, гвоздь TestBatchValueOrder)», сводку свести к ссылке на пункты. И общее правило автора («сводки не должны нести собственную фактуру») верно, но реализуемо тут дешевле: строка 162 показывает работающую альтернативу — ⚠-баннер о том, что якоря сводки не актуализируются. --- ## N108 · MINOR · `docs/architecture/14-api-contract/README.md:6` · B-мёртвый-указатель -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** СТАТУС: РАТИФИЦИРОВАН как контракт API v0 по 0.9.0 включительно (D39.169; перечень миноров и их +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> СТАТУС: РАТИФИЦИРОВАН как контракт API v0 по 0.9.0 включительно (D39.169; перечень миноров и их провенанс — ниже по файлу, здесь НЕ дублируется) + **Почему протухло:** D39.169 ратифицировала минор 0.8.0, а не 0.9.0; 0.9.0 принят и заленджен нотой D39.180 (31.08). Тело того же дока (строка 69) описывает 0.9.0 вовсе БЕЗ номера ноты — «ратифицирован №20 31.08 … заленджен №21 с паком P12», так что читатель, которому шапка дала D39.169, номера 0.9.0 не найдёт нигде. -**Улика — команда:** `grep -n '^## D39.169\|^## D39.180' docs/architecture/05-decisions-log.md | cut -c1-150; sed -n '69,71p' docs/architecture/14-api-contract/README.md | cut -c1-160` -**Улика — вывод:** 1176:## D39.169 — ПЛАТФОРМЕННЫЙ ПАК P11 ПРИНЯТ И ЗАЛЕНДЖЕН + КОНТРАКТНЫЙ МИНОР 0.8.0: отзыв сессии гасит открытый поток, застрявшие деньги видны (29.08, оркестратор №19). ✅ + +**Улика — команда:** +``` +grep -n '^## D39.169\|^## D39.180' docs/architecture/05-decisions-log.md | cut -c1-150; sed -n '69,71p' docs/architecture/14-api-contract/README.md | cut -c1-160 +``` +**Улика — вывод:** +``` +1176:## D39.169 — ПЛАТФОРМЕННЫЙ ПАК P11 ПРИНЯТ И ЗАЛЕНДЖЕН + КОНТРАКТНЫЙ МИНОР 0.8.0: отзыв сессии гасит открытый поток, застрявшие деньги видны (29.08, оркестратор №19). ✅ 1677:## D39.180 — ПАК ПЛАТФОРМЫ P12 «ДОЛГИ ПОД НОГАМИ» ПРИНЯТ И ЗАЛЕНДЖЕН вместе с контрактным минором 0.9.0 (31.08, оркестратор №21). ✅ > **0.9.0 (ратифицирован №20 31.08 по пингу зоны ДО стройки, заленджен №21 с паком P12) — ВТОРАЯ > граница пересчёта `chapters_done` и непрозрачный `Book.shape_epoch`, которым она себя называет; -**Предложенное действие:** В шапке заменить «(D39.169; …)» на «(D39.180; …)» и в перечне миноров (строка 69) дописать номер ноты: «0.9.0 (D39.180, 31.08) — ратифицирован №20 по пингу зоны ДО стройки, заленджен №21 с паком P12». +``` + +**Предложенное действие (автора находки):** В шапке заменить «(D39.169; …)» на «(D39.180; …)» и в перечне миноров (строка 69) дописать номер ноты: «0.9.0 (D39.180, 31.08) — ратифицирован №20 по пингу зоны ДО стройки, заленджен №21 с паком P12». + **Разрушающее:** False --- ## N109 · MINOR · `docs/architecture/14-api-contract/README.md:60` · B-мёртвый-указатель -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** `/v0`; вывод и провенанс — §2.19, отчёт сессии — `docs/CONTRACT_MINOR_REPORT.md` +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> `/v0`; вывод и провенанс — §2.19, отчёт сессии — `docs/CONTRACT_MINOR_REPORT.md` + **Почему протухло:** Файла по этому пути нет: отчёт вынесен в архив коммитом 31b96d1 (27.08) и лежит как `docs/archive/reports/CONTRACT_MINOR_REPORT_2026-08-27.md`. Ссылка повторяется в теле дока (строка 738) и оба раза ведёт в никуда. -**Улика — команда:** `ls docs/CONTRACT_MINOR_REPORT.md; find /home/ubuntu/projects/textmachine-main -name 'CONTRACT_MINOR_REPORT*'; git show --stat 31b96d1 | grep CONTRACT` -**Улика — вывод:** ls: cannot access 'docs/CONTRACT_MINOR_REPORT.md': No such file or directory + +**Улика — команда:** +``` +ls docs/CONTRACT_MINOR_REPORT.md; find /home/ubuntu/projects/textmachine-main -name 'CONTRACT_MINOR_REPORT*'; git show --stat 31b96d1 | grep CONTRACT +``` +**Улика — вывод:** +``` +ls: cannot access 'docs/CONTRACT_MINOR_REPORT.md': No such file or directory /home/ubuntu/projects/textmachine-main/docs/archive/reports/CONTRACT_MINOR_REPORT_2026-08-27.md .../reports/CONTRACT_MINOR_REPORT_2026-08-27.md} | 8 ++++++++ -**Положительный контроль:** Тот же `find` по каталогу docs/ находит существующие доки (`./docs/PROGRESS.md`) и находит сам искомый отчёт по basename в архиве — значит инструмент в этой области видит, а ноль по `docs/CONTRACT_MINOR_REPORT.md` есть отсутствие файла, а не слепота поиска. +``` +**Положительный контроль:** +``` +Тот же `find` по каталогу docs/ находит существующие доки (`./docs/PROGRESS.md`) и находит сам искомый отчёт по basename в архиве — значит инструмент в этой области видит, а ноль по `docs/CONTRACT_MINOR_REPORT.md` есть отсутствие файла, а не слепота поиска. +``` **Другие носители того же факта:** docs/architecture/14-api-contract/README.md:738 — «подробный разбор и отвергнутые альтернативы — отчёт `docs/CONTRACT_MINOR_REPORT.md`» (тот же мёртвый путь) -**Предложенное действие:** Обе ссылки (строки 60 и 738) переписать на `docs/archive/reports/CONTRACT_MINOR_REPORT_2026-08-27.md` и пометить ⚠-архивной, чтобы инструкции оттуда не исполнялись. + +**Предложенное действие (автора находки):** Обе ссылки (строки 60 и 738) переписать на `docs/archive/reports/CONTRACT_MINOR_REPORT_2026-08-27.md` и пометить ⚠-архивной, чтобы инструкции оттуда не исполнялись. + **Разрушающее:** False --- ## N110 · MINOR · `docs/architecture/14-api-contract/README.md:990` · C-исполненное-как-открытое -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** | `PATCH`/`DELETE /books/{id}`, `GET /runs/{id}` | колонки есть | НЕ ПОСТРОЕНО (заведено 0.3.0) | вход P7 | +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> | `PATCH`/`DELETE /books/{id}`, `GET /runs/{id}` | колонки есть | НЕ ПОСТРОЕНО (заведено 0.3.0) | вход P7 | + **Почему протухло:** Носитель «вход P7» израсходован — пак P7 принят D39.153, и ⚠-абзац НАД самой таблицей это уже пишет («Носитель израсходован: пак P7 принят D39.153»). Факт «НЕ ПОСТРОЕНО» верен (роутер монтирует 15 маршрутов из 20 операций канона: нет `updateBook`/`deleteBook`/`getRun`/`createExport`/`getExport`), но строки живут без действующего носителя — ровно то нарушение §Б-21, которое таблица и заведена чинить. Тем же дефектом больны ещё три строки: `resume` (981), `GET …/notes` (988) и «Счёт сделанного» (1004). -**Улика — команда:** `grep -c 'operationId:' docs/architecture/14-api-contract/openapi.yaml; sed -n '/^var contractSurface/,/^}$/p' platform/internal/httpapi/v0.go | grep -c 'method:'; sed -n '/^var contractSurface/,/^}$/p' platform/internal/httpapi/v0.go | grep -o 'path: "[^"]*"' ` -**Улика — вывод:** 20 + +**Улика — команда:** +``` +grep -c 'operationId:' docs/architecture/14-api-contract/openapi.yaml; sed -n '/^var contractSurface/,/^}$/p' platform/internal/httpapi/v0.go | grep -c 'method:'; sed -n '/^var contractSurface/,/^}$/p' platform/internal/httpapi/v0.go | grep -o 'path: "[^"]*"' +``` +**Улика — вывод:** +``` +20 15 path: "/capabilities" path: "/books" @@ -2178,19 +4086,32 @@ path: "/runs/{runId}/stop" path: "/runs/{runId}/resume" path: "/books" (POST) — ни одного PATCH/DELETE /books/{bookId}, ни GET /runs/{runId}, ни /exports +``` **Другие носители того же факта:** docs/PROGRESS.md:3 — «канон обещает `createExport`/`getExport`, а платформа их НЕ монтирует (смонтировано 15 операций из 20; нет также `getRun`, `updateBook`, `deleteBook`)» — этот носитель ЖИВОЙ и совпадает с деревом, platform/BACKLOG.md П-17 — долг двери выдачи (по docs/PROGRESS.md:3) -**Предложенное действие:** Заменить в четырёх строках таблицы израсходованный носитель «вход P7» на живой: для `PATCH`/`DELETE /books/{id}` и `GET /runs/{id}` — строка зонного `platform/BACKLOG.md` (или новая строка единого бэклога), для `resume` — «закрыто D39.162/PD-282 fixed» (см. отдельную находку), для остальных — действующий пак/ноту. Само правило §Б-21 при этом не трогать: оно и поймало дефект. + +**Предложенное действие (автора находки):** Заменить в четырёх строках таблицы израсходованный носитель «вход P7» на живой: для `PATCH`/`DELETE /books/{id}` и `GET /runs/{id}` — строка зонного `platform/BACKLOG.md` (или новая строка единого бэклога), для `resume` — «закрыто D39.162/PD-282 fixed» (см. отдельную находку), для остальных — действующий пак/ноту. Само правило §Б-21 при этом не трогать: оно и поймало дефект. + **Разрушающее:** False --- ## N111 · MINOR · `docs/architecture/14-api-contract/README.md:998` · B-мёртвый-указатель -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** | `Note.code` как enum спеки | карта приложения А | не enum, пока не написаны фразы | строка 148 (фразы владельца) | +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> | `Note.code` как enum спеки | карта приложения А | не enum, пока не написаны фразы | строка 148 (фразы владельца) | + **Почему протухло:** Строки 148 в едином бэклоге НЕТ — её удалил `a023e39` решением владельца 21.08. Хуже: ТОТ ЖЕ документ на строке 1670 уже это знает и пишет «⚠ Прежний указатель «строка 148» мёртв: строку удалил `a023e39` решением владельца», а действующим носителем называет строки 203/204 и делегирование фраз (D39.176 п.4, подписи владельца больше не ждём). То есть §3 спорит с Приложением А того же файла. -**Улика — команда:** `for n in 49 94 148 160 161 186 228; do printf 'строка %s: %s\n' $n "$(grep -c "^| $n |" docs/PROGRESS.md)"; done; git show --stat a023e39 | head -8` -**Улика — вывод:** строка 49: 1 + +**Улика — команда:** +``` +for n in 49 94 148 160 161 186 228; do printf 'строка %s: %s\n' $n "$(grep -c "^| $n |" docs/PROGRESS.md)"; done; git show --stat a023e39 | head -8 +``` +**Улика — вывод:** +``` +строка 49: 1 строка 94: 1 строка 148: 0 строка 160: 1 @@ -2202,100 +4123,190 @@ commit a023e39323529de3101cb8a216471b604f69d667 Date: Fri Aug 21 19:31:20 2026 Record three owner decisions: cut comment water rather than length, drop the appendix row that described a world the contract abolished, and put the complain-to-owner norm into the frontend hook docs/PROGRESS.md | 7 +++---- -**Положительный контроль:** Та же форма грепа `^| N |` по docs/PROGRESS.md находит шесть других номеров, названных этим же доком (49, 94, 160, 161, 186, 228) — инструмент строки бэклога видит; ноль даёт именно 148. +``` +**Положительный контроль:** +``` +Та же форма грепа `^| N |` по docs/PROGRESS.md находит шесть других номеров, названных этим же доком (49, 94, 160, 161, 186, 228) — инструмент строки бэклога видит; ноль даёт именно 148. +``` **Другие носители того же факта:** docs/architecture/14-api-contract/README.md:1670 — второй носитель того же факта, и он УЖЕ верен («Прежний указатель «строка 148» мёртв»); расходятся именно две копии внутри одного дока -**Предложенное действие:** В строке 998 заменить носитель «строка 148 (фразы владельца)» на тот, который сам док уже назвал в Приложении А п.3: «строка 203 (карта причина→код) + строка 204 (доставка словаря данными); фразы делегированы проекту, D39.176 п.4 — подписи владельца не ждём». + +**Предложенное действие (автора находки):** В строке 998 заменить носитель «строка 148 (фразы владельца)» на тот, который сам док уже назвал в Приложении А п.3: «строка 203 (карта причина→код) + строка 204 (доставка словаря данными); фразы делегированы проекту, D39.176 п.4 — подписи владельца не ждём». + **Разрушающее:** False --- ## N112 · MINOR · `docs/architecture/14-api-contract/README.md:1002` · B-мёртвый-указатель -**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** заказано бэкенд-паку «тихая порча», `docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md` §3.3 +**Срез:** Контракт API и его зеркала — docs/architecture/14-api-contract/README.md + docs/architecture/14-api-contract/openapi.yaml +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> заказано бэкенд-паку «тихая порча», `docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md` §3.3 + **Почему протухло:** Промт вынесен в архив: живой путь — `docs/archive/prompts/BACKEND_SILENT_HARM_SESSION_PROMPT_2026-08-28.md`. Дополнительно: половина того, на что ссылка ведёт (§3.3), в этой же клетке уже объявлена ИСПОЛНЕННОЙ («пере-снято 30.08: … ВТОРОЙ ЗАКРЫТ … D39.164 §3.3»), то есть указатель ведёт в архив за работой, которая закрыта нотой. -**Улика — команда:** `ls docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md; ls docs/archive/prompts/ | grep SILENT` -**Улика — вывод:** ls: cannot access 'docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md': No such file or directory + +**Улика — команда:** +``` +ls docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md; ls docs/archive/prompts/ | grep SILENT +``` +**Улика — вывод:** +``` +ls: cannot access 'docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md': No such file or directory BACKEND_SESSION_PROMPT_SILENT_REFUSALS.md BACKEND_SILENT_HARM_SESSION_PROMPT_2026-08-28.md -**Положительный контроль:** Тот же `ls` в `docs/archive/prompts/` возвращает два файла с искомым токеном SILENT — каталог читается, поиск работает; ноль по `docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md` есть отсутствие файла. -**Предложенное действие:** Заменить путь на `docs/archive/prompts/BACKEND_SILENT_HARM_SESSION_PROMPT_2026-08-28.md` (⚠ архив), а живым носителем оставшейся половины назвать строку бэклога 228 — она в этой же клетке уже указана и в трекере существует. +``` +**Положительный контроль:** +``` +Тот же `ls` в `docs/archive/prompts/` возвращает два файла с искомым токеном SILENT — каталог читается, поиск работает; ноль по `docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md` есть отсутствие файла. +``` + +**Предложенное действие (автора находки):** Заменить путь на `docs/archive/prompts/BACKEND_SILENT_HARM_SESSION_PROMPT_2026-08-28.md` (⚠ архив), а живым носителем оставшейся половины назвать строку бэклога 228 — она в этой же клетке уже указана и в трекере существует. + **Разрушающее:** False --- ## N113 · MINOR · `docs/architecture/15-money-path.md:6` · C-исполненное-как-открытое -**Срез:** Денежный путь — docs/architecture/15-money-path.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** `docs/architecture/14-api-contract/openapi.yaml` лежит в «своей» зоне и при этом двигается прямо сейчас паком контракта 0.9.0. +**Срез:** Денежный путь — docs/architecture/15-money-path.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> `docs/architecture/14-api-contract/openapi.yaml` лежит в «своей» зоне и при этом двигается прямо сейчас паком контракта 0.9.0. + **Почему протухло:** Пак контракта 0.9.0 ЗАЛЕНДЖЕН 31.08 коммитом 0a680a3 (D39.180), файл в дереве чист, версия в нём — 0.9.0. Настоящее время «двигается прямо сейчас» ложно, и вместе с ним протух ЕДИНСТВЕННЫЙ пример, на котором шапка держит свою норму «стабильна не зона, а чистый файл», а также обоснование в §1: «⚠ номер не ставим: файл двигает пак контракта 0.9.0». По собственному правилу дока цель теперь чиста и годна под номерную форму с токеном. -**Улика — команда:** `git log --format='%h %ad %s' --date=short -1 -- docs/architecture/14-api-contract/openapi.yaml; git status --porcelain docs/architecture/14-api-contract/openapi.yaml; grep -n '^ version:' docs/architecture/14-api-contract/openapi.yaml` -**Улика — вывод:** 0a680a3 2026-08-31 Land platform pack P12 with contract minor 0.9.0: the bank-stop bypass is gone, a chapter counts by the pass current for it, and the boundary now judges prose too + +**Улика — команда:** +``` +git log --format='%h %ad %s' --date=short -1 -- docs/architecture/14-api-contract/openapi.yaml; git status --porcelain docs/architecture/14-api-contract/openapi.yaml; grep -n '^ version:' docs/architecture/14-api-contract/openapi.yaml +``` +**Улика — вывод:** +``` +0a680a3 2026-08-31 Land platform pack P12 with contract minor 0.9.0: the bank-stop bypass is gone, a chapter counts by the pass current for it, and the boundary now judges prose too (git status --porcelain: пустой вывод — файл чист) 5: version: 0.9.0 +``` **Другие носители того же факта:** docs/architecture/15-money-path.md:14 — второй экземпляр того же истёкшего обоснования («⚠ номер не ставим: файл двигает пак контракта 0.9.0») -**Предложенное действие:** Перевести пример в прошедшее время: «…лежал в «своей» зоне и при этом двигался паком контракта 0.9.0 (заленджен 31.08, `0a680a3`)»; в §1 (строка 14) заменить обоснование на нейтральное («контракт переверстывается минорами — греп-форма дешевле поддержки») или поставить номер с токеном, раз файл чист. + +**Предложенное действие (автора находки):** Перевести пример в прошедшее время: «…лежал в «своей» зоне и при этом двигался паком контракта 0.9.0 (заленджен 31.08, `0a680a3`)»; в §1 (строка 14) заменить обоснование на нейтральное («контракт переверстывается минорами — греп-форма дешевле поддержки») или поставить номер с токеном, раз файл чист. + **Разрушающее:** False --- ## N114 · MINOR · `docs/architecture/15-money-path.md:14` · A-дрейф -**Срез:** Денежный путь — docs/architecture/15-money-path.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** Деньги видимы с первого вызова: телеметрия, потолки, явное согласие на пере-оплату (CLAUDE.md, цель 5). +**Срез:** Денежный путь — docs/architecture/15-money-path.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> Деньги видимы с первого вызова: телеметрия, потолки, явное согласие на пере-оплату (CLAUDE.md, цель 5). + **Почему протухло:** Из трёх опор канона, названных здесь, у двух в доке есть носители (телеметрия — леджер/status в §2, потолки — §2/§3), а у третьей — «явное согласие на пере-оплату» — нет ни одного: ни в §2, ни в карте §6 «Код денег движка» (там пять файлов и `pipeline/rebill.go` среди них нет). Механизм построен и ратифицирован: порог `min($0.50, 5% × ProjectedBookUSD)` с переопределением `rebill_consent_usd` (D20.2-Q2). ✅ Честно: сам пробел стоит с рождения дока (гейт заленджен 25.07, dad4b56/D39.31), то есть это НЕ чистый дрейф; но тема СДВИНУЛАСЬ 31.08 и док этого не застал: D39.182 §1(б) починил дефект, из-за которого гейт НЕ срабатывал на правке исходника на месте, а §3 той же ноты пере-определил базис порога («считается БЕЗ контура банк-ролей»). -**Улика — команда:** `grep -n 'rebillConsentFloorUSD\|rebillConsentShare\|rebill_consent_usd' backend/internal/pipeline/rebill.go backend/internal/config/book.go | head -5; sed -n '67p' docs/architecture/15-money-path.md` -**Улика — вывод:** backend/internal/pipeline/rebill.go:41: rebillConsentFloorUSD = 0.50 + +**Улика — команда:** +``` +grep -n 'rebillConsentFloorUSD\|rebillConsentShare\|rebill_consent_usd' backend/internal/pipeline/rebill.go backend/internal/config/book.go | head -5; sed -n '67p' docs/architecture/15-money-path.md +``` +**Улика — вывод:** +``` +backend/internal/pipeline/rebill.go:41: rebillConsentFloorUSD = 0.50 backend/internal/pipeline/rebill.go:42: rebillConsentShare = 0.05 backend/internal/pipeline/rebill.go:285:// `rebill_consent_usd` when it declares one, else the ratified `min($0.50, 5% × ProjectedBookUSD)` with backend/internal/config/book.go:324: bad("rebill_consent_usd must be ≥ 0 (0 = the ratified default min($0.50, 5%%×projected book cost)), got %v", b.RebillConsentUSD) | Код денег движка | `backend/internal/store/ledger.go` · `store/store.go` · `pipeline/stagerun.go` · `pipeline/status.go` · `config/book.go` | -**Положительный контроль:** ОТРИЦАТЕЛЬНОЕ: grep -in 'rebill|пере-оплат|согласи' docs/architecture/15-money-path.md → единственный хит — сама строка 14. ПОЛОЖИТЕЛЬНЫЙ КОНТРОЛЬ: тот же греп по 'ре-билл' находит строку 23 («не двигает ни снапшот, ни ре-билл») — тема доку не чужда, значит ноль по носителю гейта — факт, а не артефакт грепа. -**Предложенное действие:** Добавить в §2 буллет: «**Согласие на пере-оплату — движковый гейт.** Прогон, который пере-покупает уже оплаченные юниты дороже порога, останавливается и требует явного согласия; порог по умолчанию ратифицирован D20.2-Q2 — `min($0.50, 5% × ProjectedBookUSD)` с полом $0.50 для книги без обработанных юнитов, книга переопределяет его `rebill_consent_usd` (`backend/internal/pipeline/rebill.go`, греп `func (r *Runner) rebillConsentThreshold(`; валидатор — `backend/internal/config/book.go`, греп `rebill_consent_usd must be`). ⚠ Порог считается БЕЗ контура банк-ролей (D39.182 §3): контур не пере-оплачивается.» И внести `pipeline/rebill.go` в строку «Код денег движка» карты §6. +``` +**Положительный контроль:** +``` +ОТРИЦАТЕЛЬНОЕ: grep -in 'rebill|пере-оплат|согласи' docs/architecture/15-money-path.md → единственный хит — сама строка 14. ПОЛОЖИТЕЛЬНЫЙ КОНТРОЛЬ: тот же греп по 'ре-билл' находит строку 23 («не двигает ни снапшот, ни ре-билл») — тема доку не чужда, значит ноль по носителю гейта — факт, а не артефакт грепа. +``` + +**Предложенное действие (автора находки):** Добавить в §2 буллет: «**Согласие на пере-оплату — движковый гейт.** Прогон, который пере-покупает уже оплаченные юниты дороже порога, останавливается и требует явного согласия; порог по умолчанию ратифицирован D20.2-Q2 — `min($0.50, 5% × ProjectedBookUSD)` с полом $0.50 для книги без обработанных юнитов, книга переопределяет его `rebill_consent_usd` (`backend/internal/pipeline/rebill.go`, греп `func (r *Runner) rebillConsentThreshold(`; валидатор — `backend/internal/config/book.go`, греп `rebill_consent_usd must be`). ⚠ Порог считается БЕЗ контура банк-ролей (D39.182 §3): контур не пере-оплачивается.» И внести `pipeline/rebill.go` в строку «Код денег движка» карты §6. + **Разрушающее:** False --- ## N115 · MINOR · `docs/architecture/16-events-emitter.md:3` · E-второй-носитель -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** ⚠ **ЭМИТТЕР ПОСТРОЕН И ПРИНЯТ (D39.131, `9cfe080`, 14.08):** `events.jsonl` (StreamVersion 1.1) +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> ⚠ **ЭМИТТЕР ПОСТРОЕН И ПРИНЯТ (D39.131, `9cfe080`, 14.08):** `events.jsonl` (StreamVersion 1.1) + **Почему протухло:** Движок с 31.08 (`bb541a8`) пишет `StreamVersion = "1.2"` — минор поднят под `Finished.Volume` (D39.181 п.2). 1.1 сегодня — это версия ЧИТАТЕЛЯ платформы, а не потока. Тот же факт лежит вторым носителем в `platform/docs/STACK_DECISIONS.md:419` («движок, StreamVersion 1.1») и разошёлся с кодом там же. -**Улика — команда:** `grep -n 'const StreamVersion' backend/internal/runevents/runevents.go platform/internal/ingest/events.go; git log -S 'StreamVersion = "1.2"' --format='%h %ad %s' --date=short -- backend/internal/runevents/runevents.go; grep -n 'StreamVersion' platform/docs/STACK_DECISIONS.md` -**Улика — вывод:** backend/internal/runevents/runevents.go:49:const StreamVersion = "1.2" + +**Улика — команда:** +``` +grep -n 'const StreamVersion' backend/internal/runevents/runevents.go platform/internal/ingest/events.go; git log -S 'StreamVersion = "1.2"' --format='%h %ad %s' --date=short -- backend/internal/runevents/runevents.go; grep -n 'StreamVersion' platform/docs/STACK_DECISIONS.md +``` +**Улика — вывод:** +``` +backend/internal/runevents/runevents.go:49:const StreamVersion = "1.2" platform/internal/ingest/events.go:46:const StreamVersion = "1.1" bb541a8 2026-08-31 Land the disclosure law and its eleven instances: two of the three acceptance blockers were defects inside the cure itself platform/docs/STACK_DECISIONS.md:419:| `events.jsonl` (NDJSON эмиттера) | движок, StreamVersion 1.1 | append-only | `internal/ingest/tai +``` **Другие носители того же факта:** platform/docs/STACK_DECISIONS.md:419, docs/PROGRESS.md:205 (строка 243, arch-10 — та же находка удержана и не применена) -**Предложенное действие:** В обоих носителях: «StreamVersion 1.2 (движок пишет; читатель платформы объявляет 1.1 — мажор один, минор читателя отстаёт намеренно, `runevents.go:44-48`)». В `STACK_DECISIONS.md:419` — та же правка (зона платформы, пингом). + +**Предложенное действие (автора находки):** В обоих носителях: «StreamVersion 1.2 (движок пишет; читатель платформы объявляет 1.1 — мажор один, минор читателя отстаёт намеренно, `runevents.go:44-48`)». В `STACK_DECISIONS.md:419` — та же правка (зона платформы, пингом). + **Разрушающее:** False --- ## N116 · MINOR · `docs/architecture/16-events-emitter.md:90` · C-исполненное-как-открытое -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** **PD-61 (открыт; два свойства ДО постройки)** — `DEFECT_REGISTER.md:72` +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> **PD-61 (открыт; два свойства ДО постройки)** — `DEFECT_REGISTER.md:72` + **Почему протухло:** PD-60 (строка 88, «открыт») и PD-61 (строка 90, «открыт») оба стоят в регистре как `fixed(P6, дерево сессии)`. Шапка дока баннирует только «PD-113 открыт» — эти две подписи баннер НЕ покрывает, и строка 90 прямо называет пункт (а) «живым требованием к эмиттеру». -**Улика — команда:** `for p in PD-60 PD-61 PD-107 PD-113; do printf "%-7s: " $p; grep "^| $p |" platform/docs/DEFECT_REGISTER.md | awk -F'|' '{print $(NF-2)}'; done` -**Улика — вывод:** PD-60 : fixed(P6, дерево сессии) + +**Улика — команда:** +``` +for p in PD-60 PD-61 PD-107 PD-113; do printf "%-7s: " $p; grep "^| $p |" platform/docs/DEFECT_REGISTER.md | awk -F'|' '{print $(NF-2)}'; done +``` +**Улика — вывод:** +``` +PD-60 : fixed(P6, дерево сессии) PD-61 : fixed(P6, дерево сессии) PD-107 : open PD-113 : fixed(P6, дерево сессии) -**Положительный контроль:** Тот же разбор ячейки на PD-107 возвращает `open` — значит поле статуса читается, и `fixed` у PD-60/61 не артефакт парсинга. -**Предложенное действие:** Расширить ⚠-баннер шапки: «упоминания „PD-113 / PD-60 / PD-61 открыт“ в теле ниже — историческая фактура эпохи стройки; все три закрыты P6». Один правкой снимаются обе подписи. +``` +**Положительный контроль:** +``` +Тот же разбор ячейки на PD-107 возвращает `open` — значит поле статуса читается, и `fixed` у PD-60/61 не артефакт парсинга. +``` + +**Предложенное действие (автора находки):** Расширить ⚠-баннер шапки: «упоминания „PD-113 / PD-60 / PD-61 открыт“ в теле ниже — историческая фактура эпохи стройки; все три закрыты P6». Один правкой снимаются обе подписи. + **Разрушающее:** False --- ## N117 · MINOR · `docs/architecture/16-events-emitter.md:114` · B-мёртвый-указатель -**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** | 3 | D39.119 п.3 · D39.122 п.2(в) · D39.123 п.2(б,в), п.3 | там же `:394`, `:416`, `:428`, `:430` | +**Срез:** Эмиттер · закон шва · онтология банка — docs/architecture/16-events-emitter.md · 17-seam-inbound-law.md · 18-bank-ontology.md +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> | 3 | D39.119 п.3 · D39.122 п.2(в) · D39.123 п.2(б,в), п.3 | там же `:394`, `:416`, `:428`, `:430` | + **Почему протухло:** Тела D39.119/122/123 уехали в слайс `docs/archive/architecture/05-decisions-D39-106-123.md`, где всего 210 строк — номера 394/416/428/430 там не существуют. В ЖИВОМ журнале решений те же номера пусты (394, 416) либо принадлежат чужому телу D39.139 (428, 430). Смягчающее: строка стоит в §6, который баннирован как «ОТРАБОТАННЫЙ ХЕНДОФФ» — поэтому minor, а не major. -**Улика — команда:** `for l in 394 416 428 430; do printf "%-4s| " $l; sed -n "${l}p" docs/architecture/05-decisions-log.md | cut -c1-60; done; wc -l docs/archive/architecture/05-decisions-D39-106-123.md; grep -n '^## D39\.\(119\|122\|123\) ' docs/architecture/05-decisions-log.md docs/archive/architecture/05-decisions-D39-106-123.md | cut -c1-70` -**Улика — вывод:** 394 | + +**Улика — команда:** +``` +for l in 394 416 428 430; do printf "%-4s| " $l; sed -n "${l}p" docs/architecture/05-decisions-log.md | cut -c1-60; done; wc -l docs/archive/architecture/05-decisions-D39-106-123.md; grep -n '^## D39\.\(119\|122\|123\) ' docs/architecture/05-decisions-log.md docs/archive/architecture/05-decisions-D39-106-123.md | cut -c1-70 +``` +**Улика — вывод:** +``` +394 | 416 | 428 | (а) **Моя зона — исправлено этим лендингом:** ПТ-16/ПТ-18 держали 430 | (в) **Зона фронта — пинг-аддендум в журнал:** `FRONTEND_SESSION_PR` @@ -2303,52 +4314,107 @@ PD-113 : fixed(P6, дерево сессии) docs/archive/architecture/05-decisions-D39-106-123.md:165:## D39.119 — КУРС ВЛАДЕЛЬЦА 08.08 docs/archive/architecture/05-decisions-D39-106-123.md:189:## D39.122 — ПАК «ДВИЖКОВЫЕ БЛОКЕРЫ docs/archive/architecture/05-decisions-D39-106-123.md:201:## D39.123 — ПАК ПЛАТФОРМЫ P4 «РАННЕР» -**Положительный контроль:** Греп `^## D39\.(119|122|123) ` в живом журнале пуст, а в слайсе даёт три хита (165/189/201) — инструмент работает, ноты просто переехали. Это ровно та ловушка «пустой греп по слайсу подтвердил бы вакуумно», которую D39.156 п.5 уже разбирала на другом примере. -**Предложенное действие:** Снять line-номера совсем (D39.126: номер грепается) — «грепать `D39.119`/`D39.122`/`D39.123`; тела — слайс `docs/archive/architecture/05-decisions-D39-106-123.md`». Это же требует строка бэклога 219 («line-якоря в тела D-лога обречены съезжать»). +``` +**Положительный контроль:** +``` +Греп `^## D39\.(119|122|123) ` в живом журнале пуст, а в слайсе даёт три хита (165/189/201) — инструмент работает, ноты просто переехали. Это ровно та ловушка «пустой греп по слайсу подтвердил бы вакуумно», которую D39.156 п.5 уже разбирала на другом примере. +``` + +**Предложенное действие (автора находки):** Снять line-номера совсем (D39.126: номер грепается) — «грепать `D39.119`/`D39.122`/`D39.123`; тела — слайс `docs/archive/architecture/05-decisions-D39-106-123.md`». Это же требует строка бэклога 219 («line-якоря в тела D-лога обречены съезжать»). + **Разрушающее:** False --- ## N118 · MINOR · `docs/glossary.md:7` · B-мёртвый-указатель -**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** «Оркестратор №N» — канонический счётчик преемственности роли (текущий N — в шапке ORCHESTRATOR_SESSION_PROMPT). +**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> «Оркестратор №N» — канонический счётчик преемственности роли (текущий N — в шапке ORCHESTRATOR_SESSION_PROMPT). + **Почему протухло:** В файле docs/ORCHESTRATOR_SESSION_PROMPT.md (240 строк) символа «№» нет НИ РАЗУ: его шапка намеренно номер не держит — «Счётчик ролей ОДИН, номер выдаёт тот, кто пишет промт передачи (D39.112 п.6)». Текущий номер живёт в CURRENT-STATE docs/PROGRESS.md, который прямо объявляет себя единственным носителем («ОЧЕРЕДЬ №22 (единственный носитель — здесь)»). -**Улика — команда:** `grep -c '№' docs/ORCHESTRATOR_SESSION_PROMPT.md; wc -l docs/ORCHESTRATOR_SESSION_PROMPT.md; sed -n '3p' docs/ORCHESTRATOR_SESSION_PROMPT.md | cut -c1-90; grep -o 'ОЧЕРЕДЬ №22 (единственный носитель — здесь' docs/PROGRESS.md` -**Улика — вывод:** 0 + +**Улика — команда:** +``` +grep -c '№' docs/ORCHESTRATOR_SESSION_PROMPT.md; wc -l docs/ORCHESTRATOR_SESSION_PROMPT.md; sed -n '3p' docs/ORCHESTRATOR_SESSION_PROMPT.md | cut -c1-90; grep -o 'ОЧЕРЕДЬ №22 (единственный носитель — здесь' docs/PROGRESS.md +``` +**Улика — вывод:** +``` +0 240 docs/ORCHESTRATOR_SESSION_PROMPT.md > Счётчик ролей ОДИН, номер выдаёт тот, кто пишет промт передачи (D39.112 п.6); параллельный ОЧЕРЕДЬ №22 (единственный носитель — здесь -**Положительный контроль:** Тот же grep по тому же шаблону на docs/PROGRESS.md даёт 57 хитов (`grep -c '№' docs/PROGRESS.md` → 57), а `grep -c 'оркестратор\|Оркестратор' docs/ORCHESTRATOR_SESSION_PROMPT.md` → 3, то есть инструмент читает именно этот файл и находит в нём текст. Ноль по «№» — свойство файла, а не опечатка команды. +``` +**Положительный контроль:** +``` +Тот же grep по тому же шаблону на docs/PROGRESS.md даёт 57 хитов (`grep -c '№' docs/PROGRESS.md` → 57), а `grep -c 'оркестратор\|Оркестратор' docs/ORCHESTRATOR_SESSION_PROMPT.md` → 3, то есть инструмент читает именно этот файл и находит в нём текст. Ноль по «№» — свойство файла, а не опечатка команды. +``` **Другие носители того же факта:** docs/PROGRESS.md:3 — CURRENT-STATE, «ОЧЕРЕДЬ №22 (единственный носитель — здесь; роль передана 01.09, №21 закрыт нотой передачи D39.184)» -**Предложенное действие:** Заменить скобку на «(текущий N — в CURRENT-STATE `docs/PROGRESS.md`, единственный носитель; номер выдаёт автор промта передачи — D39.112 п.6)». + +**Предложенное действие (автора находки):** Заменить скобку на «(текущий N — в CURRENT-STATE `docs/PROGRESS.md`, единственный носитель; номер выдаёт автор промта передачи — D39.112 п.6)». + +**Разбор опровергателя:** Проверил положительным контролем, что инструмент видит именно этот файл: grep по 'оркестратор' даёт 4 хита, файл читается. При этом символа '№' в нём НОЛЬ, а шапка прямо объявляет обратную норму — номер не хранится здесь, его выдаёт автор промта передачи (D39.112 п.6). Историческая проверка добивает: до 22.08 (1ccd5e4) '№' в файле было 3, с 23.08 — ноль, то есть указатель именно СГНИЛ. Текущий номер живёт в CURRENT-STATE docs/PROGRESS.md, который объявляет себя единственным носителем («ОЧЕРЕДЬ №22 (единственный носитель — здесь)»). + **Разрушающее:** True --- ## N119 · MINOR · `docs/product-requirements.md:50` · B-мёртвый-указатель -**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** **НЕ построена дверь платформы** (`createExport`/`getExport`) — очередь №20 п.4, гейт: живой прогон п.3 +**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> **НЕ построена дверь платформы** (`createExport`/`getExport`) — очередь №20 п.4, гейт: живой прогон п.3 + **Почему протухло:** Сам факт ЖИВ (проверено кодом, см. positive_control), но указатель «очередь №20» умер дважды: роль передана №20→№21 (D39.177, 31.08) и №21→№22 (D39.184, 01.09), и CURRENT-STATE несёт «ОЧЕРЕДЬ №22». Номер очереди — движущаяся мишень, привязка к нему протухает каждой передачей роли. -**Улика — команда:** `grep -o 'ОЧЕРЕДЬ №22.\{0,60\}' docs/PROGRESS.md | head -1; grep -m1 '| D39.184 |' docs/architecture/05-decisions-index.md | cut -c1-95` -**Улика — вывод:** ОЧЕРЕДЬ №22 (единственный носитель — здесь; роль передана 01.09, №21 закрыт нотой пере + +**Улика — команда:** +``` +grep -o 'ОЧЕРЕДЬ №22.\{0,60\}' docs/PROGRESS.md | head -1; grep -m1 '| D39.184 |' docs/architecture/05-decisions-index.md | cut -c1-95 +``` +**Улика — вывод:** +``` +ОЧЕРЕДЬ №22 (единственный носитель — здесь; роль передана 01.09, №21 закрыт нотой пере | D39.184 | 01.09 | **Передача роли оркестратора №21 → №22.** Смена приняла пак платформы P12 -**Положительный контроль:** Отрицательная половина утверждения ПТ-44 проверена и ЖИВА: `grep -rn 'createExport\|getExport' platform/ --include=*.go` → 0 хитов в коде (только README/BACKLOG/архив). Положительный контроль на тот же инструмент: `grep -n 'listBooks\|getBook' platform/internal/httpapi/v0.go` → строки 75-76 с реально смонтированными ручками. Т.е. греп по зоне ручки находит, а createExport/getExport в ней нет. +``` +**Положительный контроль:** +``` +Отрицательная половина утверждения ПТ-44 проверена и ЖИВА: `grep -rn 'createExport\|getExport' platform/ --include=*.go` → 0 хитов в коде (только README/BACKLOG/архив). Положительный контроль на тот же инструмент: `grep -n 'listBooks\|getBook' platform/internal/httpapi/v0.go` → строки 75-76 с реально смонтированными ручками. Т.е. греп по зоне ручки находит, а createExport/getExport в ней нет. +``` **Другие носители того же факта:** docs/PROGRESS.md:16 — «платформенный остаток несут очередь №20 п.4 и `platform/BACKLOG.md` П-17» (тот же мёртвый номер, не в моём срезе) -**Предложенное действие:** Заменить «очередь №20 п.4» на «очередь CURRENT-STATE, п.4 «дверь выдачи» (`docs/PROGRESS.md`) + `platform/BACKLOG.md` П-17» — на носитель, а не на номер смены. + +**Предложенное действие (автора находки):** Заменить «очередь №20 п.4» на «очередь CURRENT-STATE, п.4 «дверь выдачи» (`docs/PROGRESS.md`) + `platform/BACKLOG.md` П-17» — на носитель, а не на номер смены. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Уточнение к находке: п.4 НЕ умер — в сегодняшней очереди CURRENT-STATE пункт (4) по-прежнему «дверь выдачи — строго ПОСЛЕ (3)», уехал только НОМЕР очереди (№20 → №22). Хуже другое, и находка этого не заметила: в docs/PROGRESS.md существует ВТОРАЯ нумерованная очередь — блок «ОТКРЫТО НА ОРКЕСТРАТОРЕ №21» (строки 36-40), — и в её №20-редакции (85282f2) п.4 был `PD-425`, а не дверь выдачи. То есть читатель, идущий по «очередь №20 п.4», может сесть не на тот список. Поэтому замена на «очередь CURRENT-STATE, п.4 «дверь выдачи»» верна и нужна именно словом «дверь выдачи», а не номером. Ссылка на `platform/BACKLOG.md` П-17 тоже ПРАВОМЕРНА, хотя её голова читается как «ИСПОЛНЕНО P7»: хвост той же строки несёт «Не вошло и отложено в P8: `updateBook`/`deleteBook`/`getRun`, экспорт (`createExport`/`getExport`)», и CURRENT-STATE сам называет П-17 носителем этого долга. + +**Разбор опровергателя:** Отрицательная половина клейма ПТ-44 подтверждена с положительным контролем: тот же греп по той же зоне находит смонтированные ручки listBooks/getBook (v0.go:75-76), а createExport/getExport в platform/**.go нет вовсе. Указатель же действительно протух — CURRENT-STATE несёт «ОЧЕРЕДЬ №22» (роль передана дважды: D39.177 №20→№21, D39.184 №21→№22). Но находка преувеличивает степень смерти, и это надо сказать принимающему. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Уточнение к находке: п.4 НЕ умер — в сегодняшней очереди CURRENT-STATE пункт (4) по-прежнему «дверь выдачи — строго ПОСЛЕ (3)», уехал только НОМЕР очереди (№20 → №22). Хуже другое, и находка этого не заметила: в docs/PROGRESS.md существует ВТОРАЯ нумерованная очередь — блок «ОТКРЫТО НА ОРКЕСТРАТОРЕ №21» (строки 36-40), — и в её №20-редакции (85282f2) п.4 был `PD-425`, а не дверь выдачи. То есть читатель, идущий по «очередь №20 п.4», может сесть не на тот список. Поэтому замена на «очередь CURRENT-STATE, п.4 «дверь выдачи»» верна и нужна именно словом «дверь выдачи», а не номером. Ссылка на `platform/BACKLOG.md` П-17 тоже ПРАВОМЕРНА, хотя её голова читается как «ИСПОЛНЕНО P7»: хвост той же строки несёт «Не вошло и отложено в P8: `updateBook`/`deleteBook`/`getRun`, экспорт (`createExport`/`getExport`)», и CURRENT-STATE сам называет П-17 носителем этого долга. --- ## N120 · MINOR · `docs/product-requirements.md:57` · B-мёртвый-указатель -**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** механизм готов: `tmctl export --pairs` выравнивает пары побайтно по единицам редактуры (`internal/pipeline/export.go:89-93`) +**Срез:** Глоссарий и продуктовые требования — docs/glossary.md · docs/product-requirements.md +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> механизм готов: `tmctl export --pairs` выравнивает пары побайтно по единицам редактуры (`internal/pipeline/export.go:89-93`) + **Почему протухло:** Якорь РОДИЛСЯ верным 02.08 (fbefe33: тогда 89-93 были ровно комментарием «the --pairs column aligned byte-for-byte to how the edit wave concatenated the members») и сгнил дрейфом: сегодня 89-93 — поля структуры TotalUnits/PendingUnits/GhostRows/ConfigDrift, а механизм уехал на 144-152. Линтер якорей его НЕ ловит: форма без `=токена` сверяется только на существование файла. -**Улика — команда:** `sed -n '89,93p' backend/internal/pipeline/export.go; git show fbefe33:backend/internal/pipeline/export.go | sed -n '89,90p'; grep -n 'aligned byte-for-byte' backend/internal/pipeline/export.go` -**Улика — вывод:** TotalUnits int `json:"total_units"` + +**Улика — команда:** +``` +sed -n '89,93p' backend/internal/pipeline/export.go; git show fbefe33:backend/internal/pipeline/export.go | sed -n '89,90p'; grep -n 'aligned byte-for-byte' backend/internal/pipeline/export.go +``` +**Улика — вывод:** +``` + TotalUnits int `json:"total_units"` PendingUnits int `json:"pending_units"` GhostRows int `json:"ghost_rows,omitempty"` // ConfigDrift is true when the CURRENT config renders a snapshot different ... @@ -2357,35 +4423,71 @@ docs/archive/architecture/05-decisions-D39-106-123.md:201:## D39.123 — ПАК // UNIT (the editor collapses a unit's member chunks into ONE final text) ... --- сегодня --- 147: // aligned byte-for-byte to how the edit wave concatenated the members) -**Положительный контроль:** Положительный контроль инструмента: тот же grep находит механизм в том же файле сегодня — `grep -n 'aligned byte-for-byte' backend/internal/pipeline/export.go` → `147:`; и `python3 docs/scripts/counts.py --lint` даёт «0 проблемных якорей», явно объявив «без токена — несверяемы: 721», т.е. слепота линтера подтверждена, а не предположена. -**Предложенное действие:** Заменить якорь на форму с токеном, которую линтер сверяет: (`backend/internal/pipeline/export.go`=`aligned byte-for-byte`) — либо просто снять номера строк, как это уже сделано другими коммитами по файлам живых сессий (df4b064, 11e739d). +``` +**Положительный контроль:** +``` +Положительный контроль инструмента: тот же grep находит механизм в том же файле сегодня — `grep -n 'aligned byte-for-byte' backend/internal/pipeline/export.go` → `147:`; и `python3 docs/scripts/counts.py --lint` даёт «0 проблемных якорей», явно объявив «без токена — несверяемы: 721», т.е. слепота линтера подтверждена, а не предположена. +``` + +**Предложенное действие (автора находки):** Заменить якорь на форму с токеном, которую линтер сверяет: (`backend/internal/pipeline/export.go`=`aligned byte-for-byte`) — либо просто снять номера строк, как это уже сделано другими коммитами по файлам живых сессий (df4b064, 11e739d). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верное по существу, но НЕ добавлять `backend/` к пути: короткая зонно-относительная форма `internal/pipeline/export.go` — легитимная и поддержана самим линтером (LINT_PREFIXES_RAW включает "backend/", docs/scripts/counts.py:375). Менять надо только хвост: (`internal/pipeline/export.go`=`aligned byte-for-byte`) — тогда якорь переходит из класса «несверяемых 708» в класс «сверено по содержимому 137» и следующий переезд поймает гейт, а не ревизия. + +**Разбор опровергателя:** Воспроизвёл сам: в дереве 89-93 — поля структуры отчёта экспорта (TotalUnits/PendingUnits/GhostRows/ConfigDrift), а утверждаемый механизм («--pairs column aligned byte-for-byte to how the edit wave concatenated the members») уехал в комментарий, начинающийся строкой 144, с ключевой фразой на 147. В fbefe33 (02.08) тот же комментарий стоял ровно на 89-93 — якорь родился верным и сгнил дрейфом. Слепота линтера подтверждена ИСПОЛНЕНИЕМ, а не предположением: --lint даёт «0 проблемных якорей» при «без токена — несверяемы: 708» (у автора было 721 — число живое, к сути не относится), а код линтера прямо говорит «НЕ проверяется: якорь без токена (только существование файла и длина)». + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верное по существу, но НЕ добавлять `backend/` к пути: короткая зонно-относительная форма `internal/pipeline/export.go` — легитимная и поддержана самим линтером (LINT_PREFIXES_RAW включает "backend/", docs/scripts/counts.py:375). Менять надо только хвост: (`internal/pipeline/export.go`=`aligned byte-for-byte`) — тогда якорь переходит из класса «несверяемых 708» в класс «сверено по содержимому 137» и следующий переезд поймает гейт, а не ревизия. --- ## N121 · MINOR · `frontend/README.md:21` · A-дрейф -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Маршрутов семь, и все снимаются (`/showcase` `/scale` `/empty` `/loading` `/error` `/offline` +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Маршрутов семь, и все снимаются (`/showcase` `/scale` `/empty` `/loading` `/error` `/offline` `/partial`) — маршрут выбирает мир фикстур. + **Почему протухло:** Маршрутов ВОСЕМЬ: S4 завела `/intake`, и он есть в обоих замкнутых тестом списках. Приёмка S4 в том же дереве печатает «8 маршрутов, 11 сценариев». -**Улика — команда:** `sed -n '9,18p' frontend/src/api/scenarios.ts; sed -n '20,29p' frontend/scripts/shot.mjs; grep -o '8 маршрутов, 11 сценариев' frontend/docs/frontend-PROGRESS.md` -**Улика — вывод:** export const scenarios = ['showcase','scale','empty','intake','loading','error','offline','partial'] as const; + +**Улика — команда:** +``` +sed -n '9,18p' frontend/src/api/scenarios.ts; sed -n '20,29p' frontend/scripts/shot.mjs; grep -o '8 маршрутов, 11 сценариев' frontend/docs/frontend-PROGRESS.md +``` +**Улика — вывод:** +``` +export const scenarios = ['showcase','scale','empty','intake','loading','error','offline','partial'] as const; const KNOWN_ROUTES = ['/showcase','/scale','/empty','/intake','/loading','/error','/offline','/partial']; 8 маршрутов, 11 сценариев -**Предложенное действие:** «Маршрутов семь … `/partial`» → «Маршрутов восемь … `/showcase` `/scale` `/empty` `/intake` `/loading` `/error` `/offline` `/partial`». +``` + +**Предложенное действие (автора находки):** «Маршрутов семь … `/partial`» → «Маршрутов восемь … `/showcase` `/scale` `/empty` `/intake` `/loading` `/error` `/offline` `/partial`». + +**Разбор опровергателя:** Три независимых носителя в рабочем дереве (генератор маршрутов, список скриншотера, запись приёмки) дают восемь; `/intake` завела S4. Расхождение двух списков падает тестом `src/routes.test.ts` — значит списки заведомо синхронны между собой, и отстала именно проза README. Опровергнуть нечем. + **Разрушающее:** True --- ## N122 · MINOR · `frontend/docs/BACKLOG.md:3` · B-мёртвый-указатель -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** они заходят строками единого бэклога через оркестратора (пример: строки 99–102 — пофазный прогресс · манифест глав · машиночитаемая таблица подписи · трасса) +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> они заходят строками единого бэклога через оркестратора (пример: строки 99–102 — пофазный прогресс · манифест глав · машиночитаемая таблица подписи · трасса) + **Почему протухло:** Строк 99–102 в живом `docs/PROGRESS.md` больше нет — все четыре закрыты и по норме журнала («закрытые строки здесь НЕ хранятся») ушли в D-лог. Шапка учит зону примером, который не резолвится. -**Улика — команда:** `for n in 99 100 101 102; do echo -n "row $n: "; grep -c "^| $n |" docs/PROGRESS.md; done; for n in 115 123 160; do echo -n "row $n: "; grep -c "^| $n |" docs/PROGRESS.md; done` -**Улика — вывод:** row 99: 0 + +**Улика — команда:** +``` +for n in 99 100 101 102; do echo -n "row $n: "; grep -c "^| $n |" docs/PROGRESS.md; done; for n in 115 123 160; do echo -n "row $n: "; grep -c "^| $n |" docs/PROGRESS.md; done +``` +**Улика — вывод:** +``` +row 99: 0 row 100: 0 row 101: 0 row 102: 0 @@ -2393,133 +4495,274 @@ row 102: 0 row 115: 1 row 123: 1 row 160: 1 -**Положительный контроль:** Тот же греп `^| N |` по строкам 115, 123, 160 даёт по 1 хиту — форма таблицы единого бэклога грепается, ноль по 99–102 означает отсутствие строк, а не неверный шаблон. +``` +**Положительный контроль:** +``` +Тот же греп `^| N |` по строкам 115, 123, 160 даёт по 1 хиту — форма таблицы единого бэклога грепается, ноль по 99–102 означает отсутствие строк, а не неверный шаблон. +``` **Другие носители того же факта:** frontend/docs/frontend-PROGRESS.md:249-250 («единый бэклог, строки 99–100 … строка 101»), frontend/docs/STACK_DECISIONS.md:245-257 (§8 — тот же набор четырёх пунктов) -**Предложенное действие:** Заменить пример на живой (например, строка 169 — проекция банка платформой) либо снять скобку с номерами, оставив механику «запросы к движку идут строками единого бэклога через оркестратора». + +**Предложенное действие (автора находки):** Заменить пример на живой (например, строка 169 — проекция банка платформой) либо снять скобку с номерами, оставив механику «запросы к движку идут строками единого бэклога через оркестратора». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Предложенное действие («заменить пример на живой, например строку 169») ВРЕДНО и я бы его отклонил: живая строка закроется ровно так же, и шапка сгниёт снова — механизм рота встроен в замену. Норма D39.80 существует затем, чтобы исторический номер оставался цитируемым. Верное действие — минимальная приписка, снимающая ложное ожидание у читателя, идущего в живой бэклог: «(пример: строки 99–102 … — все закрыты, тела в D39.122/D39.131)». Второй названный носитель (`frontend-PROGRESS.md:249-250`) — ДРУГОЙ и настоящий дефект класса C: он говорит «**Ждёт движка**» о закрытых строках, и вот его править нужно; он же единственный лежит в файле, куда оркестратор пишет. + +**Разбор опровергателя:** ФАКТ подтверждён с положительным контролем: строк 99–102 в живом бэклоге нет. Но КЛАСС находки («B — мёртвый указатель») опровергается тем же чтением, которое факт доказывает: `docs/PROGRESS.md:59` прямо нормирует, что ID закрытой строки стабилен НАВСЕГДА и на него ССЫЛАЮТСЯ доки и промты (D39.80), а тело уезжает в D-лог (здесь — D39.122 и D39.131). Указатель разрешается в один хоп через реестр и по построению не «уехал». Плюс сама фраза учит МЕХАНИЗМУ («запросы к движку заходят строками единого бэклога через оркестратора»), и как пример прошлого она историчеcки ИСТИННА: эти четыре строки именно так и зашли. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Предложенное действие («заменить пример на живой, например строку 169») ВРЕДНО и я бы его отклонил: живая строка закроется ровно так же, и шапка сгниёт снова — механизм рота встроен в замену. Норма D39.80 существует затем, чтобы исторический номер оставался цитируемым. Верное действие — минимальная приписка, снимающая ложное ожидание у читателя, идущего в живой бэклог: «(пример: строки 99–102 … — все закрыты, тела в D39.122/D39.131)». Второй названный носитель (`frontend-PROGRESS.md:249-250`) — ДРУГОЙ и настоящий дефект класса C: он говорит «**Ждёт движка**» о закрытых строках, и вот его править нужно; он же единственный лежит в файле, куда оркестратор пишет. --- ## N123 · MINOR · `frontend/docs/BACKLOG.md:23` · B-мёртвый-указатель -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Туда же несколько токенов, которые ни разу не используются (`--color-current-line`, `--color-hint`). +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Туда же несколько токенов, которые ни разу не используются (`--color-current-line`, `--color-hint`). + **Почему протухло:** Токена `--color-hint` в зоне больше нет вовсе — ни в `tokens.css`, ни в тестах, ни в коде; строка держит хвост на несуществующем токене. Вторая половина (`--color-current-line`, `--font-mono`) по-прежнему верна: оба лежат в именном списке `reserved` теста токенов. -**Улика — команда:** `grep -rn 'color-hint' frontend/src frontend/scripts; grep -rn 'color-current-line\|font-mono' frontend/src | head -6; grep -c '^ --color-' frontend/src/tokens/tokens.css` -**Улика — вывод:** (color-hint: ни одного хита в src/ и scripts/) + +**Улика — команда:** +``` +grep -rn 'color-hint' frontend/src frontend/scripts; grep -rn 'color-current-line\|font-mono' frontend/src | head -6; grep -c '^ --color-' frontend/src/tokens/tokens.css +``` +**Улика — вывод:** +``` +(color-hint: ни одного хита в src/ и scripts/) src/tokens/tokens.css:22: --color-current-line: #152945; src/tokens/tokens.css:123: --font-mono: 'JetBrains Mono Variable', ui-monospace, monospace; src/tokens/tokens.test.ts:246:const reserved = ['--color-current-line', '--font-mono']; 22 (объявленных --color-* токенов в tokens.css) -**Положительный контроль:** Тот же греп по `color-current-line` и `font-mono` в `frontend/src` даёт хиты в tokens.css и tokens.test.ts, а `grep -c '^ --color-' tokens.css` = 22 — инструмент видит токены этого файла; ноль по `color-hint` — факт. +``` +**Положительный контроль:** +``` +Тот же греп по `color-current-line` и `font-mono` в `frontend/src` даёт хиты в tokens.css и tokens.test.ts, а `grep -c '^ --color-' tokens.css` = 22 — инструмент видит токены этого файла; ноль по `color-hint` — факт. +``` **Другие носители того же факта:** frontend/src/tokens/tokens.test.ts:246 (живой список reserved — без --color-hint) -**Предложенное действие:** Убрать `--color-hint` из перечня Ф-20, оставив `--color-current-line` и `--font-mono` (именно они стоят в списке `reserved` теста токенов). + +**Предложенное действие (автора находки):** Убрать `--color-hint` из перечня Ф-20, оставив `--color-current-line` и `--font-mono` (именно они стоят в списке `reserved` теста токенов). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Уточнение к формулировке: после снятия `--color-hint` в скобках остаётся ОДИН токен, поэтому «несколько токенов, которые ни разу не используются (`--color-current-line`, `--color-hint`)» должно стать «токен, который ни разу не используется (`--color-current-line`)» — иначе «несколько» повиснет над одним именем и получится новая мелкая неправда. `--font-mono` в этой скобке не перечисляют: он назван отдельным предложением выше. + +**Разбор опровергателя:** Опровергнуть не удалось: `--color-hint` не объявлен, не используется и не зарезервирован — он исчез при том самом фиксе, который архивная запись описывает («Исправлено: … именной список зарезервированных под Ф-20»), а живой список `reserved` содержит ровно два имени. Остальная фактура Ф-20 (мёртвый шрифт в бандле, `--font-mono` и `--color-current-line` без единого `var(...)`) проверена и ВЕРНА — находка правильно не трогает её. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Уточнение к формулировке: после снятия `--color-hint` в скобках остаётся ОДИН токен, поэтому «несколько токенов, которые ни разу не используются (`--color-current-line`, `--color-hint`)» должно стать «токен, который ни разу не используется (`--color-current-line`)» — иначе «несколько» повиснет над одним именем и получится новая мелкая неправда. `--font-mono` в этой скобке не перечисляют: он назван отдельным предложением выше. --- ## N124 · MINOR · `frontend/docs/FRONTEND_SESSION_PROMPT.md:2` · B-мёртвый-указатель -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** на него +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> на него > так ссылаются `S3_SESSION_PROMPT.md` и `frontend/README.md`. + **Почему протухло:** `S3_SESSION_PROMPT.md` в зоне не существует: промты S3/S3.5/S3.7 архивированы 14.08 в `docs/archive/prompts/FRONTEND_S3*`, зонные копии сняты. Довод баннера («файл живой, потому что на него ссылается S3-промт») опирается на исчезнувший носитель. -**Улика — команда:** `find /home/ubuntu/projects/textmachine-main -name 'S3*SESSION_PROMPT*' -not -path '*/node_modules/*'; ls docs/archive/prompts/ | grep -i FRONTEND` -**Улика — вывод:** (find: пусто) + +**Улика — команда:** +``` +find /home/ubuntu/projects/textmachine-main -name 'S3*SESSION_PROMPT*' -not -path '*/node_modules/*'; ls docs/archive/prompts/ | grep -i FRONTEND +``` +**Улика — вывод:** +``` +(find: пусто) FRONTEND_S2_SESSION_PROMPT.md FRONTEND_S35_SESSION_PROMPT_2026-08-09.md FRONTEND_S37_SESSION_PROMPT_2026-08-10.md FRONTEND_S3_SESSION_PROMPT_2026-08-04.md -**Положительный контроль:** find frontend/docs docs/archive/prompts -name '*SESSION_PROMPT*' → 20+ файлов, включая frontend/docs/FRONTEND_SESSION_PROMPT.md и четыре FRONTEND_S* в архиве. Инструмент в этой области находит промты; ноль по `S3*SESSION_PROMPT*` — факт, а не опечатка. +``` +**Положительный контроль:** +``` +find frontend/docs docs/archive/prompts -name '*SESSION_PROMPT*' → 20+ файлов, включая frontend/docs/FRONTEND_SESSION_PROMPT.md и четыре FRONTEND_S* в архиве. Инструмент в этой области находит промты; ноль по `S3*SESSION_PROMPT*` — факт, а не опечатка. +``` **Другие носители того же факта:** frontend/docs/frontend-PROGRESS.md:52 (`S3_SESSION_PROMPT.md`), frontend/docs/frontend-PROGRESS.md:80 (`S35_SESSION_PROMPT.md` «эта папка»), frontend/docs/frontend-PROGRESS.md:208 (`S37_SESSION_PROMPT.md`), frontend/docs/frontend-PROGRESS.md:147 (`S4_SESSION_PROMPT.md` «эта папка» — уехал в frontend/docs/archive/) -**Предложенное действие:** «ссылаются `S3_SESSION_PROMPT.md` и `frontend/README.md`» → «ссылается `frontend/README.md`; промты S2–S3.7 архивированы в `docs/archive/prompts/FRONTEND_S*`». + +**Предложенное действие (автора находки):** «ссылаются `S3_SESSION_PROMPT.md` и `frontend/README.md`» → «ссылается `frontend/README.md`; промты S2–S3.7 архивированы в `docs/archive/prompts/FRONTEND_S*`». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно, но ⚠ это НЕ повод трогать сам баннер как «протухший»: баннер — механизм, и он работает; ложна ровно одна ссылка внутри него. Правка минимальная и должна СОХРАНИТЬ довод («файл живой как носитель норм»), заменив мёртвое имя на архивное: «ссылается `frontend/README.md`; промты S2–S3.7 архивированы в `docs/archive/prompts/FRONTEND_S*`, S4 — в `frontend/docs/archive/`». Список «других носителей» в находке — НЕ ошибки: `frontend-PROGRESS.md:52/80/147/208` это ХРОНИКА, ссылки на промты в момент их выдачи; переписывать хронику под сегодняшние пути запрещено нормой (историческое). Максимум — одна сноска в журнале «промты S2–S4 архивированы, пути см. `docs/archive/prompts/`». + +**Разбор опровергателя:** Отрицательный вывод получен НЕ при пустом положительном контроле: тот же самый find без сужения даёт 121 файл, включая сам разбираемый промт и четыре архивных FRONTEND_S*. Значит ноль по `S3*SESSION_PROMPT*` — факт. Вторая половина довода баннера (`frontend/README.md`) жива и проверена — README:48 ссылается. То есть баннер не рушится, у него отваливается одна из двух опор. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно, но ⚠ это НЕ повод трогать сам баннер как «протухший»: баннер — механизм, и он работает; ложна ровно одна ссылка внутри него. Правка минимальная и должна СОХРАНИТЬ довод («файл живой как носитель норм»), заменив мёртвое имя на архивное: «ссылается `frontend/README.md`; промты S2–S3.7 архивированы в `docs/archive/prompts/FRONTEND_S*`, S4 — в `frontend/docs/archive/`». Список «других носителей» в находке — НЕ ошибки: `frontend-PROGRESS.md:52/80/147/208` это ХРОНИКА, ссылки на промты в момент их выдачи; переписывать хронику под сегодняшние пути запрещено нормой (историческое). Максимум — одна сноска в журнале «промты S2–S4 архивированы, пути см. `docs/archive/prompts/`». --- ## N125 · MINOR · `frontend/docs/STACK_DECISIONS.md:111` · B-мёртвый-указатель -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** | Браузер | `@playwright/test 1.62.1` (точный пин) | +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> | Браузер | `@playwright/test 1.62.1` (точный пин) | + **Почему протухло:** Зона зависит от пакета `playwright` 1.62.1, а не от `@playwright/test`: последнего нет ни в `package.json`, ни в дереве `node_modules`. Сессия, поставившая пин буквально, добавит вторую копию браузерного рантайма. -**Улика — команда:** `grep -n 'playwright' frontend/package.json; ls -d frontend/node_modules/@playwright; node -e "console.log(require('./frontend/node_modules/playwright/package.json').version)"` -**Улика — вывод:** 35: "@axe-core/playwright": "4.12.1", + +**Улика — команда:** +``` +grep -n 'playwright' frontend/package.json; ls -d frontend/node_modules/@playwright; node -e "console.log(require('./frontend/node_modules/playwright/package.json').version)" +``` +**Улика — вывод:** +``` +35: "@axe-core/playwright": "4.12.1", 53: "playwright": "1.62.1", ls: cannot access 'frontend/node_modules/@playwright': No such file or directory 1.62.1 -**Положительный контроль:** `ls -d frontend/node_modules/playwright` → каталог есть, версия 1.62.1; `grep -c '@playwright/test' frontend/package.json` → 0 при том, что греп по `playwright` в том же файле даёт две строки. Инструмент в этой области находит пакеты — отсутствие @playwright/test не артефакт грепа. +``` +**Положительный контроль:** +``` +`ls -d frontend/node_modules/playwright` → каталог есть, версия 1.62.1; `grep -c '@playwright/test' frontend/package.json` → 0 при том, что греп по `playwright` в том же файле даёт две строки. Инструмент в этой области находит пакеты — отсутствие @playwright/test не артефакт грепа. +``` **Другие носители того же факта:** frontend/docs/FRONTEND_PLAN.md:122 -**Предложенное действие:** В таблице §3 и в таблице пинов `FRONTEND_PLAN.md:122` заменить `@playwright/test` на `playwright` (плюс `@axe-core/playwright 4.12.1`, который несёт axe-половину скриншот-цикла). + +**Предложенное действие (автора находки):** В таблице §3 и в таблице пинов `FRONTEND_PLAN.md:122` заменить `@playwright/test` на `playwright` (плюс `@axe-core/playwright 4.12.1`, который несёт axe-половину скриншот-цикла). + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно; добавить причину, иначе следующая сессия «починит» обратно: тест-раннер `@playwright/test` зоне не нужен ПО ПОСТРОЕНИЮ — тесты гоняет `vitest` (шестой шаг `check`), а playwright работает библиотекой из `scripts/shot.mjs`. Формулировать как «`playwright 1.62.1` (библиотека, не тест-раннер: тесты на vitest) + `@axe-core/playwright 4.12.1`». Второй носитель `FRONTEND_PLAN.md:122` назван верно, я его прочитал. + +**Разбор опровергателя:** Отрицательный вывод предъявлен с рабочим положительным контролем: тот же греп по 'playwright' в том же файле даёт две строки, значит ноль по `@playwright/test` — факт, а не промах шаблона. Зона зависит от библиотеки `playwright` (скриншот-цикл в `scripts/shot.mjs`) и от `@axe-core/playwright`; тест-раннер `@playwright/test` не установлен и не импортируется. Пин в таблице называет несуществующий в зоне пакет. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно; добавить причину, иначе следующая сессия «починит» обратно: тест-раннер `@playwright/test` зоне не нужен ПО ПОСТРОЕНИЮ — тесты гоняет `vitest` (шестой шаг `check`), а playwright работает библиотекой из `scripts/shot.mjs`. Формулировать как «`playwright 1.62.1` (библиотека, не тест-раннер: тесты на vitest) + `@axe-core/playwright 4.12.1`». Второй носитель `FRONTEND_PLAN.md:122` назван верно, я его прочитал. --- ## N126 · MINOR · `frontend/docs/STACK_DECISIONS.md:116` · E-второй-носитель -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** Нормативная поверхность — OpenAPI 3.1 в `docs/api-contract/openapi.yaml`: spectral линтует её +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> Нормативная поверхность — OpenAPI 3.1 в `docs/api-contract/openapi.yaml`: spectral линтует её шестым шагом `npm run check` + **Почему протухло:** Шагов у `check` шесть, и spectral — ЧЕТВЁРТЫЙ. Тот же файл на строке 148 уже утверждает «шесть шагов», то есть носитель противоречит сам себе; вторая копия ошибки — `FRONTEND_PLAN.md:124`. -**Улика — команда:** `node -e "const p=require('./frontend/package.json');console.log(p.scripts.check.split('&&').map((s,i)=>(i+1)+': '+s.trim()).join('\n'))"` -**Улика — вывод:** 1: prettier --check . + +**Улика — команда:** +``` +node -e "const p=require('./frontend/package.json');console.log(p.scripts.check.split('&&').map((s,i)=>(i+1)+': '+s.trim()).join('\n'))" +``` +**Улика — вывод:** +``` +1: prettier --check . 2: eslint . --max-warnings 0 3: stylelint "src/**/*.css" 4: npm run contract 5: tsc --noEmit 6: vitest run +``` **Другие носители того же факта:** frontend/docs/FRONTEND_PLAN.md:124, frontend/docs/STACK_DECISIONS.md:144-149 (там же верная редакция — «шесть шагов») -**Предложенное действие:** В обоих носителях «шестым шагом» → «четвёртым из шести шагов». ⚠ Уже названо как ФС-6 в фикс-листе приёмки S4 (frontend-PROGRESS.md:469) — строка не исполнена. + +**Предложенное действие (автора находки):** В обоих носителях «шестым шагом» → «четвёртым из шести шагов». ⚠ Уже названо как ФС-6 в фикс-листе приёмки S4 (frontend-PROGRESS.md:469) — строка не исполнена. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Список носителей НЕПОЛОН: помимо `STACK_DECISIONS.md:116` и `FRONTEND_PLAN.md:124` та же ошибка стоит ТРЕТИЙ раз — `frontend/docs/BACKLOG.md:16` (Ф-14: «spectral шестым шагом `check`, openapi-typescript генерит `src/api/schema.ts`»). Правя два, оставят третий. ⚠ И ещё: во всех трёх рядом стоит «нормативная поверхность — `docs/api-contract/openapi.yaml`» — а это на время фриза НЕВЕРНО (пинг №17, дописка: «зеркало = байт-копия временно НЕ действует, нормативен только канон `docs/architecture/14-api-contract/`»). Правку «четвёртым из шести» стоит вносить одним касанием с этой оговоркой, иначе исполнитель перечитает строку и унаследует вторую ложь. + +**Разбор опровергателя:** Шагов шесть, spectral — четвёртый (внутри `npm run contract`). Тот же файл на 146-147 несёт верную редакцию, т.е. носитель спорит сам с собой. ФС-6 фикс-листа приёмки S4 называет ровно это и не исполнен. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Список носителей НЕПОЛОН: помимо `STACK_DECISIONS.md:116` и `FRONTEND_PLAN.md:124` та же ошибка стоит ТРЕТИЙ раз — `frontend/docs/BACKLOG.md:16` (Ф-14: «spectral шестым шагом `check`, openapi-typescript генерит `src/api/schema.ts`»). Правя два, оставят третий. ⚠ И ещё: во всех трёх рядом стоит «нормативная поверхность — `docs/api-contract/openapi.yaml`» — а это на время фриза НЕВЕРНО (пинг №17, дописка: «зеркало = байт-копия временно НЕ действует, нормативен только канон `docs/architecture/14-api-contract/`»). Правку «четвёртым из шести» стоит вносить одним касанием с этой оговоркой, иначе исполнитель перечитает строку и унаследует вторую ложь. --- ## N127 · MINOR · `frontend/docs/frontend-PROGRESS.md:221` · C-исполненное-как-открытое -**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** **S4 (загрузка книги и запуск прогона на контракте 0.2.3, +**Срез:** Доки замороженной зоны фронта — frontend/**/*.md (10 файлов, 5167 строк) +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> **S4 (загрузка книги и запуск прогона на контракте 0.2.3, библиотека на масштабе, находки приёмки S3.7 — ждёт приёмки)**. + **Почему протухло:** Приёмка S4 состоялась 15.08 и записана В ЭТОМ ЖЕ ФАЙЛЕ строкой 465 («ПРИНЯТО и залендено `267aa35`, D39.135»). Два места одного файла говорят разное. -**Улика — команда:** `sed -n '220,221p' frontend/docs/frontend-PROGRESS.md; sed -n '465p' frontend/docs/frontend-PROGRESS.md` -**Улика — вывод:** 220: код зоны на английском; D39.129)**, **S4 (загрузка книги и запуск прогона на контракте 0.2.3, + +**Улика — команда:** +``` +sed -n '220,221p' frontend/docs/frontend-PROGRESS.md; sed -n '465p' frontend/docs/frontend-PROGRESS.md +``` +**Улика — вывод:** +``` +220: код зоны на английском; D39.129)**, **S4 (загрузка книги и запуск прогона на контракте 0.2.3, 221: библиотека на масштабе, находки приёмки S3.7 — ждёт приёмки)**. 465:### 15.08 — приёмка S4 оркестратором №17: ПРИНЯТО и залендено (`267aa35`); контракт 0.2.3 в каноне (D39.135) -**Предложенное действие:** «— ждёт приёмки» → «— ПРИНЯТ 15.08, D39.135; фикс-лист ФС-1…ФС-12 остаётся входом первого касания». +``` + +**Предложенное действие (автора находки):** «— ждёт приёмки» → «— ПРИНЯТ 15.08, D39.135; фикс-лист ФС-1…ФС-12 остаётся входом первого касания». + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Единственная из пятнадцати находок, чей носитель — ЗОННЫЙ ЖУРНАЛ, куда оркестратор пишет по решению владельца (CLAUDE.md, таблица ролей). Её можно исполнить сейчас, не дожидаясь разморозки, — в отличие от остальных четырнадцати. Заодно поправить в той же строке 249-250 («**Ждёт движка:** манифест глав и пофазный прогресс — строки 99–100, к S5 машиночитаемая таблица подписи — строка 101»): это тот же класс C в том же файле, строки закрыты D39.122, а машинная таблица с тех пор ещё и снесена D39.158. + +**Разбор опровергателя:** Проверил главное подозрение — не стоит ли строка 221 под ⚠-баннером «историческое» (тогда находка была бы анти-оверфлагом). Не стоит: баннер в этом файле начинается СТРОКОЙ НИЖЕ (222) и покрывает абзац про замок 04.08. Пункт «Пройдено» — носитель текущего состояния зоны, и он говорит «ждёт приёмки» о том, что принято 15.08 и залендено `267aa35`. Два места одного файла противоречат друг другу. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Единственная из пятнадцати находок, чей носитель — ЗОННЫЙ ЖУРНАЛ, куда оркестратор пишет по решению владельца (CLAUDE.md, таблица ролей). Её можно исполнить сейчас, не дожидаясь разморозки, — в отличие от остальных четырнадцати. Заодно поправить в той же строке 249-250 («**Ждёт движка:** манифест глав и пофазный прогресс — строки 99–100, к S5 машиночитаемая таблица подписи — строка 101»): это тот же класс C в том же файле, строки закрыты D39.122, а машинная таблица с тех пор ещё и снесена D39.158. --- ## N128 · MINOR · `platform/deploy/README.md:428` · C-исполненное-как-открытое -**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** ⚠ Выбирающего путь кода ещё нет: `Supervisor.Workdir` задаёт вызывающий, а вызывающий — воркер, +**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> ⚠ Выбирающего путь кода ещё нет: `Supervisor.Workdir` задаёт вызывающий, а вызывающий — воркер, + **Почему протухло:** Воркер существует с пака P4: прогон спавнится в `internal/runs/spawn.go`, который задаёт `Workdir: l.Workdir` транзиентному юниту, а корень УЖЕ настройка — `TM_PLATFORM_BOOKS_DIR` (обязательный абсолютный путь), от которого каталог книги строится как `filepath.Join(s.Cfg.BooksDir, id)` и сохраняется колонкой `books.workdir`. То есть обещанное «когда он появится, корень становится настройкой» произошло. Заодно мёртв и указатель: «строка 103 единого бэклога» закрыта (D-нота «строки 103/135/165/102 закрыты»), а живой бэклог закрытых строк не хранит. Сегодня `ingest.Supervisor` вообще не имеет боевого вызывающего — он остался дев-путём, о чём этот же файл честно пишет строкой выше («Дев-путь супервизора с группой процессов остаётся дев-путём»). -**Улика — команда:** `grep -rn 'Workdir:' platform/internal/runs/spawn.go | head -2; grep -n 'dir := filepath.Join(s.Cfg.BooksDir, id)' platform/internal/books/books.go; grep -rn 'ingest.Supervisor\|Supervisor{' --include=*.go platform/ | grep -v _test | wc -l; printf 'row 103 in live backlog: %s\n' $(grep -c '^| 103 ' docs/PROGRESS.md)` -**Улика — вывод:** 168: Args: runner.TranslateArgs(l.Workdir, l.VerifyBank, s.Cfg.KeysFile, + +**Улика — команда:** +``` +grep -rn 'Workdir:' platform/internal/runs/spawn.go | head -2; grep -n 'dir := filepath.Join(s.Cfg.BooksDir, id)' platform/internal/books/books.go; grep -rn 'ingest.Supervisor\|Supervisor{' --include=*.go platform/ | grep -v _test | wc -l; printf 'row 103 in live backlog: %s\n' $(grep -c '^| 103 ' docs/PROGRESS.md) +``` +**Улика — вывод:** +``` +168: Args: runner.TranslateArgs(l.Workdir, l.VerifyBank, s.Cfg.KeysFile, 170: Workdir: l.Workdir, 171: dir := filepath.Join(s.Cfg.BooksDir, id) 0 row 103 in live backlog: 0 -**Положительный контроль:** `grep -c "^| 175 " docs/PROGRESS.md` → 1: шаблон грепа находит живые строки бэклога (175/202/224 = 1), значит ноль по 103 — отсутствие строки, а не отказ инструмента. +``` +**Положительный контроль:** +``` +`grep -c "^| 175 " docs/PROGRESS.md` → 1: шаблон грепа находит живые строки бэклога (175/202/224 = 1), значит ноль по 103 — отсутствие строки, а не отказ инструмента. +``` **Другие носители того же факта:** platform/docs/PLATFORM_DIRECTION.md:101 (та же закрытая строка 103 как «носитель работы движка») -**Предложенное действие:** Заменить абзац на действующий факт: корень библиотеки — настройка `TM_PLATFORM_BOOKS_DIR` (обязательный абсолютный путь), каталог книги = `BooksDir/`, воркер спавна — `internal/runs/spawn.go`; `ingest.Supervisor` остался дев-путём. Ссылку на «строку 103» снять или заменить номером D-ноты, закрывшей её. + +**Предложенное действие (автора находки):** Заменить абзац на действующий факт: корень библиотеки — настройка `TM_PLATFORM_BOOKS_DIR` (обязательный абсолютный путь), каталог книги = `BooksDir/`, воркер спавна — `internal/runs/spawn.go`; `ingest.Supervisor` остался дев-путём. Ссылку на «строку 103» снять или заменить номером D-ноты, закрывшей её. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие принимается, с одной поправкой по существу: `TM_PLATFORM_BOOKS_DIR` не имеет дефолта — пустое значение означает «инстанс не принимает загрузок и `POST /books` вообще не монтируется» (`config.go:159-166`). Поэтому нельзя писать «её дефолт — этот каталог»: правильная формулировка — «`/srv/textmachine` это РЕКОМЕНДУЕМОЕ значение для юнита, а не дефолт кода; пусто = загрузки выключены». Иначе новая редакция заведёт ровно тот же класс дрейфа, что чинит. + +**Разбор опровергателя:** Воспроизведено полностью. Воркер спавна СУЩЕСТВУЕТ (`platform/internal/runs/spawn.go` задаёт `Workdir: l.Workdir` юниту и зовёт `Status` по нему), корень библиотеки УЖЕ настройка — `TM_PLATFORM_BOOKS_DIR` (`config.go:495`, обязательно абсолютный, `config.go:504`), а каталог книги строится как `filepath.Join(s.Cfg.BooksDir, id)` (`books.go:171`). То есть обещанное абзацем «когда он появится, корень становится настройкой» уже произошло. Отрицательное утверждение про `ingest.Supervisor` я проверил с положительным контролем: тот же греп даёт 13 вхождений всего (инструмент ищет и находит), из них вне тестов — только собственное определение пакета `supervisor.go`, ни одного боевого вызывающего. Указатель «строка 103» мёртв при работающем контроле (175 → 1). + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие принимается, с одной поправкой по существу: `TM_PLATFORM_BOOKS_DIR` не имеет дефолта — пустое значение означает «инстанс не принимает загрузок и `POST /books` вообще не монтируется» (`config.go:159-166`). Поэтому нельзя писать «её дефолт — этот каталог»: правильная формулировка — «`/srv/textmachine` это РЕКОМЕНДУЕМОЕ значение для юнита, а не дефолт кода; пусто = загрузки выключены». Иначе новая редакция заведёт ровно тот же класс дрейфа, что чинит. --- ## N129 · MINOR · `platform/docs/PLATFORM_DIRECTION.md:101` · B-мёртвый-указатель -**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* -**Вердикт пере-суда:** ПОДТВЕРЖДЕНО -**Цитата (дословно из дока):** **Носитель работы движка — строка 103 единого бэклога** (словарь событий); заводится +**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* +**Вердикт:** ПОДТВЕРЖДЕНО + +**Цитата из дока (дословно):** + +> **Носитель работы движка — строка 103 единого бэклога** (словарь событий); заводится + **Почему протухло:** Указатели на строки единого бэклога, розданные по всему срезу, в шести случаях целят в строки, которых в живой таблице БОЛЬШЕ НЕТ: правило бэклога — «Закрытые строки здесь НЕ хранятся — они уходят в D-лог с номером решения» (docs/PROGRESS.md:59). Мертвы 100, 102, 103, 139, 145, 165, 174, 199, 200, 211. Часть из них цитируется как ПРОВЕНАНС (это терпимо), но три подаются как ЖИВОЙ носитель незакрытой работы и потому читаются как открытый долг: 101 («Носитель работы движка — строка 103», словарь денежных событий), 265 («Персист манифеста глав (строка 100) — несущий для латентности чтения» — read-модель построена и персистит), и deploy/README.md:430. Читатель, пошедший по номеру, находит пустоту и не может отличить «закрыто» от «потеряно». -**Улика — команда:** `for n in 100 102 103 139 145 165 174 199 200 211 175 202 224; do printf 'row %-4s: %s\n' $n $(grep -c "^| $n " docs/PROGRESS.md); done; sed -n '59p' docs/PROGRESS.md | cut -c1-70` -**Улика — вывод:** row 100 : 0 + +**Улика — команда:** +``` +for n in 100 102 103 139 145 165 174 199 200 211 175 202 224; do printf 'row %-4s: %s\n' $n $(grep -c "^| $n " docs/PROGRESS.md); done; sed -n '59p' docs/PROGRESS.md | cut -c1-70 +``` +**Улика — вывод:** +``` +row 100 : 0 row 102 : 0 row 103 : 0 row 139 : 0 @@ -2533,21 +4776,42 @@ row 175 : 1 row 202 : 1 row 224 : 1 Закрытые строки здесь НЕ хранятся — они уходят в D-лог с номером р -**Положительный контроль:** Тот же греп на 175/202/224 даёт по 1 — шаблон работает и живые строки находит; ноль по остальным означает удалённую (закрытую) строку, а не сломанный инструмент. +``` +**Положительный контроль:** +``` +Тот же греп на 175/202/224 даёт по 1 — шаблон работает и живые строки находит; ноль по остальным означает удалённую (закрытую) строку, а не сломанный инструмент. +``` **Другие носители того же факта:** platform/docs/PLATFORM_DIRECTION.md:265 (строка 100), platform/deploy/README.md:430 (строка 103), :186 заголовок «строка 174», platform/docs/STACK_DECISIONS.md:246 (строка 165), :311 (строка 102), :414 (строка 199а), :348 (строка 200), :541 (строка 211), :545 (строка 145), :539/deploy:63 (строка 139) -**Предложенное действие:** Пройти по десяти номерам: где строка закрыта, дописать рядом номер закрывшей D-ноты («строка 103, закрыта D39.xxx») — тогда указатель ведёт в D-лог, как того и требует правило бэклога; где номер подан как незакрытый долг (101, 265, deploy:430) — пере-написать по факту исполнения. + +**Предложенное действие (автора находки):** Пройти по десяти номерам: где строка закрыта, дописать рядом номер закрывшей D-ноты («строка 103, закрыта D39.xxx») — тогда указатель ведёт в D-лог, как того и требует правило бэклога; где номер подан как незакрытый долг (101, 265, deploy:430) — пере-написать по факту исполнения. + +### ⚠ ПОПРАВКА ОПРОВЕРГАТЕЛЯ — ИСПОЛНЯЕТСЯ ОНА + +Действие верно по существу, координаты — нет: перед правкой пере-грепнуть носители, потому что шесть из семи номеров строк в other_carriers промахиваются. Действующие адреса: `platform/docs/STACK_DECISIONS.md` 267 (165), 317 (102), 357 (200), 414 (199), 547 (211), 554 (145), 560 (139); `platform/deploy/README.md` 64 (139), 179 (174), 430 (103); `platform/docs/PLATFORM_DIRECTION.md` 101 (103), 265 (100). Плюс замечание вне среза: тот же мёртвый номер несёт КОД — `backend/cmd/tmctl/main.go:72` («run `tmctl migrate` (row 174)»); правка доков его не лечит, это пинг бэкенду, а не ход оркестратора. + +**Разбор опровергателя:** Факт подтверждён с двойным контролем. Все десять номеров отсутствуют в живой таблице и по строгому, и по свободному шаблону, тогда как контрольные живые строки (175/202/224) и надгробия переехавших (96/97) находятся тем же грепом — инструмент работает. Второй контроль: номера не «выселены» в `13-tech-debt-anchors.md` (там 10 секций Б-*, ни одна не про эти строки), то есть след ведёт только в D-лог, как и предписывает правило `docs/PROGRESS.md:59`. Подтверждается и то, что три из них поданы как ЖИВОЙ незакрытый долг: 265 называет персист манифеста глав «несущим» для латентности, а таблица `chapters` построена миграцией `00002_readmodel.sql:91`. НО: список other_carriers в находке содержит НЕВЕРНЫЕ номера строк — в дереве это 267/317/357/547/554/560 (а не 246/311/348/541/545/539) и deploy 64/179 (а не 63/186). Сам факт это не отменяет, исполнителю нельзя править по этим координатам вслепую. + **Разрушающее:** True -**⚠ Опровергатель поправил действие:** Действие верно по существу, координаты — нет: перед правкой пере-грепнуть носители, потому что шесть из семи номеров строк в other_carriers промахиваются. Действующие адреса: `platform/docs/STACK_DECISIONS.md` 267 (165), 317 (102), 357 (200), 414 (199), 547 (211), 554 (145), 560 (139); `platform/deploy/README.md` 64 (139), 179 (174), 430 (103); `platform/docs/PLATFORM_DIRECTION.md` 101 (103), 265 (100). Плюс замечание вне среза: тот же мёртвый номер несёт КОД — `backend/cmd/tmctl/main.go:72` («run `tmctl migrate` (row 174)»); правка доков его не лечит, это пинг бэкенду, а не ход оркестратора. --- ## N130 · MINOR · `platform/docs/STACK_DECISIONS.md:566` · E-второй-носитель -**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* -**Вердикт пере-суда:** (не выносилось) -**Цитата (дословно из дока):** Админ-команды: `tmplatformctl grant --user --usd 5 [--note ...] [--key ...]` · +**Срез:** Доки зоны платформы: platform/README.md · platform/deploy/README.md · platform/docs/{ENGINEERING_STANDARDS,PLATFORM_DIRECTION,STACK_DECISIONS}.md · platform/docs/p8-review/* · platform/docs/p9/* +**Вердикт:** НА ПЕРЕ-СУД НЕ ВЫНОСИЛАСЬ — проверить своей командой перед правкой + +**Цитата из дока (дословно):** + +> Админ-команды: `tmplatformctl grant --user --usd 5 [--note ...] [--key ...]` · + **Почему протухло:** Перечень админ-команд отстал от CLI на семь глаголов. В диспетчере `cmd/tmplatformctl/main.go` сегодня: grant · adjust · balance · logins · revoke · book add · book refresh · books · runs · run abandon · seed · exit-marker. В списке §«Как поднять локально» нет `adjust`, `book add`, `book refresh`, `books --abandoned`, `runs [--stalled]`, `run abandon`, `exit-marker`. `platform/README.md:29-34` несёт ПОЛНЫЙ и верный список, `deploy/README.md` описывает `runs`/`run abandon`/`books --abandoned` — то есть один и тот же факт разложен по трём носителям и разошёлся ровно там, куда отсылает «Как поднять локально». Оператор, поднявший стенд по этому разделу, не узнает из него о двух командах терминального вердикта, ради которых пак P8-FIX и существовал. -**Улика — команда:** `grep -n 'case "' platform/cmd/tmplatformctl/main.go | tr -d '\t'; grep -n 'args\[0\] == "exit-marker"' platform/cmd/tmplatformctl/main.go` -**Улика — вывод:** 91:case "grant": + +**Улика — команда:** +``` +grep -n 'case "' platform/cmd/tmplatformctl/main.go | tr -d '\t'; grep -n 'args\[0\] == "exit-marker"' platform/cmd/tmplatformctl/main.go +``` +**Улика — вывод:** +``` +91:case "grant": 93:case "adjust": 95:case "balance": 97:case "logins": @@ -2560,6 +4824,9 @@ row 224 : 1 116:case "run": 121:case "seed": 70: if args[0] == "exit-marker" { +``` **Другие носители того же факта:** platform/README.md:29-34 (полный и верный список), platform/deploy/README.md:277-320 (runs / runs --stalled / run abandon / books --abandoned / book refresh), platform/docs/STACK_DECISIONS.md:80-84 (§10, «ради пяти операций» — историческое обоснование P1, трогать не надо) -**Предложенное действие:** Либо дополнить перечень 566–569 недостающими семью глаголами, либо (лучше — у факта должен быть один носитель) заменить его одной строкой-ссылкой на `platform/README.md` §«Бинари» и `deploy/README.md` §«Застрявшая работа», как уже сделано с числом гейтов батареи. + +**Предложенное действие (автора находки):** Либо дополнить перечень 566–569 недостающими семью глаголами, либо (лучше — у факта должен быть один носитель) заменить его одной строкой-ссылкой на `platform/README.md` §«Бинари» и `deploy/README.md` §«Застрявшая работа», как уже сделано с числом гейтов батареи. + **Разрушающее:** False