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

Notifications never render: `screenObscured` is permanently true because `firstPartyServiceFor("omarchy.lock")` always returns null

Reported by rugobal · filed Sep 26, 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

Notifications never render: `screenObscured` is permanently true because `firstPartyServiceFor("omarchy.lock")` always returns nullrevision 1 · captured 2026-09-28 21:23 UTC via poll

Summary

Notification toasts never render on this install (Omarchy 4.0.3-4). The daemon receives notifications, but the toast overlay window is never mapped, Super + Shift + Alt + , (history replay) shows nothing, and toast state files pile up in ~/.local/state/omarchy/notifications/ instead of being archived to history/.

The notifications service hides its popup window while it believes the screen may be obscured:

// shell/plugins/notifications/Service.qml:60
readonly property bool screenObscured:
  !idleService
  || !idleService.screensaverStateKnown
  || idleService.screensaverWindowCount > 0
  || !lockService          // <- permanently true
  || lockService.locked

lockService is shell.firstPartyServiceFor("omarchy.lock"), which can never return anything but null:

  • shell/plugins/lock/manifest.json declares omarchy.capabilities: ["authentication"]
  • ShellRoot.ensureService() keeps authentication services out of the public service map entirely — "Never publish lock/polkit through ShellRoot._services" (it only does AuthServiceStore.put(key, inst))
  • ShellRoot.firstPartyServiceFor() only looks in _services, so serviceFor("omarchy.lock") is always null. pluginFirstPartyServiceFor() additionally restricts to ["omarchy.idle", "omarchy.media", "omarchy.nightlight", "omarchy.notifications"], so scoped plugin shells can't see it either.

So !lockService is always true → screenObscured is always true → popupWindow.visible (Service.qml:972) is always false and the toast expiry timer (ticking, Service.qml:1028) never runs. This looks unconditional for the stock shell, so it should affect every install with the lock plugin enabled.

Expected

Toasts appear in the top-right for their normal lifetime, then get archived to ~/.local/state/omarchy/notifications/history/.

Actual

Nothing is ever visible. hyprctl layers never shows an omarchy-notifications layer, and ~/.local/state/omarchy/notifications/*.json accumulates popup files that never expire (a 12-minute-old toast was still "on screen" during testing).

Steps to reproduce

  1. omarchy notification send "repro" "toast should appear"
  2. No toast appears in the top-right corner
  3. hyprctl layers | grep omarchy-notifications → empty
  4. ls ~/.local/state/omarchy/notifications/*.json → the toast's popup file exists and never moves to history/
  5. Every other screenObscured term is verifiably false:
  • omarchy-shell lock isLocked → false
  • omarchy-shell idle status → "screensaverWindows": 0; journal has omarchy idle ... screensaver-state-ready: 0 window(s)
  • no notifications.json (dnd off)

→ the !lockService term is the only one that can hold the guard.

Suggested fix

Treat a missing lock service as "not locked" instead of "screen obscured":

readonly property bool screenObscured:
  !idleService
  || !idleService.screensaverStateKnown
  || idleService.screensaverWindowCount > 0
  || (lockService && lockService.locked)

(If the notifications plugin is meant to see lock state again, omarchy.lock would need to be exposed through a non-authentication surface — but the guard change above is sufficient, since the lock service is unreachable by design.)

System details

Happy to test a fix and report back.

View raw text
## Summary

Notification toasts never render on this install (Omarchy 4.0.3-4). The daemon receives notifications, but the toast overlay window is never mapped, `Super + Shift + Alt + ,` (history replay) shows nothing, and toast state files pile up in `~/.local/state/omarchy/notifications/` instead of being archived to `history/`.

The notifications service hides its popup window while it believes the screen may be obscured:

```qml
// shell/plugins/notifications/Service.qml:60
readonly property bool screenObscured:
  !idleService
  || !idleService.screensaverStateKnown
  || idleService.screensaverWindowCount > 0
  || !lockService          // <- permanently true
  || lockService.locked
```

`lockService` is `shell.firstPartyServiceFor("omarchy.lock")`, which can never return anything but `null`:

- `shell/plugins/lock/manifest.json` declares `omarchy.capabilities: ["authentication"]`
- `ShellRoot.ensureService()` keeps authentication services out of the public service map entirely — "Never publish lock/polkit through ShellRoot._services" (it only does `AuthServiceStore.put(key, inst)`)
- `ShellRoot.firstPartyServiceFor()` only looks in `_services`, so `serviceFor("omarchy.lock")` is always `null`. `pluginFirstPartyServiceFor()` additionally restricts to `["omarchy.idle", "omarchy.media", "omarchy.nightlight", "omarchy.notifications"]`, so scoped plugin shells can't see it either.

So `!lockService` is always `true` → `screenObscured` is always `true` → `popupWindow.visible` (`Service.qml:972`) is always `false` and the toast expiry timer (`ticking`, `Service.qml:1028`) never runs. This looks unconditional for the stock shell, so it should affect every install with the lock plugin enabled.

## Expected

Toasts appear in the top-right for their normal lifetime, then get archived to `~/.local/state/omarchy/notifications/history/`.

## Actual

Nothing is ever visible. `hyprctl layers` never shows an `omarchy-notifications` layer, and `~/.local/state/omarchy/notifications/*.json` accumulates popup files that never expire (a 12-minute-old toast was still "on screen" during testing).

## Steps to reproduce

1. `omarchy notification send "repro" "toast should appear"`
2. No toast appears in the top-right corner
3. `hyprctl layers | grep omarchy-notifications` → empty
4. `ls ~/.local/state/omarchy/notifications/*.json` → the toast's popup file exists and never moves to `history/`
5. Every other `screenObscured` term is verifiably false:
   - `omarchy-shell lock isLocked` → `false`
   - `omarchy-shell idle status` → `"screensaverWindows": 0`; journal has `omarchy idle ... screensaver-state-ready: 0 window(s)`
   - no `notifications.json` (`dnd` off)

   → the `!lockService` term is the only one that can hold the guard.

## Suggested fix

Treat a missing lock service as "not locked" instead of "screen obscured":

```qml
readonly property bool screenObscured:
  !idleService
  || !idleService.screensaverStateKnown
  || idleService.screensaverWindowCount > 0
  || (lockService && lockService.locked)
```

(If the notifications plugin is meant to see lock state again, `omarchy.lock` would need to be exposed through a non-authentication surface — but the guard change above is sufficient, since the lock service is unreachable by design.)

## System details

- Omarchy 4.0.3-4 (Try Omarchy QEMU guest, single monitor, Hyprland)
- Debug log: https://logs.omarchy.org/3R5Kkh (expires in 24h — happy to re-upload)

Happy to test a fix and report back.

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 02:13 UTC