From d8465b3a09582946b75fa8ca574316ee55f5e57e Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 20:07:11 +0000 Subject: [PATCH] docs: review document for #695 Issue: #695 User-Visible: no --- docs/reviews/CODE-REVIEW-695-r2.md | 175 +++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 docs/reviews/CODE-REVIEW-695-r2.md diff --git a/docs/reviews/CODE-REVIEW-695-r2.md b/docs/reviews/CODE-REVIEW-695-r2.md new file mode 100644 index 00000000..bdec325d --- /dev/null +++ b/docs/reviews/CODE-REVIEW-695-r2.md @@ -0,0 +1,175 @@ +# CODE-REVIEW #695 r2 + +Материал: `origin/dev..HEAD`, ровно `f6c76b7351b1d0b9a61954b0153a44bb098bc7f3` +(рабочая копия уже на нём). Диапазон трёх коммитов: `57ce1072` (реализация r1, +уже разобрана в CODE-REVIEW-695-r1), `51854aea` (публикация документа r1, +служебный коммит конвейера), `f6c76b73` (правка Medium из r1 — материал этого +раунда). Дельта этого раунда — ровно `57ce1072..f6c76b73`: +`.github/workflows/_process.yml`, `scripts/mutation-registry.mjs`, +`scripts/task-packet.mjs`, `test/review-doc-guard.test.mjs`, +`test/task-packet.test.mjs`. + +## Скоуп + +Единственная блокирующая находка r1 (Medium, в скоупе) — канон §5.1 +(«инфраструктурная задача (§1) без трековой метки — как `track:show`») не +был реализован ни в `_process.yml` (лимит циклов), ни в `task-packet.mjs` +(отображаемый трек). Автор заявляет это закрытым коммитом `f6c76b73` +(комментарий «Сделано (r1)»). Разбор по PROCESS.md §2.10 — по дельте: полный +пересчёт находки r1, остальное (таблица треков §5, `trackFromLabels()` для +явно помеченных задач, якоря конспектов, трейлеры) унаследовано без повтора — +дельта их не задевает (ни один из пяти изменённых файлов не входит в +доказательную базу этих пунктов r1, кроме `task-packet.mjs`, где тронута +только инфраструктурная ветвь `track`, разобранная ниже). + +## Как проверялось + +| Гейт | Команда | Результат | +|---|---|---| +| Validate на материале | CI run на `f6c76b73` (ссылка в задаче ревью) | зелёный; дешёвые гейты (`tsc`, `npm test`, `build`+`bundle-policy`) не перегонял — приняты по этой ссылке (#343) | +| Целевой юнит-набор | `node --test test/task-packet.test.mjs test/review-doc-guard.test.mjs` | 80/80 и 64/64 pass, 0 fail | +| `process-gate.mjs` (офлайн) | `node scripts/process-gate.mjs --range origin/dev..HEAD` | «гейт пройден, предупреждений 0» | +| `mutation-gate --check` | `node scripts/mutation-gate.mjs --check` | `guard-infra-keeps-ask-limit` — ok; `packet-infra-track-ignores-show-default` — ok; `browser guards: 200/200`; 3 предупреждения — все про `#650` (baseline, не связаны с диффом — совпадают с r1) | +| Мутант `guard-infra-keeps-ask-limit` | вручную заменил `limit=2` на `limit=4 # mutant` в `_process.yml`, прогнал `--test-name-pattern="r1 #695" test/review-doc-guard.test.mjs`, вернул файл (`git diff --stat` после — пусто) | **краснеет**: `AssertionError`, тест ловит мутацию | +| Мутант `packet-infra-track-ignores-show-default` | вручную заменил тернарник `hasTrackLabel(labels) ? trackFromLabels(labels) : 'show'` на `trackFromLabels(labels)` в `task-packet.mjs`, прогнал `--test-name-pattern="r1 #695" test/task-packet.test.mjs`, вернул файл | **краснеет**: `AssertionError: expected 'инфраструктурный · show', actual 'инфраструктурный · ask'` | +| Исполнение bash-ветки guard (не только чтение) | Извлёк новый блок `.github/workflows/_process.yml:224-242` в отдельный скрипт с mock `gh`/`has`, прогнал 6 сценариев (нет меток+только инфра-файлы; нет меток+файл класса A; явный `track:ask`; явный `track:show`; отказ `gh api`; legacy `small`) | все 6 дали ожидаемый `limit` — см. таблицу «Что проверено» | +| Синтаксис bash-шага | `python3 -c "yaml.safe_load(...)"` извлёк `run:` строку `guard`, `bash -n` на неё | синтаксис корректен | +| Node-однострочник guard | Прогнал ровно ту же `node --input-type=module -e '...'` строку на трёх наборах путей (только `scripts/`+`test/`; с `src/houseplan-card.ts`; пустой список) | exit 0 / 1 / 1 — соответствует логике «инфраструктура доказана только непустым списком путей без класса A» | +| `smoke-select.mjs` | `node scripts/smoke-select.mjs --base 57ce1072 --head f6c76b73` | «Исполняемого frontend-диффа нет (src/**/*.ts не тронут)» — смоки не выбираются, выбирать нечего | +| Трейлеры и провенанс коммита | `git show -s --format=full f6c76b73` | `Issue: #695`, `User-Visible: no`, корректно: изменение автоматизации/скриптов, ни одного пользовательского поведения продукта не задето | +| Сверка со сформированным пакетом | `node scripts/task-packet.mjs --issue 695` | трек issue #695 (само помечено `track:show` владельцем) печатается как `инфраструктурный · show` — согласуется с явной меткой | + +Не прогонял (и почему): golden/скриншоты, браузерные смоки (подтверждено +`smoke-select.mjs` — нечего выбирать), `pytest tests_backend`, инварианты +модели, `check-docs.mjs` (диапазон не касается `src/**`), performance-профили — +диапазон не затрагивает ни одного файла класса A и не меняет геометрию/рендер. +Полный `npm test` (3223 теста) не перегонял целиком — принят по зелёному +Validate на этом SHA (#343); перегонял точечно только изменённые тестовые +файлы и мутации по ним, плюс собственноручный харнесс исполнения bash-ветки +(таблица выше) — этого требует #454 (inline-shell без исполнения не +доказывает поведение), выполнено сверх минимума, а не вместо него. + +## Закрытие раунда r1 + +| Находка r1 | Чем закрыта | Где это видно | +|---|---|---| +| Medium: §5.1 («инфраструктура без трековой метки — `track:show`») не реализован ни в `_process.yml` (лимит циклов), ни в `task-packet.mjs` (отображаемый трек); зелёный тест `task-packet.test.mjs:171` прямо утверждал старое поведение | `_process.yml`: новая ветка `guard` (строки 224-242) — при отсутствии `track:*`/`small`/`trivial` берёт диффа ветки против `dev` через `compare` API, классифицирует пути `classify()` из `process-gate.mjs`; при непустом списке (< 300, потолок ответа) без единого файла класса A ставит `limit=2`. `task-packet.mjs`: `infraTrack = hasTrackLabel(labels) ? trackFromLabels(labels) : 'show'` (строка 189), трек инфраструктурной задачи теперь печатается как `` инфраструктурный · ${infraTrack} `` вместо голого `'инфраструктурный'`. Тест `task-packet.test.mjs:171` (`#632`), на который указывала находка, переписан: `assert.equal(packet.track, 'инфраструктурный · show', ...)` — прежнее ожидание `'инфраструктурный'` больше не в коде. | `.github/workflows/_process.yml:224-242`; `scripts/task-packet.mjs:143-144,189-191`; `test/task-packet.test.mjs:110,180,231-241`; `test/review-doc-guard.test.mjs:971-982`; оба новых мутанта (`scripts/mutation-registry.mjs`) реально ловят регресс — проверено воспроизведением (таблица «Как проверялось»), не по названию. Исполнение bash-ветки (6 сценариев харнессом) подтверждает: без метки трека и без класса A → `limit=2`; с любой явной меткой трека → блок не трогает `limit`; при отказе `gh api` или файле класса A → `limit` остаётся консервативным (не понижается ошибочно). | + +Находка полностью закрыта: и механизм (§1: «ни одного файла класса A» — +ровно та же формула, что в PROCESS.md §1), и обе точки, которые r1 назвал +нереализованными, и тест, который r1 предъявил как доказательство обратного +поведения, — всё сходится с текстом §5.1 дословно («инфраструктурная задача +(§1) без трековой метки — как `track:show`»). + +## Унаследовано из r1 (без повторной проверки — дельта не задевает) + +- **Таблица треков §5 и её колонки**, согласованность PROCESS.md / AGENTS.md / + `docs/process/AUTHOR.md` / `docs/process/REVIEWER.md` — документ + `CODE-REVIEW-695-r1.md`, дерево `2faf4770f1ba6a32a599552db9c611bfb5cc5a18`. + Ни один из этих четырёх файлов не входит в дельту `57ce1072..f6c76b73`. +- **`trackFromLabels()` для явно помеченных задач** (`track:ship` / + `track:show` / `track:ask` / legacy `trivial`/`small` / без меток → `ask`), + приоритет меток, лимит циклов для явно помеченных `track:show`/`track:ship`/ + `track:ask` в `_process.yml:87-99` — там же, дерево `2faf4770f1ba6a3...`. + Дельта r2 добавляет только новую ветку ПОСЛЕ этой логики (строки 224-242) и + не меняет ни один из проверенных r1 путей; сама функция `trackFromLabels` + в дельте не тронута (diff `scripts/task-packet.mjs` добавляет только + `hasTrackLabel` и меняет строку сборки `track`, не трогая тело + `trackFromLabels`). +- **Якоря конспектов (AC2 #634), ключевые формулировки, обратная + совместимость легаси-меток `small`/`trivial`, трейлеры провенанса, + отсутствие продуктового кода в диффе** — там же, дерево + `2faf4770f1ba6a3...`; ни один из файлов этой доказательной базы + (`PROCESS.md`, `AUTHOR.md`, `REVIEWER.md`, `test/process-digests.test.mjs`) + не входит в дельту r2. + +## Что проверено и корректно + +- **Механический признак §1 применён идентично канону**: и в `_process.yml` + (список файлов диффа `dev...branch`, ни одного `classify() === 'A'`), и в + `PROCESS.md` §1 («ни одного файла класса A») — то же условие, что уже + реализовано в `branchIsInfrastructure()` (`task-packet.mjs`, не тронут + этим диффом) и в самом `process-gate.mjs`. Никакого нового, отличного от + канона определения «инфраструктуры» дифф не вводит. +- **Приоритет явной метки владельца сохранён**: guard пропускает новую ветку + целиком (`&& ! has track:ship && ! has track:show && ! has track:ask && ! + has small && ! has trivial`), если стоит любая из пяти меток — идентичный + набор в JS (`hasTrackLabel`). Подтверждено исполнением (харнесс, сценарии + 3, 4, 6) и юнит-тестом `'метка владельца главнее'`. +- **Fail-safe при неполном доказательстве**: пустой список файлов, отказ + `gh api`, наличие файла класса A или обрезанный (≥300) ответ compare — во + всех случаях `limit` остаётся 4 (не понижается ошибочно), задача не + наказывается лимитом 2 без доказанной инфраструктуры. Подтверждено + исполнением (харнесс, сценарии 2, 5) и отдельным прогоном + node-однострочника на пустом списке. +- **Мутанты `guard-infra-keeps-ask-limit` и + `packet-infra-track-ignores-show-default`** реально ловят регресс — + проверено ручным воспроизведением обеих мутаций (не по названию `ok` в + `mutation-gate --check`, а прогоном упавшего теста). +- **Синтаксис нового bash-блока и YAML-документа** — `bash -n` и + `yaml.safe_load` не нашли ошибок; блок стоит внутри уже существующего + `if [ -n "$stage" ]`, использует уже установленное `limit` до последующих + проверок `EXHAUSTED`/`spent -ge limit` (строки 270-299) — порядок + вычисления корректен, «состояния гонки» с более поздним использованием + `limit` нет. +- **Трейлеры коммита** — `Issue: #695`, `User-Visible: no` (корректно: не + задето ни одно пользовательское поведение продукта), ветка + `issue/695-track-labels`. +- **Согласованность с самим issue #695**: у issue стоит метка `track:show` — + собранный пакет (`task-packet.mjs --issue 695`) печатает + `инфраструктурный · show`, то есть явная метка отражена корректно и + задокументированный пример (issue #695 сам себе) воспроизводится. + +## Чего не проверял + +- **Живой прогон `_process.yml` guard job** на реальном GitHub Actions + раннере с настоящим `gh api compare` — не воспроизводил; логика проверена + чтением синтаксиса, ручным bash-харнессом с мок-`gh`/`has` (6 сценариев, + см. таблицу) и юнит-тестом на текст блока. **Записываю явно: для самого + факта исполнения на реальном GitHub API — проверено чтением и + контролируемым харнессом, не живым запуском workflow.** Это не новый + пробел этой задачи — тот же пробел зафиксирован в r1 для всей ветки guard + job (тестов, исполняющих `_process.yml` напрямую, в репозитории нет ни для + старой, ни для новой логики). +- **Поведение конвейера по треку** (мутанты на диффе кандидата `ship`, + слияние без ревью модели, ребейз-семантика) — вне скоупа #695, это #696, + не проверял. +- **`process-labels/labels.tsv`** (папка владельца, вне git-дерева) — файл + недоступен ревью, как и в r1. +- **golden/скриншоты, браузерные смоки** (кроме факта, что `smoke-select.mjs` + ничего не выбрал), `pytest tests_backend`, инварианты модели, + performance-профили — диапазон не касается визуала, геометрии или Python. +- **Ребейз-устойчивость якорей мутантов** `guard-infra-keeps-ask-limit` / + `packet-infra-track-ignores-show-default` на дальнейших ребейзах — не + тестировал долгосрочную устойчивость find/replace патчей, только их + текущую верность материалу. + +## Вердикт + +Единственная находка r1 (Medium, в скоупе) закрыта построчно: оба места, +которые она называла нереализованными, реализованы правилом, дословно +совпадающим с §5.1; тест, который r1 предъявил как живое доказательство +старого поведения, переписан и подтверждает новое; оба новых мутанта реально +ловят регресс — проверено воспроизведением, а не по имени `ok`; сама bash- +ветка проверена не только чтением, но и контролируемым исполнением на шести +сценариях. Новых находок дельта `57ce1072..f6c76b73` не даёт: правка узкая, +ограничена ровно заявленным скоупом, не трогает ни один файл класса A, не +меняет ни одну ранее проверенную ветвь `trackFromLabels`/таблицы §5. + +Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0 + +--- + +--- + + + +## Материал раунда + +- Ветка: `issue/695-track-labels`, коммит `f6c76b7351b1` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `2645a194ffcd661ca9d05eb38db68d9e378faae9` + ``` + git log --all --format='%H %T' | grep 2645a194ffcd + ``` +- Тело issue: `c74209278f8dd164514eda40d8841d6eefdeffd3b490a90f47b20e3ff2b667e7` +- Вердикт конвейера: `green` · High 0