Compare commits

...

2 Commits

Author SHA1 Message Date
Дмитрий 1114cd1722 docs(brain): brain dashboard implementation plan
13 tasks across 3 phases — static server + topology extraction + 4 views
(Карта / Разбор / Лента / Агрегат). TDD on dashboard-core.js, smoke on UI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 15:04:09 +03:00
Дмитрий 092f55829b docs(brain): brain dashboard design spec
Standalone HTML dashboard that visualises the observer episode log over
the automation-graph topology — 4 views (map / task-replay / session
feed / aggregate), graph as shared canvas, 3-phase build order.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 14:22:05 +03:00
4 changed files with 1657 additions and 3 deletions
+13
View File
@@ -1475,3 +1475,16 @@ DWC
инжектим
фикстурный
роута
# Brain dashboard design spec (2026-05-19)
визуализирующий
анимируются
неподсвеченными
полл
инференс
вендорено
# Brain dashboard implementation plan (2026-05-19)
visualises
AGD
agg
+3 -3
View File
@@ -1,6 +1,6 @@
# Brain Status (auto-generated)
Last updated: 2026-05-19T10:40:30.462Z
Last updated: 2026-05-19T11:22:16.708Z
| Контролёр | Состояние | Детали |
|---|---|---|
@@ -8,11 +8,11 @@ Last updated: 2026-05-19T10:40:30.462Z
| C2 Cross-ref consistency | ✅ | [cross-ref-checker] OK — 0 drift in 4 files |
| C3 Observer-of-observer | ✅ | [observer-of-observer] OK — last read 0 week(s) ago |
| C4 Сигнальный статус | ✅ | This file (self-reference) |
| C5 Observer-coverage | ✅ | 16 episode(s), 979 recent commit(s) · Stop-hook + post-commit OK |
| C5 Observer-coverage | ✅ | 23 episode(s), 982 recent commit(s) · Stop-hook + post-commit OK |
## Метрики (информационные, не алерты)
- Observer evidence: 16 episodes this month, 0 observer_error markers, 0 PII matches before filter
- Observer evidence: 23 episodes this month, 0 observer_error markers, 0 PII matches before filter
- Использование узлов: см. `/brain-retro` (раз в спринт). **Неиспользованные узлы — не проблема** (capability-readiness; см. memory `feedback_brain_unused_tools_not_problem` — outside-repo memory store).
## Алерт-индикаторы
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,195 @@
# Дашборд мозга — дизайн
**Дата:** 2026-05-19
**Статус:** согласован в brainstorming-сессии, готов к writing-plans
**Источник:** brainstorming-сессия Claude + Дмитрий
## 1. Контекст и проблема
`docs/automation-graph.html` («карта мозга») — статический снимок: 124 узла, 130 рёбер, 11 размеченных конфликтов, плюс ручная теплокарта `NODE_META` за фиксированное окно. Карта показывает *топологию на момент*, но не показывает *работу*: как решались задачи, какие узлы задействованы, где возникли ошибки и ретраи, куда перенаправлялось, где сталкивались узлы.
Журнал исполнения уже существует — `docs/observer/episodes-YYYY-MM.jsonl`: наблюдатель (ADR-011) пишет одну запись на каждый ход (Stop-событие). В записи — выбранный узел, инструменты, ошибки, ретраи, перенаправления, hard-floor, классификация задачи, окружение. **Данные есть — нет визуализации.** Карта и журнал между собой не связаны.
## 2. Цель и не-цели
**Цель:** standalone-дашборд, визуализирующий журнал эпизодов поверх топологии карты — три способа смотреть на работу мозга плюс сама карта.
**В scope:**
- Чтение `episodes-*.jsonl` (схема v1 и v2).
- Четыре view: Карта, Разбор задачи, Лента сессии, Агрегат.
- Граф как общий холст для траекторий и тепла.
- Три слоя «конфликтов» (см. §6).
**Не-цели (YAGNI):**
- Не меняем формат эпизодов и логику наблюдателя (исключение — отдельная задача, §13).
- Не правим `/brain-retro` и контролёры.
- Не пиксель-полировка — Forest накладывается на этапе реализации (frontend-design).
- Нет истории до запуска наблюдателя — её физически нет.
- Не Vue/Vuetify-приложение — это dev-инструмент, zero-build (см. §3).
## 3. Зафиксированные решения
**Тех-модель: standalone HTML + локальный статик-сервер.** Один HTML-файл без сборки; читает свежий JSONL через статик-сервер (`fetch` с `file://` браузер блокирует). Сервер — ~20 строк на `node:http`, запуск npm-командой, гасится по Ctrl-C. Ноль новых npm-зависимостей, ноль постоянных демонов. Отвергнуты: «запекаемый файл» (генератор вшивает данные — лента не живая) и «Vue/Vuetify-приложение» (сборка + стек портала в dev-инструменте; vis.js всё равно не Vue).
**Раскладка: граф баннером сверху.** Постоянная полоса графа сверху, рабочая зона view снизу; переключатель view меняет нижнюю зону. Граф всегда виден; широкие таблицы и ленты снизу.
## 4. Архитектура
### 4.1. Слой данных
Источник — `docs/observer/episodes-YYYY-MM.jsonl`, append-only, одна строка = один эпизод.
Сервер `tools/brain-dashboard-server.mjs`:
- статика из корня репо (HTML, JS, JSONL, `automation-graph.html`, vis.js);
- эндпоинт `GET /api/episodes` → JSON-список имён файлов `docs/observer/episodes-*.jsonl` (дашборд не угадывает имена);
- больше ничего; только localhost.
Парсер (JS, внутри дашборда):
- читает каждую строку JSONL → объект эпизода;
- нормализует **v1** (строки без `schema_version` — нет `decision_provenance` / `environment` / `task_size` / `prompt_signal`, `outcome` уже проставлен) и **v2** (`schema_version: 2`);
- битые строки и строки-маркеры `observer_error` — пропускаются, ведётся счётчик «N пропущено»;
- результат — нормализованный массив эпизодов единой формы (отсутствующие v1-поля → `null`).
Производные данные (тепло, кластеры, агрегаты) считаются в браузере при загрузке. Ноль вшитых данных → всегда свежо.
### 4.2. Карта = общий холст
Сейчас топология зашита константами внутри `automation-graph.html` (один файл ≈2900 строк): `NODES` (стр. 229), `EDGES` (стр. 418), секции, `CONFLICT`-данные (стр. ≈406–614), `NODE_META` (стр. 1898), `NODE_SECTION` (стр. 2155).
**Рефактор-вынос:** константы топологии (`NODES`, `EDGES`, `SECTIONS`, `CONFLICT`-данные, `NODE_SECTION`) выносятся в `docs/automation-graph-data.js`. Старая карта `<script src>`-ит его — поведение и вид не меняются (подтвердить визуальным smoke-тестом). `NODE_META` (ручная теплокарта) **остаётся в старой карте** — дашборд её не использует, он считает тепло из эпизодов.
Дашборд импортирует `automation-graph-data.js` и строит **свой** экземпляр vis.js-графа в баннере. Этот граф управляемый — на нём анимируются траектории (Разбор) и красится тепло (Агрегат). Iframe старой карты отвергнут: чужой iframe нельзя анимировать снаружи.
Вкладка «Карта» = тот же граф дашборда в режиме «без оверлея». Файл `automation-graph.html` продолжает существовать как самостоятельная голая карта.
### 4.3. Атрибуция узлов (честная)
Граф — 124 узла (MCP-серверы, плагины, скилы, инструменты, секции). Эпизод даёт сигналы об узлах:
- `primary_rationale.node_chosen` — чаще всего `"direct"`, иногда id скила (`"superpowers:systematic-debugging"`);
- события `skill_invoked` — id скилов;
- `tool_summary.counts` — имена встроенных инструментов Claude (`Read`, `Edit`, `Bash`, `Grep`, …) и `Skill` / `ToolSearch`.
Маршрутизируемый словарь наблюдателя — `tools/observer-known-nodes.txt` (~22 имени: 13 superpowers-скилов + 7 проектных + 2 плагина/команды). Это **меньше** 124 узлов графа и пересекается с ними частично.
**Решение:** дашборд держит таблицу соответствия `сигнал эпизода → id узла графа`. Подсвечивает узлы, для которых соответствие есть. Узлы без соответствия (встроенные инструменты Claude, большинство MCP/плагинов, которые эпизоды пока не называют) **остаются неподсвеченными** — это ожидаемо и подписано в UI («атрибутировано N из M сигналов»). Полнее станет, когда роутер начнёт писать `node_chosen` детальнее — вне scope этой задачи.
## 5. Четыре view
Общий каркас (раскладка из §3): сверху переключатель view + граф-баннер; снизу — рабочая зона.
### 5.1. Карта
Граф без оверлея: топология + 11 размеченных дизайн-конфликтов (цвета RED / BLACK / GREEN из `CONFLICT_TYPES`). Фильтры по секциям/типам — переносятся из старой карты по возможности (или минимальный набор). Это «нулевое состояние» холста.
### 5.2. Разбор задачи (ретроспектива)
Нижняя зона: слева список эпизодов (фильтр: дата, `task_classification`, `outcome`, `path_type`, наличие ошибок); справа — детали выбранного.
Выбор эпизода → траектория:
- на графе подсвечиваются атрибутированные узлы (§4.3);
- справа — упорядоченный список событий эпизода (`skill_invoked` / `error` / `retry` / `hook_fired` / `interrupt` / `time_burn`), плюс шапка: классификация, `path_type`, `decision_provenance` (если `user_directed_method` — «перенаправление: выбран X, автономно был бы Y»), `hard_floor`, окружение (`economy_level`, `model`, `post_compaction`, `session_turn`, `parallel_session`), `task_size`.
Честно: внутри хода есть упорядоченный список событий, но не каждый tool-вызов по порядку — `tool_summary` даёт только счётчики. «Траектория» = последовательность событий + сводка инструментов.
### 5.3. Лента сессии (живая)
Нижняя зона: одноколоночный поток эпизодов, сгруппированных по `task_id` / `task_ref`, новый ход сверху. Карточка хода: время, классификация, `path_type`, атрибутированный узел, ошибки/ретраи (бейджи), длительность (`ended_at started_at`), флаг перенаправления.
Автоопрос: дашборд раз в N секунд (по умолчанию 5) перезапрашивает `/api/episodes` + текущий месячный JSONL, дописывает новые строки. Полл — только в этом view. Кнопка пауза/возобновить.
### 5.4. Агрегат (тренды)
Нижняя зона: плитки метрик по всем эпизодам:
- тепло узлов (авто — сколько раз каждый атрибутированный узел встречался); красит граф-баннер;
- горячие точки ошибок/ретраев (узлы и классы задач с наибольшей долей `error` / `retry`);
- доля перенаправлений (`decision_provenance.kind == "user_directed_method"`);
- распределения `economy_level`, `path_type` (improvised/regulated), `task_classification`, `outcome`;
- счётчик `observer_error` и пропущенных строк.
Опциональный reuse: для v2-эпизодов с `outcome: "unknown"` — переиспользовать детерминированный inference из `tools/brain-retro-analyzer.mjs`, если он оформлен импортируемым модулем; иначе показывать `unknown`. Решается в плане.
## 6. Конфликты — три слоя
Запрос — «где конфликты среди узлов». Эпизоды не пишут «узел A столкнулся с узлом B» — только `error` / `retry` / `hook_fired.errors`. Дашборд отдаёт три явно подписанных слоя:
1. **Дизайн-конфликты** — 11 размеченных `CONFLICT`-рёбер карты (факт, из топологии).
2. **Трение** — эпизоды с `error`/`retry`, привязанные к атрибутированным в них узлам. Это инференс («во время хода с этим узлом была ошибка»), не доказанный конфликт. Подписано.
3. **Корреляция** — эпизод с ошибкой, где атрибутированы два узла, между которыми есть `CONFLICT`-ребро → «конфликт мог реализоваться». Эвристика. Подписано.
Настоящего лога «узел×узел» нет. См. §13.
## 7. Раскладка
```
┌─────────────────────────────────────────────┐
│ [Карта] [Разбор] [Лента] [Агрегат] │ переключатель view
├─────────────────────────────────────────────┤
│ │
│ ГРАФ — баннер (vis.js) │ ~40% высоты, всегда виден
│ │
├─────────────────────────────────────────────┤
│ │
│ рабочая зона view (меняется) │ ~60% высоты
│ │
└─────────────────────────────────────────────┘
```
Граф-баннер общий для всех view; рабочая зона своя у каждого. Forest-палитра (Teal `#0F6E56`, ivory `#F6F3EC`, теало-нуар `#012019`), Inter / JetBrains Mono — накладываются на этапе реализации как CSS-переменные.
## 8. Файлы и компоненты
| Файл | Назначение | Статус |
|---|---|---|
| `docs/observer/dashboard.html` | каркас дашборда (раскладка из §3/§7, vis.js-граф) | новый |
| `docs/observer/dashboard.js` | парсер JSONL + агрегатор + 4 view + рендер графа | новый |
| `docs/automation-graph-data.js` | вынесенная топология (`NODES` / `EDGES` / `SECTIONS` / `CONFLICT` / `NODE_SECTION`) | новый (вынос) |
| `docs/automation-graph.html` | `<script src>` на data-файл; остальное без изменений | правка |
| `tools/brain-dashboard-server.mjs` | статик-сервер + `/api/episodes` | новый |
| `package.json` | скрипт `brain:dashboard` (запуск сервера + открытие браузера) | правка |
| `tools/brain-dashboard-*.test.mjs` | тесты парсера / агрегатора / сервера | новый |
`dashboard.js` при росте можно разбить на модули (`parser.js`, `aggregate.js`, `graph.js`, `views/*.js`) — решается в плане по фактическому размеру.
## 9. Обработка ошибок и граничные случаи
- Битая JSONL-строка / `observer_error`-маркер → пропуск, инкремент счётчика, показ счётчика в UI.
- Месячный файл отсутствует или пуст → не ошибка.
- Эпизодов нет вообще → дружелюбное пустое состояние.
- v1-эпизод (нет v2-полей) → недостающие поля `null`, UI показывает «—».
- Сервер не запущен → дашборд физически не откроется (он отдаётся этим же сервером); пустой `/api/episodes` → пустое состояние.
- `automation-graph-data.js` не загрузился → пустой граф + явное сообщение.
## 10. Тестирование
- **TDD** на чистую логику (`dashboard.js` — парсер, нормализация v1/v2, агрегатор, атрибуция узлов, инференс конфликтов): `tools/brain-dashboard-*.test.mjs` на `node:test`, failing-first → GREEN. Паттерн — как существующие `tools/*.test.mjs`.
- Сервер `brain-dashboard-server.mjs` — smoke-тест (поднять, дёрнуть `/api/episodes`, проверить отдачу статики).
- Вынос топологии — визуальный smoke старой карты (Playwright или ручной): карта выглядит и фильтруется как до выноса.
- Рендер view и графа — ручной визуальный smoke в браузере.
## 11. Честные ограничения
1. **Гранулярность — один эпизод на ход.** Внутри хода есть упорядоченный список событий, но не каждый tool-вызов по порядку (только счётчики `tool_summary`).
2. **«Живость» — после Stop, не в процессе.** Лента обновляется после завершения хода (+ задержка автоопроса), не пока ход идёт.
3. **Атрибуция узлов частичная**`node_chosen` чаще `direct`; словарь наблюдателя ~22 имени против 124 узлов графа. Бóльшая часть графа не подсвечивается. См. §4.3.
4. **Конфликты узел×узел не логируются** — даётся инференс (§6), не факт.
5. **История — только с запуска наблюдателя (~19.05.2026).** Раньше эпизодов нет.
## 12. Порядок сборки (3 фазы, один спек)
- **Фаза 1 — фундамент.** Статик-сервер + `/api/episodes`; вынос топологии в `automation-graph-data.js` + правка старой карты + визуальный smoke; каркас `dashboard.html`; парсер v1/v2 + атрибуция узлов (TDD); граф-баннер; view «Карта» + view «Разбор задачи»; npm-скрипт `brain:dashboard`.
- **Фаза 2 — живость.** View «Лента сессии» + автоопрос/пауза.
- **Фаза 3 — агрегат.** View «Агрегат» + тепло на граф-баннер + три слоя конфликтов (§6); опц. reuse `brain-retro-analyzer.mjs` для outcome-инференса.
## 13. Открытые вопросы / отложено
- **Настоящий лог конфликтов узел×узел** — потребует нового типа события в наблюдателе (`tools/observer-transcript-parser.mjs` + схема эпизода). Отдельная задача, не входит в этот спек.
- **Двойной клик без сервера** — если когда-нибудь понадобится: добавить генератору запекание данных, файл выродится в тех-модель «запекаемый файл» без переписывания. Сейчас YAGNI.
- **Forest-полировка** — этап реализации (frontend-design).
- **vis.js** — откуда его берёт старая карта (CDN или вендорено) — уточнить в плане, дашборд переиспользует тот же способ.