cmux NEXT: host pressure monitor on the shared host budget (part 1, no UI)

Merged
TE
14d ago
+1011-0

Leo, about the freeze when cosmic-gray was saturated: "idk if cmux next should warn a bit earlier...", "gray is an m5 pro with 48gb of ram... just limit it nicer", "cosmic gray no it should be more cpu aware", and "just warn for now instead of prenveting stuff".

This is part 1 of 2: the monitor, with no UI. Part 2 adds the titlebar indicator, the popover, the critical banner, the gallery versions and the Debug toggle.

What it does

A new CmuxNextHostPressure package turns the shared host budget into a level: normal (nothing shown), warning or critical.

  • One budget, not caps of its own. It reads acpmux's host_pressure verdict (#24290, _acpmux/status / acpmux daemon status --json). When acpmux does not report one (an older daemon, or none running), it computes the same verdict on this Mac with the host-budget contract's formula and acpmux's reasons, in its words: load1 at or over performance cores x load1_per_core, memory pressure over the formula's pressure allowance, available RAM (kern.memorystatus_level) under min_available_ram_fraction, swap growing faster than swap_growth_mb_per_min. The optional override file is read in the contract's order ($ACPMUX_HOST_BUDGET, then ~/.config/cmux/host-budget.json, hosts.<host> over formula).
  • Warning sits below acpmux's line. Warning means one part of the budget is over, or the load has reached 0.75 of the budget's load limit. On a 12 P-core M5 Pro that is load 9. Critical means memory pressure is critical, or two parts are over at once. A busy CPU on its own is a busy Mac, not a freeze, so it never shows the banner.
  • No flicker. A level rises on the first verdict that reaches it. It falls only after 2 calm verdicts in a row, and a load warning holds until the load drops 0.10 of the limit below where it started.
  • Events where it can. Memory pressure comes from DispatchSource.makeMemoryPressureSource (event-driven), and its level replaces the sampled one at once. Load, swap and acpmux's verdict have no event, so they are read on one 15 s timer (2 s leeway) on a utility queue, never per frame. A missing acpmux verdict is asked for again only every 20th read. Only level changes reach the main actor.

Stop condition: stop(), which part 2 calls when the last window closes or the app quits. Cleanup: it cancels the memory pressure source, the timer and any read in flight, and ends the run: a source hop or a main-actor level change queued from an ended run is dropped. The timer is listed (reviewed) in plans/cmux-next/idle-wakeups.md.

Tests

CmuxNextHostPressureTests, with an injected pressure source:

  • level per part of the budget, and a busy-but-fine Mac that shows nothing;
  • hysteresis: calm samples, the load deadband, critical stepping down through warning;
  • escalation normal โ†’ warning โ†’ critical โ†’ normal through the monitor, with acpmux's verdict winning over the local one;
  • the event pressure replacing the sampled one, judged against the formula's allowance;
  • a stopped monitor applying nothing, a level change queued before stop() dropped, and a hop from an ended run ignored after a restart;
  • decoding acpmux's verdict as #24290 writes it, including null load1 and swap_mb_per_min and unknown pressure;
  • the formula defaults, the override order and the swap rate.

Observed / Not run

Observed: scripts/cmux-next/prepush-gate.py reports no new violations; cmux-ci compile-check --app passes (CmuxNextApp now depends on this package, so the app compile covers it). Not run on air-blue (no local builds): the compile check and the tests run on the fleet at this head and are reported in a comment.

Before/after: no harness number, because nothing starts the monitor in this PR. The app's idle wakeups stay the same (0 added). Part 2 starts it with one 15 s timer, about 4 wakeups/min while a window is open, listed in idle-wakeups.md.

No capture: this PR has no UI (the indicator is part 2).

Changelog

none

Refs manaflow-ai/cmuxterm-hq#2995
Leo-ask: ask-42afb6109a
Leo-ask: ask-427cc7d8f5 (Leo: "the whole app froze btw i got codex to put some issues down in cmuxterm-hq")

๐Ÿค– Generated with Claude Code

Agent session: Air-Blue