Skip to content

fix(installer): raise the shipped statusline refreshInterval from 1s to 30s - #1772

Open
asdf8675309 wants to merge 1 commit into
danielmiessler:mainfrom
asdf8675309:fix/statusline-refresh-interval-default
Open

fix(installer): raise the shipped statusline refreshInterval from 1s to 30s#1772
asdf8675309 wants to merge 1 commit into
danielmiessler:mainfrom
asdf8675309:fix/statusline-refresh-interval-default

Conversation

@asdf8675309

Copy link
Copy Markdown
Contributor

What

deployStatusline() wires every new install with refreshInterval: 1, so Claude Code polls the statusline once a second. Nothing on the bar moves that fast.

  • A warm render of LIFEOS_StatusLine.sh costs 0.28–0.29s.
  • The usage numbers it shows sit behind a 900s cache TTL (USAGE_CACHE_TTL, L152).
  • There's no wall clock on the bar. The only date formats in the script are +%s, +%Y-%m-%d, and one ISO stamp.

So the fastest timer-driven value updates once every 15 minutes, and a 1s poll re-renders it about 900 times per data change.

Wasted work isn't the only cost. #1767 reports that Claude Code kills a statusline render that overruns its interval, and those killed renders leaked 244,760 scratch directories on a headless box until the filesystem ran out of inodes. That PR bounds the leak. This one takes away the pressure driving it.

Change

One literal in deployStatusline(). The file is mirrored under install/skills/, so the same line changes in both copies — identical blobs before and after.

settings.statusLine = { type: "command", command, refreshInterval: 30 };

About the 30

It's a starting point, not a computed optimum. I picked a round number comfortably inside the 900s TTL of the slowest source. I'd rather say that than dress it up as math.

The principle behind it is what matters: the idle poll should track how fast the underlying data actually changes. Right now that's the usage figure at 900s, so anything from a few seconds up is fine and 1s isn't. If the bar picks up a faster-moving source later, this number should move too. The comment in the diff says so, which keeps the reasoning attached to the number.

One thing for anyone worried about responsiveness: refreshInterval only governs the idle poll. Model, mode, and branch change on events, and those still re-render at turn boundaries no matter what this is set to. A bigger interval doesn't make the bar feel stale while you're working.

If you've got a number that fits your source's cadence better, use it.

Why it's safe

Existing installs don't get touched. The write is guarded by alreadyWired:

const alreadyWired = current?.command === command;
if (!alreadyWired) { ... }

If statusLine.command already points at the deployed script, there's no write at all, so the current refreshInterval survives byte-identical. Only fresh wiring picks up the new default.

The flip side, stated plainly: existing users keep 1 until they change it by hand or their statusline command path changes.

A longer interval fails benignly — a stale bar for up to 30s, well inside the 900s TTL of its own data. No crash path, no new dependency, no API change.

Verification

macOS 15 (Apple Silicon), bash 3.2, LIFEOS_StatusLine.sh from origin/main at 47df8ee. Five warm runs after five discarded warmups:

Run Wall time
1 0.29s
2 0.28s
3 0.29s
4 0.28s
5 0.29s
refreshInterval: 1 refreshInterval: 30
Duty cycle ~29% ~1%
Renders per 900s data change ~900 ~30

bun build --target=bun on both changed files exits 0. The repo has no test runner, so those numbers and the alreadyWired read are the reviewable proof.

Scope

Found and deliberately left alone:

  • The synchronous usage fetch on the render path (L894–L901). The mutex winner still runs security find-generic-password and curl --max-time 3 inline during a render. Weather and location already moved to detached background subshells for this exact reason — "that's what killed the perf and got the feature ripped out on 2026-05-08." The usage fetch probably wants the same treatment, but that's a different concern and a much bigger diff, so it's not in here.
  • LIFEOS_StatusLine.sh itself. fix(statusline): bound the /tmp pai-parallel leak (follow-up to #1297) #1767 is already open against that file. No overlap — this PR only touches DeployComponents.ts.
  • settings.enhancements.json and settings.system.json. Neither has a statusLine key, so there's no second default to keep in sync.

…to 30s

deployStatusline() wires every new install with refreshInterval: 1, so
Claude Code polls the statusline once per second.

Nothing on the bar changes that fast. A warm render of
LIFEOS_StatusLine.sh costs 0.28-0.29s. The usage figures it displays sit
behind a 900s cache TTL (USAGE_CACHE_TTL), and the script renders no wall
clock, so the fastest timer-driven value on the bar updates once every 15
minutes. A 1s poll re-renders it about 900 times per data change.

The waste is not the only cost. Public PR danielmiessler#1767 reports that Claude Code
terminates a statusline render which overruns its interval, and that the
killed renders leaked 244,760 scratch directories on a headless box until
the filesystem ran out of inodes.

30 is a conservative starting point, not a derived optimum. It is far
inside the 900s TTL of the slowest-moving source while cutting the duty
cycle from about 29 percent to about 1 percent. The principled value
tracks how often the underlying data actually shifts, so this number
should be revisited if the bar gains a faster-changing source. Everything
on the bar that moves on events rather than on a timer, such as the model
and the branch, still re-renders at turn boundaries regardless of this
interval.
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