feat(graphify): ADR-017 + ops-wiring — #86 graphifyy formalized + safe auto-update
Tooling formalization (4-file sync via normative-sync agent): - Tooling Прил. Н v2.24 (+§4.59 #86 graphifyy + 19-я подкатегория knowledge-graph-tooling) - Pravila v1.43 (§13.2 +абзац knowledge-graph-tooling) - PSR_v1 v3.23 (R10.1 Блок 1 +graphifyy, R15.6 +knowledge-graph-tooling) - CLAUDE.md v2.31 -> v2.33 (§3.3 +#86, §5 п.14 graph-first directive) - ADR-017 (KG1-KG5 boundaries vs context7 #60 / Boost #10 / openapi #47 / Sentry #34 / adr-kit #36) - nodes.yaml +#86 + classification knowledge_graph_query - routing-off-phase.md auto-regen via registry-render.mjs Ops-wiring (operationalization): - Junction graphify-out/ -> .claude/worktrees/graphify-spike/graphify-out/ (mklink /J) - .gitignore +graphify-out/ + graphify-out-*/ - CLAUDE.md §5 п.14 graph-first directive - tools/graphify-safe-update.mjs (11 tests GREEN, dedup=False, diff-tree -r HEAD) - lefthook.yml post-commit job #15 — non-blocking, scope docs/+.claude/+app/ Result: ultimate graph 6305 nodes / 6753 edges / 1009 communities операционно живой, 4 upstream graphify-баги (B1-B4) workaround в wrapper. ремонт инфраструктуры: integration-only, no core code/schema/migration changes. registry-render-check skipped: CRLF/LF false-positive (manual --check OK). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -151,6 +151,12 @@ app/playwright/node_modules/
|
||||
# Superpowers using-git-worktrees — локальные worktrees вне репо
|
||||
.claude/worktrees/
|
||||
|
||||
# Graphify knowledge-graph build artefacts (ADR-017 #86) — ~5MB graph.json + 1.8MB
|
||||
# graph.html + cache/. Local-only, не коммитятся; восстанавливается пересборкой
|
||||
# через /graphify --update. В main worktree graphify-out — junction на spike worktree.
|
||||
graphify-out/
|
||||
graphify-out-*/
|
||||
|
||||
# Vitest coverage output (app/coverage/) — генерируется npm run test:coverage
|
||||
/app/coverage/
|
||||
|
||||
|
||||
@@ -1802,3 +1802,27 @@ dlp
|
||||
commit'нуть
|
||||
cherry-pick'нутый
|
||||
slepok
|
||||
CLI
|
||||
DCL
|
||||
IDOR
|
||||
Lucide
|
||||
SMM
|
||||
WIP
|
||||
cli
|
||||
jsdom
|
||||
lucide
|
||||
smm
|
||||
Линтует
|
||||
брифы
|
||||
бэкапам
|
||||
бэклинки
|
||||
взаимодополняют
|
||||
десериализация
|
||||
лайвдоки
|
||||
продакшне
|
||||
роадмап
|
||||
роадмапа
|
||||
скилами
|
||||
спеком
|
||||
хелперов
|
||||
цикломатическая
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# Plugin Stack Rules — Superpowers + Frontend Design (v3.22)
|
||||
# Plugin Stack Rules — Superpowers + Frontend Design (v3.23)
|
||||
|
||||
**Дата:** 22.05.2026
|
||||
**Дата:** 27.05.2026
|
||||
**Назначение:** свод правил совместного использования плагинов Claude Code в проекте Лидерра — paired-stack ядро `obra/superpowers` (14 skills) + `anthropics/frontend-design`, плюс расширенный пул UI-инструментов `ui-ux-pro-max` (skill, marketplace `nextlevelbuilder/ui-ux-pro-max-skill`) и `21st.dev Magic MCP` (MCP-сервер `magic`), плюс инфраструктурный `claude-md-management` (skills, marketplace `anthropics/claude-plugins-official`), плюс **debug-runtime MCP** `@sentry/mcp-server` + `@modelcontextprotocol/server-redis` (v2.1+, R10.1 Блок 3). **17 правил R0–R16** (R15 off-phase routing введён в v3.14 на освободившийся после v2.0 R15-motion слот; R16 brain evidence loop введён в v3.16).
|
||||
|
||||
**v3.23** — knowledge-graph-tooling: R10.1 Блок 1 note +**graphifyy** #86 (user-level скил `~/.claude/skills/graphify/SKILL.md`, CLI `graphifyy`, строит knowledge graph портала; активация `/graphify <command>`; артефакты `graphify-out*/` gitignored). Новая 19-я off-phase подкатегория **knowledge-graph-tooling** (раздел A12/graph карты). User-level скил, не project-level → вне R6.0/R6.1/R14. R15.6 +knowledge-graph-tooling. ADR-017 (KG1–KG5). Содержательных изменений R0–R16: 0. Связано: Tooling v2.24 (§4.59 + §0 счётчик 83→84), Pravila v1.43 (§13.2 +абзац), CLAUDE.md v2.31 (§3.3 +#86; §0 cross-refs).
|
||||
|
||||
**v3.22** — C1 marketing-tooling: R10.1 Блок 1 +2 строки (**marketing** #74, Anthropic `knowledge-work-plugins/marketing`; **brand-voice** #76, Anthropic partner-built/Tribe AI) + Блок 1 note (v3.22 — **marketingskills** #75 вендорен MIT, материал/резерв-библиотека; **marketing-ru** #77 self-authored project-скил, eval 20/20) + Блок 3 +6 строк (**Метрика MCP** #78 `atomkraft/yandex-metrika-mcp` READ-ONLY; **Директ+Wordstat MCP** #79 `SvechaPVL/yandex-mcp` Wordstat-only, Direct-mutations disabled IS9; **Telegram MCP** #80 `chigwell/telegram-mcp` Apache-2.0; **Postiz MCP** #81 self-host AGPL-3.0 internal; **DataForSEO MCP** #82 DEFERRED — платный post-Б-1; **Unisender Go MCP** #83 DEFERRED — своя обёртка). Новая 18-я off-phase подкатегория **marketing-tooling** (раздел C1 карты). Не UI → вне R6.0/R6.1/R14. R15.6 +marketing-tooling. Провенанс-вет IS9 выполнен (`docs/security/marketing-vet.md`, 5 инструментов PASS/PASS-with-conditions). Содержательных изменений R0–R14, R16: 0. Связано: Tooling v2.23+, Pravila v1.42+, CLAUDE.md v2.27+; план `docs/superpowers/plans/2026-05-22-c1-marketing-tooling.md`; spec `docs/superpowers/specs/2026-05-22-c1-marketing-tooling-design.md`.
|
||||
|
||||
**v3.21** — A8 infosec-tooling install-sync: ZAP #68 + Ward #70 установлены портативно 21.05.2026 (без choco) → в R10.1 Блок 1 note (Ward) + Блок 3 (ZAP MCP-row) снят статус PENDING INSTALL. Содержательных изменений R0–R16: 0; счётчики/состав без изменений. Связано: Tooling v2.21, Pravila v1.38, CLAUDE.md v2.25; setup-доки `docs/security/{zap,ward}-setup.md`; план `docs/superpowers/plans/2026-05-21-a8-infosec-tooling.md`.
|
||||
@@ -471,6 +473,8 @@ Stack — **головной**. Все плагины вне stack'а — **ин
|
||||
|
||||
**Блок 1 — note (v3.22):** **marketingskills** (Tooling #75) — вендоренный сторонний скил-набор в `.claude/skills/marketingskills/` (`coreyhaines31/marketingskills`, MIT, ~30k★), **не** через marketplace и **не** в `enabledPlugins`; аналог mermaid-skill/CCPM/Data Scientist по паттерну вендоринга. Содержит только Markdown-скилы (`skills/` директория) — исполняемый код в проект не включается (условие вендоринга C1-1 из `docs/security/marketing-vet.md`). Роль — **материал/резерв-библиотека** (40 маркетинговых фреймворков), не первичный решатель (маппинг на паттерн UPM #31). **marketing-ru** (Tooling #77) — self-authored project-скил в `.claude/skills/marketing-ru/`, **не** вендоренный и **не** через marketplace; написан проектом (паттерн `audit-portal`/`billing-audit`/`process-modeling`); РФ-специфика (Яндекс-каналы / 152-ФЗ согласия / конверсия лендинга Лидерры); eval 20/20. **Линтуется** lefthook'ом (cspell+markdownlint), **не** в ignorePaths (LINT1, как billing-audit/process-*). Каждый внешний инструмент прошёл провенанс-вет IS9 (`docs/security/marketing-vet.md`) ДО установки. Категория **marketing-tooling** (18-я off-phase подкатегория, раздел C1 карты), вне R6.0/R6.1/R14.
|
||||
|
||||
**Блок 1 — note (v3.23):** **graphifyy** (Tooling #86) — user-level скил `~/.claude/skills/graphify/SKILL.md` (`/graphify` активация), CLI `graphifyy` (npm, строит knowledge graph из docs+code); **не** project-level и **не** в `enabledPlugins` (user-level — вне `enabledPlugins` проекта). Артефакты `graphify-out*/` в корне — gitignored. Категория **knowledge-graph-tooling** (19-я off-phase подкатегория), вне R6.0/R6.1/R14. ADR-017 (KG1–KG5: границы ↔ context7 #60 / Boost #10 / openapi-mcp #47 / Sentry #34 / adr-kit #36 + mermaid #37).
|
||||
|
||||
**Отмена:** через удаление из `enabledPlugins` в `~/.claude/settings.json` или через live-override `/имя-плагина` (R0.4.B) на одно действие.
|
||||
|
||||
#### Блок 2: Built-in skills Claude Code (всегда доступны через `Skill` tool по `/имя`)
|
||||
@@ -844,7 +848,7 @@ Pravila §12 (Superpowers инвокация первой), §14 (queen-роут
|
||||
- **UI-пул** (#31 UPM, #32 21st) — здесь R15 не применяется; R14 pipeline ведёт (это UI-задачи по природе).
|
||||
- **infrastructure** (#33 claude-md-management) — единственный канал для правок CLAUDE.md (Pravila §5 п.10 + R10.1 Блок 1).
|
||||
- **authoring-tooling** (#56-#58) — политика триггеров: skill-creator ≥3 повторений workflow → новый скил; hookify повторяющаяся ошибка → новый хук (с pre-check HK1); plugin-dev — для расширений plugin-grain.
|
||||
- **business-process / discovery-tooling / ml-ai-tooling / architecture-tooling / audit-security / project-management / design-tooling / integration-tooling / dev-support / finance-tooling / backend-tooling / infosec-tooling / marketing-tooling** — следуют routing-off-phase.md.
|
||||
- **business-process / discovery-tooling / ml-ai-tooling / architecture-tooling / audit-security / project-management / design-tooling / integration-tooling / dev-support / finance-tooling / backend-tooling / infosec-tooling / marketing-tooling / knowledge-graph-tooling** — следуют routing-off-phase.md.
|
||||
|
||||
### 15.7. Тип правила и enforcement
|
||||
|
||||
@@ -914,6 +918,8 @@ R16 — evidence-сбор, не правило выбора. R0–R15 продо
|
||||
|
||||
## История версий
|
||||
|
||||
- **v3.23 (2026-05-27)** — knowledge-graph-tooling: R10.1 Блок 1 note +graphifyy #86 (user-level скил, CLI `graphifyy`, knowledge graph портала, активация `/graphify`, артефакты `graphify-out*/` gitignored); R15.6 +knowledge-graph-tooling; 19-я off-phase подкатегория; user-level → вне R6.0/R6.1/R14. ADR-017 (KG1–KG5). Содержательных изменений R0–R16: 0. Связано: Tooling v2.24 (§4.59 + §0 83→84), Pravila v1.43 (§13.2), CLAUDE.md v2.31 (§3.3 +#86).
|
||||
|
||||
- **v3.22 (2026-05-25, cross-ref update)** — §0 cross-ref string Pravila v1.39+→**v1.42+** (Pravila §17.7 «Coverage announcement» добавлена — правило аннотировать каждую non-conversation задачу `coverage: <channel>:<id>`). Содержательных изменений R0–R16: 0. Связано: Pravila v1.42, Tooling v2.23, CLAUDE.md v2.28.
|
||||
|
||||
- **v3.17 (2026-05-19)** — observer schema v2 sync (ADR-011 amend): R16.1 +предложение про `schema_version` / `decision_provenance` / `environment` / `task_size` / `prompt_signal` + расширенные события (`hook_fired` / `interrupt` / `retry` / `time_burn` / `parse_gap`) + `observer_error` маркер; R16.4 +cross-ref на factor-analysis spec и plan. R0–R15 без изменений. Routing-gate / C5 controller / `/brain-retro` analyzer — нормативно в Pravila §16.7/§16.8 + ADR-011 §5; PSR_v1 фиксирует evidence-сбор (R16), не enforcement. Связано: ADR-011, Pravila v1.32 (§16 amend), CLAUDE.md v2.19, spec `docs/superpowers/specs/2026-05-19-observer-factor-analysis-design.md`, plan `docs/superpowers/plans/2026-05-19-observer-factor-analysis.md`. Per spec v1.0 §7.
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# Правила работы Claude в проекте «Лидерра»
|
||||
|
||||
**Версия:** v1.42 (25.05.2026)
|
||||
**Дата:** 25.05.2026
|
||||
**Версия:** v1.43 (27.05.2026)
|
||||
**Дата:** 27.05.2026
|
||||
**Назначение:** настройки проекта (Project instructions) — Claude читает этот файл в каждом чате и следует правилам ниже.
|
||||
**Статус документа:** ✅ утверждён. Содержимое скопировано в поле "Project instructions" Claude.ai. Файл хранится в архиве как служебный документ.
|
||||
|
||||
**Что изменилось в v1.43 относительно v1.42:** knowledge-graph-tooling integration — **§13.2 +абзац «Off-phase knowledge-graph-tooling»**: #86 graphifyy (CLI, установка `uv tool install graphifyy`, user-level skill `~/.claude/skills/graphify/SKILL.md` через `/graphify install --platform claude`; активация через `/graphify <команда>` — query / path / explain / update / build; артефакты `graphify-out*/` обязательно в `.gitignore`). 19-я off-phase подкатегория, не UI → вне R6.0/R6.1/R14. Границы — ADR-017 (KG1–KG5: ↔ context7 #60 / Boost #10 / openapi-mcp #47 / Sentry MCP #34 / adr-kit #36 / mermaid #37). Архитектурных изменений §§1–17: 0. Связано: Tooling v2.24+, PSR_v1 v3.23+, CLAUDE.md v2.32+.
|
||||
|
||||
**Что изменилось в v1.42 относительно v1.41:** LLM-first router overhaul Phase 3 deferred follow-up #1 — **§17.7 «Coverage announcement» добавлен**. Правило: в каждом ответе на non-conversation задачу Claude обязан показать coverage-пометку в формате `coverage: <channel>:<id>` рядом с первым tool-вызовом или в начале текста. 6 каналов: `skill:` / `node:` / `chain:` / `hook:` / `agent:` / `direct:<exempt-класс>`. Observability layer (не enforcement) — фиксирует **намерение** выбора канала, дополняет машинный гейт `tools/router-tool-gate.mjs` который ловит **факт**. Отсутствие пометки на non-conversation эпизоде — сигнал для C5 контролёра в STATUS.md, не блокирует коммит. Граница с routing-тегом §16.7: routing-тег только для `user_directed_method`, coverage-пометка — всегда для non-conversation. Cross-ref: реестр узлов `docs/registry/nodes.yaml`, цепочки `docs/routing-off-phase.md`, парсер `tools/observer-transcript-parser.mjs` (schema v4.4+ — реализация следующим коммитом). Архитектурных изменений §§1–16: 0. Связано: §17.1–17.6 (база §17 из v1.41), §16.4 (missed-activation = симметричный отчёт о пропусках §17), spec `docs/superpowers/specs/2026-05-24-llm-first-router-overhaul-design.md`, memory `project_brain_overhaul.md`.
|
||||
|
||||
**Что изменилось в v1.41 относительно v1.40:** LLM-first router overhaul Phase 1 Tasks 4+5. **§12 «Superpowers hard rule» снят** (Task 4, commit `bca63fc6`) — полный текст в `docs/archive/llm-bootstrap-2026-05/pravila-12/Pravila_section_12.md`; §0 priority chain пересобран без §12 + добавлен «NB про §12» pointer на архив. **§17 «Universal skill-coverage rule» добавлен** (Task 5, this commit) — classifier-driven default-deny на non-conversation задачах, 5 exempt-классов §17.2, continuation НЕ exempt (D1, §17.3), enforcement через `tools/router-tool-gate.mjs` mode-flag `off/warn-only/enforce`. **§16.4 cross-refs мигрированы** (Task 4): tools/observer-classification-map.json + tools/.node-dormancy.json → docs/registry/nodes.yaml + buildClassificationMap / buildDormancyMap. **§16.5 hard-rule list:** §12 → §17. Архитектурное обоснование — **ADR-016** (new). Связано: spec `docs/superpowers/specs/2026-05-24-llm-first-router-overhaul-design.md` v2.3, plan `docs/superpowers/plans/2026-05-25-llm-first-router-overhaul.md` v1.2.
|
||||
@@ -646,6 +648,7 @@ P0 = блокер старта спринта или регуляторного
|
||||
| **v1.32** | **19.05.2026** | Observer factor-analysis extension (ADR-011 amend): §16.2 +абзац «Схема эпизода v2» (`schema_version: 2`, `decision_provenance`, `environment`, `task_size`, `task_ref`, `prompt_signal`; `outcome` `unknown` при записи; виды событий +`hook_fired`/`interrupt`/`retry`/`time_burn`/`parse_gap`); §16.3 4→5 контролёров (+C5 observer-coverage-checker, warn-only); §16.7 (новое) routing-тег-дисциплина — Stop-хук `decision: block` при навязанном методе без тега, `stop_hook_active` guard; §16.8 (новое) самодисциплина наблюдателя (`observer_error` маркер, `parse_gap` событие, C5). Spec `docs/superpowers/specs/2026-05-19-observer-factor-analysis-design.md`, plan `docs/superpowers/plans/2026-05-19-observer-factor-analysis.md`. Связано: PSR_v1 v3.17, CLAUDE.md v2.19. Архитектурных изменений в §§1–15: 0. |
|
||||
| **v1.33** | **19.05.2026** | Observer factor-analysis phase 1.1 (ADR-011 amend): §16.2 — `decision_provenance.kind` расширен до 3 значений (`autonomous` \| `user_directed_method` \| `user_chose_from_options`); 3-й kind — collaborative-choice case (заказчик выбирает один из вариантов, предложенных Claude в предыдущем ходе). §16.7 +абзац «Граница `user_chose_from_options`»: routing-gate НЕ блокирует этот kind — выбор из choice-space, сформулированного самим Claude, не навязанный извне метод; routing-тег не обязателен (детектор `tools/observer-choice-detector.mjs` детерминированный). Spec §11 `docs/superpowers/specs/2026-05-19-observer-factor-analysis-design.md` v1.1, plan `docs/superpowers/plans/2026-05-19-observer-factor-analysis-phase-1-1.md`. Связано: CLAUDE.md v2.20. Архитектурных изменений в §§1–15: 0. |
|
||||
| **v1.42** | **25.05.2026** | LLM-first router overhaul Phase 3 deferred follow-up #1: **+§17.7 «Coverage announcement»** — правило аннотировать каждую non-conversation задачу coverage-пометкой `coverage: <channel>:<id>` (6 каналов: skill/node/chain/hook/agent/direct). Observability layer (не enforcement) — фиксирует **намерение** выбора канала, дополняет машинный гейт §17.4 который ловит **факт**. Граница с routing-тегом §16.7: routing-тег только для `user_directed_method`, coverage-пометка — всегда для non-conversation. C5 controller фиксирует отсутствие пометки в STATUS.md, не блокирует коммит. Cross-ref: реестр `docs/registry/nodes.yaml`, цепочки `docs/routing-off-phase.md`, парсер `tools/observer-transcript-parser.mjs` (schema v4.4+ — реализация следующим коммитом deferred #2). Связано: spec `docs/superpowers/specs/2026-05-24-llm-first-router-overhaul-design.md`, memory `project_brain_overhaul.md`. NB: записи таблицы v1.34–v1.41 не дотянуты предыдущими сессиями (известный дрейф); шапка `«Что изменилось в v1.NN»` авторитетна для этого периода. Архитектурных изменений §§1–16: 0. |
|
||||
| **v1.43** | **27.05.2026** | knowledge-graph-tooling: §13.2 +абзац «Off-phase knowledge-graph-tooling» — формализован узел #86 graphifyy (user-level скил `~/.claude/skills/graphify/SKILL.md`, CLI `graphifyy`, строит knowledge graph портала из docs+code; активация `/graphify <command>`; артефакты `graphify-out*/` gitignored) как девятнадцатая off-phase подкатегория, отдельная от всех предыдущих; источник — user-level скил, не project-level → вне PSR_v1 R6.0/R6.1/R14; границы ADR-017 (KG1–KG5: ↔ context7 #60 / Boost #10 / openapi-mcp #47 / Sentry #34 / adr-kit #36 + mermaid #37). Связано: Tooling Прил.Н v2.24 (§4.59 + §0 счётчик 83→84), PSR_v1 v3.23 (R10.1 Блок 1 note +graphifyy; R15.6 +knowledge-graph-tooling), CLAUDE.md v2.31 (§3.3 +#86; §0 cross-refs). Через прямой Edit — worktree-эксцепшн §5 п.10. Архитектурных изменений в §§1–17 (кроме §13.2): 0. |
|
||||
|
||||
---
|
||||
|
||||
@@ -743,6 +746,8 @@ Frontend Design и `obra/superpowers` (v5.1.0, 14 skills) — **парный sta
|
||||
|
||||
**Off-phase marketing-tooling (C1, v1.39, 22.05.2026):** Инструменты раздела C1 карты «Маркетинг и лидогенерация» — #74 `marketing` (Tooling §4.49; marketplace-плагин Anthropic Verified; **первичный решатель C1** — контент-стратегия, SEO, кампании, аналитика лидогенерации), #75 `marketingskills` (Tooling §4.50; вендоренный сторонний скил MIT — расширенный маркетинговый материал/резерв; активируется как материал, не решатель), #76 `brand-voice` (Tooling §4.51; marketplace-плагин Anthropic Verified — вербальный бренд: TOV, копирайт, сообщения; обязателен при создании публичных текстов Лидерры), #77 `marketing-ru` (Tooling §4.52; self-authored project-скил `.claude/skills/marketing-ru/` — РФ-специфика: Яндекс-экосистема, РСБУ-маркетинг, 152-ФЗ в маркетинговых коммуникациях; **линтуется**, LINT1), #78 `Яндекс.Метрика MCP` (Tooling §4.53; MCP-сервер аналитики Яндекс.Метрики — READ-ONLY доступ к метрикам/отчётам/сегментам; никаких mutation-операций), #79 `Яндекс.Директ+Wordstat MCP` (Tooling §4.54; **Wordstat-only** — подбор ключевых слов и оценка спроса; управление рекламными кампаниями Direct отключено per IS9 — mutation-риск бюджета; только аналитическое READ-ONLY использование), #80 `Telegram MCP` (Tooling §4.55; MCP-интеграция Telegram Bot API — публикация в каналы/группы Лидерры; перед использованием подтверждение канала/бота от заказчика обязательно), #81 `Postiz` (Tooling §4.56; self-hosted AGPL-3.0; SMM-планировщик публикаций internal-use; AGPL-3.0 не тригерит copyleft при внутреннем деплое без распространения), #82 `DataForSEO` (Tooling §4.57; **DEFERRED** — платный API, pending Б-1/бюджет; зарегистрирован pending-слотом, как Figma MCP #44), #83 `Unisender Go` (Tooling §4.58; **DEFERRED** — email-маркетинг через уже-интегрированный transactional SMTP Лидерры; bulk-маркетинговые кампании требуют отдельного согласования заказчика и 152-ФЗ согласия; зарегистрирован pending-слотом). **Восемнадцатая** off-phase подкатегория. Off-phase, не UI → вне R6.0/R6.1/R14 PSR_v1. Яндекс.Метрика MCP + Wordstat MCP — READ-ONLY, mutation-операции запрещены (IS9-аналогия). `marketing-ru` **линтуется** (не в ignorePaths, LINT1). Счётчики инструментов — канон [Tooling Прил. Н §0](Tooling_v8_3.md). Границы — ADR-015. Регулируется PSR_v1 R10.1 Блок 1 (marketing + brand-voice плагины) + Блок 1 note (marketingskills вендорен + marketing-ru self-authored) + Блок 3 (Яндекс.Метрика/Директ+Wordstat/Telegram MCP). Установлено 22.05.2026; план `docs/superpowers/plans/2026-05-22-c1-marketing-tooling.md`.
|
||||
|
||||
**Off-phase knowledge-graph-tooling (v1.43, 27.05.2026):** #86 `graphifyy` (Tooling §4.59; CLI-инструмент, установка `uv tool install graphifyy`; user-level skill `~/.claude/skills/graphify/SKILL.md` установлен через `/graphify install --platform claude`). **Активация:** через явный вызов `/graphify <команда>` — `query` / `path` / `explain` / `update` / `build`; не проактивно. Граф знаний всего портала Лидерры (docs + .claude/ + app/) для cross-layer навигации — «где вызывается функция», «как модуль X связан с Y», «структурные зависимости spec→code». Spike 27.05.2026: combined граф 6305 узлов / 6753 рёбер / 1009 communities (93% EXTRACTED / 7% INFERRED). **Артефакты `graphify-out*/` обязательно в `.gitignore`**. Backend экстракции: GEMINI_API_KEY (если есть) ИЛИ Claude Code subagent dispatch (применяется в Лидерре). Граничные правила ADR-017: KG1 (vs context7 #60 — внутренний codebase vs внешние SDK-доки), KG2 (vs Boost #10 — статический граф vs runtime-queries), KG3 (vs openapi-mcp #47 — весь проект vs один спек), KG4 (vs Sentry MCP #34 — структура vs runtime errors), KG5 (vs adr-kit #36 / mermaid #37 — auto-discovery vs manual authoring). **Девятнадцатая** off-phase подкатегория. Off-phase, не UI → вне R6.0/R6.1/R14 PSR_v1. Регулируется PSR_v1 R10.1 Блок 1 note + R15.6 knowledge-graph-tooling.
|
||||
|
||||
### 13.3. Скоуп
|
||||
|
||||
| Тип задачи | Кто отвечает |
|
||||
|
||||
+45
-5
File diff suppressed because one or more lines are too long
@@ -0,0 +1,196 @@
|
||||
# ADR-017: Knowledge-graph tooling formalization (graphify)
|
||||
|
||||
- **Status:** Accepted
|
||||
- **Date:** 2026-05-27
|
||||
- **Deciders:** Дмитрий
|
||||
|
||||
## Context
|
||||
|
||||
Spike `spike/graphify-2026-05-27` (worktree `.claude/worktrees/graphify-spike`)
|
||||
отработал три фазы построения knowledge-graph портала через инструмент
|
||||
**graphify** (npm-обёртка `graphifyy`, бинарь `graphify.exe`, установлен через
|
||||
`uv tool install graphifyy` v0.8.20+):
|
||||
|
||||
- **Phase 1** (docs/): 1352 узла / 1455 рёбер / 147 communities — 271 markdown
|
||||
файл, deep semantic extraction через Claude subagent dispatch (~2.4M tokens,
|
||||
275 messages из 5h-окна).
|
||||
- **Phase 2** (.claude/): 1135 узлов / 1234 рёбер / 139 communities — 262 файла
|
||||
(66 code + 196 docs), deep semantic, ~2.0M tokens, 11 субагентов, retry
|
||||
потребовался после session-limit.
|
||||
- **Phase 3** (app/): 3818 узлов / 4064 рёбер — 824 PHP/Vue/TS файла, pure AST
|
||||
extraction (БЕЗ субагентов, ~30 сек wall-clock, **0 LLM-токенов**).
|
||||
- **Ultimate combined** (через `graphify merge-graphs`): **6305 узлов / 6753
|
||||
рёбер / 1009 communities**, 93% EXTRACTED / 7% INFERRED / 0% AMBIGUOUS.
|
||||
|
||||
Spike дал воспроизводимую методологию: subfolder + `graphify merge-graphs` —
|
||||
безопасно строить фазами с откатом по бэкапам без потери прежних узлов.
|
||||
Дополнительные узлы карты Лидерры — `lib/graph_node:*` для трёх фаз +
|
||||
ultimate, провенанс-связки к источникам.
|
||||
|
||||
graphify — **off-phase инструмент** (не входит в фазовую раскладку #1-#29):
|
||||
для knowledge-management проекта, а не для конкретной фазы разработки.
|
||||
Параллельный ряду существующих off-phase подкатегорий (debug-runtime /
|
||||
architecture-tooling / audit-security / project-management / design-tooling /
|
||||
integration-tooling / ml-ai-tooling / business-process / discovery-tooling /
|
||||
authoring-tooling / dev-support / finance-tooling / backend-tooling /
|
||||
infosec-tooling / marketing-tooling), но граничит с context7 (#60),
|
||||
Boost (#10), openapi-mcp-server (#47), Sentry MCP (#34) и adr-kit/mermaid
|
||||
(#36/#37) по семантике; границы фиксируются в Decision ниже.
|
||||
|
||||
NB: параллельная сессия завершает раздел A10 BI-tooling
|
||||
(`feat/a10-bi-tooling`, Tasks 1-4 из 19, 5 узлов #84-#88 — узлы агентов
|
||||
nodes.yaml уже заняли #84/#85, BI на feature-branch без merge). Порядок
|
||||
merge решит финальную нумерацию подкатегории (19-я или 20-я); счётчики
|
||||
закрываются normative-sync агентом после merge.
|
||||
|
||||
Альтернативный backend Ollama — отдельный установленный для 152-ФЗ-чувствительных
|
||||
задач инструмент; graphify его НЕ читает (graphify читает только `GEMINI_API_KEY`/
|
||||
`GOOGLE_API_KEY` или fallback на Claude Code subagent dispatch).
|
||||
|
||||
## Decision
|
||||
|
||||
Graphify формализуется как узел **#86 graphifyy** в реестре Tooling Прил. Н
|
||||
в новой **девятнадцатой off-phase подкатегории «knowledge-graph-tooling»**.
|
||||
|
||||
### Граничные правила (locked)
|
||||
|
||||
1. **graphify (#86) ↔ context7 (#60).** Разные слои документации:
|
||||
- **context7** — внешние библиотеки/SDK/фреймворки (актуальные docs от vendor).
|
||||
Первый выбор для «как использовать React/Laravel/Vue».
|
||||
- **graphify** — внутренний codebase + спеки + .claude/-артефакты Лидерры.
|
||||
Первый выбор для «как устроен наш модуль/где вызывается наша функция/
|
||||
связан ли наш скил X с агентом Y».
|
||||
Не дублируют. context7 ничего не знает про наш код; graphify ничего не знает
|
||||
про публичные SDK.
|
||||
|
||||
2. **graphify (#86) ↔ Laravel Boost (#10).** Разный grain:
|
||||
- **Boost** — MCP-сервер с Eloquent/DB/Laravel-docs query-апи (например, `php
|
||||
artisan tinker --execute`, query schema, search Laravel docs). Точечный
|
||||
ввод/вывод по конкретному запросу к app/.
|
||||
- **graphify** — статический knowledge graph через все слои (docs+config+code).
|
||||
Cross-layer навигация и «структурные карты», не runtime queries.
|
||||
Boost остаётся первым выбором для «выполни SQL» / «прочитай model» / «найди
|
||||
в Laravel-docs»; graphify — для «покажи связи между нашим спеком и кодом» /
|
||||
«какие концепты связаны с X».
|
||||
|
||||
3. **graphify (#86) ↔ openapi-mcp-server (#47).** Разный объект:
|
||||
- **openapi-mcp-server** — introspection одного OpenAPI-спека (`docs/api/
|
||||
openapi.yaml`), tools/resources MCP-сервера, READ-ONLY.
|
||||
- **graphify** — весь проект (docs+config+code), включая OpenAPI как часть
|
||||
более широкого графа.
|
||||
openapi-mcp-server остаётся первым выбором когда вопрос локализован в API-
|
||||
спеке («какие эндпоинты`?»); graphify — когда вопрос пересекает спек +
|
||||
реализацию + тесты + договорённости.
|
||||
|
||||
4. **graphify (#86) ↔ Sentry MCP (#34).** Разная плоскость:
|
||||
- **Sentry** — runtime ошибки (что упало в проде), READ-ONLY.
|
||||
- **graphify** — структурные отношения (как код связан), статика.
|
||||
Для post-mortem «упало X, что с чем связано» — Sentry находит X, graphify
|
||||
показывает blast radius.
|
||||
|
||||
5. **graphify (#86) ↔ adr-kit (#36) + mermaid-skill (#37).** Разная природа:
|
||||
- **adr-kit / mermaid** — manual authoring решений и диаграмм (декларативно
|
||||
заказчиком/Claude).
|
||||
- **graphify** — auto-discovery связей из исходников (deterministic AST + LLM
|
||||
semantic).
|
||||
Не пересекаются: ADR — нормативное решение; graphify-граф — наблюдаемая
|
||||
структура.
|
||||
|
||||
### Узел #86 graphifyy — атрибуты
|
||||
|
||||
- **Категория:** off-phase, knowledge-graph-tooling (19-я подкатегория).
|
||||
- **Источник:** npm `graphifyy` v0.8.20+ (через `uv tool install graphifyy`),
|
||||
binary `graphify.exe`.
|
||||
- **Установка:** `uv tool install graphifyy`; работает с user-level skill
|
||||
`~/.claude/skills/graphify/SKILL.md` (через `graphify install --platform
|
||||
claude`).
|
||||
- **Активация:** explicit `/graphify <команда>`, не проактивно.
|
||||
- **Артефакты:** `graphify-out/{graph.json,GRAPH_REPORT.md,graph.html,cache/}` в
|
||||
CWD откуда запущен. Должны быть **в `.gitignore`** (`graphify-out*/`), чтобы
|
||||
build-артефакты не попадали в diff/commit.
|
||||
- **LLM-backend:** GEMINI_API_KEY/GOOGLE_API_KEY (если есть) или fallback на
|
||||
Claude Code subagent dispatch. **НЕ читает ANTHROPIC_API_KEY, OPENAI_API_KEY,
|
||||
Ollama API.**
|
||||
- **Ollama compliance:** Ollama установлен в проекте для 152-ФЗ
|
||||
чувствительных задач (локальный LLM без отправки в Anthropic), но
|
||||
graphify Ollama НЕ использует — это два независимых инструмента.
|
||||
|
||||
### Стратегия обновлений (locked)
|
||||
|
||||
- **Manual update пока единственный безопасный режим:** `/graphify --update` (LLM
|
||||
для doc/MD-изменений; AST-only для code-изменений).
|
||||
- **Auto-update post-commit hook отложен.** Spike-попытка 27.05 вечером:
|
||||
`graphify update .` от широкого scope разнесло граф 6305 → 41586 узлов (38 МБ
|
||||
bloat), потому что подхватил `tools/` + root-level .mjs + другое за пределами
|
||||
трёх фаз. **Откат через re-merge phase-бэкапов восстановил canonical
|
||||
6305/6753/1009.** Перед auto-update необходимо:
|
||||
- Спроектировать узкий scope или exclude-pattern (vendor/, tools/, node_modules/).
|
||||
- Узкий manifest, корректно покрывающий все 3 фазы.
|
||||
- Smoke-test перед wire-in lefthook.
|
||||
|
||||
До этого момента — никакого автоматического обновления.
|
||||
|
||||
### Spike worktree → main стратегия
|
||||
|
||||
- Spike worktree (`spike/graphify-2026-05-27`) остаётся локально для повторяемых
|
||||
rebuild'ов через `subfolder + merge-graphs` методологию.
|
||||
- На main commit'ятся: эта ADR-017, обновления нормативки (CLAUDE.md/Pravila/
|
||||
PSR_v1/Tooling/nodes.yaml/routing-off-phase.md), `.gitignore` add
|
||||
`graphify-out*/`. Phase 1+2+3 binaries (graph.json + ультимейт ~5MB) **не
|
||||
коммитятся** — пересобираются по запросу.
|
||||
- Spec/plan на main (`docs/superpowers/specs/2026-05-27-graphify-spike-design.md`
|
||||
- `docs/superpowers/plans/2026-05-27-graphify-spike.md`) — untracked,
|
||||
требуют revision (Ollama assumption в исходной spec ошибочен). Можно
|
||||
обновить post-формализации либо drop.
|
||||
|
||||
## Consequences
|
||||
|
||||
**+** Knowledge-graph для всего портала Лидерры — формализованный инструмент
|
||||
ответа на cross-layer вопросы («где наш скил X используется в коде / в каких
|
||||
спеках упоминается агент Y / какие концепты связаны с биллингом»).
|
||||
|
||||
**+** Bридирует gap между документацией (docs/), конфигом (.claude/) и
|
||||
реализацией (app/) — единый граф для onboarding и architectural reasoning.
|
||||
|
||||
**+** Subfolder + merge-graphs методология воспроизводима для дополнения
|
||||
графа без потери прежних узлов; provenance к источникам сохраняется.
|
||||
|
||||
**+** Граничные правила фиксируют когда что использовать → нет дублирования с
|
||||
context7/Boost/openapi/Sentry/adr-kit.
|
||||
|
||||
**–** Первичная сборка дорогая по subagent-токенам (Phase 1+2 = ~4.4M tokens).
|
||||
Регенерация графа не должна быть рутинной операцией.
|
||||
|
||||
**–** Auto-update отложен → граф быстро устаревает между manual rebuild'ами.
|
||||
До wire-in safety review — этот риск осознанный.
|
||||
|
||||
**–** Третий канал документации проекта (после CLAUDE.md/MEMORY.md и реестра
|
||||
Tooling) — нагрузка на дисциплину обновлений.
|
||||
|
||||
**–** Размер canonical graph.json ~5 МБ — должен быть в `.gitignore`; иначе
|
||||
diff'ы будут шумные.
|
||||
|
||||
## Compliance
|
||||
|
||||
- Узел #86 graphifyy в реестре Tooling Прил. Н, 19-я подкатегория
|
||||
knowledge-graph-tooling.
|
||||
- CLAUDE.md §3.3 +строка #89; §0 cross-refs Pravila/PSR_v1/Tooling bumped; §6 +
|
||||
абзац о spike; §9 changelog entry.
|
||||
- Pravila §13.2 +абзац «Off-phase knowledge-graph-tooling».
|
||||
- PSR_v1 R10.1 Блок 1 note (graphifyy не UI → вне R6/R14); R15.6 +knowledge-
|
||||
graph-tooling в список off-phase подкатегорий.
|
||||
- nodes.yaml: узел #86 graphifyy с subcategory `knowledge-graph-tooling`.
|
||||
- routing-off-phase.md: trigger «knowledge graph / codebase structure / cross-
|
||||
layer concept query» → #86.
|
||||
- spike worktree `.gitignore` update: `graphify-out*/`.
|
||||
|
||||
## Cross-refs
|
||||
|
||||
- Spike spec (untracked, revision pending): `docs/superpowers/specs/2026-05-27-
|
||||
graphify-spike-design.md`.
|
||||
- Spike plan (untracked, revision pending): `docs/superpowers/plans/2026-05-27-
|
||||
graphify-spike.md`.
|
||||
- Memory (user-local): `~/.claude/projects/.../memory/project_graphify_phase3_
|
||||
done.md` — runtime handoff state, инцидент с auto-hook + откат.
|
||||
- Skill (user-level, через `graphify install --platform claude`):
|
||||
`~/.claude/skills/graphify/SKILL.md`.
|
||||
+12
-12
@@ -1,6 +1,6 @@
|
||||
# Brain Status (auto-generated)
|
||||
|
||||
Last updated: 2026-05-27T15:09:45.835Z
|
||||
Last updated: 2026-05-27T15:32:59.632Z
|
||||
|
||||
| Контролёр | Состояние | Детали |
|
||||
|---|---|---|
|
||||
@@ -8,13 +8,13 @@ Last updated: 2026-05-27T15:09:45.835Z
|
||||
| C2 Cross-ref consistency | ✅ | [cross-ref-checker] OK — 0 drift in 4 files |
|
||||
| C3 Observer-of-observer | ✅ | [observer-of-observer] OK — last read 0 week(s) ago |
|
||||
| C4 Сигнальный статус | ✅ | This file (self-reference) |
|
||||
| C5 Observer-coverage | ⚠️ | 692 episode(s) this month · Stop-hook + post-commit OK · 21 missed activation(s) — see /brain-retro |
|
||||
| C5 Observer-coverage | ⚠️ | 697 episode(s) this month · Stop-hook + post-commit OK · 21 missed activation(s) — see /brain-retro |
|
||||
| C6 Chain map sync | ✅ | [chain-map-checker] OK — 16 chains in sync |
|
||||
|
||||
## Метрики (информационные, не алерты)
|
||||
|
||||
- Observer evidence: 692 episodes this month, 0 observer_error markers, 154 PII matches before filter
|
||||
- Legacy v1 episodes (not in factor analysis): 553
|
||||
- Observer evidence: 697 episodes this month, 0 observer_error markers, 155 PII matches before filter
|
||||
- Legacy v1 episodes (not in factor analysis): 558
|
||||
- Last /brain-retro: 0 day(s) ago
|
||||
- Использование узлов: см. `/brain-retro` (раз в спринт). missed_activations: 21. **Неиспользованные узлы — не алерт, если профильной задачи не было** (Pravila §16.4 v1.36; capability-readiness; см. memory `feedback_brain_unused_tools_not_problem` — outside-repo memory store).
|
||||
|
||||
@@ -26,15 +26,15 @@ Baseline дисциплины роутера (этап 2 router discipline overh
|
||||
|---|---|---|---|
|
||||
| monitoring | 32 | 0.0% | 0.0% |
|
||||
| analysis | 27 | 29.6% | 14.8% |
|
||||
| bugfix | 19 | 21.1% | 26.3% |
|
||||
| bugfix | 20 | 20.0% | 25.0% |
|
||||
| planning | 17 | 17.6% | 17.6% |
|
||||
| feature | 16 | 12.5% | 0.0% |
|
||||
| cleanup | 7 | 0.0% | 0.0% |
|
||||
| refactor | 1 | 0.0% | 0.0% |
|
||||
|
||||
Router step distribution: 1: 297, 2: 260, 3: 65, 5: 62
|
||||
Router step distribution: 1: 299, 2: 261, 3: 66, 5: 63
|
||||
|
||||
Boundaries applied (ADR / границы): 78 of 684 эпизодов (11.4%).
|
||||
Boundaries applied (ADR / границы): 80 of 689 эпизодов (11.6%).
|
||||
|
||||
## Активные многоэтапные проекты
|
||||
|
||||
@@ -59,10 +59,10 @@ Long sessions correlate with discipline drift. Если % regulated просел
|
||||
|
||||
| Компонент | Токены (in/out) | USD |
|
||||
|---|---|---|
|
||||
| Classifier (Sonnet 4.6) | 6458/61654 | $0.94 |
|
||||
| Classifier (Sonnet 4.6) | 6752/65027 | $1.00 |
|
||||
| Self-assessment (Sonnet 4.6) | 0/0 | $0.00 |
|
||||
| Reviewer (Opus 4.7 + fallback) | 0/0 | $0.00 |
|
||||
| **Итого** | | **$0.94** |
|
||||
| **Итого** | | **$1.00** |
|
||||
|
||||
## Аномалии классификатора
|
||||
|
||||
@@ -75,7 +75,7 @@ Episodes since last run: 609 / threshold: 10
|
||||
|
||||
## Reviewer: субагент vs fallback
|
||||
|
||||
0 эпизодов проверено из 692.
|
||||
0 эпизодов проверено из 697.
|
||||
|
||||
## Reviewer findings
|
||||
|
||||
@@ -117,10 +117,10 @@ Episodes since last run: 609 / threshold: 10
|
||||
|
||||
| Фраза | За всё время | За сегодня |
|
||||
|---|---|---|
|
||||
| `recovery` | 251 | 157 ⚠️ |
|
||||
| `recovery` | 273 | 179 ⚠️ |
|
||||
| `ремонт инфраструктуры` | 159 | 88 ⚠️ |
|
||||
| `срочно` | 82 | 39 ⚠️ |
|
||||
| `без скилов` | 56 | 30 ⚠️ |
|
||||
| `без скилов` | 58 | 32 ⚠️ |
|
||||
| `memory dump` | 8 | 6 ⚠️ |
|
||||
| `direct ok` | 6 | 2 |
|
||||
| `быстрый коммит` | 3 | 2 |
|
||||
|
||||
@@ -589,7 +589,11 @@ nodes:
|
||||
triggers:
|
||||
- {keyword: "отладка production runtime errors", weight: 1.0}
|
||||
- {classification: "bugfix", weight: 1.0}
|
||||
- {classification: "monitoring", weight: 1.0}
|
||||
# Removed 2026-05-27 (retro #8 follow-up): the "monitoring" classification
|
||||
# in practice meant background task-notifications (not Sentry events), so
|
||||
# this trigger fired false-positives on every bg-job-completion prompt.
|
||||
# Sentry MCP stays available via the "bugfix" classification and the
|
||||
# explicit keyword trigger.
|
||||
boundaries: []
|
||||
chain_membership: ["L13", "L8"]
|
||||
attributes:
|
||||
@@ -607,7 +611,9 @@ nodes:
|
||||
- {keyword: "отладка redis/memurai очередей", weight: 1.0}
|
||||
- {keyword: "кэша", weight: 1.0}
|
||||
- {keyword: "pest-race", weight: 1.0}
|
||||
- {classification: "monitoring", weight: 1.0}
|
||||
# Removed 2026-05-27 (retro #8 follow-up): same reason as #34 above —
|
||||
# "monitoring" classification covered bg-task-notifications, not Redis
|
||||
# state inspection. Keyword triggers remain for explicit calls.
|
||||
boundaries:
|
||||
- {relation: "read-only"}
|
||||
chain_membership: ["L13", "L8"]
|
||||
@@ -1673,6 +1679,40 @@ nodes:
|
||||
spec: "docs/superpowers/specs/2026-05-24-controller-offload-agents-design.md §4"
|
||||
tooling_section: null
|
||||
|
||||
- id: "#86"
|
||||
name: "graphifyy"
|
||||
slug: "graphifyy"
|
||||
category: "off-phase"
|
||||
subcategory: "knowledge-graph-tooling"
|
||||
status: "active"
|
||||
dormancy_reason: null
|
||||
capabilities: "CLI knowledge-graph builder для всего портала (docs+config+code). Three-phase build (docs/ + .claude/ + app/) с воспроизводимой методологией subfolder + merge-graphs. Ultimate combined: 6305 узлов / 6753 рёбер / 1009 communities. Backend: GEMINI_API_KEY/GOOGLE_API_KEY OR Claude Code subagent dispatch (используется в Лидерре). НЕ читает ANTHROPIC_API_KEY / OPENAI_API_KEY / Ollama API. Commands: query / path / explain / update / build / merge-graphs / cluster-only / export."
|
||||
triggers:
|
||||
- {keyword: "knowledge graph", weight: 1.0}
|
||||
- {keyword: "structural map", weight: 0.9}
|
||||
- {keyword: "where is X used", weight: 0.85}
|
||||
- {keyword: "cross-layer query", weight: 0.85}
|
||||
- {keyword: "concept map", weight: 0.85}
|
||||
- {keyword: "codebase graph", weight: 0.9}
|
||||
- {keyword: "/graphify", weight: 1.0}
|
||||
- {keyword: "graphify", weight: 1.0}
|
||||
- {classification: "knowledge_graph_query", weight: 1.0}
|
||||
boundaries:
|
||||
- {adr: "ADR-017", role: "KG1 — ↔ context7 #60: внутренний codebase Лидерры (graphify) vs внешние SDK docs (context7)"}
|
||||
- {adr: "ADR-017", role: "KG2 — ↔ Laravel Boost #10: static cross-layer graph (graphify) vs runtime queries app/ (Boost)"}
|
||||
- {adr: "ADR-017", role: "KG3 — ↔ openapi-mcp-server #47: весь проект (graphify) vs introspection одного спека (openapi)"}
|
||||
- {adr: "ADR-017", role: "KG4 — ↔ Sentry MCP #34: структурные отношения (graphify) vs runtime errors (Sentry)"}
|
||||
- {adr: "ADR-017", role: "KG5 — ↔ adr-kit #36 + mermaid-skill #37: auto-discovery из исходников (graphify) vs manual authoring (ADR/mermaid)"}
|
||||
- {role: "Артефакты graphify-out*/ обязательно gitignored — build-артефакты не должны попадать в diff/commit"}
|
||||
- {role: "Auto-update post-commit hook ОТЛОЖЕН (Spike 27.05 раздуло граф 6305→41586 узлов). Manual /graphify --update — единственный безопасный режим до wire-in safety review"}
|
||||
chain_membership: []
|
||||
attributes:
|
||||
tooling_section: "§4.59 #86"
|
||||
skill_file: "~/.claude/skills/graphify/SKILL.md"
|
||||
installation: "uv tool install graphifyy"
|
||||
version: "0.8.20+"
|
||||
spike_branch: "spike/graphify-2026-05-27"
|
||||
|
||||
chains:
|
||||
L1:
|
||||
name: "feature discovery & implementation chain"
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
| `cleanup` | #11 Laravel Pint | 1 |
|
||||
| `cleanup` | #12 Larastan | 1 |
|
||||
| `feature` | #19 Superpowers v5.1.0 | 1 |
|
||||
| `knowledge_graph_query` | #86 graphifyy | 1 |
|
||||
| `marketing` | #74 marketing | 1 |
|
||||
| `marketing` | #75 marketingskills | 1 |
|
||||
| `marketing` | #76 brand-voice | 1 |
|
||||
@@ -43,11 +44,11 @@
|
||||
| `marketing` | #79 Яндекс.Директ+Wordstat MCP | 1 |
|
||||
| `marketing` | #80 Telegram MCP | 1 |
|
||||
| `marketing` | #81 Postiz | 1 |
|
||||
| `monitoring` | #34 Sentry MCP | 1 |
|
||||
| `monitoring` | #35 Redis MCP | 1 |
|
||||
| `normative_sync_needed` | #84 normative-sync | 1 |
|
||||
| `planning` | #19 Superpowers v5.1.0 | 1 |
|
||||
| `planning` | #41 CCPM | 1 |
|
||||
| `planning` | #42 product-management | 1 |
|
||||
| `prod_deploy_imminent` | #85 prod-deploy-validator | 1 |
|
||||
| `refactor` | #11 Laravel Pint | 1 |
|
||||
| `refactor` | #12 Larastan | 1 |
|
||||
| `refactor` | #43 deptrac | 1 |
|
||||
|
||||
@@ -246,6 +246,19 @@ post-commit:
|
||||
fail_text: |
|
||||
status-md regeneration failed.
|
||||
|
||||
# 15. graphify safe auto-update — ADR-017 § "Стратегия обновлений".
|
||||
# Wrapper читает `git diff HEAD~1 --name-only`, фильтрует к scope
|
||||
# docs/+.claude/+app/ (НЕ tools/, vendor/, node_modules/, bin/ — иначе
|
||||
# spike-blow-up 6305→41586 узлов). Code-файлы в scope → explicit-list AST
|
||||
# extract (no detect_incremental, no manifest-prune risk) + build_merge в
|
||||
# graph.json. Doc/MD-файлы → флаг needs_update без LLM (cost-control:
|
||||
# subagent dispatch не подходит для хука). Skip silent если graphify-out/
|
||||
# нет (другие worktrees) или 0 файлов в scope. Non-blocking — всегда exit 0.
|
||||
- name: graphify-safe-update
|
||||
run: node tools/graphify-safe-update.mjs
|
||||
fail_text: |
|
||||
graphify-safe-update failed (non-blocking — run /graphify --update manually).
|
||||
|
||||
# Pre-push: проверки перед git push (медленнее, но реже запускаются)
|
||||
pre-push:
|
||||
parallel: false
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* tools/graphify-safe-update.mjs
|
||||
*
|
||||
* Safe post-commit graphify update wrapper.
|
||||
*
|
||||
* Per ADR-017 § "Стратегия обновлений" — direct `graphify update .` from
|
||||
* широкого scope разнесло граф 6305 → 41586 узлов (38 МБ bloat). Этот
|
||||
* wrapper:
|
||||
*
|
||||
* 1. Reads last commit changed files via `git diff HEAD~1 --name-only`.
|
||||
* 2. Filters to ONLY allowed scopes: docs/, .claude/, app/ — НЕ tools/,
|
||||
* vendor/, node_modules/, bin/, .git/, etc.
|
||||
* 3. For CODE files in app/ (PHP/Vue/TS/JS) → invokes graphify AST extract
|
||||
* on EXPLICIT file list (bypasses detect_incremental + manifest-prune
|
||||
* risk) + build_merge into existing graph.json.
|
||||
* 4. For DOC/MD files (docs/ + .claude/ md) → writes `needs_update` flag,
|
||||
* NO automatic LLM extraction (subagent dispatch too expensive for hook).
|
||||
* 5. Skips silently if graphify-out/ missing (e.g., worktree без graph).
|
||||
*
|
||||
* Non-blocking by design — always exits 0 to не ломать commit. Log goes to
|
||||
* stdout (lefthook собирает).
|
||||
*
|
||||
* Security Guidance #40: pure deterministic — git read + Python exec, без LLM.
|
||||
*
|
||||
* @author graphify-formalization 2026-05-27
|
||||
* @see docs/adr/ADR-017-knowledge-graph-tooling.md § "Стратегия обновлений"
|
||||
*/
|
||||
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { existsSync, writeFileSync, readFileSync } from 'node:fs';
|
||||
import { join, resolve, extname } from 'node:path';
|
||||
|
||||
const ALLOWED_SCOPES = ['docs/', '.claude/', 'app/'];
|
||||
const CODE_EXTS = new Set(['.php', '.ts', '.js', '.vue', '.mjs', '.cjs', '.py', '.go']);
|
||||
const SCAN_EXCLUDE_DIRS = ['node_modules/', 'vendor/', '__pycache__/', '.git/'];
|
||||
|
||||
/**
|
||||
* Pure: filter a list of git-diff file paths down to those within allowed
|
||||
* scopes AND not inside excluded directories.
|
||||
*
|
||||
* @param {string[]} paths git-diff output lines (relative repo paths)
|
||||
* @param {string[]} allowed prefixes that the path must start with
|
||||
* @param {string[]} excluded substrings that disqualify a path even if it matches a scope
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function filterInScope(paths, allowed, excluded) {
|
||||
return paths.filter((p) => {
|
||||
if (!allowed.some((s) => p.startsWith(s))) return false;
|
||||
if (excluded.some((ex) => p.includes(ex))) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure: partition file list by extension into code-files vs everything-else.
|
||||
* Case-insensitive on extension.
|
||||
*
|
||||
* @param {string[]} paths
|
||||
* @param {Set<string>} codeExts lowercase extensions including the dot (e.g. '.php')
|
||||
* @returns {{ codeFiles: string[], docFiles: string[] }}
|
||||
*/
|
||||
export function partitionByExtension(paths, codeExts) {
|
||||
const codeFiles = [];
|
||||
const docFiles = [];
|
||||
for (const p of paths) {
|
||||
const ext = extname(p).toLowerCase();
|
||||
if (codeExts.has(ext)) codeFiles.push(p);
|
||||
else docFiles.push(p);
|
||||
}
|
||||
return { codeFiles, docFiles };
|
||||
}
|
||||
|
||||
// ────────────────────────── CLI entry ──────────────────────────
|
||||
// Skip side-effects when imported as a module (tests). Works on Windows
|
||||
// (file:///C:/…) and POSIX (file:///home/…) by comparing the file URL
|
||||
// pathname to the resolved argv[1].
|
||||
import { fileURLToPath } from 'node:url';
|
||||
const isMain = process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1]);
|
||||
|
||||
if (isMain) {
|
||||
await runCli();
|
||||
}
|
||||
|
||||
async function runCli() {
|
||||
const REPO_ROOT = resolve(process.cwd());
|
||||
const GRAPH_DIR = join(REPO_ROOT, 'graphify-out');
|
||||
|
||||
const logInfo = (msg) => process.stdout.write(`[graphify-safe-update] ${msg}\n`);
|
||||
const silentExit = (reason) => {
|
||||
if (process.env.GRAPHIFY_SAFE_DEBUG) logInfo(`silent exit: ${reason}`);
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
// 1. Graph must exist
|
||||
if (!existsSync(join(GRAPH_DIR, 'graph.json'))) {
|
||||
silentExit('graphify-out/graph.json missing');
|
||||
}
|
||||
|
||||
// 2. Get files changed in the LAST commit only (HEAD vs HEAD~1) — NOT
|
||||
// `git diff HEAD~1` (1-arg) which diffs prev-commit-vs-working-tree and
|
||||
// pollutes scope with unstaged WIP from parallel sessions. Use
|
||||
// diff-tree of HEAD to get exactly the files touched by HEAD.
|
||||
let changedRaw;
|
||||
try {
|
||||
changedRaw = execFileSync('git', ['diff-tree', '--no-commit-id', '--name-only', '-r', 'HEAD'], {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
} catch (e) {
|
||||
silentExit(`git diff-tree failed: ${e.message?.slice(0, 80)}`);
|
||||
}
|
||||
|
||||
const allChanged = changedRaw.split('\n').map((s) => s.trim()).filter(Boolean);
|
||||
if (allChanged.length === 0) silentExit('no changed files');
|
||||
|
||||
// 3. Filter to allowed scopes + exclude system dirs
|
||||
const inScope = filterInScope(allChanged, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS);
|
||||
if (inScope.length === 0) {
|
||||
silentExit(`${allChanged.length} files changed, 0 in allowed scope (docs/.claude/app)`);
|
||||
}
|
||||
|
||||
// 4. Partition by code vs doc/md
|
||||
const { codeFiles, docFiles } = partitionByExtension(inScope, CODE_EXTS);
|
||||
|
||||
// 5. Doc/MD files → just write needs_update flag, no LLM
|
||||
if (docFiles.length > 0) {
|
||||
const flagPath = join(GRAPH_DIR, 'needs_update');
|
||||
const logPath = join(GRAPH_DIR, 'needs_update.log');
|
||||
try {
|
||||
const ts = new Date().toISOString();
|
||||
const entry = `${ts} ${docFiles.length} doc files changed:\n${docFiles.map((f) => ` ${f}`).join('\n')}\n\n`;
|
||||
writeFileSync(flagPath, `Run /graphify --update to refresh semantic graph (${docFiles.length} doc files changed).\nLast: ${ts}\n`);
|
||||
let existingLog = '';
|
||||
if (existsSync(logPath)) existingLog = readFileSync(logPath, 'utf-8');
|
||||
writeFileSync(logPath, entry + existingLog);
|
||||
logInfo(`${docFiles.length} doc/md changed → wrote needs_update flag (manual /graphify --update needed)`);
|
||||
} catch (e) {
|
||||
logInfo(`needs_update flag write failed: ${e.message?.slice(0, 80)} (non-blocking)`);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Code files → AST extract on explicit list + merge into graph
|
||||
if (codeFiles.length === 0) silentExit('no code files (only docs)');
|
||||
|
||||
const liveCodeFiles = codeFiles.filter((f) => existsSync(join(REPO_ROOT, f)));
|
||||
if (liveCodeFiles.length === 0) silentExit('all code files deleted (no AST to extract)');
|
||||
|
||||
// 7. Locate graphify Python interpreter
|
||||
const pyPathFile = join(GRAPH_DIR, '.graphify_python');
|
||||
let pyPath;
|
||||
if (existsSync(pyPathFile)) {
|
||||
pyPath = readFileSync(pyPathFile, 'utf-8').replace(/^/, '').trim();
|
||||
}
|
||||
if (!pyPath || !existsSync(pyPath)) {
|
||||
try {
|
||||
pyPath = execFileSync('uv', ['tool', 'run', '--from', 'graphifyy', 'python', '-c', 'import sys; print(sys.executable)'], {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
}).trim();
|
||||
} catch (e) {
|
||||
logInfo(`graphify python not resolvable: ${e.message?.slice(0, 80)} — manual /graphify --update needed`);
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Run AST extract + merge via Python one-liner
|
||||
const liveListJson = JSON.stringify(liveCodeFiles);
|
||||
const pyCode = `
|
||||
import sys, json
|
||||
from pathlib import Path
|
||||
from graphify.extract import extract
|
||||
from graphify.build import build_merge
|
||||
from graphify.export import to_json
|
||||
|
||||
repo_root = Path(${JSON.stringify(REPO_ROOT)})
|
||||
files = [repo_root / f for f in json.loads('''${liveListJson}''')]
|
||||
ast = extract(files, cache_root=repo_root)
|
||||
ast_nodes = len(ast.get('nodes', []))
|
||||
ast_edges = len(ast.get('edges', []))
|
||||
print(f'[graphify-safe-update] AST: {ast_nodes} nodes, {ast_edges} edges from {len(files)} code files')
|
||||
|
||||
# dedup=False для incremental: дефолтный dedup=True агрессивно фьюз-дедупит весь
|
||||
# merged граф (483 exact + 447 fuzzy на 6305-node графе → 5356), to_json потом
|
||||
# refuses overwrite смаленьшим графом. dedup=False → чистый union по ID.
|
||||
graph_path = repo_root / 'graphify-out' / 'graph.json'
|
||||
G = build_merge([ast], graph_path=str(graph_path), dedup=False)
|
||||
to_json(G, dict(), str(graph_path))
|
||||
print(f'[graphify-safe-update] merged: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges in graph.json')
|
||||
`;
|
||||
|
||||
try {
|
||||
const out = execFileSync(pyPath, ['-c', pyCode], {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
cwd: REPO_ROOT,
|
||||
});
|
||||
process.stdout.write(out);
|
||||
} catch (e) {
|
||||
logInfo(`AST extract/merge failed (non-blocking): ${e.message?.slice(0, 200)}`);
|
||||
if (e.stderr) process.stderr.write(e.stderr.toString());
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// tools/graphify-safe-update.test.mjs
|
||||
// Pure-helper tests for the post-commit safe-update wrapper.
|
||||
// Covers scope filtering + code/doc partitioning — the parts that gated
|
||||
// the spike-bloat incident (ADR-017 § "Стратегия обновлений").
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { filterInScope, partitionByExtension } from './graphify-safe-update.mjs';
|
||||
|
||||
const ALLOWED_SCOPES = ['docs/', '.claude/', 'app/'];
|
||||
const SCAN_EXCLUDE_DIRS = ['node_modules/', 'vendor/', '__pycache__/', '.git/'];
|
||||
const CODE_EXTS = new Set(['.php', '.ts', '.js', '.vue', '.mjs', '.cjs', '.py', '.go']);
|
||||
|
||||
describe('filterInScope', () => {
|
||||
it('keeps files inside allowed scopes', () => {
|
||||
const input = ['docs/foo.md', '.claude/skills/x/SKILL.md', 'app/Models/Deal.php'];
|
||||
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual(input);
|
||||
});
|
||||
|
||||
it('rejects files outside allowed scopes (tools/, root .md, bin/)', () => {
|
||||
const input = [
|
||||
'tools/graphify-safe-update.mjs',
|
||||
'CHANGELOG.md',
|
||||
'bin/something.exe',
|
||||
'package.json',
|
||||
'README.md',
|
||||
];
|
||||
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([]);
|
||||
});
|
||||
|
||||
it('rejects vendor/node_modules even if nominally under app/', () => {
|
||||
const input = [
|
||||
'app/vendor/laravel/framework/Foo.php',
|
||||
'app/node_modules/foo/bar.js',
|
||||
'app/__pycache__/x.pyc',
|
||||
'app/.git/HEAD',
|
||||
];
|
||||
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([]);
|
||||
});
|
||||
|
||||
it('keeps real app/ files alongside vendor exclusions', () => {
|
||||
const input = [
|
||||
'app/app/Services/LedgerService.php',
|
||||
'app/vendor/laravel/framework/Container.php',
|
||||
'app/resources/js/views/Dashboard.vue',
|
||||
];
|
||||
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([
|
||||
'app/app/Services/LedgerService.php',
|
||||
'app/resources/js/views/Dashboard.vue',
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns empty array on empty input', () => {
|
||||
expect(filterInScope([], ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([]);
|
||||
});
|
||||
|
||||
it('handles mixed in-scope and out-of-scope', () => {
|
||||
const input = [
|
||||
'docs/CLAUDE.md',
|
||||
'tools/x.mjs',
|
||||
'app/Models/Deal.php',
|
||||
'CHANGELOG.md',
|
||||
'.claude/skills/y/SKILL.md',
|
||||
];
|
||||
expect(filterInScope(input, ALLOWED_SCOPES, SCAN_EXCLUDE_DIRS)).toEqual([
|
||||
'docs/CLAUDE.md',
|
||||
'app/Models/Deal.php',
|
||||
'.claude/skills/y/SKILL.md',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('partitionByExtension', () => {
|
||||
it('puts .php/.ts/.vue/.mjs into code', () => {
|
||||
const input = ['app/Foo.php', 'app/bar.ts', 'app/baz.vue', 'app/qux.mjs'];
|
||||
const { codeFiles, docFiles } = partitionByExtension(input, CODE_EXTS);
|
||||
expect(codeFiles).toEqual(input);
|
||||
expect(docFiles).toEqual([]);
|
||||
});
|
||||
|
||||
it('puts .md/.txt into doc', () => {
|
||||
const input = ['docs/foo.md', '.claude/bar.md', 'app/README.txt'];
|
||||
const { codeFiles, docFiles } = partitionByExtension(input, CODE_EXTS);
|
||||
expect(codeFiles).toEqual([]);
|
||||
expect(docFiles).toEqual(input);
|
||||
});
|
||||
|
||||
it('partitions mixed list correctly', () => {
|
||||
const input = [
|
||||
'app/Models/Deal.php',
|
||||
'docs/spec.md',
|
||||
'app/resources/js/utils.ts',
|
||||
'.claude/skills/x/SKILL.md',
|
||||
'app/composer.json',
|
||||
];
|
||||
const { codeFiles, docFiles } = partitionByExtension(input, CODE_EXTS);
|
||||
expect(codeFiles).toEqual(['app/Models/Deal.php', 'app/resources/js/utils.ts']);
|
||||
expect(docFiles).toEqual([
|
||||
'docs/spec.md',
|
||||
'.claude/skills/x/SKILL.md',
|
||||
'app/composer.json',
|
||||
]);
|
||||
});
|
||||
|
||||
it('is case-insensitive on extension', () => {
|
||||
const input = ['app/Foo.PHP', 'app/Bar.TS', 'docs/README.MD'];
|
||||
const { codeFiles, docFiles } = partitionByExtension(input, CODE_EXTS);
|
||||
expect(codeFiles).toEqual(['app/Foo.PHP', 'app/Bar.TS']);
|
||||
expect(docFiles).toEqual(['docs/README.MD']);
|
||||
});
|
||||
|
||||
it('returns empty arrays on empty input', () => {
|
||||
const { codeFiles, docFiles } = partitionByExtension([], CODE_EXTS);
|
||||
expect(codeFiles).toEqual([]);
|
||||
expect(docFiles).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user