Radio State Pipeline Regression Matrix¶
Status: MOR-348 risk-to-coverage index Date: 2026-06-03
This matrix maps the original state-pipeline risks from
docs/superpowers/specs/2026-06-02-radio-state-pipeline-design.md to current
coverage. It is an index, not the release checklist. Run final gate commands
from docs/internals/radio-state-pipeline-validation.md.
CI coverage must remain hardware-free. Hardware validation remains separate and requires explicit opt-in.
Coverage Primitives¶
rigplane.core.state_pipeline_contracts:FieldPath,Observation,ChangeSet, freshness transitions, command intent/lifecycle contracts, and capability metadata.rigplane.core.state_store.StateStore: canonical state/freshness revisions, observation sequence, immutable snapshots/deltas, and stale-field repair hints.rigplane.core.acquisition_scheduler: backend-neutral ensure-fresh requests, dedupe/priority/cadence policy, external CAT pause handling, and meter coalescing.- Web delivery: HTTP and WebSocket state projections from
StateStore, legacyrevisionaliasingstateRevision, and additivetransportSeq. - Runtime/rigctld adapters: CI-V observation projection, poller command readback, rigctld GET projection, and scoped pending overlays.
Risk-To-Coverage Matrix¶
| Area | Original symptom or risk | Current coverage | Remaining release gate |
|---|---|---|---|
| StateStore invariants | Consumers could not distinguish semantic changes, freshness changes, and observation traffic. | tests/test_state_store.py::test_noop_observations_do_not_advance_state_revision, test_freshness_expiration_advances_freshness_without_state_change, test_full_snapshot_and_delta_projection_agree_after_observation_sequence, test_snapshot_output_cannot_mutate_store_owned_state, test_returned_changes_cannot_mutate_delta_history, test_direct_writer_api_is_not_exposed; tests/test_state_pipeline_contracts.py::test_observation_and_changeset_serialization_round_trip. |
Focused state pipeline batch plus full pytest gate. |
| Meters | S-meter/meter samples mutated state but waited for unrelated revision bumps before Web saw them. | tests/test_state_store.py::test_meter_delta_is_visible_without_unrelated_follow_up_revision; tests/test_state_pipeline_diagnostics.py::test_civ_meter_write_records_diagnostic_without_notify_or_revision, test_web_meter_write_delivers_state_store_revision_without_unrelated_trigger; tests/test_web_server_coverage.py::test_state_store_s_meter_change_broadcasts_without_legacy_revision_event, test_meter_only_state_store_change_emits_web_delta_without_legacy_revision; tests/test_civ_rx_coverage.py::test_meter_coalescing_applies_latest_due_sample_and_records_diagnostics, test_same_value_coalesced_meter_flush_completes_scheduler_request; tests/test_radio_poller_coverage.py::test_high_tier_emits_s_meter_on_rx_for_consecutive_cycles, test_high_tier_rotates_pwr_swr_alc_on_tx, test_low_tier_emits_at_expected_stride_for_lan. |
Live v2 Playwright plus IC-7610 LAN hardware for real cadence. |
| Frequency latency | Slow serial or request/response radios could show delayed panel tuning updates. | tests/test_radio_poller_coverage.py::test_set_freq_readback_is_applied_as_state_store_observation, test_set_freq_readback_uses_original_command_correlation, test_set_freq_readback_reconciles_only_matching_websocket_session_for_reused_command_id, test_scheduler_polling_does_not_starve_user_command_queue; tests/test_rigctld_handler.py::test_get_freq_projects_state_store_snapshot, test_get_freq_ensure_fresh_requests_bounded_projection, test_get_freq_stale_store_value_falls_through_to_radio_readback, test_set_freq_enters_command_service_and_applies_observation. |
Fake live Web v2 and X6200 serial hardware latency notes. |
| Stale fields | A missed or malformed unsolicited frame could leave old state looking authoritative. | tests/test_state_store.py::test_dropped_event_marks_stale_and_requests_reconciliation, test_freshness_service_marks_stale_and_queues_reconciliation_without_web, test_observation_refreshes_stale_field_without_semantic_state_change; tests/test_acquisition_scheduler.py::test_stale_unsolicited_field_queues_reconciliation_and_accepts_readback, test_model_service_queues_when_field_observation_max_age_expired_without_mark_stale; tests/test_civ_rx_coverage.py::test_same_value_stale_refresh_notifies_state_store_changed; tests/test_rigctld_handler.py::test_get_freq_headless_freshness_service_stale_value_queues_reconciliation. |
Hardware missed-event recovery when available. |
| Snapshots | HTTP snapshots and initial WebSocket state could disagree when built from different revision sources. | tests/test_web_server_coverage.py::test_http_snapshot_matches_initial_ws_full_state_for_same_store_revision, test_initial_full_state_envelope_revisions_match_ingested_legacy_state, test_http_and_ws_full_state_share_post_sync_legacy_snapshot, test_state_response_includes_revision_and_updated_at; tests/test_delta_encoder.py::TestDeltaEncoder::test_transport_sequence_is_split_from_canonical_state_revision. |
Live v2 Playwright. |
| WebSocket reconnect | Reconnect/reset logic could confuse canonical state revision with transport sequence. | tests/test_delta_encoder.py::TestDeltaEncoder::test_transport_sequence_is_split_from_canonical_state_revision; tests/test_web_server_coverage.py::test_initial_full_state_envelope_does_not_consume_broadcast_delta, test_on_radio_reconnect_enables_scope_without_waiting_for_broadcast; tests/test_websocket_coverage.py::test_keepalive_loop_sends_ping, test_close_idempotent, frame parsing/close-path tests. |
Live v2 Playwright reconnect observation; hardware optional. |
| Web delivery cleanup | MOR-347 removed Web poller-owned public revision; regression could reintroduce poller revisions or legacy sync in delivery. | tests/test_state_pipeline_contracts.py::test_web_poller_no_longer_exposes_public_revision_delivery_api, test_web_server_state_change_callback_does_not_bump_poller_revision, test_web_delivery_methods_snapshot_only, test_legacy_state_sync_is_not_called_from_web_server_delivery; docs/internals/legacy-state-writer-inventory.md. |
Full pytest and review of changed Web files. |
| rigctld GET | rigctld GET could read stale local cache instead of shared confirmed state. | tests/test_rigctld_handler.py::test_get_freq_projects_state_store_snapshot, test_get_mode_projects_state_store_snapshot, test_get_ptt_projects_state_store_snapshot, test_get_vfo_projects_state_store_active_slot, test_get_level_strength_projects_state_store_snapshot, test_get_level_af_projects_state_store_snapshot, test_get_func_projects_state_store_snapshot, dual-RX state-store projection tests near test_dual_rx_get_freq_vfob_projects_state_store_receiver_1 and test_dual_rx_get_mode_vfob_projects_state_store_receiver_1. |
External rigctld/Hamlib hardware or fake integration when claiming provider readiness. |
| rigctld SET / read-after-write | SET behavior could depend on unscoped pending/cache state. | tests/test_rigctld_handler.py::test_get_freq_keeps_optimistic_value_until_radio_state_catches_up, test_get_mode_read_after_write_uses_pending_overlays_until_reconciled, test_get_mode_pending_overlays_are_scoped_to_rigctld_session, test_get_ptt_keeps_optimistic_state_until_radio_state_catches_up, test_set_freq_enters_command_service_and_applies_observation; tests/test_radio_poller_coverage.py::test_queued_command_failure_emits_failed_lifecycle_and_expires_overlay, test_queued_core_timeout_emits_timed_out_lifecycle_and_expires_overlay. |
rigctld semi-integration plus Hamlib/provider hardware when in scope. |
| Pending overlays | Optimistic values could leak between Web, HTTP, rigctld sessions, or reused command IDs. | tests/test_web_server_coverage.py::test_websocket_reused_command_ids_are_scoped_per_connection, test_http_command_batch_timeout_marks_command_timed_out_and_expires_overlay; tests/test_radio_poller_coverage.py::test_set_freq_readback_reconciles_only_matching_websocket_session_for_reused_command_id, test_mark_queued_command_failed_scopes_reused_command_ids_by_source, test_mark_queued_command_failed_scopes_reused_command_ids_by_session; tests/test_rigctld_handler.py::test_get_mode_pending_overlays_are_scoped_to_rigctld_session. |
Live v2 command audit. |
| Dropped unsolicited events | Push-capable backends can lose an event, so revision alone is not proof of physical state. | tests/test_state_store.py::test_dropped_event_marks_stale_and_requests_reconciliation; tests/test_acquisition_scheduler.py::test_stale_unsolicited_field_queues_reconciliation_and_accepts_readback; tests/test_civ_rx_coverage.py::test_scheduler_active_freq_mode_request_completes_from_civ_rx_loop, test_update_state_cache_records_scheduler_result_for_matching_pending_request. |
Hardware missed-event recovery when available. |
| Adaptive acquisition | Background polling could delay commands or flood slow links. | tests/test_acquisition_scheduler.py::test_duplicate_requests_coalesce_with_highest_priority_and_urgent_deadline, test_same_family_requests_share_one_acquisition_request, test_user_facing_requests_preempt_background_telemetry, test_due_polling_emits_only_pollable_cadence_fields_and_groups_by_existing_key, test_due_polling_does_not_reemit_pending_cadence_request, test_unchanged_acquisition_result_decays_cadence_exponentially_and_caps, test_semantic_change_resets_adaptive_cadence_to_base_policy, test_diagnostics_include_cadence_next_due_pending_pressure_and_counts; tests/test_radio_poller_coverage.py::test_scheduler_due_request_sends_supported_civ_query_once, test_scheduler_polling_does_not_starve_user_command_queue. |
X6200/Yaesu hardware or simulator notes for slow-link cadence. |
| Command responses | Command acknowledgements could be mistaken for confirmed state, or confirmed responses could bypass state revisions. | tests/test_state_pipeline_contracts.py::test_command_intent_and_lifecycle_event_serialization_round_trip; tests/test_radio_poller_coverage.py::test_command_queue_entries_preserve_command_correlation_metadata, test_set_freq_readback_is_applied_as_state_store_observation, test_observable_queue_commands_apply_state_store_observations, test_compatibility_mirror_commands_reconcile_from_state_store_observation, test_select_vfo_readback_uses_original_command_correlation; tests/test_civ_rx_coverage.py::test_update_state_cache_applies_command_response_observation_once. |
Live v2 command audit and full pytest. |
| Mode/buttons | Mode, VFO, split, PTT, and button-like controls could diverge between legacy state and shared state. | tests/test_rigctld_handler.py::test_get_mode_projects_state_store_snapshot, test_get_split_vfo_reads_state_store_split_and_protocol_local_tx_vfo, test_set_mode_uses_core_contract_string_values; tests/test_radio_poller_coverage.py::test_execute_quick_dw_trigger_equalizes_then_enables_dw, test_execute_quick_split_trigger_equalizes_then_enables_split, test_select_vfo_readback_uses_original_command_correlation; tests/test_bsr_band_switching.py::TestSetBandBSRRecall::test_bsr_recall_updates_state, test_bsr_recall_emits_events. |
Live v2 Playwright. |
| Encoder-style controls | Knob/encoder controls could overwrite wrong receiver/global paths or bypass command correlation. | tests/test_civ_rx_coverage.py::test_update_radio_state_cmd14_receiver_dsp_levels, test_update_radio_state_cmd14_global_dsp_levels, test_update_radio_state_operator_toggle_family, test_update_state_cache_uses_slot_specific_observation_path_when_override_active; tests/test_radio_poller_coverage.py::test_execute_receiver_routed_set_commands_use_backend_receiver_and_target_state, test_execute_set_filter_width_dispatches_to_radio_protocol, test_execute_set_scope_edge_updates_state, test_execute_set_scope_vbw_updates_state, test_execute_set_scope_rbw_updates_state. |
Live v2 Playwright and hardware operator notes. |
| External rigctld client / Hamlib | Hamlib-backed responses must behave like observations, not a separate cache; direct libhamlib remains out of Core. |
tests/test_rigctld_handler.py state-store projection/read-after-write coverage; tests/test_rigctld_server.py::TestSemiIntegrationSerialMockRadio::test_get_and_set_frequency_flows_through_core, test_get_and_set_mode_flows_through_core; tests/integration/test_rigctld_wsjtx.py when integration tests are explicitly run. Boundary decision documented in docs/internals/hamlib-provider-rollout.md. |
External rigctld hardware/provider checklist before Hamlib readiness claim. |
| Cleanup guards | Waiters, watchdogs, stale backlog, and lifecycle servers could leak or deliver stale data after migration. | tests/test_civ_rx_coverage.py::test_cleanup_stale_civ_waiters_logs_cleaned_count, test_civ_rx_loop_sheds_stale_scope_backlog_but_keeps_control_packets, watchdog stop/reconnect tests; tests/test_lifecycle_diagnostics.py WebServer/SerialMockRadio lifecycle diagnostics; tests/test_state_pipeline_contracts.py static cleanup guards. |
Full pytest; manual check that fake live server is stopped after Playwright. |
| Frontend v2 and i18n | Frontend could consume stale revisions, private live URLs, regress localization/UI smoke, or audit controls that only mirrored RadioState rather than observation-backed live state. |
tests/test_frontend_e2e_config.py::test_frontend_e2e_does_not_default_to_private_live_backend; frontend/src/lib/i18n/__tests__/*; frontend/tests/e2e/i18n/i18n-visual.spec.ts; live frontend/tests/e2e/v2-ui-interactive.impl.ts via RIGPLANE_V2_URL. MOR-437 made the in-scope Icom v2 field families observation-backed via CI-V emitters in runtime/_civ_rx.py, and the fake SerialMockRadio is now ObservationPollable, so the live v2 audit exercises live controls through the observation pipeline rather than asserting against a RadioState mirror. |
npm run build, npm run check, npx vitest run, npm run test:e2e:i18n, live v2 Playwright. Residual non-blocking items: the mode-gated DSP/CW Pitch audit case is a known-fail (CW Pitch only writable in CW modes); Yaesu/rigctld-client provider observation gaps remain open under MOR-424. |
Hardware Validation Matrix¶
Hardware cases are outside CI. Mark each run as blocker-pass,
blocker-fail, waived, or informational.
| Hardware case | Purpose | Release status |
|---|---|---|
| IC-7610 LAN CI-V unsolicited meter/frequency burst | Compare real CI-V ingress rate with state revision and Web delivery rate. | Blocker for IC-7610 LAN release readiness unless explicitly waived. |
| IC-7610 LAN missed-event recovery | Drop or ignore one captured unsolicited frame and verify reconciliation read corrects shared state. | Blocker when claiming missed-event repair on IC-7610 hardware. |
| X6200 serial panel tuning latency | Measure bounded frequency update latency after external VFO tuning with and without unsolicited frames. | Blocker for serial-readiness claim; informational otherwise. |
| Yaesu CAT polling profile | Verify request/response polling updates shared state without model-specific state delivery branches. | Blocker for backend-neutral polling claim. |
External Hamlib rigctld client |
Verify real rigctld GET/SET responses publish observations and preserve read-after-write/read-only behavior. |
Blocker for Hamlib-provider readiness claim. |
| WebSocket reconnect against live radio | Verify reconnect full-state envelope preserves canonical state/freshness revisions while transport sequence resets. | Informational unless reconnect regression appears in fake live or hardware run. |
| Operator smoothness notes | Capture perceived meter jitter, knob feel, and UI smoothness. | Informational only; convert to blocker only with reproducible failing steps. |
Residual Non-Blocking Compatibility Shims¶
- Legacy Web
revisionremains and aliasesstateRevision. - WebSocket
transportSeqremains additive ordering metadata and is not a state/freshness revision. sync_state_store_from_radio_state(...)remains a one-way compatibility adapter at explicit ingress/startup points only.- Mutable
RadioState,StateCacheCapable, and rigctld cache fallback remain for older public/backend compatibility. - MOR-437 migrated the in-scope Icom v2 field families to observation-backed
CI-V emitters and made the fake
SerialMockRadioObservationPollable. Some non-v2 Icom families remain compatibility-onlydeferred_follow_upmirrors (seedocs/internals/legacy-state-writer-inventory.md), and the Yaesu/rigctld-client provider observation gaps stay open under MOR-424. - Hardware latency is measured evidence unless a release explicitly promises a numeric hardware target.