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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions