# CODE-REVIEW-553-r3 Issue: #553 · заход r3 · блокирующих циклов израсходовано 2 из 4 Материал: `f601182a908b5b30a302f122c89265cbf933474c` (ветка `issue/553-process-doc-canonicalization`), рабочая копия уже на нём; `git fetch`/`pull`/`checkout` на другой коммит не выполнялись. ## Почему разбор по дельте, а не заново Между r2 (`55db3d49`) и r3 (`f601182a`) нет ребейза и нет смены контракта поведения: `git log origin/dev..HEAD` показывает те же коммиты r2 плюс два новых сверху (`cc664740` — публикация документа CODE-REVIEW-553-r2, `f601182a` — правка теста). Дельта равна ровно `git diff 55db3d49..f601182a`: ``` docs/reviews/CODE-REVIEW-553-r2.md | 197 +++++++++++++++++++++++++++++++++++++ scripts/mutation-gate.mjs | 13 +++++++++++++ test/review-doc-guard.test.mjs | 2 +- 3 files changed, 211 insertions(+), 1 deletion(-) ``` `docs/reviews/CODE-REVIEW-553-r2.md` — публикация предыдущего вердикта, не предмет разбора. Реальная правка — 15 строк в двух файлах, локальная и не задевающая новую подсистему: продолжаю разбор по дельте. ## Закрытие раунда r2 Вердикт r2: жёлтый · High 0 · Medium 1 (в скоупе). Материал r2: `55db3d49866adcc35a231fde5a91692806980c97`. | Находка r2 | Чем закрыта | Где это видно | |---|---|---| | Regression-тест `#553` (`assert.doesNotMatch(process, /ревью[^\n]{0,80}заменяет тестирование/i)`) не пересекает перенос строки — дословный откат абзаца §5 к дореформенной редакции («Код-ревью не пропускается\nникогда — именно оно в этом процессе заменяет тестирование.») проходит мимо теста незамеченным | **Закрыта.** Регэкс заменён на `/ревью[\s\S]{0,120}заменяет тестирование/i` (`test/review-doc-guard.test.mjs`, коммит `f601182a`) — `[\s\S]` пересекает `\n`. Дополнительно в `scripts/mutation-gate.mjs` зарегистрирован постоянный мутант `process-canon-says-review-replaces-testing-across-line-break`, который дословно возвращает старую двухстрочную формулировку и требует, чтобы `node --test --test-name-pattern="#553" test/review-doc-guard.test.mjs` покраснел на этом мутанте — это переводит разовый ручной эксперимент r1/r2 в постоянно исполняемую проверку (нейтрализует нейтральный откат навсегда, а не только для этого захода) | Я лично прогнал `node scripts/mutation-gate.mjs --id=process-canon-says-review-replaces-testing-across-line-break`: `ok чистый прогон` → мутант применён → `ok process-canon-says-review-replaces-testing-across-line-break: заявленный тест покраснел на мутанте` → «поймано 1 из 1». Отдельно проверил, что новый регэкс не матчит текущий (некомпрометированный) `PROCESS.md`: `node -e '...test(process)...'` → `false`. Рабочая копия после эксперимента чиста (`git status --porcelain` пусто) | | Low: цитата в §11.4 («код-ревью не пропускается никогда») больше не дословно совпадает с текстом §5 | Не тронута в этой дельте — было явно на усмотрение автора («можно поправить … или снять как несущественное»), не входит в перечень противоречий issue | `grep -n "не пропускается никогда" PROCESS.md` → строка 1154 не менялась. Остаётся Low, не блокирует, автор вправе не чинить его в этой задаче | ## Унаследовано из r2 Всё, что дельта не задевает, наследуется из CODE-REVIEW-553-r2 (материал `55db3d49866adcc35a231fde5a91692806980c97`) без повторной проверки: - Все 4 названных в issue противоречия устранены текстуально (замена тестирования, архивный spec-file при выходе из `small`, `Python 3.13` в STATUS, точные pins в AGENTS) — проверено построчно в r2, дельта r3 эти строки не трогает (diff r3 ограничен `scripts/mutation-gate.mjs` и одной строкой `test/review-doc-guard.test.mjs`). - Риск-матрица §2.6 и её связь с §2.7 — не задета. - Rule №1, независимость ревьюера, арбитраж владельца, лимит циклов, авторизация релиза — разделы вне diff, не задеты. - Продукт (`src/**`, `custom_components/**/*.py`), changelog, `docs/specs/**`, история `docs/reviews/**` — не изменены. - Отсутствие конфликт-маркеров после ребейза (проверялось в r2 на `55db3d49`, в r3 ребейза не было). ## Скоуп дельты r3 Класс B (`test/review-doc-guard.test.mjs`, `scripts/mutation-gate.mjs`) — тестовая инфраструктура. Продуктовый код не тронут. Изменение состоит из двух коммитов: - `cc664740` — публикация `docs/reviews/CODE-REVIEW-553-r2.md` (артефакт прошлого раунда, не предмет разбора этого раунда). - `f601182a` — целевая правка: расширение регэкса + постоянный мутант. Трейлеры обоих коммитов: `Issue: #553`, `User-Visible: no` — корректно, видимое поведение продукта не меняется, changelog не требуется и не тронут. ## Как проверялось 1. Прочитан диф `git diff 55db3d49..f601182a` целиком (оба файла) и сверен с комментарием автора в issue («Исправление по результатам code review r2», `2026-09-13T12:06:45Z`). 2. Перечитан вердикт r2 (документ `CODE-REVIEW-553-r2.md`, опубликованный тем же деревом) и найдена находка Medium, которую эта дельта обязана закрыть. 3. Прогнан целевой тест: `node --test test/review-doc-guard.test.mjs` — 58/58 (включая изменённый ассерт `#553`). 4. Самостоятельно (не на слово автору) прогнан мутационный свидетель: `node scripts/mutation-gate.mjs --check --id=process-canon-says-review-replaces-testing-across-line-break` → `ok` (патч применим к текущему коду), затем полный прогон без `--check` → «поймано 1 из 1» — тест на мутанте покраснел, как и требуется. Это прямое доказательство «тест умеет падать» для конкретно этой регрессии, а не пересказ авторского «Verified». 5. Прогнан `node --test test/mutation-gate.test.mjs` — 51/51 (реестр мутантов не рассинхронизирован с кодом, новый id учтён корректно). 6. Проверено `git status --porcelain` пусто до и после экспериментов — рабочая копия не осталась замусоренной операциями мутационного гейта (использует временный worktree, не трогает основное дерево). 7. Сверены трейлеры обоих коммитов дельты (`git show -s --format=%B`). 8. Сверено соответствие материала: `git rev-parse HEAD` = `f601182a908b5b30a302f122c89265cbf933474c`, что совпадает с SHA, который автор явно назвал в своём последнем комментарии — расхождения «материал не запушен до метки» (#437) нет. 9. Проверено, что Validate зелёный именно на этом SHA: `gh run view` для запуска `34756229263` → `headSha` = `f601182a908b5b30a302f122c89265cbf933474c`, `conclusion: success`. Отдельно нашёл push-триггернутый прогон на этом же коммите (`34756084583`, тоже `success`) — совпадает со ссылкой в комментарии автора. ## Проверка AC issue (только те, что дельта задевает) | AC (из тела issue) | Статус | Доказательство | |---|---|---| | «Формулировка о замене testing ревью удалена/исправлена; ясный checklist проверяет результат пользователя» | **Да, полностью** (в r2 было «частично» из-за нерабочей регрессозащиты) | Регэкс пересекает перенос строк, что подтверждено проваленным прогоном на мутанте (см. «Как проверялось», п.4); false-positive на текущем корректном тексте исключён (`test(process)` → `false`) | Остальные AC не задеты этой дельтой — наследуются из r2 без повторной проверки (см. «Унаследовано из r2»). ## Находки Нет. High: 0, Medium: 0. Medium-находка r2 закрыта доказательно (не заявлением автора, а независимо воспроизведённым прогоном мутационного гейта). Low-находка r2 (стале-цитата §11.4) остаётся неисправленной, но она и в r2 была прямо оставлена на усмотрение автора и не входит в перечень противоречий issue — не блокирует, не эскалируется. ## Что проверено и корректно - Регэкс-гвард `#553` теперь пересекает перенос строки и не матчит текущий (некомпрометированный) `PROCESS.md`. - Постоянный мутационный свидетель зарегистрирован, применим к текущему коду (`--check` → `ok`) и доказательно ловит целевую регрессию (`поймано 1 из 1`), проверено мной лично, а не на слово. - Реестр мутантов (`test/mutation-gate.test.mjs`) не рассинхронизирован — 51/51. - Трейлеры обоих коммитов дельты корректны, changelog не требовался и не тронут, продукт (`src/**`, `custom_components/**/*.py`) не изменён. - Материал ревью соответствует последнему коммиту, названному автором в issue; Validate зелёный ровно на этом SHA. - Рабочая копия чиста после всех экспериментов. ## Чего не проверял - `npx tsc --noEmit`, `npm test` (полный), `npm run build` со сверкой бандла — не перегонял отдельно: покрыты зелёным Validate на точном SHA `f601182a` (run `34756229263`, `headSha` совпадает, `success`); дельта не содержит TS и не трогает `src/**`. - `node scripts/check-docs.mjs` — не применим, diff не трогает `src/**`. - `npm run invariants -- --config …` — не применим, diff не трогает геометрию/`layout`/`marker.space`/`open_spans`. - `python -m pytest tests_backend -q` — не применим, diff не трогает `custom_components/**/*.py`. - Браузерные смоки (`demo/smoke_*.mjs`) — не применимы, diff не трогает исполняемую поверхность карточки; `scripts/smoke-select.mjs` не запускал — нет продуктового кода в дельте. - `npm run golden:verify` — не применим, diff не меняет рендер/геометрию/ стили. - Полный ночной прогон `mutation-gate.yml` (все мутанты реестра) — не запускал целиком (дорогой, предрелизный/ночной гейт по PROCESS.md); прогнал точечно только новый мутант, который и является предметом этой дельты, плюс дешёвую проверку реестра (`test/mutation-gate.test.mjs`). ## Вывод Дельта r3 доказательно закрывает Medium-находку r2: регрессозащита теперь переживает перенос строки и подтверждена постоянно исполняемым мутационным свидетелем, а не разовым экспериментом ревьюера. Новых находок нет. High: 0, Medium: 0 (в задаче). Вердикт — зелёный. --- --- ## Материал раунда - Ветка: `issue/553-process-doc-canonicalization`, коммит `f601182a908b` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `c9ea196bdd7b6be335ade3ac9e8c3626245cc9c2` ``` git log --all --format='%H %T' | grep c9ea196bdd7b ``` - Тело issue: `df64e92bada59856e24d29c886d58c24ba0907508dc476bd206b0e3b1466034d` - Вердикт конвейера: `green` · High 0