Skip to content

perf(engine): idle-zero, no background git while nobody is here - #69

Merged
ShriD5 merged 3 commits into
mainfrom
perf/idle-zero
Oct 30, 2026
Merged

ShriD5 merged 3 commits into
mainfrom
perf/idle-zero

Conversation

@ShriD5

@ShriD5 ShriD5 commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

First batch of the idle-zero work: when nothing is happening, the engine should spawn nothing and wake as little as possible.

Changes (one commit each)

  • PR watch only counts people as activity. Every RPC used to mark the app active, so an open but unattended window (the PR list refresh every 120 s, the usage gauge, held Watch* streams) kept the watch polling at full speed forever. user_activity::is_user_activity is an allowlist of calls a person makes (send, steer, answer input, open a chat, focus the window, PR and git actions, terminal input). A live agent run also counts, so an agent waiting on CI keeps it at full speed. The loop sleeps until the next poll is due instead of waking every 15 s, and rechecks every 5 min while paused.
  • diff_sync repairs on demand, not every 2 min. Repairs run when someone comes back (window focus, or the first action after 5 quiet minutes; at most every 2 min), for a chat's checkout at turn end, and on watcher overflow (unchanged). The safety repair runs every 15 min, only while a person acted in that window or a run is live, and skips live-watched checkouts untouched for 6 h. A noise filter drops watch events from root target/, any node_modules/ and .git objects/ / logs/ / worktrees/; source edits, refs, HEAD and index still kick.
  • Build-slot checks park while nothing can build. The 30 s leak check and the 10-minute make --version re-probe run only while a run is live or a terminal is open (plus 2 min of grace), then the task parks with no timer until a run starts or a terminal opens.

Before / after (headless engine, same method)

godspeed headless on a fresh temp data dir, a git repo with 10 linked worktrees and one chat in each, 2 PR watches on public upstream PRs, and a connected client behaving like an open but unattended window (WatchChats, WatchSpaces, WatchSessions, WatchCheckoutDiffs, WatchWorkspaceGitStatus held; ListPullRequestWatches every 120 s; nothing user-initiated after setup). 180 s settle, then 10 minutes measured. git/gh spawns are counted exactly by PATH wrappers; wakeups and CPU come from proc_pid_rusage (top's IDLEW samples miss most wakeups). Debug builds of origin/main and this branch, forced full rebuilds.

10 idle minutes Before After
git spawns 375 (250 git -C captures, 75 rev-parse, 50 branch) 25 (rev-parse, spaces upkeep)
gh spawns 14 14 (see below)
Engine CPU 2.85 s 2.90 s
Idle wakeups 81 355
Wakeups/s (all) 23.3 23.1
Launch window (first 180 s) git / gh 151 / 6 81 / 6
  • The diff capture cycle (all 10 checkouts every 2 min, about 7 git runs each) is gone.
  • Engine CPU and total wakeups are unchanged within noise. The engine's own idle CPU is dominated by other timers that this batch does not touch.
  • Idle-wakeup counts are small and noisy between runs (98, 81, 140 across three baseline runs), so I would not read anything into 81 vs 355.
  • The PR watch only slows after 30 idle minutes and pauses after 2 h, so a 10-minute window cannot show it. Unit tests prove the classification and the loop behaviour instead.

Tests

cargo test -p zeron-engine --lib -- pr_watch:: user_activity:: diff_sync build_slots::: 60 passed, including the new ones:

  • user_activity::people_count_and_background_polls_do_not
  • pr_watch::background_calls_let_the_watch_idle_and_live_runs_keep_it_awake
  • pr_watch::the_loop_sleeps_until_the_next_poll_is_due
  • diff_sync::repair_schedule_tests::* (repair scope, stale skip, rate-limited return repair, noise paths and events, and a real watcher: writes into target/, node_modules/ and .git/objects do not kick, a source edit does)
  • build_slots::checks_park_after_a_grace_once_nothing_is_live, a_parked_loop_has_no_timer_and_wakes_on_note_live

Every RPC call used to mark the app active, so an open but unattended
window (the PR list refresh every 120 s, the usage gauge, held Watch*
streams) kept the PR watch polling at full speed forever; it never
slowed after 30 minutes or paused after 2 hours.

- user_activity::is_user_activity: an allowlist of calls a person makes
  (send, steer, answer input, open a chat, focus the window, PR and git
  actions, terminal input). Background polls do not count.
- A live agent run counts as activity, so an agent waiting on CI keeps
  the watch at full speed; the idle clock starts when the run ends.
- The loop sleeps until the next poll is due (15 s to 15 min) instead of
  waking every 15 s, and rechecks every 5 min while paused.
Every checkout was re-captured (about six git runs each) every 120 s,
whether or not anyone was using the app.

- Repairs run when someone comes back (window focus, or the first action
  after 5 quiet minutes; at most every 2 min), for a chat's checkout at
  turn end, and on watcher overflow (unchanged: the hub's rescan kicks).
- The periodic safety repair runs every 15 min, only while a person acted
  in that window or a run is live, and skips live-watched checkouts
  untouched for 6 h. Unattended and idle it spawns no git.
- A noise filter drops watch events from root target/, any node_modules/
  and .git objects/logs/worktrees, so builds and installs no longer end
  in a capture. Source edits, refs, HEAD and index still kick.
The slot pool's leak check woke every 30 s and re-ran `make --version`
every 10 minutes for the life of the engine.

Checks now run only while a run is live or a terminal is open, plus two
minutes of grace so a leak at the end still gets its two quiet ticks;
then the task parks with no timer until a run starts or a terminal
opens, and checks at once on waking. The re-probe runs at most every
10 minutes, only while checks run.
@ShriD5
ShriD5 merged commit 4498a58 into main Oct 30, 2026
23 checks passed
@ShriD5
ShriD5 deleted the perf/idle-zero branch October 30, 2026 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant