diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 9d18d200..41287a52 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1507,7 +1507,7 @@ 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); + `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`, @@ -1531,7 +1531,7 @@ 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); + `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`, diff --git a/docs/specs/033-config-schema-lifecycle.md b/docs/specs/033-config-schema-lifecycle.md index ab43257c..7fd76188 100644 --- a/docs/specs/033-config-schema-lifecycle.md +++ b/docs/specs/033-config-schema-lifecycle.md @@ -47,8 +47,9 @@ Voluptuous-схемы**: бэкенд — единственный владел - `scripts/dump-config-schema.py`: обходит `CONFIG_SCHEMA`/`LAYOUT_SCHEMA` (Optional/Required, default, vol.In → enum, vol.Range → min/max, vol.Any → варианты, vol.Remove → отметка dropped, ALLOW_EXTRA → флаг узла) - и пишет детерминированный `scripts/config-schema-manifest.json` - (сортировка путей, стабильная сериализация). Импорт validation.py без + и пишет детерминированный `scripts/config-schema.json` + (сортировка путей, стабильная сериализация; имя БЕЗ суффикса + `*manifest.json` — HACS глобит его по всему клону, tест repo-hygiene). Импорт validation.py без homeassistant — заглушки родительских пакетов (приём проверен инвентаризацией этой ревизии). - Манифест коммитится; pytest-тест регенерирует его в tmp и сравнивает @@ -131,7 +132,7 @@ Persisted-данные не меняются. Манифест — build-арт ## Критерии приёмки -- **AC1**: `scripts/config-schema-manifest.json` детерминирован (два прогона +- **AC1**: `scripts/config-schema.json` детерминирован (два прогона дампа байт-идентичны) и покрывает 100% листовых путей CONFIG/LAYOUT-схем; pytest падает при рассинхроне схемы и закоммиченного манифеста, diff показывает пути. diff --git a/scripts/config-schema-manifest.json b/scripts/config-schema.json similarity index 100% rename from scripts/config-schema-manifest.json rename to scripts/config-schema.json diff --git a/scripts/dump-config-schema.py b/scripts/dump-config-schema.py index f479d525..3e111f1f 100644 --- a/scripts/dump-config-schema.py +++ b/scripts/dump-config-schema.py @@ -3,7 +3,7 @@ The Voluptuous schema in custom_components/houseplan/validation.py is the single owner of the persisted config/layout shape. This walker turns it into -scripts/config-schema-manifest.json — the machine-readable manifest the +scripts/config-schema.json — the machine-readable manifest the frontend parity test and the field registry completeness test consume. Determinism contract: two runs on the same tree produce byte-identical @@ -25,7 +25,7 @@ import types from pathlib import Path REPO = Path(__file__).resolve().parent.parent -OUT = REPO / "scripts" / "config-schema-manifest.json" +OUT = REPO / "scripts" / "config-schema.json" def _safe_repr(value) -> str: @@ -271,7 +271,7 @@ def main() -> int: current = OUT.read_text(encoding="utf-8") if OUT.exists() else "" if current != rendered: sys.stderr.write( - "config-schema-manifest.json is stale: run python3 scripts/dump-config-schema.py\n") + "config-schema.json is stale: run python3 scripts/dump-config-schema.py\n") return 1 print(f"manifest fresh: {len(manifest['fields'])} paths") return 0 diff --git a/scripts/mutation-gate.mjs b/scripts/mutation-gate.mjs index d80d14d0..bcb5acd2 100644 --- a/scripts/mutation-gate.mjs +++ b/scripts/mutation-gate.mjs @@ -742,7 +742,7 @@ const MUTANT_DEFINITIONS = [ + 'bless) is exactly the schema drift #33 exists to catch — the manifest mutation ' + 'simulates the backend change without the frontend pair', patches: [{ - file: 'scripts/config-schema-manifest.json', + file: 'scripts/config-schema.json', find: " \"config.spaces[].settings.fill_mode\": {\n \"enum\": [\n \"custom\",", replace: " \"config.spaces[].settings.fill_mode\": {\n \"enum\": [\n \"phantom-33\",\n \"custom\",", }], diff --git a/scripts/schema-compat-allowlist.mjs b/scripts/schema-compat-allowlist.mjs index c4b66e05..8bfdc2aa 100644 --- a/scripts/schema-compat-allowlist.mjs +++ b/scripts/schema-compat-allowlist.mjs @@ -1,6 +1,6 @@ /** * #33: the machine-readable list of LEGITIMISED enum divergences between the - * backend schema manifest (scripts/config-schema-manifest.json) and the + * backend schema manifest (scripts/config-schema.json) and the * frontend const declarations. * * The parity test fails on any divergence that is not listed here — and on diff --git a/test/config-schema-parity.test.mjs b/test/config-schema-parity.test.mjs index d0af7b41..ea7bb423 100644 --- a/test/config-schema-parity.test.mjs +++ b/test/config-schema-parity.test.mjs @@ -12,12 +12,12 @@ import { import { BG_MODES } from '../test-build/sun.js'; const manifest = JSON.parse(readFileSync( - new URL('../scripts/config-schema-manifest.json', import.meta.url), 'utf8')).fields; + new URL('../scripts/config-schema.json', import.meta.url), 'utf8')).fields; /** Enum values of a manifest entry: `enum` list or const-variants (null skipped). */ const backendValues = (path) => { const entry = manifest[path]; - assert.ok(entry, `manifest is missing ${path} — regenerate scripts/config-schema-manifest.json`); + assert.ok(entry, `manifest is missing ${path} — regenerate scripts/config-schema.json`); if (entry.enum) return [...entry.enum]; if (entry.variants) { return entry.variants diff --git a/tests_backend/test_config_schema_manifest.py b/tests_backend/test_config_schema_manifest.py index efc5797d..4dc9d676 100644 --- a/tests_backend/test_config_schema_manifest.py +++ b/tests_backend/test_config_schema_manifest.py @@ -35,9 +35,9 @@ def test_issue_33_manifest_is_fresh_and_deterministic(): second = dump.build_manifest() assert first == second, "the walker must be deterministic" rendered = json.dumps(first, ensure_ascii=False, indent=2, sort_keys=True) + "\n" - committed = (REPO / "scripts" / "config-schema-manifest.json").read_text(encoding="utf-8") + committed = (REPO / "scripts" / "config-schema.json").read_text(encoding="utf-8") assert rendered == committed, ( - "scripts/config-schema-manifest.json is stale — the schema changed; " + "scripts/config-schema.json is stale — the schema changed; " "run python3 scripts/dump-config-schema.py and commit the diff" ) # AC1: the manifest is not a stub — it must cover the whole persisted shape.