Skip to content

aetherd: type panadapter/waterfall lifecycle and display controls - #6628

Merged
ten9876 merged 6 commits into
aethersdr:mainfrom
rfoust:codex/aetherd-m4-pan-display
Oct 21, 2026
Merged

ten9876 merged 6 commits into
aethersdr:mainfrom
rfoust:codex/aetherd-m4-pan-display

Conversation

@rfoust

@rfoust rfoust commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Closes #6623. Refs #3849 and #5262.

Moves desktop panadapter/waterfall lifecycle and existing display controls behind typed backend requests. This is a standalone M4 cleanup based on main, including the merged #6619 utilities, #6621 slice/split routes and #6593 DAX fix. It does not complete M4 or add daemon protocol methods, grants, transmit authority, dependencies, or new rendering/UI features.

  • Covers creation, paired pan/waterfall removal, inventory replay, startup/layout creation, geometry/band, dBm range, pixel/analyzer dimensions, averaging/FPS, RF gain, WNB, loops, noise-floor controls, and waterfall rate/levels/auto-black.
  • Flex owns bounded command encoding; native backends declare only the controls they implement. Native synchronous creation/refusal and asynchronous Demo publication are preserved.
  • Answered creation correlates the original session's actual returned ID with its new owned model. Unknown outcomes retain bounded reservations without automatic retry or orphan deletion. Waterfall status can arrive later.
  • Original model/session/ownership/pairing and per-field intent fences protect delayed initialization, compatibility fallback, removal and profile-load batches. Newer operator intent wins, including edits to individual waterfall fields after an aggregate defaults request.
  • Bridge lifecycle receipts describe admission, not observed completion. Ordinary close preserves the last pan; explicit all still respects foreign ownership and native refusal.
  • Removes 46 raw Flex command sites above the seam (171 → 125). DAX/IQ binding remains separate; the merged utility and slice/split/FDX routes are preserved.
  • Fixes the noise-floor setter keys: display pan set now sends pan_position= / pan_position_enable= as FlexLib v4.3.16 does (Panadapter.cs:530/544); noise_floor_position* remains the status key.
  • Normalizes a pan's waterfall= value to the canonical 0x%08x ID used by the inventory maps. Unprefixed digits parse as decimal, matching FlexLib StringHelper.TryParseInteger.
  • On a backend that declares its pan display controls without span (today the Demo, whose span is fixed), zoom is left alone: the −/+ pair is dimmed on every pane with the reason in its tooltip and accessible description, and keyboard, MIDI, wheel and scale-drag zoom send no span request the backend would refuse. A drag still moves the center. A pane showing KiwiSDR keeps its live zoom; disconnect restores the pair.

Protocol encoding was checked against the official published FlexLib v4.3.16 archive, including pan_position / pan_position_enable, creation and paired teardown. No proprietary-binary decompilation was used.

Constitution principle honored

Principle II: radio-authoritative observation, not request admission, establishes live resources. Principle VII: typed values, IDs and replies are validated at ingress. Principles IV and VIII: clean-room protocol authority and behavior-backed evidence.

Test plan

Three-platform qualification revision: a85749d26f27a89c68810c841f1ce7450fbdb230, based on 933f2fdeeb95ef838345afb421d1dda03b8b0197.

VM payload manifests verify that complete source tree. Their dedicated checkouts use the recorded base plus the applied patch, so their runtime build identities correctly report the base as dirty.

Platform Application + aetherd Focused CTests Production Demo/MCP checks
macOS ARM64 Pass; RADE enabled, architecture verified 20/20 39/39
Linux ARM64 Pass 20/20 39/39
Windows MSVC x64 Pass 20/20 39/39

