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.
AppLibrary icon index ignores the active icon theme: first filesystem hit wins, so app icons come from hicolor and change between rescans
Reported by assada · filed Aug 19, 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
System details
Omarchy 4.0.0-1, Hyprland 0.56.2, quickshell-git 0.3.0.r20.g28771c7-1, Arch Linux, kernel 7.1.8-arch1-3, Intel i5-8350U / UHD 620. /usr/share on btrfs. Icon theme set to Tela-circle (tela-circle-icon-theme-all 2026_07_07-1).
What is wrong?
shell/services/AppLibrary.qml builds its own icon index and consults it before the themed lookup, so the icon theme selected in org.gnome.desktop.interface icon-theme is largely ignored for application icons in the menu/launcher.
The scan (iconIndexScanCommand()) walks every XDG icon directory and takes every */apps/* and */devices/* file:
dirs="$HOME/.icons $HOME/.local/share/icons";
IFS=":"; for d in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do dirs="$dirs $d/icons"; done; unset IFS;
for ext in svg png; do
for base in $dirs; do
[[ -d $base ]] && find "$base" \( -path "*/apps/*" -o -path "*/devices/*" \) -name "*.$ext" 2>/dev/null;
done
find /usr/share/pixmaps -maxdepth 1 -name "*.$ext" 2>/dev/null;
doneindexIconLine() keys purely on the file's basename and keeps the first hit:
if (name.length > 0 && root.pendingIconIndex[name] === undefined)
root.pendingIconIndex[name] = valueand iconSource() gives that index precedence over Quickshell.iconPath():
var found = root.iconIndex[value]
if (found) return Util.fileUrl(found)
var themed = Quickshell.iconPath(value, true)
if (themed.length > 0) return themedNothing in this path knows which icon theme is active. Whichever theme on disk find happens to reach first wins.
Consequence 1 — the selected theme loses to whatever is found first. With icon-theme set to Tela-circle, only 20 of the 67 non-absolute Icon= names across my installed desktop entries resolved into Tela-circle; 42 came from hicolor/breeze/Adwaita, including names Tela-circle does provide:
NAME IN-THEME INDEX PICKS (first hit wins)
foot yes /usr/share/icons/hicolor/scalable/apps/foot.svg
mpv yes /usr/share/icons/hicolor/scalable/apps/mpv.svg
btop yes /usr/share/icons/hicolor/scalable/apps/btop.svg
kdenlive yes /usr/share/icons/hicolor/scalable/apps/kdenlive.svg
libreoffice-calc yes /usr/share/icons/hicolor/scalable/apps/libreoffice-calc.svgSince hicolor holds each application's own upstream icon, the practical effect is that installing an icon theme changes little in the launcher, and which icons do change is arbitrary.
Consequence 2 — the winner is not stable across rescans. find output order is not guaranteed. On btrfs here, five consecutive identical scans produced identical content in five different orders (diff <(sort a) <(sort b) is empty; diff a b is not). So the same name can resolve to a different theme on the next scan. Because refreshIcons() re-runs the scan whenever a consumer opens — and per #6806 it can fire roughly once a second — icons visibly change with no user action. That is how I noticed this: icons flipped between themed and stock while I was doing nothing.
Note that the code comment describes the index as a fallback for icons Qt's themed lookup misses (newly installed apps), but it is currently the primary resolver.
Steps to recreate
# 1. Install any icon theme that overlaps hicolor, e.g.
sudo pacman -S tela-circle-icon-theme
gsettings set org.gnome.desktop.interface icon-theme Tela-circle
omarchy restart shell
# 2. Replay the scan exactly as AppLibrary.qml runs it
scan() {
local dirs="$HOME/.icons $HOME/.local/share/icons"
IFS=":"; for d in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do dirs="$dirs $d/icons"; done; unset IFS
for ext in svg png; do
for base in $dirs; do
[[ -d $base ]] && find "$base" \( -path "*/apps/*" -o -path "*/devices/*" \) -name "*.$ext" 2>/dev/null
done
find /usr/share/pixmaps -maxdepth 1 -name "*.$ext" 2>/dev/null
done
}
scan > /tmp/s1; scan > /tmp/s2
# 3. The active theme loses to hicolor even though it ships the icon
ls /usr/share/icons/Tela-circle/scalable/apps/foot.svg # exists
grep -m1 '/foot\.' /tmp/s1 # -> hicolor/scalable/apps/foot.svg
# 4. Same content, different order on every run
diff <(sort /tmp/s1) <(sort /tmp/s2) | wc -l # 0
diff /tmp/s1 /tmp/s2 | wc -l # non-zeroThen open the Apps menu: icons come from a mix of themes, and change between openings.
Expected
Application icons should come from the icon theme the user selected, and should not change on their own.
Suggested fix
Either restrict the scan to the active theme plus its Inherits chain (then hicolor), or — probably simpler — restore the index to the fallback role its comment describes: try Quickshell.iconPath() first, since it already honours the icon theme, and fall back to the index only for names the themed lookup misses. Sorting each find (find ... | sort) would additionally make the outcome deterministic regardless of filesystem traversal order.
Workaround (verified)
Force the wanted theme to the front of the scan by putting its app/device icons in $HOME/.icons, which the scan reads first:
mkdir -p ~/.icons/tela-priority/scalable/{apps,devices}
find /usr/share/icons/Tela-circle/scalable/apps -maxdepth 1 -name '*.svg' -exec ln -sf {} ~/.icons/tela-priority/scalable/apps/ \;
find /usr/share/icons/Tela-circle/scalable/devices -maxdepth 1 -name '*.svg' -exec ln -sf {} ~/.icons/tela-priority/scalable/devices/ \;
omarchy restart shellThis raised the count from 20 to 46 of 67, deterministically. Two details matter: the scan's find has no -L, so a symlink to the theme directory is invisible to it and the links must be per-file; and the directory deliberately has no index.theme, so GTK/Qt do not treat it as a theme.
View raw text
### System details
Omarchy 4.0.0-1, Hyprland 0.56.2, `quickshell-git 0.3.0.r20.g28771c7-1`, Arch Linux, kernel 7.1.8-arch1-3, Intel i5-8350U / UHD 620. `/usr/share` on btrfs. Icon theme set to `Tela-circle` (`tela-circle-icon-theme-all 2026_07_07-1`).
### What is wrong?
`shell/services/AppLibrary.qml` builds its own icon index and consults it **before** the themed lookup, so the icon theme selected in `org.gnome.desktop.interface icon-theme` is largely ignored for application icons in the menu/launcher.
The scan (`iconIndexScanCommand()`) walks every XDG icon directory and takes every `*/apps/*` and `*/devices/*` file:
```bash
dirs="$HOME/.icons $HOME/.local/share/icons";
IFS=":"; for d in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do dirs="$dirs $d/icons"; done; unset IFS;
for ext in svg png; do
for base in $dirs; do
[[ -d $base ]] && find "$base" \( -path "*/apps/*" -o -path "*/devices/*" \) -name "*.$ext" 2>/dev/null;
done
find /usr/share/pixmaps -maxdepth 1 -name "*.$ext" 2>/dev/null;
done
```
`indexIconLine()` keys purely on the file's basename and keeps the first hit:
```js
if (name.length > 0 && root.pendingIconIndex[name] === undefined)
root.pendingIconIndex[name] = value
```
and `iconSource()` gives that index precedence over `Quickshell.iconPath()`:
```js
var found = root.iconIndex[value]
if (found) return Util.fileUrl(found)
var themed = Quickshell.iconPath(value, true)
if (themed.length > 0) return themed
```
Nothing in this path knows which icon theme is active. Whichever theme on disk `find` happens to reach first wins.
**Consequence 1 — the selected theme loses to whatever is found first.** With `icon-theme` set to `Tela-circle`, only 20 of the 67 non-absolute `Icon=` names across my installed desktop entries resolved into `Tela-circle`; 42 came from `hicolor`/`breeze`/`Adwaita`, including names `Tela-circle` does provide:
```
NAME IN-THEME INDEX PICKS (first hit wins)
foot yes /usr/share/icons/hicolor/scalable/apps/foot.svg
mpv yes /usr/share/icons/hicolor/scalable/apps/mpv.svg
btop yes /usr/share/icons/hicolor/scalable/apps/btop.svg
kdenlive yes /usr/share/icons/hicolor/scalable/apps/kdenlive.svg
libreoffice-calc yes /usr/share/icons/hicolor/scalable/apps/libreoffice-calc.svg
```
Since `hicolor` holds each application's own upstream icon, the practical effect is that installing an icon theme changes little in the launcher, and which icons *do* change is arbitrary.
**Consequence 2 — the winner is not stable across rescans.** `find` output order is not guaranteed. On btrfs here, five consecutive identical scans produced identical *content* in five different *orders* (`diff <(sort a) <(sort b)` is empty; `diff a b` is not). So the same name can resolve to a different theme on the next scan. Because `refreshIcons()` re-runs the scan whenever a consumer opens — and per #6806 it can fire roughly once a second — icons visibly change with no user action. That is how I noticed this: icons flipped between themed and stock while I was doing nothing.
Note that the code comment describes the index as a *fallback* for icons Qt's themed lookup misses (newly installed apps), but it is currently the primary resolver.
### Steps to recreate
```bash
# 1. Install any icon theme that overlaps hicolor, e.g.
sudo pacman -S tela-circle-icon-theme
gsettings set org.gnome.desktop.interface icon-theme Tela-circle
omarchy restart shell
# 2. Replay the scan exactly as AppLibrary.qml runs it
scan() {
local dirs="$HOME/.icons $HOME/.local/share/icons"
IFS=":"; for d in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do dirs="$dirs $d/icons"; done; unset IFS
for ext in svg png; do
for base in $dirs; do
[[ -d $base ]] && find "$base" \( -path "*/apps/*" -o -path "*/devices/*" \) -name "*.$ext" 2>/dev/null
done
find /usr/share/pixmaps -maxdepth 1 -name "*.$ext" 2>/dev/null
done
}
scan > /tmp/s1; scan > /tmp/s2
# 3. The active theme loses to hicolor even though it ships the icon
ls /usr/share/icons/Tela-circle/scalable/apps/foot.svg # exists
grep -m1 '/foot\.' /tmp/s1 # -> hicolor/scalable/apps/foot.svg
# 4. Same content, different order on every run
diff <(sort /tmp/s1) <(sort /tmp/s2) | wc -l # 0
diff /tmp/s1 /tmp/s2 | wc -l # non-zero
```
Then open the Apps menu: icons come from a mix of themes, and change between openings.
### Expected
Application icons should come from the icon theme the user selected, and should not change on their own.
### Suggested fix
Either restrict the scan to the active theme plus its `Inherits` chain (then `hicolor`), or — probably simpler — restore the index to the fallback role its comment describes: try `Quickshell.iconPath()` first, since it already honours the icon theme, and fall back to the index only for names the themed lookup misses. Sorting each `find` (`find ... | sort`) would additionally make the outcome deterministic regardless of filesystem traversal order.
### Workaround (verified)
Force the wanted theme to the front of the scan by putting its app/device icons in `$HOME/.icons`, which the scan reads first:
```bash
mkdir -p ~/.icons/tela-priority/scalable/{apps,devices}
find /usr/share/icons/Tela-circle/scalable/apps -maxdepth 1 -name '*.svg' -exec ln -sf {} ~/.icons/tela-priority/scalable/apps/ \;
find /usr/share/icons/Tela-circle/scalable/devices -maxdepth 1 -name '*.svg' -exec ln -sf {} ~/.icons/tela-priority/scalable/devices/ \;
omarchy restart shell
```
This raised the count from 20 to 46 of 67, deterministically. Two details matter: the scan's `find` has no `-L`, so a symlink to the theme *directory* is invisible to it and the links must be per-file; and the directory deliberately has no `index.theme`, so GTK/Qt do not treat it as a theme.
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
- Fixed bypr/9408· confirmed
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 08:43 UTC