From 1aa52d21071705574c6cccb1f19ccc3a8d5b9083 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 21:21:31 +0000 Subject: [PATCH] docs: review document for #700 Issue: #700 User-Visible: no --- docs/reviews/CODE-REVIEW-700-r2.md | 148 +++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 docs/reviews/CODE-REVIEW-700-r2.md diff --git a/docs/reviews/CODE-REVIEW-700-r2.md b/docs/reviews/CODE-REVIEW-700-r2.md new file mode 100644 index 00000000..7837f1d8 --- /dev/null +++ b/docs/reviews/CODE-REVIEW-700-r2.md @@ -0,0 +1,148 @@ +# CODE-REVIEW-700-r2 + +Issue: #700 · Предполёт не красит ветку задачи чужими причинами: зеркало workflow и внешние ссылки +Этап: code · Заход: r2 · Трек: show · Материал: `ed3d63e20db24fe9172c83f7ea2e824c90444dcc` +(коммит `ed3d63e2` поверх материала r1 `89d57e054cdf940d3ead83ec509075164118ee3b`, +который сам лежит поверх `origin/dev` @ `c716bb0f63104f5afb9d660bc058e095256d433d`; +на момент этого раунда `origin/dev` ушёл на 8 коммитов вперёд, слияние без +конфликта — по треку `show` ветка к `dev` не приводится, материал — ветка как есть). + +## Скоуп раунда + +Единственная цель r2 — закрыть единственную находку r1 (Medium, в скоупе): +`gh issue list … || true` в шаге «Расхождение зеркала на dev — issue +владельцу» (`.github/workflows/validate.yml`) глушил сбой чтения списка +открытых issue в пустую строку и проваливался в `gh issue create`, заводя +дубликат `[workflow-sync]` даже когда нужное issue уже открыто, но недоступно +для чтения (сеть, рейт-лимит). + +Дельта r1→r2 — ровно один коммит `ed3d63e2`, три файла: + +``` +.github/workflows/validate.yml | 9 +++++++-- +scripts/mutation-registry.mjs | 11 +++++++++++ +test/validate-workflow.test.mjs | 3 +++ +``` + +Дельта локальна и пропорциональна находке (одна правка на одну строку внутри +уже одобренного шага): AC, которые она задевает, — только тот же самый AC про +«одно issue, не дубликат» из тела #700 и контракта PROCESS.md §10.4. Остальной +диапазон (`check-docs.mjs`, `PROCESS.md`, `test/classify-changes.test.mjs`, +`test/docs-freshness.test.mjs`) не тронут этим коммитом — соответствующая +часть r1 наследуется без повторной проверки (раздел ниже). + +## Как проверялось + +Дешёвые гейты подтверждены зелёным Validate на этом же SHA `ed3d63e2` +(workflow_dispatch, run `36484635253`, `conclusion: success`, ссылка дана в +задаче ревью) — `npx tsc --noEmit`, `npm test`, `npm run build` + +`bundle-policy --verify` повторно не гонял. + +Дополнительно к зелёному Validate прогнал сам: + +| Гейт | Результат | +|---|---| +| `node --test test/validate-workflow.test.mjs` | 27/27 зелёных, включая изменённый `#700: на ветке задачи…` | +| `node --test test/classify-changes.test.mjs` | 21/21 зелёных (не тронут этим коммитом, для очистки дельты) | +| `node --test test/mutation-gate.test.mjs` | 69/69 зелёных, структура нового мутанта `workflow-sync-issue-duplicated-on-read-failure` валидна | +| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | «Исполняемого frontend-диффа нет» — src/**/*.ts не тронут, браузерные смоки нечего выбирать | +| Мутация вручную из реестра: `exit 0` → `:` в новом `if`-блоке (patch `workflow-sync-issue-duplicated-on-read-failure`), тест `--test-name-pattern="#700: на ветке задачи"` | падает (1 fail, `assert.doesNotMatch` на `|| true)` и/или `assert.match` на новый `if !`-блок), файл восстановлен из бэкапа, `git status` чист | + +Живое воспроизведение реального сбоя `gh issue list` на CI не запускал (это +живой internal API-вызов на push в `dev`, вне доступа из этой сессии, и это +разрушительное действие, если сорвётся не так, как задумано) — как и в r1, +логика закрыта юнит-тестом плюс ручной мутацией guard-команды выше. + +## Находки + +Нет. Правка r2 точна: устраняет ровно описанный в r1 путь, не расширяет и не +сужает ничего другого в шаге. + +## Закрытие раунда r1 + +| Находка r1 | Чем закрыта | Где это видно | +|---|---|---| +| Medium: `gh issue list … \|\| true` — сбой чтения списка открытых issue проваливается в `gh issue create`, заводя дубликат `[workflow-sync]` вместо предупреждения | `\|\| true` убран; чтение обёрнуто в `if ! existing=$(gh issue list …); then echo "::warning::…"; exit 0; fi` — при ненулевом коде `gh issue list` шаг предупреждает и выходит, не доходя ни до `gh issue comment`, ни до `gh issue create` | `.github/workflows/validate.yml` (коммит `ed3d63e2`, блок шага «Расхождение зеркала на dev — issue владельцу»); поведение зафиксировано тестом `test/validate-workflow.test.mjs:628-633` (два новых `assert`: на форму `if !…then…exit 0…fi` и на отсутствие `\|\| true)` до `gh issue create`) и мутантом `workflow-sync-issue-duplicated-on-read-failure` в `scripts/mutation-registry.mjs`, который я применил вручную — тест краснеет на мутированном коде и зеленеет на исходном (таблица гейтов выше) | + +## Унаследовано из r1 + +Принято без повторной проверки в этом раунде — документ и материал: +`docs/reviews/CODE-REVIEW-700-r1.md` (в дереве `545be665`), материал +`89d57e054cdf940d3ead83ec509075164118ee3b`, дерево материала `945dad882326…`: + +- Периметр `task_branch=true/false` не сузился: `check`/`advise` для + workflow_sync и `--external`/без `warn` для внешних ссылок остаются красными + для push в `dev` и для кандидата беты/релиза; предупреждением становится + только ветка задачи. +- `validate.yml` не входит в список из шести тонких зеркалируемых файлов — + этому диффу зеркалирование в `main` не требуется. +- `check-docs.mjs`: `EXTERNAL_WARN` корректно разводит `warnings`/`errors`, + выход по `errors.length` не тронут. +- Трейлеры коммита r1 (`Issue: #700`, `User-Visible: no`) — корректны, диффу + класса A/D нет. +- `PROCESS.md` §10.4 дополнен точно тем, что реализовано в r1. +- Оба мутанта r1 (`task-branch-workflow-sync-red-again`, + `external-link-warn-mode-ignored`) — краснеют на мутации, зеленеют на + исходнике (проверено в r1 вручную). +- `actionlint` не перепроверялся (недоступен локально и в r1, и сейчас); + косвенно — YAML реально исполнился на CI (run r1 `36482200722` и run r2 + `36484635253`) без ошибки парсинга. + +## Что проверено и корректно (r2, сверх наследования) + +- Новый `if`-блок синтаксически и семантически корректен для `bash -eo + pipefail` (шелл GitHub Actions по умолчанию): присваивание внутри условия + `if !` не триггерит `set -e`, а код возврата, который проверяется, — код + возврата `gh issue list` (json/jq считает сам `gh`, не отдельный процесс в + пайпе), так что сетевой сбой или рейт-лимит действительно попадает в ветку + `if`, а не проскакивает мимо неё. +- Ветка «не прочитано → предупреждение и выход» физически предшествует и + `gh issue comment`, и `gh issue create` — при сбое чтения оба этих вызова + теперь не выполняются вообще, то есть дубликат исключён, а не просто + переименован в другую ошибку. +- Комментарий в коде (`# Список не прочитан — …`) объясняет неочевидную + причину (почему это не «оставить || true»), а не пересказывает код — по + стилю совпадает с уже принятыми в r1 комментариями этого же шага. +- Новый мутант в реестре (`workflow-sync-issue-duplicated-on-read-failure`) + минимален и целится ровно в закрытый путь (`exit 0` → `:`, единственная + правка, из-за которой шаг снова провалился бы в `if [ -n "$existing" ]` + с пустым `$existing`). +- Изменение не расширяет и не меняет скоуп задачи: файлов класса A/D нет, + видимое поведение карточки не меняется, `docs/USER-GUIDE.ru.md` не + требует правок. +- Одно число, один источник (§8): числовых значений, видимых пользователю + карточки, дифф r2 не вводит. + +## Чего не проверял + +- `mutation-gate --check` (полный прогон, а не структура) и весь `npm run + gate:small` — дешёвые гейты уже подтверждены зелёным Validate на этом SHA, + трек `show` не запрашивает мутанты по диффу целиком (#696). +- Живое исполнение шага «Расхождение зеркала на dev — issue владельцу» с + реально упавшим `gh issue list` на push в `dev` — не воспроизводил (нужен + реальный сбой GitHub API или подмена `gh` внутри Actions-раннера, недоступно + из этой сессии); логика закрыта юнит-тестом плюс ручной мутацией, как в r1. +- `golden:verify`, `pytest tests_backend`, инварианты модели, performance — + неприменимо, дифф всего диапазона не трогает `src/**`, рендер, геометрию или + Python (подтверждено ещё в r1, r2 диапазон это не меняет). + +## Вердикт + +Единственная находка r1 закрыта точной правкой с тестом и проверенным +мутантом; новых находок нет. + +--- + +--- + + + +## Материал раунда + +- Ветка: `issue/700-preflight-warnings`, коммит `ed3d63e20db2` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `9a8052d18a3baa6cadd0907021a3a01dfc11d935` + ``` + git log --all --format='%H %T' | grep 9a8052d18a3b + ``` +- Тело issue: `a0c40d40c1401844c3d04ff0695dd347e76172d990004ea2799cc4df38729fce` +- Вердикт конвейера: `green` · High 0