# Automation Graph Refactor — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Apply 4 refactor fixes to `docs/automation-graph.html` from spec [2026-05-14-automation-graph-refactor-design.md](../specs/2026-05-14-automation-graph-refactor-design.md): canvas rendering artifacts fix, edge labels→tooltips, radial-sector layout, two new legend sections (когда+ограничения).

**Architecture:** Same single-file HTML. All JS lives in one `<script>` tag. Tasks land independently as atomic commits — each commit leaves the file in a working state. Visual smoke (open in Edge/Chrome) is the test method; no automated tests for browser viz.

**Tech Stack:** vis.js Network 9.1.9 (CDN), vanilla JS, CSS3 Solarized Dark. No new dependencies.

**Source spec:** `docs/superpowers/specs/2026-05-14-automation-graph-refactor-design.md`
**Base commit:** `7ee78a9` (first iteration, 73 nodes / 6 conflicts).

---

## File Map

| File | Action | Purpose |
|---|---|---|
| `docs/automation-graph.html` | Modify | The single deliverable; all changes here |
| `cspell-words.txt` | Maybe modify | Add new technical terms if cspell blocks commit |

---

## Verification Method

For each task: open `docs/automation-graph.html` in Edge (or Chrome) and visually verify the criteria listed in "Expected" steps. Browser console (F12) must show **no JS errors**. The pre-commit hook (`lefthook`) runs `gitleaks`, `stylelint` (on `.html`), `markdownlint` (on `.md`); they must pass.

---

### Task 1: Canvas rendering artifacts fix

**Files:**

- Modify: `docs/automation-graph.html` — CSS block + vis.Network options

**Spec ref:** Section 1 (Rendering artifacts фона).

- [ ] **Step 1: Add explicit canvas background CSS**

In the `<style>` block, locate `#network { flex: 1; background: #1e1e2e; }` and add a new rule **immediately after it**:

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

- [ ] **Step 2: Remove hideEdgesOnDrag from vis.Network options**

Find the `interaction` block inside `new vis.Network(...)`:

```js
interaction: {
  hover: true,
  tooltipDelay: 400,
  hideEdgesOnDrag: true,
  multiselect: false,
},
```

Remove the `hideEdgesOnDrag: true,` line:

```js
interaction: {
  hover: true,
  tooltipDelay: 400,
  multiselect: false,
},
```

- [ ] **Step 3: Visual verification**

Open `docs/automation-graph.html` in Edge. Refresh. Move mouse around canvas, hover/click random nodes, drag a node. Verify the canvas background is uniformly `#1e1e2e` — **no rectangular shadow/region artifacts** visible.

- [ ] **Step 4: Commit**

```bash
cd "c:/моя/проекты/портал crm/Документация"
git add docs/automation-graph.html
git commit -m "fix(graph): canvas rendering artifacts — explicit canvas bg + remove hideEdgesOnDrag"
```

Expected: pre-commit passes; commit hash printed.

---

### Task 2: Remove edge labels, switch to tooltips

**Files:**

- Modify: `docs/automation-graph.html` — `E()` and `CONFLICT()` factory functions

**Spec ref:** Section 2 (Подписи рёбер → в легенду).

- [ ] **Step 1: Replace E() factory**

Locate the `E()` factory:

```js
const E = (from, to, label) => ({
  from, to, label,
  color: { color: '#586e75', highlight: '#93a1a1', hover: '#93a1a1' },
  arrows: { to: { enabled: true, scaleFactor: 0.6 } },
  font: { color: '#839496', size: 10, align: 'middle', strokeWidth: 2, strokeColor: '#1e1e2e' },
  smooth: { type: 'dynamic' }
});
```

Replace with:

```js
const E = (from, to, label) => ({
  from, to,
  title: label,
  color: { color: '#586e75', highlight: '#93a1a1', hover: '#93a1a1' },
  arrows: { to: { enabled: true, scaleFactor: 0.6 } },
  smooth: { type: 'continuous', roundness: 0.5 }
});
```

Changes: removed `label` (was rendered on canvas) and `font` (no canvas text needed); changed to `title` (HTML tooltip on hover); changed `smooth.type` to `continuous` (better for radial layout in Task 6).

- [ ] **Step 2: Replace CONFLICT() factory**

Locate:

```js
const CONFLICT = (from, to, label) => ({
  from, to, label,
  dashes: true,
  width: 2,
  color: { color: '#ff5f57', highlight: '#ff8880', hover: '#ff8880' },
  arrows: { to: { enabled: true, scaleFactor: 0.7 }, from: { enabled: true, scaleFactor: 0.7 } },
  font: { color: '#ff5f57', size: 10, align: 'middle', strokeWidth: 2, strokeColor: '#1e1e2e' },
  smooth: { type: 'curvedCW', roundness: 0.35 }
});
```

Replace with:

```js
const CONFLICT = (from, to, label) => ({
  from, to,
  title: label,
  label: '⚡',
  dashes: true,
  width: 2,
  color: { color: '#ff5f57', highlight: '#ff8880', hover: '#ff8880' },
  arrows: { to: { enabled: true, scaleFactor: 0.7 }, from: { enabled: true, scaleFactor: 0.7 } },
  font: { color: '#ff5f57', size: 14, align: 'middle', strokeWidth: 3, strokeColor: '#1e1e2e' },
  smooth: { type: 'curvedCW', roundness: 0.35 }
});
```

Conflicts keep a short `⚡` marker on canvas (so the user immediately sees something is wrong); the full description still goes to `title`.

- [ ] **Step 3: Visual verification**

Open `docs/automation-graph.html` in Edge. Refresh.

- No verbose Russian text appears on canvas next to edges.
- Hover over a normal grey edge → small tooltip with the original label appears.
- Six red dashed bidirectional edges still visible with a small `⚡` marker.
- Hover over a `⚡` edge → tooltip shows the original conflict label (e.g. «⚡ T2.2»).

- [ ] **Step 4: Commit**

```bash
cd "c:/моя/проекты/портал crm/Документация"
git add docs/automation-graph.html
git commit -m "fix(graph): remove edge labels from canvas, move to hover tooltips"
```

---

### Task 3: Legend panel HTML — add «Когда» + «Ограничения» sections

**Files:**

- Modify: `docs/automation-graph.html` — `#legend-panel` block in HTML

**Spec ref:** Section 4 (Секции «Когда используется» и «Ограничения»).

- [ ] **Step 1: Insert two new legend-section divs**

Locate the legend panel in HTML:

```html
<div id="legend-panel">
  <button id="legend-close">×</button>
  <div id="legend-title">—</div>
  <div id="legend-category"></div>
  <div class="legend-section"><h4>Что делает</h4><p id="ld-desc">—</p></div>
  <div class="legend-section"><h4>Кому подчиняется</h4><ul id="ld-reports"></ul></div>
  ...
```

Insert two new `<div class="legend-section">` blocks **after** the "Что делает" section and **before** "Кому подчиняется":

```html
<div id="legend-panel">
  <button id="legend-close">×</button>
  <div id="legend-title">—</div>
  <div id="legend-category"></div>
  <div class="legend-section"><h4>Что делает</h4><p id="ld-desc">—</p></div>
  <div class="legend-section"><h4>Когда используется</h4><p id="ld-when">—</p></div>
  <div class="legend-section"><h4>Ограничения</h4><p id="ld-limits">—</p></div>
  <div class="legend-section"><h4>Кому подчиняется</h4><ul id="ld-reports"></ul></div>
  <div class="legend-section"><h4>Кто подчиняется ему</h4><ul id="ld-manages"></ul></div>
  <div class="legend-section"><h4>С кем работает одновременно</h4><ul id="ld-together"></ul></div>
  <div class="legend-section" id="conflicts-section">
    <h4>⚡ Конфликты</h4>
    <div id="ld-conflicts"></div>
  </div>
</div>
```

- [ ] **Step 2: Visual verification**

Open `docs/automation-graph.html` in Edge. Refresh.
Click on any node (e.g. "Pravila v1.13").
Expected: legend panel opens; sections "Когда используется" and "Ограничения" appear with `—` placeholder text. Other sections render content as before. No JS errors in console.

- [ ] **Step 3: Commit**

```bash
cd "c:/моя/проекты/портал crm/Документация"
git add docs/automation-graph.html
git commit -m "feat(graph): legend panel — add «Когда используется» and «Ограничения» sections"
```

---

### Task 4: nd() helper signature + showLegend rendering

**Files:**

- Modify: `docs/automation-graph.html` — `nd()` function, `NODE_DETAILS` first few entries (sanity), `showLegend()` function

**Spec ref:** Section 4 (Структура данных + Рендеринг).

This task changes the `nd()` factory signature from 5 params to 7 params. Since `NODE_DETAILS` calls `nd(desc, reportsTo, manages, together, conflicts)` 73 times, we keep backward-compat by accepting optional positional args **and** sniffing arg types.

- [ ] **Step 1: Replace nd() helper**

Locate:

```js
function nd(desc, reportsTo, manages, together, conflicts) {
  return { desc, reportsTo, manages, together, conflicts: conflicts || [] };
}
```

Replace with:

```js
function nd(desc, when, limits, reportsTo, manages, together, conflicts) {
  // Backward-compat: old 5-arg signature was nd(desc, reportsTo, manages, together, conflicts).
  // If 2nd arg is an array, treat as old-style call.
  if (Array.isArray(when)) {
    return {
      desc,
      when: '',
      limits: '',
      reportsTo: when,           // shift
      manages: limits,            // shift
      together: reportsTo,        // shift
      conflicts: (manages || []), // shift
    };
  }
  return { desc, when: when || '', limits: limits || '', reportsTo, manages, together, conflicts: conflicts || [] };
}
```

This makes existing `NODE_DETAILS` entries work unchanged; new tasks (5a-5f) will rewrite calls to 7-arg form.

- [ ] **Step 2: Update showLegend() to render when/limits**

Locate inside `showLegend(nodeId)`:

```js
document.getElementById('ld-desc').textContent = details.desc;

const ldReports = document.getElementById('ld-reports');
```

