Comet Analyzer state machine and interactive Qt gate
R154 centralizes primary GUI availability in one immutable Qt-free projection and defines a strict qualification boundary between deterministic headless checks and a real PyQt5 release pass.
Cumulative qualification: R154 PASS_HEADLESS / R161 PASS_HEADLESS / RC. Deterministic control-state and display-encoding checks passed; both real-PyQt5 smoke paths remain pending.
Canonical immutable control projection
CometAnalyzerControlState and resolve_comet_analyzer_control_state(...) own the complete primary-control decision. WLightCurves._refresh_gui_capabilities() applies the projection instead of rebuilding overlapping rules in individual handlers.
The projection owns target selection; dataset, source, settings and formula actions; Editing target visibility; Multi-colour; adaptive Break windows; Analyse; committed-view controls; Refresh; the Matplotlib toolbar; Fixed slope, fixed n and Shared n; and adaptive completeness.
The projection is Qt-free and deterministic. Qt widgets consume the result; they do not define a parallel capability policy.
EMPTY / SINGLE_TARGET / MULTI_TARGET / BUSY matrix
| State | Required primary behavior |
|---|---|
| EMPTY | Only Select comets is active. Dataset, fit, Multi-colour, range and toolbar actions are unavailable. |
| SINGLE_TARGET | Dataset actions are active. Editing target is hidden. Shared n is unavailable. |
| MULTI_TARGET | Editing target is visible and navigational. Shared n is available only for a compatible preflight group. |
| BUSY | State-changing controls, Analyse and Refresh are blocked. A committed graph remains navigable through live axis/magnitude controls and the Matplotlib toolbar. |
Adaptive completeness and fit coupling
Adaptive Analyse remains blocked until every selected target has a valid Break window. Adaptive mode disables Shared n, Fixed slope and fixed n through the same central projection. No individual handler may re-enable an incompatible fit control.
BUSY state and the committed view
BUSY distinguishes state mutation from inspection of the last committed result. Loading, target topology, formula/state changes, Analyse and Refresh are blocked while physical work is active. Axis and magnitude projection controls and the toolbar remain usable when a committed graph exists.
This preserves responsiveness without allowing the running operation to observe a partially changed compute state.
No stale worker-state restoration
The Comet Analyzer override of _analysis_set_busy() does not save and replay historical isEnabled() values. Success, failure and cancellation end by resolving capabilities from the then-current committed dataset, selected-target topology, fit mode and worker state.
start worker with one target
→ target topology changes while BUSY
→ worker finishes
→ resolve current capabilities
→ never replay obsolete one-target widget statesSuccess, exception, cooperative cancellation and finalization callbacks remain guarded by job identity.
View-only and transactional invariants
- Editing-target navigation does not redraw, reload observations or request geometry.
- Axis and magnitude changes render committed arrays and do not invalidate or start a compute worker.
- Invalid Refresh ranges preserve the previous graph.
- Incomplete calculated result sets preserve the previous graph and committed result dictionary.
- Worker failure and cancellation preserve the previous committed graph.
- Formula and dataset transactionality from R149 and the R131–R144 compute pipeline remain unchanged.
R156 interaction extension
R156 refines the R154 visual checklist without changing the central control-state projection. Fit Group labels are now directly editable only through a double-click in the registry label column; ordinary clicks remain read-only. Multi-colour Export CSV… is capability-bound to a valid calculated result.
R161/R169 display-state extension
The display-style domain sits above the same R154 capability projection. Encoding selection, target style editing, channel Marker/Colour editing and the R169 Point size controller are view-only actions. They may rebuild artists and legends but must not enable a blocked scientific action, start a worker or alter the committed dataset.
Point size is global display state with a default of 3.0. It persists independently, preserves R163 AUTO/USER ranges and does not resize formula markers or legend keys.
R163 range-state extension
Control capability and plot-range ownership are separate immutable projections. The R154 state machine decides whether range controls and Refresh are available; R163 decides whether their values represent AUTO output or a valid USER commitment for the current rendered-data identity.
Worker completion still re-resolves controls from current topology. Range resolution likewise rejects stale callback limits unless they belong to the unchanged USER identity.
Interactive release gates
R154 qualifies the complete control-state projection. R161 qualifies display-style domain state and real Matplotlib artist encoding. Each strict gate must reject a host without a working PyQt5 runtime.
QT_QPA_PLATFORM=offscreen python3 r154_comet_analyzer_interactive_release_gate.py --require-qt
QT_QPA_PLATFORM=offscreen python3 r161_comet_analyzer_display_encoding_release_gate.py --require-qtOutcome semantics remain strict:
- PASS: deterministic and real PyQt5 paths passed;
- PASS_HEADLESS: deterministic path passed but PyQt5 was unavailable; not release-qualified;
- FAIL: a deterministic/Qt check failed or strict mode lacked a Qt runtime.
The current native smoke is:
python3 r161_comet_analyzer_qt_smoke.py --nativeNative visual checklist
- EMPTY, SINGLE_TARGET, MULTI_TARGET and BUSY controls match the central projection.
- A committed graph remains navigable while BUSY, while scientific state-changing actions remain blocked.
- Comet colour / channel marker and Channel colour / comet marker render the correct identity dimensions.
- Pre-perihelion points are hollow and post-perihelion points are filled.
- Reverse-mode formulae are neutral and use sparse comet markers.
- Comets, Data channels, Perihelion side and Formulae legend blocks show only rendered identities.
- Target and channel style edits commit transactionally and restore exactly in a fresh instance.
- Point size accepts 1.0–12.0 in 0.5 steps, resizes both perihelion sides, persists across restart and resets to 3.0 with display defaults.
- Point-size changes preserve range, zoom and all prepared scientific results.
- Reverse mode supplies unique markers through 15 targets and falls back deterministically at 16.
- Fit Group inline rename and Multi-colour CSV export continue to follow the R156 contract.
- Invalid Refresh, worker error or cancellation preserves the previous graph.
Source-reported validation and limitations
- Focused R154: 13 passed, 1 skipped because PyQt5 was unavailable.
- R130–R154 selection: 189 passed, 1 skipped.
- Deterministic R154 state checks: 14/14 PASS.
- R146, R151, R152 and R153 gates: PASS; R154: PASS_HEADLESS.
- Strict qualification in the supplied environment: FAIL — qt_runtime_unavailable, expected and release-blocking.
- Syntax, documentation hygiene, performance validation and CLEAN/QA manifests passed as reported.