4-task plan for iter6 of docs/automation-graph.html: «Паспорт узла» legend section (since/changed/uses) for all 83 nodes + 2 footer toggle buttons (usage heatmap, duplicate highlight). NODE_META (83 records) and DUPLICATE_GROUPS (6 pairs D1-D5/D7) carry factual values derived from 76 session transcripts (window 09-16.05.2026) + git history; method and raw outputs in Appendix A. cspell-words.txt += pcreator, pvalid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
60 KiB
Карта автоматизации iter6 — даты, счётчик использования, дубли узлов — план реализации
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: добавить в docs/automation-graph.html (iter6) секцию «Паспорт узла» (дата внедрения, дата последнего изменения, число вызовов за 7 дней) в легенду каждого из 83 узлов и две toggle-кнопки в футере — теплокарта использования и подсветка явных дублей.
Architecture: чисто аддитивная итерация одного single-file HTML. Новый блок данных SECTION 3.6 (две константы-снимка + NODE_META на 83 записи + DUPLICATE_GROUPS + производные индексы) — между SECTION 3.5 и SECTION 4. Рендер легенды дополняется секцией «📇 Паспорт узла»; в IIFE HIGHLIGHT добавляется взаимоисключающий режим viewMode (heat / dup); в футер #cat-legend — две кнопки .cat-ctl. NODES (83), EDGES (90), CONFLICT_TYPES, builder nd(), все 83 записи NODE_DETAILS, EDGE_DETAILS (86 ключей) — не трогаются. Метрики карты остаются 83 узла / 90 рёбер / 11 конфликтов.
Tech Stack: vis.js Network 9.1.9 (CDN), ванильный JS, Solarized Dark, без шага сборки. Карта — артефакт без отдельного фреймворка unit-тестов; верификация — браузерный smoke через Playwright MCP по паттерну iter3/iter4/iter5 (browser_evaluate-ассерты + проверка консоли). Источник истины дизайна — docs/superpowers/specs/2026-05-16-automation-graph-iter6-node-meta-design.md (коммит 00937b7).
Структура файла
Единственный изменяемый файл — docs/automation-graph.html (1775 строк до iter6). Пять точек вставки/правки:
| Задача | Где (якорь, строки до-iter6) | Что |
|---|---|---|
| 1 | между }; конца EDGE_DETAILS (стр. 1328) и // SECTION 4: VIS INIT (стр. 1330) |
новый блок SECTION 3.6 — данные |
| 2 | CSS перед </style> (стр. 75); DOM после #legend-category (стр. 96); рендер в showNodeLegend после set категории (стр. 1400) |
секция «Паспорт узла» |
| 3 | CSS перед </style> (стр. 75); DOM перед </div> #cat-legend (стр. 137); правки IIFE HIGHLIGHT (стр. 1635–1770); guard в network.on('click') (стр. 1488) |
две кнопки + режим viewMode |
| 4 | memory/project_automation_map.md (вне репозитория) |
фиксация iter6 + финальный smoke |
Номера строк — на состояние файла до iter6. Задачи 1→4 выполняются по порядку; задачи 2–4 в правках опираются на стабильные якоря кода, а не на абсолютные номера (после задачи 1 строки сместятся на ~100 вниз).
Окружение для верификации (квирки memory): квирк #90 — Playwright browser_navigate отвергает file:// → отдавать карту через python -m http.server. Квирк #91 — browser_click нестабилен на узких элементах → клики делать через browser_evaluate.
Task 1: Блок данных SECTION 3.6
Files:
- Modify:
docs/automation-graph.html— вставка между стр. 1328 (};концаEDGE_DETAILS) и стр. 1330 (// SECTION 4)
Назначение: META_SNAPSHOT / META_WINDOW (снимок), NODE_META (83 записи, поля since/changed/uses/usesSrc, опционально dupNote), DUPLICATE_GROUPS (6 пар), производные DUP_BY_NODE / DUP_NODE_SET. Значения — фактические, выведены на этапе writing-plans (методика и сырые выводы — Приложение А).
- Шаг 1: вставить блок
SECTION 3.6
Edit. old_string (якорь — заголовок SECTION 4, уникален):
// ════════════════════════════════════════════════════
// SECTION 4: VIS INIT
// ════════════════════════════════════════════════════
const GROUPS = {
new_string — тот же якорь, но перед ним вставлен весь блок SECTION 3.6:
// ════════════════════════════════════════════════════
// SECTION 3.6: NODE META (iter6 — даты, использование, дубли)
// ════════════════════════════════════════════════════
// Данные — фактический снимок: даты из git/changelog/mtime, счётчик uses —
// из разбора транскриптов сессий Claude Code за окно META_WINDOW.
// Методика и воспроизводимость — план iter6, Приложение А.
const META_SNAPSHOT = '16.05.2026'; // дата генерации значений
const META_WINDOW = '09–16.05.2026'; // окно подсчёта использования (7 дней)
// uses: number — измеримый узел (0 = реально простаивал); null — измерить нельзя
// (узел-правило / плагин-обёртка / автономный демон / пассивное хранилище) → «нет данных».
// usesSrc: 'скил' | 'агент' | 'MCP' | 'хук' | 'memory-чтение' | 'коммиты' | 'инспекция' | '—'
const NODE_META = {
// ── ПРАВИЛА (4) — узлы-правила, напрямую не вызываются ──
pravila: { since: '06.05.2026', changed: '15.05.2026', uses: null, usesSrc: '—' },
claude_md: { since: '06.05.2026', changed: '15.05.2026', uses: null, usesSrc: '—' },
psr_v1: { since: '09.05.2026', changed: '15.05.2026', uses: null, usesSrc: '—' },
tooling: { since: '06.05.2026', changed: '15.05.2026', uses: null, usesSrc: '—' },
// ── ПЛАГИНЫ (5) ──
superpowers: { since: '09.05.2026', changed: '—', uses: null, usesSrc: '—' },
fd_plugin: { since: '10.05.2026', changed: '—', uses: 1, usesSrc: 'скил' },
upm: { since: '10.05.2026', changed: '—', uses: 0, usesSrc: 'скил' },
claude_md_mgmt: { since: '10.05.2026', changed: '—', uses: 15, usesSrc: 'скил' },
hookify_plugin: { since: '—', changed: '—', uses: null, usesSrc: '—' },
// ── СКИЛЫ SUPERPOWERS (14) — связка подключена 09.05.2026 ──
sk_brainstorm: { since: '09.05.2026', changed: '—', uses: 44, usesSrc: 'скил' },
sk_wplans: { since: '09.05.2026', changed: '—', uses: 54, usesSrc: 'скил' },
sk_eplans: { since: '09.05.2026', changed: '—', uses: 11, usesSrc: 'скил' },
sk_subagent: { since: '09.05.2026', changed: '—', uses: 33, usesSrc: 'скил' },
sk_tdd: { since: '09.05.2026', changed: '—', uses: 2, usesSrc: 'скил' },
sk_verify: { since: '09.05.2026', changed: '—', uses: 25, usesSrc: 'скил' },
sk_debug: { since: '09.05.2026', changed: '—', uses: 10, usesSrc: 'скил' },
sk_parallel: { since: '09.05.2026', changed: '—', uses: 3, usesSrc: 'скил' },
sk_worktree: { since: '09.05.2026', changed: '—', uses: 3, usesSrc: 'скил' },
sk_pr: { since: '09.05.2026', changed: '—', uses: 10, usesSrc: 'скил' },
sk_coderev: { since: '09.05.2026', changed: '—', uses: 1, usesSrc: 'скил' },
sk_spreview: { since: '09.05.2026', changed: '—', uses: 0, usesSrc: 'скил' },
sk_wskills: { since: '09.05.2026', changed: '—', uses: 0, usesSrc: 'скил' },
sk_elements: { since: '09.05.2026', changed: '—', uses: 0, usesSrc: 'скил' },
// ── СКИЛЫ ПРОЕКТА (2) — файлы добавлены в репозиторий 13.05.2026 ──
sk_rls: { since: '13.05.2026', changed: '—', uses: 0, usesSrc: 'скил' },
sk_qitem: { since: '13.05.2026', changed: '—', uses: 1, usesSrc: 'скил' },
// ── ХУКИ (5) ──
hk_session: { since: '—', changed: '—', uses: 67, usesSrc: 'хук' },
hk_economy: { since: '10.05.2026', changed: '—', uses: 276, usesSrc: 'хук' },
hk_pre_claude: { since: '13.05.2026', changed: '—', uses: 249, usesSrc: 'хук' },
hk_post_md: { since: '06.05.2026', changed: '—', uses: 1334, usesSrc: 'хук' },
hk_post_schema: { since: '13.05.2026', changed: '—', uses: 22, usesSrc: 'хук' },
// ── АГЕНТЫ (11) ──
ag_explore: { since: 'встроено в Claude Code', changed: '—', uses: 26, usesSrc: 'агент' },
ag_general: { since: 'встроено в Claude Code', changed: '—', uses: 512, usesSrc: 'агент' },
ag_plan: { since: 'встроено в Claude Code', changed: '—', uses: 0, usesSrc: 'агент' },
ag_pest: { since: '13.05.2026', changed: '—', uses: 2, usesSrc: 'агент' },
ag_guide: { since: 'встроено в Claude Code', changed: '—', uses: 4, usesSrc: 'агент' },
ag_statusline: { since: 'встроено в Claude Code', changed: '—', uses: 0, usesSrc: 'агент' },
ag_hookify: { since: '—', changed: '—', uses: 0, usesSrc: 'агент' },
ag_pcreator: { since: '—', changed: '—', uses: 0, usesSrc: 'агент' },
ag_pvalid: { since: '—', changed: '—', uses: 0, usesSrc: 'агент' },
ag_skreview: { since: '—', changed: '—', uses: 0, usesSrc: 'агент' },
ag_rls: { since: '13.05.2026', changed: '—', uses: 2, usesSrc: 'агент' },
// ── MCP-СЕРВЕРЫ (7) ──
mcp_21st: { since: '10.05.2026', changed: '—', uses: 0, usesSrc: 'MCP' },
mcp_pw: { since: '06.05.2026', changed: '—', uses: 706, usesSrc: 'MCP' },
mcp_gh: { since: '06.05.2026', changed: '—', uses: 0, usesSrc: 'MCP' },
mcp_boost: { since: '08.05.2026', changed: '—', uses: 73, usesSrc: 'MCP' },
mcp_redis: { since: '13.05.2026', changed: '—', uses: 0, usesSrc: 'MCP' },
mcp_sentry: { since: '13.05.2026', changed: '—', uses: 0, usesSrc: 'MCP' },
mcp_semgrep: { since: '10.05.2026', changed: '—', uses: 0, usesSrc: 'MCP' },
// ── LEFTHOOK JOBS (10) — uses ≈ число коммитов в окне, фильтр по типу файла ──
lh_mdlint: { since: '06.05.2026', changed: '—', uses: 232, usesSrc: 'коммиты' },
lh_cspell: { since: '06.05.2026', changed: '—', uses: 232, usesSrc: 'коммиты' },
lh_stylelint: { since: '06.05.2026', changed: '—', uses: 43, usesSrc: 'коммиты' },
lh_eslint: { since: '08.05.2026', changed: '—', uses: 120, usesSrc: 'коммиты' },
lh_lychee: { since: '06.05.2026', changed: '—', uses: 56, usesSrc: 'коммиты' },
lh_gitleaks: { since: '06.05.2026', changed: '—', uses: 514, usesSrc: 'коммиты' },
lh_gitleaks2: { since: '06.05.2026', changed: '—', uses: 56, usesSrc: 'коммиты' },
lh_pint: { since: '08.05.2026', changed: '—', uses: 134, usesSrc: 'коммиты' },
lh_larastan: { since: '08.05.2026', changed: '—', uses: 134, usesSrc: 'коммиты' },
lh_squawk: { since: '08.05.2026', changed: '—', uses: 19, usesSrc: 'коммиты' },
// ── MEMORY FILES (15) — uses = вызовы Read; changed = mtime файла ──
mem_user: { since: '07.05.2026', changed: '08.05.2026', uses: 0, usesSrc: 'memory-чтение' },
mem_comm: { since: '07.05.2026', changed: '08.05.2026', uses: 0, usesSrc: 'memory-чтение' },
mem_env: { since: '07.05.2026', changed: '15.05.2026', uses: 41, usesSrc: 'memory-чтение' },
mem_sp: { since: '09.05.2026', changed: '10.05.2026', uses: 1, usesSrc: 'memory-чтение' },
mem_plugins: { since: '09.05.2026', changed: '13.05.2026', uses: 9, usesSrc: 'memory-чтение' },
mem_handoff: { since: '08.05.2026', changed: '—', uses: 0, usesSrc: 'memory-чтение' },
mem_redesign: { since: '12.05.2026', changed: '—', uses: 0, usesSrc: 'memory-чтение' },
mem_devindices: { since: '12.05.2026', changed: '—', uses: 1, usesSrc: 'memory-чтение' },
mem_phase1: { since: '08.05.2026', changed: '—', uses: 1, usesSrc: 'memory-чтение' },
mem_state: { since: '07.05.2026', changed: '15.05.2026', uses: 73, usesSrc: 'memory-чтение' },
mem_brain: { since: '11.05.2026', changed: '—', uses: 1, usesSrc: 'memory-чтение' },
mem_supplier: { since: '10.05.2026', changed: '12.05.2026', uses: 13, usesSrc: 'memory-чтение' },
mem_audit: { since: '13.05.2026', changed: '—', uses: 3, usesSrc: 'memory-чтение' },
mem_archive: { since: '07.05.2026', changed: '15.05.2026', uses: 21, usesSrc: 'memory-чтение' },
mem_github: { since: '07.05.2026', changed: '15.05.2026', uses: 33, usesSrc: 'memory-чтение' },
// ── RUFLO ОРКЕСТРАТОР (9) — все внедрены big-bang'ом 15.05.2026 ──
ruflo_queen: { since: '15.05.2026', changed: '—', uses: 0, usesSrc: 'инспекция' },
ruflo_plugins: { since: '15.05.2026', changed: '—', uses: 0, usesSrc: 'инспекция' },
ruflo_workers: { since: '15.05.2026', changed: '—', uses: 0, usesSrc: 'инспекция' },
ruflo_agents_catalog: { since: '15.05.2026', changed: '—', uses: 0, usesSrc: 'инспекция',
dupNote: '100 определений агентов дублируют реестр агентов; каталог буквально содержит 2 проектных агента' },
ruflo_commands: { since: '15.05.2026', changed: '—', uses: 0, usesSrc: 'инспекция',
dupNote: '88 slash-команд дублируют роль скилов — именованные вызываемые процедуры; команды инертны' },
ruflo_daemon: { since: '15.05.2026', changed: '—', uses: null, usesSrc: '—' },
ruflo_memory: { since: '15.05.2026', changed: '—', uses: null, usesSrc: '—',
dupNote: 'дублирует роль 16 memory-файлов проекта — постоянная память между сессиями; уже ⚫-конфликт с project_state' },
ruflo_mcp: { since: '15.05.2026', changed: '—', uses: 36, usesSrc: 'MCP' },
ruflo_recall_hook: { since: '15.05.2026', changed: '—', uses: 220, usesSrc: 'хук' },
// ── MEMORY +1 (артефакт ruflo big-bang) ──
mem_ruflo: { since: '15.05.2026', changed: '16.05.2026', uses: 18, usesSrc: 'memory-чтение' },
};
// Явные парные дубли (Фича 3) — попадают в кнопку «⧉ Дубли».
const DUPLICATE_GROUPS = [
{ id: 'D1', pct: 90, members: ['hk_post_md', 'lh_mdlint'],
reason: 'оба запускают markdownlint --fix на .md — разница только в триггере (после правки в сессии против при коммите)' },
{ id: 'D2', pct: 85, members: ['lh_gitleaks', 'lh_gitleaks2'],
reason: 'оба — скан секретов gitleaks; pre-commit проверяет staged-файлы, pre-push — всю историю (надмножество)' },
{ id: 'D3', pct: 85, members: ['sk_rls', 'ag_rls'],
reason: 'оба — 7-пунктовая проверка RLS-соответствия; уже отмечено 🔴-конфликтом «нет регламента кто когда»' },
{ id: 'D4', pct: 80, members: ['sk_eplans', 'sk_subagent'],
reason: 'оба исполняют план writing-plans пошагово; разница — параллельная сессия против суб-агентов в текущей' },
{ id: 'D5', pct: 80, members: ['sk_wplans', 'ag_plan'],
reason: 'оба производят план задачи; скил writing-plans — inline с полным кодом, агент Plan — архитектурный набросок READ-ONLY' },
{ id: 'D7', pct: 80, members: ['ruflo_recall_hook', 'hk_session'],
reason: 'оба инжектят память в контекст Клода; recall-хук — на каждый промпт из базы ruflo, SessionStart — при старте из .md-файлов' },
];
// Производный индекс: nodeId -> { partner, pct, reason } — для рендера легенды и подсветки.
const DUP_BY_NODE = new Map();
DUPLICATE_GROUPS.forEach(g => g.members.forEach(m => {
const partner = g.members.find(x => x !== m);
DUP_BY_NODE.set(m, { partner, pct: g.pct, reason: g.reason });
}));
const DUP_NODE_SET = new Set(DUP_BY_NODE.keys()); // 12 узлов-членов парных дублей
// ════════════════════════════════════════════════════
// SECTION 4: VIS INIT
// ════════════════════════════════════════════════════
const GROUPS = {
- Шаг 2: smoke — данные загрузились, покрытие полное
Запустить локальный сервер и открыть карту:
# терминал: отдать docs/ по HTTP (квирк #90 — file:// не работает в Playwright)
cd "c:\моя\проекты\портал crm\Документация\docs"
python -m http.server 8011
Playwright MCP: browser_navigate → http://localhost:8011/automation-graph.html; затем browser_evaluate:
() => ({
nodeMetaKeys: Object.keys(NODE_META).length,
allCovered: NODES.every(n => !!NODE_META[n.id]),
extraKeys: Object.keys(NODE_META).filter(k => !NODES.some(n => n.id === k)),
dupGroups: DUPLICATE_GROUPS.length,
dupNodeSet: DUP_NODE_SET.size,
nodes: NODES.length, edges: EDGES.length,
})
Expected: { nodeMetaKeys: 83, allCovered: true, extraKeys: [], dupGroups: 6, dupNodeSet: 12, nodes: 83, edges: 90 }.
Затем browser_console_messages (level: error) — ожидается пусто, кроме внешнего favicon.ico 404.
- Шаг 3: коммит
git add docs/automation-graph.html
git commit -m "feat(graph): iter6 SECTION 3.6 — NODE_META + DUPLICATE_GROUPS data"
Перед коммитом сработает pre-commit lefthook (gitleaks; markdownlint/cspell — только если в коммите есть .md; здесь только .html → gitleaks + stylelint). Вывод pre-commit показать целиком. --no-verify запрещён.
Task 2: Секция «📇 Паспорт узла» в легенде узла
Files:
- Modify:
docs/automation-graph.html— CSS (перед</style>), DOM (после#legend-category), рендер (вshowNodeLegend)
Объединяет Фичи 1 и 2: одна новая секция легенды с датами + счётчиком + строкой «Дубль».
- Шаг 1: CSS секции «Паспорт»
Edit. old_string (якорь — конец блока .cat-item.active, последнее правило перед </style>):
.cat-item.active {
background: rgba(253,246,227,0.12);
box-shadow: inset 0 0 0 1px rgba(253,246,227,0.4);
color: #fdf6e3;
}
</style>
new_string:
.cat-item.active {
background: rgba(253,246,227,0.12);
box-shadow: inset 0 0 0 1px rgba(253,246,227,0.4);
color: #fdf6e3;
}
/* ── Паспорт узла (iter6) ── */
#passport-section p { font-size: 12px; color: #eee8d5; line-height: 1.6; }
#passport-section p .pp-k { color: #839496; }
</style>
- Шаг 2: DOM секции «Паспорт»
Edit. old_string (якорь — бейдж категории + начало секции «Что делает»):
<div id="legend-category"></div>
<div class="legend-section"><h4>Что делает</h4><p id="ld-desc">—</p></div>
new_string:
<div id="legend-category"></div>
<div class="legend-section" id="passport-section">
<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">Использований за 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>
<div class="legend-section"><h4>Что делает</h4><p id="ld-desc">—</p></div>
- Шаг 3: рендер «Паспорта» в
showNodeLegend
Edit. old_string (якорь — конец заполнения категории + начало ld-desc):
document.getElementById('legend-category').innerHTML =
`<span style="color:${catColor}; font-weight:600;">● ${catLabel}</span>`;
document.getElementById('ld-desc').textContent = details.desc;
new_string:
document.getElementById('legend-category').innerHTML =
`<span style="color:${catColor}; font-weight:600;">● ${catLabel}</span>`;
// ── Паспорт узла (iter6) ──
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 usesEl = document.getElementById('ld-uses');
if (meta.uses === null || meta.uses === undefined) {
usesEl.textContent = 'нет данных (узел не вызывается напрямую)';
usesEl.style.color = '#586e75';
} else {
usesEl.innerHTML = `<b>${meta.uses}</b> <span style="color:#839496;">(${meta.usesSrc}) · срез ${META_WINDOW}</span>`;
usesEl.style.color = '';
}
// строка «Дубль» — парный дубль (DUPLICATE_GROUPS) либо ролевая заметка ruflo (dupNote)
const dupRow = document.getElementById('ld-dup-row');
const dupEl = document.getElementById('ld-dup');
const pair = DUP_BY_NODE.get(nodeId);
if (pair) {
const partnerNode = NODES.find(n => n.id === pair.partner);
const partnerLabel = partnerNode ? partnerNode.label.replace(/\n/g, ' ') : pair.partner;
dupEl.innerHTML = `⧉ <b>${partnerLabel}</b> (~${pair.pct}%) — ${pair.reason}`;
dupRow.style.display = '';
} else if (meta.dupNote) {
dupEl.innerHTML = `⧉ ${meta.dupNote}`;
dupRow.style.display = '';
} else {
dupRow.style.display = 'none';
}
document.getElementById('ld-desc').textContent = details.desc;
- Шаг 4: smoke — паспорт рендерится на всех классах узлов
Перезагрузить http://localhost:8011/automation-graph.html. browser_evaluate — программно прокликать репрезентативные узлы и снять состояние паспорта:
() => {
const probe = id => {
network.selectNodes([id]);
showNodeLegend(id);
return {
since: document.getElementById('ld-since').textContent,
changed: document.getElementById('ld-changed').textContent,
uses: document.getElementById('ld-uses').textContent,
dupShown: document.getElementById('ld-dup-row').style.display !== 'none',
dup: document.getElementById('ld-dup').textContent,
};
};
return {
sk_wplans: probe('sk_wplans'), // измеримый: 54
pravila: probe('pravila'), // null → «нет данных»
sk_rls: probe('sk_rls'), // D3 → строка «Дубль» с rls-reviewer
ruflo_memory: probe('ruflo_memory'),// dupNote → строка «Дубль»
mem_state: probe('mem_state'), // 73, changed 15.05.2026
};
}
Expected:
sk_wplans.since='09.05.2026',sk_wplans.usesсодержит54и(скил)исрез 09–16.05.2026,sk_wplans.dupShown=false;pravila.uses='нет данных (узел не вызывается напрямую)',pravila.dupShown=false;sk_rls.dupShown=true,sk_rls.dupсодержитrls-reviewerи~85%;ruflo_memory.dupShown=true,ruflo_memory.dupсодержит16 memory-файлов;mem_state.since='07.05.2026',mem_state.changed='15.05.2026',mem_state.usesсодержит73.
browser_console_messages (error) — пусто, кроме favicon 404.
- Шаг 5: коммит
git add docs/automation-graph.html
git commit -m "feat(graph): iter6 — «Паспорт узла» (даты + использование) в легенде"
Показать полный вывод pre-commit.
Task 3: Кнопки футера + режим viewMode (теплокарта + дубли)
Files:
- Modify:
docs/automation-graph.html— CSS (перед</style>), DOM (#cat-legend),IIFE HIGHLIGHT,network.on('click')
Фича 2 (кнопка подсветки) + Фича 3 (кнопка дублей). Режимы heat/dup взаимоисключающие, сбрасывают пофильтровую подсветку.
- Шаг 1: CSS кнопок
.cat-ctl
Edit. old_string (якорь — CSS «Паспорта» из Task 2, конец перед </style>):
/* ── Паспорт узла (iter6) ── */
#passport-section p { font-size: 12px; color: #eee8d5; line-height: 1.6; }
#passport-section p .pp-k { color: #839496; }
</style>
new_string:
/* ── Паспорт узла (iter6) ── */
#passport-section p { font-size: 12px; color: #eee8d5; line-height: 1.6; }
#passport-section p .pp-k { color: #839496; }
/* ── Кнопки режимов в футере (iter6) ── */
.cat-ctl-sep { width: 1px; align-self: stretch; background: #586e75; margin: 0 4px; }
.cat-ctl {
background: #002b36; border: 1px solid #586e75; color: #93a1a1;
border-radius: 4px; padding: 2px 8px; font-size: 11px; cursor: pointer;
transition: background 0.12s, box-shadow 0.12s; user-select: none;
}
.cat-ctl:hover { background: #0d4a5a; color: #fdf6e3; }
.cat-ctl.active {
background: rgba(253,246,227,0.12);
box-shadow: inset 0 0 0 1px rgba(253,246,227,0.4);
color: #fdf6e3;
}
</style>
- Шаг 2: DOM кнопок в
#cat-legend
Edit. old_string (якорь — последний бейдж категории GREEN + закрытие #cat-legend):
<div class="cat-item" data-filter-key="conflict:GREEN"><div class="cat-dot" style="background:#859900; border:1px dashed #859900"></div>🟢 Закрыт правилом</div>
</div>
new_string:
<div class="cat-item" data-filter-key="conflict:GREEN"><div class="cat-dot" style="background:#859900; border:1px dashed #859900"></div>🟢 Закрыт правилом</div>
<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>
</div>
- Шаг 3:
viewModeвIIFE HIGHLIGHT— состояние + теплокарта
Edit 3a — добавить viewMode в state. old_string:
const state = {
selectedNode: null,
legendFilter: new Set(),
};
new_string:
const state = {
selectedNode: null,
legendFilter: new Set(),
viewMode: null, // null | 'heat' | 'dup' — взаимоисключающие режимы (iter6)
};
// ── Теплокарта использования (iter6) — 4 яруса по NODE_META[id].uses ──
function heatOpacity(nodeId) {
const m = NODE_META[nodeId];
const u = m ? m.uses : null;
if (u === null || u === undefined) return 0.5; // нет данных — нейтрально
if (u >= 21) return 1.0; // часто
if (u >= 6) return 0.65; // иногда
if (u >= 1) return 0.35; // редко
return 0.12; // простаивает (uses === 0)
}
// Узлы верхнего яруса теплокарты получают акцентную рамку.
function heatBorderWidth(nodeId) {
if (state.viewMode !== 'heat') return 2;
const m = NODE_META[nodeId];
const u = m ? m.uses : null;
return (typeof u === 'number' && u >= 21) ? 4 : 2;
}
// Переключатель режима — toggle; включение режима гасит пофильтровую подсветку.
function setViewMode(mode) {
state.viewMode = (state.viewMode === mode) ? null : mode;
if (state.viewMode !== null) {
state.legendFilter.clear();
state.selectedNode = null;
}
}
Edit 3b — приоритетная ветка viewMode в начале computeNodeOpacity. old_string:
function computeNodeOpacity(nodeId) {
// Row 1: focus
if (state.selectedNode !== null) {
new_string:
function computeNodeOpacity(nodeId) {
// Row 0: view-mode (iter6) — глобальная картина, поверх focus/filter
if (state.viewMode === 'heat') return heatOpacity(nodeId);
if (state.viewMode === 'dup') return DUP_NODE_SET.has(nodeId) ? OPACITY_FOCUS : OPACITY_DIM;
// Row 1: focus
if (state.selectedNode !== null) {
Edit 3c — borderWidth в applyHighlight. old_string:
function applyHighlight() {
const nodeUpdates = NODES.map(n => ({
id: n.id,
opacity: computeNodeOpacity(n.id),
}));
new_string:
function applyHighlight() {
const nodeUpdates = NODES.map(n => ({
id: n.id,
opacity: computeNodeOpacity(n.id),
borderWidth: heatBorderWidth(n.id), // 4 для верхнего яруса теплокарты, иначе 2 (iter6)
}));
Намеренно:
applyHighlightтеперь нормализуетborderWidthпри каждом вызове (2, либо 4 в heat-режиме). Это обязательно для спецификации §7.3 «при выходе из heat borderWidth возвращается к 2». Побочный эффект: толстая рамка поиска (5) сбрасывается до 2 при клике по фильтру категории после поиска — не регрессия (сам поиск не затронут; проверяется в Task 4 §9.8c8_search), а согласованное поведение (поиск и фильтр — раздельные режимы, поиск зовётclearAll).
Edit 3d — сброс viewMode в clearAll. old_string:
function clearAll() {
state.selectedNode = null;
state.legendFilter.clear();
}
new_string:
function clearAll() {
state.selectedNode = null;
state.legendFilter.clear();
state.viewMode = null;
}
Edit 3e — класс .active на кнопках в updateLegendVisuals. old_string:
function updateLegendVisuals() {
document.querySelectorAll('#cat-legend .cat-item').forEach(item => {
const key = item.dataset.filterKey;
if (!key) return;
if (state.legendFilter.has(key)) item.classList.add('active');
else item.classList.remove('active');
});
}
new_string:
function updateLegendVisuals() {
document.querySelectorAll('#cat-legend .cat-item').forEach(item => {
const key = item.dataset.filterKey;
if (!key) return;
if (state.legendFilter.has(key)) item.classList.add('active');
else item.classList.remove('active');
});
const heatBtn = document.getElementById('cat-ctl-heat');
const dupBtn = document.getElementById('cat-ctl-dup');
if (heatBtn) heatBtn.classList.toggle('active', state.viewMode === 'heat');
if (dupBtn) dupBtn.classList.toggle('active', state.viewMode === 'dup');
}
Edit 3f — обработка клика по .cat-ctl в делегате #cat-legend. old_string:
document.getElementById('cat-legend').addEventListener('click', e => {
const item = e.target.closest('.cat-item');
if (!item || !item.dataset.filterKey) return;
toggleFilter(item.dataset.filterKey);
applyHighlight();
updateLegendVisuals();
});
new_string:
document.getElementById('cat-legend').addEventListener('click', e => {
// iter6 — клик по кнопке режима heat/dup
const ctl = e.target.closest('.cat-ctl');
if (ctl) {
setViewMode(ctl.id === 'cat-ctl-heat' ? 'heat' : 'dup');
applyHighlight();
updateLegendVisuals();
return;
}
const item = e.target.closest('.cat-item');
if (!item || !item.dataset.filterKey) return;
toggleFilter(item.dataset.filterKey);
applyHighlight();
updateLegendVisuals();
});
- Шаг 4: guard клика по узлу при активном
viewMode
В режиме heat/dup клик по узлу открывает паспорт, но не должен сбивать глобальную картину режима выставлением selectedNode.
Edit. old_string:
network.on('click', params => {
if (params.nodes.length === 1) {
const id = params.nodes[0];
HIGHLIGHT.setSelectedNode(id);
HIGHLIGHT.applyHighlight();
// Right panel still shows details of the clicked node (last-clicked, even after toggle-off)
showNodeLegend(id);
} else if (params.edges.length === 1) {
showEdgeLegend(params.edges[0]);
} else if (params.nodes.length === 0 && params.edges.length === 0) {
HIGHLIGHT.setSelectedNode(null);
HIGHLIGHT.applyHighlight();
document.getElementById('legend-panel').classList.remove('visible');
}
});
new_string:
network.on('click', params => {
if (params.nodes.length === 1) {
const id = params.nodes[0];
// iter6 — в режиме heat/dup клик открывает паспорт, но не трогает подсветку режима
if (HIGHLIGHT.state.viewMode === null) {
HIGHLIGHT.setSelectedNode(id);
HIGHLIGHT.applyHighlight();
}
// Right panel still shows details of the clicked node (last-clicked, even after toggle-off)
showNodeLegend(id);
} else if (params.edges.length === 1) {
showEdgeLegend(params.edges[0]);
} else if (params.nodes.length === 0 && params.edges.length === 0) {
if (HIGHLIGHT.state.viewMode === null) {
HIGHLIGHT.setSelectedNode(null);
HIGHLIGHT.applyHighlight();
}
document.getElementById('legend-panel').classList.remove('visible');
}
});
- Шаг 5: smoke — режимы работают и взаимоисключающи
Перезагрузить карту. browser_evaluate:
() => {
const heatBtn = document.getElementById('cat-ctl-heat');
const dupBtn = document.getElementById('cat-ctl-dup');
const op = id => nodesDS.get(id).opacity;
const res = {};
heatBtn.click();
res.heatOn = HIGHLIGHT.state.viewMode; // 'heat'
res.heatBtnActive = heatBtn.classList.contains('active'); // true
res.opFreq = op('hk_post_md'); // 1.0 (uses 1334)
res.opIdle = op('mem_user'); // 0.12 (uses 0)
res.opRare = op('sk_tdd'); // 0.35 (uses 2)
res.opNull = op('pravila'); // 0.5 (uses null)
res.borderTop = nodesDS.get('mcp_pw').borderWidth; // 4
dupBtn.click();
res.dupOn = HIGHLIGHT.state.viewMode; // 'dup'
res.heatOffAfterDup= heatBtn.classList.contains('active'); // false — взаимоисключение
res.dupHighlighted = NODES.filter(n => op(n.id) === 1.0).length; // 12
res.dupMember = op('sk_rls'); // 1.0
res.dupNonMember = op('mcp_pw'); // 0.15
document.getElementById('btn-clear').click();
res.cleared = HIGHLIGHT.state.viewMode; // null
res.borderReset = nodesDS.get('mcp_pw').borderWidth; // 2
res.opAfterClear = op('mem_user'); // 1.0
return res;
}
Expected: { heatOn:'heat', heatBtnActive:true, opFreq:1, opIdle:0.12, opRare:0.35, opNull:0.5, borderTop:4, dupOn:'dup', heatOffAfterDup:false, dupHighlighted:12, dupMember:1, dupNonMember:0.15, cleared:null, borderReset:2, opAfterClear:1 }.
browser_console_messages (error) — пусто, кроме favicon 404.
- Шаг 6: коммит
git add docs/automation-graph.html
git commit -m "feat(graph): iter6 — кнопки «По использованию» / «Дубли» + режим viewMode"
Показать полный вывод pre-commit.
Task 4: Память + финальный приёмочный smoke + завершение ветки
Files:
-
Modify:
memory/project_automation_map.md(вне репозитория —C:\Users\Administrator\.claude\projects\c---------------------crm-------------\memory\) -
Modify:
MEMORY.md(тот же каталог) — однострочный указатель уже есть, обновить хук-фразу -
Шаг 1: финальный приёмочный smoke — 8 критериев спецификации
Перезагрузить http://localhost:8011/automation-graph.html. browser_evaluate — сводная проверка всех 8 критериев приёмки спецификации §9:
() => {
const op = id => nodesDS.get(id).opacity;
const r = {};
// §9.1 — граф рендерится
r.c1_nodes = NODES.length === 83 && EDGES.length === 90;
// §9.7 — NODE_META покрывает все 83 id
r.c7_coverage = NODES.every(n => !!NODE_META[n.id]) && Object.keys(NODE_META).length === 83;
// §9.2/§9.3 — паспорт: измеримый / null / парный дубль / dupNote
showNodeLegend('lh_gitleaks');
r.c2_measured = document.getElementById('ld-uses').textContent.includes('514');
r.c2_dupPair = document.getElementById('ld-dup-row').style.display !== 'none';
showNodeLegend('tooling');
r.c3_null = document.getElementById('ld-uses').textContent.startsWith('нет данных');
showNodeLegend('ruflo_commands');
r.c2_dupNote = document.getElementById('ld-dup').textContent.includes('88 slash-команд');
// §9.4 — теплокарта
document.getElementById('cat-ctl-heat').click();
r.c4_heat = HIGHLIGHT.state.viewMode === 'heat' && op('ag_general') === 1.0;
// §9.5 — дубли: ровно 12
document.getElementById('cat-ctl-dup').click();
r.c5_dup12 = NODES.filter(n => op(n.id) === 1.0).length === 12;
// §9.6 — взаимоисключение + сброс
r.c6_exclusive = !document.getElementById('cat-ctl-heat').classList.contains('active');
document.getElementById('btn-clear').click();
r.c6_clear = HIGHLIGHT.state.viewMode === null && op('mem_user') === 1.0;
// §9.8 — регрессий нет: пофильтровая подсветка iter3 жива
HIGHLIGHT.toggleFilter('group:rules'); HIGHLIGHT.applyHighlight();
r.c8_filter = op('pravila') === 1.0 && op('mcp_pw') === 0.15;
document.getElementById('btn-clear').click();
// §9.8 — поиск жив: matches → borderWidth 5 / opacity 1, прочие гаснут до 0.25
const si = document.getElementById('search');
si.value = 'gitleaks'; si.dispatchEvent(new Event('input'));
const g = nodesDS.get('lh_gitleaks');
r.c8_search = g.opacity === 1.0 && g.borderWidth === 5 && nodesDS.get('pravila').opacity === 0.25;
document.getElementById('btn-clear').click();
// §9.8 — edge-легенда жива: показ ребра переключает панель на edge-контент
showEdgeLegend(edgesDS.get()[0].id);
r.c8_edge = document.getElementById('legend-edge-content').style.display !== 'none'
&& document.getElementById('legend-node-content').style.display === 'none';
document.getElementById('legend-close').click();
return r;
}
Expected — все поля true: { c1_nodes:true, c7_coverage:true, c2_measured:true, c2_dupPair:true, c3_null:true, c2_dupNote:true, c4_heat:true, c5_dup12:true, c6_exclusive:true, c6_clear:true, c8_filter:true, c8_search:true, c8_edge:true }.
Resize-панель (setupResizeHandle) iter6 структурно не трогает — отдельная функция вне зоны правок; ручная проверка: потянуть #legend-handle, ширина меняется. Дополнительно: browser_take_screenshot (теплокарта включена) — визуальная сверка ярусности; browser_console_messages (error) — пусто, кроме внешнего favicon 404. Если хоть один критерий false — это дефект: применить superpowers:systematic-debugging, не править наугад.
- Шаг 2: обновить memory
project_automation_map.md
В файле C:\Users\Administrator\.claude\projects\c---------------------crm-------------\memory\project_automation_map.md дописать в начало описания карты пометку про iter6 (Edit, добавление фразы в существующий абзац — не переписывать файл целиком):
Добавить после фразы про iter5 closure (абзац, начинающийся с «iter5 closed»):
**iter6 closed 2026-05-16:** +секция «📇 Паспорт узла» в легенде всех 83 узлов (дата внедрения / дата последнего изменения / число вызовов за 7 дней — фактический срез окна 09–16.05.2026 из разбора 76 транскриптов сессий) + 2 toggle-кнопки футера: «🔥 По использованию» (теплокарта, 4 яруса) и «⧉ Дубли» (12 узлов из 6 явных пар D1–D5/D7). Чисто аддитивно: NODES/EDGES/NODE_DETAILS не тронуты, метрики 83/90/11 без изменений. Счётчик использования — снимок, статичный HTML его не обновляет; при следующих итерациях значения NODE_META требуют пересборки. Spec/plan — `docs/superpowers/{specs,plans}/2026-05-16-automation-graph-iter6-node-meta-*.md`.
- Шаг 3: коммит
git add docs/automation-graph.html docs/superpowers/plans/2026-05-16-automation-graph-iter6-node-meta.md
git commit -m "docs(graph): iter6 closure — план + memory-фиксация"
Memory-файл project_automation_map.md вне репозитория — в коммит не входит, в git add не включать. Показать полный вывод pre-commit (в коммите есть .md → сработают markdownlint + cspell; gitleaks всегда).
- Шаг 4: завершение ветки
Вызвать superpowers:finishing-a-development-branch: прогнать проверки, определить окружение, представить заказчику меню (merge / PR / оставить / отбросить). Перед заявлением «готово» — superpowers:verification-before-completion с реальным прогоном smoke (не «должно работать»).
Приложение А — методика и воспроизводимость фактических данных
Спецификация §5 делегирует производство значений since/changed/uses этапу writing-plans. Ниже — метод (чтобы числа были воспроизводимы) и фактические сырые выводы, на которых построен NODE_META из Task 1.
А.1. uses — счётчик вызовов за окно 09–16.05.2026 (UTC)
Источник — 76 транскриптов сессий Claude Code C:\Users\Administrator\.claude\projects\c---------------------crm-------------\*.jsonl. Каждая строка — JSON-запись; вызовы инструментов — блоки tool_use в сообщениях ассистента. Окно — по полю timestamp каждой записи: 2026-05-09T00:00:00Z … 2026-05-16T23:59:59Z.
Скрипт разбора (node, ES-модуль):
import fs from 'fs';
import path from 'path';
const DIR = 'C:/Users/Administrator/.claude/projects/c---------------------crm-------------';
const S = Date.parse('2026-05-09T00:00:00.000Z'), E = Date.parse('2026-05-16T23:59:59.999Z');
const skill = {}, agent = {}, mcp = {}, memRead = {};
const sessions = new Set();
let editMd = 0, editClaudeMd = 0, editSchema = 0, econ = 0;
const ECON = /экономия\s*\d+\s*%/i;
for (const f of fs.readdirSync(DIR).filter(x => x.endsWith('.jsonl'))) {
for (const ln of fs.readFileSync(path.join(DIR, f), 'utf8').split('\n')) {
if (!ln.trim()) continue;
let o; try { o = JSON.parse(ln); } catch { continue; }
const ts = o.timestamp ? Date.parse(o.timestamp) : NaN;
if (isNaN(ts) || ts < S || ts > E) continue;
sessions.add(f);
const m = o.message; if (!m || !Array.isArray(m.content)) continue;
if (o.type === 'user' && m.role === 'user' && !m.content.some(c => c.type === 'tool_result')) {
if (ECON.test(m.content.filter(c => c.type === 'text').map(c => c.text || '').join('\n'))) econ++;
}
for (const c of m.content) {
if (c.type !== 'tool_use') continue;
const n = c.name || '', inp = c.input || {};
if (n === 'Skill') skill[inp.skill || '(none)'] = (skill[inp.skill || '(none)'] || 0) + 1;
else if (n === 'Agent' || n === 'Task') agent[inp.subagent_type || '(none)'] = (agent[inp.subagent_type || '(none)'] || 0) + 1;
else if (n.startsWith('mcp__')) mcp[n.split('__')[1]] = (mcp[n.split('__')[1]] || 0) + 1;
else if (n === 'Read') {
const fp = (inp.file_path || '').replace(/\\/g, '/');
if (/\/memory\//i.test(fp) && fp.endsWith('.md')) { const b = fp.split('/').pop(); memRead[b] = (memRead[b] || 0) + 1; }
} else if (n === 'Edit' || n === 'Write') {
const fp = (inp.file_path || '').replace(/\\/g, '/'), b = fp.split('/').pop() || '';
if (b === 'CLAUDE.md') editClaudeMd++; else if (b.endsWith('.md')) editMd++;
if (/db\/schema\.sql$/i.test(fp)) editSchema++;
}
}
}
}
// (вывод объектов skill/agent/mcp/memRead + sessions.size/editMd/editClaudeMd/editSchema/econ)
Сырой вывод (07–16.05 прогон, окно 09–16.05): 67 сессий, 12015 tool_use, 0 ошибок разбора.
- Skill → узлы-скилы:
writing-plans54,brainstorming43 +1 (plugin:superpowers:brainstorming) = 44,subagent-driven-development33,verification-before-completion25,executing-plans11,finishing-a-development-branch10,systematic-debugging10,dispatching-parallel-agents3,using-git-worktrees3,test-driven-development2,receiving-code-review1 →sk_coderev,q-item-add1;claude-md-management:claude-md-improver12 +revise-claude-md3 = 15 →claude_md_mgmt;frontend-design:frontend-design1 →fd_plugin. Неотображённые скилы (spec-review,writing-skills,elements-of-style,rls-check,ui-ux-pro-max) →uses: 0. - Agent (
subagent_type):general-purpose473 + 39 безsubagent_type(по семантике инструмента default = general-purpose) = 512;Explore26;claude-code-guide4;rls-reviewer2;pest-parallel-debugger2. Прочие (claude3,researcher/code-analyzer/security-auditorпо 1) — не узлы карты. - MCP (
mcp__<server>__*):playwright706,laravel-boost73,ruflo36.github/redis/sentry/semgrep/magic— 0 (весь GitHub-флоу шёл черезgh/gitв Bash). - memory Read:
project_state.md73,feedback_environment.md41,reference_github.md33,reference_archive.md21,project_ruflo_integration.md18,project_supplier_integration.md13,feedback_plugin_paired_stack.md9,project_full_audit_2026-05-13.md3,feedback_superpowers_hard_rule.md/project_claude_brain.md/project_dev_indices.md/project_phase1_strategy.mdпо 1. Не упомянутые memory-узлы (user_profile,feedback_communication,reference_handoff,project_portal_redesign) → 0. - хуки: 67 сессий →
hk_session; 276 промптов с тегом «экономия N%» →hk_economy; правки*.md(кроме корневогоCLAUDE.md) 1334 →hk_post_md; правки корневогоCLAUDE.md249 (отдельный прогон с разбором полного пути: 246 абсолютных + 3 относительных, все — корневой проектный) →hk_pre_claude; правкиdb/schema.sql22 →hk_post_schema. - lefthook: коммитов в окне 514 (
git log --since --until | wc -l). Per-job — коммиты, тронувшие файлы под glob job'а (git log --name-only):*.md232 (lh_mdlint/lh_cspell),*.html43 (lh_stylelint),app/**/*.php134 (lh_pint/lh_larastan),app/resources/js/**/*.{ts,vue}120 (lh_eslint),*.sql19 (lh_squawk), все 514 (lh_gitleaks). Pre-push (lh_gitleaks2/lh_lychee) — 56 обновленийorigin/mainизgit reflog show origin/mainв окне (push'и + редкие fetch'и, приближение). - ruflo:
ruflo_mcp36 (измерено,mcp__ruflo__*).ruflo_recall_hook220 (промпты с момента регистрации хука 15.05.2026 — отдельный прогон).ruflo_queen/ruflo_workers/ruflo_plugins/ruflo_agents_catalog/ruflo_commands—0по верифицированной инспекции рантайма iter5 (рой idle, 0 задач; каталог/команды/плагины не задействованы).ruflo_daemon/ruflo_memory—null(см. А.3).
А.2. since / changed — даты
- Узлы-файлы в git (4 документа-правила,
lefthook.yml,.mcp.json, проектный.claude/settings.json,.claude/skills/*/SKILL.md,.claude/agents/*.md,tools/ruflo-recall-hook.mjs):since= первый коммит файла (git log --reverse --diff-filter/-S<токен>для отдельных job'ов/серверов/хуков в общем файле);changed= последний коммит с изменением (для документов-правил — дата последнего бампа версии). Сырьё: документы-правила —pravila06.05→15.05,claude_md06.05→15.05,psr_v109.05→15.05,tooling06.05→15.05. lefthook job'ы —sincelh_gitleaks/lh_mdlint/lh_cspell/lh_stylelint/lh_gitleaks2/lh_lychee06.05,lh_pint/lh_larastan/lh_squawk/lh_eslint08.05;-Sпоказал, что токен job'а не менялся после внедрения →changed: '—'. MCP —mcp_pw/mcp_gh06.05,mcp_boost08.05,mcp_semgrep10.05,mcp_sentry/mcp_redis13.05,ruflo_mcp15.05 (changed: '—'). Проектные хуки —hk_post_md06.05,hk_pre_claude/hk_post_schema13.05 (changed: '—'). Проектные скилы/агенты —sk_rls/sk_qitem/ag_pest/ag_rlsвсе 13.05 (файлы добавлены в репозиторий 13.05.2026;changed: '—').ruflo_recall_hook— 15.05 (changed: '—'). - Memory-файлы (вне репозитория):
changed= mtime файла (надёжен);since= более раннее из mtime-birthtime и первого упоминания файла в транскриптах (birthtime ненадёжен — Write пересоздаёт файл). Правило:changedпоказывается, только если позжеsince, иначе'—'. Сырьё первого упоминания: 07.05 —user_profile/feedback_communication/feedback_environment/project_state/reference_archive/reference_github; 08.05 —reference_handoff/project_phase1_strategy; 09.05 —feedback_superpowers_hard_rule/feedback_plugin_paired_stack; 10.05 —project_supplier_integration; 11.05 —project_claude_brain; 12.05 —project_portal_redesign/project_dev_indices; 13.05 —project_full_audit_2026-05-13; 15.05 —project_ruflo_integration. mtime:feedback_environment/project_state/reference_archive/reference_github15.05,mem_ruflo16.05, остальные — см.NODE_META. - ruflo runtime-узлы (9): big-bang 15.05.2026 (коммиты установки
55c49c9и активации рантайма5ac2961) →since: '15.05.2026',changed: '—'. - Связка-инструменты без проектного git-события: 14 скилов Superpowers + плагин
superpowers—since09.05.2026 (дата §12 hard-rule Pravila v1.4, presupposes установленный Superpowers),changed: '—'.fd_plugin/upm/claude_md_mgmt— 10.05.2026 (формализация в CLAUDE.md v1.78/v1.83/v1.84),changed: '—'.hookify_plugin+ агентыag_hookify/ag_pcreator/ag_pvalid/ag_skreview— надёжного источника нет →since: '—'. Встроенные агенты Claude Code (ag_explore/ag_general/ag_plan/ag_guide/ag_statusline) →since: 'встроено в Claude Code'.mcp_21st— глобальный (~/.claude.json, не в репозитории),since10.05.2026 (формализация v1.83).hk_session/hk_economy— в глобальном~/.claude/settings.json(не в репозитории):hk_economysince10.05.2026 (документированная сборка 6-компонентной economy-архитектуры вfeedback_superpowers_hard_rule.md);hk_sessionsince: '—'(надёжной проектной записи нет).
А.3. Уточнения к спецификации §5.1 (выработаны при производстве значений)
Спецификация §5 явно делегировала производство значений плану; таблица §5.1 — это метод (a-priori допущения), которые план уточнил по факту:
fd_plugin= 1 (неnull). §5.1 относил плагины-обёртки кnull. Ноfd_pluginне имеет на карте дочерних узлов-скилов (в отличие отsuperpowersс 14 дочерними), напрямую вызывается как скилfrontend-design:frontend-designи был вызван 1 раз.nullотрендерил бы ложную фразу «узел не вызывается напрямую». →uses: 1.upm= 0 (неnull).ui-ux-pro-max— напрямую вызываемый скил, измеримый, 0 вызовов в окне →uses: 0(измеримо простаивает), а неnull.superpowers,hookify_plugin=null— оставлены: подлинные контейнеры (использованиеsuperpowersраспределено по 14 дочерним узлам-скилам;hookify_pluginработает через/hookify+ дочерний агентag_hookify).ruflo_daemon,ruflo_memory=null. §5.1 относил все ruflo runtime-узлы к'инспекция'с числом. Но демон — автономный таймер-процесс (не «вызывается»), а память ruflo — пассивное хранилище (доступ — черезruflo_mcp/recall-хук, иначе двойной счёт). По определениюnullиз §3.2 («узел не вызывается напрямую») —nullкорректен. Остальные 5 ruflo runtime-узлов измеримо-простаивают →0(спецификация §5.1 прямо приводит «idle-воркеры ruflo» как пример0).hk_session/hk_economy— живут в глобальном~/.claude/settings.json(вне репозитория),sinceне выводится из git; см. А.2.
А.4. Пороги теплокарты
Спецификация §7.3 («пороги уточняются в плане по фактическим значениям»). Фактическое распределение uses (диапазон 0…1334) подтверждает 4-ярусную раскладку §7.3 без изменений: ≥21 (часто) / 6–20 (иногда) / 1–5 (редко) / 0 (простаивает) / null (нет данных, нейтрально 0.5). Реализовано в heatOpacity (Task 3, Шаг 3).
Самопроверка плана
- Покрытие спецификации. §3 (модель данных) → Task 1. §4 (секция «Паспорт») → Task 2. §6 (дубли D1–D5/D7) →
DUPLICATE_GROUPSв Task 1 + строка «Дубль» в Task 2; R-группы D6/D8/D9 →dupNoteу 3 ruflo-узлов в Task 1. §7 (две кнопки +viewMode) → Task 3. §5 (методика чисел) → Приложение А + Task 1. §8 (честность/устаревание) → «срез 09–16.05.2026» в рендере (Task 2) + memory-фиксация (Task 4). §9 (8 критериев приёмки) → финальный smoke Task 4 Шаг 1. Пробелов нет. - Заглушек нет. Весь код Task 1–3 приведён целиком;
NODE_META— 83 фактические записи; команды и ожидаемые выводы конкретны. - Согласованность типов.
viewMode— строгоnull|'heat'|'dup'во всех точках.heatOpacity/heatBorderWidth/setViewModeопределены в Task 3 Шаг 3 и там же используются.DUP_BY_NODE/DUP_NODE_SET/NODE_META/META_WINDOWопределены в Task 1, используются в Task 2 (рендер) и Task 3 (heatOpacity,dup-ветка) — порядок определения раньше использования (SECTION 3.6доSECTION 5/SECTION 8).state.viewModeчитается снаружи IIFE через уже экспонированныйHIGHLIGHT.state. - Объём. Одна цельная аддитивная итерация одного файла; декомпозиция на под-проекты не нужна.
- Атомарность. 4 задачи = 4 коммита, каждый оставляет карту в рабочем состоянии (Task 1 — данные не используются; Task 2 — паспорт рендерится; Task 3 — кнопки активны; Task 4 — закрытие).