Insert between them:

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

const ldReports = document.getElementById('ld-reports');
```

- [ ] **Step 3: Visual verification**

Open `docs/automation-graph.html` in Edge. Refresh. Click any node.
Expected:

- "Когда используется" shows `—` (because old-style calls have `when: ''`).
- "Ограничения" shows `без особых ограничений` (default fallback).
- All other sections render correctly with original content.
- No JS errors.

- [ ] **Step 4: Commit**

```bash
cd "c:/моя/проекты/портал crm/Документация"
git add docs/automation-graph.html
git commit -m "feat(graph): nd() helper supports when+limits fields; showLegend renders them"
```

---

### Task 5a: Fill when+limits for Rules + Plugins (9 nodes)

**Files:**

- Modify: `docs/automation-graph.html` — 9 entries in `NODE_DETAILS` (pravila, claude_md, psr_v1, tooling, superpowers, fd_plugin, upm, claude_md_mgmt, hookify_plugin)

**Spec ref:** Section 4 (Контент from нормативка).

- [ ] **Step 1: Replace 4 rules entries**

Locate `pravila: nd(...)` and replace all 4 rules with 7-arg form:

```js
  pravila: nd(
    'Главный свод правил работы Клода — приоритеты, запреты и обязательные скилы.',
    'Действует постоянно — fundamental слой автоматизации; читается при старте каждой сессии через SessionStart хук.',
    '§12 hard rule неотменяем; §9 «Отступления» не применяется к §12. Расходимость с CLAUDE.md/PSR_v1/Tooling — нарушение §7.',
    [],
    [
      { name: 'CLAUDE.md', cond: 'подчинён уровень 2a' },
      { name: 'PSR_v1', cond: 'подчинён уровень 3' },
      { name: 'Superpowers', cond: '§12 — обязывает инвокировать первым' },
      { name: 'Все компоненты', cond: 'через цепочку приоритетов §1' }
    ],
    [{ name: 'CLAUDE.md', cond: 'оба читаются при старте сессии' }]
  ),
  claude_md: nd(
    'Оперативная карта проекта — список технологий, команд, фаз, ссылок на документы.',
    'Читается при старте каждой сессии (SessionStart хук); upd при добавлении новых tooling/фазы.',
    'Правки **только** через `/claude-md-management:claude-md-improver` или `:revise-claude-md` (§5 п.10). Прямые Edit/Write запрещены — блокируются PreToolUse:CLAUDE.md-warn хуком.',
    [{ name: 'Pravila', cond: 'всегда (уровень 2a)' }],
    [
      { name: 'Tooling v1.17', cond: 'ссылается как на реестр инструментов' },
      { name: 'claude-md-mgmt', cond: '§5п.10 — единственный канал правок' }
    ],
    [
      { name: 'Pravila', cond: 'оба читаются SessionStart хуком' },
      { name: 'Tooling', cond: 'оба operational maps уровня 2' }
    ],
    [{ name: 'PSR_v1', desc: 'T2.2: §5п.10 запрещает прямые правки, но PSR_v1 не дублирует это явно — риск Edit без skill\'а' }]
  ),
  psr_v1: nd(
    'Правила совместного использования плагинов — кто с кем работает, какой pipeline обязателен.',
    'При выборе UI-инструмента (FD vs UPM vs 21st), при координации парного стека, при активации off-phase MCP.',
    'R14.5: UPM, 21st, FD — не одновременно. R6.0 фильтр стека и R6.1 hard-override Forest обязательны для UI-вывода плагинов.',
    [{ name: 'Pravila', cond: 'уровень 3 в цепочке' }],
    [
      { name: 'Superpowers + Frontend Design', cond: 'координирует как парный стек' },
      { name: 'UI UX Pro Max', cond: 'R14.3: активирует только через pipeline' },
      { name: '21st Magic MCP', cond: 'R14.4: активирует только через pipeline' }
    ],
    [{ name: 'CLAUDE.md', cond: 'оба operational maps — согласуются при правках' }],
    [{ name: 'CLAUDE.md', desc: 'T2.2: CLAUDE.md §5п.10 требует claude-md-mgmt, PSR_v1 не дублирует это ограничение — риск прямых Edit' }]
  ),
  tooling: nd(
    'Реестр 35 инструментов — когда что использовать, команды установки, конфликты.',
    'При выборе инструмента для фазы (0/1/2/3), при добавлении нового tooling, при актуализации после обновления версий.',
    'При прямом конфликте с CLAUDE.md приоритетнее CLAUDE.md (operational map уровня 2a). Изменения требуют sync с CLAUDE.md §3.',
    [
      { name: 'Pravila', cond: 'уровень 2b (operational map рядом с CLAUDE.md)' },
      { name: 'CLAUDE.md', cond: 'при прямом конфликте CLAUDE.md приоритетнее' }
    ],
    [],
    [{ name: 'CLAUDE.md', cond: 'оба operational maps — правятся синхронно' }]
  ),
```

- [ ] **Step 2: Replace 5 plugins entries**

Locate `superpowers: nd(...)` and replace all 5 plugin entries:

```js
  superpowers: nd(
    'Плагин поведения Клода — 14 скилов для TDD, дебага, планирования, параллельной работы.',
    'При creative / multi-step / debug / verify / TDD / brainstorm / parallel / worktree / finishing-PR / subagent / writing-skills задачах (карта §12.2 Pravila).',
    '§12 hard rule — единственный override: явная просьба заказчика «не используй superpowers сейчас» на текущее действие. Pravila §9 «Отступления» к §12 не применяется.',
    [
      { name: 'Pravila §12', cond: 'hard rule: инвокируется первым' },
      { name: 'PSR_v1', cond: 'координирует как парный стек с Frontend Design' }
    ],
    [{ name: 'Все 14 Superpowers-скилов', cond: 'содержит' }],
    [{ name: 'Frontend Design', cond: 'парный стек — работают вместе при UI-задачах' }],
    [{ name: 'economy-mode хук', desc: 'Economy=100% теоретически может «сэкономить» инвокацию skill\'а, нарушая §12 hard rule (§12 неотменяем)' }]
  ),
  fd_plugin: nd(
    'Плагин знаний о UI — Vue, Vuetify, a11y, паттерны компонентов для Лидерры.',
    'При UI/UX задачах — компоненты, экраны, паттерны взаимодействия; в паре с Superpowers (процесс).',
    'R6.0 фильтр стека: срезать React/Tailwind/shadcn/JSX → Vue 3 + Vuetify 3. R6.1 hard-override Forest для палитры/шрифтов/иконок.',
    [{ name: 'PSR_v1', cond: 'R5: подчинён как часть парного стека' }],
    [],
    [{ name: 'Superpowers', cond: 'парный стек — FD даёт UI-знания, Superpowers даёт процесс' }],
    [
      { name: 'UI UX Pro Max', desc: 'PSR_v1 R14.5: нельзя параллельно — оба активны в settings.json, но должны чередоваться' },
      { name: '21st Magic MCP', desc: 'PSR_v1 R14.5: нельзя параллельно с FD — оба потенциально доступны одновременно' }
    ]
  ),
  upm: nd(
    'Резервная библиотека UI — 50+ стилей, 161 палитра, 99 UX-гайдлайнов. Только по pipeline.',
    'PSR_v1 R14.3 pipeline: фаза 2 R2 fallback к FD ИЛИ фаза 1 R2 «третий вариант» в R12 архитектурном.',
    'R14.5: не параллельно с FD/21st. R6.0 фильтр стека + R6.1 hard-override Forest обязательны. Pa11y a11y на deployable.',
    [{ name: 'PSR_v1', cond: 'R14.3: активируется только через pipeline, не самостоятельно' }],
    [],
    [],
    [{ name: 'Frontend Design', desc: 'PSR_v1 R14.5: нельзя параллельно — UPM как материал, FD как решатель; риск смешивания ролей' }]
  ),
  claude_md_mgmt: nd(
    'Единственный разрешённый способ редактировать CLAUDE.md — через skill claude-md-improver или revise-claude-md.',
    'При любой правке CLAUDE.md (новая фаза, новый tooling, изменение версии, новые quirks — все через skill).',
    'PSR_v1 R10.1 блок 1 (infrastructure). Внутри flow продолжают действовать §4 правил Claude (синхронизация Pravila + Tooling).',
    [{ name: 'PSR_v1', cond: 'R10.1 Блок 1: инфраструктурная категория' }],
    [{ name: 'CLAUDE.md', cond: '§5п.10: монопольный канал правок' }],
    [{ name: 'q-item-add скил', cond: 'скил делегирует CLAUDE.md правки через этот плагин' }]
  ),
  hookify_plugin: nd(
    'Плагин создания хуков — анализирует разговоры и предлагает новые автоматизации через хуки.',
    'При запросе «давай повесим хук на это поведение» или после серии повторяющихся ошибок — анализ через conversation-analyzer агента.',
    'PSR_v1 R10.1. Новые хуки могут конфликтовать с существующими (см. конфликты ниже) — обязательная проверка settings.json до создания.',
    [{ name: 'PSR_v1', cond: 'R10.1: формализован' }],
    [{ name: 'hookify:conversation-analyzer агент', cond: 'запускает анализ разговоров' }],
    [{ name: 'hookify:conversation-analyzer', cond: 'плагин и агент работают в паре' }],
    [
      { name: 'PreToolUse:CLAUDE.md-warn', desc: 'hookify создаёт новые PreToolUse хуки динамически — может перезаписать или затенить существующий CLAUDE.md-warn' },
      { name: 'economy-mode хук', desc: 'новые хуки от hookify могут вступить в конфликт с логикой economy-mode парсера' }
    ]
  ),
