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-4689GitHub issuePublicReceived

Power panel only reads the first battery on dual-battery hardware (ignores real combined charge, no per-battery breakdown)

Reported by jadonfloyd · filed Aug 25, 2026Closed upstream on 2026-09-20 by bjarneo via an automated message, as a duplicate of #6885 (R-1856). GitHub close reason: completed.

Received and recorded verbatim. Nothing has been checked yet.

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?
Not yet mapped to any requirement or component.
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?
Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. No decision has been recorded yet.

Original report

Power panel only reads the first battery on dual-battery hardware (ignores real combined charge, no per-battery breakdown)revision 1 · captured 2026-10-04 12:03 UTC via poll

Summary

On a dual-battery laptop (e.g. Lenovo ThinkPad T450 with an internal + hot-swap "Ultrabay" battery), the Power panel's detailed stats — the big percentage, battery size, cycle count, and time remaining — only reflect the first battery UPower enumerates. When that battery happens to be sitting full (held there by a charge threshold) while the other battery is the one actually being drawn down, the panel reports a misleadingly high percentage (up to a phantom "100%") and never shows the second battery at all.

The small bar icon itself is unaffected — it already reads UPower.displayDevice, which correctly aggregates across every battery — so this is specific to the expanded panel.

Root cause

omarchy-battery-status (/usr/share/omarchy/bin/omarchy-battery-status) picks a single device here:

battery=$(upower -e 2>/dev/null | grep BAT | head -n 1)

Everything downstream (percentage, energy-full/"size", cycle count, thresholds, time-to-empty/full) is derived from that one device only. On a single-battery machine this is invisible, but on a dual-battery machine it silently reports whichever battery sorts first — which may not be the one actually discharging.

The panel's Panel.qml (/usr/share/omarchy/shell/plugins/panels/power/Panel.qml) calls this script for its batteryInfo, so the same limitation surfaces in the UI. There's also no concept anywhere in the power panel of "more than one battery" — even if the script reported the correct aggregate, there's no way to see each battery's individual charge/state/health.

Steps to reproduce

  1. On a laptop with two batteries exposed as separate UPower devices (upower -e lists two battery_BAT* entries).
  2. Let one battery sit at/near 100% (e.g. held there by a charge threshold) while the system draws down the other.
  3. Click the battery icon in the bar to open the Power panel.
  4. Observe: the panel shows the idle battery's percentage/size/cycles (e.g. "100%", size = that battery's capacity only) instead of the real combined charge, and there's no way to see the second battery.

Expected behavior

  • The panel's percentage/size/time/rate should reflect the combined charge across every battery (as UPower.displayDevice already does for the bar icon).
  • Ideally, an expandable/listed breakdown of each individual battery's percentage, charging state, and health, since on this class of hardware they often charge/discharge independently (see below).

What I did to confirm and work around it

I cloned the built-in omarchy.power plugin (omarchy plugin clone omarchy.power) and:
1. Replaced the single-device upower -i "$battery" call with a read of upower -i /org/freedesktop/UPower/devices/DisplayDevice (UPower's own correct energy-weighted aggregate) for percentage/state/rate/time, keeping the per-battery sysfs loop only for cycle count and thresholds (which DisplayDevice doesn't carry).
2. Added a per-battery breakdown section to the panel, since watching it live surfaced something worth noting: on this hardware the system fully drains one battery before touching the second, rather than discharging both proportionally — so a combined-only percentage hides real, useful state (e.g. "the primary is nearly empty, about to fail over").

