Артефакты параллельной 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>
17 KiB
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}←»
- Outgoing: список «
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">Связи — 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'а:
superpowers:writing-plans— пошаговый план имплементации- Execution choice: inline или subagent-driven
- Стадии:
- 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.