docs: name the expected_rev requirement for external writers (#368)

#340/#356 made expected_rev mandatory for config/set and layout/set over a
non-empty store — an honest protection the release notes sold only as a
stale-tab guard. A third-party script writing plans directly cannot infer
from "protects from stale tabs" that it must now read the revision first.

Both changelogs gain an explicit breaking-for-external-writers entry with
the read-then-write recipe; ARCHITECTURE.md's WS contract section extends
the #340 paragraph with the cycle external clients must follow (get rev →
send expected_rev → on conflict re-read and retry); and both conflict
messages now carry the actionable hint for scripts — "include expected_rev
from houseplan/config/get / layout/get" — alongside the tab-oriented
"reload" advice. The backend tests pin only the "revision is required"
substring and stay untouched.

Issue: #368
User-Visible: yes
This commit is contained in:
Codex
2026-08-29 12:29:33 +03:00
parent 5a285a6a4b
commit 774388c2e5
4 changed files with 32 additions and 5 deletions
+6 -4
View File
@@ -594,8 +594,9 @@ async def ws_layout_set(hass: HomeAssistant, connection, msg: dict[str, Any]) ->
)
connection.send_error(
msg["id"], "conflict",
f"Layout revision is required; reload the layout "
f"(current rev {current_rev})",
f"Layout revision is required; reload the layout, or — for "
f"external clients — include expected_rev from "
f"houseplan/layout/get (current rev {current_rev})",
)
return
if "expected_rev" in msg and msg["expected_rev"] != current_rev:
@@ -1322,8 +1323,9 @@ async def ws_config_set(hass: HomeAssistant, connection, msg: dict[str, Any]) ->
)
connection.send_error(
msg["id"], "conflict",
f"Configuration revision is required; reload the configuration "
f"(current rev {current_rev})",
f"Configuration revision is required; reload the configuration, "
f"or — for external clients — include expected_rev from "
f"houseplan/config/get (current rev {current_rev})",
)
return
if "expected_rev" in msg and msg["expected_rev"] != current_rev:
+6 -1
View File
@@ -891,7 +891,12 @@ The wire schema permits omission only for the first empty-store bootstrap at
revision zero, so the endpoint can return the stable `conflict` domain error
instead of a generic format error. A revision-less write over `rev > 0` is
rejected under the same `write_lock` before validation, no-op detection,
backup cleanup, file collection or update events (#340).
backup cleanup, file collection or update events (#340). The same rule holds
for `layout/set` (#356). External writers (scripts, automations, custom
integrations) must therefore follow the read-then-write cycle the card uses:
call `houseplan/config/get` (or `layout/get`), keep the returned `rev`, and
send it back as `expected_rev`; a `conflict` answer means the document moved —
re-read and retry with the fresh revision (#368).
The normal frontend reaches `houseplan/plan/optimize` only after the exact
preview candidate passes `src/plan-geometry-preflight.ts`. That pure barrier
+10
View File
@@ -2,6 +2,16 @@
## Unreleased
- Breaking for external writers (scripts, automations, custom integrations
that write plans directly): `houseplan/config/set` and `houseplan/layout/set`
over a non-empty store now require `expected_rev` and answer `conflict`
without it. Read the current `rev` from `houseplan/config/get` /
`houseplan/layout/get` first; the card itself has sent the revision since
v1.60 and is unaffected
([#340](https://github.com/Matysh/houseplan-card/issues/340),
[#356](https://github.com/Matysh/houseplan-card/issues/356),
[#368](https://github.com/Matysh/houseplan-card/issues/368)).
- A gate or door bound to a position-reporting cover no longer stutters the
whole card while it moves: the light cut through the opening now steps on a
5% grid, cutting the heavy geometry recomputes from about a hundred per
+10
View File
@@ -8,6 +8,16 @@
## Не выпущено
- Ломающее для внешних клиентов (скрипты, автоматизации, кастомные
интеграции, пишущие планы напрямую): `houseplan/config/set` и
`houseplan/layout/set` поверх непустого хранилища теперь требуют
`expected_rev` и без него отвечают `conflict`. Сначала прочитайте текущий
`rev` через `houseplan/config/get` / `houseplan/layout/get`; сама карточка
шлёт ревизию с v1.60 и не затронута
([#340](https://github.com/Matysh/houseplan-card/issues/340),
[#356](https://github.com/Matysh/houseplan-card/issues/356),
[#368](https://github.com/Matysh/houseplan-card/issues/368)).
- Ворота или дверь на cover-сущности с позицией больше не дёргают карточку
во время движения: световой вырез проёма ступает по сетке 5%, и тяжёлых
пересчётов геометрии вместо ~сотни за цикл — не больше двадцати