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

Cloned shell plugins silently cannot read shell.json config (omarchy.lock clone loses all idle settings)

Reported by TripleU613 · filed Sep 8, 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

Cloned shell plugins silently cannot read shell.json config (omarchy.lock clone loses all idle settings)revision 1 · captured 2026-09-28 21:23 UTC via poll

Summary

A cloned shell plugin cannot read shell.json config, and the failure is completely silent —
every setting reads as its hardcoded default with no error, no warning, and no log line. A clone
of omarchy.lock loses all of its idle settings this way, so features the user has explicitly
enabled in shell.json never run and there is nothing to indicate why.

I lost a while to this: my cloned lock plugin reported lockScreensaver as disabled for weeks
while shell.json plainly had "lockScreensaver": true. The lock screensaver had never run once.

Cause

Two gates, both in shell/shell.qml:

1. `pluginShellFor(manifest)` (~line 725):

function pluginShellFor(manifest) {
  if (!manifest || manifest.__isFirstParty) return shell
  ...
}

Only first-party plugins get the real shell. Everything else gets a PluginShellApi proxy,
and that proxy has no `shellConfig` property at all (shell/services/PluginShellApi.qml
exposes appLibrary, bar, barConfig, idleConfig, and private _-prefixed members).

So shell.shellConfig.idle — which is exactly what the first-party plugins/lock/Service.qml
uses — is undefined in any clone of it.

2. `publicIdleConfigFor(manifest)` (~line 358):

function publicIdleConfigFor(manifest) {
  var metadata = manifest && Util.isPlainObject(manifest.omarchy) ? manifest.omarchy : null
  if (!metadata || String(metadata.clonedFrom || "") !== "omarchy.idle") return ({})
  ...
}

The proxy does expose idleConfig, but only for clones of omarchy.idle. A clone of
omarchy.lock has clonedFrom: "omarchy.lock", so this returns ({}) too.

Net result: a clone of omarchy.lock reads empty via both available paths.

Reproduction

  1. Clone omarchy.lock (manifest gets "omarchy": { "clonedFrom": "omarchy.lock" }).
  2. Enable the clone and disable the original in shell.json.
  3. Set an idle value the clone depends on, e.g.:
   "idle": { "screensaver": 150, "lock": 86400, "lockScreensaver": true,
             "lockScreensaverDelay": 20, "lockScreensaverDuration": 0 }
  1. The clone's idleConfig is ({}). Everything derived from it falls back to defaults —

lockScreensaver reads as not-true, so the feature stays off.

Observed via the clone's own IPC: ssEnabled: false while shell.json said true.

Why it is easy to misdiagnose

The fallback defaults can coincide with the configured values, which hides the problem. In my
case lockScreensaverDelay: 20 and lockScreensaverDuration: 0 were both the configured
values and the hardcoded defaults, so the status output looked almost correct — only the
boolean differed, and nothing anywhere said "config not readable."

Suggested fix

Any of these would have prevented it, roughly in order of preference:

  1. Let publicIdleConfigFor serve the idle block to clones that legitimately need it —

omarchy.lock reads shellConfig.idle as a first-party, so its clones need the same data.
Perhaps derive the allowance from what the source plugin itself reads, rather than
hardcoding a single clonedFrom value.
2. Expose a read-only, sanitised shellConfig on PluginShellApi generally.
3. At minimum, warn. A clone reading shell.shellConfig and getting undefined currently
gets silence; a console.warn naming the plugin and the unreachable key would turn this
from an invisible failure into a one-line diagnosis.

Workaround for plugin authors

Read the config file directly, falling back through both API paths first:

property var fileIdleConfig: ({})
readonly property var idleConfig: {
  if (shell && shell.shellConfig && shell.shellConfig.idle) return shell.shellConfig.idle
  if (shell && shell.idleConfig && Object.keys(shell.idleConfig).length > 0) return shell.idleConfig
  return fileIdleConfig
}

FileView {
  path: root.home + "/.config/omarchy/shell.json"
  watchChanges: true
  printErrors: false
  onLoaded: {
    try {
      var parsed = JSON.parse(text())
      root.fileIdleConfig = parsed && parsed.idle ? parsed.idle : ({})
    } catch (error) {
      root.fileIdleConfig = ({})
    }
  }
  onLoadFailed: root.fileIdleConfig = ({})
  onFileChanged: reload()
}

Confirmed working — the setting reads correctly after omarchy-restart-shell.

Environment

omarchyomarchy-dev 4.0.0.r2071.ga703092-1
quickshell0.3.1-1
Hyprland0.56.2-2
Kernel7.2.3-arch1-3 (Arch)

Related

Separately, Omarchy's lock blanks the display via DPMS ~5s after locking
(idleBlankTimer { interval: 5000 } → omarchy-brightness-display off). On hardware where
dpms enable does not retrain the link, that leaves the user unable to restore their display
by any input — filed upstream as hyprwm/Hyprland#16183. Worth noting because a defensive
modeset in omarchy-system-wake (rather than trusting dpmsStatus) would make Omarchy
resilient to it.

View raw text
## Summary

