chore(archive): move untracked files + clean working tree\n\nArchived to agent-evolution/archive/:\n - test scripts, specs, data exports\n - dashboard-user-journey.md → .kilo/archive/\n\nClean: all non-ollama models verified (openrouter, openai removed)

This commit is contained in:
Deploy Bot
2026-05-27 14:04:37 +01:00
parent 530c1c1384
commit 954c739dc9
7 changed files with 5000 additions and 0 deletions

View File

@@ -0,0 +1,179 @@
# APAW Dashboard — User Journey Specification
## Overview (Вкладка 1)
### Пользователь входит
- Видит: статистика (34 агентов, 15 моделей, 347 истории, 18 рекомендаций)
- Видит: timeline с последними изменениями
- Видит: recommended agents (карточки)
### Hover
- Карточка агента: подсветка, тень
### Click
- Карточка агента: раскрывает подробности (модель, score, категория)
### Edge cases
- Пустой агентData → "No agents available"
- Нет рекомендаций → скрыть блок с recommended agents
### Console expectations
- 0 ошибок
- `renderOverview()` вызван без ошибок
---
## All Agents (Вкладка 2)
### Пользователь входит
- Видит: search input, фильтр-кнопки (All, Core Dev, QA, Security, Analysis, Process, Cognitive)
- Видит: агенты сгруппированы по категориям
- **Все** фильтр-кнопки — активная "All" подсвечена
### Hover
- Фильтр-кнопка: подсветка
- Карточка агента: тень
### Click — фильтр
- Клик "Core Dev":
1. Кнопка "Core Dev" получает класс `active` (CSS: highlighted)
2. Все секции категорий скрываются
3. Показываются **только** агенты с категорией "Core Dev"
4. **Никакой JS ошибки**
- Клик "All": все секции категорий видны, "All" активна
### Click — поиск
- Ввод "dev": фильтрация по имени + категории
- Пустой результат: "No agents found"
### Edge cases
- **Дважды кликнуть** на одну категорию → класс должен остаться active
- **Клик между категориями** → предыдущая теряет active, новая получает
- **Быстрый набор** в поиск → debounce 300ms
### Console expectations
- `filterAgents()`: 0 ошибок
- `filterCategory()`: не использует `event.target` (передаёт `this`)
- Нет ReferenceError, TypeError
**🔴 BUG:** `event.target` в `switchHmTab()` — event может быть undefined
---
## Timeline (Вкладка 3)
### Пользователь входит
- Видит: timeline с датами, моделями, commit-hash, reason
### Hover
- Элемент timeline: подсветка
### Click
- Элемент timeline: показать детали изменения (модал)
---
## Recommendations (Вкладка 4)
### Пользователь входит
- Видит: карточки рекомендаций (агент → модель, impact, дельта score)
- Видит: кнопки **"Apply Recommended Fixes"** и **"New Research Cycle"**
### Click — "Apply Recommended Fixes"
- Открывается **модал** с чеклистом:
- Каждая рекомендация: чекбокс + from → to модель + impact (low/medium/high)
- Кнопка "Apply Selected"
- Данные читаются из `agentData.agents[*].current.recommendations`
### Click — "New Research Cycle"
- Открывается **модал** с выбором:
- Source (OpenRouter, Ollama, etc.)
- Focus (IF, SWE, etc.)
- Кнопка "Start Research"
### Edge cases
- Нет рекомендаций → пустой modal с сообщением "No recommendations"
- **Функция `showResearchModal()` должна существовать**
### Console expectations
- `showApplyModal()`: читает `agentData.agents` (не `INLINE_RECOMMENDATIONS`)
- `exportRecommendations()`: генерирует JSON с реальными данными
- **🔴 BUG:** `showResearchModal()` — функция ВЫЗЫВАЕТСЯ, но НЕ СУЩЕСТВУЕТ (ReferenceError)
---
## Heatmap (Вкладка 5)
### Пользователь входит
- Видит: таблица Agent × Model
- Красные/зелёные ячейки — weighted score
- ★ = best fit, outlined = current model
- ⚠ = low IF score
### Hover
- Ячейка: tooltip с именем агента, моделью, score
### Click — ячейка
- Открывается **модал** (cellDetailModal):
- Заголовок: "Agent × Model"
- Chart.js линейный график: динамика computeAgentScore по истории
- История промтов: prompt_change из agent.history
- Кнопка закрытия ✕
### Edge cases
- Агент без истории → пустой график + "No history found"
- Быстрый double-click → не открывать два модала
### Console expectations
- `showCellDetail()`: вызывается с правильными аргументами
- `renderCellChart()`: Chart.js рендерится без ошибок
- **🔴 BUG:** HTML escaping в `renderHeatmap()` — переменные вставляются без экранирования
---
## Impact (Вкладка 6)
### Пользователь входит
- Видит: Chart.js графики (Agent Score Distribution, Model Distribution, Migration Impact)
### Console expectations
- 0 ошибок Chart.js
---
## Global — скрытые модальные окна
| Модал | Где открывается | Как закрывается |
|-------|----------------|-----------------|
| cellDetailModal | Клик ячейка heatmap | ✕ button, click outside |
| applyModal | Кнопка "Apply Recommended Fixes" | ✕ button, click outside |
| exportModal | Кнопка "Export JSON" (скрыта) | ✕ button, click outside |
| researchModal | Кнопка "New Research Cycle" | ✕ button |
| progressModal | Применение рекомендаций | ✕ button, closeProgressModal() |
**🔴 BUG:** `closeProgressModal()` — функция ВЫЗЫВАЕТСЯ, но НЕ СУЩЕСТВУЕТ (ReferenceError)
---
## Critical Path — минимальная проверка
1. Открыть http://localhost:3003
2. Переключить все 6 вкладок (Overview → Agents → Timeline → Recommendations → Heatmap → Impact)
3. На Agents: кликнуть "Core Dev" → проверить active class
4. На Recommendations: кликнуть "Apply" → модал открылся без ошибки
5. На Heatmap: кликнуть ячейку → модал с графиком открылся
6. DevTools Console: 0 ошибок
---
## Regression Checklist
После любого изменения проверить:
- [ ] Все 6 вкладок открываются без ошибок консоли
- [ ] Фильтры на All Agents переключают active state
- [ ] Кнопка "Apply Recommended Fixes" открывает модал
- [ ] Кнопка "New Research Cycle" не вызывает ReferenceError
- [ ] Клик на ячейку heatmap открывает модал
- [ ] Модальные окна закрываются по ✕ и по клику снаружи
- [ ] Поиск агентов работает (debounce)
- [ ] Нет SyntaxError в консоли

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
const { chromium } = require('playwright');
const TARGET_URL = process.env.TARGET_URL || 'http://localhost:3003';
(async () => {
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
const page = await browser.newPage();
const errors = [];
page.on('console', msg => { if (msg.type() === 'error') errors.push(msg.text()); });
page.on('pageerror', err => { errors.push('PAGE: ' + err.message); });
page.on('requestfailed', req => { errors.push('NET: ' + req.url()); });
await page.goto(TARGET_URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(2000);
for (const tab of ['overview','agents','history','recommendations','heatmap','impact']) {
try {
await page.click(`button[onclick="switchTab('${tab}')"]`);
await page.waitForTimeout(1000);
} catch(e) {}
}
await page.waitForTimeout(500);
await browser.close();
console.log('TOTAL ERRORS:', errors.length);
if (errors.length > 0) {
errors.forEach((e, i) => console.log(i + ':', e.slice(0, 120)));
} else {
console.log('Console error monitor — Dashboard');
console.log('Console errors: 0');
console.log('Network errors: 0');
}
})();

View File

@@ -0,0 +1,52 @@
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
const page = await browser.newPage();
const errors = [];
page.on('console', msg => { if (msg.type() === 'error') errors.push(msg.text()); });
page.on('pageerror', err => errors.push(err.message));
page.on('requestfailed', req => { if (!req.url().includes('favicon')) errors.push('NET: ' + req.url()); });
await page.goto('http://host.docker.internal:3003', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(2000);
const tabNames = ['overview', 'agents', 'history', 'recommendations', 'heatmap', 'impact'];
for (const tab of tabNames) {
try {
const ok = await page.evaluate((t) => {
const btns = document.querySelectorAll('button.tab-btn');
for (const btn of btns) {
if (btn.getAttribute('onclick') && btn.getAttribute('onclick').includes("'" + t + "'")) {
btn.click();
return true;
}
}
return false;
}, tab);
if (!ok) errors.push('CLICK_FAIL: tab=' + tab + ' not found');
await page.waitForTimeout(800);
} catch (e) {
errors.push('CLICK_FAIL: tab=' + tab + ' err=' + e.message.substring(0, 120));
}
}
// Click a heatmap cell
try {
const clicked = await page.evaluate(() => {
const cell = document.querySelector('#hmTable tbody td:nth-child(2)');
if (cell) { cell.click(); return true; }
return false;
});
if (!clicked) errors.push('CLICK_FAIL: heatmap-cell not found');
await page.waitForTimeout(1000);
} catch (e) {
errors.push('CLICK_FAIL: heatmap-cell err=' + e.message.substring(0, 120));
}
await browser.close();
console.log('ERRORS:');
console.log(JSON.stringify(errors));
})();

