18 KiB
SPEC-REVIEW-665-r1
- Issue: https://github.com/Matysh/houseplan-card/issues/665
- Этап:
S4-spec-review(ревью ТЗ, PROCESS.md §2.4) - Трек: лёгкий (
small), но неtrivial— авторская аналитика (комментарий- сама называет причину: правка задевает цель касания поднятой подписи
комнаты в 2.5D View/киоске (блокирующая зона
TOUCH-SUPPORT.md), поэтому её сохранение доказывается отдельным AC, а не декларацией — отсюда полноценное ревью ТЗ, а не короткий трек.
- сама называет причину: правка задевает цель касания поднятой подписи
комнаты в 2.5D View/киоске (блокирующая зона
- Материал: тело issue #665 (единственная редакция, раздел
## ТЗ) + комментарий владельца (аналитика/оценка, меткаS4-spec-review, вопросов владельцу нет). - Заход: r1 · блокирующих циклов израсходовано 0 из 2
- Роль: ревьюер ТЗ (не автор)
Скоуп ревью
CSS-баг в 2.5D View: .stage.projection-iso.mode-view .roomlabel использует
min-height: 44px + justify-content: center как способ дать поднятой подписи
комнаты минимальную цель касания. Побочный эффект — расстояние от имени
комнаты до строки показателей (.rlmetrics, абсолютно позиционирована от
низа контейнера подписи) зависит от того, насколько 44 px больше высоты
самого имени, то есть меняется при зуме. Во Flat такого эффекта нет — там
контейнер не растягивается, и зазор — постоянные 0,15em. Контракт: перенести
44×44 px минимум с самого блока подписи на невидимый ::before (приём уже
применяется для .dev/.oplock), вернув геометрию блока подписи к
поведению Flat. Не в скоупе: размер шрифта подписи на зуме, состав
показателей, раскладка столкновений поднятых подписей.
SCOPE-проверка (docs/SCOPE.md): правка обслуживает J1 («show the whole
home… room fills… values») и опирается на узкое исключение #89 (2.5D —
детерминированная проекция того же состояния/геометрии, что и Flat, без
второй модели): здесь она восстанавливает именно это равенство, устраняя
единственное расхождение геометрии подписи между Flat и 2.5D. Новой
scope-дыры не создаёт: правка не меняет состав показателей, не вводит новую
интерактивность и не расширяет исключение #89.
Как проверялось
- Прочитаны
docs/SCOPE.md,docs/process/REVIEWER.md, по ссылкам — PROCESS.md §2.4, §2.5, §7.1, §7.2, §4 (§2.10 не применялся — это r1). - Прочитано тело issue #665 целиком (
gh issue view 665 --json body) и единственный комментарий владельца (--json comments) — вопросов владельцу нет, подтверждаю по тексту. - Сверены обязательные разделы §7.1: сценарий (персона «домочадцы и киоск», поверхность 2.5D View, момент — зум) — на месте и без терминов реализации; «что человек увидит до/после» — одной фразой каждое; проблема и причина воспроизведены исполнением (демо-стенд, таблица замеров на 4 уровнях зума); скоуп/не-скоуп — явно; контракт C1–C3 — однозначен; модель данных/миграция/i18n — явное «нет»; AC1–AC3 — каждый с указанным способом доказательства (smoke/smoke/мутант); откат — «вернуть правило»; release-артефакты — changelog RU+EN, golden (условно, с оговоркой о приёмке по CI), скриншоты документации. Явного заголовка «Риски» и «UX» нет, но содержание покрыто (см. находку Low-1 ниже — только по одному пункту, доке).
- Перепроверены фактические утверждения ТЗ по коду на SHA
942b9ede(== origin/dev), а не приняты на слово:src/styles/plan.styles.ts:692-702— правило.stage.projection-iso .mode-view .roomlabel { min-width: 44px; min-height: 44px; justify-content: center; … }существует буквально как процитировано в issue.src/styles/plan.styles.ts:555-570,647-649—.roomlabelэто flex-column cgap: 0.15em;.rlname/.rlmetrics(src/houseplan-card.ts:12077, 12087) — прямые дети;.rlmetricsвынута из потока (position: absolute; top: calc(100% + 0.15em)), поэтому единственный flex-элемент —.rlname, и в 2.5Djustify-content: centerприmin-height: 44pxцентрирует имя в блоке высотой 44 px, аtop: calc(100% + 0.15em)считается от высоты контейнера (.roomlabel), а не от низа имени. Математика issue ((44 − высота_имени)/2 + 0.15em) воспроизводится по коду точно, не на веру.demo/smoke_isometric_contract.mjs:36-47(owns44) — уже берётMath.max(rect.width/height, ::before width/height), то есть уже нейтрален к тому, откуда взялся 44-px минимум (сам блок или его::before). Утверждение AC2 «owns44уже учитывает::before» — подтверждено чтением, не принято на слово.src/iso-scene-render.ts:229-264(isoRaisedOverlayHalfSize, кейс'room-label') — считает половину футпринта изnameHeight/текстовых метрик шрифта, константы44в функции нет. Утверждение C3 «раскладка столкновений уже считает от шрифта, а не от 44 px» — подтверждено.src/styles/plan.styles.ts:541-551(.oplock::before) — приём invisible-::beforeсwidth/height: max(44px, 100%),transform: translate(-50%, -50%),pointer-events: autoуже применяется в проекте буквально в предложенном автором виде — «принятое предположение» не гипотетическое, а копия рабочего паттерна.src/houseplan-card.ts:12058-12060— узел сdata-hp-iso-overlay-kind="room-label"(тот, что проверяетowns44) — это и есть сам.roomlabel, а не обёртка вокруг него; перенос минимального размера на его собственный::beforeне рассинхронизирует смок с реальным деревом DOM.
- Проверен единственный сомнительный пункт «Затронутые файлы» — заявленная
правка
docs/ISOMETRIC.md(«строка про цель касания подписи»): найдена находка Low-1 ниже. git branch -a/git log --all --oneline | grep 665— веткиissue/665-*и коммитов с трейлеромIssue: #665не существует;git diff origin/dev...HEADпуст (HEAD == origin/dev == материал). Продуктового кода для #665 нет — стадияspec, гейты (tsc,test,build, смоки, golden, инварианты) неприменимы к этому этапу, это штатное состояние, а не находка.
Находки
Low-1 (снят без возврата) — заявленная правка docs/ISOMETRIC.md
(«строка про цель касания подписи») не соответствует текущему документу
Что не так. Раздел «Затронутые файлы» называет docs/ISOMETRIC.md со
пометкой «строка про цель касания подписи», подразумевая правку существующей
строки. Проверено чтением всего файла: docs/ISOMETRIC.md не содержит ни
числа 44, ни слова touch/target/tap применительно к подписи комнаты, ни
упоминания приёма ::before. Единственное место в проекте, где написано «keeps
its existing axis-aligned minimum 44 CSS-pixel target», — это
docs/adr/160-isometric-stage3-overlays.md:60, ADR-документ (заморожена
история решения), и эта фраза не завязана на конкретный CSS-механизм
(min-height vs ::before) — после правки она останется истинной без
изменений.
Почему не High/Medium. Это не открытый продуктовый вопрос и не
противоречие контракту: ни один AC не зависит от этой правки, check-docs.mjs
не привязывает конкретную CSS-реализацию к тексту ISOMETRIC.md. Худший
исход — исполнитель не найдёт заявленную строку, ничего не тронет в этом файле
и это не сломает ни один AC.
Что нужно (не блокирует). При реализации либо снять этот пункт из
«Затронутые файлы» (в docs/ISOMETRIC.md сейчас нечего редактировать), либо
явно решить, что добавляется новая строка (например, рядом с «Device markers,
room labels/cards and opening-lock badges keep their canonical floor anchors…»,
docs/ISOMETRIC.md:220) с фактом «label touch target size lives on an
invisible ::before, matching .dev/.oplock» — тогда это новый факт, а не
правка существующего. Оставляю на усмотрение исполнителя; не создаю отдельный
issue (Low, в скоупе, не блокирует).
Что проверено и корректно
- Обязательные разделы §7.1 присутствуют по существу и в разумном порядке для однострочного CSS-бага: сценарий и «что человек увидит» — продуктовые, без терминов реализации; проблема подкреплена исполненной репродукцией (реальный демо-стенд, а не гипотеза); скоуп/не-скоуп разделены явно.
- Причина бага перепроверена по коду, а не принята на веру — воспроизведена
вся цепочка:
min-height+justify-content:centerна flex-контейнере с одним in-flow элементом +.rlmetricsабсолютно спозиционирована от контейнера, а не от имени (см. «Как проверялось» п.4). Табличные замеры в issue (4 уровня зума, отношениеgap/высота_именипадает с 2,0 до 0,16 в 2.5D и держится на ~0,09–0,1 во Flat) согласуются с этой моделью без противоречий. - Контракт C1–C3 однозначен и без пересечений: C1 — числовая цель (
0,15em, учёт--rl-name/--rl-meta), C2 — сохранение 44×44 px через::before, C3 — явный запрет трогатьisoRaisedOverlayHalfSize/Flat/редакторы/houseplan-space-card. Каждый пункт проверяем независимо от двух других. - AC1 (smoke, gap/name-height ratio на двух зумах, допуск 0,02) — численно
привязан к уже исполненной таблице замеров, метод измерения (
gap = rlmetrics.top − rlname.bottom) уже указан и воспроизводим. - AC2 (существующая
raisedTargetsOwn44Pixels/owns44) — заявление, что проверка уже нейтральна к источнику 44 px, подтверждено чтениемdemo/smoke_isometric_contract.mjs:36-47, а не принято как факт со слов автора. - AC3 (мутант — вернуть
min-height: 44px, красит AC1) — механизм find/replace для мутации CSS-строки уже используется вscripts/mutation-registry.mjsдля аналогичныхmin-height: 44px-правил; подход реализуем. - «Принятые предположения» — не гипотетический дизайн с нуля, а копия рабочего
паттерна
.oplock::before(src/styles/plan.styles.ts:541-551), уже проверенного в проде для другого раскрытого элемента; технический риск реализации низкий. - Откат, i18n, миграция, перф — покрыты явным «нет»/«вернуть правило», без зазора для домысливания.
- Открытых продуктовых вопросов нет — подтверждаю: контракт (C1: расстояние как во Flat; C2: сохранить цель 44 px) отвечает на оба вопроса, которые вообще мог бы задать владелец («что человек должен видеть» и «что не должно сломаться»), и делает это без домысливания — ожидание владельца прямо процитировано в теле issue («в 2.5D расстояние… не меняется — как в плоском виде»).
Чего не проверял
- Гейты (
tsc --noEmit,npm test,npm run build,check-docs.mjs, смоки,golden:verify, инварианты) — не прогонял: этапspec, ветки/коммитов для #665 нет,git diff origin/dev...HEADпуст. Предмет код-ревью после реализации. - Не проверял
golden-матрицу построчно на предмет того, есть ли там кадр именно 2.5D +label_temp/label_lightвключёнными на комнате с коротким именем (условие ТЗ «изменятся, если есть в матрице»); нашёл, что фикстуры с этими флагами существуют вdemo/golden/harness.mjs:621,657-660, но не сверял, попадает ли конкретно 2.5D-кадр под порог видимого изменения — это разумно оставитьgolden:verifyна код-ревью. - Не оспаривал числовой допуск AC1 (0,02) по существу — это техническая деталь реализации теста, не продуктовый контракт; при коде-ревью его стоит сверить на устойчивость (не флейкует ли на реальных шрифтах/DPR), но это не предмет ревью ТЗ.
Вердикт
Обязательные разделы полны, причина бага и оба смок-факта (AC1 таблица замеров,
AC2 уже-нейтральный owns44, C3 уже-от-шрифта isoRaisedOverlayHalfSize)
перепроверены по коду, а не приняты на слово автора — расхождений не найдено.
Единственная находка (Low-1, неточная ссылка на несуществующую строку в
docs/ISOMETRIC.md) не блокирует ни один AC и снимается без возврата автору.
Открытых продуктовых вопросов нет, откат и release-артефакты названы. Готово к
разработке.
Вердикт: зелёный · заход r1 · блокирующих циклов 0/2 · High: 0 · Medium: 0 → в задаче
Материал раунда
- Ветка:
dev, коммит942b9ede6789— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
4fd2ba833946aa89eac3733667f2d15b952b24c5git log --all --format='%H %T' | grep 4fd2ba833946 - Тело issue:
8da493f662bb94963a92e6ca40c224f16235947efbd137e809e313833e01cc97 - Вердикт конвейера:
green· High 0