fix(dashboard): per-PDF-Tabelle auf eine Datengrundlage — kanonischer pdf_key, Version+n pro Zeile, Union-Iteration (#194)#222
Open
TillQuandel wants to merge 2 commits into
Conversation
… pdf_key, Version+n pro Zeile, Union-Iteration (closes #194) Die per-PDF-Tabelle stand strukturell auf drei Datengrundlagen je Zeile: version/accept aus den Routing-Logs, hall/cov über ALLE Versionen gepoolt (Substring-Match), n_notes = Eval-Instanzen. Ergebnis: Bates-Dreifachzeilen (pdf_key-Drift bates/bates-2017/bates---2017), fehlende PDFs (nur in quality_rows: Hertzum, Kaletski) und version-gemischte Kennzahlen. - _pdf_group_key: kanonischer Gruppen-Schlüssel (SSoT mit PDF-Filter/-Dropdown, #202). Autor-Jahr-Slug — kollabiert Volltitel+Kebab derselben Quelle, hält Jahrgänge getrennt (Beutelspacher 2014 vs. 2022). _dedupe_pdf_options nutzt ihn mit. - _calc_pdf_table: Union-Iteration über Eval- und Routing-Quelle; hall/cov/ n_notes aus GENAU der neuesten Eval-Version (gegen config.AGENT_VERSION gekappt, #191-Orphan-Guard); n_notes = distinct Notes. accept bleibt aus dem Routing (note_evals hat kein acceptance_status) und wird getrennt via accept_ver/accept_n ausgewiesen. - _chart_acceptance: leitet aus den kanonischen Tabellenzeilen ab (SSoT) und trägt n je Balken → ins-quality-Streifen filtert n>0 („0 % von n Notes" vs. „0 Notes"). - KPI n_pdfs: distinct kanonische Quellen statt len(log_data) (zählte bates 3×, verpasste eval-only PDFs). KPI n_notes: distinct Notes statt Eval-Instanzen. - HTML: Seiten-Spalte entfernt (strukturell tot, DB-Fallback hart 0, nicht ableitbar); Version-/Akzeptiert-Spalten als Eval- bzw. Routing-Quelle beschriftet, accept-n in der Zelle. - Tests: test_dashboard_pdf_table.py reproduziert die 4 Hauptbefunde (RED→GRÜN).
TillQuandel
pushed a commit
that referenced
this pull request
Jul 12, 2026
…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).
…Version aus Payload, Orphan-Kennzeichnung (#194 Nachbesserung) Nachbesserung an PR #222 nach Cross-Model-Review (Fable/Mistral/Qwen, alle am Code bestätigt). 1 HOCH Legacy-Renderpfad (`python eval_dashboard.py` → _render_pdf_table) crashte mit KeyError 'pages' — _calc_pdf_table liefert das Feld nicht mehr. Seiten-Spalte auch im Legacy-Render entfernt (colspan 9→8), None-Version abgefangen. 2 HOCH Ein jahrloser Autor-Key ("beutelspacher") ist Segment-Präfix MEHRERER Jahrgangs-Gruppen und wurde per max(key=len)-Tie order-abhängig einer Gruppe zugeschlagen. Neue Regel: exakter Gruppen-Key gewinnt; sonst nur bei GENAU einem Match zuordnen; mehrdeutiger Kurz-Key → eigene Routing-only-Zeile (nicht raten). Zuordnung deterministisch. 3 MITTEL renderTable: "cur"-Highlight kam aus rows[last].version (Zeilen- reihenfolge) — die neuen Routing-only-Zeilen am Ende kaperten "aktuell" (#191-Bugklasse). Jetzt aus warnings.current_version (Server-Payload). 4 MITTEL kpi_trend.n zählte Eval-Instanzen (52), die "Evaluierte Notes"-Kachel distinct (40). Neuer SSoT-Helfer _distinct_notes, von Kachel UND Sparkline genutzt. 5 MITTEL Reine Orphan-Zeilen (alle Versionen > config.AGENT_VERSION) zeigten die Fallback-Version unmarkiert. orphan-Flag + ⚠-Badge/Tooltip. 6 NIEDRIG Leerer Gruppen-Key (pdf=".pdf") erzeugte Geisterzeile und _pdf_matches('') matchte ALLES. Sentinel "(unbenannt)", leere Keys nie als Match-Filter (auch in _calc_kpis n_pdfs). 7 NIEDRIG Routing-only-Zeilen tragen die Routing-Version im "Version (Eval)"- Feld. routing_only-Flag + "nur Routing"-Marker + präziser Tooltip. TDD: generative/tests/test_dashboard_pdf_table.py +8 Tests (RED→GRÜN) für 1,2,4,5,6,7. Suite 5709 passed, 0 failed. ruff format --check + check grün.
TillQuandel
pushed a commit
that referenced
this pull request
Jul 12, 2026
…/Extractor/Faithfulness-Skip-Events + Dashboard-Event-Robustheit (#197 Nachbesserung) Extern verifizierte Funde (Mistral-Resample + Fable). Der Gate-Funnel ist oben dicht: jeder Note-Verlust traegt jetzt ein stage_outcome-Event. Funnel-Luecken (orchestrator.py, TDD): - entity_resolution Cluster-Merge (consumed-Set): dedup/dropped/ entity_resolution_merge, Survivor als detail (strukturell wie sibling-Dedup). - _collect_stage6_results BaseException-Zweig: droppte ohne Event UND ohne Crash-Report — jetzt beides (Crash-Report ohne per-Note-Payload + Event). - run_extractors_per_concept: empty-ctext (Konzept nicht im Volltext), None (leere Extraktion) und Exception (call_failed) → extractor/dropped-Events. - _apply_faithfulness_gate: Gate aktiv + create, aber leerer page_index (PDF ohne [S. N]-Marker) → faithfulness/skipped/no_page_index. Dashboard-/DB-Event-Robustheit (minimale Hunks): - eval_dashboard._read_token_runs: Event-Records (kein model) vom call-Zaehler ausschliessen — verhindert aufgeblaehte calls + 0-Token-Phantomzeile. - eval_dashboard_server model-Backfill: Scan-Fenster 5 -> 50 Zeilen (Events am Trace-Anfang tragen kein model). - db.init_db: PRAGMA busy_timeout=5000 (kein WAL-Umbau) + _add_column-Helper, der nur 'duplicate column name' verschluckt und andere OperationalError (z.B. 'database is locked') re-raised. Tests: 8 neu (5 orchestrator TDD in test_stage_outcome_events.py, 3 in neuem test_stage_outcome_robustness.py). Suite 5705 -> 5713 passed, 0 failed. ruff format/check clean. Konflikt: #216 fasst run_extractors_per_concept Exception-Zweig an (failures) — beim Merge beide Zeilen behalten. #222/#212 konfliktfrei zu Punkt 5/6.
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.
Behebt #194. Die per-PDF-Tabelle stand strukturell auf drei Datengrundlagen je Zeile. Jede Zeile steht jetzt auf EINER Datengrundlage (Eval-Daten aus note_evals); accept bleibt bewusst aus dem Routing (pipeline_runs) und wird getrennt beschriftet.
Befund → Fix (Vorher/Nachher, echte Daten, read-only geprüft)
key in pdf+ pdf_key-Drift → Bates-Dreifachzeilebatesn=86,bates-2017n=22,bates---2017n=0)bates-2017len(log_data)= 25 (bates 3×, ohne Hertzum/Kaletski)Design-Entscheidungen
_pdf_group_key=_pdf_slug(_pdf_filter_key(...))— genau die Gruppierung, die_dedupe_pdf_optionsschon nutzt (jetzt gemeinsam). Autor-Jahr-Slug: kollabiert Volltitel („Bates - 2017 - …") und Kebab („bates-2017") derselben Quelle, hält aber Jahrgänge getrennt (Beutelspacher 2014 vs. 2022) — anders als der reine Segment-Präfix (_pdf_matches), der zwei Jahrgänge transitiv über einen Autor-Kurzkey brücken würde. Kein zweites Matching-System.note_evals.acceptance_statusist in eval_version 4.1 durchgehend NULL (485/485); genau 1 historische 1.3-Zeile trägt 'vault' — accept MUSS also aus dem Routing (pipeline_runs) kommen. Es wird auf die Eval-Version der Zeile gepinnt (prefer_ver); fehlt sie in den Runs, auf die neueste gekappte Routing-Version, dann getrennt viaaccept_ver/accept_nausgewiesen. In den echten Daten stimmt accept_ver == Eval-Version für alle Gruppen. Spaltenkopf: „Akzeptiert (Routing)" vs. „Version (Eval)"._newest_capped_versiongegenconfig.AGENT_VERSIONgekappt — verwaiste WIP-Zeilen (Nummer > Code-Version) können „neueste" nicht kapern. Fällt nur auf die neueste vorhandene zurück, wenn ALLE Versionen Orphans sind (Zeile bleibt sichtbar; Orphan-Banner meldet es separat).pageshart 0 → falsy → immer None) und aus den vorhandenen Quellen nicht ableitbar (Baseline-Logs existieren nicht, DB kennt keine Seitenzahl). Befüllen unmöglich → entfernen.is_test-Flag an der Datenquelle (pipeline_runs) — außerhalb dieses Scopes. Die zwei Routing-only-Test-PDFs erscheinen als Union-Zeilen (accept vorhanden, keine Eval-Daten) und werden aus dem ins-quality-Streifen via n>0 herausgefiltert.ins-quality-Streifen (Issue-Kommentar)
Der Streifen las den Accept-Chart-Datensatz, in dem „0 % von n Notes" nicht von „0 Notes" unterscheidbar war → meldete „Akzeptanz reicht von 0,0 % (bates---2017---information-behavior) …". Fix:
_chart_acceptanceleitet jetzt aus den kanonischen Tabellenzeilen ab (SSoT) und trägtn(distinct Eval-Notes) je Balken; der Streifen filtertn > 0(Muster wie der n_notes-Guard von ins-compare, PR #200).Vorher: „… von 0,0 % (bates---2017---information-behavior) …". Nachher (Playwright): „Akzeptanz reicht von 40,0 % (Jaiswal (2020)) bis 100,0 % (Afzal (2017)) — Spannweite 60,0 Punkte über 21 PDFs."
RED-Beweis / Tests
generative/tests/test_dashboard_pdf_table.py(neu, 9 Tests) reproduziert die vier Hauptbefunde synthetisch. RED vor dem Fix:ImportError: cannot import name '_pdf_group_key'bzw. gegen die Alt-Logik falsche Gruppierung/Zählung. GRÜN nach dem Fix. Abgedeckt: (a) Version-Mischung, (b) Bates-Substring-Doppelzählung → 1 Zeile, (c) PDF nur in quality_rows sichtbar, (d) n_notes distinct, Orphan-Version-Guard, accept-n, accept-Chart-n-Feld.Sichtverifikation (Playwright, Firefox)
C:/tmp/verify-194/— per-PDF-Tabelle + Qualitäts-Sektion, hell + dunkel. Bestätigt: Bates einmal, Hertzum/Kaletski sichtbar, accept-n unter dem Balken, keine Seiten-Spalte, ins-quality-Streifen ohne Routing-only-0 %-Eintrag.Suite / Lint
pytest generative lib/decision_engine/tests shared/tests -q→ 5701 passed, 0 failed (3 skipped, 4 deselected; Baseline 5693 — +9 neue Tests). (Ein bestehender Testtest_calc_kpis_version_anchored_not_highest_numbererwartete n_notes als Zeilenzahl; da synthetische Rows keinen note_path haben, zählt der Laufindex jede Zeile einzeln — Alt-Verhalten erhalten, echte Re-Evals mit note_path werden dedupliziert.)ruff format --check .+ruff check .grün.Merge-Reihenfolge + Konfliktflächen (empirisch via
git merge-tree)Empfohlene Reihenfolge: #217 → #219 → #221 → dieser PR (dieser zuletzt).
<thead>der cmp-Tabelle<thead>+renderTablerenderTable: #221 entfernt die Gutter-<td>, ich baue die Zellen um (Seiten-td raus, accept-n rein) — automerge-fähig bis auf den thead.Kein Python-Zeilenkonflikt: #221s einzige
eval_dashboard.py-Änderung ist in_chart_longitudinal, meine in_calc_pdf_table/_calc_kpis/_chart_acceptance.Cross-Model-Review (Mistral
magistral-medium-latest)Schwerpunkt Aggregationslogik / Versions-Max-Fallen (#191). Fazit: keine echten Bugs gefunden; explizit bestätigt:
_newest_capped_versionverhindert Orphan-Kaperung,_pdf_group_keytrennt Jahrgänge korrekt,_log_gk-Set verhindert Union-Doppelzählung. Selbst nachgeprüft:prefer_verkann nie ein Orphan sein (stammt aus der bereits gekappten Eval-Version); longest-match-Zuordnung deterministisch. Kein Fake-Fund zu verwerfen.Nachbesserung (Commit 74ef83f) — 7 Funde aus Cross-Model-Review
Konsolidierte, am Code bestätigte Funde (Fable + Mistral-Resample + Qwen). TDD für 1–7 (RED→GRÜN,
+8Tests intest_dashboard_pdf_table.py).python eval_dashboard.py→main→_build_html→_render_pdf_table) crasht mitKeyError 'pages'—_calc_pdf_tableliefert das Feld nicht mehrcolspan9→8), None-Version abgefangen. Deprecated, crasht aber nicht mehr._pdf_matchesBEIDE Jahrgangs-Gruppen (2014 und 2022);max(matched, key=len)-Tie ordnete order-abhängig/willkürlich zurenderTable:const latest = rows[rows.length-1].version— „cur"-Highlight aus der letzten Tabellenzeile; die neuen Routing-only-Zeilen am Ende kapern „aktuell" (#191-Bugklasse)warnings.current_version) statt aus der Zeilenreihenfolge.renderTable(rows, thresh, currentVer).kpi_trend.nweiter Eval-Instanzen (52)_distinct_notes(rows), genutzt von Kachel (_calc_kpis) und Sparkline (Serverquality_by_version[v]["n"]).config.AGENT_VERSION) zeigen dieversions[-1]-Fallback-Version unmarkiertorphan-Flag an der Zeile; Client rendert ⚠-Badge + Tooltip „Version neuer als config.AGENT_VERSION — vermutlich Branch-/WIP-Lauf", konsistent zum Waisen-Banner (#209)._pdf_group_key('')erzeugt Geisterzeile;_pdf_matches('')matcht ALLES und zieht jeden Routing-Run an sich"(unbenannt)"; leere Keys werden nie als Match-Filter benutzt (in_calc_pdf_tableund_calc_kpisn_pdfs).routing_only-Flag; Client zeigt „nur Routing"-Marker + präzisen Tooltip.max(matched, key=len)bei gleich langen Keys insertion-order-abhängigMerge-Hinweis für #219-Tooltips (Punkt 8)
Wichtig: Die per-PDF-Tabellen-
.hint-Tooltips aus PR #219 sind auf diesem Branch NICHT enthalten (#219 ist nicht gemergt) — sie können hier nicht korrigiert werden. Beim Merge von #222 nach #219 sind die Tooltip-Texte an DIESE Semantik anzupassen (Herleitung aus_calc_pdf_table/_accept_from_runs):hall)_pooled_hall_pct(at)über die Notes GENAU der neuesten Eval-Version des PDFs (gegenconfig.AGENT_VERSIONgekappt, #191); gepoolt Σ halluzinierte Anker / Σ Anker; Quellenote_evalsnote_evals— nicht dem Routing."cov)_median(coverage_factual)über dieselben Notes der neuesten Eval-Version; Quellenote_evalsnote_evals."accept, Kachel-Balken + Chart)_accept_from_runs: gepoolte Rate Σn_vault/ Σn_totalüber die Routing-Läufe der Version; Quellepipeline_runs(Routing) — andere Quelle als hall/covpipeline_runs(Routing) — andere Datenquelle als Fehlerquote/Belegrate;n= Zahl der Läufe, ggf. abweichende Routing-Version separat."Suite / Lint (nach Nachbesserung)
pytest generative lib/decision_engine/tests shared/tests -q→ 5709 passed, 0 failed (3 skipped, 4 deselected).ruff format --check .+ruff check .grün.