fix: roll back rejected marker writes atomically

Issue: #442
User-Visible: yes
This commit is contained in:
Sergey Matyunin
2026-09-04 00:15:44 +03:00
parent 77ac72d15d
commit fcd4e3ddfe
36 changed files with 1138 additions and 462 deletions
+5
View File
@@ -2,6 +2,11 @@
## Unreleased
- Rejected device-marker and robot-calibration saves now restore the last
server-confirmed plan without losing the open editor, automatic proposal or
manual-fit draft; success is shown only after Home Assistant accepts the
write, and Retry no longer requires recalibration
([#442](https://github.com/Matysh/houseplan-card/issues/442)).
- Removing a robot vacuum's last map route now keeps the route list explicitly
empty instead of reviving legacy calibration; routes to deleted spaces are
grouped clearly in the editor, and the live layer avoids rescanning every
+5
View File
@@ -8,6 +8,11 @@
## Не выпущено
- Если Home Assistant отклоняет сохранение устройства или калибровки робота,
план теперь возвращается к последнему подтверждённому состоянию, но сохраняет
открытый редактор, автоматическое предложение или ручную подгонку для повтора;
сообщение об успехе появляется только после принятой записи
([#442](https://github.com/Matysh/houseplan-card/issues/442)).
- После удаления последнего маршрута карты робота список теперь остаётся явно
пустым и не возвращает старую калибровку; маршруты в удалённые пространства
собраны в понятную группу, а живой слой больше не перебирает повторно все
+16
View File
@@ -556,6 +556,22 @@ full/space transfer preserve, remap or report/drop `derived_marker_state.ref`
through the same reference seam as controls and value badges. Older clients
ignore the field and may erase it if they reconstruct the marker.
## Atomic marker writes (#442)
The Device editor builds a separate complete config candidate and treats a
successful `houseplan/config/set` response as the durable boundary. If semantic
validation, transport or schema validation rejects that request, the card
restores the preceding server-confirmed config and content fingerprint, rebuilds
the visible marker set, and keeps the independent dialog draft available for
Retry. A revision loaded after a conflict or newer local content always wins;
an older rejection cannot overwrite either one.
Layout placement, obsolete-layout cleanup and copied-file cleanup happen only
after config acceptance. Their failure can report an error and keep the dialog
open, but cannot roll back a marker already accepted by the server. This is a
frontend transaction contract only: marker schema, validators, revision wire
format and downgrade behaviour are unchanged.
## Persistent manual virtual-light state
The exact `virtual` + `is_light:true` + `tap_action:toggle` combination has a
+9
View File
@@ -126,6 +126,15 @@ At `> 40 cm` nothing is saved until the user explicitly chooses **Apply**.
**Fit manually** opens the proposal in the fit overlay; **Cancel** leaves the
saved configuration byte-for-byte unchanged.
Calibration Save is complete only after Home Assistant accepts the config
write. While it is pending, automatic-calibration controls, a high-residual
proposal or the manual-fit overlay stay busy and cannot submit, close or move a
second draft. On rejection the last accepted matrix is restored immediately:
the Device editor remains open, a high-residual proposal keeps its exact matrix,
and manual fit keeps its exact transform for Retry. A rejected first-use
calibration also removes the unaccepted synthetic marker. Success closes the
relevant proposal/overlay and appears only after server acceptance.
Map ID uses one nullish chain and deliberately ignores volatile values such as
`vacuum_json_id`: