Files
houseplan-card/docs/reviews/SPEC-REVIEW-29-r2.md
T
2026-08-27 23:17:52 +00:00

164 lines
15 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-29-r2
- Issue: https://github.com/Matysh/houseplan-card/issues/29
- Этап: spec (PROCESS.md §2.4)
- Заход: r2 · блокирующих циклов израсходовано 1 из 4
- ТЗ: `docs/specs/029-device-inbox-lifecycle.md`
- SHA предыдущего вердикта (r1): `a6ce1ae7e534fa4b41fcc0ab93e5ed95a693c4c1`
- SHA материала этого раунда: `375b402d940a0ea30f23becef95f9755212ecbd9`
(ветка `issue/29-device-inbox-lifecycle`, коммит «docs: preserve hidden
device ghost mode»)
- Трек: обычный, лимит циклов ревью ТЗ — 4.
## Скоуп разбора (по дельте, PROCESS.md §2.10)
Предыдущий вердикт (r1, документ `docs/reviews/SPEC-REVIEW-29-r1.md`) —
жёлтый, две находки Medium в скоупе (M1, M2), High нет. Автор ответил
комментарием в issue со ссылкой на коммит `375b402b` (фактический SHA —
`375b402d`, см. выше) и заявил закрытие обеих находок.
Дельта — `git diff a6ce1ae7e534fa4b41fcc0ab93e5ed95a693c4c1..HEAD` по файлу
ТЗ: 36 добавленных / 11 удалённых строк, все — в §10.1, §10.2, §10.3, §10.4,
§14, AC1, AC4, AC6, §18 (риски). Дельта локальна: один документ, правки
адресуют ровно M1 и M2, новая подсистема не затронута, поведенческий
контракт вне уже согласованных Q1/AC не меняется, объём дельты не
сопоставим с объёмом исходного ТЗ (548 строк). Условия «разбор остаётся
полным» (ребейз на ушедший вперёд `dev`, смена контракта, новая подсистема,
сопоставимый объём) не выполнены — сокращаю объём разбора до дельты и её
последствий для AC1, AC4, AC6, §14, §18.
Продуктового кода на этом этапе нет, гейты §8 к ревью ТЗ не относятся (как
и в r1).
## Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| **M1** (Medium, в скоупе) — §10.1 убирал существующий режим просмотра скрытых/HA-disabled маркеров призраками на плане, не давая эквивалента в каталоге (`Find` работал только для реально отрисованного marker) | Автор не выбрал ни один из трёх предложенных путей закрытия буквально, а снял противоречие в корне: существующий локальный ghost-режим **сохранён** целиком (позиция, выбор, настройка, drag), его переключатель просто переносится с панели внутрь каталога как новый switch «Показывать скрытые на плане», доступный во всех вкладках. `Find` для скрытых/HA-disabled строк теперь доступен при включённом ghost-режиме (было — недоступен вовсе) | `docs/specs/029-device-inbox-lifecycle.md` §10.1 (новый абзац «Существующий локальный режим… сохраняется»), §10.2 п.6 (новый switch), §10.3 (столбец «Дополнительные действия» для трёх категорий — «Найти призрак при включённом ghost-режиме»), §10.4 (переписанное условие доступности `Find`), AC1 («Локальный ghost-toggle доступен внутри каталога, а его активность видна на кнопке «Устройства»»), AC4 (переписан), AC6 (добавлен абзац с явным описанием эквивалентности старому поведению), §18 (новая строка риска с защитой) |
| **M2** (Medium, в скоупе) — `marker.hide_tip` (ru/en) ссылался на удаляемую кнопку «Скрытые и деактивированные» / «Hidden and disabled», §14 не включал обновление существующих строк | §14 получил новый абзац: `marker.hide_tip` обновляется на обеих языках, чтобы вести в каталог «Устройства», плюс явное требование поиска прочих ссылок на старые названия кнопок перед реализацией | `docs/specs/029-device-inbox-lifecycle.md` §14, новый абзац сразу после списка тестируемых строк i18n |
Обе находки закрыты по существу, а не декларативно: текст, который был
предметом претензии (удаление режима просмотра позиции; необновлённая
подсказка), в текущей редакции отсутствует, проверено чтением файла
целиком, а не поиском одной фразы.
## Проверка дельты
- **AC1** (golden + smoke): формулировка «активность видна на кнопке
«Устройства»» — проверяемое поведение (класс/атрибут кнопки для smoke,
визуальное состояние для golden), метод доказательства не изменился и
остаётся адекватным.
- **AC4** (unit): новая формулировка «Show недоступен, а Find доступен
только для призрака при включённом ghost-режиме» согласована с §10.3/§10.4
той же дельты — противоречий нет.
- **AC6** (smoke): добавленный абзац описывает переключатель ghost-режима
как воспроизводящий текущее поведение 1:1 (позиция/выбор/настройка/drag,
без изменения `marker.hidden`, сброс при выходе из редактора) — это то же
утверждение, что и в §10.1, не новое обязательство.
- **§14** (i18n): новый пункт обязывает обновить `marker.hide_tip` в обоих
языках и найти прочие ссылки на старые названия кнопок. Точечно
перепроверено по коду (не только по тексту ТЗ): `src/i18n/ru.json:673` и
`src/i18n/en.json:673` — единственная пара строк с буквальной ссылкой на
удаляемую кнопку (тот же результат, что и в r1); `title.show_all` и
`devbar.show_all` и так входят в объём замены по §10.1 и отдельного
упоминания не требуют.
- **§18** (риски): новая строка «Перенос ghost-toggle делает активный режим
незаметным после закрытия каталога» ссылается на AC6 как на защиту, но
видимое active-state кнопки описано в AC1, а не в AC6 — см. находку L1
ниже.
Остальные AC (AC2, AC3, AC5, AC7–AC11) дельтой не задеты текстуально и не
зависят по доказательству от изменённых разделов — наследую вывод r1 без
повторной проверки (см. «Унаследовано»).
## Находки
### L1 (Low) — риск в §18 ссылается не на тот AC
**Файл:** `docs/specs/029-device-inbox-lifecycle.md`, §18, строка риска
«Перенос ghost-toggle делает активный режим незаметным после закрытия
каталога».
Защита названа как «Активное состояние кнопки «Устройства» и сброс при
выходе из редактора; AC6». Однако видимое active-state кнопки «Устройства»
описано в AC1 («его активность видна на кнопке «Устройства»»), а не в AC6
(AC6 — про воспроизведение самого ghost-поведения: позиция/выбор/drag).
Сброс режима при выходе из редактора действительно упомянут в AC6 —
ссылка верна только для половины защиты.
Не создаёт двусмысленности AC и не блокирует реализацию: оба AC
(AC1 и AC6) существуют, проверяемы и в сумме покрывают заявленную защиту —
перепутана только адресация в таблице рисков.
**Решение ревьюера:** снимается без правки текста ТЗ. Основание —
PROCESS.md §2.4 «Low либо правится, либо снимается решением ревьюера с
записью»: неверная перекрёстная ссылка в информационной таблице рисков не
меняет ни один AC, не вводит в заблуждение исполнителя относительно того,
*что* защищает риск (только относительно того, *под каким номером* это
искать), и правка не стоит очередного цикла ревью на light-независимой
задаче с уже потраченным одним из четырёх циклов.
## Унаследовано из r1
Документ: `docs/reviews/SPEC-REVIEW-29-r1.md`, SHA `a6ce1ae7e534fa4b41fcc0ab93e5ed95a693c4c1`.
Принято без повторной проверки в r2, поскольку дельта их не касается:
- Соответствие `docs/SCOPE.md` (job J4/J6, персона Home admin, отсутствие
выхода за продуктовую рамку, `Не входит §6`).
- Формальная полнота §7.1 (все обязательные разделы присутствуют).
- AC1–AC11 однозначны, у каждого назван способ доказательства (кроме
точечных правок AC1/AC4/AC6, разобранных выше в этом раунде).
- Корректность отражения решения владельца по Q1 в §7.2 и AC3.
- Проверка фактических утверждений о текущем поведении кодом и каноном:
`_bindingCandidates`, `HaBindingStatus.kind`, `marker.hidden`/`marker.removed`,
`.slice(0, 200)` кап, `duplicate_name_area` устарела, фильтрация
`new_device_ids` от уже скрытых id, терминология «Добавить»/«Правила
иконок».
- §21 «Принятые технические предположения» — маркировка предположений
корректна и отделена от продуктовых решений (кроме пробела M1, который
в r2 закрыт не через §21, а прямым сохранением поведения — что делает
сам вопрос неактуальным: решать стало нечего, поведение не меняется).
- Существование тестовых артефактов: `test/devices.test.mjs`,
`test/ha-binding-status.test.mjs`, `test/device-presentation*.test.mjs`,
`demo/smoke_hidden_flag.mjs`, `demo/smoke_binding_picker.mjs`,
`scripts/smoke-select.mjs`.
- Откат (§19) реалистичен, обратно совместим.
## Что проверено и признано корректным (в этом раунде)
- M1 и M2 закрыты по существу — см. таблицу выше.
- Новый текст §10.1–§10.4 и AC1/AC4/AC6 внутренне непротиворечив: описание
условия доступности `Find` (§10.4) совпадает с формулировками в таблице
действий (§10.3) и в AC4.
- Новая формулировка `marker.hide_tip` (замена ссылки на кнопку) не создаёт
нового расхождения с `src/i18n/*.json` — единственная затронутая пара
строк подтверждена прямым чтением файлов.
- Старый (ошибочный) текст §10.1 «Скрытые markers больше не рисуются…» и
«устраняет состояние панели» полностью удалён из документа — проверено
`grep` по всему файлу, совпадений нет.
- Терминология не расходится с `docs/USER-GUIDE.ru.md`/`FILTERING.md`:
старое «Скрытые и деактивированные» (панель) и новое «Показывать скрытые
на плане» (switch внутри каталога) — разные элементы UI с разной
областью действия, противоречия нет; обновление USER-GUIDE уже стоит в
§20 Release-артефакты.
## Чего не проверял
- Не повторял полную сверку §7.1/AC1–AC11 с кодом — она выполнена в r1 и
дельта её не аннулирует (см. «Унаследовано»).
- Не запускал никакие гейты (`typecheck`/`test`/`build`/смоки/`check-docs`) —
на этапе ТЗ продуктового кода нет, диф не затрагивает `src/**`, гейты §8
относятся к код-ревью.
- Не проверял `docs/ARCHITECTURE.md`/`docs/CANVAS.md` — дельта их не
касается.
- Не оценивал заново перформанс (§17 дельтой не затронут).
## Вердикт
High-находок нет. Обе Medium-находки r1 закрыты по существу и подтверждены
чтением текущего файла, а не заявлением автора. Единственная новая находка
этого раунда — Low, снята решением ревьюера с записью, без возврата на
правку.
**Вердикт: зелёный.** ТЗ переходит в «Готово к разработке».