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

Agents panel renders an arbitrarily stale usage record identically to a live one (per-record `updatedAt` is never read)

Reported by millsaj · filed Sep 21, 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

Agents panel renders an arbitrarily stale usage record identically to a live one (per-record `updatedAt` is never read)revision 1 · captured 2026-09-28 21:23 UTC via poll

What happens

Every packaged collector stamps its record with updatedAt — omarchy-agent-usage-claude, -codex and -fireworks all write it, and it is in the record on disk:

$ jq -r '.updatedAt, .schemaVersion' ~/.local/state/omarchy/agents/usage/claude-work.json
2026-09-21T22:30:27.840596+00:00
1

The panel never reads it. A record whose writer has stopped keeps being drawn as current, indefinitely, with no visual difference from one written a minute ago.

Where it goes

updatedAt appears five times in the plugin, and all five belong to cross-device sync:

  • Main.qml:654/655 and Main.qml:696 — writing the sync aggregate and this device's snapshot.
  • Main.qml:274 — syncUpdatedAt: aggregateData && aggregateData.updatedAt ? ... : "", read off aggregateData.
  • Main.qml:300 — aggregateUpdatedAtMs, same source.

The per-record timestamp is skipped at exactly the point it is in scope. Main.qml:274 sits in the block that builds the display model straight off record, two lines under record.modelUsage and record.hasLocalStats — record.updatedAt is simply never taken.

The two sync properties are then dead ends too: grep -rn "UpdatedAt" . across Main.qml, Panel.qml, Agent.qml and manifest.json returns those five lines and nothing else. Neither syncUpdatedAt nor aggregateUpdatedAtMs is consumed by Panel.qml. So the panel has no freshness indicator on any path, local or synced — the plumbing exists on both and terminates.

How I hit it

A second Claude account via an out-of-tree collector (the pattern in #9383 and discussion #10684). Because omarchy-agent-usage-update only globs $OMARCHY_PATH/bin/, that collector is driven by its own systemd timer. The timer silently disarmed itself and the record froze at Sat 19 Sep 12:22:

$ ls -l ~/.local/state/omarchy/agents/usage/
-rw------- 1 user user 1496 Sep 21 13:57 claude.json
-rw------- 1 user user 2147 Sep 19 12:22 claude-work.json     <- two days stale
-rw------- 1 user user  680 Sep 21 13:57 codex.json
-rw------- 1 user user  539 Sep 21 13:57 fireworks.json

For two days the panel showed that frozen session and weekly percentage beside the live personal account's, with nothing to tell them apart. I only noticed because the numbers looked implausibly low.

Why it isn't just a third-party-collector problem

The stale writer is what made it obvious, but nothing about the display gap is specific to out-of-tree collectors:

  • A packaged collector that starts failing leaves its last good record in place. In collect(), a failed or non-JSON run logs omarchy-agent-usage-update: <agent> collector failed to stderr and returns 1 before the mktemp/mv, so the existing file is untouched — the panel keeps drawing the last successful run with no indication anything has broken since.
  • With syncMode on, a machine that goes offline keeps contributing its last snapshot to the aggregate.

Usage percentages and reset countdowns are exactly the kind of number where "old" and "current" need to be distinguishable. A stale limits reading is not merely out of date — after a window rolls over it is a different window's number.

Suggestion

The data is already there and already plumbed; it just terminates. Past a threshold — a couple of refresh cycles, or the record's own cadence — dim the tab or add a "last updated 2d ago" line. The existing mechanism where an auth or endpoint problem replaces the plan line and repeats in a card looks like the natural home for it.

Happy to send a PR if a shape is preferred.

Environment

  • omarchy 4.0.4-1, omarchy-settings 4.0.4-1
  • Plugin files as shipped, dated Sep 15 (/usr/share/omarchy/shell/plugins/agents/)
  • systemd 261.2-1-arch, Linux 7.2.5-3-omarchy
View raw text
## What happens

Every packaged collector stamps its record with `updatedAt` — `omarchy-agent-usage-claude`, `-codex` and `-fireworks` all write it, and it is in the record on disk:

```
$ jq -r '.updatedAt, .schemaVersion' ~/.local/state/omarchy/agents/usage/claude-work.json
2026-09-21T22:30:27.840596+00:00
1
```

The panel never reads it. A record whose writer has stopped keeps being drawn as current, indefinitely, with no visual difference from one written a minute ago.

## Where it goes

`updatedAt` appears five times in the plugin, and all five belong to cross-device sync:

- `Main.qml:654`/`655` and `Main.qml:696` — *writing* the sync aggregate and this device's snapshot.
- `Main.qml:274` — `syncUpdatedAt: aggregateData && aggregateData.updatedAt ? ... : ""`, read off `aggregateData`.
- `Main.qml:300` — `aggregateUpdatedAtMs`, same source.

The per-record timestamp is skipped at exactly the point it is in scope. `Main.qml:274` sits in the block that builds the display model straight off `record`, two lines under `record.modelUsage` and `record.hasLocalStats` — `record.updatedAt` is simply never taken.

The two sync properties are then dead ends too: `grep -rn "UpdatedAt" .` across `Main.qml`, `Panel.qml`, `Agent.qml` and `manifest.json` returns those five lines and nothing else. Neither `syncUpdatedAt` nor `aggregateUpdatedAtMs` is consumed by `Panel.qml`. So the panel has no freshness indicator on any path, local or synced — the plumbing exists on both and terminates.

## How I hit it

A second Claude account via an out-of-tree collector (the pattern in #9383 and discussion #10684). Because `omarchy-agent-usage-update` only globs `$OMARCHY_PATH/bin/`, that collector is driven by its own systemd timer. The timer silently disarmed itself and the record froze at Sat 19 Sep 12:22:

```
$ ls -l ~/.local/state/omarchy/agents/usage/
-rw------- 1 user user 1496 Sep 21 13:57 claude.json
-rw------- 1 user user 2147 Sep 19 12:22 claude-work.json     <- two days stale
-rw------- 1 user user  680 Sep 21 13:57 codex.json
-rw------- 1 user user  539 Sep 21 13:57 fireworks.json
```

For two days the panel showed that frozen session and weekly percentage beside the live personal account's, with nothing to tell them apart. I only noticed because the numbers looked implausibly low.

## Why it isn't just a third-party-collector problem

The stale writer is what made it obvious, but nothing about the display gap is specific to out-of-tree collectors:

- A packaged collector that starts failing leaves its last good record in place. In `collect()`, a failed or non-JSON run logs `omarchy-agent-usage-update: <agent> collector failed` to stderr and returns `1` *before* the `mktemp`/`mv`, so the existing file is untouched — the panel keeps drawing the last successful run with no indication anything has broken since.
- With `syncMode` on, a machine that goes offline keeps contributing its last snapshot to the aggregate.

Usage percentages and reset countdowns are exactly the kind of number where "old" and "current" need to be distinguishable. A stale limits reading is not merely out of date — after a window rolls over it is a different window's number.

## Suggestion

The data is already there and already plumbed; it just terminates. Past a threshold — a couple of refresh cycles, or the record's own cadence — dim the tab or add a "last updated 2d ago" line. The existing mechanism where an auth or endpoint problem replaces the plan line and repeats in a card looks like the natural home for it.

Happy to send a PR if a shape is preferred.

## Environment

- omarchy 4.0.4-1, omarchy-settings 4.0.4-1
- Plugin files as shipped, dated Sep 15 (`/usr/share/omarchy/shell/plugins/agents/`)
- systemd 261.2-1-arch, Linux 7.2.5-3-omarchy

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 19:24 UTC