Skip to content

fix(dashboard): UX-Konsistenz P4–P8 — kategoriale Palette, Einheiten/Ticks, Tooltip-Limit, tote UI-Reste (#204)#221

Merged
TillQuandel merged 3 commits into
masterfrom
fix/204-ux-p4-p8
Jul 12, 2026
Merged

fix(dashboard): UX-Konsistenz P4–P8 — kategoriale Palette, Einheiten/Ticks, Tooltip-Limit, tote UI-Reste (#204)#221
TillQuandel merged 3 commits into
masterfrom
fix/204-ux-p4-p8

Conversation

@TillQuandel

@TillQuandel TillQuandel commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Schliesst #204 (Punkte P4–P8; P1–P3 bereits in PR #207 gemergt).

Punkt-für-Punkt

Punkt Entscheidung Begründung
P4 (Rot dreifach belegt) --enc-1/--enc-4 (kategoriale Palette) von Coral auf Violet umgestellt, --enc-2/3/5/6 (Teal/Amber) unverändert Coral war bit-identisch mit --coral (Ampel-Bad) — dieselbe Kachel-Farbe stand für "Ampel schlecht", "1. PDF-Serie" (Scatter, Trade-off-Scaling) und "1. Agent" (Balken) gleichzeitig. Nur Rot/Grün laut Issue-Wortlaut ausgetauscht, Amber blieb (nicht als Problem benannt, Prinzip Scope-Discipline).
P5 (Einheiten/Scopes) ch8 „Laufzeit je Agent": Daten vor Chart-Aufbau in Minuten konvertiert statt Sekunden+Display-Umrechnung; Titel „... pro Run" → „... je Version" (ch5+ch6, Achse zeigte immer Versionen); KPI-Label „Laufzeit" → „Laufzeit (aktuell)"; Strip-Zelle → „... Laufzeit (gesamt)" Root Cause des Tick-Bugs: Chart.js erzeugt „schöne" Ticks in der Einheit der Rohdaten (Sekunden → 2000er-Schritte), die erst nach der Minuten-Umrechnung krumm aussehen (0/33/67/100min). ch6 ist bereits minuten-nativ und hatte deshalb nie das Problem — gleiche Lösung übernommen. Vier gleichnamige „Laufzeit"-Kennzahlen (aktuell/gesamt/Median-je-Version/kumuliert-je-Agent) jetzt unterscheidbar benannt. Nebenbei gefixt: Locale-Bug an derselben Zeile (Punkt statt Komma in der Strip-Zelle).
P6 (Scatter-Tooltip) Kein Diff — bereits durch PR #205 gefixt (Filter i<3 + „+n weitere"-Footer) Verifiziert (Programmatik-Check + Screenshot), nicht erneut implementiert.
P7 (toter Hinweis) toggleRow() entfernt (nicht angeschlossen) Kein Caller (kein onclick), kein .detail-row-Markup existierte je — kein halbfertiges Feature mit erkennbarem Fortsetzungspfad, sondern komplett totes Konstrukt. Zusätzlich die dafür reservierte leere Gutter-Spalte (width:28px, Header + jede Zeile) entfernt, colspan 9→8 angepasst. Der sichtbare Hinweis-Text war bereits in PR #205 entfernt worden.
P8a (Lade-Feedback) .main.refreshing (Opacity .55, pointer-events:none) beim Filterwechsel, entfernt nach Render/Fehler Bewusst kein Wiederverwenden des Boot-Skeletons (.chart-body.loading, voller Shimmer) — der würde alten, noch gültigen Inhalt komplett verdecken statt nur zu dimmen. Nachbesserung: dimmt nur bei manuellem Filterwechsel, nicht beim 15s-Poll (s. unten).
P8b (Trade-off-Legende) ch3-Legende von ~19 Farben auf Top-N (nach Punktzahl) + graue „Weitere (N PDFs)" reduziert; Bubble-Größe („kleiner = ältere Version") im Untertitel ergänzt Gleiches Top-N-Muster wie im bestehenden Scatter (ch2, seit PR 38); Schwelle nach Nachbesserung exakt an ch2 angeglichen (s. unten). Text-Ergänzung im p-sub, nicht im p-title (der trägt bereits den #219-Hint).
P8c (n=1-Einbrüche) _chart_longitudinal liefert neu "n" (Run-Anzahl) pro Datenpunkt; ch4-Tooltip zeigt bei n=1 „n=1 Run — dünne Basis" Python-Payload-Erweiterung war laut Auftrag erlaubt, wenn ein Punkt es zwingend braucht — n fehlte komplett für die Einzel-PDF-Linien (der Median-Pfad hatte bereits _n/PDFs-Tooltip). TDD: 2 neue Tests, RED vor Implementierung.
P8d (leere Kalibrierungs-Spalten) <th> für Human-Fehlerquote/Agreement mit min-width; Dash-Platzhalter bekommen title="Noch nicht human-gelabelt" Verhindert Spalten-Kollaps unabhängig von Viewport/Datenstand, Hover erklärt den Grund statt nacktem Strich.
P8e (n-Basis-Wechsel) KPI „Automatisch akzeptiert" zeigt (n=X*) + Tooltip, wenn die Basis von der gepoolten Fehlerquote/Belegrate-Basis abweicht Accept-n kommt aus Log-Runs (generierte Notes), Hall/Cov-n aus evaluierten Notes — beide stehen nebeneinander in derselben Kachelreihe. Nachbesserung: title erklärt jetzt auch die n<20-UND-abweichende-Basis-Kombination (s. unten).
P8f (Qualität-Untertitel) „Fehlerquote + Belegrate nach Version" → „Akzeptanzrate je PDF + Fehlerquote/Belegrate je Note" Keiner der beiden Charts in der Sektion ist ein Versions-Chart.
P8g (Vergleichs-Sparkline) Klick-Sparkline der „Automatisch akzeptiert"-Kachel entfernt (kein onclick, .no-spark-Klasse, defensiver Guard in _openKpiSpark) Duplizierte 1:1 die Aussage von ch4 „Akzeptanzrate über Versionen" (eigener, größerer Panel-Chart mit Legende/PDF-Filter) — als kleine Popup-Sparkline überflüssig. Hall/Cov/Dur/Tokens/Cost behalten ihre Sparkline (kein Äquivalent-Panel vorhanden).
P8h (natives Select) .fselect bekommt overflow:hidden; text-overflow:ellipsis; white-space:nowrap In Firefox verifiziert wirksam (Screenshot: "Mahmood und University…" statt hart abgeschnitten "Mahmood und University ("). Kein Custom-Dropdown gebaut (Overkill für Low-Prio-Detail).

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

Externes Review (Fable + Qwen, konsolidiert) fand 5 Code-Punkte + 2 fehlerhafte/unvollständige Angaben in diesem PR-Body. Alle umgesetzt bzw. korrigiert:

# Schwere Fund Fix
1 Mittel (Qwen bestätigt) loadAndRender(): Erfolgspfad rendert nur bei seq===_loadSeq (Stale-Schutz gegen überholte Requests), aber der catch-Block entfernte .refreshing unbedingt — bei überlappenden Requests (schneller Filterwechsel) verschwand die Lade-Dimmung, obwohl ein neuerer Request noch lief. Entfernung im catch an denselben seq-Guard gebunden (if (seq === _loadSeq) ...classList.remove('refreshing')).
2 Niedrig (Fable) Refresh-Dimmung (pointer-events:none auf .main, das auch die Filterbar enthält) sperrte kurzzeitig auch beim automatischen 15s-Poll die Filterleiste — nicht nur bei manuellen Filterwechseln. loadAndRender(manual = true)-Parameter; der setInterval-Poll ruft loadAndRender(false) — dimmt nur noch bei vom Nutzer angestoßenen Loads.
3 Niedrig (Fable) ch8-Achse (Laufzeit je Agent): bei kleinen Datenständen (kumulierte Laufzeit aller Agenten < 60 s, z. B. Onboarding-Szenario) erzeugte Chart.js fraktionale Ticks mit voller Float-Präzision statt runder Werte. ticks.precision:1 (rundet den Tick-Schritt auf 1 Dezimale) + Ausgabe über das bestehende _fmtDE (Komma statt Punkt, konsistent zum Tooltip derselben Achse).
4 Niedrig (Fable) ch3-Legende gruppierte bereits ab >5 Keys, das Vorbild ch2 erst ab >6 (t.enc.length) — bei exakt 6 PDFs erschien dadurch unnötig eine „Weitere (1 PDFs)"-Gruppe trotz vollständig eindeutiger Farbzuordnung, plus Singular-Fehler ("1 PDFs"). Schwelle MAX_COLORED_SCALING von hartcodiert 5 auf t.enc.length (= 6) umgestellt, identisch zu ch2; Label pluralisiert korrekt ("1 PDF" vs. "N PDFs").
5 Niedrig (Fable) renderKpis: wenn n<20-Warnung UND abweichende Stichprobenbasis (nDiffers) gleichzeitig zutrafen, erklärte der title nur die n<20-Bedeutung — der Asterisk (abweichende Basis) blieb unerklärt, weil der ternary rein exklusiv war (nWarn ? ... : (nDiffers ? ... : '')). title kombiniert jetzt beide Erklärungen, wenn beide Bedingungen zutreffen.
6 PR-Body-Korrektur (dieser Abschnitt + der Konfliktflächen-Abschnitt unten): die ursprüngliche Angabe „#217: keine Überschneidung" war falsch, und die #219-Konfliktliste war unvollständig (renderStrips cell()-Aufrufe fehlten). Beide jetzt korrigiert, siehe unten.

Verifikation: node --check auf extrahiertem <script>-Block (Syntax), volle Suite + ruff format --check + ruff check erneut grün (s. „Suite & Lint" unten, inkl. 1 angepasster Test-Assertion für den neuen setInterval(() => loadAndRender(false), 15000)-Aufruf).

Konfliktflächen mit #217 / #219 (beide ungemergt, ändern dieselbe HTML)

Empfohlene Merge-Reihenfolge: #217#219 → dieser PR.

Korrektur ggü. der ersten Fassung dieses Abschnitts (Fable-Review, 2026-07-12): „#217 keine Überschneidung" war falsch — es gibt eine echte Kollisionsstelle (s. u.). Die #219-Liste war zudem unvollständig (renderStrip/cell() fehlte).

Screenshots (hell + dunkel, Vorher/Nachher via git stash)

Verzeichnis: C:/tmp/verify-204/

  • P4 + P5: pre|post-p4-p5-agents-{light,dark}.png (Agenten-Sektion: Balkenfarben + Achsen-Ticks)
  • P6: post-p6-scatter-tooltip-{light,dark}.png (Tooltip auf dichtem Cluster, 3 Einträge + „+16 weitere"-Footer)
  • P7: pre|post-p7-compare-table-{light,dark}.png (Gutter-Spalte weg)
  • P8b: pre|post-p8b-tradeoff-{light,dark}.png (Legende 19→6 Einträge)
  • P8h: post-p8h-select-longtext.png (Ellipsis statt Hart-Abschnitt)

Hinweis: Screenshots stammen aus dem ursprünglichen PR-Stand (vor der Nachbesserung); für die 5 Nachbesserungs-Punkte (kein UI-Layout-Wechsel, reine Logik-/Rand-Fälle) keine erneute Playwright-Session — Verifikation per Code-Inspektion + Testsuite (s. oben).

Suite & Lint

  • pytest generative lib/decision_engine/tests shared/tests -q: 5695 passed, 3 skipped, 4 deselected, 0 failed — erneut verifiziert nach der Nachbesserung (1 Test-Assertion an den neuen setInterval(() => loadAndRender(false), 15000)-Aufruf angepasst, Testanzahl unverändert ggü. ursprünglichem PR-Stand: Baseline 5693 + 2 Tests für _chart_longitudinal).
  • ruff format --check .: 246 files already formatted.
  • ruff check .: All checks passed.

Cross-Model-Review (Mistral magistral-medium-latest)

Keine Befunde. Reasoning-Trace ging Python-Änderung (_chart_longitudinal) und alle JS-Blöcke (Palette, ch3-Top-N, ch8-Minuten-Konvertierung, nDiffers-Bedingung, toggleRow-Entfernung, .refreshing-Lifecycle) einzeln durch, keine erfundenen Findings.

Zusätzlich: externes Fable+Qwen-Review der Nachbesserung (s. Abschnitt oben) — 5 Findings, alle umgesetzt.

Read-only

Keine Eval-Daten verändert, keine Pipeline-Läufe. Nur internal/dashboard/eval_dashboard.html + generative/eval_dashboard.py (Datenfunktion _chart_longitudinal, additiv) + Tests.

…Ticks, Tooltip-Limit, tote UI-Reste (#204)

P4: --enc-1/--enc-4 (kategoriale Palette fuer PDF-Serien/Agenten) von Coral
auf Violet umgestellt — Coral war bit-identisch mit der Ampel-Bad-Farbe,
dieselbe Kachel-Farbe stand gleichzeitig fuer "Ampel schlecht", "1. PDF" und
"1. Agent" (Scatter + Agenten-Chart betroffen).

P5: "Laufzeit je Agent" (ch8) baute die Achse bisher auf Sekunden-Rohdaten
auf und rechnete erst zur Anzeige in Minuten um — Chart.js waehlte "schoene"
Tick-Schritte in Sekunden (2000er-Schritte), die nach der min-Umrechnung
krumm wirkten (0/33/67/100min). Fix: Daten vor dem Chart-Aufbau in Minuten
konvertiert, Achse jetzt nativ minutenbasiert wie ch6 (0/50/100/.../300 min,
mit Leerzeichen). Ausserdem vier gleichnamige "Laufzeit"-Scopes ohne
Kennzeichnung: Titel "Token-Verbrauch/Laufzeit pro Run" -> "... je Version"
(Achse zeigte schon immer Versionen, nicht Runs), KPI-Label "Laufzeit" ->
"Laufzeit (aktuell)", Strip-Zelle "... Laufzeit" -> "... Laufzeit (gesamt)"
(dabei Locale-Fund an derselben Stelle mitgefixt: Punkt statt Komma).

P6: bereits durch PR #205 gefixt (Tooltip-Limit 3 + "+n weitere"), hier nur
verifiziert.

P7: toggleRow() war nie verdrahtet (kein Caller, kein .detail-row-Markup) —
Funktion + die dafuer reservierte leere Gutter-Spalte in der Vergleichs-
tabelle entfernt (colspan 9 -> 8).

P8 (gesammelt): (a) Filterwechsel/15s-Poll ohne Lade-Feedback -> .refreshing
dimmt bestehenden Inhalt waehrend fetch; (b) Trade-off-Legende (ch3) von
~19 Farben auf Top-5 + graue "Weitere"-Sammelserie reduziert (Muster wie im
Scatter, PR 38), Bubble-Groessen-Bedeutung im Untertitel ergaenzt; (c)
_chart_longitudinal liefert jetzt "n" (Runs) pro Punkt, Tooltip warnt bei
n=1 vor duenner Basis; (d) Kalibrierungs-Tabelle: leere Spalten (Human-
Fehlerquote/Agreement) mit min-width + Hover-Hinweis gegen Spalten-Kollaps;
(e) KPI "Automatisch akzeptiert" markiert (n=X*) mit Tooltip, wenn die
n-Basis von Fehlerquote/Belegrate abweicht (Log-Runs vs. evaluierte Notes);
(f) Sektions-Untertitel "Qualitaet" korrigiert (nannte "nach Version", keiner
der beiden Charts ist ein Versions-Chart); (g) redundante Klick-Sparkline an
der Accept-Kachel entfernt (Duplikat zu ch4 "Akzeptanzrate ueber Versionen");
(h) natives PDF-<select> bekommt text-overflow:ellipsis (vorher hartes
Abschneiden ohne Hinweis).

TDD fuer P8c: 2 neue Tests fuer _chart_longitudinal (RED vor Implementierung,
GREEN danach). Suite 5693 -> 5695, 0 failed. Verifikation: Playwright
(Vorher/Nachher-Screenshots hell+dunkel + programmatische Checks, alle
GREEN). Mistral-Cross-Review: keine Befunde.

Konfliktflaeche mit ungemergten PRs #217/#219 (beide aendern dieselbe HTML):
Chart-Titel/-Untertitel und die KPI-nTag-Zeile ueberschneiden sich mit #219s
Tooltip-Markup — Details in PR-Body.

Closes #204
Tilltime added 2 commits July 12, 2026 12:10
…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.
# Conflicts:
#	internal/dashboard/eval_dashboard.html
@TillQuandel TillQuandel merged commit 9726c96 into master Jul 12, 2026
4 checks passed
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.

2 participants