The five UI items in TODO.md become an executable pipeline. Planning files only — no application code changed. - 01_severity_language: one Python threshold table + `severity` on the values the pages colour, plus the .hero style. Today the thresholds are magic numbers at six JS call sites (75/90 here, 80/92 there), which is exactly how "same colour, four meanings" happens. - 02_overview_fleet_and_alerts: /api/summary gains a derived `alerts` list; the page drops to fleet + warnings. Unconfigured sources must not alert — that is invariant 3, tested. - 03_system_per_host_panels: per-host hero cards, no per-core, shared multi-host charts and the detail picker deleted. The six card metrics already exist in the payload, so the first task pins them with tests instead of inventing fields. - 04_llm_chart_readability: charts.js already exports sparkline() and no page calls it — that is why the page reads flat. Port llm_monitor's drawSpark behaviour (fixed max, right growth, p95 foots) and keep the provenance tags. - 05_ups_hero_battery_glyph: charge/load/runtime hero + an SVG battery whose fill is the charge, verified against both real UPSes and the low-charge/on-battery states only the simulator can produce. PLAN.md records the ratified anchors and the deviation worth knowing: severity vocabulary is ok|warn|fail, matching dash.css and nut.py, not the "danger" wording in the proposal — one synonym for one colour. TODO.md cleared to a bare heading; the items are now phases. .gitignore stops ignoring all of .agents/ (the roadmap is project history) while keeping phase-sessions/ and pipeline.log out. deploy/nut-sudoers rewritten: its header still blamed a port mystery that is solved; the remaining need is reading upsd.users so adding a scoped dashboards user does not delete the accounts upsmon authenticates as. LLM-Generated: true
2.6 KiB
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
01_severity_module.md—src/dashboards/severity.py: the threshold table and the classifier, unit-tested.02_severity_in_payloads.md— attachseverityto host and UPS values and to the/api/summaryrows; API tests.03_hero_number_style.md— a.herobig-number style on the existing.ok/.warn/.failcolours, plusDash.hero()andDash.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),
Noneinput, and the "unknown is not ok" rule — a value the upstream does not publish must getseverity: null, never a green pass. - API:
/api/system/snapshot,/api/ups/snapshot,/api/summarycarryseveritywhere 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 --livemust 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→Nonecurl -s localhost:8090/api/ups/snapshot | python3 -m json.toolshowsseveritybesidecharge_pct,load_pct,battery_runtime- no new colour vocabulary: classes stay
.ok/.warn/.failas indash.css uv run pytestgreen; coverage ≥94% overall, >90% onseverity.pyuv run ruff check src tests agent && uv run pyrightclean- Existing pages render unchanged (severity is additive; nothing consumes it yet) — one screenshot in
data/logs/ - no behavior change in completed phases