Matrix-Rendering-Fix + Dashboard-Politur-Bündel (Punkte 0–9)#312
Merged
Conversation
Till-Live-Befund auf master 64d8895 (eval_version 4.3 = 6 PDFs x 1 Version v0.3.144): Der Versions-Spaltenkopf "v0.3.144" rendert nicht in der Kopfzeile, sondern ~49px tiefer und ueberlappt Zeile 1 (Bates), die Kopfzeile selbst wirkt leer. Ursache (per Repro auf isoliertem Testserver mit read-only kopierten Live-Daten + Computed-Style-Diagnose bestaetigt, nicht geraten): die >=1201px-Sticky-Regel `table.cmp thead th` (#203 P3) trifft ueber die gemeinsame .cmp-Klasse auch table.pm-matrix. Der bestehende Kommentar ("sticky entfaellt fuer die Matrix ohnehin, da der pm-scroll-Wrapper ihr eigener Containing-Block ist") war ein Trugschluss: position:sticky verschiebt die Box bereits im Ruhezustand (ganz ohne Scrollen) um den top-Offset, sobald die statische Ausgangsposition die Constraint verletzt -- exakt der beobachtete 49px-Versatz. Zusaetzlich klebte die einzige Versions-Spalte am rechten Tabellenrand mit grosser Leerflaeche zur PDF-Spalte: `table.cmp { width:100% }` stretcht bei wenigen Spalten die Restbreite in die einzige Datenspalte. Fix: - `.table-wrap.pm-scroll table.cmp thead th` setzt position/box-shadow/ border-bottom explizit zurueck (Spezifitaet 0,3,3 schlaegt 0,1,3). - `table.cmp.pm-matrix { width: auto }` laesst die Matrix auf Inhaltsbreite schrumpfen -- Datenspalten ruecken links neben die PDF-Spalte, bei vielen Versionen bleibt der bestehende Scroll-Mechanismus (.pm-scroll) unveraendert wirksam. - Minor: Insight-Text zeigte "1 Versionen" statt "1 Version"; Singular/Plural-Fix gleich fuer "PDF-Quelle(n)" mitgezogen (identische Zeile/Technik). Nachweis: HTML-Anker-Tests (_build_live_html-Muster) fuer CSS-Reset, width:auto und die Singular/Plural-Ternaries; Playwright-Repro vor/nach Fix (Screenshot + computed styles: thPosition sticky->static, thRect/ firstCellRect-Ueberlappung aufgeloest, tableWidth 1162->336px) auf isoliertem Testserver mit read-only kopierten Live-Daten (eval_version 4.3).
Till-Wunsch + Zusatzbefund: Das Dashboard defaultete unveraenderlich auf
die neueste eval_version (4.3, 27 Baseline-Zeilen). Der URL-Param
?eval_version=4.1 hatte im Browser KEINE Wirkung -- der Client-Fetch von
/data.json reichte ihn nie durch (Bestands-Kommentar: "Eval-Version-
Filter entfernt: nie aus URL/State setzen"), obwohl der Server den Query-
Param bereits korrekt verarbeitete. Verifiziert: Seite zeigte unter
beiden URLs identisch 4.3.
Die Client-Funktionen fuer ein Eval-Version-Dropdown (onEvalVerChange,
_initEvalVerDropdown, _getEvalVerFromUrl/_setEvalVerInUrl) existierten
bereits verwaist im Code (Markup + Fetch-Wiring waren entfernt) --
wieder angeschlossen statt neu gebaut:
- Filterbar: neues Dropdown "Eval-Version" (Optionen aus
available_eval_versions, Anzeige "4.3 (n=27)"), Warn-Badge bei >1
Version.
- Server: available_eval_versions traegt jetzt {version, n} statt nur
den Versions-String -- n = Zeilenzahl je Version, ungefiltert
(dieselbe "Optionen vor allen Filtern"-Konvention wie all_pdfs_opts).
- loadAndRender() haengt eval_version an JEDEN /data.json-Fetch (inkl.
15s-Auto-Refresh, da derselbe Codepfad) -- /live.json bewusst NICHT
angefasst: separate, eval_version-fremde Live-Run-Progress-Seite ohne
eval_version-Konzept im Payload.
- Sichtpruefungs-Fund (Klick-Pfad 4.3->4.1->zurueck): sf-ev-pill blieb
nach manuellem Wechsel auf dem alten Wert stehen -- der Post-Fetch-
Early-Return in _initEvalVerDropdown griff faelschlich, weil
onEvalVerChange() _currentEvalVersion schon VOR dem Fetch setzt.
Rebuild jetzt nur noch an die Optionsmenge gekoppelt, pill/warn immer
aktualisiert.
- Sidebar: persistenter eval-Pill (#sf-ev-pill) neben #sf-time,
konsistent mit #pm-evalver in der Matrix-Sektion (beide aus
d.eval_version derselben Antwort).
Nachweis: Server-Tests (Zeilenzahl je Version, filterunabhaengig, Query-
Param-Aufloesung), HTML-Anker-Tests (_build_live_html-Muster) fuer
Markup/Fetch/Boot-Seed/Dropdown-Wiring, Node-Ausfuehrungstest fuer den
Pill-Guard-Fund. Playwright-Klick-Pfad auf isoliertem Testserver (Live-
Daten read-only): Dropdown zeigt "1.3 (n=87)/4.1 (n=515)/4.3 (n=27)",
Wechsel 4.3->4.1->zurueck aktualisiert Kopfzeile+Matrix+Pill korrekt,
Deep-Link ?eval_version=4.1 greift, Auswahl bleibt nach 17s (>1 Auto-
Refresh-Zyklus) stabil auf 4.1, 0 pageerrors.
…efixt D4-Bestand (Reviews 15.07.): der #305-Helper `_row_coverage()` fixte das `coverage_factual or coverage_rate`-Anti-Pattern (verschluckt eine ECHTE 0.0 als falsy, 0%-Coverage-Zeilen existieren real, Jockisch-Faelle) bisher nur fuer die neuen Matrix-/Paarvergleichs-Funktionen -- der Docstring markierte die Bestands-Stellen explizit als separates Ticket. 7 Bestandsstellen mit demselben Muster auf _row_coverage umgestellt: - eval_dashboard.py: _calc_kpis (avg_cov-KPI-Kachel), _calc_pdf_table (cov je PDF-Zeile), _chart_scatter + _build_quality_chart_data (Legacy-main()-Standalone-Pfad). - eval_dashboard_server.py: _read_calibration_data (llm_cov), quality_by_version-Aggregation (kpi_trend.cov-Sparkline), _chart_scatter_versioned (aktiver Scatter-Chart). _read_calibration_data arbeitet auf sqlite3.Row (kein `.get()`) -- _row_coverage dafuer Mapping-kompatibel gemacht (`in r.keys()` statt `.get()`, funktioniert fuer dict UND Row identisch) statt die Fallback- Logik an dieser einen Stelle separat zu duplizieren. Nicht angefasst (Suche + Pruefung, nicht blind ersetzt): die "Routing-only"-Zeilen in _calc_pdf_table setzen `cov: None` hartkodiert (keine Eval-Daten vorhanden, kein Anti-Pattern moeglich). Nachweis (TDD): 8 RED-Tests je Bestandsstelle (assert 90.0 == 0.0, zeigt das Verschlucken) + 2 fuer die sqlite3.Row-Kompatibilitaet von _row_coverage selbst, alle 10 vor dem Fix rot, danach gruen. Sweep generative/tests -k "dashboard or coverage or calib": 310 passed, keine Regression.
…es umgestellt D3 (Reviews 15.07.): version_delta() lief fuer ALLE KPI-Metriken (hall/cov/ n/accept/dur/tokens/cost) mit demselben kpi_trend["n"]-Array als n>=20- Reliability-Guard. Fuer hall/cov ist das korrekt (n = evaluierte Notes, das SIND Eval-Metriken). Fuer "accept" (Akzeptanzrate, gepoolt ueber ALLE gerouteten/generierten Notes, _pooled_accept) ist n der falsche, zu kleine Nenner -- nur eine Stichprobe wird LLM-evaluiert. Bug war "aktuell konservativ": kein falsches Delta, aber unnoetig graue/unreliable Chips, obwohl das Accept-Delta auf hunderten gerouteten Notes beruht. Fix: version_delta() bekommt optionalen n_field-Parameter (Default "n" -- rueckwaertskompatibel fuer hall/cov/dur/tokens/cost). Server uebergibt fuer "accept" n_field="accept_n" -- neues kpi_trend-Feld, Summe derselben (n_vault, n_total)-Paare wie _pooled_accept (SSoT, keine zweite Zaehlung). Nachweis (TDD): RED zeigte den Bug direkt (accept-Delta reliable=False bei n=8/9 evaluiert, obwohl 150/160 geroutete Notes vorlaegen) sowie den fehlenden n_field-Parameter/accept_n-Key. Nach Fix: 5/5 gruen, inkl. Rueckwaertskompatibilitaets-Test (hall-Delta ohne n_field unveraendert) und Server-Integrationstest (accept_n aus denselben Log-Runs wie _pooled_accept). Sweep generative/tests -k "dashboard or delta or accept": 325 passed, keine Regression.
…Pfad
D5 (Reviews 15.07.): "n_dropped" existiert strukturell NUR im DB-Fallback-
Pfad (eval_dashboard_server.py, pipeline_runs-Tabelle). Der primaere
Log-Pfad (_read_all_log_runs()) baut Zeilen aus [DRY-RUN]->(Vault|Inbox)-
Treffern -- verworfene Kandidaten (nie bis zum Draft gekommen) hinterlassen
dort GAR KEINE Zeile und tauchen im notes-Dict nie auf. `n_dropped` ist an
dieser Quelle nicht zufaellig leer, sondern nicht ermittelbar. Die Kachel
zeigte trotzdem eine stille "0" (`r.get("n_dropped", 0)`), ununterscheidbar
von "wirklich 0 verworfen" -- die bestehende Tooltip-Erklaerung deckte das
zwar schon auf, aber der angezeigte WERT selbst log weiter.
Fix: `_calc_kpis` liefert `total_dropped=None`, wenn KEINE Zeile in
all_log_runs den Key "n_dropped" traegt (Primaer-Pfad/leere Runs) -- die
Summe (inkl. echter 0, wenn die DB-Spalte 0 ist) bleibt unveraendert, sobald
mindestens eine Zeile den Key traegt (DB-Fallback-Pfad). Client zeigt "–"
statt der Zahl, Tooltip-Text an das neue Verhalten angepasst.
Bestehender Test `test_calc_kpis_total_dropped_zero_when_field_missing`
kodierte selbst das Anti-Pattern (`assert ... == 0` bei fehlendem Key) --
auf die korrigierte Erwartung (`is None`) aktualisiert, nicht geloescht
(Historie/Docstring-Verweis auf den D5-Fund ergaenzt).
Nachweis (TDD): RED zeigte `0 is None`-Fehlschlaege an allen 3 betroffenen
Stellen (kein Key, leere Runs, HTML-Anker fuer den JS-Ternary) vor dem Fix.
Nach Fix 5/5 gruen + aktualisierter Bestandstest gruen. Sweep
generative/tests -k dashboard: 312 passed, keine Regression.
…stent zur $-Eskalation #292-LOW (Reviews 15.07.): Die Nachkommastellen-Eskalation der Spark- Hilfslinien (_trendChart, kollidierende Achsenlabels bei enger Wertespanne) nutzte eine zweite, unit-fremde Formel (`_fmtDE(v, extra) + au.u`) statt der Basis-Formatierungsfunktion. Das passte zufaellig fuer "$"/generische Einheiten (deren Basis-fmt strukturell identisch ist), aber nicht fuer: - "%": Basis-Anzeige (_autoUnit) haengt GAR KEIN "%" an ("3,0" statt "3,0%") -- die Eskalationsformel dagegen IMMER (au.u="%"). Erst bei einer Werte-Kollision sprang das %-Zeichen ploetzlich auf, inkonsistent zur unmittelbar daneben unveraenderten Basis-Anzeige. - "h": Basis-Anzeige zeigt < 1h als gerundete Minuten ("2 min"), >= 1h als Stunden-Dezimalzahl ("1,2 h"). Die Eskalationsformel ignorierte das komplett und zeigte bei einer Kollision IMMER rohe Stunden-Dezimalen ("0,03h") -- ein Einheiten-Sprung weg von der Minuten-Konvention direkt daneben. Fix: `_autoUnit`s fmt-Funktionen nehmen jetzt einen optionalen Nachkomma- stellen-Parameter (Default = bisheriges hartkodiertes Verhalten je Zweig); die Eskalationsschleife ruft `au.fmt(v, extra)` statt der zweiten Formel -- SSoT, jede Einheit eskaliert automatisch in ihrer eigenen Konvention. "%" haengt dabei neu auch in der Basis-Anzeige den Suffix an (identisch zur Eskalation). Mischfall (Wertespanne straddled die 1h-Grenze nach Padding) zeigt weiterhin korrekt gemischte min/h-Einheiten -- kein Bug, dieselbe Logik wie die unveraenderte Basis-Anzeige, per Test abgesichert. Nachweis (TDD, Node-Ausfuehrung der echten JS-Funktionen, kein Nachbau): 2 RED-Failures vor dem Fix (fehlender %-Suffix in der Basis-Anzeige, Einheiten-Sprung auf Stunden-Dezimalen bei sub-1h-Kollision) + 6 bereits gruene Referenztests (u.a. $-Eskalation unveraendert). Nach Fix 9/9 gruen. Sweep generative/tests -k dashboard: 318 passed, keine Regression.
#294-Nebenfund (Reviews 15.07.): _chart_scaling() (Chart 3 "Scaling" + Legende 3) kopierte r["label"] unveraendert aus den all_log_runs-Zeilen. Im primaeren Log-Pfad (_read_all_log_runs) ist das `_PDF_LABELS.get(key, key)` -- fuer PDFs ausserhalb der 3 registrierten _PDF_LABELS-Eintraege (Regelfall) faellt das auf den rohen, klein- geschriebenen Log-Key zurueck (z. B. "cobaltite-paper" statt "Cobaltite Paper"). Dieselbe Bugklasse traf bereits Chart 2 (Trade-off, _chart_longitudinal) und wurde dort per U4-Fix lokal geloest (_PDF_LABELS.get(key) or re.sub(...).title()) -- ch3/leg3 bekam den Fix bisher nicht. Fix: dieselbe Formel in _chart_scaling, aber nur wenn label==key (der Fallback tatsaechlich griff) -- ein bereits besserer Label-Wert aus dem DB-Fallback-Pfad (pdf_label/pdf_source-Ableitung in eval_dashboard_server.py) bleibt unangetastet. Quelle bewusst NICHT zentral in _read_all_log_runs geaendert, um den Blast-Radius auf andere Konsumenten (_chart_acceptance u. a., nicht Teil dieses Fundes) nicht auszuweiten. Nachweis (TDD): RED zeigte den rohen Key in allen 3 Faellen (unregistrierte PDF, Multi-Separator-Key) vor dem Fix, GREEN danach (4/4) -- inkl. Regressionswaechter fuer den DB-Fallback-Fall (bereits guter Label bleibt erhalten). Sweep generative/tests -k dashboard: 322 passed, keine Regression.
Reviews 15.07.: Modell-Dropdown-Optionen zeigen jetzt die Anzahl VALIDER
Eval-Zeilen (hallucination_rate >= 0) je Modell -- Gemini-Fehllesungs-
Schutz: dort tragen Zeilen den bestehenden -1.0-Sentinel fuer "ungueltig"
(vgl. _chart_scatter/_matrix_cell_stats); n_valid zeigt fuer so ein Modell
korrekt 0, nicht die volle aber wertlose Zeilenzahl.
note_evals traegt kein eigenes "model"-Feld (das lebt in pipeline_runs) --
Join ueber run_id -> token_runs.model (bereits angereichert), derselbe
Mechanismus wie der bestehende Modell-Einzelwert-Filter. Basis
_matrix_base_rows (eval_version-skopiert, VOR pipeline_version/language/
pdf/run-Filtern) statt quality_rows -- dieselbe "Dropdown-Optionen vor
allen Filtern"-Konvention wie die Options-Liste selbst (Bestands-
Kommentar direkt darueber), sonst wuerde z. B. ein aktiver PDF-Filter die
angezeigten Zaehler unerwartet mitverschieben.
all_models wechselt von list[str] auf list[{model, n_valid}] (kein
bestehender Test/Konsument referenzierte die alte Form direkt). Client:
_initGlobalModelFilter zeigt "<Label> (n=X)", Dropdown-value bleibt der
Modellname (_rebuildFilterSelect-Signatur auf .model umgestellt,
Bestandsverhalten fuer die Auswahl-Persistenz ueber Auto-Refresh
unveraendert).
Nachweis (TDD): RED zeigte TypeError (all_models noch string[]) an allen
4 Stellen. Nach Fix 4/4 gruen -- inkl. Sentinel-Ausschluss, None-Werte
nicht als valide gezaehlt, Denylist-Regression, HTML-Anker fuer die
JS-Anzeige. Sweep generative/tests -k dashboard: 326 passed, keine
Regression.
…abel statt Filterbar Design-Iteration nach Till-Feedback (Sichtprüfung vor der ersten Playwright- Runde): Die urspruengliche Umsetzung von Punkt 1 haengte das eval_version- Select als eigenes Filterbar-Element an — brach deren Einzeilen-Layout bereits bei 1440px (6. Filter-Group + "weitere Versionen verfuegbar"-Badge passten nicht mehr in eine Zeile, Screenshot-Beleg vor dieser Korrektur). Filterbar-Variante verworfen zugunsten eines Inline-Selects am bestehenden, dezenten eval_version-Meta-Label in der Sidebar (#sf-ev-select, ersetzt den reinen Text-Pill #sf-ev-pill): gleiche Typografie/Farbe wie zuvor (Mono, amber-Pill-Optik unveraendert), appearance:none + eigener kleiner Chevron statt Systemstil, Optionen weiterhin "4.3 (n=27)" etc. Funktion unveraendert (Param in allen Fetches inkl. Auto-Refresh, Klick-Pfad-Fix aus der vorigen Runde bleibt erhalten: Select-Wert-Sync laeuft unabhaengig vom teuren Innerhtml-Rebuild). #pm-evalver (Matrix-Sektion) bleibt Read-only-Text und folgt der Auswahl unveraendert -- SSoT, beide lesen d.eval_version aus derselben Antwort. Die separate "weitere Versionen verfuegbar"-Warnbadge entfaellt: das Select selbst (sichtbare Optionen beim Oeffnen) ist bereits die Affordanz, dass es mehrere Versionen gibt -- kein zusaetzliches Element noetig, passt zum Ziel "so reduziert wie moeglich". Verifiziert (Playwright, isolierter Testserver, Live-Daten read-only): Filterbar wieder einzeilig bei 1440px UND 1100px, hell + dunkel (Screenshot- Vergleich vor/nach) -- Select fuegt sich unauffaellig neben "eval"-Label und "auto-refresh 15 s" ein, kein klobiges Systemelement in keinem der beiden Themes. 0 pageerrors. Nachweis: HTML-Anker-Tests aktualisiert (kein Filterbar-Element mehr, #sf-ev-select in der Sidebar, Fetch/Boot-Seed/Dropdown-Wiring-Tests unveraendert gueltig), Node-Ausfuehrungstest fuer den Select-Wert-Sync-Fund auf das neue Element umgestellt. generative/tests -k dashboard weiterhin gruen (separat vor dem naechsten Commit erneut voll durchlaufen).
…9 Filterbar-Umbruch) Reviews 15.07.: U8: kpis-perf (5 Kacheln) brach bei <=1200px auf 4 Spalten bzw. bei <=900px auf 2 Spalten -- 5 ist durch keine der beiden Spaltenzahlen teilbar (5%4=1, 5%2=1), die letzte Kachel stand dadurch allein in der letzten Zeile mit leeren Spalten daneben (Playwright-Diagnose 1100px: Kachel 4 bei x=249..455 statt bis zum rechten Rand, 3 Spalten sichtbar leer). Fix: letzte Kachel spannt per grid-column:1/-1 die volle Breite, wenn sie allein in ihrer Zeile steht -- an beiden Breakpoints. U9: das Modell-Filter brach als letzte Filter-Group allein in eine neue Zeile. Kompaktere Gruppierung durch engeres .filter-group-Padding (14px auf 10px je Seite) und schmalere .fselect-Maximalbreite (200px auf 150px) bei <=1200px -- 3 der 5 Groups stiessen vorher an die 200px-Kappung und ueberschritten die verfuegbare Breite (~880px) um ~370px. Bei 1100px bleibt "Modell" trotzdem allein in Zeile 2 (die reine Inhaltsbreite der uebrigen 4 Groups reicht nicht fuer alle 5 in eine Zeile, ohne PDF-/Lauf-Namen bei echten Filterwerten unlesbar zu stauchen) -- aber jetzt kompakt statt mit grossem Leerraum, kein Bruch mehr. Verifiziert (Playwright, isolierter Testserver, Live-Daten read-only, 1100px, hell+dunkel): KPI-Zeile ohne Luecke, Filterbar-Umbruch kompakt, 0 pageerrors. Nachweis: HTML-Anker-Tests fuer beide Breakpoint-Bloecke (grid-column-Span an beiden Media-Queries, Padding-/Max-Width-Kompaktierung). Sweep generative/tests -k dashboard: 330 passed, keine Regression.
…d-Ansicht S2-C3 (Multi-Perspektiven-Dashboard-Review 2026-07-15): note_evals enthaelt nur AKZEPTIERTE Notes (Selektions-Bias) -- sinkt die Akzeptanzquote gleichzeitig mit einer Fehlerquoten-Aenderung, ist Letztere teils Stichproben-Verschiebung statt reiner Qualitaetseffekt, ohne dass das im bestehenden Fehlerquote-Spark-Chart sichtbar waere. Fix: Akzeptanzquote als zweite, gestrichelte/gedaempfte Serie im BESTEHENDEN Fehlerquote-Spark-Chart (Klick auf die Fehlerquote-KPI-Kachel, _trendChart) -- kein neues Chart gebaut. _trendChart bekommt zwei optionale Parameter (points2, label2); Y-Achse deckt beide Serien gemeinsam ab (nur bei gleicher Einheit %, sonst ergibt eine gemeinsame Skala keinen Sinn -- z. B. die Dauer-Kachel bleibt unveraendert). Ohne points2 (alle anderen KPI-Kacheln) ist das Verhalten exakt wie zuvor. Zweite Serie bewusst schlank: eigene kleinere Punkte, aber OHNE Hover- Tooltip-Kaesten (Zweck ist der visuelle Trend-Vergleich, kein zweiter voller Interaktionslayer) -- kleine SVG-Legende (gestrichelte Linie + Label) oben links macht die zweite Serie ohne Rueckgriff auf die Kachel- Farbtoene erkennbar. Nachweis: Node-Ausfuehrungstest der echten _trendChart-Funktion (kein Nachbau) -- Overlay rendert bei uebergebenem points2, faellt bei fehlendem points2/nicht-%-Einheit/Laengen-Mismatch sauber weg (Regressionswaechter fuer alle anderen Kacheln), gemeinsame Skala deckt Werte ausserhalb des Fehlerquote-Bereichs ab. HTML-Anker fuer die Aufrufer-Verdrahtung (nur Fehlerquote-Kachel). Playwright-Sichtpruefung (eval_version 4.1, 15 Pipeline-Versionen, hell+dunkel): zwei klar unterscheidbare Linien (durch- gezogen amber + gestrichelt gedaempft) auf gemeinsamer 0-100%-Achse, Legende sichtbar, 0 pageerrors. Sweep generative/tests -k dashboard: 336 passed.
Nachbesserung Punkt 1 (4 externe Reviews nach PR #312, UX konvergent, Beleg a04-matrix-41-light-scroll400.png): der pauschale `.table-wrap.pm-scroll table.cmp thead th`-Reset (Punkt 0) setzte auch th:first-child ("Quell-PDF") auf position:static zurueck. Beim Horizontal-Scroll scrollte die Kopfzelle dadurch mit den Versions-Headern weg, waehrend die per td:first-child gepinnte PDF-Namen-Spalte im tbody stehen blieb -- der jeweils linkeste sichtbare Versions-Header landete optisch ueber der gepinnten Spalte. Fix: Reset gilt nur noch fuer thead th:not(:first-child); th:first-child bekommt eine eigene Sticky-Left-Regel (top:auto hebt das top:49px der >=1201px-Media-Query explizit auf, z-index 3 haelt sie ueber den statischen Versions-Headern und der generischen th:first-child/ td:first-child-Regel). Verifiziert per Playwright (playwright-core + firefox-1532, Testserver 8069 mit read-only kopierten Live-Daten, eval_version 4.1/15 Versionen): 1440px und 1100px, hell+dunkel, "Quell-PDF" bleibt beim Scroll sauber gepinnt, kein Versions-Header ueberlappt, 0 pageerrors.
Nachbesserung Punkt 5 (trivial): die Cap-Fussnote "N weitere Version(en)
... nicht gezeigt" war keine echte Singular/Plural-Behandlung -- bei
genau 1 uebrig gebliebenen Version stand weiterhin die Klammer "(en)"
da. Gleiche Technik wie die bereits gefixte Matrix-Insight-Zeile ("1
Version" statt "1 Versionen").
Verifiziert live (eval_version 4.1, 35 gedroppte Versionen): Text zeigt
jetzt "35 weitere Versionen" statt "35 weitere Version(en)".
Nachbesserung Punkt 2 (UX-Review 16.07, Beleg c-drift-v0.3.140-vs-v0.3.60-1100-light.png): der table-wrap-Wrapper der Versions-Vergleichstabelle (Paarvergleich-Sektion) hatte keine pm-scroll-Klasse -- bei 1100px war die letzte Spalte "Paarung" hart abgeschnitten, bei 1280px blieben nur 2px Restluft (fragil, gleiche Bugklasse wie #305/Punkt 0 oberhalb 1201px). Zusaetzlich lebte der laengere PDF-Key-Drift-Hinweis als Teil der zentrierten <td colspan>- Zeile INNERHALB der Tabelle -- deren Breite wird von den 9 Header-th bestimmt, unabhaengig vom (leeren) tbody-Inhalt, und ueberstieg die sichtbare Wrapper-Breite bei 1100px: der Text wurde mitten im Wort geclippt. Fix: derselbe pm-scroll-Mechanismus wie an der Matrix (haelt den Scroll-Container in allen Viewport-Breiten aktiv, pinnt zugleich die "Quell-PDF"-Spalte links). Der laengere Drift-Hinweis wandert in die bestehende `summary`-Zeile ausserhalb des Scroll-Containers (Section- statt Tabellenbreite als Umbruchgrenze); die In-Table-Leerzeile bleibt kurz genug, um nie clippen zu muessen. Verifiziert per Playwright (1100/1280/1440px, hell+dunkel): "Paarung" nach Scroll vollstaendig sichtbar, Drift-Hinweis komplett lesbar ohne Clipping, 0 pageerrors.
Nachbesserung Punkt 4 (Statistiker-Empfehlung, Reviews 15./16.07 zu PR #312): unter eval_version 4.3 haengen alle 27 note_evals-Zeilen an pipeline_version v0.3.144 -- das ist der Code-Stand des Re-Eval-Sweeps (reeval_baseline.py), NICHT die Erzeugungsversion der Notes. Der zugehoerige Lauf in pipeline_runs traegt dafuer den expliziten Marker pipeline_version="reeval" (verifiziert im Quellcode), verknuepft ueber run_id. Ohne Kennzeichnung liest die Matrix "v0.3.144" faelschlich als Erzeugungsversion. Fix: neue reine Funktion D._is_reeval_series(quality_rows, pipeline_runs) prueft, ob JEDE Zeile der aktiven eval_version (_matrix_base_rows, vor Einzelwert-Filtern) an einen pipeline_runs-Eintrag mit pipeline_version=="reeval" haengt. Server exponiert das Ergebnis als is_reeval_series im Payload; die Matrix-Insight-Sektion zeigt bei True die Zeile "Re-Eval-Serie: Werte messen den Eval-Stand, nicht die Erzeugungsversion der Notes." RED->GREEN: Server-/HTML-Anker-Tests liefen zuerst gegen den unveraen- derten Server (5 fehlgeschlagen, KeyError/AssertionError), nach Server- und HTML-Verdrahtung 10/10 gruen. Verifiziert live gegen die kopierten Produktionsdaten: eval_version 4.3 (realer #232-Fall) zeigt is_reeval_series=True + sichtbaren Hinweis, eval_version 4.1 (normale Generierungs-Runs) korrekt False/kein Hinweis. Playwright hell+dunkel, 0 pageerrors.
Nachbesserung Punkt 3 (Statistiker MEDIUM + UX-P3, Reviews 15./16.07 zu PR #312): die gemeinsame 0-100%-Achse des Overlays stauchte die Fehlerquoten-Serie auf ~1/3 der Chart-Hoehe (vorher hall-adaptiv), und 100%-Accept-Punkte aus n=1-3-Laeufen waren optisch nicht von belastbaren unterscheidbar; der Punkt-Tooltip zeigte nur die Fehlerquote, die Legende nur die Zweitserie. Fix (4 Teile): (a) Overlay zuschaltbar, Default AUS -- kleiner Toggle am Chart-Kopf (nur Fehlerquote-Kachel), Zustand in localStorage (analog theme-mode). Aus bleibt die Achse hall-adaptiv wie vor dem PR. (b) Punkt-Tooltip zeigt bei aktivem Overlay beide Werte ("12,0% / Akzeptanz 72,2%"). (c) Legende benennt BEIDE Serien (vorher nur die Zweitserie) + Caveat- Zeile erklaert die unterschiedliche Basis (geroutete/generierte vs. evaluierte Notes). (d) Accept-Punkte mit accept_n<20 (Payload-Feld aus Punkt 4/D3, bereits vorhanden) werden gedimmt statt gleichwertig zu belastbaren Werten dargestellt. Verifiziert per Playwright gegen echte kopierte Produktionsdaten (eval_version 4.1, 50 Versionen): Default aus (keine Dashed-Linie, Achse 0-37,4%), nach Zuschalten Achse 0-100% + beide Serien in der Legende + Caveat-Text + 4/48 Punkte volltonig (accept_n>=20) Rest gedimmt (0.35 Opacity) + kombinierter Tooltip, Toggle-Zustand bleibt nach Reload erhalten. Hell+dunkel, 0 pageerrors.
This was referenced Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Übersicht
Sammel-PR: Matrix-Rendering-Fix (Punkt 0, Till-Live-Befund) + 9 Politur-Punkte aus den Multi-Perspektiven-Reviews vom 15.07. Je Punkt ein Commit. Basis
master64d8895. Alle Punkte umgesetzt (0 von 10 ausgelassen).Arbeitsweise: eigener Worktree, isolierter Testserver (Port 8069) mit read-only kopierten Live-Daten (
quality_history.jsonl+atomic_analytics.db, Stand 16.07.), Playwright (playwright-core + firefox-1532) für alle UI-Punkte bei 1440px UND 1100px, hell+dunkel. TDD mit RED-Nachweis für die Datenlogik-Punkte (2, 4, 5, 6, 7).Punkt 0 — Matrix-Rendering kaputt (Till-Live-Befund, PFLICHT)
Befund-Quelle: Till-Live-Server, Screenshots
matrix-sicht-4.3.png(eval_version 4.3 = 6 PDFs × 1 Version v0.3.144).Ursache (per Repro auf isoliertem Testserver + Computed-Style-Diagnose bestätigt, nicht geraten):
>=1201px-Sticky-Regeltable.cmp thead th(Dashboard responsive: 1100px verschluckt Zeitstempel, Tabellen ohne Horizontal-Scroll, Sticky-Leiste verdeckt Tabellenköpfe #203 P3) trifft über die gemeinsame.cmp-Klasse AUCHtable.pm-matrix. Der bestehende Code-Kommentar ("sticky entfällt für die Matrix ohnehin, da der pm-scroll-Wrapper ihr eigener Containing-Block ist") war ein Trugschluss:position: stickyverschiebt die Box bereits im Ruhezustand — ganz ohne zu scrollen — um dentop-Offset (49px), sobald die statische Ausgangsposition die Constraint verletzt. Diagnose vor Fix:thPosition: sticky,thRect.y=249überlapptfirstCellRect(233–298) — exakt der beobachtete Versatz in Zeile 1.table.cmp { width: 100% }stretcht bei wenigen Spalten (hier: 1 Version) die Restbreite in die einzige Datenspalte → riesige Leerfläche zwischen PDF-Spalte und Werten, Header klebt rechts.Fix:
.table-wrap.pm-scroll table.cmp thead th { position: static; box-shadow: none; border-bottom: 1px solid var(--hair); }— Spezifität (0,3,3) schlägt die Media-Query-Regel (0,1,3).table.cmp.pm-matrix { width: auto; }— Matrix schrumpft auf Inhaltsbreite; bei vielen Versionen (16 Spalten) bleibt der bestehende.pm-scroll-Mechanismus unverändert wirksam.Nachweis: HTML-Anker-Tests (
test_dashboard_version_pdf_matrix.py, 3 neue Tests) + Playwright-Repro VOR/NACH Fix (Screenshot + computed styles:thPositionsticky→static, Überlappung aufgelöst,tableWidth1162→336px). Screenshots:final-02-matrix-1440-light.png,final-02-matrix-1100-dark.pngu. a.Punkt 1 — eval_version-Dropdown (Till-Wunsch, PFLICHT + Zusatzbefund)
Befund-Quelle: Till-Wunsch + Zusatzbefund während der Umsetzung:
?eval_version=4.1in der URL hatte im Browser KEINE Wirkung — der Client-Fetch von/data.jsonreichte den Param nie durch (Bestands-Kommentar: „Eval-Version-Filter entfernt: nie aus URL/State setzen"), obwohl der Server den Query-Param bereits korrekt verarbeitete. Verifiziert: Seite zeigte unter beiden URLs identisch 4.3.Design-Iteration (Till-Feedback nach erster Sichtprüfung): Die ursprüngliche Umsetzung hängte das Select als eigenes Filterbar-Element an — brach deren Einzeilen-Layout bereits bei 1440px (6. Filter-Group + Warn-Badge passten nicht mehr in eine Zeile). Filterbar-Variante verworfen zugunsten eines Inline-Selects am bestehenden, dezenten eval_version-Meta-Label in der Sidebar (
#sf-ev-select, ersetzt den reinen Text-Pill) — kein zusätzliches Filterbar-Element mehr.#pm-evalver(Matrix-Sektion) bleibt Read-only-Text und folgt der Auswahl unverändert (SSoT).Fix:
available_eval_versionsträgt jetzt{version, n}statt nur den String —n= Zeilenzahl je Version, ungefiltert (Anzeige „4.3 (n=27)").onEvalVerChange,_initEvalVerDropdown,_getEvalVerFromUrl) wieder verdrahtet statt neu gebaut —_currentEvalVersionwird beim Boot aus der URL geseedet,loadAndRender()hängteval_versionan JEDEN/data.json-Fetch (inkl. 15s-Auto-Refresh, da derselbe Codepfad)./live.json(separate Pipeline-Progress-Seite ohne eval_version-Konzept) bewusst NICHT angefasst._initEvalVerDropdowngriff fälschlich, weilonEvalVerChange()_currentEvalVersionschon vor dem Fetch setzt. Rebuild jetzt nur noch an die Optionsmenge gekoppelt, Auswahl-Sync immer.Nachweis: Server-Tests (Zeilenzahl je Version, filterunabhängig), HTML-Anker-Tests (kein Filterbar-Element, Sidebar-Select vorhanden), Node-Ausführungstest für den Select-Wert-Sync-Fund. Playwright-Klick-Pfad: Dropdown zeigt „1.3 (n=87) / 4.1 (n=515) / 4.3 (n=27)", Wechsel 4.3→4.1→zurück aktualisiert Kopfzeile+Matrix+Select korrekt, Deep-Link
?eval_version=4.1greift, Auswahl bleibt nach 17s (>1 Auto-Refresh-Zyklus) stabil auf 4.1, Filterbar wieder einzeilig bei 1440px UND 1100px (hell+dunkel), 0 pageerrors. Screenshots:final-03/04-clickpath-eval-*.png,01e/01f-sidebar-*.png.Punkt 2 — n-valid-Badge je Modell-Filter-Option
Befund-Quelle: Reviews 15.07.
Fix: Modell-Dropdown-Optionen zeigen jetzt die Anzahl VALIDER Eval-Zeilen (
hallucination_rate >= 0) je Modell — Gemini-Fehllesungs-Schutz: dort tragen Zeilen den bestehenden-1.0-Sentinel;n_validzeigt korrekt 0, nicht die volle aber wertlose Zeilenzahl.note_evalsträgt kein eigenesmodel-Feld (lebt inpipeline_runs) — Join überrun_id→token_runs.model, derselbe Mechanismus wie der bestehende Modell-Einzelwert-Filter. Basis:_matrix_base_rows(eval_version-skopiert, vor allen Einzelwert-Filtern) — dieselbe „Dropdown-Optionen vor allen Filtern"-Konvention wie die Options-Liste selbst.all_modelswechselt vonlist[str]auflist[{model, n_valid}](kein bestehender Konsument referenzierte die alte Form).Nachweis: 4 Server-/HTML-Anker-Tests (Sentinel-Ausschluss, None nicht als valide gezählt, Denylist-Regression, JS-Anzeige). Playwright: Dropdown zeigt z. B. „anthropic/claude-sonnet-4-6 (n=27)" neben mehreren „(n=0)"-Modellen (korrekt, da eval_version 4.3 nur diesem einen Modell Daten liefert). Screenshot
final-05-modell-dropdown-offen.png.Punkt 3 — Accept×Hall-Overlay je Version (S2-C3)
Befund-Quelle: Statistik-Review 15.07.:
note_evalsenthält nur AKZEPTIERTE Notes (Selektions-Bias) — sinkt die Akzeptanzquote gleichzeitig mit einer Fehlerquoten-Änderung, ist Letztere teils Stichproben-Verschiebung statt reiner Qualitätseffekt, ohne dass das im bestehenden Chart sichtbar wäre.Fix: Akzeptanzquote als zweite, gestrichelte/gedämpfte Serie im BESTEHENDEN Fehlerquote-Spark-Chart (Klick auf die Fehlerquote-KPI-Kachel,
_trendChart) — kein neues Chart gebaut (Spec-Vorgabe)._trendChartbekommt zwei optionale Parameter (points2,label2); Y-Achse deckt beide Serien gemeinsam ab, nur bei gleicher Einheit (%) — sonst ergibt eine gemeinsame Skala keinen Sinn (z. B. Dauer-Kachel bleibt unverändert). Zweite Serie bewusst schlank: eigene kleinere Punkte, aber ohne Hover-Tooltip-Kästen (Zweck ist der visuelle Trend-Vergleich); kleine SVG-Legende oben links.Nachweis: Node-Ausführungstest der echten
_trendChart-Funktion (6 Tests: Overlay rendert beipoints2, fällt bei fehlendempoints2/nicht-%-Einheit/Längen-Mismatch sauber weg, gemeinsame Skala deckt Werte außerhalb des Fehlerquote-Bereichs ab, Aufrufer-Verdrahtung nur für die Fehlerquote-Kachel). Playwright (eval_version 4.1, 15 Pipeline-Versionen, hell+dunkel): zwei klar unterscheidbare Linien auf gemeinsamer 0–100%-Achse, Legende sichtbar, 0 pageerrors. Screenshot03-accept-hall-overlay*.png.Punkt 4 — accept-Delta-Guard auf geroutete statt evaluierte Notes (D3)
Befund-Quelle: Statistik-Review 15.07.
Fix:
version_delta()lief für ALLE KPI-Metriken mit demselbenkpi_trend["n"]-Array (evaluierte Notes) als n≥20-Reliability-Guard. Für „accept" (Akzeptanzrate, gepoolt über ALLE gerouteten/generierten Notes) ist das der falsche, zu kleine Nenner — Bug ist latent: auf dem aktuellen Bestand kippt kein einziges accept-Delta von unreliable zu reliable (adversarialer Audit bestätigt: kein Delta wechselt den Zustand) — die sichtbare, verifizierte Wirkung ist ein korrigierter Tooltip-Nenner am Delta-Chip (das „n=" in „Version X (n=…)" wechselt von der Zahl evaluierter auf die Zahl gerouteter Notes, konkret 40→64 bzw. 10→4 in den geprüften Fällen). Neuer optionaler Parametern_field(Default"n", rückwärtskompatibel); Server übergibt für „accept"n_field="accept_n"— neueskpi_trend-Feld, Summe derselben(n_vault, n_total)-Paare wie_pooled_accept(SSoT).Nachweis: RED zeigte den Bug direkt (accept-Delta
reliable=Falsebei n=8/9 evaluiert, obwohl 150/160 geroutete Notes vorlagen). 5/5 grün nach Fix, inkl. Rückwärtskompatibilitäts- und Server-Integrationstest.Punkt 5 — coverage_factual-or-Anti-Pattern an Bestandsstellen (D4-Bestand)
Befund-Quelle: Statistik-Review 15.07. Der #305-Helper
_row_coverage()fixte dascoverage_factual or coverage_rate-Anti-Pattern (verschluckt eine ECHTE 0.0 als falsy, reale 0%-Coverage-Zeilen existieren) bisher nur für die neuen Matrix-Funktionen — Docstring markierte die Bestandsstellen explizit als separates Ticket.Fix: 7 Bestandsstellen auf
_row_coverageumgestellt (_calc_kpis,_calc_pdf_table,_chart_scatter,_build_quality_chart_dataineval_dashboard.py;_read_calibration_data,quality_by_version-Aggregation,_chart_scatter_versionedineval_dashboard_server.py)._read_calibration_dataarbeitet aufsqlite3.Row(kein.get()) —_row_coveragedafür Mapping-kompatibel gemacht (in r.keys()statt.get()) statt die Logik zu duplizieren. Nicht angefasst (geprüft, nicht blind ersetzt): die „Routing-only"-Zeilen in_calc_pdf_tablesetzencov: Nonehartkodiert (keine Eval-Daten vorhanden).Nachweis: 10 RED-Tests (8 zeigen
90.0 == 0.0, also das Verschlucken; 2 für die sqlite3.Row-Kompatibilität), alle grün nach Fix.Punkt 6 — Dropped-Zählung ehrlich „–" statt stiller 0 (D5)
Befund-Quelle: Statistik-Review 15.07.
Fix:
n_droppedexistiert strukturell NUR im DB-Fallback-Pfad (pipeline_runs-Tabelle). Der primäre Log-Pfad (_read_all_log_runs) baut Zeilen aus[DRY-RUN]->(Vault|Inbox)-Treffern — verworfene Kandidaten hinterlassen dort GAR KEINE Zeile, sind also nicht ermittelbar, nicht nur zufällig 0._calc_kpisliefert jetzttotal_dropped=None, wenn keine Zeile den Keyn_droppedträgt; Client zeigt „–" mit angepasstem Tooltip statt der Zahl. Bestehender Test, der die alte (== 0) Erwartung kodierte, aktualisiert (nicht gelöscht, Docstring-Verweis ergänzt).Nachweis: RED zeigte
0 is Nonean 2 Stellen + fehlenden HTML-Anker. 5/5 grün nach Fix.Punkt 7 — Spark-Achsen-Eskalation für %-/h-Einheiten (#292-LOW)
Befund-Quelle: Reviews 15.07.
Fix: Die Nachkommastellen-Eskalation bei kollidierenden Spark-Hilfslinien-Labels nutzte eine zweite, unit-fremde Formel, die zufällig zu „$" passte, aber nicht zu „%" (Basis-Anzeige hängt gar kein „%" an — inkonsistent sobald eskaliert wurde) oder „h" (< 1h zeigt „N min", Eskalation erzwang rohe Stunden-Dezimalen — Einheiten-Sprung).
_autoUnits fmt-Funktionen nehmen jetzt einen optionalen Nachkommastellen-Parameter; die Eskalation ruftau.fmt(v, extra)— SSoT, jede Einheit eskaliert in ihrer eigenen Konvention. „%" hängt neu auch in der Basis-Anzeige den Suffix an.Nachweis: Node-Ausführungstest der echten JS-Funktionen — 2 RED-Failures vor dem Fix (fehlender %-Suffix, Einheiten-Sprung bei h), 9/9 grün danach inkl. Mischfall-Regressionswächter (Wertespanne straddled die 1h-Grenze → weiterhin korrekt gemischte min/h-Einheiten, kein Bug).
Punkt 8 — ch3/leg3 Rohkey-Labels (#294-Nebenfund)
Befund-Quelle: Reviews 15.07.
Fix:
_chart_scaling()(Chart 3 „Scaling" + Legende 3) kopierter["label"]unverändert — im primären Log-Pfad ist das_PDF_LABELS.get(key, key), fällt für PDFs außerhalb der 3 registrierten Einträge (Regelfall) auf den rohen, kleingeschriebenen Log-Key zurück. Dieselbe Bugklasse traf bereits Chart 2 (dort per U4-Fix lokal gelöst) — jetzt dieselbe Formel in_chart_scaling, aber NUR wennlabel==key(der Fallback tatsächlich griff), ein bereits besserer Label-Wert aus dem DB-Fallback-Pfad bleibt unangetastet.Nachweis: 4 RED-Tests zeigten den rohen Key, GREEN danach inkl. Regressionswächter für den DB-Fallback-Fall.
Punkt 9 — Responsive-Bruch bei 1100px (U8/U9)
Befund-Quelle: UX-Review 15.07.
U8:
kpis-perf(5 Kacheln) ist durch keine der beiden Breakpoint-Spaltenzahlen (4 bzw. 2) teilbar — die letzte Kachel stand allein in der letzten Zeile mit leeren Spalten daneben. Fix: letzte Kachel spannt pergrid-column: 1 / -1die volle Breite, wenn sie allein in ihrer Zeile steht (an beiden Breakpoints).U9: Das Modell-Filter brach als letzte Filter-Group allein in eine neue Zeile. Kompaktere Gruppierung durch engeres
.filter-group-Padding (14px→10px) und schmalere.fselect-Maximalbreite (200px→150px) bei ≤1200px. Bei 1100px bleibt „Modell" trotzdem allein in Zeile 2 — die reine Inhaltsbreite der übrigen 4 Groups reicht nicht für alle 5 in eine Zeile, ohne PDF-/Lauf-Namen bei echten Filterwerten unlesbar zu stauchen — aber jetzt kompakt statt mit großem Leerraum, kein Bruch mehr. Offener Punkt: volle Einzeiligkeit bei 1100px nicht erreicht (Kompromiss zugunsten der Lesbarkeit von Lauf-/PDF-Namen bei realen Werten).Nachweis: HTML-Anker-Tests für beide Breakpoint-Blöcke. Playwright 1100px hell+dunkel: KPI-Zeile ohne Lücke, Filterbar-Umbruch kompakt statt mit großem Leerraum, 0 pageerrors.
Test-/Lint-Status
pytest generative lib/decision_engine/tests shared/tests -q: 6091 passed, 3 skipped, 8 deselected (Baseline vor dieser Serie: 6035 passed — 56 neue Tests, 0 Failures, 268s).ruff check .+ruff format --check .: alle Dateien grün.eval_dashboard.html: durchgehend genau 1 (bewusst).Kein Merge — externe Reviews (Statistiker + UX/UI) folgen.