mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
fix: close CODE-REVIEW-33-r1 M1-M3
M1: the AC7 no-import test scans the whole src tree for the CURRENT dump name (the old assertion checked the pre-rename string; proven by execution — a planted fetch now turns it red). M2: deduplicate the #33 paragraphs in both changelogs and ARCHITECTURE. M3: the auditor's exit-3 statuses match the spec contract exactly (migrate-*/deprecated-read); decision-required is live behaviour awaiting #44, drop-on-validation is the backend's own job. User-Visible: no Issue: #33
This commit is contained in:
@@ -1523,27 +1523,3 @@ 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.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.
|
||||
|
||||
@@ -2,13 +2,6 @@
|
||||
|
||||
## 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
|
||||
|
||||
@@ -8,13 +8,6 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- Старые и будущие конфигурации плана теперь защищены механикой, а не
|
||||
привычкой: схема бэкенда выгружается в закоммиченный манифест, списки
|
||||
значений фронта и бэкенда сверяются в CI с явным allow-list'ом немногих
|
||||
легитимных компат-расхождений, а lifecycle-фикстуры доказывают, что конфиг
|
||||
старейшей поддерживаемой эпохи — или из будущего — загружается без тихих
|
||||
потерь ([#33](https://github.com/Matysh/houseplan-card/issues/33)).
|
||||
|
||||
- Старые и будущие конфигурации плана теперь защищены механикой, а не
|
||||
привычкой: схема бэкенда выгружается в закоммиченный манифест, списки
|
||||
значений фронта и бэкенда сверяются в CI с явным allow-list'ом немногих
|
||||
|
||||
@@ -80,9 +80,12 @@ if (isMain) {
|
||||
// #33: exit-code contract — 0 = clean, 3 = migration available
|
||||
// (any finding whose status names a pending mechanism), 2 = invalid
|
||||
// input (unchanged). 3 is chosen to avoid colliding with 2.
|
||||
// Exactly the statuses that NAME a pending migration (#33 spec rev3):
|
||||
// decision-required fields (group_lights, exclude_integrations) are
|
||||
// live supported behaviour awaiting a #44 decision, not legacy, and
|
||||
// drop-on-validation is handled by the backend itself.
|
||||
const MIGRATION_STATUSES = new Set([
|
||||
'migrate-on-write', 'migrate-on-settings-save', 'deprecated-read',
|
||||
'drop-on-validation', 'decision-required',
|
||||
]);
|
||||
const migrationFindings = reports.some((report) => report.findings
|
||||
.some((finding) => MIGRATION_STATUSES.has(finding.status)));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { readFileSync, readdirSync } from 'node:fs';
|
||||
import { SCHEMA_COMPAT_ALLOWLIST } from '../scripts/schema-compat-allowlist.mjs';
|
||||
import { CONFIG_FIELD_REGISTRY } from '../scripts/config-field-registry.mjs';
|
||||
import {
|
||||
@@ -90,10 +90,17 @@ test('#33 AC4: every registry entry resolves to a manifest path or an explicit p
|
||||
}
|
||||
});
|
||||
|
||||
/** #33 AC7: the manifest is a build/test artefact — the bundle must not grow. */
|
||||
test('#33 AC7: no production source imports the schema manifest', () => {
|
||||
const source = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8')
|
||||
+ readFileSync(new URL('../src/houseplan-editor-runtime.ts', import.meta.url), 'utf8');
|
||||
assert.ok(!source.includes('config-schema-manifest'),
|
||||
'the manifest is test infrastructure, not runtime data');
|
||||
/** #33 AC7: the manifest is a build/test artefact — the bundle must not grow.
|
||||
* r1-M1: scan the WHOLE src tree and look for the CURRENT file name (the
|
||||
* original assertion kept checking the pre-rename string and could not fail). */
|
||||
test('#33 AC7: no production source references the schema dump', () => {
|
||||
const walk = (dir) => readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
||||
const target = new URL(entry.name + (entry.isDirectory() ? '/' : ''), dir);
|
||||
return entry.isDirectory() ? walk(target)
|
||||
: /\.(ts|js|mjs|json)$/.test(entry.name) ? [target] : [];
|
||||
});
|
||||
for (const file of walk(new URL('../src/', import.meta.url))) {
|
||||
assert.ok(!readFileSync(file, 'utf8').includes('config-schema.json'),
|
||||
`${file.pathname} references the schema dump — it is test infrastructure, not runtime data`);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user