mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-04 05:41:34 +00:00
183 lines
16 KiB
Markdown
183 lines
16 KiB
Markdown
# 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<N-1>» не завожу — по счётчику
|
||
конвейера это первый действительный заход, и я перепроверил всё заново, а
|
||
не по дельте.
|
||
|
||
## Как проверялось
|
||
|
||
Ничего не принято на слово из текста ТЗ или прежнего черновика — каждое
|
||
утверждение перепроверено чтением дерева на `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`
|
||
подтверждён повторно.
|