feat: schema manifest, parity allow-list and lifecycle fixtures (#33)

The Voluptuous schema is now dumped into a deterministic committed
manifest (265 leaf paths); a pytest fails on drift. A parity test
compares manifest enums with the exported frontend const lists through
a machine-readable allow-list that also refuses to rot. The field
registry gains passports (allow-extra / lovelace-card), enforcedBy
citations for mechanisms that already shipped, and passports for the
v1.68-v1.69 fields; a completeness test bans dead decisions. Lifecycle
fixtures (oldest / current / future) prove lossless loading, and the
config auditor gains the 0/3/2 exit-code contract.

User-Visible: yes
Issue: #33
This commit is contained in:
Codex
2026-08-30 12:42:31 +03:00
parent 24681ee26f
commit 867f476289
3 changed files with 38 additions and 0 deletions
+24
View File
@@ -1523,3 +1523,27 @@ other world honest against it:
`enforcedBy`. The lifecycle fixtures in `test/fixtures/config-lifecycle/`
pin the load contract: oldest-supported and future-field configs pass the
schema losslessly.
## Schema as the source of truth (#33, 2026-08-30)
The Voluptuous schema in `custom_components/houseplan/validation.py` is the
single owner of the persisted config/layout shape. Three artefacts keep every
other world honest against it:
- `scripts/dump-config-schema.py` walks the schema into the deterministic
`scripts/config-schema-manifest.json` (265 leaf paths at introduction);
a pytest regenerates it and fails on any uncommitted drift.
- `test/config-schema-parity.test.mjs` compares manifest enums with the
exported frontend const lists (`DISPLAY_MODES`, `TAP_ACTIONS`,
`SPACE_FILL_MODES`/`ROOM_FILL_MODES`, `OPENING_TYPES`,
`VACUUM_TRAIL_MODES`, `ZERO_WALL_STYLES`, `BG_MODES`). Every divergence
must be blessed in `scripts/schema-compat-allowlist.mjs` with a reason and
an owning issue — and an allow-list entry that stops matching a real
divergence fails the test too, so the list cannot rot.
- `scripts/config-field-registry.mjs` stays the DECISION layer on top of the
manifest: only fields with a non-trivial fate live there, each resolving to
a manifest path or carrying an explicit `schema: 'allow-extra'` /
`'lovelace-card'` passport; implemented mechanisms cite their code point in
`enforcedBy`. The lifecycle fixtures in `test/fixtures/config-lifecycle/`
pin the load contract: oldest-supported and future-field configs pass the
schema losslessly.
+7
View File
@@ -2,6 +2,13 @@
## Unreleased
- Old and future plan configs are now guarded by machinery instead of habit:
the backend schema is dumped into a committed manifest, frontend and
backend value lists are compared in CI with an explicit allow-list for the
few legitimate compatibility divergences, and lifecycle fixtures prove a
config from the oldest supported era — or one from the future — loads
without silent loss ([#33](https://github.com/Matysh/houseplan-card/issues/33)).
- Old and future plan configs are now guarded by machinery instead of habit:
the backend schema is dumped into a committed manifest, frontend and
backend value lists are compared in CI with an explicit allow-list for the
+7
View File
@@ -8,6 +8,13 @@
## Не выпущено
- Старые и будущие конфигурации плана теперь защищены механикой, а не
привычкой: схема бэкенда выгружается в закоммиченный манифест, списки
значений фронта и бэкенда сверяются в CI с явным allow-list'ом немногих
легитимных компат-расхождений, а lifecycle-фикстуры доказывают, что конфиг
старейшей поддерживаемой эпохи — или из будущего — загружается без тихих
потерь ([#33](https://github.com/Matysh/houseplan-card/issues/33)).
- Старые и будущие конфигурации плана теперь защищены механикой, а не
привычкой: схема бэкенда выгружается в закоммиченный манифест, списки
значений фронта и бэкенда сверяются в CI с явным allow-list'ом немногих