Files
houseplan-card/legacy/reviews/v1.68.0/SPEC-REVIEW-262-r1.md
T
Claudeandclaude[bot] cca9bc856b docs(reviews): документы ревью линий до v1.77.0 — в legacy/reviews (#682)
Волна 5 эпика #674, перенос документов ревью (класс C), сделан
инструментом: `node scripts/reviews-archive.mjs --through=v1.77.0 --apply`.
965 документов 332 задач разложены по `legacy/reviews/<тег>/` (v1.63.0 …
v1.77.0) — 330 задач доказаны трейлерами своей линии, два документа без
трейлера (#68, #94, работа до правила трейлеров) — по линии, где их
добавили. В `docs/reviews/` остались 154 документа задач с трейлером после
v1.77.0 — текущая линия v1.78.0 — и INDEX.md, пересобранный тем же
построителем. История не переписана: SHA-якоря «Материала раунда» живы.
Счёт раундов (`reviewRounds` по живому каталогу и архиву) совпадает до и
после переноса: 694 пары «этап:задача». Строка в `legacy/README.md`.

Issue: #682
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-27 22:10:47 +00:00

16 KiB
Raw Blame History

SPEC-REVIEW-262-r1

  • Issue: #262 — Deleting entities prevents them from being added again later
  • ТЗ: docs/specs/262-readd-child-entity-after-device-delete.md
  • Ветка: issue/262-readd-child-entity, коммит на ревью: 7b3ea37
  • Этап: S4-spec-review (ревью ТЗ, PROCESS.md §2.4)
  • Заход: r1 · блокирующих циклов израсходовано 0 из 4 (зелёный вердикт бюджет не тратит, #227)
  • Диф ревью: git diff origin/dev...origin/issue/262-readd-child-entity — только docs/specs/262-readd-child-entity-after-device-delete.md (новый файл) и одна строка в docs/specs/README.md. Продуктового кода нет — соответствует стадии S3→S4 (Rule #1: код трогают только с S5-ready).

Скоуп

Bug-репорт: после удаления HA-устройства с плана его отдельные дочерние сущности невозможно добавить обратно даже при включённом флаге «Показывать сущности» — можно вернуть только всё устройство целиком. Аналитика владельца (комментарий в issue) разложила репорт на три сценария и подтвердила, что именно третий («удалить device, добавить одну entity того же device») — баг, остальные два уже работают штатно. ТЗ фиксирует контракт на этот третий сценарий: exact live entity:X должна перевешивать exact/parent tombstone только для себя самой, не воскрешая ни родительское устройство, ни соседние сущности.

Соответствие docs/SCOPE.md: закрывает J6 («Keep the plan true as the home evolves») — часть про «поддерживать план в актуальном состоянии» и явный выбор нужной HA-привязки после удаления. Продуктовой рамке не противоречит, новых поверхностей не открывает.

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

Ревью технической части ТЗ построено не на доверии тексту, а на сверке каждого фактического утверждения (§3, §4, §6, §8) с кодом на origin/dev — здесь именно тот случай, где «утверждение о поведении не в документах и не помеченное как предположение» было бы находкой, если бы не подтвердилось.

  1. §4.1 (picker разрешает только точный tombstone binding). Прочитан _bindingCandidates() в src/houseplan-card.ts (ветка «Individual entities of devices — behind the show entities checkbox»): removedBindings — Set точных строк binding с m.removed; исключение !removedBindings.has(v) сравнивается с v = 'entity:'+eid, а не с 'device:'+parentId. При device:D tombstone isRemovedPlanEntity(h, eid, removed) возвращает true (по removed.devices.has(deviceId)), removedBindings.has('entity:'+eid) — false ⇒ строка continue отбрасывает X из списка даже при включённом чекбоксе. Ровно то, что описывает ТЗ. Подтверждено чтением.
  2. §4.2 (фикса списка недостаточно). Прочитан _saveMarker(): replacedRemovedIds = markers.filter(m => m.removed && m.binding === dlg.binding) — снимает tombstone только с тем же exact binding (entity:X), родительский device:D tombstone переживает сохранение. Прочитан buildDevices(), ветка kind === 'entity': if (isRemovedPlanEntity(fullHass, ref, removed)) continue; — уже сохранённый живой explicit marker entity:X всё равно отбрасывается, потому что removed строится из того же markers[], где всё ещё лежит device:D tombstone. Оба утверждения ТЗ подтверждены чтением, не домыслены.
  3. §8 (schema-valid combination). Прочитан validation.py: схема маркера не запрещает сосуществование двух записей с разными binding и независимым removed; для device:D removed:true + живой entity:X нет constraint, который бы это отклонил. Утверждение «уже schema-valid» — не гипотеза.
  4. §3 (воспроизведение). Прочитан demo/smoke_binding_picker.mjs (добавлен в #263): блок 3 буквально помечает o.knownDefect262ChildEntityBlocked = !(await offered(true)).includes('entity:'+childEntity) с комментарием «почитают #262, смок покраснеет, потребовав перевернуть проверку» — соответствует плану автотестов ТЗ (AC1: «проверка known defect перевёрнута в положительную»).
  5. Согласованность с #226. Прочитан docs/specs/226-entity-parent-dedup.md (решения 4–5: «явная конфигурация сильнее автоматической», «tombstone не владеет сущностью») — контракт §6.4 ТЗ #262 («явная D не подавляет явную X») этому не противоречит, использует уже принятую модель, не переопределяет её.
  6. Согласованность с #104. docs/specs/104-opening-ha-reference-after-marker-delete.md сам ссылается на isRemovedPlanEntity/isRemovedPlanSource — подтверждает заявление ТЗ, что точные ссылки opening contact/lock уже независимы от marker tombstones и не задеты этим исправлением.
  7. Регистрация smoke-связи. scripts/smoke-links.mjs уже связывает символы removedPlanBindings, isRemovedPlanEntity, deletePlanMarkerRecords со smoke_binding_picker.mjs (внесено в #263) — заявление §11 ТЗ («имя _bindingCandidates остаётся прямым совпадением, дублировать не требуется») корректно, связь уже есть, ничего добавлять не нужно.
  8. Терминология UI. Флаг называется в ТЗ «Показывать сущности» — совпадает с формулировкой из docs/USER-GUIDE.ru.md (строки 657, 1587), а не изобретён. (Сам i18n-ключ marker.show_entities в коде переведён как «Отображать сущности» — расхождение между USER-GUIDE.ru.md и ru.json существует независимо от #262, автор ТЗ корректно взял термин из пользовательского гайда, как требует AGENTS.md; фиксирую это отдельно ниже как находку вне скоупа, не блокирующую этот заход.)
  9. Обязательные разделы ТЗ (PROCESS.md §7.1). Сверены построчно: сценарий и персона (§1) — есть, включая поверхность и момент; что человек видит до/после одной фразой без терминов реализации (§2) — есть, без упоминаний функций/tombstone; проблема (§3–4); скоуп и не-скоуп (§5); контракт поведения (§6); UX/touch/a11y (§7); данные/compatibility/миграция (§8); i18n/security/performance (§9); AC1…AC6 с доказательством (§10); план автотестов (§11); файлы (§12); release-артефакты (§13); риски (§14); откат (§15); блок принятых предположений (§16). Ничего не пропущено.
  10. Проверка «не выдана ли догадка за факт». Все технические утверждения о текущем поведении (§3, §4, §8) проверены чтением реального кода (пункты 1–3 выше), а не приняты на слово. Пять пунктов §16 корректно помечены как предположения и являются техническими (форма реализации, позиция нового маркера, отсутствие whitelist для registry-hidden), а не спрятанными продуктовыми решениями — ни один не требовал вопроса владельцу.
  11. AC на проверяемость и доказательства. AC1–AC6 однозначны, у каждого указан способ доказательства (smoke/unit/«существующие unit/smoke #104/#161/#226»/gates), совпадающий с §11 и с уже зарегистрированными файлами/тестами.

Находки

Блокирующих (High) и находок Medium в скоупе — нет.

Medium — вне скоупа (не блокирует, заводится отдельным issue по §12 PROCESS.md)

Расхождение термина чекбокса между docs/USER-GUIDE.ru.md («Показывать сущности», строки 657 и 1587) и фактическим i18n-ключом marker.show_entities в src/i18n/ru.json («Отображать сущности»). Автор ТЗ #262 действовал верно — взял формулировку из канонического пользовательского гайда, как требует AGENTS.md, — но само расхождение живёт в репозитории независимо от #262 и не имеет отношения к его сценарию. Правка в этой ветке была бы посторонним скоупом (диалог маркера правит другой файл — houseplan-card.ts/ru.json, не затронутый ТЗ #262). Заведён отдельным issue — см. решение ниже.

Низких (Low) находок нет.

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

  • Диагноз двухчастного дефекта (picker exact-binding whitelist + runtime isRemovedPlanEntity в buildDevices) — точное соответствие коду dev, не гипотеза.
  • Контракт §6 (матрица Add picker, транзакция сохранения, runtime-приоритет, что остаётся удалённым) — самосогласован: список Add специально шире, чем runtime-построение (все дети D становятся видимыми в Add, но строится/рендерится только явно сохранённый marker) — это осмысленное разделение, а не противоречие.
  • Совместимость с уже принятыми контрактами #226, #161, #104 подтверждена по их собственным спекам и коду, не только заявлена.
  • Данные/миграция: комбинация device:D removed:true + живой entity:X уже schema-valid, новых полей и model bump нет — подтверждено чтением validation.py.
  • Воспроизведение (§3) подтверждено существующим browser smoke demo/smoke_binding_picker.mjs (#263), который уже фиксирует knownDefect262ChildEntityBlocked: true и явно рассчитан на переворот проверки этим исправлением.
  • Все обязательные разделы ТЗ (PROCESS.md §7.1) присутствуют и по существу, не формально.
  • Продуктовых вопросов владельцу нет, и это оправдано: ни одна неопределённость в ТЗ не задевает то, что видит или делает пользователь сверх уже описанного в §1–§2; сценарий закрыт исходным репортом и аналитическим комментарием владельца.
  • i18n-раздел корректен: новых ключей нет, использованная терминология взята из docs/USER-GUIDE.ru.md, а не изобретена.

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

  • Гейты typecheck/test/build/check-docs не гонялись: этап — ревью ТЗ (S3→S4), продуктового и тестового кода в дифе нет (только новый docs/specs/*.md и строка в docs/specs/README.md), эти гейты относятся к этапу код-ревью (PROCESS.md §2.7, §8) и будут прогнаны там.
  • Не проверялась работоспособность будущей реализации — её ещё нет; предмет этого ревью — выполнимость и однозначность ТЗ, а не код.
  • Не запускал browser smoke demo/smoke_binding_picker.mjs — код на этой ветке не менялся относительно dev, поведение смока не могло измениться; чтения исходника было достаточно, чтобы подтвердить, что смок уже воспроизводит описанный в ТЗ дефект.
  • Не проверял golden/performance/mutation-gate — на этой стадии нет ни визуальных, ни мутационных изменений: диф ограничен документацией.

Решение

Найдена ровно одна находка — Medium, вне скоупа задачи (терминологическое расхождение чекбокса, не создано и не усугублено этим ТЗ). Она не блокирует переход в S5-ready и заводится отдельным issue со ссылкой на #262, а не чинится в этой ветке.

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