From 396ec5f851993a4756368ca5de9f99674c26a716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Thu, 18 Jun 2026 23:42:15 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D1=80=D0=B0=D0=B7=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=B0=D1=87=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BA=D0=B0=20brand-voice=20=E2=80=94=203=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=B2=D1=8B=D0=BA=D0=B0=20(=D1=80=D0=BE=D1=83=D1=82=D0=B5?= =?UTF-8?q?=D1=80-=D1=80=D0=B5=D0=B5=D1=81=D1=82=D1=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Эпик роутер-реестр, спека v2 §2, этап 1. Зонтик brand-voice развёрнут в 3 карточки-навыка + 3 под-узла #76a..#76c. - 3 карточки: brand-voice-enforcement, discover-brand, guideline-generation - nodes.yaml: узел #76 → #76a..#76c; триггеры (тон/голос бренда → enforcement, brand guidelines → guideline-generation); ADR-015 MKT6 на enforcement - зонтик убран; registry-load.test: 147 узлов / 139 active Этап 1 (разворачивание): 10/12 комков готовы. Остались развилки владельцу: hookify (slug hookify:hookify кривой) и ui-ux-pro-max (требует правки PSR). Регрессия (без 5 pre-existing node:test файлов): 4365 passed, exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../contracts/brand-voice.contract.json | 12 ------ ...ice__brand-voice-enforcement.contract.json | 29 ++++++++++++++ .../brand-voice__discover-brand.contract.json | 29 ++++++++++++++ ...-voice__guideline-generation.contract.json | 29 ++++++++++++++ docs/registry/nodes.yaml | 38 ++++++++++++++++--- docs/registry/splitting-inventory.md | 7 ++-- tools/registry-load.test.mjs | 10 ++--- 7 files changed, 129 insertions(+), 25 deletions(-) delete mode 100644 docs/registry/contracts/brand-voice.contract.json create mode 100644 docs/registry/contracts/brand-voice__brand-voice-enforcement.contract.json create mode 100644 docs/registry/contracts/brand-voice__discover-brand.contract.json create mode 100644 docs/registry/contracts/brand-voice__guideline-generation.contract.json diff --git a/docs/registry/contracts/brand-voice.contract.json b/docs/registry/contracts/brand-voice.contract.json deleted file mode 100644 index de87881..0000000 --- a/docs/registry/contracts/brand-voice.contract.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "skill": "brand-voice", - "kind": "external", - "needs": ["текст/коммуникация для проверки голоса бренда"], - "produces": ["вербальные brand guidelines / проверка тональности"], - "constraints": ["вербальный бренд (ADR-015 MKT6) НЕ Brandbook визуальный", "взаимодополняют"], - "preview-form": "outline", - "defaults": ["единый тон коммуникации Лидерры"], - "key-decisions": ["тональность под канал/аудиторию"], - "acceptance-criteria": ["текст соответствует голосу бренда"], - "source": { "version": "n/a", "hash": "0000000000000000000000000000000000000000000000000000000000000000", "path": "" } -} diff --git a/docs/registry/contracts/brand-voice__brand-voice-enforcement.contract.json b/docs/registry/contracts/brand-voice__brand-voice-enforcement.contract.json new file mode 100644 index 0000000..d0a6bea --- /dev/null +++ b/docs/registry/contracts/brand-voice__brand-voice-enforcement.contract.json @@ -0,0 +1,29 @@ +{ + "skill": "brand-voice:brand-voice-enforcement", + "kind": "external", + "needs": [ + "контент для применения brand-гайдов" + ], + "produces": [ + "контент, выровненный по голосу бренда (email/pitch/LinkedIn/Slack/proposal)" + ], + "constraints": [ + "под-навык зонтика brand-voice (вербальный бренд)", + "ADR-015 MKT6: вербальный бренд vs визуальный (Brandbook v2)" + ], + "preview-form": "outline", + "defaults": [ + "brand-voice-enforcement — применение гайдов к генерации" + ], + "key-decisions": [ + "какие brand-ограничения применить" + ], + "acceptance-criteria": [ + "контент соответствует голосу бренда" + ], + "source": { + "version": "n/a", + "hash": "0000000000000000000000000000000000000000000000000000000000000000", + "path": "" + } +} diff --git a/docs/registry/contracts/brand-voice__discover-brand.contract.json b/docs/registry/contracts/brand-voice__discover-brand.contract.json new file mode 100644 index 0000000..4371887 --- /dev/null +++ b/docs/registry/contracts/brand-voice__discover-brand.contract.json @@ -0,0 +1,29 @@ +{ + "skill": "brand-voice:discover-brand", + "kind": "external", + "needs": [ + "потребность найти разрозненные brand-материалы" + ], + "produces": [ + "discovery-отчёт: найденные материалы (Notion/Confluence/Drive/Box/Figma/Gong/Slack)" + ], + "constraints": [ + "под-навык зонтика brand-voice (вербальный бренд)", + "ADR-015 MKT6: вербальный бренд vs визуальный (Brandbook v2)" + ], + "preview-form": "outline", + "defaults": [ + "discover-brand — автономный поиск brand-материалов по платформам" + ], + "key-decisions": [ + "где искать материалы" + ], + "acceptance-criteria": [ + "материалы найдены и категоризированы" + ], + "source": { + "version": "n/a", + "hash": "0000000000000000000000000000000000000000000000000000000000000000", + "path": "" + } +} diff --git a/docs/registry/contracts/brand-voice__guideline-generation.contract.json b/docs/registry/contracts/brand-voice__guideline-generation.contract.json new file mode 100644 index 0000000..c883feb --- /dev/null +++ b/docs/registry/contracts/brand-voice__guideline-generation.contract.json @@ -0,0 +1,29 @@ +{ + "skill": "brand-voice:guideline-generation", + "kind": "external", + "needs": [ + "исходные материалы (docs/transcripts/recordings)" + ], + "produces": [ + "brand voice guidelines из материалов" + ], + "constraints": [ + "под-навык зонтика brand-voice (вербальный бренд)", + "ADR-015 MKT6: вербальный бренд vs визуальный (Brandbook v2)" + ], + "preview-form": "outline", + "defaults": [ + "guideline-generation — генерация гайдов из материалов" + ], + "key-decisions": [ + "какие атрибуты голоса" + ], + "acceptance-criteria": [ + "гайды полны и без открытых вопросов" + ], + "source": { + "version": "n/a", + "hash": "0000000000000000000000000000000000000000000000000000000000000000", + "path": "" + } +} diff --git a/docs/registry/nodes.yaml b/docs/registry/nodes.yaml index 5dd2010..562c0b3 100644 --- a/docs/registry/nodes.yaml +++ b/docs/registry/nodes.yaml @@ -2270,18 +2270,17 @@ nodes: attributes: tooling_section: "§4.50 #75" - - id: "#76" - name: "brand-voice" - slug: "brand-voice" + - id: "#76a" + name: "brand-voice-enforcement" + slug: "brand-voice:brand-voice-enforcement" category: "off-phase" subcategory: "marketing-tooling" status: "active" dormancy_reason: null - capabilities: "Плагин для разработки и проверки голоса бренда: создание вербальных brand guidelines, проверка тональности текстов, обеспечение единого стиля коммуникации Лидерры." + capabilities: "Применение brand-гайдов к генерации контента (email/pitch/LinkedIn/Slack/proposal): выравнивание по голосу бренда." triggers: - {keyword: "тон бренда", weight: 1.0} - {keyword: "голос бренда", weight: 1.0} - - {keyword: "brand guidelines для текстов", weight: 1.0} - {keyword: "тон копирайта", weight: 1.0} - {keyword: "voice", weight: 1.0} - {keyword: "тональность", weight: 1.0} @@ -2293,6 +2292,35 @@ nodes: attributes: tooling_section: "§4.51 #76" + - id: "#76b" + name: "brand-voice-discover-brand" + slug: "brand-voice:discover-brand" + category: "off-phase" + subcategory: "marketing-tooling" + status: "active" + dormancy_reason: null + capabilities: "Автономный поиск разрозненных brand-материалов по платформам (Notion/Confluence/Drive/Box/Figma/Gong/Slack) → discovery-отчёт." + triggers: [] + boundaries: [] + chain_membership: [] + attributes: + tooling_section: "§4.51 #76" + + - id: "#76c" + name: "brand-voice-guideline-generation" + slug: "brand-voice:guideline-generation" + category: "off-phase" + subcategory: "marketing-tooling" + status: "active" + dormancy_reason: null + capabilities: "Генерация brand voice guidelines из исходных материалов (docs/transcripts/recordings)." + triggers: + - {keyword: "brand guidelines для текстов", weight: 1.0} + boundaries: [] + chain_membership: [] + attributes: + tooling_section: "§4.51 #76" + - id: "#77" name: "marketing-ru" slug: "marketing-ru" diff --git a/docs/registry/splitting-inventory.md b/docs/registry/splitting-inventory.md index 304a6e8..f13fc7b 100644 --- a/docs/registry/splitting-inventory.md +++ b/docs/registry/splitting-inventory.md @@ -119,9 +119,10 @@ | design-plugin (7) | ✅ DONE | bf3d557 (committed, push deferred — observer-race) | | marketing-plugin (8) | ✅ DONE | committed (push deferred) | | operations (9: +process-doc узел) | ✅ DONE | committed (push deferred) | -| finance-plugin (8, journal ×2 оставлены) | ✅ DONE | этот коммит (push deferred) | -| brand-voice (3) | ⬜ | | -| ui-ux-pro-max (7, +правка PSR) | ⬜ | | +| finance-plugin (8, journal ×2 оставлены) | ✅ DONE | committed (push deferred) | +| brand-voice (3) | ✅ DONE | этот коммит (push deferred) | +| hookify (1–2) | ⏸ развилка владельцу | — | +| ui-ux-pro-max (7, +правка PSR) | ⏸ развилка владельцу | — | ### Доп-каскады, выявленные на superpowers (учесть для крупных комков) diff --git a/tools/registry-load.test.mjs b/tools/registry-load.test.mjs index e4529fb..7c6a5f2 100644 --- a/tools/registry-load.test.mjs +++ b/tools/registry-load.test.mjs @@ -5,9 +5,9 @@ import { loadRegistry, clearCache, findByClassification, findByKeyword, findActi describe('registry-load', () => { beforeEach(() => clearCache()); - it('loads registry (145 nodes: разворачивание #33/#19/#57/#36/#42/#46/#74/#51/#61 комков 18.06.2026)', () => { + it('loads registry (147 nodes: разворачивание 9 комков #33/#19/#57/#36/#42/#46/#74/#51/#61/#76 18.06.2026)', () => { const r = loadRegistry(); - expect(r.nodes).toHaveLength(145); + expect(r.nodes).toHaveLength(147); expect(r.version).toBe('0.1.0'); }); @@ -46,9 +46,9 @@ describe('registry-load', () => { it('findActiveNodes excludes non-active (nodes.yaml registry)', () => { const r = loadRegistry(); const active = findActiveNodes(r); - // 145 nodes total; #1 historic, #17 dormant, #44/#50/#54/#67/#82/#83 deferred; - // развёрнуты: ...,#51→9,#61 finance→8 (+7) → 137 active - expect(active).toHaveLength(137); + // 147 nodes total; #1 historic, #17 dormant, #44/#50/#54/#67/#82/#83 deferred; + // развёрнуты: ...,#61→8,#76 brand-voice→3 (+2) → 139 active + expect(active).toHaveLength(139); expect(active.map(n => n.id)).toContain('#18'); expect(active.map(n => n.id)).toContain('#19a'); expect(active.map(n => n.id)).not.toContain('#1');