```

- [ ] **Step 3: Visual verification**

Open in Edge. Refresh. Click on `Pravila v1.13`, `CLAUDE.md`, `Superpowers`, `Frontend Design`.
Expected: «Когда используется» and «Ограничения» sections now show real Russian text for these 9 nodes. Other nodes still show `—`/`без особых ограничений` defaults.

- [ ] **Step 4: Commit**

```bash
cd "c:/моя/проекты/портал crm/Документация"
git add docs/automation-graph.html
git commit -m "feat(graph): when+limits content for rules + plugins (9 nodes)"
```

---

### Task 5b: Fill when+limits for Skills (16 nodes)

**Files:**

- Modify: `docs/automation-graph.html` — entries `sk_brainstorm` through `sk_elements` (14) + `sk_rls`, `sk_qitem` (2)

- [ ] **Step 1: Replace 14 superpowers skill entries**

Locate `sk_brainstorm: nd(...)` and replace all 14 SP-skills + 2 project skills with 7-arg form:

```js
  sk_brainstorm: nd(
    'Продумывает задачу вместе с заказчиком, формулирует варианты A/B/C и согласует дизайн до написания кода.',
    'При creative задаче — новая фича, нетривиальный refactor, дизайн-решение; ПЕРЕД любым кодом или планом.',
    'Обязательно «Approved» от заказчика до перехода к writing-plans. HARD-GATE: нельзя инвокировать implementation skill до утверждения дизайна.',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен для creative задач' }],
    [],
    [{ name: 'writing-plans', cond: 'вызывается сразу после brainstorming для создания плана' }]
  ),
  sk_tdd: nd(
    'Ведёт разработку через написание провального теста до кода — failing test first, потом GREEN.',
    'При любом новом production коде — backend (Pest) и frontend (Vitest).',
    'Failing test ДО написания implementation; код «должен работать» без verified теста — нарушение §12.',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен для любого нового кода' }],
    [],
    [{ name: 'executing-plans', cond: 'TDD встроен в каждый шаг плана выполнения' }]
  ),
  sk_debug: nd(
    'Системный дебаг: минимум 3 гипотезы, фальсификация каждой перед исправлением — никаких «должно работать».',
    'При unexpected behavior, фейле теста, runtime ошибке, неожиданном выводе.',
    'Минимум 3 гипотезы. Фальсификация через реальные команды/тесты, не «логика подсказывает». Никаких «попробую исправить» без проверки причины.',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен при unexpected behavior' }],
    [],
    [{ name: 'MCP: redis', cond: 'используется для дебага Redis-очередей' }]
  ),
  sk_wplans: nd(
    'Создаёт детальный план реализации с полным кодом, командами и шагами по 2-5 минут.',
    'После brainstorming (creative задача) или сразу для multi-step (≥3 шага) технической задачи.',
    'Никаких placeholder\'ов (TBD, TODO, «add validation»). Каждый шаг — реальный код или команда. Spec coverage обязателен.',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен для multi-step задач (≥3 шага)' }],
    [],
    [{ name: 'executing-plans или subagent-driven', cond: 'план передаётся в один из них для выполнения' }]
  ),
  sk_eplans: nd(
    'Выполняет готовый план шаг за шагом, отмечает чекбоксы, делает коммиты после каждого шага.',
    'После writing-plans (если выбран inline execution); альтернатива — subagent-driven для same-session.',
    'Каждый шаг отмечается checkbox\'ом, не batch\'ит коммиты — атомарно (Pravila §4.2).',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'writing-plans', cond: 'получает план от writing-plans' }, { name: 'subagent-driven', cond: 'альтернатива — зависит от выбора пользователя' }]
  ),
  sk_verify: nd(
    'Обязательная проверка перед «готово» — запускает тесты, видит реальный вывод, никаких предположений.',
    'Перед любым claim «готово»/«passed»/«closed»/«merged» — обязательно (§12 + economy 0% hard requirement).',
    'Реальный запуск, не «должно пройти». Output виден полностью. Cherry-picking запрещён («tests pass» = ровно столько, сколько прошло).',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен перед любым claim «готово»' }],
    [],
    [{ name: 'MCP: laravel-boost', cond: 'запросы к БД для проверки данных' }]
  ),
  sk_parallel: nd(
    'Разбивает независимые задачи на параллельные потоки с изоляцией через git worktrees.',
    'При наличии нескольких независимых рабочих фронтов (CTO-task + Plan task + audit-fix) одновременно.',
    'Изоляция через worktree обязательна — никакого «работаю в одной директории на 3 ветках сразу». Risk of contention.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'worktree скил', cond: 'parallel-work использует worktree для изоляции' }]
  ),
  sk_worktree: nd(
    'Создаёт изолированную копию репозитория для рискованной или параллельной работы.',
    'При parallel-work или при рискованной работе (refactor с возможностью abort, ветка экспериментов).',
    'Cleanup через ExitWorktree обязателен. Не оставлять untracked worktree\'ы — захламляют файловую систему.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'parallel-work', cond: 'worktree — инструмент для parallel-work' }]
  ),
  sk_pr: nd(
    'Чеклист финальной готовности PR — тесты, документация, чистота кода, pre-push хуки.',
    'Перед `gh pr create` или `git push` на upstream — обязательная проверка готовности.',
    'Pre-push хуки (gitleaks full-history + lychee) — не bypass\'ить через `--no-verify`. Pravila §4.2.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'MCP: github', cond: 'создаёт PR через GitHub MCP' }, { name: 'lefthook pre-push', cond: 'запускает gitleaks + lychee' }]
  ),
  sk_subagent: nd(
    'Запускает суб-агентов для крупных задач — каждый в отдельном контексте без накопленного шума.',
    'При plan-driven execution в same-session ИЛИ при делегировании поиска/анализа большого scope.',
    'Subagent в режиме экономии 0%: запрашивать raw output, не summary — решения принимать самому.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [
      { name: 'Explore агент', cond: 'запускает для поиска по кодовой базе' },
      { name: 'general-purpose агент', cond: 'запускает для сложных задач' },
      { name: 'Plan агент', cond: 'запускает для архитектурного планирования' }
    ],
    [{ name: 'writing-plans', cond: 'subagent-driven — основной способ выполнения планов' }]
  ),
  sk_wskills: nd(
    'Создаёт новые скилы по стандартному шаблону с SKILL.md, frontmatter, workflow.',
    'При формализации повторяющегося workflow в reusable skill (после 2-3 примеров одинаковой работы).',
    'Шаблон SKILL.md, frontmatter (name, description, when_to_use, allowed-tools), workflow с DOT-диаграммой.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'plugin-dev:skill-reviewer', cond: 'агент проверяет созданный скил' }]
  ),
  sk_spreview: nd(
    'Проверяет spec-документ на полноту, противоречия, placeholder\'ы и scope.',
    'После writing-plans на отдельном этапе ДО implementation — для крупных multi-task планов.',
    'Inline self-review в режиме brainstorming достаточно для small spec; для крупных — выделенная инвокация.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'brainstorming', cond: 'spec-review вызывается в конце brainstorming после записи спека' }]
  ),
  sk_coderev: nd(
    'Систематический code review — безопасность, тесты, архитектура, соответствие правилам.',
    'Перед merge PR; после крупной серии коммитов; при подготовке к релизу; при подозрении на регрессию.',
    'Не cherry-pick: review всех diff\'ов, не только tarred. SAST (Semgrep) включается обязательно.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'MCP: semgrep', cond: 'SAST-проверка при code review' }]
  ),
  sk_elements: nd(
    'Улучшает написание текстов и документации — ясность, лаконичность, без воды.',
    'При написании spec/plan/CHANGELOG/PR description — для коммуникации с командой.',
    'Без воды. Без «легко», «просто», «всего лишь». Каждое утверждение — измеримое.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    []
  ),
  sk_rls: nd(
    '7-шаговый чеклист RLS для новой таблицы: tenant_id, ENABLE RLS, политики, 5-role GRANTs, CHANGELOG, squawk, smoke test.',
    'При создании новой таблицы в db/schema.sql ИЛИ при правках существующих RLS политик.',
    '5-role GRANTs обязательны (crm_app_user / crm_app_admin / crm_supplier_worker BYPASSRLS / crm_readonly / crm_migrator). CHANGELOG_schema.md обязателен.',
    [{ name: 'Tooling §3.2', cond: 'использует squawk (#15) и команды grep' }],
    [],
    [{ name: 'MCP: laravel-boost', cond: 'SQL запросы к schema.sql для проверки' }],
    [{ name: 'rls-reviewer агент', desc: 'оба проверяют RLS compliance — скил для ручной проверки таблицы, агент для PR/diff review; нет чёткой границы когда какой' }]
  ),
  sk_qitem: nd(
    'Добавляет новый открытый вопрос в реестр Открытые_вопросы_v8_3.md с обновлением счётчиков §0 и версии.',
    'При появлении нового открытого вопроса (Биз-/CTO-/Ю-/Диз-/DO-/OPEN-) — формальный input в реестр.',
    'Категоризация (Биз-/CTO-/...) обязательна. Связанные документы (CLAUDE.md/Pravila/PSR_v1/Tooling) — синхронизируются.',
    [],
    [],
    [{ name: 'claude-md-mgmt', cond: 'скил делегирует правку CLAUDE.md через плагин (§5п.10)' }]
  ),
