Files
houseplan-card/docs/reviews/SPEC-REVIEW-511-r1.md
T
2026-09-09 14:20:48 +00:00

167 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` может остаться вторым
вариантом, если там действительно логично держать пояснение, но он не
обязан быть единственным кандидатом.
## Низкие находки (сняты ревьюером с записью, не блокируют)
1. **Раздел «откат» отсутствует в теле issue.** Шаблон лёгкого трека (§5)
требует «проблема · контракт · AC1…ACn с доказательством · **откат**» в
самом теле issue. Слово «откат» встречается только в комментарии
`S2-analysis` («откат = revert»), не в теле. Снимаю: контент тривиален и
однозначен для чистой правки скрипта + теста без состояния/миграции —
`git revert` коммита закрывает откат полностью, спорить тут не о чем.
Рекомендую автору добавить явную строку «Откат: revert коммита,
состояния/миграции нет» в тело при следующей правке (заодно с AC3), но
отдельного цикла ради этого не требую.
2. **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-находок нет. Вердикт —
жёлтый; возврат автору на правку ТЗ, следующий цикл — по дельте.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `dev`, коммит `` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Якоря снять не удалось: ветки задачи нет, материал читался по `dev`.
- Вердикт конвейера: `yellow` · High 0