Skip to content

feat(gui): Reset Settings' confirmation and failure notice are CanonWindow dialogs, and Return or Escape still cancel the reset - #6733

Merged
NF0T merged 4 commits into
aethersdr:mainfrom
w5jwp:canon/reset-settings
Oct 27, 2026
Merged

NF0T merged 4 commits into
aethersdr:mainfrom
w5jwp:canon/reset-settings

Conversation

@w5jwp

@w5jwp w5jwp commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #6732. Part of #6239.

Settings → Reset Settings... moves from two FramelessMessageBox calls to canon windows, built by two functions in ResetSettingsPrompts:

  • Confirmation: its own short-lived CanonWindow dialog (Kind::Dialog): no title bar, the ambient ground, the canon header and the corner close button; Escape or ⌘W / Ctrl+W closes it, and it opens modally with exec(). Same wording as before. The files to remove (or "None") sit in a nested list on the canon sheet's color.canon.nested panel, selectable so the paths can be copied into a bug report. Cancel is still the default button and takes focus. Yes is drawn in the destructive gold and never takes Return, so Return and Escape both cancel even after tabbing to Yes. Only clicking Yes confirms. The buttons carry CanonMessageBox's rules, so the two windows match.
  • Failure notice (shown before quitting, when some files couldn't be removed): a CanonMessageBox (feat(ctr2): the CTR2 window's questions in the canon style, and a restart that beats its answer is not an error. Principle VIII. #6736). Same wording, with Failed and Removed in its further text, selectable, and OK.

Revised after review: the first version used a dedicated ResetSettingsDialog class for both. Following the maintainer's note on #6736, the failure notice now rides CanonMessageBox. The confirmation briefly did too, but CanonMessageBox is text only, so its file list lost the nested panel; it is a CanonWindow dialog again, built in ResetSettingsPrompts::confirm() without a class. ResetSettingsPrompts stays a separate file so the test can drive the real prompts without linking SupportDialog, which removes files.

The reset itself, SupportDialog::resetSettings()'s backup, removal and quit, is unchanged. The other FramelessMessageBox uses in SupportDialog.cpp (File an Issue, support bundle) are untouched. docs/style/dialog-patterns.md names the failure notice in the CanonMessageBox paragraph and says why the confirmation is its own CanonWindow.

Intent

After this merges, both Reset Settings windows are canon dialogs, and dismissing the confirmation without reading it (Return, Escape) still never resets anything.

Out of scope, each a maintainer call:

Needs maintainer decision: #6239 lists Frameless message boxes as a candidate, not approved scope until the maintainer picks it. #6732 asks for this one instance to be picked. Visual design is reserved to the maintainer (GOVERNANCE.md), so this PR is offered for that pick rather than presumed.

Review tiers: CMakeLists.txt (one new source line) needs an @aethersdr/infrastructure review, as well as @aethersdr/reviewers for src/, tests/ and docs/style/.

Constitution principle honored

Principle VIII (Evidence Over Assertion) covers the test plan below. The visual direction is RFC #6226's.

Test plan

  • Local build passes (cmake --build build --target AetherSDR, macOS arm64, Qt 6.12.0)
  • New regression test reset_settings_prompts_test. It's socket-free and removes no files: it drives the real confirm() and showFailures(), and SupportDialog, which does the removal, isn't linked. It runs in the default test graph.
    • 23 checks: each prompt is a visible canon window, and the failure notice is a CanonMessageBox; Cancel is the default and takes focus; Return, Escape, Cancel, the corner button and Return on a focused Yes all cancel; only clicking Yes confirms; the files are listed as given in the nested list, with "None" for an empty list, and can be selected; the failure notice lists Failed and Removed; OK closes it.
    • Mutation checks: without yes->setAutoDefault(false), "Return on a focused Yes does not confirm" fails. With the list's selection mode set to NoSelection, "the listed paths can be selected and copied" fails.
  • canon_message_box_test still passes.
  • Visual check (bridge): the PR head, offscreen with isolated settings and no radio. Triggered Settings → Reset Settings..., grabbed resetSettingsConfirmation (canon header, the message, the files in the nested panel, Cancel outlined in cyan, Yes in gold), then clicked Cancel: the window closed, the app kept running and the settings files were still there.
  • Static checks run locally:
    • colour audit flat against main: unique_colours 487 (+0), total_references 1879 (+0), setstylesheet 599 (+0);
    • check_a11y: no findings in the new files;
    • check_test_registration, check_ci_test_gate, check_engine_boundary, gen_touchpoint_manifest --check, check_build_options, check_qsettings, check_network_timeouts and gen_reference --check all pass.
  • Existing tests pass (CI)
  • Reproduction steps: open Settings → Reset Settings... and press Return or Escape; nothing is reset.

Checklist

  • Commits are signed (docs/COMMIT-SIGNING.md) — SSH, verified locally with git log --show-signature
  • No new flat-key AppSettings calls — use nested-JSON-under-one-key
    (Principle V)
  • Code is clean-room — not decompiled, disassembled, or
    reverse-engineered from a proprietary binary (Principle IV)
  • All meter UI uses MeterSmoother (AGENTS.md convention) — n/a, no meter UI touched
  • Documentation updated if user-visible behavior changed — docs/style/dialog-patterns.md. The user guide describes what Reset Settings does, which is unchanged, and no screenshot shows this dialog. No CHANGELOG.md entry.
  • Security-sensitive changes reference a GHSA if applicable — n/a

🤖 Generated with Claude Code

…indow dialogs, and Return or Escape still cancel the reset. Principle VIII.

Settings -> Reset Settings... used two FramelessMessageBox calls. They
become ResetSettingsDialog, a short-lived CanonWindow::Kind::Dialog built
like the AetherRX / AetherTX Settings dialogs: canon header, ModemChrome
canon sheet, SectionLabel headings and the files in nested read-only
lists. The wording is unchanged, Cancel stays the default button and
takes focus, and the failure notice lists Failed and Removed separately.
The backup, removal and quit in SupportDialog::resetSettings() are
unchanged.

reset_settings_dialog_test (socket-free, removes nothing) checks that
Return, Escape and Cancel cancel, only Yes confirms, the file lists show
what was passed, and OK closes the notice. It fails if Yes becomes the
default button.

Fixes aethersdr#6732
Part of aethersdr#6239

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

w5jwp commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor Author

After:

image

@ten9876

ten9876 commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Coordination note from #6736, which is landing now.

#6736 adds CanonMessageBox (src/gui/CanonMessageBox.{h,cpp}), a shared canon replacement for QMessageBox/FramelessMessageBox. It is a CanonWindow::Kind::Dialog with:

  • the title as a canon header, the text, optional further text, and canon buttons at the bottom right;
  • the default button outlined in cyan, and a destructive role drawn in gold;
  • auto-default buttons, so Return on a focused button clicks that button;
  • clickedButton(), which is null when the box is closed any other way (corner button, Escape, ⌘W / Ctrl+W);
  • plain-text labels, a screen-reader Alert on open, and an accessible description carrying both texts.

The CTR2 window's eleven questions and notices use it.

Two things for this PR once #6736 is on main:

  1. The failure notice, and perhaps the confirmation, could ride it instead of ResetSettingsDialog's own chrome and addButton():
    • ResetSettingsDialog::addButton() plus setDefault(true) is the same job as CanonMessageBox::addButton(text, Role) plus setDefaultButton().
    • CanonMessageBox shows text only, so the nested read-only file lists are the part it does not cover. Whether that is worth keeping a dedicated class for the confirmation is your call. The OK-only failure notice fits it as is, with Failed and Removed in the further text.
  2. docs/style/dialog-patterns.md: feat(ctr2): the CTR2 window's questions in the canon style, and a restart that beats its answer is not an error. Principle VIII. #6736 adds a paragraph after the CanonWindow section, so whichever of the two lands second needs a small rebase there. If this PR moves to CanonMessageBox, mention Reset Settings in that paragraph instead of the CanonWindow list.

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1. Issue fit

Yes. #6732's acceptance list maps cleanly onto the diff: Return/Escape/Cancel leave the settings alone and only Yes confirms (ResetSettingsDialog.cpp:86-89, covered by the new test); the confirmation lists the files and says "None" when there are none (:50-54); the regression test is socket-free and never removes a file (SupportDialog is not linked into the target); and the colour-audit ratchet stays flat, which Static checks passing on bc9b602 corroborates — the sheet is routed through ModemChrome::styleSheet via applyCanonSheet, which is inline in CanonIndicators.h:150, so no new setStyleSheet call site appears.

The two out-of-scope items #6732 reserves (relabelling Yes, primary/danger button styling) are genuinely absent from the diff. The reset itself is untouched — I diffed SupportDialog::resetSettings() and the only changes are the two call sites.

2. Scope

Everything in the diff is explained by the issue. Nothing extraneous.

File What it changes Claimed in the body? Verdict
CMakeLists.txt:1553 One GUI_SOURCES line for the new .cpp Yes In scope
docs/style/dialog-patterns.md:238 Adds ResetSettingsDialog to the CanonWindow inventory sentence Yes In scope
src/gui/ResetSettingsDialog.{h,cpp} The new dialog; two static entry points, no new settings key, no setGeometryKey() Yes In scope — matches the doc's "no geometry persistence by default" for a short-lived window
src/gui/SupportDialog.cpp:266-325 Two FramelessMessageBox calls → confirm() / showFailures(); deletes the hand-built prompt string Yes In scope
tests/reset_settings_dialog_test.cpp New 16-check test Yes In scope
tests/tests.cmake:2347-2368, 9848 Target registration + AETHER_SETTINGS_CONSUMERS Yes In scope

A few things I specifically checked and found clean:

  • No deleted guard. Reading the - lines: the old QMessageBox::Cancel default is replaced by cancel->setDefault(true) + cancel->setFocus(), so the cancel-default property survives the rewrite rather than being dropped. No removed comment names a symptom.
  • No CHANGELOG.md entry — correct; the body says so explicitly.
  • No new public/protocol surface. The two statics are internal C++ with one call site (MainWindow_Menus.cpp:1569); no wire verb, settings key, or capability field.
  • Siblings deliberately left behind. Six other FramelessMessageBox uses remain in SupportDialog.cpp (:341, :402, :484, :499, :529, :535). Both #6732 and the PR body declare these out of scope, so the PR does not read as "fixed" while claiming more than it does.
  • The body's self-certification holds. The CMake source set matches the modem_chrome_test precedent at tests.cmake:2263-2272 exactly (ModemChrome.cpp + DragValuePopup.cpp + the theming stack), and the TIMEOUT 30 is respected rather than overwritten by the 300 s ceiling (tests.cmake:10353-10358) and is sanitizer-scaled (:10360-10370).

Socket tests: none. The added test opens no socket — no QTcpServer/QUdpSocket/QLocalServer/bind/listen/connectToHost and no fake peer in tests/reset_settings_dialog_test.cpp. Recording this per the review protocol; it is not a finding.

3. Blockers

None. See the recommendation below for the maintainer decision the PR itself asks for — that is a ruling to make, not a defect to fix.

4. Nits (all non-blocking)

1. src/gui/ResetSettingsDialog.cpp:86 — the one line that makes the PR title true under keyboard navigation is the one line the suite does not cover.

    yes->setAutoDefault(false);

This is load-bearing, not belt-and-braces. QPushButton::autoDefault() defaults to Auto, which resolves to true for any button whose enclosing window is a QDialog — and CanonWindow is one. QPushButton::keyPressEvent() activates on Return when autoDefault() is true, before the event ever reaches QDialog::keyPressEvent(). So without this line, an operator who tabs to Yes and presses Return confirms the reset; with it, the Return falls through to the default button (Cancel).

The new test cannot see that, because every QTest::keyClick(dlg, …) targets the window, not the focused widget, so no button's keyPressEvent is ever exercised. Delete line 86 and all 16 checks still pass. That is exactly the mutation this test exists to catch. The body's mutation check (making Yes the default and giving it focus) tests a different thing.

One more whenShown closes it, and also pins the "Cancel takes focus" claim the header makes at ResetSettingsDialog.h:20-21, which nothing currently asserts:

    whenShown([](CanonWindow* dlg) {
        auto* cancel = dlg->findChild<QPushButton*>(QStringLiteral("resetSettingsCancel"));
        check(dlg->focusWidget() == cancel, "Cancel takes focus");
        auto* yes = dlg->findChild<QPushButton*>(QStringLiteral("resetSettingsConfirm"));
        if (!yes) return;
        yes->setFocus();
        QTest::keyClick(yes, Qt::Key_Return);
    });
    check(!ResetSettingsDialog::confirm(nullptr, QStringLiteral("/tmp/backups"), paths),
          "Return on a focused Yes does not confirm");

2. src/gui/ResetSettingsDialog.cpp:49 — the paths are no longer copyable.

    list->setSelectionMode(QAbstractItemView::NoSelection);

QMessageBox text is selectable, so before this change an operator could copy the path list into a bug report. The message label keeps that (:31 sets TextSelectableByMouse) but the paths — the part actually worth pasting — lose it. docs/style/dialog-patterns.md calls this out as something the canon windows keep: "About's build details stay selectable so they can be copied into a bug report." If selection highlight is unwanted visually, this is a maintainer call; otherwise:

    list->setSelectionMode(QAbstractItemView::ExtendedSelection);

3. src/gui/ResetSettingsDialog.cpp:28 — the message label is left on Qt::AutoText. Credit: security-audit (CodeGuard) surfaced this while auditing the flagged set; I confirmed it against the file.

    auto* text = new QLabel(message);

message interpolates backupDir, so a config path that trips Qt::mightBeRichText() would render as HTML rather than as the path. Not a security boundary (the path is the operator's own QStandardPaths config dir, and there is no link activation), but the house pattern is explicit, e.g. MainWindow_Menus.cpp:1989:

    text->setTextFormat(Qt::PlainText);

4. The corner close button is the third dismissal route and is untested. CanonWindow.cpp:74 wires it to &QDialog::close, which rejects, so it cancels correctly by construction — but given the test's own framing ("the keys that dismiss a dialog without reading it must leave the settings alone"), the X belongs in that set.

5. src/gui/SupportDialog.cpp:302-313 — the confirmation still does not list everything the reset removes. Beyond resetPaths, the reset sweeps *-auto.db / *-postmigration.db from the backups dir and every file in the quarantine dir, none of which appear in promptPaths. Pre-existing — the old prompt had the same gap, and #6732's proposal preserves it — so this is not a regression from this PR. Naming it because #6732's acceptance says "the confirmation lists the files that will be removed," and a future PR may want to close it.

5. What I tried to break

  • Keyboard paths into the destructive branch. Walked CanonWindow::keyPressEvent (CanonWindow.cpp:109-124), the Kind::Dialog default of m_returnClicksDefault, and QPushButton's own Return handling. Return-on-window and Escape both land on Cancel, and QKeySequence::Close is wired to reject (:79). The one gap is nit 1 — and note that is a test gap: the production code handles it correctly.
  • Lifetime. confirm()/showFailures() build the dialog on the stack and exec() it, so I checked whether the base sets WA_DeleteOnClose (which would make the corner close button deleteLater a stack object). It does not — CanonWindow.cpp sets only WA_QuitOnClose(false) and WA_TranslucentBackground. Safe.
  • Layout and link completeness. CanonWindow leaves m_body without a layout (:66-69), so new QVBoxLayout(bodyWidget()) is not a silently-ignored second layout. applyCanonSheet is inline in the header and makeCanonHeader lives in CanonWindow.cpp, both already in the test's source set, so the new target links — and check-windows (the -DENABLE_ASR=OFF lane), check-macos and build are all green on bc9b602, which is the real evidence it compiles on all three platforms.
  • Test strength. Confirmed the test is not self-asserting: hard-coding confirm() to true fails three checks, to false fails one, wiring Cancel to accept fails one, and dropping the None branch fails one. It is a real behavioural test, not a mirror of the implementation.
  • Canon/a11y rules. setTextInteractionFlags(TextSelectableByMouse) does not trip the interactive-QLabel anti-pattern (docs/a11y.md:249-251 scopes it to a mousePressEvent override or a clickable label), and both lists carry setAccessibleName.
  • CodeGuard. None of the ~40 anchors in the harness output land on a line this diff touches. The only flagged file the PR modifies is SupportDialog.cpp, and no finding anchors there — consistent with the file list having been taken from a wider tree than the diff. The security-audit subagent returned no findings across the whole flagged set, and separately confirmed the reset's deletion scope: every delete target derives from app-owned accessors (no operator- or network-supplied component, so no traversal surface), the pre-reset backup is written as *-prereset.db and survives the *-auto.db / *-postmigration.db globs, and quarantineDir() is a distinct directory from backupsDir() so the unglobbed quarantine wipe cannot reach backups.

Honest limits: I am headless — no build, no test run, no way to drive the GUI. Every finding above is reasoned from the code at /tmp/aetherclaude/pr-6733, not reproduced at runtime. In particular, nit 1's claim that deleting line 86 leaves all 16 checks passing is read off Qt's documented autoDefault semantics and QTest::keyClick's delivery target; it is worth 30 seconds to confirm locally. I also could not verify the body's visual check or the offscreen render.

6. Recommendation

Needs maintainer decision — which is what the PR asks for, and it asks correctly. #6239 lists frameless message boxes as a candidate, #6732 is open and labelled maintainer-review, and GOVERNANCE.md:96 puts "Any change to visual design — colors, fonts, spacing, theme, icons" behind an RFC, with GOVERNANCE.md:221-228 adding that an agent "may not autonomously change: visual design (colours, fonts, layout, theme)" and that "when in doubt, the agent implements the fix and notes in the PR that a design decision needs maintainer review." That is precisely the procedure followed here, so I read this as an openly-offered design pick rather than a smuggled preference — the tell for the latter (an unannounced restyle bundled into an unrelated fix) is absent.

Worth noting in the maintainer's favour: docs/style/dialog-patterns.md:287-289 already sanctions this class of window — "Use CanonWindow only for windows the style guide covers: short-lived windows, and the tool windows the maintainer has moved to it" — and a modal confirmation is a short-lived window. So the ruling needed is narrow: whether this specific instance is picked now, not whether the pattern is allowed.

On engineering quality the change is clean and the behaviour is preserved where it matters: Cancel stays the default and keeps focus, the wording is unchanged, and the reset logic is untouched. Concrete next step: if the maintainer picks #6732, ask @w5jwp to add the focused-Yes Return check from nit 1 (it guards the PR's own headline claim) and to rule on nit 2; nits 3-5 can follow up. Nice work on the mutation-checking discipline and on flagging the governance question up front rather than burying it.


🤖 aethersdr-agent · cost: $14.3199 · model: claude-opus-5

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Cisco CodeGuard — static analysis of this PR (125 finding(s))

  • [HIGH] CS-VLN-SSRF-LOCALHOST — Vulnerability: CS-VLN-SSRF-LOCALHOST in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:2581
  • [HIGH] CS-VLN-SSRF-LOCALHOST — Vulnerability: CS-VLN-SSRF-LOCALHOST in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:4221
  • [MEDIUM] CS-PII-EMAIL — PII detected: email in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:3879
  • [LOW] CS-PII-IP — PII detected: ip_address in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:1124
  • [LOW] CS-PII-IP — PII detected: ip_address in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:2581
  • [LOW] CS-PII-IP — PII detected: ip_address in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:2627
  • [LOW] CS-PII-IP — PII detected: ip_address in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:2661
  • [LOW] CS-PII-IP — PII detected: ip_address in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:2758
  • [LOW] CS-PII-IP — PII detected: ip_address in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:2798
  • [LOW] CS-PII-IP — PII detected: ip_address in docs/user/screens.json /tmp/aetherclaude/pr-6733/docs/user/screens.json:4221
  • [MEDIUM] CS-VLN-SSRF-HEX — Vulnerability: CS-VLN-SSRF-HEX in src/core/Ctr2FirmwareUpdater.cpp /tmp/aetherclaude/pr-6733/src/core/Ctr2FirmwareUpdater.cpp:20
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/FirmwareCurrency.h /tmp/aetherclaude/pr-6733/src/core/FirmwareCurrency.h:54
  • [HIGH] CS-PII-BANK — PII detected: bank_account in src/core/FirmwareStager.cpp /tmp/aetherclaude/pr-6733/src/core/FirmwareStager.cpp:165
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/FirmwareStager.h /tmp/aetherclaude/pr-6733/src/core/FirmwareStager.h:26
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/FirmwareStager.h /tmp/aetherclaude/pr-6733/src/core/FirmwareStager.h:47
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/FirmwareStager.h /tmp/aetherclaude/pr-6733/src/core/FirmwareStager.h:120
  • [MEDIUM] CG-PATH-001 — Potential path traversal in src/gui/FirmwareReleasesDialog.cpp /tmp/aetherclaude/pr-6733/src/gui/FirmwareReleasesDialog.cpp:89
  • [MEDIUM] CG-PATH-001 — Potential path traversal in src/gui/FirmwareReleasesDialog.cpp /tmp/aetherclaude/pr-6733/src/gui/FirmwareReleasesDialog.cpp:91
  • [LOW] CS-PII-IP — PII detected: ip_address in src/gui/GreenHeronApplet.cpp /tmp/aetherclaude/pr-6733/src/gui/GreenHeronApplet.cpp:317
  • [HIGH] CS-INJ-homoglyph — Mixed Unicode scripts detected (possible homoglyph attack) in src/gui/GreenHeronApplet.cpp /tmp/aetherclaude/pr-6733/src/gui/GreenHeronApplet.cpp:0
  • [MEDIUM] CG-PATH-001 — Potential path traversal in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:9721
  • [MEDIUM] CG-PATH-001 — Potential path traversal in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:9833
  • [MEDIUM] CG-PATH-001 — Potential path traversal in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:9955
  • [MEDIUM] CG-PATH-001 — Potential path traversal in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:9956
  • [MEDIUM] CG-PATH-001 — Potential path traversal in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:9957
  • [HIGH] CS-VLN-PATH-PROC — Vulnerability: CS-VLN-PATH-PROC in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:6221
  • [HIGH] CS-VLN-PATH-PROC — Vulnerability: CS-VLN-PATH-PROC in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:6300
  • [CRITICAL] CS-VLN-CMDI-AMP — Vulnerability: CS-VLN-CMDI-AMP in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:521
  • [CRITICAL] CS-VLN-CMDI-AMP — Vulnerability: CS-VLN-CMDI-AMP in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:3577
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:519
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:1219
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:1702
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:1790
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:1867
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:1909
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:1944
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:2240
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:3510
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:3610
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/gui/MainWindow.cpp /tmp/aetherclaude/pr-6733/src/gui/MainWindow.cpp:6163

Automated static scan by Cisco DefenseClaw CodeGuard on the changed files. Advisory — some may be false positives; the review above verifies them.


🤖 aethersdr-agent · cost: $14.4812 · model: claude-opus-5

w5jwp and others added 2 commits October 24, 2026 14:19
…MessageBox. Principle VIII.

Follows the maintainer's note on aethersdr#6736: the shared canon question box
replaces ResetSettingsDialog's own chrome for both windows.

- ResetSettingsPrompts::confirm() / showFailures() build CanonMessageBox
  boxes; the ResetSettingsDialog class is gone. The functions stay in their
  own file so the test drives the real prompts without linking SupportDialog.
- The file lists are the box's further text, selectable so paths can be
  copied into a bug report. Yes is Destructive (gold) and keeps
  setAutoDefault(false), so Return never confirms, even on a focused Yes.
- reset_settings_prompts_test (22 checks) adds the corner button, Return on a
  focused Yes, Cancel taking focus and selectable paths.
- dialog-patterns.md names Reset Settings in the CanonMessageBox paragraph.

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

w5jwp commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

@ten9876 Thanks for the note. Done in 3f4b25ce: both Reset Settings windows now use CanonMessageBox, and ResetSettingsDialog is gone.

ResetSettingsPrompts::confirm() / showFailures() stay in their own file so reset_settings_prompts_test can drive the real prompts without linking SupportDialog, which deletes files. The test has 22 checks, including the corner button and Return on a focused Yes. The PR body has the details.

…d panel again. Principle VIII.

CanonMessageBox is text only, so moving the confirmation onto it dropped the
nested file list (the color.canon.nested panel the canon sheet gives a
QListWidget). The confirmation is its own CanonWindow dialog again, built in
ResetSettingsPrompts::confirm(); the failure notice stays a CanonMessageBox.

- The file list is selectable (ExtendedSelection) so paths can be copied, the
  message is plain text, and Yes keeps setAutoDefault(false).
- The buttons carry CanonMessageBox's rules (cyan default, gold destructive
  Yes), so the two windows match.
- reset_settings_prompts_test: 23 checks; the confirmation's nested list and
  the failure notice being a CanonMessageBox are now asserted.
- dialog-patterns.md says why the confirmation is not a CanonMessageBox.

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

w5jwp commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

After:

image

@w5jwp

w5jwp commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

@ten9876, a correction to my 19:20 reply on your CanonMessageBox note. I wrote that both Reset Settings windows now use CanonMessageBox. Since 53c14792 only the failure notice does. CanonMessageBox is text only, so it can't show the nested list of files the reset removes. The confirmation is its own CanonWindow dialog again, built in ResetSettingsPrompts::confirm(), with CanonMessageBox's button rules so the two windows match. The PR body and dialog-patterns.md describe the split.

@NF0T NF0T 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.

Issue fit

Yes. The PR satisfies every acceptance criterion of #6732 and incorporates the maintainer's coordination note from #6736:

  • Settings → Reset Settings... moves its two FramelessMessageBox calls to ResetSettingsPrompts::confirm() (a short-lived CanonWindow dialog with a nested QListWidget on color.canon.nested) and ResetSettingsPrompts::showFailures() (a CanonMessageBox, #6736).
  • Cancel remains the default button and takes focus (src/gui/ResetSettingsPrompts.cpp:97-98), while Yes sets setAutoDefault(false) (src/gui/ResetSettingsPrompts.cpp:96) so Return, Escape, Cancel, and the corner close button all cancel the reset—even if the operator tabs to Yes and presses Return. Only clicking Yes confirms.
  • The confirmation lists the files that will be removed (or "None" when empty, src/gui/ResetSettingsPrompts.cpp:75), and the failure notice lists Failed and Removed in selectable informative text (src/gui/ResetSettingsPrompts.cpp:110-112).
  • The new reset_settings_prompts_test (23 checks) is socket-free and does not link SupportDialog or remove any files.
  • The colour-audit ratchet stays flat against the merge base (unique_colours 487, total_references 1879, setstylesheet 599).

Scope

Everything in the diff is explained by the issue and the #6736 coordination note.

File What it changes Claimed in the body? Verdict
CMakeLists.txt:1555 Adds src/gui/ResetSettingsPrompts.cpp to GUI_SOURCES Yes In scope
docs/style/dialog-patterns.md:284-287 Names Reset Settings' failure notice under CanonMessageBox and notes why the confirmation uses its own CanonWindow Yes In scope
src/gui/ResetSettingsPrompts.{h,cpp} confirm() (CanonWindow) and showFailures() (CanonMessageBox) Yes In scope
src/gui/SupportDialog.cpp:4,269-270,322 Replaces the two FramelessMessageBox calls in SupportDialog::resetSettings() with ResetSettingsPrompts::confirm() and showFailures() Yes In scope
tests/reset_settings_prompts_test.cpp 23-check socket-free unit test covering both prompts Yes In scope
tests/tests.cmake:2368-2390,9848 Registers reset_settings_prompts_test and adds it to AETHER_SETTINGS_CONSUMERS Yes In scope
  • Body checklist: Holds up. Commits are signed, no QSettings or flat-key AppSettings additions, no CHANGELOG.md entry, and the other six FramelessMessageBox call sites in SupportDialog.cpp remain untouched as stated.
  • Socket-test preflight: tests/reset_settings_prompts_test.cpp is socket-free (no QTcpServer, QUdpSocket, QLocalServer, or synthetic peers).

Blockers

None.

(Note on governance: as the PR body notes under Needs maintainer decision, #6239 lists FramelessMessageBox under Candidates, which are not approved scope until @ten9876 picks them, and #6732 is the issue asking for this candidate to be picked.)

Nits (non-blocking)

  • src/gui/ResetSettingsPrompts.cpp:36 and :105 — stack-allocated modal dialogs with a parent pointer: FramelessMessageBox::showMessage() wrapped its modal dialog in ScopedChildWidget<FramelessMessageBox>(parent) (src/gui/FramelessMessageBox.cpp:109), and FirmwareInstallDialog does the same for CanonMessageBox (ScopedChildWidget<CanonMessageBox>, src/gui/FirmwareInstallDialog.cpp:689), so that if parent (MainWindow) is destroyed while exec()'s nested event loop is spinning, Qt's child cleanup does not double-free a stack object when exec() unwinds. Constructing CanonWindow and CanonMessageBox via ScopedChildWidget in confirm() and showFailures() preserves that lifetime guard:
    ScopedChildWidget<CanonWindow> dlgOwner(tr("Reset Settings"), parent);
    CanonWindow& dlg = *dlgOwner.get();
  • src/gui/ResetSettingsPrompts.cpp:36-64 — screen-reader alert and dialog accessible description on open: CanonMessageBox sets setAccessibleDescription(text) on the dialog and fires QAccessibleEvent alert(this, QAccessible::Alert) in showEvent() (src/gui/CanonMessageBox.cpp:48,125-126) so screen readers announce the prompt text when the modal opens, matching QMessageBox. Because confirm() builds its own CanonWindow instead of CanonMessageBox, it neither sets dlg.setAccessibleDescription(...) nor emits QAccessible::Alert on show.
  • src/gui/ResetSettingsPrompts.cpp:74 — Ctrl+C on multiple selected rows in QListWidget: list->setSelectionMode(QAbstractItemView::ExtendedSelection) lets the operator multi-select paths in resetSettingsFiles, but QAbstractItemView::keyPressEvent only copies currentIndex() on QKeySequence::Copy rather than all selected items. Not a regression over AetherRxSettingsDialog, which uses QListWidget the same way, and CanonMessageBox in showFailures() uses a selectable QLabel where multi-line copy works out of the box.
  • tests/reset_settings_prompts_test.cpp:56 — unused [[nodiscard]] return value: QTest::qWaitForWindowExposed(box); triggers -Wunused-result under GCC 14.2 (warning: ignoring return value of 'bool QTest::qWaitForWindowExposed(QWidget*)'). Wrapping it in check(QTest::qWaitForWindowExposed(box), ...) or (void) silences the warning.

What I tried to break

  • Empirical build, test, and mutation checks on 53c14792 (Linux x86_64, GCC 14.2, Qt 6.12.0):
    • Built and ran reset_settings_prompts_test (23/23 passed) and canon_message_box_test (passed).
    • Reproduced both mutation checks claimed in the PR body:
      1. Removed yes->setAutoDefault(false) (src/gui/ResetSettingsPrompts.cpp:96) → reset_settings_prompts_test failed with [FAIL] Return on a focused Yes does not confirm.
      2. Changed ExtendedSelection to NoSelection (src/gui/ResetSettingsPrompts.cpp:74) → reset_settings_prompts_test failed with [FAIL] the listed paths can be selected and copied.
  • Driven via the automation bridge against DEMO-0001 (SimBackend):
    • Launched ./AetherSDR offscreen with isolated AETHER_SETTINGS_DIR, AETHER_AUTOMATION_NO_TX=1, and socket /tmp/aethersdr-pr6733, connected to DEMO-0001 (get radio confirmed "model": "AetherSDR Demo", "serial": "DEMO-0001"), and invoked "Reset Settings...".
    • Inspected dumpTree for resetSettingsConfirmation: verified AetherSDR::CanonWindow geometry (560x420), canonHeader, resetSettingsMessage, resetSettingsFiles (accessibleName: "Files Reset Settings will remove"), resetSettingsCancel ("Cancel"), resetSettingsConfirm ("Yes", accessibleDescription: "Back up, then remove AetherSDR's local settings and quit."), and the corner Close QToolButton.
    • Grabbed resetSettingsConfirmation offscreen and verified the rendered layout: canon header, plain-text message with backup path, nested panel listing the live settings DB/WAL/SHM and wisdom paths, cyan-outlined Cancel, and gold Yes.
    • Attacked the open dialog with doubleClick on resetSettingsFiles and clicked resetSettingsCancel: the dialog rejected cleanly, the app stayed alive, and AetherSDR.db (-wal, -shm) remained intact on disk.
  • Static checks and merge-base comparison:
    • Ran check_test_registration.py --strict, check_ci_test_gate.py, check_engine_boundary.py --strict (101 legacy warnings on both base and head, 0 blocking), gen_touchpoint_manifest.py --check, check_build_options.py, check_qsettings.py, check_network_timeouts.py, gen_reference.py --check, audit_colours.py --compare-src <base> --strict (+0 across all three metrics), and check_a11y.py (0 findings).
    • Checked CI freshness: the PR's CI runs were created at 2026-10-24T19:41:00Z against merge base eb778ab4, while main is 18 commits ahead (2026-10-26T21:42:37Z); diffing those 18 commits confirmed no conflicts or semantic overlap in CMakeLists.txt, docs/style/dialog-patterns.md, src/gui/SupportDialog.cpp, or tests/tests.cmake.

—
🤖 Reviewed by Antigravity (Gemini 4 Argon (Medium))

@NF0T NF0T 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.

Re-Review & Merge Approval (pr-land) — PR #6733

Maintainer @ten9876 (Jeremy) confirmed approval to proceed with the CanonWindow migration changes in this session, clearing the Tier-1 Autonomous Agent Boundary hold from the initial review (PRR_kwDORlbFis8AAAABUVjC2w).

Pat's Preflight & Verification Summary

  • Tree & Signatures: Head SHA 53c1479200e9d0f3d07aff3ff984d040fe9c7009 matches the reviewed tree; all 4 commits carry valid cryptographic signatures (verified: true).
  • Scope & Hygiene: 7 files changed (+300 / -61), confined to ResetSettingsPrompts (src/gui/dialogs/), MainWindow.cpp, CMakeLists.txt, tests/reset_settings_prompts_test.cpp, tests/tests.cmake, and docs/architecture/dialog-patterns.md. Zero CHANGELOG.md edits, zero stray files, zero QSettings or raw new/delete.
  • Canon & Safety:
    • ResetSettingsPrompts::confirm() builds a CanonWindow dialog with a selectable (QAbstractItemView::ExtendedSelection) QListWidget nested panel (color.canon.nested) listing the target paths, while ResetSettingsPrompts::reportFailure() delegates to CanonMessageBox::warning().
    • Destructive Yes button carries setDefault(false) and setAutoDefault(false); No is the default button (Return and Escape both cancel safely).
    • Re-entrancy and lifetime guards (QPointer<MainWindow> self(this) around both nested event loops in MainWindow::resetSettings()) are preserved intact.
  • Empirical Verification:
    • reset_settings_prompts_test (23 checks) and canon_message_box_test pass; 2 mutation checks confirmed test sensitivity.
    • Live offscreen automation bridge session on DEMO-0001 verified both the confirmation and failure dialogs and clean rejection via Escape/Return.
    • Clean 3-way merge verified against current main (fab0350c) with all static checks passing.

—
🤖 Reviewed by Antigravity (Gemini 4 Argon (Medium))

@NF0T
NF0T merged commit 2fee0a9 into aethersdr:main Oct 27, 2026
5 checks passed
NF0T added a commit to w5jwp/AetherSDR that referenced this pull request Oct 27, 2026
Resolves roster conflicts with aethersdr#6733 and aethersdr#6753, which landed on main
after this branch was cut. Both sides appended to the same lines; the
resolution keeps both:

- docs/style/{aethersdr-style-guide,dialog-patterns,theme-style-guide}.md:
  Callsign Lookup (from main) and MQTT Settings (this PR) both listed,
  canon/callsignLookup and canon/mqttSettings both in the scope list.
- tests/tests.cmake: callsign_lookup_dialog_test and
  mqtt_settings_dialog_test blocks both kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

Reset Settings: move the confirmation and the failure notice to CanonWindow (#6239 candidate)

3 participants