View File

@@ -0,0 +1,58 @@
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
await page.goto('http://host.docker.internal:3003', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(2000);
// 1. Heatmap tab + cell click
await page.click("button[onclick*='switchTab(\\'\\'heatmap\\'\\')'\"]");
await page.waitForTimeout(1000);
const cell = await page.locator('#hmTable tbody tr:nth-child(2) td:nth-child(2)');
if (await cell.count() > 0) {
await cell.click();
await page.waitForTimeout(1000);
const modalVisible = await page.evaluate(() => {
const m = document.getElementById('cellDetailModal');
return m && getComputedStyle(m).display !== 'none';
});
console.log('heatmap cell click: cellDetailModal visible =', modalVisible);
} else {
console.log('heatmap cell: NOT FOUND');
}
// 2. All Agents + filter 'Core Dev'
await page.click("button[onclick*='switchTab(\\'\\'agents\\'\\')'\"]");
await page.waitForTimeout(1000);
const filterBtn = await page.locator('.filter-btn', { hasText: 'Core Dev' });
if (await filterBtn.count() > 0) {
await filterBtn.click();
await page.waitForTimeout(500);
const activeText = await page.evaluate(() => {
const btn = document.querySelector('.filter-btn.active');
return btn ? btn.textContent : 'NONE';
});
console.log('filter active button:', activeText);
} else {
console.log('filter Core Dev: NOT FOUND');
}
// 3. Recommendations + Apply button
await page.click("button[onclick*='switchTab(\\'\\'recommendations\\'\\')'\"]");
await page.waitForTimeout(1000);
const applyBtn = await page.locator('button', { hasText: 'Apply Recommended Fixes' });
if (await applyBtn.count() > 0) {
await applyBtn.click();
await page.waitForTimeout(500);
const applyModalVisible = await page.evaluate(() => {
const m = document.getElementById('applyModal');
return m && getComputedStyle(m).display !== 'none';
});
console.log('apply click: applyModal visible =', applyModalVisible);
} else {
console.log('Apply Recommended Fixes: NOT FOUND');
}
await browser.close();
})();

