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

17 KiB

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-находка снята с запиской выше без правки ТЗ.


Материал раунда

  • Ветка: dev, коммит `` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Якоря снять не удалось: ветки задачи нет, материал читался по dev.