mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 12:49:56 +00:00
179 lines
17 KiB
Markdown
179 lines
17 KiB
Markdown
# SPEC-REVIEW — issue #475 · заход r2
|
|
|
|
**Тема:** `mutation-gate --check` не отличает живого свидетеля от мёртвого —
|
|
добавить отбор мутантов по диффу («гард») и запуск на каждом пуше.
|
|
**Трек:** лёгкий (`small`). ТЗ живёт в теле issue. Файла в `docs/specs/` нет
|
|
и не должно быть.
|
|
**Класс изменения:** B (гейты и инструменты) — `scripts/mutation-gate.mjs`,
|
|
`test/mutation-gate.test.mjs`, `.github/workflows/validate.yml`. Ни одного
|
|
файла класса A не затронуто.
|
|
|
|
Это второй заход ревью ТЗ. Первый (r1, документ
|
|
`docs/reviews/SPEC-REVIEW-475-r1.md`, коммит `f52b2449`) дал жёлтый вердикт:
|
|
1 Medium в скоупе, 0 High. Бюджет циклов лёгкого трека — 2, зелёный вердикт
|
|
бюджет не тратит (#227), поэтому на входе в r2 израсходован 1 из 2.
|
|
|
|
## Материал этого раунда
|
|
|
|
Разбор — по дельте (PROCESS.md §2.10), не заново. Предмет — изменения тела
|
|
issue между комментарием «ТЗ готово» (2026-09-06T10:54:23Z, материал r1) и
|
|
текущим телом issue на момент этого раунда.
|
|
|
|
Спецификация лёгкого трека живёт в теле issue, а не в файле репозитория, и
|
|
`git diff <SHA>..HEAD` к ней неприменим в принципе — тело issue не версии
|
|
git. Документ r1 сам это фиксирует в блоке «Материал раунда»: «ветка `dev`,
|
|
коммит `` — ... Якоря снять не удалось: ветки задачи нет, материал читался
|
|
по `dev`». Поле SHA там пустое не потому, что кто-то забыл его заполнить или
|
|
подменил значение перед выводом (это и было бы находкой по §2.10), а потому,
|
|
что на стадии ревью ТЗ лёгкого трека веток и коммитов задачи ещё не
|
|
существует — код не написан. Отмечаю это как наблюдение по механике шаблона
|
|
документа, а не как находку против самого ТЗ: дельта тела issue
|
|
устанавливается надёжно и без git SHA — через точную цитату исходного текста
|
|
в документе r1 (раздел «Находки», кавычки контракта и AC-таблицы) и явный
|
|
комментарий владельца «Medium исправлен» (2026-09-06T11:02:13Z), построчно
|
|
перечисляющий, что изменено. Я сверил оба источника с текущим телом issue
|
|
дословно (см. ниже) — расхождений между заявленным и фактическим текстом нет.
|
|
|
|
## Закрытие раунда r1
|
|
|
|
| Находка r1 | Чем закрыта | Где это видно |
|
|
|---|---|---|
|
|
| Medium: извлечение файлов гарда ограничено суффиксами `.mjs`/`.test.mjs` — токен `tests_backend/test_ha_frontend_registration.py` (`.py`) файлом гарда не считался, 4 бэкенд-мутанта `frontend-registration-*` выпадали из отбора | Список суффиксов расширен до `.mjs`, `.test.mjs`, **`.py`** | Тело issue, раздел «1. Отбор по диффу…»: «все токены команды, оканчивающиеся на `.mjs`, `.test.mjs` **или `.py`**… Бэкенд-мутанты (`frontend-registration-*`... покрываются наравне с фронтендовыми» |
|
|
| Medium: триггер job — только `frontend == 'true'`; паттерн `frontend` в `changes` не включает `tests_backend/` и `custom_components/houseplan/frontend_registration.py` (это `backend`) — диапазон, меняющий только эти файлы, не запускал job вовсе | Триггер расширен до `frontend == 'true' \|\| backend == 'true'` (плюс правка `mutation-gate.mjs`) | Тело issue, раздел «2. `--changed` гоняется на каждом пуше»: «запускается при `frontend == 'true'` **или `backend == 'true'`** или изменении `scripts/mutation-gate.mjs` — бэкенд-мутанты патчат `.py` и охраняются pytest, и дифф только по `custom_components/…/frontend_registration.py` и `tests_backend/` даёт `backend=true` без `frontend=true`» — то есть новый триггер прямо называет прежде пропущенный случай |
|
|
| (следствие находки) отсутствовал AC, доказывающий закрытие именно этого сценария | Добавлен **AC7** | Таблица AC, строка: «AC7 \| Воспроизведение находки ревью: дифф только из `custom_components/houseplan/frontend_registration.py` и `tests_backend/test_ha_frontend_registration.py` отбирает все четыре `frontend-registration-*` мутанта \| unit по реальному реестру» |
|
|
| (сопутствующее) job не ставил Python-зависимости — без них бэкенд-гварды (`pytest`) неисполнимы даже при верном отборе | В job добавлена установка Python и `tests_backend/requirements.txt`, тем же способом, что в `mutation-gate.yml` | Тело issue, раздел 2: «Job ставит Python и `tests_backend/requirements.txt` так же, как `mutation-gate.yml`, чтобы pytest-гварды были исполнимы» |
|
|
| (сопутствующее) граница по фикстурам называла только `test/fixtures/*`, хотя бэкенд-мутанты получили свою фикстурную директорию | Граница расширена на `tests_backend/fixtures/*` | Тело issue, раздел 1, последнее предложение: «Фикстуры гардов (`test/fixtures/*`, `tests_backend/fixtures/*`) по команде не выводятся и отбором не покрываются» |
|
|
|
|
## Как проверялась дельта
|
|
|
|
Не поверил формулировке фикса на слово — перечитал реальную инфраструктуру,
|
|
на которую ссылается новый текст, потому что именно это и было предметом
|
|
находки r1 (техническая граница, а не догадка):
|
|
|
|
- `.github/workflows/validate.yml:281-282` — паттерн `backend`:
|
|
`^(custom_components/.*\.py$|tests_backend/|scripts/support-relay/|
|
|
pytest\.ini$)`. Подтверждено: и `custom_components/houseplan/
|
|
frontend_registration.py`, и `tests_backend/test_ha_frontend_registration.py`
|
|
попадают именно под него, а не под `frontend` — сценарий находки
|
|
воспроизводится буквально, и добавление `backend == 'true'` в триггер
|
|
закрывает его именно так, как описано в тексте;
|
|
- `scripts/mutation-gate.mjs:120-164` — реестр действительно содержит ровно
|
|
4 мутанта `frontend-registration-*`, `patch.file` каждого —
|
|
`custom_components/houseplan/frontend_registration.py`, `guard` каждого —
|
|
`python3 -m pytest tests_backend/test_ha_frontend_registration.py …`.
|
|
Число «четыре» в AC7 и в комментарии владельца не выдумано;
|
|
- `scripts/mutation-gate.mjs:6800-6802` — `selectChangedMutants` на сегодня
|
|
фильтрует **только** по `patch.file` (`m.patches.some((patch) =>
|
|
changed.has(patch.file))`), гард не смотрит вовсе. Это подтверждает
|
|
исходную формулировку проблемы («второй способ сгнить» — правка гарда без
|
|
правки патча — сегодня действительно не покрыт ничем) и то, что расширение
|
|
до guard-файлов — не косметика, а реальный новый путь отбора;
|
|
- `.github/workflows/validate.yml:181-190` — job `changes` действительно
|
|
экспортирует `base` и отдельно `range_base` (разные имена нарочно, для PR
|
|
и для `dev`), как и заявлено в тексте issue;
|
|
- `.github/workflows/mutation-gate.yml` — шаг «Установить backend test
|
|
dependencies» (`actions/setup-python@v7` + `pip install -r
|
|
tests_backend/requirements.txt`) существует и именно так и называется
|
|
«тем же способом» в тексте issue — не изобретённый паттерн;
|
|
- `scripts/mutation-gate.mjs` — ранний зелёный выход при пустой выборке
|
|
(`if (!selected.length) { …; return 0; }` в `main()`) существует уже
|
|
сегодня — AC5 (не тронут дельтой) остаётся доказуемым без новой логики,
|
|
как и было установлено в r1.
|
|
|
|
Гейты кода не гонялись — на стадии ревью ТЗ лёгкого трека продуктового кода
|
|
(в терминах этой задачи — кода гейта) ещё нет, дельта целиком лежит в тексте
|
|
issue; это симметрично тому, что зафиксировал r1.
|
|
|
|
## Находки
|
|
|
|
Нет. Единственная находка r1 (Medium, в скоупе) закрыта корректно и полно —
|
|
см. таблицу выше и её техническую верификацию. Новых противоречий,
|
|
недоказуемых AC или догадок, выданных за факт, дельта не вносит: оба
|
|
изменённых раздела и новый AC7 опираются на подтверждённую существующую
|
|
инфраструктуру (составы `backend`/`frontend` в `changes`, реальный реестр
|
|
мутантов, существующий приём установки Python-зависимостей в
|
|
`mutation-gate.yml`), а не на предположение.
|
|
|
|
Отдельно проверено, не сломал ли фикс что-то из уже принятого: расширение
|
|
списка суффиксов до `.py` не расширяет отбор ложно — правило по-прежнему
|
|
требует, чтобы токен совпадал с **существующим в репозитории файлом**
|
|
(AC3), поэтому произвольный `.py`-аргумент команды (флаг, шаблон) в отбор
|
|
не попадёт; добавление `backend` в триггер не запускает job на диффах, не
|
|
трогающих ни `frontend`, ни `backend` — правило OR только расширяет
|
|
множество, ранее пропущенное, и не сужает уже работавшую фронтенд-ветку.
|
|
|
|
## Унаследовано из r1
|
|
|
|
Ссылка: `docs/reviews/SPEC-REVIEW-475-r1.md`, коммит `f52b2449` (по дереву
|
|
`dev` на момент r1 — веток задачи нет, см. «Материал этого раунда» выше).
|
|
Принято без повторной проверки в этом раунде, так как дельта их не
|
|
затрагивает:
|
|
|
|
- обязательные для лёгкого трека разделы (§5) — проблема · контракт ·
|
|
AC с доказательством · откат — присутствуют, ни один не пропущен;
|
|
дельта добавила один AC (AC7) и расширила два раздела, структуру не
|
|
меняла;
|
|
- AC1, AC3, AC5, AC6 — формулировки не менялись дельтой, доказуемость,
|
|
установленная в r1, наследуется как есть (AC5 при этом попутно
|
|
переподтверждён чтением кода — см. выше, без изменения вывода);
|
|
- отказ от «отметки последнего доказательства» из исходного текста issue —
|
|
явное техническое решение с причиной, дельтой не затронуто;
|
|
- критерии лёгкого трека §5 (сложность ≤3, одна поверхность, без миграции
|
|
конфига, без нового UX-контракта, без влияния на touch) — дельта
|
|
добавляет один суффикс расширения и одно условие OR в YAML, порядок
|
|
сложности не меняет; вывод r1 «критерии не нарушены» остаётся в силе;
|
|
- продуктовый код не затронут ни одним файлом (класс A отсутствует) —
|
|
дельта тоже класса A не касается.
|
|
|
|
## Что проверено и корректно (сверх унаследованного)
|
|
|
|
- Дельта строго соответствует тому, что заявил комментарий владельца
|
|
«Medium исправлен»: все четыре перечисленных им изменения (суффикс `.py`,
|
|
условие `backend`, установка Python-зависимостей, AC7, расширение
|
|
границы фикстур) присутствуют в теле issue дословно.
|
|
- Выбранный путь закрытия («расширить, а не исключить», второй из двух
|
|
предложенных r1 вариантов) технически состоятелен: подтверждено чтением
|
|
`validate.yml` и `mutation-gate.yml`, а не принято на слово.
|
|
- Новый AC7 — не декоративный: он называет конкретное число («все четыре»),
|
|
которое совпадает с фактическим размером подмножества `frontend-
|
|
registration-*` в реестре, и явно завязан на реальные пути
|
|
(`custom_components/houseplan/frontend_registration.py`,
|
|
`tests_backend/test_ha_frontend_registration.py`), а не абстрактный
|
|
случай.
|
|
|
|
## Чего не проверял
|
|
|
|
- Не читал остальные 55+ мутантов реестра повторно на предмет суффиксов
|
|
гарда, отличных от `.mjs`/`.test.mjs`/`.py` — в r1 такая выборочная
|
|
проверка уже была сделана и её вывод («других суффиксов не встречено»)
|
|
дельтой не оспорен, поэтому не переделывал.
|
|
- Не запускал `node scripts/mutation-gate.mjs --changed=...` и не писал сам
|
|
тест — кода ещё нет, это предмет код-ревью (§2.7), как и в r1.
|
|
- Не оценивал фактическую длительность нового job на CI при непустой
|
|
выборке для бэкенд-гвардов (установка Python + pytest) — числовая оценка
|
|
осталась на уровне S2-комментария владельца («минуты»); проверка по факту
|
|
возможна только на код-ревью.
|
|
|
|
## Вердикт
|
|
|
|
Вердикт: зелёный · заход r2 · блокирующих циклов 1/2 · High: 0 · Medium: 0
|
|
|
|
Единственная находка предыдущего раунда закрыта полно и технически
|
|
корректно; дельта не вносит новых находок и не ставит под сомнение AC,
|
|
унаследованные из r1. Готово к разработке.
|
|
|
|
---
|
|
|
|
<!-- material-anchors: раздел ниже пополняется конвейером публикации -->
|
|
|
|
## Материал раунда
|
|
|
|
---
|
|
|
|
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
|
|
|
## Материал раунда
|
|
|
|
- Ветка: `dev`, коммит `` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
|
- Якоря снять не удалось: ветки задачи нет, материал читался по `dev`.
|