# SPEC-REVIEW-457-r1 - Issue: [#457](https://github.com/Matysh/houseplan-card/issues/457) — Топология Zigbee: показывать направление связи стрелкой на конце линии - ТЗ: `docs/specs/457-zigbee-route-arrows.md` - SHA материала: `f3a1dd84` (ветка `issue/457-zigbee-route-arrows`, коммит «docs: specify Zigbee route arrows») - Заход: r1 · блокирующих циклов израсходовано 0 из 4 - Трек: полный (issue помечен `feature`, `P2`, `S4-spec-review`; `small` не назначен, и это соответствует критериям §5 — новый UX-контракт, производная графовая модель, новые i18n-строки и browser-smoke) ## Скоуп ревью ТЗ описывает дерево аплинков Zigbee-топологии к координатору (BFS от узла с `role: coordinator`), стрелки на рёбрах этого дерева при hover-диагностике, и parent bubble для случаев, когда следующий узел на пути нельзя показать на плане (другое пространство / нет маркера у устройства / нет маркера у координатора). Развивает существующий контракт #54 (`docs/specs/054-zigbee-topology-overlay.md`), который явно исключал «вычисление либо изображение маршрута до координатора» из своего non-scope — #457 законно открывает этот пункт отдельной задачей. Я не пишу и не правлю ни ТЗ, ни продуктовый код — только оцениваю. ## Как проверялось Ревью ТЗ на этом этапе не гоняет гейты (кода ещё нет — весь diff класса A/B/D пуст, изменена только документация класса C). Работа состояла в очной сверке каждого фактического утверждения ТЗ с реальным состоянием репозитория на SHA `f3a1dd84`, а не с пересказом автора: - прочитан `docs/SCOPE.md` — задача продолжает J7 («Is my Zigbee mesh healthy here?»), конфликта со scope нет, это уже подтвердила и аналитика Codex в issue; - прочитан `PROCESS.md` §2.4, §2.9 (не применимо к r1), §5, §7.1, §7.2 — все обязательные разделы шаблона проверены поимённо; - прочитано тело issue #457 целиком и все 6 комментариев (аналитика владельца, два уточнения владельца, финальные решения владельца, аналитика и оценка Codex, хендофф на ревью) — сверено, что все продуктовые вопросы закрыты решениями владельца **до** написания ТЗ, а не оставлены на усмотрение реализации; - прочитан `docs/USER-GUIDE.ru.md` (раздел про Zigbee-диагностику, строки 200–230) и `docs/USER-GUIDE.md` — терминология «Показывать связи Zigbee при наведении на устройство», admin-only, mouse-only, «снимок соседства, а не маршрут каждого текущего пакета» уже зафиксирована и ТЗ её не переизобретает; - прочитан базовый контракт `docs/specs/054-zigbee-topology-overlay.md` целиком — проверено, что #457 не противоречит и не дублирует его AC, а расширяет ровно тот пункт non-scope, который #54 сознательно оставил открытым; - построчно сверены все технические утверждения ТЗ с текущим кодом: `src/zigbee-topology.ts` (модель `ZigbeeTopologyLink`/`ZigbeeHoverLine`, `observation()` — `relationship` действительно принимает только `string`, `resolveMappedTopologyHover`, `omittedCount` действительно нигде не выводится, `mapTopologyNodes` — четыре причины `unmatched_device` / `ambiguous_placement` / `provider_scan_failure` / `drawable()`), `src/hp-zigbee-topology-overlay.ts` (`viewBox="0 0 100 100" preserveAspectRatio="none"`, `.halo` радиус `1.22`, `remote` bubble, `pointer- events: none`, `forced-colors` блок), `src/zigbee-topology-overlay-bridge.ts` и вызов в `src/houseplan-card.ts` (нет проекции названий пространств в overlay сегодня, но паттерн `space.title || space.id` уже существует в файле и его перенос в bridge не требует нового конфига), `src/zigbee-topology- settings.ts` (`enabled: false` по умолчанию); - прочитана реальная анонимизированная фикстура `test/fixtures/zigbee2mqtt-networkmap-real-anonymized.json` — подтверждено, что координатор (`type: Coordinator`) присутствует, `relationship: 2` (число) и `depth: 15` действительно в данных, как заявлено в ТЗ; perf-бюджеты `normalize 80 ms / map 160 ms / first hover 180 ms / 20 repeated hovers 120 ms` в §12 ТЗ дословно совпадают с `BUDGETS` в `demo/benchmark_zigbee_topology.mjs`, а лимиты «1000 nodes / 6000 links» — с `TOPOLOGY_MAX_NODES`/`TOPOLOGY_MAX_LINKS`; проверено существование всех названных в AC инструментов доказательства: `test/zigbee-topology.test.mjs`, `demo/smoke_zigbee_topology_hover.mjs`, `demo/benchmark_zigbee_topology.mjs` — ни один AC не ссылается на несуществующий или придуманный гейт; - проверено, что backend Python не затронут: `grep` по `custom_components/` на «zigbee» не находит ничего, provider-обращения (`zha/devices`) идут через `hass.callWS` из фронтенда — подтверждает §16 «Backend Python не меняется»; - сверен формат `docs/CHANGELOG.md`/`docs/CHANGELOG.ru.md` (секция `## Unreleased`) с требованием §19 добавить одну пользовательскую запись. Полный набор автотестов/typecheck/build на этом этапе не запускался — кода нет, гонять нечего; это ожидаемо для ревью ТЗ и не является пропуском гейта. ## Находки Блокирующих (High) и находок в скоупе (Medium) нет. ### Low — зафиксировано, без правки 1. **§8, «При недостатке места bubble может сместиться на противоположную сторону marker»** не даёт алгоритмического критерия «недостатка места» (порог, приоритет сторон). Это единственное место контракта, где решение явно оставлено на усмотрение реализации, но не занесено в «Принятые предположения» (§20). Не блокирует: AC9 всё равно требует, чтобы bubble «оставался внутри видимой области overlay» и не обрезал текст, то есть наблюдаемое поведение проверяемо независимо от конкретного алгоритма позиционирования. Снимаю без правки: технический выбор компоновки внутри уже описанного визуального инварианта — по §7.1 такие решения ревьюер вправе не эскалировать, а просто отметить. ## Что проверено и корректно - Обязательные разделы §7.1 присутствуют все и в порядке, продиктованном процессом: сценарий → что человек увидит → проблема → скоуп/не-скоуп → контракт → UX → данные/миграция → i18n → AC → тесты → риски → откат → release-артефакты (§§1–19 ТЗ). - Оба «первых» продуктовых раздела (сценарий, что человек увидит до/после) называют персону из `docs/SCOPE.md`, поверхность и момент, без терминов реализации в разделе 2 — соответствует требованию. - Открытых продуктовых вопросов нет: все три развилки, которые обсуждались в комментариях (смысл стрелки, поведение при недоступном endpoint, поведение при неизвестном пути) закрыты явными решениями владельца **до** написания ТЗ, и ТЗ им не противоречит ни в одном разделе. - Технических догадок, выданных за факт и не помеченных как предположение, не найдено: единственное место, где реализация имеет свободу (алгоритм выбора родителя, имена типов, точный визуальный размер наконечника, formula позиционирования bubble при недостатке места) либо прямо названо в §20 «Принятые предположения», либо (см. Low выше) не влияет на проверяемость AC. - Каждый AC1–AC12 — проверяемое утверждение с явным способом доказательства (unit / resolver unit / browser smoke / benchmark / bundle-budget / check-docs), без формулировок вида «работает корректно» без критерия. Инвариант «по стрелкам приходишь в координатор» сформулирован как свойство дерева (§6.4), а не как свойство итоговой картинки — ТЗ прямо проговаривает, почему это важно (обрыв цепочки на неразмещённом/удалённом узле не должен делать AC1 невыполнимым в обычном доме). - Конструкция «дерево аплинков по полному графу, включая неразмещённые узлы, рисуется только по размещённым» корректно закрывает реальный дефект текущего кода: `omittedCount` в `resolveMappedTopologyHover` сегодня действительно считается, но нигде не выводится (проверено чтением `src/zigbee-topology.ts:307-333` и `src/hp-zigbee-topology-overlay.ts`) — ТЗ не выдумывает проблему. - Все построчные технические ссылки на код и фикстуры, использованные для обоснования решений (числовой `relationship` у Z2M, `depth: 15`, отсутствие проекции названий пространств в overlay, дефолт `enabled: false`, perf-бюджеты, лимиты 1000/6000), подтверждены чтением соответствующих файлов — расхождений с фактическим состоянием кода не найдено. - Не-скоуп (§5) корректно исключает смежные, но посторонние задачи (асимметрия LQI, постоянный mesh-граф, touch-жест, автоматическое размещение координатора) и совпадает с явными решениями владельца в issue не заводить их сейчас. - Откат (§18) и release-артефакты (§19) содержательны и не формальны: пользовательский откат — существующий тумблер, кодовый откат — конкретные поверхности для удаления. ## Чего не проверял - Реализуемость perf-бюджетов на **новом** коде (построение дерева) — это предмет измерения на этапе код-ревью через `npm run benchmark:zigbee-topology`, сейчас кода не существует. - Полный typecheck/test/build/golden/check-docs — не применимо к этапу ТЗ, код не менялся (diff — только `docs/specs/457-zigbee-route-arrows.md`). - Фактическую компоновку bubble в браузере (описанный в Low пункт) — там нет кода для запуска, только текст контракта. - Backend-тесты (`pytest tests_backend`) — не применимо, Python не затронут ни диффом, ни планом ТЗ. ## Вердикт Полный, тщательно проверенный по коду и данным документ, без открытых продуктовых вопросов и без непомеченных догадок. Единственное замечание — Low, снято без правки с записью в этом документе. `Вердикт: зелёный · заход r1 · блокирующих циклов 0/4 · High: 0 · Medium: 0` --- ## Материал раунда - Ветка: `issue/457-zigbee-route-arrows`, коммит `f3a1dd84e19d` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `0c76f7e322a59f7e81bbae32a7b013d63584ca5f` ``` git log --all --format='%H %T' | grep 0c76f7e322a5 ``` - ТЗ `docs/specs/457-zigbee-route-arrows.md`, блоб `2c8350e397b0192a8f0db3d1c3ee0bf9f5f6e1f6` ``` git log --all --find-object=2c8350e397b0192a8f0db3d1c3ee0bf9f5f6e1f6 -- docs/specs/457-zigbee-route-arrows.md ```