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-1315GitHub issuePublicDecided· Out of scope

omarchy.audio: bar shows the muted glyph for an unmuted Bluetooth sink connected after shell start, and never shows muted for headphone outputs

Reported by kaizen-hryy · filed Aug 28, 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

omarchy.audio: bar shows the muted glyph for an unmuted Bluetooth sink connected after shell start, and never shows muted for headphone outputsrevision 1 · captured 2026-09-28 21:24 UTC via poll

System details

  • Omarchy 4.0.1-1
  • HUAWEI WRT-WX9 (MateBook), Intel Core i7-8565U, Intel UHD Graphics 620 (i915)
  • Linux 7.1.9-arch1-2, Hyprland 0.56.2
  • quickshell 0.3.1-1
  • PipeWire 1:1.6.8-1, WirePlumber 0.5.15-1, BlueZ 5.87-2
  • Output: Bluetooth earphones (AirPods) over A2DP

What's wrong?

omarchy.audio renders the muted speaker glyph on the bar for an output that is not muted, and conversely never renders a muted glyph for headphone-class outputs. Both come from outputIcon() in plugins/panels/audio/Panel.qml:401:

(codepoints written as escapes below; the source has literal Nerd Font glyphs)

function outputIcon(volume) {
  if (!sink || !sink.audio) return "\ueee8"      // muted speaker
  if (isHeadphones(sink)) return "\U000f02cb"    // headphones
  if (outputMuted) return "\ueee8"               // muted speaker — same glyph
  var v = volume === undefined ? outputVolume : volume
  ...
}

Bug 1 — an unbound sink is indistinguishable from a muted sink

Line 404 returns the same glyph for "sink not readable" as line 406 returns for "sink is muted". So whenever sink.audio is null, the bar silently claims the user is muted.

sink is Pipewire.defaultAudioSink (Panel.qml:17). .audio is only populated once Quickshell binds the node, via PwObjectTracker { objects: root.candidateSinks } (Panel.qml:576). For a Bluetooth sink that appears after the shell has started, that binding did not happen on my system, so sink.audio stayed null.

The panel confirms it. With PipeWire reporting the sink unmuted at 40%:

$ pactl list sinks | grep -E 'Name:|Mute:|Volume: front'
        Name: bluez_output.XX_XX_XX_XX_XX_XX.1
        Mute: no
        Volume: front-left: 26214 /  40% ...

$ omarchy-audio-output-sink
bluez_output.XX_XX_XX_XX_XX_XX.1

…the panel read OUTPUT 0% and SILENCED — the ? : fallbacks at Panel.qml:146-147 firing because volumeSink.audio is null. The device still appeared in the output list, but only through the separate if (sink && list.indexOf(sink) < 0) list.unshift(sink) fallback at Panel.qml:82, which does not require binding. So the list looks healthy while nothing else is.

omarchy-audio-output-sink resolves the correct node, so volumeSinkName was right; the node itself was simply never bound.

Timing is the trigger. The shell started at 16:37:50 and the earphones connected at 18:54:21, over two hours later. omarchy restart shell fixes it immediately and permanently for that session.

Bug 2 — headphone outputs can never show muted

isHeadphones(sink) (line 405) is checked before outputMuted (line 406). For any output matching Model.js:72-87 (headphone, headset, earbud, earphone, airpod), the headphones glyph returns first and the mute state is unreachable.

Verified: with the same earphones genuinely muted (wpctl set-mute @DEFAULT_AUDIO_SINK@ 1, pactl confirming Mute: yes), the bar kept showing the plain headphones icon.

The two bugs are exact mirrors — the bar showed "muted" when it wasn't, and showed "not muted" when it was.

Steps to reproduce

Bug 1
1. Boot with no Bluetooth audio device connected, so the shell starts without one.
2. Some time later, connect Bluetooth earphones and make them the default output.
3. Confirm they are unmuted: wpctl get-volume @DEFAULT_AUDIO_SINK@ → Volume: 0.50 (no [MUTED]).
4. Observe the muted glyph on the bar.
5. Open the audio panel: OUTPUT reads 0% / SILENCED while PipeWire reports the real volume.
6. omarchy restart shell → correct headphones glyph and correct volume.

Bug 2
1. With headphones/earphones as the default output and the shell freshly restarted (so the sink is bound).
2. wpctl set-mute @DEFAULT_AUDIO_SINK@ 1.
3. The bar keeps showing the plain headphones glyph; no muted indication.

Expected

  • An unreadable/unbound sink should not be drawn as muted — it needs its own glyph, or the widget should re-resolve until the node binds.
  • Mute state should take precedence over device class, so muted headphones show a muted headphones glyph.
  • A sink that appears after the shell starts should get bound and tracked like one present at startup.

Notes

The outputMuted check could simply move above the isHeadphones check to fix Bug 2. Bug 1 is the more substantive one: the binding gap for mid-session sinks, plus the overloaded glyph that hides it.

Unrelated, noticed while gathering diagnostics on 4.0.1-1: the omarchy debug dispatcher fails with Unknown Omarchy command: omarchy debug, though omarchy --help advertises it and /usr/share/omarchy/bin/omarchy-debug works when invoked directly. Happy to file separately if useful.

