mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-06 06:38:57 +00:00
docs: specify child entity re-add lifecycle
Issue: #262 User-Visible: no
This commit is contained in:
@@ -0,0 +1,442 @@
|
||||
# Issue #262 — повторное добавление entity после удаления родительского устройства
|
||||
|
||||
- Issue: [#262](https://github.com/Matysh/houseplan-card/issues/262)
|
||||
- Ветка: `issue/262-readd-child-entity`
|
||||
- Статус: `S3-spec`
|
||||
- Приоритет: `P2`
|
||||
- Тип: `bug`
|
||||
- Трек: обычный
|
||||
- Связанные задачи: [#161](https://github.com/Matysh/houseplan-card/issues/161),
|
||||
[#226](https://github.com/Matysh/houseplan-card/issues/226),
|
||||
[#263](https://github.com/Matysh/houseplan-card/issues/263),
|
||||
[#104](https://github.com/Matysh/houseplan-card/issues/104)
|
||||
|
||||
## 1. Сценарий и персона
|
||||
|
||||
**Персона:** администратор дома из `docs/SCOPE.md`, который поддерживает план в
|
||||
актуальном состоянии (J6).
|
||||
|
||||
**Поверхность:** desktop-first редактор устройств, диалог **Добавить** → выбор
|
||||
HA-привязки → включённый флаг **Показывать сущности**.
|
||||
|
||||
**Сценарий:**
|
||||
|
||||
1. на плане размещено HA-устройство `device:D` с несколькими дочерними
|
||||
сущностями;
|
||||
2. пользователь удаляет устройство с плана, и House Plan сохраняет минимальный
|
||||
`device:D` tombstone;
|
||||
3. позже пользователь хочет вернуть не всё устройство, а только одну его
|
||||
сущность `entity:X`;
|
||||
4. пользователь открывает **Добавить**, включает **Показывать сущности**, находит
|
||||
X, сохраняет маркер и размещает его как обычную отдельную entity.
|
||||
|
||||
Это не сценарий администрирования реестра HA: устройство и X остаются активными
|
||||
в Home Assistant. Меняется только состав объектов House Plan.
|
||||
|
||||
## 2. Что человек увидит до и после
|
||||
|
||||
**До:** удалённое устройство можно вернуть целиком, но ни одну его дочернюю
|
||||
сущность нельзя найти даже при включённом **Показывать сущности**.
|
||||
|
||||
**После:** в том же списке доступны активные дочерние сущности удалённого
|
||||
устройства; сохранение выбранной сущности возвращает только её, а удалённое
|
||||
родительское устройство и остальные его сущности сами на план не возвращаются.
|
||||
|
||||
Новых кнопок, предупреждений, toast-сообщений и настроек не появляется.
|
||||
|
||||
## 3. Подтверждённое воспроизведение
|
||||
|
||||
Текущий браузерный smoke `demo/smoke_binding_picker.mjs`, добавленный в #263,
|
||||
проверяет настоящий `_bindingCandidates()` собранной карточки и фиксирует:
|
||||
|
||||
- `device:D` после удаления снова предлагается — работает;
|
||||
- обычная entity другого живого устройства появляется после включения
|
||||
**Показывать сущности** — работает;
|
||||
- `entity:X` удалённого `device:D` отсутствует —
|
||||
`knownDefect262ChildEntityBlocked: true`.
|
||||
|
||||
Автор отчёта подтвердил именно этот третий сценарий. Повторное добавление всего
|
||||
устройства является обходом, но меняет желаемый состав маркера и потому не
|
||||
закрывает проблему.
|
||||
|
||||
## 4. Причина
|
||||
|
||||
### 4.1 Picker разрешает только точный tombstone binding
|
||||
|
||||
`removedPlanBindings()` хранит отдельно удалённые device ids и entity ids.
|
||||
`isRemovedPlanEntity(hass, X, removed)` считает X удалённой, когда удалена либо
|
||||
сама `entity:X`, либо её родитель `device:D`.
|
||||
|
||||
`_bindingCandidates()` допускает исключение для точной строки из набора
|
||||
`removedBindings`:
|
||||
|
||||
- tombstone `device:D` разрешает снова показать `device:D`;
|
||||
- tombstone `entity:X` разрешает снова показать `entity:X`;
|
||||
- tombstone `device:D` **не** является точной строкой `entity:X`, поэтому X
|
||||
отбрасывается общим `isRemovedPlanEntity()`.
|
||||
|
||||
### 4.2 Исправления только списка недостаточно
|
||||
|
||||
Если принудительно записать `entity:X` поверх текущего конфига:
|
||||
|
||||
- `_saveMarker()` удалит только tombstone с тем же exact binding; родительский
|
||||
`device:D` tombstone останется;
|
||||
- `buildDevices()` снова вызовет `isRemovedPlanEntity()` и отбросит уже живой
|
||||
explicit marker X;
|
||||
- plan/render availability, live text, room-source picker и агрегаты используют
|
||||
ту же модель удаления и также могут считать X недоступной.
|
||||
|
||||
Следовательно, контракт должен охватывать и вход в picker, и runtime-приоритет
|
||||
живого exact entity-marker над родительским tombstone.
|
||||
|
||||
## 5. Scope
|
||||
|
||||
### Входит
|
||||
|
||||
1. Показ активных допустимых дочерних entity удалённого `device:D` в Add при
|
||||
включённом **Показывать сущности**.
|
||||
2. Сохранение выбранной `entity:X` как обычного живого explicit marker.
|
||||
3. Сохранение `device:D` tombstone после такого действия: родительское устройство
|
||||
не должно автоматически воскреснуть.
|
||||
4. Exact live `entity:X` имеет приоритет над tombstone самой X и/или её parent D
|
||||
во всех обычных marker-level runtime-потребителях.
|
||||
5. Все не выбранные siblings устройства D и device-level binding D остаются
|
||||
удалёнными с плана.
|
||||
6. Согласованность с ownership-контрактом #226 и существующими правилами exact
|
||||
re-add из #161.
|
||||
7. Unit, browser smoke, mutation guard, документация и changelog.
|
||||
|
||||
### Не входит
|
||||
|
||||
1. Автоматическое включение **Показывать сущности** или изменение его текста.
|
||||
2. Снятие лимита 200 строк и изменение поиска/сортировки picker.
|
||||
3. Показ HA-disabled, orphaned, registry-hidden или недоказанных entity: для них
|
||||
продолжают действовать текущие registry-фильтры.
|
||||
4. Автоматическое восстановление всех дочерних entity при выборе одной.
|
||||
5. Изменение независимого контракта opening contact/lock из #104: точные ссылки
|
||||
архитектурных объектов по-прежнему игнорируют marker tombstones.
|
||||
6. Очистка tombstones, layout или файлов через Optimize.
|
||||
7. Новая миграция формата конфига или изменение `PLAN_MODEL_VERSION`.
|
||||
|
||||
## 6. Контракт поведения
|
||||
|
||||
### 6.1 Матрица Add picker
|
||||
|
||||
Пусть активная registry-backed `entity:X` принадлежит активному `device:D`.
|
||||
|
||||
| Конфиг House Plan | `device:D` в Add | `entity:X` при выключенном флаге | `entity:X` при включённом флаге |
|
||||
|---|---:|---:|---:|
|
||||
| ничего не размещено/не удалено | по текущим правилам | нет | да |
|
||||
| живой `device:D` marker | нет | нет | да, если exact X не занята |
|
||||
| `device:D` tombstone | да | нет | **да** |
|
||||
| `entity:X` tombstone, parent жив | по текущим правилам | нет | да |
|
||||
| живой `entity:X` marker | по текущим правилам #226 | нет | нет |
|
||||
|
||||
Флаг **Показывать сущности** остаётся выключенным для нового маркера. Исправление
|
||||
не маскирует эту существующую UX-ступень.
|
||||
|
||||
### 6.2 Транзакция повторного добавления
|
||||
|
||||
При сохранении X из состояния с `device:D` tombstone итоговый persisted набор
|
||||
содержит:
|
||||
|
||||
- прежний минимальный `device:D` tombstone;
|
||||
- ровно один живой marker с binding `entity:X`;
|
||||
- ни одного `entity:X` tombstone;
|
||||
- без изменений tombstones/markers остальных binding.
|
||||
|
||||
Новый X получает обычную свежую позицию по действующему правилу Add. Позиция,
|
||||
метаданные и файлы удалённого D не восстанавливаются и не наследуются X.
|
||||
|
||||
Если конфиг содержит старый/конкурентный exact `entity:X` tombstone, сохранение X
|
||||
заменяет его как обычный exact re-add. Parent tombstone при этом сохраняется.
|
||||
|
||||
### 6.3 Runtime-приоритет
|
||||
|
||||
Для marker-level семантики действует порядок:
|
||||
|
||||
1. живой explicit `entity:X` разрешает X;
|
||||
2. exact `entity:X` tombstone подавляет X, если живого exact marker нет;
|
||||
3. `device:D` tombstone подавляет X, если живого exact marker нет;
|
||||
4. обычная registry availability решает, активна ли разрешённая X в HA.
|
||||
|
||||
Исключение относится только к exact живой entity-binding. Нельзя трактовать
|
||||
наличие **одной** восстановленной entity как снятие parent tombstone со всех
|
||||
siblings.
|
||||
|
||||
Разрешённая X должна вести себя как обычный entity-marker во всех потребителях:
|
||||
|
||||
- строиться и рендериться во View, kiosk, editors и static card;
|
||||
- получать live state, value badge, activity, Glow и разрешённые действия по
|
||||
существующим правилам домена;
|
||||
- участвовать в room aggregates/source pickers там, где обычная explicit X
|
||||
участвовала бы без tombstone;
|
||||
- быть доступной live text и marker controls, если эти потребители ссылаются на
|
||||
X по существующим правилам.
|
||||
|
||||
### 6.4 Что остаётся удалённым
|
||||
|
||||
После восстановления X:
|
||||
|
||||
- `device:D` не строится автоматически, потому что его tombstone остаётся
|
||||
точным claim родительского binding;
|
||||
- `device:D` остаётся доступным в Add для осознанного восстановления целиком;
|
||||
- любая sibling `entity:Y`, не имеющая собственного живого explicit marker,
|
||||
остаётся подавленной во всех marker-level runtime-потребителях;
|
||||
- device-level source `device:D` остаётся удалённым;
|
||||
- автоматические агрегаты не получают данные Y через ослабленный общий guard.
|
||||
|
||||
### 6.5 Дальнейшие действия
|
||||
|
||||
1. Если пользователь снова удаляет X, рядом с parent tombstone появляется exact
|
||||
`entity:X` tombstone; X снова доступна для exact re-add через picker.
|
||||
2. Если пользователь затем явно добавляет `device:D`, exact device tombstone
|
||||
заменяется живым device-marker. Живой X сохраняется рядом: два явных marker
|
||||
считаются осознанной конфигурацией по #226.
|
||||
3. Если пользователь добавляет несколько дочерних entity D, каждая становится
|
||||
отдельным explicit marker; parent tombstone остаётся один.
|
||||
|
||||
## 7. UX, touch и accessibility
|
||||
|
||||
- UI и последовательность действий не меняются.
|
||||
- Desktop-first контракт Device editor сохраняется.
|
||||
- Touch-поведение редактора не расширяется и не ухудшается: новых pointer
|
||||
targets/gestures нет.
|
||||
- Тексты и accessibility tree не меняются.
|
||||
- View/kiosk получают только ожидаемое возвращение живого marker после save;
|
||||
редакторские элементы в них не появляются.
|
||||
|
||||
## 8. Данные, compatibility и миграция
|
||||
|
||||
- Используются существующие records `marker.removed` и live `entity:*` marker.
|
||||
- Новых полей и версии модели нет; read/write migration не нужна.
|
||||
- Комбинация `device:D removed:true` + live `entity:X` уже schema-valid и должна
|
||||
round-trip без нормализации, удаляющей один из records.
|
||||
- Старый frontend, открытый одновременно после записи нового сочетания, может
|
||||
временно продолжать скрывать X по старой runtime-семантике. Он не должен
|
||||
разрушительно удалить X или parent tombstone; после обновления/reload новый
|
||||
frontend показывает X.
|
||||
- Delete/re-add остаются optimistic config-транзакциями с действующими rev
|
||||
guards; задача не вводит отдельную запись или частичный commit.
|
||||
- Layout identity X определяется текущим `markerIdForBinding()`; parent D layout
|
||||
не переиспользуется.
|
||||
|
||||
## 9. i18n, security и performance
|
||||
|
||||
### i18n
|
||||
|
||||
Новых ключей и текстов нет. Документация EN/RU обновляется симметрично.
|
||||
|
||||
### Security
|
||||
|
||||
Исправление не меняет resolver действий и lock invariant. Разрешённая X получает
|
||||
ровно те же safe/secure ограничения, что обычный explicit entity-marker.
|
||||
|
||||
### Performance
|
||||
|
||||
- Не добавляется проход по HA registry на render/update.
|
||||
- Допустима линейная подготовка небольшого набора exact live entity bindings
|
||||
вместе с уже существующим проходом по `markers[]`.
|
||||
- Проверка одного eid должна оставаться O(1) по подготовленным Set.
|
||||
- Перестроение `_devices` и render snapshots не должно создавать новый объект
|
||||
состояния на каждый вызов availability сверх текущей модели.
|
||||
|
||||
## 10. Acceptance criteria и доказательства
|
||||
|
||||
### AC1. Дочерняя entity доступна в Add
|
||||
|
||||
После удаления `device:D` активная дочерняя `entity:X` отсутствует при
|
||||
выключенном **Показывать сущности** и присутствует при включённом. Сам `device:D`
|
||||
также остаётся доступным для re-add; размещённые bindings не дублируются.
|
||||
|
||||
**Доказательство:** браузерный `demo/smoke_binding_picker.mjs` вызывает настоящий
|
||||
picker собранного бандла. Проверка known defect перевёрнута в положительную и
|
||||
проверяет checkbox boundary.
|
||||
|
||||
### AC2. Сохранение возвращает только выбранную entity
|
||||
|
||||
После save X persisted config содержит live `entity:X` и прежний `device:D`
|
||||
tombstone; X видна на плане с новой обычной позицией, parent auto-marker не
|
||||
появляется. Повторный delete/re-add X идемпотентен.
|
||||
|
||||
**Доказательство:** browser smoke проходит delete D → Add X → Save → rebuild →
|
||||
Delete X → Add X → Save и проверяет config records, layout и DOM/device list.
|
||||
Targeted unit покрывает чистую семантику tombstone/live-binding.
|
||||
|
||||
### AC3. Исключение exact и не протекает на siblings
|
||||
|
||||
При `device:D` tombstone + live `entity:X`:
|
||||
|
||||
- X доступна plan/render availability и обычным state/action/aggregate
|
||||
потребителям;
|
||||
- sibling Y без live marker остаётся удалённой;
|
||||
- `device:D` source остаётся удалённым;
|
||||
- parent auto-marker отсутствует.
|
||||
|
||||
**Доказательство:** unit-матрица для `isRemovedPlanEntity`, `isRemovedPlanSource`,
|
||||
`buildDevices`, room aggregate и explicit controls/live source; browser smoke
|
||||
проверяет видимый X и отсутствие D/Y.
|
||||
|
||||
### AC4. Существующие lifecycle-контракты не меняются
|
||||
|
||||
- exact re-add `device:D` из #161 продолжает заменять device tombstone;
|
||||
- exact re-add `entity:X` продолжает заменять entity tombstone;
|
||||
- entity tombstone не вычитает X из живого auto-parent;
|
||||
- два живых explicit `device:D` + `entity:X` сосуществуют по #226;
|
||||
- exact opening contact/lock из #104 не зависит от marker tombstones.
|
||||
|
||||
**Доказательство:** существующие unit/smoke #104/#161/#226 плюс новые regression
|
||||
assertions в picker smoke.
|
||||
|
||||
### AC5. Данные и UX совместимы
|
||||
|
||||
Нет новых config/i18n полей, model bump или миграции. Checkbox, поиск, лимит 200,
|
||||
disabled/hidden registry-фильтры, touch contract и lock safety не меняются.
|
||||
|
||||
**Доказательство:** schema/config round-trip unit, diff review, typecheck и docs
|
||||
check; новых i18n keys нет.
|
||||
|
||||
### AC6. Гейты реализации зелёные
|
||||
|
||||
- `npm run typecheck`;
|
||||
- `npm test`;
|
||||
- `npm run build` и byte-identical три bundle-копии;
|
||||
- targeted browser smokes, выбранные `smoke-select`;
|
||||
- named mutation guards §11;
|
||||
- `node scripts/check-docs.mjs`.
|
||||
|
||||
Полные golden, smoke и performance остаются предрелизными гейтами. Golden не
|
||||
требуется для этого невизуального изменения, если diff не меняет визуальные
|
||||
baseline-сценарии.
|
||||
|
||||
## 11. План автотестов
|
||||
|
||||
### Unit
|
||||
|
||||
1. `removedPlanBindings`/availability matrix:
|
||||
- parent tombstone без live child → X/Y suppressed;
|
||||
- parent tombstone + live X → X allowed, Y suppressed;
|
||||
- exact X tombstone без live X → X suppressed;
|
||||
- legacy duplicate exact tombstone + live X → live exact marker wins;
|
||||
- `device:D` source remains suppressed in every case with parent tombstone.
|
||||
2. `buildDevices`:
|
||||
- only X is built from parent tombstone + live X;
|
||||
- D does not auto-build;
|
||||
- adding live explicit D later yields D + X as #226 requires.
|
||||
3. Room/source consumers include X but not Y.
|
||||
4. Delete/re-add record helper preserves unrelated tombstones and produces one
|
||||
exact live binding.
|
||||
|
||||
### Browser smoke
|
||||
|
||||
Расширить `demo/smoke_binding_picker.mjs` полноценным lifecycle вместо одной
|
||||
проверки текущего дефекта:
|
||||
|
||||
1. удалить реальное устройство стенда;
|
||||
2. открыть Add с checkbox off/on и проверить X;
|
||||
3. сохранить X через `_saveMarker()`;
|
||||
4. проверить records, `_devices`, DOM и свежую layout position;
|
||||
5. проверить, что D по-прежнему предлагается, а X уже не дублируется;
|
||||
6. удалить и повторно добавить X;
|
||||
7. вернуть D и подтвердить осознанное сосуществование D + X.
|
||||
|
||||
`scripts/smoke-links.mjs` должен выбирать smoke при изменении helpers tombstone
|
||||
и нового exact-live override. Если имя `_bindingCandidates` остаётся прямым
|
||||
совпадением, дублировать его зарегистрированной связью не требуется.
|
||||
|
||||
### Mutation guards
|
||||
|
||||
| id | Мутант | Обязанный поймать тест |
|
||||
|---|---|---|
|
||||
| `device-tombstone-blocks-child-picker` | вернуть общий запрет X в picker при parent tombstone | AC1 browser smoke |
|
||||
| `live-child-still-suppressed-by-parent-tombstone` | удалить exact live override из runtime | AC2/AC3 unit + smoke |
|
||||
| `child-readd-clears-parent-tombstone` | при save X удалить `device:D` tombstone | AC2 smoke/config assertion |
|
||||
| `parent-tombstone-restores-all-siblings` | разрешить все entity D вместо только живых explicit bindings | AC3 unit |
|
||||
|
||||
Перед добавлением мутанты запускаются и должны быть реально пойманы. Непроверенный
|
||||
mutation id в репозиторий не добавляется.
|
||||
|
||||
## 12. Затронутые файлы и артефакты
|
||||
|
||||
Ожидаемый продуктовый diff:
|
||||
|
||||
- `src/devices.ts` — представление tombstones и exact live override;
|
||||
- `src/houseplan-card.ts` — Add candidates и, если нужно, атомарная замена exact
|
||||
entity tombstone без parent tombstone;
|
||||
- `test/devices.test.mjs` и/или узкий policy unit;
|
||||
- `demo/smoke_binding_picker.mjs`;
|
||||
- `scripts/smoke-links.mjs`, `scripts/mutation-gate.mjs`;
|
||||
- `docs/FILTERING.md`, `docs/ARCHITECTURE.md`, `docs/TESTING.md`;
|
||||
- `docs/USER-GUIDE.md`, `docs/USER-GUIDE.ru.md`;
|
||||
- `docs/CHANGELOG.md`, `docs/CHANGELOG.ru.md`;
|
||||
- три синхронные bundle-копии после build.
|
||||
|
||||
Если реализация требует новых config fields, i18n keys или backend-файлов, это
|
||||
выход за утверждённый scope и повод вернуть ТЗ на уточнение до продолжения кода.
|
||||
|
||||
## 13. Release artifacts
|
||||
|
||||
- EN/RU changelog в том же `User-Visible: yes` implementation commit;
|
||||
- EN/RU user guide и канонические filtering/architecture/testing docs;
|
||||
- bundle parity;
|
||||
- handoff-комментарий с commit SHA, точными targeted gates и остаточными рисками;
|
||||
- независимый `CODE-REVIEW-262-rN.md` до автоматического merge в `dev`.
|
||||
|
||||
Публичные screenshots не меняются: новых визуальных состояний и UI нет.
|
||||
|
||||
## 14. Риски и меры снижения
|
||||
|
||||
### R1. Воскреснет весь parent device
|
||||
|
||||
Если save X удалит `device:D` tombstone, auto-discovery построит D из остаточных
|
||||
siblings. Защита: persisted-record и DOM assertions AC2/AC3.
|
||||
|
||||
### R2. Исключение протечёт на siblings и агрегаты
|
||||
|
||||
Если parent tombstone перестанет подавлять device целиком, удалённые Y снова
|
||||
начнут влиять на room climate/LQI/Glow или controls. Защита: exact X/Y unit
|
||||
матрица и mutation `parent-tombstone-restores-all-siblings`.
|
||||
|
||||
### R3. X появится в picker, но останется мёртвой после Save
|
||||
|
||||
Изолированная UI-правка сделает выбор возможным, однако `buildDevices` и
|
||||
availability снова отбросят marker. Защита: smoke обязан пройти до DOM/live
|
||||
state после `_saveMarker()`, а не завершаться на списке candidates.
|
||||
|
||||
### R4. Сломается #226
|
||||
|
||||
Нельзя считать entity tombstone живым ownership или запретить явное
|
||||
сосуществование D + X. Защита: существующая unit/smoke-матрица #226 входит в
|
||||
обязательную регрессию AC4.
|
||||
|
||||
### R5. Старый frontend увидит новое сочетание иначе
|
||||
|
||||
Старый runtime может временно скрыть X рядом с parent tombstone. Поскольку
|
||||
records schema-valid и не удаляются старой версией, это обратимая визуальная
|
||||
деградация до reload новой сборки, а не потеря данных.
|
||||
|
||||
## 15. Откат
|
||||
|
||||
Откат — один implementation commit #262 вместе с unit/smoke/mutation,
|
||||
документацией, changelog и bundle-копиями.
|
||||
|
||||
Новых persisted fields нет, поэтому data rollback не требуется. После отката
|
||||
конфиг `device:D removed:true` + live `entity:X` остаётся валидным и сохранным,
|
||||
но старая версия снова временно не покажет X, пока пользователь не вернёт D
|
||||
целиком или не установит исправленную сборку.
|
||||
|
||||
## 16. Принятые предположения — поменять свободно на ревью ТЗ
|
||||
|
||||
1. Parent `device:D` tombstone сохраняется после re-add X. Это следует из
|
||||
пользовательского действия «вернуть одну entity», а также предотвращает
|
||||
автоматическое появление нежелательного остаточного parent marker.
|
||||
2. Live exact `entity:X` сильнее exact/parent tombstone только для X. Техническая
|
||||
форма (дополнительный Set в prepared policy, отдельный helper или иной O(1)
|
||||
resolver) не является продуктовым решением.
|
||||
3. Registry-hidden/disabled entity не получает нового специального whitelist:
|
||||
issue исправляет только plan tombstone, а не HA availability.
|
||||
4. Fresh position X определяется существующим Add-контрактом; отдельной миграции
|
||||
координат D → X нет.
|
||||
5. Полноценное добавление D после X оставляет оба explicit marker по уже
|
||||
утверждённому #226; скрытое автоматическое объединение было бы новым UX.
|
||||
|
||||
@@ -131,6 +131,7 @@ GitHub Issues и GitHub Projects (v2) остаются единственным
|
||||
| [#252](https://github.com/Matysh/houseplan-card/issues/252) Понятная и безопасная очистка забытых позиций в Optimize | [252-optimize-orphan-layout-report.md](252-optimize-orphan-layout-report.md) |
|
||||
| [#253](https://github.com/Matysh/houseplan-card/issues/253) Resize не теряет интервалы толщины стен | [253-resize-wall-thickness.md](253-resize-wall-thickness.md) |
|
||||
| [#258](https://github.com/Matysh/houseplan-card/issues/258) Канонический wall key после Optimize и storage round-trip | [258-wall-key-storage-roundtrip.md](258-wall-key-storage-roundtrip.md) |
|
||||
| [#262](https://github.com/Matysh/houseplan-card/issues/262) Повторное добавление entity после удаления родительского устройства | [262-readd-child-entity-after-device-delete.md](262-readd-child-entity-after-device-delete.md) |
|
||||
|
||||
## P3
|
||||
|
||||
|
||||
Reference in New Issue
Block a user