Skip to content

fix(daemon): make the five front-end toggles readable, and stop the panel displaying their opposite (#1276) - #1284

Merged
dc0sk merged 1 commit into
mainfrom
fix/1276-frontend-toggle-readback
Sep 5, 2026
Merged

fix(daemon): make the five front-end toggles readable, and stop the panel displaying their opposite (#1276)#1284
dc0sk merged 1 commit into
mainfrom
fix/1276-frontend-toggle-readback

Conversation

@dc0sk

@dc0sk dc0sk commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Closes #1276. Split out of #1271's design review.

The defect

SetNotch, SetAgc, SetCessb, SetLogbook and SetDcdSquelch were write-only — a client
could set them and never read them back. Verified there was no reader anywhere: DaemonConfig has no
front-end field, ControlEvent had no variant, the CLI has only the setters, the TUI drives the
engine directly. The filter isn't vacuous — the same search finds real production callers of sibling
accessors (last_rx_snr_db, in_band_interferers).

The visible consequence is a cold start, not a reconnect. notch_enabled and cessb_enabled
ship true and are applied at startup (config/src/lib.rs:683-684, server.rs:182/186), while the
panel kept shadow bools initialised false (app.rs:170-173) and never seeded them. So a default
install paints Notch and CE-SSB OFF while both are ON, from the first frame
, and the first click
sends the value already in force — a no-op that merely flips the display to match.

agc_enabled ships false, so its shadow was accidentally right. Two of the five invert; three
are merely unreadable. The test pins that split so the claim can't inflate.

The seam — corrected by review before any of this was built

The obvious fix, answering from GetConfig, does not read the engine. GetConfig is handled in
handle_command on the client task, whose ClientCtx holds Arc<Mutex> mirrors and no engine.
It would have added a daemon-side shadow: correct for the panel, but restating what the client
already believes and unable to catch the engine disagreeing.

The main loop's MetricsSnapshot is the one place that reads the engine for clients
(server.rs:1052), and the metrics task broadcasts from it at 1 Hz — so a client that connects,
reconnects, or missed a change event is correct within a second without asking.

A new variant, not fields on Metrics

Additive ControlEvent variants are absorbed by the wildcard arms every consumer has. Metrics is
not safe to extend: connection.rs:268 destructures it exhaustively, and linksim/serve.rs:405
plus a panel test construct it. MetricsSnapshot — daemon-internal, Default-constructed in one
place — is safe. The two look alike and are not.

Panel

Stores the reported state and both renders and toggles against it, falling back to the local
optimistic value only until the first report arrives. That is the pattern ToggleRepeater and the
QSY toggle already followed; these four now do too.

Tests

mod front_end_readback_tests in server.rs — unit tests, because front_end_state is private and
exporting it for an integration test is the "public API for an instrument" shape #1271 is about.

  • the shipped defaults, including that AGC is the odd one out — so the two-of-five claim is
    pinned rather than asserted;
  • the report follows the engine, not the last command.

Sabotage: replacing the engine reads with a hardcoded shadow — the design the review rejected
fails the second test with the report must follow the engine. Restored by sha256sum.

GATE: PASS 6dd9d462253efc55d241b9dda6a76486b98cf43c clean 20260905T181426Z
        suites=324 tests_passed=2458 tests_failed=0
REACH: PASS  (0 new)

Baseline

is_agc_enabled and is_notch_enabled left the reachability baseline — the outcome #1271
predicted when it labelled them READOUT instead of filing them with the tripwire counters, and the
reason it labelled them rather than accepting them as instruments.

agc_gain_db stays: this readout reports the five toggles, and the AGC's live gain is a meter — a
separate call about what the panel shows, not about closing a write-only command.

🤖 Generated with Claude Code

https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6

…anel displaying their opposite (#1276)

SetNotch, SetAgc, SetCessb, SetLogbook and SetDcdSquelch were write-only: a client could set them
and never read them back. There was no reader anywhere — DaemonConfig has no front-end field,
ControlEvent had no variant, the CLI has only the setters, the TUI drives the engine directly.

The visible defect is a COLD START, not a reconnect. notch_enabled and cessb_enabled ship true and
are applied at startup; the panel kept shadow bools initialised false and never seeded them, so a
default install painted Notch and CE-SSB OFF while both were ON from the first frame, and the first
click sent the value already in force. agc_enabled ships false, so its shadow was accidentally
right: two of five inverted, not all five. The test pins that split so the claim cannot inflate.

Seam, corrected by #1271's review before this was built: answering from GetConfig does NOT read the
engine — it is handled on the client task, whose ClientCtx holds Arc<Mutex> mirrors and no engine,
so it would have added a daemon-side shadow that restates what the client already believes and
cannot catch the engine disagreeing. The main loop's MetricsSnapshot is the one place that reads
the engine for clients.

A new ControlEvent variant, not fields on Metrics: additive variants are absorbed by the wildcard
arms every consumer has, while Metrics is destructured exhaustively by the panel and constructed by
two other sites. MetricsSnapshot is daemon-internal and Default-constructed once, so extending it
is safe — the two look alike and are not.

The panel now renders AND toggles against the reported value, falling back to its optimistic local
value only until the first report — the pattern ToggleRepeater and the QSY toggle already followed.

Tests are unit tests in server.rs because front_end_state is private; exporting it for an
integration test is the #1271 shape. The engine-truth test FAILS against a hardcoded shadow — the
design the review rejected — with "the report must follow the engine". Restored by sha256sum.

is_agc_enabled and is_notch_enabled left the reachability baseline, which is the outcome #1271
predicted when it labelled them READOUT rather than filing them with the tripwire counters.

Results: 2/2 new, daemon lib 136 pass, workspace clippy clean, REACH: PASS.

Implements: REQ-UX-02

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6
@dc0sk
dc0sk merged commit e9eadf8 into main Sep 5, 2026
4 of 5 checks passed
@dc0sk
dc0sk deleted the fix/1276-frontend-toggle-readback branch September 5, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Five front-end toggles are write-only, and two of them make a default install display the opposite of the truth

1 participant