View raw text
## System details

- Omarchy 4.0.1-1
- HUAWEI WRT-WX9 (MateBook), Intel Core i7-8565U, Intel UHD Graphics 620 (i915)
- Linux 7.1.9-arch1-2, Hyprland 0.56.2
- quickshell 0.3.1-1
- PipeWire 1:1.6.8-1, WirePlumber 0.5.15-1, BlueZ 5.87-2
- Output: Bluetooth earphones (AirPods) over A2DP

## What's wrong?

`omarchy.audio` renders the **muted** speaker glyph on the bar for an output that is not muted, and conversely never renders a muted glyph for headphone-class outputs. Both come from `outputIcon()` in `plugins/panels/audio/Panel.qml:401`:

(codepoints written as escapes below; the source has literal Nerd Font glyphs)

```qml
function outputIcon(volume) {
  if (!sink || !sink.audio) return "\ueee8"      // muted speaker
  if (isHeadphones(sink)) return "\U000f02cb"    // headphones
  if (outputMuted) return "\ueee8"               // muted speaker — same glyph
  var v = volume === undefined ? outputVolume : volume
  ...
}
```

### Bug 1 — an unbound sink is indistinguishable from a muted sink

Line 404 returns the *same glyph* for "sink not readable" as line 406 returns for "sink is muted". So whenever `sink.audio` is null, the bar silently claims the user is muted.

`sink` is `Pipewire.defaultAudioSink` (`Panel.qml:17`). `.audio` is only populated once Quickshell binds the node, via `PwObjectTracker { objects: root.candidateSinks }` (`Panel.qml:576`). For a Bluetooth sink that appears **after** the shell has started, that binding did not happen on my system, so `sink.audio` stayed null.

The panel confirms it. With PipeWire reporting the sink unmuted at 40%:

```
$ pactl list sinks | grep -E 'Name:|Mute:|Volume: front'
        Name: bluez_output.XX_XX_XX_XX_XX_XX.1
        Mute: no
        Volume: front-left: 26214 /  40% ...

$ omarchy-audio-output-sink
bluez_output.XX_XX_XX_XX_XX_XX.1
```

…the panel read **OUTPUT 0%** and **SILENCED** — the `? :` fallbacks at `Panel.qml:146-147` firing because `volumeSink.audio` is null. The device still appeared in the output list, but only through the separate `if (sink && list.indexOf(sink) < 0) list.unshift(sink)` fallback at `Panel.qml:82`, which does not require binding. So the list looks healthy while nothing else is.

`omarchy-audio-output-sink` resolves the correct node, so `volumeSinkName` was right; the node itself was simply never bound.

**Timing is the trigger.** The shell started at 16:37:50 and the earphones connected at 18:54:21, over two hours later. `omarchy restart shell` fixes it immediately and permanently for that session.

### Bug 2 — headphone outputs can never show muted

`isHeadphones(sink)` (line 405) is checked *before* `outputMuted` (line 406). For any output matching `Model.js:72-87` (`headphone`, `headset`, `earbud`, `earphone`, `airpod`), the headphones glyph returns first and the mute state is unreachable.

Verified: with the same earphones genuinely muted (`wpctl set-mute @DEFAULT_AUDIO_SINK@ 1`, `pactl` confirming `Mute: yes`), the bar kept showing the plain headphones icon.

The two bugs are exact mirrors — the bar showed "muted" when it wasn't, and showed "not muted" when it was.

### Steps to reproduce

**Bug 1**
1. Boot with no Bluetooth audio device connected, so the shell starts without one.
2. Some time later, connect Bluetooth earphones and make them the default output.
3. Confirm they are unmuted: `wpctl get-volume @DEFAULT_AUDIO_SINK@` → `Volume: 0.50` (no `[MUTED]`).
4. Observe the muted glyph on the bar.
5. Open the audio panel: OUTPUT reads 0% / SILENCED while PipeWire reports the real volume.
6. `omarchy restart shell` → correct headphones glyph and correct volume.

**Bug 2**
1. With headphones/earphones as the default output and the shell freshly restarted (so the sink is bound).
2. `wpctl set-mute @DEFAULT_AUDIO_SINK@ 1`.
3. The bar keeps showing the plain headphones glyph; no muted indication.

### Expected

- An unreadable/unbound sink should not be drawn as muted — it needs its own glyph, or the widget should re-resolve until the node binds.
- Mute state should take precedence over device class, so muted headphones show a muted headphones glyph.
- A sink that appears after the shell starts should get bound and tracked like one present at startup.

### Notes

The `outputMuted` check could simply move above the `isHeadphones` check to fix Bug 2. Bug 1 is the more substantive one: the binding gap for mid-session sinks, plus the overloaded glyph that hides it.

Unrelated, noticed while gathering diagnostics on 4.0.1-1: the `omarchy debug` dispatcher fails with `Unknown Omarchy command: omarchy debug`, though `omarchy --help` advertises it and `/usr/share/omarchy/bin/omarchy-debug` works when invoked directly. Happy to file separately if useful.

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 09:46 UTC