feat(linux): add a copyable native playback diagnostics report (#406) - #613
Merged
Conversation
Debugging Linux audio from a bug report currently means guessing: the general diagnostics snapshot says nothing about which backend is playing, whether libmpv answered, or which output subsystem is in use. This adds a Linux-only "Linux playback" card on Diagnostics & support that builds a copyable report of exactly that, plus the failure kinds seen this session. The security property is safe *by construction*, not by redaction. Every field of `LinuxPlaybackDiagnosticsData` is a closed-enum value, a bool, an int, or a version string already accepted by `sanitizeVersion`. There is no field for a stream URL, a token, a header, a path, a device node name or a raw backend error, so the renderer has nothing to strip: - the output device is reported as a driver and a kind (`usb, via pipewire`), derived from the id's prefix and a fixed substring match, so a Bluetooth sink's node name — which carries the adapter's MAC — is never kept; - failures come from `SafeEventLog`, whose entries are already fixed structural labels, aggregated to kind + count. The raw engine error, the one value that can carry a tokenized URL, is not collected anywhere; - mpv properties are an allowlist of three keys, `audio-device` reported as set/not set, and any value that is not a plain short token also as "set"; - `sanitizeVersion` rejects rather than strips, because stripping the punctuation out of a smuggled header leaves the words behind. Missing information is never an error: nothing playing reports "not probed" (honestly different from "unavailable"), an un-enumerated output list omits its line, and the card still renders. The libmpv probe never creates a player — it asks a live one or gives up. Android diagnostics are untouched: this is a separate card that renders nothing off Linux. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLUbVqJ6e9qfW5GTEFm1gN
TheZupZup
marked this pull request as ready for review
September 10, 2026 17:52
Contributor
Repository integrity reviewCLEAN Previously reported repository-integrity findings are resolved. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c245220928
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…rd (#406) Addresses the review on this PR. Two of the three findings are real and both are the same mistake: a failure was being reported as a healthy value, which hides exactly what this report exists to show. - `LibmpvAvailability.unavailable` was declared and never produced. A live player that libmpv would not answer — a timeout, an uninitialised handle, a wedged backend — came back as `reachable: true` and read as `libmpv: available`. The probe now returns the three states directly: nothing playing is "not probed", an answer is "available", and a player that cannot be asked is "unavailable". - An enumeration that failed was reported as `Outputs found: 0`. A successful enumeration always carries the system default, so an empty list can only mean the backend did not answer; it now reports that, and a count is only ever a real count. The third finding said the card's action row overflows at the window's 420 px minimum with 2x text. It does not — measured there, the labels wrap and the buttons come out 170x200, with no overflow. The cramped result is still poor for the listener who most needs large text, so the actions now stack below a text-scaled threshold instead of sharing a row. Tests cover all three, and the docs gain the table of the three libmpv states and why an unknown output count is not zero. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLUbVqJ6e9qfW5GTEFm1gN
…ics' into feat/406-linux-playback-diagnostics
TheZupZup
pushed a commit
that referenced
this pull request
Sep 10, 2026
#613 landed the Linux playback diagnostics. Two collisions, one git could see and one it could not: - both branches added a row to the same support table in docs/linux-desktop.md at the same place. Both rows are kept, hotplug next to the audio-output row it extends; - #613's new `_FakeOutputService` implements `AudioOutputDeviceService`, and this branch adds `deviceChanges` to that interface, so the fake no longer satisfied it. It gets an empty stream, like the other fakes: the diagnostics collector never listens, and nothing plugs into a fake. The second one merges clean and fails to compile, which is exactly the kind of thing that turns CI red on a "trivial" merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLUbVqJ6e9qfW5GTEFm1gN
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #406.
What this is
Debugging Linux audio from a bug report currently means guessing. The general diagnostics snapshot says nothing about which backend is playing, whether libmpv answered and at what version, or which output subsystem is in use.
This adds a Linux-only Linux playback card on Settings → Diagnostics & support, with a report that answers those questions and can be pasted straight into an issue:
Architecture
lib/core/diagnostics/linux_playback_diagnostics.dartlib/core/services/linux_mpv_probe.dartlib/features/settings/diagnostics/linux_playback_diagnostics_collector.dartlib/features/settings/diagnostics/linux_playback_diagnostics_section.dartThe renderer is pure and free of I/O, like
BugReport: it takes an already-safe snapshot and returns text.Safe by construction, not by regex
The issue asked for information that is safe by construction rather than redacted afterwards, so there is no redaction pass — there is nothing in the snapshot to redact. Every field of
LinuxPlaybackDiagnosticsDatais a closed-enum value, a bool, an int, or a version string already accepted bysanitizeVersion. There is deliberately no field for a stream URL, a token, a header, a path, a device node name, or a raw backend error.Three collection choices carry that:
The output device is never named. libmpv's id is
pipewire/alsa_output.usb-Topping_D10-00.analog-stereo, and a Bluetooth sink ispulse/bluez_output.AC_12_2F_…— the adapter's MAC, plus a speaker name the listener chose. The report saysusb, via pipewire/bluetooth, via pulseinstead: the driver comes from the id's prefix, the kind from a fixed substring match, and both results are enum constants. The classified string is not kept."the useful fact for debugging is that the selected output is HDMI, not which HDMI port on whose machine".Failures come from
SafeEventLog. Those entries are already fixed structural labels (load,resolution,timeout) written byStabilityDiagnostics, which has no parameter for a raw error. The report aggregates them to kind + count, capped at 8 kinds. The raw engine error — the one value that can carry a tokenized URL — is not collected anywhere, which is why it cannot leak. No new failure sink was added.mpv properties are an allowlist, not a blocklist: only
cache-on-disk,ao,audio-devicemay appear.audio-deviceis reported asset, never by value, and any value that is not a plain short token (^[A-Za-z0-9_.+-]{1,24}$) is also reported asset. A property added to the map later is invisible until someone decides it is safe.sanitizeVersionrejects rather than strips. Stripping the punctuation out ofmpv 1.0\nAuthorization: Bearer abcleaves the words behind, which is worse than saying nothing — so a value that is not version-shaped is dropped and its line omitted. (This one is belt and braces:mpv-versionis a compile-time constant inside libmpv.)Missing information is not a failure
Every optional line is emitted only when its value is known:
libmpv: not probed, which is honestly different fromunavailable;Outputs foundline is simply absent;Backend: none (no on-device engine on this host)rather than a pretend snapshot.The probe never creates a player: it asks a live one for
mpv-versionand gives up otherwise. A diagnostics view that spun up a second libmpv handle would be the next bug report.Tests
test/core/diagnostics/linux_playback_diagnostics_test.dart(21) —sanitizeVersionaccepting an ordinary version and rejecting a URL, a header smuggled over a newline, a path, and length-capping; the property allowlist dropping unknown keys, withholdingaudio-device's value, and withholding any non-token value; driver/kind classification including a Bluetooth MAC id classified without its MAC and a URL-shaped id still landing on an enum; the report identifying the stack, degrading when values are missing, the failure cap, and a_expectNoSecretssweep over hostile input.test/features/settings/diagnostics/linux_playback_diagnostics_collector_test.dart(10) — the collector on Linux and on Android, "not probed" vs "unavailable", the selected output reduced to driver+kind with the device name absent from the rendered report, unsupported output routing, failure aggregation fromSafeEventLog(including that non-error breadcrumbs are not failures and that the log's own bound holds), the card hidden off Linux, and the card showing + copying a report the secret sweep passes.Checks
dart format --set-exit-if-changed lib test— cleanflutter analyze lib test— no issuesflutter test— full suite, 4912 tests, all passingscripts/check_secrets.sh— passedWhat needs a real Linux box
The pure parts run in
flutter test. Two things can't, because they need libmpv actually loaded — documented indocs/linux-desktop.mdunder Playback diagnostics → What needs a real Linux box:libmpv: not probed, and no sound (no player is created to answer)libmpv: availablewith a real version, output driver/kind matching the Audio output cardNot in this PR
No playback, queue, or Android diagnostics code is changed. #403 (hotplug recovery) is a separate branch; this PR only reads the output state that #402 already ships.
🤖 Generated with Claude Code
https://claude.ai/code/session_01LLUbVqJ6e9qfW5GTEFm1gN
Generated by Claude Code