Files
portal/docs/superpowers/specs/2026-05-10-connections-graph-design.md
T
Дмитрий 5e38ff6d7e docs(viz): connections-graph spec + plan (D3 force-directed карта связей)
Артефакты параллельной Claude-сессии (mode «экономия 0%», 10.05.2026 ночь).

Spec (385 строк): расширение hooks-skills-plugins-map.html новой §X
«Связи — interactive map». Force-directed network через D3.js v7 (CDN),
~50 узлов (плагины + скилы + хук-скрипты + hook events + state-файл +
permissions + Pravila §12 + CLAUDE.md), 52 ребра. Vintage-blueprint
aesthetic. Drag/click/hover/category filters/reset.

Plan (1246 строк): пошаговая реализация для executing-plans с TDD-структурой.

cspell-words.txt: +3 термина (диспатчу/скилы/ребёр).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 23:31:44 +03:00

17 KiB
Raw Blame History

Interactive Connections Graph — Design

Дата: 2026-05-10 (ночь) Статус: Approved by user · ready for plan Автор: Claude (Opus 4.7) под mode «экономия 0%» Связано: docs/visualizations/hooks-skills-plugins-map.html (2240 строк, расширение)


1. Цель

Добавить в существующую HTML-визуализацию hooks-skills-plugins-map.html интерактивную force-directed network диаграмму, показывающую связи между всеми сущностями системы (плагины, скилы, хук-скрипты, hook events, state-файл, permissions, Pravila §12, CLAUDE.md).

Зачем: иерархия (контейнерная/приоритетная/временная) уже есть в §II после прошлой итерации. Но связи между блоками — кто что вызывает, кто что пишет/читает, кто что блокирует — ещё не визуализированы. Пользователь сказал «запутался» — нужна графическая карта зависимостей с возможностью «потрогать».

2. Scope

2.1. В scope

  • Новая секция в HTML: §X «Связи — interactive map»
  • Force-directed network через D3.js v7 (CDN)
  • ~50 узлов (полный inventory) с фильтрами по категориям
  • Интерактив: drag, click-to-highlight, hover-tooltip, category filters, reset button
  • Responsive до mobile (graph horizontally scrolls, sidebar → bottom-sheet)
  • Сохранение текущей vintage-blueprint aesthetic

2.2. Не в scope (YAGNI)

  • Поиск по имени узла
  • Экспорт PNG / share URL
  • Редактирование графа (добавление/удаление узлов через UI)
  • Анимация ребёр / pulse-effects
  • Zoom/pan (D3 поддерживает, но не нужен для 50 узлов)
  • Сохранение пользовательского layout'а (refresh = reset)

3. Архитектура

3.1. Технология

  • D3.js v7 через CDN https://cdn.jsdelivr.net/npm/d3@7. Force layout API из стандартного D3.
  • Pure SVG для рендеринга узлов и рёбер (не Canvas — для accessibility и hover-detection).
  • Vanilla JS для interactivity, без фреймворков.
  • Inline в существующий HTML — никаких отдельных файлов.

3.2. Структура данных

const nodes = [
  // Plugins (4)
  { id: 'plg:superpowers', type: 'plugin', label: 'superpowers', size: 22 },
  { id: 'plg:claude-md',   type: 'plugin', label: 'claude-md-management', size: 22 },
  { id: 'plg:fd',          type: 'plugin', label: 'frontend-design', size: 22 },
  { id: 'plg:upm',         type: 'plugin', label: 'ui-ux-pro-max', size: 22 },

  // Skills (28: 14 superpowers + 2 claude-md + 2 ui + 6 config + 2 auto + 2 review)
  { id: 'skl:brainstorming', type: 'skill', label: 'brainstorming', size: 10 },
  // ... 27 more

  // Hook scripts (7)
  { id: 'scr:skill-marker',      type: 'script', label: 'skill-marker.py', size: 14 },
  { id: 'scr:skill-check',       type: 'script', label: 'skill-check.py', size: 14 },
  { id: 'scr:economy-mode',      type: 'script', label: 'economy-mode.py', size: 14 },
  { id: 'scr:economy-self-check',type: 'script', label: 'economy-self-check.py', size: 14 },
  { id: 'scr:economy-state-guard',type:'script', label: 'economy-state-guard.py', size: 14 },
  { id: 'scr:economy-verifier',  type: 'script', label: 'economy-verifier.py (Sonnet 4.6)', size: 14 },
  { id: 'scr:economy-postcompact',type:'script', label: 'economy-postcompact.py', size: 14 },

  // Hook events (5)
  { id: 'evt:session-start',     type: 'event', label: 'SessionStart' },
  { id: 'evt:user-prompt-submit',type: 'event', label: 'UserPromptSubmit' },
  { id: 'evt:pre-tool-use',      type: 'event', label: 'PreToolUse' },
  { id: 'evt:post-compact',      type: 'event', label: 'PostCompact' },
  { id: 'evt:stop',              type: 'event', label: 'Stop' },

  // State file (1)
  { id: 'st:economy-state', type: 'state', label: '$TEMP/claude-economy-<sid>.json' },

  // Permissions (3)
  { id: 'prm:allow', type: 'perm', label: 'permissions.allow (1)' },
  { id: 'prm:deny',  type: 'perm', label: 'permissions.deny (7)' },
  { id: 'prm:ask',   type: 'perm', label: 'permissions.ask (16)' },

  // Rules (2)
  { id: 'rul:pravila-12', type: 'rule', label: 'Pravila §12 (hard rule)' },
  { id: 'rul:claude-md',  type: 'rule', label: 'CLAUDE.md' },
];

