docs(router-mentor): H-block design — node cards (R-11) + conflict edges (R-12)

Дизайн блока H реестра хвостов эпика «роутер-наставник»: наполнение графа
скилов данными — 86 карточек-контрактов (per-node, skill=slug) + явные
конфликт-рёбра (Tooling §9 + ADR, двусторонние) + инвариант покрытия (TDD).
Механику 3-A/3-B/3-C/3-D не трогаем. commit-not-push.

coverage: skill:brainstorming
This commit is contained in:
Дмитрий
2026-06-08 17:43:03 +03:00
parent c6ceee8ac5
commit ce8d9c3bd8
@@ -0,0 +1,177 @@
# Блок H — карточки узлов (R-11) + конфликт-рёбра (R-12) — дизайн
**Дата:** 2026-06-08 · **Эпик:** «роутер-наставник» · **Ветка:** `worktree-brainrepo`
**Статус:** дизайн на согласовании → writing-plans.
**Источник задачи:** реестр хвостов `docs/superpowers/plans/2026-06-08-router-mentor-loose-ends-registry.md`, блок **H** (R-11 + R-12) + handoff `docs/superpowers/plans/2026-06-08-router-mentor-H-cards-session-handoff.md`.
---
## 1. Цель и границы
Граф скилов роутера (Машина 3) живёт, но **пуст данными**: механика 3-A (контракты) / 3-B (граф узлов) / 3-C (охват) / 3-D (движок) построена и покрыта инвариантами, а наполнения нет — роутер выбирает узлы «вслепую» без карточек-контрактов и без явных конфликт-рёбер.
Этот блок наполняет граф **данными**:
- **R-11** — карточки-контракты для **всех 86 узлов** реестра (`docs/registry/contracts/<slug>.contract.json`). Сейчас написаны 2 seed-файла (`writing-plans`, `operations-process-doc`).
- **R-12** — явные конфликт-рёбра `attributes.conflicts_with` в `docs/registry/nodes.yaml`. Сейчас 0.
- **Acceptance** — один новый инвариант покрытия (код, TDD).
**Жёсткие границы (что НЕ делаем):**
- НЕ трогаем механику 3-A/3-B/3-C/3-D (готова и протестирована) — только данные + 1 инвариант.
- НЕ парсим free-text для конфликтов — только explicit `attributes.conflicts_with` (как и заложено в `node-graph.conflictsOf`).
- НЕ добавляем конфликт-рёбра без канона (Tooling §9 / ADR).
- НЕ трогаем нормативку (§0 cross-refs Pravila/PSR/Tooling/CLAUDE.md), `CLAUDE.md` в worktree, `.claude/settings.json`.
- commit-not-push: пуш только по слову «пуш».
---
## 2. Вселенная узлов (факт реестра)
`docs/registry/nodes.yaml`**86 узлов**:
| status | кол-во | примеры |
|---|---|---|
| active | ~78 | phase-0/1/2/3 + off-phase инструменты |
| deferred | 6 | #44 Figma / #50 Jupyter / #54 n8n / #67 NightOwl / #82 DataForSEO / #83 Unisender |
| dormant | 1 | ruflo |
| historic | 1 | #1 PostgreSQL MCP (заменён Boost) |
Каждый узел уже несёт: `id`, `name`, `slug`, `category`, `subcategory`, `status`, `capabilities`, `triggers`, `boundaries`, `chain_membership`, `attributes`. Это **первичный материал** для карточек.
---
## 3. Зафиксированные решения (согласовано с владельцем 2026-06-08)
| # | Решение | Значение |
|---|---|---|
| Р1 | **Гранулярность** | **per-node**: 86 карточек, по одной на узел; `contract.skill` = `node.slug`. Зонтичные узлы (superpowers #19, operations #51, marketing #74, ruflo) — **одна карточка-зонтик** на узел (общий контракт), не разворачиваются в под-скилы. |
| Р2 | **Scope** | **Все 86** полными карточками, включая 6 deferred + ruflo dormant + #1 historic. |
| Р3 | **Seed-файлы** | 2 готовых файла под-скилов (`writing-plans`, `operations-process-doc`) — **под-скилы, не узлы**; остаются как безвредные бонус-контракты. В инвариант покрытия не входят (инвариант — про узлы). |
| Р4 | **own/external** | `own` = наши self-authored project-скилы (`.claude/skills/**`, писали мы) + superpowers (по прецеденту seed `writing-plans`). `external` = marketplace-плагины + MCP-серверы + вендоренные чужие скилы + ruflo. `external` несёт `source{version,hash,path}`. |
| Р5 | **hash external** | Настоящий `sha256(SKILL.md)` + `path` — где есть локальный SKILL.md в репо (вендоренные/свои скилы). Где локального файла нет (MCP-серверы, marketplace-плагины) — нулевой плейсхолдер `0…0` + `path=""` (G4 инертен, как в seed `operations-process-doc`). |
| Р6 | **Конфликт-рёбра** | Источник — **только** Tooling §9 (запрещённые дубли) + ADR-границы (006/007/.../017). Двусторонние (A↔B оба несут ссылку). graphify — вторичная сверка кандидатов, не источник. |
| Р7 | **Acceptance** | Новый инвариант покрытия (TDD): каждый узел реестра имеет валидный контракт; конфликт-рёбра резолвятся и симметричны. |
| Р8 | **Батчинг** | По `category`/`subcategory` (фазы + off-phase подкатегории). |
---
## 4. Компонент 1 — карточки-контракты (данные)
**Где:** `docs/registry/contracts/<slug>.contract.json` (имя файла = slug узла; механика `loadRegistry` читает любой `*.contract.json`, маппинг узел↔контракт — по полю `skill`).
**Форма** (схема `tools/skill-contract.mjs`, валидатор `validateContract`):
| Поле | Тип | Содержание |
|---|---|---|
| `skill` | string | = `node.slug` (ключ связи узел↔контракт; роутер/`dispatchContract` ищут по нему) |
| `kind` | `own`\|`external` | по правилу Р4 |
| `needs` | string[] | что навыку нужно на входе (рёбра графа 3-C) |
| `produces` | string[] | что навык выдаёт (рёбра графа 3-C) |
| `constraints` | string[] | ограничения/область («только пишет план», «READ-ONLY», «marketplace skill — outputs doc only») |
| `preview-form` | enum | `none`\|`outline`\|`mockup`\|`sample`\|`dry-run`\|`diagram` — форма дешёвого образца (L1), по природе узла |
| `defaults` | string[] | разумные дефолты-на-вето (L3) |
| `key-decisions` | string[] | ключевые решения, которые навык поднимает (look-ahead роутера) |
| `acceptance-criteria` | string[] | критерий приёмки навыка заранее (L7) |
| `inherent` | `{need,rationale}[]` | «природные» нужды навыка (опционально). `rationale` — про **природу** навыка, НЕ про прошлый промах (гард R4: маркеры «забыл/прошлый раз/…» отклоняются валидатором) |
| `source` | `{version,hash,path}` | **только** для `external` (Р5) |
**Источники контента (по приоритету):**
1. `nodes.yaml``capabilities` / `boundaries` / `triggers` / `subcategory` (первичный, узлы уже описаны).
2. SKILL.md (для скилов) — реальное описание навыка.
3. Tooling Прил.Н §4.NN (роль/границы узла в каноне).
4. CLAUDE.md §3 (категория/роль).
**Нейтральность (G1, `checkContractNeutrality`).** Карточка — про **сам инструмент**, без проектных фактов («лендингу нужен teal» → хардкод). Проектные термины инъецируются страж-проверкой; авторим карточки портативно.
**Зонтичные узлы (Р1).** superpowers #19 / operations #51 / marketing #74 / ruflo — карточка описывает **узел как целое** (общие needs/produces зонтика), а не разворачивается в под-скилы. Seed-файлы `writing-plans`/`operations-process-doc` (под-скилы) остаются параллельно как бонусы.
---
## 5. Компонент 2 — конфликт-рёбра (данные)
**Где:** `nodes.yaml``<node>.attributes.conflicts_with: [<ref>, …]` (ref = id/slug/name, резолвится `resolveNode`). Читается `node-graph.conflictsOf` (free-text не парсит — только explicit).
**Источник (Р6) — только канон:**
- **Tooling §9** — явный список «два инструмента на одну задачу» (запрещённые дубли).
- **ADR-границы** — зафиксированные разграничения (ADR-006 icon-path / 007 ml / … / 017 graphify и пр.).
**Двусторонность.** Если A конфликтует с B — ссылка ставится **в обоих** узлах (`A.conflicts_with ⊇ [B]` И `B.conflicts_with ⊇ [A]`). Симметрию и отсутствие висячих ссылок проверяет инвариант (Компонент 3) + проход `sharp-edges`.
**graphify — вторичная сверка (не источник).** `graphify-out/graph.json` Grep-доступен (бинарь не на PATH, но JSON читается напрямую). Используется так: Grep на `semantically_similar_to`/`conceptually_related_to` между инструментами → **кандидаты** в конфликты → каждый кандидат **проверяется по §9/ADR**; без канона ребро не добавляется.
---
## 6. Компонент 3 — инвариант покрытия (код, TDD)
Новый тест-инвариант на **реальном** реестре (стиль m3a/m3b/m3c — `tools/m3*-invariants.test.mjs`):
1. **Покрытие контрактами:** для каждого узла `nodes.yaml` существует контракт-файл, чей `skill === node.slug`, и он проходит `validateContract` (`ok:true`). Дубли `skill` отлавливаются (как в `buildRegistry`).
2. **Целостность конфликт-рёбер:** каждый ref в любом `attributes.conflicts_with` резолвится (`resolveNode ≠ null`) **и** симметричен (`A→B ⟹ B→A`).
Цикл TDD: тест добавляется → **RED** (карточек/рёбер нет) → авторинг данных → **GREEN**. Поскольку инвариант — это `tools/*.test.mjs` (не prod-`tools/*.mjs`), enforce-tdd-gate не блокирует (он только на prod-`tools/*.mjs`).
**Заметка по 86 vs active.** Раз Р2 = все 86 получают карточки, инвариант покрытия проверяет **все узлы**, а не только active (сильнее и согласован с Р2).
---
## 7. Поток данных
```
nodes.yaml (86 узлов)
│ авторинг по узлам (capabilities/SKILL.md/Tooling §N/CLAUDE.md §3)
docs/registry/contracts/<slug>.contract.json (86)
│ loadRegistry → buildRegistry (валидация + дрейф G4 для external)
роутер look-ahead (buildRouterPrompt.contracts) / dispatchContract(skill)
связь: node.slug == contract.skill
nodes.yaml.attributes.conflicts_with → buildNodeGraph → conflictsOf(ref)
инвариант покрытия связывает оба слоя.
```
---
## 8. Батчи (≈18–20) и порядок
Батч = `category` / `subcategory` (Р8). Внутри батча узлы однородны → консистентный тон карточек + удобная variant-analysis.
- **Фазовые:** phase-0 · phase-1 · phase-2 (+Frontend Design) · phase-3.
- **Off-phase подкатегории:** architecture-tooling · audit-security · design-tooling · integration-tooling · ml-ai-tooling · business-process · discovery-tooling · authoring-tooling · dev-support · project-management · finance-tooling · backend-tooling · infosec-tooling · marketing-tooling · knowledge-graph-tooling · debug-runtime · UI-pool (UPM/21st/claude-md-management) · project-agent (#84/#85).
- **Прочее:** ruflo (dormant) + #1 historic.
- **Отдельные батчи:** (а) конфликт-рёбра R-12 — **после** всех карточек; (б) инвариант покрытия — отдельный **код-батч** (TDD).
**Цикл per-батч:** `audit-context-building` → авторинг карточек → `validateContract` 0 ошибок + инварианты GREEN (`test-driven-development`; `systematic-debugging` на красный) → `verification-before-completion``variant-analysis` (консистентность полей/тона между карточками батча).
**Гейт закрытия эпика:** `audit-context-building``sharp-edges` (двусторонняя согласованность конфликт-рёбер, нет висячих ref) → `variant-analysis``regression` (tools-only ≥ baseline) → `verification-before-completion`**commit (не push)**.
---
## 9. Тестирование (сводка)
| Уровень | Что | Где |
|---|---|---|
| Форма контракта | `validateContract.ok` на каждом новом файле | стиль m3a |
| Охват реестра | новый инвариант покрытия (§6 п.1) | новый `tools/m3*-…test.mjs` |
| Граф конфликтов | резолв + симметрия (§6 п.2) | расширение m3b / новый |
| Регрессия | vitest tools-only ≥ baseline | `cd app && npx vitest run --config vitest.config.tools.mjs … --reporter dot` |
---
## 10. Открытые мелочи (в реализацию, не блокеры)
- Точный список конфликт-рёбер собирается в батче R-12 (Grep Tooling §9 + ADR + вторичная сверка graphify graph.json).
- Для каждого external-узла — определить наличие локального SKILL.md (Р5): есть → реальный hash+path; нет → нулевой плейсхолдер.
- Имя файла seed `operations-process-doc.contract.json` — под-скил, остаётся; узловой контракт operations пишется как `operations.contract.json` (skill=`operations`).
---
## 11. Якоря
- Реестр хвостов: `docs/superpowers/plans/2026-06-08-router-mentor-loose-ends-registry.md` (блок H).
- Handoff: `docs/superpowers/plans/2026-06-08-router-mentor-H-cards-session-handoff.md`.
- Механика: `tools/skill-contract.mjs`, `tools/skill-contract-registry.mjs`, `tools/node-graph.mjs`, `tools/router-engine.mjs`, инварианты `tools/m3a..m3d-*-invariants.test.mjs`.
- Образцы: `docs/registry/contracts/writing-plans.contract.json` (own), `…/operations-process-doc.contract.json` (external).