# CODE-REVIEW-632-r3 Issue: #632 · этап: код-ревью · заход r3 · блокирующих циклов израсходовано 1 из 4 Трек: инфраструктурный (ускоренный вход, AGENTS.md §1 / PROCESS.md §1) — весь дифф класса B, ни одного файла класса A (подтверждено ниже). Материал: `HEAD` = `14396ed0b186dc9b883f0b26e460458828dcb58c` («docs(reviews): индекс после сдвига каталога (#632)»), рабочая копия уже на нём. `git log --oneline origin/dev..HEAD`: 5 коммитов; `git diff origin/dev...HEAD --stat`: ``` docs/reviews/CODE-REVIEW-632-r1.md | 231 ++++++++++++++ docs/reviews/CODE-REVIEW-632-r2.md | 202 ++++++++++++++ docs/reviews/INDEX.md | 4 +- scripts/mutation-registry.mjs | 44 +++++++ scripts/task-packet.mjs | 51 ++++++- test/task-packet.test.mjs | 69 ++++++- ``` ## Почему это r3, а не продолжение зелёного r2 — реконструкция по issue и логам r2 (`docs/reviews/CODE-REVIEW-632-r2.md`, материал `b64bc932`) уже вынес **зелёный** вердикт: High 0, Medium 0. Зелёный цикла не образует и должен двигать задачу вперёд (§4). То, что ревью назначено снова, — не сигнал о находке, а следствие события после вердикта; разобрал по комментариям issue и логам CI, а не со слов автора: 1. Пока шло ревью r2, `dev` продвинулся на 2 коммита; автор перебазировал ветку на `dev@c8f9b5d3` → кандидат `1e866291`. 2. Validate на `1e866291` красный (run `35949058000`, `conclusion: failure`, проверено `gh run view`). Разобрал сам, какой именно job/шаг упал: ``` $ gh run view 35949058000 --json headSha,conclusion,status,jobs headSha 1e86629189ddf61eacae82fac281b7a13915611a conclusion failure Предполёт: документация, провенанс, процесс failure Фронтенд: типы, юниты, мутанты, синхрон бандла success Мутанты по диффу (1..6/6) success (все шесть шардов) ``` Упал только предполётный job; **все** мутантные шарды и фронтенд-гейт — зелёные на этом кандидате. Внутри упавшего job — конкретно шаг `workflow_sync`: ``` $ gh run view 35949058000 --log-failed | grep -i "process.yml\|различаются" РАСХОЖДЕНИЕ: process.yml в main и dev различаются. mutation-gate.yml: main и dev идентичны process-resume.yml: main и dev идентичны FAIL process.yml и mutation-gate.yml в main и dev (failure) ``` Это проверка «`.github/workflows/process.yml` в `main` совпадает с `dev`» — репозиторного уровня инвариант, никак не зависящий от дерева ветки #632 (её диф вообще не трогает `.github/workflows/**`, см. список файлов выше). Причина расхождения — независимая правка #634 в `main`, не отражённая в зеркале; сам автор её так и определил в issue. 3. Автор зеркалировал `main` (`main = 7752329d`) и **переподписал вершину без изменения дерева**: `14396ed0` имеет то же дерево, что и кандидат до переподписания (дерево `0d959028`, по словам автора) — я это не принимаю на слово, а проверяю ниже блобами по каждому файлу. Итог: r3 — не ревью новой правки, а подтверждение, что дерево, получившее зелёный вердикт в r2, не изменилось, и что сорвавшийся кандидат упал по причине, не относящейся к #632. Разбираю дельту, а не задачу целиком (PROCESS.md §2.10) — она пуста по коду. ## Тождество дерева r2 → r3 (проверено блобами, не заявлением) `docs/reviews/CODE-REVIEW-632-r2.md` называет SHA материала `b64bc932`, который в этом дереве не резолвится (ожидаемо — ребейз, тот же паттерн, что уже был у r1→r2). Материал восстановлен не по SHA, а по содержимому: ``` $ git ls-tree HEAD -- scripts/task-packet.mjs test/task-packet.test.mjs scripts/mutation-registry.mjs 100644 blob f07f56b64e7b92b67b32654481b28e718788de6e scripts/mutation-registry.mjs 100644 blob c7bfebd2904fca3360b11eacedf9343d768e7915 scripts/task-packet.mjs 100644 blob 15b9c04786b6733c8564c3173f700f374cc5b22d test/task-packet.test.mjs ``` `scripts/task-packet.mjs` (`c7bfebd2…`) и `test/task-packet.test.mjs` (`15b9c047…`) — **точное совпадение** блобов, названных в r2-документе как материал ревью. Это не эквивалент по содержимому, это тот же самый файл байт в байт: решающая логика и тесты, за которые r2 выдал зелёный вердикт, не изменились ни на символ. `scripts/mutation-registry.mjs` в r2-документе назван другим блобом (`e48ddb98…`, тоже мёртвый), текущий — `f07f56b6…`. Как и в r1→r2 (там же объяснялось: между ребейзами `dev` получает чужие правки того же файла от других issue), проверил, что содержимое трёх/четырёх мутантов #632 внутри не пострадало — не по аналогии с прошлым разом, а прогоном на этом SHA: ``` $ node --test test/task-packet.test.mjs # tests 14, pass 14, fail 0 $ node scripts/mutation-gate.mjs --check | grep task-packet ok task-packet-product-flow-overrides-diff ok task-packet-review-docs-not-material ok task-packet-s6-s7-alone-not-product-flow ok task-packet-trivial-is-product-flow $ node scripts/mutation-gate.mjs --id=task-packet-trivial-is-product-flow ok чистый прогон: node --test test/task-packet.test.mjs ok task-packet-trivial-is-product-flow: заявленный тест покраснел на мутанте поймано 1 из 1 $ git status --porcelain # после гейтов — пусто ``` Все четыре мутанта задачи (включая `trivial-is-product-flow`, закрывший Medium из r1) на месте и красят ровно то, что заявлено — защитное свойство доказано исполнением на текущем SHA, не унаследовано голословно. ## Закрытие раунда r2 r2 не оставил находок (High 0, Medium 0) — таблицы «находка → чем закрыта» формально нет. Единственное, что произошло между вердиктом r2 и этим раундом, — событие вне дерева задачи (провал предполётного `workflow_sync` на промежуточном кандидате `1e866291` из-за расхождения `process.yml` между `main`/`dev`, вызванного другой задачей #634). Проверил, что расхождение устранено и не является скрытой находкой против #632: ``` $ git fetch origin main dev --quiet # только чтение ссылок, HEAD/рабочая копия не менялись $ git diff origin/main origin/dev -- .github/workflows/process.yml (пусто) ``` `process.yml` в `main` и `dev` теперь идентичны — зеркало восстановлено, причина красного дispatch снята. `git rev-parse HEAD` после `fetch` — та же `14396ed0…`, рабочая копия не трогалась (см. правило §2.7/#312 — не уходить с материала ревью; `fetch` не меняет ни HEAD, ни индекс, только обновляет удалённые ссылки для чтения). ## Унаследовано из r2 (и транзитивно из r1) Дельта по коду между r2 и r3 пуста (см. блобы выше), поэтому решающая логика повторно вручную не переразбиралась — но защитные тесты и мутанты перепрогнаны на этом SHA заново (не просто приняты на слово): - **AC1** (продуктовая S6-задача с ТЗ/ревью ТЗ сохраняет право класса A) — документ `docs/reviews/CODE-REVIEW-632-r1.md`, мутант `task-packet-product-flow-overrides-diff` — ok на этом SHA (выше). - **AC2** (`docs/reviews/**` не влияет на классификацию ветки) — тот же документ, мутант `task-packet-review-docs-not-material` — ok на этом SHA. - **AC3** (настоящая инфраструктурная задача без ТЗ сохраняет запрет) — тот же документ, мутант `task-packet-s6-s7-alone-not-product-flow` — ok на этом SHA. - **AC4** (каждый признак самодостаточен; `## ТЗшка` не считается разделом ТЗ) — регэксп `(?![\p{L}\p{N}_])` не менялся (блоб `task-packet.mjs` идентичен r2); тест на эту границу входит в те же 14 зелёных. - **Medium r1** (`trivial`-трек не входил в признаки продуктового потока) — закрыт в r1-фиксе, документ `docs/reviews/CODE-REVIEW-632-r2.md`, мутант `task-packet-trivial-is-product-flow` — ok на этом SHA (перепрогнан выше, «поймано 1 из 1», не только унаследован). - Трейлеры и класс файлов — сверены заново на этом SHA (раздел ниже), не унаследованы. ## Находки Нет. High: 0. Medium: 0. Дерево, за которое r2 выдал зелёный вердикт, не изменилось (байт в байт по двум решающим файлам); дельта — это только публикация документов ревью и восстановление зеркала `process.yml`, вызвавшего посторонний красный dispatch. Красный кандидат `1e866291` не является находкой против #632: его причина полностью внешняя (расхождение `main`/`dev` от другой задачи), подтверждена логами CI, а не заявлением автора, и уже устранена. ## Что проверено и корректно - Трейлеры коммитов: `1fbf7704` и `10d32312` несут `Issue: #632`, `User-Visible: no` — корректно, продуктового поведения нет (diff не трогает `src/**`), changelog не требуется. ``` $ git log --format='%H %s%n%b' origin/dev..HEAD -- scripts/task-packet.mjs test/task-packet.test.mjs scripts/mutation-registry.mjs ``` оба коммита несут оба трейлера. - Класс файлов: `git diff origin/dev...HEAD --name-only` — шесть файлов, все `docs/**`, `scripts/**`, `test/**`; ни одного файла класса A. Инфраструктурный трек (ускоренный вход) применён корректно. - `docs/reviews/INDEX.md` свеж: `node scripts/reviews-index.mjs --check` → «свеж», rc 0; счётчики «Документов: 1021, issue: 361» и строка `#632 r2 · 🟢 · 0 · 0` сгенерированы машиной, не вписаны руками — одно число, один источник (генератор), сверено прогоном, а не чтением. - Публикация `docs/reviews/CODE-REVIEW-632-r2.md` дословно соответствует вердикту, вынесенному в комментарии issue 2026-09-24T02:51:34Z (`Вердикт: зелёный · заход r2 · … High: 0 · Medium: 0`) — сверено `gh issue view`. - Validate зелёный на точном материале ревью: ``` $ gh run view 35957716904 --json headSha,conclusion headSha 14396ed0b186dc9b883f0b26e460458828dcb58c conclusion success ``` SHA совпадает с `git rev-parse HEAD` — дешёвые гейты (`tsc`, `npm test`, `npm run build`+сверка бандла) подтверждены этим прогоном, повторно не гонял. - Промежуточный красный `1e866291` разобран логами CI (раздел выше) — его единственная причина, `workflow_sync`/`process.yml`, не пересекается с диффом #632 и уже устранена. ## Чего не проверял - `npx tsc --noEmit` / `npm test` (полный) / `npm run build` со сверкой бандла — не перегонял отдельно: зелёный Validate есть на точном SHA `14396ed0` (ссылка выше), дифф не касается `src/**`. Точечно код и тесты задачи (`node --test test/task-packet.test.mjs`, мутанты) перепрогнал сам — см. выше. - `node scripts/check-docs.mjs` — не применим, диф не трогает `src/**`. - `npm run golden:verify`, браузерные смоки, `python -m pytest tests_backend`, `npm run invariants` — не применимы: диф не меняет рендер, геометрию, `custom_components/**/*.py` не тронут; `node scripts/smoke-select.mjs` для этого диффа не запускал повторно — вывод «исполняемого frontend-диффа нет» дан в r2 на дельте, которая с тех пор не менялась (блобы идентичны), новых причин для другого ответа нет. - Живой `node scripts/task-packet.mjs --issue 612` с реальным `gh` — не запускал (`gh` в CI-раннере ревьюера доступен только для чтения issue и run-логов через уже настроенный токен, но полный сценарий с реальным API GitHub для входных данных скрипта не воспроизводил); эквивалентно проверено на уровне `buildPacket()`/тестов в r1/r2, логика сборки входов в этом раунде не менялась вовсе (блобы идентичны). - Полный лог упавшего `1e866291` целиком (все job) не читал — только сводку `jobs[].conclusion` и grep по причине упавшего шага; этого достаточно, чтобы отделить «эта причина не в скоупе #632» от «эта причина маскирует находку», поскольку сам факт и текст ошибки (`process.yml в main и dev различаются`) не пересекается с файлами #632. ## Материал раунда ``` tree (14396ed0) — рабочая копия blob c7bfebd2904fca3360b11eacedf9343d768e7915 scripts/task-packet.mjs blob 15b9c04786b6733c8564c3173f700f374cc5b22d test/task-packet.test.mjs blob f07f56b64e7b92b67b32654481b28e718788de6e scripts/mutation-registry.mjs SHA материала ревью: 14396ed0b186dc9b883f0b26e460458828dcb58c Предыдущий материал (r2, дельта от): b64bc932 (мёртв после ребейза; task-packet.mjs/test — те же блобы, см. выше; мутация-реестр перепроверен исполнением, не по блобу) ``` ## Вердикт **Зелёный.** Дерево, за которое r2 уже выдал зелёный вердикт (High 0, Medium 0), не изменилось: `scripts/task-packet.mjs` и `test/task-packet.test.mjs` байт в байт совпадают с материалом r2; все четыре мутанта задачи и весь тестовый файл перепрогнаны на текущем SHA и зелёные. Красный промежуточный кандидат `1e866291` разобран по логам CI — причина полностью внешняя к #632 (расхождение `process.yml` между `main` и `dev`, вызванное другой задачей #634), устранена, дерево при переподписании не менялось. Трейлеры, класс файлов, свежесть индекса — в порядке. Новых находок нет. High: 0. Medium: 0. Задача готова к пред-релизной очереди. --- ## Материал раунда - Ветка: `issue/632-task-packet-track`, коммит `14396ed0b186` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `0d959028d35b205d556d2bf81ab5e2571128963e` ``` git log --all --format='%H %T' | grep 0d959028d35b ``` - Тело issue: `85577dba6802882fd2cb7cc293044cd8318bc67981c0f743e27b5e77294bdece` - Вердикт конвейера: `green` · High 0