Skip to content

feat(dashboard): Erklär-Tooltips für Fachbegriffe auf KPI-Kacheln und Chart-Überschriften#219

Open
TillQuandel wants to merge 3 commits into
masterfrom
feat/187-erklaer-tooltips
Open

feat(dashboard): Erklär-Tooltips für Fachbegriffe auf KPI-Kacheln und Chart-Überschriften#219
TillQuandel wants to merge 3 commits into
masterfrom
feat/187-erklaer-tooltips

Conversation

@TillQuandel

@TillQuandel TillQuandel commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Ansatz

CSS-only Hover-Tooltips (.hint Trigger + verschachteltes .hint-pop als Kind-Element, kein attr()/data-Attribut, kein JS-Framework) auf allen in #187 geforderten Stellen. Erklärtexte sind aus dem Berechnungscode hergeleitet (siehe Tabelle unten), nicht geraten. Bestehendes Muster geprüft (PR #209 "Cache-Quote"): das ist ein Chart.js-Daten-Tooltip mit bereits eingebauter Formel-Erklärung — kein zweites Glossar-System nötig, daher unverändert übernommen statt dupliziert.

Technik: <span class="hint" tabindex="0">Label<span class="hint-pop">Erklärung</span></span>. .hint = gepunktete Unterstreichung + cursor:help + position:relative. .hint-pop = absolut positionierte Box, opacity 0→1 bei :hover/:focus-visible. Rechtsbündige Variante (left:auto;right:0) für strukturell rand-nahe Elemente (:last-child in KPI-Reihe/Strip/Panel-Reihe/Tabellenkopf), damit nichts über den Viewport hinausragt.

Bugfix unterwegs: .kpis { overflow:hidden } hat die oberhalb der Kachel-Labels aufklappenden Tooltips abgeschnitten (nur ein Sliver sichtbar). Ursache war Eckenradius-Clipping für den Hover-/Tone-Hintergrund an Rand-Kacheln — jetzt stattdessen direkter Radius an .kpi:first-child/:last-child, overflow:hidden entfernt. Kein Effekt auf Non-Hover-Zustand (siehe Screenshots).

Begriff → Tooltip-Text → Code-Quelle

Begriff / Ort Tooltip-Kernaussage Code-Quelle
KPI „Automatisch akzeptiert" Anteil direkt in Vault übernommener Notes, gepoolt über alle Läufe der aktuellen Version (Σakzeptiert/Σgeneriert) eval_dashboard.py:_calc_kpis (avg_accept)
KPI „Fehlerquote" (Halluzinationsrate) Anteil Text-Anker, die im PDF nicht belegbar sind/widersprechen; gepoolt über alle Anker, nicht Schnitt der Einzelraten eval_dashboard.py:_pooled_hall_pct, lib/decision_engine/aggregation.py:hallucination_rate
KPI „Belegrate" (Coverage) Anteil bestätigter Anker; Median über Notes (bewusst anders als gepoolte Fehlerquote) eval_dashboard.py:_calc_kpis (avg_cov), aggregation.py:confirmed_rate
KPI „Evaluierte Notes" Stichprobengröße hinter Fehlerquote/Belegrate _calc_kpis (n_notes)
KPI „Laufzeit" Summe der aktuellen Version (+Fallback-Hinweis); Kosten-Sektion-Chart zeigt Median je Version _calc_kpis (cur_dur_h) vs. _chart_tokens_by_version (duration_min Median) — Summe-vs-Median-Klarstellung aus #198 Punkt 4
KPI „Pipeline-Tokens" Summe Input+Output der aktuellen Version, Cache exkl. _calc_kpis (cur_tokens)
KPI „API-Kosten" Summe USD der aktuellen Version; „Subscription" statt 0$ bei Abo-Läufen _calc_kpis (cur_cost_usd), config.compute_cost_per_call
n-Tag „(n=X)" Stichprobengröße; <20 = zu unsicher für Ziel-Bewertung Server nBase-Logik, _DELTA_MIN_N
Delta-Chip „Δ" Veränderung vs. Vorversion, erst ab n≥20 in beiden Versionen belastbar eval_dashboard.py:version_delta
Ziel-Chip (z. B. „Ziel ≥ 85 %") Ampel-Schwellenwert, fest im Code, keine wissenschaftliche Norm THRESH_ACCEPT/HALL/COV
Strip „PDFs/Versionen/Runs/Notes generiert" Zähl-Definitionen (Quell-PDFs, Pipeline-Versionen+Spannweite, Läufe, Draft-Notes) _calc_kpis (n_pdfs, n_versions, total_runs, total_generated)
Strip „Direkt Vault" Kumuliert über ALLE Versionen (Kontrast zur Kachel „Automatisch akzeptiert", die nur die aktuelle zeigt) _calc_kpis (total_accepted) + renderStrip
Strip „Merge-Stubs" In bestehende Note eingefügt statt neu angelegt (Draft-Aktion extend) orchestrator.py (n_merge)
Strip „Dropped" Komplett verworfene Kandidaten; nur bei DB-Fallback (fehlende Logs) gezählt, sonst 0 orchestrator.py (dropped_total), build_data DB-Fallback
Strip „Tokens" Summe über ALLE Versionen (Kontrast zur Kachel „Pipeline-Tokens") _calc_kpis (total_tokens)
„... pro akzeptierter Note" (Kosten-Insight) API-Kosten/akzeptierte Notes, nur für echte API-Läufe _chart_tokens_by_version/kpi_trend.cost_per_note (#196 P2, PR #209)
Chart „Akzeptanzrate je PDF" Median über die jeweils neueste protokollierte Version je PDF _chart_acceptance
Chart „Jede Note: Fehlerquote vs. Belegrate" Pro-Note-Werte (nicht gepoolt), wie KPI-Kacheln definiert _chart_scatter_versioned
Chart „Token-Verbrauch pro Run" Trotz Titel: Summe je Version, nicht pro Einzel-Lauf _chart_tokens_by_version
Chart „Laufzeit pro Run" Median je Version (Kontrast zur Summen-KPI-Kachel „Laufzeit") _chart_tokens_by_version (duration_min) — Summe-vs-Median #198 Punkt 4
Chart „Output-Tokens je Agent" / „Laufzeit je Agent" Kumuliert je Agent-Rolle, aktuelle Version _read_agent_stats
Chart „Skaliert die Pipeline mit der PDF-Länge?" 1 Punkt/Lauf, ältere Versionen (außerhalb der letzten 10) gedimmt _chart_scaling, mark_scaling_recency
Chart „Akzeptanzrate über Versionen" Median je PDF+Version im Zeitverlauf _chart_longitudinal
Kalibrierung „Evaluiert/Human-gelabelt/Ø Agreement/Status" Grundgesamtheit, Label-Basis, Mittelwert, Status-Hinweis _read_calibration_data, _avg_agreement
Vergleich „Akzeptiert/Fehlerquote/Belegrate" (Tabellenkopf) Andere Aggregation als KPI-Kacheln: Median bzw. Mittelwert über alle Versionen je PDF (nicht gepoolt/nur aktuelle Version) _calc_pdf_table

Verifikation

  • Playwright (Firefox, hell+dunkel): Screenshots mit sichtbar geöffnetem Tooltip auf (a) KPI-Kachel, (b) Chart-Überschrift, (c) Element am rechten Viewport-Rand → C:/tmp/verify-187/*.png (8 Screenshots + 2 Responsive-Bonusshots bei 1200/900px). Automatisierter Sweep über alle 42 Tooltip-Stellen (7 Sektionen × Re-Check = 294 Einzelprüfungen): 0 Overflow, 0 fehlende Box, opacity 1→0 korrekt bei Hover/Weg-Bewegen, keine JS-Konsolenfehler.
  • Suite: pytest generative lib/decision_engine/tests shared/tests -q → 5693 passed, 3 skipped, 4 deselected, 0 failed (nur HTML geändert, keine Python-Logik betroffen).
  • ruff: format --check . (246 Dateien bereits formatiert) + check . → beide sauber.
  • Cross-Model-Review (Mistral magistral-medium): Keine fachlichen Schieflagen in den Tooltip-Texten gefunden, CSS-Positionierung/Rand-Fälle und JS-Template-Strings als korrekt bewertet. Eigener Nachcheck deckte zusätzlich zwei Ungenauigkeiten auf, die vor diesem PR bereits behoben wurden: (1) Grenzwert-Formulierung bei der Fehlerquote-Ampel (<=5% ist noch „gut", Text sagte „unter 5%"), (2) fehlender Fallback-Hinweis bei Laufzeit/Pipeline-Tokens-Kacheln (cur_X ?? total_X).

Merge-Hinweis zu #217

Nach PR #217 (fix/213-kpi-layout) mergen. Konfliktfläche geprüft (git diff master...origin/fix/213-kpi-layout): #217 ändert exakt dieselben KPI-/Strip-CSS-Regeln (.strip .cell .sub, .kpi container-type, .kpi .top min-height, .kpi .value-row/.v/.u), die dieser PR nicht anfasst — meine Änderungen sind reine Markup-Ergänzungen (neue .hint/.hint-pop-Spans) plus eine neue, eigenständige CSS-Regel (.kpis-Overflow-Fix an anderer Stelle im Block). Ein Merge-Konflikt ist daher unwahrscheinlich, aber nicht ausgeschlossen, da beide PRs denselben CSS-Block editieren — bei Konflikt beide Änderungssätze behalten (additiv, keine sich widersprechenden Werte).

Closes #187

Nachbesserung (Cross-Model-Review Fable + Qwen, 2026-07-12)

Externes Review (Fable + Qwen, konsolidiert) fand 6 verbleibende Fehler in dieser Fassung, alle gefixt (Commit fix(dashboard): Tooltip-Texte an tatsaechliche Berechnung angepasst, valides Fokus-Markup (#187 Nachbesserung)):

  1. Ungültige Verschachtelung fokussierbarer Elemente: Die 4 Hint-Trigger auf den KPI-Kacheln (Label, n-Tag, Delta-Chip, Ziel-Chip) hatten tabindex="0", saßen aber innerhalb des <button> der Kachel. tabindex auf diesen 4 Spans entfernt — Tooltip bleibt hover-fähig, Keyboard-Zugang läuft über den umschließenden Button.
  2. Fallback-Behauptung stimmt nicht: Hints „Laufzeit"/„Pipeline-Tokens" behaupteten einen Fallback auf Gesamtwerte, falls die aktuelle Version keine Läufe hat. Tatsächlich sind cur_dur_h/cur_tokens (eval_dashboard.py:_calc_kpis) immer Zahlen (Summe über leere Liste = 0), der ??-Fallback greift nie — Laufzeit zeigt dann „0,0 h", Pipeline-Tokens (wegen zusätzlicher Falsy-Prüfung im Template) einen Strich „–". Texte auf das tatsächliche Verhalten korrigiert.
  3. Zähler/Nenner-Mismatch verschwiegen: Hint „Evaluierte Notes" (Ziel-Chip) sprach von „Anteil der insgesamt generierten Notes, der evaluiert wurde" — Zähler (n_notes) ist aber nur die aktuelle Version, Nenner (total_generated) alle Versionen. Text präzisiert.
  4. Fehlender Fallback-Hinweis: Hint „Fehlerquote" behauptete unbedingt gepoolte Berechnung; _pooled_hall_pct hat einen dokumentierten Mean-Fallback für Rows ohne Roh-Zähler (ältere DB-Einträge vor 2026-06-27). Caveat ergänzt.
  5. Markup-Inkonsistenz: Im Kalibrierungs-Strip lag .hint auf dem Label-<div> (block) statt wie überall sonst auf einem <span> — die gepunktete Unterstreichung zog sich über die volle Blockbreite. Auf <span> innerhalb des Label-<div> umgestellt (4 Stellen).
  6. Doppelte Tooltips: Der KPI-Kachel-<button> behielt title="Klick: Versions-Trend" — beim Hover über Label/n-Tag/Chip erschienen natives title UND CSS-Hint-Pop gleichzeitig. title durch aria-label ersetzt.

Nicht Teil dieser Nachbesserung: Die 3 Tooltips der Vergleichstabelle (Spaltenköpfe „Fehlerquote"/„Belegrate") und des Charts „Akzeptanzrate je PDF" beschreiben die Aggregations-Semantik von _calc_pdf_table vor PR #222#222 ändert genau diese Berechnung. Diese 3 Texte werden im #222-Branch korrigiert. #222 muss nach diesem PR gemergt werden, sonst laufen die Tooltip-Texte wieder aus dem Ruder.

Runde 2 (Opus + Mistral konvergent, 2026-07-12)

Der aria-label="Klick: Versions-Trend" aus Punkt 6 der ersten Nachbesserung war selbst fehlerhaft: Er überschreibt laut WAI-ARIA Accessible-Name-Computation den kompletten Button-Textinhalt, wodurch Screenreader auf allen KPI-Kacheln nur noch „Klick: Versions-Trend" statt Label+Wert vorlesen. Fix (Commit 86d3570): aria-label ersatzlos entfernt (Button-Textinhalt ist der korrekte Accessible Name), Klick-Hinweis stattdessen als Halbsatz an den Label-Hint-Pop-Text angehängt, damit die Information nicht verloren geht.

… Chart-Überschriften (closes #187)

CSS-only Hover-Tooltips (.hint/.hint-pop, kein JS-Framework) erklären
Halluzinationsrate/Fehlerquote, Belegrate/Coverage, Automatisch akzeptiert,
n-Angaben, Delta-/Ziel-Chips, die Gesamt-Zeile, alle Chart-Ueberschriften
sowie die Kalibrierungs- und Vergleichs-Sektion. Texte sind aus dem
Berechnungscode hergeleitet (eval_dashboard.py/_calc_kpis,
eval_dashboard_server.py/build_data, lib/decision_engine/aggregation.py)
und machen Summe-vs-Median-Unterschiede explizit (#198 Punkt 4). Zusaetzlich
.kpis-Container von overflow:hidden auf sichtbare Ecken-Radien an
.kpi:first-child/:last-child umgestellt, da das Clipping sonst die
oberhalb der Kacheln aufklappenden Tooltips abgeschnitten hat.
…valides Fokus-Markup (#187 Nachbesserung)

Cross-Model-Review (Fable + Qwen) an PR #219 fand 6 verbleibende Fehler:

1. Die 4 Hint-Trigger auf den KPI-Kacheln (Label, n-Tag, Delta-Chip,
   Ziel-Chip) hatten tabindex="0", sassen aber innerhalb des Kachel-
   <button> (ungueltige Verschachtelung fokussierbarer Elemente).
   tabindex entfernt -- Tooltip bleibt hover-faehig, Keyboard-Zugang
   laeuft ueber den Button.
2. Hints "Laufzeit"/"Pipeline-Tokens" behaupteten einen Fallback auf
   Gesamtwerte, falls die aktuelle Version keine Laeufe hat. cur_dur_h/
   cur_tokens (eval_dashboard.py:_calc_kpis) sind aber immer Zahlen
   (Summe ueber leere Liste = 0), der ??-Fallback greift nie -- Laufzeit
   zeigt dann "0,0 h", Pipeline-Tokens (zusaetzliche Falsy-Pruefung im
   Template) einen Strich "-". Texte korrigiert.
3. Hint "Evaluierte Notes" (Ziel-Chip) verschwieg den Zaehler/Nenner-
   Mismatch: n_notes ist nur die aktuelle Version, total_generated alle
   Versionen. Text praezisiert.
4. Hint "Fehlerquote" behauptete unbedingt gepoolte Berechnung;
   _pooled_hall_pct hat einen dokumentierten Mean-Fallback fuer Rows
   ohne Roh-Zaehler (aeltere DB-Eintraege vor 2026-06-27). Caveat
   ergaenzt.
5. Kalibrierungs-Strip: .hint lag auf dem Label-<div> (block) statt wie
   ueberall sonst auf einem <span> -- Unterstreichung zog sich ueber
   die volle Blockbreite. Auf <span> innerhalb des Label-<div>
   umgestellt (4 Stellen).
6. KPI-Kachel-<button> behielt title="Klick: Versions-Trend" --
   ueberlagerte beim Hover ueber Label/n-Tag/Chip das native title mit
   dem CSS-Hint-Pop. title durch aria-label ersetzt.

Nicht Teil dieser Nachbesserung: die 3 Tooltips der Vergleichstabelle
(Spaltenkoepfe Fehlerquote/Belegrate) und des Charts "Akzeptanzrate je
PDF" beschreiben die Aggregations-Semantik von _calc_pdf_table vor
PR #222 -- werden im #222-Branch korrigiert (#222 muss nach diesem PR
gemergt werden).

PR-Body von #219 um Nachbesserungs-Abschnitt ergaenzt (gh pr edit).
TillQuandel pushed a commit that referenced this pull request Jul 12, 2026
…nden-Randfaelle (#204 Nachbesserung)

Externes Review (Fable + Qwen) an PR #221 fand 5 Nachbesserungspunkte:

1. loadAndRender(): catch-Block entfernte .refreshing bisher unbedingt,
   obwohl der Erfolgspfad per seq-Guard vor Stale-Renders schuetzt — bei
   ueberlappenden Requests (schneller Filterwechsel) riss ein fehlschlagender
   AELTERER Request die Dimmung fuer einen noch laufenden NEUEREN weg.
   Entfernung jetzt an denselben seq-Guard gebunden.
2. Refresh-Dimmung (pointer-events:none auf .main) sperrte kurzzeitig auch
   die Filterbar beim automatischen 15s-Poll. loadAndRender(manual=true)
   -Parameter; Poll ruft loadAndRender(false) — dimmt nur bei manuellem
   Filterwechsel.
3. ch8-Achse erzeugte bei kleinen Datenstaenden (kumulierte Laufzeit <60s,
   Onboarding-Szenario) fraktionale Ticks mit voller Float-Praezision.
   ticks.precision:1 + Ausgabe ueber bestehendes _fmtDE (Komma, 1 Dezimale).
4. ch3-Legende gruppierte schon ab >5 Keys statt wie Vorbild ch2 ab >6
   (t.enc.length) — bei exakt 6 PDFs erschien unnoetig "Weitere (1 PDFs)"
   inkl. Singular-Fehler. Schwelle angeglichen, Label pluralisiert korrekt.
5. renderKpis: bei gleichzeitigem n<20 UND abweichender Stichprobenbasis
   erklaerte der title nur die n<20-Bedeutung, der Asterisk blieb
   unerklaert. title kombiniert jetzt beide Bedeutungen.

Test-Assertion in test_dashboard_quick_wins.py an den neuen
setInterval(() => loadAndRender(false), 15000)-Aufruf angepasst.

PR-Body von PR #221 korrigiert: "#217 keine Ueberschneidung" war falsch
(.kpi.no-spark kollidiert mit #217s mehrzeiligem .top-Block), #219-Konflikt-
liste war unvollstaendig (renderStrip-cell()-Aufrufe fehlten, Tokens-Zelle).

Suite: pytest generative lib/decision_engine/tests shared/tests -q
-> 5695 passed, 3 skipped, 4 deselected, 0 failed. ruff format/check clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Eval-Dashboard: Erklär-Tooltips für Fachbegriffe auf KPI-Kacheln und Chart-Überschriften

2 participants