Session Weather
August 9, 2026 · View on GitHub
Session weather is a health indicator shown on each session card. It condenses eight independent signals into a single score, displayed as an emoji (sunny through stormy) with a hover tooltip that lists the active factors.
Source of truth: public/weather.js.
Signals
| Signal | What it watches | Severity curve | Cap | Min turns |
|---|---|---|---|---|
ctx_pressure | Last turn's context fill % | 40% → 0, 100% → 1.0 (linear) | 1.0 | 1 |
compaction_scar | Number of compacted turns | 1 = 0.4, 2+ = 0.6 | 0.6 | 1 |
truncation | Any turn hit max_tokens (≥16K output) | Fixed 0.5 | 0.5 | 1 |
stuck | Longest consecutive tool-failure streak | ≥10 = 0.9, else 0 | 0.9 | 10 consecutive |
latency_drift | p75 of last 10 turns vs model baseline | 1x → 0, 3x → 1.0 | 1.0 | 5 |
error_cluster | 5-turn sliding window tool error rate | rate / 2.0 | 0.5 | 3 tool_use in window |
error_cumulative | Session-wide tool error ratio | 20% → 0.5, 40% → 1.0 | 1.0 | 10 tool turns |
cache_health | Median cache hit rate (last 10 turns, skip first 3) | 50% → 0, 0% → 0.5 | 0.5 | 3 qualifying |
Signal tiers
Severity caps encode relative importance:
- Functional (cap 0.9–1.0):
ctx_pressure,stuck,error_cumulative,latency_drift— directly affect whether the agent can complete its work. - Quality (cap 0.5–0.6):
compaction_scar,truncation,error_cluster— the agent runs but information or quality is degraded. - Cost/efficiency (cap 0.5):
cache_health— functionality unaffected; the session costs more and runs slower.
Provider scope
cache_health only applies to Anthropic entries (provider === 'anthropic'
or absent). OpenAI/Codex and xAI/Grok entries are skipped because their
wire parsers normalize cache fields differently.
Score composition
score = max(severities) + 0.3 * second(severities)
Only the two highest severities contribute. This prevents multiple low-severity signals from stacking into a false alarm.
Levels
| Score range | Level | Emoji | Meaning |
|---|---|---|---|
| < 0.35 | sunny | ☀️ | Operating normally |
| < 0.55 | fair | 🌤️ | Minor signals, no action needed |
| < 0.75 | cloudy | ⛅ | Quality starting to degrade |
| < 0.95 | rainy | 🌧️ | Significantly degraded, take action |
| ≥ 0.95 | stormy | ⛈️ | Critically degraded, act now |
Tooltip
The hover overlay shows different content depending on the level:
- Sunny / Fair: stats proving health — context %, error count, cache hit rate, latency ratio, compaction count.
- Cloudy / Rainy / Stormy: active factors sorted by severity, plus an action line linking to the relevant turns (when available).
Display toggle (2.3.1)
Weather display is off by default since 2.3.1. Computation and
persistence continue running — sessions.json keeps accumulating weather
objects — but the six render sites (session card emoji, lane label, turn
tooltip Health row, agent card emoji, turn card emoji, and Turn failure
rate) are gated behind weatherDisplayEnabled().
The tool-failure signals (stuck, error_cluster, error_cumulative,
tool_failure) are currently unreliable across all data paths: Anthropic
proxy reads cumulative toolFail (~16× inflation), imported data has no
signal (74% of entries), and the OpenAI-wire decoders have known defects.
See #484 for details. The remaining five signals (ctx_pressure,
compaction_scar, truncation, latency_drift, cache_health) are
unaffected but share the same emoji, so the entire display is hidden.
Toggling on for inspection
- URL param (one-shot, survives SPA navigation via load-time latch):
append
?weather=onto the dashboard URL. - localStorage (persistent per browser):
localStorage.setItem('ccxray-weather-display', 'on')in the console, then reload. Set to'off'or remove the key to revert. - Program default:
_weatherDisplayDefaultinpublic/weather.js. Will be flipped totrueonce the tool-failure signals are fixed and recalibrated (#487).
Cold-start immunity
Each signal has a minimum-turns requirement to avoid firing on incomplete
data. cache_health additionally skips the first three turns of the
session (prompt cache cold start — Math.max(3, len - 10)) and
ignores turns with fewer than 1,000 input tokens. For sessions longer
than 13 turns the full trailing 10 are examined; shorter sessions only
see turns after the cold-start boundary.