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

Bluetooth bar widget disappears and cannot be toggled back on after rfkill-blocking a ThinkPad tpacpi switch

Reported by alhasapi · filed Sep 20, 2026 · open at source

Decided: Out of scope — decided by agents · verified 3 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

Bluetooth bar widget disappears and cannot be toggled back on after rfkill-blocking a ThinkPad tpacpi switchrevision 1 · captured 2026-09-28 21:23 UTC via poll

Summary

On a ThinkPad, turning Bluetooth off from the Bluetooth bar widget makes the
widget disappear, and neither clicking where it used to be nor the
SUPER+CTRL+B / IPC toggle can turn Bluetooth back on. Only the physical
Fn radio button recovers it.

The cause is that omarchy-bluetooth-power off runs rfkill block bluetooth,
which also blocks the thinkpad_acpi master radio switch. On this hardware that
switch gates power to the internal USB Bluetooth module, so the module
de-enumerates and BlueZ unregisters the adapter entirely. Panel.qml then
treats "no adapter" as "no hardware": it hides the widget and makes its toggle a
no-op.

Environment

  • Omarchy: 4.0.4-1
  • Machine: Lenovo ThinkPad T480 (20L50000MX)
  • Bluetooth module: Bus 001 Device 051: ID 8087:0a2b Intel Corp. Bluetooth wireless interface, enumerated at USB port 1-7
$ rfkill list          # after turning Bluetooth off from the widget
0: tpacpi_bluetooth_sw: Bluetooth
	Soft blocked: yes
	Hard blocked: no
1: phy0: Wireless LAN
	Soft blocked: no
	Hard blocked: no

Root cause

omarchy-bluetooth-power implements off as:

rfkill block bluetooth

Blocking the bluetooth rfkill type blocks every switch of that type, and
this ThinkPad exposes two:

  1. platform-thinkpad_acpi:bluetooth (tpacpi_bluetooth_sw) — the master

switch, which also controls power to the module.
2. The internal USB module itself (pci-…-usb-0:7:1.0:bluetooth, USB port 1-7).

Blocking the thinkpad_acpi switch drops power to the USB module:

$ ls /sys/bus/usb/devices/      # while Bluetooth is on
… 1-7 1-7:1.0 1-7:1.1 …

$ ls /sys/bus/usb/devices/      # after `rfkill block bluetooth` settles
… (no 1-7) …

$ ls -A /sys/class/bluetooth/   # empty
$ bluetoothctl list             # empty

BlueZ therefore unregisters the adapter, and Bluetooth.defaultAdapter becomes
null in the QML.

$OMARCHY_PATH/shell/plugins/panels/bluetooth/Panel.qml then hits two
adapter-null assumptions:

  • Line 500 — visible: adapter !== null removes the bar widget entirely,

and its icon (line 59, if (!adapter) return "") would render a zero-width
button anyway.
- Lines 636-637 — the toggle bails out:

  function toggleBluetooth() {
    if (!adapter) return
    Quickshell.execDetached(["omarchy-bluetooth-power", adapter.enabled ? "off" : "on"])
  }

so the panel's switch (line 715, visible: !!root.adapter), the right-click
action, and the omarchy.bluetooth IPC handler all do nothing.

rfkill unblock bluetooth is exactly what brings the adapter back, and the
patched branch below can issue it while the adapter is absent.

Steps to reproduce

  1. On a ThinkPad (or any machine whose Bluetooth radio sits behind a second

rfkill switch of type bluetooth), click the Bluetooth bar widget and turn
it off (or run omarchy-bluetooth-power off).
2. Wait a few seconds for the internal module to power down.
3. The Bluetooth widget vanishes from the bar and /sys/class/bluetooth/ is empty.
4. Try SUPER+CTRL+B → toggle, or omarchy-shell omarchy.bluetooth toggleBluetooth:
nothing happens, the adapter stays gone.
5. Only the physical Fn radio button (which flips tpacpi_bluetooth_sw back to
soft=0 and re-powers the module) restores Bluetooth.