```

- [ ] **Step 2: Visual verification**

Refresh in Edge. Click `brainstorming`, `TDD`, `systematic-debugging`, `rls-check`. Expected: новые секции «Когда» / «Ограничения» содержат конкретный русский текст для всех 16 skills.

- [ ] **Step 3: Commit**

```bash
git add docs/automation-graph.html
git commit -m "feat(graph): when+limits content for skills (14 SP + 2 проектных)"
```

---

### Task 5c: Fill when+limits for Hooks + Agents (16 nodes)

**Files:**

- Modify: `docs/automation-graph.html` — entries `hk_pre_claude` через `hk_economy` (5) + `ag_pest` через `ag_skreview` (11)

- [ ] **Step 1: Replace 5 hooks + 11 agents entries**

Locate `hk_pre_claude: nd(...)` and replace all 16:

```js
  hk_pre_claude: nd(
    'Блокирует прямое редактирование CLAUDE.md — срабатывает на Edit/Write по этому файлу.',
    'PreToolUse — перед каждым Edit/Write tool call, matcher на путь к CLAUDE.md.',
    'Bypass запрещён. Единственный способ редактировать — claude-md-mgmt skill (§5п.10).',
    [{ name: '.claude/settings.json', cond: 'описан как PreToolUse hook' }],
    [],
    [],
    [{ name: 'hookify (плагин)', desc: 'hookify динамически создаёт новые PreToolUse хуки — может перезаписать или конкурировать с этим хуком' }]
  ),
  hk_post_md: nd(
    'После каждого Edit .md файла запускает markdownlint --fix автоматически.',
    'PostToolUse — после Edit/Write на *.md (кроме корневого CLAUDE.md, чтобы не зациклить).',
    'Не fix\'ит CLAUDE.md (исключён из matcher). При неисправимой ошибке (e.g. broken link) — warning, не block.',
    [{ name: '.claude/settings.json', cond: 'описан как PostToolUse hook' }],
    [],
    [{ name: 'lefthook:markdownlint', cond: 'дублируют задачу: хук — в сессии, lefthook — при коммите' }]
  ),
  hk_post_schema: nd(
    'После правки db/schema.sql напоминает обновить db/CHANGELOG_schema.md.',
    'PostToolUse — после Edit/Write на `db/schema.sql`.',
    'Reminder, не block. Дисциплина CHANGELOG_schema на разработчике (§4.2 Pravila).',
    [{ name: '.claude/settings.json', cond: 'описан как PostToolUse hook' }],
    [],
    [{ name: 'lefthook:squawk', cond: 'оба реагируют на изменения SQL' }]
  ),
  hk_session: nd(
    'При старте каждой сессии инжектирует CLAUDE.md, Pravila и ключевые memory-файлы в контекст.',
    'SessionStart — единожды при инициализации сессии Claude Code.',
    'Memory-список фиксированный — для расширения править hook config. Не читает 80+ квирков целиком — выборочно по relevance.',
    [{ name: '.claude/settings.json', cond: 'описан как SessionStart hook' }],
    [
      { name: 'memory:user_profile', cond: 'читает' },
      { name: 'memory:feedback_env', cond: 'читает' },
      { name: 'memory:project_state', cond: 'читает' },
      { name: 'memory:feedback_superpowers', cond: 'читает' },
      { name: 'memory:feedback_plugins', cond: 'читает' }
    ],
    []
  ),
  hk_economy: nd(
    'Перед каждым промптом парсит «экономия X%» и выставляет режим строгости (0%=полная качество, 100%=по умолчанию).',
    'UserPromptSubmit — перед каждым промптом пользователя; парсит regex /экономия\\s*(\\d+)%/.',
    '§12 hard rule **НЕ** override\'ится этим режимом — на всех уровнях. Действует только на текущую задачу — следующий промпт парсится заново.',
    [{ name: '.claude/settings.json', cond: 'описан как UserPromptSubmit hook' }],
    [],
    [],
    [{ name: 'Superpowers (§12)', desc: 'Economy=100% теоретически может «сэкономить» инвокацию skill\'а, нарушая §12 hard rule — §12 неотменяем, экономия его не override\'ит' }]
  ),
  ag_pest: nd(
    'Диагностирует падения Pest --parallel: классифицирует как реальный баг или один из 5 квирков (72-73-77...).',
    'При падении Pest --parallel ИЛИ при subdir-only flake (как в audit Phase 3 SyncSupplierProjectsJobTest).',
    'READ-ONLY на коде, не правит самостоятельно. Falsify каждую гипотезу через реальный запуск, не «вероятно квирк».',
    [{ name: 'CLAUDE.md §6', cond: 'описывает когда вызывать' }],
    [],
    [{ name: 'MCP: redis', cond: 'читает Redis для дебага quirk 72 (supplier:session race)' }]
  ),
  ag_rls: nd(
    'Проверяет RLS compliance в миграциях — 7-item чеклист с реальными запусками команд, READ-ONLY.',
    'При создании/правке миграции в db/migrations/ ИЛИ при правке db/schema.sql ИЛИ при PR review с DB-изменениями.',
    'READ-ONLY (только Read/Grep/Glob/Bash) — не пишет код. Не альтернатива sk_rls skill, у них разные сценарии.',
    [{ name: 'CLAUDE.md', cond: 'описывает в §6 и агенты/ директории' }],
    [],
    [{ name: 'MCP: laravel-boost', cond: 'SQL запросы к db/schema.sql' }],
    [{ name: 'rls-check скил', desc: 'оба покрывают RLS compliance, нет чёткой границы: агент — для PR/diff, скил — для ручной таблицы' }]
  ),
  ag_statusline: nd(
    'Настраивает строку состояния Claude Code через редактирование файла конфига.',
    'При запросе пользователя «настрой statusline» — редкая разовая задача.',
    'Правит только settings.json statusline-секцию, не другие части конфига.',
    [],
    [],
    []
  ),
  ag_guide: nd(
    'Отвечает на вопросы об API Claude Code, SDK, MCP серверах, хуках, slash commands.',
    'При вопросе про возможности Claude Code/SDK/API — «Can Claude...», «How do I...», «Does Claude...».',
    'READ-ONLY: ищет в документации, не правит код. Не для дебага кода — только для вопросов о platform.',
    [],
    [],
    [{ name: 'MCP: github', cond: 'ищет примеры в репозитории при необходимости' }]
  ),
  ag_explore: nd(
    'Быстрый поиск файлов по паттерну или символу — только чтение, без анализа.',
    'При targeted lookup — найти файл по имени или grep по символу/keyword.',
    'Не для open-ended exploration. Не для review/audit (читает excerpts, миссует content past read window).',
    [{ name: 'subagent-driven скил', cond: 'запускается для задач поиска' }],
    [],
    []
  ),
  ag_general: nd(
    'Универсальный агент для сложных multi-step исследований с полным доступом к инструментам.',
    'При сложных multi-step research / coding задачах, когда Explore недостаточно (нужен анализ, не только поиск).',
    'Полный доступ к инструментам — может писать код. Дороже Explore по токенам.',
    [{ name: 'subagent-driven скил', cond: 'запускается для основных задач' }],
    [],
    []
  ),
  ag_plan: nd(
    'Архитектор: разрабатывает планы реализации, выявляет критичные файлы, рассматривает trade-offs.',
    'При архитектурном планировании multi-component задачи (не для small refactor).',
    'READ-ONLY (no Edit/Write/NotebookEdit). Возвращает план, не реализует его.',
    [{ name: 'subagent-driven скил', cond: 'запускается для архитектурных задач' }],
    [],
    [{ name: 'writing-plans скил', cond: 'Plan агент и writing-plans решают похожую задачу для разных контекстов' }]
  ),
  ag_hookify: nd(
    'Анализирует транскрипты разговоров для поиска поведений, которые стоит предотвратить хуком.',
    'При триггере /hookify без аргументов ИЛИ при запросе «look back at this conversation, what mistakes to prevent».',
    'READ-ONLY (Read+Grep only). Рекомендует хуки, не создаёт их сам — передаёт в hookify plugin.',
    [],
    [],
    [{ name: 'hookify (плагин)', cond: 'агент передаёт рекомендации в плагин' }]
  ),
  ag_pcreator: nd(
    'Создаёт конфигурацию новых агентов по описанию пользователя.',
    'При запросе «create an agent that...» — генерация agent.md по описанию функциональности.',
    'Только Write/Read tools. Не валидирует созданного агента — передаёт в plugin-validator.',
    [],
    [],
    [{ name: 'plugin-dev:plugin-validator', cond: 'validator проверяет созданного агента' }]
  ),
  ag_pvalid: nd(
    'Проверяет структуру плагина на корректность — plugin.json, tools, manifest.',
    'После создания/изменения plugin.json или plugin component\'ов — proactive validation.',
    'READ-ONLY (Read/Grep/Glob/Bash). Сам не правит — даёт список нарушений.',
    [],
    [],
    [{ name: 'plugin-dev:agent-creator', cond: 'validator и creator работают в паре' }]
  ),
  ag_skreview: nd(
    'Оценивает качество написанного скила — описание, workflow, примеры, best practices.',
    'После создания/изменения скила через writing-skills — proactive review.',
    'READ-ONLY (Read/Grep/Glob). Не правит — выдаёт рекомендации.',
    [],
    [],
    [{ name: 'writing-skills скил', cond: 'skill-reviewer проверяет то, что writing-skills создал' }]
  ),
