Files
portal/docs/superpowers/plans/2026-05-13-automation-graph.md
T

68 KiB
Raw Blame History

Automation Graph 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: Create docs/automation-graph.html — a self-contained interactive force-directed graph of all 74 automation components of Лидерра CRM with conflict highlighting and a click-driven legend panel.

Architecture: Single HTML file, no build step. vis.js 9.1.9 from CDN handles force physics, node/edge rendering, zoom/pan/drag. Node data, edge data, and legend details are plain JS constants. Layout: toolbar top, graph canvas 78% width, legend panel 22% width (hidden until node click), category legend footer.

Tech Stack: vis.js 9.1.9 (CDN), vanilla JS ES6, CSS3 Solarized Dark theme.


File Map

File Action Purpose
docs/automation-graph.html Create The entire deliverable — ~850 lines

Task 1: HTML Shell + Solarized Styles

Files:

  • Create: docs/automation-graph.html

  • Step 1: Create the file with complete HTML structure and all CSS

Write this complete file content (data constants will be filled in Tasks 24):

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Система автоматизации Лидерры</title>
  <script src="https://unpkg.com/vis-network@9.1.9/standalone/umd/vis-network.min.js"></script>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { background: #0d0d1a; color: #fdf6e3; font-family: 'Segoe UI', system-ui, sans-serif; height: 100vh; display: flex; flex-direction: column; overflow: hidden; }

    /* ── Toolbar ── */
    #toolbar { background: #073642; border-bottom: 1px solid #586e75; padding: 8px 12px; display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
    #toolbar h1 { font-size: 14px; color: #93a1a1; white-space: nowrap; margin-right: 6px; }
    #search { background: #002b36; border: 1px solid #586e75; color: #fdf6e3; border-radius: 5px; padding: 5px 10px; font-size: 13px; width: 220px; outline: none; }
    #search:focus { border-color: #268bd2; }
    #search::placeholder { color: #586e75; }
    .btn { background: #073642; border: 1px solid #586e75; color: #93a1a1; border-radius: 5px; padding: 5px 12px; font-size: 12px; cursor: pointer; transition: all 0.15s; }
    .btn:hover { background: #0d4a5a; color: #fdf6e3; border-color: #839496; }
    #btn-clear { margin-left: auto; }

    /* ── Main area ── */
    #main { flex: 1; display: flex; overflow: hidden; }

    /* ── Graph canvas ── */
    #network { flex: 1; background: #1e1e2e; }

    /* ── Legend panel ── */
    #legend-panel { width: 300px; min-width: 300px; background: #002b36; border-left: 1px solid #586e75; overflow-y: auto; padding: 16px; display: none; flex-direction: column; gap: 14px; }
    #legend-panel.visible { display: flex; }
    #legend-close { align-self: flex-end; background: none; border: none; color: #586e75; font-size: 18px; cursor: pointer; line-height: 1; padding: 0; }
    #legend-close:hover { color: #fdf6e3; }
    #legend-title { font-size: 15px; font-weight: 600; color: #fdf6e3; word-break: break-word; }
    #legend-category { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; }
    .legend-section { background: #073642; border-radius: 6px; padding: 10px 12px; }
    .legend-section h4 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: #839496; margin-bottom: 6px; }
    .legend-section p { font-size: 13px; color: #eee8d5; line-height: 1.5; }
    .legend-section ul { list-style: none; display: flex; flex-direction: column; gap: 4px; }
    .legend-section li { font-size: 12px; color: #eee8d5; line-height: 1.4; }
    .legend-section li span.cond { color: #839496; font-style: italic; }
    .conflict-item { background: #2d0000; border-radius: 4px; padding: 6px 8px; }
    .conflict-item .cname { color: #ff5f57; font-weight: 600; font-size: 12px; }
    .conflict-item .cdesc { color: #cb9b96; font-size: 11px; margin-top: 2px; line-height: 1.4; }
    #legend-no-conflicts { font-size: 12px; color: #586e75; }

    /* ── Category legend footer ── */
    #cat-legend { background: #073642; border-top: 1px solid #586e75; padding: 7px 14px; display: flex; flex-wrap: wrap; gap: 12px; flex-shrink: 0; }
    .cat-item { display: flex; align-items: center; gap: 5px; font-size: 11px; color: #839496; }
    .cat-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
  </style>
</head>
<body>

<div id="toolbar">
  <h1>🗺 Лидерра — карта автоматизации</h1>
  <input id="search" type="text" placeholder="Поиск узла…" autocomplete="off">
  <button class="btn" id="btn-freeze">❄ Зафиксировать</button>
  <button class="btn" id="btn-unfreeze">▶ Расшевелить</button>
  <button class="btn" id="btn-reset">⊙ Сбросить вид</button>
  <button class="btn" id="btn-clear">✕ Снять выделение</button>
</div>

<div id="main">
  <div id="network"></div>
  <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>
    <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>
</div>

<div id="cat-legend">
  <div class="cat-item"><div class="cat-dot" style="background:#268bd2"></div>Правила</div>
  <div class="cat-item"><div class="cat-dot" style="background:#859900"></div>Плагины</div>
  <div class="cat-item"><div class="cat-dot" style="background:#6c71c4"></div>Скилы Superpowers</div>
  <div class="cat-item"><div class="cat-dot" style="background:#d33682"></div>Скилы проекта</div>
  <div class="cat-item"><div class="cat-dot" style="background:#2aa198"></div>Хуки</div>
  <div class="cat-item"><div class="cat-dot" style="background:#b58900"></div>Агенты</div>
  <div class="cat-item"><div class="cat-dot" style="background:#cb4b16"></div>MCP-серверы</div>
  <div class="cat-item"><div class="cat-dot" style="background:#dc322f"></div>Lefthook jobs</div>
  <div class="cat-item"><div class="cat-dot" style="background:#586e75"></div>Memory files</div>
  <div class="cat-item"><div class="cat-dot" style="background:#ff5f57; border: 1px dashed #ff5f57;"></div>— конфликт</div>
</div>

<script>
// ════════════════════════════════════════════════════
// SECTION 1: NODES (заполняется в Task 2)
// ════════════════════════════════════════════════════
const NODES = [];

// ════════════════════════════════════════════════════
// SECTION 2: EDGES (заполняется в Task 3)
// ════════════════════════════════════════════════════
const EDGES = [];

// ════════════════════════════════════════════════════
// SECTION 3: NODE DETAILS (заполняется в Task 4)
// ════════════════════════════════════════════════════
const NODE_DETAILS = {};

// ════════════════════════════════════════════════════
// SECTION 4: VIS INIT (заполняется в Task 5)
// ════════════════════════════════════════════════════

// ════════════════════════════════════════════════════
// SECTION 5: LEGEND PANEL (заполняется в Task 6)
// ════════════════════════════════════════════════════

// ════════════════════════════════════════════════════
// SECTION 6: TOOLBAR (заполняется в Task 7)
// ════════════════════════════════════════════════════
</script>
</body>
</html>
  • Step 2: Open the file in browser to verify structure loads

Open docs/automation-graph.html in browser (double-click or drag to Chrome/Edge). Expected: dark page with toolbar, empty dark canvas, footer with colored category dots. No JS errors in console (F12).


Task 2: NODES Array

Files:

  • Modify: docs/automation-graph.html — replace const NODES = []; with the full array

  • Step 1: Replace NODES placeholder with full 74-node array

Replace the line const NODES = []; with:

const NODES = [
  // ── ПРАВИЛА (4) ──────────────────────────────────
  { id: 'pravila',      label: 'Pravila v1.13',        group: 'rules',      size: 38 },
  { id: 'claude_md',    label: 'CLAUDE.md v1.92',       group: 'rules',      size: 34 },
  { id: 'psr_v1',       label: 'PSR_v1 v2.1',           group: 'rules',      size: 32 },
  { id: 'tooling',      label: 'Tooling v1.17',         group: 'rules',      size: 30 },

  // ── ПЛАГИНЫ (5) ──────────────────────────────────
  { id: 'superpowers',    label: 'Superpowers v5.1',    group: 'plugins',    size: 30 },
  { id: 'fd_plugin',      label: 'Frontend Design',      group: 'plugins',    size: 26 },
  { id: 'upm',            label: 'UI UX Pro Max',        group: 'plugins',    size: 22 },
  { id: 'claude_md_mgmt', label: 'claude-md-mgmt',       group: 'plugins',    size: 22 },
  { id: 'hookify_plugin', label: 'hookify (плагин)',     group: 'plugins',    size: 22 },

  // ── СКИЛЫ SUPERPOWERS (14) ────────────────────────
  { id: 'sk_brainstorm',  label: 'brainstorming',        group: 'skills_sp',  size: 18 },
  { id: 'sk_tdd',         label: 'TDD',                  group: 'skills_sp',  size: 18 },
  { id: 'sk_debug',       label: 'systematic-debugging', group: 'skills_sp',  size: 18 },
  { id: 'sk_wplans',      label: 'writing-plans',        group: 'skills_sp',  size: 20 },
  { id: 'sk_eplans',      label: 'executing-plans',      group: 'skills_sp',  size: 18 },
  { id: 'sk_verify',      label: 'verification-before-completion', group: 'skills_sp', size: 18 },
  { id: 'sk_parallel',    label: 'parallel-work',        group: 'skills_sp',  size: 18 },
  { id: 'sk_worktree',    label: 'worktree',             group: 'skills_sp',  size: 18 },
  { id: 'sk_pr',          label: 'finishing-pr',         group: 'skills_sp',  size: 18 },
  { id: 'sk_subagent',    label: 'subagent-driven',      group: 'skills_sp',  size: 20 },
  { id: 'sk_wskills',     label: 'writing-skills',       group: 'skills_sp',  size: 16 },
  { id: 'sk_spreview',    label: 'spec-review',          group: 'skills_sp',  size: 16 },
  { id: 'sk_coderev',     label: 'code-review',          group: 'skills_sp',  size: 16 },
  { id: 'sk_elements',    label: 'elements-of-style',    group: 'skills_sp',  size: 16 },

  // ── СКИЛЫ ПРОЕКТА (2) ────────────────────────────
  { id: 'sk_rls',         label: 'rls-check',            group: 'skills_proj', size: 20 },
  { id: 'sk_qitem',       label: 'q-item-add',           group: 'skills_proj', size: 20 },

  // ── ХУКИ .claude (5) ────────────────────────────
  { id: 'hk_pre_claude',  label: 'PreToolUse:\nCLAUDE.md-warn',   group: 'hooks', size: 22 },
  { id: 'hk_post_md',     label: 'PostToolUse:\nmarkdownlint',    group: 'hooks', size: 20 },
  { id: 'hk_post_schema', label: 'PostToolUse:\nschema-changelog',group: 'hooks', size: 20 },
  { id: 'hk_session',     label: 'SessionStart:\ncontext-inject', group: 'hooks', size: 24 },
  { id: 'hk_economy',     label: 'UserPromptSubmit:\neconomy-mode', group: 'hooks', size: 22 },

  // ── АГЕНТЫ (11) ──────────────────────────────────
  { id: 'ag_pest',        label: 'pest-parallel-debugger', group: 'agents',  size: 24 },
  { id: 'ag_rls',         label: 'rls-reviewer',           group: 'agents',  size: 22 },
  { id: 'ag_statusline',  label: 'statusline-setup',       group: 'agents',  size: 18 },
  { id: 'ag_guide',       label: 'claude-code-guide',      group: 'agents',  size: 18 },
  { id: 'ag_explore',     label: 'Explore',                group: 'agents',  size: 20 },
  { id: 'ag_general',     label: 'general-purpose',        group: 'agents',  size: 20 },
  { id: 'ag_plan',        label: 'Plan',                   group: 'agents',  size: 20 },
  { id: 'ag_hookify',     label: 'hookify:\nconversation-analyzer', group: 'agents', size: 18 },
  { id: 'ag_pcreator',    label: 'plugin-dev:\nagent-creator',  group: 'agents', size: 16 },
  { id: 'ag_pvalid',      label: 'plugin-dev:\nplugin-validator',group: 'agents', size: 16 },
  { id: 'ag_skreview',    label: 'plugin-dev:\nskill-reviewer',  group: 'agents', size: 16 },

  // ── MCP-СЕРВЕРЫ (7) ──────────────────────────────
  { id: 'mcp_pw',         label: 'MCP: playwright',       group: 'mcp',      size: 22 },
  { id: 'mcp_gh',         label: 'MCP: github',           group: 'mcp',      size: 22 },
  { id: 'mcp_boost',      label: 'MCP: laravel-boost',    group: 'mcp',      size: 24 },
  { id: 'mcp_semgrep',    label: 'MCP: semgrep',          group: 'mcp',      size: 20 },
  { id: 'mcp_sentry',     label: 'MCP: sentry',           group: 'mcp',      size: 22 },
  { id: 'mcp_redis',      label: 'MCP: redis',            group: 'mcp',      size: 22 },
  { id: 'mcp_21st',       label: 'MCP: 21st.dev Magic',   group: 'mcp',      size: 20 },

  // ── LEFTHOOK JOBS (10) ───────────────────────────
  { id: 'lh_gitleaks',    label: 'lefthook:\ngitleaks',       group: 'lefthook', size: 18 },
  { id: 'lh_mdlint',      label: 'lefthook:\nmarkdownlint',   group: 'lefthook', size: 18 },
  { id: 'lh_cspell',      label: 'lefthook:\ncspell',         group: 'lefthook', size: 18 },
  { id: 'lh_stylelint',   label: 'lefthook:\nstylelint',      group: 'lefthook', size: 16 },
  { id: 'lh_pint',        label: 'lefthook:\npint',           group: 'lefthook', size: 18 },
  { id: 'lh_larastan',    label: 'lefthook:\nlarastan',       group: 'lefthook', size: 18 },
  { id: 'lh_squawk',      label: 'lefthook:\nsquawk',         group: 'lefthook', size: 18 },
  { id: 'lh_eslint',      label: 'lefthook:\neslint-vue',     group: 'lefthook', size: 18 },
  { id: 'lh_gitleaks2',   label: 'lefthook:\ngitleaks pre-push', group: 'lefthook', size: 18 },
  { id: 'lh_lychee',      label: 'lefthook:\nlychee-links',   group: 'lefthook', size: 18 },

  // ── MEMORY FILES (15) ────────────────────────────
  { id: 'mem_user',       label: 'memory:\nuser_profile',        group: 'memory', size: 16 },
  { id: 'mem_comm',       label: 'memory:\nfeedback_comm',       group: 'memory', size: 14 },
  { id: 'mem_env',        label: 'memory:\nfeedback_env',        group: 'memory', size: 16 },
  { id: 'mem_sp',         label: 'memory:\nfeedback_superpowers',group: 'memory', size: 16 },
  { id: 'mem_plugins',    label: 'memory:\nfeedback_plugins',    group: 'memory', size: 16 },
  { id: 'mem_state',      label: 'memory:\nproject_state',       group: 'memory', size: 16 },
  { id: 'mem_phase1',     label: 'memory:\nphase1_strategy',     group: 'memory', size: 14 },
  { id: 'mem_archive',    label: 'memory:\nreference_archive',   group: 'memory', size: 14 },
  { id: 'mem_github',     label: 'memory:\nreference_github',    group: 'memory', size: 14 },
  { id: 'mem_handoff',    label: 'memory:\nreference_handoff',   group: 'memory', size: 14 },
  { id: 'mem_audit',      label: 'memory:\naudit_2026-05-13',    group: 'memory', size: 14 },
  { id: 'mem_supplier',   label: 'memory:\nsupplier_integration',group: 'memory', size: 14 },
  { id: 'mem_brain',      label: 'memory:\nclaude_brain',        group: 'memory', size: 14 },
  { id: 'mem_redesign',   label: 'memory:\nportal_redesign',     group: 'memory', size: 14 },
  { id: 'mem_devindices', label: 'memory:\ndev_indices',         group: 'memory', size: 12 },
];
  • Step 2: Refresh browser, verify

Refresh docs/automation-graph.html. Expected: ~74 colored dots appear on dark canvas, bouncing with physics. Groups visible by color (blue cluster = rules, green = plugins, etc.). Legend footer shows category dots.


Task 3: EDGES Array

Files:

  • Modify: docs/automation-graph.html — replace const EDGES = [];

  • Step 1: Replace EDGES placeholder with full edge array

Replace const EDGES = []; with:

// Helper edge factories
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' }
});
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 }
});

const EDGES = [
  // ── ПРАВИЛА — иерархия ──────────────────────────
  E('pravila', 'claude_md',    'подчиняет\n(уровень 1→2a)'),
  E('pravila', 'psr_v1',       'подчиняет\n(уровень 1→3)'),
  E('claude_md', 'tooling',    'ссылается\nна реестр'),
  E('pravila', 'superpowers',  '§12: обязывает\nинвокировать 1-м'),

  // ── PSR_v1 координирует плагины ─────────────────
  E('psr_v1', 'superpowers',   'R5: координирует\nпарный стек'),
  E('psr_v1', 'fd_plugin',     'R5: координирует\nпарный стек'),
  E('psr_v1', 'upm',           'R14.3: активирует\nтолько через pipeline'),
  E('psr_v1', 'mcp_21st',      'R14.4: активирует\nтолько через pipeline'),
  E('psr_v1', 'claude_md_mgmt','R10.1 блок 1:\nинфраструктурный'),

  // ── CLAUDE.md ────────────────────────────────────
  E('claude_md', 'mcp_boost',  'описывает §3.2'),
  E('claude_md', 'mcp_sentry', 'описывает §4.8'),
  E('claude_md', 'mcp_redis',  'описывает §4.9'),
  E('claude_md', 'claude_md_mgmt', '§5п.10:\nединственный канал'),
  E('claude_md', 'ag_pest',    'описывает\nкогда вызывать'),
  E('claude_md', 'ag_rls',     'описывает\nкогда вызывать'),

  // ── ХУКИ ────────────────────────────────────────
  E('hk_pre_claude',  'claude_md',    'проверяет\nпри Edit/Write'),
  E('hk_post_md',     'lh_mdlint',    'дублирует задачу\n(локально)'),
  E('hk_post_schema', 'claude_md',    'напоминает про\nCHANGELOG_schema'),
  E('hk_session',     'mem_user',     'читает\nпри старте'),
  E('hk_session',     'mem_env',      'читает\nпри старте'),
  E('hk_session',     'mem_sp',       'читает\nпри старте'),
  E('hk_session',     'mem_plugins',  'читает\nпри старте'),
  E('hk_session',     'mem_state',    'читает\nпри старте'),
  E('hk_economy',     'superpowers',  'парсит уровень\nэкономии'),

  // ── SUPERPOWERS содержит скилы ──────────────────
  E('superpowers', 'sk_brainstorm', 'содержит'),
  E('superpowers', 'sk_tdd',        'содержит'),
  E('superpowers', 'sk_debug',      'содержит'),
  E('superpowers', 'sk_wplans',     'содержит'),
  E('superpowers', 'sk_eplans',     'содержит'),
  E('superpowers', 'sk_verify',     'содержит'),
  E('superpowers', 'sk_parallel',   'содержит'),
  E('superpowers', 'sk_worktree',   'содержит'),
  E('superpowers', 'sk_pr',         'содержит'),
  E('superpowers', 'sk_subagent',   'содержит'),
  E('superpowers', 'sk_wskills',    'содержит'),
  E('superpowers', 'sk_spreview',   'содержит'),
  E('superpowers', 'sk_coderev',    'содержит'),
  E('superpowers', 'sk_elements',   'содержит'),

  // ── СКИЛЫ вызывают друг друга ───────────────────
  E('sk_brainstorm', 'sk_wplans',  'вызывает\nпосле дизайна'),
  E('sk_wplans',     'sk_eplans',  'вызывает\nдля выполнения'),
  E('sk_wplans',     'sk_subagent','альтернатива\nexecuting-plans'),
  E('sk_subagent',   'ag_explore', 'запускает\nдля поиска'),
  E('sk_subagent',   'ag_general', 'запускает\nдля задач'),
  E('sk_subagent',   'ag_plan',    'запускает\nдля архитектуры'),
  E('sk_parallel',   'sk_worktree','использует\nдля изоляции'),

  // ── СКИЛЫ ПРОЕКТА ───────────────────────────────
  E('sk_rls',   'tooling',      'использует\nsquawk + grep §3.2'),
  E('sk_rls',   'mcp_boost',    'SQL запросы\nк схеме'),
  E('sk_qitem', 'claude_md_mgmt','делегирует\nправку CLAUDE.md'),

  // ── CLAUDE-MD-MGMT ──────────────────────────────
  E('claude_md_mgmt', 'claude_md', 'единственный\nканал правок'),

  // ── HOOKIFY ─────────────────────────────────────
  E('ag_hookify', 'hookify_plugin', 'передаёт\nанализ'),
  E('hookify_plugin', 'hk_pre_claude', 'может создавать\nновые хуки'),
  E('hookify_plugin', 'hk_economy',    'может создавать\nновые хуки'),

  // ── АГЕНТЫ используют MCP ───────────────────────
  E('ag_pest',  'mcp_redis',  'читает\nочереди/кэш'),
  E('ag_rls',   'mcp_boost',  'SQL запросы\nк БД'),
  E('ag_guide', 'mcp_gh',     'ищет\nв репозитории'),

  // ── LEFTHOOK вызывается git ──────────────────────
  E('lh_gitleaks',  'mem_plugins', 'блокирует коммит\nпри ПДн в staged'),
  E('lh_larastan',  'mcp_boost',   'Boost даёт\nконтекст типов'),
  E('lh_squawk',    'tooling',     'соответствует\n§3.2 #15'),
  E('lh_gitleaks2', 'lh_gitleaks', 'строже:\nвся история'),
  E('lh_lychee',    'claude_md',   'проверяет\nссылки в .md'),

  // ── MEMORY читается Claude ──────────────────────
  E('mem_env',     'ag_pest',    'квирки 72/77\nиспользует агент'),
  E('mem_plugins', 'psr_v1',     'отражает\nтекущие версии'),
  E('mem_archive', 'claude_md',  'синхронизирует\nверсии доков'),

  // ── MCP ─────────────────────────────────────────
  E('mcp_pw',    'hk_session',  'используется\nдля a11y smoke'),
  E('mcp_gh',    'sk_pr',       'PR, issues\nпри finishing-pr'),
  E('mcp_boost', 'ag_rls',      'схема БД\nдля RLS-review'),

  // ══════════════════════════════════════════════════
  // КОНФЛИКТЫ (красные пунктирные рёбра)
  // ══════════════════════════════════════════════════
  CONFLICT('psr_v1',       'claude_md',       '⚡ T2.2'),
  CONFLICT('upm',          'fd_plugin',       '⚡ R14.5'),
  CONFLICT('mcp_21st',     'fd_plugin',       '⚡ R14.5'),
  CONFLICT('sk_rls',       'ag_rls',          '⚡ перекрытие RLS'),
  CONFLICT('hookify_plugin','hk_pre_claude',  '⚡ может затенить'),
  CONFLICT('hk_economy',   'superpowers',     '⚡ §12 vs Economy'),
];
  • Step 2: Refresh browser, verify

Refresh the file. Expected: coloured directed arrows between nodes. Six red dashed bidirectional arrows visible (may need to zoom in). Edge labels appear on hover.


Task 4: NODE_DETAILS Object

Files:

  • Modify: docs/automation-graph.html — replace const NODE_DETAILS = {};

  • Step 1: Replace NODE_DETAILS placeholder

Replace const NODE_DETAILS = {}; with:

const CATEGORY_LABELS = {
  rules: 'Правило',  plugins: 'Плагин',    skills_sp: 'Скил Superpowers',
  skills_proj: 'Скил проекта', hooks: 'Хук .claude', agents: 'Агент',
  mcp: 'MCP-сервер', lefthook: 'Lefthook job', memory: 'Memory-файл'
};

// Сокращение для создания записи
function nd(desc, reportsTo, manages, together, conflicts = []) {
  return { desc, reportsTo, manages, together, conflicts };
}
// reportsTo / manages / together: массив { name, cond }
// conflicts: массив { name, desc }

const NODE_DETAILS = {
  // ── ПРАВИЛА ──────────────────────────────────────
  pravila: nd(
    'Главный свод правил работы Клода — приоритеты, запреты и обязательные скилы.',
    [],
    [
      { name: 'CLAUDE.md', cond: 'подчинён уровень 2a' },
      { name: 'PSR_v1', cond: 'подчинён уровень 3' },
      { name: 'Superpowers', cond: '§12 — обязывает инвокировать первым' },
      { name: 'Все компоненты', cond: 'через цепочку приоритетов §1' }
    ],
    [{ name: 'CLAUDE.md', cond: 'оба читаются при старте сессии' }]
  ),
  claude_md: nd(
    'Оперативная карта проекта — список технологий, команд, фаз, ссылок на документы.',
    [{ 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 обязателен.',
    [{ 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 инструментов — когда что использовать, команды установки, конфликты.',
    [
      { name: 'Pravila', cond: 'уровень 2b (operational map рядом с CLAUDE.md)' },
      { name: 'CLAUDE.md', cond: 'при прямом конфликте CLAUDE.md приоритетнее' }
    ],
    [],
    [{ name: 'CLAUDE.md', cond: 'оба operational maps — правятся синхронно' }]
  ),

  // ── ПЛАГИНЫ ──────────────────────────────────────
  superpowers: nd(
    'Плагин поведения Клода — 14 скилов для TDD, дебага, планирования, параллельной работы.',
    [
      { 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, паттерны компонентов для Лидерры.',
    [{ 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.',
    [{ 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.',
    [{ name: 'PSR_v1', cond: 'R10.1 Блок 1: инфраструктурная категория' }],
    [{ name: 'CLAUDE.md', cond: '§5п.10: монопольный канал правок' }],
    [{ name: 'q-item-add скил', cond: 'скил делегирует CLAUDE.md правки через этот плагин' }]
  ),
  hookify_plugin: nd(
    'Плагин создания хуков — анализирует разговоры и предлагает новые автоматизации через хуки.',
    [{ 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 парсера' }
    ]
  ),

  // ── СКИЛЫ SUPERPOWERS ────────────────────────────
  sk_brainstorm: nd(
    'Продумывает задачу вместе с заказчиком, формулирует варианты A/B/C и согласует дизайн до написания кода.',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен для creative задач' }],
    [],
    [{ name: 'writing-plans', cond: 'вызывается сразу после brainstorming для создания плана' }]
  ),
  sk_tdd: nd(
    'Ведёт разработку через написание провального теста до кода — failing test first, потом GREEN.',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен для любого нового кода' }],
    [],
    [{ name: 'executing-plans', cond: 'TDD встроен в каждый шаг плана выполнения' }]
  ),
  sk_debug: nd(
    'Системный дебаг: минимум 3 гипотезы, фальсификация каждой перед исправлением — никаких «должно работать».',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен при unexpected behavior' }],
    [],
    [{ name: 'MCP: redis', cond: 'используется для дебага Redis-очередей' }]
  ),
  sk_wplans: nd(
    'Создаёт детальный план реализации с полным кодом, командами и шагами по 2-5 минут.',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен для multi-step задач (≥3 шага)' }],
    [],
    [{ name: 'executing-plans или subagent-driven', cond: 'план передаётся в один из них для выполнения' }]
  ),
  sk_eplans: nd(
    'Выполняет готовый план шаг за шагом, отмечает чекбоксы, делает коммиты после каждого шага.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'writing-plans', cond: 'получает план от writing-plans' }, { name: 'subagent-driven', cond: 'альтернатива — зависит от выбора пользователя' }]
  ),
  sk_verify: nd(
    'Обязательная проверка перед «готово» — запускает тесты, видит реальный вывод, никаких предположений.',
    [{ name: 'Superpowers', cond: 'содержит' }, { name: 'Pravila §12', cond: 'обязателен перед любым claim «готово»' }],
    [],
    [{ name: 'MCP: laravel-boost', cond: 'запросы к БД для проверки данных' }]
  ),
  sk_parallel: nd(
    'Разбивает независимые задачи на параллельные потоки с изоляцией через git worktrees.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'worktree скил', cond: 'parallel-work использует worktree для изоляции' }]
  ),
  sk_worktree: nd(
    'Создаёт изолированную копию репозитория для рискованной или параллельной работы.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'parallel-work', cond: 'worktree — инструмент для parallel-work' }]
  ),
  sk_pr: nd(
    'Чеклист финальной готовности PR — тесты, документация, чистота кода, pre-push хуки.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'MCP: github', cond: 'создаёт PR через GitHub MCP' }, { name: 'lefthook pre-push', cond: 'запускает gitleaks + lychee' }]
  ),
  sk_subagent: nd(
    'Запускает суб-агентов для крупных задач — каждый в отдельном контексте без накопленного шума.',
    [{ 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.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'plugin-dev:skill-reviewer', cond: 'агент проверяет созданный скил' }]
  ),
  sk_spreview: nd(
    'Проверяет spec-документ на полноту, противоречия, placeholder\'ы и scope.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'brainstorming', cond: 'spec-review вызывается в конце brainstorming после записи спека' }]
  ),
  sk_coderev: nd(
    'Систематический code review — безопасность, тесты, архитектура, соответствие правилам.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    [{ name: 'MCP: semgrep', cond: 'SAST-проверка при code review' }]
  ),
  sk_elements: nd(
    'Улучшает написание текстов и документации — ясность, лаконичность, без воды.',
    [{ name: 'Superpowers', cond: 'содержит' }],
    [],
    []
  ),

  // ── СКИЛЫ ПРОЕКТА ────────────────────────────────
  sk_rls: nd(
    '7-шаговый чеклист RLS для новой таблицы: tenant_id, ENABLE RLS, политики, 5-role GRANTs, CHANGELOG, squawk, smoke test.',
    [{ 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 и версии.',
    [],
    [],
    [{ name: 'claude-md-mgmt', cond: 'скил делегирует правку CLAUDE.md через плагин (§5п.10)' }]
  ),

  // ── ХУКИ ─────────────────────────────────────────
  hk_pre_claude: nd(
    'Блокирует прямое редактирование CLAUDE.md — срабатывает на Edit/Write по этому файлу.',
    [{ name: '.claude/settings.json', cond: 'описан как PreToolUse hook' }],
    [],
    [],
    [{ name: 'hookify (плагин)', desc: 'hookify динамически создаёт новые PreToolUse хуки — может перезаписать или конкурировать с этим хуком' }]
  ),
  hk_post_md: nd(
    'После каждого Edit .md файла запускает markdownlint --fix автоматически.',
    [{ name: '.claude/settings.json', cond: 'описан как PostToolUse hook' }],
    [],
    [{ name: 'lefthook:markdownlint', cond: 'дублируют задачу: хук — в сессии, lefthook — при коммите' }]
  ),
  hk_post_schema: nd(
    'После правки db/schema.sql напоминает обновить db/CHANGELOG_schema.md.',
    [{ name: '.claude/settings.json', cond: 'описан как PostToolUse hook' }],
    [],
    [{ name: 'lefthook:squawk', cond: 'оба реагируют на изменения SQL' }]
  ),
  hk_session: nd(
    'При старте каждой сессии инжектирует CLAUDE.md, Pravila и ключевые memory-файлы в контекст.',
    [{ 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%=по умолчанию).',
    [{ 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...).',
    [{ name: 'CLAUDE.md §6', cond: 'описывает когда вызывать' }],
    [],
    [{ name: 'MCP: redis', cond: 'читает Redis для дебага quirk 72 (supplier:session race)' }]
  ),
  ag_rls: nd(
    'Проверяет RLS compliance в миграциях — 7-item чеклист с реальными запусками команд, READ-ONLY.',
    [{ 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 через редактирование файла конфига.',
    [],
    [],
    []
  ),
  ag_guide: nd(
    'Отвечает на вопросы об API Claude Code, SDK, MCP серверах, хуках, slash commands.',
    [],
    [],
    [{ name: 'MCP: github', cond: 'ищет примеры в репозитории при необходимости' }]
  ),
  ag_explore: nd(
    'Быстрый поиск файлов по паттерну или символу — только чтение, без анализа.',
    [{ name: 'subagent-driven скил', cond: 'запускается для задач поиска' }],
    [],
    []
  ),
  ag_general: nd(
    'Универсальный агент для сложных multi-step исследований с полным доступом к инструментам.',
    [{ name: 'subagent-driven скил', cond: 'запускается для основных задач' }],
    [],
    []
  ),
  ag_plan: nd(
    'Архитектор: разрабатывает планы реализации, выявляет критичные файлы, рассматривает trade-offs.',
    [{ name: 'subagent-driven скил', cond: 'запускается для архитектурных задач' }],
    [],
    [{ name: 'writing-plans скил', cond: 'Plan агент и writing-plans решают похожую задачу для разных контекстов' }]
  ),
  ag_hookify: nd(
    'Анализирует транскрипты разговоров для поиска поведений, которые стоит предотвратить хуком.',
    [],
    [],
    [{ name: 'hookify (плагин)', cond: 'агент передаёт рекомендации в плагин' }]
  ),
  ag_pcreator: nd(
    'Создаёт конфигурацию новых агентов по описанию пользователя.',
    [],
    [],
    [{ name: 'plugin-dev:plugin-validator', cond: 'validator проверяет созданного агента' }]
  ),
  ag_pvalid: nd(
    'Проверяет структуру плагина на корректность — plugin.json, tools, manifest.',
    [],
    [],
    [{ name: 'plugin-dev:agent-creator', cond: 'validator и creator работают в паре' }]
  ),
  ag_skreview: nd(
    'Оценивает качество написанного скила — описание, workflow, примеры, best practices.',
    [],
    [],
    [{ name: 'writing-skills скил', cond: 'skill-reviewer проверяет то, что writing-skills создал' }]
  ),

  // ── MCP-СЕРВЕРЫ ──────────────────────────────────
  mcp_pw: nd(
    'Управляет браузером — снимает скриншоты, кликает, заполняет формы для smoke и a11y тестов.',
    [{ name: 'CLAUDE.md §3.1 #2', cond: 'активен с фазы 0' }],
    [],
    [{ name: 'SessionStart хук', cond: 'используется для визуальной проверки прототипов' }]
  ),
  mcp_gh: nd(
    'GitHub API — читает/создаёт PR, issues, коммиты, branches в репозитории CoralMinister/lidpotok.',
    [{ name: 'CLAUDE.md §3.1 #3', cond: 'активен с фазы 0' }],
    [],
    [{ name: 'finishing-pr скил', cond: 'создаёт PR через этот MCP' }]
  ),
  mcp_boost: nd(
    'Laravel Boost — SQL запросы к dev-БД, схема таблиц, логи ошибок, поиск в docs Laravel.',
    [{ 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, нарушения правил по паттернам.',
    [{ 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.',
    [{ name: 'CLAUDE.md §3.3 #34', cond: 'off-phase debug-runtime; pending Sentry deployment Б-1' }],
    [],
    [],
  ),
  mcp_redis: nd(
    'Читает Redis/Memurai — ключи, очереди, кэш для дебага race conditions. СТРОГО READ-ONLY.',
    [{ 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.',
    [{ name: 'PSR_v1 R14.4', cond: 'строгий pre-check: 9 условий перед активацией' }],
    [],
    [],
    [{ name: 'Frontend Design', desc: 'PSR_v1 R14.5: нельзя параллельно — 21st как генератор материала, FD как решатель; риск смешивания ролей и нарушения R6 фильтра стека' }]
  ),

  // ── LEFTHOOK JOBS ─────────────────────────────────
  lh_gitleaks: nd(
    'pre-commit: ищет ПДн, токены и API-ключи в staged-файлах. Блокирует коммит при находке.',
    [{ name: 'lefthook.yml', cond: 'job #1 в pre-commit, parallel:false' }],
    [],
    [{ name: 'lefthook:gitleaks pre-push', cond: 'pre-push версия сканирует всю историю' }]
  ),
  lh_mdlint: nd(
    'pre-commit: проверяет и авто-исправляет стиль Markdown файлов перед коммитом.',
    [{ name: 'lefthook.yml', cond: 'job #2 в pre-commit' }],
    [],
    [{ name: 'PostToolUse:markdownlint-fix хук', cond: 'оба делают одно — хук немедленно, lefthook при коммите' }]
  ),
  lh_cspell: nd(
    'pre-commit: проверяет орфографию в .md файлах по словарю cspell-words.txt.',
    [{ name: 'lefthook.yml', cond: 'job #3 в pre-commit' }],
    [],
    []
  ),
  lh_stylelint: nd(
    'pre-commit: линтует CSS в HTML-прототипах (web/v8/*.html).',
    [{ name: 'lefthook.yml', cond: 'job #4 в pre-commit' }],
    [],
    []
  ),
  lh_pint: nd(
    'pre-commit: авто-форматирует PHP код по PSR-стандарту, stage_fixed:true.',
    [{ name: 'lefthook.yml', cond: 'job #5 в pre-commit, root:app/' }],
    [],
    []
  ),
  lh_larastan: nd(
    'pre-commit: статический анализ PHP — находит ошибки типов выше baseline (Larastan L9).',
    [{ 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.).',
    [{ 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/.',
    [{ name: 'lefthook.yml', cond: 'job #8 в pre-commit, root:app/' }],
    [],
    []
  ),
  lh_gitleaks2: nd(
    'pre-push: полный скан всей истории коммитов на секреты (строже pre-commit job).',
    [{ 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).',
    [{ name: 'lefthook.yml', cond: 'job в pre-push' }],
    [],
    [{ name: 'CLAUDE.md', cond: 'проверяет ссылки в CLAUDE.md в том числе' }]
  ),

  // ── MEMORY FILES ─────────────────────────────────
  mem_user: nd(
    'Профиль заказчика: Дмитрий, Windows Server 2022, VSCode, русский язык, путь к проекту.',
    [],
    [],
    [{ name: 'SessionStart хук', cond: 'читается при старте каждой сессии' }]
  ),
  mem_comm: nd(
    'Стиль общения: короткие команды («а», «б», «делай»), варианты A/B/C, фиксация переоткрытий.',
    [],
    [],
    [{ name: 'memory:user_profile', cond: 'дополняет профиль пользователя' }]
  ),
  mem_env: nd(
    '80+ квирков окружения: специфика Windows Server, баги инструментов, митигации.',
    [],
    [],
    [
      { name: 'pest-parallel-debugger агент', cond: 'квирки 72/77 используются агентом' },
      { name: 'SessionStart хук', cond: 'читается при старте' }
    ]
  ),
  mem_sp: nd(
    'Hard rule §12 + economy hook architecture из 6 компонентов — дисциплина инвокации скилов.',
    [],
    [],
    [{ name: 'economy-mode хук', cond: 'связаны: memory описывает архитектуру хука' }]
  ),
  mem_plugins: nd(
    'Правила парного стека плагинов, debug-runtime MCP, tier-структура PSR_v1.',
    [],
    [],
    [{ name: 'PSR_v1', cond: 'memory отражает актуальные версии PSR_v1' }]
  ),
  mem_state: nd(
    'Текущее состояние: ветка, тесты (Pest/Vitest), последние коммиты, активные задачи.',
    [],
    [],
    [{ name: 'SessionStart хук', cond: 'читается при старте для быстрого контекста' }]
  ),
  mem_phase1: nd(
    'Стратегия фазы 1: native Windows стек без Docker, pg_partman заменён Artisan cron.',
    [],
    [],
    []
  ),
  mem_archive: nd(
    'Карта источников истины: версии всех 13+ ключевых документов проекта.',
    [],
    [],
    [{ name: 'CLAUDE.md', cond: 'memory синхронизирует версии с CLAUDE.md §0' }]
  ),
  mem_github: nd(
    'GitHub репозиторий CoralMinister/lidpotok: HEAD, hooks, правила push.',
    [],
    [],
    [{ name: 'MCP: github', cond: 'MCP и memory дополняют друг друга для работы с GitHub' }]
  ),
  mem_handoff: nd(
    'Дизайн-handoff Платона: что из liderra_v8_handoff/ используем, что нет.',
    [],
    [],
    []
  ),
  mem_audit: nd(
    'Полный аудит портала 13.05.2026: 38 находок, вердикт 🟡, 10 Q-items закрыты.',
    [],
    [],
    []
  ),
  mem_supplier: nd(
    'Прогресс интеграции поставщика Plans 1-5: Tasks, коммиты, блокеры.',
    [],
    [],
    []
  ),
  mem_brain: nd(
    'Claude Brain v1.0 — extracted brain repo, тег brain-v1.0, install.sh.',
    [],
    [],
    []
  ),
  mem_redesign: nd(
    'Редизайн Quiet Luxury: 20 коммитов, foundation CSS + composables + AppSidebar rewrite.',
    [],
    [],
    []
  ),
  mem_devindices: nd(
    'Dev Element Indices — временная feedback-фича для разработки; к удалению в продакшене.',
    [],
    [],
    []
  ),
};
  • Step 2: Verify in browser

Refresh. No JS errors in console. Graph structure unchanged (data only added, no rendering change yet). Console: Object.keys(NODE_DETAILS).length → should return 74.


Task 5: vis.js Network Initialization

Files:

  • Modify: docs/automation-graph.html — fill SECTION 4

  • Step 1: Replace // SECTION 4: VIS INIT comment with initialization code

// ════════════════════════════════════════════════════
// SECTION 4: VIS INIT
// ════════════════════════════════════════════════════
const GROUPS = {
  rules:       { color: { background: '#073642', border: '#268bd2', highlight: { border: '#93a1a1', background: '#0d4a5a' } }, font: { color: '#fdf6e3', size: 13, bold: true } },
  plugins:     { color: { background: '#001a00', border: '#859900', highlight: { border: '#b8cc00', background: '#002600' } }, font: { color: '#fdf6e3', size: 12 } },
  skills_sp:   { color: { background: '#1a0033', border: '#6c71c4', highlight: { border: '#9b9fea', background: '#250047' } }, font: { color: '#fdf6e3', size: 11 } },
  skills_proj: { color: { background: '#2d0020', border: '#d33682', highlight: { border: '#e869a8', background: '#3d0028' } }, font: { color: '#fdf6e3', size: 12 } },
  hooks:       { color: { background: '#002233', border: '#2aa198', highlight: { border: '#4dd7ce', background: '#003344' } }, font: { color: '#fdf6e3', size: 11 } },
  agents:      { color: { background: '#1a1200', border: '#b58900', highlight: { border: '#e0ad00', background: '#261a00' } }, font: { color: '#fdf6e3', size: 11 } },
  mcp:         { color: { background: '#2d1200', border: '#cb4b16', highlight: { border: '#ff6b30', background: '#3d1900' } }, font: { color: '#fdf6e3', size: 11 } },
  lefthook:    { color: { background: '#2d0000', border: '#dc322f', highlight: { border: '#ff5f5c', background: '#3d0000' } }, font: { color: '#fdf6e3', size: 10 } },
  memory:      { color: { background: '#112233', border: '#586e75', highlight: { border: '#839496', background: '#1a2f40' } }, font: { color: '#eee8d5', size: 10 } },
};

const nodesDS = new vis.DataSet(NODES);
const edgesDS = new vis.DataSet(EDGES);

const network = new vis.Network(
  document.getElementById('network'),
  { nodes: nodesDS, edges: edgesDS },
  {
    groups: GROUPS,
    nodes: {
      shape: 'dot',
      borderWidth: 2,
      borderWidthSelected: 3,
      font: { multi: 'html' },
    },
    edges: {
      smooth: { type: 'dynamic', roundness: 0.5 },
      selectionWidth: 2,
    },
    physics: {
      enabled: true,
      solver: 'barnesHut',
      barnesHut: {
        gravitationalConstant: -7000,
        centralGravity: 0.25,
        springLength: 130,
        springConstant: 0.04,
        damping: 0.09,
        avoidOverlap: 0.15,
      },
      stabilization: { enabled: true, iterations: 300, fit: true },
    },
    interaction: {
      hover: true,
      tooltipDelay: 400,
      hideEdgesOnDrag: true,
      multiselect: false,
    },
  }
);

// Fit after stabilization
network.once('stabilizationIterationsDone', () => {
  network.fit({ animation: { duration: 800, easingFunction: 'easeInOutQuad' } });
});
  • Step 2: Verify in browser

Refresh. Expected:

  • 74 colored nodes appear and settle with physics
  • 8 color groups clearly visible
  • Red dashed bidirectional edges for 6 conflicts
  • Gray directed arrows for normal edges
  • Hovering edge shows label
  • Zoom/pan/drag work

Task 6: Legend Panel + Click Handler

Files:

  • Modify: docs/automation-graph.html — fill SECTION 5

  • Step 1: Replace // SECTION 5: LEGEND PANEL with handler code

// ════════════════════════════════════════════════════
// SECTION 5: LEGEND PANEL
// ════════════════════════════════════════════════════
function renderLegendItem(item) {
  return `<li>${item.name}${item.cond ? ` <span class="cond">— ${item.cond}</span>` : ''}</li>`;
}

function showLegend(nodeId) {
  const node = NODES.find(n => n.id === nodeId);
  const details = NODE_DETAILS[nodeId];
  const panel = document.getElementById('legend-panel');

  if (!node || !details) { panel.classList.remove('visible'); return; }

  document.getElementById('legend-title').textContent = node.label.replace(/\n/g, ' ');

  const catLabel = CATEGORY_LABELS[node.group] || node.group;
  const catColor = GROUPS[node.group]?.color?.border || '#839496';
  document.getElementById('legend-category').innerHTML =
    `<span style="color:${catColor}; font-weight:600;">● ${catLabel}</span>`;

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

  const ldReports = document.getElementById('ld-reports');
  ldReports.innerHTML = details.reportsTo.length
    ? details.reportsTo.map(renderLegendItem).join('')
    : '<li style="color:#586e75; font-style:italic;">Не подчиняется никому</li>';

  const ldManages = document.getElementById('ld-manages');
  ldManages.innerHTML = details.manages.length
    ? details.manages.map(renderLegendItem).join('')
    : '<li style="color:#586e75; font-style:italic;">Никто не подчиняется</li>';

  const ldTogether = document.getElementById('ld-together');
  ldTogether.innerHTML = details.together.length
    ? details.together.map(renderLegendItem).join('')
    : '<li style="color:#586e75; font-style:italic;">Нет особых одновременных связей</li>';

  const ldConflicts = document.getElementById('ld-conflicts');
  if (details.conflicts && details.conflicts.length) {
    ldConflicts.innerHTML = details.conflicts.map(c =>
      `<div class="conflict-item"><div class="cname">⚡ ${c.name}</div><div class="cdesc">${c.desc}</div></div>`
    ).join('');
    document.getElementById('conflicts-section').style.display = '';
  } else {
    ldConflicts.innerHTML = '<div id="legend-no-conflicts" style="font-size:12px;color:#586e75;">Конфликтов не выявлено</div>';
    document.getElementById('conflicts-section').style.display = '';
  }

  panel.classList.add('visible');
}

network.on('click', params => {
  if (params.nodes.length === 1) {
    showLegend(params.nodes[0]);
  } else if (params.nodes.length === 0 && params.edges.length === 0) {
    document.getElementById('legend-panel').classList.remove('visible');
  }
});

document.getElementById('legend-close').addEventListener('click', () => {
  document.getElementById('legend-panel').classList.remove('visible');
});
  • Step 2: Verify click interaction

Refresh. Click on any node (e.g. "Pravila v1.13"). Expected: right panel slides open with:

  • Title: "Pravila v1.13"
  • Blue "● Правило" category badge
  • "Что делает" text
  • Lists for подчиняется/управляет/одновременно/конфликты Click on empty canvas → panel closes.

Task 7: Toolbar — Search + Buttons

Files:

  • Modify: docs/automation-graph.html — fill SECTION 6

  • Step 1: Replace // SECTION 6: TOOLBAR with toolbar handler code

// ════════════════════════════════════════════════════
// SECTION 6: TOOLBAR
// ════════════════════════════════════════════════════
let highlightedNode = null;

document.getElementById('search').addEventListener('input', function () {
  const q = this.value.trim().toLowerCase();
  if (!q) {
    // reset all node borders
    nodesDS.update(NODES.map(n => ({ id: n.id, borderWidth: 2 })));
    highlightedNode = null;
    return;
  }
  const matches = NODES.filter(n => n.label.toLowerCase().includes(q));
  // dim non-matching, highlight matching
  const updates = NODES.map(n => {
    const match = matches.some(m => m.id === n.id);
    return {
      id: n.id,
      borderWidth: match ? 5 : 1,
      opacity: match ? 1.0 : 0.25,
    };
  });
  nodesDS.update(updates);
  if (matches.length === 1) {
    network.focus(matches[0].id, { scale: 1.4, animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
    showLegend(matches[0].id);
    highlightedNode = matches[0].id;
  }
});

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' } });
});

document.getElementById('btn-clear').addEventListener('click', () => {
  document.getElementById('search').value = '';
  nodesDS.update(NODES.map(n => ({ id: n.id, borderWidth: 2, opacity: 1.0 })));
  document.getElementById('legend-panel').classList.remove('visible');
  highlightedNode = null;
});
  • Step 2: Verify toolbar

Refresh. Test:

  1. Type "Pravila" in search → blue rule nodes highlighted, others dimmed, Pravila panel opens
  2. "Зафиксировать" → nodes freeze in place
  3. "Расшевелить" → physics restarts
  4. "Сбросить вид" → camera fits all nodes
  5. "Снять выделение" → all nodes normal opacity, panel closes

Task 8: Final Verification + Commit

  • Step 1: Browser smoke test — open and check 8 criteria

Open docs/automation-graph.html in Chrome/Edge:

  1. Page loads without JS errors (F12 console clean)
  2. 74 nodes visible across 9 color groups (count roughly by category legend)
  3. Solarized dark theme throughout (dark canvas, dark toolbar, dark panel)
  4. 6 red dashed bidirectional edges visible for conflict nodes
  5. Click on "PSR_v1 v2.1" → panel opens, shows conflict with "CLAUDE.md" in red
  6. Click on "Superpowers v5.1" → panel shows 14 skils it manages
  7. Search "economy" → finds "UserPromptSubmit:\neconomy-mode" node, panel shows §12 conflict
  8. Category legend footer shows all 9 + conflict dot correctly
  • Step 2: Commit
git add docs/automation-graph.html
git commit -m "feat(docs): interactive automation graph — 74 nodes, 6 conflicts, Solarized dark vis.js"

Expected: pre-commit passes (gitleaks , markdownlint skips .html, cspell skips .html, Pint/Larastan skip .html). Commit hash shown.


Self-Review

Spec coverage check:

  • vis.js Network 9.x CDN — Task 1
  • Solarized Dark theme — Task 1 CSS
  • 74 nodes in 8 categories (+ Agents counted 11, MCP counted 7 = 9 groups total) — Task 2
  • Normal gray edges with Russian labels — Task 3
  • 6 red dashed conflict edges — Task 3
  • nodeDetails for all 74 nodes — Task 4
  • vis init + physics + group colors — Task 5
  • Click → legend panel with 5 sections — Task 6
  • Search filter — Task 7
  • Freeze/Unfreeze/Reset/Clear buttons — Task 7
  • Category legend footer — Task 1 HTML
  • Conflict descriptions in legend panel — Task 4 + 6

No placeholder scan: All node detail texts are complete Russian sentences. No TBD/TODO anywhere.

Type consistency: NODES[].id matches NODE_DETAILS{} keys exactly (74 ids). EDGES[].from/to reference only ids present in NODES. GROUPS{} keys match NODES[].group values.