Files
houseplan-card/docs/reviews/SPEC-REVIEW-514-r1.md
T
2026-09-09 21:18:51 +00:00

92 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-514-r1
- **Issue:** https://github.com/Matysh/houseplan-card/issues/514 — «E2E на реальном HA — гейт стабильного релиза»
- **ТЗ:** `docs/specs/514-e2e-stable-release-gate.md`
- **Заход:** r1 · блокирующих циклов израсходовано 0 из 4 (первый заход, бюджет ещё не тратился — #227)
- **Материал:** тело issue #514 + комментарий S2-аналитики (Codex, 2026-09-09, dev `fa01aa00`) + `docs/specs/514-e2e-stable-release-gate.md` на SHA `ea917603132e379013c70f77b802dcf25e06614e`
- **Вердикт: зелёный**
## Скоуп ревью
Задача помечена `infra`/`process`, автор явно назвал критерий §5, который трек не проходит («две поверхности — `release.yml`+скрипт в `houseplan-card`, `e2e.yml` в `houseplan-e2e` — плюс секрет владельца»), поэтому полный трек и файл в `docs/specs/` оправданы, а не выбраны по умолчанию без causa. Проверялись: обязательные разделы §7.1, однозначность и доказуемость каждого AC, отсутствие выданных за решения догадок, отсутствие продуктовых вопросов владельцу, которые ревьюер обязан снять сам.
## Как проверялось
Проверка велась не на веру к тексту ТЗ, а сверкой с фактическим состоянием обоих репозиториев — большинство утверждений автора о существующем коде оказались проверяемыми напрямую:
1. `PROCESS.md` §1–14 (жизненный цикл, лимит циклов, лёгкий трек, артефакты §7.1, гейты §8, запрещено §12) и `docs/SCOPE.md` — рамка и процесс.
2. `.github/workflows/release.yml` — текущая структура job `gate`/`build`, ровно та точка, куда ТЗ предлагает вставить новый шаг (после «Require full performance for a stable release», тот же `if: !prerelease`).
3. `scripts/validate-gate.mjs` и `scripts/merge-candidate.mjs` — образец, на который ссылается ТЗ (`VALIDATE_APPEAR_MS = 3 мин`, `VALIDATE_TOTAL_MS = 45 мин` подтверждены построчно), и паттерн чистой функции над инъектируемыми `ops`, который `e2e-gate.mjs` предлагается повторить.
4. `scripts/mutation-gate.mjs` — нашёл уже существующие мутанты по `validate-gate.mjs` (`review-starts-on-red-validate`, `review-returns-task-on-cancelled-dispatch`, `review-trusts-push-run-without-mutants`) в формате `{id, guard, because, patches:[{file, find, replace}]}`. Два мутанта, заявленные в §7 ТЗ (`release-ships-on-red-e2e`, `release-trusts-foreign-e2e-run`), — прямые аналоги уже работающего паттерна на соседнем скрипте, не выдумка.
5. `docs/DEVELOPMENT.md` (строка про Full Performance, ~371–373) и `PROCESS.md` (строка 703, «Гейт стабильного релиза») — точные места правки, названные в §8 ТЗ, существуют и содержат именно тот текст, к которому ТЗ предлагает дописать «+ E2E».
6. `test/performance-workflow.test.mjs`, `test/validate-workflow.test.mjs`, `test/release-contract.test.mjs` — подтверждают, что тестирование содержимого `.github/workflows/*.yml` через `readFileSync` + `assert` уже рабочий паттерн в этом репозитории; план тестов ТЗ (`test/release-workflow.test.mjs`) технически реализуем этим же способом.
7. **`houseplan-e2e/.github/workflows/e2e.yml`** — получен напрямую через `gh api repos/Matysh/houseplan-e2e/contents/...` (репозиторий владельца доступен для чтения). Сверены все технические утверждения §6 ТЗ построчно:
- имя job `"${{ matrix.suite }} · HP ${{ matrix.ref }} · HA ${{ matrix.ha }}"` — подтверждено, совпадает с контрактом опознания в §4/§12 ТЗ;
- `concurrency: { group: e2e-${{ github.ref }}-${{ github.event_name }}, cancel-in-progress: false }` — подтверждено буквально; утверждение «отмена — рукотворная» в §4 п.3 ТЗ не догадка, а прочитанный факт;
- `matrix.include` для `journeys-dev` (`ref: dev`) сейчас **не имеет условия** — значит на сегодняшний день этот сьют реально гоняется и на `workflow_dispatch`, что подтверждает саму проблему, которую чинит §6 ТЗ (сьют против `dev` красит гейт стабильного тега). Предлагаемое условие `if: matrix.suite != 'journeys-dev' || github.event_name == 'schedule'` — валидный паттерн (job-level `if` может читать `matrix.*` для матричных job) и действительно закрывает найденную проблему;
- `suite: upgrade` берёт `ref: ${{ inputs.upgrade_from || 'stable' }}` (не `houseplan_ref`) — то есть job этого сьюта в принципе не всегда содержит `HP <tag>` в имени. Не дефект: опознание в §4 п.2 требует «хотя бы одна» job с `HP <tag>` (её дают `journeys` и `first-run`), а сам сьют `upgrade` по назначению ставит **старую** версию и обновляется до цели внутри теста (`HOUSEPLAN_REF`, job-level env, читает `inputs.houseplan_ref` независимо от `matrix.ref`) — именно это описано в §6 ТЗ как «upgrade — со stable (предыдущий) на тег», без противоречия.
8. `.github/workflows/release-zip.yml`, `.github/workflows/publish-prerelease.yml` — подтверждают заявление §5 ТЗ: `houseplan.zip` собирается и прикладывается к релизу этими workflow независимо от нового гейта, то есть E2E действительно ставит те же байты, что скачает HACS.
## Находки
Не найдено ни одной High- или Medium-находки. Технические утверждения ТЗ о текущем состоянии `houseplan-card` и `houseplan-e2e` проверены построчно и подтвердились; ни одна не оказалась выданной за факт догадкой.
Low, снятые без правки (не искажают AC и не создают риска):
- **L1.** §4 использует переменную `t0` без явного объявления («кандидаты с `createdAt ≥ t0 − 60 с`») — по контексту это момент вызова `ops.dispatch(tag)`, аналогично `dispatchedAt` в `validate-gate.mjs`. Это техническая деталь реализации, которую §7.1 прямо разрешает решать исполнителю без продуктового вопроса владельцу («всё, чего пользователь не наблюдает, агенты решают сами»); снимается без правки ТЗ.
- **L2.** Тайм-аут `totalMs = 45 мин` переиспользован из `merge-candidate.mjs`, а не выведен из собственного `timeout-minutes: 40` job'ов `e2e.yml`; даёт 5 минут запаса поверх внутреннего лимита job'а, чего достаточно. Переиспользование готовой, уже проверенной константы — сознательный выбор автора (сам ТЗ называет источник), не находка.
## AC — проверка на выполнимость и доказуемость
Все шесть AC пронумерованы, формулируют проверяемое условие и называют способ доказательства (тест/мутант/условие в yml/живой прогон в хендоффе):
| AC | Доказательство названо | Проверяемо |
|---|---|---|
| AC1 | `test/e2e-gate.test.mjs`, `test/release-workflow.test.mjs` | да |
| AC2 | тест + мутант `release-trusts-foreign-e2e-run` | да, паттерн мутанта подтверждён на аналоге (`review-trusts-push-run-without-mutants`) |
| AC3 | условие в yml + тест | да |
| AC4 | коммит в `houseplan-e2e` + живой dispatch на реальном теге в хендоффе | да — единственный практичный способ для факта в чужом репозитории без своего процесса; честно назван как ручная/живая проверка, а не автотест |
| AC5 | штатный `mutation-gate.mjs` раннер | да |
| AC6 | ревью документации + `User-Visible: no` | да |
Ни один AC не выдаёт предположение за решение: раздел «12. Принятые предположения» отдельно и честно называет два места, где решение принято агентами без владельца (scope токена `HP_PROCESS_TOKEN`, стабильность контракта имени job), и это ровно тот класс вопросов, которые §7.1 разрешает не выносить владельцу.
## Продуктовые вопросы владельцу
Открытых продуктовых вопросов нет. Единственный вопрос из тела issue («нужен ли `E2E_DISPATCH_TOKEN`») — технический (scope токена), не продуктовый (не о том, что видит или делает человек), и он снят в самом ТЗ явным решением с фолбэком и понятным сообщением об ошибке, а не эскалирован — верно по правилу §7.1 «владельцу задаются только продуктовые вопросы».
Раздел 1.2 «Что человек увидит до и после» отвечает на оба обязательных вопроса §7.1 (кто, где, что видит) одной фразой без терминов реализации: пользователь HACS не видит ничего дополнительного (кроме отсутствия сломанного stable), владелец видит один дополнительный шаг гейта и ссылку на прогон.
## Обязательные разделы ТЗ (§7.1)
Все присутствуют: сценарий (§1.1) · что увидит человек (§1.2) · проблема (§1) · скоуп/не-скоуп (§2/§3) · контракт поведения (§4–§6) · UX/модель данных/i18n (§10.0, законно свёрнуто в одну строку — изменений нет) · критерии приёмки с доказательством (§10) · план автотестов (§7) · риски (§10.1) · откат (§9) · release-артефакты (§8, §11).
## Что не проверялось и почему
- Не запускались автотесты и гейты (`npm test`, `tsc`, `build`) — на этапе ревью ТЗ кода ещё нет, шаг относится к код-ревью (§2.7), не к ревью ТЗ (§2.4).
- Не проверялась фактическая работоспособность `HP_PROCESS_TOKEN`/`E2E_DISPATCH_TOKEN` на реальном dispatch — секреты недоступны ревьюеру и это явно вынесено в хендофф как первая живая проверка (§5 ТЗ, «проверка — первый stable после слияния»); ТЗ этого не скрывает.
- Коммит в `houseplan-e2e` (изменение условия `journeys-dev`) ещё не существует — это ожидаемо для этапа ТЗ (задача полного трека, две поверхности), сам факт зафиксирован в скоупе (§2 п.3) и будет предметом код-ревью через ссылку в хендоффе, а не этого ревью.
## Материал раунда
- SHA ветки: `ea917603132e379013c70f77b802dcf25e06614e`
- Файл ТЗ: `docs/specs/514-e2e-stable-release-gate.md`
- Тело issue #514 и комментарий S2-аналитики (id `IC_kwDOTOcLQM8AAAABTjOGtQ`) на момент ревью.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/514-e2e-stable-release-gate`, коммит `ea917603132e` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `5c67679a7f74498659849400221ca05fc4c5b2e9`
```
git log --all --format='%H %T' | grep 5c67679a7f74
```
- ТЗ `docs/specs/514-e2e-stable-release-gate.md`, блоб `6c98b844b36b81cf483f570e4a38faf8d24f2056`
```
git log --all --find-object=6c98b844b36b81cf483f570e4a38faf8d24f2056 -- docs/specs/514-e2e-stable-release-gate.md
```
- Вердикт конвейера: `green` · High 0