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

agents widget: Codex limits intermittently 'unavailable' — rpc_request select()/readline() buffering race

Reported by JackAustin-Ipau · filed Oct 9, 2026 · open at source

Decided: Out of scope — decided by agents · verified 2 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 widget: Codex limits intermittently 'unavailable' — rpc_request select()/readline() buffering racerevision 1 · captured 2026-10-09 22:30 UTC via poll

System details

Intel Core Ultra X7 358H, Intel Arc B390 (Panther Lake), Omarchy 4.0.4-1, codex-cli 0.162.0

What's wrong?

The omarchy.agents bar widget usually shows "Codex limits unavailable" for Codex, even though the account is signed in and codex app-server answers fine. The record at ~/.local/state/omarchy/agents/usage/codex.json has "usageStatusText":"Codex limits unavailable","authHelpText":"account/read", meaning rpc_request raised TimeoutError("account/read"). Claude limits display fine.

Root cause: in bin/omarchy-agent-usage-codex, rpc_request() calls select() on proc.stdout and then proc.stdout.readline(). proc.stdout is a buffered TextIOWrapper, so readline() can pull several lines from the pipe into Python's buffer. Right after initialized, codex app-server sends a remoteControl/status/changed notification and then the account/read response, about 0.5s in and often in a single chunk. readline() returns the notification, the response sits in the userspace buffer, select() on the fd never reports readable again, and the loop spins until the 4s timeout. It's timing-dependent, so it fails intermittently.

Repro:

for i in 1 2 3 4; do omarchy-agent-usage-codex --force | jq -c '[.limits|length, .usageStatusText]'; done

On my machine the stock collector failed 3 out of 4 runs ([0,"Codex limits unavailable"]). The patched version below succeeded 4 out of 4 with the same environment ([2,""], plan plus, session and weekly limits populated).

Fix: read the raw fd with os.read and split lines ourselves, so buffered data is consumed before blocking on select(). Leftover bytes are kept per fd between calls, so a response that arrives with an earlier one isn't lost.

--- a/bin/omarchy-agent-usage-codex
+++ b/bin/omarchy-agent-usage-codex
@@ -474,24 +474,34 @@
     return stats
 
 
+_rpc_pending = {}
+
+
 def rpc_request(proc, request_id, method, params=None, timeout=8):
   payload = {"id": request_id, "method": method, "params": params or {}}
   proc.stdin.write(json.dumps(payload) + "\n")
   proc.stdin.flush()
+  fd = proc.stdout.fileno()
+  buf = _rpc_pending.setdefault(fd, b"")
   deadline = time.time() + timeout
   while time.time() < deadline:
-    ready, _, _ = select.select([proc.stdout], [], [], 0.25)
+    while b"\n" in buf:
+      line, buf = buf.split(b"\n", 1)
+      _rpc_pending[fd] = buf
+      try:
+        message = json.loads(line)
+      except Exception:
+        continue
+      if message.get("id") == request_id:
+        return message
+    ready, _, _ = select.select([fd], [], [], 0.25)
     if not ready:
       continue
-    line = proc.stdout.readline()
-    if not line:
+    chunk = os.read(fd, 65536)
+    if not chunk:
       break
-    try:
-      message = json.loads(line)
-    except Exception:
-      continue
-    if message.get("id") == request_id:
-      return message
+    buf += chunk
+    _rpc_pending[fd] = buf
   raise TimeoutError(method)
 
 
View raw text
### System details
Intel Core Ultra X7 358H, Intel Arc B390 (Panther Lake), Omarchy 4.0.4-1, codex-cli 0.162.0

### What's wrong?

The `omarchy.agents` bar widget usually shows **"Codex limits unavailable"** for Codex, even though the account is signed in and `codex app-server` answers fine. The record at `~/.local/state/omarchy/agents/usage/codex.json` has `"usageStatusText":"Codex limits unavailable","authHelpText":"account/read"`, meaning `rpc_request` raised `TimeoutError("account/read")`. Claude limits display fine.

**Root cause:** in `bin/omarchy-agent-usage-codex`, `rpc_request()` calls `select()` on `proc.stdout` and then `proc.stdout.readline()`. `proc.stdout` is a buffered `TextIOWrapper`, so `readline()` can pull several lines from the pipe into Python's buffer. Right after `initialized`, codex app-server sends a `remoteControl/status/changed` notification and then the `account/read` response, about 0.5s in and often in a single chunk. `readline()` returns the notification, the response sits in the userspace buffer, `select()` on the fd never reports readable again, and the loop spins until the 4s timeout. It's timing-dependent, so it fails intermittently.

**Repro:**
```bash
for i in 1 2 3 4; do omarchy-agent-usage-codex --force | jq -c '[.limits|length, .usageStatusText]'; done
```
On my machine the stock collector failed 3 out of 4 runs (`[0,"Codex limits unavailable"]`). The patched version below succeeded 4 out of 4 with the same environment (`[2,""]`, plan `plus`, session and weekly limits populated).

**Fix:** read the raw fd with `os.read` and split lines ourselves, so buffered data is consumed before blocking on `select()`. Leftover bytes are kept per fd between calls, so a response that arrives with an earlier one isn't lost.

```diff
--- a/bin/omarchy-agent-usage-codex
+++ b/bin/omarchy-agent-usage-codex
@@ -474,24 +474,34 @@
     return stats
 
 
+_rpc_pending = {}
+
+
 def rpc_request(proc, request_id, method, params=None, timeout=8):
   payload = {"id": request_id, "method": method, "params": params or {}}
   proc.stdin.write(json.dumps(payload) + "\n")
   proc.stdin.flush()
+  fd = proc.stdout.fileno()
+  buf = _rpc_pending.setdefault(fd, b"")
   deadline = time.time() + timeout
   while time.time() < deadline:
-    ready, _, _ = select.select([proc.stdout], [], [], 0.25)
+    while b"\n" in buf:
+      line, buf = buf.split(b"\n", 1)
+      _rpc_pending[fd] = buf
+      try:
+        message = json.loads(line)
+      except Exception:
+        continue
+      if message.get("id") == request_id:
+        return message
+    ready, _, _ = select.select([fd], [], [], 0.25)
     if not ready:
       continue
-    line = proc.stdout.readline()
-    if not line:
+    chunk = os.read(fd, 65536)
+    if not chunk:
       break
-    try:
-      message = json.loads(line)
-    except Exception:
-      continue
-    if message.get("id") == request_id:
-      return message
+    buf += chunk
+    _rpc_pending[fd] = buf
   raise TimeoutError(method)
 
 
```

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 23:56 UTC