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.
Saved battery power profile never applied when booting on battery: omarchy-powerprofiles-set autodetect silently falls back to "ac"
Reported by kzagoris · filed Sep 21, 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 power profile saved for battery is never applied when the machine boots already on battery. The session comes up on whatever profile power-profiles-daemon restored from its own persisted state (here performance), and nothing corrects it for as long as the machine stays unplugged — in my case 24 hours, with power-saver sitting saved and unused.
The practical effect on a laptop is that a heavy build runs at full turbo on battery when the user has explicitly asked for power-saver.
Environment
- Omarchy 4.0.4-1
- power-profiles-daemon 0.30-1
- Kernel 7.2.6-arch2-Watanare-T2-3-t2
- CPU: Intel Core i9-9980HK (MacBookPro16,1, T2)
- Packages last updated: Sep 20 2026
Steps to reproduce
- In the power panel, select Power-saver while on battery. Confirm
~/.local/state/omarchy/powerprofiles/batterynow containspower-saver. - Ensure
power-profiles-daemon's own state has a different profile, e.g./var/lib/power-profiles-daemon/state.iniwithProfile=performance. - Shut down, unplug AC, and boot with no AC attached.
powerprofilesctl get
Expected: power-saver (the saved battery preference).
Actual: performance.
Evidence from my system
Saved preference, written Aug 25:
$ cat ~/.local/state/omarchy/powerprofiles/battery
power-saverThe boot in question began on battery:
Sep 20 09:26:53 kernel: ACPI: AC: AC Adapter [ADP1] (off-line)That boot ran 24 hours. journalctl -b -u power-profiles-daemon for it shows the daemon starting and no profile change at any point:
Sep 20 09:26:57 systemd[1]: Starting Power Profiles daemon...
Sep 20 09:26:57 systemd[1]: Started Power Profiles daemon.PPD's persisted state, which it restores at startup:
# /var/lib/power-profiles-daemon/state.ini
Profile=performanceSo the machine sat on performance, on battery, at full turbo (intel_pstate/no_turbo=0, max_perf_pct=100), for the entire session.
The setter itself works correctly — the preference is honored when it is actually invoked:
$ powerprofilesctl get
performance
$ omarchy-powerprofiles-set battery && powerprofilesctl get
power-saverAnalysis
Two things look relevant. I have verified the first as a latent defect; the second I can show is a real gap but have not proven fired in my specific boot.
1. autodetect fail-opens to ac
omarchy-powerprofiles-init runs at session start (default/hypr/autostart.lua:8) and calls omarchy-powerprofiles-set autodetect. The detection is:
if [[ $(busctl get-property org.freedesktop.UPower /org/freedesktop/UPower \
org.freedesktop.UPower OnBattery 2>/dev/null) == "b true" ]]; then
action=battery
else
action=ac
fiOnly the exact string b true selects battery. Every other outcome — UPower not yet activatable, D-Bus not ready, a timeout, any stderr-suppressed failure — falls through to ac. There is no exit-status check and no retry.
Because most users have no ac state file (the file is written only when a profile is explicitly requested for that source), action=ac then hits the fallback at the bottom of the script and resolves to `performance`:
if [[ $action == "ac" ]] && profile_available performance; then
profile=performanceSo a failed or premature UPower query on a machine booting on battery silently produces the most aggressive profile — the opposite of the saved preference, with no error surfaced. Confirmed by simulating a failing query:
$ out=$(busctl get-property org.freedesktop.NONEXISTENT /nope org.nope Nope 2>/dev/null)
$ [[ $out == "b true" ]] && echo battery || echo ac
acA failed detection should be distinguishable from a successful "not on battery", and should retry or bail rather than defaulting to the highest-power profile.
2. The shell service only reacts to transitions
shell/plugins/services/battery/Service.qml:
Connections {
target: UPower
function onOnBatteryChanged() {
root.checkBattery()
root.applyPowerProfile() // only caller
}
}applyPowerProfile() is reachable only from onOnBatteryChanged. There is no Component.onCompleted, and the 30s Timer calls only checkBattery(). So the shell never reconciles the profile against the current power source — it only reacts to AC↔battery changes.
That is by design, since omarchy-powerprofiles-init is supposed to cover startup. But it means the init is a single point of failure: if that one call resolves wrongly (see #1), nothing else in the running system ever corrects it. A periodic or startup reconcile in the service would make the outcome self-healing regardless of what happened at boot.
Suggested fix directions
- In
autodetect, checkbusctl's exit status and retry briefly; treat a failed query as unknown rather than asac. - Consider making
batterythe safer fallback when detection fails, or refusing to change the profile at all. - Optionally, have
Service.qmlreconcile the profile on startup (Component.onCompleted) or on its existing 30s timer, so a bad boot-time resolution self-corrects.
Related
#8184 covered the older udev-based boot race, and its follow-up comment describes the quattro redesign that replaced it with omarchy-powerprofiles-init at session start. This report is about that replacement path: the init is wired up correctly, but its detection can silently resolve to ac, and nothing reconciles afterwards.
View raw text
## Summary
The power profile saved for battery is never applied when the machine **boots already on battery**. The session comes up on whatever profile `power-profiles-daemon` restored from its own persisted state (here `performance`), and nothing corrects it for as long as the machine stays unplugged — in my case 24 hours, with `power-saver` sitting saved and unused.
The practical effect on a laptop is that a heavy build runs at full turbo on battery when the user has explicitly asked for `power-saver`.
## Environment
- Omarchy 4.0.4-1
- power-profiles-daemon 0.30-1
- Kernel 7.2.6-arch2-Watanare-T2-3-t2
- CPU: Intel Core i9-9980HK (MacBookPro16,1, T2)
- Packages last updated: Sep 20 2026
## Steps to reproduce
1. In the power panel, select **Power-saver** while on battery. Confirm `~/.local/state/omarchy/powerprofiles/battery` now contains `power-saver`.
2. Ensure `power-profiles-daemon`'s own state has a different profile, e.g. `/var/lib/power-profiles-daemon/state.ini` with `Profile=performance`.
3. Shut down, unplug AC, and boot **with no AC attached**.
4. `powerprofilesctl get`
**Expected:** `power-saver` (the saved battery preference).
**Actual:** `performance`.
## Evidence from my system
Saved preference, written Aug 25:
```
$ cat ~/.local/state/omarchy/powerprofiles/battery
power-saver
```
The boot in question began on battery:
```
Sep 20 09:26:53 kernel: ACPI: AC: AC Adapter [ADP1] (off-line)
```
That boot ran 24 hours. `journalctl -b -u power-profiles-daemon` for it shows the daemon starting and **no profile change at any point**:
```
Sep 20 09:26:57 systemd[1]: Starting Power Profiles daemon...
Sep 20 09:26:57 systemd[1]: Started Power Profiles daemon.
```
PPD's persisted state, which it restores at startup:
```ini
# /var/lib/power-profiles-daemon/state.ini
Profile=performance
```
So the machine sat on `performance`, on battery, at full turbo (`intel_pstate/no_turbo=0`, `max_perf_pct=100`), for the entire session.
The setter itself works correctly — the preference is honored when it is actually invoked:
```
$ powerprofilesctl get
performance
$ omarchy-powerprofiles-set battery && powerprofilesctl get
power-saver
```
## Analysis
Two things look relevant. I have verified the first as a latent defect; the second I can show is a real gap but have not proven fired in my specific boot.
### 1. `autodetect` fail-opens to `ac`
`omarchy-powerprofiles-init` runs at session start (`default/hypr/autostart.lua:8`) and calls `omarchy-powerprofiles-set autodetect`. The detection is:
```bash
if [[ $(busctl get-property org.freedesktop.UPower /org/freedesktop/UPower \
org.freedesktop.UPower OnBattery 2>/dev/null) == "b true" ]]; then
action=battery
else
action=ac
fi
```
Only the exact string `b true` selects `battery`. **Every** other outcome — UPower not yet activatable, D-Bus not ready, a timeout, any stderr-suppressed failure — falls through to `ac`. There is no exit-status check and no retry.
Because most users have no `ac` state file (the file is written only when a profile is explicitly requested for that source), `action=ac` then hits the fallback at the bottom of the script and resolves to **`performance`**:
```bash
if [[ $action == "ac" ]] && profile_available performance; then
profile=performance
```
So a failed or premature UPower query on a machine booting on battery silently produces the most aggressive profile — the opposite of the saved preference, with no error surfaced. Confirmed by simulating a failing query:
```bash
$ out=$(busctl get-property org.freedesktop.NONEXISTENT /nope org.nope Nope 2>/dev/null)
$ [[ $out == "b true" ]] && echo battery || echo ac
ac
```
A failed detection should be distinguishable from a successful "not on battery", and should retry or bail rather than defaulting to the highest-power profile.
### 2. The shell service only reacts to transitions
`shell/plugins/services/battery/Service.qml`:
```qml
Connections {
target: UPower
function onOnBatteryChanged() {
root.checkBattery()
root.applyPowerProfile() // only caller
}
}
```
`applyPowerProfile()` is reachable only from `onOnBatteryChanged`. There is no `Component.onCompleted`, and the 30s `Timer` calls only `checkBattery()`. So the shell never reconciles the profile against the current power source — it only reacts to AC↔battery *changes*.
That is by design, since `omarchy-powerprofiles-init` is supposed to cover startup. But it means the init is a single point of failure: if that one call resolves wrongly (see #1), nothing else in the running system ever corrects it. A periodic or startup reconcile in the service would make the outcome self-healing regardless of what happened at boot.
## Suggested fix directions
- In `autodetect`, check `busctl`'s exit status and retry briefly; treat a failed query as unknown rather than as `ac`.
- Consider making `battery` the safer fallback when detection fails, or refusing to change the profile at all.
- Optionally, have `Service.qml` reconcile the profile on startup (`Component.onCompleted`) or on its existing 30s timer, so a bad boot-time resolution self-corrects.
## Related
#8184 covered the older udev-based boot race, and its follow-up comment describes the `quattro` redesign that replaced it with `omarchy-powerprofiles-init` at session start. This report is about that replacement path: the init is wired up correctly, but its detection can silently resolve to `ac`, and nothing reconciles afterwards.
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 16:54 UTC