Дизайн рефакторинга docs/automation-graph.html после визуальной проверки
коммита 7ee78a9:
- canvas background на самом canvas + удаление hideEdgesOnDrag (artifacts)
- удаление labels с edges, переход на title-tooltip + legend section
- radial-sector layout: 6 колец × 4 функциональных сектора, physics off
- 2 новые секции легенды: «Когда используется» + «Ограничения»
cspell: +mgmt (валидный идентификатор узла claude_md_mgmt)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
10 KiB
Дизайн: рефакторинг автоматизация-графа Лидерры
Дата: 2026-05-14
Статус: Approved
Файл результата: docs/automation-graph.html
Базовая итерация: коммит 7ee78a9 (73 узла, 6 конфликтов, Solarized dark, force-directed)
Цель
Устранить 4 проблемы первой итерации, найденные при визуальной проверке:
- Rendering artifacts на canvas — прямоугольные тени от прошлых redraw'ов поверх фона.
- Подписи на 72 рёбрах загромождают canvas, нечитаемо.
- Force-directed layout не показывает иерархии — узлы расположены произвольно, нельзя на глаз понять, что главное.
- Легенда не отвечает на два важных вопроса: «когда узел активируется» и «какие ограничения на его использование».
Решения по пунктам
1. Rendering artifacts фона
Причина: vis.js рендерит на прозрачный canvas; фон #1e1e2e поставлен на родительский <div id="network">. Когда canvas делает partial redraw на тёмном фоне, оставшиеся «следы» от прошлых позиций узлов видны как прямоугольные регионы.
Исправление:
#network canvas { background: #1e1e2e; }
И в vis.Network options:
interaction: {
hover: true,
tooltipDelay: 400,
// hideEdgesOnDrag: true, ← УДАЛИТЬ — триггерит partial redraws
multiselect: false,
}
Объём: 2 строки CSS + 1 строка JS.
2. Подписи рёбер → в легенду
Удалить label из E() и CONFLICT() factory:
const E = (from, to, label) => ({
from, to,
title: label, // ← было label, стало title (показывается как tooltip)
color: { color: '#586e75', highlight: '#93a1a1', hover: '#93a1a1' },
arrows: { to: { enabled: true, scaleFactor: 0.6 } },
smooth: { type: 'continuous' }
});
Конфликтные рёбра остаются красными пунктирными, но с пометкой ⚡ через label (короткий маркер) — это единственный текст, остающийся на canvas. Все детали — в panel.
Подписи перестают визуально захламлять граф; информация доступна:
- через tooltip при hover на ребро (атрибут
title) - через legend panel при клике на узел — обе стороны связи в секциях «Кому подчиняется» / «Кто подчиняется ему» / «С кем работает одновременно».
3. Радиально-секторная иерархия
Структура: 6 концентрических колец + центр.
| Кольцо | Радиус | Группы (узлы) | Кол-во |
|---|---|---|---|
| 0 (центр) | 0 | pravila |
1 |
| 1 | 220 | claude_md, psr_v1, tooling |
3 |
| 2 | 400 | superpowers, fd_plugin, upm, claude_md_mgmt, hookify_plugin |
5 |
| 3 | 600 | 14 SP-skills + 2 проектных skills | 16 |
| 4 | 800 | 5 hooks + 11 agents | 16 |
| 5 | 1000 | 7 MCP + 10 lefthook jobs | 17 |
| 6 | 1180 | 15 memory files | 15 |
Группировка по секторам: угол узла на кольце выбирается не только по индексу, но и по функциональному кластеру:
| Сектор | Угловой диапазон | Функционал |
|---|---|---|
| N (workflow) | 0° – 90° (верх-право) | Superpowers + skills (brainstorm/wplans/eplans/subagent/parallel/...) + agents (Plan, Explore, general) |
| E (UI-stack) | 90° – 180° (право-низ) | Frontend Design + UPM + 21st MCP + sk_elements + skill_proj UI items |
| S (infra) | 180° – 270° (низ-лево) | hookify + claude-md-mgmt + memory + lefthook jobs (md/cspell/stylelint/eslint) |
| W (data/RLS) | 270° – 360° (лево-верх) | Boost + sentry + redis + sk_rls + ag_rls + lh_squawk + lh_pint + lh_larastan |
Это сократит длину edges (связанные узлы — в одной четверти) и сделает граф визуально читаемым.
Вычисление позиций:
function positionNode(ringIdx, angleDeg) {
const RADII = [0, 220, 400, 600, 800, 1000, 1180];
const r = RADII[ringIdx];
const a = angleDeg * Math.PI / 180;
return { x: Math.round(r * Math.cos(a)), y: Math.round(r * Math.sin(a)) };
}
Каждому из 73 узлов в массиве NODES добавляются два новых поля: ring (индекс кольца 0–6) и sectorAngle (угол в градусах). Все 73 декларации NODES обновляются. В скрипте после декларации NODES добавляется цикл, который выставляет x, y через positionNode(). Поле fixed: false оставляется по умолчанию (vis.js разрешит drag), но physics: { enabled: false } гарантирует, что узел не «спружинит» на место после drag.
vis.js options:
physics: {
enabled: false, // узлы стоят детерминированно по заданным x,y
},
edges: {
smooth: { type: 'continuous', roundness: 0.5 },
selectionWidth: 2,
}
Поведение кнопок:
- «Расшевелить» — включает physics с
forceAtlas2Based,damping: 0.6(мягкая интерактивность). - «Зафиксировать» — выключает physics обратно (
physics.enabled = false); узлы остаются в текущих позициях после драга. - «Сбросить вид» — выполняет два действия: (а) восстанавливает radial positions всех узлов через
nodesDS.update(...)(если пользователь подвинул узлы вручную) + (б) делаетnetwork.fit()для центрирования камеры. - «Снять выделение» — без изменений (closes legend panel, сбрасывает search).
4. Секции «Когда используется» и «Ограничения» в legend panel
HTML: добавить 2 секции между «Что делает» и «Кому подчиняется»:
<div class="legend-section"><h4>Когда используется</h4><p id="ld-when">—</p></div>
<div class="legend-section"><h4>Ограничения</h4><p id="ld-limits">—</p></div>
Структура данных NODE_DETAILS[id]:
function nd(desc, when, limits, reportsTo, manages, together, conflicts) {
return { desc, when, limits, reportsTo, manages, together, conflicts: conflicts || [] };
}
Контент: для каждого из 73 узлов заполняется из нормативки:
when— триггер активации (фаза, событие, контекст вызова)limits— запреты, READ-ONLY политики, обязательные фильтры, hard-rule условия
Источники нормативки для контента:
- Pravila §12 (hard rule инвокаций), §13 (UI-пул)
- CLAUDE.md §3.1/3.2/3.3/3.4 (фазы и активация инструментов)
- PSR_v1 R6/R10/R11/R14 (фильтры, координация, pipelines)
- Tooling §3.1–3.4 (команды и режимы использования)
- lefthook.yml (stage/glob для jobs)
.claude/settings.json(триггеры хуков)
Рендеринг в showLegend(): после ld-desc устанавливаются ld-when и ld-limits:
document.getElementById('ld-when').textContent = details.when || '—';
document.getElementById('ld-limits').textContent = details.limits || 'без особых ограничений';
Финальная структура legend panel
После рефакторинга панель содержит 7 секций в строгом порядке:
- Заголовок узла + категория-бадж
- Что делает
- Когда используется (новое)
- Ограничения (новое)
- Кому подчиняется
- Кто подчиняется ему
- С кем работает одновременно
- ⚡ Конфликты (только если есть)
Технология
- vis.js Network 9.1.9 (CDN), без изменений.
- Файл
docs/automation-graph.html— единственное место правок. - Spec, plan, NODES/EDGES/NODE_DETAILS — все правки внутри
<script>тега.
Ограничения
- При первом открытии граф занимает ~2400×2400px при fit-to-view; на 1920px viewport узлы внешнего кольца становятся мельче. Это компромисс за читаемую иерархию.
- При
physics.enabled = falseнет анимации stabilization — узлы появляются сразу в финальной позиции. Это плюс для понимания, минус для эффекта «оживления» графа. - Контент полей
when/limits— авторский, основанный на нормативке на момент 2026-05-14. При обновлении CLAUDE.md/Pravila/PSR_v1/Tooling — содержимое требует ручной актуализации. - Узлы можно перетаскивать мышкой при включённой физике («Расшевелить»); при
physics: disableddrag работает, но узлы не пружинят на место.