statusLine: event-driven refresh has no coalescing - a slow command degrades into N concurrent copies instead of one (Windows)
Preflight
- I searched existing issues. The closest are #57186 (asked for the 300 ms debounce to be configurable; closed
not plannedby the stale bot, whose closing message invites a new issue if still relevant) and #73785 (Windows statusline cancellation viataskkill /Toverloading WMI). This report is about a different mechanism: refreshes are requested faster than a slow command can finish, and nothing coalesces them. - Single issue.
Environment
- Claude Code native Windows build (
claude.exe), Windows 11 build 10.0.26200 - Node 22 via nvm4w. Process creation is expensive on this machine —
node -e 0alone is 829 ms median,cmd /c echois 124 ms — because endpoint security software inspects every process spawn. That amplifies the effect below, but does not cause it. settings.json:"statusLine": { "type": "command", "command": "npx -y ccstatusline@latest", "refreshInterval": 30 }— i.e. the install form recommended by ccstatusline's README
Problem
Per the statusline docs, the status line is re-run on an event-driven basis with a 300 ms debounce, in addition to refreshInterval. refreshInterval is the only user-facing knob, and it governs only the periodic timer. There appears to be no coalescing against the previous invocation: if the command takes longer than the interval between refresh requests, invocations overlap rather than queueing or being skipped.
With a status line command that takes ~10 s (see measurements below), a single session held 5 concurrent status line processes:
44248 11:21:33 node .../npx-cli.js -y ccstatusline@latest
38960 11:21:35 node .../npx-cli.js -y ccstatusline@latest
43428 11:21:37 node .../npx-cli.js -y ccstatusline@latest
43828 11:21:40 cmd /c ccstatusline
11324 11:21:41 node .../_npx/<hash>/.../ccstatusline.js
New ones appeared roughly every 2 s while the machine got progressively less responsive. Raising refreshInterval from 30 to 300 changed nothing during active turns, which is expected once you know the timer is not the source — but it is also the only lever the settings schema offers, so from the outside it looks like the setting is broken.
Note the interaction with slowness: the slower the command, the more concurrent copies of it run. That is the wrong direction, and it makes a slow status line self-amplifying rather than self-limiting.
Measurements
Same machine, same payload, medians of 5-6 runs:
| command | median |
|---|---|
| npx -y ccstatusline@latest | 9865 ms |
| node <path>/ccstatusline.js | 1054 ms |
| cmd /c echo (spawn floor) | 124 ms |
| jq-based replacement I ended up writing | 362 ms |
The npx cost is ccstatusline's packaging choice, not Claude Code's — I've raised that side with them in sirmalloc/ccstatusline#529, and it is a long-standing known issue there. What I think is Claude Code's to own is that a slow status line command degrades into N concurrent copies instead of one at a time. Any third-party status line that is slow for its own reasons hits this, so fixing it upstream in one tool does not close the hole.
Proposed solution
Any one of these would have prevented the incident:
- Coalesce: if the previous invocation is still running, skip or queue the refresh rather than starting another. Single-flight with a trailing re-run would keep the display fresh without overlap.
- Expose the debounce, e.g.
statusLine.debounceMs, as #57186 requested. - Make
refreshIntervala floor for event-driven refreshes too, so the documented knob actually bounds total invocation rate. This is what I expected it to do. - Adaptive backoff: measure the command's duration and never re-run more often than it takes to complete.
(1) seems the cheapest and would need no new settings.
Additional context
A diagnostic gap worth mentioning: statusLine changes in settings.json are only picked up at startup. While debugging this I edited command and watched for the new process for 30 s, with no effect and no indication that a restart was needed. If hot-reloading is not intended, a note in the docs would save people the confusion.
Related: #73785 (Windows cancellation path), #57186 (debounce configurability, closed stale), #82537 (statusline stops being invoked).
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