14 KiB
SPEC-REVIEW-511-r1
- Issue: #511 — «release-gate: судить по последнему завершённому прогону на SHA, отменённые не блокируют»
- Этап: ТЗ на ревью (PROCESS.md §2.4), лёгкий трек (
small) - Заход: r1 · блокирующих циклов израсходовано 1 из 2 (этот раунд — жёлтый, бюджет тратится)
- Вердикт: жёлтый
Скоуп
Issue пуре-инфраструктурная (класс B по AGENTS.md: scripts/release-gate.mjs,
test/release-gate.test.mjs), продуктового кода не касается, docs/SCOPE.md
неприменим напрямую — задача не закрывает и не ломает ни один Core user job,
это правка гейта публикации релиза. Автор (Codex) уже прогнал её через
S2-analysis по нормальному циклу (не через инфраструктурный обход Claude),
трек small выбран корректно: одна поверхность, риск 2/10, нет
конфига/i18n/перфа/touch — критерии §5 совпадают.
ТЗ живёт в теле issue (верно для small): «Проблема» → «Решение владельца» →
AC1–AC3.
Как проверялось
Кода ещё нет (этап ТЗ) — проверка велась чтением текущего репозитория и сверкой утверждений ТЗ с реальным деревом:
- прочитан
scripts/release-gate.mjs— текущаяclassifyValidateRunsподтверждает описанный в «Проблеме» баг: любойcompletedпрогон сconclusion !== 'success'(включаяcancelled) переводит гейт вfailнезависимо от порядка по времени; - прочитан
test/release-gate.test.mjs— существующий тестrelease gate fails closed for red, cancelled and skipped runsпрямо проверяет, чтоcancelledдаётfail; новый AC1 этот тест переворачивает дляcancelled— ожидаемо, будет переписан в реализации, самой ТЗ это не вредит; - прочитан
.github/workflows/release.yml(строки 43, 52) — оба вызоваrelease-gate.mjs(Validate иperformance.yml/Full Performance) идут через один и тот жеclassifyValidateRuns, так что AC2 («release.yml не меняется») структурно верен; - AC3 сверен с деревом
docs/:find docs -iname "*performance*"— файлаdocs/performance/README.mdне существует ни в каком виде (есть толькоdocs/specs/*performance*.md— черновики специфичных задач, не общая документация гейта); grep -n -i "gate\|exact-sha"поdocs/TESTING.md— документ существует, но не содержит ни одной фразы о семантике «последний завершённый прогон» сейчас; это чек-лист AC по фичам, не общее описание релизного гейта;- прочитан
docs/DEVELOPMENT.md:353-370— вот где живёт релевантный текст сегодня: «A missing, failed, cancelled or one-hour-timed-out Validate withholds the asset» (строка 360). После реализации AC1 это утверждение станет фактически неверным дляcancelled— отменённый прогон перестанет удерживать ассет, если рядом есть более поздний зелёный.
Продуктовых команд (typecheck/test/build) не гонял: на этапе ТЗ кода
нет, гонять нечего.
Находки
Medium (в скоупе задачи) — AC3 указывает не туда, реальный источник искажения не назван
Файл: тело issue #511, раздел AC, пункт AC3.
В чём дефект. AC3 требует добавить фразу о новой семантике в
docs/performance/README.md или docs/TESTING.md. Первого файла не
существует (find docs -iname "*performance*" его не находит) — ТЗ ссылается
на документ, которого нет в дереве. Второй существует, но по грепу не
содержит сейчас никакого описания семантики релизного гейта и структурно
устроен как чек-лист AC по фичам, а не как общее описание процесса релиза —
топически не то место.
При этом реальный текст, который станет неверным после реализации AC1,
уже есть и назван неверно: docs/DEVELOPMENT.md:360 прямо говорит «cancelled
… Validate withholds the asset» — а по новому контракту (AC1) отменённый
прогон удержания не даёт, если рядом есть более поздний завершённый. Этот
файл в AC3 не упомянут вообще.
Сценарий отказа. Исполнитель читает AC3 буквально: пытается открыть
docs/performance/README.md — файла нет, значит остаётся только
docs/TESTING.md, куда добавляется одна фраза не по месту. AC3 формально
закрыт (фраза добавлена, гейт check-docs не тронут — docs/TESTING.md не
входит в скриншотный отпечаток). Но docs/DEVELOPMENT.md:360 остаётся
описывать старую семантику («cancelled withholds the asset»), которая с этого
момента прямо противоречит поведению кода. Канонический документ процесса
релиза лжёт следующему читателю (агенту или владельцу), причём именно там,
где документация должна была объяснить свежую про-cancelled логику — то есть
AC3 не достигает своей цели, будучи формально выполненным.
Почему в скоупе, не Medium вне-скоупа. Правка одной фразы в правильном файле — часть этой же задачи (документация семантики гейта — прямое следствие AC1), не соседнее поведение.
Что чинить. Переформулировать AC3: заменить или дополнить список целевых
файлов на docs/DEVELOPMENT.md (минимум — исправить строку 360, которая
после реализации станет ложной), убрать несуществующий
docs/performance/README.md. docs/TESTING.md может остаться вторым
вариантом, если там действительно логично держать пояснение, но он не
обязан быть единственным кандидатом.
Низкие находки (сняты ревьюером с записью, не блокируют)
- Раздел «откат» отсутствует в теле issue. Шаблон лёгкого трека (§5)
требует «проблема · контракт · AC1…ACn с доказательством · откат» в
самом теле issue. Слово «откат» встречается только в комментарии
S2-analysis(«откат = revert»), не в теле. Снимаю: контент тривиален и однозначен для чистой правки скрипта + теста без состояния/миграции —git revertкоммита закрывает откат полностью, спорить тут не о чем. Рекомендую автору добавить явную строку «Откат: revert коммита, состояния/миграции нет» в тело при следующей правке (заодно с AC3), но отдельного цикла ради этого не требую. - AC2 и AC3 не называют явно способ доказательства (unit/backend/ smoke/golden/«ревью кода»), как того требует чек-лист DoR (§2.5). По смыслу оба доказываются диффом при код-ревью («ревью кода»), это единственное разумное прочтение для «файл не менялся» и «фраза в документации», неоднозначности здесь не возникает. Снимаю с той же рекомендацией — сформулировать явно заодно с правкой AC3.
Что проверено и корректно
- AC1 полностью и однозначно описывает алгоритм:
cancelledисключаются целиком, из оставшихся берётся хронологически последний (run_started_at/created_at), решение зависит только от него. Все шесть перечисленных пар вход/выход взаимно непротиворечивы и детерминированно выводятся из этого правила — включая пограничные случаи ([cancelled(newer), green(older)]→success, где исключениеcancelledи выбор «последнего среди оставшихся» дают однозначный результат без дополнительных допущений). Доказательство названо (тест release-gate.test). - Продуктовая рамка соблюдена: изменение не расширяет скоуп, не трогает
src/**, не создаёт новый UX-контракт, не требует i18n/changelog (User-Visible: noподразумевается корректно — правка невидима пользователю продукта). - AC2 проверяем и структурно подтверждён чтением
release.yml: оба вызоваrelease-gate.mjsиспользуют общую функцию, третьего места с отдельной логикой в workflow нет. - Отсутствие догадок, выданных за решение: «Решение владельца» в теле issue — прямая цитата ретроспективы 09.09, п.5, а не домысел автора спецификации; алгоритм AC1 полностью выводится из неё, ничего не додумано сверх текста решения.
- Продуктовых вопросов владельцу в этой ТЗ не требуется — весь материал технический (где хранится порядок сортировки прогонов, как называется функция и т.п.), решается автором/ревьюером без эскалации.
Чего не проверял
- Не гонял
npx tsc --noEmit,npm test,npm run build— кода к задаче ещё нет, гонять нечего на этапе ТЗ. - Не проверял историческую точность инцидента 09.09 (отменённый дубль
34354468354 и красный dispatch vs v1.72.0) — это утверждение о факте
прошлого прогона CI, не проверяемое чтением репозитория; для ревью ТЗ
достаточно того, что описанный баг воспроизводится логикой текущего
classifyValidateRuns. - Не рассматривал раздел
docs/CHANGELOG.md/docs/CHANGELOG.ru.md— задача корректноUser-Visible: no, оба changelog не требуются.
Итог
Единственная содержательная находка (Medium, AC3) в скоупе задачи и чинится без нового issue — правкой AC3 в теле #511. High-находок нет. Вердикт — жёлтый; возврат автору на правку ТЗ, следующий цикл — по дельте.
Материал раунда
- Ветка:
dev, коммит `` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Якоря снять не удалось: ветки задачи нет, материал читался по
dev. - Вердикт конвейера:
yellow· High 0