feat(map): automation-graph — слой функциональных разделов (iter7)
39 разделов деятельности Лидерры (5 блоков A–E) как классификация: все 83 узла распределены по разделам — 13 наполнены, 26 пусты (пустые — бизнес-домены, под которые в карте dev-автоматики узлов ещё нет). Кнопка-панель «📂 Разделы» + строка «Раздел» в Паспорте узла. Топология карты (83/90/11) и радиальный layout без изменений. Основа будущего «мозга»: 1 раздел = 1 playbook. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -90,6 +90,20 @@
|
||||
box-shadow: inset 0 0 0 1px rgba(253,246,227,0.4);
|
||||
color: #fdf6e3;
|
||||
}
|
||||
|
||||
/* ── Панель «Разделы» (функциональная квалификация) ── */
|
||||
#legend-sections-content { display: flex; flex-direction: column; gap: 10px; }
|
||||
#legend-sections-title { font-size: 15px; font-weight: 600; color: #fdf6e3; }
|
||||
.sect-bucket-h { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: #b58900; font-weight: 600; margin: 8px 0 2px; }
|
||||
.sect-row { background: #073642; border-radius: 6px; padding: 7px 10px; }
|
||||
.sect-row.sect-empty { opacity: 0.5; }
|
||||
.sect-name { font-size: 12px; color: #eee8d5; font-weight: 600; }
|
||||
.sect-name .sect-id { color: #839496; font-weight: 400; }
|
||||
.sect-cnt { font-size: 11px; color: #839496; }
|
||||
.sect-empty-mark { font-size: 11px; color: #586e75; font-style: italic; margin-top: 3px; }
|
||||
.sect-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
|
||||
.sect-chip { font-size: 10px; color: #93a1a1; background: #002b36; border: 1px solid #586e75; border-radius: 3px; padding: 1px 5px; cursor: pointer; }
|
||||
.sect-chip:hover { background: #0d4a5a; color: #fdf6e3; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -116,6 +130,7 @@
|
||||
<h4>📇 Паспорт узла</h4>
|
||||
<p><span class="pp-k">Внедрён:</span> <span id="ld-since">—</span></p>
|
||||
<p><span class="pp-k">Последнее изменение:</span> <span id="ld-changed">—</span></p>
|
||||
<p><span class="pp-k">Раздел:</span> <span id="ld-section">—</span></p>
|
||||
<p><span class="pp-k">Использований за 7 дней:</span> <span id="ld-uses">—</span></p>
|
||||
<p id="ld-dup-row" style="display:none;"><span class="pp-k">Дубль:</span> <span id="ld-dup">—</span></p>
|
||||
</div>
|
||||
@@ -141,6 +156,14 @@
|
||||
<div class="legend-section"><h4>Обязательность</h4><p id="le-mandatory">—</p></div>
|
||||
<div class="legend-section"><h4>Регламент</h4><p id="le-rule">—</p></div>
|
||||
</div>
|
||||
|
||||
<div id="legend-sections-content" style="display:none;">
|
||||
<div id="legend-sections-title">📂 Разделы деятельности Лидерры</div>
|
||||
<div class="legend-section">
|
||||
<p>Узлы карты распределены по функциональным разделам. Пустые разделы — будущие домены «мозга», под которые в карте dev-автоматики ещё нет узлов (playbook не наполнен).</p>
|
||||
</div>
|
||||
<div id="sect-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -162,6 +185,7 @@
|
||||
<span class="cat-ctl-sep"></span>
|
||||
<button class="cat-ctl" id="cat-ctl-heat" title="Подсветить узлы по числу вызовов за 7 дней">🔥 По использованию</button>
|
||||
<button class="cat-ctl" id="cat-ctl-dup" title="Подсветить явные пары дублей (D1–D5, D7)">⧉ Дубли</button>
|
||||
<button class="cat-ctl" id="cat-ctl-sect" title="Показать функциональные разделы и распределение узлов по ним">📂 Разделы</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -1505,6 +1529,105 @@ DUPLICATE_GROUPS.forEach(g => g.members.forEach(m => {
|
||||
}));
|
||||
const DUP_NODE_SET = new Set(DUP_BY_NODE.keys()); // 12 узлов-членов парных дублей
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// SECTION 3.5: ФУНКЦИОНАЛЬНЫЕ РАЗДЕЛЫ (функциональная квалификация)
|
||||
// ════════════════════════════════════════════════════
|
||||
// Разделы деятельности Лидерры. Каждый узел карты отнесён к одному разделу
|
||||
// (NODE_SECTION). Часть разделов пока пустая — это бизнес-домены, под которые
|
||||
// в карте dev-автоматики ещё нет узлов. Основа будущего «мозга»: 1 раздел =
|
||||
// 1 playbook «как и что делать».
|
||||
const SECTION_BUCKETS = [
|
||||
{ id: 'A', label: 'Технические и продуктовые' },
|
||||
{ id: 'B', label: 'Коммуникации' },
|
||||
{ id: 'C', label: 'Бизнес и операции' },
|
||||
{ id: 'D', label: 'Право и комплаенс' },
|
||||
{ id: 'E', label: 'Мета и управление' },
|
||||
];
|
||||
const SECTIONS = [
|
||||
{ id: 'A1', bucket: 'A', label: 'Программирование — backend' },
|
||||
{ id: 'A2', bucket: 'A', label: 'Программирование — frontend' },
|
||||
{ id: 'A3', bucket: 'A', label: 'Программирование — интеграции (API, вебхуки)' },
|
||||
{ id: 'A4', bucket: 'A', label: 'Дизайн (UI/UX, графика, бренд)' },
|
||||
{ id: 'A5', bucket: 'A', label: 'Тестирование, QA и отладка' },
|
||||
{ id: 'A6', bucket: 'A', label: 'Архитектура систем' },
|
||||
{ id: 'A7', bucket: 'A', label: 'DevOps, инфраструктура, деплой' },
|
||||
{ id: 'A8', bucket: 'A', label: 'Информационная безопасность' },
|
||||
{ id: 'A9', bucket: 'A', label: 'Работа с данными (БД, миграции, RLS)' },
|
||||
{ id: 'A10', bucket: 'A', label: 'Аналитика и отчётность (BI)' },
|
||||
{ id: 'A11', bucket: 'A', label: 'ML / AI-разработка' },
|
||||
{ id: 'B1', bucket: 'B', label: 'Голосовое общение по телефону' },
|
||||
{ id: 'B2', bucket: 'B', label: 'Мессенджеры' },
|
||||
{ id: 'B3', bucket: 'B', label: 'Электронная почта' },
|
||||
{ id: 'B4', bucket: 'B', label: 'SMS-рассылки' },
|
||||
{ id: 'B5', bucket: 'B', label: 'Видеосвязь' },
|
||||
{ id: 'B6', bucket: 'B', label: 'Чат на сайте / онлайн-консультант' },
|
||||
{ id: 'B7', bucket: 'B', label: 'Социальные сети' },
|
||||
{ id: 'B8', bucket: 'B', label: 'Push / in-app уведомления' },
|
||||
{ id: 'C1', bucket: 'C', label: 'Маркетинг и лидогенерация' },
|
||||
{ id: 'C2', bucket: 'C', label: 'Продажи' },
|
||||
{ id: 'C3', bucket: 'C', label: 'Квалификация и обработка лидов' },
|
||||
{ id: 'C4', bucket: 'C', label: 'Работа с поставщиками лидов' },
|
||||
{ id: 'C5', bucket: 'C', label: 'Клиентский успех, поддержка, удержание' },
|
||||
{ id: 'C6', bucket: 'C', label: 'Финансы — биллинг и тарификация' },
|
||||
{ id: 'C7', bucket: 'C', label: 'Финансы — бухгалтерия и налоги' },
|
||||
{ id: 'C8', bucket: 'C', label: 'HR и управление персоналом' },
|
||||
{ id: 'C9', bucket: 'C', label: 'Управление проектами' },
|
||||
{ id: 'C10', bucket: 'C', label: 'Бизнес-процессы (общее)' },
|
||||
{ id: 'D1', bucket: 'D', label: 'Юриспруденция и договорная работа' },
|
||||
{ id: 'D2', bucket: 'D', label: 'Защита ПДн (152-ФЗ, РКН)' },
|
||||
{ id: 'D3', bucket: 'D', label: 'Аудит и управление рисками' },
|
||||
{ id: 'E1', bucket: 'E', label: 'Мета — правила и нормативка' },
|
||||
{ id: 'E2', bucket: 'E', label: 'Мета — оркестрация и автоматизация (Claude-воркфлоу)' },
|
||||
{ id: 'E3', bucket: 'E', label: 'Документация' },
|
||||
{ id: 'E4', bucket: 'E', label: 'Управление знаниями и память' },
|
||||
{ id: 'E5', bucket: 'E', label: 'Стратегия и принятие решений' },
|
||||
{ id: 'E6', bucket: 'E', label: 'Обучение и онбординг' },
|
||||
{ id: 'E7', bucket: 'E', label: 'Исследования' },
|
||||
];
|
||||
// Узел -> раздел. Покрывает все 83 узла карты.
|
||||
const NODE_SECTION = {
|
||||
// правила (4)
|
||||
pravila: 'E1', claude_md: 'E1', psr_v1: 'E1', tooling: 'E1',
|
||||
// плагины (5)
|
||||
superpowers: 'E2', fd_plugin: 'A4', upm: 'A4', claude_md_mgmt: 'E1', hookify_plugin: 'E2',
|
||||
// скилы superpowers (14)
|
||||
sk_brainstorm: 'E5', sk_wplans: 'E2', sk_eplans: 'E2', sk_subagent: 'E2',
|
||||
sk_tdd: 'A5', sk_verify: 'A5', sk_debug: 'A5', sk_parallel: 'E2',
|
||||
sk_worktree: 'E2', sk_pr: 'E2', sk_coderev: 'A5', sk_spreview: 'A5',
|
||||
sk_wskills: 'E2', sk_elements: 'E3',
|
||||
// скилы проекта (2)
|
||||
sk_rls: 'A9', sk_qitem: 'E3',
|
||||
// хуки (5)
|
||||
hk_session: 'E4', hk_economy: 'E2', hk_pre_claude: 'E1', hk_post_md: 'E3', hk_post_schema: 'A9',
|
||||
// агенты (11)
|
||||
ag_explore: 'E2', ag_general: 'E2', ag_plan: 'E2', ag_pest: 'A5', ag_guide: 'E6',
|
||||
ag_statusline: 'E2', ag_hookify: 'E2', ag_pcreator: 'E2', ag_pvalid: 'E2',
|
||||
ag_skreview: 'E2', ag_rls: 'A9',
|
||||
// MCP-серверы (7)
|
||||
mcp_21st: 'A4', mcp_pw: 'A5', mcp_gh: 'A7', mcp_boost: 'A1',
|
||||
mcp_redis: 'A7', mcp_sentry: 'A7', mcp_semgrep: 'A8',
|
||||
// lefthook jobs (10)
|
||||
lh_mdlint: 'E3', lh_cspell: 'E3', lh_stylelint: 'A2', lh_eslint: 'A2',
|
||||
lh_lychee: 'E3', lh_gitleaks: 'A8', lh_gitleaks2: 'A8', lh_pint: 'A1',
|
||||
lh_larastan: 'A1', lh_squawk: 'A9',
|
||||
// memory files (16)
|
||||
mem_user: 'E4', mem_comm: 'E4', mem_env: 'E4', mem_sp: 'E4', mem_plugins: 'E4',
|
||||
mem_handoff: 'E4', mem_redesign: 'E4', mem_devindices: 'E4', mem_phase1: 'E4',
|
||||
mem_state: 'E4', mem_brain: 'E4', mem_supplier: 'E4', mem_audit: 'E4',
|
||||
mem_archive: 'E4', mem_github: 'E4', mem_ruflo: 'E4',
|
||||
// ruflo (9)
|
||||
ruflo_queen: 'E2', ruflo_plugins: 'E2', ruflo_workers: 'E2', ruflo_agents_catalog: 'E2',
|
||||
ruflo_commands: 'E2', ruflo_daemon: 'E2', ruflo_memory: 'E4', ruflo_mcp: 'E2',
|
||||
ruflo_recall_hook: 'E4',
|
||||
};
|
||||
// Производные индексы для рендера панели и Паспорта.
|
||||
const SECTION_BY_ID = new Map(SECTIONS.map(s => [s.id, s]));
|
||||
const SECTION_NODES = new Map(SECTIONS.map(s => [s.id, []]));
|
||||
NODES.forEach(n => {
|
||||
const sid = NODE_SECTION[n.id];
|
||||
if (sid && SECTION_NODES.has(sid)) SECTION_NODES.get(sid).push(n.id);
|
||||
});
|
||||
|
||||
// ════════════════════════════════════════════════════
|
||||
// SECTION 4: VIS INIT
|
||||
// ════════════════════════════════════════════════════
|
||||
@@ -1564,6 +1687,7 @@ function renderLegendItem(item) {
|
||||
function showNodeLegend(nodeId) {
|
||||
document.getElementById('legend-node-content').style.display = '';
|
||||
document.getElementById('legend-edge-content').style.display = 'none';
|
||||
document.getElementById('legend-sections-content').style.display = 'none';
|
||||
const node = NODES.find(n => n.id === nodeId);
|
||||
const details = NODE_DETAILS[nodeId];
|
||||
const panel = document.getElementById('legend-panel');
|
||||
@@ -1581,6 +1705,8 @@ function showNodeLegend(nodeId) {
|
||||
const meta = NODE_META[nodeId] || { since: '—', changed: '—', uses: null, usesSrc: '—' };
|
||||
document.getElementById('ld-since').textContent = meta.since || '—';
|
||||
document.getElementById('ld-changed').textContent = meta.changed || '—';
|
||||
const _sec = NODE_SECTION[nodeId] ? SECTION_BY_ID.get(NODE_SECTION[nodeId]) : null;
|
||||
document.getElementById('ld-section').textContent = _sec ? `${_sec.id} · ${_sec.label}` : '—';
|
||||
|
||||
const usesEl = document.getElementById('ld-uses');
|
||||
if (meta.uses === null || meta.uses === undefined) {
|
||||
@@ -1654,6 +1780,7 @@ function showEdgeLegend(edgeId) {
|
||||
const panel = document.getElementById('legend-panel');
|
||||
document.getElementById('legend-node-content').style.display = 'none';
|
||||
document.getElementById('legend-edge-content').style.display = '';
|
||||
document.getElementById('legend-sections-content').style.display = 'none';
|
||||
|
||||
const fromNode = NODES.find(n => n.id === edge.from);
|
||||
const toNode = NODES.find(n => n.id === edge.to);
|
||||
@@ -1693,6 +1820,35 @@ function showEdgeLegend(edgeId) {
|
||||
panel.classList.add('visible');
|
||||
}
|
||||
|
||||
// ── Панель «Разделы» — функциональная квалификация (3-й режим легенды) ──
|
||||
function showSectionsLegend() {
|
||||
document.getElementById('legend-node-content').style.display = 'none';
|
||||
document.getElementById('legend-edge-content').style.display = 'none';
|
||||
document.getElementById('legend-sections-content').style.display = '';
|
||||
let html = '';
|
||||
for (const bucket of SECTION_BUCKETS) {
|
||||
html += `<div class="sect-bucket-h">${bucket.id}. ${bucket.label}</div>`;
|
||||
for (const sec of SECTIONS.filter(s => s.bucket === bucket.id)) {
|
||||
const nodeIds = SECTION_NODES.get(sec.id) || [];
|
||||
const empty = nodeIds.length === 0;
|
||||
html += `<div class="sect-row${empty ? ' sect-empty' : ''}">`;
|
||||
html += `<div><span class="sect-name"><span class="sect-id">${sec.id}</span> ${sec.label}</span> <span class="sect-cnt">· ${nodeIds.length}</span></div>`;
|
||||
if (empty) {
|
||||
html += `<div class="sect-empty-mark">— пусто (playbook ещё не наполнен) —</div>`;
|
||||
} else {
|
||||
html += '<div class="sect-chips">' + nodeIds.map(id => {
|
||||
const node = NODES.find(n => n.id === id);
|
||||
const lbl = node ? node.label.replace(/\n/g, ' ') : id;
|
||||
return `<span class="sect-chip" data-node="${id}">${lbl}</span>`;
|
||||
}).join('') + '</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
}
|
||||
}
|
||||
document.getElementById('sect-list').innerHTML = html;
|
||||
document.getElementById('legend-panel').classList.add('visible');
|
||||
}
|
||||
|
||||
network.on('click', params => {
|
||||
if (params.nodes.length === 1) {
|
||||
const id = params.nodes[0];
|
||||
@@ -2003,6 +2159,7 @@ const HIGHLIGHT = (function setupHighlight() {
|
||||
// iter6 — клик по кнопке режима heat/dup
|
||||
const ctl = e.target.closest('.cat-ctl');
|
||||
if (ctl) {
|
||||
if (ctl.id === 'cat-ctl-sect') { showSectionsLegend(); return; }
|
||||
setViewMode(ctl.id === 'cat-ctl-heat' ? 'heat' : 'dup');
|
||||
applyHighlight();
|
||||
updateLegendVisuals();
|
||||
@@ -2026,6 +2183,19 @@ const HIGHLIGHT = (function setupHighlight() {
|
||||
};
|
||||
})();
|
||||
|
||||
// Клик по чипу узла в панели «Разделы» — открыть паспорт узла + сфокусировать граф.
|
||||
document.getElementById('sect-list').addEventListener('click', e => {
|
||||
const chip = e.target.closest('.sect-chip');
|
||||
if (!chip) return;
|
||||
const id = chip.dataset.node;
|
||||
if (HIGHLIGHT.state.viewMode === null) {
|
||||
HIGHLIGHT.setSelectedNode(id);
|
||||
HIGHLIGHT.applyHighlight();
|
||||
}
|
||||
network.focus(id, { scale: 1.2, animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
|
||||
showNodeLegend(id);
|
||||
});
|
||||
|
||||
window.addEventListener('DOMContentLoaded', restoreLegendWidth);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user