Screenshots attached:
- omarchy-battery-bug-before-stock-plugin.png — stock panel, single-battery data only (18Wh battery size = BAT0's capacity alone, no way to see BAT1).

<img width="378" height="226" alt="Image" src="https://github.com/user-attachments/assets/df22de83-28d7-4f56-9ee6-5292ee6fe6d5" />

  • omarchy-battery-bug-after-dual-battery-fix.png — patched panel: correct combined percentage/size, plus a "BATTERIES" section listing BAT0 and BAT1 individually with their own percentage, state, and health.

<img width="383" height="292" alt="Image" src="https://github.com/user-attachments/assets/3304b1af-ae3f-4b0e-a471-6b69a257d8c2" />

Secondary issue: Process command changes didn't hot-reload

While developing the fix locally, editing a cloned plugin's Process.command (in Panel.qml) did not take effect in the already-running shell, even though the shell's own log confirmed the file change was detected ("Local plugin changed, reloading: ..."). The old command kept running in the background until I ran omarchy restart shell. Plain QML/JS logic changes in the same file did seem to take effect via hot-reload; it was specifically the Process.command binding that stuck. Not sure if this is expected/known, but it cost real time to track down — might be worth a note in the plugin-authoring docs if it's a known limitation, or a fix if not.

System info

  • Machine: Lenovo ThinkPad T450 (DMI product: 20BV000AUS)
  • CPU: Intel Core i5-5300U @ 2.30GHz
  • GPU: Intel HD Graphics 5500 (Broadwell-U GT2)
  • Kernel: 7.1.9-arch1-2
  • Omarchy: 4.0.1-1
  • quickshell: 0.3.1-1
  • upower: 1.91.3-1
  • Batteries: BAT0 (LGC 45N1113, internal) and BAT1 (LGC 45N1127, Ultrabay/second bay) — both visibly degraded (77% and 65% of design capacity respectively after years of use), which is what makes the "which one is at 100%" confusion especially noticeable day to day.
View raw text




## Summary

On a dual-battery laptop (e.g. Lenovo ThinkPad T450 with an internal + hot-swap "Ultrabay" battery), the Power panel's detailed stats — the big percentage, battery size, cycle count, and time remaining — only reflect the *first* battery UPower enumerates. When that battery happens to be sitting full (held there by a charge threshold) while the *other* battery is the one actually being drawn down, the panel reports a misleadingly high percentage (up to a phantom "100%") and never shows the second battery at all.

The small bar icon itself is unaffected — it already reads `UPower.displayDevice`, which correctly aggregates across every battery — so this is specific to the expanded panel.

## Root cause

`omarchy-battery-status` (`/usr/share/omarchy/bin/omarchy-battery-status`) picks a single device here:

```bash
battery=$(upower -e 2>/dev/null | grep BAT | head -n 1)
```

Everything downstream (percentage, energy-full/"size", cycle count, thresholds, time-to-empty/full) is derived from that one device only. On a single-battery machine this is invisible, but on a dual-battery machine it silently reports whichever battery sorts first — which may not be the one actually discharging.

The panel's `Panel.qml` (`/usr/share/omarchy/shell/plugins/panels/power/Panel.qml`) calls this script for its `batteryInfo`, so the same limitation surfaces in the UI. There's also no concept anywhere in the power panel of "more than one battery" — even if the script reported the correct aggregate, there's no way to see each battery's individual charge/state/health.

## Steps to reproduce

1. On a laptop with two batteries exposed as separate UPower devices (`upower -e` lists two `battery_BAT*` entries).
2. Let one battery sit at/near 100% (e.g. held there by a charge threshold) while the system draws down the other.
3. Click the battery icon in the bar to open the Power panel.
4. Observe: the panel shows the idle battery's percentage/size/cycles (e.g. "100%", size = that battery's capacity only) instead of the real combined charge, and there's no way to see the second battery.

## Expected behavior

- The panel's percentage/size/time/rate should reflect the combined charge across every battery (as `UPower.displayDevice` already does for the bar icon).
- Ideally, an expandable/listed breakdown of each individual battery's percentage, charging state, and health, since on this class of hardware they often charge/discharge independently (see below).

## What I did to confirm and work around it

I cloned the built-in `omarchy.power` plugin (`omarchy plugin clone omarchy.power`) and:
1. Replaced the single-device `upower -i "$battery"` call with a read of `upower -i /org/freedesktop/UPower/devices/DisplayDevice` (UPower's own correct energy-weighted aggregate) for percentage/state/rate/time, keeping the per-battery sysfs loop only for cycle count and thresholds (which `DisplayDevice` doesn't carry).
2. Added a per-battery breakdown section to the panel, since watching it live surfaced something worth noting: on this hardware the system fully drains one battery before touching the second, rather than discharging both proportionally — so a combined-only percentage hides real, useful state (e.g. "the primary is nearly empty, about to fail over").

Screenshots attached:
- `omarchy-battery-bug-before-stock-plugin.png` — stock panel, single-battery data only (18Wh battery size = BAT0's capacity alone, no way to see BAT1).

<img width="378" height="226" alt="Image" src="https://github.com/user-attachments/assets/df22de83-28d7-4f56-9ee6-5292ee6fe6d5" />

- `omarchy-battery-bug-after-dual-battery-fix.png` — patched panel: correct combined percentage/size, plus a "BATTERIES" section listing BAT0 and BAT1 individually with their own percentage, state, and health.

<img width="383" height="292" alt="Image" src="https://github.com/user-attachments/assets/3304b1af-ae3f-4b0e-a471-6b69a257d8c2" />

## Secondary issue: Process command changes didn't hot-reload

While developing the fix locally, editing a cloned plugin's `Process.command` (in `Panel.qml`) did not take effect in the already-running shell, even though the shell's own log confirmed the file change was detected ("Local plugin changed, reloading: ..."). The old command kept running in the background until I ran `omarchy restart shell`. Plain QML/JS logic changes in the same file *did* seem to take effect via hot-reload; it was specifically the `Process.command` binding that stuck. Not sure if this is expected/known, but it cost real time to track down — might be worth a note in the plugin-authoring docs if it's a known limitation, or a fix if not.

## System info

- Machine: Lenovo ThinkPad T450 (DMI product: `20BV000AUS`)
- CPU: Intel Core i5-5300U @ 2.30GHz
- GPU: Intel HD Graphics 5500 (Broadwell-U GT2)
- Kernel: 7.1.9-arch1-2
- Omarchy: 4.0.1-1
- quickshell: 0.3.1-1
- upower: 1.91.3-1
- Batteries: `BAT0` (LGC 45N1113, internal) and `BAT1` (LGC 45N1127, Ultrabay/second bay) — both visibly degraded (77% and 65% of design capacity respectively after years of use), which is what makes the "which one is at 100%" confusion especially noticeable day to day.
  • df22de83-28d7-4f56-9ee6-5292ee6fe6d5
  • 3304b1af-ae3f-4b0e-a471-6b69a257d8c2

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.

No decision has been recorded yet.