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.
Audio panel: output volume and mute have no effect on device sinks
Reported by Jijjy · filed Aug 27, 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
Summary
The audio panel's OUTPUT slider and its mute toggles have no effect on real
loudness. The percentage, the knob and the OSD all move; the audio does not.
Per-application sliders (SOURCES) work correctly.
Volume keys (omarchy-audio-output-volume, via pactl) also work correctly,
which makes the panel look like the odd one out.
Cause
plugins/panels/audio/Panel.qml writes volume to the node:
volumeSink.audio.volume = volume // setOutputVolume
volumeSink.audio.muted = ... // toggleOutputMute / toggleAllMuted
source.audio.volume = ... // setInputVolumeOn a device sink that write is cosmetic. It updates the node'schannelVolumes, but the applied gain (softVolumes) never moves, so nothing
gets attenuated. Loudness on a device sink lives on its route, which is whatpactl and wpctl set.
Stream nodes are unaffected and take a direct node write, which is exactly why
the per-app sliders work and the output slider does not.
Reproduction
Any device sink (reproduced on both a Bluetooth A2DP sink and an ALSA one):
$ pw-dump 43 | jq '.[].info.params.Props[] | {channelVolumes, softVolumes}'
{ "channelVolumes": [0.216, 0.216], "softVolumes": [0.216, 0.216] }
# node-Props write -- what the panel does
$ pw-cli s 43 Props '{ channelVolumes: [ 0.05, 0.05 ] }'
$ pw-dump 43 | jq '.[].info.params.Props[] | {channelVolumes, softVolumes}'
{ "channelVolumes": [0.05, 0.05], "softVolumes": [0.216, 0.216] } # gain unchanged
$ pactl get-sink-volume <sink> | head -1
Volume: front-left: 39322 / 60% ... # still 60%
# route write -- what pactl/wpctl/the volume keys do
$ wpctl set-volume 43 0.30
$ pw-dump 43 | jq '.[].info.params.Props[] | {channelVolumes, softVolumes}'
{ "channelVolumes": [0.027, 0.027], "softVolumes": [0.027, 0.027] } # gain followsMute splits the same way: pw-cli s 43 Props '{ mute: true }' yields nodemute=True with softMute=False, and pactl get-sink-mute still reportsMute: no.
Fix
Route the device-node writes through pactl, the way the volume keys already
do, and leave the stream-node writes alone. Patched locally insetOutputVolume, setInputVolume, toggleOutputMute, toggleInputMute andtoggleAllMuted:
function writeOutputVolume(volume) {
if (!volumeSink) return
Quickshell.execDetached(["pactl", "set-sink-volume", String(volumeSink.name),
Math.round(volume * 100) + "%"])
}A drag emits a move per mouse event and each write is a process, so the output
write is gated at 40 ms: the first goes out immediately, and any arriving
during the window are coalesced into one at the end of it.
Reads need no change -- a route write updates channelVolumes too, so the
panel's displayed value stays correct.
Environment
- omarchy 4.0.0.alpha
- quickshell 0.3.1-1
- wireplumber 0.5.15-1
- pipewire 1:1.6.8-1
View raw text
## Summary
The audio panel's OUTPUT slider and its mute toggles have no effect on real
loudness. The percentage, the knob and the OSD all move; the audio does not.
Per-application sliders (SOURCES) work correctly.
Volume keys (`omarchy-audio-output-volume`, via `pactl`) also work correctly,
which makes the panel look like the odd one out.
## Cause
`plugins/panels/audio/Panel.qml` writes volume to the *node*:
```qml
volumeSink.audio.volume = volume // setOutputVolume
volumeSink.audio.muted = ... // toggleOutputMute / toggleAllMuted
source.audio.volume = ... // setInputVolume
```
On a **device** sink that write is cosmetic. It updates the node's
`channelVolumes`, but the applied gain (`softVolumes`) never moves, so nothing
gets attenuated. Loudness on a device sink lives on its route, which is what
`pactl` and `wpctl` set.
Stream nodes are unaffected and take a direct node write, which is exactly why
the per-app sliders work and the output slider does not.
## Reproduction
Any device sink (reproduced on both a Bluetooth A2DP sink and an ALSA one):
```console
$ pw-dump 43 | jq '.[].info.params.Props[] | {channelVolumes, softVolumes}'
{ "channelVolumes": [0.216, 0.216], "softVolumes": [0.216, 0.216] }
# node-Props write -- what the panel does
$ pw-cli s 43 Props '{ channelVolumes: [ 0.05, 0.05 ] }'
$ pw-dump 43 | jq '.[].info.params.Props[] | {channelVolumes, softVolumes}'
{ "channelVolumes": [0.05, 0.05], "softVolumes": [0.216, 0.216] } # gain unchanged
$ pactl get-sink-volume <sink> | head -1
Volume: front-left: 39322 / 60% ... # still 60%
# route write -- what pactl/wpctl/the volume keys do
$ wpctl set-volume 43 0.30
$ pw-dump 43 | jq '.[].info.params.Props[] | {channelVolumes, softVolumes}'
{ "channelVolumes": [0.027, 0.027], "softVolumes": [0.027, 0.027] } # gain follows
```
Mute splits the same way: `pw-cli s 43 Props '{ mute: true }'` yields node
`mute=True` with `softMute=False`, and `pactl get-sink-mute` still reports
`Mute: no`.
## Fix
Route the device-node writes through `pactl`, the way the volume keys already
do, and leave the stream-node writes alone. Patched locally in
`setOutputVolume`, `setInputVolume`, `toggleOutputMute`, `toggleInputMute` and
`toggleAllMuted`:
```qml
function writeOutputVolume(volume) {
if (!volumeSink) return
Quickshell.execDetached(["pactl", "set-sink-volume", String(volumeSink.name),
Math.round(volume * 100) + "%"])
}
```
A drag emits a move per mouse event and each write is a process, so the output
write is gated at 40 ms: the first goes out immediately, and any arriving
during the window are coalesced into one at the end of it.
Reads need no change -- a route write updates `channelVolumes` too, so the
panel's displayed value stays correct.
## Environment
- omarchy 4.0.0.alpha
- quickshell 0.3.1-1
- wireplumber 0.5.15-1
- pipewire 1:1.6.8-1
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:38 UTC