diff --git a/docs/reviews/SPEC-REVIEW-511-r2.md b/docs/reviews/SPEC-REVIEW-511-r2.md new file mode 100644 index 00000000..112a45e4 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-511-r2.md @@ -0,0 +1,165 @@ +# SPEC-REVIEW-511-r2 + +- Issue: #511 — «release-gate: судить по последнему завершённому прогону на SHA, отменённые не блокируют» +- Этап: ТЗ на ревью (PROCESS.md §2.4), лёгкий трек (`small`) +- Заход: r2 · блокирующих циклов израсходовано 1 из 2 (этот раунд — зелёный, бюджет не тратится, #227) +- Вердикт: **зелёный** + +## Скоуп раунда и объявление дельты + +Предыдущий вердикт — r1, жёлтый, документ `docs/reviews/SPEC-REVIEW-511-r1.md`, +опубликован коммитом `6842b69e` (текущий `HEAD`, дерево между раундами не +менялось вовсе — задача лёгкого трека, ТЗ живёт в теле issue, кода ещё нет). +Материал раунда r1 сам называл SHA пустым («ребейз его осиротит» — якорь не +резолвился, ветки задачи не было, читали по `dev`); в r2 сверка проще: между +раундами не было ни одного коммита, только правка тела issue. + +Дельта объявлена через историю правок issue (`userContentEdits` GraphQL, +единственный способ увидеть точный до/после тела issue, раз обычный +`git diff` тут неприменим — ТЗ не файл). Правка сделана 2026-09-09T14:20:49Z, +через 17 секунд после публикации вердикта r1 (14:20:32Z). Точный diff: + +```diff +- AC3. `docs/performance/README.md`/`docs/TESTING.md` — одна фраза о семантике «последний завершённый». ++ AC3. `docs/DEVELOPMENT.md` (канонический документ релиза; строка «A missing, failed, cancelled or one-hour-timed-out Validate withholds the asset» становится неверной) описывает новую семантику: «последний завершённый прогон, `cancelled` не считаются»; `demo/performance/README.md` — та же фраза рядом с требованием Full Performance для stable. Доказательство AC2 — `release.yml` без диффа; доказательство AC3 — grep этих фраз в тесте `release-gate.test`. ++ ++ ## Откат ++ ++ Revert одного коммита; данных и форматов нет. +``` + +«Проблема», «Решение владельца», AC1, AC2 — байт в байт без изменений. +Дельта локальна по всем критериям §2.10: не ребейз, не смена контракта +поведения, не новая подсистема, объём — одна строка AC плюс один +трёхстрочный раздел. Разбор ограничен дельтой; AC1/AC2 наследуются из r1 +без повторного вывода алгоритма. + +## Закрытие раунда r1 + +| Находка r1 | Чем закрыта | Где это видно | +|---|---|---| +| **Medium** — AC3 указывал на несуществующий `docs/performance/README.md` и топически неподходящий `docs/TESTING.md`; реальная строка `docs/DEVELOPMENT.md:360` не была названа | AC3 переписан: целевой файл — `docs/DEVELOPMENT.md`, с точной цитатой строки, которая станет неверной; `docs/performance/README.md`/`docs/TESTING.md` убраны | Тело issue #511, раздел AC, пункт AC3 (см. diff выше); подтверждено чтением `docs/DEVELOPMENT.md:359-360` — цитата совпадает дословно (перенос строки в исходнике не меняет фразу) | +| **Low 1** — раздел «откат» отсутствовал в теле issue дословно | Раздел `## Откат` добавлен в тело: «Revert одного коммита; данных и форматов нет» | Тело issue #511, последний раздел | +| **Low 2** — AC2/AC3 не называли явно способ доказательства | Дописано прямым текстом: «Доказательство AC2 — `release.yml` без диффа; доказательство AC3 — grep этих фраз в тесте `release-gate.test`» | Тело issue #511, конец пункта AC3 | + +Все три находки r1 закрыты содержательно, не декларативно — по каждой указана +конкретная строка текста, а не заявление автора. + +## Унаследовано из r1 + +Без повторной проверки принято из `docs/reviews/SPEC-REVIEW-511-r1.md` +(материал: `dev`, документ опубликован коммитом `6842b69e`, тем же, что и +текущий `HEAD`, — дерево кода между раундами идентично): + +- **AC1** полностью и однозначно описывает алгоритм: `cancelled` исключаются + целиком, из оставшихся берётся хронологически последний + (`run_started_at`/`created_at`); все шесть пар вход/выход в тексте + взаимно непротиворечивы. Текст AC1 не менялся дельтой — переверять нечего. +- **AC2** структурно подтверждён чтением `release.yml` (оба вызова + `release-gate.mjs` идут через общую `classifyValidateRuns`, третьего места + с отдельной логикой нет). Текст AC2 не менялся дельтой. +- Продуктовая рамка: задача не расширяет скоуп, не трогает `src/**`, не + создаёт новый UX-контракт, `User-Visible: no` подразумевается верно. +- Отсутствие догадок, выданных за решение: «Решение владельца» — прямая + цитата ретроспективы 09.09, п.5, алгоритм AC1 полностью выводится из неё. +- Продуктовых вопросов владельцу не требуется — весь материал технический. +- Трек `small` выбран корректно (одна поверхность класса B — `scripts/**` + + `test/**`, риск 2/10, нет конфига/i18n/перфа/touch). + +## Что проверено заново в этом раунде + +- **Цитата в AC3 для `docs/DEVELOPMENT.md`.** Прочитан файл, строки 359–360: + «*A missing, failed, cancelled or one-hour-timed-out Validate withholds the + asset*» — фраза реальна, совпадает с цитатой в AC3 дословно (в источнике + перенесена по мягкому переносу markdown между «failed,» и «cancelled», это + не меняет содержание). `grep -n` подтверждает построчно (359, 360). +- **Путь `demo/performance/README.md`.** Файл существует + (`ls demo/performand/README.md` → есть), в отличие от использованного в r1 + ошибочного `docs/performance/README.md` (`docs/performance/` не существует + вовсе — перепроверено `ls docs/performance` → «No such file or directory»). + Так что сама подмена пути — реальное исправление, не косметика. +- **Смысл «та же фраза» для `demo/performance/README.md`.** Проверил + построчно (`grep -n -i "exact-SHA|stable release|withhold|asset"`) — в этом + файле **нет** существующей фразы вида «A missing, failed, cancelled...», + которая «становится неверной»; единственное релевантное место — строки + 122–124 («Stable release assets require both exact-SHA `Validate` and + exact-SHA `Full Performance`»), и это утверждение остаётся истинным после + AC1 (требование «нужен зелёный Validate» не меняется, меняется только то, + как это «зелёный» вычисляется). Прочитав AC3 целиком («описывает новую + семантику: «последний завершённый прогон, `cancelled` не считаются»; … + та же фраза…»), нахожу более естественным прочтение «та же фраза» = та же + **новая** поясняющая фраза, добавляемая в оба файла, а не тождественная + старая формулировка, которую надо «исправить» во втором файле (там + исправлять нечего — искажённого утверждения там сейчас нет). Это не + единственно возможное прочтение, но при этом чтении AC3 однозначно + выполнимо и проверяемо (grep новой фразы в обоих файлах), а + неоднозначность — не о продукте (не «что видит пользователь», а «в какой + документ разработки идёт вспомогательное предложение») и не годится для + эскалации владельцу по правилу §7.1. Не блокирую: пограничная + формулировка, а не фактическая ошибка или неисполнимость — граница между + «дефект» и «стилистика», по-моему, здесь пройдена в пользу автора, потому + что оба чтения ведут к одному и тому же безопасному действию разработчика + (добавить/поправить одну поясняющую фразу в названном месте) и к + проверяемому тесту. +- **Тест-цель `release-gate.test`.** `test/release-gate.test.mjs` существует + уже сегодня (класс B, правится в этой же задаче), текущие тесты + (`release gate fails closed for red, cancelled and skipped runs`, `one red + duplicate blocks a green duplicate for the same SHA`) кодируют именно + старую семантику, которую AC1 переворачивает, — подтверждает, что задача + не вакуумна и есть что менять и проверять. Новых assert'ов на grep + docs-файлов в тесте пока нет (ожидаемо — реализации ещё нет), но + добавление такого теста ничем не блокировано: `docs/DEVELOPMENT.md` и + `demo/performance/README.md` — обычные текстовые файлы, `readFileSync` + + `includes` тривиален. +- **Раздел «Откат».** «Revert одного коммита; данных и форматов нет» — + точно для скоупа (правка одного CI-скрипта, одного теста и двух + документационных файлов, без миграций, без хранимого состояния, без + config-полей). Проверено чтением, не исполнением (откатывать нечего — + кода ещё нет). + +## Чего не проверял + +- Продуктовые команды (`typecheck`/`test`/`build`) не гонял — на этапе ТЗ + кода нет, гонять нечего (наследуется из r1, ситуация не изменилась). +- Историческую точность инцидента 09.09 (конкретные ID прогонов) — факт + прошлого CI, не проверяемый чтением репозитория; для ревью ТЗ достаточно, + что баг воспроизводится логикой текущего `classifyValidateRuns` (принято + в r1, дельта этого не касалась). +- Не открывал `docs/CHANGELOG.md`/`.ru.md` — `User-Visible: no`, оба + changelog не требуются (наследуется из r1). +- Не проверял, действительно ли задача подходила бы под инфраструктурный + обход без спецификации (AGENTS.md, «ни одного файла класса A» — она бы + подошла, `scripts/**` + `test/**` только класс B). Это выбор автора/ + аналитика идти полным лёгким треком, а не нарушение процесса — необязателен + к проверке ревьюером ТЗ. + +## Итог + +Все три находки r1 (1 Medium, 2 Low) закрыты конкретным текстом, не +заявлением. Дополнительная проверка второй половины переписанного AC3 +(`demo/performance/README.md`) не вскрыла блокирующего дефекта — формулировка +пограничная, но исполнима и проверяема при разумном прочтении, последствия +обеих трактовок безопасны и совпадают в действии. High-находок нет, +новых Medium/Low не заводится. AC1/AC2 наследуются из r1 без повторной +проверки алгоритма — дельта их не касалась. Вердикт — зелёный; переход в +`S5-ready`. + +--- + + + +## Материал раунда + +- Ветка/дерево: `dev`, коммит `6842b69eed1a6530d7475f4427132b4a1b501e49` (код не менялся с r1; дельта — только тело issue). +- Материал ТЗ: тело issue #511 на момент вывода вердикта; правка зафиксирована `userContentEdits` (GraphQL), `editedAt: 2026-09-09T14:20:49Z`, editor `Matysh`. +- Вердикт: `green` · High 0 · Medium 0 + +--- + + + +## Материал раунда + +- Ветка: `dev`, коммит `` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Якоря снять не удалось: ветки задачи нет, материал читался по `dev`. +- Вердикт конвейера: `green` · High 0