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

Launching… OSD can remain indefinitely due to unordered show/close IPC calls

Reported by brianmsm · filed Aug 25, 2026 · open at source

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

Launching… OSD can remain indefinitely due to unordered show/close IPC callsrevision 1 · captured 2026-09-28 21:24 UTC via poll

System details

  • AMD Ryzen AI 9 365 / Radeon 880M
  • Omarchy 4.0.1-1
  • Hyprland 0.56.2
  • Quickshell 0.3.1-1

What's wrong?

The Launching … OSD can remain visible indefinitely after the launched application has already mapped and become active. I have seen this twice; the latest occurrence was launching TIDAL Hi-Fi from the Omarchy app menu.

Expected: the OSD closes when the app maps, or at worst after the 15-second launch timeout.

Actual: the app launches successfully, but the OSD remains until manually closed with:

omarchy-shell osd close

A privacy-cropped screenshot is available if needed. The full omarchy-debug output is intentionally not attached because it contains unrelated local identifiers and network logs; the system details above and deterministic reproduction contain the relevant evidence.

Probable root cause: unordered detached show / close IPC calls

In shell/services/AppLibrary.qml, launchDelay waits 2 seconds, sets launchOsdOpen = true, and dispatches this asynchronously:

Quickshell.execDetached(["omarchy-shell", "osd", "show", JSON.stringify({
  icon: "󱓞",
  message: root.launchOsdMessage,
  duration: 0
})])

If the app maps at approximately the same time, onValuesChanged() or onActiveToplevelChanged() calls closeLaunchFeedback(). That stops launchTimeout, dispatches a separate detached osd close process, and sets launchOsdOpen = false.

The two detached IPC client processes are not ordered. The close request can reach Quickshell first, followed by the delayed show. In that ordering:

  1. close closes an OSD that is not open yet.
  2. AppLibrary marks the OSD closed and stops the 15-second timeout.
  3. show arrives afterward with duration: 0.
  4. Nothing remains that can close it, so it stays indefinitely.

This timing explains why the problem is intermittent and more likely for apps that map around the 2-second launch-feedback threshold.

Deterministic IPC-level reproduction

Running the same show and close commands concurrently reproduces the unordered final state:

payload='{"icon":"󱓞","message":"Race test","duration":0}'
open=0
closed=0
for i in $(seq 1 100); do
  omarchy-shell osd show "$payload" >/dev/null &
  omarchy-shell osd close >/dev/null &
  wait
  state=$(omarchy-shell osd state)
  if [[ $state == open ]]; then
    ((open+=1))
    omarchy-shell osd close >/dev/null
  else
    ((closed+=1))
  fi
done
printf 'open=%d closed=%d\n' "$open" "$closed"

Observed:

open=53 closed=47

A finite OSD duration provides a reliable backstop. Repeating 100 iterations with duration: 20 and checking after 30 ms resulted in:

open=0 closed=100

Possible fixes

  • Minimal/defensive: give the launch OSD a finite duration (for example 13,000 ms, the time remaining between the 2-second display delay and the existing 15-second overall timeout). Normal app mapping can still close it early, but an inverted IPC ordering can no longer strand it forever.
  • Stronger: serialize/acknowledge the show before allowing close, or pass a launch generation/token to the OSD so a close can cancel a not-yet-processed show for that generation.

This appears related in symptom, but not identical in cause, to PR #6413 (Stop stranding the Launching… OSD when a second app is launched). That fix preserved launchOsdOpen across a second launch; this is another path where the QML-side flag and actual OSD state diverge.

View raw text
## System details

- AMD Ryzen AI 9 365 / Radeon 880M
- Omarchy 4.0.1-1
- Hyprland 0.56.2
- Quickshell 0.3.1-1

## What's wrong?

The `Launching …` OSD can remain visible indefinitely after the launched application has already mapped and become active. I have seen this twice; the latest occurrence was launching TIDAL Hi-Fi from the Omarchy app menu.

Expected: the OSD closes when the app maps, or at worst after the 15-second launch timeout.

Actual: the app launches successfully, but the OSD remains until manually closed with:

```bash
omarchy-shell osd close
```

A privacy-cropped screenshot is available if needed. The full `omarchy-debug` output is intentionally not attached because it contains unrelated local identifiers and network logs; the system details above and deterministic reproduction contain the relevant evidence.

## Probable root cause: unordered detached `show` / `close` IPC calls

In `shell/services/AppLibrary.qml`, `launchDelay` waits 2 seconds, sets `launchOsdOpen = true`, and dispatches this asynchronously:

```qml
Quickshell.execDetached(["omarchy-shell", "osd", "show", JSON.stringify({
  icon: "󱓞",
  message: root.launchOsdMessage,
  duration: 0
})])
```

If the app maps at approximately the same time, `onValuesChanged()` or `onActiveToplevelChanged()` calls `closeLaunchFeedback()`. That stops `launchTimeout`, dispatches a separate detached `osd close` process, and sets `launchOsdOpen = false`.

The two detached IPC client processes are not ordered. The `close` request can reach Quickshell first, followed by the delayed `show`. In that ordering:

1. `close` closes an OSD that is not open yet.
2. AppLibrary marks the OSD closed and stops the 15-second timeout.
3. `show` arrives afterward with `duration: 0`.
4. Nothing remains that can close it, so it stays indefinitely.

This timing explains why the problem is intermittent and more likely for apps that map around the 2-second launch-feedback threshold.

## Deterministic IPC-level reproduction

Running the same `show` and `close` commands concurrently reproduces the unordered final state:

```bash
payload='{"icon":"󱓞","message":"Race test","duration":0}'
open=0
closed=0
for i in $(seq 1 100); do
  omarchy-shell osd show "$payload" >/dev/null &
  omarchy-shell osd close >/dev/null &
  wait
  state=$(omarchy-shell osd state)
  if [[ $state == open ]]; then
    ((open+=1))
    omarchy-shell osd close >/dev/null
  else
    ((closed+=1))
  fi
done
printf 'open=%d closed=%d\n' "$open" "$closed"
```

Observed:

```text
open=53 closed=47
```

A finite OSD duration provides a reliable backstop. Repeating 100 iterations with `duration: 20` and checking after 30 ms resulted in:

```text
open=0 closed=100
```

## Possible fixes

- Minimal/defensive: give the launch OSD a finite duration (for example 13,000 ms, the time remaining between the 2-second display delay and the existing 15-second overall timeout). Normal app mapping can still close it early, but an inverted IPC ordering can no longer strand it forever.
- Stronger: serialize/acknowledge the `show` before allowing `close`, or pass a launch generation/token to the OSD so a close can cancel a not-yet-processed show for that generation.

This appears related in symptom, but not identical in cause, to PR #6413 (`Stop stranding the Launching… OSD when a second app is launched`). That fix preserved `launchOsdOpen` across a second launch; this is another path where the QML-side flag and actual OSD state diverge.

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 09:05 UTC