# Дизайн: рефакторинг автоматизация-графа Лидерры

**Дата:** 2026-05-14
**Статус:** Approved
**Файл результата:** `docs/automation-graph.html`
**Базовая итерация:** коммит `7ee78a9` (73 узла, 6 конфликтов, Solarized dark, force-directed)

---

## Цель

Устранить 4 проблемы первой итерации, найденные при визуальной проверке:

1. **Rendering artifacts на canvas** — прямоугольные тени от прошлых redraw'ов поверх фона.
2. **Подписи на 72 рёбрах** загромождают canvas, нечитаемо.
3. **Force-directed layout не показывает иерархии** — узлы расположены произвольно, нельзя на глаз понять, что главное.
4. **Легенда не отвечает на два важных вопроса**: «когда узел активируется» и «какие ограничения на его использование».

---

## Решения по пунктам

### 1. Rendering artifacts фона

**Причина:** vis.js рендерит на прозрачный canvas; фон `#1e1e2e` поставлен на родительский `<div id="network">`. Когда canvas делает partial redraw на тёмном фоне, оставшиеся «следы» от прошлых позиций узлов видны как прямоугольные регионы.

**Исправление:**

```css
#network canvas { background: #1e1e2e; }
```

И в `vis.Network` options:

```js
interaction: {
  hover: true,
  tooltipDelay: 400,
  // hideEdgesOnDrag: true,  ← УДАЛИТЬ — триггерит partial redraws
  multiselect: false,
}
```

Объём: 2 строки CSS + 1 строка JS.

### 2. Подписи рёбер → в легенду

**Удалить `label` из `E()` и `CONFLICT()` factory:**

```js
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 (связанные узлы — в одной четверти) и сделает граф визуально читаемым.

**Вычисление позиций:**

```js
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:**

```js
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 секции между «Что делает» и «Кому подчиняется»:

```html
<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]`:**

```js
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`:

```js
document.getElementById('ld-when').textContent = details.when || '—';
document.getElementById('ld-limits').textContent = details.limits || 'без особых ограничений';
```

---

## Финальная структура legend panel

После рефакторинга панель содержит **7 секций** в строгом порядке:

1. Заголовок узла + категория-бадж
2. Что делает
3. **Когда используется** (новое)
4. **Ограничения** (новое)
5. Кому подчиняется
6. Кто подчиняется ему
7. С кем работает одновременно
8. ⚡ Конфликты (только если есть)

---

## Технология

- 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: disabled` drag работает, но узлы не пружинят на место.
