mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 04:38:55 +00:00
fix: keep Optimize idempotent across storage reload
Issue: #248 User-Visible: yes
This commit is contained in:
+14
-2
@@ -507,6 +507,16 @@ either store changes, commits both revisions, and retains one snapshot.
|
||||
has changed since the optimization. A crash between store writes is
|
||||
completed from the intent on the next integration setup.
|
||||
|
||||
The pair returned by `optimizePlans` is canonicalized through the same
|
||||
nine-decimal allowlist as the storage writers **before** `changed` is computed.
|
||||
This boundary is required because the normalized grid step `1 / 240` has no
|
||||
finite decimal representation: a raw binary grid node and its persisted JSON
|
||||
number are semantically identical but not `===`. Update-event reload and a cold
|
||||
read therefore receive exactly the pair the preview already retained, and a
|
||||
second run cannot manufacture fresh coordinate noise (#248). Counters describe
|
||||
only the final persisted delta; an intermediate double which canonicalizes
|
||||
back to the input is not reported as work.
|
||||
|
||||
Guarantees are covered by `test/align-grid.test.mjs` and the orchestration/
|
||||
idempotence case in `test/plan-optimizer.test.mjs`:
|
||||
|
||||
@@ -520,8 +530,10 @@ idempotence case in `test/plan-optimizer.test.mjs`:
|
||||
it unfixable);
|
||||
* a stray opening with no wall within 6 steps is left exactly where it
|
||||
is rather than teleported;
|
||||
* **idempotent**: a second run reports `moved: 0`, `changed: false`, and
|
||||
`coordsCanonicalized: 0`, and returns objects deep-equal to the first run's;
|
||||
* **idempotent across storage**: a second run in memory, after the nine-decimal
|
||||
writer round-trip, after update-event reload or after a cold read reports
|
||||
`moved: 0`, `changed: false`, and `coordsCanonicalized: 0`, and returns
|
||||
objects deep-equal to the first persisted result;
|
||||
* the report is an **upper bound**, not a sample (AUD-158B1-01).
|
||||
|
||||
Before a changed preview can expose Apply, `checkOptimizeGeometry(config)`
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Repeating “Optimize plans” immediately after a successful run now correctly
|
||||
reports that there is nothing to fix, including after the backend update
|
||||
event or a page reload. Optimize compares and retains the same nine-decimal
|
||||
geometry representation that the integration persists, so the binary
|
||||
`1/240` grid step no longer returns as freshly removed coordinate noise
|
||||
([#248](https://github.com/Matysh/houseplan-card/issues/248)).
|
||||
|
||||
- “Optimize plans” now restores device references left behind by deleted or
|
||||
independently imported spaces. Exact import copies keep their positions;
|
||||
other active devices safely lose only the missing placement and return via
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- Повторный запуск «Оптимизировать планы» сразу после успешной оптимизации
|
||||
теперь честно сообщает, что исправлять нечего, в том числе после события
|
||||
обновления backend или перезагрузки страницы. Optimize сравнивает и оставляет
|
||||
то же девятизнаковое представление геометрии, которое сохраняет интеграция,
|
||||
поэтому двоичный шаг сетки `1/240` больше не возвращается как заново
|
||||
устранённый шум координат
|
||||
([#248](https://github.com/Matysh/houseplan-card/issues/248)).
|
||||
|
||||
- «Оптимизировать планы» теперь восстанавливает ссылки устройств, оставшиеся
|
||||
после удаления или независимого импорта пространства. Для точной импортной
|
||||
копии сохраняются позиции; в остальных случаях активное устройство теряет
|
||||
|
||||
@@ -69,6 +69,12 @@ unknown fields in canonical representation, not the invisible noisy IEEE-754
|
||||
tail. A repeated canonical Save with the current revision is a no-op and does
|
||||
not invalidate the one-deep maintenance backup.
|
||||
|
||||
Optimize itself returns and compares that storage-canonical config/layout pair,
|
||||
not the higher-precision intermediate produced by the `1 / 240` grid formula.
|
||||
Consequently the normal commit, durable pending recovery, update-event reload
|
||||
and a cold read all converge on one JSON value set; feeding any of them back to
|
||||
Optimize is a no-op (#248).
|
||||
|
||||
## Open-passage opening type (#157)
|
||||
|
||||
`space.openings[].type` additionally accepts the literal `passage`. Its
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ metadata). Only an explicit owner-approved emergency hotfix may skip this gate.
|
||||
| Item | State |
|
||||
|---|---|
|
||||
| Version | **v1.67.0-beta.2 candidate** everywhere (manifest, const.py, package.json, CARD_VERSION) — second prerelease of the v1.67 line |
|
||||
| Current local cycle | v1.67.0-beta.2 packages three reviewed changes after beta.1: decor is composited above room fills (#231), opening symbols are centred across wall depth (#242), and real mouse dragging reorders space tabs with an exact insertion indicator (#243). Publication remains gated by the full exact-SHA Validate. |
|
||||
| Current local cycle | v1.67.0-beta.2 is published. Current `dev` restores orphaned device references during Optimize (#244); the #248 issue branch additionally makes Optimize idempotent across its nine-decimal backend write and reload boundary. The next prerelease remains gated by review and the full exact-SHA Validate. |
|
||||
| Hidden Labs Stage | #89 Stage 1 ships in v1.63.0-beta.1. #122 Stage 2 ships in v1.64.0 and evolves the same hidden, expiring `iso` experiment with matte walls, a low exterior floor edge, restrained shared shadows and live vertical door/window/gate panels. Flat remains default; editors and `houseplan-space-card` remain flat; live floor effects and HA actions remain unchanged. Public activation remains a separate task. |
|
||||
| Workflow | Superseded 2026-08-12: the pre-1.62 rule of "local edits without tests or commits" is **dead** — since release 1.62 every product change follows `PROCESS.md` (issue in `S5-ready`+, branch `issue/<NN>-slug`, trailers on every commit, review pipeline; `AGENTS.md` is the summary). Release mechanics below remain current. A requested pre-release gets a production build plus the smallest targeted unit/smoke set covering the changed surfaces, one tested `dev` commit/tag and a GitHub Release with `prerelease=true`; `main` stays untouched. The complete local frontend/backend/smoke gate runs only before a stable release, after which `main` is fast-forwarded to the exact tested `dev` SHA and the GitHub Release uses `prerelease=false`. Release bodies are short and bilingual (Russian first): only significant user changes get individual bullets, while minor/code-only work is grouped as `Мелкие исправления и улучшения` / `Small fixes and improvements`; every body ends with separate links to the Russian and English changelogs. Detailed RU/EN changelog bullets may link the corresponding closed GitHub Issues; open or partially delivered issues are never presented as shipped. Telegram announcements are sent only for stable releases; beta and RC publication is silent. `docs/RELEASE-NOTES.md` is the current canonical body instance; `npm run release:prerelease -- <tag> --issues=… --yes` is the primary local publication path and the manual `Publish prerelease` workflow is its GitHub-only equivalent once present on `main`. Nothing is copied to the home instance by hand |
|
||||
| GitHub | https://github.com/Matysh/houseplan-card — [Issues](https://github.com/Matysh/houseplan-card/issues) are the canonical task records; their labels carry priority and workflow status (`PROCESS.md` §9). GitHub Projects is no longer used. `main` carries stable releases; pre-release tags may point directly at `dev`. Work lands on `dev` and is merged into `main` for a stable release, so `dev` is normally equal to or ahead of `main`, never behind. Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
|
||||
|
||||
@@ -2246,6 +2246,18 @@ require hands on real hardware — they remain for the human pass.
|
||||
test_ha_import_export; mutations: `schema-quantization-removed`,
|
||||
`frontend-writes-raw-coords`, `quantization-hits-allowlist`,
|
||||
`import-path-bypasses-schema`; pre-release: golden verify].
|
||||
- [ ] **Optimize remains idempotent across storage and reload (#248)**: its
|
||||
final config/layout pair equals the shared nine-decimal writer target;
|
||||
a second run in memory, after schema/storage round-trip, after update
|
||||
events and after a cold reload returns `changed:false`, zero change
|
||||
counters and a deep-equal pair. The shared two-scale fixture is consumed
|
||||
independently by Node and Python; the Optimize handler records the exact
|
||||
pair in pending and both final stores, and startup recovery converges on
|
||||
it [unit: plan-optimizer + coordinate-canonicalization; backend:
|
||||
test_coordinate_canonicalization + test_ha_websocket +
|
||||
test_ha_import_export; auto: smoke_optimize_coordinate_canonicalization;
|
||||
mutations: `optimize-storage-boundary-removed`,
|
||||
`optimize-config-storage-half-raw`, `optimize-layout-storage-half-raw`].
|
||||
- [ ] Optimizer migration safety: legacy decor width/text size is clamped to
|
||||
the backend schema, `fill: true` receives explicit fill style, invalid
|
||||
legacy `plan_scale` is preserved for repair, an already canonical plan is
|
||||
|
||||
@@ -1377,7 +1377,8 @@ show_signal: true
|
||||
строка различает пространства с обновлённым представлением стен/связей и
|
||||
координаты, в которых устранён только вычислительный шум. Поэтому возможен
|
||||
честный предпросмотр «сдвинуто элементов — 0» с ненулевым числом очищенных
|
||||
координат. Повторный Optimize над результатом ничего не предлагает.
|
||||
координат. Повторный Optimize над результатом ничего не предлагает — ни сразу,
|
||||
ни после серверного события обновления или перезагрузки страницы.
|
||||
Неразрешённые позиции и вложенные сопоставления робота не удаляются: диалог
|
||||
показывает их отдельным предупреждением даже тогда, когда применять больше
|
||||
нечего.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
- Оценка: пользовательская ценность 6/10 · ценность для разработки 8/10 · сложность 4/10 · риск 4/10
|
||||
- Issue: [#248](https://github.com/Matysh/houseplan-card/issues/248)
|
||||
- Ветка: `issue/248-optimize-idempotence`
|
||||
- Статус ТЗ: первая редакция, на ревью
|
||||
- Статус ТЗ: утверждено (SPEC-REVIEW-248-r1 green), реализовано в ветке задачи
|
||||
|
||||
Канонические документы: `docs/SCOPE.md`, `docs/CANVAS.md`,
|
||||
`docs/CONFIG-COMPATIBILITY.md`, `docs/TOUCH-SUPPORT.md`,
|
||||
@@ -250,8 +250,8 @@ Implementation-коммит получает `User-Visible: yes` и включа
|
||||
- `docs/CHANGELOG.md` и `docs/CHANGELOG.ru.md` со ссылкой на #248;
|
||||
- `docs/CANVAS.md` — идемпотентность через storage/reload boundary;
|
||||
- `docs/CONFIG-COMPATIBILITY.md` — композиция Optimize с девятизнаковым writer;
|
||||
- `docs/USER-GUIDE.ru.md` — второй запуск после reload сообщает, что чинить
|
||||
нечего;
|
||||
- `docs/USER-GUIDE.ru.md` — существующая фраза об идемпотентности точечно
|
||||
уточнена границей reload (Low из SPEC-REVIEW-248-r1);
|
||||
- `docs/TESTING.md` — shared fixture, backend pair/recovery и mutation coverage;
|
||||
- `docs/STATUS.md` — только если текущая unreleased-сводка перечисляет
|
||||
исправления этого уровня;
|
||||
|
||||
Reference in New Issue
Block a user