Machine pressure preflight
Owner ruling 2026-10-29 (D1-A): Cyberine must not make a laptop that is already out of memory or disk worse. Before it spawns a NEW worker process, Cyberine reads a memory signal, free disk on the repo's volume (statfs), and the 1-minute load average per core. The memory signal is the kernel's memory pressure level on macOS (sysctl kern.memorystatus_vm_pressure_level: 1 normal, 2 warn, 4 critical) and swap use on Linux (/proc/meminfo). On macOS swap use is shown but never gates, because macOS grows its swap files on demand, so used/total sits near 100% whenever any swap exists (measured 2026-10-29: swap 94% while the pressure level was 1 and 51% of memory was free). While any threshold is crossed it does not spawn more and says why. Work that is already running is never paused or killed.
cyberine doctor | grep -A1 '^Machine:'
# Machine:
# [PASS] memory pressure normal (level 1, limit warn), swap 94% (info), disk 119 GB free (limit 20), load 0.6/core (limit 2.0) - ok (macOS)
# [WARN] memory pressure warn (level 2, limit warn), swap 93% (info), ... - PRESSURE: memory pressure warn (level 2, limit warn, CYBERINE_PRESSURE_MEM_LEVEL) (macOS)
# [PASS] swap 62% (limit 80), disk 120 GB free (limit 20), load 0.9/core (limit 2.0) - ok (Linux)
cyberine run --ignore-pressure "<task>" # start anyway, and lift the hold for this run
CYBERINE_PRESSURE_MEM_LEVEL=4 cyberine run "<task>" # macOS: hold only at critical
CYBERINE_PRESSURE_SWAP_PCT=90 cyberine run "<task>" # Linux
CYBERINE_PRESSURE=off cyberine autopilot # no preflight for this processWhat each front end does when a threshold is crossed:
cyberine runfirst holds for up to 2 minutes, printing a[pressure] holding cyberine run ...line with the reasons and re-checking every 10 s; it starts as soon as the reading clears (a[pressure] resumingline), and refuses with exit 2, printing the reading and the env var to change, only when the limit still holds after the wait.CYBERINE_PRESSURE_HOLD_MSsets the wait (0refuses at once) andCYBERINE_PRESSURE_RECHECK_MSthe re-check interval.cyberine run --bgdoes not hold: it refuses at once, so the caller is never blocked.--ignore-pressureskips the check and setsCYBERINE_PRESSURE=offfor the run, its background child included.launchBackgroundRun(chat/run, the TUI's new-task key, the web control route) andOrchestrator.dispatch(npm run cli, the MCP orchestrate tool, the SDK, the web chat-completions bridge) refuse the same way; they have no--ignore-pressureflag, so use the env var there.- Long-running loops hold the next spawn: the workflow engine before each worker (
WorkflowEngine.runWorker, so every fleet wave and autopilot fleet dispatch), the autopilot session loop before each new session and before the brainstorm session, theautopilot lanesscheduler before each lane task, a workflow's child-run launch, and the autonomy loop's non-code gate. A held loop logs one[pressure] holding ...line, re-checks every 30 s, and logs one[pressure] resuming ...line when the reading clears. A stop request ends the hold: the STOP file for autopilot sessions,cyberine stop <runId>for a workflow run; theautopilot lanesscheduler waits until the reading clears. - The queue consumer leaves pending entries pending and reports
paused machine-pressure; cron skips due jobs until the next tick.
Configure the limits globally in ~/.cyberine/config.json ($CYBERINE_HOME/config.json); an env var always wins over the config key:
{ "pressure": { "enabled": true, "memLevel": 2, "swapPct": 80, "diskFreeGb": 20, "loadPerCore": 2.0 } }The env vars are in the Environment variables table: CYBERINE_PRESSURE, CYBERINE_PRESSURE_MEM_LEVEL, CYBERINE_PRESSURE_SWAP_PCT, CYBERINE_PRESSURE_DISK_FREE_GB, CYBERINE_PRESSURE_LOAD_PER_CORE, CYBERINE_PRESSURE_HOLD_MS, CYBERINE_PRESSURE_RECHECK_MS, and the test-only CYBERINE_PRESSURE_FAKE. npm run selftest:machine-pressure covers the evaluator (a macOS reading with swap 96% at level 1 passes, level 2 holds), env-over-config precedence, unknown readings, off, and a faked reading that makes cyberine run refuse. The selftest sweep runs every smoke with CYBERINE_PRESSURE=off unless the shell sets it.
Known Limitations and Deferred Work — machine pressure preflight
- Windows reads unknown. There is no memory or load reading on Windows, and an unknown reading never blocks, so only free disk is checked there.
- macOS gates on the kernel's level, not on how much memory is free. The level moves between normal and warn within seconds on a busy machine, so a hold can start and end quickly; raise
CYBERINE_PRESSURE_MEM_LEVELto4to hold only at critical. macOS swap use is informational. Linux uses swap percent only; the PSI file/proc/pressure/memoryis not read. Any reading that fails on any platform also counts as unknown, never as pressure. - A reading is a snapshot. It is taken just before a spawn (cached for 5 s), so a burst of workers admitted in the same instant all see the same numbers; the gate limits new waves, not the size of one wave.
- Only new spawns are held. Workers already running keep running even if they are what is filling swap; Cyberine never pauses or kills them.
- Judges are not gated. Judge calls (LLM, adversarial, diff) finish work already admitted, so they run even under pressure.
- Remote runs are not checked. With
CYBERINE_SERVER_URLset,cyberine rundoes not read this machine, because the work runs on the server. - A smoke run alone is gated.
npm run selftest:<name>outside the sweep inherits the live gate, so an engine smoke on a machine under pressure holds with a[pressure] holdingline; setCYBERINE_PRESSURE=offfor it.
Failover preflight — cost before a run spends anything (cyberine route, cyberine run)
Both cyberine route "<task>" (dry run, nothing dispatched) and cyberine run "<task>" (foreground and --bg, printed once to stderr before dispatch, no prompt) print one compact preflight block: the...
Command Code over ACP (Agent Client Protocol)
Run recipe: