From 4d1b4499c0f557d8b05701abdd230c25fe25bb09 Mon Sep 17 00:00:00 2001 From: Nick Donohue Date: Fri, 23 Jan 2026 19:08:34 -0500 Subject: [PATCH] feat(evals): improve dashboard UX with integrated iterations and tooltips - Integrate iterations page as hash-based view in main dashboard nav - Add interactive zone conformance tooltips to pentagon radar chart - Improve timeline chart: date-based x-axis, clickable git SHA tooltips - Remove sensitivity tab from navigation - Add metrics-history to evals-deploy target for CI builds Co-Authored-By: Claude Opus 4.5 --- Makefile | 2 +- tools/evals/public/app.js | 296 +++++++++++++++++++++++++++++- tools/evals/public/index.html | 67 ++++++- tools/evals/public/js/pentagon.js | 132 ++++++++++++- tools/evals/public/js/timeline.js | 149 ++++++++++++--- tools/evals/public/styles.css | 1 + 6 files changed, 608 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index 26c08c7..69320d7 100644 --- a/Makefile +++ b/Makefile @@ -523,7 +523,7 @@ $(EVALS_DIR)/public/data/metadata.json: $(PATTERN_VIZ) @$(MAKE) evals-generate # Deploy to GitHub Pages (builds to docs/evals/) -evals-deploy: evals-generate +evals-deploy: evals-generate metrics-history @echo "Deploying evaluation dashboard to docs/evals/..." @mkdir -p docs/evals @cp -r $(EVALS_DIR)/public/* docs/evals/ diff --git a/tools/evals/public/app.js b/tools/evals/public/app.js index 450ad6f..458a6b3 100644 --- a/tools/evals/public/app.js +++ b/tools/evals/public/app.js @@ -23,8 +23,12 @@ let data = { sensitivity: null, fills: null, metricsHistory: null, + iterations: null, }; +// Iterations state +let filteredIterations = []; + let currentView = 'overview'; let player = null; let playerUI = null; @@ -97,6 +101,17 @@ async function loadData() { console.log('Metrics history not available (run "make metrics-history" to generate)'); } + // Try to load iterations data (optional - may not exist yet) + try { + const iterationsRes = await fetch('data/iterations.json'); + if (iterationsRes.ok) { + data.iterations = await iterationsRes.json(); + filteredIterations = data.iterations.iterations || []; + } + } catch (e) { + console.log('Iterations data not available'); + } + return true; } catch (err) { console.error('Error loading data:', err); @@ -823,6 +838,283 @@ function renderFillsView() { gridContainer.innerHTML = fillConfigHtml + renderFillPatternGrid(fillPatterns, selectedEntry.energy); } +// ============================================================================ +// Iterations View +// ============================================================================ + +function renderIterationsView() { + // Render summary stats + const summary = data.iterations?.summary || { total: 0, successful: 0, failed: 0, successRate: 0 }; + $('#stat-total').textContent = summary.total; + $('#stat-success').textContent = summary.successful; + $('#stat-failed').textContent = summary.failed; + $('#stat-rate').textContent = `${(summary.successRate * 100).toFixed(1)}%`; + + // Render timeline + renderIterationsTimeline(); + + // Render iteration cards + renderIterationCards(); + + // Setup event listeners + setupIterationsEventListeners(); +} + +function renderIterationsTimeline() { + const container = $('#iterations-timeline-chart'); + + if (!data.metricsHistory || !data.metricsHistory.timeline || data.metricsHistory.timeline.length < 2) { + container.innerHTML = ` +
+

Not enough historical data to display timeline

+

Run evaluations and tag baselines to track progress over time

+
+ `; + return; + } + + container.innerHTML = renderTimelineChart(data.metricsHistory, 900, 280); +} + +function renderIterationCards() { + const container = $('#iterations-list'); + + if (!filteredIterations || filteredIterations.length === 0) { + container.innerHTML = ` +
+

No iterations found.

+

Run /iterate "goal" to start hill-climbing.

+
+ `; + return; + } + + const cards = filteredIterations.map(createIterationCard).join(''); + container.innerHTML = cards; + + // Add event listeners to cards + $$('.card-header').forEach(header => { + header.addEventListener('click', toggleIterationCardDetails); + }); + + $$('.toggle-details').forEach(btn => { + btn.addEventListener('click', (e) => { + e.stopPropagation(); + toggleIterationCardDetails.call(btn.closest('.card-header'), e); + }); + }); +} + +function createIterationCard(iteration) { + const statusClass = iteration.status || 'unknown'; + const statusLabel = iteration.status || 'Unknown'; + + // Lever info + const leverInfo = iteration.lever ? ` +
+ ${iteration.lever.primary}: ${iteration.lever.oldValue !== null ? iteration.lever.oldValue.toFixed(2) : '?'} + → ${iteration.lever.newValue !== null ? iteration.lever.newValue.toFixed(2) : '?'} + ${iteration.lever.delta ? `(${iteration.lever.delta})` : ''} +
+ ` : ''; + + // Metrics info + const metricsInfo = iteration.metrics ? ` +
+ ${iteration.metrics.target ? ` +
+
Target Metric
+
+ ${iteration.metrics.target.delta} +
+
+ ` : ''} + ${iteration.metrics.maxRegression ? ` +
+
Max Regression
+
+ ${iteration.metrics.maxRegression} +
+
+ ` : ''} +
+ ` : ''; + + // Links + const links = []; + if (iteration.pr && iteration.pr !== 'null') { + links.push(`View PR ${iteration.pr}`); + } + if (iteration.commit && iteration.commit !== 'null') { + links.push(`${iteration.commit.substring(0, 7)}`); + } + if (iteration.branch && iteration.branch !== 'null') { + links.push(`${iteration.branch}`); + } + + return ` +
+
+
+
#${iteration.id}
+
${statusLabel}
+
+

