# Phase 01 — Severity language **Source:** `TODO.md` L5, L7 — "proper green (good), yellow (warn), red (danger) color coding" / "charge, load, and runtime left to be big, obvious, and color coded" **Story:** n/a **Context:** `.agents/PLAN.md` → *Architectural Anchors* (severity computed in Python, JS renders classes), `src/dashboards/sources/host.py`, `src/dashboards/sources/nut.py`, `src/dashboards/api.py`, `src/dashboards/web/static/dash.css`, `src/dashboards/web/static/app.js` ## Objective Introduce one place that decides whether a number is fine, worrying, or bad, and put that verdict on the values the API already returns. Phases 02–05 all want the same colour language; doing it here means they render a class instead of each inventing its own thresholds. Deliverable: `severity` next to the value in the payloads the pages colour, and CSS/JS helpers that make a "hero number" one call. ## Dependencies — (none) ## Tasks 1. `01_severity_module.md` — `src/dashboards/severity.py`: the threshold table and the classifier, unit-tested. 2. `02_severity_in_payloads.md` — attach `severity` to host and UPS values and to the `/api/summary` rows; API tests. 3. `03_hero_number_style.md` — a `.hero` big-number style on the existing `.ok/.warn/.fail` colours, plus `Dash.hero()` and `Dash.severityFrom(payload)` so pages read the verdict instead of passing thresholds. ## Testing & Quality - Unit: every threshold boundary (below warn, exactly at warn, between, exactly at danger, above), `None` input, and the "unknown is not ok" rule — a value the upstream does not publish must get `severity: null`, never a green pass. - API: `/api/system/snapshot`, `/api/ups/snapshot`, `/api/summary` carry `severity` where the spec says, and **no existing field changes name or meaning** (additive-only anchor). - Coverage: **>90%** on new/modified code. - Live: `uv run pytest -m live --live` must stay at 6 passed. ## Completion Criteria - [ ] `uv run python -c "from dashboards import severity; print(severity.classify('cpu.usage_pct', 95))"` → `fail`; `60` → `ok`; `None` → `None` - [ ] `curl -s localhost:8090/api/ups/snapshot | python3 -m json.tool` shows `severity` beside `charge_pct`, `load_pct`, `battery_runtime` - [ ] no new colour vocabulary: classes stay `.ok` / `.warn` / `.fail` as in `dash.css` - [ ] `uv run pytest` green; coverage ≥94% overall, >90% on `severity.py` - [ ] `uv run ruff check src tests agent && uv run pyright` clean - [ ] Existing pages render unchanged (severity is additive; nothing consumes it yet) — one screenshot in `data/logs/` - [ ] no behavior change in completed phases