Every static check asserted its rules against a hardcoded path, mostly
contents/ui/main.qml. Moving a lifecycle rule, security rule, or UI rule into a
component or JS helper therefore broke `make check` for reasons unrelated to
behavior, and the only fix was to edit the assertion in the same commit as the
move. That makes the safety net useless exactly when it matters most, and
AGENTS.md documented the resulting chore as a rule to follow.
Introduce scripts/lib/qml_surfaces.py as the single source list, grouped into
surfaces: one scope that owns its own process lifecycle and state. Assertions now
run against a surface, so extracting code out of main.qml into contents/ui/,
contents/ui/components/, or the planned controllers/ and config/ directories
keeps the check green without touching the check.
Behavior of every rule is unchanged; only what the rule is searched in widens.
Verified both directions: with two properties moved into a new
contents/ui/controllers/ file the full suite passes, and with the same rule
deleted outright test_process_lifecycle.sh still fails.
- Rules that belong to a surface use require_in_surface/reject_in_surface, or
Surface.require/function_body/id_block in the python checks. Binding main_text
to the concatenated surface kept the ~300 existing python call sites intact.
- Per-file contracts keep the *_in_file helpers on purpose: "every one of these
component delegates must declare a safe icon fallback" would be satisfied by
any single file under a surface-wide check.
- Replace id_block's forward scan for the next sibling `\n Timer {` with a
brace-matched walk back to the enclosing object, which no longer depends on
indentation or on a following declaration.
- Makefile QML_FILES, test_qml_hardening.sh, and update_translations.sh each
carried the same 27-path list; all three now read the manifest, and the
hardening check fails if a QML/JS source on disk matches no glob.
Two coverage gaps surfaced while converting and are closed here:
- test_theme_boundaries.sh globbed contents/ui/*.qml only, so all 12 components
were exempt from the hardcoded-color check. It now scans the surface, which
would otherwise have silently stopped covering popup UI once that UI moves
into a component.
- test_ui_regressions.sh listed 9 paths by hand for its popup/provider element
rules, missing 4 components. That list is now derived from the manifest.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>Simone committed on 8/17/2026, 1:51:32 PMparenta100d60
15 files changedLine totals unavailable