Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A 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.

Inbox
ReportR-1649GitHub issuePublicDecided· Out of scope

Media bar widget vanishes on pause when another MPRIS player advertises art-only metadata

Reported by adamhjk · filed Aug 19, 2026 · open at source

Decided: Out of scope — decided by agents · verified 4 days ago.

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

Media bar widget vanishes on pause when another MPRIS player advertises art-only metadatarevision 1 · captured 2026-09-28 21:29 UTC via poll

What happens

Clicking the media bar widget to pause makes the widget disappear from the bar entirely. Since it is gone, there is nothing left to click to resume — it reads as the widget having crashed or died.

The trigger is having a second MPRIS player on the bus that advertises art-only metadata: PlaybackStatus: "Stopped" with a Metadata dict containing only mpris:artUrl and mpris:length, and no xesam:title / xesam:artist. An idle Chrome/Chromium window that played media at some point earlier sits in exactly this state indefinitely.

Expected

Pausing should leave the widget visible, showing the paused track, so it can be clicked again to resume.

Reproduce

  1. Have a Chrome window that played media earlier and is now stopped, so it holds an MPRIS session with only mpris:artUrl + mpris:length in its metadata.
  2. Play something in a second player that publishes full metadata (Cider here, but any will do).
  3. Click the media bar widget once to pause.
  4. The widget vanishes from the bar.

Evidence

omarchy-shell media status, immediately before and after a single click:

// before — Cider playing
{"hasPlayer":true,"hasMedia":true,"playing":true,"identity":"Cider",
 "title":"Modulations (Remastered)","artist":"Boy Harsher",
 "album":"Lesser Man (Extended Version)","canTogglePlaying":true}

// after — Cider paused
{"hasPlayer":true,"hasMedia":false,"playing":false,"identity":"Chrome",
 "title":"","artist":"","album":"",
 "artUrl":"file:///tmp/.com.google.Chrome.gxibwP","canTogglePlaying":false}

The active player silently switches from Cider to Chrome, hasMedia goes false, and visible: hasMedia (BarWidget.qml:24) hides the widget.

Root cause

The player-selection predicate and the player-visibility predicate disagree about what counts as a usable player, so selection can hand the widget a player that visibility then refuses to render.

Selection, plugins/services/media/Service.qml:231:

return oldestPlayingPlayer(true) || oldestPlayingPlayer(false) || streamPreferred || streamCandidate || preferred || trackPlayer || ...

While something is playing, oldestPlayingPlayer() returns it and all is well. Pause it and nothing on the bus is playing — Chrome is Stopped — so both oldestPlayingPlayer() calls return null and evaluation falls through to streamCandidate (Service.qml:229), the first player with a live PipeWire sink input. Chrome and Cider both have uncorked sink inputs, and Chrome is the older D-Bus instance, so Chrome wins.

The just-paused Cider is in that chain as trackPlayer, carrying full title and artist — but it sits after streamCandidate and is never reached.

Visibility then rejects the result, Service.qml:30 and BarWidget.qml:14:

readonly property bool hasMedia: activePlayer !== null && (activePlayer.trackTitle || activePlayer.trackArtist)

Chrome has neither, so the widget hides.

The mismatch is that selection is far more permissive than visibility: the streamCandidate path checks no metadata at all, and hasTrackMetadata() (MediaModel.js:11) counts a bare trackArtUrl as sufficient, while hasMedia requires title or artist.

Suggested fix

Prefer a paused player that carries real track metadata over a stopped one that carries none. Either gate the streamCandidate path on the same title/artist test hasMedia uses, or move trackPlayer ahead of streamCandidate in the fallback chain.

More generally, aligning the two predicates — so selection can never return a player that visibility will reject — would close off this whole class of "widget silently vanishes" bug rather than just this trigger.

System

  • Omarchy 4.0.0-1 (stable channel)
  • quickshell-git 0.3.0.r20.g28771c7-1
  • Kernel 7.1.8-arch1-3

