Fix atomic model-v8 draft writes

Issue: #314
User-Visible: yes
This commit is contained in:
Matysh
2026-08-26 09:13:50 +03:00
parent 5da7812099
commit 2347e8df88
16 changed files with 657 additions and 198 deletions
+13
View File
@@ -411,6 +411,13 @@ resulting wall or partition. Ambiguity fails closed with no partial config,
history or revision update. `scripts/mutation-gate.mjs` guards every structural
writer entrance.
Draft sanitation and Undo preserve the complete record of every surviving
segment, including its stable v8 ID; only a genuinely new edge receives a new
identity. The backend stale-client guard compares only room/compatibility
contour geometry with `wall_segments[]`. Drafts, partitions, columns and
explicitly hosted openings own their identity and may be written without a
contour-catalog change, subject to the full schema (#314).
Room-boundary walls remain *derived* from room outlines (`roomEdges`, deduped by
`segKey`), so deleting a room keeps the boundaries its neighbours still
contribute. Three explicitly typed exceptions are stored per space:
@@ -979,6 +986,12 @@ single promise: one `config/set` in flight, each carrying the revision the
previous one returned. The debounce still spaces out *when* a write starts;
what it cannot do — and used to be relied on for — is keep two writes from
overlapping, which produced a self-inflicted conflict and lost the newer edit.
Physical edits additionally form a pending transaction per space. A successful
write clears only the exact accepted fingerprint, so a newer queued edit stays
pending. A rejected write synchronously restores the earliest server-backed
snapshot for every affected space, clears its gestures and geometry history,
then best-effort reloads authoritative config. Thus a newer edit made while the
rejected request was in flight cannot survive on an unaccepted base (#314).
**Persisted coordinates have one lattice-aware write boundary** (#291).
`canonicalizeConfigGeometry()` / `canonicalizeLayoutGeometry()` /
+5
View File
@@ -11,6 +11,11 @@
independent wall winning an exact overlap. Zero stays refused for
standalone walls — a wall without thickness does not exist
([#313](https://github.com/Matysh/houseplan-card/issues/313)).
- Drawing rooms on model-v8 plans is reliable again: unfinished wall chains
keep their segment identity through intermediate saves and Undo, while a
rejected save immediately restores the last server-accepted geometry instead
of leaving a temporary room or stray partition on the plan
([#314](https://github.com/Matysh/houseplan-card/issues/314)).
## v1.68.0-beta.1 — 2026-08-26
+5
View File
@@ -16,6 +16,11 @@
комнат; при точном наложении клик достаётся отдельно стоящей стене. Ноль
для неё по-прежнему отклоняется — стены без толщины не существует
([#313](https://github.com/Matysh/houseplan-card/issues/313)).
- Рисование комнат на планах model v8 снова работает надёжно: незавершённая
цепочка стен сохраняет ID сегментов при промежуточных сохранениях и отмене, а
отклонённая сервером запись сразу возвращает последнюю принятую геометрию,
не оставляя временную комнату или случайную перегородку
([#314](https://github.com/Matysh/houseplan-card/issues/314)).
## v1.68.0-beta.1 — 2026-08-26
+9
View File
@@ -69,6 +69,15 @@ rejects the write instead of allowing thickness, draft identity or an opening
host to detach. A migration or transition conflict is fail-closed: the previous
config and revision remain unchanged.
The stale-client comparison is deliberately limited to contour-coupled legacy
geometry: rooms, compatibility `walls` and `open_spans`. Self-identifying
`room_drafts`, `partitions`, `wall_columns` and explicitly hosted `openings` may
change without rewriting the room-wall catalog, but still pass the complete v8
schema. Write-time sanitation and Undo preserve every surviving draft-segment
ID. If a physical `config/set` is rejected, the frontend restores the earliest
snapshot in that pending batch before attempting a best-effort authoritative
reload; rejected draft geometry cannot be promoted by a later gesture (#314).
## Canonical geometry on write (#224, #291)
Config and layout schemas canonicalize only named persisted numbers. Lattice
+20
View File
@@ -57,6 +57,26 @@
`tests_backend/test_validation.py`. HA import/export coverage runs in the
normal Linux/CI Home Assistant harness when unavailable natively.
## Atomic model-v8 draft writes (#314)
- [ ] `demo/smoke_v8_draft_write.mjs` proves that write sanitation preserves
the carrier ID after duplicate adjacent points, Undo keeps all surviving
IDs, and two successful queued physical writes retain the newer edge.
- [ ] The same fake-WS smoke rejects the first in-flight physical write and
observes synchronous rollback of its whole pending batch: active path,
pending map and command history are empty, and finishing the tool cannot
create a ghost partition.
- [ ] `tests_backend/test_wall_segment_model.py` accepts valid independent v8
draft/partition/column/hosted-opening changes with an unchanged contour
catalog, while the existing current/downgraded stale-contour negatives
and complete `CONFIG_SCHEMA` checks remain fail-closed.
- [ ] Local commands: `npm run typecheck`, `npm run bundle:sync`,
`node demo/smoke_v8_draft_write.mjs`, targeted backend pytest and
`node scripts/check-docs.mjs --external`.
- [ ] Mutations `v8-draft-sanitation-shifts-segment-identity` and
`v8-rejected-physical-write-keeps-optimistic-draft` prove that the pure
ID fixture and browser rollback scenario fail on the original defects.
## Resize: реальный pointer pipeline (#293)
- [ ] `demo/smoke_resize_pointer_real_plan.mjs` загружает tracked fixture
+12 -12
View File
@@ -2,7 +2,7 @@
"version": 1,
"fixture": "synthetic-only",
"chromium": "151.0.7922.34",
"sourceFingerprint": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceFingerprint": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"captureScriptSha256": "ce2e9542fed9dade3085be87d16f69adb2ac8262893ad78ad966b1b9673f2983",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -14,7 +14,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "36223106c073f07d8cc3ecf8eaab37192ebb2687daba65c5c21047d0b7890de0"
},
"view-touch": {
@@ -25,7 +25,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "41e3ba67f8db0e98f26f484293af83ef937c369ca5ca6a59a3350d8954c906f4"
},
"space-create": {
@@ -36,7 +36,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "c33a7279165a4cec6fa6fadb6fd08cd967e082a17fe101ef442d27d36ae59b6b"
},
"room-contour-close": {
@@ -47,7 +47,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "b4777162eae89e0d95801721330bcd74ba761624b82b3362069d7e2d38317e08"
},
"plan-context-tray": {
@@ -58,7 +58,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "c0e28edf82f45ccc6df568d3023b681e9e4394262ad34c1605fe9eacdb57a390"
},
"device-editor": {
@@ -69,8 +69,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"imageSha256": "d0ffd31ce80bfde21ab75da356a5fc1af38246f2b301030880320620c228d89d"
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "827a5f0cd6f1c122a63a51d83569a2dbd87b70b3753266a3eb0a9f2986b1b911"
},
"device-display-preview": {
"file": "06-device-display-preview.png",
@@ -80,7 +80,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "2cdabae1f89c3286e4fac0ce30f757ee1690b707ab8a5488748b7cd420626160"
},
"background-editor": {
@@ -91,7 +91,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "30147bb00a90eea7136b4cee30995f6e6a9217b5132f3e8d3ad7471413b1af8a"
},
"room-card": {
@@ -102,7 +102,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea"
},
"device-info": {
@@ -113,7 +113,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "c512f2c8165733033d1fe90cd894611d362da4d8d65580ef4a56bae93b07e444",
"sourceSha256": "cd14953b3d076cfa936f2d3d9f614bc245e14f13374594bf93868cc86bb14a95",
"imageSha256": "dd492f53150b7149085daada5cce9eeae9bde9e7ea1d86679a54b3041f72f517"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
# Issue #314 — атомарная запись v8 drafts и независимой геометрии
Статус документа: ТЗ на ревью.
Статус документа: согласовано, реализация начата.
Issue: [#314](https://github.com/Matysh/houseplan-card/issues/314)
Предшественник: [#282](https://github.com/Matysh/houseplan-card/issues/282)
Целевая ветка: `issue/314-v8-draft-write-regression`