From 27d78801d0436f5bd496699a6ee201f8276956a9 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:10:38 +0000 Subject: [PATCH] docs: review document for #647 Issue: #647 User-Visible: no --- docs/reviews/INDEX.md | 3 +- docs/reviews/SPEC-REVIEW-647-r2.md | 234 +++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 docs/reviews/SPEC-REVIEW-647-r2.md diff --git a/docs/reviews/INDEX.md b/docs/reviews/INDEX.md index b510564d..215eb50e 100644 --- a/docs/reviews/INDEX.md +++ b/docs/reviews/INDEX.md @@ -1,10 +1,11 @@ # Индекс ревью -Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1046, issue: 368. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`. +Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1047, issue: 368. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`. | Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | Файлы | |---|---|---|---|---:|---:|---|---| | #647 | [SPEC-REVIEW-647-r1.md](SPEC-REVIEW-647-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | П.4 ТЗ переносит на новый слот прежнее; ТЗ не упоминает и не защищает документированный | `src/styles/dialogs.styles.ts` `src/styles.ts` `smoke_glow_blending.mjs` `smoke_test_facade.mjs` `smoke_unified_wall_tool.mjs` `docs/UX-MODES.md` `docs/reviews/CODE-REVIEW-195-r1.md` `src/styles/chrome.styles.ts` | +| #647 | [SPEC-REVIEW-647-r2.md](SPEC-REVIEW-647-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | — | | #646 | [CODE-REVIEW-646-r1.md](CODE-REVIEW-646-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — | | #645 | [SPEC-REVIEW-645-r1.md](SPEC-REVIEW-645-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | Отсутствует обязательная строка Touch editor: … из docs/TOUCH-SUPPORT.md → «Documentati…; ТЗ не учитывает существующую зависимость live-подписей Resize от позиции кнопки «Настро…; / Low | `docs/TOUCH-SUPPORT.md` `docs/reviews/SPEC-REVIEW-449-r1.md` `docs/specs/359-furniture-placement-preview.md` `docs/specs/449-double-fit-all.md` `src/houseplan-editor-runtime.ts` `src/houseplan-card.ts` `src/resize-labels.ts` `docs/process/REVIEWER.md` | | #645 | [SPEC-REVIEW-645-r2.md](SPEC-REVIEW-645-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | — | diff --git a/docs/reviews/SPEC-REVIEW-647-r2.md b/docs/reviews/SPEC-REVIEW-647-r2.md new file mode 100644 index 00000000..e26b375e --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-647-r2.md @@ -0,0 +1,234 @@ +# SPEC-REVIEW-647-r2 + +- **Issue:** https://github.com/Matysh/houseplan-card/issues/647 +- **Этап:** `S4-spec-review` (ревью ТЗ, PROCESS.md §2.4) +- **Трек:** `small` (лёгкий), по аналитике автора в комментарии «Аналитика» +- **Материал:** тело issue #647, раздел `## ТЗ`, редакция r2 (снимок на момент + этого ревью, 2026-09-25); предыдущий комментарий-ответ автора «ТЗ r2 — + ответ на SPEC-REVIEW-647-r1» +- **Заход:** r2 · блокирующих циклов израсходовано 1 из 2 (лёгкий трек) +- **Роль:** ревьюер ТЗ (не автор) + +## Скоуп ревью + +Редакция r2 — прямой ответ на жёлтый вердикт SPEC-REVIEW-647-r1 (0 High, +2 Medium, оба в скоупе, закрываются правкой текста). Дельта локальна: автор +переписал п.4 «Скоупа», добавил К5/К6 в «Контракт поведения» и AC5/AC6 в +таблицу доказательств, расширил «Release-артефакты» (`docs/UX-MODES.md`) и +уточнил «UX, i18n» по touch-контракту; заодно поправил абзац «Проблема (по +коду)», испорченный в r1 форматированием (вложенные обратные кавычки). +Остальные разделы ТЗ (сценарий, «что человек увидит», не-скоуп, план +автотестов, риски, откат, «принято предположительно») не менялись по +существу. + +По правилу «объём разбора по дельте» (PROCESS.md §2.10) разбор ниже +сосредоточен на: (а) действительно ли новый текст закрывает Medium-1 и +Medium-2 из r1, (б) не создали ли сами правки новой неоднозначности или +непроверяемого AC. Разделы, не задетые дельтой и уже проверенные в r1 (состав +обязательных §7.1, точность описания кода «как есть», удаление ключа +`count.devices`, не-скоуп, план автотестов, трейлеры), приняты без повторной +проверки — см. «Унаследовано из r1» ниже. + +## Как проверялось + +1. Сопоставлен текст комментария автора «ТЗ r2 — ответ на + SPEC-REVIEW-647-r1» построчно с находками r1 (Medium-1, Medium-2) — + заявленные правки существуют в текущем теле issue дословно там, где автор + их называет. +2. Перечитан весь текущий раздел `## ТЗ` целиком (не только изменённые + абзацы), чтобы делта не читалась в отрыве от контекста — противоречий + между новыми К5/К6/AC5/AC6 и остальным текстом не найдено. +3. Проверено закрытие Medium-1 (риск: новый слот наследует + `@media (max-width: 1100px) { .head .count { display: none } }` и прячет + крестик на обычной ширине): + - п.1 «Скоупа» теперь явно удаляет само правило `.head .count` вместе со + счётчиком («оно станет мёртвым»), а не просто оставляет его висеть; + - п.2 явно требует для слота «собственный класс... **не** `.count` и без + наследования его правил»; + - п.4 переписан: «слот виден и занимает своё место на любой ширине, где + видна группа кнопок режимов (в том числе 721–1100 px, где счётчик + скрывался)»; + - добавлен К6 и AC6 с прямой проверкой видимости/клика × на 390/768/1000/ + 1400 px через `elementFromPoint`, с мутантом «слоту правило скрытия + как у `.count`» → смок красный на 1000 и 768 px. 1000 px лежит внутри + диапазона 900–1100 px, который r1 просил проверить явно. + Три независимых слоя защиты (переписанный скоуп, явный запрет + наследования, тестируемый АС с мутантом) закрывают находку с запасом. +4. Проверено закрытие Medium-2 (риск: контракт hit-target ≥24×24 px из + `docs/UX-MODES.md:41-44` не упомянут в ТЗ и не защищён AC, документ не + входит в Release-артефакты): + - новый К5: «Кликабельная зона × — не меньше 24 × 24 CSS px + (`getBoundingClientRect()`...), глиф — 13 px; клик в точку, отстоящую + от центра глифа на 10 px..., закрывает редактор (как в #195)» — + дословно воспроизводит контракт из UX-MODES.md и метод регрессионной + проверки из истории #195; + - новая строка AC5 в таблице с named-мутантом «размер слота 13 px + (переменная) → смок красный» — защитный AC теперь имеет непустой + третий столбец; + - «Release-артефакты» теперь называют `docs/UX-MODES.md` явно: «строка + про panel-host (убрать «device count»...) и абзац про × (× — в + отдельном слоте..., hit target ≥ 24 × 24 px сохранён)»; + - «UX, i18n» теперь содержит явное обоснование вместо голого + утверждения: «Touch: размер кликабельной зоны × не уменьшается + (≥ 24 × 24, как сейчас)...» — это уже не непроверенное заявление из + «Аналитики» r1, а требование ТЗ со своим AC. +5. Прочитан `docs/UX-MODES.md` ещё раз (строки 1–50) и сверен построчно с + планом Release-артефактов r2: строка 14 («...device count, zoom and + actions remain») и абзац строк 33–44 (крестик как часть кнопки-режима, + «hit target of at least 24 × 24 px without changing the tab's layout + footprint») — оба фрагмента действительно устареют после правки и оба + поимённо покрыты планом автора («строка про panel-host» и «абзац про + ×»). Расхождений между тем, что попросил ревью r1, и тем, что реально + устареет в документе, нет. +6. Проверено состояние кода на материале ревью (SHA `1ba4c23a`, `git show + --stat HEAD`, `git branch -a`, `git log --oneline --all | grep 647`): + для #647 в дереве нет ничего, кроме коммита `docs: review document for + #647` (добавляет только сам документ r1 и строку в `docs/reviews/ + INDEX.md`); ветка `issue/647-toolbar-stable-width` в этом чекауте + отсутствует — код ещё не написан. Это подтверждает, что на этапе `spec` + гейты (`tsc`, `npm test`, `npm run build`, `check-docs.mjs`, смоки, + `golden`) неприменимы: сверять их не с чем, `src/**` в материале не + меняется. Ровно так же поступил ревьюер r1 (см. его «Чего не проверял»). +7. Перепроверены факты кода, на которые опирается r2 (не только цитаты + ТЗ, а сам код): `src/styles/dialogs.styles.ts:27-29` (правило `.head + .count`, `max-width: 1100px`), `src/styles/chrome.styles.ts:154-172` + (`.modetab .closex`, 24×24 хит-зона через отрицательные поля), + `src/houseplan-card.ts:10811,10820` (крестик внутри активной кнопки, + `` отдельным элементом) — совпадает с описанием + «Проблема (по коду)» дословно, включая уже исправленный в r2 абзац + (нет разрушенных обратных кавычек, текст читается корректно). +8. Проверен `docs/reviews/INDEX.md` — строка `#647 | SPEC-REVIEW-647-r1.md + | spec · r1 | 🟡 жёлтый | 0 | 2 | ...` присутствует и соответствует + документу r1; смежные подсистемные записи (`#195` code · r1/r2 — оба + 🟢, `#437` — источник сводной панели) подтверждают исходные ссылки ТЗ + на историю контрактов. +9. Перечитаны `docs/SCOPE.md` и `docs/process/REVIEWER.md` заново для этого + захода (без изменений в применимости выводов r1: задача обслуживает + J1/J6 как polish уже принятого механизма переключения режимов, вне + скоупа не выходит). + +Гейты не гонялись — этап `spec`, материал ревью текстовый (тело issue), в +дереве материала (SHA `1ba4c23a`) для #647 нет диффа `src/**`, `test/**` +или `demo/**`: код появится только после перехода в `S5-ready`/`S6-in-progress` +и будет предметом код-ревью. Это не сокращение объёма, а соответствие +самому этапу — гейты §8 привязаны к диапазону коммитов, а не к тексту +issue. + +## Находки + +Не найдено. Оба Medium из r1 закрыты правкой текста ТЗ, новых +неоднозначностей, непроверяемых AC или противоречий делта не внесла. + +## Закрытие раунда r1 + +| Находка r1 | Чем закрыта | Где это видно | +|---|---|---| +| Medium-1: п.4 ТЗ дословно предлагал унаследовать `.head .count`'s `@media (max-width: 1100px){display:none}`, крестик мог исчезнуть на обычной ширине 721–1100 px, ни один AC это не ловил | П.1 «Скоупа» удаляет само правило `.head .count`, а не наследует его; п.2 явно запрещает слоту класс `.count` и наследование его правил; п.4 переписан («слот виден... в том числе 721–1100 px, где счётчик скрывался»); добавлен К6 + строка AC6 с мутантом «правило скрытия как у `.count` → красный на 1000 и 768 px» | Тело issue #647, раздел «Скоуп» п.1–п.2, п.4 «Адаптивность»; раздел «Контракт поведения» К6; таблица «AC · чем доказан · чем краснеет», строка AC6 | +| Medium-2: контракт hit-target ≥24×24 px (`docs/UX-MODES.md:41-44`, issue #195) не упомянут в ТЗ, нет защищающего AC, `docs/UX-MODES.md` не в Release-артефактах, «Аналитика» заявляла «hit area прежние» без обоснования | Добавлен К5 (дословно воспроизводит контракт ≥24×24 px и метод проверки как в #195); добавлена строка AC5 с мутантом «размер слота 13 px (переменная) → смок красный»; «Release-артефакты» теперь называют `docs/UX-MODES.md` (обе строки: про panel-host и про ×); «UX, i18n» содержит явное обоснование вместо голого утверждения | Тело issue #647, раздел «Контракт поведения» К5; таблица AC, строка AC5; раздел «Release-артефакты»; раздел «UX, i18n, модель данных» | + +## Унаследовано из r1 (принято без повторной проверки) + +Материал: `docs/reviews/SPEC-REVIEW-647-r1.md`, SHA дерева `973c05543e3f...` +(якорь того раунда). Делта r2 не касается перечисленного ниже, поэтому +повторно не перепроверялось: + +- Обязательные разделы §7.1 присутствуют все (сценарий, «что человек + увидит до/после», проблема, скоуп/не-скоуп, UX/i18n/модель данных, план + автотестов, риски, откат) — r1, раздел «Что проверено и корректно». +- Описание бага «по коду» и границы правки совпадают с + `src/houseplan-card.ts:10797-10820` — r1, п.6 «Как проверялось» (в r2 + перепроверено повторно в п.7 этого документа, поскольку абзац + редактировался; расхождений нет). +- Безопасность удаления ключа `count.devices` (единственное место чтения, + сводная панель #437 использует другой источник) — r1, п.7 «Как + проверялось». +- AC1–AC3 однозначны и привязаны к способу доказательства с непустой + колонкой «чем краснеет» — r1, «Что проверено и корректно» (нумерация + AC1–AC3 в r2 не изменилась по содержанию, только сдвинулась таблица + вниз добавлением AC5/AC6). +- Не-скоуп сформулирован явно, без пересечения с #437 — r1, «Что проверено + и корректно». +- План автотестов ссылается на реально существующие механизмы + (`test/core-file-budget.test.mjs`, `scripts/smoke-links.mjs`, + `scripts/mutation-registry.mjs`, `scripts/smoke-select.mjs`, фасад + `data-hp="mode-tab"`) — r1, п.10 «Как проверялось». +- Трейлеры `User-Visible: yes` и требование обоих changelog в одном + коммите — r1, «Что проверено и корректно». +- Открытых продуктовых вопросов владельцу нет — r1, «Что проверено и + корректно»; в r2 не появилось новых. + +## Что проверено и корректно + +- Оба Medium из r1 закрыты по существу (не декларативно): каждое — + переписанным текстом скоупа/контракта **и** новым тестируемым AC с + названной мутацией, а не только обещанием в комментарии автора. +- Новые К5/К6 и AC5/AC6 однозначны, ссылаются на конкретный метод + измерения (`getBoundingClientRect`, `elementFromPoint`, смещение 10 px + от центра глифа) и называют мутацию, на которой тест обязан покраснеть — + колонка «чем краснеет» не пустая ни в одной новой строке. +- Правка Release-артефактов называет обе фактически устаревающие строки + `docs/UX-MODES.md` (строка 14 про panel-host/device count, абзац + 33–44 про крестик и его футпринт) — проверено построчным сравнением с + текущим содержимым файла, а не на слово автора. +- Исправление абзаца «Проблема (по коду)» (вложенные обратные кавычки r1) + не изменило фактического содержания — код-цитаты и номера строк те же. +- Дельта не создала противоречий с неизменёнными разделами: «принято + предположительно» по-прежнему корректно описывает размер слота как одну + переменную, совпадающую с прежней кликабельной зоной (24 px), что теперь + явно требуется К5. +- На материале ревью (SHA `1ba4c23a`) для #647 отсутствует какой-либо код + — подтверждено `git show --stat` и `git log`; риск «текст ТЗ разошёлся с + уже написанным кодом» (типичная причина находки на код-ревью) здесь не + применим. + +## Чего не проверял + +- Гейты (`tsc`, `npm test`, `npm run build`, `check-docs.mjs`, смоки, + `golden`, инварианты, `smoke-select.mjs`) — не прогонял: код для #647 ещё + не написан на материале ревью, диапазон ревью — текст ТЗ, а не диапазон + коммитов. Предмет код-ревью после реализации. +- Не проверял браузером в реальном рендере поведение на 1000/900 px — + вывод о том, что мутант «правило скрытия как у `.count`» действительно + покраснит смок, сделан чтением плана AC, а не исполнением ещё не + написанного смока; это неизбежно на этапе `spec` и будет прямой + обязанностью код-ревью (проверить, что `demo/smoke_toolbar_stable_width.mjs` + умеет падать на названной мутации). +- Не переоценивал заново трек `small`/критерии лёгкого трека — не задеты + делтой r2 (добавление двух AC не меняет ни сложность ≤3, ни «одну + поверхность», ни отсутствие миграции конфига), и в r1 они не были + находкой. +- Не проверял golden-кадры и скриншоты документации — правка кода ещё не + существует; риск golden-регресса уже учтён в разделе «Риски» ТЗ + (принято в r1 без замечаний). + +## Вердикт + +Найдено 0 High, 0 Medium. Оба Medium из r1 закрыты по существу текстом +редакции r2 — каждое явной правкой формулировки контракта/скоупа и новым +тестируемым AC с названной мутацией. Новых неоднозначностей или +непроверяемых AC делта не внесла. ТЗ готово к разработке. + +--- + + + +## Материал раунда + +- SHA материала: `1ba4c23a5813d0cf51fb010d79e45416a15facf2` (рабочая копия + ревью). +- Тело issue #647, редакция r2 — снимок на момент этого ревью, + 2026-09-25. + +--- + + + +## Материал раунда + +- Ветка: `dev`, коммит `1ba4c23a5813` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `b5de29ae951c3df446606e8324139fdb919ff0e1` + ``` + git log --all --format='%H %T' | grep b5de29ae951c + ``` +- Тело issue: `96f3d23ef7e626083567418cc5d5c098150d97627da79dc8ebe77b949f39a1c1` +- Вердикт конвейера: `green` · High 0