Files
houseplan-card/docs/reviews/SPEC-REVIEW-369-r1.md
T
2026-08-29 07:48:25 +00:00

198 lines
17 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-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») зафиксировано
отдельно выше — не находка по ТЗ, но стоит сообщить в процесс.