Files
houseplan-card/docs/reviews/SPEC-REVIEW-363-r1.md
T
2026-08-29 09:23:00 +00:00

16 KiB
Raw Blame History

SPEC-REVIEW-363-r1

Issue: #363 · Этап: spec (PROCESS.md §2.4) · Трек: small (лёгкий) · Заход r1 · блокирующих циклов израсходовано 0 из 2 (лимит лёгкого трека — 2, §5)

Скоуп

ТЗ в теле issue #363: возврат кнопки «Добавить» в основной тулбар Device editor рядом с «Устройства» — короткий путь к уже существующему диалогу нового устройства (_openMarkerDialog() без аргумента), убранной в cab8d128 (#29) вместе с каталогом «Устройства». Каталог не трогается. Класс изменения — A (src/houseplan-editor-runtime.ts, три словаря src/i18n/*.json) плюс C (docs/USER-GUIDE*.md, оба changelog). Метка small подтверждена в аналитике владельца (S2): сложность 2/10, одна поверхность, миграций нет, нового UX-контракта нет (контракт — тот же, что до #29).

Важное обстоятельство состояния (проверить перед чтением остального)

В issue уже есть более ранняя попытка этого же захода: комментарий от claude (2026-08-29T09:16:10Z, жёлтый вердикт, Заход r1 · блокирующих циклов 1/2 · High: 0 · Medium: 1) и закоммиченный на dev docs/reviews/SPEC-REVIEW-363-r1.md (коммит 9d6d6eb9, тот же SHA, что и текущий HEAD). Тот документ нашёл M1: раздел «Release-артефакты» не называл обязательный прогон workflow «Скриншоты документации» и docs:accept --reviewed, из-за чего docs-job в CI Validate стал бы красным по расхождению sourceFingerprint, даже если ни один PNG не изменился (сценарий #230/#234).

Задача даёт этому заходу метаданные Заход r1 · 0 из 2 — то есть с точки зрения конвейера предыдущая попытка цикл не потратила: судя по всему, тот прогон не дошёл до финального структурированного ответа (без него, по инструкции конвейера, метка не переставляется), поэтому официально не засчитан, а issue не уходил в S3-spec. Тем не менее тело issue уже переписано в «ТЗ · revision 2» и текстуально закрывает M1 (см. ниже) — то есть автор (владелец, пишущий ТЗ прямо в issue на лёгком треке) увидел незасчитанный комментарий и поправил текст до этого захода. Я не доверяю этому на слово и ниже перепроверяю сам, что revision 2 действительно закрывает M1 и что остальные утверждения черновика остаются верными на текущем дереве. Раздела «Унаследовано из r» не завожу — по счётчику конвейера это первый действительный заход, и я перепроверил всё заново, а не по дельте.

Как проверялось

Ничего не принято на слово из текста ТЗ или прежнего черновика — каждое утверждение перепроверено чтением дерева на HEAD=9d6d6eb9:

  1. M1 закрыт. AC7 сейчас: «...полный артефакт принимается через npm run docs:accept -- --reviewed --from=<artifact>, а обновлённый docs/images/screenshots.json коммитится... Все PNG канонического набора (сейчас 10) ожидаются пиксельно неизменными». Раздел «Release-артефакты» дублирует то же требование и явно называет workflow «Скриншоты документации» обязательной «даже если видимая композиция кадров не меняется». Проверил, что названные команда и workflow существуют: .github/workflows/docs-screenshots.yml (шаг публикует docs/images/screenshots.json артефактом), package.json:16 — "docs:accept": "node scripts/docs-accept.mjs". Прежний черновик просил именно это, только называл «9 PNG» — сейчас в дереве demo/docs/screenshots.mjs действительно 10 сценариев (проверил grep -n "id:" demo/docs/screenshots.mjs и docs/images/screenshots.json → scenarios.length), то есть текущее ТЗ точнее черновика, а не расходится с деревом задним числом.
  2. _openMarkerDialog() жив, контракт не меняется. src/houseplan-editor-runtime.ts:7628 public _openMarkerDialog(d?: DevItem); вызов без аргумента — путь диалога нового устройства. Кнопка «Устройства» вызывает другой метод, _openDeviceInbox() (houseplan-editor-runtime.ts:11545), а «Добавить виртуальное устройство» внутри каталога вызывает тот же _openMarkerDialog() без аргумента (houseplan-editor-runtime.ts:11578, openVirtual). Значит новая кнопка получает ровно тот же путь, что уже используется каталогом для того же исхода — вторая, независимая проверка того, что AC2 просит воспроизводимую, а не новую логику.
  3. i18n-таблица побайтово совпадает с состоянием до cab8d128. git show cab8d128^:src/i18n/en.json и ...ru.json дают ровно "devbar.add": "Add" / "Добавить" и "title.add_device": "Add a device to the plan" / "Добавить устройство на план" — совпадает посимвольно с таблицей ТЗ. Оба ключа сейчас отсутствуют во всех трёх словарях (grep -n "devbar.add\|title.add_device" src/i18n/{en,ru,de}.json — пусто), значит работа по их возврату реальна, а не фиктивна.
  4. Немецкий текст — согласованное предположение, не догадка. de.json уже использует "device_inbox.add": "Hinzufügen" для того же действия и паттерн «существительное + hinzufügen» для похожих подписей ("title.add_space": "Bereich hinzufügen"). Предложенные "Hinzufügen" / "Gerät zum Plan hinzufügen" этому соответствуют. Помечено в ТЗ как принятое предположение, которое ревьюер вправе оспорить — не оспариваю.
  5. Паритет словарей ловит забытую локаль. test/i18n.test.mjs:109-112: assert.deepEqual(Object.keys(dictionary).sort(), enKeys) для каждого языка реестра. Пропуск любого из 6 значений (2 ключа × 3 локали) красит npm test. AC4 доказуемо этим тестом, и тест умеет падать (сейчас ключей нет ни в одном словаре — не найти это на голом дереве, если ключ забыт, тест обязан споткнуться).
  6. Место вставки и порядок. Текущий _renderDevicesBar() (houseplan-editor-runtime.ts:11541-11553) начинается с кнопки device_inbox.button («Устройства»), затем devbar.rules («Правила иконок»). До #29 первой кнопкой была «Добавить» (git show cab8d128^:src/houseplan-card.ts — фрагмент, приведённый в теле issue, подтверждён отдельно). Вставка новой кнопки перед «Устройства» восстанавливает этот порядок; номера строк в S2-аналитике (:11491) немного разошлись с текущими из-за более поздних коммитов — не находка, метод и разметка на месте.
  7. Каталог и его «Добавить виртуальное устройство» не задеты. _renderDeviceInbox() / openVirtual() вне диапазона правок ТЗ. demo/smoke_device_inbox.mjs существует, его заголовок прямо ссылается на #29 («one lifecycle catalog replaces the separate Add / hidden-device paths») — подходящий существующий регресс-щуп для AC3.
  8. RU/EN руководство действительно потеряло строку. docs/USER-GUIDE.ru.md:775-786 — таблица «Редактор устройств» содержит «Устройства» и «Добавить виртуальное устройство», строки про «Добавить» нет. docs/USER-GUIDE.md:519-529 — список без пункта «Add». Оба файла названы в «Затронутых файлах».
  9. Golden-сцены разведены верно. demo/golden/matrix.mjs:381 — geometry-devices-editor-dark: mode: 'devices', без ключа dialog — тулбар виден целиком, третья кнопка попадёт в кадр. device-inbox-* (строки 383-390) все три несут dialog: 'device-inbox' — модальный каталог перекрывает тулбар, эти кадры не должны измениться. --expect-change=geometry-devices-editor-dark в AC8 — точная и единственная нужная пометка.
  10. UX-MODES.md / TOUCH-SUPPORT.md — персистентный инструмент в основном тулбаре, barclose остаётся в правом торце (editbar-end, не тронут); новых touch-целей и жестов не вводится. Конфликта с «редакторы desktop-first, touch — best effort» нет.
  11. Обязательные разделы §7.1 — сценарий+результат, скоуп/не-скоуп, контракт поведения и UX, данные/совместимость (модель не меняется), i18n, AC1-AC8 с доказательством каждого, план тестов и мутанты, риски и откат, release-артефакты — все присутствуют в теле issue.
  12. Продуктовые вопросы, которые могли уйти владельцу как технические — не найдены: три «принятых предположения» (CSS-классы/wrap, отсутствие отдельного disabled-state, немецкий перевод) — все технические/оформ- ительские, ни одно не требует продуктового решения.

Гейты этого захода

Материал — только текст issue, продуктового кода по #363 в дереве ещё нет (git log --all --oneline | grep 363 и git branch -a | grep 363 не находят ветки/коммитов задачи, кроме случайных числовых совпадений 3633a3db (#159), 9d6d6eb9/это же ревью). Прогонять typecheck/test/build/ check-docs не над чем — это ревью текста (§2.4), а не кода (§2.7); их место в коде-ревью после реализации. Отдельно перечитал и вручную проверил исполняемость test/i18n.test.mjs (пункт 5) и существование demo/smoke_device_inbox.mjs, .github/workflows/docs-screenshots.yml, scripts/docs-accept.mjs чтением, не запуском — их сегодняшнее содержимое, а не будущее поведение, было предметом проверки.

Находки

Нет. M1 предыдущего (незасчитанного) захода закрыт текстом ТЗ (см. пункт 1 выше) и проверен по дереву, а не на слово. Догадок, выданных за решение, не найдено. Технических вопросов, которые следовало решить самому вместо эскалации владельцу, не найдено — их и не было.

Что проверено и корректно

  • Обработчик _openMarkerDialog() и его контракт (пункт 2).
  • Побайтовое совпадение i18n-таблицы с состоянием до cab8d128 и паритет-тест (пункты 3, 5).
  • Согласованность предложенного немецкого текста с действующим словарём (пункт 4).
  • Место вставки и восстановленный порядок кнопок (пункт 6).
  • Независимость каталога и его «Добавить виртуальное устройство» (пункт 7).
  • Пропуск строки тулбара в RU/EN руководстве (пункт 8).
  • Разведение golden-сцен geometry-devices-editor-dark vs device-inbox-* (пункт 9).
  • Отсутствие конфликта с UX-MODES.md / TOUCH-SUPPORT.md (пункт 10).
  • Полнота обязательных разделов ТЗ §7.1 и отсутствие невынесенных продуктовых вопросов (пункты 11-12).
  • Закрытие M1 предыдущего захода текстом revision 2 (пункт 1).

Чего не проверял

  • Реализацию — её ещё нет; код-ревью (§2.7) проверит diff, а не текст ТЗ.
  • Фактический прогон typecheck/test/build/check-docs/golden — не применимо к этому заходу, материала для прогона (diff) не существует.
  • Browser-смоки и golden-захват — то же самое, нечего запускать.
  • Реальный CI-прогон workflow «Скриншоты документации» — оценивалась только корректность его упоминания в ТЗ, а не его будущее исполнение.

Вердикт

Зелёный. High: 0, Medium: 0. ТЗ полно, однозначно, каждый AC привязан к способу доказательства, единственная находка предыдущей (незасчитанной) попытки закрыта текстом и подтверждена чтением дерева. Трек small подтверждён повторно.