fix(daemon): make the five front-end toggles readable, and stop the panel displaying their opposite (#1276) - #1284
Merged
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1276. Split out of #1271's design review.
The defect
SetNotch,SetAgc,SetCessb,SetLogbookandSetDcdSquelchwere write-only — a clientcould set them and never read them back. Verified there was no reader anywhere:
DaemonConfighas nofront-end field,
ControlEventhad no variant, the CLI has only the setters, the TUI drives theengine 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_enabledandcessb_enabledship
trueand are applied at startup (config/src/lib.rs:683-684,server.rs:182/186), while thepanel kept shadow bools initialised
false(app.rs:170-173) and never seeded them. So a defaultinstall 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_enabledshipsfalse, so its shadow was accidentally right. Two of the five invert; threeare 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.GetConfigis handled inhandle_commandon the client task, whoseClientCtxholdsArc<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
MetricsSnapshotis 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
MetricsAdditive
ControlEventvariants are absorbed by the wildcard arms every consumer has.Metricsisnot safe to extend:
connection.rs:268destructures it exhaustively, andlinksim/serve.rs:405plus a panel test construct it.
MetricsSnapshot— daemon-internal,Default-constructed in oneplace — 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
ToggleRepeaterand theQSY toggle already followed; these four now do too.
Tests
mod front_end_readback_testsinserver.rs— unit tests, becausefront_end_stateis private andexporting it for an integration test is the "public API for an instrument" shape #1271 is about.
pinned rather than asserted;
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 bysha256sum.Baseline
is_agc_enabledandis_notch_enabledleft the reachability baseline — the outcome #1271predicted when it labelled them
READOUTinstead of filing them with the tripwire counters, and thereason it labelled them rather than accepting them as instruments.
agc_gain_dbstays: this readout reports the five toggles, and the AGC's live gain is a meter — aseparate call about what the panel shows, not about closing a write-only command.
🤖 Generated with Claude Code
https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6