Skip to content

menubar: idle energy on AC still high after #648 — 30s cadence x ~10 CPU-s Node spawn per tick, no change-detection, userInitiated QoS (and #648 is unreleased) #703

Description

@avs-io

Problem

Follow-up to #647 / #648 with on-machine measurements from a real user machine running the menubar (0.9.15). Idle energy use is still substantial, and the merged #648 work — which is real and large — (a) has not shipped in any release (latest tag mac-v0.9.15 predates the merge) and (b) leaves the dominant idle cost intact on AC power.

Evidence (15-min idle window, popover closed, AC, M-series; 12 GB codex + 1.2 GB claude/opencode session corpus)

  • The menubar spawned 15 codeburn status --format menubar-json --provider all --period today --no-optimize Node processes, one every 59-63s. Each burned 7.6-16.7 CPU-s, peaking at 112-360% CPU. Children alone: 142.8 CPU-s / 896s = 15.9% of a core, sustained indefinitely.
  • The Swift process itself is innocent: 0.32% avg. Activity Monitor attributes the children to the app.
  • Direct measurement of one fetch: today = 9.9 CPU-s, 2.1 GB peak footprint, 93B instructions; week = 15.8 CPU-s. Cold start is not the cost: bare node -e 0 = 0.03s, codeburn --version = 0.4s. sample of a live child shows V8 GC (ConcurrentMarking), UTF-8 decode, and JSON stringify — i.e. a full re-parse of today's sessions on every tick.
  • Total: ~16.2% of a core sustained while idle, nearly all of it in the per-tick subprocess re-parse.

What #648 already covers (merged, unreleased)

Display-sleep gating, popover-closed payload trimming, battery/low-power backoff, scoped activity assertions, cadence picker. On battery this will land a big cut once released.

Proposed fix (remainder, cheapest first)

  1. Ship a release containing menubar: cut idle energy (video-call-class drain, fixes #647) #648 — users currently have none of it.
  2. Change-detection guard (menubar: cut idle energy (video-call-class drain, fixes #647) #648 ladder item 1, unimplemented): stat provider data roots before spawning; skip the tick when nothing changed since the last successful fetch. Most idle ticks are no-ops.
  3. AC backoff: RefreshCadence.interval returns 30s whenever on AC with the popover closed (RefreshCadence.swift:34); back off to 2-5 min closed-popover regardless of power source (popover-open stays 30s).
  4. QoS: background-tick spawns still run .userInitiated (Security/CodeburnCLI.swift:98; menubar: cut idle energy (video-call-class drain, fixes #647) #648 only edited the comment). Use .utility for timer ticks, keep .userInitiated for interactive fetches.
  5. Incremental today-parse in the CLI: persist a per-file byte-offset checkpoint so a refresh parses only appended JSONL bytes; cuts the 8-16 CPU-s per genuine refresh to sub-second and benefits TUI/MCP too.

Quota-poll network behavior is tracked separately in #701.

Measurements: process-table sampling at 2s/5s intervals plus /usr/bin/time -l and sample(1); happy to attach raw CSVs. Items 2-4 are small and I'll put up a PR; items 1 and 5 are flagged for maintainer judgment (release timing, CLI architecture).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions