fix(radio): What's Playing copy and review always answer you (#1282) - #1286
Conversation
Reported by Howard Goldstein: with a station playing, "Copy What's Playing"
and "What's Playing - Review and Copy..." return to the main window having
done nothing -- no window, no copy, nothing spoken -- while with nothing
playing both speak a sensible message. Three faults behind that:
1. A *missing* track title was treated as "nothing is playing". That is the
normal state for the first seconds of a station, for a stream whose ICY
tap is refused, and for every station when track announcements are off.
The review window never opened in that case; it degraded to the
speak-only command.
2. That fallback is asynchronous. It starts a background fetch and returns,
so the palette closes with nothing on screen -- and its on_failure
swallowed the error, so nothing was spoken either. Silence, exactly as
reported. With nothing playing the same path answers immediately
("Nothing is playing."), which is why the bug looked inverted.
3. AppShellFrame._copy_to_clipboard returned None where
MainFrame._copy_to_clipboard returns a bool, and the shared radio mixin
branches on that value -- so in Quill Radio a copy that actually worked
announced "Could not copy to the clipboard."
Now: with a station on, both commands resolve the title first (announcing
"Checking what's playing...") and then finish the job. A stream that carries
no titles says so and the review window still opens naming the station -- a
listener who asked to review what is playing gets a window to arrow through,
not silence. A failed fetch is reported instead of swallowed. The copy
confirmation names what it copied. Both hosts' clipboard helpers return bool.
The command bodies live in the new ui/radio/now_playing_commands.py; the
on_resolved plumbing stays with the fetch it instruments (GATE-11 residue
rebaselined with the reasoning recorded).
Docs: CHANGELOG, release notes 1.0.0, user guide (What's playing), and the
PRD radio section, with HTML/EPUB regenerated.
Closes #1282
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Not ready to approve
copy_whats_playing can announce the “no titles” message when playback stops mid-fetch because it doesn’t re-check station state on the empty-title completion path.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR fixes a user-reported regression in the Radio “What’s Playing” copy/review commands by ensuring they resolve track titles (including via async fetch) before acting, and by aligning clipboard helper return contracts across hosts so copy success/failure is reported correctly.
Changes:
- Adds wx-free “now playing” command helpers that fetch-on-demand and always produce a user-facing outcome (copy confirmation, dialog, or spoken message).
- Threads an
on_resolvedUI-thread callback through the async title-fetch plumbing so waiting commands reliably continue on both success and failure paths. - Updates/extends unit tests and user-facing docs/release notes/changelog to reflect the new behavior and wording.
File summaries
| File | Description |
|---|---|
| tests/unit/ui/test_radio_whats_playing_commands.py | New unit tests covering copy/review behavior across cached, fetched, no-title, and nothing-playing scenarios. |
| tests/unit/ui/test_radio_now_playing_wiring.py | Updates expected copy confirmation and “nothing playing” wording for wiring-level tests. |
| quill/ui/radio/now_playing_commands.py | New wx-free helper functions for resolving/copying/showing now-playing details with consistent user feedback. |
| quill/ui/main_frame_radio.py | Adds on_resolved callback plumbing to async title fetch + replaces silent failure lambdas. |
| quill/ui/app_shell.py | Makes _copy_to_clipboard return bool to match MainFrame contract and avoid false failure announcements. |
| quill/tools/module_size_budgets.json | Rebaselines size budget entries to account for the required wiring/plumbing growth. |
| docs/user guide/userguide.md | Documents the improved “Copying and reviewing what’s playing” behavior and messaging. |
| docs/user guide/userguide.html | Regenerated HTML user guide reflecting the new paragraph. |
| docs/release notes/release1.0.0.md | Adds a release-note bullet describing the fix and user-visible outcomes. |
| docs/release notes/release1.0.0.html | Regenerated HTML release notes reflecting the new bullet. |
| docs/Product Requirement Documents and Specifications/QUILL-PRD.md | Updates PRD radio section to codify the fixed behavior and contracts. |
| docs/Product Requirement Documents and Specifications/QUILL-PRD.html | Regenerated HTML PRD reflecting the new section. |
| CHANGELOG.md | Adds a changelog entry describing the bug and the behavioral fixes. |
Review details
- Files reviewed: 13/16 changed files
- Comments generated: 1
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| def _copy(text: str) -> None: | ||
| if not text: | ||
| host._announce(NO_TITLE_MESSAGE) | ||
| return |
Closes #1282.
Howard reported that with a station playing, Copy What's Playing and What's Playing - Review and Copy... "do nothing except return to the main window" — while with nothing playing, both speak a sensible message. That inversion is the tell: the code was answering correctly only on the path where it never got as far as the real work.
Three faults
on_failure=lambda *_a: Noneswallowed any error, so nothing was ever spoken either. With nothing playing, the same path answers immediately ("Nothing is playing."), which is exactly why the bug looked backwards.AppShellFrame._copy_to_clipboardreturnedNonewhereMainFrame._copy_to_clipboardreturns abool, and the shared radio mixin branches on that value — so in Quill Radio (Howard's app) a copy that actually worked announced "Could not copy to the clipboard."The fix
With a station on, both commands now resolve the title first — announcing "Checking what's playing..." so the command never looks dead — and then finish the job.
_radio_fetch_track_titlegained anon_resolvedcallback that fires on the UI thread for success and failure, so a waiting command always gets its turn; the two swallow-everythingon_failurelambdas are gone. A stream that carries no titles says so, and the review window still opens naming the station — someone who asked to review what is playing should get a window to arrow through, not a one-shot announcement. The copy confirmation now names what it copied. Both hosts' clipboard helpers returnbool.Tests
9 new tests against the extracted, wx-free command helpers: cached title copies; a missing title is fetched and then copied rather than claiming nothing is playing; a title-less stream says so; a clipboard failure is reported; nothing playing says so; and the review window opens with the track, opens after fetching a missing title, opens naming the station when the stream has no titles, and stays closed with nothing playing. Two existing wiring tests were updated to the new wording (with the reason recorded inline).
pytest tests/unit/ui -k radio→ 153 passed. Ruff, mypy, and GATE-11 green.Docs
CHANGELOG, release notes 1.0.0, user guide (a new "Copying and reviewing what's playing" paragraph), and the PRD radio section, with HTML/EPUB regenerated.
🤖 Generated with Claude Code