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

17 KiB
Raw Blame History

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