A cloned shell plugin cannot read `shell.json` config, and the failure is **completely silent** —
every setting reads as its hardcoded default with no error, no warning, and no log line. A clone
of `omarchy.lock` loses all of its `idle` settings this way, so features the user has explicitly
enabled in `shell.json` never run and there is nothing to indicate why.

I lost a while to this: my cloned lock plugin reported `lockScreensaver` as disabled for weeks
while `shell.json` plainly had `"lockScreensaver": true`. The lock screensaver had never run once.

## Cause

Two gates, both in `shell/shell.qml`:

**1. `pluginShellFor(manifest)`** (~line 725):

```qml
function pluginShellFor(manifest) {
  if (!manifest || manifest.__isFirstParty) return shell
  ...
}
```

Only first-party plugins get the real `shell`. Everything else gets a `PluginShellApi` proxy,
and that proxy has **no `shellConfig` property at all** (`shell/services/PluginShellApi.qml`
exposes `appLibrary`, `bar`, `barConfig`, `idleConfig`, and private `_`-prefixed members).

So `shell.shellConfig.idle` — which is exactly what the first-party `plugins/lock/Service.qml`
uses — is `undefined` in any clone of it.

**2. `publicIdleConfigFor(manifest)`** (~line 358):

```qml
function publicIdleConfigFor(manifest) {
  var metadata = manifest && Util.isPlainObject(manifest.omarchy) ? manifest.omarchy : null
  if (!metadata || String(metadata.clonedFrom || "") !== "omarchy.idle") return ({})
  ...
}
```

The proxy *does* expose `idleConfig`, but only for clones of `omarchy.idle`. A clone of
`omarchy.lock` has `clonedFrom: "omarchy.lock"`, so this returns `({})` too.

Net result: a clone of `omarchy.lock` reads empty via **both** available paths.

## Reproduction

1. Clone `omarchy.lock` (manifest gets `"omarchy": { "clonedFrom": "omarchy.lock" }`).
2. Enable the clone and disable the original in `shell.json`.
3. Set an `idle` value the clone depends on, e.g.:
   ```json
   "idle": { "screensaver": 150, "lock": 86400, "lockScreensaver": true,
             "lockScreensaverDelay": 20, "lockScreensaverDuration": 0 }
   ```
4. The clone's `idleConfig` is `({})`. Everything derived from it falls back to defaults —
   `lockScreensaver` reads as not-`true`, so the feature stays off.

Observed via the clone's own IPC: `ssEnabled: false` while `shell.json` said `true`.

## Why it is easy to misdiagnose

The fallback defaults can coincide with the configured values, which hides the problem. In my
case `lockScreensaverDelay: 20` and `lockScreensaverDuration: 0` were **both** the configured
values *and* the hardcoded defaults, so the status output looked almost correct — only the
boolean differed, and nothing anywhere said "config not readable."

## Suggested fix

Any of these would have prevented it, roughly in order of preference:

1. Let `publicIdleConfigFor` serve the `idle` block to clones that legitimately need it —
   `omarchy.lock` reads `shellConfig.idle` as a first-party, so its clones need the same data.
   Perhaps derive the allowance from what the source plugin itself reads, rather than
   hardcoding a single `clonedFrom` value.
2. Expose a read-only, sanitised `shellConfig` on `PluginShellApi` generally.
3. At minimum, **warn**. A clone reading `shell.shellConfig` and getting `undefined` currently
   gets silence; a `console.warn` naming the plugin and the unreachable key would turn this
   from an invisible failure into a one-line diagnosis.

## Workaround for plugin authors

Read the config file directly, falling back through both API paths first:

```qml
property var fileIdleConfig: ({})
readonly property var idleConfig: {
  if (shell && shell.shellConfig && shell.shellConfig.idle) return shell.shellConfig.idle
  if (shell && shell.idleConfig && Object.keys(shell.idleConfig).length > 0) return shell.idleConfig
  return fileIdleConfig
}

FileView {
  path: root.home + "/.config/omarchy/shell.json"
  watchChanges: true
  printErrors: false
  onLoaded: {
    try {
      var parsed = JSON.parse(text())
      root.fileIdleConfig = parsed && parsed.idle ? parsed.idle : ({})
    } catch (error) {
      root.fileIdleConfig = ({})
    }
  }
  onLoadFailed: root.fileIdleConfig = ({})
  onFileChanged: reload()
}
```

Confirmed working — the setting reads correctly after `omarchy-restart-shell`.

## Environment

| | |
|---|---|
| omarchy | omarchy-dev 4.0.0.r2071.ga703092-1 |
| quickshell | 0.3.1-1 |
| Hyprland | 0.56.2-2 |
| Kernel | 7.2.3-arch1-3 (Arch) |

## Related

Separately, Omarchy's lock blanks the display via DPMS ~5s after locking
(`idleBlankTimer { interval: 5000 }` → `omarchy-brightness-display off`). On hardware where
`dpms enable` does not retrain the link, that leaves the user unable to restore their display
by any input — filed upstream as hyprwm/Hyprland#16183. Worth noting because a defensive
modeset in `omarchy-system-wake` (rather than trusting `dpmsStatus`) would make Omarchy
resilient to it.

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