mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-04 05:41:34 +00:00
198 lines
17 KiB
Markdown
198 lines
17 KiB
Markdown
# SPEC-REVIEW-369-r2
|
||
|
||
- Issue: https://github.com/Matysh/houseplan-card/issues/369
|
||
- Этап: ТЗ на ревью (PROCESS.md §2.4), полный трек (владелец назвал критерий
|
||
«одна поверхность» §5 как не выполненный — семь несвязанных поверхностей)
|
||
- Артефакт: `docs/specs/369-audit-lows.md`
|
||
- Материал ревью: дельта `cbf9318e..c50d9e42` («ревизия 2» документа,
|
||
`docs: #369 spec revision 2 per SPEC-REVIEW-369-r1`) — тот же единственный
|
||
файл, 3 хунка (+11/-7 строк), `User-Visible: no`, корректно для docs-коммита
|
||
- Заход: **r2** · блокирующих циклов израсходовано **1 из 4** (лимит для
|
||
полного трека — 4)
|
||
|
||
## Несовпадение номера раунда в заголовке задачи — зафиксировано, не находка по ТЗ
|
||
|
||
Заголовок этой задачи ревью объявлял «Заход: r1 · блокирующих циклов 0 из 4».
|
||
Это неверно относительно фактического состояния issue: в `docs/reviews/`
|
||
уже лежит `SPEC-REVIEW-369-r1.md` с вердиктом «жёлтый» (три Medium: M1, M2,
|
||
M3), и это подтверждено комментарием issue от `2026-08-29T07:41:01Z`
|
||
(«Вердикт: жёлтый · заход r1 · блокирующих циклов 0/4 · High: 0 · Medium: 3
|
||
→ в задаче»), а следующий комментарий владельца («Ревизия 2 ТЗ (dev
|
||
`c50d9e4`) по r1: **M1**...**M2**...**M3**... Возвращаю S4-spec-review»)
|
||
подтверждает, что это и есть возврат на правки по r1. Жёлтый вердикт тратит
|
||
бюджет §4 (не зелёный, #227) — значит фактический счёт: заход **r2**,
|
||
циклов **1/4**, а не r1/0. Использую r2 в имени этого документа: назвать
|
||
его r1 означало бы затереть уже существующий `SPEC-REVIEW-369-r1.md` —
|
||
ровно тот сценарий, от которого предостерегает сам процесс триггера. Не
|
||
продуктовая находка (не про ТЗ #369), но стоит сообщить о рассинхроне
|
||
источника номера раунда в автоматизации, которая формирует заголовок
|
||
задачи — иначе следующий заход рискует получить то же самое имя дважды.
|
||
|
||
## Скоуп раунда (правило дельты, PROCESS.md §2.9, #214)
|
||
|
||
Дельта — три точечных правки одного файла, отвечающие ровно на находки r1:
|
||
1. `- Ревизия: 1` → `- Ревизия: 2 ... — по SPEC-REVIEW-369-r1 (M1–M3)`
|
||
(метаданные, не содержательно).
|
||
2. Контракт (ж): список файлов расширен `docs/FURNITURE.md:41` (M1).
|
||
3. AC1: доказательство переформулировано с «check-docs зелёный» на «код-ревью
|
||
читает три файла» (M2).
|
||
4. AC8: добавлен мутант для (д) — «attach выброшен → смок AC5 красный по
|
||
free-флагу и счётчику слушателей» (M3).
|
||
|
||
Ни рекомбейза на ушедший вперёд `dev`, ни смены контракта поведения, ни
|
||
новой подсистемы — дельта локальна классически. Разбор этого раунда
|
||
ограничен M1–M3 и тем, не сломала ли точечная правка что-то из уже
|
||
подтверждённого в r1 (правки чисто аддитивны: добавлено имя файла, снята
|
||
формулировка доказательства AC1, добавлен один пункт списка мутантов —
|
||
ни один не пересекается текстуально с AC2–AC7 или разделами «Риски»,
|
||
«Release-артефакты», «Вне скоупа», «Откат»).
|
||
|
||
## Как проверялось
|
||
|
||
- `git diff cbf9318e..c50d9e42 -- docs/specs/369-audit-lows.md` — прочитан
|
||
целиком, три хунка перечислены выше.
|
||
- M1: контракт (ж) в текущем тексте (строка 66-68) действительно называет
|
||
четыре файла, включая `docs/FURNITURE.md:41`. `grep -rn Matyushin docs/
|
||
assets/` на HEAD (`c50d9e42`) по-прежнему находит все четыре целевых
|
||
файла (правка кода ещё не сделана — стадия ТЗ, ожидаемо) плюс историческую
|
||
прозу `docs/reviews/SPEC-REVIEW-369-r1.md` и сам текст спеки, цитирующие
|
||
старое имя как часть описания правки — см. находку L1 ниже.
|
||
- M2: AC1 в текущем тексте (строка 72-74) — «доказательство: код-ревью
|
||
читает три файла (check-docs содержимое не проверяет, он лишь не должен
|
||
покраснеть от ссылок/фингерпринта)» — снимает именно то расхождение
|
||
инструмент/утверждение, которое было найдено в r1 (`scripts/check-docs.mjs`
|
||
не читает содержимое абзацев).
|
||
- M3: AC8 (строка 92-96) теперь перечисляет мутант для (д) отдельно от
|
||
(б)/(в)/(г)/(е), с двумя названными детекторами (смена free-флага,
|
||
счётчик addEventListener/removeEventListener) — то же, что требовало M3.
|
||
- Трейлеры `c50d9e42`: `Issue: #369`, `User-Visible: no` — верно для
|
||
docs-only правки класса C (PROCESS.md §1).
|
||
- Диапазон изменений всей спек-стадии #369 (`cbf9318e` и `c50d9e42`) —
|
||
единственный файл `docs/specs/369-audit-lows.md` оба раза
|
||
(`git show --stat` на каждом коммите); `src/**`/`custom_components/**`
|
||
не тронуты ни разу.
|
||
|
||
Дешёвые гейты (`typecheck`/`test`/`build`/`check-docs`) на этом заходе не
|
||
запускал — тот же довод, что в r1: диапазон дельты не содержит ни одного
|
||
файла класса A/B (PROCESS.md §1), гейты нечего было бы доказать. Смоки,
|
||
golden, инварианты модели — не запускались по той же причине (ни
|
||
геометрия, ни `src/**`, ни визуальный результат в этой дельте не задеты).
|
||
|
||
## Закрытие раунда r1
|
||
|
||
| Находка (r1) | Чем закрыта | Где видно |
|
||
|---|---|---|
|
||
| **M1** — AC7 требовал пустой repo-wide grep, но контракт (ж) не покрывал все вхождения имени (`docs/FURNITURE.md:41` пропущен) | Контракт (ж) расширен до четырёх файлов, включая `docs/FURNITURE.md:41` | `docs/specs/369-audit-lows.md:66-68`, diff-хунк 2 (`cbf9318e..c50d9e42`) |
|
||
| **M2** — AC1 называл `check-docs` доказательством содержимого абзаца, хотя скрипт содержимое не проверяет | AC1 переформулирован: доказательство — прямое чтение трёх файлов на код-ревью, `check-docs` оставлен только структурным требованием | `docs/specs/369-audit-lows.md:72-74`, diff-хунк 3 |
|
||
| **M3** — AC8 не называл мутант для (д), смок AC5 нельзя было отличить «проверяет логику» от «проходит без неё» | AC8 получил явный мутант «attach выброшен → смок красный по free-флагу и счётчику слушателей» | `docs/specs/369-audit-lows.md:92-96`, diff-хунк 4 |
|
||
|
||
Все три Medium из r1 закрыты предметно (текст ТЗ, не заявление автора).
|
||
|
||
## Унаследовано из r1
|
||
|
||
Документ: `docs/reviews/SPEC-REVIEW-369-r1.md`, разобран на SHA `cbf9318e`.
|
||
Дельта r2 не касается ничего из перечисленного ниже — принимается без
|
||
повторной построчной проверки:
|
||
|
||
- Сценарий («до/после», персона home admin/десктоп-редакторы) и соответствие
|
||
J1/J2/J4/J5/J6 из `docs/SCOPE.md` — нарушения не найдено.
|
||
- Все шесть кодовых утверждений контракта (а)–(е) сверены r1 построчно с
|
||
деревом `dev` на `cbf9318e` (`src/devices.ts:1402-1410`,
|
||
`src/device-presentation.ts:210-224` + `src/ha-binding-status.ts:14-17,440-444`,
|
||
`src/vacuum.ts:250-256`, `src/houseplan-editor-runtime.ts:4756`,
|
||
`src/houseplan-card.ts:6109`→`_decorPointerDown`,
|
||
`custom_components/houseplan/trails.py:252-274`) — ни одно не оказалось
|
||
догадкой, выданной за факт.
|
||
- AC2–AC6 — однозначны, способ доказательства (юнит/смок) назван, дельта их
|
||
текст не трогает.
|
||
- «Вне скоупа», «Откат», «Риски», «Release-артефакты» — не тронуты дельтой,
|
||
корректны по r1.
|
||
- **L1** (избыточное условие в контракте (г): вторая половина условия по
|
||
инварианту `resolveHaBindingStatus` не добавляет новых случаев) — снята
|
||
r1 с записью, не блокирует; дельта (г) не касалась.
|
||
- **L2** (не все разделы §7.1 вынесены отдельными заголовками) — снята r1
|
||
с записью как необязательная рекомендация «на следующую правку»;
|
||
ревизия 2 её не применила — это не нарушение, поскольку r1 явно
|
||
зафиксировал находку как dismissed (не «to-do»), см. PROCESS.md: Low
|
||
«либо правится, либо снимается с записью» — уже снята.
|
||
|
||
## Находки (этот раунд)
|
||
|
||
### Low — AC7 не станет буквально «grep по репо пуст» даже после точечной правки; положительная проверка не покрывает добавленный файл
|
||
|
||
`docs/specs/369-audit-lows.md`, AC7 (строка 88-91): «(ж) grep «Matyushin» по
|
||
репо пуст; pack.json/LICENSE.md/README.md несут «Sergey Matyunin»…».
|
||
|
||
Два самостоятельных нюанса одной находки:
|
||
|
||
1. Позитивная часть AC7 («несут «Sergey Matyunin»») по-прежнему называет
|
||
только три исходных файла и не упоминает добавленный `docs/FURNITURE.md`
|
||
— асимметрично контракту (ж), который теперь называет четыре. Негативная
|
||
часть («grep пуст») это транзитивно покрывает, так что асимметрия не
|
||
создаёт дыру в доказательстве, только небольшую редакционную
|
||
рассогласованность.
|
||
2. Буквально пустой repo-wide grep недостижим и после полной правки: сам
|
||
этот файл ТЗ (`docs/specs/369-audit-lows.md`) в прозе контракта (ж) и в
|
||
AC7 цитирует «Sergey Matyushin» как часть описания правки, `docs/reviews/
|
||
SPEC-REVIEW-369-r1.md` цитирует то же самое как историческую находку, и
|
||
этот документ (SPEC-REVIEW-369-r2) продолжит цитировать обе формы имени
|
||
после публикации в `docs/reviews/`. Ровно тот же класс дефекта AC уже
|
||
разбирался в прецеденте `docs/reviews/CODE-REVIEW-269-r1.md` (Low,
|
||
«буквальная формулировка AC2 не выполняется, хотя цель AC2 достигнута») —
|
||
там тоже historical-документ ревью цитировал старый текст, и находка была
|
||
снята без правки: архивный документ «описывает код, которого уже нет»
|
||
(§7.3), переписывать его задним числом бессмысленно и запрещено духом
|
||
канона.
|
||
|
||
*Почему не блокирует:* цель AC7 — отсутствие живого расхождения имени в
|
||
активных ассетах/документации (`assets/furniture/houseplan-0.3.0/**`,
|
||
`docs/FURNITURE.md`), а не буквальный ноль совпадений во всём git-дереве
|
||
включая архив ревью. Это то же различие буквы и духа AC, что и в #269.
|
||
|
||
*Снимается без правки, с записью:* код-ревьюер этой задачи должен читать
|
||
AC7 как «`grep -rn Matyushin` пуст вне `docs/specs/**` и `docs/reviews/**`»
|
||
— не нужно заводить эту находку заново на код-ревью, ссылка на этот раздел
|
||
и на прецедент #269 достаточна.
|
||
|
||
High: 0. Medium: 0 (у обеих Medium из r1 — M1, M2, M3 — закрытие
|
||
подтверждено выше построчно).
|
||
|
||
## Что проверено и корректно
|
||
|
||
- Все три Medium из r1 (M1, M2, M3) закрыты текстом документа, не
|
||
заявлением автора — см. таблицу «Закрытие раунда r1».
|
||
- Дельта аддитивна и не пересекается текстуально ни с одним из унаследованных
|
||
разделов — риск регрессии на уже подтверждённых AC (класс дефекта #102)
|
||
отсутствует: правки не касаются формулировок AC2–AC6, контрактов (а)–(е),
|
||
разделов «Риски»/«Release-артефакты»/«Вне скоупа»/«Откат».
|
||
- Трейлеры коммита `c50d9e42` (`Issue: #369`, `User-Visible: no`) корректны
|
||
для docs-коммита класса C.
|
||
- Владелец вернул задачу в `S4-spec-review` после правки — процесс §2.4
|
||
соблюдён (комментарий `2026-08-29T07:41:51Z`).
|
||
|
||
## Чего не проверял
|
||
|
||
- `typecheck`/`test`/`build`/`check-docs`/смоки/golden/инварианты модели —
|
||
не запускал: за всю спек-стадию #369 (оба коммита, `cbf9318e` и
|
||
`c50d9e42`) единственный тронутый файл — `docs/specs/369-audit-lows.md`;
|
||
ни `src/**`, ни `custom_components/**/*.py` не задеты ни разу, гейтам
|
||
нечего было бы доказать.
|
||
- Не проверял живьём будущие юниты/смоки для (б)–(е) и (д) — их ещё нет,
|
||
сверка «умеет ли тест падать» относится к код-ревью (§2.7).
|
||
- Не проверял точную будущую формулировку абзацев VACUUM.md/USER-GUIDE —
|
||
контракт (а) намеренно не фиксирует финальный текст, только смысл; это
|
||
было явно принято в r1 и дельта этого не меняла.
|
||
- Не проверял `docs/CONFIG-COMPATIBILITY.md` — ни один из семи пунктов не
|
||
трогает конфигурацию/схему маркера (унаследовано из r1, дельта не
|
||
расширяет скоуп).
|
||
|
||
## Вердикт
|
||
|
||
Зелёный: обе стороны условия закрытия соблюдены — все три Medium из r1
|
||
(M1, M2, M3) закрыты предметно текстом документа, новых Medium/High не
|
||
найдено. Одна Low-находка этого раунда (AC7: буквально недостижимый
|
||
repo-wide grep, асимметрия позитивной проверки) снята без правки, с
|
||
записью и ссылкой на прямой прецедент #269. Расхождение номера раунда в
|
||
автоматике (задача звала себя «r1/0», фактически это «r2/1») зафиксировано
|
||
отдельно выше — не находка по ТЗ, но стоит сообщить в процесс.
|