Skip to content

fix(tx): setDax updates m_daxOn optimistically (#3721). Principle II.#3737

Merged
aethersdr-agent[bot] merged 1 commit into
mainfrom
aetherclaude/issue-3721
Jun 23, 2026
Merged

fix(tx): setDax updates m_daxOn optimistically (#3721). Principle II.#3737
aethersdr-agent[bot] merged 1 commit into
mainfrom
aetherclaude/issue-3721

Conversation

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Summary

Fixes #3721

What was changed

fix(tx): setDax updates m_daxOn optimistically (#3721). Principle II.

Files modified

  • src/models/TransmitModel.cpp
 src/models/TransmitModel.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

Generated by AetherClaude (automated agent for AetherSDR)


🤖 aethersdr-agent · cost: $2.6352 · model: claude-opus-4-8

TransmitModel::setDax sent the radio command but never wrote its local
m_daxOn member, so a read in the same tick reflected the change only
after the radio's dax= status echo (~80ms live). This mirrors the
sibling mic setters (setSpeechProcessorEnable, setMicLevel, setMonGainSb)
which update optimistically and emit micStateChanged(); setDax was the
odd one out. Latent today because the radio echoes dax= promptly, but a
slow or dropped echo would surface the same stale read as #3712.

Emits micStateChanged() (not phoneStateChanged() from the issue body):
the echo path sets m_daxOn under the micChanged flag and emits
micStateChanged() (TransmitModel.cpp:232), and PhoneCwApplet syncs the
DAX button in syncPhoneFromModel() (PhoneCwApplet.cpp:740), wired to
micStateChanged (:667-668). The re-emit is safe: syncPhoneFromModel()
sets m_updatingFromModel and wraps the button in QSignalBlocker, and the
toggled handler is gated by !m_updatingFromModel (:313-315).

commandReady stays unconditional, so callers that depend on the command
always reaching the radio (Ax25HfPacketDecodeDialog save/restore,
MainWindow shortcut/controller bindings) are unaffected; the optimistic
read actually improves the daxOn()-then-set call sites.

Blast radius: risk_score=0.128, 6 high-risk affected (top: MainWindow::MainWindow, Ax25HfPacketDecodeDialog::Ax25HfPacketDecodeDialog, MainWindow::registerShortcutActions). All real call sites verified non-regressing; high-risk entries are wiring/construction bridges that only instantiate TransmitModel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the change is correct, minimal, and follows the established pattern in this file.

Verified:

  • The optimistic setter mirrors the sibling mic setters exactly: same if (m_daxOn != on) change-guard plus emit micStateChanged() (cf. setMicLevel, TransmitModel.cpp:521-529).
  • The comment's claim that the radio echo supersedes the optimistic value checks out: incremental status parsing handles dax= (TransmitModel.cpp:100-102), setting micChanged, which emits micStateChanged() at line 232 — so the authoritative value will overwrite the optimistic one when it arrives.
  • PhoneCwApplet binds the DAX button's checked state to daxOn() (PhoneCwApplet.cpp:740), so this gives the toggle immediate visual feedback rather than waiting for the round-trip — exactly the intent of the optimistic-caching pattern.

No null-pointer, resource, or boundary concerns: this is a pure outbound-command setter with a value-equality guard. Scope is confined to the single stated function.

Thanks for the consistent, idiomatic fix. 👍


🤖 aethersdr-agent · cost: $3.6642 · model: claude-opus-4-8

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. The change is correct and consistent with the established pattern in this file.

Verified against the surrounding code:

  • Right signal: PhoneCwApplet.cpp:740 reads m_model->daxOn() under micStateChanged(), so emitting micStateChanged() is exactly what refreshes the DAX button.
  • Echo supersedes, as the comment claims: the radio echoes dax= in incremental status (TransmitModel.cpp:100-102micChangedmicStateChanged() at line 232), so the optimistic value self-corrects on the next status frame. No risk of a stuck stale value.
  • Right precedent: the guarded if (m_daxOn != on) update mirrors setMicLevel (line 521) rather than the unconditional setSpeechProcessor* setters — correct, since dax is echoed and those processor fields are not.

Single-file, additive, no convention or null-safety concerns. Thanks for the focused fix and the explanatory comment. 👍


🤖 aethersdr-agent · cost: $1.4295 · model: claude-opus-4-8

@jensenpat jensenpat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimistic meter update on state change before radio sends update. Found via agent automation testing.

@aethersdr-agent aethersdr-agent Bot merged commit 5440263 into main Jun 23, 2026
6 checks passed
@aethersdr-agent aethersdr-agent Bot deleted the aetherclaude/issue-3721 branch June 23, 2026 01:20
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.

DAX digital audio model can read stale: setDax() omits the optimistic local update (latent)

1 participant