Problem
The dashboard cache today has a fixed 5-minute TTL (cacheMaxAge in internal/tui/app.go). The trade-off is rough:
- Within 5 min: cache is served. Fine.
- After 5 min: relaunch blocks on a full scan — ~1–3s on a 1000-repo tree before anything renders. The loading screen flashes on every cold start.
- No knob: users with fast workflows or huge trees can't tune the window.
- No signal that data is stale when the cache is served — and conversely no signal that a refresh is happening when it's running.
Proposal
Switch to stale-while-revalidate, with a configurable freshness window.
Cache strategy
- Warm start, cache <
cacheFreshness: serve cache, no refresh. Silent and instant.
- Warm start, cache ≥
cacheFreshness: serve cache immediately, run a background refresh, swap data in when it lands. If the fresh data matches what's on screen (path-keyed equality on the displayed fields), the swap is silent — no badge flip, no table repaint, no "blink".
- Cold start (no cache): loading screen until first scan.
- Manual
r / Worktree-toggle slow path / Post-action: same background-refresh pattern; status line shows ↻ Refreshing…, badge in stats bar shows during the in-flight scan.
Config
# How long the on-disk scan cache stays "fresh" — reads younger than this
# serve without firing a background refresh. Accepts any Go duration
# string (30s, 1m, 5m, 1h). Default: 1m.
cacheFreshness: 1m
Layout stability (drive-by fixes)
A few visible defects fall out of the same effort:
- The status row is always reserved (1 line, blank when empty). Today it's conditional + uses
MarginTop(1), so the dashboard grows by 2 lines when a status message appears, the terminal scrolls, and every redraw paints at a new origin — visible as "shimmer".
- Panel render output (grass / disk / timeline) is cached on the model and only rebuilt on data-update or resize. Currently every keystroke while a panel is open re-runs the renderer.
- Timeline panel content is bounded by actual emitted lines rather than entry-iterations, so it never overflows the panel allocation (overflow caused the same scroll-shimmer as the status row).
Why this matters
Removes the noticeable "loading → ready" transition on every relaunch and stops the visible shimmer that's been creeping in as the dashboard accumulated chrome.
Happy to send a PR — already have a branch ready.
Problem
The dashboard cache today has a fixed 5-minute TTL (
cacheMaxAgeininternal/tui/app.go). The trade-off is rough:Proposal
Switch to stale-while-revalidate, with a configurable freshness window.
Cache strategy
cacheFreshness: serve cache, no refresh. Silent and instant.cacheFreshness: serve cache immediately, run a background refresh, swap data in when it lands. If the fresh data matches what's on screen (path-keyed equality on the displayed fields), the swap is silent — no badge flip, no table repaint, no "blink".r/ Worktree-toggle slow path / Post-action: same background-refresh pattern; status line shows↻ Refreshing…, badge in stats bar shows during the in-flight scan.Config
Layout stability (drive-by fixes)
A few visible defects fall out of the same effort:
MarginTop(1), so the dashboard grows by 2 lines when a status message appears, the terminal scrolls, and every redraw paints at a new origin — visible as "shimmer".Why this matters
Removes the noticeable "loading → ready" transition on every relaunch and stops the visible shimmer that's been creeping in as the dashboard accumulated chrome.
Happy to send a PR — already have a branch ready.