Suggested fix

Treat "no adapter" as "Bluetooth is off" rather than "no hardware", so the
widget stays reachable and its toggle can re-enable the radio. Diff against
shell/plugins/panels/bluetooth/Panel.qml:

--- a/shell/plugins/panels/bluetooth/Panel.qml
+++ b/shell/plugins/panels/bluetooth/Panel.qml
@@ -56,7 +56,7 @@
   readonly property var discoveredDevices: deviceGroups.discovered || []
 
   readonly property string icon: {
-    if (!adapter) return ""
+    if (!adapter) return "󰂲"
     if (!adapter.enabled) return "󰂲"
     if (connectedDevices.length > 0) return "󰂱"
     return "󰂯"
@@ -75,7 +75,7 @@
   ]
   readonly property bool rotatingPhrases: adapter && adapter.enabled
   readonly property string heroStatusText: {
-    if (!adapter) return "No adapter"
+    if (!adapter) return "Turned Off"
     if (!adapter.enabled) return "Turned Off"
     return activePhrases[phraseIndex % activePhrases.length]
   }
@@ -497,7 +497,7 @@
     if (selectedIndex < 0) selectedIndex = 0
   }
 
-  visible: adapter !== null
+  visible: true
   implicitWidth: button.implicitWidth
   implicitHeight: button.implicitHeight
 
