diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index c7eba927..57527978 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1,6 +1,6 @@ # Журнал прогресса -> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-06, голова D39.219 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №23** (единственный носитель — здесь; роль передана 06.09, №22 закрыт нотой передачи D39.217; ⚠ испр. 06.09: коммит передачи `c992ee5` бампнул голову и НЕ тронул номер — гейта на номер очереди нет вовсе, `counts.py` сверяет только голову, поэтому носитель разошёлся молча). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ПЕРВЫМ:** (0а) **ревизия документации на ПРОТУХШЕЕ** — по всем зонам, не только тронутым; (0б) **планы доработок в БЭКЕНД и ПЛАТФОРМУ** по остатку бэклога и регистра; (0в) **вынос неактуального в АРХИВ** (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **90** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 106 (major 3), всего рядов 458 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном). +> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-07, голова D39.220 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №23** (единственный носитель — здесь; роль передана 06.09, №22 закрыт нотой передачи D39.217; ⚠ испр. 06.09: коммит передачи `c992ee5` бампнул голову и НЕ тронул номер — гейта на номер очереди нет вовсе, `counts.py` сверяет только голову, поэтому носитель разошёлся молча). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ПЕРВЫМ:** (0а) **ревизия документации на ПРОТУХШЕЕ** — по всем зонам, не только тронутым; (0б) **планы доработок в БЭКЕНД и ПЛАТФОРМУ** по остатку бэклога и регистра; (0в) **вынос неактуального в АРХИВ** (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **90** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 106 (major 3), всего рядов 458 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном). > ⛔ **ЧЕТЫРЕ ПОТОКА РАБОТЫ И ИХ ЗАВИСИМОСТИ (заведено 06.09; до этого жило только в переписке с владельцем).** > **(1) ПОЛИГОН — «ремонт прибора»** (`docs/POLYGON_INSTRUMENT_REPAIR_SESSION_PROMPT.md`, строки 319 · 300 · 265 · 143): > блокирующая линза слепого чтения меряет верность ЧЕРНОВИКУ, а судимой руке предписано отходить от него по исходнику @@ -25,7 +25,7 @@ > - ⚠ **Норма приёмки, купленная аудитом D39.167 п.1:** акт лендинга, объявивший закрытие строки регистра или бэклога, обязан быть СВЕРЕН СТАТУСОМ в тот же заход — иначе следующий пак собирается ПО РЕГИСТРУ и едет чинить построенное. Машинная половина — строка бэклога **225**. > - ⚠ **ЛЕНТА КОНТРАКТНЫХ МИНОРОВ** (что принёс каждый — телом ноты): **0.5.0** — D39.161 · **0.6.0** — D39.162/D39.163 · **0.7.0** — D39.166 · **0.8.0** — D39.169 · **0.9.0** — D39.180 · **0.10.0** — D39.194 (дверь выдачи: `downloadExport`, `failure_code` словарём, `Export.url` относительный; ⚠ читать с эрратой 04.09-в — минор был ратифицирован без своей кодовой половины и сутки жил ложным на проводе). Прожитые дампы приёмок P9/P10 — `archive/PROGRESS-2026-08-backend.md` и тела нот. > - **ЖИВОЕ:** полигон — **фаза Д ИДЁТ** (заказ 10.08; деньги санкционированы 15.08 напрямую полигону — ⚠ числа потолка в носителях расходятся, фактическую цепь сверить при лендинге петель; ja-книга `enkan_no_hate_ja`; при ратификации фазы Д в D-ноту: декой-правило + обязательный кросс-семейный опровергатель приёмки — одобрены 10.08; свежие фриз-коммиты полигона в дереве — НЕ трогать) · платформа и контракт — статус в строке выше, здесь НЕ дублируется (один носитель на факт) · **фронт ЗАМОРОЖЕН** (D39.136 п.2 + **D39.147: морозится ДАЛЬШЕ лендинга P7 — слово владельца 17.08; P7 зону НЕ размораживает, S5-промт не выдаётся, разморозка отдельным словом по достижении сквозного пути**; перечень первого касания зоны — синк зеркала на ТЕКУЩИЙ канон (отставание и его цена — п.6 очереди выше и строка бэклога 183(б)) + перегенерация типов + моки + гейт утечки конвейера + ФС-1..12 + Ф-63/Ф-28 + фразы В-11 по словарю кодов — не отменён, ждёт разморозки) · закрытые стройки эры — реестром нот `architecture/05-decisions-index.md` (D39.130–137, одна строка на ноту). -> - **Открыто на владельце:** ✅ **Развилка строки 291 ЗАКРЫТА владельцем 05.09 — `D39.204`:** отказ резервации на ретрае деградирует во ФЛАГ («пусть флагает просто как-то»); вариант «минимальная покупка ≥ `step_max_usd`» снят. ⚠ Это оказалось НЕ сменой канона: D2 уже кончается флагом, денежная ветка до него не доходит. · ✅ **Лестница D25.1 ЗАКРЫТА владельцем 05.09 — `D39.205`:** копия БЕЗ пометки = книга БЕЗ ЕДИНОГО ПРОПУСКА; любой пропуск ⇒ пометка в файле, гранулярность — ЮНИТ (пропущенный кусок главы критичен). Имя файла решено паком `7e2226a` (вычисляется при скачивании). ⚠ Прежняя редакция держала это открытым на владельце — по ней он и получил вопрос, который сам уже закрыл · лейбл главы без заголовка (сейчас цифры; словесный шаблон = одна строка `reader.txt` после ратификации К-3) (⚠ три вопроса пака ЗАКРЫТЫ словом владельца 30.08: экспорт юзеру — всегда строить, см. п.4 очереди · модель пометок подтверждена как построена — надпись о пропуске в файле, отдельный варнинг-слой в интерфейсе НЕ строим, машинный отчёт сборки остаётся внутренним носителем (дрейф) · RTL — снят до появления право-налевой целевой пары) · ⚠ **ПЛАТФОРМЕННЫЙ ЛИСТ ЗАКРЫТ СЛОВОМ ВЛАДЕЛЬЦА 30.08 — четыре ответа и их следствия ЦЕЛИКОМ телом D39.176** (квот нет · продажа не в бете · продолжение после пополнения — руками · фразы пользователя делегированы проекту с требованием интернациональности). ⚠ **Лист ПУСТ:** последний его пункт (`PD-421`, «держит ли открытый поток сессию живой») СНЯТ замером 30.08 — окно бездействия сессии **14 суток**, абсолютный срок **30** (`platform/internal/config/config.go:339`=`defaultSessionIdle`), скольжение окна идёт на любом аутентифицированном запросе, а поток теряется лишь после того, как подметён протухший ряд, то есть после ДВУХ НЕДЕЛЬ без единого действия пользователя. ⚠ Часовой интервал в этой теме — периодичность УБОРКИ, а не срок сессии (эррата 30.08-а к D39.176 п.5) · ⚠ Гейт словаря фраз — строка **204** (D39.176 п.4). ⚠ Прежний указатель «строка 148» мёртв — строку удалил `a023e39` решением владельца, восстанавливать Приложение А по ней НЕ надо · **развязка git с origin** (локальная линия ИСТИННА, force-push его рукой; не пуллить) · В-4/Ф-30 (глава без заголовка — движковая половина строка 160) · мини-проба флора 44 (одобрена, промт не выдан; число — после ре-пробы 188) · ~~подпись денежного шага 46~~ (ратифицирована D39.190 п.4: пере-снапшот батча — единым актом, цена сегодня $0) · **схема time-based DeepSeek** — рекомендация и её доводы строкой **172**(а) · **вход ратификации фазы Д:** посылка «dspro дешевле glm» в пике ПЕРЕВЁРНУТА (×1.26 дороже, D39.137 п.4) · **возражение Sol по проходу `tier` эксп-23** (довод дословно — слайс `archive/PROGRESS-2026-08-10-15.md`: Sol судил СВОЁ семейство, что запрещено D13.3; подтвердить или снять — при лендинге петель фазы Д) · фронт-вопросы зонного журнала: В-7 (плотность; держит Ф-54) · В-8 (слово состояния в дереве) · В-9 (языки интерфейса — механизм готов, ПТ-36) · Ф-38 (вкладка «Замечания»). Закрытые пункты листа — в нотах D39.136–145, здесь не держатся. +> - **Открыто на владельце:** ⚠ **НОВОЕ 06.09: второй круг селф-ревью** — бэкенд-сессия передала слово владельца «после сдачи обязателен селф-ревью, и если он дал исправления — НОВЫЙ круг, проверяющий исполнением и исходный заказ, и закрытие находок предыдущего круга». Первую половину того же слова (комментарии) он подтвердил как РЕКОМЕНДАЦИЮ, про вторую не сказал ничего; в мандате самопроверки `CLAUDE.md` сегодня описан ОДИН круг. Сессии исполняют добровольно, каноном не стало. ⭐ **УЛИКА В ПОЛЬЗУ ПРАВИЛА, снятая в ту же ночь 07.09:** круг 2 бэкенд-сессии опроверг ЕЁ ЖЕ правку круга 1 — свёртка сравнения через `NormalizeSourceKey` вводила ЛОЖНООТРИЦАНИЕ (замер зоны: `ReplaceGlossary(族長 + 族长)` → `rows stored=2`, контроль на байт-равном `src` → `UNIQUE constraint failed`, то есть UNIQUE стоит на СЫРОМ `src` и обе строки инжектятся), и мутация «свернуть сравнение» до круга 2 ПЕРЕЖИВАЛА весь пакет. Один круг сдал бы тихо-зелёное. · ✅ **Развилка строки 291 ЗАКРЫТА владельцем 05.09 — `D39.204`:** отказ резервации на ретрае деградирует во ФЛАГ («пусть флагает просто как-то»); вариант «минимальная покупка ≥ `step_max_usd`» снят. ⚠ Это оказалось НЕ сменой канона: D2 уже кончается флагом, денежная ветка до него не доходит. · ✅ **Лестница D25.1 ЗАКРЫТА владельцем 05.09 — `D39.205`:** копия БЕЗ пометки = книга БЕЗ ЕДИНОГО ПРОПУСКА; любой пропуск ⇒ пометка в файле, гранулярность — ЮНИТ (пропущенный кусок главы критичен). Имя файла решено паком `7e2226a` (вычисляется при скачивании). ⚠ Прежняя редакция держала это открытым на владельце — по ней он и получил вопрос, который сам уже закрыл · лейбл главы без заголовка (сейчас цифры; словесный шаблон = одна строка `reader.txt` после ратификации К-3) (⚠ три вопроса пака ЗАКРЫТЫ словом владельца 30.08: экспорт юзеру — всегда строить, см. п.4 очереди · модель пометок подтверждена как построена — надпись о пропуске в файле, отдельный варнинг-слой в интерфейсе НЕ строим, машинный отчёт сборки остаётся внутренним носителем (дрейф) · RTL — снят до появления право-налевой целевой пары) · ⚠ **ПЛАТФОРМЕННЫЙ ЛИСТ ЗАКРЫТ СЛОВОМ ВЛАДЕЛЬЦА 30.08 — четыре ответа и их следствия ЦЕЛИКОМ телом D39.176** (квот нет · продажа не в бете · продолжение после пополнения — руками · фразы пользователя делегированы проекту с требованием интернациональности). ⚠ **Лист ПУСТ:** последний его пункт (`PD-421`, «держит ли открытый поток сессию живой») СНЯТ замером 30.08 — окно бездействия сессии **14 суток**, абсолютный срок **30** (`platform/internal/config/config.go:339`=`defaultSessionIdle`), скольжение окна идёт на любом аутентифицированном запросе, а поток теряется лишь после того, как подметён протухший ряд, то есть после ДВУХ НЕДЕЛЬ без единого действия пользователя. ⚠ Часовой интервал в этой теме — периодичность УБОРКИ, а не срок сессии (эррата 30.08-а к D39.176 п.5) · ⚠ Гейт словаря фраз — строка **204** (D39.176 п.4). ⚠ Прежний указатель «строка 148» мёртв — строку удалил `a023e39` решением владельца, восстанавливать Приложение А по ней НЕ надо · **развязка git с origin** (локальная линия ИСТИННА, force-push его рукой; не пуллить) · В-4/Ф-30 (глава без заголовка — движковая половина строка 160) · мини-проба флора 44 (одобрена, промт не выдан; число — после ре-пробы 188) · ~~подпись денежного шага 46~~ (ратифицирована D39.190 п.4: пере-снапшот батча — единым актом, цена сегодня $0) · **схема time-based DeepSeek** — рекомендация и её доводы строкой **172**(а) · **вход ратификации фазы Д:** посылка «dspro дешевле glm» в пике ПЕРЕВЁРНУТА (×1.26 дороже, D39.137 п.4) · **возражение Sol по проходу `tier` эксп-23** (довод дословно — слайс `archive/PROGRESS-2026-08-10-15.md`: Sol судил СВОЁ семейство, что запрещено D13.3; подтвердить или снять — при лендинге петель фазы Д) · фронт-вопросы зонного журнала: В-7 (плотность; держит Ф-54) · В-8 (слово состояния в дереве) · В-9 (языки интерфейса — механизм готов, ПТ-36) · Ф-38 (вкладка «Замечания»). Закрытые пункты листа — в нотах D39.136–145, здесь не держатся. > - **Сделано (сводно)** — снимок ленты закрытых эр в [archive/PROGRESS-2026-08-04-09.md](archive/PROGRESS-2026-08-04-09.md); детали — D-лог и слайсы. > - ⭐ **КУРС НА 04.09 — ПОРЯДОК РАБОТ, СОБРАННЫЙ ВЕЕРОМ ПО ВСЕМУ БЭКЛОГУ (31 агент, 46 кандидатов, шесть срезов + опровергатели + критик полноты).** ⚠ Главный результат метода, а не списка: **из 46 кандидатов бэклога опровержение пережили ДВА** — остальное трекерная инерция; а то, чего в трекере не было ВОВСЕ, оказалось тяжелее всего, что в нём есть (строки **269–275**, заведены этим же заходом). Порядок: > **(1) ЭКСПЛУАТАЦИЯ — ✅ ЗАКРЫТА ЛЕНДИНГОМ 05.09 (`7e2226a` + `3e84716`, акт D39.201): бэкапы с ПРЕДЪЯВЛЕННЫМ восстановлением, дверь для не-браузерного клиента, имя книги. Строки 269 · 270 · 274 закрыты и сняты из таблицы; ниже — история заказа.** Строка **269** бэкапов нет ни у оплаченной работы, ни у денежного реестра — ЕДИНСТВЕННАЯ находка дня без частичного исхода: одна потеря диска необратима и недоказуема · строка **270** у продукта НОЛЬ дверей: `bearerToken` объявлен и не выдаётся, живой цикл 04.09 прошёл через dev-вход, который прод отказывается запускать, фронт заморожен · строка **274** имя книги в выданном файле исходноязычное и не чинится ничем (дёшево, а ломается первое, что видит человек). @@ -146,6 +146,268 @@ ## Бэкенд +### Пак «ЗАКОН БАНКА — ЧЕСТНО» (06.09, промт `docs/BACKEND_CONSISTENCY_SESSION_PROMPT.md`, вход HEAD `e4097cb`). НЕ КОММИЧУ — ждёт лендинга + +**Итог: закон банка описан тем, что код делает, и найдена дыра ШИРЕ заказанной — стадия схлопывания не сверяла свою выработку со строкой банка на тот же ключ ни при какой подписи.** Закрыто в двух местах (схлопывание + страховка на слиянии), репортом, без смены поведения платного прогона. Круги самопроверки нашли **31 находку В МОЕЙ ЖЕ работе** (18 + 8 + 5), из них пять — ложные утверждения в моих же комментариях (тот самый класс, который пак чинил) и одна — РЕГРЕСС, внесённый лечением предыдущего круга. Все разобраны, списки ниже. + +⚠ **Комментарии этой смены написаны под рекомендацию владельца 06.09** («только важные детали или обоснования, без прозы, воды, истории и странных гарантий»; носитель — `docs/architecture/12-go-style-notes.md` §1, статус РЕКОМЕНДАЦИЯ). Применена только к СВОИМ комментариям пака: сплошного приведения корпуса рекомендация не заказывает. Единственный комментарий вне пака, который я тронула, — `internal/pipeline/export.go` (`TextModified`): моя правка `chunkstatus.go` сделала бы его утверждение рассогласованным, и оставить два живых противоречащих описания одного факта было нельзя. ⚠ **Второй круг самопроверки исполнен ДОБРОВОЛЬНО:** владельцем эта половина не подтверждена, каноном не стала, и как на решение владельца я на неё не ссылаюсь. + +#### 0. Исход по КАЖДОМУ пункту заказа — пунктов без исхода нет + +| пункт промта | исход | чем предъявлено | +|---|---|---| +| §4.1(а) пере-снять замер | **сделано** | таблица §1 ниже, каждый ноль с контролем | +| §4.1(б) закрыть непокрытое | **сделано в двух местах** | `membank.ConsolidationKeyConflicts` + расширенная `UnverifiedKeyConflicts`; пины + 18 мутаций | +| §4.1(в) «варнинг → стоп» | **сознательно не делаю** — по промту это пинг; вынесено оркестратору, он развёл «видеть» и «делать» по уровням и завёл строку бэклога | в коде ни `return err`, ни drop: обе проверки только `WarnContext` | +| §4.2 совет про статус | **сделано, вариант 1** (текст приведён к коду); вариант 2 отвергнут по существу — вернул бы развилку по статусу, закрытую `D39.104` п.2 | замер «status: auto не растворяет ливлок»: инжектятся ОБЕ (2/2/2), контроль 0; пин + 2 мутации | +| §4.3 комментарий постчека + пин | **сделано**, плюс два носителя той же лжи вне промта | `TestWrongDstObeyedIsNotAMiss` + мутация | +| §4.3 детектор ЗНАЧЕНИЯ | **сознательно не делаю** — запрещён промтом, живёт строкой 286 | в диффе нет source-side проверки | +| §4.4 sticky в редакторской волне | **сделано** (комментарий); сам перенос НЕ строю — по строке 321 сначала замер дрейфа | `waverun.go`, `nil` на месте; оба механизма названы с `file:line` | +| §4.4 резюм и строка статуса | **сделано** — поведение установлено по коду и запинено | `TestOrdinaryResumeMovesOnlyTheSkippedRow` + 2 мутации; мнение автора промта проверено, а не унаследовано | +| §4.5 строки 322 / 295 / 161 / 283 | **не делаю** — прямо исключены промтом | в диффе их нет | +| §5а список | **сделано, 9 позиций** с `file:line`; ни одна не чинилась | секция §8 | +| П1 страховка на слиянии | **сделано** варнингом | формат сообщения + 4 мутации; корпусный замер | +| П2 только репорт | **соблюдено** | отказ не построен | +| П3 маркер на листе подписи | **сделано**, маркер отличен от `Contradicts` | пин + 2 мутации; предъявлено, что сдвинулся только сайдкар | + +#### 1. Пере-снятый замер §4.1(а): постановка подтвердилась и оказалась уже правды + +$0, мои руки, выброшенные пробы. Каждый ноль с контролем. Точка слияния, `windowsOverlap([0,0],[3,9])=true` (контроль `[0,2]×[3,9]=false`): + +| популяция | `ApprovedSharedKeyCollisions` | `UnverifiedKeyConflicts` | +|---|---|---| +| approved × approved (КОНТРОЛЬ) | **1** | 0 | +| approved × auto (КОНТРОЛЬ) | 0 | **1** | +| draft×auto, draft×draft, auto×auto | **0** | **0** | +| общий alias, draft × auto | **0** | **0** | +| общий alias, approved × approved (КОНТРОЛЬ) | **1** | 0 | + +Внутрифайловый `parseBankSeed`: тот же `Src`, draft×auto, один файл → REFUSED (статус-слеп); общий alias, draft×auto → LOADED, ни один гард не сработал. + +⚠ **Поправка к строке 267, принята оркестратором:** контроль «общий alias, approved×approved, один файл» тоже LOADED — `LoadGlossarySeed` alias'ы не сравнивает вообще. Непокрытие через alias внутри файла — не только для неподписанных; для подписанных страховка стоит этажом выше (`bankmaterialize.go:142`). + +#### 2. Главная находка (в промте и строке 267 её нет) + +**Стадия схлопывания не сверяет выработку с существующей строкой банка на тот же firing-ключ — ни при какой подписи.** Слово владельца в строке 267 указывает сюда, и оно шире исходной постановки. Предъявлено пробами: +- `CanonConflicts` при кандидате `元始`→«камень первоисточника» и ПОДПИСАННОЙ `元始`→«юаньши` вернула 1 конфликт, но **не этот**: назвала составное `元始空窍`. Прямая пара пропущена по построению (`terminology.go:941` пропускает `n.Src == c.Key`; `CanonFor:873` — «a candidate that IS the signed row has nothing to learn from it»). КОНТРОЛЬ на случае, ради которого проверка строилась → **1**. +- `ConsolidationConflicts` на «та же поверхность дважды» → **0**; КОНТРОЛЬ «составное роняет свою же часть» → **1**. +- Ветка не мёртвая: `unsignedEngineSurfaces` выкидывает неподписанную mined-строку из сид-поверхностей майнера (выжила 1 из 2, именно `approved`), а `terminology.Merge(nil, 2 observed)` кладёт в кандидаты обе поверхности, включая ту, что банк держит `approved` (КОНТРОЛЬ: `Merge(nil,nil)` → **0**). Фильтр сид-поверхностей стоит на ЭМИССИИ (`mining.go:549`), не на входе в роль. + +#### 3. Что построено + +**(а) `membank.ConsolidationKeyConflicts`** — сверка каждой схлопнутой формы с любой строкой банка на общий firing-ключ (src или alias) при перекрытии окон и другом dst, независимо от статуса обеих сторон. Возвращает `[]BankKeyConflict` (части, не предложения: рендерят две поверхности — лог и лист подписи). Пара с совпадающим UNIQUE-кортежем не репортится: схема не даёт обеим строкам существовать. Сравнение кортежа идёт по СЫРОМУ `Src`, потому что таков ключ хранилища (см. §4-бис п.1: `族長` и `族长` с одним окном — две законные строки); соответствие обеспечивает вызывающий, передавая источник, который строка получит при посадке. +⚠ **Проверка сформулирована как РАЗНОГЛАСИЕ, а не как предсказание «обе формы поедут на провод».** Так вышло по находке второго круга: для сид-поверхности эмиссия роняет предложение, а авто-банк переписывается целиком, поэтому «BOTH survive» было бы ложью ровно на сценарии, который пиннит мой же тест. Три механизма разрешения названы в доке. +Проводка — `terminologist.go`: `glossaryRows()` (одно чтение банка, столько же обращений к store, сколько было), `consolidatedRows()`, `terminologyResult.BankConflicts` + `BankHolds`. + +**(б) `membank.UnverifiedKeyConflicts` расширена** (П1): было «approved × неподписанная», стало «любая пара, кроме approved×approved» (та у fail-loud). Осталась ВАРНИНГОМ по прежнему доводу. Нужна отдельно от (а): состояние достижимо и без схлопывания — рукописный сид `draft` + рукописная дельта `draft` с разными окнами при выключенном гейте терминолога. Сообщение называет обе строки, их подписи, оба окна и firing-ключ (`statusLabel`, `windowLabel` — `[ch 3..9]`/`[ch 1..end]` вместо сырых нулей, которые читались бы как пустое окно). + +**(в) Находка доезжает до листа подписи** (П3): `BankStopRow.BankHolds` + маркер `THE BANK ALREADY HOLDS:` — отдельное поле и отдельный маркер от `Contradicts`/`CONTRADICTS this run's own`. + +**(г) §4.3 — комментарий постчека приведён к поведению + пин `TestWrongDstObeyedIsNotAMiss`.** Промах = форма ОТСУТСТВУЕТ, и только это; послушанный неверный dst присутствует, `if present { continue }` берётся, промаха нет. Ноль confirmed-промахов говорит про ИСПОЛНЕНИЕ ПОДПИСАННОЙ половины банка — про неподписанную не говорит ничего (её промахи уходят в `Unverified` и не считаются). **Те же ложные утверждения найдены грепом и сняты ещё в двух местах, которых в промте не было:** `pipeline/disposition.go:92` и `cmd/tmctl/render.go:362`. + +**(д) §4.2 — совет «or mark one status: auto» снят.** Установлено исполнением: при `status: auto` на второй строке инжектятся ОБЕ (approved→2, draft→2, auto→2; КОНТРОЛЬ «src отсутствует в чанке» → 0). Под `D39.104` п.2 провод не ветвится по статусу ⇒ статус-слепота гварда ВЕРНА, а совет — осколок до-`D39.104` эпохи. Выход №2 («научить гвард смотреть статус») отвергнут по существу: он вернул бы закрытую доктриной развилку. + +**(е) §4.4 — два ложных комментария сняты.** `waverun.go`: оправдание `nil`-переноса sticky заменено фактом — `assignEditUnits` закрывает единицу внутри главы, sticky сбрасывается на границе ГЛАВЫ, 1962 из 2283 глав многоединичны (замер строки 321, не мой, атрибутирован); перенос НЕ строю. `chunkstatus.go`: см. §5, там моя формулировка была неверна дважды. + +**(ж) Попутно исправлена собственная ложь, созданная расширением:** `seeding.go:75` говорил «an UNSIGNED row contradicts an APPROVED term» — неправда для новой половины. ⚠ На старый текст не стоял ни один тест (`grep` в `*.go` = 0): сообщение, по которому оператор действует, было без пина. + +#### 4. Что нашли ДВА круга самопроверки в моей же работе + +Круг 1 — интервальный контролёр по явным критериям + опровергатель (оба Fable, оба на копии дерева). 18 находок, все разобраны. Самые дорогие: + +1. **`chunkstatus.go` — моя правка §4.4 была ЛОЖНОЙ.** Я написал «обычный резюм ничего здесь не пишет». Оба агента предъявили обратное, и я воспроизвёл сам: **$0-резюм (провайдер-вызовов 3→3) сдвинул `updated_at` СКИПНУТОЙ строки** (`ch1/chunk0/edit: 19:48:13 → 19:48:14`) при неподвижных `ok` и `flagged` (КОНТРОЛЬ: 3 строки из 4 не двинулись). Механизм: скип — не хранимый вердикт, флагнутая строка отдаётся из чекпойнта, а `runStageSequence` (`waverun.go`) пере-апсертит скип каждым прогоном. ⚠ **Моя ПЕРВАЯ попытка назвать механизм тоже была неверна** (указала на fast-path `stagerun.go`); поймано мутацией — мутант «fast-path обслуживает скипнутые» ПЕРЕЖИЛ пин. Формулировка исправлена на `waverun.go`, близнец `export.go` (`TextModified`) приведён в согласие, поставлен пин `TestOrdinaryResumeMovesOnlyTheSkippedRow`. +2. **Ложное срабатывание, класс A:** для сид-поверхности, задетой через banknote, выработка НИКОГДА не ложится (эмиссия её роняет), поэтому мой текст «the term would carry TWO renderings» был ложью ровно на сценарии моего же теста. Проверка пере-сформулирована как разногласие; текст варнинга и док переписаны. +3. **Ложное срабатывание, класс B:** сравнение UNIQUE-кортежа шло по сырым `Src`, а banknote-кандидат несёт сырую поверхность против нормализованного ключа ложащейся строки (`族長` против `族长`). ⚠ **Лечение круга 1 было неверным** (свёртка обеих сторон) и переделано на круге 2 — см. §4-бис п.1. +4. **Мой собственный комментарий повторял чужой misnomer:** «dropped where a SIGNED row holds the key» — `loadAutoBank` роняет предложение по ЛЮБОЙ ранее собранной строке любого статуса. Исправлено и названо ценой пропуска. +5. **`consolidatedRows` не пиннился ничем:** мутации «убрать `SinceCh`» и «убрать алиасы» переживали весь пакет `pipeline`. Добавлен `TestConsolidatedRowsCarryTheWindowAndTheAliases`; обе мутации теперь ловятся с верным текстом. +6. **Три дефекта в моём корпусном приборе:** классификация пары по подстроке `: approved "` ловила только случай, когда approved-строка печатается первой; `booksSeen` был тождествен `booksWithRows` по построению (мёртвая «контрольная» величина); подсадка строилась от `g[0]`, у которой может не быть огневого ключа или dst ⇒ ложный ОТКАЗ прибора. Все три закрыты, числа после починки те же. +7. **Двойной репорт в ОДНОМ прогоне — подтверждён моим прогоном:** авто-режим после `writeAutoBank` пере-сидит, и `UnverifiedKeyConflicts` называет ту же пару через 2 мс после стадии схлопывания (collapse=1, merge=1, КОНТРОЛЬ: всего 3 строки WARN). НЕ подавлял: это разные состояния одного термина («роль расходится с банком» до того, как строка легла, и «банк держит обе» после), и только второе означает две строки на проводе. Названо в доке; если оркестратор сочтёт это шумом — резать надо слияние. +8. **Линтер нашёл два настоящих дефекта в приборе:** непроверенный `rows.Err()` в обоих запросах. Для измерительного прибора это ровно та неисправность, ради которой он написан: усечённая итерация вернула бы меньший банк как чистый. Разведено на две функции с `defer rows.Close()` и возвратом `rows.Err()`. + +Мелкие, тоже закрыты: перебор в «zero says the bank was EXECUTED»; `render.go` — строка detail несёт и ambiguous-промахи, поэтому «an approved rendering» верно для гейта, не для строки; ассерт «one fault, not two: []» давал верный вердикт при нуле с неверным текстом. + +#### 4-бис. КРУГ 2 самопроверки — восемь находок, три серьёзные, все в моей же работе + +Мандат круга 2 (добровольный, см. оговорку выше): проверить ИСПОЛНЕНИЕМ и исходный заказ, и закрытие находок круга 1. Проверяющий — отдельный агент на копии дерева, 19 мутаций, каждая восстановлена со сверкой sha256. + +1. ⛔ **МОЙ ФИКС НОРМАЛИЗАЦИИ БЫЛ НЕВЕРЕН, А ДОВОД ПОД НИМ — ЛОЖЕН.** По находке круга 1 я свернула сравнение UNIQUE-кортежа через `NormalizeSourceKey` и написала «схема не даёт обеим строкам существовать». **Пере-мерила сама:** `ReplaceGlossary(族長 + 族长, один sense и окно)` → `err=nil, rows stored=2`; КОНТРОЛЬ с байт-равным `src` → `UNIQUE constraint failed: glossary.book_id, glossary.src, glossary.sense, glossary.since_ch, glossary.until_ch`. **UNIQUE стоит на СЫРОМ `src`**, и трад/упрощённая пара — две законные строки, обе из которых инжектятся. Значит моя свёртка вводила ЛОЖНООТРИЦАНИЕ: пропускала настоящее противоречие. Вылечено по-настоящему, а не заплатой: сравнение вернулось к сырому (зеркалит ключ хранилища), а `consolidatedRows` строит строку с `Src: c.Key` — тем источником, который она РЕАЛЬНО получит при посадке (`mining.go` кладёт `Src: c.Key`). Обе половины запинены (`TestConsolidationTupleSkipMirrorsTheStoreKey`, усиленный `TestConsolidatedRowsCarryTheWindowAndTheAliases`), обе мутации красны с верным текстом. ⚠ **До круга 2 мутация «свернуть сравнение» ПЕРЕЖИВАЛА весь пакет** — фикс был не запинен вовсе. +2. ⛔ **`chunkstatus.go` — не тот механизм В ТРЕТИЙ РАЗ подряд.** Скип в шипящей раскладке (редакторская волна, единица с флагнутым черновиком) пишет `recordSkippedStages`, а не флагнутая ветка `runStageSequence`: та берётся только при ≥2 стадиях в ОДНОЙ волне. Поймано мутацией — правка ветки `runStageSequence` мой пин ПЕРЕЖИЛА, а правка `recordSkippedStages` его уронила с верным текстом. Названы оба писателя, в доке пина тоже. +3. ⛔ **Пропуск по кортежу молчит на самой частой форме сид-строки** (окно по умолчанию, без sense). Замер: та же фикстура без `until_ch: 20` → `BankConflicts=0` и при `draft`, и при `approved`; с `until_ch: 20` → 1. **Мой же e2e-тест обходил это подобранным окном, и я этого не вынесла.** На провод не влияет (предложение отбрасывается), теряется строка на листе подписи. Названо ценой в доке `ConsolidationKeyConflicts` и вынесено в §5а №7. +4. **Тихий ноль, внесённый ЭТИМ паком:** `glossaryRows` возвращал `nil` при ошибке чтения банка без единого слова, из-за чего `BankConflicts=0` неотличим от «конфликтов нет». Теперь логируется явно, с формулировкой «их нули означают „не спросили“, а не „не нашли“». +5. **Перебор в §4.3:** «ноль confirmed-промахов = подписанная половина исполнена» — неточно: `Confirmed` требует и `approved`, и ДОВЕРЕННОГО ключа, а промах на одно-Han ключе демотируется в `Unverified`. Приведено к «подписанные строки, сработавшие на доверенном ключе». +6. **Стиль (правило владельца):** довод «containment + equal-source + approved-only» стоял ТРИЖДЫ — оставлен там, где живёт проверка, остальные заменены ссылкой; снята история в тесте («эта фикстура всегда несла»), план в коде («carry stays unbuilt until it is»), чужой замер 1962/2283 атрибутирован стенд-книге и дополнен `file:line` обоих механизмов. Проверяющий отдельно искал ПОТЕРЮ нужной детали при сокращении и не нашёл (перечитаны все 14 файлов диффа). +7. **§5а вырос с 6 позиций до 9**: №6 `bankmaterialize.go:127` (та же ложь про «signed»), №7 пропуск по кортежу на форме сид-строки по умолчанию, №9 — путь записи статуса не читается по коду (писателей два, выбор решает раскладка волны). Тихий ноль `glossaryRows` не вынесен, а ЗАКРЫТ. +8. **Подтверждено круга-2 замером:** довод про двойной репорт верен по коду (`mining.go:241-246`); корпусный прибор после починки даёт те же числа и симметричную классификацию; обе мутации `consolidatedRows` ловятся; ассерт «0 против 2» различает случаи. + +⇒ **Итог по кругам: 18 находок в круге 1 + 8 в круге 2, из них ПЯТЬ — ложные утверждения в моих же комментариях.** Это тот самый класс, который пак чинил, и он воспроизвёлся на мне пять раз. Единственное, что его ловило надёжно, — мутация, прочитанная ПО ТЕКСТУ: три раза пин переживал мутацию, и каждый раз это означало не дыру в пине, а неверный механизм в моём описании. + +#### 4-кватер. КРУГ 3 — РЕГРЕСС от моего же лечения круга 2, плюс четыре мелочи + +Круг 3 подтвердил, что все шесть находок круга 2 закрыты, и нашёл одну новую — самую дорогую в паке, потому что её внесла МОЯ ПРАВКА: + +1. ⛔ **РЕГРЕСС: метка листа подписи терялась ровно у той популяции, ради которой правка делалась.** Лечение круга 2 сменило ключ, которым `terminologist.go` пишет карту `res.BankHolds` (стал ключ посадки, `cf.Src == c.Key`), а `mining.go` продолжал читать её по `c.Src` — СЫРОЙ поверхности кандидата. У banknote-кандидата с `Key != Src` (традиционное написание, регистр, кана) метка `THE BANK ALREADY HOLDS` до листа не доезжала: счётчик и лог верны, требование П3 нарушено молча. Мои же пины этого не ловили: юнит-пин останавливался до `bankStopRows`, а e2e-пин стоит на `方源`, где `Key == Src`. **Вылечено** переводом чтения на `tres.BankHolds[c.Key]` (так же, как рядом читается `Conf`), пин продлён через `bankStopRows` на кандидате `{Key: 赵甲, Src: 趙甲}`, мутация обратно на `c.Src` красна: «the mark must reach the sheet for a candidate whose key differs from its surface: {Src:趙甲 … BankHolds:[]}». +2. **Отчёт §3(а) описывал отменённую версию кода** («сравнение свёрнуто через `NormalizeSourceKey`»). Приведён к коду; греп отозванной фразы → 0 при контроле 1. +3. **Нумерация §5а шла 1..7, 9, 8** — выправлена. +4. **Два абзаца доки `ConsolidationKeyConflicts` носили один заголовок «Cost of the skip»** и читались как дубль, хотя это разные цены. Разведены: «WHAT THE SKIP HIDES» (форма сид-строки по умолчанию) и «WHO RESOLVES A SKIPPED PAIR» (`loadAutoBank` роняет по строке любого статуса). +5. **Варнинг `glossaryRows` обещал молчание обеих проверок**, хотя из `scoreOpts` затрагивается только якорь скоринга, и при стойкой ошибке чтения печатается трижды за роль. Текст сужен до «whatever this call feeds», повторяемость названа. + +⇒ **Урок круга 3, который дороже самой находки:** правка, сделанная ПО находке ревью, — такой же новый код, как всякий другой, и требует своего пина. Мой фикс круга 2 был верен по существу и сломал доставку на этаж выше, потому что я запинила изменённую функцию, а не путь до потребителя. + +#### 4-тер. Находки кругов → что сделано → чем предъявлено + +| находка (круг) | что сделано | чем предъявлено | +|---|---|---| +| «обычный резюм ничего не пишет» ложно (1) | текст исправлен, близнец `export.go` согласован, поставлен пин | мой прогон: вызовов 3→3, скипнутая строка `19:48:13→19:48:14`, три остальные не двинулись | +| механизм резюма назван неверно (1, затем 2) | названы ОБА писателя с условием каждого; вынесено в §5а №9 как свойство места | мутация `recordSkippedStages` красна; мутации двух неверных механизмов переживали | +| «the term would carry TWO renderings» ложно для сид-поверхности (1) | проверка пере-сформулирована как РАЗНОГЛАСИЕ, три механизма разрешения названы | греп отозванной фразы 0 при контроле 1; текст варнинга и e2e-ассерт обновлены | +| сравнение кортежа по сырым `Src` (1) | свёрнуто через `NormalizeSourceKey` — **и это оказалось неверно, см. следующую строку** | — | +| **свёртка ввела ложноотрицание, довод ложен (2)** | сравнение вернулось к сырому (зеркалит ключ хранилища), `consolidatedRows` строит строку с `Src: c.Key`; обе половины запинены | мой замер: `rows stored=2` против контрольного `UNIQUE constraint failed`; обе мутации красны | +| мой комментарий повторял misnomer «SIGNED» (1) | исправлен, цена пропуска названа | греп 0 при контроле 1 | +| `consolidatedRows` не пиннился ничем (1) | добавлен `TestConsolidatedRowsCarryTheWindowAndTheAliases`, усилен на круге 2 | обе мутации красны с верным текстом; до пина переживали весь пакет | +| три дефекта корпусного прибора (1) | классификация симметрична, мёртвая ветка снята, подсадка перебирает строки | те же числа после починки: 142 книги, 13350 строк, подсадка 142/142 | +| два дефекта прибора от линтера (1) | разведено на две функции, `rows.Err()` возвращается | `golangci-lint` → `0 issues.` | +| двойной репорт в одном прогоне (1) | НЕ подавлен, назван в доке как разные состояния термина | мой прогон: collapse=1, merge=1, всего 3 строки WARN | +| пропуск по кортежу молчит на форме сид-строки по умолчанию (2) | не чинится, названо ценой в доке, вынесено в §5а №7 | замер: без `until_ch: 20` → 0 при draft и approved, с ним → 1 | +| тихий ноль `glossaryRows` (2) | ошибка чтения банка логируется | в коде `r.Log.Warn` на ветке ошибки | +| перебор «SIGNED half» (2) | сужено до «подписанные строки на ДОВЕРЕННОМ ключе» | `memory.go` `dispositionFor`/`keyTrusted`, `singleHanKeyFired` | +| стиль: довод трижды, история, план в коде (2) | оставлен один носитель, остальное ссылкой; история и план сняты; чужой замер атрибутирован | греп по каждой снятой формулировке 0 при ненулевом контроле | +| **РЕГРЕСС: метка листа терялась при `Key != Src` (3)** | чтение переведено на `tres.BankHolds[c.Key]`; пин продлён через `bankStopRows` на кандидате `{Key: 赵甲, Src: 趙甲}` | мутация обратно на `c.Src` красна: «the mark must reach the sheet for a candidate whose key differs from its surface» | +| отчёт описывал отменённую версию кода (3) | §3(а) приведён к коду | греп отозванной фразы 0 при контроле 1 | +| нумерация §5а 1..7, 9, 8 (3) | выправлена | `grep -oE '^[0-9]+\.'` → 1 2 3 4 5 6 7 8 9 | +| два абзаца доки под одним заголовком (3) | разведены на «WHAT THE SKIP HIDES» и «WHO RESOLVES A SKIPPED PAIR» | оба факта проверены по коду | +| варнинг `glossaryRows` обещал больше, чем делает (3) | сужен до «whatever this call feeds», повторяемость названа | зовётся и из `scoreOpts`, где затрагивается только якорь | + +#### 5. Таблица мутаций — правая колонка это ТЕКСТ падения, а не факт красноты + +| мутация | текст падения | восстановлено | +|---|---|---| +| `mempostcheck`: `if present{continue}` → `if !present{continue}` | «an OBEYED injection leaves its dst present, so the post-check has nothing to report: confirmed=1» | sha256 совпал | +| гвард полисемии освобождает не-approved пару | «status "draft": the polysemy livelock must be refused whatever the second row's signature» | совпал | +| в сообщение возвращён совет «mark one status: auto» | «the refusal must not offer a STATUS as a remedy — the guard ignores it and so does the wire» (×3) | совпал | +| `ConsolidationKeyConflicts`: правая сторона только approved | «an unsigned row is owed one rendering too: got 0 []» | совпал | +| то же: предложение стреляет только своим `Src` | «a contradiction reached through a shared alias must be reported: []» | совпал | +| то же: снят пропуск по UNIQUE-кортежу | «a proposal on the row's own UNIQUE key replaces it and cannot contradict it: [firing key "元始"…]» | совпал | +| `UnverifiedKeyConflicts`: возврат к approved-only | «want the approved×unsigned pair AND the unsigned×unsigned one, got 1» | совпал | +| то же: из сообщения убраны окна | «must be reported with both statuses» + «a conflict must name its firing key and both windows» | совпал | +| проводка в схлопывании получает пустой банк | «a rendering that disagrees with an UNSIGNED row of the same surface must be counted: {…BankConflicts:0…}» | совпал | +| `consolidatedRows` теряет окно **(до круга 1 ПЕРЕЖИВАЛА)** | «the candidate's window must reach the check: since_ch=0, want 7» | совпал | +| `consolidatedRows` теряет алиасы **(до круга 1 ПЕРЕЖИВАЛА)** | «the identity cluster must reach the check as alias keys: []» | совпал | +| маркер строки банка удалён из рендера листа | «the rendered table must carry the mark:» | совпал | +| обеим находкам дан ОДИН маркер | «the rendered table must carry the mark» | совпал | +| находка подшита в поле само-противоречия | «the contradicting row must be marked on the signature sheet: {…Contradicts:[unsigned draft "方源"→"Странник" [ch 1..20]] BankHolds:[]}» | совпал | +| резюм пере-пиннит каждую обслуженную строку | «an ordinary resume must not write an ok row: ch2/chunk0/edit moved …:07 → …:08» (×3) | совпал | +| сравнение кортежа свёрнуто через `NormalizeSourceKey` **(ПЕРЕЖИВАЛА до круга 2)** | «族长 against a stored 族長 is a pair the store admits, not a replacement: []» | совпал | +| `consolidatedRows` берёт сырую поверхность вместо ключа посадки **(ПЕРЕЖИВАЛА до круга 2)** | «the row must carry the source it will LAND with (the key), got "趙甲"» | совпал | +| `recordSkippedStages` перестаёт пере-записывать скип | «a SKIPPED row is re-derived and re-written by every run past it: ch1/chunk0/edit stayed at …» | совпал | + +⚠ **ЗЕЛЁНОЕ ДО КРУГА 2 → КРАСНОЕ ПОСЛЕ.** Четыре мутации переживали пин, и каждая означала не дыру в пине, а неверное ОПИСАНИЕ или незапиненный фикс. Зелёный пин при ложноотрицании дороже красного, поэтому таблица отдельно: + +| мутация | до круга 2 | после | что это значило | +|---|---|---|---| +| свернуть сравнение кортежа через `NormalizeSourceKey` | **зелено** (весь пакет `ok`) | красно: «族长 against a stored 族長 is a pair the store admits, not a replacement: []» | мой фикс круга 1 не был запинен ВОВСЕ, и сам был неверен | +| `consolidatedRows` берёт сырую поверхность вместо ключа посадки | **зелено** | красно: «the row must carry the source it will LAND with (the key), got "趙甲"» | фикстура пина имела `Src == Key`, поэтому выбор был не нагружен | +| флагнутая ветка `runStageSequence` не пере-записывает скип | **зелено** | заменена на `recordSkippedStages` — красно | механизм в комментарии назван неверно; писателей ДВА | +| fast-path `stagerun.go` обслуживает скипнутые строки | **зелено** | ветка недостижима (у скипнутой строки пуст `ContentHash`) | второй неверный механизм в том же комментарии | + +⚠ Одна мутация ОТБРОШЕНА, а не зачтена: «проводка удалена» не собралась, а сборочная ошибка — не поимка; пере-сделана компилирующейся. + +#### 6. Чем предъявлено — команды и вывод + +- **Батарея после последней правки:** `make battery` → **`MAKE_EXIT=0`**, `ok` пакетов **19** (+4 без тестов), строк `FAIL` **0**, линтер **`0 issues.`**; скипов **4**, все названы батареей: `TestMinerFullBookParity`, `TestCorpusBankKeyConflicts` (мой opt-in прибор — батарея его видит и называет), `TestHelperEventsRun`, `TestHelperKillLoop`. +- **Дифф тестов исполнением** (`git diff -- 'backend/*_test.go' | grep '^[-+]func Test'`): в отслеживаемых файлах добавлено **6**, удалено **0**; `func Fuzz` 0/0. Плюс по **1** в двух новых файлах (`bankconflict_corpus_test.go`, `resume_updatedat_test.go`) → итого **+8 / −0**. +- **Детерминизм:** 200 прогонов на одном входе — различных выводов `UnverifiedKeyConflicts` **1**, `ConsolidationKeyConflicts` **1**; КОНТРОЛЬ — заведомо map-упорядоченная склейка на тех же данных даёт **8**. Плюс `-count=5 -race` по новым тестам. +- **Корпус** (`TM_CORPUS=<репо>/books go test ./internal/pipeline/ -run TestCorpusBankKeyConflicts -v`): `.db walked=162, with a glossary table=161, without one=1, read-failed=0; books with a glossary=142, rows read=13350; ПОПУЛЯЦИЯ: книг хотя бы с одной неподписанной строкой=142, approved=1093 draft=11399 auto=858; книг с ЛЮБЫМ конфликтом=0; только approved×unsigned=0; с парой unsigned×unsigned=0; КОНТРОЛЬ: подсадка ловится на 142 из 142.` + ⇒ **0 книг из 142, и это «не встречается», а не «не смотрели»:** 12 257 из 13 350 строк корпуса неподписанные, а форма столкновения не возникает потому, что все существующие двери роняют дубль по ПОЛНОМУ ключу `(src,sense,since_ch,until_ch)`, а разные окна через них проходят. ⚠ **Старое правило на корпусе тоже даёт 0** — S14 живёт в дереве без единого наблюдённого срабатывания; без этого числа «0» прочтётся как «новая половина бесполезна». +- **§5.3, греп по каждой отозванной формулировке** (включая отозванные по находкам обоих кругов) — **22 из 22 дают 0**, у каждой рядом контрольная фраза из того же файла с ненулевым счётом. + +#### 7. Двигает ли что-нибудь снапшот, хеш запроса, ключ манифеста, версию встроенных данных + +**Нет.** Предъявлено: +- Изменены только `.go`: **16** позиций `git status` по `backend/` (14 M + 2 новых `_test.go`); `configs/` и `internal/lang/data/` — **0**. +- `git status` по всем четырём `testdata/` — пусто; КОНТРОЛЬ: голден-файлов в `internal/pipeline/testdata/golden/` — **6**. Ни один голден не пере-снимался; сайдкар листа подписи голденом не накрыт (`grep -rl 'BANK VERIFICATION TABLE' testdata/` → 0), его стерегут пять живых тестов, все зелёные. +- Пины движения прогнаны поимённо ПОСЛЕ последней правки и зелены: `go test ./internal/pipeline/ -run 'TestGoldenDeterminism|TestCutTagFollowsTheLangpack|TestManifest' -v` → **14 PASS, 0 FAIL**; `go test ./internal/membank/ -run 'TestMemoryVersion|TestMaterializeMemoryVersion' -v` → **5 PASS, 0 FAIL**. +- Новое поле листа подписи дальше сайдкара не течёт: ссылок на `BankHolds` — **12** в **3** файлах (проверка, проводка+рендер, тест); КОНТРОЛЬ — у соседнего `Contradicts`, про которое известно, что дальше листа и лога оно не идёт, тоже **12**. +- Отдельным пином: диагностика НЕ входит в подписной документ (в `signature map` нет ни маркера, ни ярлыка строки банка; КОНТРОЛЬ — тот же файл содержит `青茅山` и `terms:`). +- Обе проверки — только `WarnContext`; ни одна не возвращает ошибку и не меняет содержимого банка. Отказ (drop/stop) НЕ построен (П2). + +#### 8. §5а — «задумано против сделано». НЕ чинил, выношу списком + +1. **`mining.go:643`** `loadAutoBank(signed []store.GlossaryEntry)`, зовётся как `r.loadAutoBank(in.entries)` (`bankmaterialize.go:125`). Обещано именем параметра, докой (`:637`) и сообщением оператору (`:680` «key held by the signed %q→%q»), что предложение движка отбрасывает ПОДПИСЬ. Исполняется: популяция — сид+ruby+дельта любого статуса; неподписанная строка отбрасывает предложение и печатается оператору как «the signed». ⚠ Цену назвал опровергатель круга 1 прогоном (рукописная `draft`-строка на ТОМ ЖЕ кортеже молча съедает выработку движка, и ни одна из трёх проверок не говорит ни слова). **Атрибутирую, а не присваиваю: своим прогоном я этот случай не воспроизвёл** — моя фикстура на `since_ch: 0` дала РАЗНЫЕ кортежи (обе строки легли, обе проверки сработали: `BankConflicts=1`, collapse=1, merge=1), а вторая итерация упёрлась в гейт пере-оплаты, потому что переиспользовала тот же проект. Проверено мной только по коду: `held` в `loadAutoBank` строится из параметра, которым передаётся `in.entries` (`bankmaterialize.go:125`), то есть из строк ЛЮБОГО статуса. +2. **`terminology.go:873` и `:941`** — обе канон-проверки пропускают пару с РАВНЫМИ источниками. Проверочную половину закрыл сегодня; **якорная осталась: роль не показывают, как книга уже зовёт поверхность, которую её просят перевести.** Это меняет содержимое запроса ⇒ хеш ⇒ деньги, в пак не входит. +3. **`terminologist.go:260` против `mining.go:549`** — фильтр «поверхность уже в банке» стоит только на ЭМИССИИ. Роль и классификатор ОПЛАЧИВАЮТСЯ за поверхности, которые банк уже несёт. +4. **`bankmaterialize.go:139-141`** — комментарий на вызове говорит «two DIFFERENT approved terms», тогда как `ApprovedSharedKeyCollisions` собственной докой заявляет, что same-src пары не пропускает. +5. **`mempostcheck.go:91` против `:122`/`:130`** — дока `Shown` говорит «how many unsigned rows were actually PUT IN FRONT of the model here», а sticky-перенос `continue`-ится ДО счётчика. Имя метрики шире её смысла. +6. **`internal/pipeline/bankmaterialize.go:127`** — remark оператору «auto-bank rows dropped: their key is already held by a **signed** term (the signed term wins)». Та же ложь, что в `loadAutoBank`: держателем ключа может быть неподписанная строка, и оператор увидит её названной подписанной. +7. **`internal/membank/memseed.go`, пропуск по UNIQUE-кортежу** — сид-строка с окном ПО УМОЛЧАНИЮ и без sense делит кортеж с banknote-предложением (у обоих `since_ch 0`), поэтому самая частая форма рукописной сид-строки против выработки роли **молчит**, каким бы ни был её статус. Замер круга 2: та же фикстура без `until_ch: 20` → `BankConflicts=0` и при `draft`, и при `approved`; с `until_ch: 20` → 1. На провод это не влияет (предложение отбрасывается, двух форм там нет) — теряется строка на ЛИСТЕ ПОДПИСИ. Закрытие требует правила «заменённая строка движка против отброшенного предложения», а колонка `Source` его не даёт: дельта владельца и авто-банк движка обе читаются `mined`. +8. **Сообщения без пинов** — конкретный носитель `internal/pipeline/seeding.go:75` (текст варнинга S14). Ни один тест его не закрывал (`grep` по прежнему тексту в `*.go` = 0), поэтому расширение правила сделало операторское сообщение ложным при полностью зелёной батарее. Класс шире одного места: пины на ТЕКСТЫ операторских сообщений в зоне не ставятся системно. +9. **`internal/store/chunkstatus.go` вместе с `internal/pipeline/waverun.go` — ПУТЬ ЗАПИСИ СТАТУСА НЕ ЧИТАЕТСЯ ПО КОДУ, и это свойство места, а не невнимательность автора.** Писателей скипнутой строки ДВА, и какой сработает, решает раскладка волны: `recordSkippedStages` (`waverun.go`) — для флагнутой РЕДАКТОРСКОЙ единицы, флагнутая ветка `runStageSequence` (там же) — когда в ОДНОЙ волне две и более стадии. Читающий `chunkstatus.go` не видит ни того, ни другого. Замер: три формулировки механизма подряд оказались неверными (в дереве до пака, в моей первой правке, в моей второй), и каждую поймала только мутация — прочитанная по тексту, а не по цвету. + +#### 9. Правки существующих тестов, вызванные ЗАКАЗАННОЙ сменой поведения (`D39.183`) + +Расширение `UnverifiedKeyConflicts` заказано оркестратором (П1), поэтому два теста, описывавших прежнее поведение, — обслуживание, а не подгонка. Оба объявляю: + +1. **`membank.TestUnverifiedKeyConflicts`** утверждал `len(got) != 1`. Теперь **2**, и второе нашлось В ЕГО ЖЕ ФИКСТУРЕ, лежащей в дереве с написания: `draft «старейшина»` против `auto «глава клана»` — пара неподписанных, которую никто не видел. Прежняя гарантия сохранена дословно; добавлены обе подписи, оба окна и firing-ключ в каждом сообщении, исключение approved×approved с контролем, «пара с двумя ключами — одна поимка». +2. **`pipeline.TestUnsignedConflictIsReportedByTheRun`** утверждал подстроку `"contradicts an approved term"`, которую расширение сделало ложной. Заменён на новый текст правила плюс требование локализовать неисправность: раньше проверялись две подстроки, теперь пять. + +**Куда уехала гарантия:** «прибор видит противоречие с участием подписи» → «прибор видит любое противоречие, кроме двух подписанных, и называет обе строки так, что оператор может их найти». + +Третья правка — `bankfixpack_test.go`, ассерт на текст варнинга схлопывания: текст пере-формулирован по находке круга 1 («DISAGREES with a row the bank already holds» вместо «contradicts…»), потому что прежняя формулировка обещала две формы на проводе там, где эмиссия предложение роняет. + +#### 10. Что НЕ удалось · что не проверено · где не хватило данных + +- **НЕ измерено, производит ли обычная последовательность операций состояние строки 267.** Замерена ДОСТИЖИМОСТЬ и предъявлено, что провода её несут; на живом прогоне не наблюдалось — пак $0. +- **Корпусный ноль — про корпус, не про мир.** 142 книги — почти целиком одна книга в множестве прогонов. +- **Не мерил объём вывода при массовом срабатывании:** маркер листа подписи предъявлен на фикстуре в 3 кандидата. +- **Якорная половина находки §5а №2 не закрыта** (роли не показывают существующую форму) — меняет запрос ⇒ деньги. +- **Двойной репорт не подавлен** — назван и вынесен решением оркестратора. +- **Вопрос §4.1(в) «варнинг → стоп» не решал** (по промту пинг; оркестратор развёл «видеть» и «делать» по уровням и завёл строку бэклога). +- **Чужие зоны:** в дереве параллельно правится `platform/` — не трогал; при лендинге коммитить только `backend/` pathspec-формой. + +#### 11. Что я сам считаю слабым местом + +1. **Два круга самопроверки нашли 18+ находок В МОЕЙ РАБОТЕ, и три из них — ложные утверждения в комментариях, то есть ровно тот дефект, который пак чинил.** Одно (`chunkstatus.go`) я написал неверно ДВАЖДЫ: сначала «резюм не пишет», потом верное «пишет» с НЕВЕРНЫМ механизмом. Второе поймала только мутация. Вывод для приёмки: мои утверждения о поведении надо проверять исполнением так же, как чужие. +2. **`ConsolidationKeyConflicts` и расширенная `UnverifiedKeyConflicts` частично перекрываются**, и это теперь ИЗМЕРЕНО (§4 п.7), а не предположено. Довод в пользу двух мест реальный (разные популяции), но «одно знание — один носитель» соблюдено не идеально. +3. ⛔ **Мой довод про UNIQUE был ЛОЖЕН, и выяснилось это только на круге 2 — прогоном, которого я на круге 1 не сделала.** Я сослалась на схему вместо того, чтобы её спросить; схема ответила иначе (ключ сырой, трад/упрощённая пара — две законные строки, обе инжектятся). Урок шире места: формула «доказано схемой» стояла в моём отчёте там, где честно было «не мерено». +4. **Мой e2e-тест обходил дыру подобранным значением:** `until_ch: 20` уводит сид-строку с кортежа предложения, без него проверка молчит (§5а №7). Это тот же класс, что «тест утверждает не то, что думает», и вынести подобранное окно должна была я, а вынес круг 2. Плюс маркер предъявлен на кандидате, которого в подписном документе НЕТ (`方源` — сид-поверхность): адрес верный (стоп-таблица содержит всех кандидатов), но случай «противоречие у термина, который владелец реально подписывает» отдельным тестом не покрыт. +5. **Комментарий в `waverun.go` цитирует ЧУЖОЙ замер** (1962/2283, строка 321). Не воспроизводил — атрибутировал. +6. **Три из девяти пунктов §5а — про `terminology`/`mining`, куда пак не посылал.** Их количество говорит, что класс «описание шире/уже поведения» в этих файлах не разобран, а я прошёл по касательной. + +#### 11-бис. Дерево на момент сдачи + +`git status --short -- backend/ docs/PROGRESS.md`: + +``` + M backend/cmd/tmctl/render.go + M backend/internal/membank/mempostcheck.go + M backend/internal/membank/mempostcheck_test.go + M backend/internal/membank/memseed.go + M backend/internal/membank/memseed_test.go + M backend/internal/pipeline/bankfixpack_test.go + M backend/internal/pipeline/disposition.go + M backend/internal/pipeline/export.go + M backend/internal/pipeline/mining.go + M backend/internal/pipeline/miningstop_join_test.go + M backend/internal/pipeline/seeding.go + M backend/internal/pipeline/terminologist.go + M backend/internal/pipeline/waverun.go + M backend/internal/store/chunkstatus.go + M docs/PROGRESS.md +?? backend/internal/pipeline/bankconflict_corpus_test.go +?? backend/internal/pipeline/resume_updatedat_test.go +``` + +Индекс пуст (`git diff --cached --name-only` → пусто). Чужого в списке нет: параллельно правится `platform/` — не моя зона, не трогала. **Не коммичу** — лендит оркестратор, pathspec-формой по `backend/` и моей секции журнала. + +Батарея снята ПОСЛЕ последней правки, и это предъявлено, а не заявлено: `find backend -name '*.go' -newer <лог батареи>` → **0**; КОНТРОЛЬ `find … -newer <маркер часовой давности>` → **5**. + +#### 12. Предложения (решает оркестратор, в паке НЕ сделано) + +1. Подавлять ли повторный репорт пары на ре-сиде в авто-режиме (§4 п.7). Резать надо слияние, не схлопывание. +2. Показывать роли существующую форму поверхности (§5а №2, якорная половина) — меняет запрос и деньги. +3. Фильтровать кандидатов роли по банк-поверхностям (§5а №3) — снимает оплату за уже банкованные термины. +4. Ставить пины на ТЕКСТЫ операторских сообщений (§5а №8) — сегодня их нет системно, и расширение правила сделало один из них ложным при полностью зелёной батарее. +5. Довести разногласие до листа подписи и на форме сид-строки по умолчанию (§5а №7). Требует правила «заменённая строка движка против отброшенного предложения»; колонка `Source` его не даёт, потому что дельта владельца и авто-банк движка обе читаются `mined`. Это самая частая форма сид-строки, так что цена молчания не маргинальна. + ⚠ **ПИНГ ОРКЕСТРАТОРА 06.09 — мелочь, у которой не было носителя, и потому она здесь.** `backend/internal/bookfile/model.go:59` разросся до **183 знаков** — единственная строка длиннее 120 в файле; удлинил её текст комментария, заландженный `f1e9226`. Правка на строку, линтер молчит, срочности нет. **Записано потому, что моё решение «поправит зона при следующем касании» само носителя не имело, а следующего касания может не быть годами** — и это ровно тот класс, который смена 06.09 ловила пять раз (находка, живущая только в письме). Возьми при любом касании файла; отдельного захода не стоит. ✅ **ПИНГ ОРКЕСТРАТОРА №22 от 05.09 про якоря — ИСЧЕРПАН В ТОТ ЖЕ ДЕНЬ, работать по нему НЕ НАДО.**