14 KiB
SPEC-REVIEW-397-r2
- Issue: https://github.com/Matysh/houseplan-card/issues/397
- ТЗ:
docs/specs/397-device-position-echo.md(класс A, полный трек) - Заход: r2 · блокирующих циклов израсходовано 1 из 4 (r1 — жёлтый, потратил цикл; этот раунд бюджет не тратит, см. вердикт)
- SHA r1:
83692e79(найден в документеdocs/reviews/SPEC-REVIEW-397-r1.md, раздел "Скоуп"; в комментарии-вердикте r1 SHA не назван — это находка формата, зафиксирована ниже, не блокирует) - SHA этого раунда:
6edcde01
Скоуп раунда
Дельта — только docs/specs/397-device-position-echo.md:
git diff 83692e79..6edcde01 -- docs/specs/397-device-position-echo.md
1 file changed, 23 insertions(+), 7 deletions(-)
(второй файл в общем диапазоне, docs/reviews/SPEC-REVIEW-397-r1.md, —
публикация документа предыдущего раунда, не предмет ревью.)
Правка автора — точечный ответ на вердикт r1: один Medium (AC5б/AC7 без
способа доказательства) и два Low (диапазон строк, отсутствие фразы
"Доказательство" у AC3). Продуктовый код не тронут (src/** не в диффе) —
ожидаемо для этапа "ТЗ на ревью". Дельта локальна: не ребейз, контракт
поведения не меняется, новая подсистема не задета, объём дельты (23 строки)
несопоставим с объёмом исходной задачи (174 строки). Разбор по дельте
достаточен; полный повторный прогон не требуется.
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где видно |
|---|---|---|
| Medium: AC7 и вторая половина AC5 без способа доказательства | Добавлены пункты 7 (AC5б: удаление → честный reload → canUndo остаётся true) и 8 (AC7: запись «в полёте», сервер отвечает старой позицией → в _layout остаётся отправленная) в "План автотестов"; у AC5 и AC7 добавлена фраза "Доказательство: …" со ссылкой на эти пункты |
docs/specs/397-device-position-echo.md:121–138 (AC), :157–160 (план) |
Low-1: диапазон _persistDevicePlacement указан как :5226-5258, тело функции длиннее |
Исправлено на :5226-5261 |
docs/specs/397-device-position-echo.md:28 |
| Low-2: у AC3 нет явной фразы "Доказательство" | Добавлено "Доказательство: смок, пункт 5 плана — …" | docs/specs/397-device-position-echo.md:114–117 |
Все три закрытия проверены не на слово автора, а построчным чтением текущего
src/houseplan-card.ts и src/device-position-history.ts на SHA 6edcde01
(продуктовый код с r1 не менялся, но заново прочитан, так как новые
формулировки AC5/AC7 делают новые технические утверждения о его поведении —
это предмет дельты, не наследуется):
- Диапазон функции.
awkпо файлу подтверждает:private async _persistDevicePlacement(начинается на:5226, закрывающая скобка метода — на:5261(послеthis._persistLocalLayout(); }). Новый диапазон точен. - AC5, обоснование раздельного доказательства. Ветка удаления
(
placement === null,pending = null,houseplan/layout/delete) подтверждена буквально на:5236-5242.applyDevicePlacement(src/device-position-history.ts:45-55) приplacement === nullдействительно удаляет ключ (delete next[deviceId]), а не "заменяет значение" — рационале AC5 в ТЗ технически верно, не догадка. - AC7, механизм "в полёте побеждает".
_reloadLayoutOnly(:4904-4948) собираетmineиз_sentPos(:4918), затем накладывает поверх ответа сервера (:4922-4926:for (const [id, pos] of mine) … merged[id] = pos) до сравнения отпечатков (:4927-4928). Формулировка AC7 и сценарий плана (пункт 8) описывают ровно этот код, не предположение. - Смок для пунктов 7–8 ложится в тот же файл фикстуры
(
demo/smoke_device_position_history.mjs), что и пункты 4–6 — не пересекается сdemo/smoke_layout_sync.mjs:87, который (по находке r1) проверяет только дренаж_sentPos, а не победу при слиянии; новый пункт закрывает именно тот пробел, который был назван.
Все три закрытия — не косметика, а корректные по содержанию правки: заявленное в тексте совпадает с тем, что делает код.
Унаследовано из r1
Без повторной проверки в этом раунде принято (документ
docs/reviews/SPEC-REVIEW-397-r1.md, SHA 83692e79, дельта их не задевает):
- Обязательные разделы §7.1 присутствуют полностью, в правильном порядке; сценарий и "что человек увидит" отвечают на оба вопроса без терминов реализации; связь со SCOPE.md (J6, "Keep the plan true as the home evolves") — не изменились в дельте r2.
- Технический разбор B3 (некорректная запись в
_layout, нежеканонический отпечаток) и M1 (подстроенная фикстура смока) — воспроизведены построчно в r1, дельта r2 текст находок не меняет (кроме номера строки, отдельно перепроверен выше). - Граница скоупа: путь
_savePos/_persistLayout(подписи комнат) багом не затронут — раздел "Скоуп / не-скоуп" не менялся. - Цитата AC10 ТЗ #74 сверена с
docs/specs/074-device-position-undo.md:254— не менялась. - Модель данных/миграция, i18n, откат, release-артефакты — разделы не затронуты дельтой.
- AC1, AC2, AC4, AC6 — однозначны, доказательство названо и прослеживается до плана автотестов; текст этих AC дельтой не менялся.
- Гейты
npx tsc --noEmit,npm test(1654/1655, 1 известный skip),npm run buildбыли зелёными на83692e79; на6edcde01полный набор подтверждён Validate (см. ниже) — независимая проверка, не перенос старого результата.
Как проверялось в этом раунде
- Найден вердикт r1 в комментариях issue (жёлтый, Medium в скоупе) и SHA
ревью — из тела документа
SPEC-REVIEW-397-r1.md(в самом комментарии-вердикте SHA не указан, см. находку формата ниже). git diff 83692e79..6edcde01 -- docs/specs/397-device-position-echo.md— вся дельта прочитана целиком.- Каждое из трёх закрытий сверено с текущим кодом на
6edcde01(см. таблицу выше) — не принято на слово. - Перечитаны обязательные разделы §7.1 в местах, которые правка не касалась,
ровно настолько, чтобы убедиться в отсутствии противоречий между новым
текстом AC5/AC7/AC3 и остальным документом (сценарий, скоуп, риски) —
противоречий нет: раздел "Риски" уже (с r1) называл гонку с
_sentPosи порядок записи как явные риски, новый AC7 их формализует, не противоречит. - Гейты: Validate на
6edcde01зелёный (см. ссылку в задании на этот раунд) —npx tsc --noEmit,npm test,npm run buildне перегонялись повторно, приняты по этой ссылке.
Находки
Low (формат, не блокирует) — SHA не назван в комментарии-вердикте r1
Комментарий-вердикт r1 в issue (2026-08-30T23:19:27Z) не содержит SHA, на
котором получен результат — вопреки практике, которой сам документ ревью
r1 следует (SHA ревью: 83692e79 в теле документа). SHA пришлось
восстанавливать через документ, а не через комментарий. Не блокирует эту
задачу (SHA найден и подтверждён), но стоит перенести в шаблон
комментария-вердикта на будущее, чтобы не полагаться на то, что документ
ревью всегда будет открыт вместе с комментарием.
Новых находок по содержанию ТЗ в дельте r2 нет: оба Low и один Medium из r1 закрыты корректно и по существу (см. таблицу выше), новых недоказанных утверждений или расхождений с кодом дельта не вносит.
Что проверено и корректно
- Все три пункта закрытия r1 (Medium AC5б/AC7, Low диапазон строк, Low фраза "Доказательство" у AC3) — не косметические переформулировки, а точные по содержанию правки, проверенные построчным чтением текущего исходника.
- Новые технические утверждения, добавленные дельтой (ветка удаления снимает
ключ, а не заменяет значение;
_sentPosпобеждает при слиянии в_reloadLayoutOnlyдо сравнения отпечатков), верны и не являются непомеченными догадками. - Новые пункты плана автотестов (7, 8) целятся в код, который дельта не трогала, но который граничит с фиксом B3 (тот же риск, что и в r1) — теперь у обеих веток есть названный, воспроизводимый способ доказательства.
- Документ внутренне непротиворечив: новый текст AC не противоречит разделам "Риски", "Скоуп/не-скоуп", "Модель данных".
Чего не проверял и почему
npx tsc --noEmit,npm test,npm run build— не прогонял повторно: Validate на6edcde01зелёный (ссылка дана в задании на раунд), диф этого раунда не тронул код после последнего зелёного прогона.node scripts/check-docs.mjs— не прогонялся: диф не трогаетsrc/**.npm run invariants -- --config …— не прогонялся: диф не меняет геометрию/layout/marker.space/open_spans.demo/smoke_*.mjs,scripts/smoke-select.mjs— не прогонялись: продуктовый код и демо-фикстуры не менялись ни в r1, ни в этой дельте (это по-прежнему этап ТЗ, реализации ещё нет).npm run golden:verify,python -m pytest tests_backend -q, performance-профили,bundle:sync/bundle:budget— не применимо: дельта документационная, ни рендер, ниcustom_components/**/*.py, ниdist/**не затронуты.
Вердикт
Один Medium и два Low из r1 закрыты корректно, проверено построчно против кода на текущем SHA, новых High/Medium в дельте не найдено. Один Low (формат — SHA не назван в комментарии-вердикте r1) не блокирует и не в скоупе этой задачи.
Зелёный. ТЗ готово к DoR/S5-ready.