```

- [ ] **Step 2: Visual verification**

Refresh in Edge. Click hooks (SessionStart, PreToolUse:CLAUDE.md-warn, UserPromptSubmit:economy-mode), agents (pest-parallel-debugger, rls-reviewer, Explore). Expected: 2 новых секции заполнены для всех 16 узлов.

- [ ] **Step 3: Commit**

```bash
git add docs/automation-graph.html
git commit -m "feat(graph): when+limits content for hooks (5) + agents (11)"
```

---

### Task 5d: Fill when+limits for MCP servers (7 nodes)

**Files:**

- Modify: `docs/automation-graph.html` — entries `mcp_pw` через `mcp_21st`

- [ ] **Step 1: Replace 7 MCP entries**

```js
  mcp_pw: nd(
    'Управляет браузером — снимает скриншоты, кликает, заполняет формы для smoke и a11y тестов.',
    'При визуальной проверке прототипов (фаза 0), при a11y smoke (axe-core), при UI integration smoke.',
    'Не для production users. На каждой сессии один shared browser — конфликты при parallel-work (см. квирк #2 в memory).',
    [{ name: 'CLAUDE.md §3.1 #2', cond: 'активен с фазы 0' }],
    [],
    [{ name: 'SessionStart хук', cond: 'используется для визуальной проверки прототипов' }]
  ),
  mcp_gh: nd(
    'GitHub API — читает/создаёт PR, issues, коммиты, branches в репозитории CoralMinister/lidpotok.',
    'При работе с PR/issues, при поиске в репозитории, при создании PR через finishing-pr skill.',
    'Не push на main без явного одобрения. Pravila §4: атомарные коммиты, не batch\'ить через MCP.',
    [{ name: 'CLAUDE.md §3.1 #3', cond: 'активен с фазы 0' }],
    [],
    [{ name: 'finishing-pr скил', cond: 'создаёт PR через этот MCP' }]
  ),
  mcp_boost: nd(
    'Laravel Boost — SQL запросы к dev-БД, схема таблиц, логи ошибок, поиск в docs Laravel.',
    'Фаза 1+: при SQL запросах, при поиске в Laravel docs, при работе с Eloquent моделями.',
    '**READ-ONLY к prod** — `.env.production` не должен попадать в локальный конфиг. Не использовать Inertia/Livewire/Tailwind/Filament guidelines.',
    [{ name: 'CLAUDE.md §3.2 #10', cond: 'активен с фазы 1, READ-ONLY к prod запрещено' }],
    [],
    [{ name: 'ag_rls агент', cond: 'rls-reviewer использует Boost для SQL' }, { name: 'sk_rls скил', cond: 'rls-check использует Boost' }]
  ),
  mcp_semgrep: nd(
    'SAST анализ кода — ищет уязвимости, XSS, SQLi, нарушения правил по паттернам.',
    'Фаза 3 pre-production: при code review (sk_coderev), при CI перед релизом.',
    'Конфигурация в .semgrep.yml. False-positive\'ы документируются inline.',
    [{ name: 'CLAUDE.md §3.4 #25', cond: 'фаза 3 pre-production' }],
    [],
    [{ name: 'code-review скил', cond: 'Semgrep MCP используется при code review' }]
  ),
  mcp_sentry: nd(
    'Читает ошибки из self-hosted Sentry в Yandex Cloud — события, стектрейсы, метрики. READ-ONLY.',
    'При расследовании production runtime ошибок (после deployment Б-1).',
    '**READ-ONLY** scope (org:read/project:read/event:read). Pending Sentry instance deployment Б-1 (зависит от ООО registration).',
    [{ name: 'CLAUDE.md §3.3 #34', cond: 'off-phase debug-runtime; pending Sentry deployment Б-1' }],
    [],
    []
  ),
  mcp_redis: nd(
    'Читает Redis/Memurai — ключи, очереди, кэш для дебага race conditions. СТРОГО READ-ONLY.',
    'При дебаге Redis-очередей (Pest --parallel quirk 72), при анализе кэш-инвалидации.',
    '**СТРОГО READ-ONLY** — никаких DEL/FLUSHDB/SET/LPUSH из Claude (только GET/KEYS/LIST). Deprecated Anthropic source — миграция post-MVP.',
    [{ name: 'CLAUDE.md §3.3 #35', cond: 'off-phase debug-runtime; PSR_v1 R10.1 блок 3' }],
    [],
    [{ name: 'pest-parallel-debugger агент', cond: 'агент использует для quirk 72 (Redis race)' }]
  ),
  mcp_21st: nd(
    'Генератор стартовых шаблонов UI-компонентов через LLM. Активация только через R14.4 pipeline.',
    'Только через PSR_v1 R14.4 pipeline: pre-check 9 условий (брендовый App*? Vuetify-эквивалент? существующий компонент?).',
    'R14.5: не параллельно с FD/UPM. JSX→Vue, Tailwind→utility, shadcn→Vuetify обязательны. Pa11y a11y на deployable.',
    [{ name: 'PSR_v1 R14.4', cond: 'строгий pre-check: 9 условий перед активацией' }],
    [],
    [],
    [{ name: 'Frontend Design', desc: 'PSR_v1 R14.5: нельзя параллельно — 21st как генератор материала, FD как решатель; риск смешивания ролей и нарушения R6 фильтра стека' }]
  ),
```

- [ ] **Step 2: Visual verification**

Refresh. Click `MCP: laravel-boost`, `MCP: sentry`, `MCP: redis`. Expected: when/limits секции заполнены с READ-ONLY политиками.

- [ ] **Step 3: Commit**

```bash
git add docs/automation-graph.html
git commit -m "feat(graph): when+limits content for MCP servers (7)"
```

---

### Task 5e: Fill when+limits for Lefthook jobs (10 nodes)

**Files:**

- Modify: `docs/automation-graph.html` — entries `lh_gitleaks` через `lh_lychee`

- [ ] **Step 1: Replace 10 lefthook entries**

```js
  lh_gitleaks: nd(
    'pre-commit: ищет ПДн, токены и API-ключи в staged-файлах. Блокирует коммит при находке.',
    'pre-commit stage: на каждый `git commit` — сканирует только staged файлы.',
    'Bypass через `--no-verify` запрещён (Pravila §4.2). Находка = blocking error, не warning. Обновление словаря — `.gitleaksignore` для known-false-positives.',
    [{ name: 'lefthook.yml', cond: 'job #1 в pre-commit, parallel:false' }],
    [],
    [{ name: 'lefthook:gitleaks pre-push', cond: 'pre-push версия сканирует всю историю' }]
  ),
  lh_mdlint: nd(
    'pre-commit: проверяет и авто-исправляет стиль Markdown файлов перед коммитом.',
    'pre-commit stage: на каждый `git commit` со staged `.md` файлами.',
    'Конфиг `.markdownlint-cli2.cjs`. Auto-fix включён (stage_fixed: true) — fixed файлы повторно staged.',
    [{ name: 'lefthook.yml', cond: 'job #2 в pre-commit' }],
    [],
    [{ name: 'PostToolUse:markdownlint-fix хук', cond: 'оба делают одно — хук немедленно, lefthook при коммите' }]
  ),
  lh_cspell: nd(
    'pre-commit: проверяет орфографию в .md файлах по словарю cspell-words.txt.',
    'pre-commit stage: на каждый `git commit` со staged `.md` файлами.',
    'Словарь: `cspell-words.txt`. Кириллица в нижнем регистре. Не bypass\'ить — добавлять валидные слова в словарь.',
    [{ name: 'lefthook.yml', cond: 'job #3 в pre-commit' }],
    [],
    []
  ),
  lh_stylelint: nd(
    'pre-commit: линтует CSS в HTML-прототипах (web/v8/*.html).',
    'pre-commit stage: на staged `.html`/`.css` файлах.',
    'Stylelint конфиг в `.stylelintrc`. Deprecated keywords (`word-break: break-word`) блокируют commit.',
    [{ name: 'lefthook.yml', cond: 'job #4 в pre-commit' }],
    [],
    []
  ),
  lh_pint: nd(
    'pre-commit: авто-форматирует PHP код по PSR-стандарту, stage_fixed:true.',
    'pre-commit stage: на каждый staged `.php` файл (root: app/).',
    'Auto-fix включён — fixed файлы повторно staged. Конфиг `app/pint.json`.',
    [{ name: 'lefthook.yml', cond: 'job #5 в pre-commit, root:app/' }],
    [],
    []
  ),
  lh_larastan: nd(
    'pre-commit: статический анализ PHP — находит ошибки типов выше baseline (Larastan L9).',
    'pre-commit stage: на staged `.php` файлах (root: app/).',
    'Baseline `phpstan-baseline.neon` фиксирован — новые ошибки блокируют commit. Не bump\'ить baseline без обоснования.',
    [{ name: 'lefthook.yml', cond: 'job #6 в pre-commit, root:app/' }],
    [],
    [{ name: 'MCP: laravel-boost', cond: 'Boost даёт контекст типов через IDE stubs' }]
  ),
  lh_squawk: nd(
    'pre-commit: линтует SQL миграции на безопасные паттерны (lock-safe, concurrent, etc.).',
    'pre-commit stage: на staged `database/migrations/*.php` или `db/*.sql` файлах.',
    'Конфиг squawk.toml. Не разрешать UNSAFE миграции (`ALTER TABLE ADD COLUMN NOT NULL DEFAULT`) без явного `-- squawk-ignore`.',
    [{ name: 'lefthook.yml', cond: 'job #7 в pre-commit, glob:*.sql' }],
    [],
    [{ name: 'Tooling #15 squawk', cond: 'соответствует §3.2 entry' }]
  ),
  lh_eslint: nd(
    'pre-commit: линтует Vue/TypeScript файлы в app/resources/js/.',
    'pre-commit stage: на staged `.vue`/`.ts`/`.tsx` файлах (root: app/).',
    'Flat-config eslint 10 + plugin-vue 10. Не bypass\'ить через `--no-verify`. Errors blocking, warnings allowed.',
    [{ name: 'lefthook.yml', cond: 'job #8 в pre-commit, root:app/' }],
    [],
    []
  ),
  lh_gitleaks2: nd(
    'pre-push: полный скан всей истории коммитов на секреты (строже pre-commit job).',
    'pre-push stage: перед `git push` к remote — сканирует всю history новых коммитов.',
    'Bypass через `--no-verify` запрещён. На больших push (200+ commits) занимает 30+ сек.',
    [{ name: 'lefthook.yml', cond: 'job в pre-push' }],
    [],
    [{ name: 'lefthook:gitleaks', cond: 'pre-push версия строже pre-commit: проверяет всю историю' }]
  ),
  lh_lychee: nd(
    'pre-push: проверяет все ссылки в .md файлах на битые (docs/**/*.md, db/**/*.md, *.md).',
    'pre-push stage: перед `git push` — проверяет ссылки во всех `.md` файлах в монорепе.',
    'Внешние ссылки проверяются с timeout 10s; offline — fail. Конфиг `lychee.toml`. Не bypass\'ить через `--no-verify`.',
    [{ name: 'lefthook.yml', cond: 'job в pre-push' }],
    [],
    [{ name: 'CLAUDE.md', cond: 'проверяет ссылки в CLAUDE.md в том числе' }]
  ),