const links = [
  // contains (4 plugin → multiple skills each)
  { source: 'plg:superpowers', target: 'skl:brainstorming', type: 'contains' },
  // ... ~28 contains edges total

  // triggers (event → script, 7 edges)
  { source: 'evt:session-start',      target: 'scr:economy-self-check', type: 'triggers' },
  { source: 'evt:user-prompt-submit', target: 'scr:economy-mode', type: 'triggers' },
  { source: 'evt:pre-tool-use',       target: 'scr:skill-marker', type: 'triggers' },
  { source: 'evt:pre-tool-use',       target: 'scr:skill-check', type: 'triggers' },
  { source: 'evt:pre-tool-use',       target: 'scr:economy-state-guard', type: 'triggers' },
  { source: 'evt:post-compact',       target: 'scr:economy-postcompact', type: 'triggers' },
  { source: 'evt:stop',               target: 'scr:economy-verifier', type: 'triggers' },

  // writes (1)
  { source: 'scr:economy-mode', target: 'st:economy-state', type: 'writes' },

  // reads (3)
  { source: 'scr:economy-state-guard', target: 'st:economy-state', type: 'reads' },
  { source: 'scr:economy-verifier',    target: 'st:economy-state', type: 'reads' },
  { source: 'scr:economy-postcompact', target: 'st:economy-state', type: 'reads' },

  // mandates (1 → 14 superpowers skills)
  { source: 'rul:pravila-12', target: 'skl:brainstorming', type: 'mandates' },
  // ... 14 mandates edges

  // references (CLAUDE.md → Pravila §12, Tooling)
  { source: 'rul:claude-md', target: 'rul:pravila-12', type: 'references' },

  // blocks (permissions.ask → all hook scripts)
  { source: 'prm:ask', target: 'scr:skill-marker', type: 'blocks' },
  { source: 'prm:ask', target: 'scr:skill-check', type: 'blocks' },
  { source: 'prm:ask', target: 'scr:economy-mode', type: 'blocks' },
  // ... 7 blocks edges

  // denies (permissions.deny → state-file)
  { source: 'prm:deny', target: 'st:economy-state', type: 'denies' },
];

Итого: 50 узлов, 52 ребра.

Точный breakdown узлов: 4 plugins + 28 skills + 7 scripts + 5 events + 1 state + 3 perms + 2 rules = 50.

Точный breakdown рёбер:

  • 18 contains (plugin → skill): 14 superpowers + 2 claude-md + 1 fd + 1 upm. 10 скилов standalone (update-config, keybindings-help, simplify, fewer-permission-prompts, init, claude-api, loop, schedule, review, security-review) — НЕТ contains-edge, узел висит свободно (привязывается к Forces only)
  • 7 triggers (event → script)
  • 1 writes (economy-mode → state)
  • 3 reads (state-guard / verifier / postcompact ← state)
  • 14 mandates (Pravila §12 → 14 superpowers skills)
  • 1 references (CLAUDE.md → Pravila §12)
  • 7 blocks (permissions.ask → 7 hook scripts)
  • 1 denies (permissions.deny → state-file)

3.3. Visual encoding

Атрибут Plugins Skills Scripts Events State Perms Rules
Shape circle circle circle rect diamond hexagon star/rect
Fill rust #8B3A1B blueprint #1A6E8E amber #C48B26 sage #5E7A4F rust #8B3A1B ink-fade ink+rust frame
Size r=22 r=10 r=14 16×16 18×18 20×20 20×20
Border ink 2px ink 1.5px ink 1.5px ink 2px ink 2px solid + rust dashed ink 1.5px rust 2px

Подпись узла — fraunces 10px below the node, с paper background (rect bg) для читаемости поверх линий.

Edges by type:

Type Color Width Style Arrowhead
contains --blueprint 1.5px solid нет
triggers --sage 2px solid →arrow
writes --amber 2px solid →filled
reads --amber 1.5px dashed ←open
mandates --rust 2.5px solid →double
references --ink-fade 1px dotted →arrow
blocks --rust 1.5px dashed T-end
denies --rust 2px dashed X-end

3.4. Initial positioning (clustering)

D3 force simulation с custom forces:

const sim = d3.forceSimulation(nodes)
  .force('link', d3.forceLink(links).id(d=>d.id).distance(d => linkDistance(d.type)))
  .force('charge', d3.forceManyBody().strength(-300))
  .force('center', d3.forceCenter(W/2, H/2))
  .force('y-cluster', d3.forceY(d => yByType(d.type)).strength(0.15))
  .force('collide', d3.forceCollide().radius(d => d.size + 8));

yByType стратификация:

  • Rules (top): y=80
  • Plugins: y=160
  • Skills: y=320
  • Events: y=480
  • Scripts: y=540
  • State + Permissions: y=420 (middle, как hubs)

3.5. Interactivity

Drag node     → fx/fy фиксируется во время drag, освобождается после
Click node    → highlight all edges where source/target == clicked
              → grey out non-related (opacity 0.15)
              → show sidebar with: name, type, list of connections
Click empty   → reset highlight
Hover edge    → SVG tooltip с типом связи + source/target labels
Filter chip   → toggles display:none for all nodes of that category +
                их incident edges
Reset btn     → recalc simulation, clear filters, dismiss sidebar

3.6. Sidebar (right, hidden by default)

При клике на узел:

  • Имя узла (заголовок)
  • Тип (badge)
  • Описание (1-2 предложения из inventory)
  • Секция «Связи»:
    • Outgoing: список «→ {edge-type} → {target}»
    • Incoming: список «{source} ← {edge-type} ←»

Mobile: sidebar → bottom-sheet через position: fixed; bottom: 0.

3.7. Category filters

В шапке секции — 7 chip-buttons. Click toggles категорию:

[Plugins ●] [Skills ●] [Scripts ●] [Events ●] [State ●] [Perms ●] [Rules ●]

Каждая кнопка — toggle on/off (active state visible через filled background).

4. Detailed contracts

4.1. HTML структура

<section class="section">
  <div class="frame">
    <div class="section-num display-i">X</div>
    <h2 class="section-title">Связи &mdash; interactive map</h2>
    <p class="section-lede">...</p>

    <div class="graph-controls">
      <div class="graph-filters">
        <button class="graph-filter active" data-type="plugin">Plugins</button>
        ... 6 more
      </div>
      <button class="graph-reset">Reset layout</button>
    </div>

    <div class="graph-container">
      <svg class="graph-svg" viewBox="0 0 1100 700">
        <!-- D3 fills this -->
      </svg>
      <aside class="graph-sidebar" hidden>
        <!-- D3 populates on click -->
      </aside>
    </div>

    <div class="graph-legend">
      <!-- Static legend: shapes + colors + edge types -->
    </div>
  </div>
</section>

4.2. JS module

Один <script type="module"> блок в конце <body> (после всего HTML).

// Inline data
const nodes = [/* ... */];
const links = [/* ... */];

// Initialize D3 force simulation
const sim = d3.forceSimulation(nodes) /* ... */ ;

// Render
const svg = d3.select('.graph-svg');
const linkSel = svg.append('g').selectAll('line').data(links).join('line') /* ... */;
const nodeSel = svg.append('g').selectAll('g.node').data(nodes).join('g').classed('node', true) /* ... */;

// Interactivity handlers
nodeSel.on('click', onNodeClick);
linkSel.on('mouseover', onEdgeHover);
// filters, reset btn

