19 KiB
CODE-REVIEW-378-r2
- Issue: #378 — «Значение + состояние»: выбор источника значения, как у бейджа
- Этап: код-ревью (PROCESS.md §2.7)
- Заход: r2 · блокирующих циклов израсходовано 1 из 4 до этого вердикта
- SHA материала:
e524f07ddecf81c3e6f8cbd6e64775ff97b69d26(git rev-parse HEADсверен непосредственно перед подведением итогов) - Диапазон дельты: предыдущий вердикт (r1) получен на
552b78a134026f08...; дельта —552b78a1..e524f07d(git diff 552b78a1..e524f07d) - Полный диапазон:
origin/dev...HEAD=6e1d9364..e524f07d;origin/devне сдвинулся с r1 (git merge-base origin/dev HEAD=6e1d9364) — ребейза не было, полный повторный разбор не требуется - Спецификация:
docs/specs/378-value-face-source.md, ревью ТЗ зелёное - Предыдущее код-ревью:
docs/reviews/CODE-REVIEW-378-r1.md— жёлтый, High: 0, Medium: 1 (в скоупе) - Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0
Скоуп разбора
Дельта локальна и мала: два коммита, оба вне src/**:
dbbe94ae—demo/golden/matrix.mjs(+19 строк, новый сценарий),test/golden-matrix.test.mjs(+18 строк, структурный тест), плюс пересобранные bundle-копии (см. ниже почему).e524f07d—docs/images/06-device-editor.png,docs/images/09-device-info.png,docs/images/screenshots.json(пересъёмка после смены source fingerprint).
origin/dev не двигался, поведенческий контракт не менялся, новая подсистема
не задета, объём дельты много меньше исходной задачи → разбор ведётся по
дельте, а не заново, per PROCESS.md §2.9.
Единственная находка r1 (Medium: golden-доказательство AC2 отсутствует) — предмет этого раунда. Всё остальное — унаследовано.
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
Medium — AC2 не доказан golden-сценарием: ТЗ трижды требует golden-проверку явного источника (cover.current_position=42 → 42 %), но demo/golden/** не был тронут в r1 |
В dbbe94ae добавлен сценарий device-value-face-cover-source-dark в demo/golden/matrix.mjs: маркер golden-left-linkquality переопределён на binding: 'entity:cover.golden_door_half', display: 'value', value_source: {kind: 'entity_attribute', entity_id: 'cover.golden_door_half', attribute: 'current_position'}, stateOverrides даёт current_position: 42. test/golden-matrix.test.mjs (+тест «explicit cover value-source golden pins the 42 percent value face») проверяет структуру сценария построчно. GOLDEN_MATRIX_VERSION поднят 50→51 |
demo/golden/matrix.mjs:496-514, test/golden-matrix.test.mjs:591-607. Лично прогнал node --test test/golden-matrix.test.mjs — 43/43 зелёных; смутировал attribute: 'current_position' → 'wrong_attribute' — тест честно упал (not ok 26, diff показывает ожидаемое/полученное значение); откатил мутацию, дерево чистое. Лично прогнал npm run golden:capture -- --scenario=device-value-face-cover-source-dark и визуально проверил PNG-кадр: капсула показывает ровно 42 % на изолированном маркере — контракт AC2 выполнен буквально |
Baseline в demo/golden/baselines/** сознательно не принят разработчиком —
это верно по процессу, не пропуск: PROCESS.md §6 (таблица ролей) прямо
запрещает роли «Разработчик» «принимать golden»; принятие эталонов относится к
предрелизному окну между S8-merged и выпуском (§11.4). Ревьюер тоже не
принимает golden — не входит в роль «Ревьюер кода» (§6). Проверить AC2 без
принятого baseline можно и нужно ручным просмотром снятого кадра — это и
сделано (см. таблицу выше).
Унаследовано из r1
Из docs/reviews/CODE-REVIEW-378-r1.md (документ на SHA 552b78a1) принято без
повторной проверки — дельта этого раунда их не задевает:
- AC1 (список источников/сохранение) —
valueBadgeCandidates(),valueSourceWriteFields(), unit+smoke доказательства. - AC3 (паритет formatter badge/face) — общий
resolveValueSource(). - AC4 (legacy-совместимость) — auto-ветка байт-в-байт прежняя.
- AC5 (fail explicit: dash + диагностика) — мутационная проверка выполнена в r1.
- AC6 (независимость от лица virtual/derived_lqi) — приоритет в коде, не только в комментарии.
- AC7 (backend-валидация и reference seam: rewrite/rebind/export/import) —
validate_source(),_drop_invalid_import_marker_links,_repair_target_space_refs, счётчикMAX_MARKERS*(MAX_CONTROLS+2). - AC8 (preview/Cancel/binding reset) — единственная точка сброса
value_sourceна смену binding. - AC9 (локализация 4 языков, документация в том же коммите,
User-Visible: yes+ оба CHANGELOG) — сверено построчно в r1. - AC10 (гейты и бюджет на SHA
552b78a1) — Validate зелёный (https://github.com/Matysh/houseplan-card/actions/runs/33270791009), smoke/golden лично прогнаны ревьюером r1. - Трейлеры и процесс на коммите
591f8f6a/552b78a1.
Обоснование доверия: ни один из этих ACs не завязан на файлы, тронутые
дельтой r2 (demo/golden/matrix.mjs, test/golden-matrix.test.mjs,
docs/images/**). Функциональный код (src/**, custom_components/**/*.py)
не менялся ни байтом между r1 и r2 — подтверждено самой дельтой
(git diff --stat 552b78a1..e524f07d, нет ни одного файла src/** или *.py).
Как проверялось (r2)
| Гейт | Статус | Как |
|---|---|---|
npx tsc --noEmit, python -m pytest tests_backend -q |
не гонял — код не менялся | git diff --stat 552b78a1..e524f07d не содержит src/** и custom_components/**/*.py; на неизменном коде результат тот же, что доказан в r1 на SHA 552b78a1 |
npm test (включая новый golden-matrix.test.mjs) |
прогнал лично | npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs && node --test test/golden-matrix.test.mjs → 43/43 passed; полный npm test на этом же содержимом кода уже зелёный на CI (см. ниже) |
| Тест «explicit cover value-source golden…» умеет падать | проверил | мутация attribute: 'current_position' → 'wrong_attribute' дала честный not ok с диагностическим diff; откат вернул зелёный прогон и чистое дерево (git status --short пуст) |
node scripts/check-docs.mjs (диф трогает docs/images) |
прогнал | Documentation checks passed (7 files, 10 external links) |
node scripts/bundle-sync.mjs + сверка копий |
прогнал | git status --short пуст после синка — dist/, custom_components/.../frontend, demo/srv/assets побайтово совпадают |
node scripts/bundle-budget.mjs |
прогнал | initial View 277941 B при бюджете 300000 B |
npm run golden:capture -- --scenario=device-value-face-cover-source-dark |
прогнал, визуально проверил кадр | статус missing-baseline (ожидаемо — эталон не принят по процессу); PNG открыт лично — капсула показывает 42 %, кадр не пуст и не обрезан |
node scripts/smoke-select.mjs --base 552b78a1 --head HEAD |
прогнал | «Исполняемого frontend-диффа нет (src/**/*.ts не тронут). Browser-smoke этим диффом не выбираются — выбирать нечего»; браузерные смоки в этом раунде не прогонялись — инструмент сам подтвердил отсутствие связи, а не то, что связь пропущена |
Полный npm run golden:verify (78+1 сценариев) |
не гонял повторно целиком | не требуется: единственный новый сценарий проверен точечно (golden:capture --scenario=...) визуальным просмотром; остальные 78 не зависят от этой дельты и уже зелёные в r1 (https://github.com/Matysh/houseplan-card/actions/runs/33270791009) и повторно в CI-прогоне автора на этом же коде (https://github.com/Matysh/houseplan-card/actions/runs/33271690654, 142 passed / 1 missing-baseline — расхождение только по новому, ещё не принятому сценарию) |
npm run invariants -- --config <…> |
не требуется | дельта не трогает геометрию/ссылки (только тестовый маркер в data-only golden-фикстуре, не влияет на модель проекта) |
| Полный performance-профиль | не требуется | AC не называют влияние на перф, дельта не трогает горячие пути |
Разбор CI-статусов на промежуточных SHA (важное уточнение)
Автор сослался на «Validate на e524f07d завершился success»
(https://github.com/Matysh/houseplan-card/actions/runs/33271765594). Это
верно буквально, но проверил его смысл отдельно, потому что он вводит в
заблуждение при поверхностном чтении: в этом прогоне job'ы frontend,
golden, smoke, backend — все skipped (реюз/классификация путей:
коммит e524f07d трогает только docs/images/**, которые не входят в
sourceFingerprint). То есть этот конкретный зелёный прогон НЕ является
источником доказательства для tsc/unit/build/golden — только для
check-docs.
Источник для тех гейтов — прогон на dbbe94ae
(https://github.com/Matysh/houseplan-card/actions/runs/33271690654): job
Фронтенд: типы, юниты, мутанты, синхрон бандла = success (tsc, npm test,
bundle-sync/no-new-any все прошли), job Golden = failure (ожидаемо:
missing-baseline для одного нового сценария — это не дефект, это
единственно возможный результат verify-режима при непринятом эталоне), job
Документация = failure (screenshot source fingerprint is stale — именно
это чинит следующий коммит e524f07d). Итоговый conclusion: failure этого
прогона — ожидаемое и корректное поведение процесса, а не красный флаг:
golden не может стать зелёным до приёмки эталона, а приёмка — вне роли
разработчика и вне роли ревьюера (§6, §11.4).
Поскольку код (src/**, *.py) идентичен между dbbe94ae и e524f07d
(докс-коммит их не трогает), а sourceFingerprint тоже совпадает (докс вне
корпуса отпечатка), гейты tsc/unit/build, зафиксированные зелёными на
dbbe94ae, действительны и для HEAD e524f07d без повторного прогона.
Находки
Нет находок High или Medium.
Low — нет новых. Low из r1 (docs/specs/README.md:113, нарушение
сортировки по номеру issue) не тронут этой дельтой и остаётся снятым решением ревьюера r1 без правки — повторно фиксировать нечего.
Что проверено и корректно
- Новый golden-сценарий
device-value-face-cover-source-darkсинтаксически и семантически корректен:binding: 'entity:cover.golden_door_half'— валидный и уже используемый в этом же файле формат биндинга (src/devices.ts:75-76,src/device-inbox.ts:177),cover.golden_door_half— существующая fixture- сущность (demo/fixtures/visual-matrix.mjs:155-156, уже используется какcontactстены в этом же матриксе). Формаvalue_sourceсовпадает буква в букву с типом{kind: 'entity_attribute'; entity_id; attribute}(src/types.ts:102),current_positionчислится вVALUE_BADGE_ATTRIBUTES.cover(src/device-value-badge.ts:13) и форматируется как процент — то есть сценарий бьёт ровно в тот путь кода, который тестирует AC2, а не в косвенный. - Приём
markerOverrides+deviceOnlyдля превращения существующего id маркера в изолированный тестовый маркер — не новый паттерн: тот же приём уже используется в этом файле сценариемdevice-icon-state-table-*(demo/golden/matrix.mjs:569-585) на том же idgolden-left-linkquality. Коллизии между сценариями нет: каждый сценарий строит независимую копию конфигурации (prepareGoldenFixture),deviceOnlyизолирует рендер одним маркером. - Пересборка трёх копий бандла (
dist/,custom_components/.../frontend,demo/srv/assets) с новым__HOUSEPLAN_SOURCE_FINGERPRINT__вdbbe94ae— ожидаемый побочный эффект:scripts/source-fingerprint.mjsнамеренно включаетdemo/golden/**/*.mjs(кромеaccept.mjs/policy.mjs) в корпус отпечатка (комментарий в файле, #344) — правкаmatrix.mjsобязана поменять fingerprint и, значит, пересобранный бандл. Сверкаbundle-sync.mjsподтвердила побайтовое согласие копий на HEAD. - Докс-скриншоты (
06-device-editor.png,09-device-info.png,screenshots.json) пересчитаны корректно:sourceFingerprint/sourceSha256синхронно обновлены везде,imageSha256поменялся только у двух реально изменившихся PNG (разница в несколько байт — согласуется с перерисовкой из-за смены встроенного build fingerprint, не визуальная регрессия — самcheck-docs.mjsподтверждает согласованность инструментально). - Трейлеры
Issue: #378,User-Visible: noна обоих коммитах дельты — корректно: поведение не менялось, changelog не требуется.
Чего не проверял
- Не прогонял
npx tsc --noEmit,python -m pytest tests_backend -q, полныйnpm run buildповторно на HEAD — код (src/**,*.py) не менялся с552b78a1/dbbe94ae, где эти гейты уже зафиксированы зелёными (см. таблицу и разбор CI-статусов выше). - Не прогонял ни один
demo/smoke_*.mjs—smoke-select.mjsсам подтвердил отсутствие исполняемого frontend-диффа; выбирать было нечего, а не пропущено с риском. - Не прогонял полный
npm run golden:verifyпо всем 78+1 сценариям заново — 78 старых не задеты дельтой и уже дважды зелёные (r1-ревью и авторский прогон наdbbe94ae), новый проверен точечно личным просмотром кадра. - Не принимал и не буду принимать
demo/golden/baselines/**— вне роли ревьюера (§6); это релиз-менеджерское действие в окнеS8-merged→ выпуск (§11.4). - Не выполнял ручное тестирование в браузере вне golden/smoke-инфраструктуры.
- Полный performance-профиль не гонял — дельта не создаёт новых горячих путей (только data-only тестовая фикстура).
Вывод
Единственная находка r1 закрыта по существу и лично перепроверена: новый
golden-сценарий синтаксически верен, бьёт точно в контракт AC2, структурный
тест умеет падать, а визуальный кадр лично просмотрен и показывает 42 %.
Baseline не принят намеренно и корректно по процессу (не ошибка). Дельта вне
этого — только докс-пересъёмка, мотивированная и подтверждённая
инструментально. Новых находок нет. Вердикт — зелёный.