```

- [ ] **Step 2: Visual verification**

Refresh. Click `lefthook:gitleaks`, `lefthook:lychee-links`, `lefthook:stylelint`. Expected: 2 новых секции заполнены.

- [ ] **Step 3: Commit**

```bash
git add docs/automation-graph.html
git commit -m "feat(graph): when+limits content for lefthook jobs (10)"
```

---

### Task 5f: Fill when+limits for Memory files (15 nodes)

**Files:**

- Modify: `docs/automation-graph.html` — entries `mem_user` через `mem_devindices`

- [ ] **Step 1: Replace 15 memory entries**

```js
  mem_user: nd(
    'Профиль заказчика: Дмитрий, Windows Server 2022, VSCode, русский язык, путь к проекту.',
    'Читается при старте каждой сессии через SessionStart хук — для language/preferences.',
    'Не содержит секретов. При смене заказчика — переписать полностью.',
    [],
    [],
    [{ name: 'SessionStart хук', cond: 'читается при старте каждой сессии' }]
  ),
  mem_comm: nd(
    'Стиль общения: короткие команды («а», «б», «делай»), варианты A/B/C, фиксация переоткрытий.',
    'Читается при работе с пользователем — для соответствия стилю общения.',
    'Не путь к code, а meta-rules коммуникации. Корректировки через явное feedback от заказчика.',
    [],
    [],
    [{ name: 'memory:user_profile', cond: 'дополняет профиль пользователя' }]
  ),
  mem_env: nd(
    '80+ квирков окружения: специфика Windows Server, баги инструментов, митигации.',
    'При unexpected behavior — сначала проверка memory_env на известный квирк.',
    'Не дубль — добавлять только новые квирки. Quirk count актуализируется в `project_state.md`.',
    [],
    [],
    [
      { name: 'pest-parallel-debugger агент', cond: 'квирки 72/77 используются агентом' },
      { name: 'SessionStart хук', cond: 'читается при старте' }
    ]
  ),
  mem_sp: nd(
    'Hard rule §12 + economy hook architecture из 6 компонентов — дисциплина инвокации скилов.',
    'При работе со скилами — для соответствия §12 hard rule.',
    'Описывает архитектуру economy hook (6 компонентов) — менять только при изменении самого хука.',
    [],
    [],
    [{ name: 'economy-mode хук', cond: 'связаны: memory описывает архитектуру хука' }]
  ),
  mem_plugins: nd(
    'Правила парного стека плагинов, debug-runtime MCP, tier-структура PSR_v1.',
    'При работе с плагинами FD/UPM/21st/Sentry/Redis MCP — для tier-разделения.',
    'Синхронизируется с PSR_v1 — изменения в memory только если изменился сам PSR_v1.',
    [],
    [],
    [{ name: 'PSR_v1', cond: 'memory отражает актуальные версии PSR_v1' }]
  ),
  mem_state: nd(
    'Текущее состояние: ветка, тесты (Pest/Vitest), последние коммиты, активные задачи.',
    'Читается при старте сессии — для быстрого контекста; обновляется после крупных вех.',
    'Может становиться stale — re-Read при сомнении. Не доверять данным более 2-3 дней без verify.',
    [],
    [],
    [{ name: 'SessionStart хук', cond: 'читается при старте для быстрого контекста' }]
  ),
  mem_phase1: nd(
    'Стратегия фазы 1: native Windows стек без Docker, pg_partman заменён Artisan cron.',
    'При работе с infrastructure фазы 1 (PG/Redis/PHP-CLI на Windows native).',
    'Стратегия фиксированная до закрытия Б-1 (Managed PG в YC) или 6 месяцев — переоценка указана в файле.',
    [],
    [],
    []
  ),
  mem_archive: nd(
    'Карта источников истины: версии всех 13+ ключевых документов проекта.',
    'При вопросах «какая версия документа X» или «где источник истины для Y».',
    'Синхронизируется с CLAUDE.md §0. Изменения в memory только если изменился CLAUDE.md §0.',
    [],
    [],
    [{ name: 'CLAUDE.md', cond: 'memory синхронизирует версии с CLAUDE.md §0' }]
  ),
  mem_github: nd(
    'GitHub репозиторий CoralMinister/lidpotok: HEAD, hooks, правила push.',
    'При работе с GitHub — push, PR, branch operations.',
    'Не push --force на main (warning в Pravila). Pre-push hooks обязательны.',
    [],
    [],
    [{ name: 'MCP: github', cond: 'MCP и memory дополняют друг друга для работы с GitHub' }]
  ),
  mem_handoff: nd(
    'Дизайн-handoff Платона: что из liderra_v8_handoff/ используем, что нет.',
    'При UI/дизайн задачах — для разделения «брендбук используем» vs «состав фич — по ТЗ v8.5».',
    'Handoff — только дизайн/токены/компоненты. Функционал, состав экранов — НЕ из handoff (берём из ТЗ).',
    [],
    [],
    []
  ),
  mem_audit: nd(
    'Полный аудит портала 13.05.2026: 38 находок, вердикт 🟡, 10 Q-items закрыты.',
    'При вопросах про аудит или его последствия (Q.DEFER, P0/P1/P2 распределение).',
    'Снимок состояния — не редактировать при последующих аудитах, создавать новые memory-файлы.',
    [],
    [],
    []
  ),
  mem_supplier: nd(
    'Прогресс интеграции поставщика Plans 1-5: Tasks, коммиты, блокеры.',
    'При работе с supplier integration (Plans 1-5) — для текущего state и blockers.',
    'Блокеры (Б-1, credentials) — внешние, не разрешаются Claude\'ом. Только трекинг.',
    [],
    [],
    []
  ),
  mem_brain: nd(
    'Claude Brain v1.0 — extracted brain repo, тег brain-v1.0, install.sh.',
    'При работе с brain repository или install.sh для consumer sync.',
    'GitHub push на brain repo blocked (8.2). Не пытаться push без разрешения.',
    [],
    [],
    []
  ),
  mem_redesign: nd(
    'Редизайн Quiet Luxury: 20 коммитов, foundation CSS + composables + AppSidebar rewrite.',
    'При работе с portal redesign (frontend, AppSidebar, foundation CSS).',
    'I2 backlog в spec §15 — 10 пунктов отложены до I2. Не реализовывать I2 пункты без явного запроса.',
    [],
    [],
    []
  ),
  mem_devindices: nd(
    'Dev Element Indices — временная feedback-фича для разработки; к удалению в продакшене.',
    'При работе с dev feedback (e.g. «1030 измени цвет») — для понимания number → element mapping.',
    '**TEMPORARY** — заказчик прямо сказал «уберём в конечном релизе». Не вкладываться в долгосрочную инфраструктуру.',
    [],
    [],
    []
  ),
```

- [ ] **Step 2: Visual verification**

Refresh. Click `memory:project_state`, `memory:audit_2026-05-13`, `memory:dev_indices`. Expected: 2 новых секции заполнены для всех 15 memory узлов. Все 73 узла теперь имеют when+limits контент.

- [ ] **Step 3: Commit**

```bash
git add docs/automation-graph.html
git commit -m "feat(graph): when+limits content for memory files (15) — all 73 nodes done"
```

---

### Task 6: Radial-sector positioning

**Files:**

- Modify: `docs/automation-graph.html` — `NODES[]` массив (73 декларации) + новый `positionNode()` helper + цикл applyRadial()

**Spec ref:** Section 3 (Радиально-секторная иерархия).

This task adds `ring` and `sectorAngle` fields to each of 73 nodes and creates an apply-position helper. Sector assignment follows the functional grouping table from spec.

- [ ] **Step 1: Add positionNode() helper before NODES declaration**

Locate the comment `// SECTION 1: NODES`. **Before** the `const NODES = [` line, insert the helper:

```js
// ════════════════════════════════════════════════════
// SECTION 1: NODES
// ════════════════════════════════════════════════════

// Радиально-секторная компоновка.
// Сектора (по 90°): N=workflow (0–90), E=UI (90–180), S=infra (180–270), W=data/RLS (270–360).
const RADII = [0, 220, 400, 600, 800, 1000, 1180];
function pos(ring, angleDeg) {
  const r = RADII[ring];
  const a = angleDeg * Math.PI / 180;
  return { x: Math.round(r * Math.cos(a)), y: Math.round(r * Math.sin(a)) };
}
```

- [ ] **Step 2: Rewrite NODES array with ring + sectorAngle + (x,y) on each node**

Replace **the entire `const NODES = [...]` array** (4+5+14+2+5+11+7+10+15 = 73 entries) with the version below. Each entry receives `ring` (0–6) and `sectorAngle` (degrees). x/y computed by `pos()` and spread inline.

