KB / framework
The five-level signal scale
Last verified
Every banded signal on the platform is classified onto one universal 5-level scale: Favorable / Leaning / Neutral / Cautionary / Adverse. The colored dot next to a signal value is the level; the alignment dots, sparkline tints, and KB article band tables all map to the same five tiers. One scale, one source of truth — the band classifier in app/signals/config/signal_definitions.json.
The five levels
Universal scale — every banded metric maps to one of these
The hex codes are the canonical dashboard palette pinned in ui/src/pages/humans.astro and reused by every signal card, alignment dot, and KB band table tint.
Why five (not three, not seven)
Three levels (red / yellow / green) collapse meaningful gradations — a DIX of 0.42 (right at the bear threshold) reads the same as 0.30 (deep bear). Seven levels read like noise on a dashboard; the operator can’t hold “very-mild-cautionary vs mild-cautionary” in working memory while scanning twenty dots.
Five captures the differentiation that matters — directional bias (Favorable / Adverse), the soft directional tier next to it (Leaning / Cautionary), and a true middle (Neutral) — without overflowing the visual budget. The same five-tier shape recurs across the platform: alert severity (info / warning / critical maps onto Favorable / Cautionary / Adverse), regime label (RISK-ON / CAUTIOUS / TRANSITIONAL / RISK-OFF / PANIC also reads as a 5-band ladder), and score reconciliation labels.
How a metric maps to a level
The mapping path is registry-driven end-to-end:
- A metric’s
bands[]array insignal_definitions.jsondeclares thresholds and per-banddot_levelstrings (one of the five). band_for(metric_key, value)inapp/signals/signal_defs.pyreturns the matchingBandobject — carrieslabel,dot_level,implication, andscore_pts.- The dashboard renders
Band.dot_levelas the colored dot; the alignment surface reads it; the hybrid matcher projects it as a tag; the KB band table tint maps to it.
One classifier, many consumers. The dot you see on the dashboard, the tag the matcher pre-filters on, and the alignment leg’s implication all derive from the same band_for() call — drift between them is impossible by construction.
Where the scale surfaces
- Dashboard signal cards — the colored dot next to each metric’s value.
- Alignment dots — eleven (or twelve when
ENABLE_AI_SENTIMENT_ALIGNMENT=1) per-category dots on the Signal Alignment card; each carries a transmitted level + intensity ring. - Sparklines — the dot at the right edge picks up the latest level’s tint; historical band crossings are visible in the tint shifts.
- Score breakdown —
/api/v1/signals/score/breakdownreturnsband(level label) +pointsper component; UI renders both. - KB band tables — every
<BandTable>in this knowledge base uses the same five tones (good/okay/neutral/warn/bad). - Hybrid matcher tags — the coarse-tag filter projects
Band.dot_levelper cycle intosignal_tags; pre-filtering analogues by tag equality means “find days whose signals were at the same scale levels as today.”
See also
- Hybrid analogue matcher — uses
Band.dot_levelas the coarse-tag substrate. - Regime overrides — how regime conditions rebalance the per-metric weights without changing the level a metric sits at.
- Implementation:
app/signals/signal_defs.py(band_for),app/signals/config/signal_definitions.json(band declarations).