Skip to content

v0.4.0 — the menu-bar app

Latest

Choose a tag to compare

@domanski-ai domanski-ai released this 14 Jul 22:12

headroom v0.4.0 — the menu-bar app

The fleet now lives in your menu bar: a little head that fills like a battery.

Native menu-bar app (macOS, Tauri)

integrations/menubar — a real click-down panel, not a text menu:

  • The tray icon is the reading. A minifig-style head that fills like a
    battery with your fleet's average 5-hour battery (a current window
    contributes its level, a capped one an honest zero). Drawn at runtime,
    redrawn every minute. A dash means no live reading — and the icon applies
    the same fail-closed freshness contract as every other surface before it
    trusts a single number: exact schema, current freshness, sane clocks,
    live accounts only.
  • It drops down like a system panel. Right edge under the icon, native
    rounded corners, dark panel, no chrome. The liquid-glass widget page
    renders straight onto it.
  • It never trades your data for an error page. Once the widget has
    loaded, a reachability blip keeps the last known view (which greys itself
    out honestly); the "is the tunnel up?" screen only appears on a cold start
    with the server genuinely unreachable.
  • Viewer, not a data path. Loopback-only navigation with a strict
    allowlist, no IPC granted to the page, no telemetry. The one network read
    beyond the page itself is the public /widget.json projection, over the
    same numeric-loopback-only socket as the reachability probe — bounded,
    single-flight, size-capped.

Build it with cargo tauri build (Rust 1.88+); see the
menubar README.

Codex conversation handoff

headroom handoff now moves a Codex conversation between codex homes,
with the same transactional guarantees as the Claude path: global lock,
no-clobber publication, crash-recoverable staging, source never modified.
--headless BATON runs codex exec resume on the target for automation.

The averages headline

The glanceable number everywhere — popover, SwiftBar, Übersicht cards — is
now the fleet's average battery (5h big, 7d compact), not the fullest
single tank. Limited windows count as an honest 0; held/stale readings never
move an average. Model-scoped weekly windows (Opus, Fable, …) now ride along
as extra per-account rows, without ever driving the account state — a scoped
cap doesn't block your other models.

Display integrity (three real-world bugs)

All three were found by actually running the widgets against a live fleet:

  • verified_local slots rendered as held. The display layer held any
    account not verified via the provider — while the router happily routed
    it. Display now accepts exactly the trust states routing accepts.
  • A failed refresh attempt demoted everything. An inline refresh racing
    another collector flashed every surface to "held, never promoted to live"
    with a seconds-old snapshot on disk. Demotion now happens only when the
    snapshot is genuinely outside the freshness window — and usage.json
    carries a non-demoting refresh_attempt_failed diagnostic so a dying
    collector is visible early.
  • 34 milliseconds of clock skew held the entire fleet. The freshness
    guard treated any future evaluated_at as untrustworthy — including
    sub-second NTP drift between server and viewer. It now tolerates 10
    seconds of skew; genuinely future-dated feeds still hold.

A rate-limited rate-limit check no longer strands you

Reported by a user on X: when the usage endpoint itself returns 429,
headroom held the account and refused to launch — "no account has proven
headroom" because the meter was busy. collect now carries the last
verified reading through the throttle window: age-bounded by the same
observation window as every reading, identity re-verified at routing time,
and only when the slot's current credential binding still matches the old
row. Expired or rejected cached tokens also hold with actionable codes
(claude_usage_token_expired / _rejected) instead of an opaque collector
error.

For scripts and wrappers

  • "reserved": true on an account keeps it tracked and on the dashboard
    but excluded from all routing — never picked, never a rotation or handoff
    target. For a login that belongs to another workflow.
  • An exported CLAUDE_CONFIG_DIR / CODEX_HOME is honoured as the
    initial slot when it names a registered account — your wrapper's routing
    decision is consumed, not overridden.
  • HEADROOM_LAUNCH_MARKER — a launch-commit handshake written
    immediately before the CLI spawns, so wrappers can distinguish "the CLI
    never started" from a real exit without racing anything. Strict no-clobber
    install: the marker path must be a fresh file, and an env-controlled path
    can never overwrite anything that exists.

Community

  • @alexknowshtml fixed identity binding for personal Max accounts with a
    null orgId (#1).
  • @hanamizuki aligned the dashboard with the widget's semantics for current
    windows on limited accounts (#6) — including a same-day review turnaround.
  • The 429 report above came from X. Keep them coming — real-fleet bug
    reports have driven most of this release.

439 tests. The batch went through two cross-model adversarial review rounds
before release; every P1 was fixed in-tree. Update: git pull — the menubar
app is the only component that needs a local build.