Files
houseplan-card/docs/reviews/SPEC-REVIEW-459-r1.md
T
2026-09-05 07:25:50 +00:00

145 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-459-r1
**Issue:** #459 «Настройка «Zigbee links»: подсказка о функции, её ограничениях и легенда цветов и пунктира»
**Этап:** spec (лёгкий трек, `small`), PROCESS.md §2.4 / §5
**Заход:** r1 · блокирующих циклов израсходовано 0 из 2 (лимит лёгкого трека)
**Материал:** ТЗ в теле issue #459, редакция после комментария автора «ТЗ готово (лёгкий трек)» от 2026-09-05T07:16:30Z. Зависимость — issue #457 (`S6-in-progress`, ТЗ `docs/specs/457-zigbee-route-arrows.md` на ветке `issue/457-zigbee-route-arrows`, зелёное ревью r1). Репозиторий проверялся на `dev` `6af6d7b3` — код этой задачей ещё не тронут.
## Скоуп ревью
Лёгкий трек: ТЗ живёт в теле issue, отдельный файл в `docs/specs/` не создаётся — проверяю по шаблону §5 (проблема · контракт · AC1…ACn с доказательством · откат) и по общим требованиям §7.1 там, где они применимы (сценарий и «что человек увидит» — неявно, но выводимы из контракста §1). Задача полностью зависит от ещё не смержённого #457: проверил отдельно, что #457 достиг состояния, на которое можно ссылаться (спека принята, ревью зелёное r1), и что все технические утверждения #459 о поведении #457 (ключи, инвариант дерева, разделы §6–§9, §11) соответствуют реальному принятому тексту `docs/specs/457-zigbee-route-arrows.md`, а не являются пересказом по памяти.
## Как проверялось
Построчная сверка каждого фактического утверждения ТЗ с текущим кодом на `dev` и с принятым ТЗ #457:
- `src/hp-zigbee-topology-settings.ts:159-171` — секция `.section`/`.toggle`/`.hint`, ключи `title`/`hint` через `_t()` → `topologyT`. Место установки кружка («у заголовка, не у тумточка») соответствует реальной структуре шаблона.
- `src/hp-zigbee-topology-overlay.ts:185-189` — сплошная линия/`lqiColor`/пунктир `5 5` при `lqi === undefined`; номера строк в ТЗ (187-189) совпадают с фактическими.
- `src/logic.ts:8-11` (`lqiColor`) — `hue = clamp(0,120, (lqi-40)/140*120)`; `lqiColor(40)` → hue 0 (красный край), `lqiColor(180)` → hue 120 (зелёный край). Формула и граничные числа в ТЗ точны.
- `src/houseplan-editor-runtime.ts:1228-1233` (`_help()`) — реальный fail-closed паттерн: `nothing`, если `hasTranslation` не находит `key` или `${key}.aria`. Подтверждает, что описанное в ТЗ поведение «как у `_help()`» списано с кода, а не придумано.
- `src/i18n/topology/{en,ru,de,fr}.json` — по 25 ключей в каждом, плоские имена через `_` (`zha_hint`, `z2m_topics`, …), без точечной нотации. Подтверждает выбор имён `help`/`help_aria` (не `help.aria`) как согласованный с namespace, а не произвольный.
- `src/i18n/topology.ts` (`topologyT`) — `DICTIONARIES[lang]?.[key] ?? en[key] ?? key`. Важно для находки Low ниже.
- `test/i18n.test.mjs` и `test/i18n-dead-keys.test.mjs` — оба реально проверяют только основной словарь + `support`; `topology` не участвует ни в одном. Утверждение ТЗ «namespace не покрыт ни одним из существующих гейтов» подтверждено чтением, не только словами автора.
- `test/core-file-budget.test.mjs` — отслеживает только `houseplan-card.ts` и `houseplan-editor-runtime.ts`; `hp-zigbee-topology-settings.ts` в «Затронутых файлах» ТЗ не входит в отслеживаемые, и это не создаёт проблемы: правка не касается вызывающей стороны в `houseplan-editor-runtime.ts` (импорт и точка использования `<hp-zigbee-topology-settings>` не меняются, только внутренний рендер компонента), поэтому AC7 не сломан ссылкой на нерелевантный гейт — гейт просто останется зелёным, потому что диффа в отслеживаемых файлах нет.
- `src/hp-help.ts:80-85` — `max-width: min(320px, calc(100vw - 16px))`, `max-height: calc(100vh - 16px)`, `overflow: auto`. Подтверждает аргумент ТЗ «всплывашка вмещает» текст 500–700 знаков без изменения CSS общего компонента.
- `demo/smoke_zigbee_topology_hover.mjs:47,59` — обращается к `hp-zigbee-topology-settings` по тегу, не проверяет разметку самого заголовка секции; добавление кружка рядом с заголовком не должно задеть существующие проверки. AC8 правдоподобен.
- Сверка #457: прочитан принятый `docs/specs/457-zigbee-route-arrows.md` целиком (§1–§20) и все 8 комментариев issue #457. Ключи `route_other_space`, `route_device_not_on_plan`, `route_coordinator_not_on_plan` (§11), формулировка «новый цвет или legend не вводятся» (§8), правила исходящей/входящей стрелки, bubble только для родительского направления (§7.1–7.4), приближённость дерева между роутерами (§6, §10 через комментарий с решениями владельца) — всё это в #459 процитировано точно, без искажений и без домысливания того, чего в #457 нет.
Гейты не гонялись — код этой задачей не менялся, что и ожидается на этапе ТЗ.
## Находки
### Medium (в скоупе) — AC не покрывают главное новое содержимое подсказки
Контракт §4 п.2 обязывает подсказку назвать (дословно из тела issue): исходящую
стрелку («следующий шаг к координатору, одна исходящая у устройства и ни одной
у координатора»), входящие стрелки («те, кто ходит через него»), линию без
стрелки («запасной сосед»), подпись на конце стрелки («цель не на этом плане»)
и полное отсутствие стрелки («путь до координатора неизвестен»); п.3 отдельно
требует фразу-предостережение, прямо унаследованную от #457 §6 — «стрелки
показывают дерево маршрутов, которое строит House Plan, а не путь пакета в эту
секунду».
Ни один AC это не проверяет. AC3 — единственный, что смотрит на содержание
текста, — требует лишь «три состояния линии (сплошная цветная, серый пунктир,
со стрелкой)» и две границы шкалы LQI. Реализация, которая опишет только
цвет/пунктир линии и упомянёт слово «стрелка» вскользь, не называя направления,
не называя bubble/подпись и не давая оговорку про дерево-а-не-маршрут-пакета,
формально проходит AC1–AC8 целиком — притом что именно эти пункты являются
причиной, по которой задача вообще ждала #457 (первая строка issue: «После #457
вопросов станет больше»). Это ровно тот случай из §2.4: «жёлтый вердикт допустим
и при полностью выполненных AC, если изменение не решает заявленный сценарий».
**Воспроизведение риска:** возьмите черновик текста «Shows who talks directly to
whom. Line color = signal quality (red at 40, green at 180 and above), solid =
known, dashed = unknown.» — он проходит AC1, AC2, AC4, AC5 (нет `\n`), AC6, AC7,
AC8 и почти проходит AC3 (упомянуты сплошная/пунктир и обе границы), но не
содержит ни слова о стрелках, bubble или приближённости дерева. Пробел в AC его
не ловит.
**Правка:** расширить AC3 (или добавить AC3b) явным перечислением: строка `help`
для каждого языка должна свидетельствовать о (а) исходящей стрелке = аплинк к
координатору, (б) входящих стрелках = маршрутизация через устройство, (в) линии
без стрелки = запасной сосед, (г) отсутствии стрелки у всего устройства = путь
неизвестен, (д) подписи на конце стрелки = цель не на плане, и отдельно —
наличии оговорки о приближённости («дерево, не фактический путь пакета»).
Доказательство может остаться unit-проверкой по словарю (наличие ключевых слов/
фраз в EN, как и для остальных AC этой задачи) — изменение дешёвое, это не
довод против скоупа, только против полноты текущего перечня AC.
### Low — фикстура для AC2 не покрыта в «Затронутых файлах»
`topologyT()` (`src/i18n/topology.ts:13-15`) устроен так, что отсутствие ключа
не проявляется как «нет значения»: `DICTIONARIES[lang]?.[key] ?? en[key] ?? key`
— в худшем случае возвращает буквальный `key`, но никогда `undefined`/`''`.
Реальный `_help()` (`src/houseplan-editor-runtime.ts:1228`) отличает
«есть перевод» от «нет» через отдельную функцию `hasTranslation`, которая
привязана к основному словарю и не годится для `topology`-namespace без
адаптации. Значит фраза ТЗ «локальный аналог… и `nothing`, если хотя бы одного
ключа нет» не может быть реализована через один `topologyT()` — нужен ещё один
маленький примитив проверки присутствия ключа для `topology`-словарей (или
локальная функция, принимающая словарь параметром, что заодно объясняет
«словарь-фикстуру» из доказательства AC2). Ни этот примитив, ни файл, в котором
он появится, не названы в разделе «Затронутые файлы».
Это чисто техническая деталь (§7.1: «всё, чего пользователь не наблюдает,
агенты решают сами»), не продуктовый вопрос, и не блокирует выполнимость AC2 —
разработчик может выделить чистую функцию `resolveTopologyHelp(lang, dict)`
с тестируемой фикстурой, ровно как описано в доказательстве. Снимаю без
правки: реализатору достаточно одной строки экспорта или локальной функции,
называть это отдельным пунктом ТЗ избыточно.
## Что проверено и корректно
- Лёгкий трек подтверждён: все пять критериев §5 не нарушены, оценка автора
(сложность 2, одна поверхность, без миграции/UX-контракта/perf-touch влияния)
совпадает с моей собственной проверкой по коду.
- Все технические цитаты (номера строк, формула `lqiColor`, структура словарей,
паттерн `_help()`, отсутствие покрытия `topology` в существующих i18n-гейтах,
CSS `hp-help`) подтверждены чтением актуального кода — ни одна не оказалась
устаревшей или придуманной.
- Ссылки на #457 точны: имена ключей (`route_other_space`,
`route_device_not_on_plan`, `route_coordinator_not_on_plan`), формулировка «новый
цвет или legend не вводятся» (§8 принятого ТЗ #457), правила направления
стрелок и bubble (§7.1–7.4) процитированы без искажений; #457 находится в
состоянии, дающем право на них ссылаться (принятое ТЗ, зелёное ревью r1,
`S6-in-progress`).
- AC6 (гейт паритета словарей `topology`) обоснован верно: `test/i18n.test.mjs`
и `test/i18n-dead-keys.test.mjs` действительно не видят namespace `topology`
ни в одном виде; добавление паритета в рамках этой же задачи, которая кладёт
туда новые ключи, — не выход за скоуп (это тот же диф, а не соседний дефект).
- AC7 корректен несмотря на то, что `hp-zigbee-topology-settings.ts` не входит в
отслеживаемые `core-file-budget` файлы: диф не касается вызывающей стороны в
`houseplan-editor-runtime.ts`, поэтому гейт остаётся релевантной (хоть и не
меняющейся) проверкой, а не бутафорией.
- Откат прост и корректен: без сохранённого состояния и конфига — оговорка
верна, действие обратимо одним коммитом.
## Чего не проверял и почему
- Гейты (`typecheck`, `test`, `build`, golden, смоки) не гонялись — на этапе
ТЗ код не менялся, гонять нечего.
- Не проверял качество перевода RU/DE/FR текста подсказки — на этом этапе текст
ещё не написан; за содержательную полноту всех четырёх языков будет отвечать
код-ревью (или, как минимум, EN-версия по AC3/AC3b, доработка выше).
- Не проверял бюджет бандла (`bundle:budget`) числом — компонент и так лежит в
ленивом чанке редактора вместе с `hp-help` (оба импортируются только из
`houseplan-editor-runtime.ts`/`houseplan-onboarding-runtime.ts`), рост
стартового View-графа противоречил бы архитектуре чанков; проверено чтением
импортов, не измерением.
## Вердикт
Жёлтый: одна Medium-находка в скоупе задачи (AC3 не покрывает содержание,
ради которого затевалась задача и ради которого она ждала #457). High-находок
нет. Low-находка снята с запиской выше без правки ТЗ.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `dev`, коммит `` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Якоря снять не удалось: ветки задачи нет, материал читался по `dev`.