Skip to content

fix(qso-answerer): D-CALLER-020 — partner re-transmitting own CQ no longer false-aborts QSO#73

Merged
frank001 merged 1 commit into
mainfrom
fix/d-caller-020-working-cq-false-abort
Jul 14, 2026
Merged

fix(qso-answerer): D-CALLER-020 — partner re-transmitting own CQ no longer false-aborts QSO#73
frank001 merged 1 commit into
mainfrom
fix/d-caller-020-working-cq-false-abort

Conversation

@frank001

Copy link
Copy Markdown
Owner

Summary

  • QsoAnswererService.HandleWaitReportAsync and QsoCallerService.HandleWaitRr73Async aborted the QSO the instant they decoded any message from the partner not addressed to us — including the partner simply re-transmitting their own CQ (dest == "CQ") because they hadn't decoded us yet, which is normal early in a retry sequence, not evidence they've moved on.
  • Caught in real-hardware field testing (logs/openswfz-20260714T171230Z.log, EA1FUB): one retry fired correctly, then the partner's own CQ re-decode ended the QSO outright ("timing was impeccable, but the application just decided to end the whole qso").
  • Fix: exclude dest.Equals("CQ", OrdinalIgnoreCase) from the abort condition in both services; falls through to the existing "no matching message" retry/watchdog backstop instead. Genuine third-party addressing (a different real callsign) still aborts immediately, unchanged.
  • Pre-existing defect, not introduced by PR fix(qso-answerer): D-CALLER-018/016/019 — abort hard-stop, faster re-engage, dblclick cleanup #72 — confirmed present on main before that merge.

Test plan

  • New unit test in QsoAnswererServiceTests: partner re-transmitting own CQ in WaitReport does not abort; drives the same retry-exhaustion cycle pattern as the existing silence-based test to confirm the retry backstop still eventually gives up (AC-1, AC-5).
  • New unit test in QsoCallerServiceTests: same for WaitRr73 (AC-3).
  • Existing 6.6 (WaitReport_PartnerWorksOther_AbortsToIdle) and 5.9 (WaitRr73_PartnerWorkingOther_Aborts) genuine-third-party tests pass unmodified (AC-2, AC-4).
  • Existing WaitReport_NoResponse_RetriesThenAborts (line 460) retry-exhaustion test passes unmodified (AC-5).
  • dotnet build OpenWSFZ.slnx -c Release — 0 warnings, 0 errors (AC-6).
  • Full suite green, no regressions (AC-7).
  • Live verification (dev-task §5): added qa/d-caller-020-working-cq-false-abort-verify/, a standalone tool that genuinely synthesises + decodes real FT8 audio (qa/rr-study/synth_wav.py + the real Ft8Decoder/libft8 P/Invoke) and drives the real, unmodified QsoAnswererService/QsoCallerService with it — stronger than unit tests, which never round-trip through real FT8 packing. Verified PASS with the fix; a deliberate control run with the fix reverted reproduces the exact regression on both services (see live-reports/).

🤖 Generated with Claude Code

…onger false-aborts QSO

QsoAnswererService.HandleWaitReportAsync and QsoCallerService.HandleWaitRr73Async treated any
message from the partner not addressed to us as "partner is working another station" and aborted
immediately — including the partner simply re-transmitting their own CQ because they hadn't
decoded us yet, which is the normal, expected state early in a retry sequence, not evidence they
moved on. Real-hardware field test (logs/openswfz-20260714T171230Z.log, EA1FUB) caught this: one
retry fired correctly, then the partner's own CQ decode ended the QSO outright.

Narrow the abort condition to exclude dest == "CQ" (case-insensitive) in both services, falling
through instead to the existing "no matching message" retry/watchdog backstop — the same one that
already handles genuine silence. Genuine third-party addressing (dest is a different real
callsign) still aborts immediately, unchanged.

Added unit tests to both service test suites exercising the exact retry-exhaustion cycle pattern
with the partner's own CQ substituted for silence, proving both the no-abort behaviour and that
the retry backstop still eventually gives up. Existing 6.6/5.9 genuine-third-party tests and the
460 retry-exhaustion test pass unmodified.

Per dev-task §5, also added qa/d-caller-020-working-cq-false-abort-verify/ — a standalone tool
that synthesises and decodes real FT8 audio (qa/rr-study/synth_wav.py + the real Ft8Decoder/libft8
P/Invoke) and drives the real, unmodified QsoAnswererService/QsoCallerService with it, closing the
gap unit tests alone left (hand-authored DecodeResult strings never actually round-tripped through
real FT8 packing). Verified PASS with the fix, and a deliberate control run with the fix reverted
reproduces the exact regression on both services (see live-reports/).

Full suite 1145/1145, dotnet build -c Release 0 warnings/0 errors.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@frank001

Copy link
Copy Markdown
Owner Author

QA sign-off. Reviewed the actual diff, not just the commit message.

Fix matches the dev-task exactly — narrows the abort condition in both QsoAnswererService.HandleWaitReportAsync and QsoCallerService.HandleWaitRr73Async to exclude dest == "CQ", falling through to the existing retry/watchdog backstop instead of a same-cycle snap abort. Minimal, symmetric, no scope creep.

Tests: new coverage drives the full retry-exhaustion cycle proving both halves — partner's own CQ no longer aborts, and the existing retry-count backstop still eventually gives up on genuine silence (no infinite-retry regression introduced). Existing 6.6/5.9 genuine-third-party tests pass unmodified, confirming the correct-abort case is untouched.

Live verification exceeds the dev-task's own bar — the harness re-synthesises and re-decodes real FT8 audio through the actual Ft8Decoder/libft8 P/Invoke rather than replaying a log or hand-authoring DecodeResult strings, then drives the real production service classes. Includes a genuine control run (fix reverted → FAIL on both services, reproducing the exact field regression; fix reapplied → PASS) — a real regression guard, not a rubber stamp.

CI green on all three platforms, mergeable. Approving to merge as-is.

@frank001 frank001 merged commit 6d914e5 into main Jul 14, 2026
14 checks passed
@frank001 frank001 deleted the fix/d-caller-020-working-cq-false-abort branch July 14, 2026 19:31
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.

1 participant