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

Notification actions are advertised in GetCapabilities but never rendered: action buttons are silently dropped

Reported by fooblahblah · filed Sep 2, 2026 · open at source

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

Notification actions are advertised in GetCapabilities but never rendered: action buttons are silently droppedrevision 1 · captured 2026-09-28 21:24 UTC via poll

What happened

The notification server advertises the freedesktop actions capability, but the
shell never renders action buttons. Senders are told actions are supported, send
them, and every one is silently dropped — only the default action (the
whole-card click) is reachable.

$ busctl --user call org.freedesktop.Notifications /org/freedesktop/Notifications \
    org.freedesktop.Notifications GetServerInformation
ssss "quickshell" "quickshell" "" "1.2"

$ busctl --user call org.freedesktop.Notifications /org/freedesktop/Notifications \
    org.freedesktop.Notifications GetCapabilities
as 6 "persistence" "body" "body-markup" "body-hyperlinks" "actions" "icon-static"

The actions do arrive. A dbus-monitor trace of a Chromium extension
notification carrying two buttons:

array [
   string "0"        string "Delete"
   string "1"        string "Mark as read"
   string "default"  string "Activate"
]

The toast rendered from that payload has no buttons.

Expected

Actions other than default render as buttons on the toast and invoke the
sender's action when clicked — the behaviour of every other daemon that
advertises actions (mako, swaync, dunst, KDE's Plasma daemon).

Alternatively, if this is deliberate, actions should be dropped from
GetCapabilities and actionsSupported set to false, so senders can fall
back instead of emitting controls that go nowhere.

Steps to reproduce

notify-send -A "delete=Delete" -A "read=Mark as read" "Test" "Any buttons?"

The toast shows title and body only, and notify-send exits on timeout without
printing an action id. Real-world equivalent: any Gmail/Facebook web
notification in Chromium, which ship "Delete" / "Mark as read" / "Settings"
buttons that are visible on other desktops and absent here.

Where it comes from

plugins/notifications accepts the actions but has no path to draw them:

  • Service.qml:935 sets actionsSupported: true, and GetCapabilities

advertises actions accordingly.
- Service.qml:360 invokePopupDefault() walks ref.actions looking only for
identifier === "default" (line 380), ignores every other action, and falls
back to focusApp(entry) (line 395).
- components/NotificationCard.qml has a single card-wide MouseArea and no
repeater over actions — there is nowhere for a button to be drawn.
- NotificationLogic.js:206 POPUP_ROLES carries no actions role, so the
action list never reaches the model in the first place.

The live Notification objects are already retained in Service.liveRefs, so
the invocable action objects are present at render time. This looks like a
missing UI layer rather than a protocol gap.

Fix confirmed locally

Cloning the plugin (omarchy plugin clone omarchy.notifications) and adding
roughly 60 lines makes it work end to end:

  1. NotificationLogic.js — serialize notification.actions (skipping

default and label-less entries) into a new actions role, added to
POPUP_ROLES.
2. Service.qml — invokePopupAction(index, identifier) resolves the row
through liveRefs, calls action.invoke(), and dismisses. Restored and
replayed rows have actions cleared before append, since their sender died
with the previous server generation and their originalId may since have
been reused.
3. NotificationCard.qml — a right-aligned Repeater of Ui.Buttons,
declared after the card-wide MouseArea so buttons take their own clicks
instead of falling through to the default action. History rows leave the
property empty and are unchanged.

Verified with notify-send -A, which returned the action id delete on click,
and with a live Facebook notification in Chromium whose "Settings" button
correctly opened Chromium's per-site notification settings.

Happy to open a PR if the approach looks right.

System

  • Omarchy 4.0.2-1
  • quickshell 0.3.1-1, hyprland 0.56.2-1
  • Linux 7.1.9-arch1-2
  • Intel Core Ultra 9 285H / Intel Arc Pro 130T/140T (Arrow Lake-P)
  • Chromium 151.0.7922.173-1 (one affected sender; notify-send reproduces it

with no browser involved)

View raw text
## What happened

The notification server advertises the freedesktop `actions` capability, but the
shell never renders action buttons. Senders are told actions are supported, send
them, and every one is silently dropped — only the `default` action (the
whole-card click) is reachable.

```
$ busctl --user call org.freedesktop.Notifications /org/freedesktop/Notifications \
    org.freedesktop.Notifications GetServerInformation
ssss "quickshell" "quickshell" "" "1.2"

$ busctl --user call org.freedesktop.Notifications /org/freedesktop/Notifications \
    org.freedesktop.Notifications GetCapabilities
as 6 "persistence" "body" "body-markup" "body-hyperlinks" "actions" "icon-static"
```

The actions do arrive. A `dbus-monitor` trace of a Chromium extension
notification carrying two buttons:

```
array [
   string "0"        string "Delete"
   string "1"        string "Mark as read"
   string "default"  string "Activate"
]
```

The toast rendered from that payload has no buttons.

## Expected

Actions other than `default` render as buttons on the toast and invoke the
sender's action when clicked — the behaviour of every other daemon that
advertises `actions` (mako, swaync, dunst, KDE's Plasma daemon).

Alternatively, if this is deliberate, `actions` should be dropped from
`GetCapabilities` and `actionsSupported` set to `false`, so senders can fall
back instead of emitting controls that go nowhere.

## Steps to reproduce

```bash
notify-send -A "delete=Delete" -A "read=Mark as read" "Test" "Any buttons?"
```

The toast shows title and body only, and `notify-send` exits on timeout without
printing an action id. Real-world equivalent: any Gmail/Facebook web
notification in Chromium, which ship "Delete" / "Mark as read" / "Settings"
buttons that are visible on other desktops and absent here.

## Where it comes from

`plugins/notifications` accepts the actions but has no path to draw them:

- `Service.qml:935` sets `actionsSupported: true`, and `GetCapabilities`
  advertises `actions` accordingly.
- `Service.qml:360` `invokePopupDefault()` walks `ref.actions` looking only for
  `identifier === "default"` (line 380), ignores every other action, and falls
  back to `focusApp(entry)` (line 395).
- `components/NotificationCard.qml` has a single card-wide `MouseArea` and no
  repeater over actions — there is nowhere for a button to be drawn.
- `NotificationLogic.js:206` `POPUP_ROLES` carries no `actions` role, so the
  action list never reaches the model in the first place.

The live `Notification` objects are already retained in `Service.liveRefs`, so
the invocable action objects are present at render time. This looks like a
missing UI layer rather than a protocol gap.

## Fix confirmed locally

Cloning the plugin (`omarchy plugin clone omarchy.notifications`) and adding
roughly 60 lines makes it work end to end:

1. `NotificationLogic.js` — serialize `notification.actions` (skipping
   `default` and label-less entries) into a new `actions` role, added to
   `POPUP_ROLES`.
2. `Service.qml` — `invokePopupAction(index, identifier)` resolves the row
   through `liveRefs`, calls `action.invoke()`, and dismisses. Restored and
   replayed rows have `actions` cleared before append, since their sender died
   with the previous server generation and their `originalId` may since have
   been reused.
3. `NotificationCard.qml` — a right-aligned `Repeater` of `Ui.Button`s,
   declared after the card-wide `MouseArea` so buttons take their own clicks
   instead of falling through to the default action. History rows leave the
   property empty and are unchanged.

Verified with `notify-send -A`, which returned the action id `delete` on click,
and with a live Facebook notification in Chromium whose "Settings" button
correctly opened Chromium's per-site notification settings.

Happy to open a PR if the approach looks right.

## System

- Omarchy 4.0.2-1
- quickshell 0.3.1-1, hyprland 0.56.2-1
- Linux 7.1.9-arch1-2
- Intel Core Ultra 9 285H / Intel Arc Pro 130T/140T (Arrow Lake-P)
- Chromium 151.0.7922.173-1 (one affected sender; `notify-send` reproduces it
  with no browser involved)

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 10:32 UTC