fix(dashboard): heatmap cell click + 5th tab + model sync fixes\n\n- restore hmModal with 4 legacy tabs + new Performance Graph tab\n- fix event.target in research-dashboard.template.html switchTab\n- fix showCellDetail event.stopPropagation for modal persistence\n- update agent models + sync KILO_SPEC.md and AGENTS.md

This commit is contained in:
Deploy Bot
2026-05-27 13:46:55 +01:00
parent 36455ccf24
commit 7635cb62cd
6 changed files with 768 additions and 1363 deletions

View File

@@ -983,6 +983,7 @@
<button class="hm-tab-btn" onclick="switchHmTab('gitea')">Gitea History</button>
<button class="hm-tab-btn" onclick="switchHmTab('skills')">Skills</button>
<button class="hm-tab-btn" onclick="switchHmTab('models')">Model Timeline</button>
<button class="hm-tab-btn" onclick="switchHmTab('graph')">Performance Graph</button>
</div>
<div class="modal-body" id="hmModalBody">
<!-- Content injected by JS -->
@@ -1016,7 +1017,7 @@ Chart.defaults.borderColor = '#1e2d45';
Chart.defaults.font.family = "'Inter', sans-serif";
// Inline recommendation data fallback (from model-research-latest.json)
const INLINE_RECOMMENDATIONS = []; // REMOVED — data now comes from agentData, not hardcoded
const INLINE_RECOMMENDATIONS = []; // Deprecated — data now comes from agentData.agents[].current.recommendations
// Inline benchmark data (fallback when embedded data doesn't have model_benchmarks)
// SOURCE: agent-evolution/data/model-benchmarks-verified.json v2.0.0
@@ -5736,33 +5737,15 @@ function renderOverview() {
`).join('')
: '<p style="color: var(--text-muted);">No history yet</p>';
// Recommended agents (use inline recs if available)
let recAgents = [];
if (INLINE_RECOMMENDATIONS && INLINE_RECOMMENDATIONS.length > 0) {
recAgents = INLINE_RECOMMENDATIONS.slice(0, 6).map(r => ({ agent: r.agent, current: { recommendations: [{ priority: r.impact, target: r.source_of_truth_model || r.recommended_model, reason: r.rationale, score_before: r.score_before, score_after: r.score_after, score_delta: r.score_delta }], model: r.current_model_in_agent_versions || r.current_model, category: 'Core Dev', description: '', benchmark: { fit_score: r.score_after || 0 } } }));
} else {
recAgents = Object.entries(agentData.agents)
.filter(([_, a]) => a.current.recommendations && a.current.recommendations.length > 0)
.slice(0, 6);
}
// Recommended agents from live data
const recAgents = Object.entries(agentData.agents || {})
.filter(([_, a]) => (a.current?.recommendations || []).length > 0)
.slice(0, 6);
document.getElementById('recCount').textContent = recAgents.length;
if (INLINE_RECOMMENDATIONS && INLINE_RECOMMENDATIONS.length > 0) {
document.getElementById('recAgents').innerHTML = recAgents.map((r, idx) => renderRecCard({
agent: r.agent,
current_model: r.current?.model || '',
recommended_model: r.current?.recommendations?.[0]?.target || '',
impact: r.current?.recommendations?.[0]?.priority?.toLowerCase() || 'medium',
score_before: r.current?.recommendations?.[0]?.score_before || 0,
score_after: r.current?.recommendations?.[0]?.score_after || 0,
score_delta: r.current?.recommendations?.[0]?.score_delta || 0,
rationale: r.current?.recommendations?.[0]?.reason || ''
}, idx)).join('');
} else {
document.getElementById('recAgents').innerHTML = recAgents.map(([name, agent]) =>
renderAgentCard(name, agent, true)
).join('');
}
document.getElementById('recAgents').innerHTML = recAgents.map(([name, agent]) =>
renderAgentCard(name, agent, true)
).join('');
}
// Render All Agents
@@ -6068,7 +6051,7 @@ function renderHeatmap() {
h += `<td style="background:${hmColor(s)};color:${hmText(s)};cursor:pointer" class="${cur ? 'hm-cur' : ''}" title="${ag.n} × ${hmModels[j].n}: ${s}"
onmouseover="showTT(event,'${ag.n}','${hmModels[j].n} (${hmModels[j].p})',${s},${best},${cur},${hmModels[j].if})"
onmouseout="hideTT()"
onclick="showCellDetail('${hmModels[j].full}', '${ag.n}')">${s}${marks}</td>`;
onclick="openHmModal(event, '${ag.n}', '${hmModels[j].n}', ${s}, ${hmModels[j].if})">${s}${marks}</td>`;
});
h += '</tr>';
});
@@ -6131,7 +6114,8 @@ function closeHmModal() {
}
// Show cell detail modal with Chart.js line chart and prompt history
function showCellDetail(modelName, agentName) {
function showCellDetail(event, modelName, agentName) {
event.stopPropagation();
const agent = agentData.agents[agentName];
if (!agent) {
console.error('Agent not found:', agentName);
@@ -6345,9 +6329,13 @@ function renderHmModalContent(tabName) {
case 'models':
content = renderModelsTab(agent);
break;
case 'graph':
content = renderGraphTab(agent);
break;
}
body.innerHTML = `<div class="hm-tab-content active" style="display:block">${content}</div>`;
if (tabName === 'graph') setTimeout(() => renderCellChart(hmCurrentAgent, agent.current?.model || ''), 50);
}
function renderPromptTab(agent) {
@@ -6486,6 +6474,46 @@ function renderModelsTab(agent) {
return html;
}
function renderGraphTab(agent) {
return `
<div style="margin-bottom:20px">
<h3 style="margin-bottom:10px;color:var(--accent-cyan)">Performance Over Time</h3>
<div style="position:relative;height:300px">
<canvas id="cellChartCanvas"></canvas>
</div>
</div>
<div>
<h3 style="margin-bottom:10px;color:var(--accent-cyan)">Prompt Change History</h3>
<div id="promptHistoryList" style="max-height:300px;overflow-y:auto">
${renderPromptHistory(agent)}
</div>
</div>
`;
}
function renderPromptHistory(agent) {
const promptChanges = (agent.history || []).filter(item => item.change_type === 'prompt_change');
if (promptChanges.length === 0) {
return '<p style="color:var(--text-muted);text-align:center;padding:20px">No prompt change history found</p>';
}
let html = '<ul style="list-style:none;padding:0">';
promptChanges.forEach(change => {
html += `
<li style="padding:10px;border-bottom:1px solid var(--border);margin-bottom:10px">
<div style="display:flex;justify-content:space-between;margin-bottom:5px">
<span style="font-family:'JetBrains Mono',monospace;font-size:.8em;color:var(--text-muted)">${formatDate(change.date)}</span>
<span style="font-family:'JetBrains Mono',monospace;font-size:.8em;color:var(--accent-cyan)">${change.commit ? change.commit.substring(0,7) : 'unknown'}</span>
</div>
<div style="font-size:.9em;color:var(--text-secondary)">${change.reason || 'No reason provided'}</div>
</li>
`;
});
html += '</ul>';
return html;
}
// Compute composite score for any model name
// Formula (v2): IF_score * 0.85 + context_window_bonus (SWE-bench removed — all values unverifiable)
function computeAgentScore(modelName) {
@@ -6919,7 +6947,7 @@ function simulateApply() {
progressStatus.textContent = 'Complete!';
progressResult.classList.add('show');
const recs = INLINE_RECOMMENDATIONS && INLINE_RECOMMENDATIONS.length > 0 ? INLINE_RECOMMENDATIONS : [];
const recs = Object.values(agentData.agents || {}).filter(a => (a.current?.recommendations || []).length > 0);
progressResultText.textContent = `${recs.length} recommendations applied. Run 'bun run sync:evolution' to update dashboard.`;
}
}
@@ -6970,11 +6998,11 @@ function showResearchModal() {
step.classList.add('done');
});
const recs = INLINE_RECOMMENDATIONS && INLINE_RECOMMENDATIONS.length > 0 ? INLINE_RECOMMENDATIONS : [];
const modelsCount = new Set(recs.map(r => r.current_model).concat(recs.map(r => r.source_of_truth_model || r.recommended_model))).size;
const recsCount = recs.filter(r => r.score_delta > 0).length;
document.getElementById('researchSummaryText').textContent =
const recs = Object.values(agentData.agents || {}).filter(a => (a.current?.recommendations || []).length > 0);
const modelsCount = new Set(recs.flatMap(a => [a.current?.model, a.current?.recommendations?.[0]?.target])).size;
const recsCount = recs.filter(a => (a.current?.recommendations?.[0]?.score_delta || 0) > 0).length;
document.getElementById('researchSummaryText').textContent =
`${modelsCount} models evaluated. ${recsCount} recommendations found. ${recs.length - recsCount} idle models detected.`;
researchSummary.classList.add('show');
}