mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 13:18:58 +00:00
145 lines
17 KiB
Markdown
145 lines
17 KiB
Markdown
# 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`.
|