Files
houseplan-card/docs/reviews/CODE-REVIEW-459-r2.md
T
2026-09-05 08:46:15 +00:00

139 lines
20 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.
# Код-ревью #459 — заход r2
Настройка «Zigbee links»: подсказка о функции, её ограничениях и легенда цветов, пунктира и стрелок.
Ветка `issue/459-zigbee-links-help`, HEAD `50bc1d6955dc59ffbd9c10cc31be26418bf1674d`, база `origin/dev` = `7320937f627f18d0f473ca526d68906bea620a27` (после ребейза; `#457` уже влился в `dev`).
## Почему разбор ПОЛНЫЙ, а не по дельте
Раунд r2 открыт не находкой ревьюера, а ребейзом: код-ревью r1 (`951cbd01`) вынесло **зелёный** вердикт (комментарий 9), но к моменту его публикации `dev` продвинулся на 10 коммитов и слияние конфликтовало (комментарии 10–11). Автор сделал `git rebase origin/dev`, запушил `50bc1d69` и вернул `S7-code-review` (комментарий 12).
Ребейз на ушедший вперёд `dev` — это один из явно названных случаев, когда разбор остаётся полным независимо от размера дельты (§7.2, §2.9): база изменилась, а не только правка автора. Поэтому я:
1. заново прогнал все дешёвые гейты на текущем `HEAD` (ниже);
2. заново подтвердил каждый AC самостоятельно (тестом или чтением);
3. отдельно проверил, что именно изменилось при ребейзе и что — нет.
## Материал ребейза: что реально изменилось
Побайтовое сравнение продуктовых файлов задачи между `951cbd01` (материал r1) и `50bc1d69` (текущий HEAD):
```
git diff 951cbd0174bb HEAD -- src/hp-zigbee-topology-settings.ts src/i18n/topology.ts
```
— **пусто**. Файл-компонент и резолвер `hasTopologyTranslation` не изменились ни на символ.
Блок тестов `#459` в `test/zigbee-topology.test.mjs` (от маркера `// #459.` до конца файла) сравнен построчно — **идентичен**. Дифф всего файла между теми же SHA показывает изменения (162 строки), но это тесты `#457`, добавленные в `dev` после `951cbd01`, не пересекающиеся с блоком `#459`.
Четыре словаря `topology/{en,ru,de,fr}.json` получили от `#457` три новых ключа (`route_device_not_on_plan`, `route_coordinator_not_on_plan`, `route_other_space`) — не пересекаются со строками `help`/`help_aria` этой задачи и добавлены во все четыре языка одинаково (паритет AC6 не пострадал — проверено гейтом ниже).
`scripts/mutation-gate.mjs`: два мутанта `topology-help-*` этой задачи не менялись; `#457` добавил в тот же файл свои четыре мутанта рядом.
Конфликт при ребейзе (по словам автора, комментарий 12) был только в производных/документационных файлах: `docs/CHANGELOG*.md` (обе записи, `#457` и `#459`, сохранены — проверил чтением, обе на месте в обоих языках), `docs/images/screenshots.json` (отпечаток источника, пересчитан штатно) и весь закоммиченный бандл (пересобран заново). Я подтвердил это сверкой дифф-статов и содержимого, а не принял на слово.
`src/hp-help.ts`, `hp-zigbee-topology-overlay.ts`, `device-presentation.ts`, `logic.ts` — не тронуты ни этой задачей, ни ребейзом (`git diff origin/dev...HEAD` по этим путям пуст). Контракт §5 ТЗ («что не меняется») держится.
## Как проверялось
Материал — `git log --oneline origin/dev..HEAD` (5 коммитов) и `git diff origin/dev...HEAD` (42 файла: 8 продуктовых/тестовых + генерируемые + документация). Зелёного Validate на `50bc1d69` нет: единственный прогон на этом SHA (33954914944) — **failure**, один упавший джоб «Смоки в браузере (шард 1 из 3)», лог которого называет ровно `FAIL smoke_furniture` — то же самое, что автор описал в комментарии 12 и на что завёл отдельный `#460` (`trivial`, `S6-in-progress`, не про `#459`). Проверил сам, не на слово: `gh run view 33954914944 --log-failed` показывает единственную причину падения — `smoke_furniture`, диф `#459` этот файл не трогает.
Поэтому дешёвые гейты прогнал сам, на `50bc1d69`:
| Гейт | Результат |
|---|---|
| `npx tsc --noEmit` | OK, без ошибок |
| `npm test` | **1987 pass / 0 fail / 1 skip** (1988 тестов, `npm run inventory` подтверждает то же число); все 7 тестов `#459` (`AC1, AC2, AC3, AC3b, AC4, AC5, AC6`) — `ok` |
| `npm run build` | чисто, `git status` после сборки пуст — закоммиченный `dist` byte-for-byte совпадает со свежей сборкой |
| `npm run bundle:sync` | пересборка + синхронизация `custom_components/houseplan/frontend` и `demo/srv/assets`; `git status` после — чисто, три копии совпадают |
| `npm run bundle:budget` | initial View **297 496 Б** (потолок 298 000±2000, budget 300 000) — совпадает с заявленным автором числом; потолок не поднимался; предупреждение о низком запасе — унаследованный долг `#367`, не новый |
| `node scripts/check-docs.mjs` | passed, 7 файлов, 12 внешних ссылок |
| `node scripts/mutation-gate.mjs --check` | 463 якоря (совпадает с заявленным автором «Реестр 457 → 459»), оба мутанта `topology-help-*` — `ok`; один посторонний `FAIL` — backend-гейт `tests_backend/test_ha_websocket.py` падает из-за отсутствующего локально модуля `pytest`, окружение не задевает `custom_components/**/*.py` (не тронут этой задачей) |
| `node scripts/mutation-gate.mjs --id=topology-help-renders-without-aria` | **лично применил мутацию** — целевой тест покраснел, дерево вернулось чистым |
| `node scripts/mutation-gate.mjs --id=topology-help-drops-arrow-legend` | то же — покраснел, поймано |
| `node demo/smoke_zigbee_topology_hover.mjs` (AC8) | **OK**, 21/21 проверок `true` — оверлей, цвета, стрелки, default-off, ленивая загрузка не изменились |
| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | **НЕОПРЕДЕЛЁННОСТЬ** — 2 изменённых файла `src/**`, 7 символов (`DICTIONARIES`, `Lang`, `TopologyI18nKey`, `_help`, `hasTopologyTranslation`, `langOf`, `topologyT`), ни один не связан ни с одним из 223 смоков доказуемо. Единственный смок, названный в AC (`smoke_zigbee_topology_hover`, AC8), прогнан отдельно выше вручную — инструмент про него не обязан знать, так как оверлей сам не менялся |
| `npm run golden:verify` | **153/153 passed, 0 расхождений** — включая `general-color-popover-desktop-en`, единственную сцену, задетую этой задачей (принятый на r1 эталон пережил ребейз без нового расхождения) |
| `node --test test/single-source-numbers.test.mjs` | 3/3 pass (общий гейт «одно число — один источник», диф его не расширяет и не нарушает) |
Не прогонял: `python -m pytest tests_backend` (диф не трогает `custom_components/**/*.py`); полные performance-профили (не названы в AC, диф не задевает чувствительные пути); `npm run invariants` (диф не касается стен/`layout`/`marker.space`/`open_spans` — задача только про словарь и один компонент настройки, геометрия не затронута).
## Проверка по AC — заново, не по памяти r1
- **AC1** — `render()` кладёт `${this._t('title')}${this._help()}` в `.section` (строка 187 файла, не изменилась с r1), не в `.toggle`. Тест `подсказка вызывается из блока настройки топологии` проверяет именно эту разметку — прогнан, зелёный. Открытие по наведению/фокусу/клику — поведение самого `hp-help.ts` (не тронут).
- **AC2** — `_help()` проверяет `hasTopologyTranslation(lang,'help')` и `...'help_aria'` до рендера, иначе `nothing`. Мутант `topology-help-renders-without-aria` лично применён и пойман (см. таблицу выше).
- **AC3 / AC3b** — тест `подсказка называет все шесть пунктов легенды` проверяет 10 паттернов по нормативной ru-строке (LQI, 40, 180, пунктир, исходящая→координатор, входящие, «без стрелки», «подпись на конце стрелки», «отсутствие стрелки»); отдельный тест ловит оговорку о приближённости (`дерево маршрут`, `не путь пакета`). Прогнаны — зелёные. Мутант `topology-help-drops-arrow-legend` лично применён и пойман.
- **AC4** — тест импортирует `lqiColor` из собранного `test-build/logic.js` и берёт `hue(40)=0`, `hue(180)=120` из функции, затем ищет те же числа в тексте — границы не захардкожены в тесте отдельной константой. Прогнан — зелёный.
- **AC5** — для всех четырёх словарей: нет `\n` в `help`, обе строки (`help`, `help_aria`) непустые. Прогнан — зелёный.
- **AC6** — гейт паритета ключей и плейсхолдеров четырёх словарей против английского; проверил, что три новых ключа `route_*` от `#457` не сломали паритет (все четыре языка получили одинаковый набор). Прогнан — зелёный.
- **AC7** — `core-file-budget` не задет (файл не входит в список ядровых), `bundle:budget` — 297 496 Б, на 2 Б меньше `dev` по словам автора; лично прогнал и подтвердил число, потолок не поднимался.
- **AC8** — `smoke_zigbee_topology_hover.mjs` лично прогнан, все проверки `true`.
Утверждённый нормативный текст §4.1 (654 знака ru) сверен посимвольно (`len(help)==654` в Python) — совпадает дословно с текстом, зафиксированным владельцем в теле issue. Переводы en/de/fr прочитаны целиком: каждый несёт все пункты легенды и оговорку о приближённости без потерь смысла.
## Закрытие раунда r1
Раунд r1 (комментарий 9, документ `docs/reviews/CODE-REVIEW-459-r1.md`, SHA `951cbd01`) вынес **зелёный** вердикт без Medium — переоткрытия по существу не было, только вынужденный технический возврат из-за конфликта слияния (комментарий 11). Формальных «находок к исправлению» из r1 нет; ниже — судьба трёх Low, отмеченных там:
| Находка r1 (Low) | Статус в r2 |
|---|---|
| Отчёт реализации называл «шесть новых тестов», в дифе — семь | не тронуто и не требовалось: чисто редакторская неточность отчёта, к коду отношения не имеет |
| Новый `_help()` не проставляет `data-help-key` (в отличие от `houseplan-editor-runtime.ts:1232`, `houseplan-onboarding-runtime.ts:58`) | **не исправлено, по-прежнему так** — проверил чтением всех трёх мест повторно; не блокирует ни один AC, поведение кружка не страдает |
| Гейт паритета AC6 лёг в `test/zigbee-topology.test.mjs`, а не в `test/i18n.test.mjs`, как называли «Затронутые файлы» ТЗ | не тронуто — организационное расхождение, гейт работает (перепроверил падением не требовалось: тест сам содержит зелёную/фейлящую пару сценариев) |
Ни одна не блокирует и в r2 не эскалируется — все три и раньше были приняты без правки с запиской, а код с тех пор не менялся.
## Унаследовано из r1 без повторного построчного разбора
Ниже — то, что я не выводил заново с нуля, а принял на основании доказанной побайтовой идентичности кода (раздел «Материал ребейза» выше) и полного документа `docs/reviews/CODE-REVIEW-459-r1.md` (SHA `951cbd01`):
- построчная сверка контракта §1–§5 ТЗ с кодом (импорт `hp-help`, локальный `_help()`, namespace `topologyT`, отказ от переиспользования редакторского `_help()`) — код не менялся, вывод r1 в силе;
- перевод легенды на en/de/fr сверен вручную построчно в r1; я самостоятельно перечитал все три файла в этом раунде (раздел AC выше) и подтверждаю тот же вывод независимо, а не только по ссылке;
- откат (§ ТЗ «Откат») — не проверялся заново, код рендера и словарей не менялся, ревертить по-прежнему нечего кроме вызова хелпера.
Каждый AC при этом я подтвердил **заново сам** (гейты и тесты выше), а не принял на слово — «унаследовано» здесь относится только к смысловому построчному разбору контракта, не к проверяемости.
## Что проверено и корректно
- Все восемь AC доказаны прогонами на текущем `HEAD`, не на памяти о r1.
- Оба мутанта реально ловятся — применил лично, не поверил реестру.
- `golden:verify` — 153/153, включая сцену, задетую этой задачей и ребейзом.
- Ребейз не потерял ни одной записи `CHANGELOG`, не сломал паритет словарей, не задел оверлей/`hp-help`/`logic.ts`.
- Единственный красный CI на `50bc1d69` — независимо подтверждённый предсуществующий флейк `smoke_furniture` (`#460`), не имеющий отношения к `#459` и не тронутый этим диффом.
- Трейлеры коммитов: `Issue: #459` на всех пяти, `User-Visible: yes` в `feat`-коммите сопровождается правкой обоих changelog в том же коммите; `Release:`/`Baseline-Reviewed:` на коммитах, трогающих `demo/golden/baselines/**`.
## Чего не проверял и почему
- `python -m pytest tests_backend` — `custom_components/**/*.py` в дифе нет.
- Полные performance-профили — не названы в AC, диф не задевает чувствительные к перфу пути (текст словаря и один компонент настройки).
- `npm run invariants` — диф не касается стен, `layout`, `marker.space`, `open_spans` или иных геометрических ссылок.
- Остальные 222 браузерных смока сверх названного в AC8 — инструмент отбора вернул НЕОПРЕДЕЛЁННОСТЬ, тематически ни один не относится к namespace-компоненту без побочных эффектов на других поверхностях.
## Итог
Продуктовый код и тесты этой задачи не изменились ни на символ со времени зелёного r1 — ребейз задел только производные файлы и документацию, и это подтверждено сравнением, а не заявлено. Полный самостоятельный прогон гейтов на текущем `HEAD` (`50bc1d69`) — тесты, сборка, бюджет, документация, мутанты, golden, целевой смок — зелёный. Восемь AC доказаны заново. Три Low из r1 остаются в силе, без блокировки. Красный CI на этом SHA — подтверждённо посторонний флейк, заведённый отдельным issue.
**Вердикт: зелёный.**
---
<!-- material-anchors: подготовлено ревьюером r2 -->
## Материал раунда
- Ветка: `issue/459-zigbee-links-help`, HEAD `50bc1d6955dc59ffbd9c10cc31be26418bf1674d`.
- База: `origin/dev` = `7320937f627f18d0f473ca526d68906bea620a27`.
- Предыдущий материал (r1, зелёный): `951cbd0174bbbc4cb069cf5f8832da8cc49f2ace`, документ `docs/reviews/CODE-REVIEW-459-r1.md`.
- Побайтовое сравнение продуктовых файлов `951cbd01`→`50bc1d69`: `src/hp-zigbee-topology-settings.ts`, `src/i18n/topology.ts` — пусто (идентичны); блок `#459` в `test/zigbee-topology.test.mjs` — идентичен построчно.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/459-zigbee-links-help`, коммит `50bc1d6955dc` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `4cb9c23fd0ca54ef2841de18ac1b353fc54c5c3c`
```
git log --all --format='%H %T' | grep 4cb9c23fd0ca
```