```js
const NODES = [
  // ── ПРАВИЛА (4) ── центр + первое кольцо ───────
  { id: 'pravila',      label: 'Pravila v1.13',        group: 'rules', size: 38, ring: 0, ...pos(0, 0)    },
  { id: 'claude_md',    label: 'CLAUDE.md v1.92',       group: 'rules', size: 34, ring: 1, ...pos(1, 30)   },
  { id: 'psr_v1',       label: 'PSR_v1 v2.1',           group: 'rules', size: 32, ring: 1, ...pos(1, 150)  },
  { id: 'tooling',      label: 'Tooling v1.17',         group: 'rules', size: 30, ring: 1, ...pos(1, 270)  },

  // ── ПЛАГИНЫ (5) ── второе кольцо ───────────────
  { id: 'superpowers',    label: 'Superpowers v5.1',    group: 'plugins', size: 30, ring: 2, ...pos(2, 45)   },  // N sector
  { id: 'fd_plugin',      label: 'Frontend Design',      group: 'plugins', size: 26, ring: 2, ...pos(2, 135)  },  // E sector
  { id: 'upm',            label: 'UI UX Pro Max',        group: 'plugins', size: 22, ring: 2, ...pos(2, 165)  },  // E sector
  { id: 'claude_md_mgmt', label: 'claude-md-mgmt',       group: 'plugins', size: 22, ring: 2, ...pos(2, 225)  },  // S sector
  { id: 'hookify_plugin', label: 'hookify (плагин)',     group: 'plugins', size: 22, ring: 2, ...pos(2, 200)  },  // S sector

  // ── СКИЛЫ SUPERPOWERS (14) — N sector (0–90) ────
  { id: 'sk_brainstorm',  label: 'brainstorming',        group: 'skills_sp', size: 18, ring: 3, ...pos(3, 5)   },
  { id: 'sk_wplans',      label: 'writing-plans',        group: 'skills_sp', size: 20, ring: 3, ...pos(3, 11)  },
  { id: 'sk_eplans',      label: 'executing-plans',      group: 'skills_sp', size: 18, ring: 3, ...pos(3, 17)  },
  { id: 'sk_subagent',    label: 'subagent-driven',      group: 'skills_sp', size: 20, ring: 3, ...pos(3, 23)  },
  { id: 'sk_tdd',         label: 'TDD',                  group: 'skills_sp', size: 18, ring: 3, ...pos(3, 29)  },
  { id: 'sk_verify',      label: 'verification-before-completion', group: 'skills_sp', size: 18, ring: 3, ...pos(3, 36) },
  { id: 'sk_debug',       label: 'systematic-debugging', group: 'skills_sp', size: 18, ring: 3, ...pos(3, 43)  },
  { id: 'sk_parallel',    label: 'parallel-work',        group: 'skills_sp', size: 18, ring: 3, ...pos(3, 50)  },
  { id: 'sk_worktree',    label: 'worktree',             group: 'skills_sp', size: 18, ring: 3, ...pos(3, 57)  },
  { id: 'sk_pr',          label: 'finishing-pr',         group: 'skills_sp', size: 18, ring: 3, ...pos(3, 64)  },
  { id: 'sk_coderev',     label: 'code-review',          group: 'skills_sp', size: 16, ring: 3, ...pos(3, 71)  },
  { id: 'sk_spreview',    label: 'spec-review',          group: 'skills_sp', size: 16, ring: 3, ...pos(3, 78)  },
  { id: 'sk_wskills',     label: 'writing-skills',       group: 'skills_sp', size: 16, ring: 3, ...pos(3, 85)  },
  { id: 'sk_elements',    label: 'elements-of-style',    group: 'skills_sp', size: 16, ring: 3, ...pos(3, 92)  },  // crosses to E

  // ── СКИЛЫ ПРОЕКТА (2) — W sector (RLS) ─────────
  { id: 'sk_rls',         label: 'rls-check',            group: 'skills_proj', size: 20, ring: 3, ...pos(3, 305) },  // W (RLS)
  { id: 'sk_qitem',       label: 'q-item-add',           group: 'skills_proj', size: 20, ring: 3, ...pos(3, 220) },  // S (infra-like)

  // ── ХУКИ (5) — S+infra ────────────────────────
  { id: 'hk_session',     label: 'SessionStart:\ncontext-inject', group: 'hooks', size: 24, ring: 4, ...pos(4, 100) },  // E (boundary with workflow)
  { id: 'hk_economy',     label: 'UserPromptSubmit:\neconomy-mode', group: 'hooks', size: 22, ring: 4, ...pos(4, 95) },
  { id: 'hk_pre_claude',  label: 'PreToolUse:\nCLAUDE.md-warn',   group: 'hooks', size: 22, ring: 4, ...pos(4, 215) },  // S (infra)
  { id: 'hk_post_md',     label: 'PostToolUse:\nmarkdownlint',    group: 'hooks', size: 20, ring: 4, ...pos(4, 195) },
  { id: 'hk_post_schema', label: 'PostToolUse:\nschema-changelog',group: 'hooks', size: 20, ring: 4, ...pos(4, 300) },  // W (data)

  // ── АГЕНТЫ (11) — N (workflow) + W (RLS) ──────
  { id: 'ag_explore',     label: 'Explore',                group: 'agents', size: 20, ring: 4, ...pos(4, 10)  },
  { id: 'ag_general',     label: 'general-purpose',        group: 'agents', size: 20, ring: 4, ...pos(4, 25)  },
  { id: 'ag_plan',        label: 'Plan',                   group: 'agents', size: 20, ring: 4, ...pos(4, 40)  },
  { id: 'ag_pest',        label: 'pest-parallel-debugger', group: 'agents', size: 24, ring: 4, ...pos(4, 55)  },
  { id: 'ag_guide',       label: 'claude-code-guide',      group: 'agents', size: 18, ring: 4, ...pos(4, 70)  },
  { id: 'ag_statusline',  label: 'statusline-setup',       group: 'agents', size: 18, ring: 4, ...pos(4, 85)  },
  { id: 'ag_hookify',     label: 'hookify:\nconversation-analyzer', group: 'agents', size: 18, ring: 4, ...pos(4, 230) },  // S
  { id: 'ag_pcreator',    label: 'plugin-dev:\nagent-creator',  group: 'agents', size: 16, ring: 4, ...pos(4, 245) },  // S
  { id: 'ag_pvalid',      label: 'plugin-dev:\nplugin-validator',group: 'agents', size: 16, ring: 4, ...pos(4, 260) },  // S
  { id: 'ag_skreview',    label: 'plugin-dev:\nskill-reviewer',  group: 'agents', size: 16, ring: 4, ...pos(4, 275) },  // W
  { id: 'ag_rls',         label: 'rls-reviewer',           group: 'agents', size: 22, ring: 4, ...pos(4, 315) },  // W (RLS)

  // ── MCP-СЕРВЕРЫ (7) — E (UI) + W (data) ───────
  { id: 'mcp_21st',       label: 'MCP: 21st.dev Magic',   group: 'mcp', size: 20, ring: 5, ...pos(5, 130) },  // E
  { id: 'mcp_pw',         label: 'MCP: playwright',       group: 'mcp', size: 22, ring: 5, ...pos(5, 110) },
  { id: 'mcp_gh',         label: 'MCP: github',           group: 'mcp', size: 22, ring: 5, ...pos(5, 75)  },  // N (workflow)
  { id: 'mcp_boost',      label: 'MCP: laravel-boost',    group: 'mcp', size: 24, ring: 5, ...pos(5, 290) },  // W
  { id: 'mcp_redis',      label: 'MCP: redis',            group: 'mcp', size: 22, ring: 5, ...pos(5, 310) },  // W
  { id: 'mcp_sentry',     label: 'MCP: sentry',           group: 'mcp', size: 22, ring: 5, ...pos(5, 330) },  // W
  { id: 'mcp_semgrep',    label: 'MCP: semgrep',          group: 'mcp', size: 20, ring: 5, ...pos(5, 350) },  // W

  // ── LEFTHOOK JOBS (10) — S+W (infra/data) ─────
  { id: 'lh_mdlint',      label: 'lefthook:\nmarkdownlint',   group: 'lefthook', size: 18, ring: 5, ...pos(5, 185) },  // S
  { id: 'lh_cspell',      label: 'lefthook:\ncspell',         group: 'lefthook', size: 18, ring: 5, ...pos(5, 200) },
  { id: 'lh_stylelint',   label: 'lefthook:\nstylelint',      group: 'lefthook', size: 16, ring: 5, ...pos(5, 215) },
  { id: 'lh_eslint',      label: 'lefthook:\neslint-vue',     group: 'lefthook', size: 18, ring: 5, ...pos(5, 230) },
  { id: 'lh_lychee',      label: 'lefthook:\nlychee-links',   group: 'lefthook', size: 18, ring: 5, ...pos(5, 245) },
  { id: 'lh_gitleaks',    label: 'lefthook:\ngitleaks',       group: 'lefthook', size: 18, ring: 5, ...pos(5, 260) },
  { id: 'lh_gitleaks2',   label: 'lefthook:\ngitleaks pre-push', group: 'lefthook', size: 18, ring: 5, ...pos(5, 275) },  // S/W boundary
  { id: 'lh_pint',        label: 'lefthook:\npint',           group: 'lefthook', size: 18, ring: 5, ...pos(5, 25)  },  // N (workflow)
  { id: 'lh_larastan',    label: 'lefthook:\nlarastan',       group: 'lefthook', size: 18, ring: 5, ...pos(5, 50)  },  // N
  { id: 'lh_squawk',      label: 'lefthook:\nsquawk',         group: 'lefthook', size: 18, ring: 5, ...pos(5, 320) },  // W (data)

  // ── MEMORY FILES (15) — distributed на внешнем кольце
  { id: 'mem_user',       label: 'memory:\nuser_profile',        group: 'memory', size: 16, ring: 6, ...pos(6, 0)   },
  { id: 'mem_comm',       label: 'memory:\nfeedback_comm',       group: 'memory', size: 14, ring: 6, ...pos(6, 24)  },
  { id: 'mem_env',        label: 'memory:\nfeedback_env',        group: 'memory', size: 16, ring: 6, ...pos(6, 48)  },
  { id: 'mem_sp',         label: 'memory:\nfeedback_superpowers',group: 'memory', size: 16, ring: 6, ...pos(6, 72)  },
  { id: 'mem_plugins',    label: 'memory:\nfeedback_plugins',    group: 'memory', size: 16, ring: 6, ...pos(6, 96)  },
  { id: 'mem_handoff',    label: 'memory:\nreference_handoff',   group: 'memory', size: 14, ring: 6, ...pos(6, 120) },
  { id: 'mem_redesign',   label: 'memory:\nportal_redesign',     group: 'memory', size: 14, ring: 6, ...pos(6, 144) },
  { id: 'mem_devindices', label: 'memory:\ndev_indices',         group: 'memory', size: 12, ring: 6, ...pos(6, 168) },
  { id: 'mem_phase1',     label: 'memory:\nphase1_strategy',     group: 'memory', size: 14, ring: 6, ...pos(6, 192) },
  { id: 'mem_state',      label: 'memory:\nproject_state',       group: 'memory', size: 16, ring: 6, ...pos(6, 216) },
  { id: 'mem_brain',      label: 'memory:\nclaude_brain',        group: 'memory', size: 14, ring: 6, ...pos(6, 240) },
  { id: 'mem_supplier',   label: 'memory:\nsupplier_integration',group: 'memory', size: 14, ring: 6, ...pos(6, 264) },
  { id: 'mem_audit',      label: 'memory:\naudit_2026-05-13',    group: 'memory', size: 14, ring: 6, ...pos(6, 288) },
  { id: 'mem_archive',    label: 'memory:\nreference_archive',   group: 'memory', size: 14, ring: 6, ...pos(6, 312) },
  { id: 'mem_github',     label: 'memory:\nreference_github',    group: 'memory', size: 14, ring: 6, ...pos(6, 336) },
];
```