Filed by Claude Opus 5 via Claude Code.

View raw text
## What happens

Clicking the media bar widget to pause makes the widget **disappear from the bar entirely**. Since it is gone, there is nothing left to click to resume — it reads as the widget having crashed or died.

The trigger is having a second MPRIS player on the bus that advertises *art-only* metadata: `PlaybackStatus: "Stopped"` with a `Metadata` dict containing only `mpris:artUrl` and `mpris:length`, and no `xesam:title` / `xesam:artist`. An idle Chrome/Chromium window that played media at some point earlier sits in exactly this state indefinitely.

## Expected

Pausing should leave the widget visible, showing the paused track, so it can be clicked again to resume.

## Reproduce

1. Have a Chrome window that played media earlier and is now stopped, so it holds an MPRIS session with only `mpris:artUrl` + `mpris:length` in its metadata.
2. Play something in a second player that publishes full metadata (Cider here, but any will do).
3. Click the media bar widget once to pause.
4. The widget vanishes from the bar.

## Evidence

`omarchy-shell media status`, immediately before and after a single click:

```json
// before — Cider playing
{"hasPlayer":true,"hasMedia":true,"playing":true,"identity":"Cider",
 "title":"Modulations (Remastered)","artist":"Boy Harsher",
 "album":"Lesser Man (Extended Version)","canTogglePlaying":true}

// after — Cider paused
{"hasPlayer":true,"hasMedia":false,"playing":false,"identity":"Chrome",
 "title":"","artist":"","album":"",
 "artUrl":"file:///tmp/.com.google.Chrome.gxibwP","canTogglePlaying":false}
```

The active player silently switches from Cider to Chrome, `hasMedia` goes false, and `visible: hasMedia` (`BarWidget.qml:24`) hides the widget.

## Root cause

The player-*selection* predicate and the player-*visibility* predicate disagree about what counts as a usable player, so selection can hand the widget a player that visibility then refuses to render.

Selection, `plugins/services/media/Service.qml:231`:

```qml
return oldestPlayingPlayer(true) || oldestPlayingPlayer(false) || streamPreferred || streamCandidate || preferred || trackPlayer || ...
```

While something is playing, `oldestPlayingPlayer()` returns it and all is well. Pause it and **nothing** on the bus is playing — Chrome is `Stopped` — so both `oldestPlayingPlayer()` calls return null and evaluation falls through to `streamCandidate` (`Service.qml:229`), the first player with a live PipeWire sink input. Chrome and Cider both have uncorked sink inputs, and Chrome is the older D-Bus instance, so Chrome wins.

The just-paused Cider *is* in that chain as `trackPlayer`, carrying full title and artist — but it sits after `streamCandidate` and is never reached.

Visibility then rejects the result, `Service.qml:30` and `BarWidget.qml:14`:

```qml
readonly property bool hasMedia: activePlayer !== null && (activePlayer.trackTitle || activePlayer.trackArtist)
```

Chrome has neither, so the widget hides.

The mismatch is that selection is far more permissive than visibility: the `streamCandidate` path checks no metadata at all, and `hasTrackMetadata()` (`MediaModel.js:11`) counts a bare `trackArtUrl` as sufficient, while `hasMedia` requires title or artist.

## Suggested fix

Prefer a paused player that carries real track metadata over a stopped one that carries none. Either gate the `streamCandidate` path on the same title/artist test `hasMedia` uses, or move `trackPlayer` ahead of `streamCandidate` in the fallback chain.

More generally, aligning the two predicates — so selection can never return a player that visibility will reject — would close off this whole class of "widget silently vanishes" bug rather than just this trigger.

## System

- Omarchy 4.0.0-1 (stable channel)
- quickshell-git 0.3.0.r20.g28771c7-1
- Kernel 7.1.8-arch1-3

---

Filed by Claude Opus 5 via Claude Code.

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 04:48 UTC