# SPEC-REVIEW-481-r2 Issue: #481 «changed_mutants: журнал пойманных свидетелей вместо суда по диапазону» Этап: ТЗ на ревью (PROCESS.md §2.4). Трек: `small` — ТЗ в теле issue, файл `docs/specs/481-*.md` не создан (проверено: `ls docs/specs/ | grep -i 481` — пусто). Заход: r2 · блокирующих циклов израсходовано 1 из 2 (r1 был жёлтым и потратил цикл; зелёный вердикт цикла не образует, #227). ## Скоуп Предмет ревью — тело issue #481 на текущий момент, целиком, но разбор — по дельте между r1 и r2 (PROCESS.md §2.10): полностью переразобраны только два места, которых касалась правка автора; остальное (AC1–AC5, AC7, ссылки, оценка, список файлов) унаследовано из r1 без повторной проверки по существу, с точечной сверкой фактов, которые дёшево перепроверить (см. «Унаследовано из r1»). ## Как проверялось 1. Найден вердикт r1 — комментарий в issue (жёлтый, High: 0, Medium: 2) и документ `docs/reviews/SPEC-REVIEW-481-r1.md` (коммит `2f65a184`, `git show 2f65a184:docs/reviews/SPEC-REVIEW-481-r1.md`). Блок «Материал раунда» в конце r1-документа несёт **пустой SHA ветки** («Ветка: dev, коммит `` — ... Якоря снять не удалось: ветки задачи нет, материал читался по dev»). По PROCESS.md §2.10 это прямо названо нормальным случаем, а не находкой: «если SHA не резолвится — это не находка, а обычное дело»; находкой было бы только SHA, мёртвое уже на момент публикации — здесь SHA просто не указано, а не указано неверно. Дельту поэтому объявляю не по коду, а по тексту: r1-документ дословно цитирует то, что было в теле issue на момент r1 (формулировки находок 1 и 2 ниже), и комментарий автора после r1 прямо перечисляет, что изменено. Оба источника сверены с текущим телом issue построчно. 2. Дельта = добавленный `### 6. Откат` внутри «Решение» + переписанная колонка «Доказательство» строки AC6 в таблице критериев. Больше правок в теле issue между r1 и r2 нет (сверено построчно с текстом, процитированным в r1-документе и с комментарием автора). 3. По каждой из двух Medium-находок r1 — сверено дословное совпадение предложенного в r1 исправления с тем, что появилось в issue (см. таблицу ниже). 4. AC1–AC5, AC7 дельта не затрагивает — не переразбирались по существу. Дёшево сверено: файл `docs/specs/` по-прежнему отсутствует; названные в AC7 идентификаторы свидетелей (`ledger-records-escaped`, `ledger-version-sensitive`, `ledger-written-at-end-only`) и тесты `#481 AC1`…`#481 AC5` по-прежнему существуют в коде ветки (`grep` по `scripts/mutation-gate.mjs`, `test/mutation-gate.test.mjs`, `test/validate-workflow.test.mjs`) — спецификация не разошлась с уже существующей реализацией на ветке задачи. Это не код-ревью (реализация по существу не оценивалась), а та же перекрёстная сверка реализуемости, что делал r1. 5. Числа в комментарии автора после r1 (132/134 отобрано, 132 пропущено, 113/113 без изменения отпечатка) — не доказательство (AC6 явно это оговаривает), поэтому не перепроверялись побитово; но сами исторические SHA, на которые они ссылаются, проверены на существование и соответствие описанию: `052549fa` — «test: accept golden baselines after #471 and #476», `f2e38e48` — «build: prepare v1.73.0-beta.2 candidate», `5d281171` — «ci: shard changed mutants before release», `04f4cd10` — «docs: refresh screenshot source fingerprint». Все существуют, ни один не выдуман, описание соответствует содержимому коммитов. 6. Гейты (typecheck/test/build) не гонялись — по прецеденту r1 и по существу этапа: материал spec-review — текст ТЗ, а не диапазон кода; их прогон принадлежит код-ревью (§2.7). ## Закрытие раунда r1 | Находка r1 | Чем закрыта | Где видно | |---|---|---| | Medium 1: отсутствует обязательный раздел «откат» (шаблон §5) | Добавлен `### 6. Откат` внутри «Решение», текст почти дословно повторяет формулировку, предложенную r1 («ревёрт коммита… `readLedger` трактует отсутствующий, битый или чужой по схеме файл как пустой журнал… старое поведение восстанавливается без ручных действий») | Тело issue #481, раздел «Решение» → «### 6. Откат» | | Medium 2: AC6 доказывается способом вне таксономии §2.5 («замер при реализации, числа в issue» — запрещено правилом 18) | Колонка «Доказательство» строки AC6 переписана на «ревью кода (иллюстративные числа — комментарий автора в issue, не доказательство)» — выбран путь (b) из двух предложенных r1; критерий AC6 сформулирован как структурное утверждение, проверяемое чтением кода (отбор по диффу не изменён, сужение только через `splitByLedger`, версия-инвариантность — следствие AC1), а не как конкретное число мутантов | Тело issue #481, таблица «Критерии приёмки», строка AC6 | | Low 3: влияние на perf/touch не названо явно словом «нет» (снято r1 без правки, с запиской) | Закрыто попутно тем же новым разделом: «Влияние на perf/touch/UX — нет: затронуты только скрипты, workflow и тесты» | Тело issue #481, конец «### 6. Откат» | ## Унаследовано из r1 Документ: `docs/reviews/SPEC-REVIEW-481-r1.md` (коммит `2f65a184`; материал раунда — `dev` на момент r1, SHA ветки в документе не резолвлен — по §2.10 это нормальный случай, не находка). Принято без повторной проверки по существу: - AC1–AC5 и AC7 однозначны, проверяемы, попадают в разрешённую таксономию доказательств (unit / контрактный тест воркфлоу / отрицательные прогоны свидетелей); на ветке задачи для них есть реальные автотесты, различающие правильное и сломанное поведение. - Ссылки на #475, #480, #472, #388 точны и соответствуют описанию автора. - Технические факты не домысел: `guardFiles()` экспортирована, `visualFingerprint` и нормализация версии существуют в `scripts/source-fingerprint.mjs`, структура `MUTANT_DEFINITIONS` соответствует описанию. - Раздел «Что не меняется» корректно ограничивает скоуп (полный прогон, `--check`, `--id` журнал не читают). - `actions/cache/save … if: always()` — осознанное обоснованное отступление от соседнего success-only паттерна `performance_smoke`. - Трек `small` обоснован: критерии §5 выполняются одновременно; файла `docs/specs/481-*.md` нет. - «Затронутые файлы» — верно, всё класса B/C, ни одного класса A; подтверждено дополнительно и в этом раунде: `git diff --stat` с момента r1-документа (коммит `2f65a184`) по текущий HEAD `2c50e736` трогает ровно перечисленные файлы (`scripts/mutation-gate.mjs`, `scripts/source-fingerprint.mjs`, `.github/workflows/validate.yml`, `test/mutation-gate.test.mjs`, `test/validate-workflow.test.mjs`, `docs/TESTING.md`) плюс служебный `docs/images/screenshots.json` (отпечаток скриншотов, механика докс-гейта, не продуктовый файл) — класс A по-прежнему не задет. ## Находки нового раунда Нет. High: 0, Medium: 0. ## Что проверено и корректно (в этом раунде) - Текст нового раздела «Откат» точно соответствует поведению `readLedger`, независимо подтверждённому ещё в r1 чтением кода ветки: отсутствующий, битый или несовпадающий по схеме файл трактуется как пустой журнал. - AC6 больше не привязан к нефальсифицируемому числу и не требует ручного «замера»: формулировка проверяема чтением кода, а поле доказательства явно в разрешённой таксономии §2.5 и не нарушает правило 18 (числа помечены как иллюстрация, не доказательство). - Названные в комментарии автора исторические SHA (`052549fa`, `f2e38e48`, `5d281171`, `04f4cd10`) существуют и соответствуют описанным событиям (релизный кандидат, шардирование, докс-коммит) — иллюстрация не построена на выдуманных ссылках, хотя сама она и не является доказательством AC6. - Открытых продуктовых вопросов в тексте нет; догадок, выданных за решённое поведение, не найдено — как в дельте, так и в унаследованной части (перепроверено r1, не оспаривается). ## Чего не проверял - Гейты (`tsc --noEmit`, `npm test`, `npm run build`) не гонялись: на этапе spec-review материал — текст ТЗ, а не диапазон кода; тот же выбор сделал r1 для этой же задачи по той же причине. `node scripts/check-docs.mjs` не запускался по той же причине — этот этап не проверяет состояние `src/**`. - Не пересчитывал побитово иллюстративные числа из комментария автора (132 из 134 отобранных пропущено по журналу; 113 из 113 без изменения отпечатка на бампе версии) — они явно помечены как не-доказательство, реальный прогон на историческом диапазоне и фиксация команды/результата — задача код-ревью (вариант (b) из находки r1 №2 прямо поручает это код-ревьюеру). - Не переоценивал по существу реализацию на ветке `issue/481-mutation-ledger` — это предмет код-ревью (§2.7), не этого этапа; использовал код только как перекрёстную проверку (не домысел ли текст ТЗ), как и в r1. ## Вердикт Зелёный. High: 0, Medium: 0. Обе Medium-находки r1 закрыты текстом issue дословно по предложенным вариантам исправления, Low закрыт попутно. Статус — «Готово к разработке». --- Материал раунда: тело issue #481 на момент r2; предыдущий документ — `docs/reviews/SPEC-REVIEW-481-r1.md` (коммит `2f65a184`); HEAD ветки на момент этого разбора — `2c50e736` (detached, `origin/issue/481-mutation-ledger`). --- ## Материал раунда - Ветка: `issue/481-mutation-ledger`, коммит `2c50e7367100` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `3d4c141fe31c1bad1f12948956a3246c678eec0c` ``` git log --all --format='%H %T' | grep 3d4c141fe31c ```