- [ ] **Step 3: Visual verification**

Refresh in Edge. Expected:

- Pravila в центре.
- 3 узла на первом кольце вокруг Pravila.
- 5 плагинов на втором кольце.
- 14+2 skills на третьем (workflow в N-секторе).
- Узлы группируются по секторам — RLS-related (`sk_rls`, `ag_rls`, `mcp_boost`, `mcp_redis`, `mcp_sentry`, `lh_squawk`) в W-секторе (лево-верх).
- Граф больше не «free-floating» — иерархия от центра наружу видна.
- Физика всё ещё включена (Task 7 это изменит) — узлы могут «дрейфовать», но начинают с radial позиций.

- [ ] **Step 4: Commit**

```bash
git add docs/automation-graph.html
git commit -m "feat(graph): radial-sector layout — 6 колец × 4 сектора (workflow/UI/infra/data)"
```

---

### Task 7: physics off + button handlers + smooth: continuous

**Files:**

- Modify: `docs/automation-graph.html` — vis.Network options + Section 6 (TOOLBAR) кнопочные обработчики

**Spec ref:** Section 3 (vis.js options + Поведение кнопок).

- [ ] **Step 1: Disable physics by default + simplify edges**

Locate the `physics` block in `new vis.Network(...)`:

```js
physics: {
  enabled: true,
  solver: 'barnesHut',
  barnesHut: { ... },
  stabilization: { enabled: true, iterations: 300, fit: true },
},
```

Replace with:

```js
physics: {
  enabled: false,
},
```

Also update edges section (if not already done in Task 2):

```js
edges: {
  smooth: { type: 'continuous', roundness: 0.5 },
  selectionWidth: 2,
},
```

- [ ] **Step 2: Update stabilizationIterationsDone callback**

Since physics is off by default, stabilization won't fire. Replace:

```js
network.once('stabilizationIterationsDone', () => {
  network.fit({ animation: { duration: 800, easingFunction: 'easeInOutQuad' } });
});
```

with immediate fit on `afterDrawing`:

```js
network.once('afterDrawing', () => {
  network.fit({ animation: { duration: 600, easingFunction: 'easeInOutQuad' } });
});
```

- [ ] **Step 3: Update button handlers in SECTION 6**

Locate the toolbar handlers. Replace:

```js
document.getElementById('btn-freeze').addEventListener('click', () => {
  network.setOptions({ physics: { enabled: false } });
});

document.getElementById('btn-unfreeze').addEventListener('click', () => {
  network.setOptions({ physics: { enabled: true } });
});

document.getElementById('btn-reset').addEventListener('click', () => {
  network.fit({ animation: { duration: 600, easingFunction: 'easeInOutQuad' } });
});
```

with:

```js
document.getElementById('btn-freeze').addEventListener('click', () => {
  network.setOptions({ physics: { enabled: false } });
});

document.getElementById('btn-unfreeze').addEventListener('click', () => {
  network.setOptions({
    physics: {
      enabled: true,
      solver: 'forceAtlas2Based',
      forceAtlas2Based: {
        gravitationalConstant: -50,
        centralGravity: 0.0,
        springLength: 100,
        springConstant: 0.02,
        damping: 0.6,
        avoidOverlap: 0.4,
      },
    },
  });
});

document.getElementById('btn-reset').addEventListener('click', () => {
  // (a) Restore radial positions
  nodesDS.update(NODES.map(n => ({ id: n.id, x: n.x, y: n.y })));
  // (b) Refit camera
  network.fit({ animation: { duration: 600, easingFunction: 'easeInOutQuad' } });
});
```

- [ ] **Step 4: Visual verification**

Refresh in Edge. Expected:

- Граф появляется сразу в radial layout, без stabilization анимации.
- Узлы стоят детерминированно по своим (x,y) позициям.
- Нажми «Расшевелить» — узлы начинают слегка двигаться (мягкая физика).
- Нажми «Зафиксировать» — узлы замирают.
- Передвинь узел мышкой — он остаётся в новой позиции (физика off).
- Нажми «Сбросить вид» — узлы возвращаются в radial позиции + камера центрируется.
- Edges идут плавными дугами (smooth continuous), не пересекают узлы агрессивно.

- [ ] **Step 5: Commit**

```bash
git add docs/automation-graph.html
git commit -m "feat(graph): physics off by default + buttons restore radial layout + smooth continuous"
```

---

### Task 8: Final smoke test + summary commit

**Files:**

- No new modifications (smoke test only)

- [ ] **Step 1: Full visual smoke**

Open `docs/automation-graph.html` in Edge. F12 → Console. Verify:

1. **No JS errors in console** — clean output.
2. **No canvas rendering artifacts** — фон ровный (`#1e1e2e`).
3. **Радиальная компоновка** — Pravila в центре, 3 правила вокруг, 5 плагинов на втором кольце, и т.д.
4. **Нет подписей на edges** — только узлы + стрелки + 6 красных ⚡ маркеров.
5. **Hover на edge** → tooltip с описанием связи.
6. **Click on node** → правая панель: 7 секций (что делает / **когда** / **ограничения** / подчиняется / подчиняется ему / одновременно / конфликты).
7. **Все 73 узла имеют контент когда/ограничения** — нет `—` для основной секции (проверь 6 случайных узлов в разных группах).
8. **Toolbar:**
   - Поиск `econ` → находит UserPromptSubmit:economy-mode, остальные dim'нуты.
   - «Зафиксировать» / «Расшевелить» работают.
   - «Сбросить вид» возвращает radial + центрирует камеру.
   - «Снять выделение» закрывает легенду + clears search.
9. **6 красных конфликтов** видны и кликабельны (для tooltip).

- [ ] **Step 2: Data integrity check via node**

Run validation script:

```bash
cd "c:/моя/проекты/портал crm/Документация" && node -e "
const fs = require('fs');
const html = fs.readFileSync('docs/automation-graph.html', 'utf8');
const start = html.indexOf('<script>') + 8;
const end = html.lastIndexOf('</script>');
let script = html.slice(start, end);
const cutAt = script.indexOf('// SECTION 4: VIS INIT');
script = script.slice(0, cutAt);
const wrapped = '(function(){\n' + script + '\nreturn {NODES,EDGES,NODE_DETAILS};\n})()';
fs.writeFileSync('docs/check_tmp.js', 'const r = ' + wrapped + ';\nconsole.log(\"NODES:\", r.NODES.length);\nconsole.log(\"EDGES:\", r.EDGES.length);\nconsole.log(\"NODE_DETAILS:\", Object.keys(r.NODE_DETAILS).length);\nconst missing = r.NODES.filter(n => !r.NODE_DETAILS[n.id]).map(n => n.id);\nconsole.log(\"Missing details:\", missing.length ? missing.join(\",\") : \"NONE\");\nconst noRing = r.NODES.filter(n => n.ring === undefined).map(n => n.id);\nconsole.log(\"No ring:\", noRing.length ? noRing.join(\",\") : \"NONE\");\nconst noWhen = r.NODES.filter(n => !(r.NODE_DETAILS[n.id] && r.NODE_DETAILS[n.id].when)).map(n => n.id);\nconsole.log(\"No when:\", noWhen.length ? noWhen.join(\",\") : \"NONE\");\nconst conflicts = r.EDGES.filter(e => e.dashes);\nconsole.log(\"Conflict edges:\", conflicts.length);\n');
" && node docs/check_tmp.js && rm docs/check_tmp.js
```

Expected output:

```
NODES: 73
EDGES: 72
NODE_DETAILS: 73
Missing details: NONE
No ring: NONE
No when: NONE
Conflict edges: 6
```

If any value differs — fix before proceeding.

- [ ] **Step 3: No additional commit (all changes already committed in Tasks 1-7)**

This task is verification-only. Если все 8 пунктов visual smoke + 6 строк data integrity check соответствуют ожиданиям — задача завершена.

---

## Self-Review

**1. Spec coverage check:**

- ✅ Spec §1 (Rendering artifacts фона) → Task 1
- ✅ Spec §2 (Подписи рёбер → tooltips) → Task 2
- ✅ Spec §3 (Радиально-секторная иерархия) → Tasks 6 + 7
- ✅ Spec §4 (Когда + ограничения секции) → Tasks 3 + 4 (HTML/render) + Tasks 5a-5f (content)
- ✅ Spec «Финальная структура legend panel 7 секций» → Task 3 (HTML order)
- ✅ Spec «Поведение кнопок» → Task 7

**2. Placeholder scan:** Нет TBD/TODO. Все 73 узла имеют полный контент по when/limits (Tasks 5a-5f). Все 73 узла имеют ring + sectorAngle (Task 6).

**3. Type consistency:**

- `nd()` signature: 7 args (desc, when, limits, reportsTo, manages, together, conflicts). Backward-compat handler в Task 4. Все 73 entries (Tasks 5a-5f) — в новой 7-arg форме.
- `pos(ring, angleDeg)` consistent в Task 6.
- `physics.enabled = false` consistent в Task 7 (initial state + после «Зафиксировать»).
- IDs `pravila`, `claude_md`, etc. — unchanged from base commit.
- 73 NODES count preserved across all tasks.

**4. Backward-compat в Task 4 `nd()`:** до Tasks 5a-5f часть NODE_DETAILS будут 5-arg форме — handler ловит `Array.isArray(when)` и сдвигает аргументы. Это работающее промежуточное состояние.

---

## Execution Handoff

Plan complete and saved to `docs/superpowers/plans/2026-05-14-automation-graph-refactor.md`. Two execution options:

**1. Subagent-Driven (recommended)** — fresh subagent per task + two-stage review между задачами.

**2. Inline Execution** — sequential execution в этой сессии через executing-plans.

**Which approach?**
