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

StateRequired primary behavior
EMPTYOnly Select comets is active. Dataset, fit, Multi-colour, range and toolbar actions are unavailable.
SINGLE_TARGETDataset actions are active. Editing target is hidden. Shared n is unavailable.
MULTI_TARGETEditing target is visible and navigational. Shared n is available only for a compatible preflight group.
BUSYState-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 states

Success, 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.

R156 Fit Group and CSV contract

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.

R169 point-size contract

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.

Full range-state contract

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-qt

Outcome 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 --native

Native visual checklist

  1. EMPTY, SINGLE_TARGET, MULTI_TARGET and BUSY controls match the central projection.
  2. A committed graph remains navigable while BUSY, while scientific state-changing actions remain blocked.
  3. Comet colour / channel marker and Channel colour / comet marker render the correct identity dimensions.
  4. Pre-perihelion points are hollow and post-perihelion points are filled.
  5. Reverse-mode formulae are neutral and use sparse comet markers.
  6. Comets, Data channels, Perihelion side and Formulae legend blocks show only rendered identities.
  7. Target and channel style edits commit transactionally and restore exactly in a fresh instance.
  8. 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.
  9. Point-size changes preserve range, zoom and all prepared scientific results.
  10. Reverse mode supplies unique markers through 15 targets and falls back deterministically at 16.
  11. Fit Group inline rename and Multi-colour CSV export continue to follow the R156 contract.
  12. 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.

User state guide · Testing contract · Supplied R154 report