Ревью #682 r1, Medium: перенос добавляет документу уровень вложенности (`docs/reviews/X.md` → `legacy/reviews/<тег>/X.md`, `docs/specs/` → `legacy/specs/`), а относительные ссылки внутри перенесённых документов и в соседях, ссылавшихся на них, никто не пересчитывал — на `97d19268` 53 битые ссылки в 46 файлах (заявление «все 26 резолвятся» в `7feb6177` было верно только до переноса документов ревью). Гейты архив не смотрят. `reviews-archive.mjs`: `repairLinks` пересчитывает ссылку, если она не резолвится от нового места, а цель находится от нового или старого места через карту переносов; битая и до переноса ссылка не трогается. `--apply` делает это само, `--repair-links=<rev>` — для всех переименований `<rev>..HEAD`, `--check-links` печатает битые. Этим коммитом `--repair-links=origin/dev` переписал ровно 53 ссылки в 46 файлах; остались две прежние «...»-заглушки в CODE-REVIEW-448-r2 (битые и на dev). Тесты: перенесённый документ, сосед со ссылкой в архив, ТЗ со ссылкой на позже перенесённое ревью, битая-до-переноса не трогается, в `legacy/` битых нет; мутант `reviews-archive-links-from-new-place-only`. PROCESS §2.10 и DEVELOPMENT › Release называют переписывание и `--check-links`. Issue: #682 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
14 KiB
Ревью ТЗ — issue #200, цикл r1
- Этап:
S4-spec-review(PROCESS.md §2.4) - Артефакт ТЗ:
docs/specs/200-room-label-parity.md, коммит4089c91на веткеissue/200-room-label-parity - Issue: #200
- Ревьюер: Claude (роль «ревьюер ТЗ», отдельная сессия от аналитика/автора)
- Трек: обычный (не
small) — верно, т.к. меняется явный UX-контракт кнопки между View и Plan editor (§5 PROCESS.md исключает small при новом UX-контракте)
Скоуп ревью
Оценивалось ТЗ docs/specs/200-room-label-parity.md целиком: наличие
обязательных разделов §7.1 PROCESS.md, однозначность и доказуемость AC1–AC9,
отсутствие догадок, выданных за решённый факт, соответствие
docs/SCOPE.md (job J4/J6), docs/UX-MODES.md, docs/TESTING.md,
docs/USER-GUIDE.ru.md, docs/STYLING-HOOKS.md и текущему коду
(src/houseplan-card.ts, src/styles.ts, demo/smoke_room_link.mjs).
Продуктовый код не менялся и не мог быть изменён (задача на этапе ТЗ).
Как проверялось
- Прочитаны
docs/SCOPE.md,AGENTS.md,PROCESS.mdцеликом. - Прочитано тело issue #200 и все 4 комментария: аналитика, вопросы владельцу Q1–Q3, принятые дефолты, хендофф автора ТЗ.
- Прочитан весь текст ТЗ построчно, сверен с §7.1 (обязательные разделы) и §2.5 (DoR-чеклист).
- Проверена заявленная причина дефекта чтением продуктового кода:
src/houseplan-card.ts:17144-17150(_renderRoomLabel, условие!this._markup && r.area),src/houseplan-card.ts:1203-1205(геттер_markup, true только дляmode === 'plan'),src/houseplan-card.ts:15807-15808(условие рендера самого room-label). Проверен существующий смокdemo/smoke_room_link.mjs(текущий инвариантout.noneInPlan) и запись контракта вdocs/TESTING.md:932-935. - Проверен CSS
.rlgo/.roomlabel/.rlname/.rlmetricsвsrc/styles.ts:872-989, включая комментарий в коде («the name renders in exactly the same place in view mode and in the plan editor (owner's request)») — он подтверждает, что геометрический инвариант уже задумывался раньше и сейчас нарушен именно тем местом, которое называет ТЗ. - Проверено соответствие терминологии:
src/i18n/ru.json:526(room.open_area→ «Открыть зону в HA»),docs/USER-GUIDE.ru.md(упоминаний.rlgo/значка перехода нет — новый пользовательский сценарий действительно не появляется). - Проверено, что файл ТЗ и запись в
docs/specs/README.mdдобавлены одним коммитом4089c91с трейлерамиIssue: #200/User-Visible: no— корректно для чистой документации без изменения поведения. - Сверено сравнение "лёгкий трек не подходит" с критериями §5 PROCESS.md.
Код не менялся, чтения было достаточно: вопрос ревью ТЗ — «выполнимо и проверяемо ли», а не «работает ли реализация».
Проверено и корректно
- Все обязательные разделы §7.1 присутствуют: сценарий и персона (§1), что человек увидит до/после без терминов реализации (§2), проблема и подтверждённая причина (§3), scope/non-scope (§4–5), контракт поведения (§6), данные/миграция (§7), UX/i18n/touch (§8), AC1–AC9 с доказательством (§9), план автотестов (§10), риски (§11), rollback (§12), release-артефакты (§13).
- Причина дефекта подтверждена чтением кода, а не заявлена на веру. Условие
!this._markup && r.areaв_renderRoomLabel(houseplan-card.ts:17144) действительно единственное место, отличающее.rlnameмежду View и Plan;.rlmetricsвынесен из centering math и одинаков — ровно то, что говорит ТЗ. - Продуктовые вопросы владельцу заданы по существу, не подменены техническими. Q1 («что делает иконка в Plan») и Q3 («что сохраняется поверх карточки») — то, что видит и делает человек; Q2 («в какой системе координат мерить „не сдвигается ни на пиксель“») пограничен, но корректно сведён к наблюдаемому факту (anchor vs viewport), а не к технической реализации — ответ владельца определяет ожидаемое пользовательское поведение теста, а не структуру кода. Владелец принял все три дефолта, вопросы закрыты до начала написания ТЗ.
- Все AC пронумерованы, однозначны и несут явный способ доказательства
(
smoke/golden/gates/mutation gate) — выполняется требование DoR (§2.5 PROCESS.md). AC4/AC5 задают числовой допуск (≤0,5 CSS px, DPR 1 и 2, light/dark) — не оценочная формулировка, а измеримый критерий. - AC9 (mutation gate) — сильная часть ТЗ. Оба мутанта воспроизводят ровно
те регрессии, которых боится сценарий (иконка исчезла в Plan; иконка
появилась, но стала кликабельной и ломает drag) и соответствуют
установленному в проекте формату
scripts/mutation-gate.mjs(issue #85). - Не-скоуп сформулирован точно и предотвращает соблазн заодно поправить
соседнее: абсолютные координаты страницы, миграцию слоя
layout, unnamed placeholder, resize handles, кнопку настроек, touch policy, isometric, performance pipeline — всё явно исключено с объяснением почему. - Технические решения корректно помечены как предположения, а не факты
(§14 «принято предположительно, поменять свободно при ревью»): отсутствие
title/role/tab stop у иконки в Plan, переиспользование существующих
.roomlabel/styling hooks без нового wrapper, приоритет числового smoke над golden. Ревьюер эти предположения не оспаривает — они разумны и не влияют на AC. docs/TESTING.mdи user-facing changelog корректно учтены как release-артефакты (AC7, §13); существующая запись вdocs/TESTING.md:932-935(«no icon in editors... [auto: smoke_room_link]») действительно требует правки, и ТЗ это называет прямо.docs/USER-GUIDE.ru.mdне требует нового сценария — проверено: гайд вообще не описывает иконку перехода к зоне отдельно (только общую фразу про карточки/названия комнат, docs/USER-GUIDE.ru.md:271), значит добавить противоречащую формулировку неоткуда; проверка "при реализации перепроверить формулировки" в ТЗ достаточна.- Откат и миграция: корректно — persisted-формат и API не меняются, downgrade возвращает старый визуальный сдвиг без порчи данных.
- Оценка "лёгкий трек не подходит" верна: критерий §5 «нет нового UX-контракта» нарушается (кнопка получает новую видимую форму в Plan), значит обычный трек — правильный выбор, а не подстраховка.
Находки
Low — неточная формулировка причины дефекта в §3 ТЗ
Файл: docs/specs/200-room-label-parity.md, раздел 3 («Проблема и
подтверждённая причина»).
Суть: ТЗ утверждает: «условие !this._markup && r.area рендерит .rlgo
только в View». Это неточно: _markup (houseplan-card.ts:1203) истинен
только при mode === 'plan', то есть .rlgo при выполнении условия рендерится
не только в View, но и в редакторах Devices/Background — там, где включена
настройка «Показывать названия» (houseplan-card.ts:15807,
disp.showNames || ... || this._markup). Иконка сегодня отсутствует только в
Plan editor, а не «только присутствует в View».
Проверено, что это не влияет на корректность AC. Единственное изменение,
которое требуют AC1–AC9, — снять условие !this._markup для Plan; поведение
Devices/Background editors не меняется и не тестируется в этом ТЗ (что и
верно — они вне заявленного scope «Plan editor ↔ View»). Наивная реализация
(рендерить .rlgo при r.area независимо от режима, событийный контракт
регулировать отдельно только для Plan) не пострадает от этой неточности.
Почему не блокирует: это неточность в описании диагноза, а не в контракте поведения (раздел 6) или в AC (раздел 9), которые сформулированы верно и не опираются на слово «только в View». Реализация и код-ревью будут сверяться с разделами 6 и 9, а не с формулировкой раздела 3.
Решение ревьюера: снимается без правки, с записью в этом документе (разрешено §2.4/§3 PROCESS.md: «Low либо правится, либо снимается решением ревьюера с записью»). Если автор всё же правит ТЗ по другой находке в будущем цикле, желательно заодно уточнить формулировку («рендерится везде, кроме Plan editor»), но отдельного цикла ревью это не требует.
Других находок — Low, Medium или High — не выявлено.
Чего не проверял
- Не проверялась реализация — на этапе ТЗ её не существует; код-ревью будет
отдельным циклом (
S7-code-review) после написания кода. - Не запускались
npm test/npm run build/смоки: код не менялся, гейты ТЗ не требуют их прогона на этом этапе (документация — класс C, продуктовый код не тронут). - Не проверялась визуальная точность будущих golden-скриншотов — они появятся только в реализации; ТЗ описывает их корректно (focused, light/dark), этого достаточно для стадии ревью ТЗ.
- Не оценивалась производительность на реальном большом плане — ТЗ обоснованно сводит риск к «пренебрежимо мал» (один существующий DOM-узел в ещё одном режиме), и это утверждение проверяется кодом-ревью, а не спецификацией.
Вердикт
Все обязательные разделы на месте, все AC однозначны и снабжены способом доказательства, причина дефекта подтверждена чтением кода, продуктовые вопросы владельцу закрыты до написания ТЗ, non-scope точен, mutation-gate усиливает доказуемость AC1/AC3. Единственная находка — Low, не влияющая на корректность контракта, снята с записью.
Вердикт: зелёный · цикл r1/4 · High: 0 · Medium: 0 → в задаче