mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
167 lines
14 KiB
Markdown
167 lines
14 KiB
Markdown
# 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
|