Publication head: 87501ac87210c8325f315dd72f81b3b409021029, incorporating main at 839434225e973edba35d0c5757a2d67bac5f595c (#6593). The only merge conflict was generated log source line numbers. Comparing the normalized patch before/after integration, excluding those anchors, gives identical code, tests and documentation changes. Post-integration Mac application/daemon, 21 focused tests (including the upstream DAX-holder regression) and 39 MCP checks all pass; ARM64 and RADE checks also pass. Linux/Windows were not rerun at the publication head; their results above are pinned to the three-platform revision, not represented as a final-head or RF test.

The local Windows deployment helper's archive timestamp handling was repaired and regression-tested separately; that tooling repair is not part of this product PR.

The 20 tests cover pan control/dimension/drop-notice routing, radio-owned and client display settings, inventory/status policy, pan decode/dBm/frame guards, backend affinity/family replacement, noise-floor gating, HL2 creation, Demo, read-only metadata, desktop utility controls and both merged slice lifecycle targets. New regressions are socket-free and exercise the production Flex encoder, reply/publication order, timeout reservations, requester death, ID reuse, ownership changes, history bounds, newer/reentrant intent and partial/reverse-ACK teardown.

Mutation check: disabling creation identity protection and restoring the old noise-floor key made five regression assertions fail; restored production code returned them to green. Full changed-code review and incremental security review found no remaining findings. Formal security passes were pinned to immutable pre-rebase implementation/correction revisions; integration resolutions were reviewed separately, without claiming a new formal scan of the final head.

MCP checks use the unmodified production stdio server, authenticated task-owned sockets, fresh isolated settings, DEMO-0001, AETHER_AUTOMATION_NO_TX=1 and auto-connect disabled. They exercise repeated create/close, last-pan/native refusals, display controls and readback, reflow, pop-out/redock, production layout-driven creation/settling/teardown, slice 0 tuning, blocked MOX and disconnect. Mac uses Cocoa; VM runs use Qt offscreen. These are Demo/control-path proofs, not physical-radio, RF, GPU-rendering, or full-suite/CI acceptance.

Engine-boundary, capability-record, command-plane and touchpoint ratchets, test registration, generated references, 707 Log Analyzer checks, whitespace checks and the documentation build pass. Updated user/bridge/design docs and generated source citations; no visual redesign or screenshot change.

Checklist

  • Local/native application and daemon builds and focused tests pass.
  • Physical-radio behavior tested — not performed in this RX-only Demo qualification.
  • Existing tests pass in CI — pending the PR run; full suite not run locally.
  • Commits are SSH-signed.
  • No new flat-key AppSettings persistence or credentials.
  • Clean-room implementation; no meter rendering changes.
  • User documentation updated in this PR; CHANGELOG.md untouched.
  • No reportable security finding requiring an advisory.

Generated with OpenAI Codex.

@rfoust
rfoust marked this pull request as ready for review October 20, 2026 18:58
@rfoust
rfoust requested review from a team as code owners October 20, 2026 18:58
Copilot AI balanced review requested due to automatic review settings October 20, 2026 18:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-20T19:05:30.758935Z
We couldn’t complete this review. Comment @codex review to try again.
87501ac Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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

Review: adversarial pass at head 87501ac8

Issue fit. #6623 asks for pan/waterfall lifecycle and the remaining display-control producers to move behind typed backend requests. It also asks for session/identity/pairing fences, a capacity count that includes pending creates, and truthful partial-teardown reporting. The diff does all of that. check_command_plane.py drops from 171 to 125, and I re-ran it on the merge base and the head to confirm. Engine-boundary findings are flat (101 on both sides; only one line number moved). The design holds up under attack (details below). One thing blocks merge: three already-registered tests go red on this head and pass on the merge base. PR CI doesn't run them, so CI is green, but main's unfiltered sanitizer lane will fail.

Scope

File / group What it changes Claimed? Verdict
PanDisplayRequest.h, PanLifecycleRequest.h, IRadioBackend.h, RadioCapabilities.h, per-backend capabilities() Typed request + capability declarations Yes In scope. I checked each declaration against the backend's setPan* overrides; they match.
FlexPanControls.cpp, FlexBackend.* Flex encoder for display/lifecycle Yes In scope
RadioModel*.{h,cpp}, RadioModel_PanControls.cpp Pending-create reservations, fences, migrated producers Yes In scope
MainWindow*.cpp Callers moved to typed requests; createPansSequentially moved and fenced Yes In scope
SimBackend.* Answered creation with returned ID; geometry declared as Center only Partly See nit 1: undisclosed user-visible effect on Demo zoom
PanadapterModel.{h,cpp} Revisions; waterfall= value now normalized to 0x%08x No Undisclosed behavior change, breaks a registered test (blocker 1c)
FlexPanControls.cpp:651-652 noise_floor_position= → pan_position= Mentioned as "checked", not as a change Correct per FlexLib; see nit 2
AutomationServer.cpp, bridge docs pan close now returns ok:false when nothing was requested; per-entry requested Yes, docs updated Bridge reply shape changed (public surface), but disclosed and documented. Fine.
rules.json Log-analyzer line anchors refreshed Yes (commits 3–4) Generated; fine
tests/*, tests.cmake New pan_control_routing_test, rewritten dimension/drop-notice tests Yes See nit 4 (comment placement)
tools/check_command_plane.py Baseline 115→83, 10→5, 10→1 Yes Shrink only. Correct direction, verified

The body's "full suite not run locally" is accurate, and blocker 1 is what it missed.

Blockers

1. Three registered tests pass on the merge base (83943422) and fail on this head. None of the three is in ci.yml's -R filter, so PR CI can't see them. The Saturday sanitizer lane runs ctest --test-dir build --no-tests=error with no filter (.github/workflows/sanitizers.yml:265), so main goes red after merge. I built the same targets on both trees and ran each binary:

rigctl_rf_gain_pan_test            base=0 head=1
receive_intent_routing_test        base=0 head=1
panadapter_model_rx_antenna_test   base=0 head=1

(transmit_seam_drop_notice_test and control_receive_test fail on both trees. That's pre-existing and not this PR.)

  • 1a. tests/rigctl_rf_gain_pan_test.cpp:56: StubBackend::capabilities() returns {}. Under the PR's own rule ("native backends declare only the controls they implement"), setPanRfGainFor → requestPanDisplay now refuses because no panDisplayControl is declared. The stub never sees the gain, and 8 assertions fail ([FAIL] L RF reaches the backend's pan RF gain verb …). Production backends do declare RfGain, so the fix belongs in the fixture:
        RadioCapabilities capabilities() const override
        {
            RadioCapabilities c;
            c.panDisplayControl = PanDisplayControl{{PanDisplayField::RfGain}, {}};
            return c;
        }
  • 1b. tests/receive_intent_routing_test.cpp:359: requestPanBand(QStringLiteral("receiver"), …) passes the backend-namespace pan ID. No PanadapterModel exists under that ID; the fixture never publishes one. RadioModel.cpp requestPanBand now returns on !panadapter(panId) before dispatchPanBand emits panBandAboutToDispatch, so the Kiwi handoff ordering is never exercised ([FAIL] typed Kiwi mute handoff completes before band dispatch continues, without a raw bypass). The new guard is reasonable. The test needs to publish a pan and pass its model ID. If you think the guard should allow this, say why.
  • 1c. tests/panadapter_model_rx_antenna_test.cpp:155: FAIL … expected 4200000f, got 0x4200000f. This comes from the PanadapterModel.cpp waterfall= normalization. The normalization itself is right: it matches RadioStatusOwnership::hexId() / normalizedFlexId, which key the inventory maps your new fences look up. FlexLib's StringHelper.TryParseInteger (v4.3.16, Util/StringHelper.cs:33) also reads unprefixed values as decimal, the same as your base-0 parse. But it is an undisclosed behavior change. Please update the expectation to 0x4200000f and mention the normalization in the body.

Process note: a route refactor like this one should run every tests/ target that overrides setPan* or create/removePanadapter, or that asserts display pan/panafall wire text, before opening the PR. This has broken main before (#6199).

Nits (non-blocking)

  1. Demo zoom now posts the "unsupported control" notice. SimBackend.cpp declares PanDisplayControl{{Geometry}, {Center}}. The default IRadioBackend::requestPanDisplay (IRadioBackend.h, Geometry case) refuses the whole request when bandwidthMhz is set and Bandwidth isn't declared. dispatchPanCenterBandwidth dispatches with announce=true, so commandDropped fires. Bridge evidence: fresh connect to DEMO-0001, one invoke panZoomOutBtn click:

    • head log: W aether.protocol Backend refused operation pan display (announced); status bar: "This radio doesn't support that control — nothing was sent to the radio. Further unsupported controls are logged."
    • base: no refusal and no notice (Pan range request: explicit-pan-range center 14.1 bandwidth 0.012).

    The Demo span is fixed, so the notice is arguably more honest than base's silent no-op. But it's an undisclosed user-visible change on the default first-run radio. Pick one and say so in the body: (a) gate panZoomInBtn/panZoomOutBtn on the declared Bandwidth component, as panZoomSegBtn/panZoomBandBtn already are on Demo; or (b) have dispatchPanCenterBandwidth drop undeclared geometry components instead of refusing the whole request. Separately, MainWindow.cpp:8815 labels every false from requestPanCenter as (deferred: profile load), which is now wrong for a capability refusal.

  2. FlexPanControls.cpp:651-652, pan_position= / pan_position_enable=. This is correct: FlexLib v4.3.16 Panadapter.cs:530/544 sends these keys, and status reports noise_floor_position. But it fixes a wire key that was wrong on main, and the body presents it as "checked" rather than "changed". Please add a sentence. Also, setPanNoiseFloorPosition / setPanNoiseFloorEnable have no callers outside RadioModel, so the fix is unreachable today.

  3. RadioModel_PanControls.cpp requestPanCreation. An uncertain or timed-out request keeps its QTimer (stopped, not deleteLater'd) until the session ends. It's bounded by the 64 cap, so this is cosmetic.

  4. tests/tests.cmake ~8901. The new pan_control_routing_test block is inserted between radio_model_client_state_test's comment ("RadioModel's own client-owned state on an ANAN…") and its add_executable, so that comment now sits above the wrong target. Move the new block above the comment.

  5. CI freshness. The earliest run on this head was created 2026-10-20T18:45Z; main's tip is 2026-10-21T04:28Z (#6645, #6558, #6551 landed since). None of those touch this PR's files, so I see no conflict risk. The green still describes an older merge.

What I verified vs. read

  • Built the head and the merge base (lean shared-core build) and ran 63 focused tests on the head: the 24 pan/display targets plus 39 socket-free targets that touch the migrated verbs. I A/B'd every failure against the base (blocker 1). Socket preflight: no new test opens, binds or connects a socket. pan_control_routing_test links Qt6::Network only through FlexBackend.h/RadioConnection.h; the fixture's RadioConnection is never opened.
  • Mutation: I removed + m_pendingPanCreations.size() from the capacity guard, and [FAIL] pending creates share finite backend capacity fired. Restoring the line (sha-checked) made it green again.
  • Bridge, Demo only (DEMO-0001, offscreen, isolated settings, AETHER_AUTOMATION_NO_TX=1; both instances closed afterwards):
    • create ×2 → close active → close all → recreate up to the limit (3 admitted, 4th refused: at panadapter limit (4), all four published).
    • sim disconnect straight after a create, then reconnect: no leaked reservation.
    • Receipts are now truthful. On base, pan close active on Demo pan 0 returned ok:true,"requested":true and the pan stayed. On head it returns "requested":false,"ok":false.
    • Spectrum drag gave an identical centre delta on both builds, so centre-only geometry is unaffected.
  • Read, not driven: the PanLayoutDialog path into the rewritten createPansSequentially. The dialog's buttons have no objectName or accessibleName, so the bridge can't address them (a pre-existing a11y gap, not this PR). No physical-radio or Flex-firmware behavior was exercised.
  • Attacks that held: waterfall-ID base-0 parse vs FlexLib (consistent); fallback display waterfall set gated on real firmware rejection, not local hold codes (test-pinned); the remove fence still sends panafall remove when the pan stays held; capability declarations vs backend overrides (all consistent); EB2/EB3 per-file findings diffed base→head (no growth).

Requesting changes for blocker 1 only. The three test fixes are small. The rest is solid work.

@ten9876 ten9876 self-assigned this Oct 21, 2026

@ten9876 ten9876 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: #6623's typed pan/waterfall lifecycle and display migration is implemented at head 87501ac87210c8325f315dd72f81b3b409021029. Creation admission is separated from owned publication, native versus answered creation is explicit, pending allocations consume bounded capacity, and original-session/resource/intent fences cover initialization, profile batches and fallback. Request changes: three pre-existing registered regression targets were not adapted to the new contracts and fail on this head.

Scope

File/group Change Claimed? Verdict
Request headers, IRadioBackend.h, RadioCapabilities.h, backend declarations Typed lifecycle/display requests and implemented field sets Yes In scope; capability-map documentation missing (nit)
FlexPanControls.cpp, FlexBackend.* Bounded Flex encoding, creation fallback and paired teardown Yes In scope; noise-floor key correction is supported by published FlexLib
RadioModel*.cpp/.h, PanadapterModel.* Dispatch, reservations, identity/intent fencing and normalized waterfall IDs Yes; normalization implicit In scope; migrate existing test expectations
MainWindow*, SimBackend.* Typed callers, guarded sequential layouts, answered Demo creation Yes In scope; Demo zoom refusal needs disclosure (nit)
AutomationServer.cpp, bridge/user/design docs Truthful admission receipts and lifecycle documentation Yes In scope; public receipt semantics disclosed for maintainer review
tests/*, tests.cmake, CMakeLists.txt Regression coverage and production/test registration Yes In scope; existing consumers need updates below
rules.json, check_command_plane.py Generated log anchors and shrinking command baseline Yes In scope; verified

Everything in the diff is explained by the issue. Four commits' author dates are October 20 and their subjects match the migration, aggregate-default fences and integration. No unrelated build workaround, new dependency, visual redesign, daemon method or transmit authority was found. The body accurately disclaims full-suite, physical-radio and final-head Linux/Windows qualification.

Blockers

  1. Adapt the three registered tests to the new production contracts. Independently reproduced the earlier review's failures on head; these targets are outside the frozen PR test gate and are registered for the unfiltered post-merge/full-suite lanes.

    • tests/rigctl_rf_gain_pan_test.cpp:56: the recording backend returns empty capabilities. setPanRfGainFor() now correctly requires panDisplayControl/RfGain, so eight existing assertions fail, including [FAIL] L RF reaches the backend's pan RF gain verb. Declare the implemented field in the fixture:

          RadioCapabilities capabilities() const override
          {
              RadioCapabilities caps;
              caps.panDisplayControl = PanDisplayControl{{PanDisplayField::RfGain}, {}};
              return caps;
          }
    • tests/receive_intent_routing_test.cpp:359: the band-handoff test passes the backend ID receiver without publishing a pan. The new model-identity check returns before panBandAboutToDispatch, producing [FAIL] typed Kiwi mute handoff completes before band dispatch continues, without a raw bypass. Publish a pan through the fixture's production geometry signal and address requestPanBand() using its mapped model ID; retain the ordering assertion.

    • tests/panadapter_model_rx_antenna_test.cpp:155: the expectation still requires bare 4200000f, while PanadapterModel.cpp:564 now stores canonical 0x4200000f. Observed: expected 4200000f, got 0x4200000f. Keep the normalization and update the expectation:

              EXPECT_EQ(pan.waterfallId(), QStringLiteral("0x4200000f"));

Nits — non-blocking

  • src/core/backends/RadioCapabilities.h:367: add panDisplayControl and panLifecycleControl to docs/architecture/radio-capabilities-map.md, including backend field sets and consumers. The header's ADDING A FIELD contract explicitly says to record new fields there.
  • src/core/backends/sim/SimBackend.cpp:279: Demo declares center-only geometry, while existing zoom controls submit bandwidth too. The adapter refuses the whole request, changing a silent no-op into an unsupported-control notice. Disclose this observable behavior and have Jeremy confirm whether the controls should be dimmed with a reason. The noise-floor command-key correction and waterfall-ID canonicalization should also be named explicitly in the body.
  • tests/tests.cmake:8901: the inserted pan-control target separates the ANAN client-state test from its explanatory comment. Move the pan-control block above that comment.

Verification and limits

  • Built and tested: Linux x86_64, pinned Qt 6.12, lean shared-core (AETHER_SHARED_CORE=ON, -O2 -g1) scratch builds at head and merge base 839434225e973edba35d0c5757a2d67bac5f595c. Head: 3/6 focused CTests pass; the three failures above all pass on the base (3/3). New/modified pan tests use injected transports and unopened RadioConnection objects, with no bound/listening/connected test sockets. No broad suite was run.
  • Mutation: removed + m_pendingPanCreations.size() from the creation-capacity guard in the scratch head. pan_control_routing_test failed with [FAIL] pending creates share finite backend capacity; restored the exact original source and it passed again. Scratch source is clean.
  • Bridge: drove the head through explicit socket /tmp/pr6628-review-head.sock, generic Qt theme/offscreen, isolated settings, auto-connect disabled, authenticated bridge, DEMO-0001, and AETHER_AUTOMATION_NO_TX=1. Checked connected identity, invoked panZoomOutBtn, created three pans, verified all four in get pans, rejected a fifth, removed pan 1, and requested close-all. The primary Demo pan was retained with "requested":false; the other two had "requested":true and disappeared on readback. sim disconnect also dispatched. The task-owned process was closed. Initial sandbox startup could not initialize the audio/bridge; the successful isolated run used the approved outside-sandbox execution.
  • Observed zoom change: after the zoom invocation the head log contains WRN aether.protocol: Backend refused operation pan display (announced) and Pan range request: explicit-pan-range center 14.1 bandwidth 0.012 (deferred: profile load). The latter incorrectly labels a capability refusal as profile deferral; this is part of the non-blocking disclosure/gating note above.
  • Attacks that held: creation reply/publication order, malformed IDs/values, timeout reservations, dead requesters, ID/ownership reuse, bounded retirement history, reverse/partial teardown replies, newer component edits after aggregate waterfall defaults, reentrant profile batches, and actual radio pixel versus local analyzer-point routing are exercised by the passing focused pan tests and were also traced through their production consumers.
  • Governance/static checks: command-plane counts are 171 → 125; EB2/EB3 remain at 101 findings, with only a source line number moving; touchpoint manifests check clean on both trees. Capability boolean count stays 70. Registration, frozen CI gate and whitespace checks pass. FlexLib v4.3.16's published Panadapter.cs confirms pan_position / pan_position_enable setters and noise_floor_position status parsing. No rule weakening or new TX route was found.
  • Limits: no physical radio, RF, GPU rendering, macOS/Windows builds, complete suite, or independently reproduced three-platform payload/security-scan provenance. A separate invocable automated code-review skill was unavailable; the manual pass included verification of the earlier posted review rather than adopting its conclusions. Required PR CI succeeded, but its earliest run creation was 2026-10-20T18:45:23Z, before current main's 2026-10-21T04:28:28Z tip. These pull_request runs test the merge tree frozen at creation; strict freshness is disabled, so green does not qualify today's main merge.

The identical Demo sequence also ran on merge base through /tmp/pr6628-review-base.sock, with separate isolated settings and the same TX/auto-connect restrictions; that instance was closed. Both trees publish four pans, reject a fifth and retain the native primary pan. For an explicit close-all with only pan 0 left:

base: {"ok":true,"pan":"close","requested":true,"closed":[{"panId":"0x40000000","resolved":true,"waterfallId":"0x42000000"}]}
head: {"ok":false,"pan":"close","requested":false,"closed":[{"panId":"0x40000000","requested":false,"resolved":true,"waterfallId":"0x42000000"}]}

The base zoom log contains only Pan range request: explicit-pan-range center 14.1 bandwidth 0.012; the head adds the refusal quoted above. A second head run using a fresh store and /tmp/pr6628-review-head-final.sock reproduced both differences after restoration of the capacity mutation. It too was closed.

Recommendation: Request changes for the three failing regression targets. Preserve the production identity/capability guards; update their fixtures and expectations, then rerun those targets alongside the new pan tests. Approval remains the operator's decision.

ten9876 and others added 2 commits October 21, 2026 06:23
…e VIII.

Three registered tests asserted pre-typed behavior: the RF gain stub now
declares the control it implements, the Kiwi band handoff addresses a
published model pan, and the waterfall-ID test pins FlexLib's
TryParseInteger normalization (unprefixed digits are decimal).

A backend that declares its pan display controls without span (the Demo)
cannot zoom. The -/+ pair is dimmed with the reason in its tooltip and
accessible description, and keyboard, MIDI, wheel and scale-drag zoom no
longer send a span request it would refuse; a drag still moves the
center. A pane showing KiwiSDR keeps its live pair and zoom. The pair is
re-derived on a late capability republish and once per Kiwi pane walk,
and an unchanged accessible description is not re-announced.

Retire the pan-creation deadline timer once its request is notified, keep
the new test's tests.cmake block clear of its neighbor's comment, and
label a refused pan range request as such in the debug log.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e III.

RadioCapabilities' ADDING A FIELD contract asks for each field to be set in
every backend and recorded in the capabilities map. ANAN, Icom and RTL now
declare their absent pan lifecycle explicitly, and the map lists every
backend's display fields, span component and creation shape, with the
tests that pin them.

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

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

Approving at b105ed2. Every finding from both review passes is fixed, and each fix is proven below.

Fixed

  • rigctl_rf_gain_pan_test, receive_intent_routing_test, panadapter_model_rx_antenna_test: red at 87501ac and green on merge base 8394342. Each fixture now follows the new typed contracts (d82018c), and all three pass at this head.
  • Demo zoom (maintainer decisions): a declared display record without span dims the −/+ pair with its reason. Keyboard, MIDI, wheel and scale-drag zoom send no span request; drag sends center only; Kiwi panes keep their zoom (d82018c). Bridge evidence on DEMO-0001: both buttons enabled=False with the reason as tooltip and accessible description; shortcut pan_zoom_in/out ×3 gives no refusal and no notice; drag moves center 14.1 → 14.1012; disconnect restores the live pair. Mutations: declaring Bandwidth on the Demo fails only the Demo's fixed span dims the -/+ pair; dropping the Kiwi exclusion fails the sync source check.
  • Capabilities map entry plus explicit absent panLifecycleControl on ANAN/Icom/RTL (b105ed2). Deadline timer released at notify-only sites, tests.cmake comment placement, debug label, and PR-body disclosures (noise-floor key, waterfall-ID normalization, Demo span) (d82018c). The automated pass also produced the capability-republish re-sync, a single sync per Kiwi pane walk, and unchanged accessible descriptions no longer being re-announced.

Verified: 25 focused tests plus a 38-target socket-free set. The only failures are transmit_seam_drop_notice_test and control_receive_test, which fail identically on the merge base; main's #6637 has since fixed control_receive_test. Engine-boundary, touchpoint, capability-record and command-plane outputs match 87501ac; log rules 707/707; required CI is green on this head.

Not verified: physical radio and Flex firmware cleanup, macOS/Windows locally (CI covers the builds), and the PanLayoutDialog path into createPansSequentially, whose buttons have no names for the bridge to address. On the Demo, Ctrl+wheel zoom is already clamped by the widget's own span limits, so that route is reasoned from code.

@ten9876
ten9876 merged commit 8f00c05 into aethersdr:main Oct 21, 2026
11 checks passed
jensenpat pushed a commit that referenced this pull request Oct 22, 2026
…e display replay. Principle VIII. (#6672)

## Problem and behavior

Restore the silent optional-display replay contract broken by #6628. `RadioModel::setPanWnb` and `setPanWnbLevel` dispatch stored values without reporting unsupported WNB controls. Explicit operator requests retain their existing unsupported-control notices; supported writes and capability/session fences are unchanged.

Band-stack bookmark recall also replays WNB on/off and level silently. The unwired `MainWindow::restoreBandState` helper silently replays both LevelRange and RF gain; this helper has no production caller at this head, so no live band-switch symptom is claimed for it. The operator explicitly requested the RF-gain and band-stack additions during landing. Code comments explain the replay policy, and the user guide documents unsupported WNB bookmark recall.

`announce=false` suppresses both the refusal warning and the dropped-command signal; it does not turn an unsupported request into an accepted one.

## Validation

- macOS app and `pan_display_drop_notice_test` built with Qt 6.12 and `-j22`.
- Headless focused CTest: 1/1 passed. Coverage includes Icom/HL2 WNB replay, silent unsupported LevelRange, geometry-only RF-gain replay, unchanged explicit operator notices, and supported Flex WNB/RF-gain encoding to the second pane through an unopened command sink. No socket binds, network connections or synthetic firmware peer are used by the added cases.
- Mutation: removing the WNB replay suppression fails exactly four assertions (WNB and level, Icom and HL2); restoring it passes. The operator-notice and Flex dispatch controls pass in both versions.
- Isolated offscreen demo bridge, serial `DEMO-0001`, TX disabled: recalled a bookmark storing WNB on/73, reached 14.175 MHz with filter 300–2700 Hz, retained unsupported WNB off/50, and produced no unsupported-control status notice or pan-display refusal log. The instance was closed.
- Engine boundary, touchpoint manifest, capability records, command plane, test registration, frozen CI selection, QSettings and generated-reference checks pass; merge-base/head gate output is identical. Log Analyzer references regenerated; 707 rule checks pass.
- Hosted Docs CI validates the user-guide change; no local site/PDF build. Live radio/RF and the full suite are unverified. `icom_family_test` was not executed because its connection path attempts actual UDP transport to a test address; socket-free injected coverage verifies this notice policy instead.

Refs #6667. This PR addresses its display-replay leg only.

Generated with OpenAI Codex (GPT-6 Astra)
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.

aetherd M4: type panadapter/waterfall lifecycle and display controls

3 participants