From 19f154aa0591ca60616421e62feb99a05d881cdf Mon Sep 17 00:00:00 2001 From: TillQuandel Date: Wed, 15 Jul 2026 01:00:00 +0200 Subject: [PATCH 1/2] =?UTF-8?q?fix(dashboard):=20Spark-Trend-Row=20=C3=B6f?= =?UTF-8?q?fnet=20unter=20der=20Gruppe=20der=20angeklickten=20Kachel=20(#2?= =?UTF-8?q?74-Nachbesserung)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Till-Live-Fund: Nach dem KPI-Gruppen-Umbau (#274) sitzt die einzige #spark-row fix im HTML nach #kpis-perf. Klick auf eine Kachel der oberen Gruppe "Qualität" oeffnete die Versions-Trend-Row deshalb unter der FALSCHEN (unteren) Gruppe statt direkt darunter. _openKpiSpark haengt die Row jetzt per insertAdjacentElement('afterend', ...) dynamisch unter die Gruppe der angeklickten Kachel (idx<4 = Qualitaet, dieselbe Slice-Grenze wie kpiDefs.slice(0,4)/.slice(4) in renderKpis). Reposition laeuft in beiden Faellen (Frisch-Oeffnen UND Umschalten zwischen den Gruppen bei bereits offener Row) — renderKpis selbst fasst die Row nicht an (nur die innerHTML der beiden Kacheln-Container), sie springt also zwischen periodischen Re-Renders nicht zurueck. --- generative/tests/test_dashboard_kpi_groups.py | 16 ++++++++++++++++ internal/dashboard/eval_dashboard.html | 14 ++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/generative/tests/test_dashboard_kpi_groups.py b/generative/tests/test_dashboard_kpi_groups.py index 54b4be9..793b1a6 100644 --- a/generative/tests/test_dashboard_kpi_groups.py +++ b/generative/tests/test_dashboard_kpi_groups.py @@ -69,3 +69,19 @@ def test_kpi_defs_order_matches_group_split_quality_first_four_perf_last_five(): # Slice-Grenze bei Index 4 im tatsaechlichen Render-Code verankert. assert ".slice(0, 4)" in html assert ".slice(4)" in html + + +def test_spark_row_relocates_to_clicked_kpi_group_on_open(): + """#274-Nachbesserung (Till-Live-Fund): die EINE #spark-row saß nach dem + Gruppen-Umbau (#274) fix im HTML nach #kpis-perf — ein Klick auf eine + Qualitäts-Kachel (idx<4) öffnete die Trend-Row also unter der FALSCHEN + (unteren) Gruppe statt direkt unter der eigenen. Fix: _openKpiSpark hängt + die Row per insertAdjacentElement('afterend', ...) dynamisch unter die + Gruppe der angeklickten Kachel — dieselbe idx<4-Grenze wie der + kpiDefs-Slice-Split oben. + """ + html = _build_live_html() + open_fn = html.split("window._openKpiSpark = function(idx) {")[1].split("window.closeKpiSpark = function()")[0] + assert "insertAdjacentElement('afterend', row)" in open_fn + assert "kpis-quality" in open_fn and "kpis-perf" in open_fn + assert "idx < 4" in open_fn diff --git a/internal/dashboard/eval_dashboard.html b/internal/dashboard/eval_dashboard.html index 43cbdab..60d1e44 100644 --- a/internal/dashboard/eval_dashboard.html +++ b/internal/dashboard/eval_dashboard.html @@ -1639,6 +1639,20 @@ row.style.setProperty('--spark-border-color', _toneColor(tone)); } + // #274-Nachbesserung (Till-Live-Fund): es gibt nur EINE #spark-row im DOM + // (kein Duplikat je Gruppe) — sie muss beim Oeffnen/Umschalten direkt unter + // die Gruppe der angeklickten Kachel wandern, sonst haengt sie fix unter der + // unteren Gruppe (kpis-perf), auch wenn eine Qualitaets-Kachel angeklickt + // wurde. idx<4 ist dieselbe Slice-Grenze wie kpiDefs.slice(0,4)/.slice(4) + // in renderKpis. Muss VOR dem Oeffnen (open-Klasse) UND beim Umschalten + // zwischen den beiden Gruppen laufen (Re-Render in renderKpis fasst die + // Row selbst nicht an — nur die innerHTML der beiden Kacheln-Container — + // sie springt also zwischen Renders nicht zurueck). + if (row) { + const host = document.getElementById(idx < 4 ? 'kpis-quality' : 'kpis-perf'); + if (host) host.insertAdjacentElement('afterend', row); + } + if (wasOpen && inner) { inner.classList.add('switching'); setTimeout(() => { From d74eb655be2a6249a8164cb84519ee3e59bfd40c Mon Sep 17 00:00:00 2001 From: TillQuandel Date: Wed, 15 Jul 2026 01:02:02 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fix(dashboard):=20Accept-Kachel=20wieder=20?= =?UTF-8?q?klickbar=20=E2=80=94=20#221=20hatte=20sie=20versehentlich=20sti?= =?UTF-8?q?llgelegt=20(Till-Live-Fund)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #221 (#204 P8g) hatte der Kachel "Automatisch akzeptiert" sparkless:true gegeben mit der Begruendung, ch4 "Akzeptanzrate über Versionen" zeige den Trend bereits — die Klick-Sparkline sei ein reines Duplikat. Till stuft das als Regression ein: ch4 zeigt die Rate JE PDF (mehrere Linien pro Version, Median), die Kachel-Sparkline dagegen die GEPOOLTE Rate ueber alle PDFs — dieselbe Semantik wie die Klick-Sparklines von Fehlerquote/Belegrate daneben. Kein Duplikat, also wieder klickbar. sparkless:true entfernt + veralteten Kommentar korrigiert. Trend-Datenkette geprueft: kpiTrend.accept (eval_dashboard_server.py, _pooled_accept aus all_log_runs, gepoolt n_vault/n_total je Version) war bereits vollstaendig verdrahtet und unveraendert seit vor #221 — #221 hat nur den Klick-Handler im Frontend entfernt, die Datenkette nie angefasst. n-Guard (n<20) und Null-Handling in _trendChart greifen fuer 'accept' bereits generisch wie bei hall/cov/dur/tokens, keine Sonderbehandlung noetig. --- generative/tests/test_dashboard_kpi_groups.py | 15 +++++++++++++++ internal/dashboard/eval_dashboard.html | 16 +++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/generative/tests/test_dashboard_kpi_groups.py b/generative/tests/test_dashboard_kpi_groups.py index 793b1a6..10c9d72 100644 --- a/generative/tests/test_dashboard_kpi_groups.py +++ b/generative/tests/test_dashboard_kpi_groups.py @@ -85,3 +85,18 @@ def test_spark_row_relocates_to_clicked_kpi_group_on_open(): assert "insertAdjacentElement('afterend', row)" in open_fn assert "kpis-quality" in open_fn and "kpis-perf" in open_fn assert "idx < 4" in open_fn + + +def test_kpi_accept_tile_is_clickable_again(): + """#221 (#204 P8g) hatte der Accept-Kachel `sparkless:true` gegeben (kein + onclick mehr) mit der Begründung, ch4 "Akzeptanzrate über Versionen" + zeige denselben Trend bereits. Till-Live-Fund/Regression: ch4 zeigt die + Rate JE PDF (mehrere Linien je Version), die Kachel-Sparkline dagegen die + GEPOOLTE Rate über alle PDFs — analog zu Fehlerquote/Belegrate daneben, + kein reines Duplikat. sparkless entfernt, Kachel wieder klickbar. + Isoliert auf den kpi-accept-Eintrag: kpi-wall/kpi-tokens-billable bleiben + sparkless (dafür fehlt serverseitig weiterhin eine Zeitreihe je Version). + """ + html = _build_live_html() + accept_def = html.split("id:'kpi-accept'")[1].split("id:'kpi-hall'")[0] + assert "sparkless" not in accept_def diff --git a/internal/dashboard/eval_dashboard.html b/internal/dashboard/eval_dashboard.html index 60d1e44..0164c75 100644 --- a/internal/dashboard/eval_dashboard.html +++ b/internal/dashboard/eval_dashboard.html @@ -1296,16 +1296,22 @@ // #187: hint = Begriffserklärung für Nicht-Techniker, je Kachel via // Code-Herleitung (siehe PR-Beschreibung für Begriff→Text→Quelle-Tabelle). const kpiDefs = [ - // #204 P8g: kein onclick-Sparkline mehr — "Automatisch akzeptiert" hat - // bereits einen eigenen Versions-Trend-Chart (ch4 "Akzeptanzrate je - // Version" in der Trade-off-Sektion); die Klick-Sparkline duplizierte - // dieselbe Aussage in einer zweiten, kleineren Darstellung. + // #221-Nachbesserung (#204 P8g, Till-Live-Fund): hier stand vormals + // sparkless:true mit der Begruendung, "Automatisch akzeptiert" habe + // bereits einen eigenen Versions-Trend-Chart (ch4 "Akzeptanzrate über + // Versionen" in der Trade-off-Sektion) — die Klick-Sparkline sei ein + // reines Duplikat. Till stuft das als Regression ein: ch4 zeigt die Rate + // JE PDF (mehrere Linien pro Version, Median), die Kachel-Sparkline dagegen + // die GEPOOLTE Rate ueber alle PDFs — dieselbe Semantik wie die + // Klick-Sparklines von Fehlerquote/Belegrate daneben. Kein Duplikat, also + // wieder klickbar (kpiTrend.accept liefert bereits echte Werte, siehe + // eval_dashboard_server.py _pooled_accept — unveraendert seit vor #221). { id:'kpi-accept', key:'accept', label:'Automatisch akzeptiert', // #237: bei 0 evaluierten Notes (Merge-only-/Eval-Stage-ausgefallener Lauf) // ist die Akzeptanzrate zwar rechnerisch gültig (Basis: generierte/ // Vault-Notes, nicht Evals), aber ohne Eval-Abdeckung nicht als // Qualitätsampel aussagekräftig — Kachel neutralisiert statt grün. - val: kpis.n_notes===0 ? null : kpis.avg_accept, u:'%', digits:1, sparkless:true, + val: kpis.n_notes===0 ? null : kpis.avg_accept, u:'%', digits:1, hint:'Anteil der von der Pipeline erzeugten Notes, die ohne manuelles Zusammenführen direkt in den Vault übernommen wurden. Gepoolt über alle Läufe der aktuellen Pipeline-Version (Summe akzeptiert / Summe generiert), nicht der Schnitt einzelner PDFs.', tone: kpis.n_notes===0 ? 'neutral' : (kpis.avg_accept!=null?(kpis.avg_accept>=T.accept[0]?'good':kpis.avg_accept<=T.accept[1]?'bad':'warn'):'neutral'), tgt: kpis.n_notes===0 ? '–' : (kpis.avg_accept!=null?(kpis.avg_accept>=T.accept[0]?'Ziel erreicht':`Ziel ≥ ${T.accept[0]} %`):'–'),