You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
opencode-cli serve CPU saturated (200%+) by repeated full-patch generation — only workaround is disabling snapshot #21699
When using OpenCode Desktop App (v1.4.0), the bundled opencode-cli serve backend process consistently exceeds 200% CPU during active agent sessions. The only effective workaround is setting "snapshot": false in config, which disables session change tracking and file revert/undo — a significant feature trade-off.
Root cause (CPU profiled):
Top frame: vcs.ts:63 accounts for ~65% of total sampled time
summarize() generates full unified patches on every file event when it only needs summary statistics
Environment:
Item
Detail
Machine
Apple M4 Max, 128 GB RAM, 4 TB SSD
OS
macOS 26.2 (Build 25C56), arm64
Client
OpenCode Desktop App v1.4.0
Backend
opencode-cli serve (bundled with Desktop App)
Project
Large monorepo (~51,000 files excl. node_modules/.git, 19 packages)
Suggested fixes (by priority):
Separate summary stats from full patch generation — summarize() only needs additions/deletions/files counts, which git diff --numstat provides cheaply. Generate actual patch strings only on demand when the user expands a specific file in the review UI.
Debounce + coalesce refreshVcs() — use a dirty-flag + debounce (300–1000 ms) so a burst of file writes triggers only one diff instead of clearing in-flight dedup state on every event.
Use git-native patch output instead of JS-side structuredPatch() — moves the O(N·D) algorithm out of the Node event loop entirely.
changed the title [-]perf: opencode-cli server CPU saturated (200%+) by repeated full-patch generation — only workaround is disabling snapshot[/-][+]opencode-cli serve CPU saturated (200%+) by repeated full-patch generation — only workaround is disabling snapshot[/+]on Apr 10, 2026
Adding some evidence here from a different angle that might shift the priority of fix # 2 (debounce + coalesce refreshVcs()) above fix # 1 (separate summary stats from full patches).
I was hitting the same 100%+ CPU symptom on a large monorepo, but when I looked at Activity Monitor while it was happening, the split was 89% system time vs 11% user time:
If structuredPatch() were the dominant cost, I'd expect that to show up as user time. 89% system time points at the kernel doing fork/exec/wait/SIGCHLD work. The cost is spawning git processes, not running the JS-side patch builder.
Looking at the process tree at the same moment, I had 53+ concurrent git status --porcelain=v1 --untracked-files=all --no-renames -z -- . targeting the same worktree
So in this case the bottleneck is the unbounded fan-out itself. Even with structuredPatch made cheap, an unbounded number of git status --untracked-files=all invocations stacking faster than they finish on a large repo is enough on its own to peg CPU.
(Filed the zombie-reaping side of this separately at #25883 since it seems like a distinct bug worth its own thread. 4,978 <defunct> children persisting for several minutes after activity stops.)
Activity
[-]perf: opencode-cli server CPU saturated (200%+) by repeated full-patch generation — only workaround is disabling snapshot[/-][+]opencode-cli serve CPU saturated (200%+) by repeated full-patch generation — only workaround is disabling snapshot[/+]ChrisJamesHassell commentedon May 5, 2026
Adding some evidence here from a different angle that might shift the priority of fix # 2 (debounce + coalesce
refreshVcs()) above fix # 1 (separate summary stats from full patches).I was hitting the same 100%+ CPU symptom on a large monorepo, but when I looked at Activity Monitor while it was happening, the split was 89% system time vs 11% user time:
If
structuredPatch()were the dominant cost, I'd expect that to show up as user time. 89% system time points at the kernel doing fork/exec/wait/SIGCHLD work. The cost is spawning git processes, not running the JS-side patch builder.Looking at the process tree at the same moment, I had 53+ concurrent
git status --porcelain=v1 --untracked-files=all --no-renames -z -- .targeting the same worktreeSo in this case the bottleneck is the unbounded fan-out itself. Even with
structuredPatchmade cheap, an unbounded number ofgit status --untracked-files=allinvocations stacking faster than they finish on a large repo is enough on its own to peg CPU.(Filed the zombie-reaping side of this separately at #25883 since it seems like a distinct bug worth its own thread. 4,978
<defunct>children persisting for several minutes after activity stops.)github-actions commentedon Jul 5, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.