View File

@@ -0,0 +1,17 @@
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
const page = await browser.newPage();
const errors = [];
page.on('console', msg => { if (msg.type() === 'error') errors.push(msg.text()); });
page.on('pageerror', err => errors.push('PAGE: ' + err.message));
page.on('requestfailed', req => { if (!req.url().includes('favicon')) errors.push('NET: ' + req.url()); });
await page.goto('http://host.docker.internal:3003', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(2000);
for (const t of ['overview','agents','history','recommendations','heatmap','impact']) {
try { await page.click(`button[onclick="switchTab('${t}')"]`); await page.waitForTimeout(1000); } catch(e) {}
}
await page.waitForTimeout(500);
await browser.close();
console.log(JSON.stringify(errors));
})();

View File

@@ -0,0 +1,25 @@
const { chromium } = require('playwright');
const TARGET = 'http://host.docker.internal:3003';
(async () => {
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
const page = await browser.newPage();
const errors = [];
page.on('console', msg => { if (msg.type() === 'error') errors.push(msg.text()); });
page.on('pageerror', err => { errors.push('PAGE: ' + err.message); });
page.on('requestfailed', req => { if (!req.url().includes('favicon')) errors.push('NET: ' + req.url().split('/').pop()); });
await page.goto(TARGET, { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(2000);
for (const tab of ['overview','agents','history','recommendations','heatmap','impact']) {
try { await page.click(`button[onclick="switchTab('')"]`); await page.waitForTimeout(800); } catch(e) {}
}
await page.waitForTimeout(500);
await browser.close();
console.log('');
console.log('TOTAL ERRORS:', errors.length);
if (errors.length > 0) errors.forEach((e, i) => console.log(i + ':', e.slice(0, 120)));
else console.log('Zero console, page, network errors on all 6 tabs');
})();