4.3. CSS additions

.graph-controls { ... display: flex; justify-content: space-between; }
.graph-filter { ... border: 1px solid var(--rule); padding: 6px 14px; cursor: pointer; }
.graph-filter.active { background: var(--ink); color: var(--paper); }
.graph-container { display: grid; grid-template-columns: 1fr 280px; gap: 24px; }
.graph-sidebar { ... }
.graph-legend { ... }
.node { cursor: pointer; }
.node text { font-family: 'Fraunces', serif; font-size: 9px; ... }
.link.triggers { stroke: var(--sage); stroke-width: 2; }
.link.writes { stroke: var(--amber); stroke-width: 2; marker-end: url(#arrow-amber); }
...
.dimmed { opacity: 0.15; }
.highlighted { opacity: 1; }

@media (max-width: 900px) {
  .graph-container { grid-template-columns: 1fr; }
  .graph-sidebar { position: fixed; bottom: 0; ... }
}

5. Testing strategy

5.1. Manual verification

  • Все 50 узлов рендерятся
  • Все 52 ребра видны
  • Drag любого узла работает
  • Click на plugin: подсвечены все его skills + связи
  • Click на state-file: подсвечены 1 writer + 3 readers + 1 denier (5 связей)
  • Click на Pravila §12: подсвечены 14 superpowers skills
  • Filter «Plugins» toggled off → исчезают 4 plugin узла + их contains edges
  • Reset clears filters, recenters layout, dismisses sidebar
  • Mobile (<900px): graph остаётся видимым, sidebar бекомес bottom-sheet
  • Без интернета: D3 не загружается, но HTML page продолжает работать (graph section показывает fallback message)

5.2. Browser compatibility

  • Chrome 120+, Firefox 120+, Edge 120+, Safari 17+ (D3 v7 поддерживает)
  • IE / старый Safari — не поддерживаются (out of scope)

5.3. Performance

50 узлов + 52 ребра — well within D3's force layout capability. Should reach stable layout within 2-3 seconds. Idle CPU после стабилизации.

6. Risks

Риск Митигация
D3 CDN недоступен (offline) Graceful fallback: проверка typeof d3 !== 'undefined', иначе показать message «Connections graph requires internet (D3 v7 CDN). Other sections work offline.»
Слишком плотный граф Filters + drag + центральная гравитация рассеивают plotpoints
Конфликт CSS-имён с существующими (.graph-* уникальные prefix) Prefix graph- для всех новых классов
Изменения в data — где править Все nodes/links inline в <script> блоке, легко находится по // Inline data маркеру
Mobile UX плохой при ~50 узлах Уменьшаем размер узлов на mobile, sidebar в bottom-sheet, scrollable graph horizontally

7. Success criteria

После имплементации:

  • Открыть HTML в браузере → виден новый раздел §X «Связи»
  • Граф ренджерится в течение 3 секунд
  • Все 50 узлов с подписями
  • Все 52 ребра с правильными цветами по типу
  • Click на любой узел → видны все его связи, sidebar показывает details
  • Filter toggle работает (показывает/скрывает категорию)
  • Reset кнопка возвращает initial state
  • No console errors
  • Mobile (<900px): функциональный, не сломан
  • Без D3 CDN: graceful fallback message виден
  • Файл валиден HTML (0 unclosed tags, 0 orphan closes)

8. Scope каскадирующих изменений

  • Существующие секции I-IX — без изменений
  • Текущая §X (Practical Actions) переименовывается в §XI
  • 1 renumber в section-num + текстовые правки в footer если упоминают «9 sections» (не упоминают)
  • Файл вырастает с 2240 строк → ~2500 (~260 добавки)

9. Open questions

Нет. Все ключевые решения утверждены в brainstorming диалоге выше.

10. Next steps

После approve'а этого spec'а:

  1. superpowers:writing-plans — пошаговый план имплементации
  2. Execution choice: inline или subagent-driven
  3. Стадии:
    • a. Renumber текущей §X → §XI
    • b. Insert new §X section structure (HTML + CSS skeleton)
    • c. Inline data (nodes + links)
    • d. D3 simulation + rendering
    • e. Interactivity: click, drag, hover
    • f. Filters + reset
    • g. Sidebar populate
    • h. Mobile responsive
    • i. CDN fallback
    • j. Manual verification против success criteria

Status: Approved by user via brainstorming dialogue · ready for writing-plans.