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.
Low-battery warning never fires while docked: onBattery guard suppresses it when a dock supplies power but the battery still drains
Reported by zorzysty · filed Sep 9, 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 happened
My laptop drained from 20% to 2% over ~22 minutes and was force-suspended by upowerd's critical-power action. I never got the "Time to recharge!" warning at any point.
The cause is the onBattery term in the low-battery guard, in shell/plugins/services/battery/BatteryModel.js:
function isDischarging(device, onBattery, dischargingState) {
return !!(device && device.isPresent && onBattery && device.state === dischargingState)
}UPower.onBattery reflects the daemon's OnBattery property, which is false whenever any power-supply line-power device is online — regardless of whether the battery is actually draining. So on a dock or charger that supplies power but less than the machine is drawing, device.state is Discharging and the percentage falls to zero while onBattery stays false, and the warning is suppressed for the entire discharge.
This silences the warning in the case where it is arguably most valuable: the user believes they are plugged in, so they have no reason to check the battery themselves.
Expected
A low-battery warning whenever the battery is actually discharging past the threshold, whether or not a line-power supply happens to be online.
Steps to reproduce
- Dock a laptop whose draw exceeds what the dock delivers (mine: ThinkPad P16s Gen 4 AMD driving two external 1080p displays over USB-C DP-MST,
performancepower profile). - Confirm the mismatch:
upower -i /org/freedesktop/UPower/devices/battery_BAT0showsstate: dischargingwhile/sys/class/power_supply/AC/onlineis1. - Verify
busctl get-property org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower OnBatteryreturnsb false. - Let the battery drain past 10%.
No notification is sent. It drains to the upowerd critical action.
Evidence from my system
UPower's own charge history for the affected window, /var/lib/upower/history-charge-*.dat:
07:52 20.000 discharging <- boot, docked
07:59 10.000 discharging <- threshold crossed
08:00 8.000 discharging
08:14 2.000 discharging <- upowerd critical suspendjournalctl -b:
08:14:37 systemd-logind: suspend requested from client PID ... ('upowerd') (unit upower.service)Notification history under ~/.local/state/omarchy/notifications/history/ has gapless sequence ids across that whole window with no battery entry, so omarchy-battery-low was never invoked. Line-power state was online throughout (/sys/class/power_supply/ucsi-source-psy-USBC000:001/online = 1, AC/online = 1).
I ruled out the obvious alternatives: the service does load and tick every 30s with correct UPower values (verified by instrumenting a clone and restarting the shell), omarchy-battery-low is on the shell's PATH and works when invoked manually, and DND was off.
Suggested fix
Drop the onBattery term — device.state === dischargingState already answers the question, and it is the only one of the two that is true in this scenario:
function isDischarging(device, onBattery, dischargingState) {
return !!(device && device.isPresent && device.state === dischargingState)
}I've been running this locally as a cloned plugin. Happy to send it as a PR if you'd like, including a case in the model's unit coverage for docked-but-draining.
Note that Service.qml's applyPowerProfile() is a separate, legitimate use of UPower.onBattery and should keep it.
System
- Omarchy 4.0.2-1 (bug also present on
quattroas of today) - ThinkPad P16s Gen 4 AMD, Ryzen AI 9 HX PRO 370, Radeon 890M
- Kernel 7.1.9-arch1-2, quickshell 0.3.1-1
(omarchy debug isn't a command in 4.0.2, so the above is gathered manually. No screenshot: the bug is the absence of a notification.)
View raw text
## What happened
My laptop drained from 20% to 2% over ~22 minutes and was force-suspended by `upowerd`'s critical-power action. I never got the "Time to recharge!" warning at any point.
The cause is the `onBattery` term in the low-battery guard, in `shell/plugins/services/battery/BatteryModel.js`:
```js
function isDischarging(device, onBattery, dischargingState) {
return !!(device && device.isPresent && onBattery && device.state === dischargingState)
}
```
`UPower.onBattery` reflects the daemon's `OnBattery` property, which is false whenever any power-supply line-power device is online — regardless of whether the battery is actually draining. So on a dock or charger that supplies power but **less than the machine is drawing**, `device.state` is `Discharging` and the percentage falls to zero while `onBattery` stays `false`, and the warning is suppressed for the entire discharge.
This silences the warning in the case where it is arguably most valuable: the user believes they are plugged in, so they have no reason to check the battery themselves.
## Expected
A low-battery warning whenever the battery is actually discharging past the threshold, whether or not a line-power supply happens to be online.
## Steps to reproduce
1. Dock a laptop whose draw exceeds what the dock delivers (mine: ThinkPad P16s Gen 4 AMD driving two external 1080p displays over USB-C DP-MST, `performance` power profile).
2. Confirm the mismatch: `upower -i /org/freedesktop/UPower/devices/battery_BAT0` shows `state: discharging` while `/sys/class/power_supply/AC/online` is `1`.
3. Verify `busctl get-property org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower OnBattery` returns `b false`.
4. Let the battery drain past 10%.
No notification is sent. It drains to the `upowerd` critical action.
## Evidence from my system
UPower's own charge history for the affected window, `/var/lib/upower/history-charge-*.dat`:
```
07:52 20.000 discharging <- boot, docked
07:59 10.000 discharging <- threshold crossed
08:00 8.000 discharging
08:14 2.000 discharging <- upowerd critical suspend
```
`journalctl -b`:
```
08:14:37 systemd-logind: suspend requested from client PID ... ('upowerd') (unit upower.service)
```
Notification history under `~/.local/state/omarchy/notifications/history/` has gapless sequence ids across that whole window with no battery entry, so `omarchy-battery-low` was never invoked. Line-power state was online throughout (`/sys/class/power_supply/ucsi-source-psy-USBC000:001/online` = `1`, `AC/online` = `1`).
I ruled out the obvious alternatives: the service does load and tick every 30s with correct UPower values (verified by instrumenting a clone and restarting the shell), `omarchy-battery-low` is on the shell's PATH and works when invoked manually, and DND was off.
## Suggested fix
Drop the `onBattery` term — `device.state === dischargingState` already answers the question, and it is the only one of the two that is true in this scenario:
```js
function isDischarging(device, onBattery, dischargingState) {
return !!(device && device.isPresent && device.state === dischargingState)
}
```
I've been running this locally as a cloned plugin. Happy to send it as a PR if you'd like, including a case in the model's unit coverage for docked-but-draining.
Note that `Service.qml`'s `applyPowerProfile()` is a separate, legitimate use of `UPower.onBattery` and should keep it.
## System
- Omarchy 4.0.2-1 (bug also present on `quattro` as of today)
- ThinkPad P16s Gen 4 AMD, Ryzen AI 9 HX PRO 370, Radeon 890M
- Kernel 7.1.9-arch1-2, quickshell 0.3.1-1
(`omarchy debug` isn't a command in 4.0.2, so the above is gathered manually. No screenshot: the bug is the absence of a notification.)
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 11:16 UTC