@@ -633,8 +633,10 @@
   // switch only moves once BlueZ catches up, so a second click inside that window
   // would re-read the old state and undo the first.
   function toggleBluetooth() {
-    if (!adapter) return
-    Quickshell.execDetached(["omarchy-bluetooth-power", adapter.enabled ? "off" : "on"])
+    if (adapter)
+      Quickshell.execDetached(["omarchy-bluetooth-power", adapter.enabled ? "off" : "on"])
+    else
+      Quickshell.execDetached(["omarchy-bluetooth-power", "on"])
   }
 
   IpcHandler {
@@ -712,7 +714,7 @@
           // header's only cursor target.
           ToggleSwitch {
             id: powerSwitch
-            visible: !!root.adapter
+            visible: true
             checked: !!root.adapter && root.adapter.enabled
             hasCursor: root.headerHasCursor
             foreground: root.bar.foreground
@@ -867,7 +869,7 @@
         Text {
           textFormat: Text.PlainText
           visible: root.connectedDevices.length === 0 && root.scrollRows.length === 0
-          text: !root.adapter ? "No Bluetooth adapter"
+          text: !root.adapter ? "Bluetooth is off"
               : !root.adapter.enabled ? "Turn Bluetooth on to scan"
               : "Scanning for devices…"
           color: Qt.darker(root.bar.foreground, 1.5)

With this applied, the widget stays in the bar showing the off glyph, and
toggling it while the adapter is absent runs omarchy-bluetooth-power on
(rfkill unblock bluetooth), which powers the module back up and restores the
adapter. All other adapter-null guards in the panel (discovery retry, the
Connections targets, onOpenedChanged, device sections) already tolerate a
null adapter and need no change.

Tradeoff: on a machine with no Bluetooth hardware at all, the widget now shows an
always-off icon instead of being hidden. That seemed the better default, but a
Bluetooth.adapters.length === 0 hardware check could be used to hide it in that
case if preferred.

Verification

Applied locally as a cloned plugin (~/.config/omarchy/plugins/…-bluetooth/),
with Bluetooth off and /sys/class/bluetooth/ empty:

$ omarchy-shell omarchy.bluetooth toggleBluetooth     # adapter is null
$ rfkill list
0: tpacpi_bluetooth_sw: Bluetooth
	Soft blocked: no
$ ls /sys/class/bluetooth/
hci0

and toggling with the adapter present still runs omarchy-bluetooth-power off
as before. A full shell restart was required for the clone's edit to take effect;
the local-plugin hot-reload logged Local plugin changed, reloading but the
running panel kept the pre-edit code (worth a separate look).

View raw text
## Summary

On a ThinkPad, turning Bluetooth **off** from the Bluetooth bar widget makes the
widget disappear, and neither clicking where it used to be nor the
`SUPER+CTRL+B` / IPC toggle can turn Bluetooth back on. Only the physical
`Fn` radio button recovers it.

The cause is that `omarchy-bluetooth-power off` runs `rfkill block bluetooth`,
which also blocks the `thinkpad_acpi` master radio switch. On this hardware that
switch gates power to the internal USB Bluetooth module, so the module
de-enumerates and BlueZ unregisters the adapter entirely. `Panel.qml` then
treats "no adapter" as "no hardware": it hides the widget and makes its toggle a
no-op.

## Environment

- Omarchy: `4.0.4-1`
- Machine: Lenovo ThinkPad T480 (`20L50000MX`)
- Bluetooth module: `Bus 001 Device 051: ID 8087:0a2b Intel Corp. Bluetooth wireless interface`, enumerated at USB port `1-7`

```
$ rfkill list          # after turning Bluetooth off from the widget
0: tpacpi_bluetooth_sw: Bluetooth
	Soft blocked: yes
	Hard blocked: no
1: phy0: Wireless LAN
	Soft blocked: no
	Hard blocked: no
```

## Root cause

`omarchy-bluetooth-power` implements off as:

```bash
rfkill block bluetooth
```

Blocking the `bluetooth` rfkill **type** blocks every switch of that type, and
this ThinkPad exposes two:

1. `platform-thinkpad_acpi:bluetooth` (`tpacpi_bluetooth_sw`) — the master
   switch, which also controls power to the module.
2. The internal USB module itself (`pci-…-usb-0:7:1.0:bluetooth`, USB port `1-7`).

Blocking the `thinkpad_acpi` switch drops power to the USB module:

```
$ ls /sys/bus/usb/devices/      # while Bluetooth is on
… 1-7 1-7:1.0 1-7:1.1 …

$ ls /sys/bus/usb/devices/      # after `rfkill block bluetooth` settles
… (no 1-7) …

$ ls -A /sys/class/bluetooth/   # empty
$ bluetoothctl list             # empty
```

BlueZ therefore unregisters the adapter, and `Bluetooth.defaultAdapter` becomes
`null` in the QML.

`$OMARCHY_PATH/shell/plugins/panels/bluetooth/Panel.qml` then hits two
adapter-null assumptions:

- **Line 500** — `visible: adapter !== null` removes the bar widget entirely,
  and its `icon` (line 59, `if (!adapter) return ""`) would render a zero-width
  button anyway.
- **Lines 636-637** — the toggle bails out:

  ```js
  function toggleBluetooth() {
    if (!adapter) return
    Quickshell.execDetached(["omarchy-bluetooth-power", adapter.enabled ? "off" : "on"])
  }
  ```

  so the panel's switch (line 715, `visible: !!root.adapter`), the right-click
  action, and the `omarchy.bluetooth` IPC handler all do nothing.

`rfkill unblock bluetooth` is exactly what brings the adapter back, and the
patched branch below can issue it while the adapter is absent.

## Steps to reproduce

1. On a ThinkPad (or any machine whose Bluetooth radio sits behind a second
   `rfkill` switch of type `bluetooth`), click the Bluetooth bar widget and turn
   it off (or run `omarchy-bluetooth-power off`).
2. Wait a few seconds for the internal module to power down.
3. The Bluetooth widget vanishes from the bar and `/sys/class/bluetooth/` is empty.
4. Try `SUPER+CTRL+B` → toggle, or `omarchy-shell omarchy.bluetooth toggleBluetooth`:
   nothing happens, the adapter stays gone.
5. Only the physical `Fn` radio button (which flips `tpacpi_bluetooth_sw` back to
   `soft=0` and re-powers the module) restores Bluetooth.

## Suggested fix

Treat "no adapter" as "Bluetooth is off" rather than "no hardware", so the
widget stays reachable and its toggle can re-enable the radio. Diff against
`shell/plugins/panels/bluetooth/Panel.qml`:

```diff
--- a/shell/plugins/panels/bluetooth/Panel.qml
+++ b/shell/plugins/panels/bluetooth/Panel.qml
@@ -56,7 +56,7 @@
   readonly property var discoveredDevices: deviceGroups.discovered || []
 
   readonly property string icon: {
-    if (!adapter) return ""
+    if (!adapter) return "󰂲"
     if (!adapter.enabled) return "󰂲"
     if (connectedDevices.length > 0) return "󰂱"
     return "󰂯"
@@ -75,7 +75,7 @@
   ]
   readonly property bool rotatingPhrases: adapter && adapter.enabled
   readonly property string heroStatusText: {
-    if (!adapter) return "No adapter"
+    if (!adapter) return "Turned Off"
     if (!adapter.enabled) return "Turned Off"
     return activePhrases[phraseIndex % activePhrases.length]
   }
@@ -497,7 +497,7 @@
     if (selectedIndex < 0) selectedIndex = 0
   }
 
-  visible: adapter !== null
+  visible: true
   implicitWidth: button.implicitWidth
   implicitHeight: button.implicitHeight
 
@@ -633,8 +633,10 @@
   // switch only moves once BlueZ catches up, so a second click inside that window
   // would re-read the old state and undo the first.
   function toggleBluetooth() {
-    if (!adapter) return
-    Quickshell.execDetached(["omarchy-bluetooth-power", adapter.enabled ? "off" : "on"])
+    if (adapter)
+      Quickshell.execDetached(["omarchy-bluetooth-power", adapter.enabled ? "off" : "on"])
+    else
+      Quickshell.execDetached(["omarchy-bluetooth-power", "on"])
   }
 
   IpcHandler {
@@ -712,7 +714,7 @@
           // header's only cursor target.
           ToggleSwitch {
             id: powerSwitch
-            visible: !!root.adapter
+            visible: true
             checked: !!root.adapter && root.adapter.enabled
             hasCursor: root.headerHasCursor
             foreground: root.bar.foreground
@@ -867,7 +869,7 @@
         Text {
           textFormat: Text.PlainText
           visible: root.connectedDevices.length === 0 && root.scrollRows.length === 0
-          text: !root.adapter ? "No Bluetooth adapter"
+          text: !root.adapter ? "Bluetooth is off"
               : !root.adapter.enabled ? "Turn Bluetooth on to scan"
               : "Scanning for devices…"
           color: Qt.darker(root.bar.foreground, 1.5)
```

With this applied, the widget stays in the bar showing the off glyph, and
toggling it while the adapter is absent runs `omarchy-bluetooth-power on`
(`rfkill unblock bluetooth`), which powers the module back up and restores the
adapter. All other adapter-null guards in the panel (discovery retry, the
`Connections` targets, `onOpenedChanged`, device sections) already tolerate a
null adapter and need no change.

Tradeoff: on a machine with no Bluetooth hardware at all, the widget now shows an
always-off icon instead of being hidden. That seemed the better default, but a
`Bluetooth.adapters.length === 0` hardware check could be used to hide it in that
case if preferred.

## Verification

Applied locally as a cloned plugin (`~/.config/omarchy/plugins/…-bluetooth/`),
with Bluetooth off and `/sys/class/bluetooth/` empty:

```
$ omarchy-shell omarchy.bluetooth toggleBluetooth     # adapter is null
$ rfkill list
0: tpacpi_bluetooth_sw: Bluetooth
	Soft blocked: no
$ ls /sys/class/bluetooth/
hci0
```

and toggling with the adapter present still runs `omarchy-bluetooth-power off`
as before. A full shell restart was required for the clone's edit to take effect;
the local-plugin hot-reload logged `Local plugin changed, reloading` but the
running panel kept the pre-edit code (worth a separate look).

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:23 UTC