Proof Portal
Omarchy
ProbeLabs65 findings · 95 requirementsA proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.
Media keys start an idle Spotify instead of the last-used player: selectActivePlayer() falls back to whoever holds a corked PipeWire stream
Reported by bldeden · filed Sep 21, 2026 · open at source
At a glance
- 1 · What is claimed?
- Only the report text below — no validator has restated it as checkable claims yet.
- 2 · What does it affect?
- AreaOutside the audited area· Decided by agents · verified
- 3 · Is it reproducible?
- No validation has been run yet.
- 4 · What remains unclear?
- Not validated yet: no claims, no reproducer.
- 5 · What happens next?
- Decided by agents and verified by a second, independent agent — see the decision.
Original report
What happens
With two MPRIS players open and both paused (Chromium/YouTube and Spotify), pressing XF86AudioPlay starts Spotify, not the player that was last used. It reproduces reliably after YouTube has been paused for a while.
What I expected
Play/pause resumes whatever I last paused.
Why
shell/plugins/services/media/Service.qml → selectActivePlayer() (4.0.3, line 231):
return oldestPlayingPlayer(true) || oldestPlayingPlayer(false) || streamPreferred || streamCandidate || preferred || trackPlayer || ...When nothing is playing, both oldestPlayingPlayer() calls return null and selection falls to streamCandidate — the first player with a matching PipeWire playback stream (playerHasPlaybackStream, line 213). That test only asks whether a stream node exists, not whether it is running:
- Spotify keeps a corked sink-input open indefinitely while paused, so it always has a stream.
- Chromium tears its streams down after a media session goes idle, and drops its MPRIS object entirely when a tab's media session ends.
So once YouTube has been paused long enough, Spotify is the only player holding a stream and wins every time.
$ pactl list sink-inputs | grep -E "Sink Input|application.name|Corked"
Sink Input #269
Corked: yes
application.name = "Spotify"Note streamCandidate is evaluated before preferred, so even an explicit preference set by the previous media-key press loses to an unrelated app that merely holds an idle stream.
Two things make it stick:
preferredPlayerKeyis the dbus name (org.mpris.MediaPlayer2.chromium.instance1451). Chromium recreates its MPRIS object per media session, andsyncPlayingOrder()clears the preference when the object disappears (line 125) — so the preference is lost exactly in the case that needs it.preferredPlayerKeyandplayerStartedAtare in-memory only. Afteromarchy restart shell(or an update) there is no history at all, and selection drops straight to the stream-based fallback.
Steps to reproduce
- Open Spotify, play a track, pause it. Leave it running.
- Play a YouTube video in Chromium, then pause it.
- Wait until Chromium has released its playback streams (a few minutes; YouTube's "Video paused. Continue watching?" prompt is a good marker).
- Press the play/pause media key → Spotify starts.
Also reproduces immediately after omarchy restart shell with both players paused.
Suggested fix
Rank a recency signal above the stream-existence fallback, and make it durable:
- Track "last actually playing" per player and prefer that over
streamCandidatewhen nothing is playing. - Key the preference/recency by application rather than dbus name, so Chromium re-registering its MPRIS object doesn't wipe it.
- Persist it under
~/.local/state/omarchy/so a shell restart doesn't reset the decision to "whoever holds a stream" (the notifications plugin already does this for DND).
Alternatively, playerHasPlaybackStream() could ignore corked streams — but PwNode doesn't expose corked state, so the recency route looks more tractable.
I have a working patch along these lines running locally in a cloned omarchy.media; happy to open a PR if the approach seems right.
System
| Omarchy | 4.0.3-1 |
| Kernel | 7.2.3-arch1-3 |
| CPU | AMD Ryzen 7 9800X3D |
| GPU | AMD Radeon RX 9070 XT (Navi 48) |
| Hyprland | 0.56.2 |
| Quickshell | 0.3.1-1 |
| Chromium | 152.0.7977.82-1 |
| Spotify | 1:1.2.96.518-2 |
View raw text
### What happens With two MPRIS players open and both paused (Chromium/YouTube and Spotify), pressing `XF86AudioPlay` starts **Spotify**, not the player that was last used. It reproduces reliably after YouTube has been paused for a while. ### What I expected Play/pause resumes whatever I last paused. ### Why `shell/plugins/services/media/Service.qml` → `selectActivePlayer()` (4.0.3, line 231): ```qml return oldestPlayingPlayer(true) || oldestPlayingPlayer(false) || streamPreferred || streamCandidate || preferred || trackPlayer || ... ``` When nothing is playing, both `oldestPlayingPlayer()` calls return null and selection falls to `streamCandidate` — the first player with a matching PipeWire playback stream (`playerHasPlaybackStream`, line 213). That test only asks whether a stream node *exists*, not whether it is running: * **Spotify keeps a corked sink-input open indefinitely while paused**, so it always has a stream. * **Chromium tears its streams down** after a media session goes idle, and drops its MPRIS object entirely when a tab's media session ends. So once YouTube has been paused long enough, Spotify is the only player holding a stream and wins every time. ``` $ pactl list sink-inputs | grep -E "Sink Input|application.name|Corked" Sink Input #269 Corked: yes application.name = "Spotify" ``` Note `streamCandidate` is evaluated **before** `preferred`, so even an explicit preference set by the previous media-key press loses to an unrelated app that merely holds an idle stream. Two things make it stick: 1. `preferredPlayerKey` is the dbus name (`org.mpris.MediaPlayer2.chromium.instance1451`). Chromium recreates its MPRIS object per media session, and `syncPlayingOrder()` clears the preference when the object disappears (line 125) — so the preference is lost exactly in the case that needs it. 2. `preferredPlayerKey` and `playerStartedAt` are in-memory only. After `omarchy restart shell` (or an update) there is no history at all, and selection drops straight to the stream-based fallback. ### Steps to reproduce 1. Open Spotify, play a track, pause it. Leave it running. 2. Play a YouTube video in Chromium, then pause it. 3. Wait until Chromium has released its playback streams (a few minutes; YouTube's "Video paused. Continue watching?" prompt is a good marker). 4. Press the play/pause media key → Spotify starts. Also reproduces immediately after `omarchy restart shell` with both players paused. ### Suggested fix Rank a recency signal above the stream-existence fallback, and make it durable: * Track "last actually playing" per player and prefer that over `streamCandidate` when nothing is playing. * Key the preference/recency by application rather than dbus name, so Chromium re-registering its MPRIS object doesn't wipe it. * Persist it under `~/.local/state/omarchy/` so a shell restart doesn't reset the decision to "whoever holds a stream" (the notifications plugin already does this for DND). Alternatively, `playerHasPlaybackStream()` could ignore corked streams — but PwNode doesn't expose corked state, so the recency route looks more tractable. I have a working patch along these lines running locally in a cloned `omarchy.media`; happy to open a PR if the approach seems right. ### System | | | |---|---| | Omarchy | 4.0.3-1 | | Kernel | 7.2.3-arch1-3 | | CPU | AMD Ryzen 7 9800X3D | | GPU | AMD Radeon RX 9070 XT (Navi 48) | | Hyprland | 0.56.2 | | Quickshell | 0.3.1-1 | | Chromium | 152.0.7977.82-1 | | Spotify | 1:1.2.96.518-2 |
Validation
Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.
Linked
Not linked to any known issue, other report or change yet.
Decision
Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.
- Out of scopeDecided by agents · verified · 2026-10-09 16:57 UTC