checks / unit (pull_request) Successful in 5m1s
The add-on had NO translations/ directory, so Home Assistant fell back to rendering the raw option KEYS. An operator opening Configuration read `unit_sign_disagree_w`, `saturation_cycles`, `integrator_max_w` - seventy of them, in file order, with no description and no way to tell a constant derived from 1890 closed-loop test runs from their own inverter's rating. ⚠️ PRESENTATION ONLY. Not one default, not one schema range and not one line of behaviour moved. The 175 non-comment lines of config.yaml are byte-identical to release/1.0's, reordered; the only comment lines that changed are the group banners and five sentences whose "above"/"below" the reorder made false. * translations/en.yaml and translations/nl.yaml: a name and a description for all 70 options and all 7 per-entry keys of the `inverters:` repeatable section. The Dutch is written for a Flemish homeowner - afname/injectie, kwartierpiek, capaciteitstarief - not transliterated from the English. * ⭐ Every derived constant says WHERE IT CAME FROM and whether to touch it. `unit_sign_disagree_s` cites its 1890 closed-loop runs (SAFETY-30); `circulation_w` its 924 runs and the measured 400 W knee (SAFETY-31); `meter_freeze_s` its 2399 s capture (SAFETY-18); `bms_soc_floor` register 0x0560. The two numbers that are the operator's - max_w and peak_cap_w - say "THIS IS YOUR NUMBER". `cycle_cost_threshold_eur`, which is neither, says it is an open product question (OPT-08). * config.yaml reordered into task order in BOTH halves, comments carried with their keys: hardware -> meter -> ratings and limits -> tariff -> maintenance -> safety -> advanced. ⚠️ test_inverter.py:1788's trap is PRESERVED: the nested per-entry `max_w:` still precedes the flat one under `schema:`, and `inverters: []` still ships in the `options:` half. Moving the nested block below the flat one is a 1-byte edit; it was run and goes red in test_inverter.py and test_translations.py. * tools/supervisor_shape_probe.py section F MEASURES the result against ghcr.io/home-assistant/amd64-hassio-supervisor:2026.08.0 - Supervisor's own _read_app_translations(), its own UiOptions, and the frontend's label resolution transcribed from the shipped bundle - and prints every rendered row in both languages. `all cases passed`, exit 0. * ⚠️ ONE MEASURED NEGATIVE RESULT, NOT GLOSSED: the per-entry keys inside the `inverters:` section still render RAW on frontend 20250925.1, because a list-of-objects goes to ha-selector-object and the converter sets no `label` and no `translation_key`. The `fields:` translations are shipped and validated - that is Supervisor's own shape for this - but this card does not claim they reach the screen. The `inverters` label and description do. * test_translations.py: a translation naming a non-existent option, or an option with no translation, fails CI - both directions, both languages, top level and per entry. Mutation-tested BOTH WAYS: eleven in-process mutations each required to NAME the key they broke, plus a negative control; six more run on disk through the purge-and-re-read protocol. Two of the eleven are the ones that cost the WHOLE FILE rather than one label - an unrecognised top-level block, and a `network:` value that is not a string. Supervisor drops a file that fails SCHEMA_APP_TRANSLATIONS with only a log warning, so those silently return the entire tab to raw keys; neither is reachable through the `configuration:` coverage, which is why they were hunted for and closed. * tools/translations_lint.py is the hand parser CI needs (no PyYAML in the add-on image) and the probe grades it, key by key and string by string, against Supervisor's own loader. It is strict and RAISES rather than skipping; nine malformed shapes prove it. Full 20-file suite green (19 existing + test_translations.py). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU