18 KiB
CODE-REVIEW-610-r1
Issue: #610 · Этап: code (PROCESS.md §2.7) · Заход r1 · блокирующих циклов 0/2
Материал: git log --oneline origin/dev..HEAD / git diff origin/dev...HEAD,
рабочая копия на 2aa4dcf5bd15be4d58d4db8a076327970c062cce (проверено git rev-parse HEAD).
Три коммита диапазона:
0b1b6bf9docs: SPEC-REVIEW-610-r1 (спецификационное ревью, код не менялся).3fb0d7fdfix: clarify unsaved-settings confirmation (#610) —Issue: #610,User-Visible: yes.2aa4dcf5test: align device form smoke with atomic inputs (#610) —Issue: #610,User-Visible: no.
Скоуп задачи
RU-копия и иконка диалога «Отменить изменения?» для четырёх сценариев
несохранённых настроек (room/space/marker/general settings): кнопки
Продолжить/Отменить → Вернуться/Не сохранять, иконка открытого замка →
mdi:content-save-off-outline в заголовке и на кнопке подтверждения. Данные,
схема, порядок кнопок, EN/DE/FR, прочие warning/destructive-подтверждения — не
трогаются. Соответствует ТЗ из тела issue (§2-3) и продолжает J4/J6 из
docs/SCOPE.md («keep the plan true», понятное безопасное действие в
редакторах). Спецификационное ревью (docs/reviews/SPEC-REVIEW-610-r1.md,
зелёный, 0 High/Medium) уже проверило исполнимость ТЗ; данный документ читает
только реализацию.
Как проверялось
Дешёвые гейты на 2aa4dcf5 уже подтверждены зелёным Validate
(https://github.com/Matysh/houseplan-card/actions/runs/35824681786, headSha
сверен командой gh run view 35824681786 --json headSha — совпадает с HEAD),
поэтому npx tsc --noEmit/npm test/npm run build со сверкой bundle tree не
перегонялись отдельно как самостоятельный гейт. Но для проверки дисциплины
«тест умеет падать» ниже они были задействованы вручную, локально:
- Построчно сверил каждый файл диффа (
git diff origin/dev...HEAD) с контрактом ТЗ п.4 и п.6 («Затронутые файлы»):src/danger-confirm.ts— новое полеconfirmIcon?: stringвHpConfirmRequest, комментарий поясняет назначение.src/hp-confirm.ts— кнопка подтверждения теперь берётrequest.confirmIcon || (destructive ? ... : ...), существующий fallback не тронут; заголовочная иконка (request.icon || ...) не менялась — этот путь был реализован до задачи (см. SPEC-REVIEW находку про п.2 ТЗ).src/editors/{general-settings-dialog,marker-dialog,room-settings-dialog,space-form}.ts— ровно в четырёх вызовах_confirmDangerдобавлена параicon: 'mdi:content-save-off-outline', confirmIcon: 'mdi:content-save-off-outline', без изменения остальных полей запроса.src/i18n/settings/ru.json— изменены ровно два ключа (dialog.discard_confirm,dialog.discard_keep);dialog.discard_title/_messageне тронуты.
grep -rn "content-save-off-outline" src/ demo/ docs/— иконка встречается ровно в 4 продуктовых call site + в тестах/документации; ни одного лишнего вызова_confirmDangerне задето.grep -rln "Продолжить|Отменить" demo src docs scripts(исключаяdemo/srv, сгенерированный) — все прочие вхождения принадлежат другим i18n-ключам (vac.route_*_body,space.copy_optimize_body,history.undo*,radar.discard_setup_title, заголовокdialog.discard_title) — пересечения со скоупом нет, не-скоуп из ТЗ §3 реален, а не декларативен.- Проверил
data-kindна всех четырёх диалогов (marker,room,space— вspace-settings-dialog.tsиspace-copy-runtime.ts,settings), чтобы убедиться, что новыйdemo/smoke_discard_copy.mjsдействительно находит каждый из четырёх диалогов по селектору, который использует. - Тест умеет падать (проверено исполнением, не на слово). Вручную
применил патч мутанта
discard-confirm-action-icon-falls-back-to-lockизscripts/mutation-registry.mjsкsrc/hp-confirm.ts(откатилrequest.confirmIcon ||обратно к жёсткому fallback), пересобрал (npm run build— прошёл,tsc --noEmitвнутри чистый), синхронизировал бандл (node scripts/bundle-sync.mjs) и прогналnode demo/smoke_discard_copy.mjs: упали ровно 4 проверки ({marker,room,space,settings}CopyAndIcons), остальные (draft/discard поведение, fallback чужого warning) остались зелёными — мутант пойман ровно там, где и должен. Вернул файл (cpиз бэкапа), пересобрал и пересинхронизировал —git status --porcelainпуст, дерево совпадает с коммитом;node demo/smoke_discard_copy.mjsснова зелёный (14/14). Round-trip заодно подтвердил AC6: три копии bundle tree (dist,custom_components/houseplan/frontend,demo/srv/assets) совпадают на этом SHA, раз пересборка не создала диффа. - Прогнал вручную (все зелёные, полный вывод в терминале сессии):
node demo/smoke_discard_copy.mjs— новый смок задачи, все 14 полей true.node demo/smoke_dialog_polish_603.mjs— RU/EN/DE/FR × light/dark × DPR 1/2 × ширины 320/360/560/640 (AC2), включая добавленную проверкуdiscardIconsдля каждой комбинации.node demo/smoke_room_settings_form.mjs— обновлённые подписиВернуться/Не сохранятьв контексте формы комнаты.node demo/smoke_danger_confirmation.mjs— Escape/крестик/scrim/replace, unlock-подтверждение (unlockCancelAccept) — доказывает, что чужие warning/destructive сценарии не задеты (AC4, ТЗ п.4.7).node demo/smoke_device_settings_form.mjs— смок, изменённый вторым коммитом (2aa4dcf5); зелёный, подтверждает, что правка приводит его к контракту атомарных числовых полей #608, не меняя продуктовый код.node scripts/check-docs.mjs— обязателен, т.к. диф трогаетsrc/**(изменяет отпечаток скриншотов); «Documentation checks passed (7 files, 12 external links)».
node scripts/smoke-select.mjs --base origin/dev --head HEAD— НЕОПРЕДЕЛЁННОСТЬ: «дифф исполняемый, но ни один смок не связан доказуемо», 0 символов на изменённых строках. Причина понятна и не тревожна: инструмент строит таблицу символов по объявлениям верхнего уровня/членов класса с модификатором или_-префиксом (scripts/smoke-select.mjsDECLARATIONS); новое полеconfirmIcon?: stringвнутриinterface HpConfirmRequest— рядовой публичный член без модификатора и без_— таким объявлением не считается, поэтому зарегистрированная связь изscripts/smoke-links.mjs(символыconfirmIcon,HpConfirmRequest→smoke_discard_copy.mjs,smoke_dialog_polish_603.mjs) не сработала на автомате. Решение по строке: прогнал оба зарегистрированных смока вручную (см. п.6) — они прямо и единственно доказывают AC1-AC4, поэтому автоматическое молчание инструмента не меняет вывод, просто не освобождает от чтения AC.- Инварианты модели (
npm run invariants) не запускал — диф не трогает геометрию,layout,marker.space, толщину стен илиopen_spans.pytest tests_backendне запускал — Python не тронут.npm run golden:verifyне запускал по существу изображения (см. ниже), но проверил, что диф вdemo/golden/ограничен одной строкой ожидаемых лейблов вharness.mjs, а сам baseline-эталон не тронут — ровно то, что требует AC5 и п.10 ТЗ (принятие golden только из pre-release Linux CI).
Находки
High: 0. Medium: 0. Задача реализована точно по ТЗ, без расширения и без сужения скоупа.
Что проверено и корректно
- AC1/AC2 (browser smoke). Все четыре
discard-*-dialogпоказываютВернуться/Не сохранять;smoke_dialog_polish_603.mjsподтверждает точные подписи, одну строку без переноса на 320-640px, autofocus безопасной кнопки и возврат к форме — во всех языках/темах/DPR (не только RU, что важно: EN/DE/FR подписи не изменились — проверено выражениемexpectedв смоке и явным сравнением с ТЗ п.4.6). - AC3 (иконка).
mdi:content-save-off-outlineпередаётся явно в заголовок и на кнопку подтверждения из всех четырёх call site; других мест правка не касается — подтверждено и построчным чтением, и grep по всему дереву. - AC4 (fallback других confirmation).
HpConfirm.render()используетrequest.confirmIcon || (destructive ? 'mdi:trash-can-outline' : 'mdi:lock-open-variant')— при отсутствии override (все прочие вызывающие сайты) поведение идентично коду до правки.smoke_discard_copy.mjsпроверяет это прямо (unrelatedWarningKeepsLockGlyphs), аsmoke_danger_confirmation.mjs— что unlock-сценарий не деградировал. - AC5 (доки/golden).
docs/USER-GUIDE.ru.mdоднозначно объясняет обе кнопки в разделе, общем для всех четырёх форм (единый form-kit). Golden harness (demo/golden/harness.mjs) обновлён под новые подписи, сам эталонroom-discard-dialog-mobile-ruне тронут — приёмка по обычному pre-release-процессу, как и требует ТЗ п.10. - AC6 (гейты). Validate на этом самом SHA зелёный (сверено
gh run viewпоheadSha); дополнительно вручную воспроизведена сборка + bundle-sync + round-trip мутанта без остаточного диффа — три копии бандла синхронны. - Мутационный тест ловит регресс. Отдельно от заявления автора —
воспроизвёл мутацию
discard-confirm-action-icon-falls-back-to-lockруками и убедился, что именноsmoke_discard_copy.mjsи только он падает (4 из 4 полей*CopyAndIcons), остальное поведение остаётся зелёным. - Второй коммит (
2aa4dcf5) не расширяет скоуп. Это тестовое выравниваниеsmoke_device_settings_form.mjsпод уже существующий атомарный контракт числовых полей из #608 (input— черновик,change— фиксация); продуктовый код не тронут,User-Visible: noверно. - Трейлеры и changelog. Оба продуктовых коммита несут
Issue: #610;User-Visible: yesу коммита с реальным изменением сопровождается правкой обоих changelog (docs/CHANGELOG.md,docs/CHANGELOG.ru.md) в том же коммите3fb0d7fd. - Не найдено дублирования источника значения («одно число — один источник»): диф не вводит новых видимых пользователю величин — только текст двух кнопок и один и тот же строковый литерал иконки, используемый ровно там, где он должен отображаться (заголовок + кнопка одного диалога), не в двух независимых местах с потенциально разными источниками.
Чего не проверял
- Не гонял
npx tsc --noEmit/npm test/npm run buildкак самостоятельный формальный гейт — они уже зелёные на этом SHA по ссылке Validate; вместо этого использовалnpm run buildдважды как побочный инструмент для ручного round-trip мутанта (что фактически их и покрыло). - Не гонял полный
npm run golden:verify(реальный рендер и сравнение пикселей) — диф не меняет геометрию/CSS, только текст кнопок и строку иконки внутри уже существующего slot; визуальную часть придётся принять на pre-release Linux CI, как и предписывает ТЗ (эталон умышленно не обновлён в этом коммите). - Не гонял
npm run invariantsиpytest tests_backend— diff не касается геометрии модели и Python-кода. - Не гонял весь
demo/smoke_*.mjs(263 файла) — задача узкая и локальная (один компонент, один i18n-ключ, четыре вызывающих сайта); прогнал шесть смоков, названных в ТЗ/выбранных по прямому текстовому упоминанию символов задачи (smoke_discard_copy,smoke_dialog_polish_603,smoke_room_settings_form,smoke_danger_confirmation,smoke_device_settings_form— единственный смок второго коммита,check-docs.mjs— обязателен дляsrc/**).smoke-select.mjsвернул НЕОПРЕДЕЛЁННОСТЬ (см. «Как проверялось», п.7) — расширять выбор дальше не было оснований: ни один другой смок не упоминает изменённые символы (confirmIcon,discard_confirm,discard_keep) и не тестирует discard-диалоги.
Вердикт
Зелёный. Реализация точно соответствует ТЗ #610: изменены ровно те два RU-ключа и ровно четыре call site, что были указаны; fallback прочих warning/destructive confirmation не сломан (проверено и чтением, и воспроизведением мутанта); AC1-AC6 доказаны либо автотестом, который подтверждённо умеет падать, либо чтением кода с явной пометкой. High: 0, Medium: 0.
Материал раунда
- HEAD:
2aa4dcf5bd15be4d58d4db8a076327970c062cce(свереноgit rev-parse HEAD). - Диапазон:
origin/dev..HEAD, три коммита (0b1b6bf9,3fb0d7fd,2aa4dcf5). - Validate на этом SHA: https://github.com/Matysh/houseplan-card/actions/runs/35824681786
(
conclusion: success,headShaсверен командойgh run view).
Материал раунда
- Ветка:
issue/610-discard-copy-icon, коммит2aa4dcf5bd15— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
1826c6f7b4fc1e4136644304d6aa38299134378cgit log --all --format='%H %T' | grep 1826c6f7b4fc - Тело issue:
ba5e9ea73d14fd143edf181ea90e230889146e7e2a6ce3ed6a46625deae355f1 - Вердикт конвейера:
green· High 0