${escapeHTML(iteration.goal)}

+ ${leverInfo} + ${metricsInfo} + ${links.length > 0 ? `` : ''} +
+ +
+ ${createIterationCardDetails(iteration)} +
+ + +
+ `; +} + +function createIterationCardDetails(iteration) { + const sections = []; + + if (iteration.sections?.goal) { + sections.push(` +
+

Goal

+
${escapeHTML(iteration.sections.goal)}
+
+ `); + } + + if (iteration.sections?.decision) { + sections.push(` +
+

Decision

+
${escapeHTML(iteration.sections.decision)}
+
+ `); + } + + if (iteration.sections?.notes) { + sections.push(` +
+

Notes

+
${escapeHTML(iteration.sections.notes)}
+
+ `); + } + + if (sections.length === 0) { + return '

No additional details available

'; + } + + return sections.join(''); +} + +function toggleIterationCardDetails(e) { + const card = e.currentTarget.closest('.iteration-card'); + const details = card.querySelector('.card-details'); + const toggleBtn = card.querySelector('.toggle-details'); + const toggleText = toggleBtn.querySelector('.toggle-text'); + const toggleIcon = toggleBtn.querySelector('.toggle-icon'); + + if (details.classList.contains('expanded')) { + details.classList.remove('expanded'); + toggleText.textContent = 'Show Details'; + toggleIcon.classList.remove('expanded'); + } else { + details.classList.add('expanded'); + toggleText.textContent = 'Hide Details'; + toggleIcon.classList.add('expanded'); + } +} + +function setupIterationsEventListeners() { + const searchInput = $('#search-iterations'); + const filterStatus = $('#filter-status'); + const sortSelect = $('#sort-iterations'); + + // Remove existing listeners by replacing elements (simple approach) + if (searchInput && !searchInput.dataset.listenerAttached) { + searchInput.addEventListener('input', applyIterationsFilters); + searchInput.dataset.listenerAttached = 'true'; + } + + if (filterStatus && !filterStatus.dataset.listenerAttached) { + filterStatus.addEventListener('change', applyIterationsFilters); + filterStatus.dataset.listenerAttached = 'true'; + } + + if (sortSelect && !sortSelect.dataset.listenerAttached) { + sortSelect.addEventListener('change', applyIterationsSort); + sortSelect.dataset.listenerAttached = 'true'; + } +} + +function applyIterationsFilters() { + const searchTerm = ($('#search-iterations')?.value || '').toLowerCase(); + const statusFilter = $('#filter-status')?.value || 'all'; + + filteredIterations = (data.iterations?.iterations || []).filter(iteration => { + // Status filter + if (statusFilter !== 'all' && iteration.status !== statusFilter) { + return false; + } + + // Search filter + if (searchTerm) { + const searchableText = [ + iteration.id, + iteration.goal, + iteration.lever?.primary, + iteration.sections?.goal, + iteration.sections?.decision, + iteration.sections?.notes + ].filter(Boolean).join(' ').toLowerCase(); + + if (!searchableText.includes(searchTerm)) { + return false; + } + } + + return true; + }); + + applyIterationsSort(); +} + +function applyIterationsSort() { + const sortBy = $('#sort-iterations')?.value || 'newest'; + + switch (sortBy) { + case 'oldest': + filteredIterations.sort((a, b) => a.id.localeCompare(b.id)); + break; + case 'impact': + filteredIterations.sort((a, b) => { + const deltaA = parseFloat(a.metrics?.target?.delta?.replace('%', '') || '0'); + const deltaB = parseFloat(b.metrics?.target?.delta?.replace('%', '') || '0'); + return deltaB - deltaA; + }); + break; + case 'newest': + default: + filteredIterations.sort((a, b) => b.id.localeCompare(a.id)); + break; + } + + renderIterationCards(); +} + +function escapeHTML(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; +} + // ============================================================================ // Navigation // ============================================================================ @@ -841,7 +1133,7 @@ function showView(viewName) { case 'sweeps': renderSweepsView(); break; case 'seeds': renderSeedsView(); break; case 'fills': renderFillsView(); break; - case 'sensitivity': renderSensitivityView(); break; + case 'iterations': renderIterationsView(); break; } } @@ -865,7 +1157,7 @@ function showView(viewName) { function getViewFromHash() { const hash = window.location.hash.slice(1); // Remove the '#' - const validViews = ['overview', 'presets', 'sweeps', 'seeds', 'fills', 'sensitivity']; + const validViews = ['overview', 'presets', 'sweeps', 'seeds', 'fills', 'iterations']; return validViews.includes(hash) ? hash : 'overview'; } diff --git a/tools/evals/public/index.html b/tools/evals/public/index.html index 951f41a..6a2b048 100644 --- a/tools/evals/public/index.html +++ b/tools/evals/public/index.html @@ -24,8 +24,7 @@

DuoPulse v5 Pattern Evaluation Dashboard

- - Iterations + @@ -99,11 +98,65 @@

Fill System Evaluation

- -