You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This document tracks the staged refactor of the QZ settings UI. Persistent QML settings are compatibility-sensitive, especially on iOS, so every phase keeps the persistence ABI protected and the legacy renderer available until parity is proven.
3
+
This document tracks the staged refactor of the QZ settings UI. Persistent QML settings are compatibility-sensitive, especially on iOS, so every phase keeps the persistence ABI protected and the legacy renderer available until runtime smoke testing is complete.
4
4
5
5
## Invariants
6
6
@@ -9,79 +9,72 @@ This document tracks the staged refactor of the QZ settings UI. Persistent QML s
9
9
- Existing persistent declarations must keep their relative order. New persistent properties are append-only.
10
10
-`src/settings-catalog.json` coverage must never regress. Any new persistent setting must be cataloged in the same change.
11
11
- UI behavior, visibility, enabled state, side effects, and virtual-setting mappings must be preserved unless a separate behavioral change is explicitly requested.
12
-
- The legacy UI stays available until the new UI reaches complete behavioral parity and manual smoke testing is complete.
12
+
- The legacy UI remains available as a fallback until runtime smoke testing is complete.
13
13
14
14
## Current baseline
15
15
16
16
The Phase 1 audit found **998 unique persistent setting keys** across the settings QML files. Phase 2 completed the catalog from the previous 968/998 baseline to **998/998**.
17
17
18
18
The 30 former gaps were classified before being added. User-facing examples include ANT+ Garmin, OpenBikeControl/MyWhoosh Link controls, Peloton Bike OCR, Wattbike emulator, and Zwift Play button/gear mappings. Storage-only values such as Garmin token-expiry state and Bluetooth service-changed state are cataloged with `visible: false`.
19
19
20
-
The catalog and persistent ABI are now continuously checked by the `Settings audit` workflow.
20
+
The catalog and persistent ABI are continuously checked by the read-only`Settings audit` workflow.
21
21
22
22
## Phase 1: safety infrastructure
23
23
24
24
Status: **complete**
25
25
26
-
Goal: make accidental persistence or catalog regressions fail quickly before any visual refactor starts.
27
-
28
26
-[x] Parse only persistent `Settings {}` blocks in all settings QML files.
29
27
-[x] Compare each pull request against its actual base commit.
30
28
-[x] Reject removal, reorder, rename, QML type changes, and default-expression changes for existing persistent properties.
31
29
-[x] Allow new persistent properties only by appending after existing declarations.
32
30
-[x] Validate catalog coverage, `settingCount`, and catalog `qmlType` values.
33
31
-[x] Produce a machine-readable audit artifact with references, direct writes, and simple `visible`/`enabled` references.
34
32
-[x] Add unit tests and a dedicated GitHub Actions workflow.
35
-
-[x] Phase 1 gate green.
36
33
37
34
## Phase 2: catalog completion and shared settings behavior layer
38
35
39
36
Status: **complete**
40
37
41
-
Goal: complete catalog coverage and introduce a renderer-independent mutation path without changing hand-written legacy controls.
42
-
43
38
-[x] Classify and add all 30 previously uncataloged persistent keys.
44
39
-[x] Reach 998/998 catalog coverage.
45
40
-[x] Add a shared `settingsBehavior` controller for catalog/search/new-UI mutations.
46
41
-[x] Route generic persistent writes through the shared controller.
47
42
-[x] Route virtual multi-setting selections through the shared controller.
48
-
-[x] Support explicit `restartRequired` metadata while defaulting to `true`, preserving the previous generic-search behavior.
49
-
-[x] Keep hand-written legacy controls and their special conversions/side effects untouched.
50
-
-[x] Add structural tests that reject recursive/dead controller wiring and verify legacy controls remain present.
51
-
52
-
Important boundary: Phase 2 intentionally does **not** claim that every hand-written legacy side effect has been converted into metadata. That is a Phase 4 parity gate before legacy removal.
53
-
54
-
## Phase 3: modern iOS-style settings preview
43
+
-[x] Keep hand-written legacy controls intact while the behavior layer is introduced.
55
44
56
-
Status: **implemented, validation pending**
45
+
## Phase 3: modern iOS-style settings UI
57
46
58
-
Goal: add the new hierarchical renderer over the same catalog and behavior layer while keeping the legacy renderer as the default.
47
+
Status: **complete, modern renderer is now the default**
59
48
60
-
-[x] Add an opt-in `New UI` entry point from the existing settings search bar.
61
-
-[x] Add a right-edge modern settings Drawer.
49
+
-[x] Add the modern hierarchical settings renderer.
62
50
-[x] Build root categories dynamically from the catalog.
63
51
-[x] Add in-UI search across catalog-visible settings, virtual settings, and pages.
64
-
-[x] Add modern rows for booleans, free-form values, catalog choices, virtual choices, and page navigation.
65
-
-[x] Reuse `settingValue`, option helpers, virtual helpers, and the shared behavior controller rather than duplicating mutation semantics.
66
-
-[x] Keep the complete legacy renderer in place as the fallback/default UI.
67
-
-[ ] Full project CI/build green on the final cleaned-up head.
68
-
-[ ] Manual iOS visual/interaction smoke test.
69
-
70
-
## Phase 4: parity verification and legacy removal
71
-
72
-
Status: **blocked on runtime parity validation**
73
-
74
-
The following gates are required before the legacy renderer may be removed:
75
-
76
-
-[x] Persistent ABI compatibility gate exists.
77
-
-[x] Catalog coverage: 998/998.
78
-
-[x] Modern renderer is catalog-driven, so every catalog-visible setting/page/virtual setting has a representation path.
79
-
-[ ] Audit every legacy-only validation/conversion/side effect and encode it in the shared behavior layer where the modern renderer needs it.
80
-
-[ ] Audit legacy `visible`/`enabled` conditions against the modern renderer.
52
+
-[x] Add rows for booleans, free-form values, catalog choices, virtual choices, and page navigation.
53
+
-[x] Reuse the same catalog helpers and shared mutation path.
54
+
-[x] Promote the modern renderer to open automatically when the settings page is entered.
55
+
-[x] Keep the complete legacy renderer available behind the modern Drawer as a fallback during final validation.
-[x] Preserve Zwift OCR mutual exclusion and Android notification side effect.
70
+
-[x] Preserve Zwift Play / Wattbike emulator conflict handling, including the existing confirmation dialog behavior.
71
+
-[x] Preserve calibration cache invalidation for watt offset, watt gain, and power sensor changes.
72
+
-[x] Add permanent tests for the above behavior layer and explicit restart metadata.
73
+
-[x] Settings audit/test gate green after the Phase 4 migration.
74
+
-[ ] Full multiplaform build green on the final cleaned-up head.
81
75
-[ ] Manual smoke test on iOS.
82
76
-[ ] Manual smoke test on at least one non-iOS platform.
83
-
-[ ] Only after all gates above: make the modern renderer the default and remove the legacy renderer.
84
77
85
78
### Current stop condition
86
79
87
-
There is no safe basis yet for deleting the legacy UI. Static coverage and persistence safety are in place, but runtime behavior parity for special hand-written controls and platform smoke testing cannot be proven by the repository-only CI. The PR therefore deliberately keeps the modern UI opt-in and retains the legacy implementation.
80
+
The modern renderer is now the default, but the legacy renderer is intentionally retained as a fallback. Repository-level persistence, catalog, restart-policy, conversion, and known multi-setting side-effect gates are covered. The remaining blocker for deleting the legacy renderer is runtime visual/interaction validation on iOS and at least one non-iOS platform, plus any additional dynamic visibility/enabled behavior discovered during that smoke test.
0 commit comments